Snap for 11698527 from 2224b1ba35043d7b6c0d00b8727444bba7aa742b to mainline-appsearch-release

Change-Id: I0f32d6d86a8a25472bd3256927906b89ac2520cf
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index b4cb6e3..3c49053 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -29,6 +29,7 @@
 version: 2
 updates:
   - package-ecosystem: "github-actions"
+    if: github.repository == "openthread/openthread"
     directory: "/"
     schedule:
       interval: "weekly"
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 5f3d85a..953788e 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -49,11 +49,11 @@
     runs-on: ubuntu-22.04
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
       with:
         egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
       with:
         submodules: true
     - name: Bootstrap
@@ -71,11 +71,11 @@
     runs-on: ubuntu-latest
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
       with:
         egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
     - uses: gaurav-nelson/github-action-markdown-link-check@5c5dfc0ac2e225883c0e5f03a85311ec2830d368 # v1
       with:
         use-verbose-mode: 'yes'
@@ -85,11 +85,11 @@
     runs-on: ubuntu-22.04
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
       with:
         egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
       with:
         submodules: true
     - name: Bootstrap
@@ -104,11 +104,11 @@
     runs-on: ubuntu-20.04
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
       with:
         egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
       with:
         submodules: true
     - name: Bootstrap
@@ -144,11 +144,11 @@
       CXX: ${{ matrix.compiler_cpp }}
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
       with:
         egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
       with:
         submodules: true
     - name: Bootstrap
@@ -163,11 +163,11 @@
     runs-on: ubuntu-22.04
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
       with:
         egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
       with:
         submodules: true
     - name: Bootstrap
@@ -182,18 +182,18 @@
     runs-on: ubuntu-20.04
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
       with:
         egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
       with:
         submodules: true
     - name: Bootstrap
       run: |
         sudo apt-get --no-install-recommends install -y ninja-build libreadline-dev libncurses-dev
         rm -rf third_party/mbedtls/repo
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
       with:
         repository: ARMmbed/mbedtls
         ref: v3.5.0
@@ -233,13 +233,16 @@
           - gcc_ver: 12
             gcc_download_url: https://developer.arm.com/-/media/Files/downloads/gnu/12.2.rel1/binrel/arm-gnu-toolchain-12.2.rel1-x86_64-arm-none-eabi.tar.xz
             gcc_extract_dir: arm-gnu-toolchain-12.2.rel1-x86_64-arm-none-eabi
+          - gcc_ver: 13
+            gcc_download_url: https://developer.arm.com/-/media/Files/downloads/gnu/13.2.rel1/binrel/arm-gnu-toolchain-13.2.rel1-x86_64-arm-none-eabi.tar.xz
+            gcc_extract_dir: arm-gnu-toolchain-13.2.Rel1-x86_64-arm-none-eabi
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
       with:
         egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
       with:
         submodules: true
     - name: Bootstrap
@@ -276,11 +279,11 @@
       CXX: g++-${{ matrix.gcc_ver }}
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
       with:
         egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
       with:
         submodules: true
     - name: Bootstrap
@@ -309,11 +312,11 @@
       CXX: clang++-${{ matrix.clang_ver }}
     steps:
       - name: Harden Runner
-        uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+        uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
         with:
           egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-      - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+      - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
         with:
           submodules: true
       - name: Bootstrap
@@ -347,11 +350,11 @@
       LDFLAGS: -m32
     steps:
       - name: Harden Runner
-        uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+        uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
         with:
           egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-      - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+      - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
         with:
           submodules: true
       - name: Bootstrap
@@ -375,11 +378,11 @@
     runs-on: ubuntu-20.04
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
       with:
         egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
       with:
         submodules: true
     - name: Bootstrap
@@ -411,11 +414,11 @@
       CXX: ${{ matrix.CXX }}
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
       with:
         egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
       with:
         submodules: true
     - name: Bootstrap
@@ -435,11 +438,11 @@
       image: openthread/environment
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
       with:
         egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
       with:
         submodules: true
     - name: Install unzip
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index 17044aa..cc1f543 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -54,19 +54,19 @@
 
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
       with:
         egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
     - name: Checkout repository
-      uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+      uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
 
     - name: Bootstrap
       run: |
         sudo apt-get --no-install-recommends install -y ninja-build libreadline-dev libncurses-dev
 
     - name: Initialize CodeQL
-      uses: github/codeql-action/init@74483a38d39275f33fcff5f35b679b5ca4a26a99 # v2.22.5
+      uses: github/codeql-action/init@8a470fddafa5cbb6266ee11b37ef4d8aae19c571 # v3.24.6
       with:
         languages: ${{ matrix.language }}
         # If you wish to specify custom queries, you can do so here or in a config file.
@@ -80,6 +80,6 @@
         ./script/test build
 
     - name: Perform CodeQL Analysis
-      uses: github/codeql-action/analyze@74483a38d39275f33fcff5f35b679b5ca4a26a99 # v2.22.5
+      uses: github/codeql-action/analyze@8a470fddafa5cbb6266ee11b37ef4d8aae19c571 # v3.24.6
       with:
         category: "/language:${{matrix.language}}"
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 8cce3c7..9738b85 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -55,11 +55,11 @@
           - docker_name: environment
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
       with:
         egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
       with:
         submodules: true
 
@@ -83,7 +83,7 @@
           ${TAGS} --file ${DOCKER_FILE} ." >> $GITHUB_OUTPUT
 
     - name: Set up Docker Buildx
-      uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0
+      uses: docker/setup-buildx-action@2b51285047da1547ffb1b2203d8be4c0af6b1f20 # v3.2.0
 
     - name: Docker Buildx (build)
       run: |
@@ -91,7 +91,7 @@
 
     - name: Login to DockerHub
       if: success() && github.repository == 'openthread/openthread' && github.event_name != 'pull_request'
-      uses: docker/login-action@465a07811f14bebb1938fbed4728c6a1ff8901fc # v2.2.0
+      uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0
       with:
         username: ${{ secrets.DOCKER_USERNAME }}
         password: ${{ secrets.DOCKER_PASSWORD }}
diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml
index 9327427..da612bb 100644
--- a/.github/workflows/fuzz.yml
+++ b/.github/workflows/fuzz.yml
@@ -45,7 +45,7 @@
    runs-on: ubuntu-20.04
    steps:
    - name: Harden Runner
-     uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+     uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
      with:
        egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
@@ -61,7 +61,7 @@
        fuzz-seconds: 1800
        dry-run: false
    - name: Upload Crash
-     uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+     uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
      if: failure()
      with:
        name: artifacts
diff --git a/.github/workflows/makefile-check.yml b/.github/workflows/makefile-check.yml
index 2f50447..1d7fa55 100644
--- a/.github/workflows/makefile-check.yml
+++ b/.github/workflows/makefile-check.yml
@@ -48,11 +48,11 @@
     runs-on: ubuntu-20.04
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
       with:
         egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
       with:
         submodules: true
     - name: Check
diff --git a/.github/workflows/otbr.yml b/.github/workflows/otbr.yml
index 62cddbf..ac04744 100644
--- a/.github/workflows/otbr.yml
+++ b/.github/workflows/otbr.yml
@@ -62,7 +62,7 @@
       # of OMR prefix and Domain prefix is not deterministic.
       BORDER_ROUTING: 0
     steps:
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
       with:
         submodules: true
     - name: Build OTBR Docker
@@ -86,11 +86,12 @@
         export CI_ENV="$(bash <(curl -s https://codecov.io/env)) -e GITHUB_ACTIONS -e COVERAGE"
         echo "CI_ENV=${CI_ENV}"
         sudo -E ./script/test cert_suite ./tests/scripts/thread-cert/backbone/*.py || (sudo chmod a+r *.log *.json *.pcap && false)
-    - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+    - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
       with:
         name: cov-thread-1-3-backbone-docker
         path: /tmp/coverage/
-    - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+        retention-days: 1
+    - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
       if: ${{ failure() }}
       with:
         name: thread-1-3-backbone-results
@@ -103,10 +104,11 @@
     - name: Generate Coverage
       run: |
         ./script/test generate_coverage gcc
-    - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+    - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
       with:
         name: cov-thread-1-3-backbone
         path: tmp/coverage.info
+        retention-days: 1
 
   thread-border-router:
     runs-on: ubuntu-20.04
@@ -143,7 +145,7 @@
             cert_scripts: ./tests/scripts/thread-cert/border_router/nat64/*.py
             packet_verification: 1
             nat64: 1
-            description: "nat64 openthread"
+            description: "nat64"
           - otbr_mdns: "avahi"
             otbr_trel: 0
             cert_scripts: ./tests/scripts/thread-cert/border_router/*.py
@@ -162,7 +164,7 @@
             packet_verification: 1
             nat64: 0
             use_core_firewall: 1
-            description: "core firewall"
+            description: "core-firewall"
     name: BR ${{ matrix.description }} (${{ matrix.otbr_mdns }}, TREL=${{matrix.otbr_trel}})
     env:
       REFERENCE_DEVICE: 1
@@ -179,7 +181,7 @@
       NAT64: ${{ matrix.nat64 }}
       MAX_JOBS: 3
     steps:
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
     - name: Set firewall environment variables
       if: ${{ matrix.use_core_firewall }}
       run: |
@@ -206,14 +208,15 @@
         export CI_ENV="$(bash <(curl -s https://codecov.io/env)) -e GITHUB_ACTIONS -e COVERAGE"
         echo "CI_ENV=${CI_ENV}"
         sudo -E ./script/test cert_suite ${{ matrix.cert_scripts }} || (sudo chmod a+r *.log *.json *.pcap && false)
-    - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+    - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
       with:
-        name: cov-thread-border-router-docker
+        name: cov-br-docker-${{ matrix.description }}-${{ matrix.otbr_mdns }}-${{matrix.otbr_trel}}
         path: /tmp/coverage/
-    - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+        retention-days: 1
+    - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
       if: ${{ failure() }}
       with:
-        name: thread-border-router-results
+        name: br-results-${{ matrix.description }}-${{ matrix.otbr_mdns }}-${{matrix.otbr_trel}}
         path: |
           *.pcap
           *.json
@@ -223,10 +226,11 @@
     - name: Generate Coverage
       run: |
         ./script/test generate_coverage gcc
-    - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+    - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
       with:
-        name: cov-thread-border-router
+        name: cov-br-${{ matrix.description }}-${{ matrix.otbr_mdns }}-${{matrix.otbr_trel}}
         path: tmp/coverage.info
+        retention-days: 1
 
   upload-coverage:
     needs:
@@ -234,31 +238,26 @@
     - thread-border-router
     runs-on: ubuntu-20.04
     steps:
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
       with:
         submodules: true
     - name: Bootstrap
       run: |
         sudo apt-get --no-install-recommends install -y lcov
-    - uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
+    - uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
       with:
         path: coverage/
+        pattern: cov-*
+        merge-multiple: true
     - name: Combine Coverage
       continue-on-error: true
       run: |
         script/test combine_coverage
     - name: Upload Coverage
       continue-on-error: true
-      uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # v3.1.4
+      uses: codecov/codecov-action@0cfda1dd0a4ad9efc75517f399d859cd1ea4ced1 # v4.0.2
+      env:
+        CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
       with:
         files: final.info
         fail_ci_if_error: true
-
-  delete-coverage-artifacts:
-    needs: upload-coverage
-    runs-on: ubuntu-20.04
-    steps:
-    - uses: geekyeggo/delete-artifact@54ab544f12cdb7b71613a16a2b5a37a9ade990af # v2.0.0
-      with:
-        name: cov-*
-        useGlob: true
diff --git a/.github/workflows/otci.yml b/.github/workflows/otci.yml
index ece75a0..d22c227 100644
--- a/.github/workflows/otci.yml
+++ b/.github/workflows/otci.yml
@@ -57,11 +57,11 @@
       REAL_DEVICE: 0
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
       with:
         egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
     - name: Bootstrap
       run: |
         sudo rm /etc/apt/sources.list.d/* && sudo apt-get update
diff --git a/.github/workflows/otns.yml b/.github/workflows/otns.yml
index 9f4feae..7abbc91 100644
--- a/.github/workflows/otns.yml
+++ b/.github/workflows/otns.yml
@@ -58,16 +58,16 @@
     runs-on: ubuntu-22.04
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
       with:
         egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
-    - uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
+    - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
       with:
         go-version: "1.20"
     - name: Set up Python
-      uses: actions/setup-python@61a6322f88396a6271a6ee3565807d608ecaddd1 # v4.7.0
+      uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5.0.0
       with:
         python-version: "3.9"
     - name: Bootstrap
@@ -82,7 +82,7 @@
           cd /tmp/otns
           ./script/test py-unittests
         )
-    - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+    - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
       if: ${{ failure() }}
       with:
         name: unittests-pcaps
@@ -92,21 +92,22 @@
     - name: Generate Coverage
       run: |
         ./script/test generate_coverage gcc
-    - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+    - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
       with:
         name: cov-otns-unittests
         path: tmp/coverage.info
+        retention-days: 1
 
   examples:
     name: Examples
     runs-on: ubuntu-22.04
     steps:
-      - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
-      - uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
+      - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
+      - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
         with:
           go-version: "1.20"
       - name: Set up Python
-        uses: actions/setup-python@61a6322f88396a6271a6ee3565807d608ecaddd1 # v4.7.0
+        uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5.0.0
         with:
           python-version: "3.9"
       - name: Bootstrap
@@ -121,7 +122,7 @@
             cd /tmp/otns
             ./script/test py-examples
           )
-      - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+      - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
         if: ${{ failure() }}
         with:
           name: examples-pcaps
@@ -131,7 +132,7 @@
       - name: Generate Coverage
         run: |
           ./script/test generate_coverage gcc
-      - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+      - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
         with:
           name: cov-otns-examples
           path: tmp/coverage.info
@@ -159,16 +160,16 @@
       STRESS_LEVEL: ${{ matrix.stress_level }}
     steps:
       - name: Harden Runner
-        uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+        uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
         with:
           egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-      - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
-      - uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
+      - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
+      - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
         with:
           go-version: "1.20"
       - name: Set up Python
-        uses: actions/setup-python@61a6322f88396a6271a6ee3565807d608ecaddd1 # v4.7.0
+        uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5.0.0
         with:
           python-version: "3.9"
       - name: Bootstrap
@@ -183,7 +184,7 @@
             cd /tmp/otns
             ./script/test stress-tests ${{ matrix.suite }}
           )
-      - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+      - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
         if: ${{ failure() }}
         with:
           name: stress-tests-${{ matrix.suite }}-pcaps
@@ -193,10 +194,11 @@
       - name: Generate Coverage
         run: |
           ./script/test generate_coverage gcc
-      - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+      - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
         with:
           name: cov-otns-stress-tests-${{ matrix.suite }}
           path: tmp/coverage.info
+          retention-days: 1
 
   upload-coverage:
     needs:
@@ -206,17 +208,19 @@
     runs-on: ubuntu-22.04
     steps:
       - name: Harden Runner
-        uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+        uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
         with:
           egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-      - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+      - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
       - name: Bootstrap
         run: |
           sudo apt-get --no-install-recommends install -y lcov
-      - uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
+      - uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
         with:
           path: coverage/
+          pattern: cov-*
+          merge-multiple: true
       - name: Upload Coverage
         run: |
           script/test upload_codecov
diff --git a/.github/workflows/posix.yml b/.github/workflows/posix.yml
index b019a25..fd8e7d8 100644
--- a/.github/workflows/posix.yml
+++ b/.github/workflows/posix.yml
@@ -52,14 +52,15 @@
       CXXFLAGS: -DCLI_COAP_SECURE_USE_COAP_DEFAULT_HANDLER=1 -DOPENTHREAD_CONFIG_MLE_MAX_CHILDREN=15
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
       with:
         egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
     - name: Bootstrap
       run: |
         sudo apt-get --no-install-recommends install -y expect ninja-build lcov socat
+        pip install bleak
     - name: Run RCP Mode
       run: |
         ulimit -c unlimited
@@ -75,7 +76,7 @@
           CRASHED=$(./script/test check_crash | tail -1)
           [[ $CRASHED -eq "1" ]] && echo "Crashed!" || echo "Not crashed."
           echo "CRASHED_RCP=$CRASHED" >> $GITHUB_ENV
-    - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+    - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
       if: ${{ failure() && env.CRASHED_RCP == '1' }}
       with:
         name: core-expect-rcp
@@ -84,10 +85,11 @@
     - name: Generate Coverage
       run: |
         ./script/test generate_coverage gcc
-    - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+    - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
       with:
         name: cov-expects-linux-1
         path: tmp/coverage.info
+        retention-days: 1
     - name: Run TUN Mode
       run: |
         sudo rm /etc/apt/sources.list.d/* && sudo apt-get update
@@ -107,13 +109,13 @@
           CRASHED=$(./script/test check_crash | tail -1)
           [[ $CRASHED -eq "1" ]] && echo "Crashed!" || echo "Not crashed."
           echo "CRASHED_TUN=$CRASHED" >> $GITHUB_ENV
-    - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+    - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
       if: ${{ failure() && env.CRASHED_TUN == '1' }}
       with:
         name: core-expect-linux
         path: |
           ./ot-core-dump/*
-    - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+    - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
       if: ${{ failure() }}
       with:
         name: syslog-expect-linux
@@ -121,10 +123,11 @@
     - name: Generate Coverage
       run: |
         ./script/test generate_coverage gcc
-    - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+    - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
       with:
         name: cov-expects-linux-2
         path: tmp/coverage.info
+        retention-days: 1
 
   thread-cert:
     runs-on: ubuntu-20.04
@@ -135,11 +138,11 @@
       VIRTUAL_TIME: 1
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
       with:
         egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
       with:
         submodules: true
     - name: Bootstrap
@@ -153,7 +156,7 @@
     - name: Run
       run: |
         MAX_JOBS=$(getconf _NPROCESSORS_ONLN) ./script/test cert_suite ./tests/scripts/thread-cert/Cert_*.py ./tests/scripts/thread-cert/test_*.py
-    - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+    - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
       if: ${{ failure() }}
       with:
         name: thread-cert
@@ -161,7 +164,7 @@
     - name: Generate Coverage
       run: |
         ./script/test generate_coverage gcc
-    - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+    - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
       with:
         name: cov-thread-cert
         path: tmp/coverage.info
@@ -179,11 +182,11 @@
       OT_READLINE: 'readline'
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
       with:
         egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
     - name: Bootstrap
       run: |
         sudo rm /etc/apt/sources.list.d/* && sudo apt-get update
@@ -211,10 +214,11 @@
     - name: Generate Coverage
       run: |
         ./script/test generate_coverage gcc
-    - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+    - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
       with:
-        name: cov-pty-linux-${{ matrix.DAEMON }}
+        name: cov-pty-linux-${{ matrix.OT_DAEMON }}
         path: tmp/coverage.info
+        retention-days: 1
 
   pty-macos:
     name: pty-macos OT_DAEMON=${{ matrix.OT_DAEMON }}
@@ -228,11 +232,11 @@
       OT_READLINE: 'off'
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
       with:
         egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
     - name: Bootstrap
       run: |
         rm -f /usr/local/bin/2to3
@@ -258,11 +262,11 @@
     runs-on: ubuntu-20.04
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
       with:
         egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
     - name: Bootstrap
       env:
         GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
@@ -278,10 +282,11 @@
     - name: Generate Coverage
       run: |
         ./script/test generate_coverage gcc
-    - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+    - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
       with:
         name: cov-rcp-stack-reset
         path: tmp/coverage.info
+        retention-days: 1
 
   upload-coverage:
     needs:
@@ -291,38 +296,28 @@
     runs-on: ubuntu-20.04
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
       with:
         egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
       with:
         submodules: true
     - name: Bootstrap
       run: |
         sudo apt-get --no-install-recommends install -y lcov
-    - uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
+    - uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
       with:
         path: coverage/
+        pattern: cov-*
+        merge-multiple: true
     - name: Combine Coverage
       run: |
         script/test combine_coverage
     - name: Upload Coverage
-      uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # v3.1.4
+      uses: codecov/codecov-action@0cfda1dd0a4ad9efc75517f399d859cd1ea4ced1 # v4.0.2
+      env:
+        CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
       with:
         files: final.info
         fail_ci_if_error: true
-
-  delete-coverage-artifacts:
-    needs: upload-coverage
-    runs-on: ubuntu-20.04
-    steps:
-    - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
-      with:
-        egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
-
-    - uses: geekyeggo/delete-artifact@54ab544f12cdb7b71613a16a2b5a37a9ade990af # v2.0.0
-      with:
-        name: cov-*
-        useGlob: true
diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml
index 76ce1c9..a9a13cc 100644
--- a/.github/workflows/scorecards.yml
+++ b/.github/workflows/scorecards.yml
@@ -60,7 +60,7 @@
 
     steps:
       - name: "Checkout code"
-        uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+        uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
         with:
           persist-credentials: false
 
@@ -87,7 +87,7 @@
       # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
       # format to the repository Actions tab.
       - name: "Upload artifact"
-        uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.0
+        uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v3.1.0
         with:
           name: SARIF file
           path: results.sarif
@@ -95,6 +95,6 @@
 
       # Upload the results to GitHub's code scanning dashboard.
       - name: "Upload to code-scanning"
-        uses: github/codeql-action/upload-sarif@74483a38d39275f33fcff5f35b679b5ca4a26a99 # v2.1.27
+        uses: github/codeql-action/upload-sarif@8a470fddafa5cbb6266ee11b37ef4d8aae19c571 # v2.1.27
         with:
           sarif_file: results.sarif
diff --git a/.github/workflows/simulation-1.1.yml b/.github/workflows/simulation-1.1.yml
index 3705ceb..ee08f39 100644
--- a/.github/workflows/simulation-1.1.yml
+++ b/.github/workflows/simulation-1.1.yml
@@ -55,11 +55,11 @@
       MULTIPLY: 3
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
       with:
         egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
       with:
         submodules: true
     - name: Bootstrap
@@ -76,7 +76,7 @@
     - name: Run
       run: |
         ./script/test cert_suite ./tests/scripts/thread-cert/Cert_*.py ./tests/scripts/thread-cert/test_*.py
-    - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+    - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
       if: ${{ failure() }}
       with:
         name: packet-verification-pcaps
@@ -86,10 +86,11 @@
     - name: Generate Coverage
       run: |
         ./script/test generate_coverage gcc
-    - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+    - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
       with:
         name: cov-packet-verification
         path: tmp/coverage.info
+        retention-days: 1
 
   cli-ftd:
     runs-on: ubuntu-20.04
@@ -103,11 +104,11 @@
       VIRTUAL_TIME: 1
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
       with:
         egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
       with:
         submodules: true
     - name: Bootstrap
@@ -121,7 +122,7 @@
     - name: Run
       run: |
         ./script/test cert_suite ./tests/scripts/thread-cert/Cert_*.py ./tests/scripts/thread-cert/test_*.py
-    - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+    - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
       if: ${{ failure() }}
       with:
         name: cli-ftd-thread-cert
@@ -129,10 +130,11 @@
     - name: Generate Coverage
       run: |
         ./script/test generate_coverage gcc
-    - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+    - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
       with:
         name: cov-cli-ftd
         path: tmp/coverage.info
+        retention-days: 1
 
   cli-mtd:
     name: cli-mtd MESSAGE_USE_HEAP=${{ matrix.message_use_heap }}
@@ -153,11 +155,11 @@
       MESSAGE_USE_HEAP: ${{ matrix.message_use_heap }}
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
       with:
         egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
       with:
         submodules: true
     - name: Bootstrap
@@ -171,7 +173,7 @@
     - name: Run
       run: |
         ./script/test cert_suite ./tests/scripts/thread-cert/Cert_*.py ./tests/scripts/thread-cert/test_*.py
-    - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+    - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
       if: ${{ failure() }}
       with:
         name: cli-mtd-thread-cert
@@ -179,10 +181,11 @@
     - name: Generate Coverage
       run: |
         ./script/test generate_coverage gcc
-    - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+    - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
       with:
         name: cov-cli-mtd-${{ matrix.message_use_heap }}
         path: tmp/coverage.info
+        retention-days: 1
 
   cli-time-sync:
     runs-on: ubuntu-20.04
@@ -196,11 +199,11 @@
       VIRTUAL_TIME: 1
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
       with:
         egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
       with:
         submodules: true
     - name: Bootstrap
@@ -214,7 +217,7 @@
     - name: Run
       run: |
         ./script/test cert_suite ./tests/scripts/thread-cert/Cert_*.py ./tests/scripts/thread-cert/test_*.py
-    - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+    - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
       if: ${{ failure() }}
       with:
         name: cli-time-sync-thread-cert
@@ -222,10 +225,11 @@
     - name: Generate Coverage
       run: |
         ./script/test generate_coverage gcc
-    - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+    - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
       with:
         name: cov-cli-time-sync
         path: tmp/coverage.info
+        retention-days: 1
 
   expects:
     runs-on: ubuntu-20.04
@@ -235,14 +239,15 @@
       THREAD_VERSION: 1.1
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
       with:
         egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
     - name: Bootstrap
       run: |
         sudo apt-get --no-install-recommends install -y expect ninja-build lcov socat
+        pip install bleak
     - name: Run
       run: |
         ulimit -c unlimited
@@ -254,7 +259,7 @@
           CRASHED=$(./script/test check_crash | tail -1)
           [[ $CRASHED -eq "1" ]] && echo "Crashed!" || echo "Not crashed."
           echo "CRASHED_CLI=$CRASHED" >> $GITHUB_ENV
-    - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+    - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
       if: ${{ failure() && env.CRASHED_CLI == '1' }}
       with:
         name: core-expect-cli
@@ -263,10 +268,11 @@
     - name: Generate Coverage
       run: |
         ./script/test generate_coverage gcc
-    - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+    - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
       with:
         name: cov-expects
         path: tmp/coverage.info
+        retention-days: 1
 
   ot-commissioner:
     runs-on: ubuntu-22.04
@@ -274,11 +280,11 @@
       THREAD_VERSION: 1.1
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
       with:
         egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
       with:
         submodules: true
     - name: Bootstrap
@@ -312,10 +318,11 @@
     - name: Generate Coverage
       run: |
         ./script/test generate_coverage gcc
-    - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+    - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
       with:
         name: cov-ot-commissioner
         path: tmp/coverage.info
+        retention-days: 1
 
   multiple-instance:
     runs-on: ubuntu-20.04
@@ -326,11 +333,11 @@
       CXXFLAGS: "-DOPENTHREAD_CONFIG_LOG_PREPEND_UPTIME=0"
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
       with:
         egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
       with:
         submodules: true
     - name: Bootstrap
@@ -343,7 +350,7 @@
     - name: Run
       run: |
         ./script/test cert_suite ./tests/scripts/thread-cert/Cert_*.py ./tests/scripts/thread-cert/test_*.py
-    - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+    - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
       if: ${{ failure() }}
       with:
         name: ot_testing
@@ -351,10 +358,11 @@
     - name: Generate Coverage
       run: |
         ./script/test generate_coverage gcc
-    - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+    - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
       with:
         name: cov-multiple-instance
         path: tmp/coverage.info
+        retention-days: 1
 
   upload-coverage:
     needs:
@@ -368,38 +376,28 @@
     runs-on: ubuntu-20.04
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
       with:
         egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
       with:
         submodules: true
     - name: Bootstrap
       run: |
         sudo apt-get --no-install-recommends install -y lcov
-    - uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
+    - uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
       with:
         path: coverage/
+        pattern: cov-*
+        merge-multiple: true
     - name: Combine Coverage
       run: |
         script/test combine_coverage
     - name: Upload Coverage
-      uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # v3.1.4
+      uses: codecov/codecov-action@0cfda1dd0a4ad9efc75517f399d859cd1ea4ced1 # v4.0.2
+      env:
+        CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
       with:
         files: final.info
         fail_ci_if_error: true
-
-  delete-coverage-artifacts:
-    needs: upload-coverage
-    runs-on: ubuntu-20.04
-    steps:
-    - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
-      with:
-        egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
-
-    - uses: geekyeggo/delete-artifact@54ab544f12cdb7b71613a16a2b5a37a9ade990af # v2.0.0
-      with:
-        name: cov-*
-        useGlob: true
diff --git a/.github/workflows/simulation-1.2.yml b/.github/workflows/simulation-1.2.yml
index d88be6c..26153f4 100644
--- a/.github/workflows/simulation-1.2.yml
+++ b/.github/workflows/simulation-1.2.yml
@@ -66,11 +66,11 @@
         arch: ["m32", "m64"]
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
       with:
         egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
       with:
         submodules: true
     - name: Bootstrap
@@ -95,12 +95,12 @@
           CRASHED=$(./script/test check_crash | tail -1)
           [[ $CRASHED -eq "1" ]] && echo "Crashed!" || echo "Not crashed."
           echo "CRASHED=$CRASHED" >> $GITHUB_ENV
-    - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+    - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
       if: ${{ failure() }}
       with:
         name: thread-1-3-${{ matrix.compiler.c }}-${{ matrix.arch }}-pcaps
         path: "*.pcap"
-    - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+    - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
       if: ${{ failure() && env.CRASHED == '1' }}
       with:
         name: core-packet-verification-thread-1-3
@@ -109,10 +109,11 @@
     - name: Generate Coverage
       run: |
         ./script/test generate_coverage "${{ matrix.compiler.gcov }}"
-    - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+    - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
       with:
         name: cov-thread-1-3-${{ matrix.compiler.c }}-${{ matrix.arch }}
         path: tmp/coverage.info
+        retention-days: 1
 
   packet-verification-low-power:
     runs-on: ubuntu-20.04
@@ -127,11 +128,11 @@
       INTER_OP_BBR: 0
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
       with:
         egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
       with:
         submodules: true
     - name: Bootstrap
@@ -165,14 +166,14 @@
           CRASHED=$(./script/test check_crash | tail -1)
           [[ $CRASHED -eq "1" ]] && echo "Crashed!" || echo "Not crashed."
           echo "CRASHED=$CRASHED" >> $GITHUB_ENV
-    - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+    - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
       if: ${{ failure() }}
       with:
         name: packet-verification-low-power-pcaps
         path: |
           *.pcap
           *.json
-    - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+    - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
       if: ${{ failure() && env.CRASHED == '1' }}
       with:
         name: core-packet-verification-low-power
@@ -181,10 +182,11 @@
     - name: Generate Coverage
       run: |
         ./script/test generate_coverage gcc
-    - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+    - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
       with:
         name: cov-packet-verification-low-power
         path: tmp/coverage.info
+        retention-days: 1
 
   packet-verification-1-1-on-1-3:
     runs-on: ubuntu-20.04
@@ -197,11 +199,11 @@
       MULTIPLY: 3
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
       with:
         egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
       with:
         submodules: true
     - name: Bootstrap
@@ -218,7 +220,7 @@
     - name: Run
       run: |
         ./script/test cert_suite ./tests/scripts/thread-cert/Cert_*.py ./tests/scripts/thread-cert/test_*.py
-    - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+    - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
       if: ${{ failure() }}
       with:
         name: packet-verification-1.1-on-1.3-pcaps
@@ -228,10 +230,61 @@
     - name: Generate Coverage
       run: |
         ./script/test generate_coverage gcc
-    - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+    - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
       with:
         name: cov-packet-verification-1-1-on-1-3
         path: tmp/coverage.info
+        retention-days: 1
+
+  channel-manager-csl:
+    runs-on: ubuntu-20.04
+    env:
+      CFLAGS: -m32
+      CXXFLAGS: -m32
+      LDFLAGS: -m32
+      COVERAGE: 1
+      THREAD_VERSION: 1.3
+      VIRTUAL_TIME: 1
+      INTER_OP: 1
+      INTER_OP_BBR: 1
+      ADDON_FEAT_1_2: 1
+    steps:
+    - name: Harden Runner
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
+      with:
+        egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
+
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
+      with:
+        submodules: true
+    - name: Bootstrap
+      run: |
+        sudo rm /etc/apt/sources.list.d/* && sudo apt-get update
+        sudo apt-get --no-install-recommends install -y g++-multilib lcov ninja-build python3-setuptools python3-wheel
+        python3 -m pip install -r tests/scripts/thread-cert/requirements.txt
+    - name: Build
+      run: |
+        OT_OPTIONS="-DOT_CHANNEL_MANAGER_CSL=ON" ./script/test build
+    - name: Run
+      run: |
+        ulimit -c unlimited
+        ./script/test cert_suite ./tests/scripts/thread-cert/Cert_*.py
+        ./script/test cert_suite ./tests/scripts/thread-cert/test_*.py
+        ./script/test cert_suite ./tests/scripts/thread-cert/v1_2_*.py
+        ./script/test cert_suite ./tests/scripts/thread-cert/addon_test_channel_manager_autocsl*.py
+    - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+      if: ${{ failure() }}
+      with:
+        name: channel-manager-csl
+        path: ot_testing
+    - name: Generate Coverage
+      run: |
+        ./script/test generate_coverage gcc
+    - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+      with:
+        name: cov-channel-manager-csl
+        path: tmp/coverage.info
+        retention-days: 1
 
   expects:
     runs-on: ubuntu-20.04
@@ -241,16 +294,17 @@
       VIRTUAL_TIME: 0
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
       with:
         egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
       with:
         submodules: true
     - name: Bootstrap
       run: |
         sudo apt-get --no-install-recommends install -y expect ninja-build lcov socat
+        pip install bleak
     - name: Run RCP Mode
       run: |
         ulimit -c unlimited
@@ -262,7 +316,7 @@
           CRASHED=$(./script/test check_crash | tail -1)
           [[ $CRASHED -eq "1" ]] && echo "Crashed!" || echo "Not crashed."
           echo "CRASHED=$CRASHED" >> $GITHUB_ENV
-    - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+    - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
       if: ${{ failure() && env.CRASHED == '1' }}
       with:
         name: core-expect-1-3
@@ -271,10 +325,11 @@
     - name: Generate Coverage
       run: |
         ./script/test generate_coverage gcc
-    - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+    - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
       with:
         name: cov-expects
         path: tmp/coverage.info
+        retention-days: 1
 
   thread-1-3-posix:
     runs-on: ubuntu-20.04
@@ -289,11 +344,11 @@
       INTER_OP: 1
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
       with:
         egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
       with:
         submodules: true
     - name: Bootstrap
@@ -320,12 +375,12 @@
           CRASHED=$(./script/test check_crash | tail -1)
           [[ $CRASHED -eq "1" ]] && echo "Crashed!" || echo "Not crashed."
           echo "CRASHED=$CRASHED" >> $GITHUB_ENV
-    - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+    - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
       if: ${{ failure() }}
       with:
         name: thread-1-3-posix-pcaps
         path: "*.pcap"
-    - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+    - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
       if: ${{ failure() && env.CRASHED == '1' }}
       with:
         name: core-thread-1-3-posix
@@ -334,10 +389,11 @@
     - name: Generate Coverage
       run: |
         ./script/test generate_coverage gcc
-    - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+    - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
       with:
         name: cov-thread-1-3-posix
         path: tmp/coverage.info
+        retention-days: 1
 
   upload-coverage:
     needs:
@@ -349,38 +405,28 @@
     runs-on: ubuntu-20.04
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
       with:
         egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
       with:
         submodules: true
     - name: Bootstrap
       run: |
         sudo apt-get --no-install-recommends install -y lcov
-    - uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
+    - uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
       with:
         path: coverage/
+        pattern: cov-*
+        merge-multiple: true
     - name: Combine Coverage
       run: |
         script/test combine_coverage
     - name: Upload Coverage
-      uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # v3.1.4
+      uses: codecov/codecov-action@0cfda1dd0a4ad9efc75517f399d859cd1ea4ced1 # v4.0.2
+      env:
+        CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
       with:
         files: final.info
         fail_ci_if_error: true
-
-  delete-coverage-artifacts:
-    needs: upload-coverage
-    runs-on: ubuntu-20.04
-    steps:
-    - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
-      with:
-        egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
-
-    - uses: geekyeggo/delete-artifact@54ab544f12cdb7b71613a16a2b5a37a9ade990af # v2.0.0
-      with:
-        name: cov-*
-        useGlob: true
diff --git a/.github/workflows/size.yml b/.github/workflows/size.yml
index f0a364d..98de9d7 100644
--- a/.github/workflows/size.yml
+++ b/.github/workflows/size.yml
@@ -49,11 +49,11 @@
     runs-on: ubuntu-20.04
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
       with:
         egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
     - name: Run
       env:
         OT_BASE_BRANCH: "${{ github.base_ref }}"
diff --git a/.github/workflows/toranj.yml b/.github/workflows/toranj.yml
index 16e9e92..6b79b13 100644
--- a/.github/workflows/toranj.yml
+++ b/.github/workflows/toranj.yml
@@ -59,11 +59,11 @@
       TORANJ_EVENT_NAME: ${{ github.event_name }}
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
       with:
         egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
       with:
         submodules: true
     - name: Bootstrap
@@ -90,11 +90,11 @@
       TORANJ_CLI: 1
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
       with:
         egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
       with:
         submodules: true
     - name: Bootstrap
@@ -111,22 +111,23 @@
       if: "matrix.TORANJ_RADIO != 'multi'"
       run: |
         ./script/test generate_coverage gcc
-    - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+    - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
       if: "matrix.TORANJ_RADIO != 'multi'"
       with:
         name: cov-toranj-cli-${{ matrix.TORANJ_RADIO }}
         path: tmp/coverage.info
+        retention-days: 1
 
   toranj-unittest:
     name: toranj-unittest
     runs-on: ubuntu-20.04
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
       with:
         egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
       with:
         submodules: true
     - name: Bootstrap
@@ -158,44 +159,56 @@
         git clean -dfx
         ./tests/toranj/build.sh --enable-plat-key-ref all
 
+  toranj-macos:
+    name: toranj-macos
+    runs-on: macos-14
+    steps:
+    - name: Harden Runner
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
+      with:
+        egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
+
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
+      with:
+        submodules: true
+    - name: Bootstrap
+      env:
+        GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
+      run: |
+        brew update
+        brew install ninja
+    - name: Build & Run
+      run: |
+        ./tests/toranj/build.sh posix-15.4
+
   upload-coverage:
     needs:
     - toranj-cli
     runs-on: ubuntu-20.04
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
       with:
         egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
       with:
         submodules: true
     - name: Bootstrap
       run: |
         sudo apt-get --no-install-recommends install -y lcov
-    - uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
+    - uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
       with:
         path: coverage/
+        pattern: cov-*
+        merge-multiple: true
     - name: Combine Coverage
       run: |
         script/test combine_coverage
     - name: Upload Coverage
-      uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # v3.1.4
+      uses: codecov/codecov-action@0cfda1dd0a4ad9efc75517f399d859cd1ea4ced1 # v4.0.2
+      env:
+        CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
       with:
         files: final.info
         fail_ci_if_error: true
-
-  delete-coverage-artifacts:
-    needs: upload-coverage
-    runs-on: ubuntu-20.04
-    steps:
-    - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
-      with:
-        egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
-
-    - uses: geekyeggo/delete-artifact@54ab544f12cdb7b71613a16a2b5a37a9ade990af # v2.0.0
-      with:
-        name: cov-*
-        useGlob: true
diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml
index 1df2df5..bf685ca 100644
--- a/.github/workflows/unit.yml
+++ b/.github/workflows/unit.yml
@@ -49,11 +49,11 @@
     runs-on: ubuntu-20.04
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
       with:
         egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
       with:
         submodules: true
     - name: Build
@@ -67,11 +67,11 @@
       COVERAGE: 1
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
       with:
         egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
       with:
         submodules: true
     - name: Bootstrap
@@ -93,49 +93,39 @@
     - name: Generate Coverage
       run: |
         ./script/test generate_coverage gcc
-    - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+    - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
       with:
         name: cov-unit-tests
         path: tmp/coverage.info
-
+        retention-days: 1
 
   upload-coverage:
     needs: unit-tests
     runs-on: ubuntu-20.04
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
       with:
         egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
       with:
         submodules: true
     - name: Bootstrap
       run: |
         sudo apt-get --no-install-recommends install -y lcov
-    - uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
+    - uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
       with:
         path: coverage/
+        pattern: cov-*
+        merge-multiple: true
     - name: Combine Coverage
       run: |
         script/test combine_coverage
     - name: Upload Coverage
-      uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # v3.1.4
+      uses: codecov/codecov-action@0cfda1dd0a4ad9efc75517f399d859cd1ea4ced1 # v4.0.2
+      env:
+        CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
       with:
         files: final.info
         fail_ci_if_error: true
-
-  delete-coverage-artifacts:
-    needs: upload-coverage
-    runs-on: ubuntu-20.04
-    steps:
-    - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
-      with:
-        egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
-
-    - uses: geekyeggo/delete-artifact@54ab544f12cdb7b71613a16a2b5a37a9ade990af # v2.0.0
-      with:
-        name: cov-*
-        useGlob: true
diff --git a/.github/workflows/version.yml b/.github/workflows/version.yml
index 41a4e8d..16dc637 100644
--- a/.github/workflows/version.yml
+++ b/.github/workflows/version.yml
@@ -45,11 +45,11 @@
     runs-on: ubuntu-20.04
     steps:
     - name: Harden Runner
-      uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
+      uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
       with:
         egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
 
-    - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+    - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
       with:
         submodules: true
     - name: Check
diff --git a/Android.bp b/Android.bp
index 30edee7..2c3a4dd 100644
--- a/Android.bp
+++ b/Android.bp
@@ -27,6 +27,7 @@
 //
 
 package {
+    default_team: "trendy_team_fwk_thread_network",
     default_applicable_licenses: ["external_openthread_license"],
 }
 
@@ -69,7 +70,7 @@
 }
 
 cc_defaults {
-    name : "ot_posix_cflags_defaults",
+    name: "ot_posix_cflags_defaults",
     cflags: [
         "-DMBEDTLS_CONFIG_FILE=\"mbedtls-config.h\"",
         "-DOPENTHREAD_CONFIG_FILE=\"src/android/openthread-android-config.h\"",
@@ -85,7 +86,6 @@
         "-DPACKAGE_TARNAME=\"openthread\"",
         "-DPACKAGE_URL=\"http://github.com/openthread/openthread\"",
         "-DSPINEL_PLATFORM_HEADER=\"spinel_platform.h\"",
-        "-DOPENTHREAD_POSIX_CONFIG_SPINEL_HDLC_INTERFACE_ENABLE=0",
         "-DOPENTHREAD_POSIX_CONFIG_SPINEL_SPI_INTERFACE_ENABLE=0",
         "-DOPENTHREAD_POSIX_CONFIG_SPINEL_VENDOR_INTERFACE_ENABLE=1",
     ],
@@ -101,23 +101,6 @@
     ],
 }
 
-soong_config_module_type {
-    name: "ot_config_cc_defaults",
-    module_type: "cc_defaults",
-    config_namespace: "openthread",
-    bool_variables: ["diag"],
-    properties: ["cflags"],
-}
-
-ot_config_cc_defaults {
-    name: "ot_config_defaults",
-    soong_config_variables: {
-        diag: {
-            cflags: ["-DOPENTHREAD_CONFIG_DIAG_ENABLE=1"],
-        },
-    },
-}
-
 filegroup {
     name: "openthread_core_srcs",
     srcs: [
@@ -238,7 +221,6 @@
         "src/core/meshcop/dataset_manager.cpp",
         "src/core/meshcop/dataset_manager_ftd.cpp",
         "src/core/meshcop/dataset_updater.cpp",
-        "src/core/meshcop/dtls.cpp",
         "src/core/meshcop/energy_scan_client.cpp",
         "src/core/meshcop/extended_panid.cpp",
         "src/core/meshcop/joiner.cpp",
@@ -248,6 +230,7 @@
         "src/core/meshcop/meshcop_tlvs.cpp",
         "src/core/meshcop/network_name.cpp",
         "src/core/meshcop/panid_query_client.cpp",
+        "src/core/meshcop/secure_transport.cpp",
         "src/core/meshcop/timestamp.cpp",
         "src/core/net/checksum.cpp",
         "src/core/net/dhcp6_client.cpp",
@@ -357,13 +340,15 @@
         "src/cli/cli_dns.cpp",
         "src/cli/cli_history.cpp",
         "src/cli/cli_joiner.cpp",
+        "src/cli/cli_link_metrics.cpp",
         "src/cli/cli_mac_filter.cpp",
         "src/cli/cli_network_data.cpp",
-        "src/cli/cli_output.cpp",
+        "src/cli/cli_ping.cpp",
         "src/cli/cli_srp_client.cpp",
         "src/cli/cli_srp_server.cpp",
         "src/cli/cli_tcp.cpp",
         "src/cli/cli_udp.cpp",
+        "src/cli/cli_utils.cpp",
     ],
 }
 
@@ -391,6 +376,7 @@
         "examples/platforms/simulation/logging.c",
         "examples/platforms/simulation/misc.c",
         "examples/platforms/simulation/radio.c",
+        "examples/platforms/simulation/simul_utils.c",
         "examples/platforms/simulation/spi-stubs.c",
         "examples/platforms/simulation/system.c",
         "examples/platforms/simulation/trel.c",
@@ -482,10 +468,9 @@
 
 cc_library_static {
     name: "ot-core",
-    apex_available: [ "com.android.tethering" ],
+    apex_available: ["com.android.tethering"],
     min_sdk_version: "30",
     defaults: [
-        "ot_config_defaults",
         "ot-daemon-debuggable-cc-defaults",
         "ot_posix_cflags_defaults",
     ],
@@ -538,6 +523,7 @@
         "src/android/thread_network_hal/vendor_interface.cpp",
         "src/lib/hdlc/hdlc.cpp",
         "src/lib/platform/exit_code.c",
+        "src/lib/spinel/logger.cpp",
         "src/lib/spinel/radio_spinel.cpp",
         "src/lib/spinel/spinel.c",
         "src/lib/spinel/spinel_buffer.cpp",
@@ -549,10 +535,9 @@
 
 cc_library_static {
     name: "libopenthread-cli",
-    apex_available: [ "com.android.tethering" ],
+    apex_available: ["com.android.tethering"],
     min_sdk_version: "30",
     defaults: [
-        "ot_config_defaults",
         "ot-daemon-debuggable-cc-defaults",
         "ot_posix_cflags_defaults",
     ],
@@ -591,19 +576,20 @@
         "src/cli/cli_dns.cpp",
         "src/cli/cli_history.cpp",
         "src/cli/cli_joiner.cpp",
+        "src/cli/cli_link_metrics.cpp",
         "src/cli/cli_mac_filter.cpp",
         "src/cli/cli_network_data.cpp",
-        "src/cli/cli_output.cpp",
+        "src/cli/cli_ping.cpp",
         "src/cli/cli_srp_client.cpp",
         "src/cli/cli_srp_server.cpp",
         "src/cli/cli_tcp.cpp",
         "src/cli/cli_udp.cpp",
+        "src/cli/cli_utils.cpp",
     ],
 }
 
 cc_binary {
     name: "ot-ctl",
-    vendor: true,
     defaults: ["ot_posix_cflags_defaults"],
     generated_headers: ["ot_version_header"],
 
@@ -630,7 +616,7 @@
 }
 
 cc_defaults {
-    name : "ot_simulation_cflags_defaults",
+    name: "ot_simulation_cflags_defaults",
 
     local_include_dirs: [
         "examples/platforms/simulation",
@@ -654,6 +640,7 @@
     cflags: [
         "-DMBEDTLS_CONFIG_FILE=\"mbedtls-config.h\"",
         "-DOPENTHREAD_CONFIG_ANDROID_VERSION_HEADER_ENABLE=1",
+        "-DOPENTHREAD_CONFIG_DIAG_ENABLE=1",
         "-DOPENTHREAD_CONFIG_DNS_CLIENT_ENABLE=1",
         "-DOPENTHREAD_CONFIG_ECDSA_ENABLE=1",
         "-DOPENTHREAD_CONFIG_FILE=\"openthread-config-android-version.h\"",
@@ -848,7 +835,6 @@
     ],
 }
 
-
 cc_library_static {
     name: "openthread-spinel-rcp",
     vendor_available: true,
diff --git a/README.md b/README.md
index 0c4a4f5..ba4fb10 100644
--- a/README.md
+++ b/README.md
@@ -27,7 +27,7 @@
 
 # Who supports OpenThread?
 
-<a href="https://www.amazon.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-amazon.png" alt="Amazon" width="200px"></a><a href="https://www.arm.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-arm.png" alt="ARM" width="200px"></a><a href="https://www.cascoda.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-cascoda.png" alt="Cascoda" width="200px"></a><a href="https://www.eero.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-eero.png" alt="Eero" width="200px"></a><a href="https://www.espressif.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-espressif-github.png" alt="Espressif" width="200px"></a><a href="https://www.google.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-google.png" alt="Google" width="200px"></a><a href="https://www.infineon.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-infineon.png" alt="Infineon" width="200px"></a><a href="https://mmbnetworks.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-mmb-networks.png" alt="MMB Networks" width="200px"></a><a href="https://www.nabucasa.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-nabu-casa.png" alt="Nabu Casa" width="200px"></a><a href="https://www.nanoleaf.me/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-nanoleaf.png" alt="Nanoleaf" width="200px"></a><a href="http://www.nordicsemi.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-nordic.png" alt="Nordic" width="200px"></a><a href="http://www.nxp.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-nxp.png" alt="NXP" width="200px"></a><a href="http://www.qorvo.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-qorvo.png" alt="Qorvo" width="200px"></a><a href="https://www.qualcomm.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-qc.png" alt="Qualcomm" width="200px"></a><a href="https://www.samsung.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-samsung.png" alt="Samsung" width="200px"></a><a href="https://www.silabs.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-silabs.png" alt="Silicon Labs" width="200px"></a><a href="https://www.st.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-stm.png" alt="STMicroelectronics" width="200px"></a><a href="https://www.synopsys.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-synopsys.png" alt="Synopsys" width="200px"></a><a href="https://www.telink-semi.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-telink-github.png" alt="Telink Semiconductor" width="200px"></a><a href="https://www.ti.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-ti.png" alt="Texas Instruments" width="200px"></a><a href="https://www.zephyrproject.org/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-zephyr.png" alt="Zephyr Project" width="200px"></a>
+<a href="https://www.amazon.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-amazon.png" alt="Amazon" width="200px"></a><a href="https://www.aqara.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-aqara.png" alt="Aqara" width="200px"></a><a href="https://www.arm.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-arm.png" alt="ARM" width="200px"></a><a href="https://www.cascoda.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-cascoda.png" alt="Cascoda" width="200px"></a><a href="https://www.eero.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-eero.png" alt="Eero" width="200px"></a><a href="https://www.espressif.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-espressif-github.png" alt="Espressif" width="200px"></a><a href="https://www.google.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-google.png" alt="Google" width="200px"></a><a href="https://www.infineon.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-infineon.png" alt="Infineon" width="200px"></a><a href="https://mmbnetworks.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-mmb-networks.png" alt="MMB Networks" width="200px"></a><a href="https://www.nabucasa.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-nabu-casa.png" alt="Nabu Casa" width="200px"></a><a href="https://www.nanoleaf.me/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-nanoleaf.png" alt="Nanoleaf" width="200px"></a><a href="http://www.nordicsemi.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-nordic.png" alt="Nordic" width="200px"></a><a href="http://www.nxp.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-nxp.png" alt="NXP" width="200px"></a><a href="http://www.qorvo.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-qorvo.png" alt="Qorvo" width="200px"></a><a href="https://www.qualcomm.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-qc.png" alt="Qualcomm" width="200px"></a><a href="https://www.samsung.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-samsung.png" alt="Samsung" width="200px"></a><a href="https://www.silabs.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-silabs.png" alt="Silicon Labs" width="200px"></a><a href="https://www.st.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-stm.png" alt="STMicroelectronics" width="200px"></a><a href="https://www.synopsys.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-synopsys.png" alt="Synopsys" width="200px"></a><a href="https://www.telink-semi.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-telink-github.png" alt="Telink Semiconductor" width="200px"></a><a href="https://www.ti.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-ti.png" alt="Texas Instruments" width="200px"></a><a href="https://www.zephyrproject.org/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-zephyr.png" alt="Zephyr Project" width="200px"></a>
 
 # Getting started
 
diff --git a/doc/images/ot-contrib-aqara.png b/doc/images/ot-contrib-aqara.png
new file mode 100644
index 0000000..7fed684
--- /dev/null
+++ b/doc/images/ot-contrib-aqara.png
Binary files differ
diff --git a/doc/ot_api_doc.h b/doc/ot_api_doc.h
index 84e48c8..ac6ec5a 100644
--- a/doc/ot_api_doc.h
+++ b/doc/ot_api_doc.h
@@ -53,6 +53,7 @@
  * @defgroup api-net                  IPv6 Networking
  * @{
  *
+ * @defgroup api-ble-secure           BLE Secure
  * @defgroup api-dns                  DNS
  * @defgroup api-dnssd-server         DNS-SD Server
  * @defgroup api-icmp6                ICMPv6
@@ -168,10 +169,13 @@
  * @{
  *
  * @defgroup plat-alarm               Alarm
+ * @defgroup plat-ble                 BLE
  * @defgroup plat-crypto              Crypto - Platform
  * @defgroup plat-dns                 DNS - Platform
+ * @defgroup plat-dns-sd              DNS-SD (mDNS)
  * @defgroup plat-entropy             Entropy
  * @defgroup plat-factory-diagnostics Factory Diagnostics - Platform
+ * @defgroup plat-infra-if            Infrastructure Interface
  * @defgroup plat-logging             Logging - Platform
  * @defgroup plat-memory              Memory
  * @defgroup plat-messagepool         Message Pool
@@ -184,7 +188,6 @@
  * @defgroup plat-time                Time Service
  * @defgroup plat-toolchain           Toolchain
  * @defgroup plat-trel                TREL - Platform
- * @defgroup plat-infra-if            Infrastructure Interface
  *
  * @}
  *
diff --git a/doc/ot_config_doc.h b/doc/ot_config_doc.h
new file mode 100644
index 0000000..87af229
--- /dev/null
+++ b/doc/ot_config_doc.h
@@ -0,0 +1,90 @@
+/*
+ *  Copyright (c) 2023, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * @brief
+ *  This file defines the Doxygen group structure for OpenThread documentation.
+ */
+
+/**
+ * @defgroup config                   Config Variables
+ * @brief
+ *   This module lists all OpenThread configuration variables.
+ *
+ * @{
+ *
+ * @defgroup config-announce-sender          Announce Sender
+ * @defgroup config-backbone-router          Backbone Router Services
+ * @defgroup config-border-agent             Border Agent
+ * @defgroup config-border-router            Border Router
+ * @defgroup config-border-routing           Border Routing Manager
+ * @defgroup config-channel-manager          Channel Manager
+ * @defgroup config-channel-monitor          Channel Monitor
+ * @defgroup config-child-supervision        Child Supervision
+ * @defgroup config-coap                     CoAP
+ * @defgroup config-commissioner             Commissioner
+ * @defgroup config-crypto                   Crypto Backend Library
+ * @defgroup config-dataset-updater          Dataset Updater
+ * @defgroup config-dhcpv6-client            DHCPv6 Client
+ * @defgroup config-dhcpv6-server            DHCPv6 Server
+ * @defgroup config-diag                     DIAG Service
+ * @defgroup config-dns-client               DNS Client
+ * @defgroup config-dns-dso                  DNS Stateful Operations
+ * @defgroup config-dnssd-server             DNS-SD Server
+ * @defgroup config-history-tracker          History Tracker
+ * @defgroup config-ip6                      IP6 Service
+ * @defgroup config-joiner                   Joiner
+ * @defgroup config-link-metrics-manager     Link Metrics Manager
+ * @defgroup config-link-quality             Link Quality
+ * @defgroup config-link-raw                 Link Raw Service
+ * @defgroup config-logging                  Logging Service
+ * @defgroup config-mac                      MAC
+ * @defgroup config-mesh-diag                Mesh Diagnostic
+ * @defgroup config-mesh-forwarder           Mesh Forwarder
+ * @defgroup config-misc                     Miscellaneous Constants
+ * @defgroup config-mle                      MLE Service
+ * @defgroup config-nat64                    NAT64
+ * @defgroup config-netdata-publisher        Network Data Publisher
+ * @defgroup config-network-diagnostic       Network Diagnostics
+ * @defgroup config-parent-search            Parent Search
+ * @defgroup config-ping-sender              Ping Sender
+ * @defgroup config-platform                 Platform Specific Services
+ * @defgroup config-power-calibration        Power Calibration
+ * @defgroup config-radio-link               Radio Links
+ * @defgroup config-secure-transport         Secure Transport
+ * @defgroup config-sntp-client              SNTP Client
+ * @defgroup config-srp-client               SRP Client
+ * @defgroup config-srp-server               SRP Server
+ * @defgroup config-time-sync                Time Sync Service
+ * @defgroup config-tmf                      Thread Management Framework Service
+ * @defgroup config-trel                     TREL
+ *
+ * @}
+ *
+ */
diff --git a/etc/cmake/options.cmake b/etc/cmake/options.cmake
index 3b5a155..da77ef0 100644
--- a/etc/cmake/options.cmake
+++ b/etc/cmake/options.cmake
@@ -172,6 +172,7 @@
 ot_option(OT_BACKBONE_ROUTER OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE "backbone router functionality")
 ot_option(OT_BACKBONE_ROUTER_DUA_NDPROXYING OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE "BBR DUA ND Proxy")
 ot_option(OT_BACKBONE_ROUTER_MULTICAST_ROUTING OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE "BBR MR")
+ot_option(OT_BLE_TCAT OPENTHREAD_CONFIG_BLE_TCAT_ENABLE "Ble based thread commissioning")
 ot_option(OT_BORDER_AGENT OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE "border agent")
 ot_option(OT_BORDER_AGENT_ID OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE "create and save border agent ID")
 ot_option(OT_BORDER_ROUTER OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE "border router")
@@ -179,6 +180,7 @@
 ot_option(OT_BORDER_ROUTING_DHCP6_PD OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE "dhcpv6 pd support in border routing")
 ot_option(OT_BORDER_ROUTING_COUNTERS OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE "border routing counters")
 ot_option(OT_CHANNEL_MANAGER OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE "channel manager")
+ot_option(OT_CHANNEL_MANAGER_CSL OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE "channel manager for csl channel")
 ot_option(OT_CHANNEL_MONITOR OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE "channel monitor")
 ot_option(OT_COAP OPENTHREAD_CONFIG_COAP_API_ENABLE "coap api")
 ot_option(OT_COAP_BLOCK OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE "coap block-wise transfer (RFC7959)")
@@ -217,6 +219,7 @@
 ot_option(OT_MESSAGE_USE_HEAP OPENTHREAD_CONFIG_MESSAGE_USE_HEAP_ENABLE "heap allocator for message buffers")
 ot_option(OT_MLE_LONG_ROUTES OPENTHREAD_CONFIG_MLE_LONG_ROUTES_ENABLE "MLE long routes extension (experimental)")
 ot_option(OT_MLR OPENTHREAD_CONFIG_MLR_ENABLE "Multicast Listener Registration (MLR)")
+ot_option(OT_MULTIPAN_RCP OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE "enable multi-PAN RCP")
 ot_option(OT_MULTIPLE_INSTANCE OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE "multiple instances")
 ot_option(OT_NAT64_BORDER_ROUTING OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE "border routing NAT64")
 ot_option(OT_NAT64_TRANSLATOR OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE "NAT64 translator support")
@@ -229,12 +232,14 @@
 ot_option(OT_PLATFORM_BOOTLOADER_MODE OPENTHREAD_CONFIG_PLATFORM_BOOTLOADER_MODE_ENABLE "platform bootloader mode")
 ot_option(OT_PLATFORM_KEY_REF OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE "platform key reference secure storage")
 ot_option(OT_PLATFORM_NETIF OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE "platform netif")
+ot_option(OT_PLATFORM_POWER_CALIBRATION OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE "power calibration")
 ot_option(OT_PLATFORM_UDP OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE "platform UDP")
 ot_option(OT_REFERENCE_DEVICE OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE "test harness reference device")
 ot_option(OT_SERVICE OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE "Network Data service")
 ot_option(OT_SETTINGS_RAM OPENTHREAD_SETTINGS_RAM "volatile-only storage of settings")
 ot_option(OT_SLAAC OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE "SLAAC address")
 ot_option(OT_SNTP_CLIENT OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE "SNTP client")
+ot_option(OT_SRP_ADV_PROXY OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE "SRP advertising proxy")
 ot_option(OT_SRP_CLIENT OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE "SRP client")
 ot_option(OT_SRP_SERVER OPENTHREAD_CONFIG_SRP_SERVER_ENABLE "SRP server")
 ot_option(OT_TCP OPENTHREAD_CONFIG_TCP_ENABLE "TCP")
@@ -246,7 +251,6 @@
 ot_option(OT_UPTIME OPENTHREAD_CONFIG_UPTIME_ENABLE "uptime")
 
 option(OT_DOC "build OpenThread documentation")
-option(OT_MULTIPAN_RCP "Multi-PAN RCP" OFF)
 message(STATUS "- - - - - - - - - - - - - - - - ")
 
 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -275,7 +279,7 @@
 endif()
 
 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-set(OT_THREAD_VERSION_VALUES "1.1" "1.2" "1.3" "1.3.1")
+set(OT_THREAD_VERSION_VALUES "1.1" "1.2" "1.3" "1.3.1" "1.4")
 set(OT_THREAD_VERSION "1.3" CACHE STRING "set Thread version")
 set_property(CACHE OT_THREAD_VERSION PROPERTY STRINGS "${OT_THREAD_VERSION_VALUES}")
 list(FIND OT_THREAD_VERSION_VALUES "${OT_THREAD_VERSION}" ot_index)
@@ -283,7 +287,7 @@
     message(STATUS "OT_THREAD_VERSION=\"${OT_THREAD_VERSION}\"")
     message(FATAL_ERROR "Invalid value for OT_THREAD_VERSION - valid values are: " "${OT_THREAD_VERSION_VALUES}")
 endif()
-set(OT_VERSION_SUFFIX_LIST "1_1" "1_2" "1_3" "1_3_1")
+set(OT_VERSION_SUFFIX_LIST "1_1" "1_2" "1_3" "1_3_1" "1_4")
 list(GET OT_VERSION_SUFFIX_LIST ${ot_index} OT_VERSION_SUFFIX)
 target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_THREAD_VERSION=OT_THREAD_VERSION_${OT_VERSION_SUFFIX}")
 message(STATUS "OT_THREAD_VERSION=\"${OT_THREAD_VERSION}\" -> OPENTHREAD_CONFIG_THREAD_VERSION=OT_THREAD_VERSION_${OT_VERSION_SUFFIX}")
@@ -321,6 +325,7 @@
 
 ot_int_option(OT_MLE_MAX_CHILDREN OPENTHREAD_CONFIG_MLE_MAX_CHILDREN "set maximum number of children")
 ot_int_option(OT_RCP_RESTORATION_MAX_COUNT OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT "set max RCP restoration count")
+ot_int_option(OT_RCP_TX_WAIT_TIME_SECS OPENTHREAD_SPINEL_CONFIG_RCP_TX_WAIT_TIME_SECS "set RCP TX wait TIME in seconds")
 
 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
diff --git a/etc/gn/openthread.gni b/etc/gn/openthread.gni
index 9bbb543..277ee83 100644
--- a/etc/gn/openthread.gni
+++ b/etc/gn/openthread.gni
@@ -81,6 +81,9 @@
     # Enable backbone router functionality
     openthread_config_backbone_router_enable = false
 
+    # Enable BLE based commissioning functionality
+    openthread_config_ble_tcat_enable = false
+
     # Enable border agent support
     openthread_config_border_agent_enable = false
 
diff --git a/examples/config/ot-core-config-check-size-br.h b/examples/config/ot-core-config-check-size-br.h
index 8310019..e01bc9c 100644
--- a/examples/config/ot-core-config-check-size-br.h
+++ b/examples/config/ot-core-config-check-size-br.h
@@ -40,6 +40,7 @@
 #define OPENTHREAD_CONFIG_ASSERT_ENABLE 1
 #define OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE 1
 #define OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE 1
+#define OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE 1
 #define OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE 1
 #define OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE 1
 #define OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE 1
diff --git a/examples/config/ot-core-config-check-size-ftd.h b/examples/config/ot-core-config-check-size-ftd.h
index 63e7ad4..bdd69d0 100644
--- a/examples/config/ot-core-config-check-size-ftd.h
+++ b/examples/config/ot-core-config-check-size-ftd.h
@@ -41,6 +41,7 @@
 #define OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE 0
 #define OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE 1
 #define OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE 1
+#define OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE 1
 #define OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE 0
 #define OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE 0
 #define OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE 0
diff --git a/examples/config/ot-core-config-check-size-mtd.h b/examples/config/ot-core-config-check-size-mtd.h
index 88b2c11..d5ca74c 100644
--- a/examples/config/ot-core-config-check-size-mtd.h
+++ b/examples/config/ot-core-config-check-size-mtd.h
@@ -41,6 +41,7 @@
 #define OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE 0
 #define OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE 0
 #define OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE 0
+#define OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE 0
 #define OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE 0
 #define OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE 0
 #define OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE 0
diff --git a/examples/platforms/simulation/CMakeLists.txt b/examples/platforms/simulation/CMakeLists.txt
index 439fcb9..12580e2 100644
--- a/examples/platforms/simulation/CMakeLists.txt
+++ b/examples/platforms/simulation/CMakeLists.txt
@@ -59,9 +59,11 @@
 
 add_library(openthread-simulation
     alarm.c
+    ble.c
     crypto.c
     diag.c
     dns.c
+    dnssd.c
     dso_transport.c
     entropy.c
     flash.c
@@ -70,6 +72,7 @@
     misc.c
     multipan.c
     radio.c
+    simul_utils.c
     spi-stubs.c
     system.c
     trel.c
diff --git a/examples/platforms/simulation/ble.c b/examples/platforms/simulation/ble.c
new file mode 100644
index 0000000..d48922a
--- /dev/null
+++ b/examples/platforms/simulation/ble.c
@@ -0,0 +1,216 @@
+/*
+ *  Copyright (c) 2023, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "platform-simulation.h"
+
+#include <errno.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <openthread/platform/ble.h>
+
+#include "openthread/error.h"
+#include "utils/code_utils.h"
+
+#define PLAT_BLE_MSG_DATA_MAX 2048
+static uint8_t sBleBuffer[PLAT_BLE_MSG_DATA_MAX];
+
+static int sFd = -1;
+
+static const uint16_t kPortBase = 10000;
+static uint16_t       sPort     = 0;
+struct sockaddr_in    sSockaddr;
+
+static void initFds(void)
+{
+    int                fd;
+    int                one = 1;
+    struct sockaddr_in sockaddr;
+
+    memset(&sockaddr, 0, sizeof(sockaddr));
+
+    sPort                    = (uint16_t)(kPortBase + gNodeId);
+    sockaddr.sin_family      = AF_INET;
+    sockaddr.sin_port        = htons(sPort);
+    sockaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
+
+    otEXPECT_ACTION((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) != -1, perror("socket(sFd)"));
+
+    otEXPECT_ACTION(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) != -1,
+                    perror("setsockopt(sFd, SO_REUSEADDR)"));
+    otEXPECT_ACTION(setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) != -1,
+                    perror("setsockopt(sFd, SO_REUSEPORT)"));
+
+    otEXPECT_ACTION(bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) != -1, perror("bind(sFd)"));
+
+    // Fd is successfully initialized.
+    sFd = fd;
+
+exit:
+    if (sFd == -1)
+    {
+        exit(EXIT_FAILURE);
+    }
+}
+
+static void deinitFds(void)
+{
+    if (sFd != -1)
+    {
+        close(sFd);
+        sFd = -1;
+    }
+}
+
+otError otPlatBleEnable(otInstance *aInstance)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+    initFds();
+    return OT_ERROR_NONE;
+}
+
+otError otPlatBleDisable(otInstance *aInstance)
+{
+    deinitFds();
+    OT_UNUSED_VARIABLE(aInstance);
+    return OT_ERROR_NONE;
+}
+
+otError otPlatBleGapAdvStart(otInstance *aInstance, uint16_t aInterval)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+    OT_UNUSED_VARIABLE(aInterval);
+    return OT_ERROR_NONE;
+}
+
+otError otPlatBleGapAdvStop(otInstance *aInstance)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+    return OT_ERROR_NONE;
+}
+
+otError otPlatBleGapDisconnect(otInstance *aInstance)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+    return OT_ERROR_NONE;
+}
+
+otError otPlatBleGattMtuGet(otInstance *aInstance, uint16_t *aMtu)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+    *aMtu = PLAT_BLE_MSG_DATA_MAX - 1;
+    return OT_ERROR_NONE;
+}
+
+otError otPlatBleGattServerIndicate(otInstance *aInstance, uint16_t aHandle, const otBleRadioPacket *aPacket)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+    OT_UNUSED_VARIABLE(aHandle);
+
+    ssize_t rval;
+    otError error = OT_ERROR_NONE;
+
+    otEXPECT_ACTION(sFd != -1, error = OT_ERROR_INVALID_STATE);
+    rval = sendto(sFd, (const char *)aPacket->mValue, aPacket->mLength, 0, (struct sockaddr *)&sSockaddr,
+                  sizeof(sSockaddr));
+    if (rval == -1)
+    {
+        perror("BLE simulation sendto failed.");
+    }
+
+exit:
+    return error;
+}
+
+void platformBleDeinit(void) { deinitFds(); }
+
+void platformBleUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, struct timeval *aTimeout, int *aMaxFd)
+{
+    OT_UNUSED_VARIABLE(aTimeout);
+    OT_UNUSED_VARIABLE(aWriteFdSet);
+
+    if (aReadFdSet != NULL && sFd != -1)
+    {
+        FD_SET(sFd, aReadFdSet);
+
+        if (aMaxFd != NULL && *aMaxFd < sFd)
+        {
+            *aMaxFd = sFd;
+        }
+    }
+}
+
+void platformBleProcess(otInstance *aInstance, const fd_set *aReadFdSet, const fd_set *aWriteFdSet)
+{
+    OT_UNUSED_VARIABLE(aWriteFdSet);
+
+    otEXPECT(sFd != -1);
+
+    if (FD_ISSET(sFd, aReadFdSet))
+    {
+        socklen_t len = sizeof(sSockaddr);
+        ssize_t   rval;
+        memset(&sSockaddr, 0, sizeof(sSockaddr));
+        rval = recvfrom(sFd, sBleBuffer, sizeof(sBleBuffer), 0, (struct sockaddr *)&sSockaddr, &len);
+        if (rval > 0)
+        {
+            otBleRadioPacket myPacket;
+            myPacket.mValue  = sBleBuffer;
+            myPacket.mLength = (uint16_t)rval;
+            myPacket.mPower  = 0;
+            otPlatBleGattServerOnWriteRequest(
+                aInstance, 0,
+                &myPacket); // TODO consider passing otPlatBleGattServerOnWriteRequest as a callback function
+        }
+        else if (rval == 0)
+        {
+            // socket is closed, which should not happen
+            assert(false);
+        }
+        else if (errno != EINTR && errno != EAGAIN)
+        {
+            perror("recvfrom BLE simulation failed");
+            exit(EXIT_FAILURE);
+        }
+    }
+exit:
+    return;
+}
+
+OT_TOOL_WEAK void otPlatBleGattServerOnWriteRequest(otInstance             *aInstance,
+                                                    uint16_t                aHandle,
+                                                    const otBleRadioPacket *aPacket)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+    OT_UNUSED_VARIABLE(aHandle);
+    OT_UNUSED_VARIABLE(aPacket);
+    assert(false);
+    /* In case of rcp there is a problem with linking to otPlatBleGattServerOnWriteRequest
+     * which is available in FTD/MTD library.
+     */
+}
diff --git a/examples/platforms/simulation/dnssd.c b/examples/platforms/simulation/dnssd.c
new file mode 100644
index 0000000..7967dd4
--- /dev/null
+++ b/examples/platforms/simulation/dnssd.c
@@ -0,0 +1,107 @@
+/*
+ *  Copyright (c) 2023, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "platform-simulation.h"
+
+#include <openthread/platform/dnssd.h>
+
+#if OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE
+otPlatDnssdState otPlatDnssdGetState(otInstance *aInstance)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+
+    return OT_PLAT_DNSSD_STOPPED;
+}
+
+void otPlatDnssdRegisterService(otInstance                 *aInstance,
+                                const otPlatDnssdService   *aService,
+                                otPlatDnssdRequestId        aRequestId,
+                                otPlatDnssdRegisterCallback aCallback)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+    OT_UNUSED_VARIABLE(aService);
+    OT_UNUSED_VARIABLE(aRequestId);
+    OT_UNUSED_VARIABLE(aCallback);
+}
+
+void otPlatDnssdUnregisterService(otInstance                 *aInstance,
+                                  const otPlatDnssdService   *aService,
+                                  otPlatDnssdRequestId        aRequestId,
+                                  otPlatDnssdRegisterCallback aCallback)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+    OT_UNUSED_VARIABLE(aService);
+    OT_UNUSED_VARIABLE(aRequestId);
+    OT_UNUSED_VARIABLE(aCallback);
+}
+
+void otPlatDnssdRegisterHost(otInstance                 *aInstance,
+                             const otPlatDnssdHost      *aHost,
+                             otPlatDnssdRequestId        aRequestId,
+                             otPlatDnssdRegisterCallback aCallback)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+    OT_UNUSED_VARIABLE(aHost);
+    OT_UNUSED_VARIABLE(aRequestId);
+    OT_UNUSED_VARIABLE(aCallback);
+}
+
+void otPlatDnssdUnregisterHost(otInstance                 *aInstance,
+                               const otPlatDnssdHost      *aHost,
+                               otPlatDnssdRequestId        aRequestId,
+                               otPlatDnssdRegisterCallback aCallback)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+    OT_UNUSED_VARIABLE(aHost);
+    OT_UNUSED_VARIABLE(aRequestId);
+    OT_UNUSED_VARIABLE(aCallback);
+}
+
+void otPlatDnssdRegisterKey(otInstance                 *aInstance,
+                            const otPlatDnssdKey       *aKey,
+                            otPlatDnssdRequestId        aRequestId,
+                            otPlatDnssdRegisterCallback aCallback)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+    OT_UNUSED_VARIABLE(aKey);
+    OT_UNUSED_VARIABLE(aRequestId);
+    OT_UNUSED_VARIABLE(aCallback);
+}
+
+void otPlatDnssdUnregisterKey(otInstance                 *aInstance,
+                              const otPlatDnssdKey       *aKey,
+                              otPlatDnssdRequestId        aRequestId,
+                              otPlatDnssdRegisterCallback aCallback)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+    OT_UNUSED_VARIABLE(aKey);
+    OT_UNUSED_VARIABLE(aRequestId);
+    OT_UNUSED_VARIABLE(aCallback);
+}
+
+#endif // OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE
diff --git a/examples/platforms/simulation/infra_if.c b/examples/platforms/simulation/infra_if.c
index 9596406..ccb5e9b 100644
--- a/examples/platforms/simulation/infra_if.c
+++ b/examples/platforms/simulation/infra_if.c
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2021, The OpenThread Authors.
+ *  Copyright (c) 2024, The OpenThread Authors.
  *  All rights reserved.
  *
  *  Redistribution and use in source and binary forms, with or without
@@ -28,15 +28,154 @@
 
 #include "platform-simulation.h"
 
+#include <openthread/icmp6.h>
+#include <openthread/ip6.h>
+#include <openthread/logging.h>
 #include <openthread/platform/infra_if.h>
 
-#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
+#include "simul_utils.h"
+#include "utils/code_utils.h"
+
+#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE && !OPENTHREAD_RADIO
+
+#define DEBUG_LOG 0
+
+#if DEBUG_LOG
+#define LOG(...) otLogNotePlat("[infra-if] "__VA_ARGS__)
+#else
+#define LOG(...) \
+    do           \
+    {            \
+    } while (0)
+#endif
+
+#define INFRA_IF_SIM_PORT 9800
+#define INFRA_IF_MAX_PACKET_SIZE 1800
+#define INFRA_IF_MAX_PENDING_TX 64
+#define INFRA_IF_NEIGHBOR_ADVERT_SIZE 24
+
+typedef struct Message
+{
+    uint32_t     mIfIndex;
+    otIp6Address mSrc;
+    otIp6Address mDst;
+    uint16_t     mDataLength;
+    uint8_t      mData[INFRA_IF_MAX_PACKET_SIZE];
+} Message;
+
+static bool         sInitialized = false;
+static otIp6Address sIp6Address;
+static otIp6Address sLinkLocalAllNodes;
+static otIp6Address sLinkLocalAllRouters;
+static utilsSocket  sSocket;
+static uint16_t     sPortOffset   = 0;
+static uint8_t      sNumPendingTx = 0;
+static Message      sPendingTx[INFRA_IF_MAX_PENDING_TX];
+
+//---------------------------------------------------------------------------------------------------------------------
+
+static bool addressesMatch(const otIp6Address *aFirstAddr, const otIp6Address *aSecondAddr)
+{
+    return memcmp(aFirstAddr, aSecondAddr, sizeof(otIp6Address)) == 0;
+}
+
+static uint16_t getMessageSize(const Message *aMessage)
+{
+    return (uint16_t)(&aMessage->mData[aMessage->mDataLength] - (const uint8_t *)aMessage);
+}
+
+static void sendPendingTxMessages(void)
+{
+    for (uint8_t i = 0; i < sNumPendingTx; i++)
+    {
+        utilsSendOverSocket(&sSocket, &sPendingTx[i], getMessageSize(&sPendingTx[i]));
+    }
+
+    sNumPendingTx = 0;
+}
+
+static void sendNeighborAdvert(const Message *aNsMessage)
+{
+    Message *message;
+    uint8_t  index;
+
+    assert(sNumPendingTx < INFRA_IF_MAX_PENDING_TX);
+
+    message = &sPendingTx[sNumPendingTx++];
+
+    message->mIfIndex = aNsMessage->mIfIndex;
+    message->mSrc     = sIp6Address;
+    message->mDst     = aNsMessage->mSrc;
+
+    // Neighbor Advertisement Message (RFC 4861)
+    //
+    //   0                   1                   2                   3
+    //   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+    //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    //  |     Type      |     Code      |          Checksum             |
+    //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    //  |R|S|O|                     Reserved                            |
+    //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    //  |                                                               |
+    //  +                                                               +
+    //  |                                                               |
+    //  +                       Target Address                          +
+    //  |                                                               |
+    //  +                                                               +
+    //  |                                                               |
+    //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+    index = 0;
+    memset(message->mData, 0, INFRA_IF_NEIGHBOR_ADVERT_SIZE);
+
+    message->mData[index++] = OT_ICMP6_TYPE_NEIGHBOR_ADVERT;           // Type.
+    index += 3;                                                        // Code is zero. Checksum (uint16) as zero.
+    message->mData[index++] = 0xd0;                                    // Flags, set R and S bits.
+    index += 3;                                                        // Skip over the reserved bytes.
+    memcpy(&message->mData[index], &sIp6Address, sizeof(sIp6Address)); // Set the target address field.
+    index += sizeof(sIp6Address);
+
+    assert(index == INFRA_IF_NEIGHBOR_ADVERT_SIZE);
+
+    message->mDataLength = INFRA_IF_NEIGHBOR_ADVERT_SIZE;
+}
+
+static void processMessage(otInstance *aInstance, Message *aMessage, uint16_t aLength)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+
+    otEXPECT(aLength > 0);
+    otEXPECT(getMessageSize(aMessage) == aLength);
+    otEXPECT(aMessage->mDataLength > 0);
+
+    // Validate the dest address.
+    otEXPECT(addressesMatch(&aMessage->mDst, &sIp6Address) || addressesMatch(&aMessage->mDst, &sLinkLocalAllNodes) ||
+             addressesMatch(&aMessage->mDst, &sLinkLocalAllRouters));
+
+    if (aMessage->mData[0] == OT_ICMP6_TYPE_NEIGHBOR_SOLICIT)
+    {
+        LOG("Received NS, responding with NA");
+        sendNeighborAdvert(aMessage);
+    }
+    else
+    {
+        LOG("Received msg, len:%u", aMessage->mDataLength);
+        otPlatInfraIfRecvIcmp6Nd(aInstance, aMessage->mIfIndex, &aMessage->mSrc, aMessage->mData,
+                                 aMessage->mDataLength);
+    }
+
+exit:
+    return;
+}
+
+//---------------------------------------------------------------------------------------------------------------------
+// otPlatInfraIf
+
 bool otPlatInfraIfHasAddress(uint32_t aInfraIfIndex, const otIp6Address *aAddress)
 {
     OT_UNUSED_VARIABLE(aInfraIfIndex);
-    OT_UNUSED_VARIABLE(aAddress);
 
-    return false;
+    return addressesMatch(aAddress, &sIp6Address);
 }
 
 otError otPlatInfraIfSendIcmp6Nd(uint32_t            aInfraIfIndex,
@@ -44,12 +183,27 @@
                                  const uint8_t      *aBuffer,
                                  uint16_t            aBufferLength)
 {
-    OT_UNUSED_VARIABLE(aInfraIfIndex);
-    OT_UNUSED_VARIABLE(aDestAddress);
-    OT_UNUSED_VARIABLE(aBuffer);
-    OT_UNUSED_VARIABLE(aBufferLength);
+    otError  error = OT_ERROR_FAILED;
+    Message *message;
 
-    return OT_ERROR_NONE;
+    otEXPECT(sInitialized);
+    otEXPECT(sNumPendingTx < INFRA_IF_MAX_PENDING_TX);
+
+    message = &sPendingTx[sNumPendingTx++];
+
+    message->mIfIndex = aInfraIfIndex;
+    message->mSrc     = sIp6Address;
+    message->mDst     = *aDestAddress;
+
+    assert(aBufferLength <= INFRA_IF_MAX_PACKET_SIZE);
+    message->mDataLength = aBufferLength;
+    memcpy(message->mData, aBuffer, aBufferLength);
+    error = OT_ERROR_NONE;
+
+    LOG("otPlatInfraIfSendIcmp6Nd() msg-len:%u", aBufferLength);
+
+exit:
+    return error;
 }
 
 otError otPlatInfraIfDiscoverNat64Prefix(uint32_t aInfraIfIndex)
@@ -58,4 +212,127 @@
 
     return OT_ERROR_NONE;
 }
-#endif
+
+//---------------------------------------------------------------------------------------------------------------------
+// platformInfraIf
+
+void platformInfraIfInit(void)
+{
+    char *str;
+
+    otEXPECT(!sInitialized);
+
+    sInitialized = true;
+
+    memset(&sIp6Address, 0, sizeof(sIp6Address));
+    sIp6Address.mFields.m8[0]  = 0xfe;
+    sIp6Address.mFields.m8[1]  = 0x80;
+    sIp6Address.mFields.m8[15] = (uint8_t)(gNodeId & 0xff);
+
+    // "ff02::01"
+    memset(&sLinkLocalAllNodes, 0, sizeof(sLinkLocalAllNodes));
+    sLinkLocalAllNodes.mFields.m8[0]  = 0xff;
+    sLinkLocalAllNodes.mFields.m8[1]  = 0x02;
+    sLinkLocalAllNodes.mFields.m8[15] = 0x01;
+
+    // "ff02::02"
+    memset(&sLinkLocalAllRouters, 0, sizeof(sLinkLocalAllRouters));
+    sLinkLocalAllRouters.mFields.m8[0]  = 0xff;
+    sLinkLocalAllRouters.mFields.m8[1]  = 0x02;
+    sLinkLocalAllRouters.mFields.m8[15] = 0x02;
+
+    str = getenv("PORT_OFFSET");
+
+    if (str != NULL)
+    {
+        char *endptr;
+
+        sPortOffset = (uint16_t)strtol(str, &endptr, 0);
+
+        if (*endptr != '\0')
+        {
+            fprintf(stderr, "\r\nInvalid PORT_OFFSET: %s\r\n", str);
+            exit(EXIT_FAILURE);
+        }
+
+        sPortOffset *= (MAX_NETWORK_SIZE + 1);
+    }
+
+    utilsInitSocket(&sSocket, INFRA_IF_SIM_PORT + sPortOffset);
+
+exit:
+    return;
+}
+
+void platformInfraIfDeinit(void)
+{
+    otEXPECT(sInitialized);
+    sInitialized = false;
+    utilsDeinitSocket(&sSocket);
+
+exit:
+    return;
+}
+
+void platformInfraIfUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, int *aMaxFd)
+{
+    otEXPECT(sInitialized);
+
+    utilsAddSocketRxFd(&sSocket, aReadFdSet, aMaxFd);
+
+    if (sNumPendingTx > 0)
+    {
+        utilsAddSocketTxFd(&sSocket, aWriteFdSet, aMaxFd);
+    }
+
+exit:
+    return;
+}
+
+void platformInfraIfProcess(otInstance *aInstance, const fd_set *aReadFdSet, const fd_set *aWriteFdSet)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+
+    otEXPECT(sInitialized);
+
+    if ((sNumPendingTx > 0) && utilsCanSocketSend(&sSocket, aWriteFdSet))
+    {
+        sendPendingTxMessages();
+    }
+
+    if (utilsCanSocketReceive(&sSocket, aReadFdSet))
+    {
+        Message  message;
+        uint16_t len;
+
+        message.mDataLength = 0;
+
+        len = utilsReceiveFromSocket(&sSocket, &message, sizeof(message), NULL);
+        processMessage(aInstance, &message, len);
+    }
+
+exit:
+    return;
+}
+
+//---------------------------------------------------------------------------------------------------------------------
+// Provide weak implementation (used for RCP builds).
+// `OPENTHREAD_RADIO` is not available in simulation platform
+
+OT_TOOL_WEAK void otPlatInfraIfRecvIcmp6Nd(otInstance         *aInstance,
+                                           uint32_t            aInfraIfIndex,
+                                           const otIp6Address *aSrcAddress,
+                                           const uint8_t      *aBuffer,
+                                           uint16_t            aBufferLength)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+    OT_UNUSED_VARIABLE(aInfraIfIndex);
+    OT_UNUSED_VARIABLE(aSrcAddress);
+    OT_UNUSED_VARIABLE(aBuffer);
+    OT_UNUSED_VARIABLE(aBufferLength);
+
+    fprintf(stderr, "\n\r Weak otPlatInfraIfRecvIcmp6Nd is being used\n\r");
+    exit(1);
+}
+
+#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
diff --git a/examples/platforms/simulation/platform-simulation.h b/examples/platforms/simulation/platform-simulation.h
index 0592b14..d440768 100644
--- a/examples/platforms/simulation/platform-simulation.h
+++ b/examples/platforms/simulation/platform-simulation.h
@@ -159,7 +159,7 @@
 void platformRadioReceive(otInstance *aInstance, uint8_t *aBuf, uint16_t aBufLength);
 
 /**
- * Updates the file descriptor sets with file descriptors used by the radio driver.
+ * Updates the file descriptor sets with file descriptors used by the BLE radio driver.
  *
  * @param[in,out]  aReadFdSet   A pointer to the read file descriptors.
  * @param[in,out]  aWriteFdSet  A pointer to the write file descriptors.
@@ -305,4 +305,67 @@
 
 #endif // OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
 
+#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
+
+/**
+ * Initializes the platform infra-if module.
+ *
+ */
+void platformInfraIfInit(void);
+
+/**
+ * Shuts down the platform infra-if module.
+ *
+ */
+void platformInfraIfDeinit(void);
+
+/**
+ * Updates the file descriptor sets with file descriptors used by the infra-if module
+ *
+ * @param[in,out]  aReadFdSet   A pointer to the read file descriptors.
+ * @param[in,out]  aWriteFdSet  A pointer to the write file descriptors.
+ * @param[in,out]  aMaxFd       A pointer to the max file descriptor.
+ *
+ */
+void platformInfraIfUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, int *aMaxFd);
+
+/**
+ * Performs infra-if module processing.
+ *
+ * @param[in]  aInstance    The OpenThread instance structure.
+ * @param[in]  aReadFdSet   A pointer to the read file descriptors.
+ * @param[in]  aWriteFdSet  A pointer to the write file descriptors.
+ *
+ */
+void platformInfraIfProcess(otInstance *aInstance, const fd_set *aReadFdSet, const fd_set *aWriteFdSet);
+
+#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
+
+/**
+ * Shuts down the BLE service used by OpenThread.
+ *
+ */
+void platformBleDeinit(void);
+
+/**
+ * Updates the file descriptor sets with file descriptors used by the radio driver.
+ *
+ * @param[in,out]  aReadFdSet   A pointer to the read file descriptors.
+ * @param[in,out]  aWriteFdSet  A pointer to the write file descriptors.
+ * @param[in,out]  aTimeout     A pointer to the timeout.
+ * @param[in,out]  aMaxFd       A pointer to the max file descriptor.
+ *
+ */
+void platformBleUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, struct timeval *aTimeout, int *aMaxFd);
+
+/**
+ * Performs BLE driver processing.
+ *
+ * @param[in]  aInstance    The OpenThread instance structure.
+ * @param[in]  aReadFdSet   A pointer to the read file descriptors.
+ * @param[in]  aWriteFdSet  A pointer to the write file descriptors.
+ *
+ */
+void platformBleProcess(otInstance *aInstance, const fd_set *aReadFdSet, const fd_set *aWriteFdSet);
+
 #endif // PLATFORM_SIMULATION_H_
diff --git a/examples/platforms/simulation/radio.c b/examples/platforms/simulation/radio.c
index 6ba2a5d..960d8c6 100644
--- a/examples/platforms/simulation/radio.c
+++ b/examples/platforms/simulation/radio.c
@@ -41,14 +41,12 @@
 #include <openthread/platform/radio.h>
 #include <openthread/platform/time.h>
 
+#include "simul_utils.h"
 #include "utils/code_utils.h"
 #include "utils/link_metrics.h"
 #include "utils/mac_frame.h"
 #include "utils/soft_source_match_table.h"
 
-// The IPv4 group for receiving packets of radio simulation
-#define OT_RADIO_GROUP "224.0.0.116"
-
 #define MS_PER_S 1000
 #define US_PER_MS 1000
 
@@ -75,11 +73,9 @@
 extern uint16_t sPortBase;
 extern uint16_t sPortOffset;
 #else
-static int      sTxFd       = -1;
-static int      sRxFd       = -1;
-static uint16_t sPortBase   = 9000;
-static uint16_t sPortOffset = 0;
-static uint16_t sPort       = 0;
+static utilsSocket sSocket;
+static uint16_t    sPortBase   = 9000;
+static uint16_t    sPortOffset = 0;
 #endif
 
 static int8_t   sEnergyScanResult  = OT_RADIO_RSSI_INVALID;
@@ -190,6 +186,8 @@
 {
     bool isConnectable = true;
 
+    otEXPECT_ACTION(aNodeId != gNodeId, isConnectable = false);
+
     switch (sFilterMode)
     {
     case kFilterOff:
@@ -202,6 +200,7 @@
         break;
     }
 
+exit:
     return isConnectable;
 }
 
@@ -407,82 +406,15 @@
     sPromiscuous = aEnable;
 }
 
-#if OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0
-static void initFds(void)
-{
-    int                fd;
-    int                one = 1;
-    struct sockaddr_in sockaddr;
-
-    memset(&sockaddr, 0, sizeof(sockaddr));
-
-    otEXPECT_ACTION((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) != -1, perror("socket(sTxFd)"));
-
-    sPort                    = (uint16_t)(sPortBase + sPortOffset + gNodeId);
-    sockaddr.sin_family      = AF_INET;
-    sockaddr.sin_port        = htons(sPort);
-    sockaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
-
-    otEXPECT_ACTION(setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &sockaddr.sin_addr, sizeof(sockaddr.sin_addr)) != -1,
-                    perror("setsockopt(sTxFd, IP_MULTICAST_IF)"));
-
-    otEXPECT_ACTION(setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &one, sizeof(one)) != -1,
-                    perror("setsockopt(sRxFd, IP_MULTICAST_LOOP)"));
-
-    otEXPECT_ACTION(bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) != -1, perror("bind(sTxFd)"));
-
-    // Tx fd is successfully initialized.
-    sTxFd = fd;
-
-    otEXPECT_ACTION((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) != -1, perror("socket(sRxFd)"));
-
-    otEXPECT_ACTION(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) != -1,
-                    perror("setsockopt(sRxFd, SO_REUSEADDR)"));
-    otEXPECT_ACTION(setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) != -1,
-                    perror("setsockopt(sRxFd, SO_REUSEPORT)"));
-
-    {
-        struct ip_mreqn mreq;
-
-        memset(&mreq, 0, sizeof(mreq));
-        inet_pton(AF_INET, OT_RADIO_GROUP, &mreq.imr_multiaddr);
-
-        // Always use loopback device to send simulation packets.
-        mreq.imr_address.s_addr = inet_addr("127.0.0.1");
-
-        otEXPECT_ACTION(setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &mreq.imr_address, sizeof(mreq.imr_address)) != -1,
-                        perror("setsockopt(sRxFd, IP_MULTICAST_IF)"));
-        otEXPECT_ACTION(setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) != -1,
-                        perror("setsockopt(sRxFd, IP_ADD_MEMBERSHIP)"));
-    }
-
-    sockaddr.sin_family      = AF_INET;
-    sockaddr.sin_port        = htons((uint16_t)(sPortBase + sPortOffset));
-    sockaddr.sin_addr.s_addr = inet_addr(OT_RADIO_GROUP);
-
-    otEXPECT_ACTION(bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) != -1, perror("bind(sRxFd)"));
-
-    // Rx fd is successfully initialized.
-    sRxFd = fd;
-
-exit:
-    if (sRxFd == -1 || sTxFd == -1)
-    {
-        exit(EXIT_FAILURE);
-    }
-}
-#endif // OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0
-
 void platformRadioInit(void)
 {
-#if OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0
+#if !OPENTHREAD_SIMULATION_VIRTUAL_TIME
     parseFromEnvAsUint16("PORT_BASE", &sPortBase);
-
     parseFromEnvAsUint16("PORT_OFFSET", &sPortOffset);
     sPortOffset *= (MAX_NETWORK_SIZE + 1);
 
-    initFds();
-#endif // OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0
+    utilsInitSocket(&sSocket, sPortBase + sPortOffset);
+#endif
 
     sReceiveFrame.mPsdu  = sReceiveMessage.mPsdu;
     sTransmitFrame.mPsdu = sTransmitMessage.mPsdu;
@@ -859,24 +791,14 @@
 #else
 void platformRadioUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, struct timeval *aTimeout, int *aMaxFd)
 {
-    if (aReadFdSet != NULL && (sState != OT_RADIO_STATE_TRANSMIT || sTxWait))
+    if (sState != OT_RADIO_STATE_TRANSMIT || sTxWait)
     {
-        FD_SET(sRxFd, aReadFdSet);
-
-        if (aMaxFd != NULL && *aMaxFd < sRxFd)
-        {
-            *aMaxFd = sRxFd;
-        }
+        utilsAddSocketRxFd(&sSocket, aReadFdSet, aMaxFd);
     }
 
-    if (aWriteFdSet != NULL && platformRadioIsTransmitPending())
+    if (platformRadioIsTransmitPending())
     {
-        FD_SET(sTxFd, aWriteFdSet);
-
-        if (aMaxFd != NULL && *aMaxFd < sTxFd)
-        {
-            *aMaxFd = sTxFd;
-        }
+        utilsAddSocketTxFd(&sSocket, aWriteFdSet, aMaxFd);
     }
 
     if (sEnergyScanning)
@@ -900,18 +822,7 @@
 }
 
 // no need to close in virtual time mode.
-void platformRadioDeinit(void)
-{
-    if (sRxFd != -1)
-    {
-        close(sRxFd);
-    }
-
-    if (sTxFd != -1)
-    {
-        close(sTxFd);
-    }
-}
+void platformRadioDeinit(void) { utilsDeinitSocket(&sSocket); }
 #endif // OPENTHREAD_SIMULATION_VIRTUAL_TIME
 
 void platformRadioProcess(otInstance *aInstance, const fd_set *aReadFdSet, const fd_set *aWriteFdSet)
@@ -919,41 +830,22 @@
     OT_UNUSED_VARIABLE(aReadFdSet);
     OT_UNUSED_VARIABLE(aWriteFdSet);
 
-#if OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0
-    if (FD_ISSET(sRxFd, aReadFdSet))
+#if !OPENTHREAD_SIMULATION_VIRTUAL_TIME
+    if (utilsCanSocketReceive(&sSocket, aReadFdSet))
     {
-        struct sockaddr_in sockaddr;
-        socklen_t          len = sizeof(sockaddr);
-        ssize_t            rval;
+        uint16_t senderNodeId;
+        uint16_t len;
 
-        memset(&sockaddr, 0, sizeof(sockaddr));
-        rval =
-            recvfrom(sRxFd, (char *)&sReceiveMessage, sizeof(sReceiveMessage), 0, (struct sockaddr *)&sockaddr, &len);
+        len = utilsReceiveFromSocket(&sSocket, &sReceiveMessage, sizeof(sReceiveMessage), &senderNodeId);
 
-        if (rval > 0)
+        if (NodeIdFilterIsConnectable(senderNodeId))
         {
-            uint16_t srcPort   = ntohs(sockaddr.sin_port);
-            uint16_t srcNodeId = srcPort - sPortOffset - sPortBase;
-
-            if (NodeIdFilterIsConnectable(srcNodeId) && srcPort != sPort)
-            {
-                sReceiveFrame.mLength = (uint16_t)(rval - 1);
-
-                radioReceive(aInstance);
-            }
-        }
-        else if (rval == 0)
-        {
-            // socket is closed, which should not happen
-            assert(false);
-        }
-        else if (errno != EINTR && errno != EAGAIN)
-        {
-            perror("recvfrom(sRxFd)");
-            exit(EXIT_FAILURE);
+            sReceiveFrame.mLength = len - 1;
+            radioReceive(aInstance);
         }
     }
-#endif // OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0
+#endif
+
     if (platformRadioIsTransmitPending())
     {
         radioSendMessage(aInstance);
@@ -968,33 +860,17 @@
 
 void radioTransmit(struct RadioMessage *aMessage, const struct otRadioFrame *aFrame)
 {
-#if OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0
-    ssize_t            rval;
-    struct sockaddr_in sockaddr;
-
-    memset(&sockaddr, 0, sizeof(sockaddr));
-    sockaddr.sin_family = AF_INET;
-    inet_pton(AF_INET, OT_RADIO_GROUP, &sockaddr.sin_addr);
-
-    sockaddr.sin_port = htons((uint16_t)(sPortBase + sPortOffset));
-    rval =
-        sendto(sTxFd, (const char *)aMessage, 1 + aFrame->mLength, 0, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
-
-    if (rval < 0)
-    {
-        perror("sendto(sTxFd)");
-        exit(EXIT_FAILURE);
-    }
-#else  // OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0
+#if !OPENTHREAD_SIMULATION_VIRTUAL_TIME
+    utilsSendOverSocket(&sSocket, aMessage, aFrame->mLength + 1); // + 1 is for `mChannel`
+#else
     struct Event event;
 
     event.mDelay      = 1; // 1us for now
     event.mEvent      = OT_SIM_EVENT_RADIO_RECEIVED;
     event.mDataLength = 1 + aFrame->mLength; // include channel in first byte
     memcpy(event.mData, aMessage, event.mDataLength);
-
     otSimSendEvent(&event);
-#endif // OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0
+#endif
 }
 
 void radioSendAck(void)
@@ -1331,11 +1207,18 @@
     OT_UNUSED_VARIABLE(aShortAddr);
     OT_UNUSED_VARIABLE(aExtAddr);
 
-    otError error = OT_ERROR_NONE;
-
     sCslPeriod = aCslPeriod;
 
-    return error;
+    return OT_ERROR_NONE;
+}
+
+otError otPlatRadioResetCsl(otInstance *aInstance)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+
+    sCslPeriod = 0;
+
+    return OT_ERROR_NONE;
 }
 
 void otPlatRadioUpdateCslSampleTime(otInstance *aInstance, uint32_t aCslSampleTime)
diff --git a/examples/platforms/simulation/simul_utils.c b/examples/platforms/simulation/simul_utils.c
new file mode 100644
index 0000000..36b69af
--- /dev/null
+++ b/examples/platforms/simulation/simul_utils.c
@@ -0,0 +1,228 @@
+/*
+ *  Copyright (c) 2024, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "simul_utils.h"
+
+#include <errno.h>
+#include <sys/time.h>
+
+#include "utils/code_utils.h"
+
+#define UTILS_SOCKET_LOCAL_HOST_ADDR "127.0.0.1"
+#define UTILS_SOCKET_GROUP_ADDR "224.0.0.116"
+
+const char *gLocalHost = UTILS_SOCKET_LOCAL_HOST_ADDR;
+
+void utilsAddFdToFdSet(int aFd, fd_set *aFdSet, int *aMaxFd)
+{
+    otEXPECT(aFd >= 0);
+    otEXPECT(aFdSet != NULL);
+
+    FD_SET(aFd, aFdSet);
+
+    otEXPECT(aMaxFd != NULL);
+
+    if (*aMaxFd < aFd)
+    {
+        *aMaxFd = aFd;
+    }
+
+exit:
+    return;
+}
+
+void utilsInitSocket(utilsSocket *aSocket, uint16_t aPortBase)
+{
+    int                fd;
+    int                one = 1;
+    int                rval;
+    struct sockaddr_in sockaddr;
+    struct ip_mreqn    mreq;
+
+    aSocket->mInitialized = false;
+    aSocket->mPortBase    = aPortBase;
+    aSocket->mPort        = (uint16_t)(aSocket->mPortBase + gNodeId);
+
+    //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+    // Prepare `mTxFd`
+
+    fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+    otEXPECT_ACTION(fd != -1, perror("socket(TxFd)"));
+
+    memset(&sockaddr, 0, sizeof(sockaddr));
+    sockaddr.sin_family      = AF_INET;
+    sockaddr.sin_port        = htons(aSocket->mPort);
+    sockaddr.sin_addr.s_addr = inet_addr(gLocalHost);
+
+    rval = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &sockaddr.sin_addr, sizeof(sockaddr.sin_addr));
+    otEXPECT_ACTION(rval != -1, perror("setsockopt(TxFd, IP_MULTICAST_IF)"));
+
+    rval = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &one, sizeof(one));
+    otEXPECT_ACTION(rval != -1, perror("setsockopt(TxFd, IP_MULTICAST_LOOP)"));
+
+    rval = bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
+    otEXPECT_ACTION(rval != -1, perror("bind(TxFd)"));
+
+    aSocket->mTxFd = fd;
+
+    //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+    // Prepare `mRxFd`
+
+    fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+    otEXPECT_ACTION(fd != -1, perror("socket(RxFd)"));
+
+    rval = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
+    otEXPECT_ACTION(rval != -1, perror("setsockopt(RxFd, SO_REUSEADDR)"));
+
+    rval = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one));
+    otEXPECT_ACTION(rval != -1, perror("setsockopt(RxFd, SO_REUSEPORT)"));
+
+    memset(&mreq, 0, sizeof(mreq));
+    inet_pton(AF_INET, UTILS_SOCKET_GROUP_ADDR, &mreq.imr_multiaddr);
+
+    mreq.imr_address.s_addr = inet_addr(gLocalHost);
+
+    rval = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &mreq.imr_address, sizeof(mreq.imr_address));
+    otEXPECT_ACTION(rval != -1, perror("setsockopt(RxFd, IP_MULTICAST_IF)"));
+
+    rval = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
+    otEXPECT_ACTION(rval != -1, perror("setsockopt(RxFd, IP_ADD_MEMBERSHIP)"));
+
+    sockaddr.sin_family      = AF_INET;
+    sockaddr.sin_port        = htons(aSocket->mPortBase);
+    sockaddr.sin_addr.s_addr = inet_addr(UTILS_SOCKET_GROUP_ADDR);
+
+    rval = bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
+    otEXPECT_ACTION(rval != -1, perror("bind(RxFd)"));
+
+    aSocket->mRxFd = fd;
+
+    aSocket->mInitialized = true;
+
+exit:
+    if (!aSocket->mInitialized)
+    {
+        exit(EXIT_FAILURE);
+    }
+}
+
+void utilsDeinitSocket(utilsSocket *aSocket)
+{
+    if (aSocket->mInitialized)
+    {
+        close(aSocket->mRxFd);
+        close(aSocket->mTxFd);
+        aSocket->mInitialized = false;
+    }
+}
+
+void utilsAddSocketRxFd(const utilsSocket *aSocket, fd_set *aFdSet, int *aMaxFd)
+{
+    otEXPECT(aSocket->mInitialized);
+    utilsAddFdToFdSet(aSocket->mRxFd, aFdSet, aMaxFd);
+
+exit:
+    return;
+}
+
+void utilsAddSocketTxFd(const utilsSocket *aSocket, fd_set *aFdSet, int *aMaxFd)
+{
+    otEXPECT(aSocket->mInitialized);
+    utilsAddFdToFdSet(aSocket->mTxFd, aFdSet, aMaxFd);
+
+exit:
+    return;
+}
+
+bool utilsCanSocketReceive(const utilsSocket *aSocket, const fd_set *aReadFdSet)
+{
+    return aSocket->mInitialized && FD_ISSET(aSocket->mRxFd, aReadFdSet);
+}
+
+bool utilsCanSocketSend(const utilsSocket *aSocket, const fd_set *aWriteFdSet)
+{
+    return aSocket->mInitialized && FD_ISSET(aSocket->mTxFd, aWriteFdSet);
+}
+
+uint16_t utilsReceiveFromSocket(const utilsSocket *aSocket,
+                                void              *aBuffer,
+                                uint16_t           aBufferSize,
+                                uint16_t          *aSenderNodeId)
+{
+    struct sockaddr_in sockaddr;
+    socklen_t          socklen = sizeof(sockaddr);
+    ssize_t            rval;
+    uint16_t           len = 0;
+
+    memset(&sockaddr, 0, sizeof(sockaddr));
+
+    rval = recvfrom(aSocket->mRxFd, (char *)aBuffer, aBufferSize, 0, (struct sockaddr *)&sockaddr, &socklen);
+
+    if (rval > 0)
+    {
+        uint16_t senderPort = ntohs(sockaddr.sin_port);
+
+        if (aSenderNodeId != NULL)
+        {
+            *aSenderNodeId = (uint16_t)(senderPort - aSocket->mPortBase);
+        }
+
+        len = (uint16_t)rval;
+    }
+    else if (rval == 0)
+    {
+        assert(false);
+    }
+    else if (errno != EINTR && errno != EAGAIN)
+    {
+        perror("recvfrom(RxFd)");
+        exit(EXIT_FAILURE);
+    }
+
+    return len;
+}
+
+void utilsSendOverSocket(const utilsSocket *aSocket, const void *aBuffer, uint16_t aBufferLength)
+{
+    ssize_t            rval;
+    struct sockaddr_in sockaddr;
+
+    memset(&sockaddr, 0, sizeof(sockaddr));
+    sockaddr.sin_family = AF_INET;
+    sockaddr.sin_port   = htons(aSocket->mPortBase);
+    inet_pton(AF_INET, UTILS_SOCKET_GROUP_ADDR, &sockaddr.sin_addr);
+
+    rval =
+        sendto(aSocket->mTxFd, (const char *)aBuffer, aBufferLength, 0, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
+
+    if (rval < 0)
+    {
+        perror("sendto(sTxFd)");
+        exit(EXIT_FAILURE);
+    }
+}
diff --git a/examples/platforms/simulation/simul_utils.h b/examples/platforms/simulation/simul_utils.h
new file mode 100644
index 0000000..0d70f2d
--- /dev/null
+++ b/examples/platforms/simulation/simul_utils.h
@@ -0,0 +1,151 @@
+/*
+ *  Copyright (c) 2024, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef PLATFORM_SIMULATION_SOCKET_UTILS_H_
+#define PLATFORM_SIMULATION_SOCKET_UTILS_H_
+
+#include "platform-simulation.h"
+
+/**
+ * Represents a socket for communication with other simulation node.
+ *
+ * This is used for emulation of 15.4 radio or other interfaces.
+ *
+ */
+typedef struct utilsSocket
+{
+    bool     mInitialized; ///< Whether or not initialized.
+    int      mTxFd;        ///< RX file descriptor.
+    int      mRxFd;        ///< TX file descriptor.
+    uint16_t mPortBase;    ///< Base port number value.
+    uint16_t mPort;        ///< The port number used by this node
+} utilsSocket;
+
+extern const char *gLocalHost; ///< Local host address to use for sockets
+
+/**
+ * Adds a file descriptor (FD) to a given FD set.
+ *
+ * @param[in] aFd      The FD to add.
+ * @param[in] aFdSet   The FD set to add to.
+ * @param[in] aMaxFd   A pointer to track maximum FD in @p aFdSet (can be NULL).
+ *
+ */
+void utilsAddFdToFdSet(int aFd, fd_set *aFdSet, int *aMaxFd);
+
+/**
+ * Initializes the socket.
+ *
+ * @param[in] aSocket     The socket to initialize.
+ * @param[in] aPortBase   The base port number value. Nodes will determine their port as `aPortBased + gNodeId`.
+ *
+ */
+void utilsInitSocket(utilsSocket *aSocket, uint16_t aPortBase);
+
+/**
+ * De-initializes the socket.
+ *
+ * @param[in] aSocket   The socket to de-initialize.
+ *
+ */
+void utilsDeinitSocket(utilsSocket *aSocket);
+
+/**
+ * Adds sockets RX FD to a given FD set.
+ *
+ * @param[in] aSocket   The socket.
+ * @param[in] aFdSet    The (read) FD set to add to.
+ * @param[in] aMaxFd    A pointer to track maximum FD in @p aFdSet (can be NULL).
+ *
+ */
+void utilsAddSocketRxFd(const utilsSocket *aSocket, fd_set *aFdSet, int *aMaxFd);
+
+/**
+ * Adds sockets TX FD to a given FD set.
+ *
+ * @param[in] aSocket   The socket.
+ * @param[in] aFdSet    The (write) FD set to add to.
+ * @param[in] aMaxFd    A pointer to track maximum FD in @p aFdSet (can be NULL).
+ *
+ */
+void utilsAddSocketTxFd(const utilsSocket *aSocket, fd_set *aFdSet, int *aMaxFd);
+
+/**
+ * Indicates whether the socket can receive.
+ *
+ * @param[in] aSocket       The socket.
+ * @param[in] aReadFdSet    The read FD set.
+ *
+ * @retval TRUE   The socket RX FD is in @p aReadFdSet, and socket can receive.
+ * @retval FALSE  The socket RX FD is not in @p aReadFdSet. Socket is not ready to receive.
+ *
+ */
+bool utilsCanSocketReceive(const utilsSocket *aSocket, const fd_set *aReadFdSet);
+
+/**
+ * Indicates whether the socket can send.
+ *
+ * @param[in] aSocket   The socket.
+ * @param[in] aFdSet    The write FD set.
+ *
+ * @retval TRUE   The socket TX FD is in @p aWriteFdSet, and socket can send.
+ * @retval FALSE  The socket TX FD is not in @p aWriteFdSet. Socket is not ready to send.
+ *
+ */
+bool utilsCanSocketSend(const utilsSocket *aSocket, const fd_set *aWriteFdSet);
+
+/**
+ * Receives data from socket.
+ *
+ * MUST be used when `utilsCanSocketReceive()` returns `TRUE.
+ *
+ * @param[in] aSocket          The socket.
+ * @param[out] aBuffer         The buffer to output the read content.
+ * @param[in]  aBufferSize     Maximum size of buffer in bytes.
+ * @param[out] aSenderNodeId   A pointer to return the Node ID of the sender (derived from the port number).
+ *                             Can be NULL if not needed.
+ *
+ * @returns The number of received bytes written into @p aBuffer.
+ *
+ */
+uint16_t utilsReceiveFromSocket(const utilsSocket *aSocket,
+                                void              *aBuffer,
+                                uint16_t           aBufferSize,
+                                uint16_t          *aSenderNodeId);
+
+/**
+ * Sends data over the socket.
+ *
+ * @param[in] aSocket         The socket.
+ * @param[in] aBuffer         The buffer containing the bytes to sent.
+ * @param[in]  aBufferSize    Size of data in @p buffer in bytes.
+ *
+ */
+void utilsSendOverSocket(const utilsSocket *aSocket, const void *aBuffer, uint16_t aBufferLength);
+
+#endif // PLATFORM_SIMULATION_SOCKET_UTILS_H_
diff --git a/examples/platforms/simulation/system.c b/examples/platforms/simulation/system.c
index 535cb1c..2abe892 100644
--- a/examples/platforms/simulation/system.c
+++ b/examples/platforms/simulation/system.c
@@ -36,19 +36,27 @@
 
 #if OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0
 
+#include <arpa/inet.h>
 #include <assert.h>
 #include <errno.h>
 #include <getopt.h>
+#include <ifaddrs.h>
 #include <libgen.h>
+#include <netinet/in.h>
 #include <stddef.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
 
 #include <openthread/tasklet.h>
 #include <openthread/platform/alarm-milli.h>
 #include <openthread/platform/radio.h>
 
+#include "simul_utils.h"
+#include "utils/code_utils.h"
+
 uint32_t gNodeId = 1;
 
 extern bool        gPlatformPseudoResetWasRequested;
@@ -71,6 +79,7 @@
 {
     OT_SIM_OPT_HELP               = 'h',
     OT_SIM_OPT_ENABLE_ENERGY_SCAN = 'E',
+    OT_SIM_OPT_LOCAL_HOST         = 'L',
     OT_SIM_OPT_SLEEP_TO_TX        = 't',
     OT_SIM_OPT_TIME_SPEED         = 's',
     OT_SIM_OPT_LOG_FILE           = 'l',
@@ -96,6 +105,56 @@
     exit(aExitCode);
 }
 
+static const char *GetLocalHostAddress(const char *aLocalHost)
+{
+    struct ifaddrs *ifaddr;
+    static char     ipstr[INET_ADDRSTRLEN] = {0};
+    const char     *rval                   = NULL;
+
+    {
+        struct in_addr addr;
+
+        otEXPECT_ACTION(inet_aton(aLocalHost, &addr) == 0, rval = aLocalHost);
+    }
+
+    if (getifaddrs(&ifaddr) == -1)
+    {
+        perror("getifaddrs");
+        exit(EXIT_FAILURE);
+    }
+
+    for (struct ifaddrs *ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
+    {
+        if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != AF_INET)
+        {
+            continue;
+        }
+
+        if (strcmp(ifa->ifa_name, aLocalHost) == 0)
+        {
+            struct sockaddr_in *addr = (struct sockaddr_in *)ifa->ifa_addr;
+
+            if (inet_ntop(AF_INET, &addr->sin_addr, ipstr, sizeof(ipstr)))
+            {
+                break;
+            }
+        }
+    }
+
+    freeifaddrs(ifaddr);
+
+    if (ipstr[0] == '\0')
+    {
+        fprintf(stderr, "Local host address not found!\n");
+        exit(EXIT_FAILURE);
+    }
+
+    rval = ipstr;
+
+exit:
+    return rval;
+}
+
 void otSysInit(int aArgCount, char *aArgVector[])
 {
     char    *endptr;
@@ -106,6 +165,7 @@
         {"enable-energy-scan", no_argument, 0, OT_SIM_OPT_ENABLE_ENERGY_SCAN},
         {"sleep-to-tx", no_argument, 0, OT_SIM_OPT_SLEEP_TO_TX},
         {"time-speed", required_argument, 0, OT_SIM_OPT_TIME_SPEED},
+        {"local-host", required_argument, 0, OT_SIM_OPT_LOCAL_HOST},
 #if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_PLATFORM_DEFINED)
         {"log-file", required_argument, 0, OT_SIM_OPT_LOG_FILE},
 #endif
@@ -113,9 +173,9 @@
     };
 
 #if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_PLATFORM_DEFINED)
-    static const char options[] = "Ehts:l:";
+    static const char options[] = "Ehts:L:l:";
 #else
-    static const char options[] = "Ehts:";
+    static const char options[] = "Ehts:L:";
 #endif
 
     if (gPlatformPseudoResetWasRequested)
@@ -149,6 +209,10 @@
         case OT_SIM_OPT_SLEEP_TO_TX:
             gRadioCaps |= OT_RADIO_CAPS_SLEEP_TO_TX;
             break;
+        case OT_SIM_OPT_LOCAL_HOST:
+            gLocalHost = GetLocalHostAddress(optarg);
+            fprintf(stderr, "Simulate on %s\n", gLocalHost);
+            break;
         case OT_SIM_OPT_TIME_SPEED:
             speedUpFactor = (uint32_t)strtol(optarg, &endptr, 10);
             if (*endptr != '\0' || speedUpFactor == 0)
@@ -189,6 +253,9 @@
 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
     platformTrelInit(speedUpFactor);
 #endif
+#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
+    platformInfraIfInit();
+#endif
     platformRandomInit();
 }
 
@@ -200,6 +267,9 @@
 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
     platformTrelDeinit();
 #endif
+#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
+    //    platformInfrIfDeinit();
+#endif
     platformLoggingDeinit();
 }
 
@@ -222,6 +292,13 @@
 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
     platformTrelUpdateFdSet(&read_fds, &write_fds, &timeout, &max_fd);
 #endif
+#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
+    platformInfraIfUpdateFdSet(&read_fds, &write_fds, &max_fd);
+#endif
+
+#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
+    platformBleUpdateFdSet(&read_fds, &write_fds, &timeout, &max_fd);
+#endif
 
     if (otTaskletsArePending(aInstance))
     {
@@ -235,6 +312,9 @@
     {
         platformUartProcess();
         platformRadioProcess(aInstance, &read_fds, &write_fds);
+#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
+        platformBleProcess(aInstance, &read_fds, &write_fds);
+#endif
     }
     else if (errno != EINTR)
     {
@@ -246,6 +326,9 @@
 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
     platformTrelProcess(aInstance, &read_fds, &write_fds);
 #endif
+#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
+    platformInfraIfProcess(aInstance, &read_fds, &write_fds);
+#endif
 
     if (gTerminate)
     {
diff --git a/examples/platforms/simulation/trel.c b/examples/platforms/simulation/trel.c
index 3413c93..696b1a5 100644
--- a/examples/platforms/simulation/trel.c
+++ b/examples/platforms/simulation/trel.c
@@ -31,6 +31,7 @@
 #include <openthread/random_noncrypto.h>
 #include <openthread/platform/trel.h>
 
+#include "simul_utils.h"
 #include "utils/code_utils.h"
 
 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
@@ -38,8 +39,6 @@
 // Change DEBUG_LOG to all extra logging
 #define DEBUG_LOG 0
 
-// The IPv4 group for receiving
-#define TREL_SIM_GROUP "224.0.0.116"
 #define TREL_SIM_PORT 9200
 
 #define TREL_MAX_PACKET_SIZE 1800
@@ -67,16 +66,15 @@
 static uint8_t sNumPendingTx = 0;
 static Message sPendingTx[TREL_MAX_PENDING_TX];
 
-static int      sTxFd       = -1;
-static int      sRxFd       = -1;
-static uint16_t sPortOffset = 0;
-static bool     sEnabled    = false;
-static uint16_t sUdpPort;
+static utilsSocket sSocket;
+static uint16_t    sPortOffset = 0;
+static bool        sEnabled    = false;
 
-static bool     sServiceRegistered = false;
-static uint16_t sServicePort;
-static uint8_t  sServiceTxtLength;
-static char     sServiceTxtData[TREL_MAX_SERVICE_TXT_DATA_LEN];
+static bool               sServiceRegistered = false;
+static uint16_t           sServicePort;
+static uint8_t            sServiceTxtLength;
+static char               sServiceTxtData[TREL_MAX_SERVICE_TXT_DATA_LEN];
+static otPlatTrelCounters sCounters;
 
 #if DEBUG_LOG
 static void dumpBuffer(const void *aBuffer, uint16_t aLength)
@@ -116,80 +114,6 @@
 }
 #endif
 
-static void initFds(void)
-{
-    int                fd;
-    int                one = 1;
-    struct sockaddr_in sockaddr;
-    struct ip_mreqn    mreq;
-
-    memset(&sockaddr, 0, sizeof(sockaddr));
-
-    otEXPECT_ACTION((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) != -1, perror("socket(sTxFd)"));
-
-    sUdpPort                 = (uint16_t)(TREL_SIM_PORT + sPortOffset + gNodeId);
-    sockaddr.sin_family      = AF_INET;
-    sockaddr.sin_port        = htons(sUdpPort);
-    sockaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
-
-    otEXPECT_ACTION(setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &sockaddr.sin_addr, sizeof(sockaddr.sin_addr)) != -1,
-                    perror("setsockopt(sTxFd, IP_MULTICAST_IF)"));
-
-    otEXPECT_ACTION(setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &one, sizeof(one)) != -1,
-                    perror("setsockopt(sTxFd, IP_MULTICAST_LOOP)"));
-
-    otEXPECT_ACTION(bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) != -1, perror("bind(sTxFd)"));
-
-    // Tx fd is successfully initialized.
-    sTxFd = fd;
-
-    otEXPECT_ACTION((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) != -1, perror("socket(sRxFd)"));
-
-    otEXPECT_ACTION(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) != -1,
-                    perror("setsockopt(sRxFd, SO_REUSEADDR)"));
-    otEXPECT_ACTION(setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) != -1,
-                    perror("setsockopt(sRxFd, SO_REUSEPORT)"));
-
-    memset(&mreq, 0, sizeof(mreq));
-    inet_pton(AF_INET, TREL_SIM_GROUP, &mreq.imr_multiaddr);
-
-    // Always use loopback device to send simulation packets.
-    mreq.imr_address.s_addr = inet_addr("127.0.0.1");
-
-    otEXPECT_ACTION(setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &mreq.imr_address, sizeof(mreq.imr_address)) != -1,
-                    perror("setsockopt(sRxFd, IP_MULTICAST_IF)"));
-    otEXPECT_ACTION(setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) != -1,
-                    perror("setsockopt(sRxFd, IP_ADD_MEMBERSHIP)"));
-
-    sockaddr.sin_family      = AF_INET;
-    sockaddr.sin_port        = htons((uint16_t)(TREL_SIM_PORT + sPortOffset));
-    sockaddr.sin_addr.s_addr = inet_addr(TREL_SIM_GROUP);
-
-    otEXPECT_ACTION(bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) != -1, perror("bind(sRxFd)"));
-
-    // Rx fd is successfully initialized.
-    sRxFd = fd;
-
-exit:
-    if (sRxFd == -1 || sTxFd == -1)
-    {
-        exit(EXIT_FAILURE);
-    }
-}
-
-static void deinitFds(void)
-{
-    if (sRxFd != -1)
-    {
-        close(sRxFd);
-    }
-
-    if (sTxFd != -1)
-    {
-        close(sTxFd);
-    }
-}
-
 static uint16_t getMessageSize(const Message *aMessage)
 {
     return (uint16_t)(&aMessage->mData[aMessage->mDataLength] - (const uint8_t *)aMessage);
@@ -197,31 +121,13 @@
 
 static void sendPendingTxMessages(void)
 {
-    ssize_t            rval;
-    struct sockaddr_in sockaddr;
-
-    memset(&sockaddr, 0, sizeof(sockaddr));
-    sockaddr.sin_family = AF_INET;
-    inet_pton(AF_INET, TREL_SIM_GROUP, &sockaddr.sin_addr);
-
-    sockaddr.sin_port = htons((uint16_t)(TREL_SIM_PORT + sPortOffset));
-
     for (uint8_t i = 0; i < sNumPendingTx; i++)
     {
-        uint16_t size = getMessageSize(&sPendingTx[i]);
-
 #if DEBUG_LOG
         fprintf(stderr, "\r\n[trel-sim] Sending message (num:%d, type:%s, port:%u)\r\n", i,
                 messageTypeToString(sPendingTx[i].mType), sPendingTx[i].mSockAddr.mPort);
 #endif
-
-        rval = sendto(sTxFd, &sPendingTx[i], size, 0, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
-
-        if (rval < 0)
-        {
-            perror("sendto(sTxFd)");
-            exit(EXIT_FAILURE);
-        }
+        utilsSendOverSocket(&sSocket, &sPendingTx[i], getMessageSize(&sPendingTx[i]));
     }
 
     sNumPendingTx = 0;
@@ -278,7 +184,7 @@
     switch (aMessage->mType)
     {
     case TREL_DATA_MESSAGE:
-        otEXPECT(aMessage->mSockAddr.mPort == sUdpPort);
+        otEXPECT(aMessage->mSockAddr.mPort == sSocket.mPort);
         otPlatTrelHandleReceived(aInstance, aMessage->mData, aMessage->mDataLength);
         break;
 
@@ -308,7 +214,7 @@
 {
     OT_UNUSED_VARIABLE(aInstance);
 
-    *aUdpPort = sUdpPort;
+    *aUdpPort = sSocket.mPort;
 
 #if DEBUG_LOG
     fprintf(stderr, "\r\n[trel-sim] otPlatTrelEnable() *aUdpPort=%u\r\n", *aUdpPort);
@@ -388,6 +294,8 @@
 #if DEBUG_LOG
     fprintf(stderr, "\r\n[trel-sim] otPlatTrelSend(len:%u, port:%u)\r\n", aUdpPayloadLen, aDestSockAddr->mPort);
 #endif
+    ++sCounters.mTxPackets;
+    sCounters.mTxBytes += aUdpPayloadLen;
 }
 
 //---------------------------------------------------------------------------------------------------------------------
@@ -414,65 +322,61 @@
         sPortOffset *= (MAX_NETWORK_SIZE + 1);
     }
 
-    initFds();
+    utilsInitSocket(&sSocket, TREL_SIM_PORT + sPortOffset);
 
     OT_UNUSED_VARIABLE(aSpeedUpFactor);
 }
 
-void platformTrelDeinit(void) { deinitFds(); }
+void platformTrelDeinit(void) { utilsDeinitSocket(&sSocket); }
 
 void platformTrelUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, struct timeval *aTimeout, int *aMaxFd)
 {
     OT_UNUSED_VARIABLE(aTimeout);
 
     // Always ready to receive
-    if (aReadFdSet != NULL)
+    utilsAddSocketRxFd(&sSocket, aReadFdSet, aMaxFd);
+
+    if (sNumPendingTx > 0)
     {
-        FD_SET(sRxFd, aReadFdSet);
-
-        if (aMaxFd != NULL && *aMaxFd < sRxFd)
-        {
-            *aMaxFd = sRxFd;
-        }
-    }
-
-    if ((aWriteFdSet != NULL) && (sNumPendingTx > 0))
-    {
-        FD_SET(sTxFd, aWriteFdSet);
-
-        if (aMaxFd != NULL && *aMaxFd < sTxFd)
-        {
-            *aMaxFd = sTxFd;
-        }
+        utilsAddSocketTxFd(&sSocket, aWriteFdSet, aMaxFd);
     }
 }
 
 void platformTrelProcess(otInstance *aInstance, const fd_set *aReadFdSet, const fd_set *aWriteFdSet)
 {
-    if (FD_ISSET(sTxFd, aWriteFdSet) && (sNumPendingTx > 0))
+    if ((sNumPendingTx > 0) && utilsCanSocketSend(&sSocket, aWriteFdSet))
     {
         sendPendingTxMessages();
     }
 
-    if (FD_ISSET(sRxFd, aReadFdSet))
+    if (utilsCanSocketReceive(&sSocket, aReadFdSet))
     {
-        Message message;
-        ssize_t rval;
+        Message  message;
+        uint16_t len;
 
         message.mDataLength = 0;
 
-        rval = recvfrom(sRxFd, (char *)&message, sizeof(message), 0, NULL, NULL);
+        len = utilsReceiveFromSocket(&sSocket, &message, sizeof(message), NULL);
 
-        if (rval < 0)
+        if (len > 0)
         {
-            perror("recvfrom(sRxFd)");
-            exit(EXIT_FAILURE);
+            processMessage(aInstance, &message, len);
         }
-
-        processMessage(aInstance, &message, (uint16_t)(rval));
     }
 }
 
+const otPlatTrelCounters *otPlatTrelGetCounters(otInstance *aInstance)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+    return &sCounters;
+}
+
+void otPlatTrelResetCounters(otInstance *aInstance)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+    memset(&sCounters, 0, sizeof(sCounters));
+}
+
 //---------------------------------------------------------------------------------------------------------------------
 
 // This is added for RCP build to be built ok
diff --git a/examples/platforms/simulation/uart.c b/examples/platforms/simulation/uart.c
index fe3fceb..70d3871 100644
--- a/examples/platforms/simulation/uart.c
+++ b/examples/platforms/simulation/uart.c
@@ -40,6 +40,7 @@
 
 #include <openthread/platform/debug_uart.h>
 
+#include "simul_utils.h"
 #include "utils/code_utils.h"
 #include "utils/uart.h"
 
@@ -172,34 +173,13 @@
 
 void platformUartUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, fd_set *aErrorFdSet, int *aMaxFd)
 {
-    if (aReadFdSet != NULL)
+    utilsAddFdToFdSet(s_in_fd, aReadFdSet, aMaxFd);
+    utilsAddFdToFdSet(s_in_fd, aErrorFdSet, aMaxFd);
+
+    if ((s_write_length > 0))
     {
-        FD_SET(s_in_fd, aReadFdSet);
-
-        if (aErrorFdSet != NULL)
-        {
-            FD_SET(s_in_fd, aErrorFdSet);
-        }
-
-        if (aMaxFd != NULL && *aMaxFd < s_in_fd)
-        {
-            *aMaxFd = s_in_fd;
-        }
-    }
-
-    if ((aWriteFdSet != NULL) && (s_write_length > 0))
-    {
-        FD_SET(s_out_fd, aWriteFdSet);
-
-        if (aErrorFdSet != NULL)
-        {
-            FD_SET(s_out_fd, aErrorFdSet);
-        }
-
-        if (aMaxFd != NULL && *aMaxFd < s_out_fd)
-        {
-            *aMaxFd = s_out_fd;
-        }
+        utilsAddFdToFdSet(s_out_fd, aWriteFdSet, aMaxFd);
+        utilsAddFdToFdSet(s_out_fd, aErrorFdSet, aMaxFd);
     }
 }
 
diff --git a/examples/platforms/utils/mac_frame.cpp b/examples/platforms/utils/mac_frame.cpp
index 0ae88db..36a1c97 100644
--- a/examples/platforms/utils/mac_frame.cpp
+++ b/examples/platforms/utils/mac_frame.cpp
@@ -43,7 +43,7 @@
     Mac::Address      dst;
     Mac::PanId        panid;
 
-    SuccessOrExit(frame.GetDstAddr(dst));
+    VerifyOrExit(frame.GetDstAddr(dst) == kErrorNone, rval = false);
 
     switch (dst.GetType())
     {
diff --git a/include/openthread/BUILD.gn b/include/openthread/BUILD.gn
index e4760c2..5948fb7 100644
--- a/include/openthread/BUILD.gn
+++ b/include/openthread/BUILD.gn
@@ -41,6 +41,7 @@
   public = [
     "backbone_router.h",
     "backbone_router_ftd.h",
+    "ble_secure.h",
     "border_agent.h",
     "border_router.h",
     "border_routing.h",
@@ -84,11 +85,13 @@
     "ping_sender.h",
     "platform/alarm-micro.h",
     "platform/alarm-milli.h",
+    "platform/ble.h",
     "platform/border_routing.h",
     "platform/crypto.h",
     "platform/debug_uart.h",
     "platform/diag.h",
     "platform/dns.h",
+    "platform/dnssd.h",
     "platform/dso_transport.h",
     "platform/entropy.h",
     "platform/flash.h",
@@ -115,6 +118,7 @@
     "srp_client_buffers.h",
     "srp_server.h",
     "tasklet.h",
+    "tcat.h",
     "tcp.h",
     "tcp_ext.h",
     "thread.h",
diff --git a/include/openthread/ble_secure.h b/include/openthread/ble_secure.h
new file mode 100644
index 0000000..98d0156
--- /dev/null
+++ b/include/openthread/ble_secure.h
@@ -0,0 +1,434 @@
+/*
+ *  Copyright (c) 2023, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * @brief
+ *  This file defines the top-level functions for the OpenThread BLE Secure implementation.
+ *
+ *  @note
+ *   The functions in this module require the build-time feature `OPENTHREAD_CONFIG_BLE_TCAT_ENABLE=1`.
+ *
+ *  @note
+ *   To enable cipher suite DTLS_PSK_WITH_AES_128_CCM_8, MBEDTLS_KEY_EXCHANGE_PSK_ENABLED
+ *    must be enabled in mbedtls-config.h
+ *   To enable cipher suite DTLS_ECDHE_ECDSA_WITH_AES_128_CCM_8,
+ *    MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED must be enabled in mbedtls-config.h.
+ */
+
+#ifndef OPENTHREAD_BLE_SECURE_H_
+#define OPENTHREAD_BLE_SECURE_H_
+
+#include <stdint.h>
+#include <openthread/message.h>
+#include <openthread/tcat.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @addtogroup api-ble-secure
+ *
+ * @brief
+ *   This module includes functions that control BLE Secure (TLS over BLE) communication.
+ *
+ *   The functions in this module are available when BLE Secure API feature
+ *   (`OPENTHREAD_CONFIG_BLE_TCAT_ENABLE`) is enabled.
+ *
+ * @{
+ *
+ */
+
+/**
+ * Pointer to call when ble secure connection state changes.
+ *
+ * @param[in]  aInstance            A pointer to an OpenThread instance.
+ * @param[in]  aConnected           TRUE, if a secure connection was established, FALSE otherwise.
+ * @param[in]  aBleConnectionOpen   TRUE if a BLE connection was established to carry a TLS data stream, FALSE
+ *                                  otherwise.
+ * @param[in]  aContext             A pointer to arbitrary context information.
+ *
+ */
+typedef void (*otHandleBleSecureConnect)(otInstance *aInstance,
+                                         bool        aConnected,
+                                         bool        aBleConnectionOpen,
+                                         void       *aContext);
+
+/**
+ * Pointer to call when data was received over a BLE Secure TLS connection.
+ *
+ */
+typedef otHandleTcatApplicationDataReceive otHandleBleSecureReceive;
+
+/**
+ * Starts the BLE Secure service.
+ * When TLV mode is active, the function @p aReceiveHandler will be called once a complete TLV was received and the
+ * message offset points to the TLV value.
+ *
+ * @param[in]  aInstance        A pointer to an OpenThread instance.
+ * @param[in]  aConnectHandler  A pointer to a function that will be called when the connection
+ *                              state changes.
+ * @param[in]  aReceiveHandler  A pointer to a function that will be called once data has been received
+ *                              over the TLS connection.
+ * @param[in]  aTlvMode         A boolean value indicating if line mode shall be activated.
+ * @param[in]  aContext         A pointer to arbitrary context information. May be NULL if not used.
+ *
+ * @retval OT_ERROR_NONE        Successfully started the BLE Secure server.
+ * @retval OT_ERROR_ALREADY     The service was stated already.
+ *
+ */
+otError otBleSecureStart(otInstance              *aInstance,
+                         otHandleBleSecureConnect aConnectHandler,
+                         otHandleBleSecureReceive aReceiveHandler,
+                         bool                     aTlvMode,
+                         void                    *aContext);
+
+/**
+ * Enables the TCAT protocol over BLE Secure.
+ *
+ * @param[in]  aInstance         A pointer to an OpenThread instance.
+ * @param[in]  aVendorInfo       A pointer to the Vendor Information (must remain valid after the method call, may be
+ *                               NULL).
+ * @param[in]  aHandler          A pointer to a function that is called when the join operation completes.
+ *
+ * @retval OT_ERROR_NONE              Successfully started the BLE Secure Joiner role.
+ * @retval OT_ERROR_INVALID_ARGS      @p aElevationPsk or @p aVendorInfo is invalid.
+ * @retval OT_ERROR_INVALID_STATE     The BLE function has not been started or line mode is not selected.
+ *
+ */
+otError otBleSecureTcatStart(otInstance *aInstance, const otTcatVendorInfo *aVendorInfo, otHandleTcatJoin aHandler);
+
+/**
+ * Stops the BLE Secure server.
+ *
+ * @param[in]  aInstance  A pointer to an OpenThread instance.
+ *
+ */
+void otBleSecureStop(otInstance *aInstance);
+
+/**
+ * Sets the Pre-Shared Key (PSK) and cipher suite
+ * TLS_PSK_WITH_AES_128_CCM_8.
+ *
+ * @note Requires the build-time feature `MBEDTLS_KEY_EXCHANGE_PSK_ENABLED` to be enabled.
+ *
+ * @param[in]  aInstance     A pointer to an OpenThread instance.
+ * @param[in]  aPsk          A pointer to the PSK.
+ * @param[in]  aPskLength    The PSK length.
+ * @param[in]  aPskIdentity  The Identity Name for the PSK.
+ * @param[in]  aPskIdLength  The PSK Identity Length.
+ *
+ */
+void otBleSecureSetPsk(otInstance    *aInstance,
+                       const uint8_t *aPsk,
+                       uint16_t       aPskLength,
+                       const uint8_t *aPskIdentity,
+                       uint16_t       aPskIdLength);
+
+/**
+ * Returns the peer x509 certificate base64 encoded.
+ *
+ * @note Requires the build-time features `MBEDTLS_BASE64_C` and
+ *       `MBEDTLS_SSL_KEEP_PEER_CERTIFICATE` to be enabled.
+ *
+ * @param[in]       aInstance        A pointer to an OpenThread instance.
+ * @param[out]      aPeerCert        A pointer to the base64 encoded certificate buffer.
+ * @param[in,out]   aCertLength      On input, the size the max size of @p aPeerCert.
+ *                                   On output, the length of the base64 encoded peer certificate.
+ *
+ * @retval OT_ERROR_NONE            Successfully get the peer certificate.
+ * @retval OT_ERROR_INVALID_ARGS    @p aInstance or @p aCertLength is invalid.
+ * @retval OT_ERROR_INVALID_STATE   Not connected yet.
+ * @retval OT_ERROR_NO_BUFS         Can't allocate memory for certificate.
+ *
+ */
+otError otBleSecureGetPeerCertificateBase64(otInstance *aInstance, unsigned char *aPeerCert, size_t *aCertLength);
+
+/**
+ * Returns an attribute value identified by its OID from the subject
+ * of the peer x509 certificate. The peer OID is provided in binary format.
+ * The attribute length is set if the attribute was successfully read or zero
+ * if unsuccessful. The ASN.1 type as is set as defineded in the ITU-T X.690 standard
+ * if the attribute was successfully read.
+ *
+ * @note Requires the build-time feature
+ *       `MBEDTLS_SSL_KEEP_PEER_CERTIFICATE` to be enabled.
+ *
+ * @param[in]     aInstance           A pointer to an OpenThread instance.
+ * @param[in]     aOid                A pointer to the OID to be found.
+ * @param[in]     aOidLength          The length of the OID.
+ * @param[out]    aAttributeBuffer    A pointer to the attribute buffer.
+ * @param[in,out] aAttributeLength    On input, the size the max size of @p aAttributeBuffer.
+ *                                    On output, the length of the attribute written to the buffer.
+ * @param[out]    aAsn1Type           A pointer to the ASN.1 type of the attribute written to the buffer.
+ *
+ * @retval OT_ERROR_INVALID_STATE     Not connected yet.
+ * @retval OT_ERROR_INVALID_ARGS      Invalid attribute length.
+ * @retval OT_ERROR_NONE              Successfully read attribute.
+ * @retval OT_ERROR_NO_BUFS           Insufficient memory for storing the attribute value.
+ *
+ */
+otError otBleSecureGetPeerSubjectAttributeByOid(otInstance *aInstance,
+                                                const char *aOid,
+                                                size_t      aOidLength,
+                                                uint8_t    *aAttributeBuffer,
+                                                size_t     *aAttributeLength,
+                                                int        *aAsn1Type);
+
+/**
+ * Returns an attribute value for the OID 1.3.6.1.4.1.44970.x from the v3 extensions of
+ * the peer x509 certificate, where the last digit x is set to aThreadOidDescriptor.
+ * The attribute length is set if the attribute was successfully read or zero if unsuccessful.
+ * Requires a connection to be active.
+ *
+ * @note Requires the build-time feature
+ *       `MBEDTLS_SSL_KEEP_PEER_CERTIFICATE` to be enabled.
+ *
+ * @param[in]     aInstance             A pointer to an OpenThread instance.
+ * @param[in]     aThreadOidDescriptor  The last digit of the Thread attribute OID.
+ * @param[out]    aAttributeBuffer      A pointer to the attribute buffer.
+ * @param[in,out] aAttributeLength      On input, the size the max size of @p aAttributeBuffer.
+ *                                      On output, the length of the attribute written to the buffer.
+ *
+ * @retval OT_ERROR_NONE                Successfully read attribute.
+ * @retval OT_ERROR_INVALID_ARGS        Invalid attribute length.
+ * @retval OT_NOT_FOUND                 The requested attribute was not found.
+ * @retval OT_ERROR_NO_BUFS             Insufficient memory for storing the attribute value.
+ * @retval OT_ERROR_INVALID_STATE       Not connected yet.
+ * @retval OT_ERROR_NOT_IMPLEMENTED     The value of aThreadOidDescriptor is >127.
+ * @retval OT_ERROR_PARSE               The certificate extensions could not be parsed.
+ *
+ */
+otError otBleSecureGetThreadAttributeFromPeerCertificate(otInstance *aInstance,
+                                                         int         aThreadOidDescriptor,
+                                                         uint8_t    *aAttributeBuffer,
+                                                         size_t     *aAttributeLength);
+
+/**
+ * Returns an attribute value for the OID 1.3.6.1.4.1.44970.x from the v3 extensions of
+ * the own x509 certificate, where the last digit x is set to aThreadOidDescriptor.
+ * The attribute length is set if the attribute was successfully read or zero if unsuccessful.
+ * Requires a connection to be active.
+ *
+ * @param[in]     aInstance             A pointer to an OpenThread instance.
+ * @param[in]     aThreadOidDescriptor  The last digit of the Thread attribute OID.
+ * @param[out]    aAttributeBuffer      A pointer to the attribute buffer.
+ * @param[in,out] aAttributeLength      On input, the size the max size of @p aAttributeBuffer.
+ *                                      On output, the length of the attribute written to the buffer.
+ *
+ * @retval OT_ERROR_NONE                Successfully read attribute.
+ * @retval OT_ERROR_INVALID_ARGS        Invalid attribute length.
+ * @retval OT_NOT_FOUND                 The requested attribute was not found.
+ * @retval OT_ERROR_NO_BUFS             Insufficient memory for storing the attribute value.
+ * @retval OT_ERROR_INVALID_STATE       Not connected yet.
+ * @retval OT_ERROR_NOT_IMPLEMENTED     The value of aThreadOidDescriptor is >127.
+ * @retval OT_ERROR_PARSE               The certificate extensions could not be parsed.
+ *
+ */
+otError otBleSecureGetThreadAttributeFromOwnCertificate(otInstance *aInstance,
+                                                        int         aThreadOidDescriptor,
+                                                        uint8_t    *aAttributeBuffer,
+                                                        size_t     *aAttributeLength);
+
+/**
+ * Sets the authentication mode for the BLE secure connection.
+ *
+ * Disable or enable the verification of peer certificate.
+ * Must be called before start.
+ *
+ * @param[in]   aInstance               A pointer to an OpenThread instance.
+ * @param[in]   aVerifyPeerCertificate  true, to verify the peer certificate.
+ *
+ */
+void otBleSecureSetSslAuthMode(otInstance *aInstance, bool aVerifyPeerCertificate);
+
+/**
+ * Sets the local device's X509 certificate with corresponding private key for
+ * TLS session with TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8.
+ *
+ * @note Requires `MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED=1`.
+ *
+ * @param[in]  aInstance          A pointer to an OpenThread instance.
+ * @param[in]  aX509Cert          A pointer to the PEM formatted X509 certificate.
+ * @param[in]  aX509Length        The length of certificate.
+ * @param[in]  aPrivateKey        A pointer to the PEM formatted private key.
+ * @param[in]  aPrivateKeyLength  The length of the private key.
+ *
+ */
+void otBleSecureSetCertificate(otInstance    *aInstance,
+                               const uint8_t *aX509Cert,
+                               uint32_t       aX509Length,
+                               const uint8_t *aPrivateKey,
+                               uint32_t       aPrivateKeyLength);
+
+/**
+ * Sets the trusted top level CAs. It is needed for validating the
+ * certificate of the peer.
+ *
+ * TLS mode "ECDHE ECDSA with AES 128 CCM 8" for secure BLE.
+ *
+ * @note Requires `MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED=1`.
+ *
+ * @param[in]  aInstance                A pointer to an OpenThread instance.
+ * @param[in]  aX509CaCertificateChain  A pointer to the PEM formatted X509 CA chain.
+ * @param[in]  aX509CaCertChainLength   The length of chain.
+ *
+ */
+void otBleSecureSetCaCertificateChain(otInstance    *aInstance,
+                                      const uint8_t *aX509CaCertificateChain,
+                                      uint32_t       aX509CaCertChainLength);
+
+/**
+ * Initializes TLS session with a peer using an already open BLE connection.
+ *
+ * @param[in]  aInstance               A pointer to an OpenThread instance.
+ *
+ * @retval OT_ERROR_NONE  Successfully started TLS connection.
+ *
+ */
+otError otBleSecureConnect(otInstance *aInstance);
+
+/**
+ * Stops the BLE and TLS connection.
+ *
+ * @param[in]  aInstance  A pointer to an OpenThread instance.
+ *
+ */
+void otBleSecureDisconnect(otInstance *aInstance);
+
+/**
+ * Indicates whether or not the TLS session is active (connected or conneting).
+ *
+ * @param[in]  aInstance  A pointer to an OpenThread instance.
+ *
+ * @retval TRUE  If TLS session is active.
+ * @retval FALSE If TLS session is not active.
+ *
+ */
+bool otBleSecureIsConnectionActive(otInstance *aInstance);
+
+/**
+ * Indicates whether or not the TLS session is connected.
+ *
+ * @param[in]  aInstance  A pointer to an OpenThread instance.
+ *
+ * @retval TRUE   The TLS session is connected.
+ * @retval FALSE  The TLS session is not connected.
+ *
+ */
+bool otBleSecureIsConnected(otInstance *aInstance);
+
+/**
+ * Indicates whether or not the TCAT agent is enabled.
+ *
+ * @retval TRUE   The TCAT agent is enabled.
+ * @retval FALSE  The TCAT agent is not enabled.
+ *
+ */
+bool otBleSecureIsTcatEnabled(otInstance *aInstance);
+
+/**
+ * Indicates whether or not a TCAT command class is authorized.
+ *
+ * @param[in]  aInstance  A pointer to an OpenThread instance.
+ * @param[in]  aCommandClass  A command class to check.
+ *
+ * @retval TRUE   The command class is authorized.
+ * @retval FALSE  The command class is not authorized.
+ *
+ */
+bool otBleSecureIsCommandClassAuthorized(otInstance *aInstance, otTcatCommandClass aCommandClass);
+
+/**
+ * Sends a secure BLE message.
+ *
+ * @param[in]  aInstance     A pointer to an OpenThread instance.
+ * @param[in]  aMessage      A pointer to the message to send.
+ *
+ * If the return value is OT_ERROR_NONE, OpenThread takes ownership of @p aMessage, and the caller should no longer
+ * reference @p aMessage. If the return value is not OT_ERROR_NONE, the caller retains ownership of @p aMessage,
+ * including freeing @p aMessage if the message buffer is no longer needed.
+ *
+ * @retval OT_ERROR_NONE           Successfully sent message.
+ * @retval OT_ERROR_NO_BUFS        Failed to allocate buffer memory.
+ * @retval OT_ERROR_INVALID_STATE  TLS connection was not initialized.
+ *
+ */
+otError otBleSecureSendMessage(otInstance *aInstance, otMessage *aMessage);
+
+/**
+ * Sends a secure BLE data packet.
+ *
+ * @param[in]  aInstance     A pointer to an OpenThread instance.
+ * @param[in]  aBuf          A pointer to the data to send as the Value of the TCAT Send Application Data TLV.
+ * @param[in]  aLength       A number indicating the length of the data buffer.
+ *
+ * @retval OT_ERROR_NONE           Successfully sent data.
+ * @retval OT_ERROR_NO_BUFS        Failed to allocate buffer memory.
+ * @retval OT_ERROR_INVALID_STATE  TLS connection was not initialized.
+ *
+ */
+otError otBleSecureSend(otInstance *aInstance, uint8_t *aBuf, uint16_t aLength);
+
+/**
+ * Sends a secure BLE data packet containing a TCAT Send Application Data TLV.
+ *
+ * @param[in]  aInstance       A pointer to an OpenThread instance.
+ * @param[in]  aBuf            A pointer to the data to send as the Value of the TCAT Send Application Data TLV.
+ * @param[in]  aLength         A number indicating the length of the data buffer.
+ *
+ * @retval OT_ERROR_NONE           Successfully sent data.
+ * @retval OT_ERROR_NO_BUFS        Failed to allocate buffer memory.
+ * @retval OT_ERROR_INVALID_STATE  TLS connection was not initialized.
+ *
+ */
+otError otBleSecureSendApplicationTlv(otInstance *aInstance, uint8_t *aBuf, uint16_t aLength);
+
+/**
+ * Flushes the send buffer.
+ *
+ * @param[in]  aInstance     A pointer to an OpenThread instance.
+ *
+ * @retval OT_ERROR_NONE           Successfully flushed output buffer.
+ * @retval OT_ERROR_NO_BUFS        Failed to allocate buffer memory.
+ * @retval OT_ERROR_INVALID_STATE  TLS connection was not initialized.
+ *
+ */
+otError otBleSecureFlush(otInstance *aInstance);
+
+/**
+ * @}
+ *
+ */
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* OPENTHREAD_BLE_SECURE_H_ */
diff --git a/include/openthread/border_agent.h b/include/openthread/border_agent.h
index 1c7a535..33578e7 100644
--- a/include/openthread/border_agent.h
+++ b/include/openthread/border_agent.h
@@ -58,6 +58,30 @@
 #define OT_BORDER_AGENT_ID_LENGTH (16)
 
 /**
+ * Minimum length of the ephemeral key string.
+ *
+ */
+#define OT_BORDER_AGENT_MIN_EPHEMERAL_KEY_LENGTH (6)
+
+/**
+ * Maximum length of the ephemeral key string.
+ *
+ */
+#define OT_BORDER_AGENT_MAX_EPHEMERAL_KEY_LENGTH (32)
+
+/**
+ * Default ephemeral key timeout interval in milliseconds.
+ *
+ */
+#define OT_BORDER_AGENT_DEFAULT_EPHEMERAL_KEY_TIMEOUT (2 * 60 * 1000u)
+
+/**
+ * Maximum ephemeral key timeout interval in milliseconds.
+ *
+ */
+#define OT_BORDER_AGENT_MAX_EPHEMERAL_KEY_TIMEOUT (10 * 60 * 1000u)
+
+/**
  * @struct otBorderAgentId
  *
  * Represents a Border Agent ID.
@@ -109,6 +133,8 @@
 /**
  * Gets the randomly generated Border Agent ID.
  *
+ * Requires `OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE`.
+ *
  * The ID is saved in persistent storage and survives reboots. The typical use case of the ID is to
  * be published in the MeshCoP mDNS service as the `id` TXT value for the client to identify this
  * Border Router/Agent device.
@@ -127,6 +153,8 @@
 /**
  * Sets the Border Agent ID.
  *
+ * Requires `OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE`.
+ *
  * The Border Agent ID will be saved in persistent storage and survive reboots. It's required to
  * set the ID only once after factory reset. If the ID has never been set by calling this function,
  * a random ID will be generated and returned when `otBorderAgentGetId` is called.
@@ -143,6 +171,112 @@
 otError otBorderAgentSetId(otInstance *aInstance, const otBorderAgentId *aId);
 
 /**
+ * Sets the ephemeral key for a given timeout duration.
+ *
+ * Requires `OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE`.
+ *
+ * The ephemeral key can be set when the Border Agent is already running and is not currently connected to any external
+ * commissioner (i.e., it is in `OT_BORDER_AGENT_STATE_STARTED` state). Otherwise `OT_ERROR_INVALID_STATE` is returned.
+ *
+ * The given @p aKeyString is directly used as the ephemeral PSK (excluding the trailing null `\0` character ).
+ * The @p aKeyString length must be between `OT_BORDER_AGENT_MIN_EPHEMERAL_KEY_LENGTH` and
+ * `OT_BORDER_AGENT_MAX_EPHEMERAL_KEY_LENGTH`, inclusive.
+ *
+ * Setting the ephemeral key again before a previously set key has timed out will replace the previously set key and
+ * reset the timeout.
+ *
+ * While the timeout interval is in effect, the ephemeral key can be used only once by an external commissioner to
+ * connect. Once the commissioner disconnects, the ephemeral key is cleared, and the Border Agent reverts to using
+ * PSKc.
+ *
+ * @param[in] aInstance    The OpenThread instance.
+ * @param[in] aKeyString   The ephemeral key string (used as PSK excluding the trailing null `\0` character).
+ * @param[in] aTimeout     The timeout duration in milliseconds to use the ephemeral key.
+ *                         If zero, the default `OT_BORDER_AGENT_DEFAULT_EPHEMERAL_KEY_TIMEOUT` value will be used.
+ *                         If the given timeout value is larger than `OT_BORDER_AGENT_MAX_EPHEMERAL_KEY_TIMEOUT`, the
+ *                         max value `OT_BORDER_AGENT_MAX_EPHEMERAL_KEY_TIMEOUT` will be used instead.
+ * @param[in] aUdpPort     The UDP port to use with ephemeral key. If zero, an ephemeral port will be used.
+ *                         `otBorderAgentGetUdpPort()` will return the current UDP port being used.
+ *
+ * @retval OT_ERROR_NONE           Successfully set the ephemeral key.
+ * @retval OT_ERROR_INVALID_STATE  Border Agent is not running or it is connected to an external commissioner.
+ * @retval OT_ERROR_INVALID_ARGS   The given @p aKeyString is not valid (too short or too long).
+ * @retval OT_ERROR_FAILED         Failed to set the key (e.g., could not bind to UDP port).
+
+ *
+ */
+otError otBorderAgentSetEphemeralKey(otInstance *aInstance,
+                                     const char *aKeyString,
+                                     uint32_t    aTimeout,
+                                     uint16_t    aUdpPort);
+
+/**
+ * Cancels the ephemeral key that is in use.
+ *
+ * Requires `OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE`.
+ *
+ * Can be used to cancel a previously set ephemeral key before it times out. If the Border Agent is not running or
+ * there is no ephemeral key in use, calling this function has no effect.
+ *
+ * If a commissioner is connected using the ephemeral key and is currently active, calling this function does not
+ * change its state. In this case the `otBorderAgentIsEphemeralKeyActive()` will continue to return `TRUE` until the
+ * commissioner disconnects.
+ *
+ * @param[in] aInstance    The OpenThread instance.
+ *
+ */
+void otBorderAgentClearEphemeralKey(otInstance *aInstance);
+
+/**
+ * Indicates whether or not an ephemeral key is currently active.
+ *
+ * Requires `OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE`.
+ *
+ * @param[in] aInstance    The OpenThread instance.
+ *
+ * @retval TRUE    An ephemeral key is active.
+ * @retval FALSE   No ephemeral key is active.
+ *
+ */
+bool otBorderAgentIsEphemeralKeyActive(otInstance *aInstance);
+
+/**
+ * Callback function pointer to signal changes related to the Border Agent's ephemeral key.
+ *
+ * This callback is invoked whenever:
+ *
+ * - The Border Agent starts using an ephemeral key.
+ * - Any parameter related to the ephemeral key, such as the port number, changes.
+ * - The Border Agent stops using the ephemeral key due to:
+ *   - A direct call to `otBorderAgentClearEphemeralKey()`.
+ *   - The ephemeral key timing out.
+ *   - An external commissioner successfully using the key to connect and then disconnecting.
+ *   - Reaching the maximum number of allowed failed connection attempts.
+ *
+ * Any OpenThread API, including `otBorderAgent` APIs, can be safely called from this callback.
+ *
+ * @param[in] aContext   A pointer to an arbitrary context (provided when callback is set).
+ *
+ */
+typedef void (*otBorderAgentEphemeralKeyCallback)(void *aContext);
+
+/**
+ * Sets the callback function used by the Border Agent to notify any changes related to use of ephemeral key.
+ *
+ * Requires `OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE`.
+ *
+ * A subsequent call to this function will replace any previously set callback.
+ *
+ * @param[in] aInstance    The OpenThread instance.
+ * @param[in] aCallback    The callback function pointer.
+ * @param[in] aContext     The arbitrary context to use with callback.
+ *
+ */
+void otBorderAgentSetEphemeralKeyCallback(otInstance                       *aInstance,
+                                          otBorderAgentEphemeralKeyCallback aCallback,
+                                          void                             *aContext);
+
+/**
  * @}
  *
  */
diff --git a/include/openthread/border_routing.h b/include/openthread/border_routing.h
index 3fd95c6..1e5ff61 100644
--- a/include/openthread/border_routing.h
+++ b/include/openthread/border_routing.h
@@ -119,6 +119,17 @@
 } otBorderRoutingPrefixTableEntry;
 
 /**
+ * Represents a group of data of platform-generated RA messages processed.
+ *
+ */
+typedef struct otPdProcessedRaInfo
+{
+    uint32_t mNumPlatformRaReceived;   ///< The number of platform generated RA handled by ProcessPlatformGeneratedRa.
+    uint32_t mNumPlatformPioProcessed; ///< The number of PIO processed for adding OMR prefixes.
+    uint32_t mLastPlatformRaMsec;      ///< The timestamp of last processed RA message.
+} otPdProcessedRaInfo;
+
+/**
  * Represents the state of Border Routing Manager.
  *
  */
@@ -229,6 +240,22 @@
 void otBorderRoutingClearRouteInfoOptionPreference(otInstance *aInstance);
 
 /**
+ * Sets additional options to append at the end of emitted Router Advertisement (RA) messages.
+ *
+ * The content of @p aOptions is copied internally, so it can be a temporary buffer (e.g., a stack allocated array).
+ *
+ * Subsequent calls to this function overwrite the previously set value.
+ *
+ * @param[in] aOptions   A pointer to the encoded options. Can be `NULL` to clear.
+ * @param[in] aLength    Number of bytes in @p aOptions.
+ *
+ * @retval OT_ERROR_NONE     Successfully set the extra option bytes.
+ * @retval OT_ERROR_NO_BUFS  Could not allocate buffer to save the buffer.
+ *
+ */
+otError otBorderRoutingSetExtraRouterAdvertOptions(otInstance *aInstance, const uint8_t *aOptions, uint16_t aLength);
+
+/**
  * Gets the current preference used for published routes in Network Data.
  *
  * The preference is determined as follows:
@@ -308,6 +335,21 @@
 otError otBorderRoutingGetPdOmrPrefix(otInstance *aInstance, otBorderRoutingPrefixTableEntry *aPrefixInfo);
 
 /**
+ * Gets the data of platform generated RA message processed..
+ *
+ * `OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE` must be enabled.
+ *
+ * @param[in]   aInstance    A pointer to an OpenThread instance.
+ * @param[out]  aPrefixInfo  A pointer to where the prefix info will be output to.
+ *
+ * @retval  OT_ERROR_NONE           Successfully retrieved the Info.
+ * @retval  OT_ERROR_INVALID_STATE  The Border Routing Manager is not initialized yet.
+ * @retval  OT_ERROR_NOT_FOUND      There are no valid Info on this BR.
+ *
+ */
+otError otBorderRoutingGetPdProcessedRaInfo(otInstance *aInstance, otPdProcessedRaInfo *aPdProcessedRaInfo);
+
+/**
  * Gets the currently favored Off-Mesh-Routable (OMR) Prefix.
  *
  * The favored OMR prefix can be discovered from Network Data or can be this device's local OMR prefix.
diff --git a/include/openthread/channel_manager.h b/include/openthread/channel_manager.h
index 6afe3a4..e024957 100644
--- a/include/openthread/channel_manager.h
+++ b/include/openthread/channel_manager.h
@@ -47,8 +47,14 @@
  * @brief
  *   This module includes functions for Channel Manager.
  *
- *   The functions in this module are available when Channel Manager feature
- *   (`OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE`) is enabled. Channel Manager is available only on an FTD build.
+ *   The functions in this module are available when Channel Manager features
+ *   `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` or `OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE &&
+ * OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE` are enabled. Channel Manager behavior depends on the
+ * device role. It manages the network-wide PAN channel on a Full Thread Device in rx-on-when-idle mode, or with
+ * `OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE` set,
+ *   selects CSL channel in synchronized rx-off-when-idle mode. On a Minimal Thread Device
+ *   `OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE` selects
+ * the CSL channel.
  *
  * @{
  *
@@ -77,7 +83,9 @@
 uint8_t otChannelManagerGetRequestedChannel(otInstance *aInstance);
 
 /**
- * Gets the delay (in seconds) used by Channel Manager for a channel change.
+ * Gets the delay (in seconds) used by Channel Manager for a network channel change.
+ *
+ * Only available on FTDs.
  *
  * @param[in]  aInstance          A pointer to an OpenThread instance.
  *
@@ -87,10 +95,10 @@
 uint16_t otChannelManagerGetDelay(otInstance *aInstance);
 
 /**
- * Sets the delay (in seconds) used for a channel change.
+ * Sets the delay (in seconds) used for a network channel change.
  *
- * The delay should preferably be longer than the maximum data poll interval used by all sleepy-end-devices within the
- * Thread network.
+ * Only available on FTDs. The delay should preferably be longer than the maximum data poll interval used by all
+ * Sleepy End Devices within the Thread network.
  *
  * @param[in]  aInstance          A pointer to an OpenThread instance.
  * @param[in]  aDelay             Delay in seconds.
@@ -117,7 +125,7 @@
  *
  * 2) If the first step passes, then `ChannelManager` selects a potentially better channel. It uses the collected
  *    channel quality data by `ChannelMonitor` module. The supported and favored channels are used at this step.
- *    (see otChannelManagerSetSupportedChannels() and otChannelManagerSetFavoredChannels()).
+ *    (see `otChannelManagerSetSupportedChannels()` and `otChannelManagerSetFavoredChannels()`).
  *
  * 3) If the newly selected channel is different from the current channel, `ChannelManager` requests/starts the
  *    channel change process (internally invoking a `RequestChannelChange()`).
@@ -132,10 +140,41 @@
 otError otChannelManagerRequestChannelSelect(otInstance *aInstance, bool aSkipQualityCheck);
 
 /**
- * Enables or disables the auto-channel-selection functionality.
+ * Requests that `ChannelManager` checks and selects a new CSL channel and starts a CSL channel change.
+ *
+ * Only available with `OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE &&
+ * OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE`. This function asks the `ChannelManager` to select a
+ * channel by itself (based on collected channel quality info).
+ *
+ * Once called, the Channel Manager will perform the following 3 steps:
+ *
+ * 1) `ChannelManager` decides if the CSL channel change would be helpful. This check can be skipped if
+ *    `aSkipQualityCheck` is set to true (forcing a CSL channel selection to happen and skipping the quality check).
+ *    This step uses the collected link quality metrics on the device (such as CCA failure rate, frame and message
+ *    error rates per neighbor, etc.) to determine if the current channel quality is at the level that justifies
+ *    a CSL channel change.
+ *
+ * 2) If the first step passes, then `ChannelManager` selects a potentially better CSL channel. It uses the collected
+ *    channel quality data by `ChannelMonitor` module. The supported and favored channels are used at this step.
+ *    (see `otChannelManagerSetSupportedChannels()` and `otChannelManagerSetFavoredChannels()`).
+ *
+ * 3) If the newly selected CSL channel is different from the current CSL channel, `ChannelManager` starts the
+ *    CSL channel change process.
+ *
+ * @param[in] aInstance                A pointer to an OpenThread instance.
+ * @param[in] aSkipQualityCheck        Indicates whether the quality check (step 1) should be skipped.
+ *
+ * @retval OT_ERROR_NONE               Channel selection finished successfully.
+ * @retval OT_ERROR_NOT_FOUND          Supported channel mask is empty, therefore could not select a channel.
+ *
+ */
+otError otChannelManagerRequestCslChannelSelect(otInstance *aInstance, bool aSkipQualityCheck);
+
+/**
+ * Enables or disables the auto-channel-selection functionality for network channel.
  *
  * When enabled, `ChannelManager` will periodically invoke a `RequestChannelSelect(false)`. The period interval
- * can be set by `SetAutoChannelSelectionInterval()`.
+ * can be set by `otChannelManagerSetAutoChannelSelectionInterval()`.
  *
  * @param[in]  aInstance    A pointer to an OpenThread instance.
  * @param[in]  aEnabled     Indicates whether to enable or disable this functionality.
@@ -144,7 +183,7 @@
 void otChannelManagerSetAutoChannelSelectionEnabled(otInstance *aInstance, bool aEnabled);
 
 /**
- * Indicates whether the auto-channel-selection functionality is enabled or not.
+ * Indicates whether the auto-channel-selection functionality for a network channel is enabled or not.
  *
  * @param[in]  aInstance    A pointer to an OpenThread instance.
  *
@@ -154,6 +193,33 @@
 bool otChannelManagerGetAutoChannelSelectionEnabled(otInstance *aInstance);
 
 /**
+ * Enables or disables the auto-channel-selection functionality for a CSL channel.
+ *
+ * Only available with `OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE &&
+ * OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE`. When enabled, `ChannelManager` will periodically invoke
+ * a `otChannelManagerRequestCslChannelSelect()`. The period interval can be set by
+ * `otChannelManagerSetAutoChannelSelectionInterval()`.
+ *
+ * @param[in]  aInstance    A pointer to an OpenThread instance.
+ * @param[in]  aEnabled     Indicates whether to enable or disable this functionality.
+ *
+ */
+void otChannelManagerSetAutoCslChannelSelectionEnabled(otInstance *aInstance, bool aEnabled);
+
+/**
+ * Indicates whether the auto-csl-channel-selection functionality is enabled or not.
+ *
+ * Only available with `OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE &&
+ * OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE`.
+ *
+ * @param[in]  aInstance    A pointer to an OpenThread instance.
+ *
+ * @returns TRUE if enabled, FALSE if disabled.
+ *
+ */
+bool otChannelManagerGetAutoCslChannelSelectionEnabled(otInstance *aInstance);
+
+/**
  * Sets the period interval (in seconds) used by auto-channel-selection functionality.
  *
  * @param[in] aInstance   A pointer to an OpenThread instance.
diff --git a/include/openthread/coap.h b/include/openthread/coap.h
index 4b7149a..d5d0206 100644
--- a/include/openthread/coap.h
+++ b/include/openthread/coap.h
@@ -357,7 +357,7 @@
 typedef void (*otCoapRequestHandler)(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo);
 
 /**
- * Pointer is called when a CoAP message with an block-wise transfer option is received.
+ * Pointer is called when a CoAP message with a block-wise transfer option is received.
  *
  * Is available when OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE configuration
  * is enabled.
diff --git a/include/openthread/coap_secure.h b/include/openthread/coap_secure.h
index 6a96ea3..25b56bf 100644
--- a/include/openthread/coap_secure.h
+++ b/include/openthread/coap_secure.h
@@ -77,6 +77,15 @@
 typedef void (*otHandleCoapSecureClientConnect)(bool aConnected, void *aContext);
 
 /**
+ * Callback function pointer to notify when the CoAP secure agent is automatically stopped due to reaching the maximum
+ * number of connection attempts.
+ *
+ * @param[in] aContext    A pointer to arbitrary context information.
+ *
+ */
+typedef void (*otCoapSecureAutoStopCallback)(void *aContext);
+
+/**
  * Starts the CoAP Secure service.
  *
  * @param[in]  aInstance  A pointer to an OpenThread instance.
@@ -88,6 +97,26 @@
 otError otCoapSecureStart(otInstance *aInstance, uint16_t aPort);
 
 /**
+ * Starts the CoAP secure service and sets the maximum number of allowed connection attempts before stopping the
+ * agent automatically.
+ *
+ * @param[in] aInstance       A pointer to an OpenThread instance.
+ * @param[in] aPort           The local UDP port to bind to.
+ * @param[in] aMaxAttempts    Maximum number of allowed connection request attempts. Zero indicates no limit.
+ * @param[in] aCallback       Callback to notify if max number of attempts has reached and agent is stopped.
+ * @param[in] aContext        A pointer to arbitrary context to use with @p aCallback.
+ *
+ * @retval OT_ERROR_NONE        Successfully started the CoAP agent.
+ * @retval OT_ERROR_ALREADY     Already started.
+ *
+ */
+otError otCoapSecureStartWithMaxConnAttempts(otInstance                  *aInstance,
+                                             uint16_t                     aPort,
+                                             uint16_t                     aMaxAttempts,
+                                             otCoapSecureAutoStopCallback aCallback,
+                                             void                        *aContext);
+
+/**
  * Stops the CoAP Secure server.
  *
  * @param[in]  aInstance  A pointer to an OpenThread instance.
@@ -231,6 +260,17 @@
 bool otCoapSecureIsConnectionActive(otInstance *aInstance);
 
 /**
+ * Indicates whether or not the DTLS session is closed.
+ *
+ * @param[in]  aInstance  A pointer to an OpenThread instance.
+ *
+ * @retval TRUE   The DTLS session is closed.
+ * @retval FALSE  The DTLS session is not closed.
+ *
+ */
+bool otCoapSecureIsClosed(otInstance *aInstance);
+
+/**
  * Sends a CoAP request block-wise over secure DTLS connection.
  *
  * Is available when OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE configuration
diff --git a/include/openthread/dns_client.h b/include/openthread/dns_client.h
index 0fd3458..49dcdfd 100644
--- a/include/openthread/dns_client.h
+++ b/include/openthread/dns_client.h
@@ -408,7 +408,7 @@
  * MUST only be used from `otDnsBrowseCallback`.
  *
  * The response may include multiple service instance records. @p aIndex can be used to iterate through the list. Index
- * zero gives the the first record. When we reach end of the list, `OT_ERROR_NOT_FOUND` is returned.
+ * zero gives the first record. When we reach end of the list, `OT_ERROR_NOT_FOUND` is returned.
  *
  * Note that this function gets the service instance label and not the full service instance name which is of the form
  * `<Instance>.<Service>.<Domain>`.
diff --git a/include/openthread/dnssd_server.h b/include/openthread/dnssd_server.h
index 98f83d6..c008e32 100644
--- a/include/openthread/dnssd_server.h
+++ b/include/openthread/dnssd_server.h
@@ -148,19 +148,32 @@
 } otDnssdQueryType;
 
 /**
+ * Represents the count of queries, responses, failures handled by upstream DNS server.
+ *
+ * Requires `OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE`.
+ */
+typedef struct otUpstreamDnsCounters
+{
+    uint32_t mQueries;   ///< The number of queries forwarded.
+    uint32_t mResponses; ///< The number of responses forwarded.
+    uint32_t mFailures;  ///< The number of upstream DNS failures.
+} otUpstreamDnsCounters;
+
+/**
  * Contains the counters of DNS-SD server.
  *
  */
 typedef struct otDnssdCounters
 {
-    uint32_t mSuccessResponse;        ///< The number of successful responses
-    uint32_t mServerFailureResponse;  ///< The number of server failure responses
-    uint32_t mFormatErrorResponse;    ///< The number of format error responses
-    uint32_t mNameErrorResponse;      ///< The number of name error responses
-    uint32_t mNotImplementedResponse; ///< The number of 'not implemented' responses
-    uint32_t mOtherResponse;          ///< The number of other responses
-
-    uint32_t mResolvedBySrp; ///< The number of queries completely resolved by the local SRP server
+    uint32_t              mSuccessResponse;        ///< The number of successful responses.
+    uint32_t              mServerFailureResponse;  ///< The number of server failure responses.
+    uint32_t              mFormatErrorResponse;    ///< The number of format error responses.
+    uint32_t              mNameErrorResponse;      ///< The number of name error responses.
+    uint32_t              mNotImplementedResponse; ///< The number of 'not implemented' responses.
+    uint32_t              mOtherResponse;          ///< The number of other responses.
+    uint32_t              mResolvedBySrp;          ///< The number of queries resolved by the local SRP server.
+    otUpstreamDnsCounters mUpstreamDnsCounters;    ///< The number of queries, responses,
+                                                   ///< failures handled by upstream DNS server.
 } otDnssdCounters;
 
 /**
diff --git a/include/openthread/history_tracker.h b/include/openthread/history_tracker.h
index 08dc8d9..72a0c4a 100644
--- a/include/openthread/history_tracker.h
+++ b/include/openthread/history_tracker.h
@@ -426,8 +426,8 @@
 /**
  * Converts a given entry age to a human-readable string.
  *
- * The entry age string follows the format "<hh>:<mm>:<ss>.<mmmm>" for hours, minutes, seconds and millisecond (if
- * shorter than one day) or "<dd> days <hh>:<mm>:<ss>.<mmmm>" (if longer than one day).
+ * The entry age string follows the format `hours:minutes:seconds:milliseconds` (if
+ * shorter than one day) or `days:hours:minutes:seconds`(if longer than one day).
  *
  * If the resulting string does not fit in @p aBuffer (within its @p aSize characters), the string will be truncated
  * but the outputted string is always null-terminated.
diff --git a/include/openthread/instance.h b/include/openthread/instance.h
index b67b303..1ce766c 100644
--- a/include/openthread/instance.h
+++ b/include/openthread/instance.h
@@ -53,7 +53,7 @@
  * @note This number versions both OpenThread platform and user APIs.
  *
  */
-#define OPENTHREAD_API_VERSION (379)
+#define OPENTHREAD_API_VERSION (402)
 
 /**
  * @addtogroup api-instance
diff --git a/include/openthread/ip6.h b/include/openthread/ip6.h
index 850c64c..2636702 100644
--- a/include/openthread/ip6.h
+++ b/include/openthread/ip6.h
@@ -52,12 +52,13 @@
  *
  */
 
-#define OT_IP6_PREFIX_SIZE 8                           ///< Size of an IPv6 prefix (bytes)
-#define OT_IP6_PREFIX_BITSIZE (OT_IP6_PREFIX_SIZE * 8) ///< Size of an IPv6 prefix (bits)
-#define OT_IP6_IID_SIZE 8                              ///< Size of an IPv6 Interface Identifier (bytes)
-#define OT_IP6_ADDRESS_SIZE 16                         ///< Size of an IPv6 address (bytes)
-#define OT_IP6_HEADER_SIZE 40                          ///< Size of an IPv6 header (bytes)
-#define OT_IP6_HEADER_PROTO_OFFSET 6                   ///< Offset of the proto field in the IPv6 header (bytes)
+#define OT_IP6_PREFIX_SIZE 8                             ///< Size of an IPv6 prefix (bytes)
+#define OT_IP6_PREFIX_BITSIZE (OT_IP6_PREFIX_SIZE * 8)   ///< Size of an IPv6 prefix (bits)
+#define OT_IP6_IID_SIZE 8                                ///< Size of an IPv6 Interface Identifier (bytes)
+#define OT_IP6_ADDRESS_SIZE 16                           ///< Size of an IPv6 address (bytes)
+#define OT_IP6_ADDRESS_BITSIZE (OT_IP6_ADDRESS_SIZE * 8) ///< Size of an IPv6 address (bits)
+#define OT_IP6_HEADER_SIZE 40                            ///< Size of an IPv6 header (bytes)
+#define OT_IP6_HEADER_PROTO_OFFSET 6                     ///< Offset of the proto field in the IPv6 header (bytes)
 
 /**
  * @struct otIp6InterfaceIdentifier
@@ -189,6 +190,7 @@
     unsigned int mScopeOverride : 4;      ///< The IPv6 scope of this address.
     bool         mRloc : 1;               ///< TRUE if the address is an RLOC, FALSE otherwise.
     bool         mMeshLocal : 1;          ///< TRUE if the address is mesh-local, FALSE otherwise.
+    bool         mSrpRegistered : 1;      ///< Used by OT core only (indicates whether registered by SRP Client).
     const struct otNetifAddress *mNext;   ///< A pointer to the next network interface address.
 } otNetifAddress;
 
@@ -234,7 +236,6 @@
     otIp6Address mPeerAddr; ///< The peer IPv6 address.
     uint16_t     mSockPort; ///< The local transport-layer port.
     uint16_t     mPeerPort; ///< The peer transport-layer port.
-    const void  *mLinkInfo; ///< A pointer to link-specific information.
     uint8_t      mHopLimit; ///< The IPv6 Hop Limit value. Only applies if `mAllowZeroHopLimit` is FALSE.
                             ///< If `0`, IPv6 Hop Limit is default value `OPENTHREAD_CONFIG_IP6_HOP_LIMIT_DEFAULT`.
                             ///< Otherwise, specifies the IPv6 Hop Limit.
@@ -477,6 +478,7 @@
     uint8_t             mPrefixLength;  ///< The prefix length of mAddress if it is a unicast address.
     uint8_t             mScope : 4;     ///< The scope of this address.
     bool                mPreferred : 1; ///< Whether this is a preferred address.
+    bool                mMeshLocal : 1; ///< Whether this is a mesh-local unicast/anycast address.
 } otIp6AddressInfo;
 
 /**
@@ -906,6 +908,8 @@
     otPacketsAndBytes mInboundMulticast;  ///< The counters for inbound multicast.
     otPacketsAndBytes mOutboundUnicast;   ///< The counters for outbound unicast.
     otPacketsAndBytes mOutboundMulticast; ///< The counters for outbound multicast.
+    otPacketsAndBytes mInboundInternet;   ///< The counters for inbound Internet when DHCPv6 PD enabled.
+    otPacketsAndBytes mOutboundInternet;  ///< The counters for outbound Internet when DHCPv6 PD enabled.
     uint32_t          mRaRx;              ///< The number of received RA packets.
     uint32_t          mRaTxSuccess;       ///< The number of RA packets successfully transmitted.
     uint32_t          mRaTxFailure;       ///< The number of RA packets failed to transmit.
diff --git a/include/openthread/link.h b/include/openthread/link.h
index f4d203e..15bc0d8 100644
--- a/include/openthread/link.h
+++ b/include/openthread/link.h
@@ -55,27 +55,6 @@
 #define OT_US_PER_TEN_SYMBOLS OT_RADIO_TEN_SYMBOLS_TIME ///< Time for 10 symbols in units of microseconds
 
 /**
- * Represents link-specific information for messages received from the Thread radio.
- *
- */
-typedef struct otThreadLinkInfo
-{
-    uint16_t mPanId;                   ///< Source PAN ID
-    uint8_t  mChannel;                 ///< 802.15.4 Channel
-    int8_t   mRss;                     ///< Received Signal Strength in dBm.
-    uint8_t  mLqi;                     ///< Link Quality Indicator for a received message.
-    bool     mLinkSecurity : 1;        ///< Indicates whether or not link security is enabled.
-    bool     mIsDstPanIdBroadcast : 1; ///< Indicates whether or not destination PAN ID is broadcast.
-
-    // Applicable/Required only when time sync feature (`OPENTHREAD_CONFIG_TIME_SYNC_ENABLE`) is enabled.
-    uint8_t mTimeSyncSeq;       ///< The time sync sequence.
-    int64_t mNetworkTimeOffset; ///< The time offset to the Thread network time, in microseconds.
-
-    // Applicable only when OPENTHREAD_CONFIG_MULTI_RADIO feature is enabled.
-    uint8_t mRadioType; ///< Radio link type.
-} otThreadLinkInfo;
-
-/**
  * Used to indicate no fixed received signal strength was set
  *
  */
diff --git a/include/openthread/message.h b/include/openthread/message.h
index 3c2938c..39e14d4 100644
--- a/include/openthread/message.h
+++ b/include/openthread/message.h
@@ -91,6 +91,27 @@
 } otMessageSettings;
 
 /**
+ * Represents link-specific information for messages received from the Thread radio.
+ *
+ */
+typedef struct otThreadLinkInfo
+{
+    uint16_t mPanId;                   ///< Source PAN ID
+    uint8_t  mChannel;                 ///< 802.15.4 Channel
+    int8_t   mRss;                     ///< Received Signal Strength in dBm (averaged over fragments)
+    uint8_t  mLqi;                     ///< Average Link Quality Indicator (averaged over fragments)
+    bool     mLinkSecurity : 1;        ///< Indicates whether or not link security is enabled.
+    bool     mIsDstPanIdBroadcast : 1; ///< Indicates whether or not destination PAN ID is broadcast.
+
+    // Applicable/Required only when time sync feature (`OPENTHREAD_CONFIG_TIME_SYNC_ENABLE`) is enabled.
+    uint8_t mTimeSyncSeq;       ///< The time sync sequence.
+    int64_t mNetworkTimeOffset; ///< The time offset to the Thread network time, in microseconds.
+
+    // Applicable only when OPENTHREAD_CONFIG_MULTI_RADIO feature is enabled.
+    uint8_t mRadioType; ///< Radio link type.
+} otThreadLinkInfo;
+
+/**
  * Free an allocated message buffer.
  *
  * @param[in]  aMessage  A pointer to a message buffer.
@@ -266,12 +287,26 @@
 /**
  * Returns the average RSS (received signal strength) associated with the message.
  *
+ * @param[in]  aMessage  A pointer to a message buffer.
+ *
  * @returns The average RSS value (in dBm) or OT_RADIO_RSSI_INVALID if no average RSS is available.
  *
  */
 int8_t otMessageGetRss(const otMessage *aMessage);
 
 /**
+ * Retrieves the link-specific information for a message received over Thread radio.
+ *
+ * @param[in] aMessage    The message from which to retrieve `otThreadLinkInfo`.
+ * @pram[out] aLinkInfo   A pointer to an `otThreadLinkInfo` to populate.
+ *
+ * @retval OT_ERROR_NONE       Successfully retrieved the link info, @p `aLinkInfo` is updated.
+ * @retval OT_ERROR_NOT_FOUND  Message origin is not `OT_MESSAGE_ORIGIN_THREAD_NETIF`.
+ *
+ */
+otError otMessageGetThreadLinkInfo(const otMessage *aMessage, otThreadLinkInfo *aLinkInfo);
+
+/**
  * Append bytes to a message.
  *
  * @param[in]  aMessage  A pointer to a message buffer.
diff --git a/include/openthread/nat64.h b/include/openthread/nat64.h
index 930c4c3..61fe1b6 100644
--- a/include/openthread/nat64.h
+++ b/include/openthread/nat64.h
@@ -416,6 +416,30 @@
  */
 void otIp4ExtractFromIp6Address(uint8_t aPrefixLength, const otIp6Address *aIp6Address, otIp4Address *aIp4Address);
 
+/**
+ * Extracts the IPv4 address from a given IPv4-mapped IPv6 address.
+ *
+ * An IPv4-mapped IPv6 address consists of an 80-bit prefix of zeros, the next 16 bits set to ones, and the remaining,
+ * least-significant 32 bits contain the IPv4 address, e.g., `::ffff:192.0.2.128` representing `192.0.2.128`.
+ *
+ * @param[in]  aIp6Address  An IPv6 address to extract IPv4 from.
+ * @param[out] aIp4Address  An IPv4 address to output the extracted address.
+ *
+ * @retval OT_ERROR_NONE   Extracted the IPv4 address successfully. @p aIp4Address is updated.
+ * @retval OT_ERROR_PARSE  The @p aIp6Address does not follow the IPv4-mapped IPv6 address format.
+ *
+ */
+otError otIp4FromIp4MappedIp6Address(const otIp6Address *aIp6Address, otIp4Address *aIp4Address);
+
+/**
+ * Converts a given IP4 address to an IPv6 address following the IPv4-mapped IPv6 address format.
+ *
+ * @param[in]  aIp4Address  An IPv4 address to convert.
+ * @param[out] aIp6Address  An IPv6 address to set.
+ *
+ */
+void otIp4ToIp4MappedIp6Address(const otIp4Address *aIp4Address, otIp6Address *aIp6Address);
+
 #define OT_IP4_ADDRESS_STRING_SIZE 17 ///< Length of 000.000.000.000 plus a suffix NUL
 
 /**
diff --git a/include/openthread/netdiag.h b/include/openthread/netdiag.h
index 040ad75..ff99b38 100644
--- a/include/openthread/netdiag.h
+++ b/include/openthread/netdiag.h
@@ -49,57 +49,43 @@
  *
  */
 
-/**
- * Maximum Number of Network Diagnostic TLV Types to Request or Reset.
- */
-#define OT_NETWORK_DIAGNOSTIC_TYPELIST_MAX_ENTRIES 19
-
-/**
- * Size of Network Diagnostic Child Table entry.
- */
-#define OT_NETWORK_DIAGNOSTIC_CHILD_TABLE_ENTRY_SIZE 3
-
-/**
- * Initializer for otNetworkDiagIterator.
- */
-#define OT_NETWORK_DIAGNOSTIC_ITERATOR_INIT 0
-
-enum
-{
-    OT_NETWORK_DIAGNOSTIC_TLV_EXT_ADDRESS          = 0,  ///< MAC Extended Address TLV
-    OT_NETWORK_DIAGNOSTIC_TLV_SHORT_ADDRESS        = 1,  ///< Address16 TLV
-    OT_NETWORK_DIAGNOSTIC_TLV_MODE                 = 2,  ///< Mode TLV
-    OT_NETWORK_DIAGNOSTIC_TLV_TIMEOUT              = 3,  ///< Timeout TLV (the maximum polling time period for SEDs)
-    OT_NETWORK_DIAGNOSTIC_TLV_CONNECTIVITY         = 4,  ///< Connectivity TLV
-    OT_NETWORK_DIAGNOSTIC_TLV_ROUTE                = 5,  ///< Route64 TLV
-    OT_NETWORK_DIAGNOSTIC_TLV_LEADER_DATA          = 6,  ///< Leader Data TLV
-    OT_NETWORK_DIAGNOSTIC_TLV_NETWORK_DATA         = 7,  ///< Network Data TLV
-    OT_NETWORK_DIAGNOSTIC_TLV_IP6_ADDR_LIST        = 8,  ///< IPv6 Address List TLV
-    OT_NETWORK_DIAGNOSTIC_TLV_MAC_COUNTERS         = 9,  ///< MAC Counters TLV
-    OT_NETWORK_DIAGNOSTIC_TLV_BATTERY_LEVEL        = 14, ///< Battery Level TLV
-    OT_NETWORK_DIAGNOSTIC_TLV_SUPPLY_VOLTAGE       = 15, ///< Supply Voltage TLV
-    OT_NETWORK_DIAGNOSTIC_TLV_CHILD_TABLE          = 16, ///< Child Table TLV
-    OT_NETWORK_DIAGNOSTIC_TLV_CHANNEL_PAGES        = 17, ///< Channel Pages TLV
-    OT_NETWORK_DIAGNOSTIC_TLV_TYPE_LIST            = 18, ///< Type List TLV
-    OT_NETWORK_DIAGNOSTIC_TLV_MAX_CHILD_TIMEOUT    = 19, ///< Max Child Timeout TLV
-    OT_NETWORK_DIAGNOSTIC_TLV_VERSION              = 24, ///< Version TLV
-    OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_NAME          = 25, ///< Vendor Name TLV
-    OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_MODEL         = 26, ///< Vendor Model TLV
-    OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_SW_VERSION    = 27, ///< Vendor SW Version TLV
-    OT_NETWORK_DIAGNOSTIC_TLV_THREAD_STACK_VERSION = 28, ///< Thread Stack Version TLV
-    OT_NETWORK_DIAGNOSTIC_TLV_CHILD                = 29, ///< Child TLV
-    OT_NETWORK_DIAGNOSTIC_TLV_CHILD_IP6_ADDR_LIST  = 30, ///< Child IPv6 Address List TLV
-    OT_NETWORK_DIAGNOSTIC_TLV_ROUTER_NEIGHBOR      = 31, ///< Router Neighbor TLV
-    OT_NETWORK_DIAGNOSTIC_TLV_ANSWER               = 32, ///< Answer TLV
-    OT_NETWORK_DIAGNOSTIC_TLV_QUERY_ID             = 33, ///< Query ID TLV
-    OT_NETWORK_DIAGNOSTIC_TLV_MLE_COUNTERS         = 34, ///< MLE Counters TLV
-
-};
+#define OT_NETWORK_DIAGNOSTIC_TLV_EXT_ADDRESS 0           ///< MAC Extended Address TLV
+#define OT_NETWORK_DIAGNOSTIC_TLV_SHORT_ADDRESS 1         ///< Address16 TLV
+#define OT_NETWORK_DIAGNOSTIC_TLV_MODE 2                  ///< Mode TLV
+#define OT_NETWORK_DIAGNOSTIC_TLV_TIMEOUT 3               ///< Timeout TLV (max polling time period for SEDs)
+#define OT_NETWORK_DIAGNOSTIC_TLV_CONNECTIVITY 4          ///< Connectivity TLV
+#define OT_NETWORK_DIAGNOSTIC_TLV_ROUTE 5                 ///< Route64 TLV
+#define OT_NETWORK_DIAGNOSTIC_TLV_LEADER_DATA 6           ///< Leader Data TLV
+#define OT_NETWORK_DIAGNOSTIC_TLV_NETWORK_DATA 7          ///< Network Data TLV
+#define OT_NETWORK_DIAGNOSTIC_TLV_IP6_ADDR_LIST 8         ///< IPv6 Address List TLV
+#define OT_NETWORK_DIAGNOSTIC_TLV_MAC_COUNTERS 9          ///< MAC Counters TLV
+#define OT_NETWORK_DIAGNOSTIC_TLV_BATTERY_LEVEL 14        ///< Battery Level TLV
+#define OT_NETWORK_DIAGNOSTIC_TLV_SUPPLY_VOLTAGE 15       ///< Supply Voltage TLV
+#define OT_NETWORK_DIAGNOSTIC_TLV_CHILD_TABLE 16          ///< Child Table TLV
+#define OT_NETWORK_DIAGNOSTIC_TLV_CHANNEL_PAGES 17        ///< Channel Pages TLV
+#define OT_NETWORK_DIAGNOSTIC_TLV_TYPE_LIST 18            ///< Type List TLV
+#define OT_NETWORK_DIAGNOSTIC_TLV_MAX_CHILD_TIMEOUT 19    ///< Max Child Timeout TLV
+#define OT_NETWORK_DIAGNOSTIC_TLV_EUI64 23                ///< EUI64 TLV
+#define OT_NETWORK_DIAGNOSTIC_TLV_VERSION 24              ///< Thread Version TLV
+#define OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_NAME 25          ///< Vendor Name TLV
+#define OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_MODEL 26         ///< Vendor Model TLV
+#define OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_SW_VERSION 27    ///< Vendor SW Version TLV
+#define OT_NETWORK_DIAGNOSTIC_TLV_THREAD_STACK_VERSION 28 ///< Thread Stack Version TLV (codebase/commit version)
+#define OT_NETWORK_DIAGNOSTIC_TLV_CHILD 29                ///< Child TLV
+#define OT_NETWORK_DIAGNOSTIC_TLV_CHILD_IP6_ADDR_LIST 30  ///< Child IPv6 Address List TLV
+#define OT_NETWORK_DIAGNOSTIC_TLV_ROUTER_NEIGHBOR 31      ///< Router Neighbor TLV
+#define OT_NETWORK_DIAGNOSTIC_TLV_ANSWER 32               ///< Answer TLV
+#define OT_NETWORK_DIAGNOSTIC_TLV_QUERY_ID 33             ///< Query ID TLV
+#define OT_NETWORK_DIAGNOSTIC_TLV_MLE_COUNTERS 34         ///< MLE Counters TLV
+#define OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_APP_URL 35       ///< Vendor App URL TLV
 
 #define OT_NETWORK_DIAGNOSTIC_MAX_VENDOR_NAME_TLV_LENGTH 32          ///< Max length of Vendor Name TLV.
 #define OT_NETWORK_DIAGNOSTIC_MAX_VENDOR_MODEL_TLV_LENGTH 32         ///< Max length of Vendor Model TLV.
 #define OT_NETWORK_DIAGNOSTIC_MAX_VENDOR_SW_VERSION_TLV_LENGTH 16    ///< Max length of Vendor SW Version TLV.
 #define OT_NETWORK_DIAGNOSTIC_MAX_THREAD_STACK_VERSION_TLV_LENGTH 64 ///< Max length of Thread Stack Version TLV.
+#define OT_NETWORK_DIAGNOSTIC_MAX_VENDOR_APP_URL_TLV_LENGTH 96       ///< Max length of Vendor App URL TLV.
+
+#define OT_NETWORK_DIAGNOSTIC_ITERATOR_INIT 0 ///<  Initializer for `otNetworkDiagIterator`.
 
 typedef uint16_t otNetworkDiagIterator; ///< Used to iterate through Network Diagnostic TLV.
 
@@ -109,50 +95,15 @@
  */
 typedef struct otNetworkDiagConnectivity
 {
-    /**
-     * The priority of the sender as a parent.
-     */
-    int8_t mParentPriority;
-
-    /**
-     * The number of neighboring devices with which the sender shares a link of quality 3.
-     */
-    uint8_t mLinkQuality3;
-
-    /**
-     * The number of neighboring devices with which the sender shares a link of quality 2.
-     */
-    uint8_t mLinkQuality2;
-
-    /**
-     * The number of neighboring devices with which the sender shares a link of quality 1.
-     */
-    uint8_t mLinkQuality1;
-
-    /**
-     * The sender's routing cost to the Leader.
-     */
-    uint8_t mLeaderCost;
-
-    /**
-     * The most recent ID sequence number received by the sender.
-     */
-    uint8_t mIdSequence;
-
-    /**
-     * The number of active Routers in the sender's Thread Network Partition.
-     */
-    uint8_t mActiveRouters;
-
-    /**
-     * The guaranteed buffer capacity in octets for all IPv6 datagrams destined to a given SED. Optional.
-     */
-    uint16_t mSedBufferSize;
-
-    /**
-     * The guaranteed queue capacity in number of IPv6 datagrams destined to a given SED. Optional.
-     */
-    uint8_t mSedDatagramCount;
+    int8_t   mParentPriority;   ///< The priority of the sender as a parent.
+    uint8_t  mLinkQuality3;     ///< Number of neighbors with link of quality 3.
+    uint8_t  mLinkQuality2;     ///< Number of neighbors with link of quality 2.
+    uint8_t  mLinkQuality1;     ///< Number of neighbors with link of quality 1.
+    uint8_t  mLeaderCost;       ///< Cost to the Leader.
+    uint8_t  mIdSequence;       ///< Most recent received ID seq number.
+    uint8_t  mActiveRouters;    ///< Number of active routers.
+    uint16_t mSedBufferSize;    ///< Buffer capacity in bytes for SEDs. Optional.
+    uint8_t  mSedDatagramCount; ///< Queue capacity (number of IPv6 datagrams) per SED. Optional.
 } otNetworkDiagConnectivity;
 
 /**
@@ -168,7 +119,7 @@
 } otNetworkDiagRouteData;
 
 /**
- * Represents a Network Diagnostic Route TLV value.
+ * Represents a Network Diagnostic Route64 TLV value.
  *
  */
 typedef struct otNetworkDiagRoute
@@ -176,17 +127,9 @@
     /**
      * The sequence number associated with the set of Router ID assignments in #mRouteData.
      */
-    uint8_t mIdSequence;
-
-    /**
-     * Number of elements in #mRouteData.
-     */
-    uint8_t mRouteCount;
-
-    /**
-     * Link Quality and Routing Cost data.
-     */
-    otNetworkDiagRouteData mRouteData[OT_NETWORK_MAX_ROUTER_ID + 1];
+    uint8_t                mIdSequence;                              ///< Sequence number for Router ID assignments.
+    uint8_t                mRouteCount;                              ///< Number of routes.
+    otNetworkDiagRouteData mRouteData[OT_NETWORK_MAX_ROUTER_ID + 1]; ///< Link Quality and Routing Cost data.
 } otNetworkDiagRoute;
 
 /**
@@ -237,28 +180,10 @@
  */
 typedef struct otNetworkDiagChildEntry
 {
-    /**
-     * Expected poll time expressed as 2^(Timeout-4) seconds.
-     */
-    uint16_t mTimeout : 5;
-
-    /**
-     * Link Quality In value in [0,3].
-     *
-     * Value 0 indicates that sender does not support the feature to provide link quality info.
-     *
-     */
-    uint8_t mLinkQuality : 2;
-
-    /**
-     * Child ID from which an RLOC can be generated.
-     */
-    uint16_t mChildId : 9;
-
-    /**
-     * Link mode bits.
-     */
-    otLinkModeConfig mMode;
+    uint16_t mTimeout : 5;     ///< Expected poll timeout expressed as 2^(Timeout-4) seconds.
+    uint8_t  mLinkQuality : 2; ///< Link Quality In value [0,3]. Zero indicate sender cannot provide link quality info.
+    uint16_t mChildId : 9;     ///< Child ID (derived from child RLOC)
+    otLinkModeConfig mMode;    ///< Link mode.
 } otNetworkDiagChildEntry;
 
 /**
@@ -267,14 +192,12 @@
  */
 typedef struct otNetworkDiagTlv
 {
-    /**
-     * The Network Diagnostic TLV type.
-     */
-    uint8_t mType;
+    uint8_t mType; ///< The Network Diagnostic TLV type.
 
     union
     {
         otExtAddress              mExtAddress;
+        otExtAddress              mEui64;
         uint16_t                  mAddr16;
         otLinkModeConfig          mMode;
         uint32_t                  mTimeout;
@@ -291,6 +214,7 @@
         char                      mVendorModel[OT_NETWORK_DIAGNOSTIC_MAX_VENDOR_MODEL_TLV_LENGTH + 1];
         char                      mVendorSwVersion[OT_NETWORK_DIAGNOSTIC_MAX_VENDOR_SW_VERSION_TLV_LENGTH + 1];
         char                      mThreadStackVersion[OT_NETWORK_DIAGNOSTIC_MAX_THREAD_STACK_VERSION_TLV_LENGTH + 1];
+        char                      mVendorAppUrl[OT_NETWORK_DIAGNOSTIC_MAX_VENDOR_APP_URL_TLV_LENGTH + 1];
         struct
         {
             uint8_t mCount;
@@ -299,13 +223,12 @@
         struct
         {
             uint8_t      mCount;
-            otIp6Address mList[OT_NETWORK_BASE_TLV_MAX_LENGTH / OT_IP6_ADDRESS_SIZE];
+            otIp6Address mList[OT_NETWORK_BASE_TLV_MAX_LENGTH / sizeof(otIp6Address)];
         } mIp6AddrList;
         struct
         {
-            uint8_t mCount;
-            otNetworkDiagChildEntry
-                mTable[OT_NETWORK_BASE_TLV_MAX_LENGTH / OT_NETWORK_DIAGNOSTIC_CHILD_TABLE_ENTRY_SIZE];
+            uint8_t                 mCount;
+            otNetworkDiagChildEntry mTable[OT_NETWORK_BASE_TLV_MAX_LENGTH / sizeof(otNetworkDiagChildEntry)];
         } mChildTable;
         struct
         {
@@ -416,16 +339,26 @@
 const char *otThreadGetVendorModel(otInstance *aInstance);
 
 /**
- * Get the vendor sw version string.
+ * Get the vendor software version string.
  *
  * @param[in]  aInstance      A pointer to an OpenThread instance.
  *
- * @returns The vendor sw version string.
+ * @returns The vendor software version string.
  *
  */
 const char *otThreadGetVendorSwVersion(otInstance *aInstance);
 
 /**
+ * Get the vendor app URL string.
+ *
+ * @param[in]  aInstance      A pointer to an OpenThread instance.
+ *
+ * @returns The vendor app URL string.
+ *
+ */
+const char *otThreadGetVendorAppUrl(otInstance *aInstance);
+
+/**
  * Set the vendor name string.
  *
  * Requires `OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE`.
@@ -477,6 +410,23 @@
 otError otThreadSetVendorSwVersion(otInstance *aInstance, const char *aVendorSwVersion);
 
 /**
+ * Set the vendor app URL string.
+ *
+ * Requires `OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE`.
+ *
+ * @p aVendorAppUrl should be UTF8 with max length of 64 chars (`MAX_VENDOR_APPL_URL_TLV_LENGTH`). Maximum length
+ * does not include the null `\0` character.
+ *
+ * @param[in] aInstance          A pointer to an OpenThread instance.
+ * @param[in] aVendorAppUrl      The vendor app URL string.
+ *
+ * @retval OT_ERROR_NONE          Successfully set the vendor app URL string.
+ * @retval OT_ERROR_INVALID_ARGS  @p aVendorAppUrl is not valid (too long or not UTF8).
+ *
+ */
+otError otThreadSetVendorAppUrl(otInstance *aInstance, const char *aVendorAppUrl);
+
+/**
  * @}
  *
  */
diff --git a/include/openthread/platform/ble.h b/include/openthread/platform/ble.h
new file mode 100644
index 0000000..bf7e48b
--- /dev/null
+++ b/include/openthread/platform/ble.h
@@ -0,0 +1,293 @@
+/*
+ *  Copyright (c) 2023, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * @brief
+ *   This file defines a OpenThread BLE GATT peripheral interface driver.
+ *
+ */
+
+#ifndef OPENTHREAD_PLATFORM_BLE_H_
+#define OPENTHREAD_PLATFORM_BLE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+#include <openthread/error.h>
+#include <openthread/instance.h>
+
+/**
+ * @addtogroup plat-ble
+ *
+ * @brief
+ *   This module includes the platform abstraction for BLE Host communication.
+ *   The platform needs to implement Bluetooth LE 4.2 or higher.
+ *
+ * @{
+ *
+ */
+
+/**
+ * Time slot duration on PHY layer in microseconds (0.625ms).
+ *
+ */
+
+#define OT_BLE_TIMESLOT_UNIT 625
+
+/**
+ * Minimum allowed interval for advertising packet in OT_BLE_ADV_INTERVAL_UNIT units (20ms).
+ *
+ */
+
+#define OT_BLE_ADV_INTERVAL_MIN 0x0020
+
+/**
+ * Maximum allowed interval for advertising packet in OT_BLE_ADV_INTERVAL_UNIT units (10.24s).
+ *
+ */
+
+#define OT_BLE_ADV_INTERVAL_MAX 0x4000
+
+/**
+ * Default interval for advertising packet (ms).
+ *
+ */
+
+#define OT_BLE_ADV_INTERVAL_DEFAULT 100
+
+/**
+ * Unit used to calculate interval duration (0.625ms).
+ *
+ */
+
+#define OT_BLE_ADV_INTERVAL_UNIT OT_BLE_TIMESLOT_UNIT
+
+/**
+ * Maximum allowed ATT MTU size (must be >= 23).
+ *
+ */
+
+#define OT_BLE_ATT_MTU_MAX 67
+
+/**
+ * Default power value for BLE.
+ */
+
+#define OT_BLE_DEFAULT_POWER 0
+
+/**
+ * Represents a BLE packet.
+ *
+ */
+typedef struct otBleRadioPacket
+{
+    uint8_t *mValue;  ///< The value of an attribute
+    uint16_t mLength; ///< Length of the @p mValue.
+    int8_t   mPower;  ///< Transmit/receive power in dBm.
+} otBleRadioPacket;
+
+/*******************************************************************************
+ * @section Bluetooth Low Energy management.
+ ******************************************************************************/
+
+/**
+ * Enable the Bluetooth Low Energy radio.
+ *
+ * @note BLE Device should use the highest ATT_MTU supported that does not
+ * exceed OT_BLE_ATT_MTU_MAX octets.
+ *
+ * @param[in] aInstance  The OpenThread instance structure.
+ *
+ * @retval OT_ERROR_NONE         Successfully enabled.
+ * @retval OT_ERROR_FAILED       The BLE radio could not be enabled.
+ */
+otError otPlatBleEnable(otInstance *aInstance);
+
+/**
+ * Disable the Bluetooth Low Energy radio.
+ *
+ * When disabled, the BLE stack will flush event queues and not generate new
+ * events. The BLE peripheral is turned off or put into a low power sleep
+ * state. Any dynamic memory used by the stack should be released,
+ * but static memory may remain reserved.
+ *
+ * @param[in] aInstance  The OpenThread instance structure.
+ *
+ * @retval OT_ERROR_NONE        Successfully transitioned to disabled.
+ * @retval OT_ERROR_FAILED      The BLE radio could not be disabled.
+ *
+ */
+otError otPlatBleDisable(otInstance *aInstance);
+
+/****************************************************************************
+ * @section Bluetooth Low Energy GAP.
+ ***************************************************************************/
+
+/**
+ * Starts BLE Advertising procedure.
+ *
+ * The BLE device shall use undirected advertising with no filter applied.
+ * A single BLE Advertising packet must be sent on all advertising
+ * channels (37, 38 and 39).
+ *
+ * @note This function shall be used only for BLE Peripheral role.
+ *
+ * @param[in] aInstance  The OpenThread instance structure.
+ * @param[in] aInterval  The interval between subsequent advertising packets
+ *                       in OT_BLE_ADV_INTERVAL_UNIT units.
+ *                       Shall be within OT_BLE_ADV_INTERVAL_MIN and
+ *                       OT_BLE_ADV_INTERVAL_MAX range or OT_BLE_ADV_INTERVAL_DEFAULT
+ *                       for a default value set at compile time.
+ *
+ * @retval OT_ERROR_NONE           Advertising procedure has been started.
+ * @retval OT_ERROR_INVALID_STATE  BLE Device is in invalid state.
+ * @retval OT_ERROR_INVALID_ARGS   Invalid interval value has been supplied.
+ *
+ */
+otError otPlatBleGapAdvStart(otInstance *aInstance, uint16_t aInterval);
+
+/**
+ * Stops BLE Advertising procedure.
+ *
+ * @note This function shall be used only for BLE Peripheral role.
+ *
+ * @param[in] aInstance  The OpenThread instance structure.
+ *
+ * @retval OT_ERROR_NONE           Advertising procedure has been stopped.
+ * @retval OT_ERROR_INVALID_STATE  BLE Device is in invalid state.
+ *
+ */
+otError otPlatBleGapAdvStop(otInstance *aInstance);
+
+/**
+ * The BLE driver calls this method to notify OpenThread that a BLE Central Device has
+ * been connected.
+ *
+ * @param[in]  aInstance     The OpenThread instance structure.
+ * @param[in]  aConnectionId The identifier of the open connection.
+ *
+ */
+extern void otPlatBleGapOnConnected(otInstance *aInstance, uint16_t aConnectionId);
+
+/**
+ * The BLE driver calls this method to notify OpenThread that the BLE Central Device
+ * has been disconnected.
+ *
+ * @param[in]  aInstance     The OpenThread instance structure.
+ * @param[in]  aConnectionId The identifier of the closed connection.
+ *
+ */
+extern void otPlatBleGapOnDisconnected(otInstance *aInstance, uint16_t aConnectionId);
+
+/**
+ * Disconnects BLE connection.
+ *
+ * The BLE device shall use the Remote User Terminated Connection (0x13) reason
+ * code when disconnecting from the peer BLE device..
+ *
+ * @param[in] aInstance  The OpenThread instance structure.
+ *
+ * @retval OT_ERROR_NONE           Disconnection procedure has been started.
+ * @retval OT_ERROR_INVALID_STATE  BLE Device is in invalid state.
+ *
+ */
+otError otPlatBleGapDisconnect(otInstance *aInstance);
+
+/*******************************************************************************
+ * @section Bluetooth Low Energy GATT Common.
+ *******************************************************************************/
+
+/**
+ * Reads currently use value of ATT_MTU.
+ *
+ * @param[in]   aInstance  The OpenThread instance structure.
+ * @param[out]  aMtu       A pointer to output the current ATT_MTU value.
+ *
+ * @retval OT_ERROR_NONE     ATT_MTU value has been placed in @p aMtu.
+ * @retval OT_ERROR_FAILED   BLE Device cannot determine its ATT_MTU.
+ *
+ */
+otError otPlatBleGattMtuGet(otInstance *aInstance, uint16_t *aMtu);
+
+/**
+ * The BLE driver calls this method to notify OpenThread that ATT_MTU has been updated.
+ *
+ * @param[in]  aInstance     The OpenThread instance structure.
+ * @param[in]  aMtu          The updated ATT_MTU value.
+ *
+ */
+extern void otPlatBleGattOnMtuUpdate(otInstance *aInstance, uint16_t aMtu);
+
+/*******************************************************************************
+ * @section Bluetooth Low Energy GATT Server.
+ ******************************************************************************/
+
+/**
+ * Sends ATT Handle Value Indication.
+ *
+ * @note This function shall be used only for GATT Server.
+ *
+ * @param[in] aInstance   The OpenThread instance structure.
+ * @param[in] aHandle     The handle of the attribute to be indicated.
+ * @param[in] aPacket     A pointer to the packet contains value to be indicated.
+ *
+ * @retval OT_ERROR_NONE           ATT Handle Value Indication has been sent.
+ * @retval OT_ERROR_INVALID_STATE  BLE Device is in invalid state.
+ * @retval OT_ERROR_INVALID_ARGS   Invalid handle value, data or data length has been supplied.
+ * @retval OT_ERROR_NO_BUFS        No available internal buffer found.
+ *
+ */
+otError otPlatBleGattServerIndicate(otInstance *aInstance, uint16_t aHandle, const otBleRadioPacket *aPacket);
+
+/**
+ * The BLE driver calls this method to notify OpenThread that an ATT Write Request
+ * packet has been received.
+ *
+ * @note This function shall be used only for GATT Server.
+ *
+ * @param[in] aInstance   The OpenThread instance structure.
+ * @param[in] aHandle     The handle of the attribute to be written.
+ * @param[in] aPacket     A pointer to the packet contains value to be written to the attribute.
+ *
+ */
+extern void otPlatBleGattServerOnWriteRequest(otInstance *aInstance, uint16_t aHandle, const otBleRadioPacket *aPacket);
+
+/**
+ * @}
+ *
+ */
+
+#ifdef __cplusplus
+} // end of extern "C"
+#endif
+
+#endif // OPENTHREAD_PLATFORM_BLE_H_
diff --git a/include/openthread/platform/crypto.h b/include/openthread/platform/crypto.h
index 8762fc2..fe3c0ea 100644
--- a/include/openthread/platform/crypto.h
+++ b/include/openthread/platform/crypto.h
@@ -743,14 +743,18 @@
  * @param[in]     aKeyLen            Length of generated key in bytes.
  * @param[out]    aKey               A pointer to the generated key.
  *
+ * @retval OT_ERROR_NONE          A new key-pair was generated successfully.
+ * @retval OT_ERROR_NO_BUFS       Failed to allocate buffer for key generation.
+ * @retval OT_ERROR_NOT_CAPABLE   Feature not supported.
+ * @retval OT_ERROR_FAILED        Failed to generate key.
  */
-void otPlatCryptoPbkdf2GenerateKey(const uint8_t *aPassword,
-                                   uint16_t       aPasswordLen,
-                                   const uint8_t *aSalt,
-                                   uint16_t       aSaltLen,
-                                   uint32_t       aIterationCounter,
-                                   uint16_t       aKeyLen,
-                                   uint8_t       *aKey);
+otError otPlatCryptoPbkdf2GenerateKey(const uint8_t *aPassword,
+                                      uint16_t       aPasswordLen,
+                                      const uint8_t *aSalt,
+                                      uint16_t       aSaltLen,
+                                      uint32_t       aIterationCounter,
+                                      uint16_t       aKeyLen,
+                                      uint8_t       *aKey);
 
 /**
  * @}
diff --git a/include/openthread/platform/dnssd.h b/include/openthread/platform/dnssd.h
new file mode 100644
index 0000000..e7a2a7f
--- /dev/null
+++ b/include/openthread/platform/dnssd.h
@@ -0,0 +1,427 @@
+/*
+ *  Copyright (c) 2023, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * @brief
+ *   This file includes the platform abstraction for DNS-SD (e.g., mDNS) on the infrastructure network.
+ *
+ */
+
+#ifndef OPENTHREAD_PLATFORM_DNSSD_H_
+#define OPENTHREAD_PLATFORM_DNSSD_H_
+
+#include <stdint.h>
+
+#include <openthread/dns.h>
+#include <openthread/error.h>
+#include <openthread/instance.h>
+#include <openthread/ip6.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @addtogroup plat-dns-sd
+ *
+ * @brief
+ *   This module includes the platform abstraction for DNS-SD (e.g., mDNS) on the infrastructure network.
+ *
+ * @{
+ *
+ * The DNS-SD platform APIs are used only when `OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE` is enabled.
+ *
+ */
+
+/**
+ * Represents the state of the DNS-SD platform.
+ *
+ */
+typedef enum otPlatDnssdState
+{
+    OT_PLAT_DNSSD_STOPPED, ///< Stopped and unable to register any service or host.
+    OT_PLAT_DNSSD_READY,   ///< Running and ready to register service or host.
+} otPlatDnssdState;
+
+/**
+ * Represents a request ID for registering/unregistering a service or host.
+ *
+ */
+typedef uint32_t otPlatDnssdRequestId;
+
+/**
+ * Represents the callback function used when registering/unregistering a host or service.
+ *
+ * See `otPlatDnssdRegisterService()`, `otPlatDnssdUnregisterService()`, `otPlatDnssdRegisterHost()`, and
+ * `otPlatDnssdUnregisterHost()` for more details about when to invoke the callback and the `aError` values that can
+ * be returned in each case.
+ *
+ * @param[in] aInstance     The OpenThread instance.
+ * @param[in] aRequestId    The request ID.
+ * @param[in] aError        Error indicating the outcome of request.
+ *
+ */
+typedef void (*otPlatDnssdRegisterCallback)(otInstance *aInstance, otPlatDnssdRequestId aRequestId, otError aError);
+
+/**
+ * Represents a DNS-SD service.
+ *
+ * See `otPlatDnssdRegisterService()`, `otPlatDnssdUnregisterService()` for more details about fields in each case.
+ *
+ */
+typedef struct otPlatDnssdService
+{
+    const char        *mHostName;            ///< The host name (does not include domain name).
+    const char        *mServiceInstance;     ///< The service instance name label (not the full name).
+    const char        *mServiceType;         ///< The service type (e.g., "_mt._udp", does not include domain name).
+    const char *const *mSubTypeLabels;       ///< Array of sub-type labels (can be NULL if no label).
+    uint16_t           mSubTypeLabelsLength; ///< Length of array of sub-type labels.
+    const uint8_t     *mTxtData;             ///< Encoded TXT data bytes.
+    uint16_t           mTxtDataLength;       ///< Length of TXT data.
+    uint16_t           mPort;                ///< The service port number.
+    uint16_t           mPriority;            ///< The service priority.
+    uint16_t           mWeight;              ///< The service weight.
+    uint32_t           mTtl;                 ///< The service TTL in seconds.
+    uint32_t           mInfraIfIndex;        ///< The infrastructure network interface index.
+} otPlatDnssdService;
+
+/**
+ * Represents a DNS-SD host.
+ *
+ * See `otPlatDnssdRegisterHost()`, `otPlatDnssdUnregisterHost()` for more details about fields in each case.
+ *
+ */
+typedef struct otPlatDnssdHost
+{
+    const char         *mHostName;        ///< The host name (does not include domain name).
+    const otIp6Address *mAddresses;       ///< Array of IPv6 host addresses.
+    uint16_t            mAddressesLength; ///< Number of entries in @p mAddresses array.
+    uint32_t            mTtl;             ///< The host TTL in seconds.
+    uint32_t            mInfraIfIndex;    ///< The infrastructure network interface index.
+} otPlatDnssdHost;
+
+/**
+ * Represents a DNS-SD key record.
+ *
+ * See `otPlatDnssdRegisterKey()`, `otPlatDnssdUnregisterKey()` for more details about fields in each case.
+ *
+ */
+typedef struct otPlatDnssdKey
+{
+    const char    *mName;          ///< A host or a service instance name (does not include domain name).
+    const char    *mServiceType;   ///< The service type if key is for a service (does not include domain name).
+    const uint8_t *mKeyData;       ///< Byte array containing the key record data.
+    uint16_t       mKeyDataLength; ///< Length of @p mKeyData in bytes.
+    uint16_t       mClass;         ///< The resource record class.
+    uint32_t       mTtl;           ///< The TTL in seconds.
+    uint32_t       mInfraIfIndex;  ///< The infrastructure network interface index.
+} otPlatDnssdKey;
+
+/**
+ * Callback to notify state changes of the DNS-SD platform.
+ *
+ * The OpenThread stack will call `otPlatDnssdGetState()` (from this callback or later) to get the new state. The
+ * platform MUST therefore ensure that the returned state from `otPlatDnssdGetState()` is updated before calling this.
+ *
+ * @param[in] aInstance The OpenThread instance structure.
+ *
+ */
+extern void otPlatDnssdStateHandleStateChange(otInstance *aInstance);
+
+/**
+ * Gets the current state of the DNS-SD module.
+ *
+ * The platform MUST notify the OpenThread stack whenever its state gets changed by invoking
+ * `otPlatDnssdStateHandleStateChange()`.
+ *
+ * @param[in] aInstance     The OpenThread instance.
+ *
+ * @returns The current state of the DNS-SD module.
+ *
+ */
+otPlatDnssdState otPlatDnssdGetState(otInstance *aInstance);
+
+/**
+ * Registers or updates a service on the infrastructure network's DNS-SD module.
+ *
+ * The @p aService and all its contained information (strings and buffers) are only valid during this call. The
+ * platform MUST save a copy of the information if it wants to retain the information after returning from this
+ * function.
+ *
+ * The fields in @p aService follow these rules:
+ *
+ * - The `mServiceInstance` and `mServiceType` fields specify the service instance label and service type name,
+ *   respectively. They are never NULL.
+ * - The `mHostName` field specifies the host name of the service if it is not NULL. Otherwise, if it is NULL, it
+ *   indicates that this service is for the device itself and leaves the host name selection to DNS-SD platform.
+ * - The `mSubTypeLabels` is an array of strings representing sub-types associated with the service. It can be NULL
+ *   if there are no sub-types. Otherwise, the array length is specified by `mSubTypeLabelsLength`.
+ * - The `mTxtData` and `mTxtDataLength` fields specify the encoded TXT data.
+ * - The `mPort`, `mWeight`, and `mPriority` fields specify the service's parameters (as specified in DNS SRV record).
+ * - The `mTtl` field specifies the TTL if non-zero. If zero, the platform can choose the TTL to use.
+ * - The `mInfraIfIndex` field, if non-zero, specifies the infrastructure network interface index to use for this
+ *   request. If zero, the platform implementation can decided the interface.
+ *
+ * When the `mHostName` field in @p aService is not NULL (indicating that this registration is on behalf of another
+ * host), the OpenThread stack will ensure that `otPlatDnssdRegisterHost()` is also called for the same host before any
+ * service registration requests for the same host.
+ *
+ * Once the registration request is finished, either successfully or failed, the platform reports the outcome by
+ * invoking the @p aCallback and passing the same @p aRequestId in the callback. The @p aCallback function pointer can
+ * be NULL, which indicates that the OpenThread stack does not need to be notified of the outcome of the request.
+ * If the outcome is determined, the platform implementation may invoke the @p aCallback before returning from this
+ * function. The OpenThread stack will ensure to handle such a situation.
+ *
+ * On success, the @p aCallback MUST be called (if non-NULL) with `OT_ERROR_NONE` as the `aError` input parameter. If
+ * the registration causes a name conflict on DNS-SD domain (the service instance name is already claimed by another
+ * host), the `OT_ERROR_DUPLICATED` error MUST be used. The platform implementation can use other `OT_ERROR` types for
+ * other types of errors.
+ *
+ * The platform implementation MUST not assume that the @p aRequestId used in subsequent requests will be different.
+ * OpenThread may reuse the same request ID again for a different request.
+ *
+ * The OpenThread stack will not register the same service (with no changes) that was registered successfully earlier.
+ * Therefore, the platform implementation does not need to check for duplicate/same service and can assume that calls
+ * to this function are either registering a new entry or changing some parameter in a previously registered item. As
+ * a result, these changes always need to be synced on the infrastructure DNS-SD module.
+ *
+ * The OpenThread stack does not require the platform implementation to always invoke the @p aCallback function.
+ * The OpenThread stack has its own mechanism to time out an aged request with no response. This relaxes the
+ * requirement for platform implementations.
+ *
+ * @param[in] aInstance     The OpenThread instance.
+ * @param[in] aService      Information about the service to register.
+ * @param[in] aRequestId    The ID associated with this request.
+ * @param[in] aCallback     The callback function pointer to report the outcome (may be NULL if no callback needed).
+ *
+ */
+void otPlatDnssdRegisterService(otInstance                 *aInstance,
+                                const otPlatDnssdService   *aService,
+                                otPlatDnssdRequestId        aRequestId,
+                                otPlatDnssdRegisterCallback aCallback);
+
+/**
+ * Unregisters a service on the infrastructure network's DNS-SD module.
+ *
+ * The @p aService and all its contained information (strings and buffers) are only valid during this call. The
+ * platform MUST save a copy of the information if it wants to retain the information after returning from this
+ * function.
+ *
+ * The fields in @p aService follow these rules:
+ *
+ * - The `mServiceInstance` and `mServiceType` fields specify the service instance label and service type name,
+ *   respectively. They are never NULL.
+ * - The `mHostName` field specifies the host name of the service if it is not NULL. Otherwise, if it is NULL, it
+ *   indicates that this service is for the device itself and leaves the host name selection to DNS-SD platform.
+ * - The `mInfraIfIndex` field, if non-zero, specifies the infrastructure network interface index to use for this
+ *   request. If zero, the platform implementation can decided the interface.
+ * - The rest of the fields in @p aService structure MUST be ignored in `otPlatDnssdUnregisterService()` call and may
+ *   be set to zero by the OpenThread stack.
+ *
+ * Regarding the invocation of the @p aCallback and the reuse of the @p aRequestId, this function follows the same
+ * rules as described in `otPlatDnssdRegisterService()`.
+ *
+ * The OpenThread stack may request the unregistration of a service that was not previously registered, and the
+ * platform implementation MUST handle this case. In such a case, the platform can use either `OT_ERROR_NOT_FOUND` to
+ * indicate that there was no such registration, or `OT_ERROR_NONE` when invoking the @p aCallback function. The
+ * OpenThread stack will handle either case correctly.
+ *
+ * @param[in] aInstance     The OpenThread instance.
+ * @param[in] aService      Information about the service to unregister.
+ * @param[in] aRequestId    The ID associated with this request.
+ * @param[in] aCallback     The callback function pointer to report the outcome (may be NULL if no callback needed).
+ *
+ */
+void otPlatDnssdUnregisterService(otInstance                 *aInstance,
+                                  const otPlatDnssdService   *aService,
+                                  otPlatDnssdRequestId        aRequestId,
+                                  otPlatDnssdRegisterCallback aCallback);
+
+/**
+ * Registers or updates a host on the infrastructure network's DNS-SD module.
+ *
+ * The @p aHost and all its contained information (strings and arrays) are only valid during this call. The
+ * platform MUST save a copy of the information if it wants to retain the information after returning from this
+ * function.
+ *
+ * The fields in @p aHost follow these rules:
+ *
+ * - The `mHostName` field specifies the host name to register. It is never NULL.
+ * - The `mAddresses` field is an array of IPv6 addresses to register with the host. `mAddressesLength` field provides
+ *   the number of entries in `mAddresses` array. The platform implementation MUST not filter or remove any of
+ *   addresses in the list.
+ *   The OpenThread stack will already ensure that the given addresses are externally reachable. For example, when
+ *   registering host from an SRP registration, link-local or mesh-local addresses associated with the host which are
+ *   intended for use within Thread mesh are not included in `mAddresses` array passed to this API. The `mAddresses`
+ *   array can be empty with zero `mAddressesLength`. In such a case, the platform MUST stop advertising any addresses
+ *   for this host name on the infrastructure DNS-SD.
+ * - The `mTtl` field specifies the TTL if non-zero. If zero, the platform can choose the TTL to use.
+ * - The `mInfraIfIndex` field, if non-zero, specifies the infrastructure network interface index to use for this
+ *   request. If zero, the platform implementation can decided the interface.
+ *
+ * Regarding the invocation of the @p aCallback and the reuse of the @p aRequestId, this function follows the same
+ * rules as described in `otPlatDnssdRegisterService()`.
+ *
+ * The OpenThread stack will not register the same host (with no changes) that was registered successfully earlier.
+ * Therefore, the platform implementation does not need to check for duplicate/same host and can assume that calls
+ * to this function are either registering a new entry or changing some parameter in a previously registered item. As
+ * a result, these changes always need to be synced on the infrastructure DNS-SD module.
+ *
+ * @param[in] aInstance     The OpenThread instance.
+ * @param[in] aHost         Information about the host to register.
+ * @param[in] aRequestId    The ID associated with this request.
+ * @param[in] aCallback     The callback function pointer to report the outcome (may be NULL if no callback needed).
+ *
+ */
+void otPlatDnssdRegisterHost(otInstance                 *aInstance,
+                             const otPlatDnssdHost      *aHost,
+                             otPlatDnssdRequestId        aRequestId,
+                             otPlatDnssdRegisterCallback aCallback);
+
+/**
+ * Unregisters a host on the infrastructure network's DNS-SD module.
+ *
+ * The @p aHost and all its contained information (strings and arrays) are only valid during this call. The
+ * platform MUST save a copy of the information if it wants to retain the information after returning from this
+ * function.
+ *
+ * The fields in @p aHost follow these rules:
+ *
+ * - The `mHostName` field specifies the host name to unregister. It is never NULL.
+ * - The `mInfraIfIndex` field, if non-zero, specifies the infrastructure network interface index to use for this
+ *   request. If zero, the platform implementation can decided the interface.
+ * - The rest of the fields in @p aHost structure MUST be ignored in `otPlatDnssdUnregisterHost()` call and may
+ *   be set to zero by the OpenThread stack.
+ *
+ * Regarding the invocation of the @p aCallback and the reuse of the @p aRequestId, this function follows the same
+ * rules as described in `otPlatDnssdRegisterService()`.
+ *
+ * The OpenThread stack may request the unregistration of a host that was not previously registered, and the platform
+ * implementation MUST handle this case. In such a case, the platform can use either `OT_ERROR_NOT_FOUND` to indicate
+ * that there was no such registration, or `OT_ERROR_NONE` when invoking the @p aCallback function. OpenThread stack
+ * will handle either case correctly.
+ *
+ * When unregistering a host, the OpenThread stack will also unregister any previously registered services
+ * associated with the same host (by calling `otPlatDnssdUnregisterService()`). However, the platform implementation
+ * MAY assume that unregistering a host also unregisters all its associated services.
+ *
+ * @param[in] aInstance     The OpenThread instance.
+ * @param[in] aHost         Information about the host to unregister.
+ * @param[in] aRequestId    The ID associated with this request.
+ * @param[in] aCallback     The callback function pointer to report the outcome (may be NULL if no callback needed).
+ *
+ */
+void otPlatDnssdUnregisterHost(otInstance                 *aInstance,
+                               const otPlatDnssdHost      *aHost,
+                               otPlatDnssdRequestId        aRequestId,
+                               otPlatDnssdRegisterCallback aCallback);
+
+/**
+ * Registers or updates a key record on the infrastructure network's DNS-SD module.
+ *
+ * The @p aKey and all its contained information (strings and arrays) are only valid during this call. The
+ * platform MUST save a copy of the information if it wants to retain the information after returning from this
+ * function.
+ *
+ * The fields in @p aKey follow these rules:
+ *
+ * - If the key is associated with a host, `mName` field specifies the host name and `mServiceType` will be NULL.
+ * - If the key is associated with a service, `mName` field specifies the service instance label and `mServiceType`
+ *   field specifies the service type. In this case the DNS name for key record is `{mName}.{mServiceTye}`.
+ * - The `mKeyData` field contains the key record's data with `mKeyDataLength` as its length in byes. It is never NULL.
+ * - The `mClass` fields specifies the resource record class to use when registering key record.
+ * - The `mTtl` field specifies the TTL if non-zero. If zero, the platform can choose the TTL to use.
+ * - The `mInfraIfIndex` field, if non-zero, specifies the infrastructure network interface index to use for this
+ *   request. If zero, the platform implementation can decided the interface.
+ *
+ * Regarding the invocation of the @p aCallback and the reuse of the @p aRequestId, this function follows the same
+ * rules as described in `otPlatDnssdRegisterService()`.
+ *
+ * The OpenThread stack will not register the same key (with no changes) that was registered successfully earlier.
+ * Therefore, the platform implementation does not need to check for duplicate/same name and can assume that calls
+ * to this function are either registering a new key or changing the key data in a previously registered one. As
+ * a result, these changes always need to be synced on the infrastructure DNS-SD module.
+ *
+ * @param[in] aInstance     The OpenThread instance.
+ * @param[in] aHost         Information about the key record to register.
+ * @param[in] aRequestId    The ID associated with this request.
+ * @param[in] aCallback     The callback function pointer to report the outcome (may be NULL if no callback needed).
+ *
+ */
+void otPlatDnssdRegisterKey(otInstance                 *aInstance,
+                            const otPlatDnssdKey       *aKey,
+                            otPlatDnssdRequestId        aRequestId,
+                            otPlatDnssdRegisterCallback aCallback);
+
+/**
+ * Unregisters a key record on the infrastructure network's DNS-SD module.
+ *
+ * The @p aKey and all its contained information (strings and arrays) are only valid during this call. The
+ * platform MUST save a copy of the information if it wants to retain the information after returning from this
+ * function.
+ *
+ * The fields in @p aKey follow these rules:
+ *
+ * - If the key is associated with a host, `mName` field specifies the host name and `mServiceType` will be NULL.
+ * - If the key is associated with a service, `mName` field specifies the service instance label and `mServiceType`
+ *   field specifies the service type. In this case the DNS name for key record is `{mName}.{mServiceTye}`.
+ * - The `mInfraIfIndex` field, if non-zero, specifies the infrastructure network interface index to use for this
+ *   request. If zero, the platform implementation can decided the interface.
+ * - The rest of the fields in @p aKey structure MUST be ignored in `otPlatDnssdUnregisterKey()` call and may
+ *   be set to zero by the OpenThread stack.
+ *
+ * Regarding the invocation of the @p aCallback and the reuse of the @p aRequestId, this function follows the same
+ * rules as described in `otPlatDnssdRegisterService()`.
+ *
+ * The OpenThread stack may request the unregistration of a key that was not previously registered, and the platform
+ * implementation MUST handle this case. In such a case, the platform can use either `OT_ERROR_NOT_FOUND` to indicate
+ * that there was no such registration, or `OT_ERROR_NONE` when invoking the @p aCallback function. the OpenThread
+ * stack will handle either case correctly.
+ *
+ * @param[in] aInstance     The OpenThread instance.
+ * @param[in] aKey          Information about the key to unregister.
+ * @param[in] aRequestId    The ID associated with this request.
+ * @param[in] aCallback     The callback function pointer to report the outcome (may be NULL if no callback needed).
+ *
+ */
+void otPlatDnssdUnregisterKey(otInstance                 *aInstance,
+                              const otPlatDnssdKey       *aKey,
+                              otPlatDnssdRequestId        aRequestId,
+                              otPlatDnssdRegisterCallback aCallback);
+
+/**
+ * @}
+ *
+ */
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // OPENTHREAD_PLATFORM_DNSSD_H_
diff --git a/include/openthread/platform/radio.h b/include/openthread/platform/radio.h
index e2bc3db..9f6bd31 100644
--- a/include/openthread/platform/radio.h
+++ b/include/openthread/platform/radio.h
@@ -1132,9 +1132,9 @@
  *
  * @note Platforms should use CSL peer addresses to include CSL IE when generating enhanced acks.
  *
- * @retval  kErrorNotImplemented Radio driver doesn't support CSL.
- * @retval  kErrorFailed         Other platform specific errors.
- * @retval  kErrorNone           Successfully enabled or disabled CSL.
+ * @retval  OT_ERROR_NOT_IMPLEMENTED Radio driver doesn't support CSL.
+ * @retval  OT_ERROR_FAILED          Other platform specific errors.
+ * @retval  OT_ERROR_NONE            Successfully enabled or disabled CSL.
  *
  */
 otError otPlatRadioEnableCsl(otInstance         *aInstance,
@@ -1143,6 +1143,20 @@
                              const otExtAddress *aExtAddr);
 
 /**
+ * Reset CSL receiver in the platform.
+ *
+ * @note Defaults to `otPlatRadioEnableCsl(aInstance,0, Mac::kShortAddrInvalid, nullptr);`
+ *
+ * @param[in]  aInstance     The OpenThread instance structure.
+ *
+ * @retval  OT_ERROR_NOT_IMPLEMENTED Radio driver doesn't support CSL.
+ * @retval  OT_ERROR_FAILED          Other platform specific errors.
+ * @retval  OT_ERROR_NONE            Successfully disabled CSL.
+ *
+ */
+otError otPlatRadioResetCsl(otInstance *aInstance);
+
+/**
  * Update CSL sample time in radio driver.
  *
  * Sample time is stored in radio driver as a copy to calculate phase when
diff --git a/include/openthread/platform/trel.h b/include/openthread/platform/trel.h
index b372fde..f9e068f 100644
--- a/include/openthread/platform/trel.h
+++ b/include/openthread/platform/trel.h
@@ -198,6 +198,35 @@
 extern void otPlatTrelHandleReceived(otInstance *aInstance, uint8_t *aBuffer, uint16_t aLength);
 
 /**
+ * Represents a group of TREL related counters in the platform layer.
+ *
+ */
+typedef struct otPlatTrelCounters
+{
+    uint64_t mTxPackets; ///< Number of packets successfully transmitted through TREL.
+    uint64_t mTxBytes;   ///< Sum of size of packets successfully transmitted through TREL.
+    uint64_t mTxFailure; ///< Number of packet transmission failures through TREL.
+    uint64_t mRxPackets; ///< Number of packets received through TREL.
+    uint64_t mRxBytes;   ///< Sum of size of packets received through TREL.
+} otPlatTrelCounters;
+
+/**
+ * Gets the pointer to the TREL counters in the platform layer.
+ *
+ * @param[in] aInstance        The OpenThread instance structure.
+ *
+ */
+const otPlatTrelCounters *otPlatTrelGetCounters(otInstance *aInstance);
+
+/**
+ * Resets the TREL counters in the platform layer.
+ *
+ * @param[in] aInstance        The OpenThread instance structure.
+ *
+ */
+void otPlatTrelResetCounters(otInstance *aInstance);
+
+/**
  * @}
  *
  */
diff --git a/include/openthread/tcat.h b/include/openthread/tcat.h
new file mode 100644
index 0000000..291ce48
--- /dev/null
+++ b/include/openthread/tcat.h
@@ -0,0 +1,170 @@
+/*
+ *  Copyright (c) 2023, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * @brief
+ *  This file defines the top-level functions for the OpenThread TCAT.
+ *
+ *  @note
+ *   The functions in this module require the build-time feature `OPENTHREAD_CONFIG_BLE_TCAT_ENABLE=1`.
+ *
+ *  @note
+ *   To enable cipher suite DTLS_PSK_WITH_AES_128_CCM_8, MBEDTLS_KEY_EXCHANGE_PSK_ENABLED
+ *    must be enabled in mbedtls-config.h
+ *   To enable cipher suite DTLS_ECDHE_ECDSA_WITH_AES_128_CCM_8,
+ *    MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED must be enabled in mbedtls-config.h.
+ */
+
+#ifndef OPENTHREAD_TCAT_H_
+#define OPENTHREAD_TCAT_H_
+
+#include <stdint.h>
+#include <openthread/message.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @addtogroup api-ble-secure
+ *
+ * @brief
+ *   This module includes functions that implement TCAT communication.
+ *
+ *   The functions in this module are available when TCAT feature
+ *   (`OPENTHREAD_CONFIG_BLE_TCAT_ENABLE`) is enabled.
+ *
+ * @{
+ *
+ */
+
+#define OT_TCAT_MAX_SERVICE_NAME_LENGTH \
+    15 ///< Maximum string length of a UDP or TCP service name (does not include null char).
+
+/**
+ * Represents TCAT status code.
+ *
+ */
+typedef enum otTcatStatusCode
+{
+    OT_TCAT_STATUS_SUCCESS       = 0, ///< Command or request was successfully processed
+    OT_TCAT_STATUS_UNSUPPORTED   = 1, ///< Requested command or received TLV is not supported
+    OT_TCAT_STATUS_PARSE_ERROR   = 2, ///< Request / command could not be parsed correctly
+    OT_TCAT_STATUS_VALUE_ERROR   = 3, ///< The value of the transmitted TLV has an error
+    OT_TCAT_STATUS_GENERAL_ERROR = 4, ///< An error not matching any other category occurred
+    OT_TCAT_STATUS_BUSY          = 5, ///< Command cannot be executed because the resource is busy
+    OT_TCAT_STATUS_UNDEFINED    = 6, ///< The requested value, data or service is not defined (currently) or not present
+    OT_TCAT_STATUS_HASH_ERROR   = 7, ///< The hash value presented by the commissioner was incorrect
+    OT_TCAT_STATUS_UNAUTHORIZED = 16, ///< Sender does not have sufficient authorization for the given command
+
+} otTcatStatusCode;
+
+/**
+ * Represents TCAT application protocol.
+ *
+ */
+typedef enum otTcatApplicationProtocol
+{
+    OT_TCAT_APPLICATION_PROTOCOL_NONE   = 0, ///< Message which has been sent without activating the TCAT agent
+    OT_TCAT_APPLICATION_PROTOCOL_STATUS = 1, ///< Message directed to a UDP service
+    OT_TCAT_APPLICATION_PROTOCOL_TCP    = 2, ///< Message directed to a TCP service
+
+} otTcatApplicationProtocol;
+
+/**
+ * Represents a TCAT command class.
+ *
+ */
+typedef enum otTcatCommandClass
+{
+    OT_TCAT_COMMAND_CLASS_GENERAL         = 0, ///< TCAT commands related to general operations
+    OT_TCAT_COMMAND_CLASS_COMMISSIONING   = 1, ///< TCAT commands related to commissioning
+    OT_TCAT_COMMAND_CLASS_EXTRACTION      = 2, ///< TCAT commands related to key extraction
+    OT_TCAT_COMMAND_CLASS_DECOMMISSIONING = 3, ///< TCAT commands related to de-commissioning
+    OT_TCAT_COMMAND_CLASS_APPLICATION     = 4, ///< TCAT commands related to application layer
+
+} otTcatCommandClass;
+
+/**
+ * This structure represents a TCAT vendor information.
+ *
+ * The content of this structure MUST persist and remain unchanged while a TCAT session is running.
+ *
+ */
+typedef struct otTcatVendorInfo
+{
+    const char *mProvisioningUrl; ///< Provisioning URL path string
+    const char *mVendorName;      ///< Vendor name string
+    const char *mVendorModel;     ///< Vendor model string
+    const char *mVendorSwVersion; ///< Vendor software version string
+    const char *mVendorData;      ///< Vendor specific data string
+    const char *mPskdString;      ///< Vendor managed pre-shared key for device
+    const char *mInstallCode;     ///< Vendor managed install code string
+    const char *mDeviceId; ///< Vendor managed device ID string (if NULL: device ID is set to EUI-64 in binary format)
+
+} otTcatVendorInfo;
+
+/**
+ * Pointer to call when application data was received over a TCAT TLS connection.
+ *
+ * @param[in]  aInstance                 A pointer to an OpenThread instance.
+ * @param[in]  aMessage                  A pointer to the message.
+ * @param[in]  aOffset                   The offset where the application data begins.
+ * @param[in]  aTcatApplicationProtocol  The protocol type of the message received.
+ * @param[in]  aServiceName              The name of the service the message is direced to.
+ * @param[in]  aContext                  A pointer to arbitrary context information.
+ *
+ */
+typedef void (*otHandleTcatApplicationDataReceive)(otInstance               *aInstance,
+                                                   const otMessage          *aMessage,
+                                                   int32_t                   aOffset,
+                                                   otTcatApplicationProtocol aTcatApplicationProtocol,
+                                                   const char               *aServiceName,
+                                                   void                     *aContext);
+
+/**
+ * Pointer to call to notify the completion of a join operation.
+ *
+ * @param[in]  aError           OT_ERROR_NONE if the join process succeeded.
+ *                              OT_ERROR_SECURITY if the join process failed due to security credentials.
+ * @param[in]  aContext         A pointer to arbitrary context information.
+ *
+ */
+typedef void (*otHandleTcatJoin)(otError aError, void *aContext);
+
+/**
+ * @}
+ *
+ */
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* OPENTHREAD_TCAT_H_ */
diff --git a/include/openthread/tcp.h b/include/openthread/tcp.h
index caa0d4e..649a4b4 100644
--- a/include/openthread/tcp.h
+++ b/include/openthread/tcp.h
@@ -231,7 +231,7 @@
 /**
  * Represents a TCP endpoint.
  *
- * An TCP endpoint acts an endpoint of TCP connection. It can be used to
+ * A TCP endpoint acts an endpoint of TCP connection. It can be used to
  * initiate TCP connections, and, once a TCP connection is established, send
  * data to and receive data from the connection peer.
  *
diff --git a/include/openthread/thread.h b/include/openthread/thread.h
index cb0d6bd..4d814c1 100644
--- a/include/openthread/thread.h
+++ b/include/openthread/thread.h
@@ -238,7 +238,7 @@
  * @param[in] aEnabled  TRUE if Thread is enabled, FALSE otherwise.
  *
  * @retval OT_ERROR_NONE           Successfully started Thread protocol operation.
- * @retval OT_ERROR_INVALID_STATE  The network interface was not not up.
+ * @retval OT_ERROR_INVALID_STATE  The network interface was not up.
  *
  */
 otError otThreadSetEnabled(otInstance *aInstance, bool aEnabled);
@@ -709,7 +709,7 @@
  * @sa otThreadSetKeySwitchGuardTime
  *
  */
-uint32_t otThreadGetKeySwitchGuardTime(otInstance *aInstance);
+uint16_t otThreadGetKeySwitchGuardTime(otInstance *aInstance);
 
 /**
  * Sets the thrKeySwitchGuardTime (in hours).
@@ -723,7 +723,7 @@
  * @sa otThreadGetKeySwitchGuardTime
  *
  */
-void otThreadSetKeySwitchGuardTime(otInstance *aInstance, uint32_t aKeySwitchGuardTime);
+void otThreadSetKeySwitchGuardTime(otInstance *aInstance, uint16_t aKeySwitchGuardTime);
 
 /**
  * Detach from the Thread network.
diff --git a/include/openthread/trel.h b/include/openthread/trel.h
index 2358ea2..e9a0bf0 100644
--- a/include/openthread/trel.h
+++ b/include/openthread/trel.h
@@ -39,6 +39,7 @@
 #include <openthread/dataset.h>
 #include <openthread/ip6.h>
 #include <openthread/platform/radio.h>
+#include "openthread/platform/trel.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -124,6 +125,16 @@
 const otTrelPeer *otTrelGetNextPeer(otInstance *aInstance, otTrelPeerIterator *aIterator);
 
 /**
+ * Returns the number of TREL peers.
+ *
+ * @param[in]  aInstance  A pointer to an OpenThread instance.
+ *
+ * @returns  The number of TREL peers.
+ *
+ */
+uint16_t otTrelGetNumberOfPeers(otInstance *aInstance);
+
+/**
  * Sets the filter mode (enables/disables filtering).
  *
  * When filter mode is enabled, any rx and tx traffic through TREL interface is silently dropped. This is mainly
@@ -150,6 +161,30 @@
 bool otTrelIsFilterEnabled(otInstance *aInstance);
 
 /**
+ * Represents a group of TREL related counters.
+ *
+ */
+typedef otPlatTrelCounters otTrelCounters;
+
+/**
+ * Gets the TREL counters.
+ *
+ * @param[in]  aInstance  A pointer to an OpenThread instance.
+ *
+ * @returns  A pointer to the TREL counters.
+ *
+ */
+const otTrelCounters *otTrelGetCounters(otInstance *aInstance);
+
+/**
+ * Resets the TREL counters.
+ *
+ * @param[in]  aInstance  A pointer to an OpenThread instance.
+ *
+ */
+void otTrelResetCounters(otInstance *aInstance);
+
+/**
  * @}
  *
  */
diff --git a/script/check-scan-build b/script/check-scan-build
index ef723fa..2e1463c 100755
--- a/script/check-scan-build
+++ b/script/check-scan-build
@@ -69,10 +69,12 @@
     "-DOT_PING_SENDER=ON"
     "-DOT_PLATFORM=external"
     "-DOT_RCP_RESTORATION_MAX_COUNT=2"
+    "-DOT_RCP_TX_WAIT_TIME_SECS=5"
     "-DOT_REFERENCE_DEVICE=ON"
     "-DOT_SERVICE=ON"
     "-DOT_SLAAC=ON"
     "-DOT_SNTP_CLIENT=ON"
+    "-DOT_SRP_ADV_PROXY=ON"
     "-DOT_SRP_CLIENT=ON"
     "-DOT_SRP_SERVER=ON"
     "-DOT_VENDOR_NAME=OpenThread"
diff --git a/script/check-simulation-build-cmake b/script/check-simulation-build-cmake
index ee1e2ff..eaee371 100755
--- a/script/check-simulation-build-cmake
+++ b/script/check-simulation-build-cmake
@@ -201,6 +201,9 @@
     # Build with RAM settings
     reset_source
     "$(dirname "$0")"/cmake-build simulation -DOT_SETTINGS_RAM=ON
+
+    reset_source
+    "$(dirname "$0")"/cmake-build simulation -DOT_BLE_TCAT=ON
 }
 
 build_nest_common()
diff --git a/script/cmake-build b/script/cmake-build
index e457bfc..f8e227c 100755
--- a/script/cmake-build
+++ b/script/cmake-build
@@ -102,12 +102,14 @@
     "-DOT_NETDIAG_CLIENT=ON"
     "-DOT_PING_SENDER=ON"
     "-DOT_RCP_RESTORATION_MAX_COUNT=2"
+    "-DOT_RCP_TX_WAIT_TIME_SECS=5"
     "-DOT_REFERENCE_DEVICE=ON"
     "-DOT_SERVICE=ON"
     "-DOT_SNTP_CLIENT=ON"
     "-DOT_SRP_CLIENT=ON"
     "-DOT_SRP_SERVER=ON"
     "-DOT_UPTIME=ON"
+    "-DOT_BLE_TCAT=ON"
 )
 readonly OT_POSIX_SIM_COMMON_OPTIONS
 
diff --git a/script/make-pretty b/script/make-pretty
index 086ba4c..a0921af 100755
--- a/script/make-pretty
+++ b/script/make-pretty
@@ -95,6 +95,7 @@
     '-DOT_BORDER_ROUTING=ON'
     '-DOT_BORDER_ROUTING_DHCP6_PD=ON'
     '-DOT_CHANNEL_MANAGER=ON'
+    '-DOT_CHANNEL_MANAGER_CSL=ON'
     '-DOT_CHANNEL_MONITOR=ON'
     '-DOT_COAP=ON'
     '-DOT_COAP_BLOCK=ON'
@@ -131,6 +132,7 @@
     '-DOT_SERVICE=ON'
     '-DOT_SLAAC=ON'
     '-DOT_SNTP_CLIENT=ON'
+    '-DOT_SRP_ADV_PROXY=ON'
     '-DOT_SRP_CLIENT=ON'
     '-DOT_SRP_SERVER=ON'
     '-DOT_THREAD_VERSION=1.3'
diff --git a/src/android/openthread-android-config.h b/src/android/openthread-android-config.h
index a5d4dc2..89558c2 100644
--- a/src/android/openthread-android-config.h
+++ b/src/android/openthread-android-config.h
@@ -56,23 +56,14 @@
 #define OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE 0
 
 /**
- * Temporarily disable PLATFORM_UDP to make ot-daemon usable with the command line "ot-ctl" tool.
+ * Enables platform UDP support.
  */
-// FIXME(296975198): refactor to skip posix/udp.cpp when the tunnel interface is not
-// available, instead of crash
-#define OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE 0
+#define OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE 1
 
 /**
- * Disables the DAEMON_CLI feature because the Android build system default is built with "Release"
- * type all build buid variants (user, userdebug and eng), but we want to disable CLI for OT daemon
- * in product.
- *
- * This flag will be overriden in "product_variables.debuggable.cflags" to enable CLI in userdebug
- * or eng build, see "ot-daemon-debuggable-cc-defaults".
+ * Enables CLI for Thread certification.
  */
-#ifndef OPENTHREAD_POSIX_CONFIG_DAEMON_CLI_ENABLE
-#define OPENTHREAD_POSIX_CONFIG_DAEMON_CLI_ENABLE 0
-#endif
+#define OPENTHREAD_POSIX_CONFIG_DAEMON_CLI_ENABLE 1
 
 /**
  * @def OPENTHREAD_POSIX_CONFIG_THREAD_NETIF_DEFAULT_NAME
@@ -85,4 +76,32 @@
 /**
  * Avoids killing the ot-daemon process when the infra link disappears.
  */
-#define OPENTHREAD_POSIX_CONFIG_EXIT_ON_INFRA_NETIF_LOST_ENABLE 0
\ No newline at end of file
+#define OPENTHREAD_POSIX_CONFIG_EXIT_ON_INFRA_NETIF_LOST_ENABLE 0
+
+/**
+ * Enables the posix platform to support power calibration.
+ *
+ * This flag is needed by the framework API `setChannelTargetPower()` to set the target power of each channel.
+ */
+#define OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE 1
+
+// Enable the configuration file support for power calibration.
+#define OPENTHREAD_POSIX_CONFIG_CONFIGURATION_FILE_ENABLE 1
+
+// Disable the max power table support, this feature is supported by
+// OPENTHREAD_POSIX_CONFIG_CONFIGURATION_FILE_ENABLE and
+// OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE.
+#define OPENTHREAD_POSIX_CONFIG_MAX_POWER_TABLE_ENABLE 0
+
+// Enable the spinel vendor interface to adopt the HAL-based RCP.
+#define OPENTHREAD_POSIX_CONFIG_SPINEL_VENDOR_INTERFACE_ENABLE 1
+
+// Using packet filtering in OT core instead of the iptables-based firewall feature.
+#define OPENTHREAD_POSIX_CONFIG_FIREWALL_ENABLE 0
+
+// Disable the feature to add external routes to POSIX kernel when external routes are changed in
+// netdata.
+#define OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE 0
+
+// Disable backtrace support as Android already has support for dumping backtrace after crash.
+#define OPENTHREAD_POSIX_CONFIG_BACKTRACE_ENABLE 0
diff --git a/src/android/openthread-core-android-config.h b/src/android/openthread-core-android-config.h
index 28d669d..929b4aa 100644
--- a/src/android/openthread-core-android-config.h
+++ b/src/android/openthread-core-android-config.h
@@ -29,6 +29,8 @@
 #ifndef OPENTHREAD_CORE_ANDROID_CONFIG_H_
 #define OPENTHREAD_CORE_ANDROID_CONFIG_H_
 
+#include <assert.h>
+
 /**
  * @def OPENTHREAD_CONFIG_PLATFORM_INFO
  *
@@ -43,7 +45,7 @@
  * The run time data path.
  *
  */
-#define OPENTHREAD_CONFIG_POSIX_SETTINGS_PATH "/data/misc/threadnetwork"
+#define OPENTHREAD_CONFIG_POSIX_SETTINGS_PATH "/data/misc/apexdata/com.android.tethering/ot-daemon"
 
 /**
  * @def OPENTHREAD_POSIX_CONFIG_DAEMON_SOCKET_BASENAME
@@ -51,7 +53,7 @@
  * Define socket basename used by POSIX app daemon.
  *
  */
-#define OPENTHREAD_POSIX_CONFIG_DAEMON_SOCKET_BASENAME "/data/misc/threadnetwork/openthread-%s"
+#define OPENTHREAD_POSIX_CONFIG_DAEMON_SOCKET_BASENAME "/dev/socket/ot-daemon/%s"
 
 /**
  * @def OPENTHREAD_POSIX_CONFIG_RCP_PTY_ENABLE
@@ -62,16 +64,6 @@
 #define OPENTHREAD_POSIX_CONFIG_RCP_PTY_ENABLE 1
 
 /**
- * @def OPENTHREAD_CONFIG_NUM_MESSAGE_BUFFERS
- *
- * The number of message buffers in the buffer pool.
- *
- * NOTE: Router devices that support 32 children will have 260 message buffers minimum.
- *       This allows for one 1KB frame per child with a little overhead.
- */
-#define OPENTHREAD_CONFIG_NUM_MESSAGE_BUFFERS 280
-
-/**
  * @def OPENTHREAD_CONFIG_MAC_DEFAULT_MAX_FRAME_RETRIES_DIRECT
  *
  * The maximum number of retries allowed after a transmission failure for direct transmissions.
@@ -102,14 +94,6 @@
 #define OPENTHREAD_CONFIG_MAC_MAX_TX_ATTEMPTS_INDIRECT_POLLS 16
 
 /**
- * @def OPENTHREAD_CONFIG_TMF_ADDRESS_CACHE_ENTRIES
- *
- * The number of EID-to-RLOC cache entries.
- *
- */
-#define OPENTHREAD_CONFIG_TMF_ADDRESS_CACHE_ENTRIES 32
-
-/**
  * @def OPENTHREAD_CONFIG_TMF_ADDRESS_QUERY_MAX_RETRY_DELAY
  *
  * Maximum retry delay for address query (in seconds).
@@ -125,7 +109,20 @@
  * The maximum number of children.
  *
  */
-#define OPENTHREAD_CONFIG_MLE_MAX_CHILDREN 64
+#define OPENTHREAD_CONFIG_MLE_MAX_CHILDREN 128
+
+/**
+ * @def OPENTHREAD_CONFIG_NUM_MESSAGE_BUFFERS
+ *
+ * The number of message buffers in the buffer pool.
+ *
+ * NOTE: This allows for one 1KB frame per child.
+ */
+#define OPENTHREAD_CONFIG_NUM_MESSAGE_BUFFERS (OPENTHREAD_CONFIG_MLE_MAX_CHILDREN * (32 / sizeof(void *)))
+
+static_assert(OPENTHREAD_CONFIG_NUM_MESSAGE_BUFFERS * (32 * sizeof(void *)) ==
+                  OPENTHREAD_CONFIG_MLE_MAX_CHILDREN * 1024,
+              "Message buffer should be 1KB per child");
 
 /**
  * @def OPENTHREAD_CONFIG_MLE_IP_ADDRS_PER_CHILD
@@ -213,7 +210,10 @@
  * is set, this defines the most verbose log level possible.
  *
  */
-#define OPENTHREAD_CONFIG_LOG_LEVEL OT_LOG_LEVEL_INFO
+#define OPENTHREAD_CONFIG_LOG_LEVEL OT_LOG_LEVEL_DEBG
+
+// The initial log level used when OpenThread is initialized.
+#define OPENTHREAD_CONFIG_LOG_LEVEL_INIT OT_LOG_LEVEL_INFO
 
 /**
  * @def OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE
@@ -274,6 +274,7 @@
 #define OPENTHREAD_CONFIG_UDP_FORWARD_ENABLE 0
 
 #define OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE 1
+#define OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE 1
 #define OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE 1
 #define OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE 1
 #define OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE 1
@@ -296,4 +297,69 @@
 
 #define OPENTHREAD_POSIX_CONFIG_SPINEL_VENDOR_INTERFACE_URL_PROTOCOL_NAME "threadnetwork_hal"
 
+// Enables CLI logs for debugging
+#define OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_ENABLE 1
+#define OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_LEVEL OT_LOG_LEVEL_INFO
+
+// Enables diag commands for RF testing
+#define OPENTHREAD_CONFIG_DIAG_ENABLE 1
+
+// Enable restriction on local source to larger scope multicast forwarding
+#define OPENTHREAD_CONFIG_IP6_RESTRICT_FORWARDING_LARGER_SCOPE_MCAST_WITH_LOCAL_SRC 1
+
+// Set the max log size to 1024 to allow the RCP to upload longer log.
+#define OPENTHREAD_CONFIG_LOG_MAX_SIZE 1024
+
+// Disable as Android device will not work as commissioner.
+#define OPENTHREAD_CONFIG_COMMISSIONER_ENABLE 0
+
+// Vendor extension module is not used on Android.
+#define OPENTHREAD_ENABLE_VENDOR_EXTENSION 0
+
+// Allow longer input string of 1300 bytes for CLI commands.
+#define OPENTHREAD_CONFIG_CLI_MAX_LINE_LENGTH 1300
+
+// Power calibration on RCP is not needed.
+#define OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE 0
+
+// Enable aborting the host when receiving unexpected reset from RCP.
+#define OPENTHREAD_SPINEL_CONFIG_ABORT_ON_UNEXPECTED_RCP_RESET_ENABLE 1
+
+// Disable the support for locally initializing an Active Operational Dataset.
+#define OPENTHREAD_CONFIG_OPERATIONAL_DATASET_AUTO_INIT 0
+
+// Disable TCAT over BLE support.
+#define OPENTHREAD_CONFIG_BLE_TCAT_ENABLE 0
+
+// Disable BLE secure support.
+#define OPENTHREAD_CONFIG_CLI_BLE_SECURE_ENABLE 0
+
+// Set the interval (in units of microseconds) for host-rcp time sync to 10 seconds.
+#define OPENTHREAD_SPINEL_CONFIG_RCP_TIME_SYNC_INTERVAL (10 * 1000 * 1000)
+
+// Enable tracking the uptime of OpenThread instance.
+#define OPENTHREAD_CONFIG_UPTIME_ENABLE 1
+
+// The Thread version is 1.3
+#define OPENTHREAD_CONFIG_THREAD_VERSION OT_THREAD_VERSION_1_3
+
+// Thread 1.2 Domain Unicast Address feature is not supported.
+#define OPENTHREAD_CONFIG_DUA_ENABLE 0
+#define OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE 0
+
+// Enable to support BBR feature.
+#define OPENTHREAD_CONFIG_MLR_ENABLE 1
+
+// Enable the external heap.
+#define OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE 1
+
+// Disable TREL as it's not yet supported.
+#define OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE 0
+
+// Enable Link Metrics subject feature for Thread certification test.
+#define OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE 1
+
+// Enable Link Metrics initiator feature for Thread certification test.
+#define OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE 1
+
 #endif // OPENTHREAD_CORE_ANDROID_CONFIG_H_
diff --git a/src/android/thread_network_hal/hal_interface.cpp b/src/android/thread_network_hal/hal_interface.cpp
index f1de827..5776c43 100644
--- a/src/android/thread_network_hal/hal_interface.cpp
+++ b/src/android/thread_network_hal/hal_interface.cpp
@@ -64,6 +64,9 @@
     IgnoreError(aRadioUrl.ParseUint8("id", mHalInterfaceId));
     memset(&mInterfaceMetrics, 0, sizeof(mInterfaceMetrics));
     mInterfaceMetrics.mRcpInterfaceType = kSpinelInterfaceTypeVendor;
+
+    VerifyOrDie(ABinderProcess_setupPolling(&mBinderFd) == ::STATUS_OK, OT_EXIT_FAILURE);
+    VerifyOrDie(mBinderFd >= 0, OT_EXIT_FAILURE);
 }
 
 otError HalInterface::Init(SpinelInterface::ReceiveFrameCallback aCallback,
@@ -72,15 +75,10 @@
 {
     static const std::string            kServicePrefix = std::string() + IThreadChip::descriptor + "/chip";
     const char                         *value;
-    binder_status_t                     binderStatus;
     ScopedAStatus                       ndkStatus;
     std::shared_ptr<ThreadChipCallback> callback;
     std::string                         serviceName = kServicePrefix + std::to_string(mHalInterfaceId);
 
-    binderStatus = ABinderProcess_setupPolling(&mBinderFd);
-    VerifyOrDie(binderStatus == ::STATUS_OK, OT_EXIT_FAILURE);
-    VerifyOrDie(mBinderFd >= 0, OT_EXIT_FAILURE);
-
     otLogInfoPlat("[HAL] Wait for getting the service %s ...", serviceName.c_str());
     mThreadChip = IThreadChip::fromBinder(::ndk::SpAIBinder(AServiceManager_waitForService(serviceName.c_str())));
     VerifyOrDie(mThreadChip != nullptr, OT_EXIT_FAILURE);
@@ -118,7 +116,15 @@
     DieNow(OT_EXIT_FAILURE);
 }
 
-HalInterface::~HalInterface(void) { Deinit(); }
+HalInterface::~HalInterface(void)
+{
+    Deinit();
+
+    if (mBinderFd >= 0)
+    {
+        close(mBinderFd);
+    }
+}
 
 void HalInterface::Deinit(void)
 {
@@ -130,11 +136,6 @@
         mThreadChipCallback = nullptr;
     }
 
-    if (mBinderFd >= 0)
-    {
-        close(mBinderFd);
-    }
-
     mRxFrameCallback = nullptr;
     mRxFrameContext  = nullptr;
     mRxFrameBuffer   = nullptr;
@@ -146,11 +147,7 @@
     return kBusSpeed;
 }
 
-otError HalInterface::HardwareReset(void)
-{
-    mThreadChip->hardwareReset();
-    return OT_ERROR_NONE;
-}
+otError HalInterface::HardwareReset(void) { return StatusToError(mThreadChip->hardwareReset()); }
 
 void HalInterface::UpdateFdSet(void *aMainloopContext)
 {
@@ -270,7 +267,7 @@
     return;
 }
 
-otError HalInterface::StatusToError(ScopedAStatus &aStatus)
+otError HalInterface::StatusToError(const ScopedAStatus &aStatus) const
 {
     otError error = OT_ERROR_FAILED;
 
diff --git a/src/android/thread_network_hal/hal_interface.hpp b/src/android/thread_network_hal/hal_interface.hpp
index ef72d05..c3e9aed 100644
--- a/src/android/thread_network_hal/hal_interface.hpp
+++ b/src/android/thread_network_hal/hal_interface.hpp
@@ -164,7 +164,7 @@
 private:
     void        ReceiveFrameCallback(const std::vector<uint8_t> &aFrame);
     static void BinderDeathCallback(void *aContext);
-    otError     StatusToError(::ndk::ScopedAStatus &aStatus);
+    otError     StatusToError(const ::ndk::ScopedAStatus &aStatus) const;
 
     class ThreadChipCallback : public ::aidl::android::hardware::threadnetwork::BnThreadChipCallback
     {
diff --git a/src/cli/BUILD.gn b/src/cli/BUILD.gn
index b7ef731..b74577a 100644
--- a/src/cli/BUILD.gn
+++ b/src/cli/BUILD.gn
@@ -55,8 +55,8 @@
   "cli_mac_filter.hpp",
   "cli_network_data.cpp",
   "cli_network_data.hpp",
-  "cli_output.cpp",
-  "cli_output.hpp",
+  "cli_ping.cpp",
+  "cli_ping.hpp",
   "cli_srp_client.cpp",
   "cli_srp_client.hpp",
   "cli_srp_server.cpp",
@@ -65,6 +65,8 @@
   "cli_tcp.hpp",
   "cli_udp.cpp",
   "cli_udp.hpp",
+  "cli_utils.cpp",
+  "cli_utils.hpp",
   "x509_cert_key.hpp",
 ]
 
diff --git a/src/cli/CMakeLists.txt b/src/cli/CMakeLists.txt
index 6be4777..9dfee40 100644
--- a/src/cli/CMakeLists.txt
+++ b/src/cli/CMakeLists.txt
@@ -45,11 +45,13 @@
     cli_link_metrics.cpp
     cli_mac_filter.cpp
     cli_network_data.cpp
-    cli_output.cpp
+    cli_ping.cpp
     cli_srp_client.cpp
     cli_srp_server.cpp
+    cli_tcat.cpp
     cli_tcp.cpp
     cli_udp.cpp
+    cli_utils.cpp
 )
 
 set(OT_CLI_VENDOR_EXTENSION "" CACHE STRING "Path to CMake file to define and link cli vendor extension")
diff --git a/src/cli/README.md b/src/cli/README.md
index c0b21e4..c8dfe27 100644
--- a/src/cli/README.md
+++ b/src/cli/README.md
@@ -40,6 +40,7 @@
 - [counters](#counters)
 - [csl](#csl)
 - [dataset](README_DATASET.md)
+- [debug](#debug)
 - [delaytimermin](#delaytimermin)
 - [detach](#detach)
 - [deviceprops](#deviceprops)
@@ -85,6 +86,7 @@
 - [networkkey](#networkkey)
 - [networkname](#networkname)
 - [networktime](#networktime)
+- [nexthop](#nexthop)
 - [panid](#panid)
 - [parent](#parent)
 - [parentpriority](#parentpriority)
@@ -117,6 +119,7 @@
 - [sntp](#sntp-query-sntp-server-ip-sntp-server-port)
 - [state](#state)
 - [srp](README_SRP.md)
+- [tcat](README_TCAT.md)
 - [tcp](README_TCP.md)
 - [thread](#thread-start)
 - [timeinqueue](#timeinqueue)
@@ -355,12 +358,99 @@
 
 Print border agent state.
 
+Possible states are
+
+- `Stopped` : Border Agent is stopped.
+- `Started` : Border Agent is running with no active connection with external commissioner.
+- `Active` : Border Agent is running and is connected with an external commissioner.
+
 ```bash
 > ba state
 Started
 Done
 ```
 
+### ba ephemeralkey
+
+Indicates if an ephemeral key is active.
+
+Requires `OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE`.
+
+```bash
+> ba ephemeralkey
+inactive
+Done
+
+> ba ephemeralkey set Z10X20g3J15w1000P60m16 1000
+Done
+
+> ba ephemeralkey
+active
+Done
+```
+
+### ba ephemeralkey set \<keystring\> \[timeout\] \[port\]
+
+Sets the ephemeral key for a given timeout duration.
+
+Requires `OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE`.
+
+The ephemeral key can be set when Border Agent is already running and is not currently connected to any external commissioner (i.e., `ba state` gives `Started`).
+
+The `keystring` string is directly used as the ephemeral PSK (excluding the trailing null `\0` character). Its length MUST be between 6 and 32, inclusive.
+
+The `timeout` is in milliseconds. If not provided or set to zero, the default value of 2 minutes will be used. If the timeout value is larger than 10 minutes, the 10 minutes timeout value will be used instead.
+
+The `port` specifies the UDP port to use with the ephemeral key. If UDP port is zero or is not provided, an ephemeral port will be used. `ba port` will give the current UDP port in use by the Border Agent.
+
+Setting the ephemeral key again before a previously set one is timed out, will replace the previous one.
+
+While the timeout interval is in effect, the ephemeral key can be used only once by an external commissioner to connect. Once the commissioner disconnects, the ephemeral key is cleared, and Border Agent reverts to using PSKc.
+
+```bash
+> ba ephemeralkey set Z10X20g3J15w1000P60m16 5000 1234
+Done
+```
+
+### ba ephemeralkey clear
+
+Cancels the ephemeral key in use if any.
+
+Requires `OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE`.
+
+Can be used to cancel a previously set ephemeral key before it is used or times out. If the Border Agent is not running or there is no ephemeral key in use, calling this function has no effect.
+
+If a commissioner is connected using the ephemeral key and is currently active, calling this method does not change its state. In this case the `ba ephemeralkey` will continue to return `active` until the commissioner disconnects.
+
+```bash
+> ba ephemeralkey clear
+Done
+```
+
+### ba ephemeralkey callback enable
+
+Enables callback from Border Agent for ephemeral key state changes.
+
+```bash
+> ba ephemeralkey callback enable
+Done
+
+> ba ephemeralkey set W10X12 5000 49155
+Done
+
+BorderAgent callback: Ephemeral key active, port:49155
+BorderAgent callback: Ephemeral key inactive
+```
+
+### ba ephemeralkey callback disable
+
+Disables callback from Border Agent for ephemeral key state changes.
+
+```bash
+> ba ephemeralkey callback disable
+Done
+```
+
 ### bufferinfo
 
 Show the current message buffer information.
@@ -1024,29 +1114,51 @@
 Done
 ```
 
-### networktime
+### debug
 
-Get the Thread network time and the time sync parameters.
+Executes a series of CLI commands to gather information about the device and thread network. This is intended for debugging.
 
-```bash
-> networktime
-Network Time:     21084154us (synchronized)
-Time Sync Period: 100s
-XTAL Threshold:   300ppm
-Done
-```
+The output will display each executed CLI command preceded by "\$", followed by the corresponding command's generated output.
 
-### networktime \<timesyncperiod\> \<xtalthreshold\>
+The generated output encompasses the following information:
 
-Set time sync parameters
+- Version
+- Current state
+- RLOC16, extended MAC address
+- Unicast and multicast IPv6 address list
+- Channel
+- PAN ID and extended PAN ID
+- Network Data
+- Partition ID
+- Leader Data
 
-- timesyncperiod: The time synchronization period, in seconds.
-- xtalthreshold: The XTAL accuracy threshold for a device to become Router-Capable device, in PPM.
+If the device is operating as FTD:
 
-```bash
-> networktime 100 300
-Done
-```
+- Child and neighbor table
+- Router table and next hop Info
+- Address cache table
+- Registered MTD child IPv6 address
+- Device properties
+
+If the device supports and acts as an SRP client:
+
+- SRP client state
+- SRP client services and host info
+
+If the device supports and acts as an SRP sever:
+
+- SRP server state and address mode
+- SRP server registered hosts and services
+
+If the device supports TREL:
+
+- TREL status and peer table
+
+If the device supports and acts as a border router:
+
+- BR state
+- BR prefixes (OMR, on-link, NAT64)
+- Discovered prefix table
 
 ### delaytimermin
 
@@ -1714,6 +1826,8 @@
 
 Set the Thread Key Sequence Counter.
 
+This command is reserved for testing and demo purposes only. Changing Key Sequence Counter will render a production application non-compliant with the Thread Specification.
+
 ```bash
 > keysequence counter 10
 Done
@@ -1731,7 +1845,9 @@
 
 ### keysequence guardtime \<guardtime\>
 
-Set Thread Key Switch Guard Time (in hours) 0 means Thread Key Switch immediately if key index match
+Set Thread Key Switch Guard Time (in hours).
+
+This command is reserved for testing and demo purposes only. Changing Key Switch Guard Time will render a production application non-compliant with the Thread Specification.
 
 ```bash
 > keysequence guardtime 0
@@ -2648,6 +2764,61 @@
 Done
 ```
 
+### networktime
+
+Get the Thread network time and the time sync parameters.
+
+```bash
+> networktime
+Network Time:     21084154us (synchronized)
+Time Sync Period: 100s
+XTAL Threshold:   300ppm
+Done
+```
+
+### networktime \<timesyncperiod\> \<xtalthreshold\>
+
+Set time sync parameters
+
+- timesyncperiod: The time synchronization period, in seconds.
+- xtalthreshold: The XTAL accuracy threshold for a device to become Router-Capable device, in PPM.
+
+```bash
+> networktime 100 300
+Done
+```
+
+### nexthop
+
+Output the table of allocated Router IDs and the current next hop (as Router ID) and path cost for each ID.
+
+```bash
+> nexthop
+| ID   |NxtHop| Cost |
++------+------+------+
+|    9 |    9 |    1 |
+|   25 |   25 |    0 |
+|   30 |   30 |    1 |
+|   46 |    - |    - |
+|   50 |   30 |    3 |
+|   60 |   30 |    2 |
+Done
+```
+
+### nexthop \<rloc16\>
+
+Get the next hop (as RLOC16) and path cost towards a given RLOC16 destination.
+
+```bash
+> nexthop 0xc000
+0xc000 cost:0
+Done
+
+nexthop 0x8001
+0x2000 cost:3
+Done
+```
+
 ### panid
 
 Get the IEEE 802.15.4 PAN ID value.
diff --git a/src/cli/README_BR.md b/src/cli/README_BR.md
index dcc174c..8376290 100644
--- a/src/cli/README_BR.md
+++ b/src/cli/README_BR.md
@@ -12,6 +12,7 @@
 - [nat64prefix](#nat64prefix)
 - [omrprefix](#omrprefix)
 - [onlinkprefix](#onlinkprefix)
+- [pd](#pd)
 - [prefixtable](#prefixtable)
 - [rioprf](#rioprf)
 - [routeprf](#routeprf)
@@ -33,7 +34,9 @@
 enable
 omrprefix
 onlinkprefix
+pd
 prefixtable
+raoptions
 rioprf
 routeprf
 routers
@@ -176,6 +179,36 @@
 Done
 ```
 
+### pd
+
+Usage: `br pd [enable|disable]`
+
+Enable/Disable the DHCPv6 PD.
+
+```bash
+> br pd enable
+Done
+
+> br pd disable
+Done
+```
+
+Usage: `br pd state`
+
+Get the state of DHCPv6 PD.
+
+`OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE` is required.
+
+- `disabled`: DHCPv6 PD is disabled on the border router.
+- `stopped`: DHCPv6 PD in enabled but won't try to request and publish a prefix.
+- `running`: DHCPv6 PD is enabled and will try to request and publish a prefix.
+
+```bash
+> br pd state
+running
+Done
+```
+
 ### prefixtable
 
 Usage: `br prefixtable`
@@ -203,6 +236,28 @@
 Done
 ```
 
+### raoptions
+
+Usage: `br raoptions <options>`
+
+Sets additional options to append at the end of emitted Router Advertisement (RA) messages. `<options>` provided as hex bytes.
+
+```bash
+> br raoptions 0400ff00020001
+Done
+```
+
+### raoptions clear
+
+Usage: `br raoptions clear`
+
+Clear any previously set additional options to append at the end of emitted Router Advertisement (RA) messages.
+
+```bash
+> br raoptions clear
+Done
+```
+
 ### rioprf
 
 Usage: `br rioprf`
diff --git a/src/cli/README_COAPS.md b/src/cli/README_COAPS.md
index c8c91fa..c0dce81 100644
--- a/src/cli/README_COAPS.md
+++ b/src/cli/README_COAPS.md
@@ -87,6 +87,9 @@
 - [delete](#delete-uri-path-type-payload)
 - [disconnect](#disconnect)
 - [get](#get-uri-path-type)
+- [isclosed](#isclosed)
+- [isconnactive](#isconnactive)
+- [isconnected](#isconnected)
 - [post](#post-uri-path-type-payload)
 - [psk](#psk-psk-pskid)
 - [put](#put-uri-path-type-payload)
@@ -102,11 +105,13 @@
 
 ```bash
 > coaps help
-help
 connect
 delete
 disconnect
 get
+isclosed
+isconnactive
+isconnected
 post
 psk
 put
@@ -231,11 +236,16 @@
 Done
 ```
 
-### start
+### start \[check-peer-cert\|max-conn-attempts\]
 
 Starts the application coaps service.
 
-- checkPeerCert: Peer Certificate Check can be disabled by typing false.
+The `check-peer-cert` parameter determines if the peer-certificate check is enabled (default) or disabled. The `max-conn-attempts` parameter sets the maximum number of allowed attempts, successful or failed, to connect to the CoAP Secure server. The default value of this parameter is 0, which means that there is no limit to the number of attempts. The `check-peer-cert` and `max-conn-attempts` parameters work together in the following combinations, even though you can only specify one argument:
+
+- No argument specified: Defaults are used.
+- Setting `check-peer-cert` to `true`: Has the same effect as as omitting the argument, which is that the `check-peer-cert` value is `true`, and the `max-conn-attempts` value is 0.
+- Setting `check-peer-cert` to `false`: `check-peer-cert` value is `false`, and the `max-conn-attempts` value is `0`.
+- Specifying a number: `check-peer-cert` is `true`, and the `max-conn-attempts` value is the number specified in the argument.
 
 ```bash
 > coaps start
@@ -251,6 +261,36 @@
 Done
 ```
 
+### isconnected
+
+Indicates whether or not the CoAP secure service is connected.
+
+```bash
+> coaps isconnected
+yes
+Done
+```
+
+### isconnactive
+
+Indicates whether or not the CoAP secure service connection is active (already connected or establishing a connection).
+
+```bash
+> coaps isconnactive
+yes
+Done
+```
+
+### isclosed
+
+Indicates whether or not the CoAP secure service is closed.
+
+```bash
+> coaps isclosed
+no
+Done
+```
+
 ### x509
 
 Set DTLS ciphersuite to `TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8`.
diff --git a/src/cli/README_DATASET.md b/src/cli/README_DATASET.md
index 54f4c1e..1f32cce 100644
--- a/src/cli/README_DATASET.md
+++ b/src/cli/README_DATASET.md
@@ -107,6 +107,58 @@
    Done
    ```
 
+### Using the Dataset Updater to update Operational Dataset
+
+Dataset Updater can be used for a delayed update of network parameters on all devices of a Thread Network.
+
+1. Clear the dataset buffer and add the Dataset fields to update.
+
+   ```bash
+   > dataset clear
+   Done
+
+   > dataset channel 12
+   Done
+   ```
+
+2. Set the delay timer parameter (example uses 5 minutes or 300000 ms). Check the resulting dataset. There is no need to specify active or pending timestamps because the Dataset Updater will handle this. If specified the `dataset updater start` will issue an error.
+
+   ```bash
+   > dataset delay 300000
+
+   > dataset
+   Channel: 12
+   Delay: 30000
+   Done
+   ```
+
+3. Start the Dataset Updater, which will prepare a Pending Operation Dataset and inform the Leader to distribute it to other devices.
+
+   ```bash
+   > dataset updater start
+   Done
+
+   > dataset updater
+   Enabled
+   ```
+
+4. After about 5 minutes, the changes are applied to the Active Operational Dataset on the Leader. This can also be checked at other devices on the network: these should have applied the new Dataset too, at approximately the same time as the Leader has done this.
+
+   ```bash
+   > dataset active
+   Active Timestamp: 10
+   Channel: 12
+   Channel Mask: 0x07fff800
+   Ext PAN ID: 324a71d90cdc8345
+   Mesh Local Prefix: fd7d:da74:df5e:80c::/64
+   Network Key: be768535bac1b8d228960038311d6ca2
+   Network Name: OpenThread-bcaf
+   PAN ID: 0xbcaf
+   PSKc: e79b274ab22414a814ed5cce6a30be67
+   Security Policy: 672 onrc 0
+   Done
+   ```
+
 ### Using the Pending Operational Dataset for Delayed Dataset Updates
 
 The Pending Operational Dataset can be used for a delayed update of network parameters on all devices of a Thread Network. If certain Active Operational Dataset parameters need to be changed, but the change would impact the connectivity of the network, delaying the update helps to let all devices receive the new parameters before the update is applied. Examples of such parameters are the channel, PAN ID, certain Security Policy bits, or Network Key.
@@ -241,6 +293,7 @@
 - [pskc](#pskc)
 - [securitypolicy](#securitypolicy)
 - [tlvs](#tlvs)
+- [updater](#updater)
 
 ## Command Details
 
@@ -695,3 +748,74 @@
 0e080000000000010000000300001635060004001fffe00208d196fa2040e973b60708fdbbc310c48f3a3905109929154dbc363218bcd22f907caf5c15030f4f70656e5468726561642d646532620102de2b041015b2c16f7ba92ed4bc7b1ee054f1553f0c0402a0f7f8
 Done
 ```
+
+### updater
+
+Usage: `dataset updater`
+
+Requires `OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE`.
+
+Indicate whether there is an ongoing Operation Dataset update request.
+
+```bash
+> dataset updater
+Enabled
+```
+
+### updater start
+
+Usage: `dataset updater start`
+
+Requires `OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE`.
+
+Request network to update its Operation Dataset to the current operational dataset buffer.
+
+The current operational dataset buffer should contain the fields to be updated with their new values. It must not contain Active or Pending Timestamp fields. The Delay field is optional. If not provided, a default value (1000 ms) is used.
+
+```bash
+> channel
+19
+Done
+
+> dataset clear
+Done
+> dataset channel 15
+Done
+> dataset
+Channel: 15
+Done
+
+> dataset updater start
+Done
+> dataset updater
+Enabled
+Done
+
+Dataset update complete: OK
+
+> channel
+15
+Done
+```
+
+### updater cancel
+
+Usage: `dataset updater cancel`
+
+Requires `OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE`.
+
+Cancels an ongoing (if any) Operational Dataset update request.
+
+```bash
+> dataset updater start
+Done
+> dataset updater
+Enabled
+Done
+>
+> dataset updater cancel
+Done
+> dataset updater
+Disabled
+Done
+```
diff --git a/src/cli/README_HISTORY.md b/src/cli/README_HISTORY.md
index 3fa681a..21f2dc5 100644
--- a/src/cli/README_HISTORY.md
+++ b/src/cli/README_HISTORY.md
@@ -345,7 +345,7 @@
 
 Print the route table history. Each item provides:
 
-- Event (`Added`, `Removed`, `NextHopChnaged`, `CostChanged`)
+- Event (`Added`, `Removed`, `NextHopChanged`, `CostChanged`)
 - Router ID and RLOC16 of router
 - Next Hop (Router ID and RLOC16) - `none` if no next hop.
 - Path cost (old `->` new) - `inf` to indicate infinite path cost.
diff --git a/src/cli/README_NETDATA.md b/src/cli/README_NETDATA.md
index ad33bb9..9402885 100644
--- a/src/cli/README_NETDATA.md
+++ b/src/cli/README_NETDATA.md
@@ -334,10 +334,12 @@
 
 ### show
 
-Usage: `netdata show [local] [-x]`
+Usage: `netdata show [local] [-x] [\<rloc16\>]`
 
 Print entries in Network Data, on-mesh prefixes, external routes, services, and 6LoWPAN context information.
 
+If the optional `rloc16` input is specified, prints the entries associated with the given RLOC16 only. The RLOC16 filtering can be used when `-x` or `local` are not used.
+
 On-mesh prefixes are listed under `Prefixes` header:
 
 - The on-mesh prefix
@@ -406,6 +408,19 @@
 Done
 ```
 
+Print Network Data entries from the Leader associated with `0xa00` RLOC16.
+
+```bash
+> netdata show 0xa00
+Prefixes:
+fd00:dead:beef:cafe::/64 paros med a000
+Routes:
+fd00:1234:0:0::/64 s med a000
+Services:
+44970 5d fddead00beef00007bad0069ce45948504d2 s a000
+Done
+```
+
 Print Network Data received from the Leader as hex-encoded TLVs.
 
 ```bash
diff --git a/src/cli/README_SRP.md b/src/cli/README_SRP.md
index ce2139f..0ad7dd4 100644
--- a/src/cli/README_SRP.md
+++ b/src/cli/README_SRP.md
@@ -98,7 +98,7 @@
 Services:
 44970 5d c002 s 8400
 Done
-srp client start fded:5114:8263:1fe1:68bc:ec03:c1ad:9325 49154
+> srp client start fded:5114:8263:1fe1:68bc:ec03:c1ad:9325 49154
 Done
 ```
 
diff --git a/src/cli/README_SRP_SERVER.md b/src/cli/README_SRP_SERVER.md
index ad87182..f86e52f 100644
--- a/src/cli/README_SRP_SERVER.md
+++ b/src/cli/README_SRP_SERVER.md
@@ -8,6 +8,7 @@
 
 - [help](#help)
 - [addrmode](#addrmode)
+- [auto](#auto)
 - [disable](#disable)
 - [domain](#domain)
 - [enable](#enable)
@@ -28,6 +29,7 @@
 ```bash
 > srp server help
 addrmode
+auto
 disable
 domain
 enable
@@ -67,6 +69,25 @@
 Done
 ```
 
+### auto
+
+Usage: `srp server auto [enable|disable]`
+
+Enables or disables the auto-enable mode on the SRP server.
+
+When this mode is enabled, the Border Routing Manager controls if and when to enable or disable the SRP server.
+
+This command requires that `OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE` be enabled.
+
+```bash
+> srp server auto enable
+Done
+
+> srp server auto
+Enabled
+Done
+```
+
 ### disable
 
 Usage: `srp server disable`
diff --git a/src/cli/README_TCAT.md b/src/cli/README_TCAT.md
new file mode 100644
index 0000000..066a571
--- /dev/null
+++ b/src/cli/README_TCAT.md
@@ -0,0 +1,37 @@
+# OpenThread CLI - TCAT Example
+
+## Command List
+
+- help [#help]
+- start [#start]
+- stop [#stop]
+
+### help
+
+print help
+
+```bash
+tcat help
+help
+start
+stop
+Done
+```
+
+### start
+
+Start tcat server and ble advertisement.
+
+```bash
+tcat start
+Done
+```
+
+### stop
+
+Stop tcat server and ble advertisement.
+
+```bash
+tcat stop
+Done
+```
diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp
index cf9fea6..33d1123 100644
--- a/src/cli/cli.cpp
+++ b/src/cli/cli.cpp
@@ -68,7 +68,9 @@
 #include <openthread/backbone_router_ftd.h>
 #endif
 #endif
-#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && OPENTHREAD_FTD
+#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && \
+    (OPENTHREAD_FTD ||                          \
+     (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE))
 #include <openthread/channel_manager.h>
 #endif
 #if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
@@ -99,8 +101,9 @@
 
 Interpreter::Interpreter(Instance *aInstance, otCliOutputCallback aCallback, void *aContext)
     : OutputImplementer(aCallback, aContext)
-    , Output(aInstance, *this)
+    , Utils(aInstance, *this)
     , mCommandIsPending(false)
+    , mInternalDebugCommand(false)
     , mTimer(*aInstance, HandleTimer, this)
 #if OPENTHREAD_FTD || OPENTHREAD_MTD
 #if OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE
@@ -148,6 +151,12 @@
 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
     , mLinkMetrics(aInstance, *this)
 #endif
+#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE && OPENTHREAD_CONFIG_CLI_BLE_SECURE_ENABLE
+    , mTcat(aInstance, *this)
+#endif
+#if OPENTHREAD_CONFIG_PING_SENDER_ENABLE
+    , mPing(aInstance, *this)
+#endif
 #if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE
     , mLocateInProgress(false)
 #endif
@@ -156,13 +165,23 @@
 #if (OPENTHREAD_FTD || OPENTHREAD_MTD) && OPENTHREAD_CONFIG_CLI_REGISTER_IP6_RECV_CALLBACK
     otIp6SetReceiveCallback(GetInstancePtr(), &Interpreter::HandleIp6Receive, this);
 #endif
-    memset(&mUserCommands, 0, sizeof(mUserCommands));
+    ClearAllBytes(mUserCommands);
 
     OutputPrompt();
 }
 
 void Interpreter::OutputResult(otError aError)
 {
+    if (mInternalDebugCommand)
+    {
+        if (aError != OT_ERROR_NONE)
+        {
+            OutputLine("Error %u: %s", aError, otThreadErrorToString(aError));
+        }
+
+        ExitNow();
+    }
+
     OT_ASSERT(mCommandIsPending);
 
     VerifyOrExit(aError != OT_ERROR_PENDING);
@@ -311,24 +330,30 @@
 
     OT_ASSERT(aBuf != nullptr);
 
-    // Ignore the command if another command is pending.
-    VerifyOrExit(!mCommandIsPending, args[0].Clear());
-    mCommandIsPending = true;
+    if (!mInternalDebugCommand)
+    {
+        // Ignore the command if another command is pending.
+        VerifyOrExit(!mCommandIsPending, args[0].Clear());
+        mCommandIsPending = true;
 
-    VerifyOrExit(StringLength(aBuf, kMaxLineLength) <= kMaxLineLength - 1, error = OT_ERROR_PARSE);
+        VerifyOrExit(StringLength(aBuf, kMaxLineLength) <= kMaxLineLength - 1, error = OT_ERROR_PARSE);
+    }
 
-    SuccessOrExit(error = Utils::CmdLineParser::ParseCmd(aBuf, args, kMaxArgs));
+    SuccessOrExit(error = ot::Utils::CmdLineParser::ParseCmd(aBuf, args, kMaxArgs));
     VerifyOrExit(!args[0].IsEmpty(), mCommandIsPending = false);
 
-    LogInput(args);
+    if (!mInternalDebugCommand)
+    {
+        LogInput(args);
 
 #if OPENTHREAD_CONFIG_DIAG_ENABLE
-    if (otDiagIsEnabled(GetInstancePtr()) && (args[0] != "diag") && (args[0] != "factoryreset"))
-    {
-        OutputLine("under diagnostics mode, execute 'diag stop' before running any other commands.");
-        ExitNow(error = OT_ERROR_INVALID_STATE);
-    }
+        if (otDiagIsEnabled(GetInstancePtr()) && (args[0] != "diag") && (args[0] != "factoryreset"))
+        {
+            OutputLine("under diagnostics mode, execute 'diag stop' before running any other commands.");
+            ExitNow(error = OT_ERROR_INVALID_STATE);
+        }
 #endif
+    }
 
     error = ProcessCommand(args);
 
@@ -385,26 +410,6 @@
     return error;
 }
 
-otError Interpreter::ParseEnableOrDisable(const Arg &aArg, bool &aEnable)
-{
-    otError error = OT_ERROR_NONE;
-
-    if (aArg == "enable")
-    {
-        aEnable = true;
-    }
-    else if (aArg == "disable")
-    {
-        aEnable = false;
-    }
-    else
-    {
-        error = OT_ERROR_INVALID_COMMAND;
-    }
-
-    return error;
-}
-
 #if OPENTHREAD_FTD || OPENTHREAD_MTD
 
 otError Interpreter::ParseJoinerDiscerner(Arg &aArg, otJoinerDiscerner &aDiscerner)
@@ -418,7 +423,7 @@
 
     VerifyOrExit(separator != nullptr, error = OT_ERROR_NOT_FOUND);
 
-    SuccessOrExit(error = Utils::CmdLineParser::ParseAsUint8(separator + 1, aDiscerner.mLength));
+    SuccessOrExit(error = ot::Utils::CmdLineParser::ParseAsUint8(separator + 1, aDiscerner.mLength));
     VerifyOrExit(aDiscerner.mLength > 0 && aDiscerner.mLength <= 64, error = OT_ERROR_INVALID_ARGS);
     *separator = '\0';
     error      = aArg.ParseAsUint64(aDiscerner.mValue);
@@ -427,57 +432,6 @@
     return error;
 }
 
-#if OPENTHREAD_CONFIG_PING_SENDER_ENABLE
-
-otError Interpreter::ParsePingInterval(const Arg &aArg, uint32_t &aInterval)
-{
-    otError        error    = OT_ERROR_NONE;
-    const char    *string   = aArg.GetCString();
-    const uint32_t msFactor = 1000;
-    uint32_t       factor   = msFactor;
-
-    aInterval = 0;
-
-    while (*string)
-    {
-        if ('0' <= *string && *string <= '9')
-        {
-            // In the case of seconds, change the base of already calculated value.
-            if (factor == msFactor)
-            {
-                aInterval *= 10;
-            }
-
-            aInterval += static_cast<uint32_t>(*string - '0') * factor;
-
-            // In the case of milliseconds, change the multiplier factor.
-            if (factor != msFactor)
-            {
-                factor /= 10;
-            }
-        }
-        else if (*string == '.')
-        {
-            // Accept only one dot character.
-            VerifyOrExit(factor == msFactor, error = OT_ERROR_INVALID_ARGS);
-
-            // Start analyzing hundreds of milliseconds.
-            factor /= 10;
-        }
-        else
-        {
-            ExitNow(error = OT_ERROR_INVALID_ARGS);
-        }
-
-        string++;
-    }
-
-exit:
-    return error;
-}
-
-#endif // OPENTHREAD_CONFIG_PING_SENDER_ENABLE
-
 otError Interpreter::ParsePreference(const Arg &aArg, otRoutePreference &aPreference)
 {
     otError error = OT_ERROR_NONE;
@@ -532,12 +486,13 @@
                                        otIp6Address &aAddress,
                                        bool         &aSynthesized)
 {
-    Error error = kErrorNone;
+    Error error = OT_ERROR_NONE;
 
     VerifyOrExit(!aArg.IsEmpty(), error = OT_ERROR_INVALID_ARGS);
     error        = aArg.ParseAsIp6Address(aAddress);
     aSynthesized = false;
-    if (error != kErrorNone)
+
+    if (error != OT_ERROR_NONE)
     {
         // It might be an IPv4 address, let's have a try.
         otIp4Address ip4Address;
@@ -637,6 +592,98 @@
         }
     }
 #endif // OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE
+#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
+    else if (aArgs[0] == "ephemeralkey")
+    {
+        /**
+         * @cli ba ephemeralkey
+         * @code
+         * ba ephemeralkey
+         * active
+         * Done
+         * @endcode
+         * @par api_copy
+         * #otBorderAgentIsEphemeralKeyActive
+         */
+        if (aArgs[1].IsEmpty())
+        {
+            OutputLine("%sactive", otBorderAgentIsEphemeralKeyActive(GetInstancePtr()) ? "" : "in");
+        }
+        /**
+         * @cli ba ephemeralkey set <keystring> [timeout-in-msec] [port]
+         * @code
+         * ba ephemeralkey set Z10X20g3J15w1000P60m16 5000 1234
+         * Done
+         * @endcode
+         * @par api_copy
+         * #otBorderAgentSetEphemeralKey
+         */
+        else if (aArgs[1] == "set")
+        {
+            uint32_t timeout = 0;
+            uint16_t port    = 0;
+
+            VerifyOrExit(!aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
+
+            if (!aArgs[3].IsEmpty())
+            {
+                SuccessOrExit(error = aArgs[3].ParseAsUint32(timeout));
+            }
+
+            if (!aArgs[4].IsEmpty())
+            {
+                SuccessOrExit(error = aArgs[4].ParseAsUint16(port));
+            }
+
+            error = otBorderAgentSetEphemeralKey(GetInstancePtr(), aArgs[2].GetCString(), timeout, port);
+        }
+        /**
+         * @cli ba ephemeralkey clear
+         * @code
+         * ba ephemeralkey clear
+         * Done
+         * @endcode
+         * @par api_copy
+         * #otBorderAgentClearEphemeralKey
+         */
+        else if (aArgs[1] == "clear")
+        {
+            otBorderAgentClearEphemeralKey(GetInstancePtr());
+        }
+        /**
+         * @cli ba ephemeralkey callback (enable, disable)
+         * @code
+         * ba ephemeralkey callback enable
+         * Done
+         * ba ephemeralkey set W10X1 5000 49155
+         * Done
+         * BorderAgent callback: Ephemeral key active, port:49155
+         * BorderAgent callback: Ephemeral key inactive
+         * @endcode
+         * @par api_copy
+         * #otBorderAgentSetEphemeralKeyCallback
+         */
+        else if (aArgs[1] == "callback")
+        {
+            bool enable;
+
+            SuccessOrExit(error = ParseEnableOrDisable(aArgs[2], enable));
+
+            if (enable)
+            {
+                otBorderAgentSetEphemeralKeyCallback(GetInstancePtr(), HandleBorderAgentEphemeralKeyStateChange, this);
+            }
+            else
+            {
+                otBorderAgentSetEphemeralKeyCallback(GetInstancePtr(), nullptr, nullptr);
+            }
+        }
+        else
+        {
+            error = OT_ERROR_INVALID_ARGS;
+        }
+    }
+#endif // OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
     else
     {
         ExitNow(error = OT_ERROR_INVALID_COMMAND);
@@ -645,6 +692,28 @@
 exit:
     return error;
 }
+
+#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
+void Interpreter::HandleBorderAgentEphemeralKeyStateChange(void *aContext)
+{
+    reinterpret_cast<Interpreter *>(aContext)->HandleBorderAgentEphemeralKeyStateChange();
+}
+
+void Interpreter::HandleBorderAgentEphemeralKeyStateChange(void)
+{
+    bool active = otBorderAgentIsEphemeralKeyActive(GetInstancePtr());
+
+    OutputFormat("BorderAgent callback: Ephemeral key %sactive", active ? "" : "in");
+
+    if (active)
+    {
+        OutputFormat(", port:%u", otBorderAgentGetUdpPort(GetInstancePtr()));
+    }
+
+    OutputNewLine();
+}
+#endif
+
 #endif // OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
 
 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
@@ -786,9 +855,6 @@
      */
     else if (aArgs[0] == "mappings")
     {
-        otNat64AddressMappingIterator iterator;
-        otNat64AddressMapping         mapping;
-
         static const char *const kNat64StatusLevel1Title[] = {"", "Address", "", "4 to 6", "6 to 4"};
 
         static const uint8_t kNat64StatusLevel1ColumnWidths[] = {
@@ -803,6 +869,9 @@
             18, 42, 18, 8, 10, 14, 10, 14,
         };
 
+        otNat64AddressMappingIterator iterator;
+        otNat64AddressMapping         mapping;
+
         OutputTableHeader(kNat64StatusLevel1Title, kNat64StatusLevel1ColumnWidths);
         OutputTableHeader(kNat64StatusTableHeader, kNat64StatusTableColumnWidths);
 
@@ -1357,7 +1426,9 @@
         }
     }
 #endif // OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
-#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && OPENTHREAD_FTD
+#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && \
+    (OPENTHREAD_FTD ||                          \
+     (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE))
     else if (aArgs[0] == "manager")
     {
         /**
@@ -1374,26 +1445,42 @@
          * @endcode
          * @par
          * Get the channel manager state.
-         * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` is required.
+         * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` or `OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE &&
+         * OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE` is required.
          * @sa otChannelManagerGetRequestedChannel
          */
         if (aArgs[1].IsEmpty())
         {
             OutputLine("channel: %u", otChannelManagerGetRequestedChannel(GetInstancePtr()));
+#if OPENTHREAD_FTD
             OutputLine("auto: %d", otChannelManagerGetAutoChannelSelectionEnabled(GetInstancePtr()));
+#endif
+#if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
+            OutputLine("autocsl: %u", otChannelManagerGetAutoCslChannelSelectionEnabled(GetInstancePtr()));
+#endif
 
+#if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && \
+     OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
+            if (otChannelManagerGetAutoChannelSelectionEnabled(GetInstancePtr()) ||
+                otChannelManagerGetAutoCslChannelSelectionEnabled(GetInstancePtr()))
+#elif OPENTHREAD_FTD
             if (otChannelManagerGetAutoChannelSelectionEnabled(GetInstancePtr()))
+#elif (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
+            if (otChannelManagerGetAutoCslChannelSelectionEnabled(GetInstancePtr()))
+#endif
             {
                 Mac::ChannelMask supportedMask(otChannelManagerGetSupportedChannels(GetInstancePtr()));
                 Mac::ChannelMask favoredMask(otChannelManagerGetFavoredChannels(GetInstancePtr()));
-
+#if OPENTHREAD_FTD
                 OutputLine("delay: %u", otChannelManagerGetDelay(GetInstancePtr()));
+#endif
                 OutputLine("interval: %lu", ToUlong(otChannelManagerGetAutoChannelSelectionInterval(GetInstancePtr())));
                 OutputLine("cca threshold: 0x%04x", otChannelManagerGetCcaFailureRateThreshold(GetInstancePtr()));
                 OutputLine("supported: %s", supportedMask.ToString().AsCString());
                 OutputLine("favored: %s", favoredMask.ToString().AsCString());
             }
         }
+#if OPENTHREAD_FTD
         /**
          * @cli channel manager change
          * @code
@@ -1422,7 +1509,9 @@
          * @cparam channel manager select @ca{skip-quality-check}
          * Use a `1` or `0` for the boolean `skip-quality-check`.
          * @par
-         * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE` are required.
+         * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` or `OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE &&
+         * OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE`, and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE`
+         * are required.
          * @par api_copy
          * #otChannelManagerRequestChannelSelect
          */
@@ -1433,7 +1522,7 @@
             SuccessOrExit(error = aArgs[2].ParseAsBool(enable));
             error = otChannelManagerRequestChannelSelect(GetInstancePtr(), enable);
         }
-#endif
+#endif // OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
         /**
          * @cli channel manager auto
          * @code
@@ -1444,7 +1533,9 @@
          * @cparam channel manager auto @ca{enable}
          * `1` is a boolean to `enable`.
          * @par
-         * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE` are required.
+         * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` or `OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE &&
+         * OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE`, and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE`
+         * are required.
          * @par api_copy
          * #otChannelManagerSetAutoChannelSelectionEnabled
          */
@@ -1455,6 +1546,32 @@
             SuccessOrExit(error = aArgs[2].ParseAsBool(enable));
             otChannelManagerSetAutoChannelSelectionEnabled(GetInstancePtr(), enable);
         }
+#endif // OPENTHREAD_FTD
+#if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
+        /**
+         * @cli channel manager autocsl
+         * @code
+         * channel manager autocsl 1
+         * Done
+         * @endcode
+         * @cparam channel manager autocsl @ca{enable}
+         * `1` is a boolean to `enable`.
+         * @par
+         * Enables or disables the auto channel selection functionality for a CSL channel.
+         * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` or `OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE &&
+         * OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE`, and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE`
+         * are required.
+         * @sa otChannelManagerSetAutoCslChannelSelectionEnabled
+         */
+        else if (aArgs[1] == "autocsl")
+        {
+            bool enable;
+
+            SuccessOrExit(error = aArgs[2].ParseAsBool(enable));
+            otChannelManagerSetAutoCslChannelSelectionEnabled(GetInstancePtr(), enable);
+        }
+#endif // (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
+#if OPENTHREAD_FTD
         /**
          * @cli channel manager delay
          * @code
@@ -1472,6 +1589,7 @@
         {
             error = ProcessGetSet(aArgs + 2, otChannelManagerGetDelay, otChannelManagerSetDelay);
         }
+#endif
         /**
          * @cli channel manager interval
          * @code
@@ -1481,7 +1599,9 @@
          * @endcode
          * @cparam channel manager interval @ca{interval-seconds}
          * @par
-         * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE` are required.
+         * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` or `OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE &&
+         * OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE`, and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE`
+         * are required.
          * @par api_copy
          * #otChannelManagerSetAutoChannelSelectionInterval
          */
@@ -1498,7 +1618,9 @@
          * @endcode
          * @cparam channel manager supported @ca{mask}
          * @par
-         * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE` are required.
+         * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` or `OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE &&
+         * OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE`, and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE`
+         * are required.
          * @par api_copy
          * #otChannelManagerSetSupportedChannels
          */
@@ -1515,7 +1637,9 @@
          * @endcode
          * @cparam channel manager favored @ca{mask}
          * @par
-         * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE` are required.
+         * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` or `OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE &&
+         * OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE`, and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE`
+         * are required.
          * @par api_copy
          * #otChannelManagerSetFavoredChannels
          */
@@ -1533,7 +1657,9 @@
          * @cparam channel manager threshold @ca{threshold-percent}
          * Use a hex value for `threshold-percent`. `0` maps to 0% and `0xffff` maps to 100%.
          * @par
-         * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE` are required.
+         * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` or `OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE &&
+         * OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE`, and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE`
+         * are required.
          * @par api_copy
          * #otChannelManagerSetCcaFailureRateThreshold
          */
@@ -2398,9 +2524,9 @@
      */
     if (aArgs[0].IsEmpty())
     {
-        OutputLine("Channel: %u", otLinkGetCslChannel(GetInstancePtr()));
-        OutputLine("Period: %luus", ToUlong(otLinkGetCslPeriod(GetInstancePtr())));
-        OutputLine("Timeout: %lus", ToUlong(otLinkGetCslTimeout(GetInstancePtr())));
+        OutputLine("channel: %u", otLinkGetCslChannel(GetInstancePtr()));
+        OutputLine("period: %luus", ToUlong(otLinkGetCslPeriod(GetInstancePtr())));
+        OutputLine("timeout: %lus", ToUlong(otLinkGetCslTimeout(GetInstancePtr())));
     }
     /**
      * @cli csl channel
@@ -2696,7 +2822,7 @@
     otCacheEntryIterator iterator;
     otCacheEntryInfo     entry;
 
-    memset(&iterator, 0, sizeof(iterator));
+    ClearAllBytes(iterator);
 
     while (true)
     {
@@ -4106,7 +4232,7 @@
     otError          error = OT_ERROR_NONE;
     otLinkModeConfig linkMode;
 
-    memset(&linkMode, 0, sizeof(otLinkModeConfig));
+    ClearAllBytes(linkMode);
 
     if (aArgs[0].IsEmpty())
     {
@@ -4129,7 +4255,7 @@
      * @par api_copy
      * #otThreadSetLinkMode
      * @cparam mode [@ca{rdn}]
-     * - `-`: -: no flags set (rx-off-when-idle, minimal Thread device, stable network data)
+     * - `-`: no flags set (rx-off-when-idle, minimal Thread device, stable network data)
      * - `r`: rx-on-when-idle
      * - `d`: Full Thread Device
      * - `n`: Full Network Data
@@ -5530,55 +5656,6 @@
 #endif
 
 #if OPENTHREAD_CONFIG_PING_SENDER_ENABLE
-
-void Interpreter::HandlePingReply(const otPingSenderReply *aReply, void *aContext)
-{
-    static_cast<Interpreter *>(aContext)->HandlePingReply(aReply);
-}
-
-void Interpreter::HandlePingReply(const otPingSenderReply *aReply)
-{
-    OutputFormat("%u bytes from ", static_cast<uint16_t>(aReply->mSize + sizeof(otIcmp6Header)));
-    OutputIp6Address(aReply->mSenderAddress);
-    OutputLine(": icmp_seq=%u hlim=%u time=%ums", aReply->mSequenceNumber, aReply->mHopLimit, aReply->mRoundTripTime);
-}
-
-void Interpreter::HandlePingStatistics(const otPingSenderStatistics *aStatistics, void *aContext)
-{
-    static_cast<Interpreter *>(aContext)->HandlePingStatistics(aStatistics);
-}
-
-void Interpreter::HandlePingStatistics(const otPingSenderStatistics *aStatistics)
-{
-    OutputFormat("%u packets transmitted, %u packets received.", aStatistics->mSentCount, aStatistics->mReceivedCount);
-
-    if ((aStatistics->mSentCount != 0) && !aStatistics->mIsMulticast &&
-        aStatistics->mReceivedCount <= aStatistics->mSentCount)
-    {
-        uint32_t packetLossRate =
-            1000 * (aStatistics->mSentCount - aStatistics->mReceivedCount) / aStatistics->mSentCount;
-
-        OutputFormat(" Packet loss = %lu.%u%%.", ToUlong(packetLossRate / 10),
-                     static_cast<uint16_t>(packetLossRate % 10));
-    }
-
-    if (aStatistics->mReceivedCount != 0)
-    {
-        uint32_t avgRoundTripTime = 1000 * aStatistics->mTotalRoundTripTime / aStatistics->mReceivedCount;
-
-        OutputFormat(" Round-trip min/avg/max = %u/%u.%u/%u ms.", aStatistics->mMinRoundTripTime,
-                     static_cast<uint16_t>(avgRoundTripTime / 1000), static_cast<uint16_t>(avgRoundTripTime % 1000),
-                     aStatistics->mMaxRoundTripTime);
-    }
-
-    OutputNewLine();
-
-    if (!mPingIsAsync)
-    {
-        OutputResult(OT_ERROR_NONE);
-    }
-}
-
 /**
  * @cli ping
  * @code
@@ -5614,108 +5691,7 @@
  * Note: The command will return InvalidState when the preferred NAT64 prefix is unavailable.
  * @sa otPingSenderPing
  */
-template <> otError Interpreter::Process<Cmd("ping")>(Arg aArgs[])
-{
-    otError            error = OT_ERROR_NONE;
-    otPingSenderConfig config;
-    bool               async = false;
-    bool               nat64SynthesizedAddress;
-
-    /**
-     * @cli ping stop
-     * @code
-     * ping stop
-     * Done
-     * @endcode
-     * @par
-     * Stop sending ICMPv6 Echo Requests.
-     * @sa otPingSenderStop
-     */
-    if (aArgs[0] == "stop")
-    {
-        otPingSenderStop(GetInstancePtr());
-        ExitNow();
-    }
-    else if (aArgs[0] == "async")
-    {
-        async = true;
-        aArgs++;
-    }
-
-    memset(&config, 0, sizeof(config));
-
-    if (aArgs[0] == "-I")
-    {
-        SuccessOrExit(error = aArgs[1].ParseAsIp6Address(config.mSource));
-
-#if !OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
-        VerifyOrExit(otIp6HasUnicastAddress(GetInstancePtr(), &config.mSource), error = OT_ERROR_INVALID_ARGS);
-#endif
-        aArgs += 2;
-    }
-
-    if (aArgs[0] == "-m")
-    {
-        config.mMulticastLoop = true;
-        aArgs++;
-    }
-
-    SuccessOrExit(error = ParseToIp6Address(GetInstancePtr(), aArgs[0], config.mDestination, nat64SynthesizedAddress));
-    if (nat64SynthesizedAddress)
-    {
-        OutputFormat("Pinging synthesized IPv6 address: ");
-        OutputIp6AddressLine(config.mDestination);
-    }
-
-    if (!aArgs[1].IsEmpty())
-    {
-        SuccessOrExit(error = aArgs[1].ParseAsUint16(config.mSize));
-    }
-
-    if (!aArgs[2].IsEmpty())
-    {
-        SuccessOrExit(error = aArgs[2].ParseAsUint16(config.mCount));
-    }
-
-    if (!aArgs[3].IsEmpty())
-    {
-        SuccessOrExit(error = ParsePingInterval(aArgs[3], config.mInterval));
-    }
-
-    if (!aArgs[4].IsEmpty())
-    {
-        SuccessOrExit(error = aArgs[4].ParseAsUint8(config.mHopLimit));
-        config.mAllowZeroHopLimit = (config.mHopLimit == 0);
-    }
-
-    if (!aArgs[5].IsEmpty())
-    {
-        uint32_t timeout;
-
-        SuccessOrExit(error = ParsePingInterval(aArgs[5], timeout));
-        VerifyOrExit(timeout <= NumericLimits<uint16_t>::kMax, error = OT_ERROR_INVALID_ARGS);
-        config.mTimeout = static_cast<uint16_t>(timeout);
-    }
-
-    VerifyOrExit(aArgs[6].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
-
-    config.mReplyCallback      = Interpreter::HandlePingReply;
-    config.mStatisticsCallback = Interpreter::HandlePingStatistics;
-    config.mCallbackContext    = this;
-
-    SuccessOrExit(error = otPingSenderPing(GetInstancePtr(), &config));
-
-    mPingIsAsync = async;
-
-    if (!async)
-    {
-        error = OT_ERROR_PENDING;
-    }
-
-exit:
-    return error;
-}
-
+template <> otError Interpreter::Process<Cmd("ping")>(Arg aArgs[]) { return mPing.Process(aArgs); }
 #endif // OPENTHREAD_CONFIG_PING_SENDER_ENABLE
 
 /**
@@ -5840,7 +5816,7 @@
 {
     otError error = OT_ERROR_NONE;
 
-    memset(&aConfig, 0, sizeof(otBorderRouterConfig));
+    ClearAllBytes(aConfig);
 
     SuccessOrExit(error = aArgs[0].ParseAsIp6Prefix(aConfig.mPrefix));
     aArgs++;
@@ -6267,7 +6243,7 @@
 {
     otError error = OT_ERROR_NONE;
 
-    memset(&aConfig, 0, sizeof(otExternalRouteConfig));
+    ClearAllBytes(aConfig);
 
     SuccessOrExit(error = aArgs[0].ParseAsIp6Prefix(aConfig.mPrefix));
     aArgs++;
@@ -7240,6 +7216,128 @@
     return error;
 }
 
+/**
+ * @cli debug
+ * @par
+ * Executes a series of CLI commands to gather information about the device and thread network. This is intended for
+ * debugging.
+ * The output will display each executed CLI command preceded by `$`, followed by the corresponding command's
+ * generated output.
+ * The generated output encompasses the following information:
+ * - Version
+ * - Current state
+ * - RLOC16, extended MAC address
+ * - Unicast and multicast IPv6 address list
+ * - Channel
+ * - PAN ID and extended PAN ID
+ * - Network Data
+ * - Partition ID
+ * - Leader Data
+ * @par
+ * If the device is operating as FTD:
+ * - Child and neighbor table
+ * - Router table and next hop info
+ * - Address cache table
+ * - Registered MTD child IPv6 address
+ * - Device properties
+ * @par
+ * If the device supports and acts as an SRP client:
+ * - SRP client state
+ * - SRP client services and host info
+ * @par
+ * If the device supports and acts as an SRP sever:
+ * - SRP server state and address mode
+ * - SRP server registered hosts and services
+ * @par
+ * If the device supports TREL:
+ * - TREL status and peer table
+ * @par
+ * If the device supports and acts as a border router:
+ * - BR state
+ * - BR prefixes (OMR, on-link, NAT64)
+ * - Discovered prefix table
+ */
+template <> otError Interpreter::Process<Cmd("debug")>(Arg aArgs[])
+{
+    static constexpr uint16_t kMaxDebugCommandSize = 30;
+
+    static const char *const kDebugCommands[] = {
+        "version",
+        "state",
+        "rloc16",
+        "extaddr",
+        "ipaddrs",
+        "ipmaddrs",
+        "channel",
+        "panid",
+        "extpanid",
+        "netdata show",
+        "netdata show -x",
+        "partitionid",
+        "leaderdata",
+#if OPENTHREAD_FTD
+        "child table",
+        "childip",
+        "neighbor table",
+        "router table",
+        "nexthop",
+        "eidcache",
+#if OPENTHREAD_CONFIG_MLE_DEVICE_PROPERTY_LEADER_WEIGHT_ENABLE
+        "deviceprops",
+#endif
+#endif // OPENTHREAD_FTD
+#if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE
+        "srp client state",
+        "srp client host",
+        "srp client service",
+        "srp client server",
+#endif
+#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
+        "srp server state",
+        "srp server addrmode",
+        "srp server host",
+        "srp server service",
+#endif
+#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
+        "trel",
+        "trel peers",
+#endif
+#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
+        "br state",
+        "br omrprefix",
+        "br onlinkprefix",
+        "br prefixtable",
+#if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
+        "br nat64prefix",
+#endif
+#endif
+        "bufferinfo",
+    };
+
+    char commandString[kMaxDebugCommandSize];
+
+    OT_UNUSED_VARIABLE(aArgs);
+
+    mInternalDebugCommand = true;
+
+    for (const char *debugCommand : kDebugCommands)
+    {
+        strncpy(commandString, debugCommand, sizeof(commandString) - 1);
+        commandString[sizeof(commandString) - 1] = '\0';
+
+        OutputLine("$ %s", commandString);
+        ProcessLine(commandString);
+    }
+
+    mInternalDebugCommand = false;
+
+    return OT_ERROR_NONE;
+}
+
+#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE && OPENTHREAD_CONFIG_CLI_BLE_SECURE_ENABLE
+template <> otError Interpreter::Process<Cmd("tcat")>(Arg aArgs[]) { return mTcat.Process(aArgs); }
+#endif
+
 #if OPENTHREAD_CONFIG_TCP_ENABLE && OPENTHREAD_CONFIG_CLI_TCP_ENABLE
 template <> otError Interpreter::Process<Cmd("tcp")>(Arg aArgs[]) { return mTcp.Process(aArgs); }
 #endif
@@ -7619,7 +7717,7 @@
         }
         else
         {
-            VerifyOrExit(aArgs[1].IsEmpty(), error = kErrorInvalidArgs);
+            VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
         }
 
         if (isTable)
@@ -7658,6 +7756,41 @@
             }
         }
     }
+    /**
+     * @cli trel counters
+     * @code
+     * trel counters
+     * Inbound:  Packets 32 Bytes 4000
+     * Outbound: Packets 4 Bytes 320 Failures 1
+     * Done
+     * @endcode
+     * @par api_copy
+     * #otTrelGetCounters
+     */
+    else if (aArgs[0] == "counters")
+    {
+        if (aArgs[1].IsEmpty())
+        {
+            OutputTrelCounters(*otTrelGetCounters(GetInstancePtr()));
+        }
+        /**
+         * @cli trel counters reset
+         * @code
+         * trel counters reset
+         * Done
+         * @endcode
+         * @par api_copy
+         * #otTrelResetCounters
+         */
+        else if ((aArgs[1] == "reset") && aArgs[2].IsEmpty())
+        {
+            otTrelResetCounters(GetInstancePtr());
+        }
+        else
+        {
+            error = OT_ERROR_INVALID_ARGS;
+        }
+    }
     else
     {
         error = OT_ERROR_INVALID_ARGS;
@@ -7666,6 +7799,19 @@
 exit:
     return error;
 }
+
+void Interpreter::OutputTrelCounters(const otTrelCounters &aCounters)
+{
+    Uint64StringBuffer u64StringBuffer;
+
+    OutputFormat("Inbound: Packets %s ", Uint64ToString(aCounters.mRxPackets, u64StringBuffer));
+    OutputLine("Bytes %s", Uint64ToString(aCounters.mRxBytes, u64StringBuffer));
+
+    OutputFormat("Outbound: Packets %s ", Uint64ToString(aCounters.mTxPackets, u64StringBuffer));
+    OutputFormat("Bytes %s ", Uint64ToString(aCounters.mTxBytes, u64StringBuffer));
+    OutputLine("Failures %s", Uint64ToString(aCounters.mTxFailure, u64StringBuffer));
+}
+
 #endif
 
 template <> otError Interpreter::Process<Cmd("vendor")>(Arg aArgs[])
@@ -7690,7 +7836,7 @@
         error = ProcessGet(aArgs, otThreadGetVendorName);
 #else
         /**
-         * @cli vendor name (name)
+         * @cli vendor name (set)
          * @code
          * vendor name nest
          * Done
@@ -7720,7 +7866,7 @@
         error = ProcessGet(aArgs, otThreadGetVendorModel);
 #else
         /**
-         * @cli vendor model (name)
+         * @cli vendor model (set)
          * @code
          * vendor model Hub\ Max
          * Done
@@ -7750,7 +7896,7 @@
         error = ProcessGet(aArgs, otThreadGetVendorSwVersion);
 #else
         /**
-         * @cli vendor swversion (version)
+         * @cli vendor swversion (set)
          * @code
          * vendor swversion Marble3.5.1
          * Done
@@ -7762,6 +7908,36 @@
         error = ProcessGetSet(aArgs, otThreadGetVendorSwVersion, otThreadSetVendorSwVersion);
 #endif
     }
+    /**
+     * @cli vendor appurl
+     * @code
+     * vendor appurl
+     * http://www.example.com
+     * Done
+     * @endcode
+     * @par api_copy
+     * #otThreadGetVendorAppUrl
+     */
+    else if (aArgs[0] == "appurl")
+    {
+        aArgs++;
+
+#if !OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE
+        error = ProcessGet(aArgs, otThreadGetVendorAppUrl);
+#else
+        /**
+         * @cli vendor appurl (set)
+         * @code
+         * vendor appurl http://www.example.com
+         * Done
+         * @endcode
+         * @par api_copy
+         * #otThreadSetVendorAppUrl
+         * @cparam vendor appurl @ca{url}
+         */
+        error = ProcessGetSet(aArgs, otThreadGetVendorAppUrl, otThreadSetVendorAppUrl);
+#endif
+    }
 
     return error;
 }
@@ -7770,9 +7946,11 @@
 
 template <> otError Interpreter::Process<Cmd("networkdiagnostic")>(Arg aArgs[])
 {
+    static constexpr uint16_t kMaxTlvs = 35;
+
     otError      error = OT_ERROR_NONE;
     otIp6Address address;
-    uint8_t      tlvTypes[OT_NETWORK_DIAGNOSTIC_TYPELIST_MAX_ENTRIES];
+    uint8_t      tlvTypes[kMaxTlvs];
     uint8_t      count = 0;
 
     SuccessOrExit(error = aArgs[1].ParseAsIp6Address(address));
@@ -7786,9 +7964,9 @@
     /**
      * @cli networkdiagnostic get
      * @code
-     * networkdiagnostic get fdde:ad00:beef:0:0:ff:fe00:fc00 0 1 6e
+     * networkdiagnostic get fdde:ad00:beef:0:0:ff:fe00:fc00 0 1 6 23
      * DIAG_GET.rsp/ans: 00080e336e1c41494e1c01020c000608640b0f674074c503
-     * Ext Address: '0e336e1c41494e1c'
+     * Ext Address: 0e336e1c41494e1c
      * Rloc16: 0x0c00
      * Leader Data:
      *     PartitionId: 0x640b0f67
@@ -7796,6 +7974,7 @@
      *     DataVersion: 116
      *     StableDataVersion: 197
      *     LeaderRouterId: 0x03
+     * EUI64: 18b4300000000004
      * Done
      * @endcode
      * @code
@@ -7805,7 +7984,7 @@
      * Rloc16: 0x0c00
      * Done
      * DIAG_GET.rsp/ans: 00083efcdb7e3f9eb0f201021800
-     * Ext Address: '3efcdb7e3f9eb0f2'
+     * Ext Address: 3efcdb7e3f9eb0f2
      * Rloc16: 0x1800
      * Done
      * @endcode
@@ -7828,12 +8007,15 @@
      * - `16`: Child Table TLV
      * - `17`: Channel Pages TLV
      * - `19`: Max Child Timeout TLV
+     * - `23`: EUI64 TLV
+     * - `24`: Version TLV (version number for the protocols and features)
      * - `25`: Vendor Name TLV
      * - `26`: Vendor Model TLV
      * - `27`: Vendor SW Version TLV
-     * - `28`: Thread Stack Version TLV
+     * - `28`: Thread Stack Version TLV (version identifier as UTF-8 string for Thread stack codebase/commit/version)
      * - `29`: Child TLV
      * - `34`: MLE Counters TLV
+     * - `35`: Vendor App URL TLV
      * @par
      * Sends a network diagnostic request to retrieve specified Type Length Values (TLVs)
      * for the specified addresses(es).
@@ -7856,8 +8038,9 @@
      * @cparam networkdiagnostic reset @ca{addr} @ca{type(s)}
      * @par
      * Sends a network diagnostic request to reset the specified Type Length Values (TLVs)
-     * on the specified address(es). The only supported TLV value at this time for this
-     * command is `9` (MAC Counters TLV).
+     * on the specified address(es). This command only supports the
+     * following TLV values: `9` (MAC Counters TLV) or `34` (MLE
+     * Counters TLV)
      * @sa otThreadSendDiagnosticReset
      */
     else if (aArgs[0] == "reset")
@@ -7920,7 +8103,7 @@
         switch (diagTlv.mType)
         {
         case OT_NETWORK_DIAGNOSTIC_TLV_EXT_ADDRESS:
-            OutputFormat("Ext Address: '");
+            OutputFormat("Ext Address: ");
             OutputExtAddressLine(diagTlv.mData.mExtAddress);
             break;
         case OT_NETWORK_DIAGNOSTIC_TLV_SHORT_ADDRESS:
@@ -7946,7 +8129,7 @@
             OutputLeaderData(kIndentSize, diagTlv.mData.mLeaderData);
             break;
         case OT_NETWORK_DIAGNOSTIC_TLV_NETWORK_DATA:
-            OutputFormat("Network Data: '");
+            OutputFormat("Network Data: ");
             OutputBytesLine(diagTlv.mData.mNetworkData.m8, diagTlv.mData.mNetworkData.mCount);
             break;
         case OT_NETWORK_DIAGNOSTIC_TLV_IP6_ADDR_LIST:
@@ -7987,6 +8170,10 @@
         case OT_NETWORK_DIAGNOSTIC_TLV_MAX_CHILD_TIMEOUT:
             OutputLine("Max Child Timeout: %lu", ToUlong(diagTlv.mData.mMaxChildTimeout));
             break;
+        case OT_NETWORK_DIAGNOSTIC_TLV_EUI64:
+            OutputFormat("EUI64: ");
+            OutputExtAddressLine(diagTlv.mData.mEui64);
+            break;
         case OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_NAME:
             OutputLine("Vendor Name: %s", diagTlv.mData.mVendorName);
             break;
@@ -7996,6 +8183,9 @@
         case OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_SW_VERSION:
             OutputLine("Vendor SW Version: %s", diagTlv.mData.mVendorSwVersion);
             break;
+        case OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_APP_URL:
+            OutputLine("Vendor App URL: %s", diagTlv.mData.mVendorAppUrl);
+            break;
         case OT_NETWORK_DIAGNOSTIC_TLV_THREAD_STACK_VERSION:
             OutputLine("Thread Stack Version: %s", diagTlv.mData.mThreadStackVersion);
             break;
@@ -8174,76 +8364,6 @@
     Interpreter::sInterpreter = new (&sInterpreterRaw) Interpreter(instance, aCallback, aContext);
 }
 
-otError Interpreter::ProcessEnableDisable(Arg aArgs[], SetEnabledHandler aSetEnabledHandler)
-{
-    otError error = OT_ERROR_NONE;
-    bool    enable;
-
-    if (ParseEnableOrDisable(aArgs[0], enable) == OT_ERROR_NONE)
-    {
-        aSetEnabledHandler(GetInstancePtr(), enable);
-    }
-    else
-    {
-        error = OT_ERROR_INVALID_COMMAND;
-    }
-
-    return error;
-}
-
-otError Interpreter::ProcessEnableDisable(Arg aArgs[], SetEnabledHandlerFailable aSetEnabledHandler)
-{
-    otError error = OT_ERROR_NONE;
-    bool    enable;
-
-    if (ParseEnableOrDisable(aArgs[0], enable) == OT_ERROR_NONE)
-    {
-        error = aSetEnabledHandler(GetInstancePtr(), enable);
-    }
-    else
-    {
-        error = OT_ERROR_INVALID_COMMAND;
-    }
-
-    return error;
-}
-
-otError Interpreter::ProcessEnableDisable(Arg               aArgs[],
-                                          IsEnabledHandler  aIsEnabledHandler,
-                                          SetEnabledHandler aSetEnabledHandler)
-{
-    otError error = OT_ERROR_NONE;
-
-    if (aArgs[0].IsEmpty())
-    {
-        OutputEnabledDisabledStatus(aIsEnabledHandler(GetInstancePtr()));
-    }
-    else
-    {
-        error = ProcessEnableDisable(aArgs, aSetEnabledHandler);
-    }
-
-    return error;
-}
-
-otError Interpreter::ProcessEnableDisable(Arg                       aArgs[],
-                                          IsEnabledHandler          aIsEnabledHandler,
-                                          SetEnabledHandlerFailable aSetEnabledHandler)
-{
-    otError error = OT_ERROR_NONE;
-
-    if (aArgs[0].IsEmpty())
-    {
-        OutputEnabledDisabledStatus(aIsEnabledHandler(GetInstancePtr()));
-    }
-    else
-    {
-        error = ProcessEnableDisable(aArgs, aSetEnabledHandler);
-    }
-
-    return error;
-}
-
 void Interpreter::OutputPrompt(void)
 {
 #if OPENTHREAD_CONFIG_CLI_PROMPT_ENABLE
@@ -8338,6 +8458,7 @@
         CmdEntry("csl"),
 #endif
         CmdEntry("dataset"),
+        CmdEntry("debug"),
 #if OPENTHREAD_FTD
         CmdEntry("delaytimermin"),
 #endif
@@ -8509,6 +8630,9 @@
         CmdEntry("srp"),
 #endif
         CmdEntry("state"),
+#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE && OPENTHREAD_CONFIG_CLI_BLE_SECURE_ENABLE
+        CmdEntry("tcat"),
+#endif
 #if OPENTHREAD_CONFIG_TCP_ENABLE && OPENTHREAD_CONFIG_CLI_TCP_ENABLE
         CmdEntry("tcp"),
 #endif
diff --git a/src/cli/cli.hpp b/src/cli/cli.hpp
index 81c5fe7..75ea629 100644
--- a/src/cli/cli.hpp
+++ b/src/cli/cli.hpp
@@ -68,11 +68,13 @@
 #include "cli/cli_link_metrics.hpp"
 #include "cli/cli_mac_filter.hpp"
 #include "cli/cli_network_data.hpp"
-#include "cli/cli_output.hpp"
+#include "cli/cli_ping.hpp"
 #include "cli/cli_srp_client.hpp"
 #include "cli/cli_srp_server.hpp"
+#include "cli/cli_tcat.hpp"
 #include "cli/cli_tcp.hpp"
 #include "cli/cli_udp.hpp"
+#include "cli/cli_utils.hpp"
 #if OPENTHREAD_CONFIG_COAP_API_ENABLE
 #include "cli/cli_coap.hpp"
 #endif
@@ -106,7 +108,7 @@
  * Implements the CLI interpreter.
  *
  */
-class Interpreter : public OutputImplementer, public Output
+class Interpreter : public OutputImplementer, public Utils
 {
 #if OPENTHREAD_FTD || OPENTHREAD_MTD
     friend class Br;
@@ -116,6 +118,7 @@
     friend class Joiner;
     friend class LinkMetrics;
     friend class NetworkData;
+    friend class PingSender;
     friend class SrpClient;
     friend class SrpServer;
 #endif
@@ -125,8 +128,6 @@
     friend void otCliOutputFormat(const char *aFmt, ...);
 
 public:
-    typedef Utils::CmdLineParser::Arg Arg;
-
     /**
      * Constructor
      *
@@ -176,19 +177,6 @@
     void ProcessLine(char *aBuf);
 
     /**
-     * Checks a given argument string against "enable" or "disable" commands.
-     *
-     * @param[in]  aArg     The argument string to parse.
-     * @param[out] aEnable  Boolean variable to return outcome on success.
-     *                      Set to TRUE for "enable" command, and FALSE for "disable" command.
-     *
-     * @retval OT_ERROR_NONE             Successfully parsed the @p aString and updated @p aEnable.
-     * @retval OT_ERROR_INVALID_COMMAND  The @p aString is not "enable" or "disable" command.
-     *
-     */
-    static otError ParseEnableOrDisable(const Arg &aArg, bool &aEnable);
-
-    /**
      * Adds commands to the user command table.
      *
      * @param[in]  aCommands  A pointer to an array with user commands.
@@ -255,14 +243,14 @@
      * If the argument string is an IPv4 address, this method will try to synthesize an IPv6 address using preferred
      * NAT64 prefix in the network data.
      *
-     * @param[in]  aInstance       A pointer to openthread instance.
+     * @param[in]  aInstance       A pointer to OpenThread instance.
      * @param[in]  aArg            The argument string to parse.
      * @param[out] aAddress        A reference to an `otIp6Address` to output the parsed IPv6 address.
      * @param[out] aSynthesized    Whether @p aAddress is synthesized from an IPv4 address.
      *
-     * @retval OT_ERROR_NONE          The argument was parsed successfully.
-     * @retval OT_ERROR_INVALID_ARGS  The argument is empty or does not contain valid IP address.
-     * @retval OT_ERROR_INVALID_STATE No valid NAT64 prefix in the network data.
+     * @retval OT_ERROR_NONE           The argument was parsed successfully.
+     * @retval OT_ERROR_INVALID_ARGS   The argument is empty or does not contain a valid IP address.
+     * @retval OT_ERROR_INVALID_STATE  No valid NAT64 prefix in the network data.
      *
      */
     static otError ParseToIp6Address(otInstance   *aInstance,
@@ -274,14 +262,10 @@
     static Interpreter *sInterpreter;
 
 private:
-    enum
-    {
-        kIndentSize            = 4,
-        kMaxArgs               = 32,
-        kMaxAutoAddresses      = 8,
-        kMaxLineLength         = OPENTHREAD_CONFIG_CLI_MAX_LINE_LENGTH,
-        kMaxUserCommandEntries = OPENTHREAD_CONFIG_CLI_MAX_USER_CMD_ENTRIES,
-    };
+    static constexpr uint8_t  kIndentSize            = 4;
+    static constexpr uint16_t kMaxArgs               = 32;
+    static constexpr uint16_t kMaxLineLength         = OPENTHREAD_CONFIG_CLI_MAX_LINE_LENGTH;
+    static constexpr uint16_t kMaxUserCommandEntries = OPENTHREAD_CONFIG_CLI_MAX_USER_CMD_ENTRIES;
 
     static constexpr uint32_t kNetworkDiagnosticTimeoutMsecs = 5000;
     static constexpr uint32_t kLocateTimeoutMsecs            = 2500;
@@ -290,100 +274,9 @@
 
     using Command = CommandEntry<Interpreter>;
 
-    template <typename ValueType> using GetHandler         = ValueType (&)(otInstance *);
-    template <typename ValueType> using SetHandler         = void (&)(otInstance *, ValueType);
-    template <typename ValueType> using SetHandlerFailable = otError (&)(otInstance *, ValueType);
-    using IsEnabledHandler                                 = bool (&)(otInstance *);
-    using SetEnabledHandler                                = void (&)(otInstance *, bool);
-    using SetEnabledHandlerFailable                        = otError (&)(otInstance *, bool);
-
-    // Returns format string to output a `ValueType` (e.g., "%u" for `uint16_t`).
-    template <typename ValueType> static constexpr const char *FormatStringFor(void);
-
-    // General template implementation.
-    // Specializations for `uint32_t` and `int32_t` are added at the end.
-    template <typename ValueType> otError ProcessGet(Arg aArgs[], GetHandler<ValueType> aGetHandler)
-    {
-        static_assert(
-            TypeTraits::IsSame<ValueType, uint8_t>::kValue || TypeTraits::IsSame<ValueType, uint16_t>::kValue ||
-                TypeTraits::IsSame<ValueType, int8_t>::kValue || TypeTraits::IsSame<ValueType, int16_t>::kValue ||
-                TypeTraits::IsSame<ValueType, const char *>::kValue,
-            "ValueType must be an  8, 16 `int` or `uint` type, or a `const char *`");
-
-        otError error = OT_ERROR_NONE;
-
-        VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
-        OutputLine(FormatStringFor<ValueType>(), aGetHandler(GetInstancePtr()));
-
-    exit:
-        return error;
-    }
-
-    template <typename ValueType> otError ProcessSet(Arg aArgs[], SetHandler<ValueType> aSetHandler)
-    {
-        otError   error;
-        ValueType value;
-
-        SuccessOrExit(error = aArgs[0].ParseAs<ValueType>(value));
-        VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
-
-        aSetHandler(GetInstancePtr(), value);
-
-    exit:
-        return error;
-    }
-
-    template <typename ValueType> otError ProcessSet(Arg aArgs[], SetHandlerFailable<ValueType> aSetHandler)
-    {
-        otError   error;
-        ValueType value;
-
-        SuccessOrExit(error = aArgs[0].ParseAs<ValueType>(value));
-        VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
-
-        error = aSetHandler(GetInstancePtr(), value);
-
-    exit:
-        return error;
-    }
-
-    template <typename ValueType>
-    otError ProcessGetSet(Arg aArgs[], GetHandler<ValueType> aGetHandler, SetHandler<ValueType> aSetHandler)
-    {
-        otError error = ProcessGet(aArgs, aGetHandler);
-
-        VerifyOrExit(error != OT_ERROR_NONE);
-        error = ProcessSet(aArgs, aSetHandler);
-
-    exit:
-        return error;
-    }
-
-    template <typename ValueType>
-    otError ProcessGetSet(Arg aArgs[], GetHandler<ValueType> aGetHandler, SetHandlerFailable<ValueType> aSetHandler)
-    {
-        otError error = ProcessGet(aArgs, aGetHandler);
-
-        VerifyOrExit(error != OT_ERROR_NONE);
-        error = ProcessSet(aArgs, aSetHandler);
-
-    exit:
-        return error;
-    }
-
-    otError ProcessEnableDisable(Arg aArgs[], SetEnabledHandler aSetEnabledHandler);
-    otError ProcessEnableDisable(Arg aArgs[], SetEnabledHandlerFailable aSetEnabledHandler);
-    otError ProcessEnableDisable(Arg aArgs[], IsEnabledHandler aIsEnabledHandler, SetEnabledHandler aSetEnabledHandler);
-    otError ProcessEnableDisable(Arg                       aArgs[],
-                                 IsEnabledHandler          aIsEnabledHandler,
-                                 SetEnabledHandlerFailable aSetEnabledHandler);
-
     void OutputPrompt(void);
     void OutputResult(otError aError);
 
-#if OPENTHREAD_CONFIG_PING_SENDER_ENABLE
-    otError ParsePingInterval(const Arg &aArg, uint32_t &aInterval);
-#endif
     static otError ParseJoinerDiscerner(Arg &aArg, otJoinerDiscerner &aDiscerner);
 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
     static otError ParsePrefix(Arg aArgs[], otBorderRouterConfig &aConfig);
@@ -457,10 +350,6 @@
     void OutputMultiRadioInfo(const otMultiRadioNeighborInfo &aMultiRadioInfo);
 #endif
 
-#if OPENTHREAD_CONFIG_PING_SENDER_ENABLE
-    static void HandlePingReply(const otPingSenderReply *aReply, void *aContext);
-    static void HandlePingStatistics(const otPingSenderStatistics *aStatistics, void *aContext);
-#endif
     static void HandleActiveScanResult(otActiveScanResult *aResult, void *aContext);
     static void HandleEnergyScanResult(otEnergyScanResult *aResult, void *aContext);
     static void HandleLinkPcapReceive(const otRadioFrame *aFrame, bool aIsTx, void *aContext);
@@ -482,6 +371,9 @@
     void OutputChildTableEntry(uint8_t aIndentSize, const otNetworkDiagChildEntry &aChildEntry);
 #endif
 
+#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
+    void OutputTrelCounters(const otTrelCounters &aCounters);
+#endif
 #if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
     void OutputNat64Counters(const otNat64Counters &aCounters);
 #endif
@@ -493,10 +385,6 @@
     static void HandleSntpResponse(void *aContext, uint64_t aTime, otError aResult);
 #endif
 
-#if OPENTHREAD_CONFIG_PING_SENDER_ENABLE
-    void HandlePingReply(const otPingSenderReply *aReply);
-    void HandlePingStatistics(const otPingSenderStatistics *aStatistics);
-#endif
     void HandleActiveScanResult(otActiveScanResult *aResult);
     void HandleEnergyScanResult(otEnergyScanResult *aResult);
     void HandleLinkPcapReceive(const otRadioFrame *aFrame, bool aIsTx);
@@ -504,6 +392,11 @@
     void HandleSntpResponse(uint64_t aTime, otError aResult);
 #endif
 
+#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE && OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
+    static void HandleBorderAgentEphemeralKeyStateChange(void *aContext);
+    void        HandleBorderAgentEphemeralKeyStateChange(void);
+#endif
+
     static void HandleDetachGracefullyResult(void *aContext);
     void        HandleDetachGracefullyResult(void);
 
@@ -532,6 +425,7 @@
 
     UserCommandsEntry mUserCommands[kMaxUserCommandEntries];
     bool              mCommandIsPending;
+    bool              mInternalDebugCommand;
 
     TimerMilliContext mTimer;
 
@@ -594,56 +488,19 @@
 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
     LinkMetrics mLinkMetrics;
 #endif
+#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE && OPENTHREAD_CONFIG_CLI_BLE_SECURE_ENABLE
+    Tcat mTcat;
+#endif
+#if OPENTHREAD_CONFIG_PING_SENDER_ENABLE
+    PingSender mPing;
+#endif
 #endif // OPENTHREAD_FTD || OPENTHREAD_MTD
 
-#if OPENTHREAD_CONFIG_PING_SENDER_ENABLE
-    bool mPingIsAsync : 1;
-#endif
 #if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE
     bool mLocateInProgress : 1;
 #endif
 };
 
-// Specializations of `FormatStringFor<ValueType>()`
-
-template <> inline constexpr const char *Interpreter::FormatStringFor<uint8_t>(void) { return "%u"; }
-
-template <> inline constexpr const char *Interpreter::FormatStringFor<uint16_t>(void) { return "%u"; }
-
-template <> inline constexpr const char *Interpreter::FormatStringFor<uint32_t>(void) { return "%lu"; }
-
-template <> inline constexpr const char *Interpreter::FormatStringFor<int8_t>(void) { return "%d"; }
-
-template <> inline constexpr const char *Interpreter::FormatStringFor<int16_t>(void) { return "%d"; }
-
-template <> inline constexpr const char *Interpreter::FormatStringFor<int32_t>(void) { return "%ld"; }
-
-template <> inline constexpr const char *Interpreter::FormatStringFor<const char *>(void) { return "%s"; }
-
-// Specialization of ProcessGet<> for `uint32_t` and `int32_t`
-
-template <> inline otError Interpreter::ProcessGet<uint32_t>(Arg aArgs[], GetHandler<uint32_t> aGetHandler)
-{
-    otError error = OT_ERROR_NONE;
-
-    VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
-    OutputLine(FormatStringFor<uint32_t>(), ToUlong(aGetHandler(GetInstancePtr())));
-
-exit:
-    return error;
-}
-
-template <> inline otError Interpreter::ProcessGet<int32_t>(Arg aArgs[], GetHandler<int32_t> aGetHandler)
-{
-    otError error = OT_ERROR_NONE;
-
-    VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
-    OutputLine(FormatStringFor<int32_t>(), static_cast<long int>(aGetHandler(GetInstancePtr())));
-
-exit:
-    return error;
-}
-
 } // namespace Cli
 } // namespace ot
 
diff --git a/src/cli/cli_bbr.cpp b/src/cli/cli_bbr.cpp
index d37d3df..0f86b24 100644
--- a/src/cli/cli_bbr.cpp
+++ b/src/cli/cli_bbr.cpp
@@ -290,8 +290,7 @@
  */
 template <> otError Bbr::Process<Cmd("jitter")>(Arg aArgs[])
 {
-    return Interpreter::GetInterpreter().ProcessGetSet(aArgs, otBackboneRouterGetRegistrationJitter,
-                                                       otBackboneRouterSetRegistrationJitter);
+    return ProcessGetSet(aArgs, otBackboneRouterGetRegistrationJitter, otBackboneRouterSetRegistrationJitter);
 }
 
 /**
diff --git a/src/cli/cli_bbr.hpp b/src/cli/cli_bbr.hpp
index 40ed979..120518a 100644
--- a/src/cli/cli_bbr.hpp
+++ b/src/cli/cli_bbr.hpp
@@ -42,7 +42,7 @@
 #include <openthread/backbone_router_ftd.h>
 
 #include "cli/cli_config.h"
-#include "cli/cli_output.hpp"
+#include "cli/cli_utils.hpp"
 
 namespace ot {
 namespace Cli {
@@ -51,11 +51,9 @@
  * Implements the BBR CLI interpreter.
  *
  */
-class Bbr : private Output
+class Bbr : private Utils
 {
 public:
-    typedef Utils::CmdLineParser::Arg Arg;
-
     /**
      * Constructor.
      *
@@ -64,7 +62,7 @@
      *
      */
     Bbr(otInstance *aInstance, OutputImplementer &aOutputImplementer)
-        : Output(aInstance, aOutputImplementer)
+        : Utils(aInstance, aOutputImplementer)
     {
     }
 
diff --git a/src/cli/cli_br.cpp b/src/cli/cli_br.cpp
index bd4d0df..53f2b32 100644
--- a/src/cli/cli_br.cpp
+++ b/src/cli/cli_br.cpp
@@ -435,6 +435,66 @@
     return error;
 }
 
+#if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
+template <> otError Br::Process<Cmd("pd")>(Arg aArgs[])
+{
+    otError error = OT_ERROR_NONE;
+
+    /**
+     * @cli br pd (enable,disable)
+     * @code
+     * br pd enable
+     * Done
+     * @endcode
+     * @code
+     * br pd disable
+     * Done
+     * @endcode
+     * @cparam br pd @ca{enable|disable}
+     * @par api_copy
+     * #otBorderRoutingDhcp6PdSetEnabled
+     *
+     */
+    if (ProcessEnableDisable(aArgs, otBorderRoutingDhcp6PdSetEnabled) == OT_ERROR_NONE)
+    {
+    }
+    /**
+     * @cli br pd state
+     * @code
+     * br pd state
+     * running
+     * Done
+     * @endcode
+     * @par api_copy
+     * #otBorderRoutingDhcp6PdGetState
+     */
+    else if (aArgs[0] == "state")
+    {
+        static const char *const kDhcpv6PdStateStrings[] = {
+            "disabled", // (0) OT_BORDER_ROUTING_DHCP6_PD_STATE_DISABLED
+            "stopped",  // (1) OT_BORDER_ROUTING_DHCP6_PD_STATE_STOPPED
+            "running",  // (2) OT_BORDER_ROUTING_DHCP6_PD_STATE_RUNNING
+        };
+
+        static_assert(0 == OT_BORDER_ROUTING_DHCP6_PD_STATE_DISABLED,
+                      "OT_BORDER_ROUTING_DHCP6_PD_STATE_DISABLED value is not expected!");
+        static_assert(1 == OT_BORDER_ROUTING_DHCP6_PD_STATE_STOPPED,
+                      "OT_BORDER_ROUTING_DHCP6_PD_STATE_STOPPED value is not expected!");
+        static_assert(2 == OT_BORDER_ROUTING_DHCP6_PD_STATE_RUNNING,
+                      "OT_BORDER_ROUTING_DHCP6_PD_STATE_RUNNING value is not expected!");
+
+        OutputLine("%s", Stringify(otBorderRoutingDhcp6PdGetState(GetInstancePtr()), kDhcpv6PdStateStrings));
+    }
+    else
+    {
+        ExitNow(error = OT_ERROR_INVALID_COMMAND);
+    }
+
+exit:
+    return error;
+}
+#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
+
 /**
  * @cli br routers
  * @code
@@ -478,6 +538,46 @@
                aEntry.mStubRouterFlag);
 }
 
+template <> otError Br::Process<Cmd("raoptions")>(Arg aArgs[])
+{
+    static constexpr uint16_t kMaxExtraOptions = 800;
+
+    otError  error = OT_ERROR_NONE;
+    uint8_t  options[kMaxExtraOptions];
+    uint16_t length;
+
+    /**
+     * @cli br raoptions (set,clear)
+     * @code
+     * br raoptions 0400ff00020001
+     * Done
+     * @endcode
+     * @code
+     * br raoptions clear
+     * Done
+     * @endcode
+     * @cparam br raoptions @ca{options|clear}
+     * `br raoptions clear` passes a `nullptr` to #otBorderRoutingSetExtraRouterAdvertOptions.
+     * Otherwise, you can pass the `options` byte as hex data.
+     * @par api_copy
+     * #otBorderRoutingSetExtraRouterAdvertOptions
+     */
+    if (aArgs[0] == "clear")
+    {
+        length = 0;
+    }
+    else
+    {
+        length = sizeof(options);
+        SuccessOrExit(error = aArgs[0].ParseAsHexString(length, options));
+    }
+
+    error = otBorderRoutingSetExtraRouterAdvertOptions(GetInstancePtr(), length > 0 ? options : nullptr, length);
+
+exit:
+    return error;
+}
+
 template <> otError Br::Process<Cmd("rioprf")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
@@ -638,7 +738,11 @@
 #endif
         CmdEntry("omrprefix"),
         CmdEntry("onlinkprefix"),
+#if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
+        CmdEntry("pd"),
+#endif
         CmdEntry("prefixtable"),
+        CmdEntry("raoptions"),
         CmdEntry("rioprf"),
         CmdEntry("routeprf"),
         CmdEntry("routers"),
diff --git a/src/cli/cli_br.hpp b/src/cli/cli_br.hpp
index 01fcdcf..5a877a8 100644
--- a/src/cli/cli_br.hpp
+++ b/src/cli/cli_br.hpp
@@ -39,7 +39,7 @@
 #include <openthread/border_routing.h>
 
 #include "cli/cli_config.h"
-#include "cli/cli_output.hpp"
+#include "cli/cli_utils.hpp"
 
 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
 
@@ -50,11 +50,9 @@
  * Implements the Border Router CLI interpreter.
  *
  */
-class Br : private Output
+class Br : private Utils
 {
 public:
-    typedef Utils::CmdLineParser::Arg Arg;
-
     /**
      * Constructor
      *
@@ -63,7 +61,7 @@
      *
      */
     Br(otInstance *aInstance, OutputImplementer &aOutputImplementer)
-        : Output(aInstance, aOutputImplementer)
+        : Utils(aInstance, aOutputImplementer)
     {
     }
 
diff --git a/src/cli/cli_coap.cpp b/src/cli/cli_coap.cpp
index 7f71781..5edbe00 100644
--- a/src/cli/cli_coap.cpp
+++ b/src/cli/cli_coap.cpp
@@ -45,7 +45,7 @@
 namespace Cli {
 
 Coap::Coap(otInstance *aInstance, OutputImplementer &aOutputImplementer)
-    : Output(aInstance, aOutputImplementer)
+    : Utils(aInstance, aOutputImplementer)
     , mUseDefaultRequestTxParameters(true)
     , mUseDefaultResponseTxParameters(true)
 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
@@ -58,15 +58,15 @@
     , mBlockCount(1)
 #endif
 {
-    memset(&mResource, 0, sizeof(mResource));
+    ClearAllBytes(mResource);
 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
-    memset(&mRequestAddr, 0, sizeof(mRequestAddr));
-    memset(&mSubscriberSock, 0, sizeof(mSubscriberSock));
-    memset(&mRequestToken, 0, sizeof(mRequestToken));
-    memset(&mSubscriberToken, 0, sizeof(mSubscriberToken));
-    memset(&mRequestUri, 0, sizeof(mRequestUri));
+    ClearAllBytes(mRequestAddr);
+    ClearAllBytes(mSubscriberSock);
+    ClearAllBytes(mRequestToken);
+    ClearAllBytes(mSubscriberToken);
+    ClearAllBytes(mRequestUri);
 #endif
-    memset(&mUriPath, 0, sizeof(mUriPath));
+    ClearAllBytes(mUriPath);
     strncpy(mResourceContent, "0", sizeof(mResourceContent));
     mResourceContent[sizeof(mResourceContent) - 1] = '\0';
 }
@@ -78,7 +78,7 @@
     otMessage    *message = nullptr;
     otMessageInfo messageInfo;
 
-    memset(&messageInfo, 0, sizeof(messageInfo));
+    ClearAllBytes(messageInfo);
     messageInfo.mPeerAddr = mRequestAddr;
     messageInfo.mPeerPort = OT_DEFAULT_COAP_PORT;
 
@@ -94,8 +94,8 @@
     SuccessOrExit(error = otCoapMessageAppendUriPathOptions(message, mRequestUri));
     SuccessOrExit(error = otCoapSendRequest(GetInstancePtr(), message, &messageInfo, &Coap::HandleResponse, this));
 
-    memset(&mRequestAddr, 0, sizeof(mRequestAddr));
-    memset(&mRequestUri, 0, sizeof(mRequestUri));
+    ClearAllBytes(mRequestAddr);
+    ClearAllBytes(mRequestUri);
     mRequestTokenLength = 0;
 
 exit:
@@ -110,7 +110,7 @@
 
 void Coap::CancelSubscriber(void)
 {
-    memset(&mSubscriberSock, 0, sizeof(mSubscriberSock));
+    ClearAllBytes(mSubscriberSock);
     mSubscriberTokenLength = 0;
 }
 #endif // OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
@@ -142,6 +142,17 @@
 }
 
 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
+/**
+ * @cli coap cancel
+ * @code
+ * coap cancel
+ * Done
+ * @endcode
+ * @par
+ * Cancels an existing observation subscription to a remote resource on the CoAP server.
+ * @note This command is available only when `OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE` is set.
+ * @csa{coap observe}
+ */
 template <> otError Coap::Process<Cmd("cancel")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
@@ -150,6 +161,23 @@
 }
 #endif
 
+/**
+ * @cli coap resource (get,set)
+ * @code
+ * coap resource test-resource
+ * Done
+ * @endcode
+ * @code
+ * coap resource
+ * test-resource
+ * Done
+ * @endcode
+ * @cparam coap resource [@ca{uri-path}]
+ * @par
+ * Gets or sets the URI path of the CoAP server resource.
+ * @sa otCoapAddResource
+ * @sa otCoapAddBlockWiseResource
+ */
 template <> otError Coap::Process<Cmd("resource")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
@@ -189,6 +217,20 @@
     return error;
 }
 
+/**
+ * @cli coap set
+ * @code
+ * coap set Testing123
+ * Done
+ * @endcode
+ * @cparam coap set @ca{new-content}
+ * @par
+ * Sets the content sent by the resource on the CoAP server.
+ * If a CoAP client is observing the resource, a notification is sent to that client.
+ * @csa{coap observe}
+ * @sa otCoapMessageInit
+ * @sa otCoapNewMessage
+ */
 template <> otError Coap::Process<Cmd("set")>(Arg aArgs[])
 {
 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
@@ -207,7 +249,7 @@
         if (mSubscriberTokenLength > 0)
         {
             // Notify the subscriber
-            memset(&messageInfo, 0, sizeof(messageInfo));
+            ClearAllBytes(messageInfo);
             messageInfo.mPeerAddr = mSubscriberSock.mAddress;
             messageInfo.mPeerPort = mSubscriberSock.mPort;
 
@@ -250,6 +292,16 @@
     return error;
 }
 
+/**
+ * @cli coap start
+ * @code
+ * coap start
+ * Done
+ * @endcode
+ * @par
+ * Starts the CoAP server. @moreinfo{@coap}.
+ * @sa otCoapStart
+ */
 template <> otError Coap::Process<Cmd("start")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
@@ -257,6 +309,15 @@
     return otCoapStart(GetInstancePtr(), OT_DEFAULT_COAP_PORT);
 }
 
+/**
+ * @cli coap stop
+ * @code
+ * coap stop
+ * Done
+ * @endcode
+ * @par api_copy
+ * #otCoapStop
+ */
 template <> otError Coap::Process<Cmd("stop")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
@@ -270,6 +331,53 @@
     return otCoapStop(GetInstancePtr());
 }
 
+/**
+ * @cli coap parameters(get,set)
+ * @code
+ * coap parameters request
+ * Transmission parameters for request:
+ * ACK_TIMEOUT=1000 ms, ACK_RANDOM_FACTOR=255/254, MAX_RETRANSMIT=2
+ * Done
+ * @endcode
+ * @code
+ * coap parameters request default
+ * Transmission parameters for request:
+ * default
+ * Done
+ * @endcode
+ * @code
+ * coap parameters request 1000 255 254 2
+ * Transmission parameters for request:
+ * ACK_TIMEOUT=1000 ms, ACK_RANDOM_FACTOR=255/254, MAX_RETRANSMIT=2
+ * Done
+ * @endcode
+ * @cparam coap parameters @ca{type} [@ca{default} | <!--
+ * -->@ca{ack_timeout ack_random_factor_numerator <!--
+ * -->ack_random_factor_denominator max_retransmit}]
+ *   * `type`: `request` for CoAP requests, or `response` for CoAP responses.
+        If no more parameters are given, the command prints the current configuration.
+ *   * `default`: Sets the transmission parameters to
+        the following default values:
+ *       * `ack_timeout`: 2000 milliseconds
+ *       * `ack_random_factor_numerator`: 3
+ *       * `ack_random_factor_denominator`: 2
+ *       * `max_retransmit`: 4
+ *   * `ack_timeout`: The `ACK_TIMEOUT` (0-UINT32_MAX) in milliseconds.
+       Refer to RFC7252.
+ *   * `ack_random_factor_numerator`:
+       The `ACK_RANDOM_FACTOR` numerator, with possible values
+       of 0-255. Refer to RFC7252.
+ *   * `ack_random_factor_denominator`:
+ *     The `ACK_RANDOM_FACTOR` denominator, with possible values
+ *     of 0-255. Refer to RFC7252.
+ *   * `max_retransmit`: The `MAX_RETRANSMIT` (0-255). Refer to RFC7252.
+ * @par
+ * Gets current CoAP parameter values if the command is run with no optional
+ * parameters.
+ * @par
+ * Sets the CoAP parameters either to their default values or to the values
+ * you specify, depending on the syntax chosen.
+ */
 template <> otError Coap::Process<Cmd("parameters")>(Arg aArgs[])
 {
     otError             error = OT_ERROR_NONE;
@@ -328,15 +436,138 @@
     return error;
 }
 
+/**
+ * @cli coap get
+ * @code
+ * coap get fdde:ad00:beef:0:2780:9423:166c:1aac test-resource
+ * Done
+ * @endcode
+ * @code
+ * coap get fdde:ad00:beef:0:2780:9423:166c:1aac test-resource block-1024
+ * Done
+ * @endcode
+ * @cparam coap get @ca{address} @ca{uri-path} [@ca{type}]
+ *   * `address`: IPv6 address of the CoAP server.
+ *   * `uri-path`: URI path of the resource.
+ *   * `type`:
+ *       * `con`: Confirmable
+ *       * `non-con`: Non-confirmable (default)
+ *       * `block-`: Use this option, followed by the block-wise value,
+ *          if the response should be transferred block-wise. Valid
+ *          values are: `block-16`, `block-32`, `block-64`, `block-128`,
+ *          `block-256`, `block-512`, or `block-1024`.
+ * @par
+ * Gets information about the specified CoAP resource on the CoAP server.
+ */
 template <> otError Coap::Process<Cmd("get")>(Arg aArgs[]) { return ProcessRequest(aArgs, OT_COAP_CODE_GET); }
 
+/**
+ * @cli coap post
+ * @code
+ * coap post fdde:ad00:beef:0:2780:9423:166c:1aac test-resource con hellothere
+ * Done
+ * @endcode
+ * @code
+ * coap post fdde:ad00:beef:0:2780:9423:166c:1aac test-resource block-1024 10
+ * Done
+ * @endcode
+ * @cparam coap post @ca{address} @ca{uri-path} [@ca{type}] [@ca{payload}]
+ *   * `address`: IPv6 address of the CoAP server.
+ *   * `uri-path`: URI path of the resource.
+ *   * `type`:
+ *         * `con`: Confirmable
+ *         * `non-con`: Non-confirmable (default)
+ *         * `block-`: Use this option, followed by the block-wise value,
+ *            to send blocks with a randomly generated number of bytes
+ *            for the payload. Valid values are:
+ *            `block-16`, `block-32`, `block-64`, `block-128`,
+ *            `block-256`, `block-512`, or `block-1024`.
+ *   * `payload`: CoAP payload request, which if used is either a string or an
+ *     integer, depending on the `type`. If the `type` is `con` or `non-con`,
+ *     the `payload` parameter is optional. If you leave out the
+ *     `payload` parameter, an empty payload is sent. However, If you use the
+ *     `payload` parameter, its value must be a string, such as
+ *     `hellothere`.  If the `type` is `block-`,
+ *     the value of the`payload` parameter must be an integer that specifies
+ *     the number of blocks to send. The `block-` type requires
+ *     `OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE` to be set.
+ * @par
+ * Creates the specified CoAP resource. @moreinfo{@coap}.
+ */
 template <> otError Coap::Process<Cmd("post")>(Arg aArgs[]) { return ProcessRequest(aArgs, OT_COAP_CODE_POST); }
 
+/**
+ * @cli coap put
+ * @code
+ * coap put fdde:ad00:beef:0:2780:9423:166c:1aac test-resource con hellothere
+ * Done
+ * @endcode
+ * @code
+ * coap put fdde:ad00:beef:0:2780:9423:166c:1aac test-resource block-1024 10
+ * Done
+ * @endcode
+ * @cparam coap put @ca{address} @ca{uri-path} [@ca{type}] [@ca{payload}]
+ *   * `address`: IPv6 address of the CoAP server.
+ *   * `uri-path`: URI path of the resource.
+ *   * `type`:
+ *         * `con`: Confirmable
+ *         * `non-con`: Non-confirmable (default)
+ *         * `block-`: Use this option, followed by the block-wise value,
+ *            to send blocks with a randomly generated number of bytes
+ *            for the payload. Valid values are:
+ *            `block-16`, `block-32`, `block-64`, `block-128`,
+ *            `block-256`, `block-512`, or `block-1024`.
+ *   * `payload`: CoAP payload request, which if used is either a string or an
+ *     integer, depending on the `type`. If the `type` is `con` or `non-con`,
+ *     the `payload` parameter is optional. If you leave out the
+ *     `payload` parameter, an empty payload is sent. However, If you use the
+ *     `payload` parameter, its value must be a string, such as
+ *     `hellothere`. If the `type` is `block-`,
+ *     the value of the`payload` parameter must be an integer that specifies
+ *     the number of blocks to send. The `block-` type requires
+ *     `OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE` to be set.
+ * @par
+ * Modifies the specified CoAP resource. @moreinfo{@coap}.
+ */
 template <> otError Coap::Process<Cmd("put")>(Arg aArgs[]) { return ProcessRequest(aArgs, OT_COAP_CODE_PUT); }
 
+/**
+ * @cli coap delete
+ * @code
+ * coap delete fdde:ad00:beef:0:2780:9423:166c:1aac test-resource con hellothere
+ * Done
+ * @endcode
+ * @cparam coap delete @ca{address} @ca{uri-path} [@ca{type}] [@ca{payload}]
+ *   * `address`: IPv6 address of the CoAP server.
+ *   * `uri-path`: URI path of the resource.
+ *   * `type`:
+ *       * `con`: Confirmable
+ *       * `non-con`: Non-confirmable (default)
+ *   * `payload`: The CoAP payload string. For example, `hellothere`.
+ *  @par
+ *  Deletes the specified CoAP resource.
+ */
 template <> otError Coap::Process<Cmd("delete")>(Arg aArgs[]) { return ProcessRequest(aArgs, OT_COAP_CODE_DELETE); }
 
 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
+/**
+ * @cli coap observe
+ * @code
+ * coap observe fdde:ad00:beef:0:2780:9423:166c:1aac test-resource
+ * Done
+ * @endcode
+ * @cparam coap observe @ca{address} @ca{uri-path} [@ca{type}]
+ *   * `address`: IPv6 address of the CoAP server.
+ *   * `uri-path`: URI path of the resource.
+ *   * `type`:
+ *       * `con`: Confirmable
+ *       * `non-con`: Non-confirmable (default).
+ * @par
+ * Triggers a subscription request which allows the CoAP client to
+ * observe the specified resource on the CoAP server for possible changes
+ * in its state.
+ * @note This command is available only when `OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE` is set.
+ */
 template <> otError Coap::Process<Cmd("observe")>(Arg aArgs[])
 {
     return ProcessRequest(aArgs, OT_COAP_CODE_GET, /* aCoapObserve */ true);
@@ -494,7 +725,7 @@
         SuccessOrExit(error = otMessageAppend(message, aArgs[3].GetCString(), payloadLength));
     }
 
-    memset(&messageInfo, 0, sizeof(messageInfo));
+    ClearAllBytes(messageInfo);
     messageInfo.mPeerAddr = coapDestinationIp;
     messageInfo.mPeerPort = OT_DEFAULT_COAP_PORT;
 
@@ -718,7 +949,7 @@
         }
         else
         {
-            responseCode = OT_COAP_CODE_VALID;
+            responseCode = OT_COAP_CODE_CHANGED;
         }
 
         responseMessage = otCoapNewMessage(GetInstancePtr(), nullptr);
diff --git a/src/cli/cli_coap.hpp b/src/cli/cli_coap.hpp
index f3357fc..95ce7b9 100644
--- a/src/cli/cli_coap.hpp
+++ b/src/cli/cli_coap.hpp
@@ -40,7 +40,7 @@
 
 #include <openthread/coap.h>
 
-#include "cli/cli_output.hpp"
+#include "cli/cli_utils.hpp"
 
 namespace ot {
 namespace Cli {
@@ -49,11 +49,9 @@
  * Implements the CLI CoAP server and client.
  *
  */
-class Coap : private Output
+class Coap : private Utils
 {
 public:
-    typedef Utils::CmdLineParser::Arg Arg;
-
     /**
      * Constructor
      *
@@ -78,11 +76,8 @@
     otError Process(Arg aArgs[]);
 
 private:
-    enum
-    {
-        kMaxUriLength  = 32,
-        kMaxBufferSize = 16
-    };
+    static constexpr uint16_t kMaxUriLength  = 32;
+    static constexpr uint16_t kMaxBufferSize = 16;
 
     using Command = CommandEntry<Coap>;
 
diff --git a/src/cli/cli_coap_secure.cpp b/src/cli/cli_coap_secure.cpp
index 518e907..2ff4f68 100644
--- a/src/cli/cli_coap_secure.cpp
+++ b/src/cli/cli_coap_secure.cpp
@@ -47,7 +47,7 @@
 namespace Cli {
 
 CoapSecure::CoapSecure(otInstance *aInstance, OutputImplementer &aOutputImplementer)
-    : Output(aInstance, aOutputImplementer)
+    : Utils(aInstance, aOutputImplementer)
     , mShutdownFlag(false)
     , mUseCertificate(false)
     , mPskLength(0)
@@ -56,10 +56,10 @@
     , mBlockCount(1)
 #endif
 {
-    memset(&mResource, 0, sizeof(mResource));
-    memset(&mPsk, 0, sizeof(mPsk));
-    memset(&mPskId, 0, sizeof(mPskId));
-    memset(&mUriPath, 0, sizeof(mUriPath));
+    ClearAllBytes(mResource);
+    ClearAllBytes(mPsk);
+    ClearAllBytes(mPskId);
+    ClearAllBytes(mUriPath);
     strncpy(mResourceContent, "0", sizeof(mResourceContent));
     mResourceContent[sizeof(mResourceContent) - 1] = '\0';
 }
@@ -90,6 +90,22 @@
     OutputNewLine();
 }
 
+/**
+ * @cli coaps resource (get,set)
+ * @code
+ * coaps resource test-resource
+ * Done
+ * @endcode
+ * @code
+ * coaps resource
+ * test-resource
+ * Done
+ * @endcode
+ * @cparam coaps resource [@ca{uri-path}]
+ * @par
+ * Gets or sets the URI path of the CoAPS server resource. @moreinfo{@coaps}.
+ * @sa otCoapSecureAddBlockWiseResource
+ */
 template <> otError CoapSecure::Process<Cmd("resource")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
@@ -128,6 +144,16 @@
     return error;
 }
 
+/**
+ * @cli coaps set
+ * @code
+ * coaps set Testing123
+ * Done
+ * @endcode
+ * @cparam coaps set @ca{new-content}
+ * @par
+ * Sets the content sent by the resource on the CoAPS server. @moreinfo{@coaps}.
+ */
 template <> otError CoapSecure::Process<Cmd("set")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
@@ -147,10 +173,50 @@
     return error;
 }
 
+/**
+ * @cli coaps start
+ * @code
+ * coaps start
+ * Done
+ * @endcode
+ * @code
+ * coaps start false
+ * Done
+ * @endcode
+ * @code
+ * coaps start 8
+ * Done
+ * @endcode
+ * @cparam coaps start [@ca{check-peer-cert} | @ca{max-conn-attempts}]
+ * The `check-peer-cert` parameter determines if the peer-certificate check is
+ * enabled (default) or disabled.
+ * The `max-conn-attempts` parameter sets the maximum number of allowed
+ * attempts, successful or failed, to connect to the CoAP Secure server.
+ * The default value of this parameter is `0`, which means that there is
+ * no limit to the number of attempts.
+ * The `check-peer-cert` and `max-conn-attempts` parameters work
+ * together in the following combinations, even though you can only specify
+ * one argument:
+ *   * No argument specified: Defaults are used.
+ *   * Setting `check-peer-cert` to `true`:
+ *     Has the same effect as omitting the argument, which is that the
+ *     `check-peer-cert` value is `true`, and the `max-conn-attempts` value is 0.
+ *   * Setting `check-peer-cert` to `false`:
+ *    `check-peer-cert` value is `false`, and the `max-conn-attempts` value is 0.
+ *   * Specifying a number:
+ *     `check-peer-cert` is `true`, and the `max-conn-attempts` value is the
+ *     number specified in the argument.
+ * @par
+ * Starts the CoAP Secure service. @moreinfo{@coaps}.
+ * @sa otCoapSecureStart
+ * @sa otCoapSecureSetSslAuthMode
+ * @sa otCoapSecureSetClientConnectedCallback
+ */
 template <> otError CoapSecure::Process<Cmd("start")>(Arg aArgs[])
 {
-    otError error          = OT_ERROR_NONE;
-    bool    verifyPeerCert = true;
+    otError  error           = OT_ERROR_NONE;
+    bool     verifyPeerCert  = true;
+    uint16_t maxConnAttempts = 0;
 
     if (!aArgs[0].IsEmpty())
     {
@@ -158,9 +224,13 @@
         {
             verifyPeerCert = false;
         }
+        else if (aArgs[0] == "true")
+        {
+            verifyPeerCert = true;
+        }
         else
         {
-            VerifyOrExit(aArgs[0] == "true", error = OT_ERROR_INVALID_ARGS);
+            SuccessOrExit(error = aArgs[0].ParseAsUint16(maxConnAttempts));
         }
     }
 
@@ -171,12 +241,23 @@
     otCoapSecureSetDefaultHandler(GetInstancePtr(), &CoapSecure::DefaultHandler, this);
 #endif
 
-    error = otCoapSecureStart(GetInstancePtr(), OT_DEFAULT_COAP_SECURE_PORT);
+    error = otCoapSecureStartWithMaxConnAttempts(GetInstancePtr(), OT_DEFAULT_COAP_SECURE_PORT, maxConnAttempts,
+                                                 nullptr, nullptr);
 
 exit:
     return error;
 }
 
+/**
+ * @cli coaps stop
+ * @code
+ * coaps stop
+ * Done
+ * @endcode
+ * @par
+ * Stops the CoAP Secure service. @moreinfo{@coaps}.
+ * @sa otCoapSecureStop
+ */
 template <> otError CoapSecure::Process<Cmd("stop")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
@@ -200,12 +281,171 @@
     return OT_ERROR_NONE;
 }
 
+/**
+ * @cli coaps isclosed
+ * @code
+ * coaps isclosed
+ * no
+ * Done
+ * @endcode
+ * @par
+ * Indicates if the CoAP Secure service is closed. @moreinfo{@coaps}.
+ * @sa otCoapSecureIsClosed
+ */
+template <> otError CoapSecure::Process<Cmd("isclosed")>(Arg aArgs[])
+{
+    return ProcessIsRequest(aArgs, otCoapSecureIsClosed);
+}
+
+/**
+ * @cli coaps isconnected
+ * @code
+ * coaps isconnected
+ * yes
+ * Done
+ * @endcode
+ * @par
+ * Indicates if the CoAP Secure service is connected. @moreinfo{@coaps}.
+ * @sa otCoapSecureIsConnected
+ */
+template <> otError CoapSecure::Process<Cmd("isconnected")>(Arg aArgs[])
+{
+    return ProcessIsRequest(aArgs, otCoapSecureIsConnected);
+}
+
+/**
+ * @cli coaps isconnactive
+ * @code
+ * coaps isconnactive
+ * yes
+ * Done
+ * @endcode
+ * @par
+ * Indicates if the CoAP Secure service connection is active
+ * (either already connected or in the process of establishing a connection).
+ * @moreinfo{@coaps}.
+ * @sa otCoapSecureIsConnectionActive
+ */
+template <> otError CoapSecure::Process<Cmd("isconnactive")>(Arg aArgs[])
+{
+    return ProcessIsRequest(aArgs, otCoapSecureIsConnectionActive);
+}
+
+otError CoapSecure::ProcessIsRequest(Arg aArgs[], bool (*IsChecker)(otInstance *))
+{
+    otError error = OT_ERROR_NONE;
+
+    VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
+    OutputLine("%s", IsChecker(GetInstancePtr()) ? "yes" : "no");
+
+exit:
+    return error;
+}
+
+/**
+ * @cli coaps get
+ * @code
+ * coaps get test-resource
+ * Done
+ * @endcode
+ * @code
+ * coaps get test-resource block-1024
+ * Done
+ * @endcode
+ * @cparam coaps get @ca{uri-path} [@ca{type}]
+ *   * `uri-path`: URI path of the resource.
+ *   * `type`:
+ *       * `con`: Confirmable
+ *       * `non-con`: Non-confirmable (default)
+ *       * `block-`: Use this option, followed by the block-wise value,
+ *         if the response should be transferred block-wise.
+ *         Valid values are: `block-16`, `block-32`, `block-64`, `block-128`,
+ *         `block-256`, `block-512`, or `block-1024`.
+ * @par
+ * Gets information about the specified CoAPS resource on the CoAPS server.
+ * @moreinfo{@coaps}.
+ */
 template <> otError CoapSecure::Process<Cmd("get")>(Arg aArgs[]) { return ProcessRequest(aArgs, OT_COAP_CODE_GET); }
 
+/**
+ * @cli coaps post
+ * @code
+ * coaps post test-resource con hellothere
+ * Done
+ * @endcode
+ * @code
+ * coaps post test-resource block-1024 10
+ * Done
+ * @endcode
+ * @cparam @ca{uri-path} [@ca{type}] [@ca{payload}]
+ *   * `uri-path`: URI path of the resource.
+ *   * `type`:
+ *       * `con`: Confirmable
+ *       * `non-con`: Non-confirmable (default)
+ *       * `block-`: Use this option, followed by the block-wise value,
+ *         to send blocks with a randomly generated number of bytes for the payload.
+ *         Valid values are: `block-16`, `block-32`, `block-64`, `block-128`,
+ *         `block-256`, `block-512`, or `block-1024`.
+ *	 * `payload`:  CoAPS payload request, which if used is either a string
+ *	   or an integer, depending on the `type`. If the `type` is `con` or `non-con`,
+ *	   the payload parameter is optional. If you leave out the payload
+ *	   parameter, an empty payload is sent. However, If you use the payload
+ *	   parameter, its value must be a string, such as `hellothere`. If the
+ *	   `type` is `block-`, the value of the payload parameter must be an
+ *	   integer that specifies the number of blocks to send. The `block-` type
+ *	   requires `OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE` to be set.
+ * @par
+ * Creates the specified CoAPS resource. @moreinfo{@coaps}.
+ */
 template <> otError CoapSecure::Process<Cmd("post")>(Arg aArgs[]) { return ProcessRequest(aArgs, OT_COAP_CODE_POST); }
 
+/**
+ * @cli coaps put
+ * @code
+ * coaps put test-resource con hellothere
+ * Done
+ * @endcode
+ * @code
+ * coaps put test-resource block-1024 10
+ * Done
+ * @endcode
+ * @cparam @ca{uri-path} [@ca{type}] [@ca{payload}]
+ *   * `uri-path`: URI path of the resource.
+ *   * `type`:
+ *       * `con`: Confirmable
+ *       * `non-con`: Non-confirmable (default)
+ *       * `block-`: Use this option, followed by the block-wise value,
+ *         to send blocks with a randomly generated number of bytes for the payload.
+ *         Valid values are: `block-16`, `block-32`, `block-64`, `block-128`,
+ *         `block-256`, `block-512`, or `block-1024`.
+ *	 * `payload`:  CoAPS payload request, which if used is either a string
+ *	   or an integer, depending on the `type`. If the `type` is `con` or `non-con`,
+ *	   the payload parameter is optional. If you leave out the payload
+ *	   parameter, an empty payload is sent. However, If you use the payload
+ *	   parameter, its value must be a string, such as `hellothere`. If the
+ *	   `type` is `block-`, the value of the payload parameter must be an
+ *	   integer that specifies the number of blocks to send. The `block-` type
+ *	   requires `OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE` to be set.
+ * @par
+ * Modifies the specified CoAPS resource. @moreinfo{@coaps}.
+ */
 template <> otError CoapSecure::Process<Cmd("put")>(Arg aArgs[]) { return ProcessRequest(aArgs, OT_COAP_CODE_PUT); }
 
+/**
+ * @cli coaps delete
+ * @code
+ * coaps delete test-resource con hellothere
+ * Done
+ * @endcode
+ * @cparam coaps delete @ca{uri-path} [@ca{type}] [@ca{payload}]
+ *   * `uri-path`: URI path of the resource.
+ *   * `type`:
+ *       * `con`: Confirmable
+ *       * `non-con`: Non-confirmable (default)
+ *   * `payload`: CoAPS payload request.
+ * @par
+ * The CoAPS payload string to delete.
+ */
 template <> otError CoapSecure::Process<Cmd("delete")>(Arg aArgs[])
 {
     return ProcessRequest(aArgs, OT_COAP_CODE_DELETE);
@@ -361,12 +601,26 @@
     return error;
 }
 
+/**
+ * @cli coaps connect
+ * @code
+ * coaps connect fdde:ad00:beef:0:9903:14b:27e0:5744
+ * Done
+ * coaps connected
+ * @endcode
+ * @cparam coaps connect @ca{address}
+ * The `address` parameter is the IPv6 address of the peer.
+ * @par
+ * Initializes a Datagram Transport Layer Security (DTLS) session with a peer.
+ * @moreinfo{@coaps}.
+ * @sa otCoapSecureConnect
+ */
 template <> otError CoapSecure::Process<Cmd("connect")>(Arg aArgs[])
 {
     otError    error;
     otSockAddr sockaddr;
 
-    memset(&sockaddr, 0, sizeof(sockaddr));
+    ClearAllBytes(sockaddr);
     SuccessOrExit(error = aArgs[0].ParseAsIp6Address(sockaddr.mAddress));
     sockaddr.mPort = OT_DEFAULT_COAP_SECURE_PORT;
 
@@ -381,6 +635,17 @@
     return error;
 }
 
+/**
+ * @cli coaps disconnect
+ * @code
+ * coaps disconnect
+ * coaps disconnected
+ * Done
+ * @endcode
+ * @par
+ * Stops the DTLS session.
+ * @sa otCoapSecureDisconnect
+ */
 template <> otError CoapSecure::Process<Cmd("disconnect")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
@@ -390,6 +655,22 @@
     return OT_ERROR_NONE;
 }
 
+/**
+ * <!--- This tag is before the IF statement so that Doxygen imports the command. --->
+ * @cli coaps psk
+ * @code
+ * coaps psk 1234 key1
+ * Done
+ * @endcode
+ * @cparam coaps psk @ca{psk-value} @ca{psk-id}
+ *   * `psk-value`: The pre-shared key
+ *   * `psk-id`: The pre-shared key identifier.
+ * @par
+ * Sets the pre-shared key (PSK) and cipher suite DTLS_PSK_WITH_AES_128_CCM_8.
+ * @note This command requires the build-time feature
+ * `MBEDTLS_KEY_EXCHANGE_PSK_ENABLED` to be enabled.
+ * @sa #otCoapSecureSetPsk
+ */
 #ifdef MBEDTLS_KEY_EXCHANGE_PSK_ENABLED
 template <> otError CoapSecure::Process<Cmd("psk")>(Arg aArgs[])
 {
@@ -416,6 +697,22 @@
 }
 #endif // MBEDTLS_KEY_EXCHANGE_PSK_ENABLED
 
+/**
+ * <!--- This tag is before the IF statement so that Doxygen imports the command. --->
+ * @cli coaps x509
+ * @code
+ * coaps x509
+ * Done
+ * @endcode
+ * @par
+ * Sets the X509 certificate of the local device with the corresponding private key for
+ * the DTLS session with `DTLS_ECDHE_ECDSA_WITH_AES_128_CCM_8`.
+ * @note This command requires `MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED=1`
+ * to be enabled.
+ * The X.509 certificate is stored in the location: `src/cli/x509_cert_key.hpp`.
+ * @sa otCoapSecureSetCertificate
+ * @sa otCoapSecureSetCaCertificateChain
+ */
 #ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
 template <> otError CoapSecure::Process<Cmd("x509")>(Arg aArgs[])
 {
@@ -442,11 +739,13 @@
     }
 
     static constexpr Command kCommands[] = {
-        CmdEntry("connect"), CmdEntry("delete"),   CmdEntry("disconnect"), CmdEntry("get"),   CmdEntry("post"),
+        CmdEntry("connect"),  CmdEntry("delete"),       CmdEntry("disconnect"),  CmdEntry("get"),
+        CmdEntry("isclosed"), CmdEntry("isconnactive"), CmdEntry("isconnected"), CmdEntry("post"),
 #ifdef MBEDTLS_KEY_EXCHANGE_PSK_ENABLED
         CmdEntry("psk"),
 #endif
-        CmdEntry("put"),     CmdEntry("resource"), CmdEntry("set"),        CmdEntry("start"), CmdEntry("stop"),
+        CmdEntry("put"),      CmdEntry("resource"),     CmdEntry("set"),         CmdEntry("start"),
+        CmdEntry("stop"),
 #ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
         CmdEntry("x509"),
 #endif
diff --git a/src/cli/cli_coap_secure.hpp b/src/cli/cli_coap_secure.hpp
index 2165642..92a7a95 100644
--- a/src/cli/cli_coap_secure.hpp
+++ b/src/cli/cli_coap_secure.hpp
@@ -42,7 +42,7 @@
 
 #include <openthread/coap_secure.h>
 
-#include "cli/cli_output.hpp"
+#include "cli/cli_utils.hpp"
 
 #ifndef CLI_COAP_SECURE_USE_COAP_DEFAULT_HANDLER
 #define CLI_COAP_SECURE_USE_COAP_DEFAULT_HANDLER 0
@@ -55,11 +55,9 @@
  * Implements the CLI CoAP Secure server and client.
  *
  */
-class CoapSecure : private Output
+class CoapSecure : private Utils
 {
 public:
-    typedef Utils::CmdLineParser::Arg Arg;
-
     /**
      * Constructor
      *
@@ -84,13 +82,10 @@
     otError Process(Arg aArgs[]);
 
 private:
-    enum
-    {
-        kMaxUriLength   = 32,
-        kMaxBufferSize  = 16,
-        kPskMaxLength   = 32,
-        kPskIdMaxLength = 32
-    };
+    static constexpr uint16_t kMaxUriLength   = 32;
+    static constexpr uint16_t kMaxBufferSize  = 16;
+    static constexpr uint8_t  kPskMaxLength   = 32;
+    static constexpr uint8_t  kPskIdMaxLength = 32;
 
     using Command = CommandEntry<CoapSecure>;
 
@@ -106,6 +101,7 @@
     template <CommandId kCommandId> otError Process(Arg aArgs[]);
 
     otError ProcessRequest(Arg aArgs[], otCoapCode aCoapCode);
+    otError ProcessIsRequest(Arg aArgs[], bool (*IsChecker)(otInstance *));
 
     void Stop(void);
 
diff --git a/src/cli/cli_commissioner.cpp b/src/cli/cli_commissioner.cpp
index d8fa14a..03aa838 100644
--- a/src/cli/cli_commissioner.cpp
+++ b/src/cli/cli_commissioner.cpp
@@ -40,6 +40,24 @@
 namespace ot {
 namespace Cli {
 
+/**
+ * @cli commissioner announce
+ * @code
+ * commissioner announce 0x00050000 2 32 fdde:ad00:beef:0:0:ff:fe00:c00
+ * Done
+ * @endcode
+ * @cparam commissioner announce @ca{mask} @ca{count} @ca{period} @ca{destination}
+ *   * `mask`: Bitmask that identifies channels for sending MLE `Announce` messages.
+ *   * `count`: Number of MLE `Announce` transmissions per channel.
+ *   * `period`: Number of milliseconds between successive MLE `Announce` transmissions.
+ *   * `destination`: Destination IPv6 address for the message. The message may be multicast.
+ * @par
+ * Sends an Announce Begin message.
+ * @note Use this command only after successfully starting the %Commissioner role
+ * with the `commissioner start` command.
+ * @csa{commissioner start}
+ * @sa otCommissionerAnnounceBegin
+ */
 template <> otError Commissioner::Process<Cmd("announce")>(Arg aArgs[])
 {
     otError      error;
@@ -59,6 +77,27 @@
     return error;
 }
 
+/**
+ * @cli commissioner energy
+ * @code
+ * commissioner energy 0x00050000 2 32 1000 fdde:ad00:beef:0:0:ff:fe00:c00
+ * Done
+ * Energy: 00050000 0 0 0 0
+ * @endcode
+ * @cparam commissioner energy @ca{mask} @ca{count} @ca{period} @ca{scanDuration} @ca{destination}
+ *   * `mask`: Bitmask that identifies channels for performing IEEE 802.15.4 energy scans.
+ *   * `count`: Number of IEEE 802.15.4 energy scans per channel.
+ *   * `period`: Number of milliseconds between successive IEEE 802.15.4 energy scans.
+ *   * `scanDuration`: Scan duration in milliseconds to use when
+ *     performing an IEEE 802.15.4 energy scan.
+ *   * `destination`: Destination IPv6 address for the message. The message may be multicast.
+ * @par
+ * Sends an Energy Scan Query message. Command output is printed as it is received.
+ * @note Use this command only after successfully starting the %Commissioner role
+ * with the `commissioner start` command.
+ * @csa{commissioner start}
+ * @sa otCommissionerEnergyScan
+ */
 template <> otError Commissioner::Process<Cmd("energy")>(Arg aArgs[])
 {
     otError      error;
@@ -88,6 +127,20 @@
     const otExtAddress *addrPtr = nullptr;
     otJoinerDiscerner   discerner;
 
+    /**
+     * @cli commissioner joiner table
+     * @code
+     * commissioner joiner table
+     * | ID                    | PSKd                             | Expiration |
+     * +-----------------------+----------------------------------+------------+
+     * |                     * |                           J01NME |      81015 |
+     * |      d45e64fa83f81cf7 |                           J01NME |     101204 |
+     * | 0x0000000000000abc/12 |                           J01NME |     114360 |
+     * Done
+     * @endcode
+     * @par
+     * Lists all %Joiner entries in table format.
+     */
     if (aArgs[0] == "table")
     {
         uint16_t     iter = 0;
@@ -133,7 +186,7 @@
 
     VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
 
-    memset(&discerner, 0, sizeof(discerner));
+    ClearAllBytes(discerner);
 
     if (aArgs[1] == "*")
     {
@@ -152,6 +205,29 @@
         SuccessOrExit(error);
     }
 
+    /**
+     * @cli commissioner joiner add
+     * @code
+     * commissioner joiner add d45e64fa83f81cf7 J01NME
+     * Done
+     * @endcode
+     * @code
+     * commissioner joiner add 0xabc/12 J01NME
+     * Done
+     * @endcode
+     * @cparam commissioner joiner add @ca{eui64}|@ca{discerner pksd} [@ca{timeout}]
+     *   * `eui64`: IEEE EUI-64 of the %Joiner. To match any joiner, use `*`.
+     *   * `discerner`: The %Joiner discerner in the format `number/length`.
+     *   * `pksd`: Pre-Shared Key for the joiner.
+     *   * `timeout`: The %Joiner timeout in seconds.
+     * @par
+     * Adds a joiner entry.
+     * @note Use this command only after successfully starting the %Commissioner role
+     * with the `commissioner start` command.
+     * @csa{commissioner start}
+     * @sa otCommissionerAddJoiner
+     * @sa otCommissionerAddJoinerWithDiscerner
+     */
     if (aArgs[0] == "add")
     {
         uint32_t timeout = kDefaultJoinerTimeout;
@@ -171,6 +247,27 @@
         {
             error = otCommissionerAddJoiner(GetInstancePtr(), addrPtr, aArgs[2].GetCString(), timeout);
         }
+        /**
+         * @cli commissioner joiner remove
+         * @code
+         * commissioner joiner remove d45e64fa83f81cf7
+         * Done
+         * @endcode
+         * @code
+         * commissioner joiner remove 0xabc/12
+         * Done
+         * @endcode
+         * @cparam commissioner joiner remove @ca{eui64}|@ca{discerner}
+         *   * `eui64`: IEEE EUI-64 of the joiner. To match any joiner, use `*`.
+         *   * `discerner`: The joiner discerner in the format `number/length`.
+         * @par
+         * Removes a %Joiner entry.
+         * @note Use this command only after successfully starting the %Commissioner role
+         * with the `commissioner start` command.
+         * @csa{commissioner start}
+         * @sa otCommissionerRemoveJoiner
+         * @sa otCommissionerRemoveJoinerWithDiscerner
+         */
     }
     else if (aArgs[0] == "remove")
     {
@@ -192,6 +289,25 @@
     return error;
 }
 
+/**
+ * @cli commissioner mgmtget
+ * @code
+ * commissioner mgmtget locator sessionid
+ * Done
+ * @endcode
+ * @cparam commissioner mgmtget [locator] [sessionid] <!--
+ * -->                          [steeringdata] [joinerudpport] <!--
+ * -->                          [-x @ca{TLVs}]
+ *   * `locator`: Border Router RLOC16.
+ *   * `sessionid`: Session ID of the %Commissioner.
+ *   * `steeringdata`: Steering data.
+ *   * `joinerudpport`: %Joiner UDP port.
+ *   * `TLVs`: The set of TLVs to be retrieved.
+ * @par
+ * Sends a `MGMT_GET` (Management Get) message to the Leader.
+ * Variable values that have been set using the `commissioner mgmtset` command are returned.
+ * @sa otCommissionerSendMgmtGet
+ */
 template <> otError Commissioner::Process<Cmd("mgmtget")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
@@ -239,6 +355,25 @@
     return error;
 }
 
+/**
+ * @cli commissioner mgmtset
+ * @code
+ * commissioner mgmtset joinerudpport 9988
+ * Done
+ * @endcode
+ * @cparam commissioner mgmtset [locator @ca{locator}] [sessionid @ca{sessionid}] <!--
+ * -->                          [steeringdata @ca{steeringdata}] [joinerudpport @ca{joinerudpport}] <!--
+ * -->                          [-x @ca{TLVs}]
+ *   * `locator`: Border Router RLOC16.
+ *   * `sessionid`: Session ID of the %Commissioner.
+ *   * `steeringdata`: Steering data.
+ *   * `joinerudpport`: %Joiner UDP port.
+ *   * `TLVs`: The set of TLVs to be retrieved.
+ * @par
+ * Sends a `MGMT_SET` (Management Set) message to the Leader, and sets the
+ * variables to the values specified.
+ * @sa otCommissionerSendMgmtSet
+ */
 template <> otError Commissioner::Process<Cmd("mgmtset")>(Arg aArgs[])
 {
     otError                error;
@@ -248,7 +383,7 @@
 
     VerifyOrExit(!aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
 
-    memset(&dataset, 0, sizeof(dataset));
+    ClearAllBytes(dataset);
 
     for (; !aArgs->IsEmpty(); aArgs++)
     {
@@ -301,6 +436,25 @@
     return error;
 }
 
+/**
+ * @cli commissioner panid
+ * @code
+ * commissioner panid 0xdead 0x7fff800 fdde:ad00:beef:0:0:ff:fe00:c00
+ * Done
+ * Conflict: dead, 00000800
+ * @endcode
+ * @cparam commissioner panid @ca{panid} @ca{mask} @ca{destination}
+ *   * `paind`: PAN ID to use to check for conflicts.
+ *   * `mask`; Bitmask that identifies channels to perform IEEE 802.15.4
+ *     Active Scans.
+ *   * `destination`: IPv6 destination address for the message. The message may be multicast.
+ * @par
+ * Sends a PAN ID query. Command output is returned as it is received.
+ * @note Use this command only after successfully starting the %Commissioner role
+ * with the `commissioner start` command.
+ * @csa{commissioner start}
+ * @sa otCommissionerPanIdQuery
+ */
 template <> otError Commissioner::Process<Cmd("panid")>(Arg aArgs[])
 {
     otError      error;
@@ -318,6 +472,17 @@
     return error;
 }
 
+/**
+ * @cli commissioner provisioningurl
+ * @code
+ * commissioner provisioningurl http://github.com/openthread/openthread
+ * Done
+ * @endcode
+ * @cparam commissioner provisioningurl @ca{provisioningurl}
+ * @par
+ * Sets the %Commissioner provisioning URL.
+ * @sa otCommissionerSetProvisioningUrl
+ */
 template <> otError Commissioner::Process<Cmd("provisioningurl")>(Arg aArgs[])
 {
     // If aArgs[0] is empty, `GetCString() will return `nullptr`
@@ -325,6 +490,17 @@
     return otCommissionerSetProvisioningUrl(GetInstancePtr(), aArgs[0].GetCString());
 }
 
+/**
+ * @cli commissioner sessionid
+ * @code
+ * commissioner sessionid
+ * 0
+ * Done
+ * @endcode
+ * @par
+ * Gets the current %Commissioner session ID.
+ * @sa otCommissionerGetSessionId
+ */
 template <> otError Commissioner::Process<Cmd("sessionid")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
@@ -334,6 +510,22 @@
     return OT_ERROR_NONE;
 }
 
+/**
+ * @cli commissioner id (get,set)
+ * @code
+ * commissioner id OpenThread Commissioner
+ * Done
+ * @endcode
+ * @code
+ * commissioner id
+ * OpenThread Commissioner
+ * Done
+ * @endcode
+ * @cparam commissioner id @ca{name}
+ * @par
+ * Gets or sets the OpenThread %Commissioner ID name.
+ * @sa otCommissionerSetId
+ */
 template <> otError Commissioner::Process<Cmd("id")>(Arg aArgs[])
 {
     otError error;
@@ -351,6 +543,20 @@
     return error;
 }
 
+/**
+ * @cli commissioner start
+ * @code
+ * commissioner start
+ * Commissioner: petitioning
+ * Done
+ * Commissioner: active
+ * @endcode
+ * @par
+ * Starts the Thread %Commissioner role.
+ * @note The `commissioner` commands are available only when
+ * `OPENTHREAD_CONFIG_COMMISSIONER_ENABLE` and `OPENTHREAD_FTD` are set.
+ * @sa otCommissionerStart
+ */
 template <> otError Commissioner::Process<Cmd("start")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
@@ -422,6 +628,16 @@
     OutputNewLine();
 }
 
+/**
+ * @cli commissioner stop
+ * @code
+ * commissioner stop
+ * Done
+ * @endcode
+ * @par
+ * Stops the Thread %Commissioner role.
+ * @sa otCommissionerStop
+ */
 template <> otError Commissioner::Process<Cmd("stop")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
@@ -429,6 +645,18 @@
     return otCommissionerStop(GetInstancePtr());
 }
 
+/**
+ * @cli commissioner state
+ * @code
+ * commissioner state
+ * active
+ * Done
+ * @endcode
+ * @par
+ * Returns the current state of the %Commissioner. Possible values are
+ * `active`, `disabled`, or `petition` (petitioning to become %Commissioner).
+ * @sa otCommissionerState
+ */
 template <> otError Commissioner::Process<Cmd("state")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
diff --git a/src/cli/cli_commissioner.hpp b/src/cli/cli_commissioner.hpp
index 8969bd7..a5feac4 100644
--- a/src/cli/cli_commissioner.hpp
+++ b/src/cli/cli_commissioner.hpp
@@ -38,7 +38,7 @@
 
 #include <openthread/commissioner.h>
 
-#include "cli/cli_output.hpp"
+#include "cli/cli_utils.hpp"
 
 #if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD
 
@@ -49,11 +49,9 @@
  * Implements the Commissioner CLI interpreter.
  *
  */
-class Commissioner : private Output
+class Commissioner : private Utils
 {
 public:
-    typedef Utils::CmdLineParser::Arg Arg;
-
     /**
      * Constructor
      *
@@ -62,7 +60,7 @@
      *
      */
     Commissioner(otInstance *aInstance, OutputImplementer &aOutputImplementer)
-        : Output(aInstance, aOutputImplementer)
+        : Utils(aInstance, aOutputImplementer)
     {
     }
 
@@ -81,10 +79,7 @@
     otError Process(Arg aArgs[]);
 
 private:
-    enum
-    {
-        kDefaultJoinerTimeout = 120, ///< Default timeout for Joiners, in seconds.
-    };
+    static constexpr uint32_t kDefaultJoinerTimeout = 120; ///< Default timeout for Joiners, in seconds.
 
     using Command = CommandEntry<Commissioner>;
 
diff --git a/src/cli/cli_config.h b/src/cli/cli_config.h
index c3762c4..0eb9b2d 100644
--- a/src/cli/cli_config.h
+++ b/src/cli/cli_config.h
@@ -59,6 +59,16 @@
 #endif
 
 /**
+ * @def OPENTHREAD_CONFIG_CLI_BLE_SECURE_ENABLE
+ *
+ * Indicates whether TCAT should be enabled in the CLI tool.
+ *
+ */
+#ifndef OPENTHREAD_CONFIG_CLI_BLE_SECURE_ENABLE
+#define OPENTHREAD_CONFIG_CLI_BLE_SECURE_ENABLE 1
+#endif
+
+/**
  * @def OPENTHREAD_CONFIG_CLI_TCP_ENABLE
  *
  * Indicates whether TCP should be enabled in the CLI tool.
@@ -186,4 +196,14 @@
 #define OPENTHREAD_CONFIG_CLI_REGISTER_IP6_RECV_CALLBACK 0
 #endif
 
+/**
+ * @def OPENTHREAD_CONFIG_CLI_BLE_SECURE_ENABLE
+ *
+ * Define to 1 to enable BLE secure support.
+ *
+ */
+#ifndef OPENTHREAD_CONFIG_CLI_BLE_SECURE_ENABLE
+#define OPENTHREAD_CONFIG_CLI_BLE_SECURE_ENABLE 0
+#endif
+
 #endif // CONFIG_CLI_H_
diff --git a/src/cli/cli_dataset.cpp b/src/cli/cli_dataset.cpp
index 32a4372..63e3203 100644
--- a/src/cli/cli_dataset.cpp
+++ b/src/cli/cli_dataset.cpp
@@ -526,7 +526,7 @@
     }
     else
     {
-        memset(&dataset, 0, sizeof(dataset));
+        ClearAllBytes(dataset);
         SuccessOrExit(error = (this->*aMapper.mParse)(aArgs, dataset));
         dataset.mComponents.*aMapper.mIsPresentPtr = true;
         SuccessOrExit(error = otDatasetUpdateTlvs(&dataset, &sDatasetTlvs));
@@ -716,7 +716,7 @@
 {
     OT_UNUSED_VARIABLE(aArgs);
 
-    memset(&sDatasetTlvs, 0, sizeof(sDatasetTlvs));
+    ClearAllBytes(sDatasetTlvs);
     return OT_ERROR_NONE;
 }
 
@@ -765,7 +765,7 @@
     uint8_t              tlvs[128];
     uint8_t              tlvsLength = 0;
 
-    memset(&dataset, 0, sizeof(dataset));
+    ClearAllBytes(dataset);
 
     for (Arg *arg = &aArgs[1]; !arg->IsEmpty();)
     {
@@ -853,7 +853,7 @@
     bool                           destAddrSpecified = false;
     otIp6Address                   address;
 
-    memset(&datasetComponents, 0, sizeof(datasetComponents));
+    ClearAllBytes(datasetComponents);
 
     for (Arg *arg = &aArgs[1]; !arg->IsEmpty(); arg++)
     {
@@ -1003,7 +1003,7 @@
     otSecurityPolicy policy;
     uint8_t          versionThreshold;
 
-    memset(&policy, 0, sizeof(policy));
+    ClearAllBytes(policy);
 
     SuccessOrExit(error = aArgs->ParseAsUint16(policy.mRotationTime));
     aArgs++;
@@ -1154,10 +1154,46 @@
 {
     otError error = OT_ERROR_NONE;
 
+    /**
+     * @cli dataset updater
+     * @code
+     * dataset updater
+     * Enabled
+     * Done
+     * @endcode
+     * @par api_copy
+     * #otDatasetUpdaterIsUpdateOngoing
+     */
     if (aArgs[0].IsEmpty())
     {
         OutputEnabledDisabledStatus(otDatasetUpdaterIsUpdateOngoing(GetInstancePtr()));
     }
+    /**
+     * @cli dataset updater start
+     * @code
+     * channel
+     * 19
+     * Done
+     * dataset clear
+     * Done
+     * dataset channel 15
+     * Done
+     * dataset
+     * Channel: 15
+     * Done
+     * dataset updater start
+     * Done
+     * dataset updater
+     * Enabled
+     * Done
+     * Dataset update complete: OK
+     * channel
+     * 15
+     * Done
+     * @endcode
+     * @par api_copy
+     * #otDatasetUpdaterRequestUpdate
+     */
     else if (aArgs[0] == "start")
     {
         otOperationalDataset dataset;
@@ -1166,6 +1202,15 @@
         SuccessOrExit(
             error = otDatasetUpdaterRequestUpdate(GetInstancePtr(), &dataset, &Dataset::HandleDatasetUpdater, this));
     }
+    /**
+     * @cli dataset updater cancel
+     * @code
+     * @dataset updater cancel
+     * Done
+     * @endcode
+     * @par api_copy
+     * #otDatasetUpdaterCancelUpdate
+     */
     else if (aArgs[0] == "cancel")
     {
         otDatasetUpdaterCancelUpdate(GetInstancePtr());
diff --git a/src/cli/cli_dataset.hpp b/src/cli/cli_dataset.hpp
index 8587862..06055e3 100644
--- a/src/cli/cli_dataset.hpp
+++ b/src/cli/cli_dataset.hpp
@@ -40,7 +40,7 @@
 
 #include <openthread/dataset.h>
 
-#include "cli/cli_output.hpp"
+#include "cli/cli_utils.hpp"
 
 namespace ot {
 namespace Cli {
@@ -49,13 +49,11 @@
  * Implements the Dataset CLI interpreter.
  *
  */
-class Dataset : private Output
+class Dataset : private Utils
 {
 public:
-    typedef Utils::CmdLineParser::Arg Arg;
-
     Dataset(otInstance *aInstance, OutputImplementer &aOutputImplementer)
-        : Output(aInstance, aOutputImplementer)
+        : Utils(aInstance, aOutputImplementer)
     {
     }
 
diff --git a/src/cli/cli_dns.cpp b/src/cli/cli_dns.cpp
index c532a0f..5148bb3 100644
--- a/src/cli/cli_dns.cpp
+++ b/src/cli/cli_dns.cpp
@@ -416,15 +416,15 @@
 
     otError error = OT_ERROR_NONE;
     bool    recursionDesired;
-    bool    nat64SynthesizedAddress;
+    bool    nat64Synth;
 
-    memset(aConfig, 0, sizeof(otDnsQueryConfig));
+    ClearAllBytes(*aConfig);
 
     VerifyOrExit(!aArgs[0].IsEmpty(), aConfig = nullptr);
 
     SuccessOrExit(error = Interpreter::ParseToIp6Address(GetInstancePtr(), aArgs[0], aConfig->mServerSockAddr.mAddress,
-                                                         nat64SynthesizedAddress));
-    if (nat64SynthesizedAddress)
+                                                         nat64Synth));
+    if (nat64Synth)
     {
         OutputFormat("Synthesized IPv6 DNS server address: ");
         OutputIp6AddressLine(aConfig->mServerSockAddr.mAddress);
@@ -679,8 +679,7 @@
          * @par api_copy
          * #otDnssdUpstreamQuerySetEnabled
          */
-        error = Interpreter::GetInterpreter().ProcessEnableDisable(aArgs + 1, otDnssdUpstreamQueryIsEnabled,
-                                                                   otDnssdUpstreamQuerySetEnabled);
+        error = ProcessEnableDisable(aArgs + 1, otDnssdUpstreamQueryIsEnabled, otDnssdUpstreamQuerySetEnabled);
     }
 #endif // OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE
     else
diff --git a/src/cli/cli_dns.hpp b/src/cli/cli_dns.hpp
index b9f1679..bf31ed7 100644
--- a/src/cli/cli_dns.hpp
+++ b/src/cli/cli_dns.hpp
@@ -54,7 +54,7 @@
 #include <openthread/dnssd_server.h>
 
 #include "cli/cli_config.h"
-#include "cli/cli_output.hpp"
+#include "cli/cli_utils.hpp"
 
 namespace ot {
 namespace Cli {
@@ -63,11 +63,9 @@
  * Implements the DNS CLI interpreter.
  *
  */
-class Dns : private Output
+class Dns : private Utils
 {
 public:
-    typedef Utils::CmdLineParser::Arg Arg;
-
     /**
      * Constructor.
      *
@@ -76,7 +74,7 @@
      *
      */
     Dns(otInstance *aInstance, OutputImplementer &aOutputImplementer)
-        : Output(aInstance, aOutputImplementer)
+        : Utils(aInstance, aOutputImplementer)
     {
     }
 
diff --git a/src/cli/cli_history.cpp b/src/cli/cli_history.cpp
index b44a74f..82d7555 100644
--- a/src/cli/cli_history.cpp
+++ b/src/cli/cli_history.cpp
@@ -71,6 +71,62 @@
     return aArgs[0].IsEmpty() ? OT_ERROR_NONE : OT_ERROR_INVALID_ARGS;
 }
 
+/**
+ * @cli history ipaddr
+ * @code
+ * history ipaddr
+ * | Age                  | Event   | Address / Prefix Length                     | Origin |Scope| P | V | R |
+ * +----------------------+---------+---------------------------------------------+--------+-----+---+---+---+
+ * |         00:00:04.991 | Removed | 2001:dead:beef:cafe:c4cb:caba:8d55:e30b/64  | slaac  |  14 | Y | Y | N |
+ * |         00:00:44.647 | Added   | 2001:dead:beef:cafe:c4cb:caba:8d55:e30b/64  | slaac  |  14 | Y | Y | N |
+ * |         00:01:07.199 | Added   | fd00:0:0:0:0:0:0:1/64                       | manual |  14 | Y | Y | N |
+ * |         00:02:17.885 | Added   | fdde:ad00:beef:0:0:ff:fe00:fc00/64          | thread |   3 | N | Y | N |
+ * |         00:02:17.885 | Added   | fdde:ad00:beef:0:0:ff:fe00:5400/64          | thread |   3 | N | Y | Y |
+ * |         00:02:20.107 | Removed | fdde:ad00:beef:0:0:ff:fe00:5400/64          | thread |   3 | N | Y | Y |
+ * |         00:02:21.575 | Added   | fdde:ad00:beef:0:0:ff:fe00:5400/64          | thread |   3 | N | Y | Y |
+ * |         00:02:21.575 | Added   | fdde:ad00:beef:0:ecea:c4fc:ad96:4655/64     | thread |   3 | N | Y | N |
+ * |         00:02:23.904 | Added   | fe80:0:0:0:3c12:a4d2:fbe0:31ad/64           | thread |   2 | Y | Y | N |
+ * Done
+ * @endcode
+ * @code
+ * history ipaddr list 5
+ * 00:00:20.327 -> event:Removed address:2001:dead:beef:cafe:c4cb:caba:8d55:e30b <!--
+ * -->prefixlen:64 origin:slaac scope:14 preferred:yes valid:yes rloc:no
+ * 00:00:59.983 -> event:Added address:2001:dead:beef:cafe:c4cb:caba:8d55:e30b <!--
+ * -->prefixlen:64 origin:slaac scope:14 preferred:yes valid:yes rloc:no
+ * 00:01:22.535 -> event:Added address:fd00:0:0:0:0:0:0:1 prefixlen:64 <!--
+ * -->origin:manual scope:14 preferred:yes valid:yes rloc:no
+ * 00:02:33.221 -> event:Added address:fdde:ad00:beef:0:0:ff:fe00:fc00 <!--
+ * -->prefixlen:64 origin:thread scope:3 preferred:no valid:yes rloc:no
+ * 00:02:33.221 -> event:Added address:fdde:ad00:beef:0:0:ff:fe00:5400 <!--
+ * -->prefixlen:64 origin:thread scope:3 preferred:no valid:yes rloc:yes
+ * Done
+ * @endcode
+ * @cparam history ipaddr [@ca{list}] [@ca{num-entries}]
+ * * Use the `list` option to display the output in list format. Otherwise,
+ *   the output is shown in table format.
+ * * Use the `num-entries` option to limit the output to the number of
+ *   most-recent entries specified. If this option is not used, all stored
+ *   entries are shown in the output.
+ * @par
+ * Displays the unicast IPv6 address history in table or list format.
+ * @par
+ * Each table or list entry provides:
+ * * Age: Time elapsed since the command was issued, and given in the format:
+ *        `hours`:`minutes`:`seconds`:`milliseconds`
+ * * Event: Possible values are `Added` or `Removed`.
+ * * Address/Prefix Length: Unicast address with its prefix length (in bits).
+ * * Origin: Possible value are `thread`, `slaac`, `dhcp6`, or `manual`.
+ * * Scope: IPv6 address scope.
+ * * P: Preferred flag.
+ * * V: Valid flag.
+ * * RLOC (R): This flag indicates if the IPv6 address is a routing locator.
+ * @note
+ * All commands under `history` require the `OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE`
+ * feature to be enabled.
+ * @sa otHistoryTrackerEntryAgeToString
+ * @sa otHistoryTrackerIterateUnicastAddressHistory
+ */
 template <> otError History::Process<Cmd("ipaddr")>(Arg aArgs[])
 {
     otError                                   error;
@@ -134,6 +190,54 @@
     return error;
 }
 
+/**
+ * @cli history ipmaddr
+ * @code
+ * history ipmaddr
+ * | Age                  | Event        | Multicast Address                       | Origin |
+ * +----------------------+--------------+-----------------------------------------+--------+
+ * |         00:00:08.592 | Unsubscribed | ff05:0:0:0:0:0:0:1                      | Manual |
+ * |         00:01:25.353 | Subscribed   | ff05:0:0:0:0:0:0:1                      | Manual |
+ * |         00:01:54.953 | Subscribed   | ff03:0:0:0:0:0:0:2                      | Thread |
+ * |         00:01:54.953 | Subscribed   | ff02:0:0:0:0:0:0:2                      | Thread |
+ * |         00:01:59.329 | Subscribed   | ff33:40:fdde:ad00:beef:0:0:1            | Thread |
+ * |         00:01:59.329 | Subscribed   | ff32:40:fdde:ad00:beef:0:0:1            | Thread |
+ * |         00:02:01.129 | Subscribed   | ff03:0:0:0:0:0:0:fc                     | Thread |
+ * |         00:02:01.129 | Subscribed   | ff03:0:0:0:0:0:0:1                      | Thread |
+ * |         00:02:01.129 | Subscribed   | ff02:0:0:0:0:0:0:1                      | Thread |
+ * Done
+ * @endcode
+ * @code
+ * history ipmaddr list
+ * 00:00:25.447 -> event:Unsubscribed address:ff05:0:0:0:0:0:0:1 origin:Manual
+ * 00:01:42.208 -> event:Subscribed address:ff05:0:0:0:0:0:0:1 origin:Manual
+ * 00:02:11.808 -> event:Subscribed address:ff03:0:0:0:0:0:0:2 origin:Thread
+ * 00:02:11.808 -> event:Subscribed address:ff02:0:0:0:0:0:0:2 origin:Thread
+ * 00:02:16.184 -> event:Subscribed address:ff33:40:fdde:ad00:beef:0:0:1 origin:Thread
+ * 00:02:16.184 -> event:Subscribed address:ff32:40:fdde:ad00:beef:0:0:1 origin:Thread
+ * 00:02:17.984 -> event:Subscribed address:ff03:0:0:0:0:0:0:fc origin:Thread
+ * 00:02:17.984 -> event:Subscribed address:ff03:0:0:0:0:0:0:1 origin:Thread
+ * 00:02:17.984 -> event:Subscribed address:ff02:0:0:0:0:0:0:1 origin:Thread
+ * Done
+ * @endcode
+ * @cparam history ipmaddr [@ca{list}] [@ca{num-entries}]
+ * * Use the `list` option to display the output in list format. Otherwise,
+ *   the output is shown in table format.
+ * * Use the `num-entries` option to limit the output to the number of
+ *   most-recent entries specified. If this option is not used, all stored
+ *   entries are shown in the output.
+ * @par
+ * Displays the multicast IPv6 address history in table or list format.
+ * @par
+ * Each table or list entry provides:
+ * * Age: Time elapsed since the command was issued, and given in the format:
+ *        `hours`:`minutes`:`seconds`:`milliseconds`
+ * * Event: Possible values are `Subscribed` or `Unsubscribed`.
+ * * Multicast Address
+ * * Origin: Possible values are `Thread` or `Manual`.
+ * @sa otHistoryTrackerEntryAgeToString
+ * @sa otHistoryTrackerIterateMulticastAddressHistory
+ */
 template <> otError History::Process<Cmd("ipmaddr")>(Arg aArgs[])
 {
     static const char *const kEventStrings[] = {
@@ -191,6 +295,62 @@
     return error;
 }
 
+/**
+ * @cli history neighbor
+ * @code
+ * history neighbor
+ * | Age                  | Type   | Event     | Extended Address | RLOC16 | Mode | Ave RSS |
+ * +----------------------+--------+-----------+------------------+--------+------+---------+
+ * |         00:00:29.233 | Child  | Added     | ae5105292f0b9169 | 0x8404 | -    |     -20 |
+ * |         00:01:38.368 | Child  | Removed   | ae5105292f0b9169 | 0x8401 | -    |     -20 |
+ * |         00:04:27.181 | Child  | Changed   | ae5105292f0b9169 | 0x8401 | -    |     -20 |
+ * |         00:04:51.236 | Router | Added     | 865c7ca38a5fa960 | 0x9400 | rdn  |     -20 |
+ * |         00:04:51.587 | Child  | Removed   | 865c7ca38a5fa960 | 0x8402 | rdn  |     -20 |
+ * |         00:05:22.764 | Child  | Changed   | ae5105292f0b9169 | 0x8401 | rn   |     -20 |
+ * |         00:06:40.764 | Child  | Added     | 4ec99efc874a1841 | 0x8403 | r    |     -20 |
+ * |         00:06:44.060 | Child  | Added     | 865c7ca38a5fa960 | 0x8402 | rdn  |     -20 |
+ * |         00:06:49.515 | Child  | Added     | ae5105292f0b9169 | 0x8401 | -    |     -20 |
+ * Done
+ * @endcode
+ * @code
+ * history neighbor list
+ * 00:00:34.753 -> type:Child event:Added extaddr:ae5105292f0b9169 rloc16:0x8404 mode:- rss:-20
+ * 00:01:43.888 -> type:Child event:Removed extaddr:ae5105292f0b9169 rloc16:0x8401 mode:- rss:-20
+ * 00:04:32.701 -> type:Child event:Changed extaddr:ae5105292f0b9169 rloc16:0x8401 mode:- rss:-20
+ * 00:04:56.756 -> type:Router event:Added extaddr:865c7ca38a5fa960 rloc16:0x9400 mode:rdn rss:-20
+ * 00:04:57.107 -> type:Child event:Removed extaddr:865c7ca38a5fa960 rloc16:0x8402 mode:rdn rss:-20
+ * 00:05:28.284 -> type:Child event:Changed extaddr:ae5105292f0b9169 rloc16:0x8401 mode:rn rss:-20
+ * 00:06:46.284 -> type:Child event:Added extaddr:4ec99efc874a1841 rloc16:0x8403 mode:r rss:-20
+ * 00:06:49.580 -> type:Child event:Added extaddr:865c7ca38a5fa960 rloc16:0x8402 mode:rdn rss:-20
+ * 00:06:55.035 -> type:Child event:Added extaddr:ae5105292f0b9169 rloc16:0x8401 mode:- rss:-20
+ * Done
+ * @endcode
+ * @cparam history neighbor [@ca{list}] [@ca{num-entries}]
+ * * Use the `list` option to display the output in list format. Otherwise,
+ *   the output is shown in table format.
+ * * Use the `num-entries` option to limit the output to the number of
+ *   most-recent entries specified. If this option is not used, all stored
+ *   entries are shown in the output.
+ * @par
+ * Displays the neighbor history in table or list format.
+ * @par
+ * Each table or list entry provides:
+ * * Age: Time elapsed since the command was issued, and given in the format:
+ *        `hours`:`minutes`:`seconds`:`milliseconds`
+ * * Type: `Child` or `Router`.
+ * * Event: Possible values are `Added`, `Removed`, or `Changed`.
+ * * Extended Address
+ * * RLOC16
+ * * Mode: MLE link mode. Possible values:
+ *     * `-`: no flags set (rx-off-when-idle, minimal Thread device,
+ *       stable network data).
+ *     * `r`: rx-on-when-idle
+ *     * `d`: Full Thread Device.
+ *     * `n`: Full Network Data
+ * * Ave RSS: Average number of frames (in dBm) received from the neighbor at the
+ *   time the entry was recorded.
+ * @sa otHistoryTrackerIterateNeighborHistory
+ */
 template <> otError History::Process<Cmd("neighbor")>(Arg aArgs[])
 {
     static const char *const kEventString[] = {
@@ -256,6 +416,87 @@
     return error;
 }
 
+/**
+ * @cli history router
+ * @code
+ * history router
+ * | Age                  | Event          | ID (RLOC16) | Next Hop    | Path Cost  |
+ * +----------------------+----------------+-------------+-------------+------------+
+ * |         00:00:05.258 | NextHopChanged |  7 (0x1c00) | 34 (0x8800) | inf ->   3 |
+ * |         00:00:08.604 | NextHopChanged | 34 (0x8800) | 34 (0x8800) | inf ->   2 |
+ * |         00:00:08.604 | Added          |  7 (0x1c00) |        none | inf -> inf |
+ * |         00:00:11.931 | Added          | 34 (0x8800) |        none | inf -> inf |
+ * |         00:00:14.948 | Removed        | 59 (0xec00) |        none | inf -> inf |
+ * |         00:00:14.948 | Removed        | 54 (0xd800) |        none | inf -> inf |
+ * |         00:00:14.948 | Removed        | 34 (0x8800) |        none | inf -> inf |
+ * |         00:00:14.948 | Removed        |  7 (0x1c00) |        none | inf -> inf |
+ * |         00:00:54.795 | NextHopChanged | 59 (0xec00) | 34 (0x8800) |   1 ->   5 |
+ * |         00:02:33.735 | NextHopChanged | 54 (0xd800) |        none |  15 -> inf |
+ * |         00:03:10.915 | CostChanged    | 54 (0xd800) | 34 (0x8800) |  13 ->  15 |
+ * |         00:03:45.716 | NextHopChanged | 54 (0xd800) | 34 (0x8800) |  15 ->  13 |
+ * |         00:03:46.188 | CostChanged    | 54 (0xd800) | 59 (0xec00) |  13 ->  15 |
+ * |         00:04:19.124 | CostChanged    | 54 (0xd800) | 59 (0xec00) |  11 ->  13 |
+ * |         00:04:52.008 | CostChanged    | 54 (0xd800) | 59 (0xec00) |   9 ->  11 |
+ * |         00:05:23.176 | CostChanged    | 54 (0xd800) | 59 (0xec00) |   7 ->   9 |
+ * |         00:05:51.081 | CostChanged    | 54 (0xd800) | 59 (0xec00) |   5 ->   7 |
+ * |         00:06:48.721 | CostChanged    | 54 (0xd800) | 59 (0xec00) |   3 ->   5 |
+ * |         00:07:13.792 | NextHopChanged | 54 (0xd800) | 59 (0xec00) |   1 ->   3 |
+ * |         00:09:28.681 | NextHopChanged |  7 (0x1c00) | 34 (0x8800) | inf ->   3 |
+ * |         00:09:31.882 | Added          |  7 (0x1c00) |        none | inf -> inf |
+ * |         00:09:51.240 | NextHopChanged | 54 (0xd800) | 54 (0xd800) | inf ->   1 |
+ * |         00:09:54.204 | Added          | 54 (0xd800) |        none | inf -> inf |
+ * |         00:10:20.645 | NextHopChanged | 34 (0x8800) | 34 (0x8800) | inf ->   2 |
+ * |         00:10:24.242 | NextHopChanged | 59 (0xec00) | 59 (0xec00) | inf ->   1 |
+ * |         00:10:24.242 | Added          | 34 (0x8800) |        none | inf -> inf |
+ * |         00:10:41.900 | NextHopChanged | 59 (0xec00) |        none |   1 -> inf |
+ * |         00:10:42.480 | Added          |  3 (0x0c00) |  3 (0x0c00) | inf -> inf |
+ * |         00:10:43.614 | Added          | 59 (0xec00) | 59 (0xec00) | inf ->   1 |
+ * Done
+ * @endcode
+ * @code
+ * history router list 20
+ * 00:00:06.959 -> event:NextHopChanged router:7(0x1c00) nexthop:34(0x8800) old-cost:inf new-cost:3
+ * 00:00:10.305 -> event:NextHopChanged router:34(0x8800) nexthop:34(0x8800) old-cost:inf new-cost:2
+ * 00:00:10.305 -> event:Added router:7(0x1c00) nexthop:none old-cost:inf new-cost:inf
+ * 00:00:13.632 -> event:Added router:34(0x8800) nexthop:none old-cost:inf new-cost:inf
+ * 00:00:16.649 -> event:Removed router:59(0xec00) nexthop:none old-cost:inf new-cost:inf
+ * 00:00:16.649 -> event:Removed router:54(0xd800) nexthop:none old-cost:inf new-cost:inf
+ * 00:00:16.649 -> event:Removed router:34(0x8800) nexthop:none old-cost:inf new-cost:inf
+ * 00:00:16.649 -> event:Removed router:7(0x1c00) nexthop:none old-cost:inf new-cost:inf
+ * 00:00:56.496 -> event:NextHopChanged router:59(0xec00) nexthop:34(0x8800) old-cost:1 new-cost:5
+ * 00:02:35.436 -> event:NextHopChanged router:54(0xd800) nexthop:none old-cost:15 new-cost:inf
+ * 00:03:12.616 -> event:CostChanged router:54(0xd800) nexthop:34(0x8800) old-cost:13 new-cost:15
+ * 00:03:47.417 -> event:NextHopChanged router:54(0xd800) nexthop:34(0x8800) old-cost:15 new-cost:13
+ * 00:03:47.889 -> event:CostChanged router:54(0xd800) nexthop:59(0xec00) old-cost:13 new-cost:15
+ * 00:04:20.825 -> event:CostChanged router:54(0xd800) nexthop:59(0xec00) old-cost:11 new-cost:13
+ * 00:04:53.709 -> event:CostChanged router:54(0xd800) nexthop:59(0xec00) old-cost:9 new-cost:11
+ * 00:05:24.877 -> event:CostChanged router:54(0xd800) nexthop:59(0xec00) old-cost:7 new-cost:9
+ * 00:05:52.782 -> event:CostChanged router:54(0xd800) nexthop:59(0xec00) old-cost:5 new-cost:7
+ * 00:06:50.422 -> event:CostChanged router:54(0xd800) nexthop:59(0xec00) old-cost:3 new-cost:5
+ * 00:07:15.493 -> event:NextHopChanged router:54(0xd800) nexthop:59(0xec00) old-cost:1 new-cost:3
+ * 00:09:30.382 -> event:NextHopChanged router:7(0x1c00) nexthop:34(0x8800) old-cost:inf new-cost:3
+ * Done
+ * @endcode
+ * @cparam history router [@ca{list}] [@ca{num-entries}]
+ * * Use the `list` option to display the output in list format. Otherwise,
+ *   the output is shown in table format.
+ * * Use the `num-entries` option to limit the output to the number of
+ *   most-recent entries specified. If this option is not used, all stored
+ *   entries are shown in the output.
+ * @par
+ * Displays the route-table history in table or list format.
+ * @par
+ * Each table or list entry provides:
+ * * Age: Time elapsed since the command was issued, and given in the format:
+ *        `hours`:`minutes`:`seconds`:`milliseconds`
+ * * Event: Possible values are `Added`, `Removed`, `NextHopChanged`, or `CostChanged`.
+ * * ID (RLOC16): Router ID and RLOC16 of the router.
+ * * Next Hop: Router ID and RLOC16 of the next hop. If there is no next hop,
+ *             `none` is shown.
+ * * Path Cost: old cost `->` new cost. A value of `inf` indicates an infinite
+ *      	path cost.
+ * @sa otHistoryTrackerIterateRouterHistory
+ */
 template <> otError History::Process<Cmd("router")>(Arg aArgs[])
 {
     static const char *const kEventString[] = {
@@ -342,6 +583,61 @@
     return error;
 }
 
+/**
+ * @cli history netinfo
+ * @code
+ * history netinfo
+ * | Age                  | Role     | Mode | RLOC16 | Partition ID |
+ * +----------------------+----------+------+--------+--------------+
+ * |         00:00:10.069 | router   | rdn  | 0x6000 |    151029327 |
+ * |         00:02:09.337 | child    | rdn  | 0x2001 |    151029327 |
+ * |         00:02:09.338 | child    | rdn  | 0x2001 |    151029327 |
+ * |         00:07:40.806 | child    | -    | 0x2001 |    151029327 |
+ * |         00:07:42.297 | detached | -    | 0x6000 |            0 |
+ * |         00:07:42.968 | disabled | -    | 0x6000 |            0 |
+ * Done
+ * @endcode
+ * @code
+ * history netinfo list
+ * 00:00:59.467 -> role:router mode:rdn rloc16:0x6000 partition-id:151029327
+ * 00:02:58.735 -> role:child mode:rdn rloc16:0x2001 partition-id:151029327
+ * 00:02:58.736 -> role:child mode:rdn rloc16:0x2001 partition-id:151029327
+ * 00:08:30.204 -> role:child mode:- rloc16:0x2001 partition-id:151029327
+ * 00:08:31.695 -> role:detached mode:- rloc16:0x6000 partition-id:0
+ * 00:08:32.366 -> role:disabled mode:- rloc16:0x6000 partition-id:0
+ * Done
+ * @endcode
+ * @code
+ * history netinfo 2
+ * | Age                  | Role     | Mode | RLOC16 | Partition ID |
+ * +----------------------+----------+------+--------+--------------+
+ * |         00:02:05.451 | router   | rdn  | 0x6000 |    151029327 |
+ * |         00:04:04.719 | child    | rdn  | 0x2001 |    151029327 |
+ * Done
+ * @endcode
+ * @cparam history netinfo [@ca{list}] [@ca{num-entries}]
+ * * Use the `list` option to display the output in list format. Otherwise,
+ *   the output is shown in table format.
+ * * Use the `num-entries` option to limit the output to the number of
+ *   most-recent entries specified. If this option is not used, all stored
+ *   entries are shown in the output.
+ * @par
+ * Displays the network info history in table or list format.
+ * @par
+ * Each table or list entry provides:
+ * * Age: Time elapsed since the command was issued, and given in the format:
+ *        `hours`:`minutes`:`seconds`:`milliseconds`
+ * * Role: Device role. Possible values are `router`, `child`, `detached`, or `disabled`.
+ * * Mode: MLE link mode. Possible values:
+ *     * `-`: no flags set (rx-off-when-idle, minimal Thread device,
+ *       stable network data).
+ *     * `r`: rx-on-when-idle
+ *     * `d`: Full Thread Device.
+ *     * `n`: Full Network Data
+ * * RLOC16
+ * * Partition ID.
+ * @sa otHistoryTrackerIterateNetInfoHistory
+ */
 template <> otError History::Process<Cmd("netinfo")>(Arg aArgs[])
 {
     otError                            error;
@@ -385,10 +681,324 @@
     return error;
 }
 
+/**
+ * @cli history rx
+ * @code
+ * history rx
+ * | Age                  | Type             | Len   | Chksum | Sec | Prio | RSS  |Dir | Neighb | Radio |
+ * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+
+ * |                      | UDP              |    50 | 0xbd26 |  no |  net |  -20 | RX | 0x4800 |  15.4 |
+ * |         00:00:07.640 | src: [fe80:0:0:0:d03d:d3e7:cc5e:7cd7]:19788                                 |
+ * |                      | dst: [ff02:0:0:0:0:0:0:1]:19788                                             |
+ * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+
+ * |                      | HopOpts          |    44 | 0x0000 | yes | norm |  -20 | RX | 0x4800 |  15.4 |
+ * |         00:00:09.263 | src: [fdde:ad00:beef:0:0:ff:fe00:4800]:0                                    |
+ * |                      | dst: [ff03:0:0:0:0:0:0:2]:0                                                 |
+ * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+
+ * |                      | UDP              |    12 | 0x3f7d | yes |  net |  -20 | RX | 0x4800 |  15.4 |
+ * |         00:00:09.302 | src: [fdde:ad00:beef:0:0:ff:fe00:4800]:61631                                |
+ * |                      | dst: [fdde:ad00:beef:0:0:ff:fe00:4801]:61631                                |
+ * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+
+ * |                      | ICMP6(EchoReqst) |    16 | 0x942c | yes | norm |  -20 | RX | 0x4800 |  15.4 |
+ * |         00:00:09.304 | src: [fdde:ad00:beef:0:ac09:a16b:3204:dc09]:0                               |
+ * |                      | dst: [fdde:ad00:beef:0:dc0e:d6b3:f180:b75b]:0                               |
+ * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+
+ * |                      | HopOpts          |    44 | 0x0000 | yes | norm |  -20 | RX | 0x4800 |  15.4 |
+ * |         00:00:09.304 | src: [fdde:ad00:beef:0:0:ff:fe00:4800]:0                                    |
+ * |                      | dst: [ff03:0:0:0:0:0:0:2]:0                                                 |
+ * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+
+ * |                      | UDP              |    50 | 0x2e37 |  no |  net |  -20 | RX | 0x4800 |  15.4 |
+ * |         00:00:21.622 | src: [fe80:0:0:0:d03d:d3e7:cc5e:7cd7]:19788                                 |
+ * |                      | dst: [ff02:0:0:0:0:0:0:1]:19788                                             |
+ * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+
+ * |                      | UDP              |    50 | 0xe177 |  no |  net |  -20 | RX | 0x4800 |  15.4 |
+ * |         00:00:26.640 | src: [fe80:0:0:0:d03d:d3e7:cc5e:7cd7]:19788                                 |
+ * |                      | dst: [ff02:0:0:0:0:0:0:1]:19788                                             |
+ * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+
+ * |                      | UDP              |   165 | 0x82ee | yes |  net |  -20 | RX | 0x4800 |  15.4 |
+ * |         00:00:30.000 | src: [fe80:0:0:0:d03d:d3e7:cc5e:7cd7]:19788                                 |
+ * |                      | dst: [fe80:0:0:0:a4a5:bbac:a8e:bd07]:19788                                  |
+ * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+
+ * |                      | UDP              |    93 | 0x52df |  no |  net |  -20 | RX | unknwn |  15.4 |
+ * |         00:00:30.480 | src: [fe80:0:0:0:d03d:d3e7:cc5e:7cd7]:19788                                 |
+ * |                      | dst: [fe80:0:0:0:a4a5:bbac:a8e:bd07]:19788                                  |
+ * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+
+ * |                      | UDP              |    50 | 0x5ccf |  no |  net |  -20 | RX | unknwn |  15.4 |
+ * |         00:00:30.772 | src: [fe80:0:0:0:d03d:d3e7:cc5e:7cd7]:19788                                 |
+ * |                      | dst: [ff02:0:0:0:0:0:0:1]:19788                                             |
+ * Done
+ * @endcode
+ * @code
+ * history rx list 4
+ * 00:00:13.368
+       type:UDP len:50 checksum:0xbd26 sec:no prio:net rss:-20 from:0x4800 radio:15.4
+       src:[fe80:0:0:0:d03d:d3e7:cc5e:7cd7]:19788
+       dst:[ff02:0:0:0:0:0:0:1]:19788
+ * 00:00:14.991
+       type:HopOpts len:44 checksum:0x0000 sec:yes prio:norm rss:-20 from:0x4800 radio:15.4
+       src:[fdde:ad00:beef:0:0:ff:fe00:4800]:0
+       dst:[ff03:0:0:0:0:0:0:2]:0
+ * 00:00:15.030
+       type:UDP len:12 checksum:0x3f7d sec:yes prio:net rss:-20 from:0x4800 radio:15.4
+       src:[fdde:ad00:beef:0:0:ff:fe00:4800]:61631
+       dst:[fdde:ad00:beef:0:0:ff:fe00:4801]:61631
+ * 00:00:15.032
+       type:ICMP6(EchoReqst) len:16 checksum:0x942c sec:yes prio:norm rss:-20 from:0x4800 radio:15.4
+       src:[fdde:ad00:beef:0:ac09:a16b:3204:dc09]:0
+       dst:[fdde:ad00:beef:0:dc0e:d6b3:f180:b75b]:0
+ * Done
+ * @endcode
+ * @cparam history rx [@ca{list}] [@ca{num-entries}]
+ * * Use the `list` option to display the output in list format. Otherwise,
+ *   the output is shown in table format.
+ * * Use the `num-entries` option to limit the output to the number of
+ *   most-recent entries specified. If this option is not used, all stored
+ *   entries are shown in the output.
+ * @par
+ * Displays the IPv6 message RX history in table or list format.
+ * @par
+ * Each table or list entry provides:
+ * * Age: Time elapsed since the command was issued, and given in the format:
+ *        `hours`:`minutes`:`seconds`:`milliseconds`
+ * * Type:
+ *     * IPv6 message type, such as `UDP`, `TCP`, `HopOpts`, and `ICMP6` (and its subtype).
+ *     * `src`: Source IPv6 address and port number.
+ *     * `dst`: Destination IPv6 address and port number (port number is valid
+         for UDP/TCP, otherwise it is 0).
+ * * Len: IPv6 payload length (excluding the IPv6 header).
+ * * Chksum: Message checksum (valid for UDP, TCP, or ICMP6 messages).
+ * * Sec: Indicates if link-layer security was used.
+ * * Prio: Message priority. Possible values are `low`, `norm`, `high`, or
+ *         `net` (for Thread control messages).
+ * * RSS: Received Signal Strength (in dBm), averaged over all received fragment
+ *        frames that formed the message. For TX history, `NA` (not applicable)
+          is displayed.
+ * * Dir: Shows whether the message was sent (`TX`) or received (`RX`). A failed
+ *        transmission is indicated with `TX-F` in table format or
+ *        `tx-success:no` in list format. Examples of a failed transmission
+ *        include a `tx`getting aborted and no `ack` getting sent from the peer for
+ *        any of the message fragments.
+ * * Neighb: Short address (RLOC16) of the neighbor with whom the message was
+ *           sent/received. If the frame was broadcast, it is shown as
+ *           `bcast` in table format or `0xffff` in list format. If the short
+ *           address of the neighbor is not available, it is shown as `unknwn` in
+ *           table format or `0xfffe` in list format.
+ * * Radio: Radio link on which the message was sent/received (useful when
+            `OPENTHREAD_CONFIG_MULTI_RADIO` is enabled). Can be `15.4`, `trel`,
+            or `all` (if sent on all radio links).
+ * @sa otHistoryTrackerIterateRxHistory
+ */
 template <> otError History::Process<Cmd("rx")>(Arg aArgs[]) { return ProcessRxTxHistory(kRx, aArgs); }
 
+/**
+ * @cli history rxtx
+ * @code
+ * history rxtx
+ * | Age                  | Type             | Len   | Chksum | Sec | Prio | RSS  |Dir | Neighb | Radio |
+ * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+
+ * |                      | HopOpts          |    44 | 0x0000 | yes | norm |  -20 | RX | 0x0800 |  15.4 |
+ * |         00:00:09.267 | src: [fdde:ad00:beef:0:0:ff:fe00:800]:0                                     |
+ * |                      | dst: [ff03:0:0:0:0:0:0:2]:0                                                 |
+ * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+
+ * |                      | UDP              |    12 | 0x6c6b | yes |  net |  -20 | RX | 0x0800 |  15.4 |
+ * |         00:00:09.290 | src: [fdde:ad00:beef:0:0:ff:fe00:800]:61631                                 |
+ * |                      | dst: [fdde:ad00:beef:0:0:ff:fe00:801]:61631                                 |
+ * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+
+ * |                      | ICMP6(EchoReqst) |    16 | 0xc6a2 | yes | norm |  -20 | RX | 0x0800 |  15.4 |
+ * |         00:00:09.292 | src: [fdde:ad00:beef:0:efe8:4910:cf95:dee9]:0                               |
+ * |                      | dst: [fdde:ad00:beef:0:af4c:3644:882a:3698]:0                               |
+ * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+
+ * |                      | ICMP6(EchoReply) |    16 | 0xc5a2 | yes | norm |  NA  | TX | 0x0800 |  15.4 |
+ * |         00:00:09.292 | src: [fdde:ad00:beef:0:af4c:3644:882a:3698]:0                               |
+ * |                      | dst: [fdde:ad00:beef:0:efe8:4910:cf95:dee9]:0                               |
+ * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+
+ * |                      | UDP              |    50 | 0xaa0d | yes |  net |  NA  | TX | 0x0800 |  15.4 |
+ * |         00:00:09.294 | src: [fdde:ad00:beef:0:0:ff:fe00:801]:61631                                 |
+ * |                      | dst: [fdde:ad00:beef:0:0:ff:fe00:800]:61631                                 |
+ * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+
+ * |                      | HopOpts          |    44 | 0x0000 | yes | norm |  -20 | RX | 0x0800 |  15.4 |
+ * |         00:00:09.296 | src: [fdde:ad00:beef:0:0:ff:fe00:800]:0                                     |
+ * |                      | dst: [ff03:0:0:0:0:0:0:2]:0                                                 |
+ * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+
+ * |                      | UDP              |    50 | 0xc1d8 |  no |  net |  -20 | RX | 0x0800 |  15.4 |
+ * |         00:00:09.569 | src: [fe80:0:0:0:54d9:5153:ffc6:df26]:19788                                 |
+ * |                      | dst: [ff02:0:0:0:0:0:0:1]:19788                                             |
+ * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+
+ * |                      | UDP              |    50 | 0x3cb1 |  no |  net |  -20 | RX | 0x0800 |  15.4 |
+ * |         00:00:16.519 | src: [fe80:0:0:0:54d9:5153:ffc6:df26]:19788                                 |
+ * |                      | dst: [ff02:0:0:0:0:0:0:1]:19788                                             |
+ * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+
+ * |                      | UDP              |    50 | 0xeda0 |  no |  net |  -20 | RX | 0x0800 |  15.4 |
+ * |         00:00:20.599 | src: [fe80:0:0:0:54d9:5153:ffc6:df26]:19788                                 |
+ * |                      | dst: [ff02:0:0:0:0:0:0:1]:19788                                             |
+ * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+
+ * |                      | UDP              |   165 | 0xbdfa | yes |  net |  -20 | RX | 0x0800 |  15.4 |
+ * |         00:00:21.059 | src: [fe80:0:0:0:54d9:5153:ffc6:df26]:19788                                 |
+ * |                      | dst: [fe80:0:0:0:8893:c2cc:d983:1e1c]:19788                                 |
+ * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+
+ * |                      | UDP              |    64 | 0x1c11 |  no |  net |  NA  | TX | 0x0800 |  15.4 |
+ * |         00:00:21.062 | src: [fe80:0:0:0:8893:c2cc:d983:1e1c]:19788                                 |
+ * |                      | dst: [fe80:0:0:0:54d9:5153:ffc6:df26]:19788                                 |
+ * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+
+ * |                      | UDP              |    93 | 0xedff |  no |  net |  -20 | RX | unknwn |  15.4 |
+ * |         00:00:21.474 | src: [fe80:0:0:0:54d9:5153:ffc6:df26]:19788                                 |
+ * |                      | dst: [fe80:0:0:0:8893:c2cc:d983:1e1c]:19788                                 |
+ * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+
+ * |                      | UDP              |    44 | 0xd383 |  no |  net |  NA  | TX | bcast  |  15.4 |
+ * |         00:00:21.811 | src: [fe80:0:0:0:8893:c2cc:d983:1e1c]:19788                                 |
+ * |                      | dst: [ff02:0:0:0:0:0:0:2]:19788                                             |
+ * Done
+ * @endcode
+ * @code
+ * history rxtx list 5
+ * 00:00:02.100
+       type:UDP len:50 checksum:0xd843 sec:no prio:net rss:-20 from:0x0800 radio:15.4
+       src:[fe80:0:0:0:54d9:5153:ffc6:df26]:19788
+       dst:[ff02:0:0:0:0:0:0:1]:19788
+ * 00:00:15.331
+       type:HopOpts len:44 checksum:0x0000 sec:yes prio:norm rss:-20 from:0x0800 radio:15.4
+       src:[fdde:ad00:beef:0:0:ff:fe00:800]:0
+       dst:[ff03:0:0:0:0:0:0:2]:0
+ * 00:00:15.354
+       type:UDP len:12 checksum:0x6c6b sec:yes prio:net rss:-20 from:0x0800 radio:15.4
+       src:[fdde:ad00:beef:0:0:ff:fe00:800]:61631
+       dst:[fdde:ad00:beef:0:0:ff:fe00:801]:61631
+ * 00:00:15.356
+       type:ICMP6(EchoReqst) len:16 checksum:0xc6a2 sec:yes prio:norm rss:-20 from:0x0800 radio:15.4
+       src:[fdde:ad00:beef:0:efe8:4910:cf95:dee9]:0
+       dst:[fdde:ad00:beef:0:af4c:3644:882a:3698]:0
+ * 00:00:15.356
+       type:ICMP6(EchoReply) len:16 checksum:0xc5a2 sec:yes prio:norm tx-success:yes to:0x0800 radio:15.4
+       src:[fdde:ad00:beef:0:af4c:3644:882a:3698]:0
+       dst:[fdde:ad00:beef:0:efe8:4910:cf95:dee9]:0
+ * Done
+ * @endcode
+ * @cparam history rxtx [@ca{list}] [@ca{num-entries}]
+ * * Use the `list` option to display the output in list format. Otherwise,
+ *   the output is shown in table format.
+ * * Use the `num-entries` option to limit the output to the number of
+ *   most-recent entries specified. If this option is not used, all stored
+ *   entries are shown in the output.
+ * @par
+ * Displays the combined IPv6 message RX and TX history in table or list format.
+ * @par
+ * Each table or list entry provides:
+ * * Age: Time elapsed since the command was issued, and given in the format:
+ *        `hours`:`minutes`:`seconds`:`milliseconds`
+ * * Type:
+ *     * IPv6 message type, such as `UDP`, `TCP`, `HopOpts`, and `ICMP6` (and its subtype).
+ *     * `src`: Source IPv6 address and port number.
+ *     * `dst`: Destination IPv6 address and port number (port number is valid
+         for UDP/TCP, otherwise it is 0).
+ * * Len: IPv6 payload length (excluding the IPv6 header).
+ * * Chksum: Message checksum (valid for UDP, TCP, or ICMP6 messages).
+ * * Sec: Indicates if link-layer security was used.
+ * * Prio: Message priority. Possible values are `low`, `norm`, `high`, or
+ *         `net` (for Thread control messages).
+ * * RSS: Received Signal Strength (in dBm), averaged over all received fragment
+ *        frames that formed the message. For TX history, `NA` (not applicable)
+          is displayed.
+ * * Dir: Shows whether the message was sent (`TX`) or received (`RX`). A failed
+ *        transmission is indicated with `TX-F` in table format or
+ *        `tx-success:no` in list format. Examples of a failed transmission
+ *        include a `tx`getting aborted and no `ack` getting sent from the peer for
+ *        any of the message fragments.
+ * * Neighb: Short address (RLOC16) of the neighbor with whom the message was
+ *           sent/received. If the frame was broadcast, it is shown as
+ *           `bcast` in table format or `0xffff` in list format. If the short
+ *           address of the neighbor is not available, it is shown as `unknwn` in
+ *           table format or `0xfffe` in list format.
+ * * Radio: Radio link on which the message was sent/received (useful when
+            `OPENTHREAD_CONFIG_MULTI_RADIO` is enabled). Can be `15.4`, `trel`,
+            or `all` (if sent on all radio links).
+ * @sa otHistoryTrackerIterateRxHistory
+ * @sa otHistoryTrackerIterateTxHistory
+ */
 template <> otError History::Process<Cmd("rxtx")>(Arg aArgs[]) { return ProcessRxTxHistory(kRxTx, aArgs); }
 
+/**
+ * @cli history tx
+ * @code
+ * history tx
+ * | Age                  | Type             | Len   | Chksum | Sec | Prio | RSS  |Dir | Neighb | Radio |
+ * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+
+ * |                      | ICMP6(EchoReply) |    16 | 0x932c | yes | norm |  NA  | TX | 0x4800 |  15.4 |
+ * |         00:00:18.798 | src: [fdde:ad00:beef:0:dc0e:d6b3:f180:b75b]:0                               |
+ * |                      | dst: [fdde:ad00:beef:0:ac09:a16b:3204:dc09]:0                               |
+ * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+
+ * |                      | UDP              |    50 | 0xce87 | yes |  net |  NA  | TX | 0x4800 |  15.4 |
+ * |         00:00:18.800 | src: [fdde:ad00:beef:0:0:ff:fe00:4801]:61631                                |
+ * |                      | dst: [fdde:ad00:beef:0:0:ff:fe00:4800]:61631                                |
+ * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+
+ * |                      | UDP              |    64 | 0xf7ba |  no |  net |  NA  | TX | 0x4800 |  15.4 |
+ * |         00:00:39.499 | src: [fe80:0:0:0:a4a5:bbac:a8e:bd07]:19788                                  |
+ * |                      | dst: [fe80:0:0:0:d03d:d3e7:cc5e:7cd7]:19788                                 |
+ * +----------------------+------------------+-------+--------+-----+------+------+----+--------+-------+
+ * |                      | UDP              |    44 | 0x26d4 |  no |  net |  NA  | TX | bcast  |  15.4 |
+ * |         00:00:40.256 | src: [fe80:0:0:0:a4a5:bbac:a8e:bd07]:19788                                  |
+ * |                      | dst: [ff02:0:0:0:0:0:0:2]:19788                                             |
+ * Done
+ * @endcode
+ * @code
+ * history tx list
+ * 00:00:23.957
+       type:ICMP6(EchoReply) len:16 checksum:0x932c sec:yes prio:norm tx-success:yes to:0x4800 radio:15.4
+       src:[fdde:ad00:beef:0:dc0e:d6b3:f180:b75b]:0
+       dst:[fdde:ad00:beef:0:ac09:a16b:3204:dc09]:0
+ * 00:00:23.959
+       type:UDP len:50 checksum:0xce87 sec:yes prio:net tx-success:yes to:0x4800 radio:15.4
+       src:[fdde:ad00:beef:0:0:ff:fe00:4801]:61631
+       dst:[fdde:ad00:beef:0:0:ff:fe00:4800]:61631
+ * 00:00:44.658
+       type:UDP len:64 checksum:0xf7ba sec:no prio:net tx-success:yes to:0x4800 radio:15.4
+       src:[fe80:0:0:0:a4a5:bbac:a8e:bd07]:19788
+       dst:[fe80:0:0:0:d03d:d3e7:cc5e:7cd7]:19788
+ * 00:00:45.415
+       type:UDP len:44 checksum:0x26d4 sec:no prio:net tx-success:yes to:0xffff radio:15.4
+       src:[fe80:0:0:0:a4a5:bbac:a8e:bd07]:19788
+       dst:[ff02:0:0:0:0:0:0:2]:19788
+ * Done
+ * @endcode
+ * @cparam history tx [@ca{list}] [@ca{num-entries}]
+ * * Use the `list` option to display the output in list format. Otherwise,
+ *   the output is shown in table format.
+ * * Use the `num-entries` option to limit the output to the number of
+ *   most-recent entries specified. If this option is not used, all stored
+ *   entries are shown in the output.
+ * @par
+ * Displays the IPv6 message TX history in table or list format.
+ * @par
+ * Each table or list entry provides:
+ * * Age: Time elapsed since the command was issued, and given in the format:
+ *        `hours`:`minutes`:`seconds`:`milliseconds`
+ * * Type:
+ *     * IPv6 message type, such as `UDP`, `TCP`, `HopOpts`, and `ICMP6` (and its subtype).
+ *     * `src`: Source IPv6 address and port number.
+ *     * `dst`: Destination IPv6 address and port number (port number is valid
+         for UDP/TCP, otherwise it is 0).
+ * * Len: IPv6 payload length (excluding the IPv6 header).
+ * * Chksum: Message checksum (valid for UDP, TCP, or ICMP6 messages).
+ * * Sec: Indicates if link-layer security was used.
+ * * Prio: Message priority. Possible values are `low`, `norm`, `high`, or
+ *         `net` (for Thread control messages).
+ * * RSS: Received Signal Strength (in dBm), averaged over all received fragment
+ *        frames that formed the message. For TX history, `NA` (not applicable)
+          is displayed.
+ * * Dir: Shows whether the message was sent (`TX`) or received (`RX`). A failed
+ *        transmission is indicated with `TX-F` in table format or
+ *        `tx-success:no` in list format. Examples of a failed transmission
+ *        include a `tx`getting aborted and no `ack` getting sent from the peer for
+ *        any of the message fragments.
+ * * Neighb: Short address (RLOC16) of the neighbor with whom the message was
+ *           sent/received. If the frame was broadcast, it is shown as
+ *           `bcast` in table format or `0xffff` in list format. If the short
+ *           address of the neighbor is not available, it is shown as `unknwn` in
+ *           table format or `0xfffe` in list format.
+ * * Radio: Radio link on which the message was sent/received (useful when
+            `OPENTHREAD_CONFIG_MULTI_RADIO` is enabled). Can be `15.4`, `trel`,
+            or `all` (if sent on all radio links).
+ * @sa otHistoryTrackerIterateTxHistory
+ */
 template <> otError History::Process<Cmd("tx")>(Arg aArgs[]) { return ProcessRxTxHistory(kTx, aArgs); }
 
 const char *History::MessagePriorityToString(uint8_t aPriority)
@@ -636,6 +1246,58 @@
     OutputLine("| %20s | dst: %-70s |", "", addrString);
 }
 
+/**
+ * @cli history prefix
+ * @code
+ * history prefix
+ * | Age                  | Event   | Prefix                                      | Flags     | Pref | RLOC16 |
+ * +----------------------+---------+---------------------------------------------+-----------+------+--------+
+ * |         00:00:10.663 | Added   | fd00:1111:2222:3333::/64                    | paro      | med  | 0x5400 |
+ * |         00:01:02.054 | Removed | fd00:dead:beef:1::/64                       | paros     | high | 0x5400 |
+ * |         00:01:21.136 | Added   | fd00:abba:cddd:0::/64                       | paos      | med  | 0x5400 |
+ * |         00:01:45.144 | Added   | fd00:dead:beef:1::/64                       | paros     | high | 0x3c00 |
+ * |         00:01:50.944 | Added   | fd00:dead:beef:1::/64                       | paros     | high | 0x5400 |
+ * |         00:01:59.887 | Added   | fd00:dead:beef:1::/64                       | paros     | med  | 0x8800 |
+ * Done
+ * @endcode
+ * @code
+ * history prefix list
+ * 00:04:12.487 -> event:Added prefix:fd00:1111:2222:3333::/64 flags:paro pref:med rloc16:0x5400
+ * 00:05:03.878 -> event:Removed prefix:fd00:dead:beef:1::/64 flags:paros pref:high rloc16:0x5400
+ * 00:05:22.960 -> event:Added prefix:fd00:abba:cddd:0::/64 flags:paos pref:med rloc16:0x5400
+ * 00:05:46.968 -> event:Added prefix:fd00:dead:beef:1::/64 flags:paros pref:high rloc16:0x3c00
+ * 00:05:52.768 -> event:Added prefix:fd00:dead:beef:1::/64 flags:paros pref:high rloc16:0x5400
+ * 00:06:01.711 -> event:Added prefix:fd00:dead:beef:1::/64 flags:paros pref:med rloc16:0x8800
+ * Done
+ * @endcode
+ * @cparam history prefix [@ca{list}] [@ca{num-entries}]
+ * * Use the `list` option to display the output in list format. Otherwise,
+ *   the output is shown in table format.
+ * * Use the `num-entries` option to limit the output to the number of
+ *   most-recent entries specified. If this option is not used, all stored
+ *   entries are shown in the output.
+ * @par
+ * Displays the network data for the mesh prefix history in table or list format.
+ * @par
+ * Each table or list entry provides:
+ * * Age: Time elapsed since the command was issued, and given in the format:
+ *        `hours`:`minutes`:`seconds`:`milliseconds`
+ * * Event: Possible values are `Added` or `Removed`.
+ * * Prefix
+ * * Flags/meaning:
+ *     * `p`: Preferred flag
+ *     * `a`: Stateless IPv6 address auto-configuration flag.
+ *     * `d`: DHCPv6 IPv6 address configuration flag.
+ *     * `c`: DHCPv6 other-configuration flag.
+ *     * `r`: Default route flag.
+ *     * `o`: On mesh flag.
+ *     * `s`: Stable flag.
+ *     * `n`: Nd Dns flag.
+ *     * `D`: Domain prefix flag.
+ * * Pref: Preference. Values can be either `high`, `med`, or `low`.
+ * * RLOC16
+ * @sa otHistoryTrackerIterateOnMeshPrefixHistory
+ */
 template <> otError History::Process<Cmd("prefix")>(Arg aArgs[])
 {
     otError                                 error;
@@ -686,6 +1348,45 @@
     return error;
 }
 
+/**
+ * @cli history route
+ * @code
+ * history route
+ * | Age                  | Event   | Route                                       | Flags     | Pref | RLOC16 |
+ * +----------------------+---------+---------------------------------------------+-----------+------+--------+
+ * |         00:00:05.456 | Removed | fd00:1111:0::/48                            | s         | med  | 0x3c00 |
+ * |         00:00:29.310 | Added   | fd00:1111:0::/48                            | s         | med  | 0x3c00 |
+ * |         00:00:42.822 | Added   | fd00:1111:0::/48                            | s         | med  | 0x5400 |
+ * |         00:01:27.688 | Added   | fd00:aaaa:bbbb:cccc::/64                    | s         | med  | 0x8800 |
+ * Done
+ * @endcode
+ * @code
+ * history route list 2
+ * 00:00:48.704 -> event:Removed route:fd00:1111:0::/48 flags:s pref:med rloc16:0x3c00
+ * 00:01:12.558 -> event:Added route:fd00:1111:0::/48 flags:s pref:med rloc16:0x3c00
+ * Done
+ * @endcode
+ * @cparam history route [@ca{list}] [@ca{num-entries}]
+ * * Use the `list` option to display the output in list format. Otherwise,
+ *   the output is shown in table format.
+ * * Use the `num-entries` option to limit the output to the number of
+ *   most-recent entries specified. If this option is not used, all stored
+ *   entries are shown in the output.
+ * @par
+ * Displays the network data external-route history in table or list format.
+ * @par
+ * Each table or list entry provides:
+ * * Age: Time elapsed since the command was issued, and given in the format:
+ *        `hours`:`minutes`:`seconds`:`milliseconds`
+ * * Event: Possible values are `Added` or `Removed`.
+ * * Route
+ * * Flags/meaning:
+ *     * `s`: Stable flag.
+ *     * `n`: NAT64 flag.
+ * * Pref: Preference. Values can be either `high`, `med`, or `low`.
+ * * RLOC16
+ * @sa otHistoryTrackerIterateExternalRouteHistory
+ */
 template <> otError History::Process<Cmd("route")>(Arg aArgs[])
 {
     otError                                  error;
diff --git a/src/cli/cli_history.hpp b/src/cli/cli_history.hpp
index 90f832f..6958b04 100644
--- a/src/cli/cli_history.hpp
+++ b/src/cli/cli_history.hpp
@@ -39,7 +39,7 @@
 #include <openthread/history_tracker.h>
 
 #include "cli/cli_config.h"
-#include "cli/cli_output.hpp"
+#include "cli/cli_utils.hpp"
 
 #if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
 
@@ -50,11 +50,9 @@
  * Implements the History Tracker CLI interpreter.
  *
  */
-class History : private Output
+class History : private Utils
 {
 public:
-    typedef Utils::CmdLineParser::Arg Arg;
-
     /**
      * Constructor
      *
@@ -63,7 +61,7 @@
      *
      */
     History(otInstance *aInstance, OutputImplementer &aOutputImplementer)
-        : Output(aInstance, aOutputImplementer)
+        : Utils(aInstance, aOutputImplementer)
     {
     }
 
diff --git a/src/cli/cli_joiner.cpp b/src/cli/cli_joiner.cpp
index cf84968..8050879 100644
--- a/src/cli/cli_joiner.cpp
+++ b/src/cli/cli_joiner.cpp
@@ -78,7 +78,7 @@
     {
         otJoinerDiscerner discerner;
 
-        memset(&discerner, 0, sizeof(discerner));
+        ClearAllBytes(discerner);
 
         /**
          * @cli joiner discerner clear
diff --git a/src/cli/cli_joiner.hpp b/src/cli/cli_joiner.hpp
index e45dbf8..297525c 100644
--- a/src/cli/cli_joiner.hpp
+++ b/src/cli/cli_joiner.hpp
@@ -38,7 +38,7 @@
 
 #include <openthread/joiner.h>
 
-#include "cli/cli_output.hpp"
+#include "cli/cli_utils.hpp"
 
 #if OPENTHREAD_CONFIG_JOINER_ENABLE
 
@@ -49,11 +49,9 @@
  * Implements the Joiner CLI interpreter.
  *
  */
-class Joiner : private Output
+class Joiner : private Utils
 {
 public:
-    typedef Utils::CmdLineParser::Arg Arg;
-
     /**
      * Constructor
      *
@@ -62,7 +60,7 @@
      *
      */
     Joiner(otInstance *aInstance, OutputImplementer &aOutputImplementer)
-        : Output(aInstance, aOutputImplementer)
+        : Utils(aInstance, aOutputImplementer)
     {
     }
 
diff --git a/src/cli/cli_link_metrics.cpp b/src/cli/cli_link_metrics.cpp
index b8c2846..a12515d 100644
--- a/src/cli/cli_link_metrics.cpp
+++ b/src/cli/cli_link_metrics.cpp
@@ -36,7 +36,7 @@
 #include <openthread/link_metrics.h>
 
 #include "cli/cli.hpp"
-#include "cli/cli_output.hpp"
+#include "cli/cli_utils.hpp"
 #include "common/code_utils.hpp"
 
 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
@@ -45,7 +45,7 @@
 namespace Cli {
 
 LinkMetrics::LinkMetrics(otInstance *aInstance, OutputImplementer &aOutputImplementer)
-    : Output(aInstance, aOutputImplementer)
+    : Utils(aInstance, aOutputImplementer)
     , mLinkMetricsQueryInProgress(false)
 {
 }
@@ -138,7 +138,7 @@
 
     SuccessOrExit(error = aArgs[0].ParseAsIp6Address(address));
 
-    memset(&seriesFlags, 0, sizeof(otLinkMetricsSeriesFlags));
+    ClearAllBytes(seriesFlags);
 
     /**
      * @cli linkmetrics mgmt forward
@@ -171,7 +171,7 @@
         uint8_t       seriesId;
         otLinkMetrics linkMetrics;
 
-        memset(&linkMetrics, 0, sizeof(otLinkMetrics));
+        ClearAllBytes(linkMetrics);
         SuccessOrExit(error = aArgs[2].ParseAsUint8(seriesId));
         VerifyOrExit(!aArgs[3].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
 
@@ -368,7 +368,7 @@
 
     VerifyOrExit(!aFlags.IsEmpty(), error = OT_ERROR_INVALID_ARGS);
 
-    memset(&aLinkMetrics, 0, sizeof(aLinkMetrics));
+    ClearAllBytes(aLinkMetrics);
 
     for (const char *arg = aFlags.GetCString(); *arg != '\0'; arg++)
     {
diff --git a/src/cli/cli_link_metrics.hpp b/src/cli/cli_link_metrics.hpp
index d62be78..40b67c6 100644
--- a/src/cli/cli_link_metrics.hpp
+++ b/src/cli/cli_link_metrics.hpp
@@ -38,7 +38,7 @@
 
 #include <openthread/link_metrics.h>
 
-#include "cli/cli_output.hpp"
+#include "cli/cli_utils.hpp"
 
 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
 
@@ -50,11 +50,9 @@
  *
  */
 
-class LinkMetrics : private Output
+class LinkMetrics : private Utils
 {
 public:
-    typedef Utils::CmdLineParser::Arg Arg;
-
     /**
      * Constructor
      *
diff --git a/src/cli/cli_mac_filter.hpp b/src/cli/cli_mac_filter.hpp
index 1394d4f..0f03b1b 100644
--- a/src/cli/cli_mac_filter.hpp
+++ b/src/cli/cli_mac_filter.hpp
@@ -41,7 +41,7 @@
 #include <openthread/link.h>
 
 #include "cli/cli_config.h"
-#include "cli/cli_output.hpp"
+#include "cli/cli_utils.hpp"
 
 namespace ot {
 namespace Cli {
@@ -50,11 +50,9 @@
  * Implements the MAC Filter CLI interpreter.
  *
  */
-class MacFilter : private Output
+class MacFilter : private Utils
 {
 public:
-    typedef Utils::CmdLineParser::Arg Arg;
-
     /**
      * Constructor.
      *
@@ -63,7 +61,7 @@
      *
      */
     MacFilter(otInstance *aInstance, OutputImplementer &aOutputImplementer)
-        : Output(aInstance, aOutputImplementer)
+        : Utils(aInstance, aOutputImplementer)
     {
     }
 
diff --git a/src/cli/cli_network_data.cpp b/src/cli/cli_network_data.cpp
index 85c3e26..7aa0dd5 100644
--- a/src/cli/cli_network_data.cpp
+++ b/src/cli/cli_network_data.cpp
@@ -44,7 +44,7 @@
 namespace Cli {
 
 NetworkData::NetworkData(otInstance *aInstance, OutputImplementer &aOutputImplementer)
-    : Output(aInstance, aOutputImplementer)
+    : Utils(aInstance, aOutputImplementer)
 {
 #if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
     mFullCallbackWasCalled = false;
@@ -564,17 +564,78 @@
     return error;
 }
 
-void NetworkData::OutputPrefixes(bool aLocal)
+void NetworkData::OutputNetworkData(bool aLocal, uint16_t aRloc16)
 {
-    otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
-    otBorderRouterConfig  config;
+    otNetworkDataIterator  iterator = OT_NETWORK_DATA_ITERATOR_INIT;
+    otBorderRouterConfig   prefix;
+    otExternalRouteConfig  route;
+    otServiceConfig        service;
+    otLowpanContextInfo    context;
+    otCommissioningDataset dataset;
 
     OutputLine("Prefixes:");
 
-    while (GetNextPrefix(&iterator, &config, aLocal) == OT_ERROR_NONE)
+    while (GetNextPrefix(&iterator, &prefix, aLocal) == OT_ERROR_NONE)
     {
-        OutputPrefix(config);
+        if ((aRloc16 == kAnyRloc16) || (aRloc16 == prefix.mRloc16))
+        {
+            OutputPrefix(prefix);
+        }
     }
+
+    OutputLine("Routes:");
+    iterator = OT_NETWORK_DATA_ITERATOR_INIT;
+
+    while (GetNextRoute(&iterator, &route, aLocal) == OT_ERROR_NONE)
+    {
+        if ((aRloc16 == kAnyRloc16) || (aRloc16 == route.mRloc16))
+        {
+            OutputRoute(route);
+        }
+    }
+
+    OutputLine("Services:");
+    iterator = OT_NETWORK_DATA_ITERATOR_INIT;
+
+    while (GetNextService(&iterator, &service, aLocal) == OT_ERROR_NONE)
+    {
+        if ((aRloc16 == kAnyRloc16) || (aRloc16 == service.mServerConfig.mRloc16))
+        {
+            OutputService(service);
+        }
+    }
+
+    VerifyOrExit(!aLocal);
+    VerifyOrExit(aRloc16 == kAnyRloc16);
+
+    OutputLine("Contexts:");
+    iterator = OT_NETWORK_DATA_ITERATOR_INIT;
+
+    while (otNetDataGetNextLowpanContextInfo(GetInstancePtr(), &iterator, &context) == OT_ERROR_NONE)
+    {
+        OutputIp6Prefix(context.mPrefix);
+        OutputLine(" %u %c", context.mContextId, context.mCompressFlag ? 'c' : '-');
+    }
+
+    otNetDataGetCommissioningDataset(GetInstancePtr(), &dataset);
+
+    OutputLine("Commissioning:");
+
+    dataset.mIsSessionIdSet ? OutputFormat("%u ", dataset.mSessionId) : OutputFormat("- ");
+    dataset.mIsLocatorSet ? OutputFormat("%04x ", dataset.mLocator) : OutputFormat("- ");
+    dataset.mIsJoinerUdpPortSet ? OutputFormat("%u ", dataset.mJoinerUdpPort) : OutputFormat("- ");
+    dataset.mIsSteeringDataSet ? OutputBytes(dataset.mSteeringData.m8, dataset.mSteeringData.mLength)
+                               : OutputFormat("-");
+
+    if (dataset.mHasExtraTlv)
+    {
+        OutputFormat(" e");
+    }
+
+    OutputNewLine();
+
+exit:
+    return;
 }
 
 otError NetworkData::GetNextRoute(otNetworkDataIterator *aIterator, otExternalRouteConfig *aConfig, bool aLocal)
@@ -597,19 +658,6 @@
     return error;
 }
 
-void NetworkData::OutputRoutes(bool aLocal)
-{
-    otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
-    otExternalRouteConfig config;
-
-    OutputLine("Routes:");
-
-    while (GetNextRoute(&iterator, &config, aLocal) == OT_ERROR_NONE)
-    {
-        OutputRoute(config);
-    }
-}
-
 otError NetworkData::GetNextService(otNetworkDataIterator *aIterator, otServiceConfig *aConfig, bool aLocal)
 {
     otError error;
@@ -630,65 +678,6 @@
     return error;
 }
 
-void NetworkData::OutputServices(bool aLocal)
-{
-    otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
-    otServiceConfig       config;
-
-    OutputLine("Services:");
-
-    while (GetNextService(&iterator, &config, aLocal) == OT_ERROR_NONE)
-    {
-        OutputService(config);
-    }
-}
-
-void NetworkData::OutputLowpanContexts(bool aLocal)
-{
-    otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
-    otLowpanContextInfo   info;
-
-    VerifyOrExit(!aLocal);
-
-    OutputLine("Contexts:");
-
-    while (otNetDataGetNextLowpanContextInfo(GetInstancePtr(), &iterator, &info) == OT_ERROR_NONE)
-    {
-        OutputIp6Prefix(info.mPrefix);
-        OutputLine(" %u %c", info.mContextId, info.mCompressFlag ? 'c' : '-');
-    }
-
-exit:
-    return;
-}
-
-void NetworkData::OutputCommissioningDataset(bool aLocal)
-{
-    otCommissioningDataset dataset;
-
-    VerifyOrExit(!aLocal);
-
-    otNetDataGetCommissioningDataset(GetInstancePtr(), &dataset);
-
-    OutputLine("Commissioning:");
-
-    dataset.mIsSessionIdSet ? OutputFormat("%u ", dataset.mSessionId) : OutputFormat("- ");
-    dataset.mIsLocatorSet ? OutputFormat("%04x ", dataset.mLocator) : OutputFormat("- ");
-    dataset.mIsJoinerUdpPortSet ? OutputFormat("%u ", dataset.mJoinerUdpPort) : OutputFormat("- ");
-    dataset.mIsSteeringDataSet ? OutputBytes(dataset.mSteeringData.m8, dataset.mSteeringData.mLength)
-                               : OutputFormat("-");
-
-    if (dataset.mHasExtraTlv)
-    {
-        OutputFormat(" e");
-    }
-
-    OutputNewLine();
-
-exit:
-    return;
-}
-
 otError NetworkData::OutputBinary(bool aLocal)
 {
     otError error;
@@ -737,8 +726,16 @@
  * 08040b02174703140040fd00deadbeefcafe0504dc00330007021140
  * Done
  * @endcode
- * @cparam netdata show [@ca{-x}]
+ * @code
+ * netdata show 0xdc00
+ * Prefixes:
+ * fd00:dead:beef:cafe::/64 paros med dc00
+ * Routes:
+ * Services:
+ * Done
+ * @cparam netdata show [@ca{-x}|@ca{rloc16}]
  * *   The optional `-x` argument gets Network Data as hex-encoded TLVs.
+ * *   The optional `rloc16` argument gets all prefix/route/service entries associated with a given RLOC16.
  * @par
  * `netdata show` from OT CLI gets full Network Data received from the Leader. This command uses several
  * API functions to combine prefixes, routes, and services, including #otNetDataGetNextOnMeshPrefix,
@@ -795,9 +792,10 @@
  */
 template <> otError NetworkData::Process<Cmd("show")>(Arg aArgs[])
 {
-    otError error  = OT_ERROR_INVALID_ARGS;
-    bool    local  = false;
-    bool    binary = false;
+    otError  error  = OT_ERROR_INVALID_ARGS;
+    uint16_t rloc16 = kAnyRloc16;
+    bool     local  = false;
+    bool     binary = false;
 
     for (uint8_t i = 0; !aArgs[i].IsEmpty(); i++)
     {
@@ -832,21 +830,22 @@
         }
         else
         {
-            ExitNow(error = OT_ERROR_INVALID_ARGS);
+            SuccessOrExit(error = aArgs[i].ParseAsUint16(rloc16));
         }
     }
 
+    if (local || binary)
+    {
+        VerifyOrExit(rloc16 == kAnyRloc16, error = OT_ERROR_INVALID_ARGS);
+    }
+
     if (binary)
     {
         error = OutputBinary(local);
     }
     else
     {
-        OutputPrefixes(local);
-        OutputRoutes(local);
-        OutputServices(local);
-        OutputLowpanContexts(local);
-        OutputCommissioningDataset(local);
+        OutputNetworkData(local, rloc16);
         error = OT_ERROR_NONE;
     }
 
diff --git a/src/cli/cli_network_data.hpp b/src/cli/cli_network_data.hpp
index 15af650..ba8f281 100644
--- a/src/cli/cli_network_data.hpp
+++ b/src/cli/cli_network_data.hpp
@@ -38,7 +38,7 @@
 
 #include <openthread/netdata.h>
 
-#include "cli/cli_output.hpp"
+#include "cli/cli_utils.hpp"
 
 namespace ot {
 namespace Cli {
@@ -47,11 +47,9 @@
  * Implements the Network Data CLI.
  *
  */
-class NetworkData : private Output
+class NetworkData : private Utils
 {
 public:
-    typedef Utils::CmdLineParser::Arg Arg;
-
     /**
      * This constant specifies the string size for representing Network Data prefix/route entry flags.
      *
@@ -132,6 +130,8 @@
 private:
     using Command = CommandEntry<NetworkData>;
 
+    static constexpr uint16_t kAnyRloc16 = 0xffff;
+
     template <CommandId kCommandId> otError Process(Arg aArgs[]);
 
     otError GetNextPrefix(otNetworkDataIterator *aIterator, otBorderRouterConfig *aConfig, bool aLocal);
@@ -139,11 +139,7 @@
     otError GetNextService(otNetworkDataIterator *aIterator, otServiceConfig *aConfig, bool aLocal);
 
     otError OutputBinary(bool aLocal);
-    void    OutputPrefixes(bool aLocal);
-    void    OutputRoutes(bool aLocal);
-    void    OutputServices(bool aLocal);
-    void    OutputLowpanContexts(bool aLocal);
-    void    OutputCommissioningDataset(bool aLocal);
+    void    OutputNetworkData(bool aLocal, uint16_t aRloc16);
 
 #if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
     static void HandleNetdataFull(void *aContext) { static_cast<NetworkData *>(aContext)->HandleNetdataFull(); }
diff --git a/src/cli/cli_ping.cpp b/src/cli/cli_ping.cpp
new file mode 100644
index 0000000..1111e3d
--- /dev/null
+++ b/src/cli/cli_ping.cpp
@@ -0,0 +1,256 @@
+/*
+ *  Copyright (c) 2024, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ *   This file implements the CLI interpreter for Ping Sender function.
+ */
+
+#include "cli_ping.hpp"
+
+#include <openthread/ping_sender.h>
+
+#include "cli/cli.hpp"
+#include "cli/cli_utils.hpp"
+#include "common/code_utils.hpp"
+
+#if OPENTHREAD_CONFIG_PING_SENDER_ENABLE
+
+namespace ot {
+namespace Cli {
+
+PingSender::PingSender(otInstance *aInstance, OutputImplementer &aOutputImplementer)
+    : Utils(aInstance, aOutputImplementer)
+    , mPingIsAsync(false)
+{
+}
+
+otError PingSender::Process(Arg aArgs[])
+{
+    otError            error = OT_ERROR_NONE;
+    otPingSenderConfig config;
+    bool               async = false;
+    bool               nat64Synth;
+
+    /**
+     * @cli ping stop
+     * @code
+     * ping stop
+     * Done
+     * @endcode
+     * @par
+     * Stop sending ICMPv6 Echo Requests.
+     * @sa otPingSenderStop
+     */
+    if (aArgs[0] == "stop")
+    {
+        otPingSenderStop(GetInstancePtr());
+        ExitNow();
+    }
+    else if (aArgs[0] == "async")
+    {
+        async = true;
+        aArgs++;
+    }
+
+    ClearAllBytes(config);
+
+    if (aArgs[0] == "-I")
+    {
+        SuccessOrExit(error = aArgs[1].ParseAsIp6Address(config.mSource));
+
+#if !OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
+        VerifyOrExit(otIp6HasUnicastAddress(GetInstancePtr(), &config.mSource), error = OT_ERROR_INVALID_ARGS);
+#endif
+        aArgs += 2;
+    }
+
+    if (aArgs[0] == "-m")
+    {
+        config.mMulticastLoop = true;
+        aArgs++;
+    }
+
+    SuccessOrExit(error = Interpreter::ParseToIp6Address(GetInstancePtr(), aArgs[0], config.mDestination, nat64Synth));
+
+    if (nat64Synth)
+    {
+        OutputFormat("Pinging synthesized IPv6 address: ");
+        OutputIp6AddressLine(config.mDestination);
+    }
+
+    if (!aArgs[1].IsEmpty())
+    {
+        SuccessOrExit(error = aArgs[1].ParseAsUint16(config.mSize));
+    }
+
+    if (!aArgs[2].IsEmpty())
+    {
+        SuccessOrExit(error = aArgs[2].ParseAsUint16(config.mCount));
+    }
+
+    if (!aArgs[3].IsEmpty())
+    {
+        SuccessOrExit(error = ParsePingInterval(aArgs[3], config.mInterval));
+    }
+
+    if (!aArgs[4].IsEmpty())
+    {
+        SuccessOrExit(error = aArgs[4].ParseAsUint8(config.mHopLimit));
+        config.mAllowZeroHopLimit = (config.mHopLimit == 0);
+    }
+
+    if (!aArgs[5].IsEmpty())
+    {
+        uint32_t timeout;
+
+        SuccessOrExit(error = ParsePingInterval(aArgs[5], timeout));
+        VerifyOrExit(timeout <= NumericLimits<uint16_t>::kMax, error = OT_ERROR_INVALID_ARGS);
+        config.mTimeout = static_cast<uint16_t>(timeout);
+    }
+
+    VerifyOrExit(aArgs[6].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
+
+    config.mReplyCallback      = PingSender::HandlePingReply;
+    config.mStatisticsCallback = PingSender::HandlePingStatistics;
+    config.mCallbackContext    = this;
+
+    SuccessOrExit(error = otPingSenderPing(GetInstancePtr(), &config));
+
+    mPingIsAsync = async;
+
+    if (!async)
+    {
+        error = OT_ERROR_PENDING;
+    }
+
+exit:
+    return error;
+}
+
+otError PingSender::ParsePingInterval(const Arg &aArg, uint32_t &aInterval)
+{
+    otError        error    = OT_ERROR_NONE;
+    const char    *string   = aArg.GetCString();
+    const uint32_t msFactor = 1000;
+    uint32_t       factor   = msFactor;
+
+    aInterval = 0;
+
+    while (*string)
+    {
+        if ('0' <= *string && *string <= '9')
+        {
+            // In the case of seconds, change the base of already calculated value.
+            if (factor == msFactor)
+            {
+                aInterval *= 10;
+            }
+
+            aInterval += static_cast<uint32_t>(*string - '0') * factor;
+
+            // In the case of milliseconds, change the multiplier factor.
+            if (factor != msFactor)
+            {
+                factor /= 10;
+            }
+        }
+        else if (*string == '.')
+        {
+            // Accept only one dot character.
+            VerifyOrExit(factor == msFactor, error = OT_ERROR_INVALID_ARGS);
+
+            // Start analyzing hundreds of milliseconds.
+            factor /= 10;
+        }
+        else
+        {
+            ExitNow(error = OT_ERROR_INVALID_ARGS);
+        }
+
+        string++;
+    }
+
+exit:
+    return error;
+}
+
+void PingSender::HandlePingReply(const otPingSenderReply *aReply, void *aContext)
+{
+    static_cast<PingSender *>(aContext)->HandlePingReply(aReply);
+}
+
+void PingSender::HandlePingReply(const otPingSenderReply *aReply)
+{
+    OutputFormat("%u bytes from ", static_cast<uint16_t>(aReply->mSize + sizeof(otIcmp6Header)));
+    OutputIp6Address(aReply->mSenderAddress);
+    OutputLine(": icmp_seq=%u hlim=%u time=%ums", aReply->mSequenceNumber, aReply->mHopLimit, aReply->mRoundTripTime);
+}
+
+void PingSender::HandlePingStatistics(const otPingSenderStatistics *aStatistics, void *aContext)
+{
+    static_cast<PingSender *>(aContext)->HandlePingStatistics(aStatistics);
+}
+
+void PingSender::HandlePingStatistics(const otPingSenderStatistics *aStatistics)
+{
+    OutputFormat("%u packets transmitted, %u packets received.", aStatistics->mSentCount, aStatistics->mReceivedCount);
+
+    if ((aStatistics->mSentCount != 0) && !aStatistics->mIsMulticast &&
+        aStatistics->mReceivedCount <= aStatistics->mSentCount)
+    {
+        uint32_t packetLossRate =
+            1000 * (aStatistics->mSentCount - aStatistics->mReceivedCount) / aStatistics->mSentCount;
+
+        OutputFormat(" Packet loss = %lu.%u%%.", ToUlong(packetLossRate / 10),
+                     static_cast<uint16_t>(packetLossRate % 10));
+    }
+
+    if (aStatistics->mReceivedCount != 0)
+    {
+        uint32_t avgRoundTripTime = 1000 * aStatistics->mTotalRoundTripTime / aStatistics->mReceivedCount;
+
+        OutputFormat(" Round-trip min/avg/max = %u/%u.%u/%u ms.", aStatistics->mMinRoundTripTime,
+                     static_cast<uint16_t>(avgRoundTripTime / 1000), static_cast<uint16_t>(avgRoundTripTime % 1000),
+                     aStatistics->mMaxRoundTripTime);
+    }
+
+    OutputNewLine();
+
+    if (!mPingIsAsync)
+    {
+        OutputResult(OT_ERROR_NONE);
+    }
+}
+
+void PingSender::OutputResult(otError aError) { Interpreter::GetInterpreter().OutputResult(aError); }
+
+} // namespace Cli
+} // namespace ot
+
+#endif // OPENTHREAD_CONFIG_PING_SENDER_ENABLE
diff --git a/src/cli/cli_ping.hpp b/src/cli/cli_ping.hpp
new file mode 100644
index 0000000..6504412
--- /dev/null
+++ b/src/cli/cli_ping.hpp
@@ -0,0 +1,97 @@
+/*
+ *  Copyright (c) 2024, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ *   This file contains definitions for the CLI interpreter for Ping Sneder function.
+ */
+
+#ifndef CLI_PING_SENDER_HPP_
+#define CLI_PING_SENDER_HPP_
+
+#include "openthread-core-config.h"
+
+#include <openthread/ping_sender.h>
+
+#include "cli/cli_utils.hpp"
+
+#if OPENTHREAD_CONFIG_PING_SENDER_ENABLE
+
+namespace ot {
+namespace Cli {
+
+/**
+ * Implements the Ping Sender CLI interpreter.
+ *
+ */
+
+class PingSender : private Utils
+{
+public:
+    /**
+     * Constructor
+     *
+     * @param[in]  aInstance            The OpenThread Instance.
+     * @param[in]  aOutputImplementer   An `OutputImplementer`.
+     *
+     */
+    PingSender(otInstance *aInstance, OutputImplementer &aOutputImplementer);
+
+    /**
+     * Processes a CLI sub-command.
+     *
+     * @param[in]  aArgs     An array of command line arguments.
+     *
+     * @retval OT_ERROR_NONE              Successfully executed the CLI command.
+     * @retval OT_ERROR_PENDING           The CLI command was successfully started but final result is pending.
+     * @retval OT_ERROR_INVALID_COMMAND   Invalid or unknown CLI command.
+     * @retval OT_ERROR_INVALID_ARGS      Invalid arguments.
+     * @retval ...                        Error during execution of the CLI command.
+     *
+     */
+    otError Process(Arg aArgs[]);
+
+private:
+    otError ParsePingInterval(const Arg &aArg, uint32_t &aInterval);
+
+    static void HandlePingReply(const otPingSenderReply *aReply, void *aContext);
+    void        HandlePingReply(const otPingSenderReply *aReply);
+    static void HandlePingStatistics(const otPingSenderStatistics *aStatistics, void *aContext);
+    void        HandlePingStatistics(const otPingSenderStatistics *aStatistics);
+
+    void OutputResult(otError aError);
+
+    bool mPingIsAsync : 1;
+};
+
+} // namespace Cli
+} // namespace ot
+
+#endif // OPENTHREAD_CONFIG_PING_SENDER_ENABLE
+
+#endif // CLI_PING_SENDER_HPP_
diff --git a/src/cli/cli_srp_client.cpp b/src/cli/cli_srp_client.cpp
index 58c9ef4..a5301f3 100644
--- a/src/cli/cli_srp_client.cpp
+++ b/src/cli/cli_srp_client.cpp
@@ -58,7 +58,7 @@
 }
 
 SrpClient::SrpClient(otInstance *aInstance, OutputImplementer &aOutputImplementer)
-    : Output(aInstance, aOutputImplementer)
+    : Utils(aInstance, aOutputImplementer)
     , mCallbackEnabled(false)
 {
     otSrpClientSetCallback(GetInstancePtr(), SrpClient::HandleCallback, this);
@@ -80,6 +80,7 @@
      * @endcode
      * @par
      * Indicates the current state of auto-start mode (enabled or disabled).
+     * @moreinfo{@srp}.
      * @sa otSrpClientIsAutoStartModeEnabled
      */
     if (aArgs[0].IsEmpty())
@@ -159,6 +160,7 @@
  * @cparam srp client callback [@ca{enable}|@ca{disable}]
  * @par
  * Gets or enables/disables printing callback events from the SRP client.
+ * @moreinfo{@srp}.
  * @sa otSrpClientSetCallback
  */
 template <> otError SrpClient::Process<Cmd("callback")>(Arg aArgs[])
@@ -210,7 +212,7 @@
      * To set the client host name when the host has either been removed or not yet
      * registered with the server, use the `name` parameter.
      * @par
-     * Gets or sets the host name of the SRP client.
+     * Gets or sets the host name of the SRP client. @moreinfo{@srp}.
      * @sa otSrpClientSetHostName
      */
     else if (aArgs[0] == "name")
@@ -285,6 +287,7 @@
          * @par
          * Indicates whether auto address mode is enabled. If auto address mode is not
          * enabled, then the list of SRP client host addresses is returned.
+         * @moreinfo{@srp}.
          * @sa otSrpClientGetHostInfo
          */
         if (aArgs[1].IsEmpty())
@@ -326,7 +329,7 @@
          *     running. This will also disable auto host address mode.
          * @par
          * Enable auto host address mode or explicitly set the list of host
-         * addresses.
+         * addresses. @moreinfo{@srp}.
          * @sa otSrpClientEnableAutoHostAddress
          * @sa otSrpClientSetHostAddresses
          */
@@ -383,6 +386,7 @@
      *     `removekeylease` parameter is specified first in the command.
      * @par
      * Removes SRP client host information and all services from the SRP server.
+     * @moreinfo{@srp}.
      * @sa otSrpClientRemoveHostAndServices
      * @sa otSrpClientSetHostName
      */
@@ -449,7 +453,7 @@
  */
 template <> otError SrpClient::Process<Cmd("leaseinterval")>(Arg aArgs[])
 {
-    return Interpreter::GetInterpreter().ProcessGetSet(aArgs, otSrpClientGetLeaseInterval, otSrpClientSetLeaseInterval);
+    return ProcessGetSet(aArgs, otSrpClientGetLeaseInterval, otSrpClientSetLeaseInterval);
 }
 
 /**
@@ -471,8 +475,7 @@
  */
 template <> otError SrpClient::Process<Cmd("keyleaseinterval")>(Arg aArgs[])
 {
-    return Interpreter::GetInterpreter().ProcessGetSet(aArgs, otSrpClientGetKeyLeaseInterval,
-                                                       otSrpClientSetKeyLeaseInterval);
+    return ProcessGetSet(aArgs, otSrpClientGetKeyLeaseInterval, otSrpClientSetKeyLeaseInterval);
 }
 
 template <> otError SrpClient::Process<Cmd("server")>(Arg aArgs[])
@@ -490,7 +493,7 @@
      * @par
      * Gets the socket address (IPv6 address and port number) of the SRP server
      * that is being used by the SRP client. If the client is not running, the address
-     * is unspecified (all zeros) with a port number of 0.
+     * is unspecified (all zeros) with a port number of 0. @moreinfo{@srp}.
      * @sa otSrpClientGetServerAddress
      */
     if (aArgs[0].IsEmpty())
@@ -573,11 +576,12 @@
      * * -->                          [@ca{weight}] [@ca{txt}]
      * The `servicename` parameter can optionally include a list of service subtype labels that are
      * separated by commas. The examples here use generic naming. The `priority` and `weight` (both are `uint16_t`
-     * values) parameters are optional, and if not provided zero is used. The optional `txt` parameter sets the TXT data
-     * associated with the service. The `txt` value must be in hex-string format and is treated as an already encoded
-     * TXT data byte sequence.
+     * values) parameters are optional, and if not provided zero is used. The optional `txt` parameter sets the TXT
+     * data associated with the service. The `txt` value must be in hex-string format and is treated as an already
+     * encoded TXT data byte sequence.
      * @par
      * Adds a service with a given instance name, service name, and port number.
+     * @moreinfo{@srp}.
      * @sa otSrpClientAddService
      */
     else if (aArgs[0] == "add")
@@ -652,15 +656,15 @@
      * @par
      * Gets or sets the service key record inclusion mode in the SRP client.
      * This command is intended for testing only, and requires that
-     * `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` be enabled.
+     * `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` be enabled. @moreinfo{@srp}.
      * @sa otSrpClientIsServiceKeyRecordEnabled
      */
     else if (aArgs[0] == "key")
     {
         // `key [enable/disable]`
 
-        error = Interpreter::GetInterpreter().ProcessEnableDisable(aArgs + 1, otSrpClientIsServiceKeyRecordEnabled,
-                                                                   otSrpClientSetServiceKeyRecordEnabled);
+        error = ProcessEnableDisable(aArgs + 1, otSrpClientIsServiceKeyRecordEnabled,
+                                     otSrpClientSetServiceKeyRecordEnabled);
     }
 #endif // OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
     else
@@ -844,7 +848,7 @@
  * @endcode
  * @cparam srp client start @ca{serveraddr} @ca{serverport}
  * @par
- * Starts the SRP client operation.
+ * Starts the SRP client operation. @moreinfo{@srp}.
  * @sa otSrpClientStart
  */
 template <> otError SrpClient::Process<Cmd("start")>(Arg aArgs[])
@@ -871,6 +875,7 @@
  * @endcode
  * @par api_copy
  * #otSrpClientIsRunning
+ * @moreinfo{@srp}.
  */
 template <> otError SrpClient::Process<Cmd("state")>(Arg aArgs[])
 {
@@ -923,7 +928,7 @@
  */
 template <> otError SrpClient::Process<Cmd("ttl")>(Arg aArgs[])
 {
-    return Interpreter::GetInterpreter().ProcessGetSet(aArgs, otSrpClientGetTtl, otSrpClientSetTtl);
+    return ProcessGetSet(aArgs, otSrpClientGetTtl, otSrpClientSetTtl);
 }
 
 void SrpClient::HandleCallback(otError                    aError,
diff --git a/src/cli/cli_srp_client.hpp b/src/cli/cli_srp_client.hpp
index 5d96a13..85564cd 100644
--- a/src/cli/cli_srp_client.hpp
+++ b/src/cli/cli_srp_client.hpp
@@ -40,7 +40,7 @@
 #include <openthread/srp_client_buffers.h>
 
 #include "cli/cli_config.h"
-#include "cli/cli_output.hpp"
+#include "cli/cli_utils.hpp"
 
 #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE
 
@@ -51,11 +51,9 @@
  * Implements the SRP Client CLI interpreter.
  *
  */
-class SrpClient : private Output
+class SrpClient : private Utils
 {
 public:
-    typedef Utils::CmdLineParser::Arg Arg;
-
     /**
      * Constructor
      *
@@ -80,11 +78,8 @@
     otError Process(Arg aArgs[]);
 
 private:
-    enum : uint8_t
-    {
-        kMaxHostAddresses = OPENTHREAD_CONFIG_SRP_CLIENT_BUFFERS_MAX_HOST_ADDRESSES,
-        kIndentSize       = 4,
-    };
+    static constexpr uint8_t kIndentSize       = 4;
+    static constexpr uint8_t kMaxHostAddresses = OPENTHREAD_CONFIG_SRP_CLIENT_BUFFERS_MAX_HOST_ADDRESSES;
 
     using Command = CommandEntry<SrpClient>;
 
diff --git a/src/cli/cli_srp_server.cpp b/src/cli/cli_srp_server.cpp
index 98213d5..5e71a4a 100644
--- a/src/cli/cli_srp_server.cpp
+++ b/src/cli/cli_srp_server.cpp
@@ -115,13 +115,13 @@
  * to enable or disable the SRP server.
  * @par
  * This command requires that `OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE` be enabled.
+ * @moreinfo{@srp}.
  * @sa otSrpServerIsAutoEnableMode
  * @sa otSrpServerSetAutoEnableMode
  */
 template <> otError SrpServer::Process<Cmd("auto")>(Arg aArgs[])
 {
-    return Interpreter::GetInterpreter().ProcessEnableDisable(aArgs, otSrpServerIsAutoEnableMode,
-                                                              otSrpServerSetAutoEnableMode);
+    return ProcessEnableDisable(aArgs, otSrpServerIsAutoEnableMode, otSrpServerSetAutoEnableMode);
 }
 #endif
 
@@ -173,6 +173,8 @@
  *               The SRP server may become active when the existing
  *               SRP servers are no longer active within the Thread network.
  *  * `running`: The SRP server is active and can handle service registrations.
+ * @par
+ * @moreinfo{@srp}.
  * @sa otSrpServerGetState
  */
 template <> otError SrpServer::Process<Cmd("state")>(Arg aArgs[])
@@ -211,7 +213,7 @@
  * @endcode
  * @cparam srp server [@ca{enable}|@ca{disable}]
  * @par
- * Enables or disables the SRP server.
+ * Enables or disables the SRP server. @moreinfo{@srp}.
  * @sa otSrpServerSetEnabled
  */
 template <> otError SrpServer::Process<Cmd("disable")>(Arg aArgs[])
@@ -308,7 +310,7 @@
  * Done
  * @endcode
  * @par
- * Returns information about all registered hosts.
+ * Returns information about all registered hosts. @moreinfo{@srp}.
  * @sa otSrpServerGetNextHost
  * @sa otSrpServerHostGetAddresses
  * @sa otSrpServerHostGetFullName
@@ -411,6 +413,7 @@
  * The `TXT` record is displayed
  * as an array of entries. If an entry contains a key, the key is printed in
  * ASCII format. The value portion is printed in hexadecimal bytes.
+ * @moreinfo{@srp}.
  * @sa otSrpServerServiceGetInstanceName
  * @sa otSrpServerServiceGetServiceName
  * @sa otSrpServerServiceGetSubTypeServiceNameAt
diff --git a/src/cli/cli_srp_server.hpp b/src/cli/cli_srp_server.hpp
index 344d3c2..b110eb8 100644
--- a/src/cli/cli_srp_server.hpp
+++ b/src/cli/cli_srp_server.hpp
@@ -38,7 +38,7 @@
 
 #include <openthread/srp_server.h>
 
-#include "cli/cli_output.hpp"
+#include "cli/cli_utils.hpp"
 
 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
 
@@ -49,11 +49,9 @@
  * Implements the SRP Server CLI interpreter.
  *
  */
-class SrpServer : private Output
+class SrpServer : private Utils
 {
 public:
-    typedef Utils::CmdLineParser::Arg Arg;
-
     /**
      * Constructor
      *
@@ -62,7 +60,7 @@
      *
      */
     SrpServer(otInstance *aInstance, OutputImplementer &aOutputImplementer)
-        : Output(aInstance, aOutputImplementer)
+        : Utils(aInstance, aOutputImplementer)
     {
     }
 
diff --git a/src/cli/cli_tcat.cpp b/src/cli/cli_tcat.cpp
new file mode 100644
index 0000000..1ef73a5
--- /dev/null
+++ b/src/cli/cli_tcat.cpp
@@ -0,0 +1,179 @@
+/*
+ *  Copyright (c) 2023, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "openthread-core-config.h"
+
+#include "cli/cli_utils.hpp"
+
+#include "cli/cli_tcat.hpp"
+
+#include <openthread/ble_secure.h>
+
+#include <mbedtls/oid.h>
+#include <openthread/tcat.h>
+#include <openthread/platform/ble.h>
+
+#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE && OPENTHREAD_CONFIG_CLI_BLE_SECURE_ENABLE
+
+#define OT_CLI_TCAT_X509_CERT                                              \
+    "-----BEGIN CERTIFICATE-----\r\n"                                      \
+    "MIIBmDCCAT+gAwIBAgIEAQIDBDAKBggqhkjOPQQDAjBvMQswCQYDVQQGEwJYWDEQ\r\n" \
+    "MA4GA1UECBMHTXlTdGF0ZTEPMA0GA1UEBxMGTXlDaXR5MQ8wDQYDVQQLEwZNeVVu\r\n" \
+    "aXQxETAPBgNVBAoTCE15VmVuZG9yMRkwFwYDVQQDExB3d3cubXl2ZW5kb3IuY29t\r\n" \
+    "MB4XDTIzMTAxNjEwMzk1NFoXDTI0MTAxNjEwMzk1NFowIjEgMB4GA1UEAxMXbXl2\r\n" \
+    "ZW5kb3IuY29tL3RjYXQvbXlkZXYwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQB\r\n" \
+    "aWwFDNj1bpQIdN+Kp2cHWw55U/+fa+OmZnoy1B4BOT+822jdwPBuyXWAQoBdYdQJ\r\n" \
+    "ff4RgmhczyV4PhArPIuAoxYwFDASBgkrBgEEAYLfKgMEBQABAQEBMAoGCCqGSM49\r\n" \
+    "BAMCA0cAMEQCIBEHxiEDij26y6V77Q311Gj4CZAuZuPGXZpnzL2BLk7bAiAlFk6G\r\n" \
+    "mYGzkcrYyssFI9HlPgrisWoMmgummaTtCuvrEw==\r\n"                         \
+    "-----END CERTIFICATE-----\r\n"
+
+#define OT_CLI_TCAT_PRIV_KEY                                               \
+    "-----BEGIN EC PRIVATE KEY-----\r\n"                                   \
+    "MHcCAQEEIDeJ6lVQKiOIBxKwTZp6TkU5QVHt9pvXOR9CGpPBI3DhoAoGCCqGSM49\r\n" \
+    "AwEHoUQDQgAEAWlsBQzY9W6UCHTfiqdnB1sOeVP/n2vjpmZ6MtQeATk/vNto3cDw\r\n" \
+    "bsl1gEKAXWHUCX3+EYJoXM8leD4QKzyLgA==\r\n"                             \
+    "-----END EC PRIVATE KEY-----\r\n"
+
+#define OT_CLI_TCAT_TRUSTED_ROOT_CERTIFICATE                               \
+    "-----BEGIN CERTIFICATE-----\r\n"                                      \
+    "MIICCDCCAa2gAwIBAgIJAIKxygBXoH+5MAoGCCqGSM49BAMCMG8xCzAJBgNVBAYT\r\n" \
+    "AlhYMRAwDgYDVQQIEwdNeVN0YXRlMQ8wDQYDVQQHEwZNeUNpdHkxDzANBgNVBAsT\r\n" \
+    "Bk15VW5pdDERMA8GA1UEChMITXlWZW5kb3IxGTAXBgNVBAMTEHd3dy5teXZlbmRv\r\n" \
+    "ci5jb20wHhcNMjMxMDE2MTAzMzE1WhcNMjYxMDE2MTAzMzE1WjBvMQswCQYDVQQG\r\n" \
+    "EwJYWDEQMA4GA1UECBMHTXlTdGF0ZTEPMA0GA1UEBxMGTXlDaXR5MQ8wDQYDVQQL\r\n" \
+    "EwZNeVVuaXQxETAPBgNVBAoTCE15VmVuZG9yMRkwFwYDVQQDExB3d3cubXl2ZW5k\r\n" \
+    "b3IuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEWdyzPAXGKeZY94OhHAWX\r\n" \
+    "HzJfQIjGSyaOzlgL9OEFw2SoUDncLKPGwfPAUSfuMyEkzszNDM0HHkBsDLqu4n25\r\n" \
+    "/6MyMDAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU4EynoSw9eDKZEVPkums2\r\n" \
+    "IWLAJCowCgYIKoZIzj0EAwIDSQAwRgIhAMYGGL9xShyE6P9wEU+MAYF6W3CzdrwV\r\n" \
+    "kuerX1encIH2AiEA5rq490NUobM1Au43roxJq1T6Z43LscPVbGZfULD1Jq0=\r\n"     \
+    "-----END CERTIFICATE-----\r\n"
+
+namespace ot {
+
+namespace Cli {
+
+const char kPskdVendor[] = "J01NM3";
+const char kUrl[]        = "dummy_url";
+
+static void HandleBleSecureReceive(otInstance               *aInstance,
+                                   const otMessage          *aMessage,
+                                   int32_t                   aOffset,
+                                   otTcatApplicationProtocol aTcatApplicationProtocol,
+                                   const char               *aServiceName,
+                                   void                     *aContext)
+{
+    OT_UNUSED_VARIABLE(aContext);
+    OT_UNUSED_VARIABLE(aTcatApplicationProtocol);
+    OT_UNUSED_VARIABLE(aServiceName);
+
+    static constexpr int     kTextMaxLen   = 100;
+    static constexpr uint8_t kBufPrefixLen = 5;
+
+    uint16_t nLen;
+    uint8_t  buf[kTextMaxLen];
+
+    nLen = otMessageRead(aMessage, (uint16_t)aOffset, buf + kBufPrefixLen, sizeof(buf) - kBufPrefixLen - 1);
+
+    memcpy(buf, "RECV:", kBufPrefixLen);
+
+    buf[nLen + kBufPrefixLen] = 0;
+
+    IgnoreReturnValue(otBleSecureSendApplicationTlv(aInstance, buf, (uint16_t)strlen((char *)buf)));
+    IgnoreReturnValue(otBleSecureFlush(aInstance));
+}
+
+template <> otError Tcat::Process<Cmd("start")>(Arg aArgs[])
+{
+    OT_UNUSED_VARIABLE(aArgs);
+
+    otError error = OT_ERROR_NONE;
+
+    ClearAllBytes(mVendorInfo);
+    mVendorInfo.mPskdString      = kPskdVendor;
+    mVendorInfo.mProvisioningUrl = kUrl;
+
+    otBleSecureSetCertificate(GetInstancePtr(), reinterpret_cast<const uint8_t *>(OT_CLI_TCAT_X509_CERT),
+                              sizeof(OT_CLI_TCAT_X509_CERT), reinterpret_cast<const uint8_t *>(OT_CLI_TCAT_PRIV_KEY),
+                              sizeof(OT_CLI_TCAT_PRIV_KEY));
+
+    otBleSecureSetCaCertificateChain(GetInstancePtr(),
+                                     reinterpret_cast<const uint8_t *>(OT_CLI_TCAT_TRUSTED_ROOT_CERTIFICATE),
+                                     sizeof(OT_CLI_TCAT_TRUSTED_ROOT_CERTIFICATE));
+
+    otBleSecureSetSslAuthMode(GetInstancePtr(), true);
+
+    SuccessOrExit(error = otBleSecureStart(GetInstancePtr(), nullptr, HandleBleSecureReceive, true, nullptr));
+    SuccessOrExit(error = otBleSecureTcatStart(GetInstancePtr(), &mVendorInfo, nullptr));
+
+exit:
+    return error;
+}
+
+template <> otError Tcat::Process<Cmd("stop")>(Arg aArgs[])
+{
+    OT_UNUSED_VARIABLE(aArgs);
+
+    otBleSecureStop(GetInstancePtr());
+
+    return OT_ERROR_NONE;
+}
+
+otError Tcat::Process(Arg aArgs[])
+{
+#define CmdEntry(aCommandString)                            \
+    {                                                       \
+        aCommandString, &Tcat::Process<Cmd(aCommandString)> \
+    }
+
+    static constexpr Command kCommands[] = {CmdEntry("start"), CmdEntry("stop")};
+
+    static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
+
+    otError        error = OT_ERROR_NONE;
+    const Command *command;
+
+    if (aArgs[0].IsEmpty() || (aArgs[0] == "help"))
+    {
+        OutputCommandTable(kCommands);
+        ExitNow(error = aArgs[0].IsEmpty() ? error : OT_ERROR_NONE);
+    }
+
+    command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
+    VerifyOrExit(command != nullptr);
+
+    error = (this->*command->mHandler)(aArgs + 1);
+
+exit:
+    return error;
+}
+
+} // namespace Cli
+} // namespace ot
+#endif // OPENTHREAD_CONFIG_BLE_TCAT_ENABLE && OPENTHREAD_CONFIG_CLI_BLE_SECURE_ENABLE
diff --git a/src/cli/cli_tcat.hpp b/src/cli/cli_tcat.hpp
new file mode 100644
index 0000000..697cba1
--- /dev/null
+++ b/src/cli/cli_tcat.hpp
@@ -0,0 +1,91 @@
+/*
+ *  Copyright (c) 2023, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef CLI_TCAT_HPP_
+#define CLI_TCAT_HPP_
+
+#include "openthread-core-config.h"
+
+#include <openthread/tcat.h>
+
+#include "cli/cli_utils.hpp"
+
+#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE && OPENTHREAD_CONFIG_CLI_BLE_SECURE_ENABLE
+
+namespace ot {
+
+namespace Cli {
+
+/**
+ * Implements the Tcat CLI interpreter.
+ *
+ */
+class Tcat : private Utils
+{
+public:
+    /**
+     * Constructor
+     *
+     * @param[in]  aInstance            The OpenThread Instance.
+     * @param[in]  aOutputImplementer   An `OutputImplementer`.
+     *
+     */
+    Tcat(otInstance *aInstance, OutputImplementer &aOutputImplementer)
+        : Utils(aInstance, aOutputImplementer)
+    {
+    }
+
+    /**
+     * Processes a CLI sub-command.
+     *
+     * @param[in]  aArgs     An array of command line arguments.
+     *
+     * @retval OT_ERROR_NONE              Successfully executed the CLI command.
+     * @retval OT_ERROR_PENDING           The CLI command was successfully started but final result is pending.
+     * @retval OT_ERROR_INVALID_COMMAND   Invalid or unknown CLI command.
+     * @retval OT_ERROR_INVALID_ARGS      Invalid arguments.
+     * @retval ...                        Error during execution of the CLI command.
+     *
+     */
+    otError Process(Arg aArgs[]);
+
+private:
+    using Command = CommandEntry<Tcat>;
+
+    template <CommandId kCommandId> otError Process(Arg aArgs[]);
+
+    otTcatVendorInfo mVendorInfo;
+};
+
+} // namespace Cli
+
+} // namespace ot
+
+#endif // OPENTHREAD_CONFIG_BLE_TCAT_ENABLE && OPENTHREAD_CONFIG_CLI_BLE_SECURE_ENABLE
+
+#endif // CLI_TCAT_HPP_
diff --git a/src/cli/cli_tcp.cpp b/src/cli/cli_tcp.cpp
index ddfc098..b891c2d 100644
--- a/src/cli/cli_tcp.cpp
+++ b/src/cli/cli_tcp.cpp
@@ -61,7 +61,7 @@
 #endif
 
 TcpExample::TcpExample(otInstance *aInstance, OutputImplementer &aOutputImplementer)
-    : Output(aInstance, aOutputImplementer)
+    : Utils(aInstance, aOutputImplementer)
     , mInitialized(false)
     , mEndpointConnected(false)
     , mEndpointConnectedFastOpen(false)
@@ -81,9 +81,10 @@
 void TcpExample::MbedTlsDebugOutput(void *ctx, int level, const char *file, int line, const char *str)
 {
     TcpExample &tcpExample = *static_cast<TcpExample *>(ctx);
+
     tcpExample.OutputLine("%s:%d:%d: %s", file, line, level, str);
 }
-#endif // OPENTHREAD_CONFIG_TLS_ENABLE
+#endif
 
 /**
  * @cli tcp init
@@ -219,8 +220,9 @@
     {
         otTcpEndpointInitializeArgs endpointArgs;
 
-        memset(&endpointArgs, 0x00, sizeof(endpointArgs));
+        ClearAllBytes(endpointArgs);
         endpointArgs.mEstablishedCallback = HandleTcpEstablishedCallback;
+
         if (mUseCircularSendBuffer)
         {
             endpointArgs.mForwardProgressCallback = HandleTcpForwardProgressCallback;
@@ -229,6 +231,7 @@
         {
             endpointArgs.mSendDoneCallback = HandleTcpSendDoneCallback;
         }
+
         endpointArgs.mReceiveAvailableCallback = HandleTcpReceiveAvailableCallback;
         endpointArgs.mDisconnectedCallback     = HandleTcpDisconnectedCallback;
         endpointArgs.mContext                  = this;
@@ -241,12 +244,13 @@
     {
         otTcpListenerInitializeArgs listenerArgs;
 
-        memset(&listenerArgs, 0x00, sizeof(listenerArgs));
+        ClearAllBytes(listenerArgs);
         listenerArgs.mAcceptReadyCallback = HandleTcpAcceptReadyCallback;
         listenerArgs.mAcceptDoneCallback  = HandleTcpAcceptDoneCallback;
         listenerArgs.mContext             = this;
 
         error = otTcpListenerInitialize(GetInstancePtr(), &mListener, &listenerArgs);
+
         if (error != OT_ERROR_NONE)
         {
             IgnoreReturnValue(otTcpEndpointDeinitialize(&mEndpoint));
@@ -289,7 +293,7 @@
         mbedtls_pk_free(&mPKey);
         mbedtls_x509_crt_free(&mSrvCert);
     }
-#endif // OPENTHREAD_CONFIG_TLS_ENABLE
+#endif
 
     endpointError = otTcpEndpointDeinitialize(&mEndpoint);
     mSendBusy     = false;
@@ -322,7 +326,7 @@
  * Associates an IPv6 address and a port to the example TCP endpoint provided by
  * the `tcp` CLI. Associating the TCP endpoint to an IPv6
  * address and port is referred to as "naming the TCP endpoint." This binds the
- * endpoint for communication.
+ * endpoint for communication. @moreinfo{@tcp}.
  * @sa otTcpBind
  */
 template <> otError TcpExample::Process<Cmd("bind")>(Arg aArgs[])
@@ -367,27 +371,28 @@
  * Establishes a connection with the specified peer.
  * @par
  * If the connection establishment is successful, the resulting TCP connection
- * is associated with the example TCP endpoint.
+ * is associated with the example TCP endpoint. @moreinfo{@tcp}.
  * @sa otTcpConnect
  */
 template <> otError TcpExample::Process<Cmd("connect")>(Arg aArgs[])
 {
     otError    error;
     otSockAddr sockaddr;
-    bool       nat64SynthesizedAddress;
+    bool       nat64Synth;
     uint32_t   flags;
 
     VerifyOrExit(mInitialized, error = OT_ERROR_INVALID_STATE);
 
-    SuccessOrExit(
-        error = Interpreter::ParseToIp6Address(GetInstancePtr(), aArgs[0], sockaddr.mAddress, nat64SynthesizedAddress));
-    if (nat64SynthesizedAddress)
+    SuccessOrExit(error = Interpreter::ParseToIp6Address(GetInstancePtr(), aArgs[0], sockaddr.mAddress, nat64Synth));
+
+    if (nat64Synth)
     {
         OutputFormat("Connecting to synthesized IPv6 address: ");
         OutputIp6AddressLine(sockaddr.mAddress);
     }
 
     SuccessOrExit(error = aArgs[1].ParseAsUint16(sockaddr.mPort));
+
     if (aArgs[2].IsEmpty())
     {
         flags = OT_TCP_CONNECT_NO_FAST_OPEN;
@@ -406,6 +411,7 @@
         {
             ExitNow(error = OT_ERROR_INVALID_ARGS);
         }
+
         VerifyOrExit(aArgs[3].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
     }
 
@@ -419,7 +425,7 @@
             OutputLine("mbedtls_ssl_config_defaults returned %d", rv);
         }
     }
-#endif // OPENTHREAD_CONFIG_TLS_ENABLE
+#endif
 
     SuccessOrExit(error = otTcpConnect(&mEndpoint, &sockaddr, flags));
     mEndpointConnected         = true;
@@ -448,7 +454,7 @@
  * remote TCP endpoint.
  * @par
  * Sends data over the TCP connection associated with the example TCP endpoint
- * that is provided with the `tcp` CLI.
+ * that is provided with the `tcp` CLI. @moreinfo{@tcp}.
  */
 template <> otError TcpExample::Process<Cmd("send")>(Arg aArgs[])
 {
@@ -466,16 +472,19 @@
         {
             int rv = mbedtls_ssl_write(&mSslContext, reinterpret_cast<unsigned char *>(aArgs[0].GetCString()),
                                        aArgs[0].GetLength());
+
             if (rv < 0 && rv != MBEDTLS_ERR_SSL_WANT_WRITE && rv != MBEDTLS_ERR_SSL_WANT_READ)
             {
-                ExitNow(error = kErrorFailed);
+                ExitNow(error = OT_ERROR_FAILED);
             }
-            error = kErrorNone;
+
+            error = OT_ERROR_NONE;
         }
         else
-#endif // OPENTHREAD_CONFIG_TLS_ENABLE
+#endif
         {
             size_t written;
+
             SuccessOrExit(error = otTcpCircularSendBufferWrite(&mEndpoint, &mSendBuffer, aArgs[0].GetCString(),
                                                                aArgs[0].GetLength(), &written, 0));
         }
@@ -526,6 +535,7 @@
     if (aArgs[0] == "result")
     {
         OutputFormat("TCP Benchmark Status: ");
+
         if (mBenchmarkBytesTotal != 0)
         {
             OutputLine("Ongoing");
@@ -571,6 +581,7 @@
             SuccessOrExit(error = aArgs[1].ParseAsUint32(mBenchmarkBytesTotal));
             VerifyOrExit(mBenchmarkBytesTotal != 0, error = OT_ERROR_INVALID_ARGS);
         }
+
         VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
 
         mBenchmarkStart       = TimerMilli::GetNow();
@@ -594,10 +605,12 @@
                 mBenchmarkLinks[i].mNext   = nullptr;
                 mBenchmarkLinks[i].mData   = mSendBufferBytes;
                 mBenchmarkLinks[i].mLength = sizeof(mSendBufferBytes);
+
                 if (i == 0 && mBenchmarkBytesTotal % sizeof(mSendBufferBytes) != 0)
                 {
                     mBenchmarkLinks[i].mLength = mBenchmarkBytesTotal % sizeof(mSendBufferBytes);
                 }
+
                 error = otTcpSendByReference(&mEndpoint, &mBenchmarkLinks[i],
                                              i == toSendOut - 1 ? 0 : OT_TCP_SEND_MORE_TO_COME);
                 VerifyOrExit(error == OT_ERROR_NONE, mBenchmarkBytesTotal = 0);
@@ -681,7 +694,7 @@
  *   and are associated with the example TCP endpoint.
  * @par
  * Uses the example TCP listener to listen for incoming connections on the
- * specified IPv6 address and port.
+ * specified IPv6 address and port. @moreinfo{@tcp}.
  * @sa otTcpListen
  */
 template <> otError TcpExample::Process<Cmd("listen")>(Arg aArgs[])
@@ -812,7 +825,7 @@
         PrepareTlsHandshake();
         ContinueTlsHandshake();
     }
-#endif // OPENTHREAD_CONFIG_TLS_ENABLE
+#endif
 }
 
 void TcpExample::HandleTcpSendDone(otTcpEndpoint *aEndpoint, otLinkedBuffer *aData)
@@ -834,10 +847,13 @@
     {
         OT_ASSERT(aData != &mSendLink);
         OT_ASSERT(mBenchmarkBytesUnsent >= aData->mLength);
+
         mBenchmarkBytesUnsent -= aData->mLength; // could be less than sizeof(mSendBufferBytes) for the first link
+
         if (mBenchmarkBytesUnsent >= OT_ARRAY_LENGTH(mBenchmarkLinks) * sizeof(mSendBufferBytes))
         {
             aData->mLength = sizeof(mSendBufferBytes);
+
             if (otTcpSendByReference(&mEndpoint, aData, 0) != OT_ERROR_NONE)
             {
                 OutputLine("TCP Benchmark Failed");
@@ -907,7 +923,7 @@
 #if OPENTHREAD_CONFIG_TLS_ENABLE
     if (mUseTls && ContinueTlsHandshake())
     {
-        return;
+        ExitNow();
     }
 #endif
 
@@ -917,15 +933,18 @@
         if (mUseTls)
         {
             uint8_t buffer[500];
+
             for (;;)
             {
                 int rv = mbedtls_ssl_read(&mSslContext, buffer, sizeof(buffer));
+
                 if (rv < 0)
                 {
                     if (rv == MBEDTLS_ERR_SSL_WANT_READ)
                     {
                         break;
                     }
+
                     OutputLine("TLS receive failure: %d", rv);
                 }
                 else
@@ -941,13 +960,16 @@
         {
             const otLinkedBuffer *data;
             size_t                totalReceived = 0;
+
             IgnoreError(otTcpReceiveByReference(aEndpoint, &data));
+
             for (; data != nullptr; data = data->mNext)
             {
                 OutputLine("TCP: Received %u bytes: %.*s", static_cast<unsigned>(data->mLength),
                            static_cast<unsigned>(data->mLength), reinterpret_cast<const char *>(data->mData));
                 totalReceived += data->mLength;
             }
+
             OT_ASSERT(aBytesAvailable == totalReceived);
             IgnoreReturnValue(otTcpCommitReceive(aEndpoint, totalReceived, 0));
         }
@@ -957,6 +979,11 @@
     {
         OutputLine("TCP: Reached end of stream");
     }
+
+    ExitNow();
+
+exit:
+    return;
 }
 
 void TcpExample::HandleTcpDisconnected(otTcpEndpoint *aEndpoint, otTcpDisconnectedReason aReason)
@@ -1035,8 +1062,10 @@
         {
             OutputLine("mbedtls_ssl_config_defaults returned %d", rv);
         }
+
         mbedtls_ssl_conf_ca_chain(&mSslConfig, mSrvCert.next, nullptr);
         rv = mbedtls_ssl_conf_own_cert(&mSslConfig, &mSrvCert, &mPKey);
+
         if (rv != 0)
         {
             OutputLine("mbedtls_ssl_conf_own_cert returned %d", rv);
@@ -1076,6 +1105,7 @@
         {
             int rv = mbedtls_ssl_write(&mSslContext, reinterpret_cast<const unsigned char *>(sBenchmarkData),
                                        toSendThisIteration);
+
             if (rv > 0)
             {
                 written = static_cast<size_t>(rv);
@@ -1083,12 +1113,13 @@
             }
             else if (rv != MBEDTLS_ERR_SSL_WANT_WRITE && rv != MBEDTLS_ERR_SSL_WANT_READ)
             {
-                ExitNow(error = kErrorFailed);
+                ExitNow(error = OT_ERROR_FAILED);
             }
-            error = kErrorNone;
+
+            error = OT_ERROR_NONE;
         }
         else
-#endif // OPENTHREAD_CONFIG_TLS_ENABLE
+#endif
         {
             SuccessOrExit(error = otTcpCircularSendBufferWrite(&mEndpoint, &mSendBuffer, sBenchmarkData,
                                                                toSendThisIteration, &written, flag));
@@ -1132,17 +1163,21 @@
 void TcpExample::PrepareTlsHandshake(void)
 {
     int rv;
+
     rv = mbedtls_ssl_set_hostname(&mSslContext, "localhost");
+
     if (rv != 0)
     {
         OutputLine("mbedtls_ssl_set_hostname returned %d", rv);
     }
+
     rv = mbedtls_ssl_set_hs_ecjpake_password(&mSslContext, reinterpret_cast<const unsigned char *>(sEcjpakePassword),
                                              sEcjpakePasswordLength);
     if (rv != 0)
     {
         OutputLine("mbedtls_ssl_set_hs_ecjpake_password returned %d", rv);
     }
+
     mbedtls_ssl_set_bio(&mSslContext, &mEndpointAndCircularSendBuffer, otTcpMbedTlsSslSendCallback,
                         otTcpMbedTlsSslRecvCallback, nullptr);
     mTlsHandshakeComplete = false;
@@ -1156,6 +1191,7 @@
     if (!mTlsHandshakeComplete)
     {
         rv = mbedtls_ssl_handshake(&mSslContext);
+
         if (rv == 0)
         {
             OutputLine("TLS Handshake Complete");
@@ -1165,6 +1201,7 @@
         {
             OutputLine("TLS Handshake Failed: %d", rv);
         }
+
         wasNotAlreadyDone = true;
     }
 
diff --git a/src/cli/cli_tcp.hpp b/src/cli/cli_tcp.hpp
index 50e7280..e658ede 100644
--- a/src/cli/cli_tcp.hpp
+++ b/src/cli/cli_tcp.hpp
@@ -49,7 +49,7 @@
 #endif
 
 #include "cli/cli_config.h"
-#include "cli/cli_output.hpp"
+#include "cli/cli_utils.hpp"
 #include "common/time.hpp"
 
 namespace ot {
@@ -59,11 +59,9 @@
  * Implements a CLI-based TCP example.
  *
  */
-class TcpExample : private Output
+class TcpExample : private Utils
 {
 public:
-    using Arg = Utils::CmdLineParser::Arg;
-
     /**
      * Constructor
      *
diff --git a/src/cli/cli_udp.cpp b/src/cli/cli_udp.cpp
index 0ec70cc..299be12 100644
--- a/src/cli/cli_udp.cpp
+++ b/src/cli/cli_udp.cpp
@@ -44,10 +44,10 @@
 namespace Cli {
 
 UdpExample::UdpExample(otInstance *aInstance, OutputImplementer &aOutputImplementer)
-    : Output(aInstance, aOutputImplementer)
+    : Utils(aInstance, aOutputImplementer)
     , mLinkSecurityEnabled(true)
 {
-    memset(&mSocket, 0, sizeof(mSocket));
+    ClearAllBytes(mSocket);
 }
 
 /**
@@ -76,9 +76,8 @@
  * - `port`: UDP port number to bind to. Each of the examples is using port number 1234.
  * @par
  * Assigns an IPv6 address and a port to an open socket, which binds the socket for communication.
- * Assigning the IPv6 address and port is referred to as naming the socket.
+ * Assigning the IPv6 address and port is referred to as naming the socket. @moreinfo{@udp}.
  * @sa otUdpBind
- * @sa @udp
  */
 template <> otError UdpExample::Process<Cmd("bind")>(Arg aArgs[])
 {
@@ -127,17 +126,17 @@
  * `InvalidState` when the preferred NAT64 prefix is unavailable.
  * @par api_copy
  * #otUdpConnect
- * @sa @udp
+ * @moreinfo{@udp}.
  */
 template <> otError UdpExample::Process<Cmd("connect")>(Arg aArgs[])
 {
     otError    error;
     otSockAddr sockaddr;
-    bool       nat64SynthesizedAddress;
+    bool       nat64Synth;
 
-    SuccessOrExit(
-        error = Interpreter::ParseToIp6Address(GetInstancePtr(), aArgs[0], sockaddr.mAddress, nat64SynthesizedAddress));
-    if (nat64SynthesizedAddress)
+    SuccessOrExit(error = Interpreter::ParseToIp6Address(GetInstancePtr(), aArgs[0], sockaddr.mAddress, nat64Synth));
+
+    if (nat64Synth)
     {
         OutputFormat("Connecting to synthesized IPv6 address: ");
         OutputIp6AddressLine(sockaddr.mAddress);
@@ -244,12 +243,11 @@
  *   - `-s`: Auto-generated payload with the specified length given in the `value` parameter.
  *   - `-x`: Binary data in hexadecimal representation given in the `value` parameter.
  * @par
- * Sends a UDP message using the socket.
+ * Sends a UDP message using the socket. @moreinfo{@udp}.
  * @csa{udp open}
  * @csa{udp bind}
  * @csa{udp connect}
  * @sa otUdpSend
- * @sa @udp
  */
 template <> otError UdpExample::Process<Cmd("send")>(Arg aArgs[])
 {
@@ -258,7 +256,7 @@
     otMessageInfo     messageInfo;
     otMessageSettings messageSettings = {mLinkSecurityEnabled, OT_MESSAGE_PRIORITY_NORMAL};
 
-    memset(&messageInfo, 0, sizeof(messageInfo));
+    ClearAllBytes(messageInfo);
 
     // Possible argument formats:
     //
@@ -269,11 +267,12 @@
 
     if (!aArgs[2].IsEmpty())
     {
-        bool nat64SynthesizedAddress;
+        bool nat64Synth;
 
-        SuccessOrExit(error = Interpreter::ParseToIp6Address(GetInstancePtr(), aArgs[0], messageInfo.mPeerAddr,
-                                                             nat64SynthesizedAddress));
-        if (nat64SynthesizedAddress)
+        SuccessOrExit(
+            error = Interpreter::ParseToIp6Address(GetInstancePtr(), aArgs[0], messageInfo.mPeerAddr, nat64Synth));
+
+        if (nat64Synth)
         {
             OutputFormat("Sending to synthesized IPv6 address: ");
             OutputIp6AddressLine(messageInfo.mPeerAddr);
@@ -399,10 +398,7 @@
 
 otError UdpExample::PrepareHexStringPayload(otMessage &aMessage, const char *aHexString)
 {
-    enum : uint8_t
-    {
-        kBufferSize = 50,
-    };
+    static constexpr uint16_t kBufferSize = 50;
 
     otError  error;
     uint8_t  buf[kBufferSize];
@@ -412,7 +408,7 @@
     while (!done)
     {
         length = sizeof(buf);
-        error  = Utils::CmdLineParser::ParseAsHexStringSegment(aHexString, length, buf);
+        error  = ot::Utils::CmdLineParser::ParseAsHexStringSegment(aHexString, length, buf);
 
         VerifyOrExit((error == OT_ERROR_NONE) || (error == OT_ERROR_PENDING));
         done = (error == OT_ERROR_NONE);
diff --git a/src/cli/cli_udp.hpp b/src/cli/cli_udp.hpp
index 0c15c80..d1c7050 100644
--- a/src/cli/cli_udp.hpp
+++ b/src/cli/cli_udp.hpp
@@ -38,7 +38,7 @@
 
 #include <openthread/udp.h>
 
-#include "cli/cli_output.hpp"
+#include "cli/cli_utils.hpp"
 
 namespace ot {
 namespace Cli {
@@ -47,11 +47,9 @@
  * Implements a CLI-based UDP example.
  *
  */
-class UdpExample : private Output
+class UdpExample : private Utils
 {
 public:
-    typedef Utils::CmdLineParser::Arg Arg;
-
     /**
      * Constructor
      *
diff --git a/src/cli/cli_output.cpp b/src/cli/cli_utils.cpp
similarity index 71%
rename from src/cli/cli_output.cpp
rename to src/cli/cli_utils.cpp
index c70eacd..f2f5359 100644
--- a/src/cli/cli_output.cpp
+++ b/src/cli/cli_utils.cpp
@@ -31,7 +31,7 @@
  *   This file contains implementation of the CLI output module.
  */
 
-#include "cli_output.hpp"
+#include "cli_utils.hpp"
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -48,7 +48,7 @@
 namespace ot {
 namespace Cli {
 
-const char Output::kUnknownString[] = "unknown";
+const char Utils::kUnknownString[] = "unknown";
 
 OutputImplementer::OutputImplementer(otCliOutputCallback aCallback, void *aCallbackContext)
     : mCallback(aCallback)
@@ -60,7 +60,7 @@
 {
 }
 
-void Output::OutputFormat(const char *aFormat, ...)
+void Utils::OutputFormat(const char *aFormat, ...)
 {
     va_list args;
 
@@ -69,7 +69,7 @@
     va_end(args);
 }
 
-void Output::OutputFormat(uint8_t aIndentSize, const char *aFormat, ...)
+void Utils::OutputFormat(uint8_t aIndentSize, const char *aFormat, ...)
 {
     va_list args;
 
@@ -80,7 +80,7 @@
     va_end(args);
 }
 
-void Output::OutputLine(const char *aFormat, ...)
+void Utils::OutputLine(const char *aFormat, ...)
 {
     va_list args;
 
@@ -91,7 +91,7 @@
     OutputNewLine();
 }
 
-void Output::OutputLine(uint8_t aIndentSize, const char *aFormat, ...)
+void Utils::OutputLine(uint8_t aIndentSize, const char *aFormat, ...)
 {
     va_list args;
 
@@ -104,11 +104,11 @@
     OutputNewLine();
 }
 
-void Output::OutputNewLine(void) { OutputFormat("\r\n"); }
+void Utils::OutputNewLine(void) { OutputFormat("\r\n"); }
 
-void Output::OutputSpaces(uint8_t aCount) { OutputFormat("%*s", aCount, ""); }
+void Utils::OutputSpaces(uint8_t aCount) { OutputFormat("%*s", aCount, ""); }
 
-void Output::OutputBytes(const uint8_t *aBytes, uint16_t aLength)
+void Utils::OutputBytes(const uint8_t *aBytes, uint16_t aLength)
 {
     for (uint16_t i = 0; i < aLength; i++)
     {
@@ -116,13 +116,13 @@
     }
 }
 
-void Output::OutputBytesLine(const uint8_t *aBytes, uint16_t aLength)
+void Utils::OutputBytesLine(const uint8_t *aBytes, uint16_t aLength)
 {
     OutputBytes(aBytes, aLength);
     OutputNewLine();
 }
 
-const char *Output::Uint64ToString(uint64_t aUint64, Uint64StringBuffer &aBuffer)
+const char *Utils::Uint64ToString(uint64_t aUint64, Uint64StringBuffer &aBuffer)
 {
     char *cur = &aBuffer.mChars[Uint64StringBuffer::kSize - 1];
 
@@ -143,24 +143,24 @@
     return cur;
 }
 
-void Output::OutputUint64(uint64_t aUint64)
+void Utils::OutputUint64(uint64_t aUint64)
 {
     Uint64StringBuffer buffer;
 
     OutputFormat("%s", Uint64ToString(aUint64, buffer));
 }
 
-void Output::OutputUint64Line(uint64_t aUint64)
+void Utils::OutputUint64Line(uint64_t aUint64)
 {
     OutputUint64(aUint64);
     OutputNewLine();
 }
 
-void Output::OutputEnabledDisabledStatus(bool aEnabled) { OutputLine(aEnabled ? "Enabled" : "Disabled"); }
+void Utils::OutputEnabledDisabledStatus(bool aEnabled) { OutputLine(aEnabled ? "Enabled" : "Disabled"); }
 
 #if OPENTHREAD_FTD || OPENTHREAD_MTD
 
-void Output::OutputIp6Address(const otIp6Address &aAddress)
+void Utils::OutputIp6Address(const otIp6Address &aAddress)
 {
     char string[OT_IP6_ADDRESS_STRING_SIZE];
 
@@ -169,13 +169,13 @@
     return OutputFormat("%s", string);
 }
 
-void Output::OutputIp6AddressLine(const otIp6Address &aAddress)
+void Utils::OutputIp6AddressLine(const otIp6Address &aAddress)
 {
     OutputIp6Address(aAddress);
     OutputNewLine();
 }
 
-void Output::OutputIp6Prefix(const otIp6Prefix &aPrefix)
+void Utils::OutputIp6Prefix(const otIp6Prefix &aPrefix)
 {
     char string[OT_IP6_PREFIX_STRING_SIZE];
 
@@ -184,25 +184,25 @@
     OutputFormat("%s", string);
 }
 
-void Output::OutputIp6PrefixLine(const otIp6Prefix &aPrefix)
+void Utils::OutputIp6PrefixLine(const otIp6Prefix &aPrefix)
 {
     OutputIp6Prefix(aPrefix);
     OutputNewLine();
 }
 
-void Output::OutputIp6Prefix(const otIp6NetworkPrefix &aPrefix)
+void Utils::OutputIp6Prefix(const otIp6NetworkPrefix &aPrefix)
 {
     OutputFormat("%x:%x:%x:%x::/64", (aPrefix.m8[0] << 8) | aPrefix.m8[1], (aPrefix.m8[2] << 8) | aPrefix.m8[3],
                  (aPrefix.m8[4] << 8) | aPrefix.m8[5], (aPrefix.m8[6] << 8) | aPrefix.m8[7]);
 }
 
-void Output::OutputIp6PrefixLine(const otIp6NetworkPrefix &aPrefix)
+void Utils::OutputIp6PrefixLine(const otIp6NetworkPrefix &aPrefix)
 {
     OutputIp6Prefix(aPrefix);
     OutputNewLine();
 }
 
-void Output::OutputSockAddr(const otSockAddr &aSockAddr)
+void Utils::OutputSockAddr(const otSockAddr &aSockAddr)
 {
     char string[OT_IP6_SOCK_ADDR_STRING_SIZE];
 
@@ -211,13 +211,13 @@
     return OutputFormat("%s", string);
 }
 
-void Output::OutputSockAddrLine(const otSockAddr &aSockAddr)
+void Utils::OutputSockAddrLine(const otSockAddr &aSockAddr)
 {
     OutputSockAddr(aSockAddr);
     OutputNewLine();
 }
 
-void Output::OutputDnsTxtData(const uint8_t *aTxtData, uint16_t aTxtDataLength)
+void Utils::OutputDnsTxtData(const uint8_t *aTxtData, uint16_t aTxtDataLength)
 {
     otDnsTxtEntry         entry;
     otDnsTxtEntryIterator iterator;
@@ -262,7 +262,7 @@
     OutputFormat("]");
 }
 
-const char *Output::PercentageToString(uint16_t aValue, PercentageStringBuffer &aBuffer)
+const char *Utils::PercentageToString(uint16_t aValue, PercentageStringBuffer &aBuffer)
 {
     uint32_t     scaledValue = aValue;
     StringWriter writer(aBuffer.mChars, sizeof(aBuffer.mChars));
@@ -275,7 +275,7 @@
 
 #endif // OPENTHREAD_FTD || OPENTHREAD_MTD
 
-void Output::OutputFormatV(const char *aFormat, va_list aArguments) { mImplementer.OutputV(aFormat, aArguments); }
+void Utils::OutputFormatV(const char *aFormat, va_list aArguments) { mImplementer.OutputV(aFormat, aArguments); }
 
 void OutputImplementer::OutputV(const char *aFormat, va_list aArguments)
 {
@@ -370,7 +370,7 @@
 }
 
 #if OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_ENABLE
-void Output::LogInput(const Arg *aArgs)
+void Utils::LogInput(const Arg *aArgs)
 {
     String<kInputOutputLogStringSize> inputString;
 
@@ -383,7 +383,7 @@
 }
 #endif
 
-void Output::OutputTableHeader(uint8_t aNumColumns, const char *const aTitles[], const uint8_t aWidths[])
+void Utils::OutputTableHeader(uint8_t aNumColumns, const char *const aTitles[], const uint8_t aWidths[])
 {
     for (uint8_t index = 0; index < aNumColumns; index++)
     {
@@ -412,7 +412,7 @@
     OutputTableSeparator(aNumColumns, aWidths);
 }
 
-void Output::OutputTableSeparator(uint8_t aNumColumns, const uint8_t aWidths[])
+void Utils::OutputTableSeparator(uint8_t aNumColumns, const uint8_t aWidths[])
 {
     for (uint8_t index = 0; index < aNumColumns; index++)
     {
@@ -427,5 +427,95 @@
     OutputLine("+");
 }
 
+otError Utils::ParseEnableOrDisable(const Arg &aArg, bool &aEnable)
+{
+    otError error = OT_ERROR_NONE;
+
+    if (aArg == "enable")
+    {
+        aEnable = true;
+    }
+    else if (aArg == "disable")
+    {
+        aEnable = false;
+    }
+    else
+    {
+        error = OT_ERROR_INVALID_COMMAND;
+    }
+
+    return error;
+}
+
+otError Utils::ProcessEnableDisable(Arg aArgs[], SetEnabledHandler aSetEnabledHandler)
+{
+    otError error = OT_ERROR_NONE;
+    bool    enable;
+
+    if (ParseEnableOrDisable(aArgs[0], enable) == OT_ERROR_NONE)
+    {
+        aSetEnabledHandler(GetInstancePtr(), enable);
+    }
+    else
+    {
+        error = OT_ERROR_INVALID_COMMAND;
+    }
+
+    return error;
+}
+
+otError Utils::ProcessEnableDisable(Arg aArgs[], SetEnabledHandlerFailable aSetEnabledHandler)
+{
+    otError error = OT_ERROR_NONE;
+    bool    enable;
+
+    if (ParseEnableOrDisable(aArgs[0], enable) == OT_ERROR_NONE)
+    {
+        error = aSetEnabledHandler(GetInstancePtr(), enable);
+    }
+    else
+    {
+        error = OT_ERROR_INVALID_COMMAND;
+    }
+
+    return error;
+}
+
+otError Utils::ProcessEnableDisable(Arg               aArgs[],
+                                    IsEnabledHandler  aIsEnabledHandler,
+                                    SetEnabledHandler aSetEnabledHandler)
+{
+    otError error = OT_ERROR_NONE;
+
+    if (aArgs[0].IsEmpty())
+    {
+        OutputEnabledDisabledStatus(aIsEnabledHandler(GetInstancePtr()));
+    }
+    else
+    {
+        error = ProcessEnableDisable(aArgs, aSetEnabledHandler);
+    }
+
+    return error;
+}
+
+otError Utils::ProcessEnableDisable(Arg                       aArgs[],
+                                    IsEnabledHandler          aIsEnabledHandler,
+                                    SetEnabledHandlerFailable aSetEnabledHandler)
+{
+    otError error = OT_ERROR_NONE;
+
+    if (aArgs[0].IsEmpty())
+    {
+        OutputEnabledDisabledStatus(aIsEnabledHandler(GetInstancePtr()));
+    }
+    else
+    {
+        error = ProcessEnableDisable(aArgs, aSetEnabledHandler);
+    }
+
+    return error;
+}
+
 } // namespace Cli
 } // namespace ot
diff --git a/src/cli/cli_output.hpp b/src/cli/cli_utils.hpp
similarity index 73%
rename from src/cli/cli_output.hpp
rename to src/cli/cli_utils.hpp
index 8941f7e..ebf9cc1 100644
--- a/src/cli/cli_output.hpp
+++ b/src/cli/cli_utils.hpp
@@ -45,6 +45,7 @@
 #include "common/binary_search.hpp"
 #include "common/num_utils.hpp"
 #include "common/string.hpp"
+#include "common/type_traits.hpp"
 #include "utils/parse_cmdline.hpp"
 
 namespace ot {
@@ -69,7 +70,7 @@
     return (aString[0] == '\0') ? 0 : (static_cast<uint8_t>(aString[0]) + Cmd(aString + 1) * 255u);
 }
 
-class Output;
+class Utils;
 
 /**
  * Implements the basic output functions.
@@ -77,7 +78,7 @@
  */
 class OutputImplementer
 {
-    friend class Output;
+    friend class Utils;
 
 public:
     /**
@@ -110,13 +111,13 @@
 };
 
 /**
- * Provides CLI output helper methods.
+ * Provides CLI helper methods.
  *
  */
-class Output
+class Utils
 {
 public:
-    typedef Utils::CmdLineParser::Arg Arg; ///< An argument
+    typedef ot::Utils::CmdLineParser::Arg Arg; ///< An argument
 
     /**
      * Represent a CLI command table entry, mapping a command with `aName` to a handler method.
@@ -189,7 +190,7 @@
      * @param[in] aImplementer        An `OutputImplementer`.
      *
      */
-    Output(otInstance *aInstance, OutputImplementer &aImplementer)
+    Utils(otInstance *aInstance, OutputImplementer &aImplementer)
         : mInstance(aInstance)
         , mImplementer(aImplementer)
     {
@@ -527,6 +528,123 @@
         }
     }
 
+    /**
+     * Clears (sets to zero) all bytes of a given object.
+     *
+     * @tparam ObjectType    The object type.
+     *
+     * @param[in] aObject    A reference to the object of type `ObjectType` to clear all its bytes.
+     *
+     */
+    template <typename ObjectType> static void ClearAllBytes(ObjectType &aObject)
+    {
+        static_assert(!TypeTraits::IsPointer<ObjectType>::kValue, "ObjectType must not be a pointer");
+
+        memset(reinterpret_cast<void *>(&aObject), 0, sizeof(ObjectType));
+    }
+
+    // Definitions of handlers to process Get/Set/Enable/Disable.
+    template <typename ValueType> using GetHandler         = ValueType (&)(otInstance *);
+    template <typename ValueType> using SetHandler         = void (&)(otInstance *, ValueType);
+    template <typename ValueType> using SetHandlerFailable = otError (&)(otInstance *, ValueType);
+    using IsEnabledHandler                                 = bool (&)(otInstance *);
+    using SetEnabledHandler                                = void (&)(otInstance *, bool);
+    using SetEnabledHandlerFailable                        = otError (&)(otInstance *, bool);
+
+    // Returns format string to output a `ValueType` (e.g., "%u" for `uint16_t`).
+    template <typename ValueType> static constexpr const char *FormatStringFor(void);
+
+    /**
+     * Checks a given argument string against "enable" or "disable" commands.
+     *
+     * @param[in]  aArg     The argument string to parse.
+     * @param[out] aEnable  Boolean variable to return outcome on success.
+     *                      Set to TRUE for "enable" command, and FALSE for "disable" command.
+     *
+     * @retval OT_ERROR_NONE             Successfully parsed the @p aString and updated @p aEnable.
+     * @retval OT_ERROR_INVALID_COMMAND  The @p aString is not "enable" or "disable" command.
+     *
+     */
+    static otError ParseEnableOrDisable(const Arg &aArg, bool &aEnable);
+
+    // General template implementation.
+    // Specializations for `uint32_t` and `int32_t` are added at the end.
+    template <typename ValueType> otError ProcessGet(Arg aArgs[], GetHandler<ValueType> aGetHandler)
+    {
+        static_assert(
+            TypeTraits::IsSame<ValueType, uint8_t>::kValue || TypeTraits::IsSame<ValueType, uint16_t>::kValue ||
+                TypeTraits::IsSame<ValueType, int8_t>::kValue || TypeTraits::IsSame<ValueType, int16_t>::kValue ||
+                TypeTraits::IsSame<ValueType, const char *>::kValue,
+            "ValueType must be an  8, 16 `int` or `uint` type, or a `const char *`");
+
+        otError error = OT_ERROR_NONE;
+
+        VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
+        OutputLine(FormatStringFor<ValueType>(), aGetHandler(GetInstancePtr()));
+
+    exit:
+        return error;
+    }
+
+    template <typename ValueType> otError ProcessSet(Arg aArgs[], SetHandler<ValueType> aSetHandler)
+    {
+        otError   error;
+        ValueType value;
+
+        SuccessOrExit(error = aArgs[0].ParseAs<ValueType>(value));
+        VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
+
+        aSetHandler(GetInstancePtr(), value);
+
+    exit:
+        return error;
+    }
+
+    template <typename ValueType> otError ProcessSet(Arg aArgs[], SetHandlerFailable<ValueType> aSetHandler)
+    {
+        otError   error;
+        ValueType value;
+
+        SuccessOrExit(error = aArgs[0].ParseAs<ValueType>(value));
+        VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
+
+        error = aSetHandler(GetInstancePtr(), value);
+
+    exit:
+        return error;
+    }
+
+    template <typename ValueType>
+    otError ProcessGetSet(Arg aArgs[], GetHandler<ValueType> aGetHandler, SetHandler<ValueType> aSetHandler)
+    {
+        otError error = ProcessGet(aArgs, aGetHandler);
+
+        VerifyOrExit(error != OT_ERROR_NONE);
+        error = ProcessSet(aArgs, aSetHandler);
+
+    exit:
+        return error;
+    }
+
+    template <typename ValueType>
+    otError ProcessGetSet(Arg aArgs[], GetHandler<ValueType> aGetHandler, SetHandlerFailable<ValueType> aSetHandler)
+    {
+        otError error = ProcessGet(aArgs, aGetHandler);
+
+        VerifyOrExit(error != OT_ERROR_NONE);
+        error = ProcessSet(aArgs, aSetHandler);
+
+    exit:
+        return error;
+    }
+
+    otError ProcessEnableDisable(Arg aArgs[], SetEnabledHandler aSetEnabledHandler);
+    otError ProcessEnableDisable(Arg aArgs[], SetEnabledHandlerFailable aSetEnabledHandler);
+    otError ProcessEnableDisable(Arg aArgs[], IsEnabledHandler aIsEnabledHandler, SetEnabledHandler aSetEnabledHandler);
+    otError ProcessEnableDisable(Arg                       aArgs[],
+                                 IsEnabledHandler          aIsEnabledHandler,
+                                 SetEnabledHandlerFailable aSetEnabledHandler);
+
 protected:
     void OutputFormatV(const char *aFormat, va_list aArguments);
 
@@ -546,6 +664,46 @@
     OutputImplementer &mImplementer;
 };
 
+// Specializations of `FormatStringFor<ValueType>()`
+
+template <> inline constexpr const char *Utils::FormatStringFor<uint8_t>(void) { return "%u"; }
+
+template <> inline constexpr const char *Utils::FormatStringFor<uint16_t>(void) { return "%u"; }
+
+template <> inline constexpr const char *Utils::FormatStringFor<uint32_t>(void) { return "%lu"; }
+
+template <> inline constexpr const char *Utils::FormatStringFor<int8_t>(void) { return "%d"; }
+
+template <> inline constexpr const char *Utils::FormatStringFor<int16_t>(void) { return "%d"; }
+
+template <> inline constexpr const char *Utils::FormatStringFor<int32_t>(void) { return "%ld"; }
+
+template <> inline constexpr const char *Utils::FormatStringFor<const char *>(void) { return "%s"; }
+
+// Specialization of ProcessGet<> for `uint32_t` and `int32_t`
+
+template <> inline otError Utils::ProcessGet<uint32_t>(Arg aArgs[], GetHandler<uint32_t> aGetHandler)
+{
+    otError error = OT_ERROR_NONE;
+
+    VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
+    OutputLine(FormatStringFor<uint32_t>(), ToUlong(aGetHandler(GetInstancePtr())));
+
+exit:
+    return error;
+}
+
+template <> inline otError Utils::ProcessGet<int32_t>(Arg aArgs[], GetHandler<int32_t> aGetHandler)
+{
+    otError error = OT_ERROR_NONE;
+
+    VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
+    OutputLine(FormatStringFor<int32_t>(), static_cast<long int>(aGetHandler(GetInstancePtr())));
+
+exit:
+    return error;
+}
+
 } // namespace Cli
 } // namespace ot
 
diff --git a/src/cli/ftd.cmake b/src/cli/ftd.cmake
index df9b37a..7b6f08a 100644
--- a/src/cli/ftd.cmake
+++ b/src/cli/ftd.cmake
@@ -31,6 +31,8 @@
 target_compile_definitions(openthread-cli-ftd
     PRIVATE
         OPENTHREAD_FTD=1
+        OPENTHREAD_MTD=0
+        OPENTHREAD_RADIO=0
 )
 
 target_compile_options(openthread-cli-ftd PRIVATE
diff --git a/src/cli/mtd.cmake b/src/cli/mtd.cmake
index b5eb1db..733bfaa 100644
--- a/src/cli/mtd.cmake
+++ b/src/cli/mtd.cmake
@@ -30,7 +30,9 @@
 
 target_compile_definitions(openthread-cli-mtd
     PRIVATE
+        OPENTHREAD_FTD=0
         OPENTHREAD_MTD=1
+        OPENTHREAD_RADIO=0
 )
 
 target_compile_options(openthread-cli-mtd PRIVATE
diff --git a/src/cli/radio.cmake b/src/cli/radio.cmake
index 28320d6..47652d2 100644
--- a/src/cli/radio.cmake
+++ b/src/cli/radio.cmake
@@ -30,6 +30,8 @@
 
 target_compile_definitions(openthread-cli-radio
     PRIVATE
+        OPENTHREAD_FTD=0
+        OPENTHREAD_MTD=0
         OPENTHREAD_RADIO=1
         OPENTHREAD_RADIO_CLI=1
 )
@@ -43,7 +45,7 @@
 target_sources(openthread-cli-radio
     PRIVATE
         cli.cpp
-        cli_output.cpp
+        cli_utils.cpp
 )
 
 if(NOT DEFINED OT_MBEDTLS_RCP)
diff --git a/src/core/BUILD.gn b/src/core/BUILD.gn
index 37843dd..715741d 100644
--- a/src/core/BUILD.gn
+++ b/src/core/BUILD.gn
@@ -41,6 +41,8 @@
       defines += [ "OPENTHREAD_CONFIG_THREAD_VERSION=OT_THREAD_VERSION_1_3" ]
     } else if (openthread_config_thread_version == "1.3.1") {
       defines += [ "OPENTHREAD_CONFIG_THREAD_VERSION=OT_THREAD_VERSION_1_3_1" ]
+    } else if (openthread_config_thread_version == "1.4") {
+      defines += [ "OPENTHREAD_CONFIG_THREAD_VERSION=OT_THREAD_VERSION_1_4" ]
     } else if (openthread_config_thread_version != "") {
       assert(false,
              "Unrecognized Thread version: ${openthread_config_thread_version}")
@@ -74,6 +76,10 @@
       defines += [ "OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE=1" ]
     }
 
+    if (openthread_config_ble_tcat_enable) {
+      defines += [ "OPENTHREAD_CONFIG_BLE_TCAT_ENABLE=1" ]
+    }
+
     if (openthread_config_border_agent_enable) {
       defines += [ "OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE=1" ]
     }
@@ -301,6 +307,7 @@
 openthread_core_files = [
   "api/backbone_router_api.cpp",
   "api/backbone_router_ftd_api.cpp",
+  "api/ble_secure_api.cpp",
   "api/border_agent_api.cpp",
   "api/border_router_api.cpp",
   "api/border_routing_api.cpp",
@@ -510,8 +517,6 @@
   "meshcop/dataset_manager_ftd.cpp",
   "meshcop/dataset_updater.cpp",
   "meshcop/dataset_updater.hpp",
-  "meshcop/dtls.cpp",
-  "meshcop/dtls.hpp",
   "meshcop/energy_scan_client.cpp",
   "meshcop/energy_scan_client.hpp",
   "meshcop/extended_panid.cpp",
@@ -530,6 +535,10 @@
   "meshcop/network_name.hpp",
   "meshcop/panid_query_client.cpp",
   "meshcop/panid_query_client.hpp",
+  "meshcop/secure_transport.cpp",
+  "meshcop/secure_transport.hpp",
+  "meshcop/tcat_agent.cpp",
+  "meshcop/tcat_agent.hpp",
   "meshcop/timestamp.cpp",
   "meshcop/timestamp.hpp",
   "net/checksum.cpp",
@@ -546,6 +555,8 @@
   "net/dns_platform.cpp",
   "net/dns_types.cpp",
   "net/dns_types.hpp",
+  "net/dnssd.cpp",
+  "net/dnssd.hpp",
   "net/dnssd_server.cpp",
   "net/dnssd_server.hpp",
   "net/icmp6.cpp",
@@ -575,6 +586,8 @@
   "net/sntp_client.hpp",
   "net/socket.cpp",
   "net/socket.hpp",
+  "net/srp_advertising_proxy.cpp",
+  "net/srp_advertising_proxy.hpp",
   "net/srp_client.cpp",
   "net/srp_client.hpp",
   "net/srp_server.cpp",
@@ -585,6 +598,8 @@
   "net/tcp6_ext.hpp",
   "net/udp6.cpp",
   "net/udp6.hpp",
+  "radio/ble_secure.cpp",
+  "radio/ble_secure.hpp",
   "radio/max_power_table.hpp",
   "radio/radio.cpp",
   "radio/radio.hpp",
@@ -789,7 +804,6 @@
     "config/dns_client.h",
     "config/dns_dso.h",
     "config/dnssd_server.h",
-    "config/dtls.h",
     "config/history_tracker.h",
     "config/ip6.h",
     "config/joiner.h",
@@ -811,11 +825,13 @@
     "config/platform.h",
     "config/power_calibration.h",
     "config/radio_link.h",
+    "config/secure_transport.h",
     "config/sntp_client.h",
     "config/srp_client.h",
     "config/srp_server.h",
     "config/time_sync.h",
     "config/tmf.h",
+    "config/trel.h",
     "openthread-core-config.h",
   ]
   public_configs = [
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 813979a..5e6cee1 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -33,6 +33,7 @@
 set(COMMON_SOURCES
     api/backbone_router_api.cpp
     api/backbone_router_ftd_api.cpp
+    api/ble_secure_api.cpp
     api/border_agent_api.cpp
     api/border_router_api.cpp
     api/border_routing_api.cpp
@@ -148,7 +149,6 @@
     meshcop/dataset_manager.cpp
     meshcop/dataset_manager_ftd.cpp
     meshcop/dataset_updater.cpp
-    meshcop/dtls.cpp
     meshcop/energy_scan_client.cpp
     meshcop/extended_panid.cpp
     meshcop/joiner.cpp
@@ -158,6 +158,8 @@
     meshcop/meshcop_tlvs.cpp
     meshcop/network_name.cpp
     meshcop/panid_query_client.cpp
+    meshcop/secure_transport.cpp
+    meshcop/tcat_agent.cpp
     meshcop/timestamp.cpp
     net/checksum.cpp
     net/dhcp6_client.cpp
@@ -166,6 +168,7 @@
     net/dns_dso.cpp
     net/dns_platform.cpp
     net/dns_types.cpp
+    net/dnssd.cpp
     net/dnssd_server.cpp
     net/icmp6.cpp
     net/ip4_types.cpp
@@ -180,11 +183,13 @@
     net/netif.cpp
     net/sntp_client.cpp
     net/socket.cpp
+    net/srp_advertising_proxy.cpp
     net/srp_client.cpp
     net/srp_server.cpp
     net/tcp6.cpp
     net/tcp6_ext.cpp
     net/udp6.cpp
+    radio/ble_secure.cpp
     radio/radio.cpp
     radio/radio_callbacks.cpp
     radio/radio_platform.cpp
diff --git a/src/core/api/ble_secure_api.cpp b/src/core/api/ble_secure_api.cpp
new file mode 100644
index 0000000..d30a7f0
--- /dev/null
+++ b/src/core/api/ble_secure_api.cpp
@@ -0,0 +1,184 @@
+/*
+ *  Copyright (c) 2023, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ *   This file implements the OpenThread BLE Secure API.
+ */
+
+#include "openthread-core-config.h"
+
+#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
+
+#include <openthread/ble_secure.h>
+#include <openthread/platform/ble.h>
+
+#include "common/as_core_type.hpp"
+#include "common/code_utils.hpp"
+#include "common/locator_getters.hpp"
+#include "meshcop/tcat_agent.hpp"
+#include "radio/ble_secure.hpp"
+
+using namespace ot;
+
+otError otBleSecureStart(otInstance              *aInstance,
+                         otHandleBleSecureConnect aConnectHandler,
+                         otHandleBleSecureReceive aReceiveHandler,
+                         bool                     aTlvMode,
+                         void                    *aContext)
+{
+    return AsCoreType(aInstance).Get<Ble::BleSecure>().Start(aConnectHandler, aReceiveHandler, aTlvMode, aContext);
+}
+
+otError otBleSecureTcatStart(otInstance *aInstance, const otTcatVendorInfo *aVendorInfo, otHandleTcatJoin aHandler)
+{
+    return AsCoreType(aInstance).Get<Ble::BleSecure>().TcatStart(AsCoreType(aVendorInfo), aHandler);
+}
+
+void otBleSecureStop(otInstance *aInstance) { AsCoreType(aInstance).Get<Ble::BleSecure>().Stop(); }
+
+#ifdef MBEDTLS_KEY_EXCHANGE_PSK_ENABLED
+void otBleSecureSetPsk(otInstance    *aInstance,
+                       const uint8_t *aPsk,
+                       uint16_t       aPskLength,
+                       const uint8_t *aPskIdentity,
+                       uint16_t       aPskIdLength)
+{
+    AssertPointerIsNotNull(aPsk);
+    AssertPointerIsNotNull(aPskIdentity);
+    OT_ASSERT(aPskLength != 0 && aPskIdLength != 0);
+
+    AsCoreType(aInstance).Get<Ble::BleSecure>().SetPreSharedKey(aPsk, aPskLength, aPskIdentity, aPskIdLength);
+}
+#endif // MBEDTLS_KEY_EXCHANGE_PSK_ENABLED
+
+#if defined(MBEDTLS_BASE64_C) && defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
+otError otBleSecureGetPeerCertificateBase64(otInstance *aInstance, unsigned char *aPeerCert, size_t *aCertLength)
+{
+    return AsCoreType(aInstance).Get<Ble::BleSecure>().GetPeerCertificateBase64(aPeerCert, aCertLength);
+}
+#endif // defined(MBEDTLS_BASE64_C) && defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
+
+#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
+otError otBleSecureGetPeerSubjectAttributeByOid(otInstance *aInstance,
+                                                const char *aOid,
+                                                size_t      aOidLength,
+                                                uint8_t    *aAttributeBuffer,
+                                                size_t     *aAttributeLength,
+                                                int        *aAsn1Type)
+{
+    return AsCoreType(aInstance).Get<Ble::BleSecure>().GetPeerSubjectAttributeByOid(aOid, aOidLength, aAttributeBuffer,
+                                                                                    aAttributeLength, aAsn1Type);
+}
+
+otError otBleSecureGetThreadAttributeFromPeerCertificate(otInstance *aInstance,
+                                                         int         aThreadOidDescriptor,
+                                                         uint8_t    *aAttributeBuffer,
+                                                         size_t     *aAttributeLength)
+{
+    return AsCoreType(aInstance).Get<Ble::BleSecure>().GetThreadAttributeFromPeerCertificate(
+        aThreadOidDescriptor, aAttributeBuffer, aAttributeLength);
+}
+#endif // defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
+
+otError otBleSecureGetThreadAttributeFromOwnCertificate(otInstance *aInstance,
+                                                        int         aThreadOidDescriptor,
+                                                        uint8_t    *aAttributeBuffer,
+                                                        size_t     *aAttributeLength)
+{
+    return AsCoreType(aInstance).Get<Ble::BleSecure>().GetThreadAttributeFromOwnCertificate(
+        aThreadOidDescriptor, aAttributeBuffer, aAttributeLength);
+}
+
+void otBleSecureSetSslAuthMode(otInstance *aInstance, bool aVerifyPeerCertificate)
+{
+    AsCoreType(aInstance).Get<Ble::BleSecure>().SetSslAuthMode(aVerifyPeerCertificate);
+}
+
+#ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
+void otBleSecureSetCertificate(otInstance    *aInstance,
+                               const uint8_t *aX509Cert,
+                               uint32_t       aX509Length,
+                               const uint8_t *aPrivateKey,
+                               uint32_t       aPrivateKeyLength)
+{
+    OT_ASSERT(aX509Cert != nullptr && aX509Length != 0 && aPrivateKey != nullptr && aPrivateKeyLength != 0);
+
+    AsCoreType(aInstance).Get<Ble::BleSecure>().SetCertificate(aX509Cert, aX509Length, aPrivateKey, aPrivateKeyLength);
+}
+
+void otBleSecureSetCaCertificateChain(otInstance    *aInstance,
+                                      const uint8_t *aX509CaCertificateChain,
+                                      uint32_t       aX509CaCertChainLength)
+{
+    OT_ASSERT(aX509CaCertificateChain != nullptr && aX509CaCertChainLength != 0);
+
+    AsCoreType(aInstance).Get<Ble::BleSecure>().SetCaCertificateChain(aX509CaCertificateChain, aX509CaCertChainLength);
+}
+#endif // MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
+
+otError otBleSecureConnect(otInstance *aInstance) { return AsCoreType(aInstance).Get<Ble::BleSecure>().Connect(); }
+
+void otBleSecureDisconnect(otInstance *aInstance) { AsCoreType(aInstance).Get<Ble::BleSecure>().Disconnect(); }
+
+bool otBleSecureIsConnectionActive(otInstance *aInstance)
+{
+    return AsCoreType(aInstance).Get<Ble::BleSecure>().IsConnectionActive();
+}
+
+bool otBleSecureIsConnected(otInstance *aInstance) { return AsCoreType(aInstance).Get<Ble::BleSecure>().IsConnected(); }
+
+bool otBleSecureIsTcatEnabled(otInstance *aInstance)
+{
+    return AsCoreType(aInstance).Get<Ble::BleSecure>().IsTcatEnabled();
+}
+
+bool otBleSecureIsCommandClassAuthorized(otInstance *aInstance, otTcatCommandClass aCommandClass)
+{
+    return AsCoreType(aInstance).Get<Ble::BleSecure>().IsCommandClassAuthorized(
+        static_cast<Ble::BleSecure::CommandClass>(aCommandClass));
+}
+
+otError otBleSecureSendMessage(otInstance *aInstance, otMessage *aMessage)
+{
+    return AsCoreType(aInstance).Get<Ble::BleSecure>().SendMessage(AsCoreType(aMessage));
+}
+
+otError otBleSecureSend(otInstance *aInstance, uint8_t *aBuf, uint16_t aLength)
+{
+    return AsCoreType(aInstance).Get<Ble::BleSecure>().Send(aBuf, aLength);
+}
+
+otError otBleSecureSendApplicationTlv(otInstance *aInstance, uint8_t *aBuf, uint16_t aLength)
+{
+    return AsCoreType(aInstance).Get<Ble::BleSecure>().SendApplicationTlv(aBuf, aLength);
+}
+
+otError otBleSecureFlush(otInstance *aInstance) { return AsCoreType(aInstance).Get<Ble::BleSecure>().Flush(); }
+
+#endif // OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
diff --git a/src/core/api/border_agent_api.cpp b/src/core/api/border_agent_api.cpp
index 3590116..6a9f42f 100644
--- a/src/core/api/border_agent_api.cpp
+++ b/src/core/api/border_agent_api.cpp
@@ -64,4 +64,35 @@
     return AsCoreType(aInstance).Get<MeshCoP::BorderAgent>().GetUdpPort();
 }
 
+#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
+
+otError otBorderAgentSetEphemeralKey(otInstance *aInstance,
+                                     const char *aKeyString,
+                                     uint32_t    aTimeout,
+                                     uint16_t    aUdpPort)
+{
+    AssertPointerIsNotNull(aKeyString);
+
+    return AsCoreType(aInstance).Get<MeshCoP::BorderAgent>().SetEphemeralKey(aKeyString, aTimeout, aUdpPort);
+}
+
+void otBorderAgentClearEphemeralKey(otInstance *aInstance)
+{
+    AsCoreType(aInstance).Get<MeshCoP::BorderAgent>().ClearEphemeralKey();
+}
+
+bool otBorderAgentIsEphemeralKeyActive(otInstance *aInstance)
+{
+    return AsCoreType(aInstance).Get<MeshCoP::BorderAgent>().IsEphemeralKeyActive();
+}
+
+void otBorderAgentSetEphemeralKeyCallback(otInstance                       *aInstance,
+                                          otBorderAgentEphemeralKeyCallback aCallback,
+                                          void                             *aContext)
+{
+    AsCoreType(aInstance).Get<MeshCoP::BorderAgent>().SetEphemeralKeyCallback(aCallback, aContext);
+}
+
+#endif // OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
+
 #endif // OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
diff --git a/src/core/api/border_routing_api.cpp b/src/core/api/border_routing_api.cpp
index 9fbfbcb..7c8002d 100644
--- a/src/core/api/border_routing_api.cpp
+++ b/src/core/api/border_routing_api.cpp
@@ -75,6 +75,11 @@
     AsCoreType(aInstance).Get<BorderRouter::RoutingManager>().ClearRouteInfoOptionPreference();
 }
 
+otError otBorderRoutingSetExtraRouterAdvertOptions(otInstance *aInstance, const uint8_t *aOptions, uint16_t aLength)
+{
+    return AsCoreType(aInstance).Get<BorderRouter::RoutingManager>().SetExtraRouterAdvertOptions(aOptions, aLength);
+}
+
 otRoutePreference otBorderRoutingGetRoutePreference(otInstance *aInstance)
 {
     return static_cast<otRoutePreference>(
@@ -104,6 +109,13 @@
 
     return AsCoreType(aInstance).Get<BorderRouter::RoutingManager>().GetPdOmrPrefix(*aPrefixInfo);
 }
+
+otError otBorderRoutingGetPdProcessedRaInfo(otInstance *aInstance, otPdProcessedRaInfo *aPdProcessedRaInfo)
+{
+    AssertPointerIsNotNull(aPdProcessedRaInfo);
+
+    return AsCoreType(aInstance).Get<BorderRouter::RoutingManager>().GetPdProcessedRaInfo(*aPdProcessedRaInfo);
+}
 #endif
 
 otError otBorderRoutingGetFavoredOmrPrefix(otInstance *aInstance, otIp6Prefix *aPrefix, otRoutePreference *aPreference)
diff --git a/src/core/api/channel_manager_api.cpp b/src/core/api/channel_manager_api.cpp
index af17e0d..3aa5d36 100644
--- a/src/core/api/channel_manager_api.cpp
+++ b/src/core/api/channel_manager_api.cpp
@@ -33,7 +33,9 @@
 
 #include "openthread-core-config.h"
 
-#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && OPENTHREAD_FTD
+#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && \
+    (OPENTHREAD_FTD ||                          \
+     (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE))
 
 #include <openthread/channel_manager.h>
 
@@ -43,16 +45,19 @@
 
 using namespace ot;
 
+#if OPENTHREAD_FTD
 void otChannelManagerRequestChannelChange(otInstance *aInstance, uint8_t aChannel)
 {
-    AsCoreType(aInstance).Get<Utils::ChannelManager>().RequestChannelChange(aChannel);
+    AsCoreType(aInstance).Get<Utils::ChannelManager>().RequestNetworkChannelChange(aChannel);
 }
+#endif
 
 uint8_t otChannelManagerGetRequestedChannel(otInstance *aInstance)
 {
     return AsCoreType(aInstance).Get<Utils::ChannelManager>().GetRequestedChannel();
 }
 
+#if OPENTHREAD_FTD
 uint16_t otChannelManagerGetDelay(otInstance *aInstance)
 {
     return AsCoreType(aInstance).Get<Utils::ChannelManager>().GetDelay();
@@ -66,19 +71,39 @@
 #if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
 otError otChannelManagerRequestChannelSelect(otInstance *aInstance, bool aSkipQualityCheck)
 {
-    return AsCoreType(aInstance).Get<Utils::ChannelManager>().RequestChannelSelect(aSkipQualityCheck);
+    return AsCoreType(aInstance).Get<Utils::ChannelManager>().RequestNetworkChannelSelect(aSkipQualityCheck);
 }
 #endif
 
 void otChannelManagerSetAutoChannelSelectionEnabled(otInstance *aInstance, bool aEnabled)
 {
-    AsCoreType(aInstance).Get<Utils::ChannelManager>().SetAutoChannelSelectionEnabled(aEnabled);
+    AsCoreType(aInstance).Get<Utils::ChannelManager>().SetAutoNetworkChannelSelectionEnabled(aEnabled);
 }
 
 bool otChannelManagerGetAutoChannelSelectionEnabled(otInstance *aInstance)
 {
-    return AsCoreType(aInstance).Get<Utils::ChannelManager>().GetAutoChannelSelectionEnabled();
+    return AsCoreType(aInstance).Get<Utils::ChannelManager>().GetAutoNetworkChannelSelectionEnabled();
 }
+#endif // OPENTHREAD_FTD
+
+#if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
+#if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
+otError otChannelManagerRequestCslChannelSelect(otInstance *aInstance, bool aSkipQualityCheck)
+{
+    return AsCoreType(aInstance).Get<Utils::ChannelManager>().RequestCslChannelSelect(aSkipQualityCheck);
+}
+#endif
+
+void otChannelManagerSetAutoCslChannelSelectionEnabled(otInstance *aInstance, bool aEnabled)
+{
+    AsCoreType(aInstance).Get<Utils::ChannelManager>().SetAutoCslChannelSelectionEnabled(aEnabled);
+}
+
+bool otChannelManagerGetAutoCslChannelSelectionEnabled(otInstance *aInstance)
+{
+    return AsCoreType(aInstance).Get<Utils::ChannelManager>().GetAutoCslChannelSelectionEnabled();
+}
+#endif
 
 otError otChannelManagerSetAutoChannelSelectionInterval(otInstance *aInstance, uint32_t aInterval)
 {
diff --git a/src/core/api/coap_secure_api.cpp b/src/core/api/coap_secure_api.cpp
index 86f76b4..e5ac4c0 100644
--- a/src/core/api/coap_secure_api.cpp
+++ b/src/core/api/coap_secure_api.cpp
@@ -50,6 +50,15 @@
     return AsCoreType(aInstance).GetApplicationCoapSecure().Start(aPort);
 }
 
+otError otCoapSecureStartWithMaxConnAttempts(otInstance                  *aInstance,
+                                             uint16_t                     aPort,
+                                             uint16_t                     aMaxAttempts,
+                                             otCoapSecureAutoStopCallback aCallback,
+                                             void                        *aContext)
+{
+    return AsCoreType(aInstance).GetApplicationCoapSecure().Start(aPort, aMaxAttempts, aCallback, aContext);
+}
+
 #ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
 void otCoapSecureSetCertificate(otInstance    *aInstance,
                                 const uint8_t *aX509Cert,
@@ -122,6 +131,8 @@
     return AsCoreType(aInstance).GetApplicationCoapSecure().IsConnectionActive();
 }
 
+bool otCoapSecureIsClosed(otInstance *aInstance) { return AsCoreType(aInstance).GetApplicationCoapSecure().IsClosed(); }
+
 void otCoapSecureStop(otInstance *aInstance) { AsCoreType(aInstance).GetApplicationCoapSecure().Stop(); }
 
 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
@@ -171,7 +182,7 @@
                                             otHandleCoapSecureClientConnect aHandler,
                                             void                           *aContext)
 {
-    AsCoreType(aInstance).GetApplicationCoapSecure().SetClientConnectedCallback(aHandler, aContext);
+    AsCoreType(aInstance).GetApplicationCoapSecure().SetConnectedCallback(aHandler, aContext);
 }
 
 void otCoapSecureSetDefaultHandler(otInstance *aInstance, otCoapRequestHandler aHandler, void *aContext)
diff --git a/src/core/api/message_api.cpp b/src/core/api/message_api.cpp
index 41dedc1..fe55b90 100644
--- a/src/core/api/message_api.cpp
+++ b/src/core/api/message_api.cpp
@@ -90,6 +90,11 @@
 
 int8_t otMessageGetRss(const otMessage *aMessage) { return AsCoreType(aMessage).GetAverageRss(); }
 
+otError otMessageGetThreadLinkInfo(const otMessage *aMessage, otThreadLinkInfo *aLinkInfo)
+{
+    return AsCoreType(aMessage).GetLinkInfo(AsCoreType(aLinkInfo));
+}
+
 otError otMessageAppend(otMessage *aMessage, const void *aBuf, uint16_t aLength)
 {
     AssertPointerIsNotNull(aBuf);
diff --git a/src/core/api/nat64_api.cpp b/src/core/api/nat64_api.cpp
index 02aa785..f2130e6 100644
--- a/src/core/api/nat64_api.cpp
+++ b/src/core/api/nat64_api.cpp
@@ -140,6 +140,16 @@
     AsCoreType(aIp4Address).ExtractFromIp6Address(aPrefixLength, AsCoreType(aIp6Address));
 }
 
+otError otIp4FromIp4MappedIp6Address(const otIp6Address *aIp6Address, otIp4Address *aIp4Address)
+{
+    return AsCoreType(aIp4Address).ExtractFromIp4MappedIp6Address(AsCoreType(aIp6Address));
+}
+
+void otIp4ToIp4MappedIp6Address(const otIp4Address *aIp4Address, otIp6Address *aIp6Address)
+{
+    AsCoreType(aIp6Address).SetToIp4Mapped(AsCoreType(aIp4Address));
+}
+
 otError otIp4AddressFromString(const char *aString, otIp4Address *aAddress)
 {
     AssertPointerIsNotNull(aString);
diff --git a/src/core/api/netdiag_api.cpp b/src/core/api/netdiag_api.cpp
index ffcc4c1..36c084d 100644
--- a/src/core/api/netdiag_api.cpp
+++ b/src/core/api/netdiag_api.cpp
@@ -89,6 +89,11 @@
     return AsCoreType(aInstance).Get<NetworkDiagnostic::Server>().GetVendorSwVersion();
 }
 
+const char *otThreadGetVendorAppUrl(otInstance *aInstance)
+{
+    return AsCoreType(aInstance).Get<NetworkDiagnostic::Server>().GetVendorAppUrl();
+}
+
 #if OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE
 otError otThreadSetVendorName(otInstance *aInstance, const char *aVendorName)
 {
@@ -104,4 +109,9 @@
 {
     return AsCoreType(aInstance).Get<NetworkDiagnostic::Server>().SetVendorSwVersion(aVendorSwVersion);
 }
+
+otError otThreadSetVendorAppUrl(otInstance *aInstance, const char *aVendorAppUrl)
+{
+    return AsCoreType(aInstance).Get<NetworkDiagnostic::Server>().SetVendorAppUrl(aVendorAppUrl);
+}
 #endif
diff --git a/src/core/api/thread_api.cpp b/src/core/api/thread_api.cpp
index 6bf2ca3..97e5508 100644
--- a/src/core/api/thread_api.cpp
+++ b/src/core/api/thread_api.cpp
@@ -275,15 +275,15 @@
 
 void otThreadSetKeySequenceCounter(otInstance *aInstance, uint32_t aKeySequenceCounter)
 {
-    AsCoreType(aInstance).Get<KeyManager>().SetCurrentKeySequence(aKeySequenceCounter);
+    AsCoreType(aInstance).Get<KeyManager>().SetCurrentKeySequence(aKeySequenceCounter, KeyManager::kForceUpdate);
 }
 
-uint32_t otThreadGetKeySwitchGuardTime(otInstance *aInstance)
+uint16_t otThreadGetKeySwitchGuardTime(otInstance *aInstance)
 {
     return AsCoreType(aInstance).Get<KeyManager>().GetKeySwitchGuardTime();
 }
 
-void otThreadSetKeySwitchGuardTime(otInstance *aInstance, uint32_t aKeySwitchGuardTime)
+void otThreadSetKeySwitchGuardTime(otInstance *aInstance, uint16_t aKeySwitchGuardTime)
 {
     AsCoreType(aInstance).Get<KeyManager>().SetKeySwitchGuardTime(aKeySwitchGuardTime);
 }
diff --git a/src/core/api/trel_api.cpp b/src/core/api/trel_api.cpp
index 687c116..09344d3 100644
--- a/src/core/api/trel_api.cpp
+++ b/src/core/api/trel_api.cpp
@@ -60,6 +60,11 @@
     return AsCoreType(aInstance).Get<Trel::Interface>().GetNextPeer(*aIterator);
 }
 
+uint16_t otTrelGetNumberOfPeers(otInstance *aInstance)
+{
+    return AsCoreType(aInstance).Get<Trel::Interface>().GetNumberOfPeers();
+}
+
 void otTrelSetFilterEnabled(otInstance *aInstance, bool aEnable)
 {
     AsCoreType(aInstance).Get<Trel::Interface>().SetFilterEnabled(aEnable);
@@ -70,4 +75,11 @@
     return AsCoreType(aInstance).Get<Trel::Interface>().IsFilterEnabled();
 }
 
+const otTrelCounters *otTrelGetCounters(otInstance *aInstance)
+{
+    return AsCoreType(aInstance).Get<Trel::Interface>().GetCounters();
+}
+
+void otTrelResetCounters(otInstance *aInstance) { AsCoreType(aInstance).Get<Trel::Interface>().ResetCounters(); }
+
 #endif // OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
diff --git a/src/core/backbone_router/bbr_manager.cpp b/src/core/backbone_router/bbr_manager.cpp
index 551cfa2..4fb15e4 100644
--- a/src/core/backbone_router/bbr_manager.cpp
+++ b/src/core/backbone_router/bbr_manager.cpp
@@ -40,6 +40,7 @@
 #include "common/locator_getters.hpp"
 #include "common/log.hpp"
 #include "common/num_utils.hpp"
+#include "common/numeric_limits.hpp"
 #include "common/random.hpp"
 #include "instance/instance.hpp"
 #include "thread/mle_types.hpp"
@@ -202,7 +203,7 @@
     }
     else
     {
-        VerifyOrExit(timeout < UINT32_MAX, status = ThreadStatusTlv::kMlrNoPersistent);
+        VerifyOrExit(timeout < NumericLimits<uint32_t>::kMax, status = ThreadStatusTlv::kMlrNoPersistent);
 
         if (timeout != 0)
         {
diff --git a/src/core/border_router/infra_if.cpp b/src/core/border_router/infra_if.cpp
index 07b5dc5..a152e62 100644
--- a/src/core/border_router/infra_if.cpp
+++ b/src/core/border_router/infra_if.cpp
@@ -152,6 +152,10 @@
 
     Get<RoutingManager>().HandleInfraIfStateChanged();
 
+#if OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE
+    Get<Srp::AdvertisingProxy>().HandleInfraIfStateChanged();
+#endif
+
 exit:
     return error;
 }
diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp
index e485368..bebd795 100644
--- a/src/core/border_router/routing_manager.cpp
+++ b/src/core/border_router/routing_manager.cpp
@@ -72,8 +72,7 @@
     , mIsEnabled(false)
     , mInfraIf(aInstance)
     , mOmrPrefixManager(aInstance)
-    , mRioPreference(NetworkData::kRoutePreferenceLow)
-    , mUserSetRioPreference(false)
+    , mRioAdvertiser(aInstance)
     , mOnLinkPrefixManager(aInstance)
     , mDiscoveredPrefixTable(aInstance)
     , mRoutePublisher(aInstance)
@@ -153,45 +152,6 @@
     return state;
 }
 
-void RoutingManager::SetRouteInfoOptionPreference(RoutePreference aPreference)
-{
-    LogInfo("User explicitly set RIO Preference to %s", RoutePreferenceToString(aPreference));
-    mUserSetRioPreference = true;
-    UpdateRioPreference(aPreference);
-}
-
-void RoutingManager::ClearRouteInfoOptionPreference(void)
-{
-    VerifyOrExit(mUserSetRioPreference);
-
-    LogInfo("User cleared explicitly set RIO Preference");
-    mUserSetRioPreference = false;
-    SetRioPreferenceBasedOnRole();
-
-exit:
-    return;
-}
-
-void RoutingManager::SetRioPreferenceBasedOnRole(void)
-{
-    UpdateRioPreference(Get<Mle::Mle>().IsRouterOrLeader() ? NetworkData::kRoutePreferenceMedium
-                                                           : NetworkData::kRoutePreferenceLow);
-}
-
-void RoutingManager::UpdateRioPreference(RoutePreference aPreference)
-{
-    VerifyOrExit(mRioPreference != aPreference);
-
-    LogInfo("RIO Preference changed: %s -> %s", RoutePreferenceToString(mRioPreference),
-            RoutePreferenceToString(aPreference));
-    mRioPreference = aPreference;
-
-    ScheduleRoutingPolicyEvaluation(kAfterRandomDelay);
-
-exit:
-    return;
-}
-
 Error RoutingManager::GetOmrPrefix(Ip6::Prefix &aPrefix) const
 {
     Error error = kErrorNone;
@@ -214,6 +174,17 @@
 exit:
     return error;
 }
+
+Error RoutingManager::GetPdProcessedRaInfo(PdProcessedRaInfo &aPdProcessedRaInfo)
+{
+    Error error = kErrorNone;
+
+    VerifyOrExit(IsInitialized(), error = kErrorInvalidState);
+    error = mPdPrefixManager.GetProcessedRaInfo(aPdProcessedRaInfo);
+
+exit:
+    return error;
+}
 #endif
 
 Error RoutingManager::GetFavoredOmrPrefix(Ip6::Prefix &aPrefix, RoutePreference &aPreference) const
@@ -366,12 +337,6 @@
 
     SendRouterAdvertisement(kInvalidateAllPrevPrefixes);
 
-#if OPENTHREAD_CONFIG_BORDER_ROUTING_USE_HEAP_ENABLE
-    mAdvertisedPrefixes.Free();
-#else
-    mAdvertisedPrefixes.Clear();
-#endif
-
     mDiscoveredPrefixTable.RemoveAllEntries();
     mDiscoveredPrefixStaleTimer.Stop();
 
@@ -398,6 +363,22 @@
     return;
 }
 
+Error RoutingManager::SetExtraRouterAdvertOptions(const uint8_t *aOptions, uint16_t aLength)
+{
+    Error error = kErrorNone;
+
+    if (aOptions == nullptr)
+    {
+        mExtraRaOptions.Free();
+    }
+    else
+    {
+        error = mExtraRaOptions.SetFrom(aOptions, aLength);
+    }
+
+    return error;
+}
+
 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
 void RoutingManager::HandleSrpServerAutoEnableMode(void)
 {
@@ -446,9 +427,9 @@
 
 void RoutingManager::HandleNotifierEvents(Events aEvents)
 {
-    if (aEvents.Contains(kEventThreadRoleChanged) && !mUserSetRioPreference)
+    if (aEvents.Contains(kEventThreadRoleChanged))
     {
-        SetRioPreferenceBasedOnRole();
+        mRioAdvertiser.HandleRoleChanged();
     }
 
     mRoutePublisher.HandleNotifierEvents(aEvents);
@@ -513,7 +494,10 @@
     mNat64PrefixManager.Evaluate();
 #endif
 
-    SendRouterAdvertisement(kAdvPrefixesFromNetData);
+    if (IsInitalPolicyEvaluationDone())
+    {
+        SendRouterAdvertisement(kAdvPrefixesFromNetData);
+    }
 
 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
     if (Get<Srp::Server>().IsAutoEnableMode() && IsInitalPolicyEvaluationDone())
@@ -603,36 +587,24 @@
 
 void RoutingManager::SendRouterAdvertisement(RouterAdvTxMode aRaTxMode)
 {
-    // RA message max length is derived to accommodate:
-    //
-    // - The RA header.
-    // - One RA Flags Extensions Option (with stub router flag).
-    // - One PIO for current local on-link prefix.
-    // - At most `kMaxOldPrefixes` for old deprecating on-link prefixes.
-    // - At most twice `kMaxOnMeshPrefixes` RIO for on-mesh prefixes.
-    //   Factor two is used for RIO to account for entries invalidating
-    //   previous prefixes while adding new ones.
-
-    static constexpr uint16_t kMaxRaLength =
-        sizeof(Ip6::Nd::RouterAdvertMessage::Header) + sizeof(Ip6::Nd::RaFlagsExtOption) +
-        sizeof(Ip6::Nd::PrefixInfoOption) + sizeof(Ip6::Nd::PrefixInfoOption) * OnLinkPrefixManager::kMaxOldPrefixes +
-        2 * kMaxOnMeshPrefixes * (sizeof(Ip6::Nd::RouteInfoOption) + sizeof(Ip6::Prefix));
-
-    uint8_t                         buffer[kMaxRaLength];
-    Ip6::Nd::RouterAdvertMessage    raMsg(mRaInfo.mHeader, buffer);
-    NetworkData::Iterator           iterator;
-    NetworkData::OnMeshPrefixConfig prefixConfig;
+    Error                   error = kErrorNone;
+    RouterAdvert::TxMessage raMsg;
+    RouterAdvert::Header    header;
+    Ip6::Address            destAddress;
+    InfraIf::Icmp6Packet    packet;
 
     LogInfo("Preparing RA");
 
-    mDiscoveredPrefixTable.DetermineAndSetFlags(raMsg);
+    header = mRaInfo.mHeader;
+    mDiscoveredPrefixTable.DetermineAndSetFlags(header);
 
-    LogInfo("- RA Header - flags - M:%u O:%u", raMsg.GetHeader().IsManagedAddressConfigFlagSet(),
-            raMsg.GetHeader().IsOtherConfigFlagSet());
-    LogInfo("- RA Header - default route - lifetime:%u", raMsg.GetHeader().GetRouterLifetime());
+    SuccessOrExit(error = raMsg.AppendHeader(header));
+
+    LogInfo("- RA Header - flags - M:%u O:%u", header.IsManagedAddressConfigFlagSet(), header.IsOtherConfigFlagSet());
+    LogInfo("- RA Header - default route - lifetime:%u", header.GetRouterLifetime());
 
 #if OPENTHREAD_CONFIG_BORDER_ROUTING_STUB_ROUTER_FLAG_IN_EMITTED_RA_ENABLE
-    SuccessOrAssert(raMsg.AppendFlagsExtensionOption(/* aStubRouterFlag */ true));
+    SuccessOrExit(error = raMsg.AppendFlagsExtensionOption(/* aStubRouterFlag */ true));
     LogInfo("- FlagsExt - StubRouter:1");
 #endif
 
@@ -640,215 +612,42 @@
     // advertised or deprecated and for old prefix if is being
     // deprecated.
 
-    mOnLinkPrefixManager.AppendAsPiosTo(raMsg);
+    SuccessOrExit(error = mOnLinkPrefixManager.AppendAsPiosTo(raMsg));
 
-    // Determine which previously advertised prefixes need to be
-    // invalidated. Under `kInvalidateAllPrevPrefixes` mode we need
-    // to invalidate all. Under `kAdvPrefixesFromNetData` mode, we
-    // check Network Data entries and invalidate any previously
-    // advertised prefix that is no longer present in the Network
-    // Data. We go through all Network Data prefixes and mark the
-    // ones we find in `mAdvertisedPrefixes` as deleted by setting
-    // the prefix length to zero). By the end, the remaining entries
-    // in the array with a non-zero prefix length are invalidated.
-
-    if (aRaTxMode != kInvalidateAllPrevPrefixes)
+    if (aRaTxMode == kInvalidateAllPrevPrefixes)
     {
-        iterator = NetworkData::kIteratorInit;
-
-        while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, prefixConfig) == kErrorNone)
-        {
-            if (!prefixConfig.mOnMesh || prefixConfig.mDp ||
-                (prefixConfig.GetPrefix() == mOmrPrefixManager.GetLocalPrefix().GetPrefix()))
-            {
-                continue;
-            }
-
-            mAdvertisedPrefixes.MarkAsDeleted(prefixConfig.GetPrefix());
-        }
-
-        if (mOmrPrefixManager.IsLocalAddedInNetData())
-        {
-            mAdvertisedPrefixes.MarkAsDeleted(mOmrPrefixManager.GetLocalPrefix().GetPrefix());
-        }
+        SuccessOrExit(error = mRioAdvertiser.InvalidatPrevRios(raMsg));
+    }
+    else
+    {
+        SuccessOrExit(error = mRioAdvertiser.AppendRios(raMsg));
     }
 
-    for (const OnMeshPrefix &prefix : mAdvertisedPrefixes)
+    if (mExtraRaOptions.GetLength() > 0)
     {
-        if (prefix.GetLength() != 0)
-        {
-            SuccessOrAssert(raMsg.AppendRouteInfoOption(prefix, /* aRouteLifetime */ 0, mRioPreference));
-            LogRouteInfoOption(prefix, 0, mRioPreference);
-        }
+        SuccessOrExit(error = raMsg.AppendBytes(mExtraRaOptions.GetBytes(), mExtraRaOptions.GetLength()));
     }
 
-    // Discover and add prefixes from Network Data to advertise as
-    // RIO in the Router Advertisement message.
+    VerifyOrExit(raMsg.ContainsAnyOptions());
 
-    mAdvertisedPrefixes.Clear();
+    destAddress.SetToLinkLocalAllNodesMulticast();
+    raMsg.GetAsPacket(packet);
 
-    if (aRaTxMode == kAdvPrefixesFromNetData)
-    {
-        // `mAdvertisedPrefixes` array has a limited size. We add more
-        // important prefixes first in the array to ensure they are
-        // advertised in the RA message. Note that `Add()` method
-        // will ensure to add a prefix only once (will check if
-        // prefix is already present in the array).
+    mRaInfo.IncrementTxCountAndSaveHash(packet);
 
-        // (1) Local OMR prefix.
+    SuccessOrExit(error = mInfraIf.Send(packet, destAddress));
 
-        if (mOmrPrefixManager.IsLocalAddedInNetData())
-        {
-            mAdvertisedPrefixes.Add(mOmrPrefixManager.GetLocalPrefix().GetPrefix());
-        }
-
-        // (2) Favored OMR prefix.
-
-        if (!mOmrPrefixManager.GetFavoredPrefix().IsEmpty() && !mOmrPrefixManager.GetFavoredPrefix().IsDomainPrefix())
-        {
-            mAdvertisedPrefixes.Add(mOmrPrefixManager.GetFavoredPrefix().GetPrefix());
-        }
-
-        // (3) All other OMR prefixes.
-
-        iterator = NetworkData::kIteratorInit;
-
-        while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, prefixConfig) == kErrorNone)
-        {
-            // Local OMR prefix is added to the array depending on
-            // `mOmrPrefixManager.IsLocalAddedInNetData()` at step (1).
-            // As we iterate through the Network Data prefixes, we skip
-            // over entries matching the local OMR prefix. This
-            // ensures that we stop including it in emitted RA
-            // message as soon as we decide to remove it from Network
-            // Data. Note that upon requesting it to be removed from
-            // Network Data the change needs to be registered with
-            // leader and can take some time to be updated in Network
-            // Data.
-
-            if (prefixConfig.mDp)
-            {
-                continue;
-            }
-
-            if (IsValidOmrPrefix(prefixConfig) &&
-                (prefixConfig.GetPrefix() != mOmrPrefixManager.GetLocalPrefix().GetPrefix()))
-            {
-                mAdvertisedPrefixes.Add(prefixConfig.GetPrefix());
-            }
-        }
-
-        // (4) All other on-mesh prefixes (excluding Domain Prefix).
-
-        iterator = NetworkData::kIteratorInit;
-
-        while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, prefixConfig) == kErrorNone)
-        {
-            if (prefixConfig.mOnMesh && !prefixConfig.mDp && !IsValidOmrPrefix(prefixConfig))
-            {
-                mAdvertisedPrefixes.Add(prefixConfig.GetPrefix());
-            }
-        }
-
-        for (const OnMeshPrefix &prefix : mAdvertisedPrefixes)
-        {
-            SuccessOrAssert(raMsg.AppendRouteInfoOption(prefix, kDefaultOmrPrefixLifetime, mRioPreference));
-            LogRouteInfoOption(prefix, kDefaultOmrPrefixLifetime, mRioPreference);
-        }
-    }
-
-    if (raMsg.ContainsAnyOptions())
-    {
-        Error        error;
-        Ip6::Address destAddress;
-
-        ++mRaInfo.mTxCount;
-
-        destAddress.SetToLinkLocalAllNodesMulticast();
-
-        error = mInfraIf.Send(raMsg.GetAsPacket(), destAddress);
-
-        if (error == kErrorNone)
-        {
-            mRaInfo.mLastTxTime = TimerMilli::GetNow();
-            Get<Ip6::Ip6>().GetBorderRoutingCounters().mRaTxSuccess++;
-            LogInfo("Sent RA on %s", mInfraIf.ToString().AsCString());
-            DumpDebg("[BR-CERT] direction=send | type=RA |", raMsg.GetAsPacket().GetBytes(),
-                     raMsg.GetAsPacket().GetLength());
-        }
-        else
-        {
-            Get<Ip6::Ip6>().GetBorderRoutingCounters().mRaTxFailure++;
-            LogWarn("Failed to send RA on %s: %s", mInfraIf.ToString().AsCString(), ErrorToString(error));
-        }
-    }
-}
-
-bool RoutingManager::IsReceivedRouterAdvertFromManager(const Ip6::Nd::RouterAdvertMessage &aRaMessage) const
-{
-    // Determines whether or not a received RA message was prepared by
-    // by `RoutingManager` itself.
-
-    bool        isFromManager = false;
-    uint16_t    rioCount      = 0;
-    Ip6::Prefix prefix;
-
-    VerifyOrExit(aRaMessage.ContainsAnyOptions());
-
-    for (const Ip6::Nd::Option &option : aRaMessage)
-    {
-        switch (option.GetType())
-        {
-        case Ip6::Nd::Option::kTypePrefixInfo:
-        {
-            const Ip6::Nd::PrefixInfoOption &pio = static_cast<const Ip6::Nd::PrefixInfoOption &>(option);
-
-            VerifyOrExit(pio.IsValid());
-            pio.GetPrefix(prefix);
-
-            // If it is a non-deprecated PIO, it should match the
-            // local on-link prefix.
-
-            if (pio.GetPreferredLifetime() > 0)
-            {
-                VerifyOrExit(prefix == mOnLinkPrefixManager.GetLocalPrefix());
-            }
-
-            break;
-        }
-
-        case Ip6::Nd::Option::kTypeRouteInfo:
-        {
-            // RIO (with non-zero lifetime) should match entries from
-            // `mAdvertisedPrefixes`. We keep track of the number
-            // of matched RIOs and check after the loop ends that all
-            // entries were seen.
-
-            const Ip6::Nd::RouteInfoOption &rio = static_cast<const Ip6::Nd::RouteInfoOption &>(option);
-
-            VerifyOrExit(rio.IsValid());
-            rio.GetPrefix(prefix);
-
-            if (rio.GetRouteLifetime() != 0)
-            {
-                VerifyOrExit(mAdvertisedPrefixes.Contains(prefix));
-                rioCount++;
-            }
-
-            break;
-        }
-
-        default:
-            ExitNow();
-        }
-    }
-
-    VerifyOrExit(rioCount == mAdvertisedPrefixes.GetLength());
-
-    isFromManager = true;
+    mRaInfo.mLastTxTime = TimerMilli::GetNow();
+    Get<Ip6::Ip6>().GetBorderRoutingCounters().mRaTxSuccess++;
+    LogInfo("Sent RA on %s", mInfraIf.ToString().AsCString());
+    DumpDebg("[BR-CERT] direction=send | type=RA |", packet.GetBytes(), packet.GetLength());
 
 exit:
-    return isFromManager;
+    if (error != kErrorNone)
+    {
+        Get<Ip6::Ip6>().GetBorderRoutingCounters().mRaTxFailure++;
+        LogWarn("Failed to send RA on %s: %s", mInfraIf.ToString().AsCString(), ErrorToString(error));
+    }
 }
 
 bool RoutingManager::IsValidBrUlaPrefix(const Ip6::Prefix &aBrUlaPrefix)
@@ -868,7 +667,7 @@
     return (aPrefix.GetLength() == kOmrPrefixLength) && !aPrefix.IsLinkLocal() && !aPrefix.IsMulticast();
 }
 
-bool RoutingManager::IsValidOnLinkPrefix(const Ip6::Nd::PrefixInfoOption &aPio)
+bool RoutingManager::IsValidOnLinkPrefix(const PrefixInfoOption &aPio)
 {
     Ip6::Prefix prefix;
 
@@ -923,10 +722,10 @@
 
 void RoutingManager::HandleNeighborAdvertisement(const InfraIf::Icmp6Packet &aPacket)
 {
-    const Ip6::Nd::NeighborAdvertMessage *naMsg;
+    const NeighborAdvertMessage *naMsg;
 
     VerifyOrExit(aPacket.GetLength() >= sizeof(naMsg));
-    naMsg = reinterpret_cast<const Ip6::Nd::NeighborAdvertMessage *>(aPacket.GetBytes());
+    naMsg = reinterpret_cast<const NeighborAdvertMessage *>(aPacket.GetBytes());
 
     mDiscoveredPrefixTable.ProcessNeighborAdvertMessage(*naMsg);
 
@@ -936,7 +735,7 @@
 
 void RoutingManager::HandleRouterAdvertisement(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress)
 {
-    Ip6::Nd::RouterAdvertMessage routerAdvMessage(aPacket);
+    RouterAdvert::RxMessage routerAdvMessage(aPacket);
 
     OT_ASSERT(mIsRunning);
 
@@ -960,7 +759,7 @@
     return;
 }
 
-bool RoutingManager::ShouldProcessPrefixInfoOption(const Ip6::Nd::PrefixInfoOption &aPio, const Ip6::Prefix &aPrefix)
+bool RoutingManager::ShouldProcessPrefixInfoOption(const PrefixInfoOption &aPio, const Ip6::Prefix &aPrefix)
 {
     // Indicate whether to process or skip a given prefix
     // from a PIO (from received RA message).
@@ -986,7 +785,7 @@
     return shouldProcess;
 }
 
-bool RoutingManager::ShouldProcessRouteInfoOption(const Ip6::Nd::RouteInfoOption &aRio, const Ip6::Prefix &aPrefix)
+bool RoutingManager::ShouldProcessRouteInfoOption(const RouteInfoOption &aRio, const Ip6::Prefix &aPrefix)
 {
     // Indicate whether to process or skip a given prefix
     // from a RIO (from received RA message).
@@ -1012,16 +811,16 @@
     VerifyOrExit(mOmrPrefixManager.GetLocalPrefix().GetPrefix() != aPrefix);
 
     // Ignore OMR prefixes advertised by ourselves or in current Thread Network Data.
-    // The `mAdvertisedPrefixes` and the OMR prefix set in Network Data should eventually
+    // The `RioAdvertiser` prefixes and the OMR prefix set in Network Data should eventually
     // be equal, but there is time that they are not synchronized immediately:
-    // 1. Network Data could contain more OMR prefixes than `mAdvertisedPrefixes` because
+    // 1. Network Data could contain more OMR prefixes than `RioAdvertiser` because
     //    we added random delay before Evaluating routing policy when Network Data is changed.
-    // 2. `mAdvertisedPrefixes` could contain more OMR prefixes than Network Data because
+    // 2. `RioAdvertiser` prefixes could contain more OMR prefixes than Network Data because
     //    it takes time to sync a new OMR prefix into Network Data (multicast loopback RA
     //    messages are usually faster than Thread Network Data propagation).
     // They are the reasons why we need both the checks.
 
-    VerifyOrExit(!mAdvertisedPrefixes.Contains(aPrefix));
+    VerifyOrExit(!mRioAdvertiser.HasAdvertised(aPrefix));
     VerifyOrExit(!Get<RoutingManager>().NetworkDataContainsOmrPrefix(aPrefix));
 
     shouldProcess = true;
@@ -1085,18 +884,18 @@
     return contains;
 }
 
-void RoutingManager::UpdateRouterAdvertHeader(const Ip6::Nd::RouterAdvertMessage *aRouterAdvertMessage)
+void RoutingManager::UpdateRouterAdvertHeader(const RouterAdvert::RxMessage *aRouterAdvertMessage)
 {
     // Updates the `mRaInfo` from the given RA message.
 
-    Ip6::Nd::RouterAdvertMessage::Header oldHeader;
+    RouterAdvert::Header oldHeader;
 
     if (aRouterAdvertMessage != nullptr)
     {
         // We skip and do not update RA header if the received RA message
         // was not prepared and sent by `RoutingManager` itself.
 
-        VerifyOrExit(!IsReceivedRouterAdvertFromManager(*aRouterAdvertMessage));
+        VerifyOrExit(!mRaInfo.IsRaFromManager(*aRouterAdvertMessage));
     }
 
     oldHeader                 = mRaInfo.mHeader;
@@ -1199,8 +998,8 @@
 {
 }
 
-void RoutingManager::DiscoveredPrefixTable::ProcessRouterAdvertMessage(const Ip6::Nd::RouterAdvertMessage &aRaMessage,
-                                                                       const Ip6::Address                 &aSrcAddress)
+void RoutingManager::DiscoveredPrefixTable::ProcessRouterAdvertMessage(const RouterAdvert::RxMessage &aRaMessage,
+                                                                       const Ip6::Address            &aSrcAddress)
 {
     // Process a received RA message and update the prefix table.
 
@@ -1230,20 +1029,20 @@
 
     ProcessRaHeader(aRaMessage.GetHeader(), *router);
 
-    for (const Ip6::Nd::Option &option : aRaMessage)
+    for (const Option &option : aRaMessage)
     {
         switch (option.GetType())
         {
-        case Ip6::Nd::Option::kTypePrefixInfo:
-            ProcessPrefixInfoOption(static_cast<const Ip6::Nd::PrefixInfoOption &>(option), *router);
+        case Option::kTypePrefixInfo:
+            ProcessPrefixInfoOption(static_cast<const PrefixInfoOption &>(option), *router);
             break;
 
-        case Ip6::Nd::Option::kTypeRouteInfo:
-            ProcessRouteInfoOption(static_cast<const Ip6::Nd::RouteInfoOption &>(option), *router);
+        case Option::kTypeRouteInfo:
+            ProcessRouteInfoOption(static_cast<const RouteInfoOption &>(option), *router);
             break;
 
-        case Ip6::Nd::Option::kTypeRaFlagsExtension:
-            ProcessRaFlagsExtOption(static_cast<const Ip6::Nd::RaFlagsExtOption &>(option), *router);
+        case Option::kTypeRaFlagsExtension:
+            ProcessRaFlagsExtOption(static_cast<const RaFlagsExtOption &>(option), *router);
             break;
 
         default:
@@ -1259,8 +1058,7 @@
     return;
 }
 
-void RoutingManager::DiscoveredPrefixTable::ProcessRaHeader(const Ip6::Nd::RouterAdvertMessage::Header &aRaHeader,
-                                                            Router                                     &aRouter)
+void RoutingManager::DiscoveredPrefixTable::ProcessRaHeader(const RouterAdvert::Header &aRaHeader, Router &aRouter)
 {
     Entry      *entry;
     Ip6::Prefix prefix;
@@ -1302,8 +1100,7 @@
     return;
 }
 
-void RoutingManager::DiscoveredPrefixTable::ProcessPrefixInfoOption(const Ip6::Nd::PrefixInfoOption &aPio,
-                                                                    Router                          &aRouter)
+void RoutingManager::DiscoveredPrefixTable::ProcessPrefixInfoOption(const PrefixInfoOption &aPio, Router &aRouter)
 {
     Ip6::Prefix prefix;
     Entry      *entry;
@@ -1348,8 +1145,7 @@
     return;
 }
 
-void RoutingManager::DiscoveredPrefixTable::ProcessRouteInfoOption(const Ip6::Nd::RouteInfoOption &aRio,
-                                                                   Router                         &aRouter)
+void RoutingManager::DiscoveredPrefixTable::ProcessRouteInfoOption(const RouteInfoOption &aRio, Router &aRouter)
 {
     Ip6::Prefix prefix;
     Entry      *entry;
@@ -1391,8 +1187,8 @@
     return;
 }
 
-void RoutingManager::DiscoveredPrefixTable::ProcessRaFlagsExtOption(const Ip6::Nd::RaFlagsExtOption &aRaFlagsOption,
-                                                                    Router                          &aRouter)
+void RoutingManager::DiscoveredPrefixTable::ProcessRaFlagsExtOption(const RaFlagsExtOption &aRaFlagsOption,
+                                                                    Router                 &aRouter)
 {
     VerifyOrExit(aRaFlagsOption.IsValid());
     aRouter.mStubRouterFlag = aRaFlagsOption.IsStubRouterFlagSet();
@@ -1702,8 +1498,7 @@
 
 void RoutingManager::DiscoveredPrefixTable::SignalTableChanged(void) { mSignalTask.Post(); }
 
-void RoutingManager::DiscoveredPrefixTable::ProcessNeighborAdvertMessage(
-    const Ip6::Nd::NeighborAdvertMessage &aNaMessage)
+void RoutingManager::DiscoveredPrefixTable::ProcessNeighborAdvertMessage(const NeighborAdvertMessage &aNaMessage)
 {
     Router *router;
 
@@ -1782,8 +1577,8 @@
 
 void RoutingManager::DiscoveredPrefixTable::SendNeighborSolicitToRouter(const Router &aRouter)
 {
-    InfraIf::Icmp6Packet            packet;
-    Ip6::Nd::NeighborSolicitMessage neighborSolicitMsg;
+    InfraIf::Icmp6Packet   packet;
+    NeighborSolicitMessage neighborSolicitMsg;
 
     VerifyOrExit(!Get<RoutingManager>().mRsSender.IsInProgress());
 
@@ -1799,10 +1594,10 @@
     return;
 }
 
-void RoutingManager::DiscoveredPrefixTable::DetermineAndSetFlags(Ip6::Nd::RouterAdvertMessage &aRaMessage) const
+void RoutingManager::DiscoveredPrefixTable::DetermineAndSetFlags(RouterAdvert::Header &aHeader) const
 {
     // Determine the `M` and `O` flags to include in the RA message
-    // header `aRaMessage` to be emitted.
+    // header to be emitted.
     //
     // If any discovered router on infrastructure which is not itself a
     // stub router (e.g., another Thread BR) includes the `M` or `O`
@@ -1825,12 +1620,12 @@
 
         if (router.mManagedAddressConfigFlag)
         {
-            aRaMessage.GetHeader().SetManagedAddressConfigFlag();
+            aHeader.SetManagedAddressConfigFlag();
         }
 
         if (router.mOtherConfigFlag)
         {
-            aRaMessage.GetHeader().SetOtherConfigFlag();
+            aHeader.SetOtherConfigFlag();
         }
     }
 }
@@ -1917,7 +1712,7 @@
 //---------------------------------------------------------------------------------------------------------------------
 // DiscoveredPrefixTable::Entry
 
-void RoutingManager::DiscoveredPrefixTable::Entry::SetFrom(const Ip6::Nd::RouterAdvertMessage::Header &aRaHeader)
+void RoutingManager::DiscoveredPrefixTable::Entry::SetFrom(const RouterAdvert::Header &aRaHeader)
 {
     mPrefix.Clear();
     mType                    = kTypeRoute;
@@ -1926,7 +1721,7 @@
     mLastUpdateTime          = TimerMilli::GetNow();
 }
 
-void RoutingManager::DiscoveredPrefixTable::Entry::SetFrom(const Ip6::Nd::PrefixInfoOption &aPio)
+void RoutingManager::DiscoveredPrefixTable::Entry::SetFrom(const PrefixInfoOption &aPio)
 {
     aPio.GetPrefix(mPrefix);
     mType                      = kTypeOnLink;
@@ -1935,7 +1730,7 @@
     mLastUpdateTime            = TimerMilli::GetNow();
 }
 
-void RoutingManager::DiscoveredPrefixTable::Entry::SetFrom(const Ip6::Nd::RouteInfoOption &aRio)
+void RoutingManager::DiscoveredPrefixTable::Entry::SetFrom(const RouteInfoOption &aRio)
 {
     aRio.GetPrefix(mPrefix);
     mType                    = kTypeRoute;
@@ -1980,7 +1775,7 @@
 {
     OT_ASSERT(IsOnLinkPrefix());
 
-    return mLastUpdateTime + TimeMilli::SecToMsec(GetPreferredLifetime()) <= TimerMilli::GetNow();
+    return mLastUpdateTime + CalculateExpireDelay(GetPreferredLifetime()) <= TimerMilli::GetNow();
 }
 
 RoutingManager::RoutePreference RoutingManager::DiscoveredPrefixTable::Entry::GetPreference(void) const
@@ -2227,6 +2022,45 @@
     return;
 }
 
+bool RoutingManager::OmrPrefixManager::ShouldAdvertiseLocalAsRio(void) const
+{
+    // Determines whether the local OMR prefix should be advertised as
+    // RIO in emitted RAs. To advertise, we must have decided to
+    // publish it, and it must already be added and present in the
+    // Network Data. This ensures that we only advertise the local
+    // OMR prefix in emitted RAs when, as a Border Router, we can
+    // accept and route messages using an OMR-based address
+    // destination, which requires the prefix to be present in
+    // Network Data. Similarly, we stop advertising (and start
+    // deprecating) the OMR prefix in RAs as soon as we decide to
+    // remove it. After requesting its removal from Network Data, it
+    // may still be present in Network Data for a short interval due
+    // to delays in registering changes with the leader.
+
+    bool                            shouldAdvertise = false;
+    NetworkData::Iterator           iterator        = NetworkData::kIteratorInit;
+    NetworkData::OnMeshPrefixConfig prefixConfig;
+
+    VerifyOrExit(mIsLocalAddedInNetData);
+
+    while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, prefixConfig) == kErrorNone)
+    {
+        if (!IsValidOmrPrefix(prefixConfig))
+        {
+            continue;
+        }
+
+        if (prefixConfig.GetPrefix() == mLocalPrefix.GetPrefix())
+        {
+            shouldAdvertise = true;
+            break;
+        }
+    }
+
+exit:
+    return shouldAdvertise;
+}
+
 Error RoutingManager::OmrPrefixManager::AddLocalToNetData(void)
 {
     Error error = kErrorNone;
@@ -2628,13 +2462,18 @@
     return (GetState() == kPublishing) || (GetState() == kAdvertising);
 }
 
-void RoutingManager::OnLinkPrefixManager::AppendAsPiosTo(Ip6::Nd::RouterAdvertMessage &aRaMessage)
+Error RoutingManager::OnLinkPrefixManager::AppendAsPiosTo(RouterAdvert::TxMessage &aRaMessage)
 {
-    AppendCurPrefix(aRaMessage);
-    AppendOldPrefixes(aRaMessage);
+    Error error;
+
+    SuccessOrExit(error = AppendCurPrefix(aRaMessage));
+    error = AppendOldPrefixes(aRaMessage);
+
+exit:
+    return error;
 }
 
-void RoutingManager::OnLinkPrefixManager::AppendCurPrefix(Ip6::Nd::RouterAdvertMessage &aRaMessage)
+Error RoutingManager::OnLinkPrefixManager::AppendCurPrefix(RouterAdvert::TxMessage &aRaMessage)
 {
     // Append the local on-link prefix to the `aRaMessage` as a PIO
     // only if it is being advertised or deprecated.
@@ -2643,6 +2482,7 @@
     // If in `kDeprecating` state, we include it as PIO with zero
     // preferred lifetime and the remaining valid lifetime.
 
+    Error     error             = kErrorNone;
     uint32_t  validLifetime     = kDefaultOnLinkPrefixLifetime;
     uint32_t  preferredLifetime = kDefaultOnLinkPrefixLifetime;
     TimeMilli now               = TimerMilli::GetNow();
@@ -2664,17 +2504,18 @@
         ExitNow();
     }
 
-    SuccessOrAssert(aRaMessage.AppendPrefixInfoOption(mLocalPrefix, validLifetime, preferredLifetime));
+    SuccessOrExit(error = aRaMessage.AppendPrefixInfoOption(mLocalPrefix, validLifetime, preferredLifetime));
 
     LogPrefixInfoOption(mLocalPrefix, validLifetime, preferredLifetime);
 
 exit:
-    return;
+    return error;
 }
 
-void RoutingManager::OnLinkPrefixManager::AppendOldPrefixes(Ip6::Nd::RouterAdvertMessage &aRaMessage)
+Error RoutingManager::OnLinkPrefixManager::AppendOldPrefixes(RouterAdvert::TxMessage &aRaMessage)
 {
-    TimeMilli now = TimerMilli::GetNow();
+    Error     error = kErrorNone;
+    TimeMilli now   = TimerMilli::GetNow();
     uint32_t  validLifetime;
 
     for (const OldPrefix &oldPrefix : mOldLocalPrefixes)
@@ -2685,10 +2526,13 @@
         }
 
         validLifetime = TimeMilli::MsecToSec(oldPrefix.mExpireTime - now);
-        SuccessOrAssert(aRaMessage.AppendPrefixInfoOption(oldPrefix.mPrefix, validLifetime, 0));
+        SuccessOrExit(error = aRaMessage.AppendPrefixInfoOption(oldPrefix.mPrefix, validLifetime, 0));
 
         LogPrefixInfoOption(oldPrefix.mPrefix, validLifetime, 0);
     }
+
+exit:
+    return error;
 }
 
 void RoutingManager::OnLinkPrefixManager::HandleNetDataChange(void)
@@ -2867,18 +2711,247 @@
 }
 
 //---------------------------------------------------------------------------------------------------------------------
-// OnMeshPrefixArray
+// RioAdvertiser
 
-void RoutingManager::OnMeshPrefixArray::Add(const OnMeshPrefix &aPrefix)
+RoutingManager::RioAdvertiser::RioAdvertiser(Instance &aInstance)
+    : InstanceLocator(aInstance)
+    , mTimer(aInstance)
+    , mPreference(NetworkData::kRoutePreferenceLow)
+    , mUserSetPreference(false)
 {
-    // Checks if `aPrefix` is already present in the array and if not
-    // adds it as new entry.
+}
 
+void RoutingManager::RioAdvertiser::SetPreference(RoutePreference aPreference)
+{
+    LogInfo("User explicitly set RIO Preference to %s", RoutePreferenceToString(aPreference));
+    mUserSetPreference = true;
+    UpdatePreference(aPreference);
+}
+
+void RoutingManager::RioAdvertiser::ClearPreference(void)
+{
+    VerifyOrExit(mUserSetPreference);
+
+    LogInfo("User cleared explicitly set RIO Preference");
+    mUserSetPreference = false;
+    SetPreferenceBasedOnRole();
+
+exit:
+    return;
+}
+
+void RoutingManager::RioAdvertiser::HandleRoleChanged(void)
+{
+    if (!mUserSetPreference)
+    {
+        SetPreferenceBasedOnRole();
+    }
+}
+
+void RoutingManager::RioAdvertiser::SetPreferenceBasedOnRole(void)
+{
+    UpdatePreference(Get<Mle::Mle>().IsRouterOrLeader() ? NetworkData::kRoutePreferenceMedium
+                                                        : NetworkData::kRoutePreferenceLow);
+}
+
+void RoutingManager::RioAdvertiser::UpdatePreference(RoutePreference aPreference)
+{
+    VerifyOrExit(mPreference != aPreference);
+
+    LogInfo("RIO Preference changed: %s -> %s", RoutePreferenceToString(mPreference),
+            RoutePreferenceToString(aPreference));
+    mPreference = aPreference;
+
+    Get<RoutingManager>().ScheduleRoutingPolicyEvaluation(kAfterRandomDelay);
+
+exit:
+    return;
+}
+
+Error RoutingManager::RioAdvertiser::InvalidatPrevRios(RouterAdvert::TxMessage &aRaMessage)
+{
+    Error error = kErrorNone;
+
+    for (const RioPrefix &prefix : mPrefixes)
+    {
+        SuccessOrExit(error = AppendRio(prefix.mPrefix, /* aRouteLifetime */ 0, aRaMessage));
+    }
+
+#if OPENTHREAD_CONFIG_BORDER_ROUTING_USE_HEAP_ENABLE
+    mPrefixes.Free();
+#endif
+
+    mPrefixes.Clear();
+    mTimer.Stop();
+
+exit:
+    return error;
+}
+
+Error RoutingManager::RioAdvertiser::AppendRios(RouterAdvert::TxMessage &aRaMessage)
+{
+    Error                           error    = kErrorNone;
+    TimeMilli                       now      = TimerMilli::GetNow();
+    TimeMilli                       nextTime = now.GetDistantFuture();
+    RioPrefixArray                  oldPrefixes;
+    NetworkData::Iterator           iterator = NetworkData::kIteratorInit;
+    NetworkData::OnMeshPrefixConfig prefixConfig;
+    const OmrPrefixManager         &omrPrefixManager = Get<RoutingManager>().mOmrPrefixManager;
+
+#if OPENTHREAD_CONFIG_BORDER_ROUTING_USE_HEAP_ENABLE
+    oldPrefixes.TakeFrom(static_cast<RioPrefixArray &&>(mPrefixes));
+#else
+    oldPrefixes = mPrefixes;
+#endif
+
+    mPrefixes.Clear();
+
+    // `mPrefixes` array can have a limited size. We add more
+    // important prefixes first in the array to ensure they are
+    // advertised in the RA message. Note that `Add()` method
+    // will ensure to add a prefix only once (will check if
+    // prefix is already present in the array).
+
+    // (1) Local OMR prefix.
+
+    if (omrPrefixManager.ShouldAdvertiseLocalAsRio())
+    {
+        mPrefixes.Add(omrPrefixManager.GetLocalPrefix().GetPrefix());
+    }
+
+    // (2) Favored OMR prefix.
+
+    if (!omrPrefixManager.GetFavoredPrefix().IsEmpty() && !omrPrefixManager.GetFavoredPrefix().IsDomainPrefix())
+    {
+        mPrefixes.Add(omrPrefixManager.GetFavoredPrefix().GetPrefix());
+    }
+
+    // (3) All other OMR prefixes.
+
+    iterator = NetworkData::kIteratorInit;
+
+    while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, prefixConfig) == kErrorNone)
+    {
+        // Decision on whether or not to include the local OMR prefix is
+        // delegated to `OmrPrefixManager.ShouldAdvertiseLocalAsRio()`
+        // at step (1). Here as we iterate over the Network Data
+        // prefixes, we skip entries matching the local OMR prefix.
+        // In particular, `OmrPrefixManager` may have decided to remove the
+        // local prefix and not advertise it anymore, but it may still be
+        // present in the Network Data (due to delay of registering changes
+        // with leader).
+
+        if (prefixConfig.mDp)
+        {
+            continue;
+        }
+
+        if (IsValidOmrPrefix(prefixConfig) &&
+            (prefixConfig.GetPrefix() != omrPrefixManager.GetLocalPrefix().GetPrefix()))
+        {
+            mPrefixes.Add(prefixConfig.GetPrefix());
+        }
+    }
+
+    // (4) All other on-mesh prefixes (excluding Domain Prefix).
+
+    iterator = NetworkData::kIteratorInit;
+
+    while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, prefixConfig) == kErrorNone)
+    {
+        if (prefixConfig.mOnMesh && !prefixConfig.mDp && !IsValidOmrPrefix(prefixConfig))
+        {
+            mPrefixes.Add(prefixConfig.GetPrefix());
+        }
+    }
+
+    // Determine deprecating prefixes
+
+    for (RioPrefix &prefix : oldPrefixes)
+    {
+        if (mPrefixes.ContainsMatching(prefix.mPrefix))
+        {
+            continue;
+        }
+
+        if (prefix.mIsDeprecating)
+        {
+            if (now >= prefix.mExpirationTime)
+            {
+                SuccessOrExit(error = AppendRio(prefix.mPrefix, /* aRouteLifetime */ 0, aRaMessage));
+                continue;
+            }
+        }
+        else
+        {
+            prefix.mIsDeprecating  = true;
+            prefix.mExpirationTime = now + kDeprecationTime;
+        }
+
+        if (mPrefixes.PushBack(prefix) != kErrorNone)
+        {
+            LogWarn("Too many deprecating on-mesh prefixes, removing %s", prefix.mPrefix.ToString().AsCString());
+            SuccessOrExit(error = AppendRio(prefix.mPrefix, /* aRouteLifetime */ 0, aRaMessage));
+        }
+
+        nextTime = Min(nextTime, prefix.mExpirationTime);
+    }
+
+    // Advertise all prefixes in `mPrefixes`
+
+    for (const RioPrefix &prefix : mPrefixes)
+    {
+        uint32_t lifetime = kDefaultOmrPrefixLifetime;
+
+        if (prefix.mIsDeprecating)
+        {
+            lifetime = TimeMilli::MsecToSec(prefix.mExpirationTime - now);
+        }
+
+        SuccessOrExit(error = AppendRio(prefix.mPrefix, lifetime, aRaMessage));
+    }
+
+    if (nextTime != now.GetDistantFuture())
+    {
+        mTimer.FireAtIfEarlier(nextTime);
+    }
+
+exit:
+    return error;
+}
+
+Error RoutingManager::RioAdvertiser::AppendRio(const Ip6::Prefix       &aPrefix,
+                                               uint32_t                 aRouteLifetime,
+                                               RouterAdvert::TxMessage &aRaMessage)
+{
     Error error;
 
-    VerifyOrExit(!Contains(aPrefix));
+    SuccessOrExit(error = aRaMessage.AppendRouteInfoOption(aPrefix, aRouteLifetime, mPreference));
+    LogRouteInfoOption(aPrefix, aRouteLifetime, mPreference);
 
-    error = PushBack(aPrefix);
+exit:
+    return error;
+}
+
+void RoutingManager::RioAdvertiser::HandleTimer(void)
+{
+    Get<RoutingManager>().ScheduleRoutingPolicyEvaluation(kImmediately);
+}
+
+void RoutingManager::RioAdvertiser::RioPrefixArray::Add(const Ip6::Prefix &aPrefix)
+{
+    // Checks if `aPrefix` is already present in the array and if not
+    // adds it as a new entry.
+
+    Error     error;
+    RioPrefix newEntry;
+
+    VerifyOrExit(!ContainsMatching(aPrefix));
+
+    newEntry.Clear();
+    newEntry.mPrefix = aPrefix;
+
+    error = PushBack(newEntry);
 
     if (error != kErrorNone)
     {
@@ -2889,19 +2962,6 @@
     return;
 }
 
-void RoutingManager::OnMeshPrefixArray::MarkAsDeleted(const OnMeshPrefix &aPrefix)
-{
-    // Searches for a matching entry to `aPrefix` and if found marks
-    // it as deleted by setting prefix length to zero.
-
-    OnMeshPrefix *entry = Find(aPrefix);
-
-    if (entry != nullptr)
-    {
-        entry->SetLength(0);
-    }
-}
-
 //---------------------------------------------------------------------------------------------------------------------
 // RoutePublisher
 
@@ -3346,6 +3406,66 @@
 #endif // OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
 
 //---------------------------------------------------------------------------------------------------------------------
+// RaInfo
+
+void RoutingManager::RaInfo::IncrementTxCountAndSaveHash(const InfraIf::Icmp6Packet &aRaMessage)
+{
+    mTxCount++;
+    mLastHashIndex++;
+
+    if (mLastHashIndex == kNumHashEntries)
+    {
+        mLastHashIndex = 0;
+    }
+
+    CalculateHash(aRaMessage, mHashes[mLastHashIndex]);
+}
+
+bool RoutingManager::RaInfo::IsRaFromManager(const Ip6::Nd::RouterAdvert::RxMessage &aRaMessage) const
+{
+    // Determines whether or not a received RA message was prepared by
+    // by `RoutingManager` itself (is present in the saved `mHashes`).
+
+    bool     isFromManager = false;
+    uint16_t hashIndex     = mLastHashIndex;
+    uint32_t count         = Min<uint32_t>(mTxCount, kNumHashEntries);
+    Hash     hash;
+
+    CalculateHash(aRaMessage.GetAsPacket(), hash);
+
+    for (; count > 0; count--)
+    {
+        if (mHashes[hashIndex] == hash)
+        {
+            isFromManager = true;
+            break;
+        }
+
+        // Go to the previous index (ring buffer)
+
+        if (hashIndex == 0)
+        {
+            hashIndex = kNumHashEntries - 1;
+        }
+        else
+        {
+            hashIndex--;
+        }
+    }
+
+    return isFromManager;
+}
+
+void RoutingManager::RaInfo::CalculateHash(const InfraIf::Icmp6Packet &aRaMessage, Hash &aHash)
+{
+    Crypto::Sha256 sha256;
+
+    sha256.Start();
+    sha256.Update(aRaMessage.GetBytes(), aRaMessage.GetLength());
+    sha256.Finish(aHash);
+}
+
+//---------------------------------------------------------------------------------------------------------------------
 // RsSender
 
 RoutingManager::RsSender::RsSender(Instance &aInstance)
@@ -3377,10 +3497,10 @@
 
 Error RoutingManager::RsSender::SendRs(void)
 {
-    Ip6::Address                  destAddress;
-    Ip6::Nd::RouterSolicitMessage routerSolicit;
-    InfraIf::Icmp6Packet          packet;
-    Error                         error;
+    Ip6::Address         destAddress;
+    RouterSolicitMessage routerSolicit;
+    InfraIf::Icmp6Packet packet;
+    Error                error;
 
     packet.InitFrom(routerSolicit);
     destAddress.SetToLinkLocalAllRoutersMulticast();
@@ -3453,6 +3573,9 @@
     : InstanceLocator(aInstance)
     , mEnabled(false)
     , mIsRunning(false)
+    , mNumPlatformPioProcessed(0)
+    , mNumPlatformRaReceived(0)
+    , mLastPlatformRaTime(0)
     , mTimer(aInstance)
 {
     mPrefix.Clear();
@@ -3519,6 +3642,20 @@
     return error;
 }
 
+Error RoutingManager::PdPrefixManager::GetProcessedRaInfo(PdProcessedRaInfo &aPdProcessedRaInfo) const
+{
+    Error error = kErrorNone;
+
+    VerifyOrExit(IsRunning() && HasPrefix(), error = kErrorNotFound);
+
+    aPdProcessedRaInfo.mNumPlatformRaReceived   = mNumPlatformRaReceived;
+    aPdProcessedRaInfo.mNumPlatformPioProcessed = mNumPlatformPioProcessed;
+    aPdProcessedRaInfo.mLastPlatformRaMsec      = TimerMilli::GetNow() - mLastPlatformRaTime;
+
+exit:
+    return error;
+}
+
 void RoutingManager::PdPrefixManager::WithdrawPrefix(void)
 {
     VerifyOrExit(HasPrefix());
@@ -3536,12 +3673,14 @@
 
 void RoutingManager::PdPrefixManager::ProcessPlatformGeneratedRa(const uint8_t *aRouterAdvert, const uint16_t aLength)
 {
-    Error                                     error = kErrorNone;
-    Ip6::Nd::RouterAdvertMessage::Icmp6Packet packet;
+    Error                     error = kErrorNone;
+    RouterAdvert::Icmp6Packet packet;
 
     VerifyOrExit(IsRunning(), LogWarn("Ignore platform generated RA since PD is disabled or not running."));
     packet.Init(aRouterAdvert, aLength);
-    error = Process(Ip6::Nd::RouterAdvertMessage(packet));
+    error = Process(RouterAdvert::RxMessage(packet));
+    mNumPlatformRaReceived++;
+    mLastPlatformRaTime = TimerMilli::GetNow();
 
 exit:
     if (error != kErrorNone)
@@ -3550,7 +3689,7 @@
     }
 }
 
-Error RoutingManager::PdPrefixManager::Process(const Ip6::Nd::RouterAdvertMessage &aMessage)
+Error RoutingManager::PdPrefixManager::Process(const RouterAdvert::RxMessage &aMessage)
 {
     Error                        error = kErrorNone;
     DiscoveredPrefixTable::Entry favoredEntry;
@@ -3559,17 +3698,17 @@
     VerifyOrExit(aMessage.IsValid(), error = kErrorParse);
     favoredEntry.Clear();
 
-    for (const Ip6::Nd::Option &option : aMessage)
+    for (const Option &option : aMessage)
     {
         DiscoveredPrefixTable::Entry entry;
 
-        if (option.GetType() != Ip6::Nd::Option::Type::kTypePrefixInfo ||
-            !static_cast<const Ip6::Nd::PrefixInfoOption &>(option).IsValid())
+        if (option.GetType() != Option::kTypePrefixInfo || !static_cast<const PrefixInfoOption &>(option).IsValid())
         {
             continue;
         }
 
-        entry.SetFrom(static_cast<const Ip6::Nd::PrefixInfoOption &>(option));
+        mNumPlatformPioProcessed++;
+        entry.SetFrom(static_cast<const PrefixInfoOption &>(option));
 
         if (!IsValidPdPrefix(entry.GetPrefix()))
         {
@@ -3642,7 +3781,7 @@
 
 extern "C" void otPlatBorderRoutingProcessIcmp6Ra(otInstance *aInstance, const uint8_t *aMessage, uint16_t aLength)
 {
-    AsCoreType(aInstance).Get<BorderRouter::RoutingManager>().ProcessPlatfromGeneratedRa(aMessage, aLength);
+    AsCoreType(aInstance).Get<BorderRouter::RoutingManager>().ProcessPlatformGeneratedRa(aMessage, aLength);
 }
 #endif // OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
 
diff --git a/src/core/border_router/routing_manager.hpp b/src/core/border_router/routing_manager.hpp
index 10062e8..37de6ec 100644
--- a/src/core/border_router/routing_manager.hpp
+++ b/src/core/border_router/routing_manager.hpp
@@ -55,6 +55,7 @@
 #include "common/error.hpp"
 #include "common/heap_allocatable.hpp"
 #include "common/heap_array.hpp"
+#include "common/heap_data.hpp"
 #include "common/linked_list.hpp"
 #include "common/locator.hpp"
 #include "common/message.hpp"
@@ -62,6 +63,7 @@
 #include "common/pool.hpp"
 #include "common/string.hpp"
 #include "common/timer.hpp"
+#include "crypto/sha256.hpp"
 #include "net/ip6.hpp"
 #include "net/nat64_translator.hpp"
 #include "net/nd6.hpp"
@@ -88,6 +90,7 @@
     typedef otBorderRoutingPrefixTableIterator PrefixTableIterator; ///< Prefix Table Iterator.
     typedef otBorderRoutingPrefixTableEntry    PrefixTableEntry;    ///< Prefix Table Entry.
     typedef otBorderRoutingRouterEntry         RouterEntry;         ///< Router Entry.
+    typedef otPdProcessedRaInfo                PdProcessedRaInfo;   ///< Data of PdProcessedRaInfo.
 
     /**
      * This constant specifies the maximum number of route prefixes that may be published by `RoutingManager`
@@ -208,7 +211,7 @@
      * @returns The current Route Info Option preference.
      *
      */
-    RoutePreference GetRouteInfoOptionPreference(void) const { return mRioPreference; }
+    RoutePreference GetRouteInfoOptionPreference(void) const { return mRioAdvertiser.GetPreference(); }
 
     /**
      * Explicitly sets the preference to use when advertising Route Info Options (RIO) in Router
@@ -220,7 +223,7 @@
      * @param[in] aPreference   The route preference to use.
      *
      */
-    void SetRouteInfoOptionPreference(RoutePreference aPreference);
+    void SetRouteInfoOptionPreference(RoutePreference aPreference) { mRioAdvertiser.SetPreference(aPreference); }
 
     /**
      * Clears a previously set preference value for advertised Route Info Options.
@@ -229,7 +232,23 @@
      * in router/leader role and low preference when in child role.
      *
      */
-    void ClearRouteInfoOptionPreference(void);
+    void ClearRouteInfoOptionPreference(void) { mRioAdvertiser.ClearPreference(); }
+
+    /**
+     * Sets additional options to append at the end of emitted Router Advertisement (RA) messages.
+     *
+     * The content of @p aOptions is copied internally, so can be a temporary stack variable.
+     *
+     * Subsequent calls to this method will overwrite the previously set value.
+     *
+     * @param[in] aOptions   A pointer to the encoded options. Can be `nullptr` to clear.
+     * @param[in] aLength    Number of bytes in @p aOptions.
+     *
+     * @retval kErrorNone     Successfully set the extra option bytes.
+     * @retval kErrorNoBufs   Could not allocate buffer to save the buffer.
+     *
+     */
+    Error SetExtraRouterAdvertOptions(const uint8_t *aOptions, uint16_t aLength);
 
     /**
      * Gets the current preference used for published routes in Network Data.
@@ -282,7 +301,7 @@
     /**
      * Returns the platform provided off-mesh-routable (OMR) prefix.
      *
-     * The prefix is extracted from the platform generated RA messages handled by `ProcessPlatfromGeneratedNd()`.
+     * The prefix is extracted from the platform generated RA messages handled by `ProcessPlatformGeneratedNd()`.
      *
      * @param[out] aPrefixInfo      A reference to where the prefix info will be output to.
      *
@@ -292,6 +311,18 @@
      *
      */
     Error GetPdOmrPrefix(PrefixTableEntry &aPrefixInfo) const;
+
+    /**
+     * Returns platform generated RA message processed information.
+     *
+     * @param[out] aPdProcessedRaInfo      A reference to where the PD processed RA info will be output to.
+     *
+     * @retval kErrorNone           Successfully retrieved the Info.
+     * @retval kErrorNotFound       There are no valid RA process info on this BR.
+     * @retval kErrorInvalidState   The Border Routing Manager is not initialized yet.
+     *
+     */
+    Error GetPdProcessedRaInfo(PdProcessedRaInfo &aPdProcessedRaInfo);
 #endif
 
     /**
@@ -504,7 +535,7 @@
      * @param[in] aLength       The length of the router advertisement message.
      *
      */
-    void ProcessPlatfromGeneratedRa(const uint8_t *aRouterAdvert, uint16_t aLength)
+    void ProcessPlatformGeneratedRa(const uint8_t *aRouterAdvert, uint16_t aLength)
     {
         mPdPrefixManager.ProcessPlatformGeneratedRa(aRouterAdvert, aLength);
     }
@@ -568,6 +599,15 @@
     static_assert(kPolicyEvaluationMaxDelay > kPolicyEvaluationMinDelay,
                   "kPolicyEvaluationMaxDelay must be larger than kPolicyEvaluationMinDelay");
 
+    using Option                 = Ip6::Nd::Option;
+    using PrefixInfoOption       = Ip6::Nd::PrefixInfoOption;
+    using RouteInfoOption        = Ip6::Nd::RouteInfoOption;
+    using RaFlagsExtOption       = Ip6::Nd::RaFlagsExtOption;
+    using RouterAdvert           = Ip6::Nd::RouterAdvert;
+    using NeighborAdvertMessage  = Ip6::Nd::NeighborAdvertMessage;
+    using NeighborSolicitMessage = Ip6::Nd::NeighborSolicitMessage;
+    using RouterSolicitMessage   = Ip6::Nd::RouterSolicitMessage;
+
     enum RouterAdvTxMode : uint8_t // Used in `SendRouterAdvertisement()`
     {
         kInvalidateAllPrevPrefixes,
@@ -617,9 +657,8 @@
     public:
         explicit DiscoveredPrefixTable(Instance &aInstance);
 
-        void ProcessRouterAdvertMessage(const Ip6::Nd::RouterAdvertMessage &aRaMessage,
-                                        const Ip6::Address                 &aSrcAddress);
-        void ProcessNeighborAdvertMessage(const Ip6::Nd::NeighborAdvertMessage &aNaMessage);
+        void ProcessRouterAdvertMessage(const RouterAdvert::RxMessage &aRaMessage, const Ip6::Address &aSrcAddress);
+        void ProcessNeighborAdvertMessage(const NeighborAdvertMessage &aNaMessage);
 
         bool ContainsDefaultOrNonUlaRoutePrefix(void) const;
         bool ContainsNonUlaOnLinkPrefix(void) const;
@@ -635,7 +674,7 @@
 
         TimeMilli CalculateNextStaleTime(TimeMilli aNow) const;
 
-        void DetermineAndSetFlags(Ip6::Nd::RouterAdvertMessage &aRaMessage) const;
+        void DetermineAndSetFlags(RouterAdvert::Header &aHeader) const;
 
         void  InitIterator(PrefixTableIterator &aIterator) const;
         Error GetNextEntry(PrefixTableIterator &aIterator, PrefixTableEntry &aEntry) const;
@@ -711,9 +750,9 @@
                 TimeMilli mNow;
             };
 
-            void               SetFrom(const Ip6::Nd::RouterAdvertMessage::Header &aRaHeader);
-            void               SetFrom(const Ip6::Nd::PrefixInfoOption &aPio);
-            void               SetFrom(const Ip6::Nd::RouteInfoOption &aRio);
+            void               SetFrom(const RouterAdvert::Header &aRaHeader);
+            void               SetFrom(const PrefixInfoOption &aPio);
+            void               SetFrom(const RouteInfoOption &aRio);
             Type               GetType(void) const { return mType; }
             bool               IsOnLinkPrefix(void) const { return (mType == kTypeOnLink); }
             bool               IsRoutePrefix(void) const { return (mType == kTypeRoute); }
@@ -811,10 +850,10 @@
             void SetInitTime(void) { mData32 = TimerMilli::GetNow().GetValue(); }
         };
 
-        void         ProcessRaHeader(const Ip6::Nd::RouterAdvertMessage::Header &aRaHeader, Router &aRouter);
-        void         ProcessPrefixInfoOption(const Ip6::Nd::PrefixInfoOption &aPio, Router &aRouter);
-        void         ProcessRouteInfoOption(const Ip6::Nd::RouteInfoOption &aRio, Router &aRouter);
-        void         ProcessRaFlagsExtOption(const Ip6::Nd::RaFlagsExtOption &aFlagsOption, Router &aRouter);
+        void         ProcessRaHeader(const RouterAdvert::Header &aRaHeader, Router &aRouter);
+        void         ProcessPrefixInfoOption(const PrefixInfoOption &aPio, Router &aRouter);
+        void         ProcessRouteInfoOption(const RouteInfoOption &aRio, Router &aRouter);
+        void         ProcessRaFlagsExtOption(const RaFlagsExtOption &aFlagsOption, Router &aRouter);
         bool         Contains(const Entry::Checker &aChecker) const;
         void         RemovePrefix(const Entry::Matcher &aMatcher);
         void         RemoveOrDeprecateEntriesFromInactiveRouters(void);
@@ -896,7 +935,7 @@
         void                    Stop(void);
         void                    Evaluate(void);
         void                    UpdateDefaultRouteFlag(bool aDefaultRoute);
-        bool                    IsLocalAddedInNetData(void) const { return mIsLocalAddedInNetData; }
+        bool                    ShouldAdvertiseLocalAsRio(void) const;
         const Ip6::Prefix      &GetGeneratedPrefix(void) const { return mGeneratedPrefix; }
         const OmrPrefix        &GetLocalPrefix(void) const { return mLocalPrefix; }
         const FavoredOmrPrefix &GetFavoredPrefix(void) const { return mFavoredPrefix; }
@@ -938,7 +977,7 @@
         bool               IsInitalEvaluationDone(void) const;
         void               HandleDiscoveredPrefixTableChanged(void);
         bool               ShouldPublishUlaRoute(void) const;
-        void               AppendAsPiosTo(Ip6::Nd::RouterAdvertMessage &aRaMessage);
+        Error              AppendAsPiosTo(RouterAdvert::TxMessage &aRaMessage);
         bool               IsPublishingOrAdvertising(void) const;
         void               HandleNetDataChange(void);
         void               HandleExtPanIdChange(void);
@@ -967,8 +1006,8 @@
         void  PublishAndAdvertise(void);
         void  Deprecate(void);
         void  ResetExpireTime(TimeMilli aNow);
-        void  AppendCurPrefix(Ip6::Nd::RouterAdvertMessage &aRaMessage);
-        void  AppendOldPrefixes(Ip6::Nd::RouterAdvertMessage &aRaMessage);
+        Error AppendCurPrefix(RouterAdvert::TxMessage &aRaMessage);
+        Error AppendOldPrefixes(RouterAdvert::TxMessage &aRaMessage);
         void  DeprecateOldPrefix(const Ip6::Prefix &aPrefix, TimeMilli aExpireTime);
         void  SavePrefix(const Ip6::Prefix &aPrefix, TimeMilli aExpireTime);
 
@@ -984,18 +1023,60 @@
         ExpireTimer                       mTimer;
     };
 
-    typedef Ip6::Prefix OnMeshPrefix;
+    void HandleRioAdvertiserimer(void) { mRioAdvertiser.HandleTimer(); }
 
-    class OnMeshPrefixArray :
-#if OPENTHREAD_CONFIG_BORDER_ROUTING_USE_HEAP_ENABLE
-        public Heap::Array<OnMeshPrefix>
-#else
-        public Array<OnMeshPrefix, kMaxOnMeshPrefixes>
-#endif
+    class RioAdvertiser : public InstanceLocator
     {
+        // Manages the list of prefixes advertised as RIO in emitted
+        // RA. The RIO prefixes are discovered from on-mesh prefixes in
+        // network data including OMR prefix from `OmrPrefixManager`.
+        // It also handles deprecating removed prefixes.
+
     public:
-        void Add(const OnMeshPrefix &aPrefix);
-        void MarkAsDeleted(const OnMeshPrefix &aPrefix);
+        explicit RioAdvertiser(Instance &aInstance);
+
+        RoutePreference GetPreference(void) const { return mPreference; }
+        void            SetPreference(RoutePreference aPreference);
+        void            ClearPreference(void);
+        void            HandleRoleChanged(void);
+        Error           AppendRios(RouterAdvert::TxMessage &aRaMessage);
+        Error           InvalidatPrevRios(RouterAdvert::TxMessage &aRaMessage);
+        bool            HasAdvertised(const Ip6::Prefix &aPrefix) const { return mPrefixes.ContainsMatching(aPrefix); }
+        uint16_t        GetAdvertisedRioCount(void) const { return mPrefixes.GetLength(); }
+        void            HandleTimer(void);
+
+    private:
+        static constexpr uint32_t kDeprecationTime = TimeMilli::SecToMsec(300);
+
+        struct RioPrefix : public Clearable<RioPrefix>
+        {
+            bool Matches(const Ip6::Prefix &aPrefix) const { return (mPrefix == aPrefix); }
+
+            Ip6::Prefix mPrefix;
+            bool        mIsDeprecating;
+            TimeMilli   mExpirationTime;
+        };
+
+        struct RioPrefixArray :
+#if OPENTHREAD_CONFIG_BORDER_ROUTING_USE_HEAP_ENABLE
+            public Heap::Array<RioPrefix>
+#else
+            public Array<RioPrefix, 2 * kMaxOnMeshPrefixes>
+#endif
+        {
+            void Add(const Ip6::Prefix &aPrefix);
+        };
+
+        void  SetPreferenceBasedOnRole(void);
+        void  UpdatePreference(RoutePreference aPreference);
+        Error AppendRio(const Ip6::Prefix &aPrefix, uint32_t aRouteLifetime, RouterAdvert::TxMessage &aRaMessage);
+
+        using RioTimer = TimerMilliIn<RoutingManager, &RoutingManager::HandleRioAdvertiserimer>;
+
+        RioPrefixArray  mPrefixes;
+        RioTimer        mTimer;
+        RoutePreference mPreference;
+        bool            mUserSetPreference;
     };
 
 #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
@@ -1097,26 +1178,44 @@
 
     struct RaInfo
     {
-        // Tracks info about emitted RA messages: Number of RAs sent,
-        // last tx time, header to use and whether the header is
-        // discovered from receiving RAs from the host itself. This
-        // ensures that if an entity on host is advertising certain
+        // Tracks info about emitted RA messages:
+        //
+        // - Number of RAs sent
+        // - Last RA TX time
+        // - Hashes of last TX RAs (to tell if a received RA is from
+        //   `RoutingManager` itself)
+        // - RA header to use, and
+        // - Whether the RA header is discovered from receiving RAs
+        //   from the host itself.
+        //
+        // This ensures that if an entity on host is advertising certain
         // info in its RA header (e.g., a default route), the RAs we
         // emit from `RoutingManager` also include the same header.
 
+        typedef Crypto::Sha256::Hash Hash;
+
+        static constexpr uint16_t kNumHashEntries = 5;
+
         RaInfo(void)
             : mHeaderUpdateTime(TimerMilli::GetNow())
             , mIsHeaderFromHost(false)
             , mTxCount(0)
             , mLastTxTime(TimerMilli::GetNow() - kMinDelayBetweenRtrAdvs)
+            , mLastHashIndex(0)
         {
         }
 
-        Ip6::Nd::RouterAdvertMessage::Header mHeader;
-        TimeMilli                            mHeaderUpdateTime;
-        bool                                 mIsHeaderFromHost;
-        uint32_t                             mTxCount;
-        TimeMilli                            mLastTxTime;
+        void        IncrementTxCountAndSaveHash(const InfraIf::Icmp6Packet &aRaMessage);
+        bool        IsRaFromManager(const Ip6::Nd::RouterAdvert::RxMessage &aRaMessage) const;
+        static void CalculateHash(const InfraIf::Icmp6Packet &aRaMessage, Hash &aHash);
+
+        RouterAdvert::Header mHeader;
+        TimeMilli            mHeaderUpdateTime;
+        bool                 mIsHeaderFromHost;
+        uint32_t             mTxCount;
+        TimeMilli            mLastTxTime;
+        Hash                 mHashes[kNumHashEntries];
+        uint16_t             mLastHashIndex;
     };
 
     void HandleRsSenderTimer(void) { mRsSender.HandleTimer(); }
@@ -1178,6 +1277,7 @@
 
         void  ProcessPlatformGeneratedRa(const uint8_t *aRouterAdvert, uint16_t aLength);
         Error GetPrefixInfo(PrefixTableEntry &aInfo) const;
+        Error GetProcessedRaInfo(PdProcessedRaInfo &aPdProcessedRaInfo) const;
         void  HandleTimer(void) { WithdrawPrefix(); }
 
         static const char *StateToString(Dhcp6PdState aState);
@@ -1190,7 +1290,7 @@
         }
 
     private:
-        Error Process(const Ip6::Nd::RouterAdvertMessage &aMessage);
+        Error Process(const RouterAdvert::RxMessage &aMessage);
         void  EvaluateStateChange(Dhcp6PdState aOldState);
         void  WithdrawPrefix(void);
         void  StartStop(bool aStart);
@@ -1199,6 +1299,9 @@
 
         bool                         mEnabled;
         bool                         mIsRunning;
+        uint32_t                     mNumPlatformPioProcessed;
+        uint32_t                     mNumPlatformRaReceived;
+        TimeMilli                    mLastPlatformRaTime;
         PlatformOmrPrefixTimer       mTimer;
         DiscoveredPrefixTable::Entry mPrefix;
     };
@@ -1210,8 +1313,6 @@
     void  HandleNotifierEvents(Events aEvents);
     bool  IsInitialized(void) const { return mInfraIf.IsInitialized(); }
     bool  IsEnabled(void) const { return mIsEnabled; }
-    void  SetRioPreferenceBasedOnRole(void);
-    void  UpdateRioPreference(RoutePreference aPreference);
     Error LoadOrGenerateRandomBrUlaPrefix(void);
 
     void EvaluateRoutingPolicy(void);
@@ -1225,17 +1326,16 @@
     void HandleRouterAdvertisement(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress);
     void HandleRouterSolicit(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress);
     void HandleNeighborAdvertisement(const InfraIf::Icmp6Packet &aPacket);
-    bool ShouldProcessPrefixInfoOption(const Ip6::Nd::PrefixInfoOption &aPio, const Ip6::Prefix &aPrefix);
-    bool ShouldProcessRouteInfoOption(const Ip6::Nd::RouteInfoOption &aRio, const Ip6::Prefix &aPrefix);
+    bool ShouldProcessPrefixInfoOption(const PrefixInfoOption &aPio, const Ip6::Prefix &aPrefix);
+    bool ShouldProcessRouteInfoOption(const RouteInfoOption &aRio, const Ip6::Prefix &aPrefix);
     void UpdateDiscoveredPrefixTableOnNetDataChange(void);
     bool NetworkDataContainsOmrPrefix(const Ip6::Prefix &aPrefix) const;
     bool NetworkDataContainsUlaRoute(void) const;
-    void UpdateRouterAdvertHeader(const Ip6::Nd::RouterAdvertMessage *aRouterAdvertMessage);
-    bool IsReceivedRouterAdvertFromManager(const Ip6::Nd::RouterAdvertMessage &aRaMessage) const;
+    void UpdateRouterAdvertHeader(const RouterAdvert::RxMessage *aRouterAdvertMessage);
     void ResetDiscoveredPrefixStaleTimer(void);
 
     static bool IsValidBrUlaPrefix(const Ip6::Prefix &aBrUlaPrefix);
-    static bool IsValidOnLinkPrefix(const Ip6::Nd::PrefixInfoOption &aPio);
+    static bool IsValidOnLinkPrefix(const PrefixInfoOption &aPio);
     static bool IsValidOnLinkPrefix(const Ip6::Prefix &aOnLinkPrefix);
 
     static void LogPrefixInfoOption(const Ip6::Prefix &aPrefix, uint32_t aValidLifetime, uint32_t aPreferredLifetime);
@@ -1259,10 +1359,7 @@
 
     OmrPrefixManager mOmrPrefixManager;
 
-    // List of on-mesh prefixes (discovered from Network Data) which
-    // were advertised as RIO in the last sent RA message.
-    OnMeshPrefixArray mAdvertisedPrefixes;
-
+    RioAdvertiser   mRioAdvertiser;
     RoutePreference mRioPreference;
     bool            mUserSetRioPreference;
 
@@ -1280,8 +1377,9 @@
     PdPrefixManager mPdPrefixManager;
 #endif
 
-    RaInfo   mRaInfo;
-    RsSender mRsSender;
+    RaInfo     mRaInfo;
+    RsSender   mRsSender;
+    Heap::Data mExtraRaOptions;
 
     DiscoveredPrefixStaleTimer mDiscoveredPrefixStaleTimer;
     RoutingPolicyTimer         mRoutingPolicyTimer;
diff --git a/src/core/coap/coap.cpp b/src/core/coap/coap.cpp
index 696d0fc..6afd693 100644
--- a/src/core/coap/coap.cpp
+++ b/src/core/coap/coap.cpp
@@ -619,7 +619,6 @@
 {
     Error            error       = kErrorNone;
     bool             isOptionSet = false;
-    uint64_t         optionBuf   = 0;
     uint16_t         blockOption = 0;
     Option::Iterator iterator;
 
@@ -655,8 +654,9 @@
         }
 
         // Copy option
-        SuccessOrExit(error = iterator.ReadOptionValue(&optionBuf));
-        SuccessOrExit(error = aRequest.AppendOption(optionNumber, iterator.GetOption()->GetLength(), &optionBuf));
+        SuccessOrExit(error = aRequest.AppendOptionFromMessage(optionNumber, iterator.GetOption()->GetLength(),
+                                                               iterator.GetMessage(),
+                                                               iterator.GetOptionValueMessageOffset()));
     }
 
     if (!isOptionSet)
diff --git a/src/core/coap/coap_message.cpp b/src/core/coap/coap_message.cpp
index cbf5448..4998de7 100644
--- a/src/core/coap/coap_message.cpp
+++ b/src/core/coap/coap_message.cpp
@@ -146,8 +146,12 @@
     return rval;
 }
 
-Error Message::AppendOption(uint16_t aNumber, uint16_t aLength, const void *aValue)
+Error Message::AppendOptionHeader(uint16_t aNumber, uint16_t aLength)
 {
+    /*
+     * Appends a CoAP Option header field (Option Delta/Length) per RFC 7252.
+     */
+
     Error    error = kErrorNone;
     uint16_t delta;
     uint8_t  header[kMaxOptionHeaderSize];
@@ -167,10 +171,33 @@
     VerifyOrExit(static_cast<uint32_t>(GetLength()) + headerLength + aLength < kMaxHeaderLength, error = kErrorNoBufs);
 
     SuccessOrExit(error = AppendBytes(header, headerLength));
-    SuccessOrExit(error = AppendBytes(aValue, aLength));
 
     GetHelpData().mOptionLast = aNumber;
 
+exit:
+    return error;
+}
+
+Error Message::AppendOption(uint16_t aNumber, uint16_t aLength, const void *aValue)
+{
+    Error error = kErrorNone;
+
+    SuccessOrExit(error = AppendOptionHeader(aNumber, aLength));
+    SuccessOrExit(error = AppendBytes(aValue, aLength));
+
+    GetHelpData().mHeaderLength = GetLength();
+
+exit:
+    return error;
+}
+
+Error Message::AppendOptionFromMessage(uint16_t aNumber, uint16_t aLength, const Message &aMessage, uint16_t aOffset)
+{
+    Error error = kErrorNone;
+
+    SuccessOrExit(error = AppendOptionHeader(aNumber, aLength));
+    SuccessOrExit(error = AppendBytesFromMessage(aMessage, aOffset, aLength));
+
     GetHelpData().mHeaderLength = GetLength();
 
 exit:
diff --git a/src/core/coap/coap_message.hpp b/src/core/coap/coap_message.hpp
index 789c919..62128b6 100644
--- a/src/core/coap/coap_message.hpp
+++ b/src/core/coap/coap_message.hpp
@@ -402,6 +402,23 @@
     Error AppendOption(uint16_t aNumber, uint16_t aLength, const void *aValue);
 
     /**
+     * Appends a CoAP option reading Option value from another or potentially the same message.
+     *
+     * @param[in] aNumber   The CoAP Option number.
+     * @param[in] aLength   The CoAP Option length.
+     * @param[in] aMessage  The message to read the CoAP Option value from (it can be the same as the current message).
+     * @param[in] aOffset   The offset in @p aMessage to start reading the CoAP Option value from (@p aLength bytes are
+     *                      used as Option value).
+     *
+     * @retval kErrorNone         Successfully appended the option.
+     * @retval kErrorInvalidArgs  The option type is not equal or greater than the last option type.
+     * @retval kErrorNoBufs       The option length exceeds the buffer size.
+     * @retval kErrorParse        Not enough bytes in @p aMessage to read @p aLength bytes from @p aOffset.
+     *
+     */
+    Error AppendOptionFromMessage(uint16_t aNumber, uint16_t aLength, const Message &aMessage, uint16_t aOffset);
+
+    /**
      * Appends an unsigned integer CoAP option as specified in RFC-7252 section-3.2
      *
      * @param[in]  aNumber  The CoAP Option number.
@@ -966,6 +983,8 @@
     }
 
     uint8_t WriteExtendedOptionField(uint16_t aValue, uint8_t *&aBuffer);
+
+    Error AppendOptionHeader(uint16_t aNumber, uint16_t aLength);
 };
 
 /**
@@ -1187,6 +1206,16 @@
          */
         uint16_t GetPayloadMessageOffset(void) const { return mNextOptionOffset; }
 
+        /**
+         * Gets the offset of beginning of the CoAP Option Value.
+         *
+         * MUST be used during the iterator is in progress.
+         *
+         * @returns The offset of beginning of the CoAP Option Value
+         *
+         */
+        uint16_t GetOptionValueMessageOffset(void) const { return mNextOptionOffset - mOption.mLength; }
+
     private:
         // `mOption.mLength` value to indicate iterator is done.
         static constexpr uint16_t kIteratorDoneLength = 0xffff;
diff --git a/src/core/coap/coap_secure.cpp b/src/core/coap/coap_secure.cpp
index 7d0fbe1..875d6c0 100644
--- a/src/core/coap/coap_secure.cpp
+++ b/src/core/coap/coap_secure.cpp
@@ -28,13 +28,14 @@
 
 #include "coap_secure.hpp"
 
-#if OPENTHREAD_CONFIG_DTLS_ENABLE
+#if OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE
 
 #include "common/locator_getters.hpp"
 #include "common/log.hpp"
 #include "common/new.hpp"
 #include "instance/instance.hpp"
-#include "meshcop/dtls.hpp"
+#include "meshcop/secure_transport.hpp"
+
 #include "thread/thread_netif.hpp"
 
 /**
@@ -54,27 +55,40 @@
 {
 }
 
-Error CoapSecure::Start(uint16_t aPort)
+Error CoapSecure::Start(uint16_t aPort) { return Start(aPort, /* aMaxAttempts */ 0, nullptr, nullptr); }
+
+Error CoapSecure::Start(uint16_t aPort, uint16_t aMaxAttempts, AutoStopCallback aCallback, void *aContext)
 {
-    Error error = kErrorNone;
+    Error error;
 
-    mConnectedCallback.Clear();
-
-    SuccessOrExit(error = mDtls.Open(&CoapSecure::HandleDtlsReceive, &CoapSecure::HandleDtlsConnected, this));
-    SuccessOrExit(error = mDtls.Bind(aPort));
+    SuccessOrExit(error = Open(aMaxAttempts, aCallback, aContext));
+    error = mDtls.Bind(aPort);
 
 exit:
     return error;
 }
 
-Error CoapSecure::Start(MeshCoP::Dtls::TransportCallback aCallback, void *aContext)
+Error CoapSecure::Start(MeshCoP::SecureTransport::TransportCallback aCallback, void *aContext)
 {
-    Error error = kErrorNone;
+    Error error;
 
+    SuccessOrExit(error = Open(/* aMaxAttemps */ 0, nullptr, nullptr));
+    error = mDtls.Bind(aCallback, aContext);
+
+exit:
+    return error;
+}
+
+Error CoapSecure::Open(uint16_t aMaxAttempts, AutoStopCallback aCallback, void *aContext)
+{
+    Error error = kErrorAlready;
+
+    SuccessOrExit(mDtls.SetMaxConnectionAttempts(aMaxAttempts, HandleDtlsAutoClose, this));
+    mAutoStopCallback.Set(aCallback, aContext);
     mConnectedCallback.Clear();
+    SuccessOrExit(mDtls.Open(HandleDtlsReceive, HandleDtlsConnected, this));
 
-    SuccessOrExit(error = mDtls.Open(&CoapSecure::HandleDtlsReceive, &CoapSecure::HandleDtlsConnected, this));
-    SuccessOrExit(error = mDtls.Bind(aCallback, aContext));
+    error = kErrorNone;
 
 exit:
     return error;
@@ -98,7 +112,7 @@
 void CoapSecure::SetPsk(const MeshCoP::JoinerPskd &aPskd)
 {
     static_assert(static_cast<uint16_t>(MeshCoP::JoinerPskd::kMaxLength) <=
-                      static_cast<uint16_t>(MeshCoP::Dtls::kPskMaxLength),
+                      static_cast<uint16_t>(MeshCoP::SecureTransport::kPskMaxLength),
                   "The maximum length of DTLS PSK is smaller than joiner PSKd");
 
     SuccessOrAssert(mDtls.SetPsk(reinterpret_cast<const uint8_t *>(aPskd.GetAsCString()), aPskd.GetLength()));
@@ -171,6 +185,17 @@
 
 void CoapSecure::HandleDtlsConnected(bool aConnected) { mConnectedCallback.InvokeIfSet(aConnected); }
 
+void CoapSecure::HandleDtlsAutoClose(void *aContext)
+{
+    return static_cast<CoapSecure *>(aContext)->HandleDtlsAutoClose();
+}
+
+void CoapSecure::HandleDtlsAutoClose(void)
+{
+    Stop();
+    mAutoStopCallback.InvokeIfSet();
+}
+
 void CoapSecure::HandleDtlsReceive(void *aContext, uint8_t *aBuf, uint16_t aLength)
 {
     return static_cast<CoapSecure *>(aContext)->HandleDtlsReceive(aBuf, aLength);
@@ -224,4 +249,4 @@
 } // namespace Coap
 } // namespace ot
 
-#endif // OPENTHREAD_CONFIG_DTLS_ENABLE
+#endif // OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE
diff --git a/src/core/coap/coap_secure.hpp b/src/core/coap/coap_secure.hpp
index b24e7d0..0565ef5 100644
--- a/src/core/coap/coap_secure.hpp
+++ b/src/core/coap/coap_secure.hpp
@@ -31,12 +31,12 @@
 
 #include "openthread-core-config.h"
 
-#if OPENTHREAD_CONFIG_DTLS_ENABLE
+#if OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE
 
 #include "coap/coap.hpp"
 #include "common/callback.hpp"
-#include "meshcop/dtls.hpp"
 #include "meshcop/meshcop.hpp"
+#include "meshcop/secure_transport.hpp"
 
 #include <openthread/coap_secure.h>
 
@@ -62,6 +62,13 @@
     typedef void (*ConnectedCallback)(bool aConnected, void *aContext);
 
     /**
+     * Callback to notify when the agent is automatically stopped due to reaching the maximum number of connection
+     * attempts.
+     *
+     */
+    typedef otCoapSecureAutoStopCallback AutoStopCallback;
+
+    /**
      * Initializes the object.
      *
      * @param[in]  aInstance           A reference to the OpenThread instance.
@@ -82,6 +89,21 @@
     Error Start(uint16_t aPort);
 
     /**
+     * Starts the secure CoAP agent and sets the maximum number of allowed connection attempts before stopping the
+     * agent automatically.
+     *
+     * @param[in] aPort           The local UDP port to bind to.
+     * @param[in] aMaxAttempts    Maximum number of allowed connection request attempts. Zero indicates no limit.
+     * @param[in] aCallback       Callback to notify if max number of attempts has reached and agent is stopped.
+     * @param[in] aContext        A pointer to arbitrary context to use with `AutoStopCallback`.
+     *
+     * @retval kErrorNone        Successfully started the CoAP agent.
+     * @retval kErrorAlready     Already started.
+     *
+     */
+    Error Start(uint16_t aPort, uint16_t aMaxAttempts, AutoStopCallback aCallback, void *aContext);
+
+    /**
      * Starts the secure CoAP agent, but do not use socket to transmit/receive messages.
      *
      * @param[in]  aCallback  A pointer to a function for sending messages.
@@ -91,7 +113,7 @@
      * @retval kErrorAlready     Already started.
      *
      */
-    Error Start(MeshCoP::Dtls::TransportCallback aCallback, void *aContext);
+    Error Start(MeshCoP::SecureTransport::TransportCallback aCallback, void *aContext);
 
     /**
      * Sets connected callback of this secure CoAP agent.
@@ -142,6 +164,15 @@
     bool IsConnected(void) const { return mDtls.IsConnected(); }
 
     /**
+     * Indicates whether or not the DTLS session is closed.
+     *
+     * @retval TRUE   The DTLS session is closed
+     * @retval FALSE  The DTLS session is not closed.
+     *
+     */
+    bool IsClosed(void) const { return mDtls.IsClosed(); }
+
+    /**
      * Stops the DTLS connection.
      *
      */
@@ -153,7 +184,7 @@
      * @returns  A reference to the DTLS object.
      *
      */
-    MeshCoP::Dtls &GetDtls(void) { return mDtls; }
+    MeshCoP::SecureTransport &GetDtls(void) { return mDtls; }
 
     /**
      * Gets the UDP port of this agent.
@@ -259,18 +290,6 @@
 #endif // defined(MBEDTLS_BASE64_C) && defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
 
     /**
-     * Sets the connected callback to indicate, when a Client connect to the CoAP Secure server.
-     *
-     * @param[in]  aCallback     A pointer to a function that will be called once DTLS connection is established.
-     * @param[in]  aContext      A pointer to arbitrary context information.
-     *
-     */
-    void SetClientConnectedCallback(ConnectedCallback aCallback, void *aContext)
-    {
-        mConnectedCallback.Set(aCallback, aContext);
-    }
-
-    /**
      * Sets the authentication mode for the CoAP secure connection. It disables or enables the verification
      * of peer certificate.
      *
@@ -382,7 +401,7 @@
      */
     void HandleUdpReceive(ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
     {
-        return mDtls.HandleUdpReceive(aMessage, aMessageInfo);
+        return mDtls.HandleReceive(aMessage, aMessageInfo);
     }
 
     /**
@@ -394,6 +413,8 @@
     const Ip6::MessageInfo &GetMessageInfo(void) const { return mDtls.GetMessageInfo(); }
 
 private:
+    Error Open(uint16_t aMaxAttempts, AutoStopCallback aCallback, void *aContext);
+
     static Error Send(CoapBase &aCoapBase, ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
     {
         return static_cast<CoapSecure &>(aCoapBase).Send(aMessage, aMessageInfo);
@@ -403,14 +424,18 @@
     static void HandleDtlsConnected(void *aContext, bool aConnected);
     void        HandleDtlsConnected(bool aConnected);
 
+    static void HandleDtlsAutoClose(void *aContext);
+    void        HandleDtlsAutoClose(void);
+
     static void HandleDtlsReceive(void *aContext, uint8_t *aBuf, uint16_t aLength);
     void        HandleDtlsReceive(uint8_t *aBuf, uint16_t aLength);
 
     static void HandleTransmit(Tasklet &aTasklet);
     void        HandleTransmit(void);
 
-    MeshCoP::Dtls               mDtls;
+    MeshCoP::SecureTransport    mDtls;
     Callback<ConnectedCallback> mConnectedCallback;
+    Callback<AutoStopCallback>  mAutoStopCallback;
     ot::MessageQueue            mTransmitQueue;
     TaskletContext              mTransmitTask;
 };
@@ -418,6 +443,6 @@
 } // namespace Coap
 } // namespace ot
 
-#endif // OPENTHREAD_CONFIG_DTLS_ENABLE
+#endif // OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE
 
 #endif // COAP_SECURE_HPP_
diff --git a/src/core/common/clearable.hpp b/src/core/common/clearable.hpp
index f91c15f..bc1b1f6 100644
--- a/src/core/common/clearable.hpp
+++ b/src/core/common/clearable.hpp
@@ -38,9 +38,26 @@
 
 #include <string.h>
 
+#include "common/type_traits.hpp"
+
 namespace ot {
 
 /**
+ * Clears (sets to zero) all bytes of a given object.
+ *
+ * @tparam ObjectType    The object type.
+ *
+ * @param[in] aObject    A reference to the object of type `ObjectType` to clear all its bytes.
+ *
+ */
+template <typename ObjectType> void ClearAllBytes(ObjectType &aObject)
+{
+    static_assert(!TypeTraits::IsPointer<ObjectType>::kValue, "ObjectType must not be a pointer");
+
+    memset(reinterpret_cast<void *>(&aObject), 0, sizeof(ObjectType));
+}
+
+/**
  * Defines a `Clearable` object which provides `Clear()` method.
  *
  * The `Clear` implementation simply sets all the bytes of a `Type` instance to zero.
@@ -52,7 +69,7 @@
 template <typename Type> class Clearable
 {
 public:
-    void Clear(void) { memset(reinterpret_cast<void *>(static_cast<Type *>(this)), 0, sizeof(Type)); }
+    void Clear(void) { ClearAllBytes<Type>(*static_cast<Type *>(this)); }
 };
 
 } // namespace ot
diff --git a/src/core/common/heap_data.cpp b/src/core/common/heap_data.cpp
index d12686e..9c09e59 100644
--- a/src/core/common/heap_data.cpp
+++ b/src/core/common/heap_data.cpp
@@ -78,6 +78,25 @@
     TakeFrom(aData);
 }
 
+bool Data::Matches(const uint8_t *aBuffer, uint16_t aLength) const
+{
+    bool matches = false;
+
+    VerifyOrExit(aLength == mData.GetLength());
+
+    if (IsNull())
+    {
+        matches = (aLength == 0);
+    }
+    else
+    {
+        matches = mData.MatchesBytesIn(aBuffer);
+    }
+
+exit:
+    return matches;
+}
+
 void Data::Free(void)
 {
     Heap::Free(mData.GetBytes());
diff --git a/src/core/common/heap_data.hpp b/src/core/common/heap_data.hpp
index 9fbbe3d..2e3118f 100644
--- a/src/core/common/heap_data.hpp
+++ b/src/core/common/heap_data.hpp
@@ -170,6 +170,29 @@
     void CopyBytesTo(uint8_t *aBuffer) const { return mData.CopyBytesTo(aBuffer); }
 
     /**
+     * Compares the `Data` content with the bytes from a given buffer.
+     *
+     * @param[in] aBuffer   A pointer to a buffer to compare with the data.
+     * @param[in] aLength   The length of @p aBuffer.
+     *
+     * @retval TRUE   The `Data` content matches the bytes in @p aBuffer.
+     * @retval FALSE  The `Data` content does not match the byes in @p aBuffer.
+     *
+     */
+    bool Matches(const uint8_t *aBuffer, uint16_t aLength) const;
+
+    /**
+     * Overloads operator `==` to compare the `Data` content with the content from another one.
+     *
+     * @param[in] aOtherData   The other `Data` to compare with.
+     *
+     * @retval TRUE   The two `Data` instances have matching content (same length and same bytes).
+     * @retval FALSE  The two `Data` instances do not have matching content.
+     *
+     */
+    bool operator==(const Data &aOtherData) const { return mData == aOtherData.mData; }
+
+    /**
      * Frees any buffer allocated by the `Heap::Data`.
      *
      * The `Heap::Data` destructor will automatically call `Free()`. This method allows caller to free the buffer
diff --git a/src/core/common/log.cpp b/src/core/common/log.cpp
index 1809cd5..afddcfd 100644
--- a/src/core/common/log.cpp
+++ b/src/core/common/log.cpp
@@ -136,6 +136,16 @@
     return;
 }
 
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN)
+void Logger::LogOnError(const char *aModuleName, Error aError, const char *aText)
+{
+    if (aError != kErrorNone)
+    {
+        LogAtLevel<kLogLevelWarn>(aModuleName, "Failed to %s: %s", aText, ErrorToString(aError));
+    }
+}
+#endif
+
 #if OPENTHREAD_CONFIG_LOG_PKT_DUMP
 
 template <LogLevel kLogLevel>
diff --git a/src/core/common/log.hpp b/src/core/common/log.hpp
index 47293e0..49f0416 100644
--- a/src/core/common/log.hpp
+++ b/src/core/common/log.hpp
@@ -162,6 +162,22 @@
 #define LogDebg(...)
 #endif
 
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN)
+/**
+ * Emits an error log message at warning log level if there is an error.
+ *
+ * The emitted log will use the the following format "Failed to {aText}: {ErrorToString(aError)}", and will be emitted
+ * only if there is an error, i.e., @p aError is not `kErrorNone`.
+ *
+ * @param[in] aError       The error to check and log.
+ * @param[in] aText        The text to include in the log.
+ *
+ */
+#define LogWarnOnError(aError, aText) Logger::LogOnError(kLogModuleName, aError, aText)
+#else
+#define LogWarnOnError(aError, aText)
+#endif
+
 #if OT_SHOULD_LOG
 /**
  * Emits a log message at a given log level.
@@ -316,6 +332,10 @@
 
     static void LogVarArgs(const char *aModuleName, LogLevel aLogLevel, const char *aFormat, va_list aArgs);
 
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN)
+    static void LogOnError(const char *aModuleName, Error aError, const char *aText);
+#endif
+
 #if OPENTHREAD_CONFIG_LOG_PKT_DUMP
     static constexpr uint8_t kStringLineLength = 80;
     static constexpr uint8_t kDumpBytesPerLine = 16;
diff --git a/src/core/common/message.cpp b/src/core/common/message.cpp
index 1c7028d..3a4c342 100644
--- a/src/core/common/message.cpp
+++ b/src/core/common/message.cpp
@@ -75,7 +75,7 @@
 
     VerifyOrExit((message = static_cast<Message *>(NewBuffer(aSettings.GetPriority()))) != nullptr);
 
-    memset(message, 0, sizeof(*message));
+    ClearAllBytes(*message);
     message->SetMessagePool(this);
     message->SetType(aType);
     message->SetReserved(aReserveHeader);
@@ -770,12 +770,19 @@
     SuccessOrExit(error = messageCopy->AppendBytesFromMessage(*this, 0, aLength));
 
     // Copy selected message information.
+
     offset = Min(GetOffset(), aLength);
     messageCopy->SetOffset(offset);
 
     messageCopy->SetSubType(GetSubType());
     messageCopy->SetLoopbackToHostAllowed(IsLoopbackToHostAllowed());
     messageCopy->SetOrigin(GetOrigin());
+    messageCopy->SetTimestamp(GetTimestamp());
+    messageCopy->SetMeshDest(GetMeshDest());
+    messageCopy->SetPanId(GetPanId());
+    messageCopy->SetChannel(GetChannel());
+    messageCopy->SetRssAverager(GetRssAverager());
+    messageCopy->SetLqiAverager(GetLqiAverager());
 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
     messageCopy->SetTimeSync(IsTimeSync());
 #endif
@@ -795,18 +802,48 @@
 bool Message::IsChildPending(void) const { return GetMetadata().mChildMask.HasAny(); }
 #endif
 
-void Message::SetLinkInfo(const ThreadLinkInfo &aLinkInfo)
+Error Message::GetLinkInfo(ThreadLinkInfo &aLinkInfo) const
 {
-    SetLinkSecurityEnabled(aLinkInfo.mLinkSecurity);
-    SetPanId(aLinkInfo.mPanId);
-    AddRss(aLinkInfo.mRss);
-#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
-    AddLqi(aLinkInfo.mLqi);
+    Error error = kErrorNone;
+
+    VerifyOrExit(IsOriginThreadNetif(), error = kErrorNotFound);
+
+    aLinkInfo.Clear();
+
+    aLinkInfo.mPanId               = GetPanId();
+    aLinkInfo.mChannel             = GetChannel();
+    aLinkInfo.mRss                 = GetAverageRss();
+    aLinkInfo.mLqi                 = GetAverageLqi();
+    aLinkInfo.mLinkSecurity        = IsLinkSecurityEnabled();
+    aLinkInfo.mIsDstPanIdBroadcast = IsDstPanIdBroadcast();
+
+#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
+    aLinkInfo.mTimeSyncSeq       = GetTimeSyncSeq();
+    aLinkInfo.mNetworkTimeOffset = GetNetworkTimeOffset();
 #endif
+
+#if OPENTHREAD_CONFIG_MULTI_RADIO
+    aLinkInfo.mRadioType = GetRadioType();
+#endif
+
+exit:
+    return error;
+}
+
+void Message::UpdateLinkInfoFrom(const ThreadLinkInfo &aLinkInfo)
+{
+    SetPanId(aLinkInfo.mPanId);
+    SetChannel(aLinkInfo.mChannel);
+    AddRss(aLinkInfo.mRss);
+    AddLqi(aLinkInfo.mLqi);
+    SetLinkSecurityEnabled(aLinkInfo.mLinkSecurity);
+    GetMetadata().mIsDstPanIdBroadcast = aLinkInfo.IsDstPanIdBroadcast();
+
 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
     SetTimeSyncSeq(aLinkInfo.mTimeSyncSeq);
     SetNetworkTimeOffset(aLinkInfo.mNetworkTimeOffset);
 #endif
+
 #if OPENTHREAD_CONFIG_MULTI_RADIO
     SetRadioType(static_cast<Mac::RadioType>(aLinkInfo.mRadioType));
 #endif
diff --git a/src/core/common/message.hpp b/src/core/common/message.hpp
index 66153e2..f3dfd54 100644
--- a/src/core/common/message.hpp
+++ b/src/core/common/message.hpp
@@ -200,9 +200,7 @@
         uint16_t     mPanId;       // PAN ID (used for MLE Discover Request and Response).
         uint8_t      mChannel;     // The message channel (used for MLE Announce).
         RssAverager  mRssAverager; // The averager maintaining the received signal strength (RSS) average.
-#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
-        LqiAverager mLqiAverager; // The averager maintaining the Link quality indicator (LQI) average.
-#endif
+        LqiAverager  mLqiAverager; // The averager maintaining the Link quality indicator (LQI) average.
 #if OPENTHREAD_FTD
         ChildMask mChildMask; // ChildMask to indicate which sleepy children need to receive this.
 #endif
@@ -218,7 +216,9 @@
         bool    mMulticastLoop : 1;       // Whether this multicast message may be looped back.
         bool    mResolvingAddress : 1;    // Whether the message is pending an address query resolution.
         bool    mAllowLookbackToHost : 1; // Whether the message is allowed to be looped back to host.
-        uint8_t mOrigin : 2;              // The origin of the message.
+        bool    mIsDstPanIdBroadcast : 1; // IWhether the dest PAN ID is broadcast.
+        uint8_t mOrigin : 2;
+        // The origin of the message.
 #if OPENTHREAD_CONFIG_MULTI_RADIO
         uint8_t mRadioType : 2;      // The radio link type the message was received on, or should be sent on.
         bool    mIsRadioTypeSet : 1; // Whether the radio type is set.
@@ -286,7 +286,8 @@
         kTypeSupervision  = 2, ///< A child supervision frame.
         kTypeMacEmptyData = 3, ///< An empty MAC data frame.
         kTypeIp4          = 4, ///< A full uncompressed IPv4 packet, for NAT64.
-        kTypeOther        = 5, ///< Other (data) message.
+        kTypeBle          = 5, ///< A BLE payload message.
+        kTypeOther        = 6, ///< Other (data) message.
     };
 
     /**
@@ -1014,11 +1015,15 @@
     void SetMeshDest(uint16_t aMeshDest) { GetMetadata().mMeshDest = aMeshDest; }
 
     /**
-     * Returns the IEEE 802.15.4 Destination PAN ID.
+     * Returns the IEEE 802.15.4 Source or Destination PAN ID.
      *
-     * @note Only use this when sending MLE Discover Request or Response messages.
+     * For a message received over the Thread radio, specifies the Source PAN ID when present in MAC header, otherwise
+     * specifies the Destination PAN ID.
      *
-     * @returns The IEEE 802.15.4 Destination PAN ID.
+     * For a message to be sent over the Thread radio, this is set and used for MLE Discover Request or Response
+     * messages.
+     *
+     * @returns The IEEE 802.15.4 PAN ID.
      *
      */
     uint16_t GetPanId(void) const { return GetMetadata().mPanId; }
@@ -1034,6 +1039,17 @@
     void SetPanId(uint16_t aPanId) { GetMetadata().mPanId = aPanId; }
 
     /**
+     * Indicates whether the Destination PAN ID is broadcast.
+     *
+     * This is applicable for messages received over Thread radio.
+     *
+     * @retval TRUE   The Destination PAN ID is broadcast.
+     * @retval FALSE  The Destination PAN ID is not broadcast.
+     *
+     */
+    bool IsDstPanIdBroadcast(void) const { return GetMetadata().mIsDstPanIdBroadcast; }
+
+    /**
      * Returns the IEEE 802.15.4 Channel to use for transmission.
      *
      * @note Only use this when sending MLE Announce messages.
@@ -1254,7 +1270,6 @@
      */
     const RssAverager &GetRssAverager(void) const { return GetMetadata().mRssAverager; }
 
-#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
     /**
      * Updates the average LQI (Link Quality Indicator) associated with the message.
      *
@@ -1281,7 +1296,25 @@
      *
      */
     uint8_t GetPsduCount(void) const { return GetMetadata().mLqiAverager.GetCount(); }
-#endif
+
+    /**
+     * Returns a const reference to LqiAverager of the message.
+     *
+     * @returns A const reference to the LqiAverager of the message.
+     *
+     */
+    const LqiAverager &GetLqiAverager(void) const { return GetMetadata().mLqiAverager; }
+
+    /**
+     * Retrieves `ThreadLinkInfo` from the message if received over Thread radio with origin `kOriginThreadNetif`.
+     *
+     * @pram[out] aLinkInfo     A reference to a `ThreadLinkInfo` to populate.
+     *
+     * @retval kErrorNone       Successfully retrieved the link info, @p `aLinkInfo` is updated.
+     * @retval kErrorNotFound   Message origin is not `kOriginThreadNetif`.
+     *
+     */
+    Error GetLinkInfo(ThreadLinkInfo &aLinkInfo) const;
 
     /**
      * Sets the message's link info properties (PAN ID, link security, RSS) from a given `ThreadLinkInfo`.
@@ -1289,7 +1322,7 @@
      * @param[in] aLinkInfo   The `ThreadLinkInfo` instance from which to set message's related properties.
      *
      */
-    void SetLinkInfo(const ThreadLinkInfo &aLinkInfo);
+    void UpdateLinkInfoFrom(const ThreadLinkInfo &aLinkInfo);
 
     /**
      * Returns a pointer to the message queue (if any) where this message is queued.
@@ -1488,6 +1521,9 @@
     void SetMessageQueue(MessageQueue *aMessageQueue);
     void SetPriorityQueue(PriorityQueue *aPriorityQueue);
 
+    void SetRssAverager(const RssAverager &aRssAverager) { GetMetadata().mRssAverager = aRssAverager; }
+    void SetLqiAverager(const LqiAverager &aLqiAverager) { GetMetadata().mLqiAverager = aLqiAverager; }
+
     Message       *&Next(void) { return GetMetadata().mNext; }
     Message *const &Next(void) const { return GetMetadata().mNext; }
     Message       *&Prev(void) { return GetMetadata().mPrev; }
diff --git a/src/core/common/settings.cpp b/src/core/common/settings.cpp
index 504eb7c..3e1f860 100644
--- a/src/core/common/settings.cpp
+++ b/src/core/common/settings.cpp
@@ -106,7 +106,7 @@
 }
 #endif
 
-#if OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE
+#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE && OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE
 void SettingsBase::BorderAgentId::Log(Action aAction) const
 {
     char         buffer[sizeof(BorderAgentId) * 2 + 1];
@@ -115,7 +115,7 @@
     sw.AppendHexBytes(GetId().mId, sizeof(BorderAgentId));
     LogInfo("%s BorderAgentId {id:%s}", ActionToString(aAction), buffer);
 }
-#endif // OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE
+#endif // OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE && OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE
 
 #endif // OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
 
@@ -531,7 +531,7 @@
             break;
 #endif
 
-#if OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE
+#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE && OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE
         case kKeyBorderAgentId:
             reinterpret_cast<const BorderAgentId *>(aValue)->Log(aAction);
             break;
diff --git a/src/core/common/settings.hpp b/src/core/common/settings.hpp
index 199eb9f..1f5a33a 100644
--- a/src/core/common/settings.hpp
+++ b/src/core/common/settings.hpp
@@ -417,7 +417,7 @@
          */
         void Init(void)
         {
-            memset(this, 0, sizeof(*this));
+            ClearAllBytes(*this);
             SetVersion(kThreadVersion1p1);
         }
 
@@ -767,7 +767,7 @@
     } OT_TOOL_PACKED_END;
 #endif // OPENTHREAD_CONFIG_SRP_SERVER_ENABLE && OPENTHREAD_CONFIG_SRP_SERVER_PORT_SWITCH_ENABLE
 
-#if OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE
+#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE && OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE
     /**
      * Represents the Border Agent ID.
      *
@@ -784,7 +784,7 @@
          * Initializes the `BorderAgentId` object.
          *
          */
-        void Init(void) { mId = {}; }
+        void Init(void) { ClearAllBytes(mId); }
 
         /**
          * Returns the Border Agent ID.
@@ -813,7 +813,7 @@
 
         MeshCoP::BorderAgent::Id mId;
     } OT_TOOL_PACKED_END;
-#endif // OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE
+#endif // OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE && OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE
 
 protected:
     explicit SettingsBase(Instance &aInstance)
diff --git a/src/core/common/string.cpp b/src/core/common/string.cpp
index 4019628..c759110 100644
--- a/src/core/common/string.cpp
+++ b/src/core/common/string.cpp
@@ -89,13 +89,16 @@
 
 uint16_t StringLength(const char *aString, uint16_t aMaxLength)
 {
-    uint16_t ret;
+    uint16_t ret = 0;
 
-    for (ret = 0; (ret < aMaxLength) && (aString[ret] != kNullChar); ret++)
+    VerifyOrExit(aString != nullptr);
+
+    for (; (ret < aMaxLength) && (aString[ret] != kNullChar); ret++)
     {
         // Empty loop.
     }
 
+exit:
     return ret;
 }
 
diff --git a/src/core/common/string.hpp b/src/core/common/string.hpp
index 0c73bb6..358d7f8 100644
--- a/src/core/common/string.hpp
+++ b/src/core/common/string.hpp
@@ -85,8 +85,8 @@
  * @param[in] aString      A pointer to the string.
  * @param[in] aMaxLength   The maximum length in bytes.
  *
- * @returns The number of characters that precede the terminating null character or @p aMaxLength, whichever is
- *          smaller.
+ * @returns The number of characters that precede the terminating null character or @p aMaxLength,
+ *          whichever is smaller. `0` if @p aString is `nullptr`.
  *
  */
 uint16_t StringLength(const char *aString, uint16_t aMaxLength);
diff --git a/src/core/common/tasklet.hpp b/src/core/common/tasklet.hpp
index 48a3f07..5158bdd 100644
--- a/src/core/common/tasklet.hpp
+++ b/src/core/common/tasklet.hpp
@@ -45,8 +45,6 @@
 
 namespace ot {
 
-class TaskletScheduler;
-
 /**
  * @addtogroup core-tasklet
  *
diff --git a/src/core/config/announce_sender.h b/src/core/config/announce_sender.h
index b71d88e..7d6b8ae 100644
--- a/src/core/config/announce_sender.h
+++ b/src/core/config/announce_sender.h
@@ -28,7 +28,7 @@
 
 /**
  * @file
- *   This file includes compile-time configurations for the DNS Client.
+ *   This file includes compile-time configurations for the Announce Sender.
  *
  */
 
@@ -36,6 +36,16 @@
 #define CONFIG_ANNOUNCE_SENDER_H_
 
 /**
+ * @addtogroup config-announce-sender
+ *
+ * @brief
+ *   This module includes configuration variables for the Announce Sender.
+ *
+ * @{
+ *
+ */
+
+/**
  * @def OPENTHREAD_CONFIG_ANNOUNCE_SENDER_ENABLE
  *
  * Define as 1 to enable `AnnounceSender` which will periodically send MLE Announce message on all channels.
@@ -90,4 +100,9 @@
 #define OPENTHREAD_CONFIG_ANNOUNCE_SENDER_JITTER_INTERVAL 500
 #endif
 
+/**
+ * @}
+ *
+ */
+
 #endif // CONFIG_ANNOUNCE_SENDER_H_
diff --git a/src/core/config/backbone_router.h b/src/core/config/backbone_router.h
index e2bdbd3..398e005 100644
--- a/src/core/config/backbone_router.h
+++ b/src/core/config/backbone_router.h
@@ -36,6 +36,16 @@
 #define CONFIG_BACKBONE_ROUTER_H_
 
 /**
+ * @addtogroup config-backbone-router
+ *
+ * @brief
+ *   This module includes configuration variables for Backbone Router services.
+ *
+ * @{
+ *
+ */
+
+/**
  * @def OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
  *
  * Define to 1 to enable Backbone Router support.
@@ -113,4 +123,9 @@
 #define OPENTHREAD_CONFIG_NDPROXY_TABLE_ENTRY_NUM 250
 #endif
 
+/**
+ * @}
+ *
+ */
+
 #endif // CONFIG_BACKBONE_ROUTER_H_
diff --git a/src/core/config/border_agent.h b/src/core/config/border_agent.h
index c7bb8df..ab3925e 100644
--- a/src/core/config/border_agent.h
+++ b/src/core/config/border_agent.h
@@ -36,6 +36,16 @@
 #define CONFIG_BORDER_AGENT_H_
 
 /**
+ * @addtogroup config-border-agent
+ *
+ * @brief
+ *   This module includes configuration variables for Border Agent.
+ *
+ * @{
+ *
+ */
+
+/**
  * @def OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
  *
  * Define to 1 to enable Border Agent support.
@@ -62,7 +72,22 @@
  *
  */
 #ifndef OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE
-#define OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE 0
+#define OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE 1
 #endif
 
+/**
+ * @def OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
+ *
+ * Define to 1 to enable ephemeral key mechanism and its APIs in Border Agent.
+ *
+ */
+#ifndef OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
+#define OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_4)
+#endif
+
+/**
+ * @}
+ *
+ */
+
 #endif // CONFIG_BORDER_AGENT_H_
diff --git a/src/core/config/border_router.h b/src/core/config/border_router.h
index 543ea8f..d158674 100644
--- a/src/core/config/border_router.h
+++ b/src/core/config/border_router.h
@@ -35,6 +35,16 @@
 #ifndef CONFIG_BORDER_ROUTER_H_
 #define CONFIG_BORDER_ROUTER_H_
 
+/**
+ * @addtogroup config-border-router
+ *
+ * @brief
+ *   This module includes configuration variables for Border Router services.
+ *
+ * @{
+ *
+ */
+
 #include <config/border_routing.h>
 
 /**
@@ -83,4 +93,9 @@
 #define OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
 #endif
 
+/**
+ * @}
+ *
+ */
+
 #endif // CONFIG_BORDER_ROUTER_H_
diff --git a/src/core/config/border_routing.h b/src/core/config/border_routing.h
index 4640a08..344cde4 100644
--- a/src/core/config/border_routing.h
+++ b/src/core/config/border_routing.h
@@ -36,6 +36,16 @@
 #define CONFIG_BORDER_ROUTING_H_
 
 /**
+ * @addtogroup config-border-routing
+ *
+ * @brief
+ *   This module includes configuration variables for Border Routing Manager.
+ *
+ * @{
+ *
+ */
+
+/**
  * @def OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
  *
  * Define to 1 to enable Border Routing Manager feature.
@@ -162,4 +172,9 @@
 #define OPENTHREAD_CONFIG_BORDER_ROUTING_MOCK_PLAT_APIS_ENABLE 0
 #endif
 
+/**
+ * @}
+ *
+ */
+
 #endif // CONFIG_BORDER_ROUTING_H_
diff --git a/src/core/config/channel_manager.h b/src/core/config/channel_manager.h
index 0057b66..4ecb3dc 100644
--- a/src/core/config/channel_manager.h
+++ b/src/core/config/channel_manager.h
@@ -36,6 +36,16 @@
 #define CONFIG_CHANNEL_MANAGER_H_
 
 /**
+ * @addtogroup config-channel-manager
+ *
+ * @brief
+ *   This module includes configuration variables for Channel Manager.
+ *
+ * @{
+ *
+ */
+
+/**
  * @def OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE
  *
  * Define as 1 to enable Channel Manager support.
@@ -46,6 +56,18 @@
 #endif
 
 /**
+ * @def OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE
+ *
+ * Define as 1 to enable Channel Manager support for selecting CSL channels.
+ *
+ * `OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE` must be enabled in addition.
+ *
+ */
+#ifndef OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE
+#define OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE 0
+#endif
+
+/**
  * @def OPENTHREAD_CONFIG_CHANNEL_MANAGER_MINIMUM_DELAY
  *
  * The minimum delay (in seconds) used by Channel Manager module for performing a channel change.
@@ -136,4 +158,9 @@
 #define OPENTHREAD_CONFIG_CHANNEL_MANAGER_CCA_FAILURE_THRESHOLD (0xffff * 14 / 100)
 #endif
 
+/**
+ * @}
+ *
+ */
+
 #endif // CONFIG_CHANNEL_MANAGER_H_
diff --git a/src/core/config/channel_monitor.h b/src/core/config/channel_monitor.h
index 92b263e..312b168 100644
--- a/src/core/config/channel_monitor.h
+++ b/src/core/config/channel_monitor.h
@@ -36,6 +36,16 @@
 #define CONFIG_CHANNEL_MONITOR_H_
 
 /**
+ * @addtogroup config-channel-monitor
+ *
+ * @brief
+ *   This module includes configuration variables for Channel Monitor.
+ *
+ * @{
+ *
+ */
+
+/**
  * @def OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
  *
  * Define to 1 to enable Channel Monitor support.
@@ -92,4 +102,9 @@
 #define OPENTHREAD_CONFIG_CHANNEL_MONITOR_SAMPLE_WINDOW 960
 #endif
 
+/**
+ * @}
+ *
+ */
+
 #endif // CONFIG_CHANNEL_MONITOR_H_
diff --git a/src/core/config/child_supervision.h b/src/core/config/child_supervision.h
index 94e8ec6..a072949 100644
--- a/src/core/config/child_supervision.h
+++ b/src/core/config/child_supervision.h
@@ -36,6 +36,16 @@
 #define CONFIG_CHILD_SUPERVISION_H_
 
 /**
+ * @addtogroup config-child-supervision
+ *
+ * @brief
+ *   This module includes configuration variables for Child Supervision.
+ *
+ * @{
+ *
+ */
+
+/**
  * @def OPENTHREAD_CONFIG_CHILD_SUPERVISION_INTERVAL
  *
  * The default supervision interval in seconds to use when in child state. Zero indicates no supervision needed.
@@ -86,4 +96,9 @@
 #define OPENTHREAD_CONFIG_CHILD_SUPERVISION_OLDER_VERSION_CHILD_DEFAULT_INTERVAL 129
 #endif
 
+/**
+ * @}
+ *
+ */
+
 #endif // CONFIG_CHILD_SUPERVISION_H_
diff --git a/src/core/config/coap.h b/src/core/config/coap.h
index c3ee709..518da39 100644
--- a/src/core/config/coap.h
+++ b/src/core/config/coap.h
@@ -36,6 +36,16 @@
 #define CONFIG_COAP_H_
 
 /**
+ * @addtogroup config-coap
+ *
+ * @brief
+ *   This module includes configuration variables for CoAP.
+ *
+ * @{
+ *
+ */
+
+/**
  * @def OPENTHREAD_CONFIG_COAP_SERVER_MAX_CACHED_RESPONSES
  *
  * Maximum number of cached responses for CoAP Confirmable messages.
@@ -97,4 +107,9 @@
 #define OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE 0
 #endif
 
+/**
+ * @}
+ *
+ */
+
 #endif // CONFIG_COAP_H_
diff --git a/src/core/config/commissioner.h b/src/core/config/commissioner.h
index ff43adb..9a8d961 100644
--- a/src/core/config/commissioner.h
+++ b/src/core/config/commissioner.h
@@ -36,6 +36,16 @@
 #define CONFIG_COMMISSIONER_H_
 
 /**
+ * @addtogroup config-commissioner
+ *
+ * @brief
+ *   This module includes configuration variables for the Commissioner.
+ *
+ * @{
+ *
+ */
+
+/**
  * @def OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
  *
  * Define to 1 to enable Commissioner support.
@@ -66,4 +76,9 @@
 #define OPENTHREAD_CONFIG_COMMISSIONER_JOINER_SESSION_TIMEOUT 30
 #endif
 
+/**
+ * @}
+ *
+ */
+
 #endif // CONFIG_COMMISSIONER_H_
diff --git a/src/core/config/crypto.h b/src/core/config/crypto.h
index b7fb5c3..b60520c 100644
--- a/src/core/config/crypto.h
+++ b/src/core/config/crypto.h
@@ -30,6 +30,16 @@
 #define CONFIG_CRYPTO_H_
 
 /**
+ * @addtogroup config-crypto
+ *
+ * @brief
+ *   This module includes configuration variables for the Crypto Backend Library.
+ *
+ * @{
+ *
+ */
+
+/**
  * @def OPENTHREAD_CONFIG_CRYPTO_LIB
  *
  * Selects the crypto backend library for OpenThread.
@@ -95,4 +105,9 @@
 
 #endif // OPENTHREAD_CONFIG_CRYPTO_LIB == OPENTHREAD_CONFIG_CRYPTO_LIB_PLATFORM
 
+/**
+ * @}
+ *
+ */
+
 #endif // CONFIG_CRYPTO_H_
diff --git a/src/core/config/dataset_updater.h b/src/core/config/dataset_updater.h
index 4055748..2543afe 100644
--- a/src/core/config/dataset_updater.h
+++ b/src/core/config/dataset_updater.h
@@ -36,6 +36,16 @@
 #define CONFIG_DATASET_UPDATER_H_
 
 /**
+ * @addtogroup config-dataset-updater
+ *
+ * @brief
+ *   This module includes configuration variables for Dataset Updater.
+ *
+ * @{
+ *
+ */
+
+/**
  * @def OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE
  *
  * Define as 1 to enable Dataset Updater support.
@@ -67,4 +77,9 @@
 #define OPENTHREAD_CONFIG_DATASET_UPDATER_DEFAULT_RETRY_WAIT_INTERVAL 1500
 #endif
 
+/**
+ * @}
+ *
+ */
+
 #endif // CONFIG_DATASET_UPDATER_H_
diff --git a/src/core/config/dhcp6_client.h b/src/core/config/dhcp6_client.h
index 9b50636..2bf1b6a 100644
--- a/src/core/config/dhcp6_client.h
+++ b/src/core/config/dhcp6_client.h
@@ -36,6 +36,16 @@
 #define CONFIG_DHCP6_CLIENT_H_
 
 /**
+ * @addtogroup config-dhcpv6-client
+ *
+ * @brief
+ *   This module includes configuration variables for the DHCPv6 client.
+ *
+ * @{
+ *
+ */
+
+/**
  * @def OPENTHREAD_CONFIG_DHCP6_CLIENT_ENABLE
  *
  * Define to 1 to enable DHCPv6 Client support.
@@ -65,4 +75,9 @@
 #define OPENTHREAD_CONFIG_DHCP6_CLIENT_NUM_PREFIXES 4
 #endif
 
+/**
+ * @}
+ *
+ */
+
 #endif // CONFIG_DHCP6_CLIENT_H_
diff --git a/src/core/config/dhcp6_server.h b/src/core/config/dhcp6_server.h
index d833c6d..8e01fb4 100644
--- a/src/core/config/dhcp6_server.h
+++ b/src/core/config/dhcp6_server.h
@@ -36,6 +36,16 @@
 #define CONFIG_DHCP6_SERVER_H_
 
 /**
+ * @addtogroup config-dhcpv6-server
+ *
+ * @brief
+ *   This module includes configuration variables for the DHCPv6 server.
+ *
+ * @{
+ *
+ */
+
+/**
  * @def OPENTHREAD_CONFIG_DHCP6_SERVER_ENABLE
  *
  * Define to 1 to enable DHCPv6 Server support.
@@ -55,4 +65,9 @@
 #define OPENTHREAD_CONFIG_DHCP6_SERVER_NUM_PREFIXES 4
 #endif
 
+/**
+ * @}
+ *
+ */
+
 #endif // CONFIG_DHCP6_SERVER_H_
diff --git a/src/core/config/diag.h b/src/core/config/diag.h
index a38d547..03261c7 100644
--- a/src/core/config/diag.h
+++ b/src/core/config/diag.h
@@ -36,6 +36,16 @@
 #define CONFIG_DIAG_H_
 
 /**
+ * @addtogroup config-diag
+ *
+ * @brief
+ *   This module includes configuration variables for the DIAG service.
+ *
+ * @{
+ *
+ */
+
+/**
  * @def OPENTHREAD_CONFIG_DIAG_ENABLE
  *
  * Define to 1 to enable Factory Diagnostics support.
@@ -75,4 +85,9 @@
 #define OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE 256
 #endif
 
+/**
+ * @}
+ *
+ */
+
 #endif // CONFIG_DIAG_H_
diff --git a/src/core/config/dns_client.h b/src/core/config/dns_client.h
index d727261..6db961d 100644
--- a/src/core/config/dns_client.h
+++ b/src/core/config/dns_client.h
@@ -35,6 +35,16 @@
 #ifndef CONFIG_DNS_CLIENT_H_
 #define CONFIG_DNS_CLIENT_H_
 
+/**
+ * @addtogroup config-dns-client
+ *
+ * @brief
+ *   This module includes configuration variables for the DNS client.
+ *
+ * @{
+ *
+ */
+
 #include "config/ip6.h"
 #include "config/srp_client.h"
 
@@ -181,4 +191,9 @@
 #define OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_QUERY_MAX_SIZE 1024
 #endif
 
+/**
+ * @}
+ *
+ */
+
 #endif // CONFIG_DNS_CLIENT_H_
diff --git a/src/core/config/dns_dso.h b/src/core/config/dns_dso.h
index 8b7d5a5..9b5ed57 100644
--- a/src/core/config/dns_dso.h
+++ b/src/core/config/dns_dso.h
@@ -36,6 +36,16 @@
 #define CONFIG_DNS_DSO_H_
 
 /**
+ * @addtogroup config-dns-dso
+ *
+ * @brief
+ *   This module includes configuration variables for DNS Stateful Operations.
+ *
+ * @{
+ *
+ */
+
+/**
  * @def OPENTHREAD_CONFIG_DNS_DSO_ENABLE
  *
  * Define to 1 to enable DSO support.
@@ -87,4 +97,9 @@
 #define OPENTHREAD_CONFIG_DNS_DSO_MOCK_PLAT_APIS_ENABLE 0
 #endif
 
+/**
+ * @}
+ *
+ */
+
 #endif // CONFIG_DNS_DSO_H_
diff --git a/src/core/config/dnssd_server.h b/src/core/config/dnssd_server.h
index 8d6bb46..36b5620 100644
--- a/src/core/config/dnssd_server.h
+++ b/src/core/config/dnssd_server.h
@@ -36,6 +36,16 @@
 #define CONFIG_DNSSD_SERVER_H_
 
 /**
+ * @addtogroup config-dnssd-server
+ *
+ * @brief
+ *   This module includes configuration variables for the DNS-SD server.
+ *
+ * @{
+ *
+ */
+
+/**
  * @def OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE
  *
  * Define to 1 to enable DNS-SD Server support.
@@ -48,7 +58,7 @@
 /**
  * @def OPENTHREAD_CONFIG_DNSSD_SERVER_PORT
  *
- * Define the the DNS-SD Server port.
+ * Define the DNS-SD Server port.
  *
  */
 #ifndef OPENTHREAD_CONFIG_DNSSD_SERVER_PORT
@@ -98,4 +108,9 @@
 #define OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_MOCK_PLAT_APIS_ENABLE 0
 #endif
 
+/**
+ * @}
+ *
+ */
+
 #endif // CONFIG_DNSSD_SERVER_H_
diff --git a/src/core/config/history_tracker.h b/src/core/config/history_tracker.h
index 591474a..3da4ae8 100644
--- a/src/core/config/history_tracker.h
+++ b/src/core/config/history_tracker.h
@@ -36,6 +36,16 @@
 #define CONFIG_HISTORY_TRACKER_H_
 
 /**
+ * @addtogroup config-history-tracker
+ *
+ * @brief
+ *   This module includes configuration variables for History Tracker.
+ *
+ * @{
+ *
+ */
+
+/**
  * @def OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
  *
  * Define as 1 to enable History Tracker module.
@@ -163,4 +173,9 @@
 #define OPENTHREAD_CONFIG_HISTORY_TRACKER_EXTERNAL_ROUTE_LIST_SIZE 32
 #endif
 
+/**
+ * @}
+ *
+ */
+
 #endif // CONFIG_HISTORY_TRACKER_H_
diff --git a/src/core/config/ip6.h b/src/core/config/ip6.h
index 8f349f1..f16f8a3 100644
--- a/src/core/config/ip6.h
+++ b/src/core/config/ip6.h
@@ -35,7 +35,18 @@
 #ifndef CONFIG_IP6_H_
 #define CONFIG_IP6_H_
 
+/**
+ * @addtogroup config-ip6
+ *
+ * @brief
+ *   This module includes configuration variables for the IP6 service.
+ *
+ * @{
+ *
+ */
+
 #include "config/border_routing.h"
+#include "config/misc.h"
 
 /**
  * @def OPENTHREAD_CONFIG_IP6_MAX_EXT_UCAST_ADDRS
@@ -130,6 +141,20 @@
 #endif
 
 /**
+ * @def OPENTHREAD_CONFIG_IP6_SLAAC_DEPRECATION_INTERVAL
+ *
+ * Specifies the deprecating time of SLAAC addresses in seconds.
+ *
+ * Applicable only if OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE is enabled.
+ *
+ * Deprecating interval is used once an on-mesh prefix is removed from Network Data before removing the SLAAC address.
+ *
+ */
+#ifndef OPENTHREAD_CONFIG_IP6_SLAAC_DEPRECATION_INTERVAL
+#define OPENTHREAD_CONFIG_IP6_SLAAC_DEPRECATION_INTERVAL 300
+#endif
+
+/**
  * @def OPENTHREAD_CONFIG_MPL_SEED_SET_ENTRIES
  *
  * The number of MPL Seed Set entries for duplicate detection.
@@ -179,7 +204,7 @@
  *
  */
 #ifndef OPENTHREAD_CONFIG_TLS_ENABLE
-#define OPENTHREAD_CONFIG_TLS_ENABLE OPENTHREAD_CONFIG_TCP_ENABLE
+#define OPENTHREAD_CONFIG_TLS_ENABLE (OPENTHREAD_CONFIG_TCP_ENABLE || OPENTHREAD_CONFIG_BLE_TCAT_ENABLE)
 #endif
 
 /**
@@ -202,4 +227,19 @@
 #define OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
 #endif
 
+/**
+ * @def OPENTHREAD_CONFIG_IP6_RESTRICT_FORWARDING_LARGER_SCOPE_MCAST_WITH_LOCAL_SRC
+ *
+ * Define as 1 to restrict multicast forwarding to larger scope from local sources.
+ *
+ */
+#ifndef OPENTHREAD_CONFIG_IP6_RESTRICT_FORWARDING_LARGER_SCOPE_MCAST_WITH_LOCAL_SRC
+#define OPENTHREAD_CONFIG_IP6_RESTRICT_FORWARDING_LARGER_SCOPE_MCAST_WITH_LOCAL_SRC 0
+#endif
+
+/**
+ * @}
+ *
+ */
+
 #endif // CONFIG_IP6_H_
diff --git a/src/core/config/joiner.h b/src/core/config/joiner.h
index c01dc30..fdf2bef 100644
--- a/src/core/config/joiner.h
+++ b/src/core/config/joiner.h
@@ -36,6 +36,16 @@
 #define CONFIG_JOINER_H_
 
 /**
+ * @addtogroup config-joiner
+ *
+ * @brief
+ *   This module includes configuration variables for the Joiner.
+ *
+ * @{
+ *
+ */
+
+/**
  * @def OPENTHREAD_CONFIG_JOINER_ENABLE
  *
  * Define to 1 to enable Joiner support.
@@ -55,4 +65,9 @@
 #define OPENTHREAD_CONFIG_JOINER_MAX_CANDIDATES 2
 #endif
 
+/**
+ * @}
+ *
+ */
+
 #endif // CONFIG_JOINER_H_
diff --git a/src/core/config/link_metrics_manager.h b/src/core/config/link_metrics_manager.h
index 96fed11..8ce7311 100644
--- a/src/core/config/link_metrics_manager.h
+++ b/src/core/config/link_metrics_manager.h
@@ -36,6 +36,16 @@
 #define CONFIG_LINK_METRICS_MANAGER_H_
 
 /**
+ * @addtogroup config-link-metrics-manager
+ *
+ * @brief
+ *   This module includes configuration variables for Link Metrics Manager.
+ *
+ * @{
+ *
+ */
+
+/**
  * @def OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
  *
  * Define as 1 to enable Link Metrics Manager support.
@@ -55,4 +65,9 @@
 #define OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ON_BY_DEFAULT 0
 #endif
 
+/**
+ * @}
+ *
+ */
+
 #endif // CONFIG_LINK_METRICS_MANAGER_H_
diff --git a/src/core/config/link_quality.h b/src/core/config/link_quality.h
index f85c1a1..2ed1d51 100644
--- a/src/core/config/link_quality.h
+++ b/src/core/config/link_quality.h
@@ -36,6 +36,16 @@
 #define CONFIG_LINK_QUALITY_H_
 
 /**
+ * @addtogroup config-link-quality
+ *
+ * @brief
+ *   This module includes configuration variables for the Link Quality service.
+ *
+ * @{
+ *
+ */
+
+/**
  * @def OPENTHREAD_CONFIG_CCA_FAILURE_RATE_AVERAGING_WINDOW
  *
  * OpenThread's MAC implementation maintains the average failure rate of CCA (Clear Channel Assessment) operation on
@@ -74,4 +84,9 @@
 #define OPENTHREAD_CONFIG_IPV6_TX_ERR_RATE_AVERAGING_WINDOW 128
 #endif
 
+/**
+ * @}
+ *
+ */
+
 #endif // CONFIG_LINK_QUALITY_H_
diff --git a/src/core/config/link_raw.h b/src/core/config/link_raw.h
index 6c2ceeb..dd7dae2 100644
--- a/src/core/config/link_raw.h
+++ b/src/core/config/link_raw.h
@@ -36,6 +36,16 @@
 #define CONFIG_LINK_RAW_H_
 
 /**
+ * @addtogroup config-link-raw
+ *
+ * @brief
+ *   This module includes configuration variables for the Link Raw service.
+ *
+ * @{
+ *
+ */
+
+/**
  * @def OPENTHREAD_CONFIG_LINK_RAW_ENABLE
  *
  * Define to 1 to enable the Link Raw service.
@@ -45,4 +55,9 @@
 #define OPENTHREAD_CONFIG_LINK_RAW_ENABLE 0
 #endif
 
+/**
+ * @}
+ *
+ */
+
 #endif // CONFIG_LINK_RAW_H_
diff --git a/src/core/config/logging.h b/src/core/config/logging.h
index aacec69..c3d6602 100644
--- a/src/core/config/logging.h
+++ b/src/core/config/logging.h
@@ -36,6 +36,16 @@
 #define CONFIG_LOGGING_H_
 
 /**
+ * @addtogroup config-logging
+ *
+ * @brief
+ *   This module includes configuration variables for the Logging service.
+ *
+ * @{
+ *
+ */
+
+/**
  * @def OPENTHREAD_CONFIG_LOG_OUTPUT
  *
  * Selects if, and where the LOG output goes to.
@@ -184,4 +194,9 @@
 #define OPENTHREAD_CONFIG_LOG_MAX_SIZE 150
 #endif
 
+/**
+ * @}
+ *
+ */
+
 #endif // CONFIG_LOGGING_H_
diff --git a/src/core/config/mac.h b/src/core/config/mac.h
index d0cefa6..ec7e2f9 100644
--- a/src/core/config/mac.h
+++ b/src/core/config/mac.h
@@ -35,6 +35,16 @@
 #ifndef CONFIG_MAC_H_
 #define CONFIG_MAC_H_
 
+/**
+ * @addtogroup config-mac
+ *
+ * @brief
+ *   This module includes configuration variables for MAC.
+ *
+ * @{
+ *
+ */
+
 #include "config/time_sync.h"
 
 /**
@@ -587,4 +597,9 @@
 #define OPENTHREAD_CONFIG_MAC_DATA_POLL_TIMEOUT 100
 #endif
 
+/**
+ * @}
+ *
+ */
+
 #endif // CONFIG_MAC_H_
diff --git a/src/core/config/mesh_diag.h b/src/core/config/mesh_diag.h
index 4049255..962cca8 100644
--- a/src/core/config/mesh_diag.h
+++ b/src/core/config/mesh_diag.h
@@ -35,6 +35,16 @@
 #ifndef CONFIG_MESH_DIAG_H_
 #define CONFIG_MESH_DIAG_H_
 
+/**
+ * @addtogroup config-mesh-diag
+ *
+ * @brief
+ *   This module includes configuration variables for Mesh Diagnostic.
+ *
+ * @{
+ *
+ */
+
 #include "config/border_routing.h"
 
 /**
@@ -59,4 +69,9 @@
 #define OPENTHREAD_CONFIG_MESH_DIAG_RESPONSE_TIMEOUT 5000
 #endif
 
+/**
+ * @}
+ *
+ */
+
 #endif // CONFIG_MESH_DIAG_H_
diff --git a/src/core/config/mesh_forwarder.h b/src/core/config/mesh_forwarder.h
index c5e12d4..f4a83a6 100644
--- a/src/core/config/mesh_forwarder.h
+++ b/src/core/config/mesh_forwarder.h
@@ -34,6 +34,16 @@
 #ifndef CONFIG_MESH_FORWARDER_H_
 #define CONFIG_MESH_FORWARDER_H_
 
+/**
+ * @addtogroup config-mesh-forwarder
+ *
+ * @brief
+ *   This module includes configuration variables for the Mesh Forwarder.
+ *
+ * @{
+ *
+ */
+
 #include "config/border_router.h"
 
 /**
@@ -200,4 +210,9 @@
 #define OPENTHREAD_CONFIG_TX_QUEUE_STATISTICS_HISTOGRAM_BIN_INTERVAL 10
 #endif
 
+/**
+ * @}
+ *
+ */
+
 #endif // CONFIG_MESH_FORWARDER_H_
diff --git a/src/core/config/misc.h b/src/core/config/misc.h
index c9be7ec..70e0c12 100644
--- a/src/core/config/misc.h
+++ b/src/core/config/misc.h
@@ -34,6 +34,16 @@
 #ifndef CONFIG_MISC_H_
 #define CONFIG_MISC_H_
 
+/**
+ * @addtogroup config-misc
+ *
+ * @brief
+ *   This module includes configuration variables for Miscellaneous constants.
+ *
+ * @{
+ *
+ */
+
 #include "config/coap.h"
 #include "config/srp_server.h"
 
@@ -414,9 +424,14 @@
 /**
  * @def OPENTHREAD_CONFIG_DEFAULT_SED_BUFFER_SIZE
  *
- * This setting configures the default buffer size for IPv6 datagram destined for an attached SED.
- * A Thread Router MUST be able to buffer at least one 1280-octet IPv6 datagram for an attached SED according to
- * the Thread Conformance Specification.
+ * Specifies the value used in emitted Connectivity TLV "Rx-off Child Buffer Size" field which indicates the
+ * guaranteed buffer capacity for all IPv6 datagrams destined to a given rx-off-when-idle child.
+ *
+ * Changing this config does not automatically adjust message buffers. Vendors should ensure their device can support
+ * the specified value based on the message buffer model used:
+ *  - OT internal message pool (refer to `OPENTHREAD_CONFIG_NUM_MESSAGE_BUFFERS` and `MESSAGE_BUFFER_SIZE`), or
+ *  - Heap allocated message buffers (refer to `OPENTHREAD_CONFIG_MESSAGE_USE_HEAP_ENABLE),
+ *  - Platform-specific message management (refer to`OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT`).
  *
  */
 #ifndef OPENTHREAD_CONFIG_DEFAULT_SED_BUFFER_SIZE
@@ -426,9 +441,11 @@
 /**
  * @def OPENTHREAD_CONFIG_DEFAULT_SED_DATAGRAM_COUNT
  *
- * This setting configures the default datagram count of 106-octet IPv6 datagram per attached SED.
- * A Thread Router MUST be able to buffer at least one 106-octet IPv6 datagram per attached SED according to
- * the Thread Conformance Specification.
+ * Specifies the value used in emitted Connectivity TLV "Rx-off Child Datagram Count" field which indicates the
+ * guaranteed queue capacity in number of IPv6 datagrams destined to a given rx-off-when-idle child.
+ *
+ * Similar to `OPENTHREAD_CONFIG_DEFAULT_SED_BUFFER_SIZE`, vendors should ensure their device can support the specified
+ * value based on the message buffer model used.
  *
  */
 #ifndef OPENTHREAD_CONFIG_DEFAULT_SED_DATAGRAM_COUNT
@@ -572,4 +589,19 @@
 #define OPENTHREAD_CONFIG_OPERATIONAL_DATASET_AUTO_INIT 0
 #endif
 
+/**
+ * @def OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
+ *
+ * Define to 1 to enable TCAT over BLE support.
+ *
+ */
+#ifndef OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
+#define OPENTHREAD_CONFIG_BLE_TCAT_ENABLE 0
+#endif
+
+/**
+ * @}
+ *
+ */
+
 #endif // CONFIG_MISC_H_
diff --git a/src/core/config/mle.h b/src/core/config/mle.h
index 7a7915a..d09a3cc 100644
--- a/src/core/config/mle.h
+++ b/src/core/config/mle.h
@@ -36,6 +36,16 @@
 #define CONFIG_MLE_H_
 
 /**
+ * @addtogroup config-mle
+ *
+ * @brief
+ *   This module includes configuration variables for the MLE service.
+ *
+ * @{
+ *
+ */
+
+/**
  * @def OPENTHREAD_CONFIG_MLE_MAX_ROUTERS
  *
  * The maximum number of routers in a Thread network.
@@ -94,12 +104,12 @@
  * Define as 1 to enable feature to set device properties which are used for calculating the local leader weight on a
  * device.
  *
- * It is enabled by default on Thread Version 1.3.1 or later.
+ * It is enabled by default on Thread Version 1.4 or later.
  *
  */
 #ifndef OPENTHREAD_CONFIG_MLE_DEVICE_PROPERTY_LEADER_WEIGHT_ENABLE
 #define OPENTHREAD_CONFIG_MLE_DEVICE_PROPERTY_LEADER_WEIGHT_ENABLE \
-    (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_3_1)
+    (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_4)
 #endif
 
 /**
@@ -338,4 +348,9 @@
 #define OPENTHREAD_CONFIG_MLE_LINK_METRICS_SERIES_MTD 2
 #endif
 
+/**
+ * @}
+ *
+ */
+
 #endif // CONFIG_MLE_H_
diff --git a/src/core/config/nat64.h b/src/core/config/nat64.h
index fff5883..f2a7a2b 100644
--- a/src/core/config/nat64.h
+++ b/src/core/config/nat64.h
@@ -36,6 +36,16 @@
 #define CONFIG_NAT64_H_
 
 /**
+ * @addtogroup config-nat64
+ *
+ * @brief
+ *   This module includes configuration variables for NAT64.
+ *
+ * @{
+ *
+ */
+
+/**
  * @def OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
  *
  * Define to 1 to enable the internal NAT64 translator.
@@ -75,4 +85,9 @@
 #define OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE 0
 #endif
 
+/**
+ * @}
+ *
+ */
+
 #endif
diff --git a/src/core/config/netdata_publisher.h b/src/core/config/netdata_publisher.h
index ee8fe58..1286852 100644
--- a/src/core/config/netdata_publisher.h
+++ b/src/core/config/netdata_publisher.h
@@ -35,6 +35,16 @@
 #ifndef CONFIG_NETDATA_PUBLISHER_H_
 #define CONFIG_NETDATA_PUBLISHER_H_
 
+/**
+ * @addtogroup config-netdata-publisher
+ *
+ * @brief
+ *   This module includes configuration variables for Network Data Publisher.
+ *
+ * @{
+ *
+ */
+
 #include "config/border_router.h"
 #include "config/border_routing.h"
 #include "config/srp_server.h"
@@ -156,4 +166,9 @@
 #define OPENTHREAD_CONFIG_NETDATA_PUBLISHER_MAX_PREFIX_ENTRIES 3
 #endif
 
+/**
+ * @}
+ *
+ */
+
 #endif // CONFIG_NETDATA_PUBLISHER_H_
diff --git a/src/core/config/network_diagnostic.h b/src/core/config/network_diagnostic.h
index 2aaf260..6f670ef 100644
--- a/src/core/config/network_diagnostic.h
+++ b/src/core/config/network_diagnostic.h
@@ -36,6 +36,16 @@
 #define CONFIG_NETWORK_DIAGNOSTIC_H_
 
 /**
+ * @addtogroup config-network-diagnostic
+ *
+ * @brief
+ *   This module includes configuration variables for Network Diagnostics.
+ *
+ * @{
+ *
+ */
+
+/**
  * @def OPENTHREAD_CONFIG_NET_DIAG_VENDOR_NAME
  *
  * Specifies the default Vendor Name string.
@@ -66,6 +76,16 @@
 #endif
 
 /**
+ * @def OPENTHREAD_CONFIG_NET_DIAG_VENDOR_APP_URL
+ *
+ * Specifies the default Vendor App URL string.
+ *
+ */
+#ifndef OPENTHREAD_CONFIG_NET_DIAG_VENDOR_APP_URL
+#define OPENTHREAD_CONFIG_NET_DIAG_VENDOR_APP_URL ""
+#endif
+
+/**
  * @def OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE
  *
  * Define as 1 to add APIs to allow Vendor Name, Model, SW Version to change at run-time.
@@ -82,4 +102,9 @@
 #define OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE 0
 #endif
 
+/**
+ * @}
+ *
+ */
+
 #endif // CONFIG_NETWORK_DIAGNOSTIC_H_
diff --git a/src/core/config/openthread-core-config-check.h b/src/core/config/openthread-core-config-check.h
index 5d26a6e..de4fe15 100644
--- a/src/core/config/openthread-core-config-check.h
+++ b/src/core/config/openthread-core-config-check.h
@@ -122,7 +122,7 @@
 #endif
 
 #ifdef OPENTHREAD_ENABLE_DTLS
-#error "OPENTHREAD_ENABLE_DTLS was replaced by OPENTHREAD_CONFIG_DTLS_ENABLE."
+#error "OPENTHREAD_ENABLE_DTLS was replaced by OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE."
 #endif
 
 #ifdef OPENTHREAD_ENABLE_JAM_DETECTION
diff --git a/src/core/config/parent_search.h b/src/core/config/parent_search.h
index 9c78bc0..c5a978b 100644
--- a/src/core/config/parent_search.h
+++ b/src/core/config/parent_search.h
@@ -36,6 +36,16 @@
 #define CONFIG_PARENT_SEARCH_H_
 
 /**
+ * @addtogroup config-parent-search
+ *
+ * @brief
+ *   This module includes configuration variables for Parent Search.
+ *
+ * @{
+ *
+ */
+
+/**
  * @def OPENTHREAD_CONFIG_PARENT_SEARCH_ENABLE
  *
  * Define as 1 to enable periodic parent search feature.
@@ -93,4 +103,9 @@
 #define OPENTHREAD_CONFIG_PARENT_SEARCH_RSS_THRESHOLD -65
 #endif
 
+/**
+ * @}
+ *
+ */
+
 #endif // CONFIG_PARENT_SEARCH_H_
diff --git a/src/core/config/ping_sender.h b/src/core/config/ping_sender.h
index d655b1d..7c5d25d 100644
--- a/src/core/config/ping_sender.h
+++ b/src/core/config/ping_sender.h
@@ -36,6 +36,16 @@
 #define CONFIG_PING_SENDER_H_
 
 /**
+ * @addtogroup config-ping-sender
+ *
+ * @brief
+ *   This module includes configuration variables for Ping Sender.
+ *
+ * @{
+ *
+ */
+
+/**
  * @def OPENTHREAD_CONFIG_PING_SENDER_ENABLE
  *
  * Define to 1 to enable ping sender module.
@@ -89,4 +99,9 @@
 #define OPENTHREAD_CONFIG_PING_SENDER_DEFAULT_COUNT 1
 #endif
 
+/**
+ * @}
+ *
+ */
+
 #endif // CONFIG_PING_SENDER_H_
diff --git a/src/core/config/platform.h b/src/core/config/platform.h
index 9fa475b..a3ebf23 100644
--- a/src/core/config/platform.h
+++ b/src/core/config/platform.h
@@ -35,6 +35,18 @@
 #ifndef CONFIG_PLATFORM_H_
 #define CONFIG_PLATFORM_H_
 
+#include "config/srp_server.h"
+
+/**
+ * @addtogroup config-platform
+ *
+ * @brief
+ *   This module includes configuration variables for platform-specific services.
+ *
+ * @{
+ *
+ */
+
 /**
  * @def OPENTHREAD_CONFIG_PLATFORM_INFO
  *
@@ -98,6 +110,16 @@
 #endif
 
 /**
+ * @def OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE
+ *
+ * Define as 1 to enable DNSSD (mDNS) platform module.
+ *
+ */
+#ifndef OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE
+#define OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE
+#endif
+
+/**
  * @def OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
  *
  * Define to 1 if you want to enable radio coexistence implemented in platform.
@@ -186,4 +208,9 @@
 #endif
 #endif
 
+/**
+ * @}
+ *
+ */
+
 #endif // CONFIG_PLATFORM_H_
diff --git a/src/core/config/power_calibration.h b/src/core/config/power_calibration.h
index 56e9467..2b79684 100644
--- a/src/core/config/power_calibration.h
+++ b/src/core/config/power_calibration.h
@@ -36,6 +36,16 @@
 #define CONFIG_POWER_CALIBRATION_H_
 
 /**
+ * @addtogroup config-power-calibration
+ *
+ * @brief
+ *   This module includes configuration variables for Power Calibration.
+ *
+ * @{
+ *
+ */
+
+/**
  * @def OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE
  *
  * Define as 1 to enable power calibration support.
@@ -65,4 +75,9 @@
 #define OPENTHREAD_CONFIG_POWER_CALIBRATION_NUM_CALIBRATED_POWER_ENTRIES 6
 #endif
 
+/**
+ * @}
+ *
+ */
+
 #endif // CONFIG_POWER_CALIBRATION_H_
diff --git a/src/core/config/radio_link.h b/src/core/config/radio_link.h
index 14e9a12..d56f9db 100644
--- a/src/core/config/radio_link.h
+++ b/src/core/config/radio_link.h
@@ -36,6 +36,16 @@
 #define CONFIG_RADIO_LINK_H_
 
 /**
+ * @addtogroup config-radio-link
+ *
+ * @brief
+ *   This module includes configuration variables for radio links.
+ *
+ * @{
+ *
+ */
+
+/**
  * @def OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
  *
  * Set to 1 to enable support for IEEE802.15.4 radio link.
@@ -93,4 +103,9 @@
 #define OPENTHREAD_CONFIG_MULTI_RADIO_FRAG_TAG_TIMEOUT (20 * 1000)
 #endif
 
+/**
+ * @}
+ *
+ */
+
 #endif // CONFIG_RADIO_LINK_H_
diff --git a/src/core/config/dtls.h b/src/core/config/secure_transport.h
similarity index 74%
rename from src/core/config/dtls.h
rename to src/core/config/secure_transport.h
index 09e8337..eb636ba 100644
--- a/src/core/config/dtls.h
+++ b/src/core/config/secure_transport.h
@@ -28,12 +28,22 @@
 
 /**
  * @file
- *   This file includes compile-time configurations for DTLS.
+ *   This file includes compile-time configurations for TLS/DTLS.
  *
  */
 
-#ifndef CONFIG_DTLS_H_
-#define CONFIG_DTLS_H_
+#ifndef CONFIG_SECURE_TRANSPORT_H_
+#define CONFIG_SECURE_TRANSPORT_H_
+
+/**
+ * @addtogroup config-secure-transport
+ *
+ * @brief
+ *   This module includes configuration variables for Secure Transport.
+ *
+ * @{
+ *
+ */
 
 #include "config/border_agent.h"
 #include "config/coap.h"
@@ -51,15 +61,24 @@
 #endif
 
 /**
- * @def OPENTHREAD_CONFIG_DTLS_ENABLE
+ * @def OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE
  *
- *  Define to 1 to enable DTLS.
+ *  Define to 1 to enable DTLS/TLS.
  *
  */
-#ifndef OPENTHREAD_CONFIG_DTLS_ENABLE
-#define OPENTHREAD_CONFIG_DTLS_ENABLE                                                     \
+#ifndef OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE
+#define OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE                                         \
     (OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE || OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE || \
-     OPENTHREAD_CONFIG_COMMISSIONER_ENABLE || OPENTHREAD_CONFIG_JOINER_ENABLE)
+     OPENTHREAD_CONFIG_COMMISSIONER_ENABLE || OPENTHREAD_CONFIG_JOINER_ENABLE || OPENTHREAD_CONFIG_BLE_TCAT_ENABLE)
 #endif
 
-#endif // CONFIG_DTLS_H_
+#ifdef OPENTHREAD_CONFIG_DTLS_ENABLE
+#error "OPENTHREAD_CONFIG_DTLS_ENABLE is deprecated please use OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE instead"
+#endif
+
+/**
+ * @}
+ *
+ */
+
+#endif // CONFIG_SECURE_TRANSPORT_H_
diff --git a/src/core/config/sntp_client.h b/src/core/config/sntp_client.h
index bd8d5fa..48079a8 100644
--- a/src/core/config/sntp_client.h
+++ b/src/core/config/sntp_client.h
@@ -36,6 +36,16 @@
 #define CONFIG_SNTP_CLIENT_H_
 
 /**
+ * @addtogroup config-sntp-client
+ *
+ * @brief
+ *   This module includes configuration variables for the SNTP Client.
+ *
+ * @{
+ *
+ */
+
+/**
  * @def OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE
  *
  * Define to 1 to enable SNTP Client support.
@@ -65,4 +75,9 @@
 #define OPENTHREAD_CONFIG_SNTP_CLIENT_MAX_RETRANSMIT 2
 #endif
 
+/**
+ * @}
+ *
+ */
+
 #endif // CONFIG_SNTP_CLIENT_H_
diff --git a/src/core/config/srp_client.h b/src/core/config/srp_client.h
index ca609ab..34c9abf 100644
--- a/src/core/config/srp_client.h
+++ b/src/core/config/srp_client.h
@@ -35,6 +35,16 @@
 #ifndef CONFIG_SRP_CLIENT_H_
 #define CONFIG_SRP_CLIENT_H_
 
+/**
+ * @addtogroup config-srp-client
+ *
+ * @brief
+ *   This module includes configuration variables for the SRP Client.
+ *
+ * @{
+ *
+ */
+
 #include "config/misc.h"
 
 /**
@@ -69,7 +79,7 @@
  *
  */
 #ifndef OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_DEFAULT_MODE
-#define OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_DEFAULT_MODE 0
+#define OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_DEFAULT_MODE 1
 #endif
 
 /**
@@ -274,7 +284,7 @@
  * @def OPENTHREAD_CONFIG_SRP_CLIENT_RETRY_WAIT_INTERVAL_JITTER
  *
  * Specifies jitter (in msec) for retry wait interval. If the current retry wait interval is smaller than the jitter
- * then the the wait interval itself is used as jitter (e.g., with jitter 500 msec and if retry interval is 300ms
+ * then the wait interval itself is used as jitter (e.g., with jitter 500 msec and if retry interval is 300ms
  * the retry interval is then randomly selected from [0, 2*300] ms).
  *
  */
@@ -402,4 +412,9 @@
 #define OPENTHREAD_CONFIG_SRP_CLIENT_BUFFERS_TXT_BUFFER_SIZE 64
 #endif
 
+/**
+ * @}
+ *
+ */
+
 #endif // CONFIG_SRP_CLIENT_H_
diff --git a/src/core/config/srp_server.h b/src/core/config/srp_server.h
index 5b76ec3..7e6cd18 100644
--- a/src/core/config/srp_server.h
+++ b/src/core/config/srp_server.h
@@ -36,6 +36,16 @@
 #define CONFIG_SRP_SERVER_H_
 
 /**
+ * @addtogroup config-srp-server
+ *
+ * @brief
+ *   This module includes configuration variables for the SRP Server.
+ *
+ * @{
+ *
+ */
+
+/**
  * @def OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
  *
  * Define to 1 to enable SRP Server support.
@@ -109,4 +119,19 @@
 #define OPENTHREAD_CONFIG_SRP_SERVER_SERVICE_UPDATE_TIMEOUT ((4 * 250u) + 250u)
 #endif
 
+/**
+ * @def OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE
+ *
+ * Define to 1 to enable Advertising Proxy for SRP Sever.
+ *
+ */
+#ifndef OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE
+#define OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE 0
+#endif
+
+/**
+ * @}
+ *
+ */
+
 #endif // CONFIG_SRP_SERVER_H_
diff --git a/src/core/config/time_sync.h b/src/core/config/time_sync.h
index c56d32d..43d232c 100644
--- a/src/core/config/time_sync.h
+++ b/src/core/config/time_sync.h
@@ -36,6 +36,16 @@
 #define CONFIG_TIME_SYNC_H_
 
 /**
+ * @addtogroup config-time-sync
+ *
+ * @brief
+ *   This module includes configuration variables for the Time Sync service.
+ *
+ * @{
+ *
+ */
+
+/**
  * @def OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
  *
  * Define as 1 to enable the time synchronization service feature.
@@ -94,4 +104,9 @@
 #define OPENTHREAD_CONFIG_TIME_SYNC_JUMP_NOTIF_MIN_US 10000
 #endif
 
+/**
+ * @}
+ *
+ */
+
 #endif // CONFIG_TIME_SYNC_H_
diff --git a/src/core/config/tmf.h b/src/core/config/tmf.h
index 42d3a70..18a397d 100644
--- a/src/core/config/tmf.h
+++ b/src/core/config/tmf.h
@@ -35,6 +35,17 @@
 #ifndef CONFIG_TMF_H_
 #define CONFIG_TMF_H_
 
+/**
+ * @addtogroup config-tmf
+ *
+ * @brief
+ *   This module includes configuration variables for the Thread Management
+ *   Framework service.
+ *
+ * @{
+ *
+ */
+
 #include "config/border_router.h"
 
 /**
@@ -247,4 +258,9 @@
 #error "Thread 1.2 or higher version is required for OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE"
 #endif
 
+/**
+ * @}
+ *
+ */
+
 #endif // CONFIG_TMF_H_
diff --git a/src/core/config/dtls.h b/src/core/config/trel.h
similarity index 64%
copy from src/core/config/dtls.h
copy to src/core/config/trel.h
index 09e8337..8d378ee 100644
--- a/src/core/config/dtls.h
+++ b/src/core/config/trel.h
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2021, The OpenThread Authors.
+ *  Copyright (c) 2024, The OpenThread Authors.
  *  All rights reserved.
  *
  *  Redistribution and use in source and binary forms, with or without
@@ -28,38 +28,39 @@
 
 /**
  * @file
- *   This file includes compile-time configurations for DTLS.
+ *   This file includes compile-time configurations for TREL.
  *
  */
 
-#ifndef CONFIG_DTLS_H_
-#define CONFIG_DTLS_H_
-
-#include "config/border_agent.h"
-#include "config/coap.h"
-#include "config/commissioner.h"
-#include "config/joiner.h"
+#ifndef CONFIG_TREL_H_
+#define CONFIG_TREL_H_
 
 /**
- * @def OPENTHREAD_CONFIG_DTLS_MAX_CONTENT_LEN
+ * @addtogroup config-trel
  *
- * The max length of the OpenThread dtls content buffer.
+ * @brief
+ *   This module includes configuration variables for TREL.
+ *
+ * @{
  *
  */
-#ifndef OPENTHREAD_CONFIG_DTLS_MAX_CONTENT_LEN
-#define OPENTHREAD_CONFIG_DTLS_MAX_CONTENT_LEN MBEDTLS_SSL_IN_CONTENT_LEN
+
+/**
+ * @def OPENTHREAD_CONFIG_TREL_PEER_TABLE_SIZE
+ *
+ * Specifies the capacity of TREL peer table. Only non-zero value
+ * will be directly used for setting the TREL peer table capacity.
+ * Zero value lets the size to be determined by the OT stack itself
+ * which is derived based on other configurations such as a child
+ * table size, neighbor table size, etc.
+ */
+#ifndef OPENTHREAD_CONFIG_TREL_PEER_TABLE_SIZE
+#define OPENTHREAD_CONFIG_TREL_PEER_TABLE_SIZE (0)
 #endif
 
 /**
- * @def OPENTHREAD_CONFIG_DTLS_ENABLE
- *
- *  Define to 1 to enable DTLS.
+ * @}
  *
  */
-#ifndef OPENTHREAD_CONFIG_DTLS_ENABLE
-#define OPENTHREAD_CONFIG_DTLS_ENABLE                                                     \
-    (OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE || OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE || \
-     OPENTHREAD_CONFIG_COMMISSIONER_ENABLE || OPENTHREAD_CONFIG_JOINER_ENABLE)
-#endif
 
-#endif // CONFIG_DTLS_H_
+#endif // CONFIG_TREL_H_
diff --git a/src/core/crypto/crypto_platform.cpp b/src/core/crypto/crypto_platform.cpp
index 6c7e54c..4865b08 100644
--- a/src/core/crypto/crypto_platform.cpp
+++ b/src/core/crypto/crypto_platform.cpp
@@ -662,6 +662,94 @@
 
 #endif // #if !OPENTHREAD_RADIO
 
+#if OPENTHREAD_FTD
+
+OT_TOOL_WEAK otError otPlatCryptoPbkdf2GenerateKey(const uint8_t *aPassword,
+                                                   uint16_t       aPasswordLen,
+                                                   const uint8_t *aSalt,
+                                                   uint16_t       aSaltLen,
+                                                   uint32_t       aIterationCounter,
+                                                   uint16_t       aKeyLen,
+                                                   uint8_t       *aKey)
+{
+#if (MBEDTLS_VERSION_NUMBER >= 0x03050000)
+    const size_t kBlockSize = MBEDTLS_CMAC_MAX_BLOCK_SIZE;
+#else
+    const size_t kBlockSize = MBEDTLS_CIPHER_BLKSIZE_MAX;
+#endif
+    uint8_t  prfInput[OT_CRYPTO_PBDKF2_MAX_SALT_SIZE + 4]; // Salt || INT(), for U1 calculation
+    long     prfOne[kBlockSize / sizeof(long)];
+    long     prfTwo[kBlockSize / sizeof(long)];
+    long     keyBlock[kBlockSize / sizeof(long)];
+    uint32_t blockCounter = 0;
+    uint8_t *key          = aKey;
+    uint16_t keyLen       = aKeyLen;
+    uint16_t useLen       = 0;
+    Error    error        = kErrorNone;
+    int      ret;
+
+    OT_ASSERT(aSaltLen <= sizeof(prfInput));
+    memcpy(prfInput, aSalt, aSaltLen);
+    OT_ASSERT(aIterationCounter % 2 == 0);
+    aIterationCounter /= 2;
+
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+    // limit iterations to avoid OSS-Fuzz timeouts
+    aIterationCounter = 2;
+#endif
+
+    while (keyLen)
+    {
+        ++blockCounter;
+        prfInput[aSaltLen + 0] = static_cast<uint8_t>(blockCounter >> 24);
+        prfInput[aSaltLen + 1] = static_cast<uint8_t>(blockCounter >> 16);
+        prfInput[aSaltLen + 2] = static_cast<uint8_t>(blockCounter >> 8);
+        prfInput[aSaltLen + 3] = static_cast<uint8_t>(blockCounter);
+
+        // Calculate U_1
+        ret = mbedtls_aes_cmac_prf_128(aPassword, aPasswordLen, prfInput, aSaltLen + 4,
+                                       reinterpret_cast<uint8_t *>(keyBlock));
+        VerifyOrExit(ret == 0, error = MbedTls::MapError(ret));
+
+        // Calculate U_2
+        ret = mbedtls_aes_cmac_prf_128(aPassword, aPasswordLen, reinterpret_cast<const uint8_t *>(keyBlock), kBlockSize,
+                                       reinterpret_cast<uint8_t *>(prfOne));
+        VerifyOrExit(ret == 0, error = MbedTls::MapError(ret));
+
+        for (uint32_t j = 0; j < kBlockSize / sizeof(long); ++j)
+        {
+            keyBlock[j] ^= prfOne[j];
+        }
+
+        for (uint32_t i = 1; i < aIterationCounter; ++i)
+        {
+            // Calculate U_{2 * i - 1}
+            ret = mbedtls_aes_cmac_prf_128(aPassword, aPasswordLen, reinterpret_cast<const uint8_t *>(prfOne),
+                                           kBlockSize, reinterpret_cast<uint8_t *>(prfTwo));
+            VerifyOrExit(ret == 0, error = MbedTls::MapError(ret));
+            // Calculate U_{2 * i}
+            ret = mbedtls_aes_cmac_prf_128(aPassword, aPasswordLen, reinterpret_cast<const uint8_t *>(prfTwo),
+                                           kBlockSize, reinterpret_cast<uint8_t *>(prfOne));
+            VerifyOrExit(ret == 0, error = MbedTls::MapError(ret));
+
+            for (uint32_t j = 0; j < kBlockSize / sizeof(long); ++j)
+            {
+                keyBlock[j] ^= prfOne[j] ^ prfTwo[j];
+            }
+        }
+
+        useLen = Min(keyLen, static_cast<uint16_t>(kBlockSize));
+        memcpy(key, keyBlock, useLen);
+        key += useLen;
+        keyLen -= useLen;
+    }
+
+exit:
+    return error;
+}
+
+#endif // #if OPENTHREAD_FTD
+
 #elif OPENTHREAD_CONFIG_CRYPTO_LIB == OPENTHREAD_CONFIG_CRYPTO_LIB_PSA
 
 #if !OPENTHREAD_RADIO
@@ -709,86 +797,27 @@
 
 #endif // #if !OPENTHREAD_RADIO
 
-#endif // #if OPENTHREAD_CONFIG_CRYPTO_LIB == OPENTHREAD_CONFIG_CRYPTO_LIB_MBEDTLS
-
-//---------------------------------------------------------------------------------------------------------------------
-// APIs to be used in "hybrid" mode by every OPENTHREAD_CONFIG_CRYPTO_LIB variant until full PSA support is ready
-
 #if OPENTHREAD_FTD
 
-OT_TOOL_WEAK void otPlatCryptoPbkdf2GenerateKey(const uint8_t *aPassword,
-                                                uint16_t       aPasswordLen,
-                                                const uint8_t *aSalt,
-                                                uint16_t       aSaltLen,
-                                                uint32_t       aIterationCounter,
-                                                uint16_t       aKeyLen,
-                                                uint8_t       *aKey)
+OT_TOOL_WEAK otError otPlatCryptoPbkdf2GenerateKey(const uint8_t *aPassword,
+                                                   uint16_t       aPasswordLen,
+                                                   const uint8_t *aSalt,
+                                                   uint16_t       aSaltLen,
+                                                   uint32_t       aIterationCounter,
+                                                   uint16_t       aKeyLen,
+                                                   uint8_t       *aKey)
 {
-#if (MBEDTLS_VERSION_NUMBER >= 0x03050000)
-    const size_t kBlockSize = MBEDTLS_CMAC_MAX_BLOCK_SIZE;
-#else
-    const size_t kBlockSize = MBEDTLS_CIPHER_BLKSIZE_MAX;
-#endif
-    uint8_t  prfInput[OT_CRYPTO_PBDKF2_MAX_SALT_SIZE + 4]; // Salt || INT(), for U1 calculation
-    long     prfOne[kBlockSize / sizeof(long)];
-    long     prfTwo[kBlockSize / sizeof(long)];
-    long     keyBlock[kBlockSize / sizeof(long)];
-    uint32_t blockCounter = 0;
-    uint8_t *key          = aKey;
-    uint16_t keyLen       = aKeyLen;
-    uint16_t useLen       = 0;
+    OT_UNUSED_VARIABLE(aPassword);
+    OT_UNUSED_VARIABLE(aPasswordLen);
+    OT_UNUSED_VARIABLE(aSalt);
+    OT_UNUSED_VARIABLE(aSaltLen);
+    OT_UNUSED_VARIABLE(aIterationCounter);
+    OT_UNUSED_VARIABLE(aKeyLen);
+    OT_UNUSED_VARIABLE(aKey);
 
-    OT_ASSERT(aSaltLen <= sizeof(prfInput));
-    memcpy(prfInput, aSalt, aSaltLen);
-    OT_ASSERT(aIterationCounter % 2 == 0);
-    aIterationCounter /= 2;
-
-#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
-    // limit iterations to avoid OSS-Fuzz timeouts
-    aIterationCounter = 2;
-#endif
-
-    while (keyLen)
-    {
-        ++blockCounter;
-        prfInput[aSaltLen + 0] = static_cast<uint8_t>(blockCounter >> 24);
-        prfInput[aSaltLen + 1] = static_cast<uint8_t>(blockCounter >> 16);
-        prfInput[aSaltLen + 2] = static_cast<uint8_t>(blockCounter >> 8);
-        prfInput[aSaltLen + 3] = static_cast<uint8_t>(blockCounter);
-
-        // Calculate U_1
-        mbedtls_aes_cmac_prf_128(aPassword, aPasswordLen, prfInput, aSaltLen + 4,
-                                 reinterpret_cast<uint8_t *>(keyBlock));
-
-        // Calculate U_2
-        mbedtls_aes_cmac_prf_128(aPassword, aPasswordLen, reinterpret_cast<const uint8_t *>(keyBlock), kBlockSize,
-                                 reinterpret_cast<uint8_t *>(prfOne));
-
-        for (uint32_t j = 0; j < kBlockSize / sizeof(long); ++j)
-        {
-            keyBlock[j] ^= prfOne[j];
-        }
-
-        for (uint32_t i = 1; i < aIterationCounter; ++i)
-        {
-            // Calculate U_{2 * i - 1}
-            mbedtls_aes_cmac_prf_128(aPassword, aPasswordLen, reinterpret_cast<const uint8_t *>(prfOne), kBlockSize,
-                                     reinterpret_cast<uint8_t *>(prfTwo));
-            // Calculate U_{2 * i}
-            mbedtls_aes_cmac_prf_128(aPassword, aPasswordLen, reinterpret_cast<const uint8_t *>(prfTwo), kBlockSize,
-                                     reinterpret_cast<uint8_t *>(prfOne));
-
-            for (uint32_t j = 0; j < kBlockSize / sizeof(long); ++j)
-            {
-                keyBlock[j] ^= prfOne[j] ^ prfTwo[j];
-            }
-        }
-
-        useLen = Min(keyLen, static_cast<uint16_t>(kBlockSize));
-        memcpy(key, keyBlock, useLen);
-        key += useLen;
-        keyLen -= useLen;
-    }
+    return OT_ERROR_NOT_CAPABLE;
 }
 
 #endif // #if OPENTHREAD_FTD
+
+#endif // #if OPENTHREAD_CONFIG_CRYPTO_LIB == OPENTHREAD_CONFIG_CRYPTO_LIB_MBEDTLS
diff --git a/src/core/crypto/mbedtls.cpp b/src/core/crypto/mbedtls.cpp
index f8c1c53..dc18949 100644
--- a/src/core/crypto/mbedtls.cpp
+++ b/src/core/crypto/mbedtls.cpp
@@ -53,11 +53,11 @@
 
 MbedTls::MbedTls(void)
 {
-#if OPENTHREAD_CONFIG_ENABLE_BUILTIN_MBEDTLS_MANAGEMENT
 #ifdef MBEDTLS_DEBUG_C
     // mbedTLS's debug level is almost the same as OpenThread's
     mbedtls_debug_set_threshold(OPENTHREAD_CONFIG_LOG_LEVEL);
 #endif
+#if OPENTHREAD_CONFIG_ENABLE_BUILTIN_MBEDTLS_MANAGEMENT
     mbedtls_platform_set_calloc_free(Heap::CAlloc, Heap::Free);
 #endif // OPENTHREAD_CONFIG_ENABLE_BUILTIN_MBEDTLS_MANAGEMENT
 }
diff --git a/src/core/diags/factory_diags.cpp b/src/core/diags/factory_diags.cpp
index 249594f..78845f1 100644
--- a/src/core/diags/factory_diags.cpp
+++ b/src/core/diags/factory_diags.cpp
@@ -846,7 +846,7 @@
 
     // This `rcp` command is for debugging and testing only, building only when NDEBUG is not defined
     // so that it will be excluded from release build.
-#if !defined(NDEBUG) && defined(OPENTHREAD_RADIO)
+#if OPENTHREAD_RADIO && !defined(NDEBUG)
     if (aArgsLength > 0 && !strcmp(aArgs[0], "rcp"))
     {
         aArgs++;
diff --git a/src/core/diags/factory_diags.hpp b/src/core/diags/factory_diags.hpp
index 5d015d2..1b72a15 100644
--- a/src/core/diags/factory_diags.hpp
+++ b/src/core/diags/factory_diags.hpp
@@ -42,6 +42,7 @@
 
 #include <openthread/platform/radio.h>
 
+#include "common/clearable.hpp"
 #include "common/error.hpp"
 #include "common/locator.hpp"
 #include "common/non_copyable.hpp"
@@ -131,10 +132,8 @@
         Error (Diags::*mCommand)(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen);
     };
 
-    struct Stats
+    struct Stats : public Clearable<Stats>
     {
-        void Clear(void) { memset(this, 0, sizeof(*this)); }
-
         uint32_t mReceivedPackets;
         uint32_t mSentPackets;
         int8_t   mFirstRssi;
diff --git a/src/core/ftd.cmake b/src/core/ftd.cmake
index dd6dae2..074c436 100644
--- a/src/core/ftd.cmake
+++ b/src/core/ftd.cmake
@@ -30,6 +30,8 @@
 
 target_compile_definitions(openthread-ftd PRIVATE
     OPENTHREAD_FTD=1
+    OPENTHREAD_MTD=0
+    OPENTHREAD_RADIO=0
 )
 
 target_compile_options(openthread-ftd PRIVATE
diff --git a/src/core/instance/instance.cpp b/src/core/instance/instance.cpp
index 706671a..3076110 100644
--- a/src/core/instance/instance.cpp
+++ b/src/core/instance/instance.cpp
@@ -87,6 +87,11 @@
     , mSettings(*this)
     , mSettingsDriver(*this)
     , mMessagePool(*this)
+#if OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE
+    // DNS-SD (mDNS) platform is initialized early to
+    // allow other modules to use it.
+    , mDnssd(*this)
+#endif
     , mIp6(*this)
     , mThreadNetif(*this)
     , mTmfAgent(*this)
@@ -120,6 +125,9 @@
 #if OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE
     , mSntpClient(*this)
 #endif
+#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
+    , mBackboneRouterLocal(*this)
+#endif
     , mActiveDataset(*this)
     , mPendingDataset(*this)
     , mExtendedPanIdManager(*this)
@@ -156,7 +164,7 @@
 #if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD
     , mCommissioner(*this)
 #endif
-#if OPENTHREAD_CONFIG_DTLS_ENABLE
+#if OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE
     , mTmfSecureAgent(*this)
 #endif
 #if OPENTHREAD_CONFIG_JOINER_ENABLE
@@ -173,7 +181,6 @@
     , mBackboneRouterLeader(*this)
 #endif
 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
-    , mBackboneRouterLocal(*this)
     , mBackboneRouterManager(*this)
 #endif
 #if OPENTHREAD_CONFIG_MLR_ENABLE || (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE)
@@ -185,6 +192,9 @@
 #endif
 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
     , mSrpServer(*this)
+#if OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE
+    , mSrpAdvertisingProxy(*this)
+#endif
 #endif
 #if OPENTHREAD_FTD
     , mChildSupervisor(*this)
@@ -211,13 +221,18 @@
 #if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
     , mApplicationCoapSecure(*this, /* aLayerTwoSecurity */ true)
 #endif
+#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
+    , mApplicationBleSecure(*this)
+#endif
 #if OPENTHREAD_CONFIG_PING_SENDER_ENABLE
     , mPingSender(*this)
 #endif
 #if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
     , mChannelMonitor(*this)
 #endif
-#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && OPENTHREAD_FTD
+#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && \
+    (OPENTHREAD_FTD ||                          \
+     (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE))
     , mChannelManager(*this)
 #endif
 #if OPENTHREAD_CONFIG_MESH_DIAG_ENABLE && OPENTHREAD_FTD
@@ -456,7 +471,7 @@
     Get<Tmf::Agent>().GetRequestMessages().GetInfo(aInfo.mCoapQueue);
     Get<Tmf::Agent>().GetCachedResponses().GetInfo(aInfo.mCoapQueue);
 
-#if OPENTHREAD_CONFIG_DTLS_ENABLE
+#if OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE
     Get<Tmf::SecureAgent>().GetRequestMessages().GetInfo(aInfo.mCoapSecureQueue);
     Get<Tmf::SecureAgent>().GetCachedResponses().GetInfo(aInfo.mCoapSecureQueue);
 #endif
diff --git a/src/core/instance/instance.hpp b/src/core/instance/instance.hpp
index 596a5c6..30b443b 100644
--- a/src/core/instance/instance.hpp
+++ b/src/core/instance/instance.hpp
@@ -88,6 +88,7 @@
 #include "net/dhcp6_server.hpp"
 #include "net/dns_client.hpp"
 #include "net/dns_dso.hpp"
+#include "net/dnssd.hpp"
 #include "net/dnssd_server.hpp"
 #include "net/ip6.hpp"
 #include "net/ip6_filter.hpp"
@@ -95,8 +96,10 @@
 #include "net/nd_agent.hpp"
 #include "net/netif.hpp"
 #include "net/sntp_client.hpp"
+#include "net/srp_advertising_proxy.hpp"
 #include "net/srp_client.hpp"
 #include "net/srp_server.hpp"
+#include "radio/ble_secure.hpp"
 #include "thread/address_resolver.hpp"
 #include "thread/announce_begin_server.hpp"
 #include "thread/announce_sender.hpp"
@@ -457,6 +460,12 @@
     SettingsDriver mSettingsDriver;
     MessagePool    mMessagePool;
 
+#if OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE
+    // DNS-SD (mDNS) platform is initialized early to
+    // allow other modules to use it.
+    Dnssd mDnssd;
+#endif
+
     Ip6::Ip6    mIp6;
     ThreadNetif mThreadNetif;
     Tmf::Agent  mTmfAgent;
@@ -501,6 +510,10 @@
     Sntp::Client mSntpClient;
 #endif
 
+#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
+    BackboneRouter::Local mBackboneRouterLocal;
+#endif
+
     MeshCoP::ActiveDatasetManager  mActiveDataset;
     MeshCoP::PendingDatasetManager mPendingDataset;
     MeshCoP::ExtendedPanIdManager  mExtendedPanIdManager;
@@ -547,7 +560,7 @@
     MeshCoP::Commissioner mCommissioner;
 #endif
 
-#if OPENTHREAD_CONFIG_DTLS_ENABLE
+#if OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE
     Tmf::SecureAgent mTmfSecureAgent;
 #endif
 
@@ -569,7 +582,6 @@
 #endif
 
 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
-    BackboneRouter::Local   mBackboneRouterLocal;
     BackboneRouter::Manager mBackboneRouterManager;
 #endif
 
@@ -583,6 +595,9 @@
 
 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
     Srp::Server mSrpServer;
+#if OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE
+    Srp::AdvertisingProxy mSrpAdvertisingProxy;
+#endif
 #endif
 
 #if OPENTHREAD_FTD
@@ -618,6 +633,10 @@
     Coap::CoapSecure mApplicationCoapSecure;
 #endif
 
+#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
+    Ble::BleSecure mApplicationBleSecure;
+#endif
+
 #if OPENTHREAD_CONFIG_PING_SENDER_ENABLE
     Utils::PingSender mPingSender;
 #endif
@@ -626,7 +645,9 @@
     Utils::ChannelMonitor mChannelMonitor;
 #endif
 
-#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && OPENTHREAD_FTD
+#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && \
+    (OPENTHREAD_FTD ||                          \
+     (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE))
     Utils::ChannelManager mChannelManager;
 #endif
 
@@ -830,7 +851,7 @@
 
 template <> inline Tmf::Agent &Instance::Get(void) { return mTmfAgent; }
 
-#if OPENTHREAD_CONFIG_DTLS_ENABLE
+#if OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE
 template <> inline Tmf::SecureAgent &Instance::Get(void) { return mTmfSecureAgent; }
 #endif
 
@@ -856,6 +877,10 @@
 template <> inline PanIdQueryClient &Instance::Get(void) { return mCommissioner.GetPanIdQueryClient(); }
 #endif
 
+#if OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE
+template <> inline Dnssd &Instance::Get(void) { return mDnssd; }
+#endif
+
 #if OPENTHREAD_CONFIG_JOINER_ENABLE
 template <> inline MeshCoP::Joiner &Instance::Get(void) { return mJoiner; }
 #endif
@@ -923,7 +948,9 @@
 template <> inline Utils::ChannelMonitor &Instance::Get(void) { return mChannelMonitor; }
 #endif
 
-#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && OPENTHREAD_FTD
+#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && \
+    (OPENTHREAD_FTD ||                          \
+     (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE))
 template <> inline Utils::ChannelManager &Instance::Get(void) { return mChannelManager; }
 #endif
 
@@ -1007,7 +1034,7 @@
 template <> inline BorderRouter::RoutingManager &Instance::Get(void) { return mRoutingManager; }
 
 template <> inline BorderRouter::InfraIf &Instance::Get(void) { return mRoutingManager.mInfraIf; }
-#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
+#endif
 
 #if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
 template <> inline Nat64::Translator &Instance::Get(void) { return mNat64Translator; }
@@ -1015,6 +1042,13 @@
 
 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
 template <> inline Srp::Server &Instance::Get(void) { return mSrpServer; }
+#if OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE
+template <> inline Srp::AdvertisingProxy &Instance::Get(void) { return mSrpAdvertisingProxy; }
+#endif
+#endif // OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
+
+#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
+template <> inline Ble::BleSecure &Instance::Get(void) { return mApplicationBleSecure; }
 #endif
 
 #endif // OPENTHREAD_MTD || OPENTHREAD_FTD
diff --git a/src/core/mac/mac.cpp b/src/core/mac/mac.cpp
index 15f5606..e70fc98 100644
--- a/src/core/mac/mac.cpp
+++ b/src/core/mac/mac.cpp
@@ -234,7 +234,7 @@
     uint16_t             payloadLength;
 #endif
 
-    memset(&aResult, 0, sizeof(ActiveScanResult));
+    ClearAllBytes(aResult);
 
     VerifyOrExit(aBeaconFrame != nullptr, error = kErrorInvalidArgs);
 
@@ -1134,9 +1134,13 @@
     }
 
     // Only track the CCA success rate for frame transmissions
-    // on the PAN channel.
+    // on the PAN channel or the CSL channel.
 
+#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
+    if ((aChannel == mPanChannel) || (IsCslEnabled() && (aChannel == mCslChannel)))
+#else
     if (aChannel == mPanChannel)
+#endif
     {
         if (mCcaSampleCount < kMaxCcaSampleCount)
         {
@@ -1311,6 +1315,12 @@
 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
                 ProcessCsl(*aAckFrame, dstAddr);
 #endif
+#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
+                if (!GetRxOnWhenIdle() && aFrame.GetHeaderIe(CslIe::kHeaderIeId) != nullptr)
+                {
+                    Get<DataPollSender>().ResetKeepAliveTimer();
+                }
+#endif
             }
         }
     }
@@ -1631,7 +1641,7 @@
 
         if (keySequence > keyManager.GetCurrentKeySequence())
         {
-            keyManager.SetCurrentKeySequence(keySequence);
+            keyManager.SetCurrentKeySequence(keySequence, KeyManager::kApplyKeySwitchGuard);
         }
     }
 
@@ -2153,7 +2163,7 @@
 }
 #endif
 
-void Mac::ResetRetrySuccessHistogram() { memset(&mRetryHistogram, 0, sizeof(mRetryHistogram)); }
+void Mac::ResetRetrySuccessHistogram() { ClearAllBytes(mRetryHistogram); }
 #endif // OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_ENABLE
 
 uint8_t Mac::ComputeLinkMargin(int8_t aRss) const { return ot::ComputeLinkMargin(GetNoiseFloor(), aRss); }
diff --git a/src/core/mac/mac.hpp b/src/core/mac/mac.hpp
index 1b2ae6c..6f95ab7 100644
--- a/src/core/mac/mac.hpp
+++ b/src/core/mac/mac.hpp
@@ -39,6 +39,7 @@
 #include <openthread/platform/radio.h>
 #include <openthread/platform/time.h>
 
+#include "common/clearable.hpp"
 #include "common/locator.hpp"
 #include "common/log.hpp"
 #include "common/non_copyable.hpp"
@@ -496,7 +497,7 @@
      * Resets mac counters
      *
      */
-    void ResetCounters(void) { memset(&mCounters, 0, sizeof(mCounters)); }
+    void ResetCounters(void) { ClearAllBytes(mCounters); }
 
     /**
      * Returns the MAC counter.
diff --git a/src/core/mac/mac_frame.cpp b/src/core/mac/mac_frame.cpp
index 47debef..9946bb2 100644
--- a/src/core/mac/mac_frame.cpp
+++ b/src/core/mac/mac_frame.cpp
@@ -1220,9 +1220,11 @@
 {
     uint8_t *cur = GetThreadIe(ThreadIe::kEnhAckProbingIe);
 
-    OT_ASSERT(cur != nullptr);
-
+    VerifyOrExit(cur != nullptr);
     memcpy(cur + sizeof(HeaderIe) + sizeof(VendorIeHeader), aValue, aLen);
+
+exit:
+    return;
 }
 #endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
 
@@ -1385,7 +1387,7 @@
     uint16_t fcf = static_cast<uint16_t>(kTypeAck) | aFrame.GetVersion();
 
     mChannel = aFrame.mChannel;
-    memset(&mInfo.mTxInfo, 0, sizeof(mInfo.mTxInfo));
+    ClearAllBytes(mInfo.mTxInfo);
 
     if (aIsFramePending)
     {
@@ -1449,7 +1451,7 @@
     // Prepare the ack frame
 
     mChannel = aRxFrame.mChannel;
-    memset(&mInfo.mTxInfo, 0, sizeof(mInfo.mTxInfo));
+    ClearAllBytes(mInfo.mTxInfo);
 
     InitMacHeader(kTypeAck, kVersion2015, addrs, panIds, static_cast<SecurityLevel>(securityLevel),
                   static_cast<KeyIdMode>(keyIdMode));
diff --git a/src/core/meshcop/announce_begin_client.cpp b/src/core/meshcop/announce_begin_client.cpp
index 5d3b9d9..07ee272 100644
--- a/src/core/meshcop/announce_begin_client.cpp
+++ b/src/core/meshcop/announce_begin_client.cpp
@@ -60,10 +60,9 @@
                                        uint16_t            aPeriod,
                                        const Ip6::Address &aAddress)
 {
-    Error                   error = kErrorNone;
-    MeshCoP::ChannelMaskTlv channelMask;
-    Tmf::MessageInfo        messageInfo(GetInstance());
-    Coap::Message          *message = nullptr;
+    Error            error = kErrorNone;
+    Tmf::MessageInfo messageInfo(GetInstance());
+    Coap::Message   *message = nullptr;
 
     VerifyOrExit(Get<MeshCoP::Commissioner>().IsActive(), error = kErrorInvalidState);
     VerifyOrExit((message = Get<Tmf::Agent>().NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
@@ -74,9 +73,7 @@
     SuccessOrExit(
         error = Tlv::Append<MeshCoP::CommissionerSessionIdTlv>(*message, Get<MeshCoP::Commissioner>().GetSessionId()));
 
-    channelMask.Init();
-    channelMask.SetChannelMask(aChannelMask);
-    SuccessOrExit(error = channelMask.AppendTo(*message));
+    SuccessOrExit(error = MeshCoP::ChannelMaskTlv::AppendTo(*message, aChannelMask));
 
     SuccessOrExit(error = Tlv::Append<MeshCoP::CountTlv>(*message, aCount));
     SuccessOrExit(error = Tlv::Append<MeshCoP::PeriodTlv>(*message, aPeriod));
diff --git a/src/core/meshcop/border_agent.cpp b/src/core/meshcop/border_agent.cpp
index 142c606..24e7331 100644
--- a/src/core/meshcop/border_agent.cpp
+++ b/src/core/meshcop/border_agent.cpp
@@ -128,7 +128,7 @@
 
 exit:
     FreeMessageOnError(message, error);
-    LogError("send error CoAP message", error);
+    LogWarnOnError(error, "send error CoAP message");
 }
 
 void BorderAgent::SendErrorMessage(const Coap::Message &aRequest, bool aSeparate, Error aError)
@@ -158,7 +158,7 @@
 
 exit:
     FreeMessageOnError(message, error);
-    LogError("send error CoAP message", error);
+    LogWarnOnError(error, "send error CoAP message");
 }
 
 Error BorderAgent::SendMessage(Coap::Message &aMessage)
@@ -239,6 +239,12 @@
 #if OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE
     , mIdInitialized(false)
 #endif
+#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
+    , mUsingEphemeralKey(false)
+    , mOldUdpPort(0)
+    , mEphemeralKeyTimer(aInstance)
+    , mEphemeralKeyTask(aInstance)
+#endif
 {
     mCommissionerAloc.InitAsThreadOriginMeshLocal();
 }
@@ -341,7 +347,7 @@
 
 exit:
     FreeMessageOnError(message, error);
-    LogError("send proxy stream", error);
+    LogWarnOnError(error, "send proxy stream");
 }
 
 bool BorderAgent::HandleUdpReceive(void *aContext, const otMessage *aMessage, const otMessageInfo *aMessageInfo)
@@ -392,7 +398,7 @@
     FreeMessageOnError(message, error);
     if (error != kErrorDestinationAddressFiltered)
     {
-        LogError("notify commissioner on ProxyRx (c/ur)", error);
+        LogWarnOnError(error, "notify commissioner on ProxyRx (c/ur)");
     }
 
     return error != kErrorDestinationAddressFiltered;
@@ -430,7 +436,7 @@
     LogInfo("Sent to commissioner");
 
 exit:
-    LogError("send to commissioner", error);
+    LogWarnOnError(error, "send to commissioner");
     return error;
 }
 
@@ -514,7 +520,7 @@
 
 exit:
     FreeMessageOnError(message, error);
-    LogError("send to joiner router request RelayTx (c/tx)", error);
+    LogWarnOnError(error, "send to joiner router request RelayTx (c/tx)");
 }
 
 Error BorderAgent::ForwardToLeader(const Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo, Uri aUri)
@@ -570,7 +576,7 @@
     LogInfo("Forwarded request to leader on %s", PathForUri(aUri));
 
 exit:
-    LogError("forward to leader", error);
+    LogWarnOnError(error, "forward to leader");
 
     if (error != kErrorNone)
     {
@@ -599,25 +605,55 @@
         LogInfo("Commissioner disconnected");
         IgnoreError(Get<Ip6::Udp>().RemoveReceiver(mUdpReceiver));
         Get<ThreadNetif>().RemoveUnicastAddress(mCommissionerAloc);
-        mState        = kStateStarted;
-        mUdpProxyPort = 0;
+
+#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
+        if (mUsingEphemeralKey)
+        {
+            RestartAfterRemovingEphemeralKey();
+        }
+        else
+#endif
+        {
+            mState        = kStateStarted;
+            mUdpProxyPort = 0;
+        }
     }
 }
 
 uint16_t BorderAgent::GetUdpPort(void) const { return Get<Tmf::SecureAgent>().GetUdpPort(); }
 
-void BorderAgent::Start(void)
+Error BorderAgent::Start(uint16_t aUdpPort)
 {
     Error error;
     Pskc  pskc;
 
-    VerifyOrExit(mState == kStateStopped, error = kErrorNone);
-
     Get<KeyManager>().GetPskc(pskc);
-    SuccessOrExit(error = Get<Tmf::SecureAgent>().Start(kUdpPort));
-    SuccessOrExit(error = Get<Tmf::SecureAgent>().SetPsk(pskc.m8, Pskc::kSize));
-
+    error = Start(aUdpPort, pskc.m8, Pskc::kSize);
     pskc.Clear();
+
+    return error;
+}
+
+Error BorderAgent::Start(uint16_t aUdpPort, const uint8_t *aPsk, uint8_t aPskLength)
+{
+    Error error = kErrorNone;
+
+    VerifyOrExit(mState == kStateStopped);
+
+#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
+    if (mUsingEphemeralKey)
+    {
+        SuccessOrExit(error = Get<Tmf::SecureAgent>().Start(aUdpPort, kMaxEphemeralKeyConnectionAttempts,
+                                                            HandleSecureAgentStopped, this));
+    }
+    else
+#endif
+    {
+        SuccessOrExit(error = Get<Tmf::SecureAgent>().Start(aUdpPort));
+    }
+
+    SuccessOrExit(error = Get<Tmf::SecureAgent>().SetPsk(aPsk, aPskLength));
+
     Get<Tmf::SecureAgent>().SetConnectedCallback(HandleConnected, this);
 
     mState        = kStateStarted;
@@ -626,7 +662,8 @@
     LogInfo("Border Agent start listening on port %u", GetUdpPort());
 
 exit:
-    LogError("start agent", error);
+    LogWarnOnError(error, "start agent");
+    return error;
 }
 
 void BorderAgent::HandleTimeout(void)
@@ -642,27 +679,128 @@
 {
     VerifyOrExit(mState != kStateStopped);
 
+#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
+    if (mUsingEphemeralKey)
+    {
+        mUsingEphemeralKey = false;
+        mEphemeralKeyTimer.Stop();
+        mEphemeralKeyTask.Post();
+    }
+#endif
+
     mTimer.Stop();
     Get<Tmf::SecureAgent>().Stop();
 
     mState        = kStateStopped;
     mUdpProxyPort = 0;
-
     LogInfo("Border Agent stopped");
 
 exit:
     return;
 }
 
-#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN)
-void BorderAgent::LogError(const char *aActionText, Error aError)
+#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
+
+Error BorderAgent::SetEphemeralKey(const char *aKeyString, uint32_t aTimeout, uint16_t aUdpPort)
 {
-    if (aError != kErrorNone)
+    Error    error  = kErrorNone;
+    uint16_t length = StringLength(aKeyString, kMaxEphemeralKeyLength + 1);
+
+    VerifyOrExit(mState == kStateStarted, error = kErrorInvalidState);
+    VerifyOrExit((length >= kMinEphemeralKeyLength) && (length <= kMaxEphemeralKeyLength), error = kErrorInvalidArgs);
+
+    if (!mUsingEphemeralKey)
     {
-        LogWarn("Failed to %s: %s", aActionText, ErrorToString(aError));
+        mOldUdpPort = GetUdpPort();
     }
+
+    Stop();
+
+    // We set the `mUsingEphemeralKey` before `Start()` since
+    // callbacks (like `HandleConnected()`) may be invoked from
+    // `Start()` itself.
+
+    mUsingEphemeralKey = true;
+
+    error = Start(aUdpPort, reinterpret_cast<const uint8_t *>(aKeyString), static_cast<uint8_t>(length));
+
+    if (error != kErrorNone)
+    {
+        mUsingEphemeralKey = false;
+        IgnoreError(Start(mOldUdpPort));
+        ExitNow();
+    }
+
+    mEphemeralKeyTask.Post();
+
+    if (aTimeout == 0)
+    {
+        aTimeout = kDefaultEphemeralKeyTimeout;
+    }
+
+    aTimeout = Min(aTimeout, kMaxEphemeralKeyTimeout);
+
+    mEphemeralKeyTimer.Start(aTimeout);
+
+    LogInfo("Allow ephemeral key for %lu msec on port %u", ToUlong(aTimeout), GetUdpPort());
+
+exit:
+    return error;
 }
-#endif
+
+void BorderAgent::ClearEphemeralKey(void)
+{
+    VerifyOrExit(mUsingEphemeralKey);
+
+    LogInfo("Clearing ephemeral key");
+    mEphemeralKeyTimer.Stop();
+
+    switch (mState)
+    {
+    case kStateStarted:
+        RestartAfterRemovingEphemeralKey();
+        break;
+
+    case kStateStopped:
+    case kStateActive:
+        // If there is an active commissioner connection, we wait till
+        // it gets disconnected before removing ephemeral key and
+        // restarting the agent.
+        break;
+    }
+
+exit:
+    return;
+}
+
+void BorderAgent::HandleEphemeralKeyTimeout(void)
+{
+    LogInfo("Ephemeral key timed out");
+    ClearEphemeralKey();
+}
+
+void BorderAgent::InvokeEphemeralKeyCallback(void) { mEphemeralKeyCallback.InvokeIfSet(); }
+
+void BorderAgent::RestartAfterRemovingEphemeralKey(void)
+{
+    LogInfo("Removing ephemeral key and restarting agent");
+
+    Stop();
+    IgnoreError(Start(mOldUdpPort));
+}
+
+void BorderAgent::HandleSecureAgentStopped(void *aContext)
+{
+    reinterpret_cast<BorderAgent *>(aContext)->HandleSecureAgentStopped();
+}
+
+void BorderAgent::HandleSecureAgentStopped(void)
+{
+    LogInfo("Reached max allowed connection attempts with ephemeral key");
+    RestartAfterRemovingEphemeralKey();
+}
+
+#endif // OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
 
 } // namespace MeshCoP
 } // namespace ot
diff --git a/src/core/meshcop/border_agent.hpp b/src/core/meshcop/border_agent.hpp
index 9f721d6..933a95c 100644
--- a/src/core/meshcop/border_agent.hpp
+++ b/src/core/meshcop/border_agent.hpp
@@ -45,6 +45,8 @@
 #include "common/locator.hpp"
 #include "common/non_copyable.hpp"
 #include "common/notifier.hpp"
+#include "common/tasklet.hpp"
+#include "meshcop/secure_transport.hpp"
 #include "net/udp6.hpp"
 #include "thread/tmf.hpp"
 #include "thread/uri_paths.hpp"
@@ -60,6 +62,30 @@
     friend class Tmf::SecureAgent;
 
 public:
+    /**
+     * Minimum length of the ephemeral key string.
+     *
+     */
+    static constexpr uint16_t kMinEphemeralKeyLength = OT_BORDER_AGENT_MIN_EPHEMERAL_KEY_LENGTH;
+
+    /**
+     * Maximum length of the ephemeral key string.
+     *
+     */
+    static constexpr uint16_t kMaxEphemeralKeyLength = OT_BORDER_AGENT_MAX_EPHEMERAL_KEY_LENGTH;
+
+    /**
+     * Default ephemeral key timeout interval in milliseconds.
+     *
+     */
+    static constexpr uint32_t kDefaultEphemeralKeyTimeout = OT_BORDER_AGENT_DEFAULT_EPHEMERAL_KEY_TIMEOUT;
+
+    /**
+     * Maximum ephemeral key timeout interval in milliseconds.
+     *
+     */
+    static constexpr uint32_t kMaxEphemeralKeyTimeout = OT_BORDER_AGENT_MAX_EPHEMERAL_KEY_TIMEOUT;
+
     typedef otBorderAgentId Id; ///< Border Agent ID.
 
     /**
@@ -125,7 +151,7 @@
      * Starts the Border Agent service.
      *
      */
-    void Start(void);
+    void Start(void) { IgnoreError(Start(kUdpPort)); }
 
     /**
      * Stops the Border Agent service.
@@ -141,6 +167,75 @@
      */
     State GetState(void) const { return mState; }
 
+#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
+    /**
+     * Sets the ephemeral key for a given timeout duration.
+     *
+     * The ephemeral key can be set when the Border Agent is already running and is not currently connected to any
+     * external commissioner (i.e., it is in `kStateStarted` state).
+     *
+     * The given @p aKeyString is directly used as the ephemeral PSK (excluding the trailing null `\0` character). Its
+     * length must be between `kMinEphemeralKeyLength` and `kMaxEphemeralKeyLength`, inclusive.
+     *
+     * Setting the ephemeral key again before a previously set one is timed out will replace the previous one and will
+     * reset the timeout.
+     *
+     * While the timeout interval is in effect, the ephemeral key can be used only once by an external commissioner to
+     * connect. Once the commissioner disconnects, the ephemeral key is cleared, and Border Agent reverts to using
+     * PSKc.
+     *
+     * @param[in] aKeyString   The ephemeral key.
+     * @param[in] aTimeout     The timeout duration in milliseconds to use the ephemeral key.
+     *                         If zero, the default `kDefaultEphemeralKeyTimeout` value will be used.
+     *                         If the timeout value is larger than `kMaxEphemeralKeyTimeout`, the max value will be
+     *                         used instead.
+     * @param[in] aUdpPort     The UDP port to use with ephemeral key. If UDP port is zero, an ephemeral port will be
+     *                         used. `GetUdpPort()` will return the current UDP port being used.
+     *
+     * @retval kErrorNone           Successfully set the ephemeral key.
+     * @retval kErrorInvalidState   Agent is not running or connected to external commissioner.
+     * @retval kErrorInvalidArgs    The given @p aKeyString is not valid.
+     * @retval kErrorFailed         Failed to set the key (e.g., could not bind to UDP port).
+     *
+     */
+    Error SetEphemeralKey(const char *aKeyString, uint32_t aTimeout, uint16_t aUdpPort);
+
+    /**
+     * Cancels the ephemeral key in use if any.
+     *
+     * Can be used to cancel a previously set ephemeral key before it times out. If the Border Agent is not running or
+     * there is no ephemeral key in use, calling this function has no effect.
+     *
+     * If a commissioner is connected using the ephemeral key and is currently active, calling this method does not
+     * change its state. In this case the `IsEphemeralKeyActive()` will continue to return `true` until the commissioner
+     * disconnects.
+     *
+     */
+    void ClearEphemeralKey(void);
+
+    /**
+     * Indicates whether or not an ephemeral key is currently active.
+     *
+     * @retval TRUE    An ephemeral key is active.
+     * @retval FALSE   No ephemeral key is active.
+     *
+     */
+    bool IsEphemeralKeyActive(void) const { return mUsingEphemeralKey; }
+
+    /**
+     * Callback function pointer to notify when there is any changes related to use of ephemeral key by Border Agent.
+     *
+     *
+     */
+    typedef otBorderAgentEphemeralKeyCallback EphemeralKeyCallback;
+
+    void SetEphemeralKeyCallback(EphemeralKeyCallback aCallback, void *aContext)
+    {
+        mEphemeralKeyCallback.Set(aCallback, aContext);
+    }
+
+#endif // OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
+
     /**
      * Returns the UDP Proxy port to which the commissioner is currently
      * bound.
@@ -151,9 +246,16 @@
     uint16_t GetUdpProxyPort(void) const { return mUdpProxyPort; }
 
 private:
+    static_assert(kMaxEphemeralKeyLength <= SecureTransport::kPskMaxLength,
+                  "Max ephemeral key length is larger than max PSK len");
+
     static constexpr uint16_t kUdpPort          = OPENTHREAD_CONFIG_BORDER_AGENT_UDP_PORT;
     static constexpr uint32_t kKeepAliveTimeout = 50 * 1000; // Timeout to reject a commissioner (in msec)
 
+#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
+    static constexpr uint16_t kMaxEphemeralKeyConnectionAttempts = 10;
+#endif
+
     class ForwardContext : public InstanceLocatorInit, public Heap::Allocatable<ForwardContext>
     {
     public:
@@ -171,6 +273,9 @@
         uint8_t  mToken[Coap::Message::kMaxTokenLength]; // The CoAP Token of the original request.
     };
 
+    Error Start(uint16_t aUdpPort);
+    Error Start(uint16_t aUdpPort, const uint8_t *aPsk, uint8_t aPskLength);
+
     void HandleNotifierEvents(Events aEvents);
 
     Coap::Message::Code CoapCodeFromError(Error aError);
@@ -185,6 +290,14 @@
 
     void HandleTimeout(void);
 
+#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
+    void        RestartAfterRemovingEphemeralKey(void);
+    void        HandleEphemeralKeyTimeout(void);
+    void        InvokeEphemeralKeyCallback(void);
+    static void HandleSecureAgentStopped(void *aContext);
+    void        HandleSecureAgentStopped(void);
+#endif
+
     static void HandleCoapResponse(void                *aContext,
                                    otMessage           *aMessage,
                                    const otMessageInfo *aMessageInfo,
@@ -195,13 +308,11 @@
     static bool HandleUdpReceive(void *aContext, const otMessage *aMessage, const otMessageInfo *aMessageInfo);
     bool        HandleUdpReceive(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
 
-#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN)
-    void LogError(const char *aActionText, Error aError);
-#else
-    void LogError(const char *, Error) {}
-#endif
-
     using TimeoutTimer = TimerMilliIn<BorderAgent, &BorderAgent::HandleTimeout>;
+#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
+    using EphemeralKeyTimer = TimerMilliIn<BorderAgent, &BorderAgent::HandleEphemeralKeyTimeout>;
+    using EphemeralKeyTask  = TaskletIn<BorderAgent, &BorderAgent::InvokeEphemeralKeyCallback>;
+#endif
 
     State                      mState;
     uint16_t                   mUdpProxyPort;
@@ -212,6 +323,13 @@
     Id   mId;
     bool mIdInitialized;
 #endif
+#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
+    bool                           mUsingEphemeralKey;
+    uint16_t                       mOldUdpPort;
+    EphemeralKeyTimer              mEphemeralKeyTimer;
+    EphemeralKeyTask               mEphemeralKeyTask;
+    Callback<EphemeralKeyCallback> mEphemeralKeyCallback;
+#endif
 };
 
 DeclareTmfHandler(BorderAgent, kUriRelayRx);
diff --git a/src/core/meshcop/commissioner.cpp b/src/core/meshcop/commissioner.cpp
index 3009a47..999d732 100644
--- a/src/core/meshcop/commissioner.cpp
+++ b/src/core/meshcop/commissioner.cpp
@@ -72,7 +72,7 @@
     , mPanIdQuery(aInstance)
     , mState(kStateDisabled)
 {
-    memset(reinterpret_cast<void *>(mJoiners), 0, sizeof(mJoiners));
+    ClearAllBytes(mJoiners);
 
     mCommissionerAloc.InitAsThreadOriginMeshLocal();
     mCommissionerAloc.mPreferred = true;
@@ -302,9 +302,9 @@
     if ((error != kErrorNone) && (error != kErrorAlready))
     {
         Get<Tmf::SecureAgent>().Stop();
+        LogWarnOnError(error, "start commissioner");
     }
 
-    LogError("start commissioner", error);
     return error;
 }
 
@@ -343,7 +343,11 @@
 #endif
 
 exit:
-    LogError("stop commissioner", error);
+    if (error != kErrorAlready)
+    {
+        LogWarnOnError(error, "stop commissioner");
+    }
+
     return error;
 }
 
@@ -405,7 +409,8 @@
     error = SendMgmtCommissionerSetRequest(dataset, nullptr, 0);
 
 exit:
-    LogError("send MGMT_COMMISSIONER_SET.req", error);
+    LogWarnOnError(error, "send MGMT_COMMISSIONER_SET.req");
+    OT_UNUSED_VARIABLE(error);
 }
 
 void Commissioner::ClearJoiners(void)
@@ -476,7 +481,7 @@
 
 void Commissioner::Joiner::CopyToJoinerInfo(otJoinerInfo &aJoiner) const
 {
-    memset(&aJoiner, 0, sizeof(aJoiner));
+    ClearAllBytes(aJoiner);
 
     switch (mType)
     {
@@ -857,7 +862,7 @@
 
 exit:
     FreeMessageOnError(message, error);
-    LogError("send keep alive", error);
+    LogWarnOnError(error, "send keep alive");
 }
 
 void Commissioner::HandleLeaderKeepAliveResponse(void                *aContext,
diff --git a/src/core/meshcop/commissioner.hpp b/src/core/meshcop/commissioner.hpp
index b62a8d2..fda668c 100644
--- a/src/core/meshcop/commissioner.hpp
+++ b/src/core/meshcop/commissioner.hpp
@@ -50,9 +50,9 @@
 #include "common/timer.hpp"
 #include "mac/mac_types.hpp"
 #include "meshcop/announce_begin_client.hpp"
-#include "meshcop/dtls.hpp"
 #include "meshcop/energy_scan_client.hpp"
 #include "meshcop/panid_query_client.hpp"
+#include "meshcop/secure_transport.hpp"
 #include "net/ip6_address.hpp"
 #include "net/udp6.hpp"
 #include "thread/key_manager.hpp"
diff --git a/src/core/meshcop/dataset.cpp b/src/core/meshcop/dataset.cpp
index 00c8d6d..0285956 100644
--- a/src/core/meshcop/dataset.cpp
+++ b/src/core/meshcop/dataset.cpp
@@ -159,7 +159,7 @@
     : mUpdateTime(0)
     , mLength(0)
 {
-    memset(mTlvs, 0, sizeof(mTlvs));
+    ClearAllBytes(mTlvs);
 }
 
 void Dataset::Clear(void) { mLength = 0; }
@@ -194,14 +194,14 @@
             break;
 
         case Tlv::kChannel:
-            aDatasetInfo.SetChannel(As<ChannelTlv>(cur)->GetChannel());
+            aDatasetInfo.SetChannel(cur->ReadValueAs<ChannelTlv>().GetChannel());
             break;
 
         case Tlv::kChannelMask:
         {
-            uint32_t mask = As<ChannelMaskTlv>(cur)->GetChannelMask();
+            uint32_t mask;
 
-            if (mask != 0)
+            if (As<ChannelMaskTlv>(cur)->ReadChannelMask(mask) == kErrorNone)
             {
                 aDatasetInfo.SetChannelMask(mask);
             }
@@ -251,10 +251,10 @@
     }
 }
 
-void Dataset::ConvertTo(otOperationalDatasetTlvs &aDataset) const
+void Dataset::ConvertTo(Tlvs &aTlvs) const
 {
-    memcpy(aDataset.mTlvs, mTlvs, mLength);
-    aDataset.mLength = static_cast<uint8_t>(mLength);
+    memcpy(aTlvs.mTlvs, mTlvs, mLength);
+    aTlvs.mLength = static_cast<uint8_t>(mLength);
 }
 
 void Dataset::Set(Type aType, const Dataset &aDataset)
@@ -271,10 +271,10 @@
     mUpdateTime = aDataset.GetUpdateTime();
 }
 
-void Dataset::SetFrom(const otOperationalDatasetTlvs &aDataset)
+void Dataset::SetFrom(const Tlvs &aTlvs)
 {
-    mLength = aDataset.mLength;
-    memcpy(mTlvs, aDataset.mTlvs, mLength);
+    mLength = aTlvs.mLength;
+    memcpy(mTlvs, aTlvs.mTlvs, mLength);
 }
 
 Error Dataset::SetFrom(const Info &aDatasetInfo)
@@ -304,18 +304,18 @@
 
     if (aDatasetInfo.IsChannelPresent())
     {
-        ChannelTlv tlv;
-        tlv.Init();
-        tlv.SetChannel(aDatasetInfo.GetChannel());
-        IgnoreError(WriteTlv(tlv));
+        ChannelTlvValue channelValue;
+
+        channelValue.SetChannelAndPage(aDatasetInfo.GetChannel());
+        IgnoreError(Write<ChannelTlv>(channelValue));
     }
 
     if (aDatasetInfo.IsChannelMaskPresent())
     {
-        ChannelMaskTlv tlv;
-        tlv.Init();
-        tlv.SetChannelMask(aDatasetInfo.GetChannelMask());
-        IgnoreError(WriteTlv(tlv));
+        ChannelMaskTlv::Value value;
+
+        ChannelMaskTlv::PrepareValue(value, aDatasetInfo.GetChannelMask());
+        IgnoreError(WriteTlv(Tlv::kChannelMask, value.mData, value.mLength));
     }
 
     if (aDatasetInfo.IsExtendedPanIdPresent())
@@ -527,7 +527,7 @@
         {
         case Tlv::kChannel:
         {
-            uint8_t channel = static_cast<uint8_t>(As<ChannelTlv>(cur)->GetChannel());
+            uint8_t channel = static_cast<uint8_t>(cur->ReadValueAs<ChannelTlv>().GetChannel());
 
             error = mac.SetPanChannel(channel);
 
diff --git a/src/core/meshcop/dataset.hpp b/src/core/meshcop/dataset.hpp
index 131a271..5fbc799 100644
--- a/src/core/meshcop/dataset.hpp
+++ b/src/core/meshcop/dataset.hpp
@@ -76,6 +76,12 @@
     };
 
     /**
+     * Represents a Dataset as a sequence of TLVs.
+     *
+     */
+    typedef otOperationalDatasetTlvs Tlvs;
+
+    /**
      * Represents presence of different components in Active or Pending Operational Dataset.
      *
      */
@@ -770,10 +776,10 @@
     /**
      * Converts the TLV representation to structure representation.
      *
-     * @param[out] aDataset  A reference to `otOperationalDatasetTlvs` to output the Dataset.
+     * @param[out] aTlvs  A reference to output the Dataset as a sequence of TLVs.
      *
      */
-    void ConvertTo(otOperationalDatasetTlvs &aDataset) const;
+    void ConvertTo(Tlvs &aTlvs) const;
 
     /**
      * Returns the Dataset size in bytes.
@@ -859,10 +865,10 @@
     /**
      * Sets the Dataset using @p aDataset.
      *
-     * @param[in]  aDataset  The input Dataset as otOperationalDatasetTlvs.
+     * @param[in]  aDataset  The input Dataset as `Tlvs`.
      *
      */
-    void SetFrom(const otOperationalDatasetTlvs &aDataset);
+    void SetFrom(const Tlvs &aTlvs);
 
     /**
      * Appends the MLE Dataset TLV but excluding MeshCoP Sub Timestamp TLV.
diff --git a/src/core/meshcop/dataset_local.cpp b/src/core/meshcop/dataset_local.cpp
index 4c971c7..ba4c5fc 100644
--- a/src/core/meshcop/dataset_local.cpp
+++ b/src/core/meshcop/dataset_local.cpp
@@ -147,15 +147,15 @@
     return error;
 }
 
-Error DatasetLocal::Read(otOperationalDatasetTlvs &aDataset) const
+Error DatasetLocal::Read(Dataset::Tlvs &aDatasetTlvs) const
 {
     Dataset dataset;
     Error   error;
 
-    memset(&aDataset, 0, sizeof(aDataset));
+    ClearAllBytes(aDatasetTlvs);
 
     SuccessOrExit(error = Read(dataset));
-    dataset.ConvertTo(aDataset);
+    dataset.ConvertTo(aDatasetTlvs);
 
 exit:
     return error;
@@ -173,11 +173,11 @@
     return error;
 }
 
-Error DatasetLocal::Save(const otOperationalDatasetTlvs &aDataset)
+Error DatasetLocal::Save(const Dataset::Tlvs &aDatasetTlvs)
 {
     Dataset dataset;
 
-    dataset.SetFrom(aDataset);
+    dataset.SetFrom(aDatasetTlvs);
 
     return Save(dataset);
 }
diff --git a/src/core/meshcop/dataset_local.hpp b/src/core/meshcop/dataset_local.hpp
index 249f069..40e52bd 100644
--- a/src/core/meshcop/dataset_local.hpp
+++ b/src/core/meshcop/dataset_local.hpp
@@ -134,13 +134,13 @@
     /**
      * Retrieves the dataset from non-volatile memory.
      *
-     * @param[out]  aDataset  Where to place the dataset.
+     * @param[out]  aDatasetTlvs  Where to place the dataset.
      *
      * @retval kErrorNone      Successfully retrieved the dataset.
      * @retval kErrorNotFound  There is no corresponding dataset stored in non-volatile memory.
      *
      */
-    Error Read(otOperationalDatasetTlvs &aDataset) const;
+    Error Read(Dataset::Tlvs &aDatasetTlvs) const;
 
     /**
      * Returns the local time this dataset was last updated or restored.
@@ -164,13 +164,13 @@
     /**
      * Stores the dataset into non-volatile memory.
      *
-     * @param[in]  aDataset  The Dataset to save as `otOperationalDatasetTlvs`.
+     * @param[in]  aDatasetTlvs  The Dataset to save as `Dataset::Tlvs`.
      *
      * @retval kErrorNone             Successfully saved the dataset.
      * @retval kErrorNotImplemented   The platform does not implement settings functionality.
      *
      */
-    Error Save(const otOperationalDatasetTlvs &aDataset);
+    Error Save(const Dataset::Tlvs &aDatasetTlvs);
 
     /**
      * Stores the dataset into non-volatile memory.
diff --git a/src/core/meshcop/dataset_manager.cpp b/src/core/meshcop/dataset_manager.cpp
index bc31698..6c49a23 100644
--- a/src/core/meshcop/dataset_manager.cpp
+++ b/src/core/meshcop/dataset_manager.cpp
@@ -160,11 +160,11 @@
     return error;
 }
 
-Error DatasetManager::Save(const otOperationalDatasetTlvs &aDataset)
+Error DatasetManager::Save(const Dataset::Tlvs &aDatasetTlvs)
 {
     Error error;
 
-    SuccessOrExit(error = mLocal.Save(aDataset));
+    SuccessOrExit(error = mLocal.Save(aDatasetTlvs));
     HandleDatasetUpdated();
 
 exit:
@@ -228,7 +228,7 @@
 
     channelMaskTlv = As<ChannelMaskTlv>(dataset.FindTlv(Tlv::kChannelMask));
     VerifyOrExit(channelMaskTlv != nullptr, error = kErrorNotFound);
-    VerifyOrExit((mask = channelMaskTlv->GetChannelMask()) != 0);
+    SuccessOrExit(channelMaskTlv->ReadChannelMask(mask));
 
     aChannelMask.SetMask(mask & Get<Mac::Mac>().GetSupportedChannelMask().GetMask());
 
@@ -292,7 +292,11 @@
         OT_FALL_THROUGH;
 
     default:
-        LogError("send Dataset set to leader", error);
+        if (error != kErrorAlready)
+        {
+            LogWarnOnError(error, "send Dataset set to leader");
+        }
+
         FreeMessage(message);
         break;
     }
@@ -697,11 +701,11 @@
     return error;
 }
 
-Error PendingDatasetManager::Save(const otOperationalDatasetTlvs &aDataset)
+Error PendingDatasetManager::Save(const Dataset::Tlvs &aDatasetTlvs)
 {
     Error error;
 
-    SuccessOrExit(error = DatasetManager::Save(aDataset));
+    SuccessOrExit(error = DatasetManager::Save(aDatasetTlvs));
     StartDelayTimer();
 
 exit:
diff --git a/src/core/meshcop/dataset_manager.hpp b/src/core/meshcop/dataset_manager.hpp
index 36162a3..c0d90fe 100644
--- a/src/core/meshcop/dataset_manager.hpp
+++ b/src/core/meshcop/dataset_manager.hpp
@@ -96,13 +96,13 @@
     /**
      * Retrieves the dataset from non-volatile memory.
      *
-     * @param[out]  aDataset  Where to place the dataset.
+     * @param[out]  aDatasetTlvs  Where to place the dataset.
      *
      * @retval kErrorNone      Successfully retrieved the dataset.
      * @retval kErrorNotFound  There is no corresponding dataset stored in non-volatile memory.
      *
      */
-    Error Read(otOperationalDatasetTlvs &aDataset) const { return mLocal.Read(aDataset); }
+    Error Read(Dataset::Tlvs &aDatasetTlvs) const { return mLocal.Read(aDatasetTlvs); }
 
     /**
      * Retrieves the channel mask from local dataset.
@@ -261,13 +261,13 @@
     /**
      * Saves the Operational Dataset in non-volatile memory.
      *
-     * @param[in]  aDataset  The Operational Dataset.
+     * @param[in]  aDatasetTlvs  The Operational Dataset as `Dataset::Tlvs`.
      *
      * @retval kErrorNone             Successfully saved the dataset.
      * @retval kErrorNotImplemented   The platform does not implement settings functionality.
      *
      */
-    Error Save(const otOperationalDatasetTlvs &aDataset);
+    Error Save(const Dataset::Tlvs &aDatasetTlvs);
 
     /**
      * Sets the Operational Dataset for the partition.
@@ -459,13 +459,13 @@
     /**
      * Sets the Operational Dataset in non-volatile memory.
      *
-     * @param[in]  aDataset  The Operational Dataset.
+     * @param[in]  aDatasetTlvs  The Operational Dataset as `Dataset::Tlvs`.
      *
      * @retval kErrorNone            Successfully saved the dataset.
      * @retval kErrorNotImplemented  The platform does not implement settings functionality.
      *
      */
-    Error Save(const otOperationalDatasetTlvs &aDataset) { return DatasetManager::Save(aDataset); }
+    Error Save(const Dataset::Tlvs &aDatasetTlvs) { return DatasetManager::Save(aDatasetTlvs); }
 
 #if OPENTHREAD_FTD
 
@@ -558,13 +558,13 @@
      *
      * Also starts the Delay Timer.
      *
-     * @param[in]  aDataset  The Operational Dataset.
+     * @param[in]  aDatasetTlvs  The Operational Dataset as a sequence of TLVs.
      *
      * @retval kErrorNone            Successfully saved the dataset.
      * @retval kErrorNotImplemented  The platform does not implement settings functionality.
      *
      */
-    Error Save(const otOperationalDatasetTlvs &aDataset);
+    Error Save(const Dataset::Tlvs &aDatasetTlvs);
 
     /**
      * Sets the Operational Dataset for the partition.
diff --git a/src/core/meshcop/dataset_manager_ftd.cpp b/src/core/meshcop/dataset_manager_ftd.cpp
index 3eec1a8..5f74842 100644
--- a/src/core/meshcop/dataset_manager_ftd.cpp
+++ b/src/core/meshcop/dataset_manager_ftd.cpp
@@ -82,7 +82,7 @@
     StateTlv::State    state                    = StateTlv::kReject;
     Dataset            dataset;
     Timestamp          activeTimestamp;
-    ChannelTlv         channel;
+    ChannelTlvValue    channelValue;
     uint16_t           sessionId;
     Ip6::NetworkPrefix meshLocalPrefix;
     NetworkKey         networkKey;
@@ -116,12 +116,11 @@
         VerifyOrExit(Timestamp::Compare(&activeTimestamp, mLocal.GetTimestamp()) > 0);
     }
 
-    // check channel
-    if (Tlv::FindTlv(aMessage, channel) == kErrorNone)
+    if (Tlv::Find<ChannelTlv>(aMessage, channelValue) == kErrorNone)
     {
-        VerifyOrExit(channel.IsValid());
+        VerifyOrExit(channelValue.IsValid());
 
-        if (channel.GetChannel() != Get<Mac::Mac>().GetPanChannel())
+        if (channelValue.GetChannel() != Get<Mac::Mac>().GetPanChannel())
         {
             doesAffectConnectivity = true;
         }
@@ -312,18 +311,18 @@
 
     if (!dataset.Contains<ChannelTlv>())
     {
-        ChannelTlv tlv;
-        tlv.Init();
-        tlv.SetChannel(Get<Mac::Mac>().GetPanChannel());
-        IgnoreError(dataset.WriteTlv(tlv));
+        ChannelTlvValue channelValue;
+
+        channelValue.SetChannelAndPage(Get<Mac::Mac>().GetPanChannel());
+        IgnoreError(dataset.Write<ChannelTlv>(channelValue));
     }
 
     if (!dataset.Contains<ChannelMaskTlv>())
     {
-        ChannelMaskTlv tlv;
-        tlv.Init();
-        tlv.SetChannelMask(Get<Mac::Mac>().GetSupportedChannelMask().GetMask());
-        IgnoreError(dataset.WriteTlv(tlv));
+        ChannelMaskTlv::Value value;
+
+        ChannelMaskTlv::PrepareValue(value, Get<Mac::Mac>().GetSupportedChannelMask().GetMask());
+        IgnoreError(dataset.WriteTlv(Tlv::kChannelMask, value.mData, value.mLength));
     }
 
     if (!dataset.Contains<ExtendedPanIdTlv>())
diff --git a/src/core/meshcop/dtls.cpp b/src/core/meshcop/dtls.cpp
deleted file mode 100644
index e3c2275..0000000
--- a/src/core/meshcop/dtls.cpp
+++ /dev/null
@@ -1,1007 +0,0 @@
-/*
- *  Copyright (c) 2016, The OpenThread Authors.
- *  All rights reserved.
- *
- *  Redistribution and use in source and binary forms, with or without
- *  modification, are permitted provided that the following conditions are met:
- *  1. Redistributions of source code must retain the above copyright
- *     notice, this list of conditions and the following disclaimer.
- *  2. Redistributions in binary form must reproduce the above copyright
- *     notice, this list of conditions and the following disclaimer in the
- *     documentation and/or other materials provided with the distribution.
- *  3. Neither the name of the copyright holder nor the
- *     names of its contributors may be used to endorse or promote products
- *     derived from this software without specific prior written permission.
- *
- *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- *  POSSIBILITY OF SUCH DAMAGE.
- */
-
-/**
- * @file
- *   This file implements the necessary hooks for mbedTLS.
- */
-
-#include "dtls.hpp"
-
-#include <mbedtls/debug.h>
-#ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
-#include <mbedtls/pem.h>
-#endif
-
-#include <openthread/platform/radio.h>
-
-#include "common/as_core_type.hpp"
-#include "common/code_utils.hpp"
-#include "common/debug.hpp"
-#include "common/encoding.hpp"
-#include "common/locator_getters.hpp"
-#include "common/log.hpp"
-#include "common/timer.hpp"
-#include "crypto/mbedtls.hpp"
-#include "crypto/sha256.hpp"
-#include "instance/instance.hpp"
-#include "thread/thread_netif.hpp"
-
-#if OPENTHREAD_CONFIG_DTLS_ENABLE
-
-namespace ot {
-namespace MeshCoP {
-
-RegisterLogModule("Dtls");
-
-#if (MBEDTLS_VERSION_NUMBER >= 0x03010000)
-const uint16_t Dtls::sGroups[] = {MBEDTLS_SSL_IANA_TLS_GROUP_SECP256R1, MBEDTLS_SSL_IANA_TLS_GROUP_NONE};
-#else
-const mbedtls_ecp_group_id Dtls::sCurves[] = {MBEDTLS_ECP_DP_SECP256R1, MBEDTLS_ECP_DP_NONE};
-#endif
-
-#if defined(MBEDTLS_KEY_EXCHANGE__WITH_CERT__ENABLED) || defined(MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED)
-#if (MBEDTLS_VERSION_NUMBER >= 0x03020000)
-const uint16_t Dtls::sSignatures[] = {MBEDTLS_TLS1_3_SIG_ECDSA_SECP256R1_SHA256, MBEDTLS_TLS1_3_SIG_NONE};
-#else
-const int Dtls::sHashes[] = {MBEDTLS_MD_SHA256, MBEDTLS_MD_NONE};
-#endif
-#endif
-
-Dtls::Dtls(Instance &aInstance, bool aLayerTwoSecurity)
-    : InstanceLocator(aInstance)
-    , mState(kStateClosed)
-    , mPskLength(0)
-    , mVerifyPeerCertificate(true)
-    , mTimer(aInstance, Dtls::HandleTimer, this)
-    , mTimerIntermediate(0)
-    , mTimerSet(false)
-    , mLayerTwoSecurity(aLayerTwoSecurity)
-    , mReceiveMessage(nullptr)
-    , mSocket(aInstance)
-    , mMessageSubType(Message::kSubTypeNone)
-    , mMessageDefaultSubType(Message::kSubTypeNone)
-{
-#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
-#ifdef MBEDTLS_KEY_EXCHANGE_PSK_ENABLED
-    mPreSharedKey         = nullptr;
-    mPreSharedKeyIdentity = nullptr;
-    mPreSharedKeyIdLength = 0;
-    mPreSharedKeyLength   = 0;
-#endif
-
-#ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
-    mCaChainSrc       = nullptr;
-    mCaChainLength    = 0;
-    mOwnCertSrc       = nullptr;
-    mOwnCertLength    = 0;
-    mPrivateKeySrc    = nullptr;
-    mPrivateKeyLength = 0;
-    memset(&mCaChain, 0, sizeof(mCaChain));
-    memset(&mOwnCert, 0, sizeof(mOwnCert));
-    memset(&mPrivateKey, 0, sizeof(mPrivateKey));
-#endif
-#endif
-
-    memset(mCipherSuites, 0, sizeof(mCipherSuites));
-    memset(mPsk, 0, sizeof(mPsk));
-    memset(&mSsl, 0, sizeof(mSsl));
-    memset(&mConf, 0, sizeof(mConf));
-
-#ifdef MBEDTLS_SSL_COOKIE_C
-    memset(&mCookieCtx, 0, sizeof(mCookieCtx));
-#endif
-}
-
-void Dtls::FreeMbedtls(void)
-{
-#if defined(MBEDTLS_SSL_SRV_C) && defined(MBEDTLS_SSL_COOKIE_C)
-    mbedtls_ssl_cookie_free(&mCookieCtx);
-#endif
-#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
-#ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
-    mbedtls_x509_crt_free(&mCaChain);
-    mbedtls_x509_crt_free(&mOwnCert);
-    mbedtls_pk_free(&mPrivateKey);
-#endif
-#endif
-    mbedtls_ssl_config_free(&mConf);
-    mbedtls_ssl_free(&mSsl);
-}
-
-Error Dtls::Open(ReceiveHandler aReceiveHandler, ConnectedHandler aConnectedHandler, void *aContext)
-{
-    Error error;
-
-    VerifyOrExit(mState == kStateClosed, error = kErrorAlready);
-
-    SuccessOrExit(error = mSocket.Open(&Dtls::HandleUdpReceive, this));
-
-    mConnectedCallback.Set(aConnectedHandler, aContext);
-    mReceiveCallback.Set(aReceiveHandler, aContext);
-    mState = kStateOpen;
-
-exit:
-    return error;
-}
-
-Error Dtls::Connect(const Ip6::SockAddr &aSockAddr)
-{
-    Error error;
-
-    VerifyOrExit(mState == kStateOpen, error = kErrorInvalidState);
-
-    mMessageInfo.SetPeerAddr(aSockAddr.GetAddress());
-    mMessageInfo.SetPeerPort(aSockAddr.mPort);
-
-    error = Setup(true);
-
-exit:
-    return error;
-}
-
-void Dtls::HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
-{
-    static_cast<Dtls *>(aContext)->HandleUdpReceive(AsCoreType(aMessage), AsCoreType(aMessageInfo));
-}
-
-void Dtls::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
-{
-    switch (mState)
-    {
-    case Dtls::kStateClosed:
-        ExitNow();
-
-    case Dtls::kStateOpen:
-        mMessageInfo.SetPeerAddr(aMessageInfo.GetPeerAddr());
-        mMessageInfo.SetPeerPort(aMessageInfo.GetPeerPort());
-        mMessageInfo.SetIsHostInterface(aMessageInfo.IsHostInterface());
-
-        mMessageInfo.SetSockAddr(aMessageInfo.GetSockAddr());
-        mMessageInfo.SetSockPort(aMessageInfo.GetSockPort());
-
-        SuccessOrExit(Setup(false));
-        break;
-
-    default:
-        // Once DTLS session is started, communicate only with a peer.
-        VerifyOrExit((mMessageInfo.GetPeerAddr() == aMessageInfo.GetPeerAddr()) &&
-                     (mMessageInfo.GetPeerPort() == aMessageInfo.GetPeerPort()));
-        break;
-    }
-
-#ifdef MBEDTLS_SSL_SRV_C
-    if (mState == Dtls::kStateConnecting)
-    {
-        IgnoreError(SetClientId(mMessageInfo.GetPeerAddr().mFields.m8, sizeof(mMessageInfo.GetPeerAddr().mFields)));
-    }
-#endif
-
-    Receive(aMessage);
-
-exit:
-    return;
-}
-
-uint16_t Dtls::GetUdpPort(void) const { return mSocket.GetSockName().GetPort(); }
-
-Error Dtls::Bind(uint16_t aPort)
-{
-    Error error;
-
-    VerifyOrExit(mState == kStateOpen, error = kErrorInvalidState);
-    VerifyOrExit(!mTransportCallback.IsSet(), error = kErrorAlready);
-
-    SuccessOrExit(error = mSocket.Bind(aPort, Ip6::kNetifUnspecified));
-
-exit:
-    return error;
-}
-
-Error Dtls::Bind(TransportCallback aCallback, void *aContext)
-{
-    Error error = kErrorNone;
-
-    VerifyOrExit(mState == kStateOpen, error = kErrorInvalidState);
-    VerifyOrExit(!mSocket.IsBound(), error = kErrorAlready);
-    VerifyOrExit(!mTransportCallback.IsSet(), error = kErrorAlready);
-
-    mTransportCallback.Set(aCallback, aContext);
-
-exit:
-    return error;
-}
-
-Error Dtls::Setup(bool aClient)
-{
-    int rval;
-
-    // do not handle new connection before guard time expired
-    VerifyOrExit(mState == kStateOpen, rval = MBEDTLS_ERR_SSL_TIMEOUT);
-
-    mState = kStateInitializing;
-
-    mbedtls_ssl_init(&mSsl);
-    mbedtls_ssl_config_init(&mConf);
-#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
-#ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
-    mbedtls_x509_crt_init(&mCaChain);
-    mbedtls_x509_crt_init(&mOwnCert);
-    mbedtls_pk_init(&mPrivateKey);
-#endif
-#endif
-#if defined(MBEDTLS_SSL_SRV_C) && defined(MBEDTLS_SSL_COOKIE_C)
-    mbedtls_ssl_cookie_init(&mCookieCtx);
-#endif
-
-    rval = mbedtls_ssl_config_defaults(&mConf, aClient ? MBEDTLS_SSL_IS_CLIENT : MBEDTLS_SSL_IS_SERVER,
-                                       MBEDTLS_SSL_TRANSPORT_DATAGRAM, MBEDTLS_SSL_PRESET_DEFAULT);
-    VerifyOrExit(rval == 0);
-
-#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
-    if (mVerifyPeerCertificate && mCipherSuites[0] == MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8)
-    {
-        mbedtls_ssl_conf_authmode(&mConf, MBEDTLS_SSL_VERIFY_REQUIRED);
-    }
-    else
-    {
-        mbedtls_ssl_conf_authmode(&mConf, MBEDTLS_SSL_VERIFY_NONE);
-    }
-#else
-    OT_UNUSED_VARIABLE(mVerifyPeerCertificate);
-#endif
-
-    mbedtls_ssl_conf_rng(&mConf, Crypto::MbedTls::CryptoSecurePrng, nullptr);
-#if (MBEDTLS_VERSION_NUMBER >= 0x03020000)
-    mbedtls_ssl_conf_min_tls_version(&mConf, MBEDTLS_SSL_VERSION_TLS1_2);
-    mbedtls_ssl_conf_max_tls_version(&mConf, MBEDTLS_SSL_VERSION_TLS1_2);
-#else
-    mbedtls_ssl_conf_min_version(&mConf, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3);
-    mbedtls_ssl_conf_max_version(&mConf, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3);
-#endif
-
-    OT_ASSERT(mCipherSuites[1] == 0);
-    mbedtls_ssl_conf_ciphersuites(&mConf, mCipherSuites);
-    if (mCipherSuites[0] == MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8)
-    {
-#if (MBEDTLS_VERSION_NUMBER >= 0x03010000)
-        mbedtls_ssl_conf_groups(&mConf, sGroups);
-#else
-        mbedtls_ssl_conf_curves(&mConf, sCurves);
-#endif
-#if defined(MBEDTLS_KEY_EXCHANGE__WITH_CERT__ENABLED) || defined(MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED)
-#if (MBEDTLS_VERSION_NUMBER >= 0x03020000)
-        mbedtls_ssl_conf_sig_algs(&mConf, sSignatures);
-#else
-        mbedtls_ssl_conf_sig_hashes(&mConf, sHashes);
-#endif
-#endif
-    }
-
-#if (MBEDTLS_VERSION_NUMBER >= 0x03000000)
-    mbedtls_ssl_set_export_keys_cb(&mSsl, HandleMbedtlsExportKeys, this);
-#else
-    mbedtls_ssl_conf_export_keys_cb(&mConf, HandleMbedtlsExportKeys, this);
-#endif
-
-    mbedtls_ssl_conf_handshake_timeout(&mConf, 8000, 60000);
-    mbedtls_ssl_conf_dbg(&mConf, HandleMbedtlsDebug, this);
-
-#if defined(MBEDTLS_SSL_SRV_C) && defined(MBEDTLS_SSL_COOKIE_C)
-    if (!aClient)
-    {
-        rval = mbedtls_ssl_cookie_setup(&mCookieCtx, Crypto::MbedTls::CryptoSecurePrng, nullptr);
-        VerifyOrExit(rval == 0);
-
-        mbedtls_ssl_conf_dtls_cookies(&mConf, mbedtls_ssl_cookie_write, mbedtls_ssl_cookie_check, &mCookieCtx);
-    }
-#endif
-
-    rval = mbedtls_ssl_setup(&mSsl, &mConf);
-    VerifyOrExit(rval == 0);
-
-    mbedtls_ssl_set_bio(&mSsl, this, &Dtls::HandleMbedtlsTransmit, HandleMbedtlsReceive, nullptr);
-    mbedtls_ssl_set_timer_cb(&mSsl, this, &Dtls::HandleMbedtlsSetTimer, HandleMbedtlsGetTimer);
-
-    if (mCipherSuites[0] == MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8)
-    {
-        rval = mbedtls_ssl_set_hs_ecjpake_password(&mSsl, mPsk, mPskLength);
-    }
-#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
-    else
-    {
-        rval = SetApplicationCoapSecureKeys();
-    }
-#endif
-    VerifyOrExit(rval == 0);
-
-    mReceiveMessage = nullptr;
-    mMessageSubType = Message::kSubTypeNone;
-    mState          = kStateConnecting;
-
-    if (mCipherSuites[0] == MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8)
-    {
-        LogInfo("DTLS started");
-    }
-#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
-    else
-    {
-        LogInfo("Application Coap Secure DTLS started");
-    }
-#endif
-
-    mState = kStateConnecting;
-
-    Process();
-
-exit:
-    if ((mState == kStateInitializing) && (rval != 0))
-    {
-        mState = kStateOpen;
-        FreeMbedtls();
-    }
-
-    return Crypto::MbedTls::MapError(rval);
-}
-
-#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
-int Dtls::SetApplicationCoapSecureKeys(void)
-{
-    int rval = 0;
-
-    switch (mCipherSuites[0])
-    {
-    case MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8:
-#ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
-        if (mCaChainSrc != nullptr)
-        {
-            rval = mbedtls_x509_crt_parse(&mCaChain, static_cast<const unsigned char *>(mCaChainSrc),
-                                          static_cast<size_t>(mCaChainLength));
-            VerifyOrExit(rval == 0);
-            mbedtls_ssl_conf_ca_chain(&mConf, &mCaChain, nullptr);
-        }
-
-        if (mOwnCertSrc != nullptr && mPrivateKeySrc != nullptr)
-        {
-            rval = mbedtls_x509_crt_parse(&mOwnCert, static_cast<const unsigned char *>(mOwnCertSrc),
-                                          static_cast<size_t>(mOwnCertLength));
-            VerifyOrExit(rval == 0);
-
-#if (MBEDTLS_VERSION_NUMBER >= 0x03000000)
-            rval = mbedtls_pk_parse_key(&mPrivateKey, static_cast<const unsigned char *>(mPrivateKeySrc),
-                                        static_cast<size_t>(mPrivateKeyLength), nullptr, 0,
-                                        Crypto::MbedTls::CryptoSecurePrng, nullptr);
-#else
-            rval = mbedtls_pk_parse_key(&mPrivateKey, static_cast<const unsigned char *>(mPrivateKeySrc),
-                                        static_cast<size_t>(mPrivateKeyLength), nullptr, 0);
-#endif
-            VerifyOrExit(rval == 0);
-            rval = mbedtls_ssl_conf_own_cert(&mConf, &mOwnCert, &mPrivateKey);
-            VerifyOrExit(rval == 0);
-        }
-#endif
-        break;
-
-    case MBEDTLS_TLS_PSK_WITH_AES_128_CCM_8:
-#ifdef MBEDTLS_KEY_EXCHANGE_PSK_ENABLED
-        rval = mbedtls_ssl_conf_psk(&mConf, static_cast<const unsigned char *>(mPreSharedKey), mPreSharedKeyLength,
-                                    static_cast<const unsigned char *>(mPreSharedKeyIdentity), mPreSharedKeyIdLength);
-        VerifyOrExit(rval == 0);
-#endif
-        break;
-
-    default:
-        LogCrit("Application Coap Secure: Not supported cipher.");
-        rval = MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
-        ExitNow();
-        break;
-    }
-
-exit:
-    return rval;
-}
-
-#endif // OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
-
-void Dtls::Close(void)
-{
-    Disconnect();
-
-    mState    = kStateClosed;
-    mTimerSet = false;
-    mTransportCallback.Clear();
-
-    IgnoreError(mSocket.Close());
-    mTimer.Stop();
-}
-
-void Dtls::Disconnect(void)
-{
-    VerifyOrExit(mState == kStateConnecting || mState == kStateConnected);
-
-    mbedtls_ssl_close_notify(&mSsl);
-    mState = kStateCloseNotify;
-    mTimer.Start(kGuardTimeNewConnectionMilli);
-
-    mMessageInfo.Clear();
-    IgnoreError(mSocket.Connect());
-
-    FreeMbedtls();
-
-exit:
-    return;
-}
-
-Error Dtls::SetPsk(const uint8_t *aPsk, uint8_t aPskLength)
-{
-    Error error = kErrorNone;
-
-    VerifyOrExit(aPskLength <= sizeof(mPsk), error = kErrorInvalidArgs);
-
-    memcpy(mPsk, aPsk, aPskLength);
-    mPskLength       = aPskLength;
-    mCipherSuites[0] = MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8;
-    mCipherSuites[1] = 0;
-
-exit:
-    return error;
-}
-
-#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
-#ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
-
-void Dtls::SetCertificate(const uint8_t *aX509Certificate,
-                          uint32_t       aX509CertLength,
-                          const uint8_t *aPrivateKey,
-                          uint32_t       aPrivateKeyLength)
-{
-    OT_ASSERT(aX509CertLength > 0);
-    OT_ASSERT(aX509Certificate != nullptr);
-
-    OT_ASSERT(aPrivateKeyLength > 0);
-    OT_ASSERT(aPrivateKey != nullptr);
-
-    mOwnCertSrc       = aX509Certificate;
-    mOwnCertLength    = aX509CertLength;
-    mPrivateKeySrc    = aPrivateKey;
-    mPrivateKeyLength = aPrivateKeyLength;
-
-    mCipherSuites[0] = MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8;
-    mCipherSuites[1] = 0;
-}
-
-void Dtls::SetCaCertificateChain(const uint8_t *aX509CaCertificateChain, uint32_t aX509CaCertChainLength)
-{
-    OT_ASSERT(aX509CaCertChainLength > 0);
-    OT_ASSERT(aX509CaCertificateChain != nullptr);
-
-    mCaChainSrc    = aX509CaCertificateChain;
-    mCaChainLength = aX509CaCertChainLength;
-}
-
-#endif // MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
-
-#ifdef MBEDTLS_KEY_EXCHANGE_PSK_ENABLED
-void Dtls::SetPreSharedKey(const uint8_t *aPsk, uint16_t aPskLength, const uint8_t *aPskIdentity, uint16_t aPskIdLength)
-{
-    OT_ASSERT(aPsk != nullptr);
-    OT_ASSERT(aPskIdentity != nullptr);
-    OT_ASSERT(aPskLength > 0);
-    OT_ASSERT(aPskIdLength > 0);
-
-    mPreSharedKey         = aPsk;
-    mPreSharedKeyLength   = aPskLength;
-    mPreSharedKeyIdentity = aPskIdentity;
-    mPreSharedKeyIdLength = aPskIdLength;
-
-    mCipherSuites[0] = MBEDTLS_TLS_PSK_WITH_AES_128_CCM_8;
-    mCipherSuites[1] = 0;
-}
-#endif
-
-#if defined(MBEDTLS_BASE64_C) && defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
-Error Dtls::GetPeerCertificateBase64(unsigned char *aPeerCert, size_t *aCertLength, size_t aCertBufferSize)
-{
-    Error error = kErrorNone;
-
-    VerifyOrExit(mState == kStateConnected, error = kErrorInvalidState);
-
-#if (MBEDTLS_VERSION_NUMBER >= 0x03010000)
-    VerifyOrExit(mbedtls_base64_encode(aPeerCert, aCertBufferSize, aCertLength,
-                                       mSsl.MBEDTLS_PRIVATE(session)->MBEDTLS_PRIVATE(peer_cert)->raw.p,
-                                       mSsl.MBEDTLS_PRIVATE(session)->MBEDTLS_PRIVATE(peer_cert)->raw.len) == 0,
-                 error = kErrorNoBufs);
-#else
-    VerifyOrExit(
-        mbedtls_base64_encode(
-            aPeerCert, aCertBufferSize, aCertLength,
-            mSsl.MBEDTLS_PRIVATE(session)->MBEDTLS_PRIVATE(peer_cert)->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(p),
-            mSsl.MBEDTLS_PRIVATE(session)->MBEDTLS_PRIVATE(peer_cert)->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(len)) == 0,
-        error = kErrorNoBufs);
-#endif
-
-exit:
-    return error;
-}
-#endif // defined(MBEDTLS_BASE64_C) && defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
-
-#endif // OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
-
-#ifdef MBEDTLS_SSL_SRV_C
-Error Dtls::SetClientId(const uint8_t *aClientId, uint8_t aLength)
-{
-    int rval = mbedtls_ssl_set_client_transport_id(&mSsl, aClientId, aLength);
-    return Crypto::MbedTls::MapError(rval);
-}
-#endif
-
-Error Dtls::Send(Message &aMessage, uint16_t aLength)
-{
-    Error   error = kErrorNone;
-    uint8_t buffer[kApplicationDataMaxLength];
-
-    VerifyOrExit(aLength <= kApplicationDataMaxLength, error = kErrorNoBufs);
-
-    // Store message specific sub type.
-    if (aMessage.GetSubType() != Message::kSubTypeNone)
-    {
-        mMessageSubType = aMessage.GetSubType();
-    }
-
-    aMessage.ReadBytes(0, buffer, aLength);
-
-    SuccessOrExit(error = Crypto::MbedTls::MapError(mbedtls_ssl_write(&mSsl, buffer, aLength)));
-
-    aMessage.Free();
-
-exit:
-    return error;
-}
-
-void Dtls::Receive(Message &aMessage)
-{
-    mReceiveMessage = &aMessage;
-
-    Process();
-
-    mReceiveMessage = nullptr;
-}
-
-int Dtls::HandleMbedtlsTransmit(void *aContext, const unsigned char *aBuf, size_t aLength)
-{
-    return static_cast<Dtls *>(aContext)->HandleMbedtlsTransmit(aBuf, aLength);
-}
-
-int Dtls::HandleMbedtlsTransmit(const unsigned char *aBuf, size_t aLength)
-{
-    Error error;
-    int   rval = 0;
-
-    if (mCipherSuites[0] == MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8)
-    {
-        LogDebg("HandleMbedtlsTransmit");
-    }
-#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
-    else
-    {
-        LogDebg("ApplicationCoapSecure HandleMbedtlsTransmit");
-    }
-#endif
-
-    error = HandleDtlsSend(aBuf, static_cast<uint16_t>(aLength), mMessageSubType);
-
-    // Restore default sub type.
-    mMessageSubType = mMessageDefaultSubType;
-
-    switch (error)
-    {
-    case kErrorNone:
-        rval = static_cast<int>(aLength);
-        break;
-
-    case kErrorNoBufs:
-        rval = MBEDTLS_ERR_SSL_WANT_WRITE;
-        break;
-
-    default:
-        LogWarn("HandleMbedtlsTransmit: %s error", ErrorToString(error));
-        rval = MBEDTLS_ERR_NET_SEND_FAILED;
-        break;
-    }
-
-    return rval;
-}
-
-int Dtls::HandleMbedtlsReceive(void *aContext, unsigned char *aBuf, size_t aLength)
-{
-    return static_cast<Dtls *>(aContext)->HandleMbedtlsReceive(aBuf, aLength);
-}
-
-int Dtls::HandleMbedtlsReceive(unsigned char *aBuf, size_t aLength)
-{
-    int rval;
-
-    if (mCipherSuites[0] == MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8)
-    {
-        LogDebg("HandleMbedtlsReceive");
-    }
-#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
-    else
-    {
-        LogDebg("ApplicationCoapSecure HandleMbedtlsReceive");
-    }
-#endif
-
-    VerifyOrExit(mReceiveMessage != nullptr && (rval = mReceiveMessage->GetLength() - mReceiveMessage->GetOffset()) > 0,
-                 rval = MBEDTLS_ERR_SSL_WANT_READ);
-
-    if (aLength > static_cast<size_t>(rval))
-    {
-        aLength = static_cast<size_t>(rval);
-    }
-
-    rval = mReceiveMessage->ReadBytes(mReceiveMessage->GetOffset(), aBuf, static_cast<uint16_t>(aLength));
-    mReceiveMessage->MoveOffset(rval);
-
-exit:
-    return rval;
-}
-
-int Dtls::HandleMbedtlsGetTimer(void *aContext) { return static_cast<Dtls *>(aContext)->HandleMbedtlsGetTimer(); }
-
-int Dtls::HandleMbedtlsGetTimer(void)
-{
-    int rval;
-
-    if (mCipherSuites[0] == MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8)
-    {
-        LogDebg("HandleMbedtlsGetTimer");
-    }
-#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
-    else
-    {
-        LogDebg("ApplicationCoapSecure HandleMbedtlsGetTimer");
-    }
-#endif
-
-    if (!mTimerSet)
-    {
-        rval = -1;
-    }
-    else if (!mTimer.IsRunning())
-    {
-        rval = 2;
-    }
-    else if (mTimerIntermediate <= TimerMilli::GetNow())
-    {
-        rval = 1;
-    }
-    else
-    {
-        rval = 0;
-    }
-
-    return rval;
-}
-
-void Dtls::HandleMbedtlsSetTimer(void *aContext, uint32_t aIntermediate, uint32_t aFinish)
-{
-    static_cast<Dtls *>(aContext)->HandleMbedtlsSetTimer(aIntermediate, aFinish);
-}
-
-void Dtls::HandleMbedtlsSetTimer(uint32_t aIntermediate, uint32_t aFinish)
-{
-    if (mCipherSuites[0] == MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8)
-    {
-        LogDebg("SetTimer");
-    }
-#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
-    else
-    {
-        LogDebg("ApplicationCoapSecure SetTimer");
-    }
-#endif
-
-    if (aFinish == 0)
-    {
-        mTimerSet = false;
-        mTimer.Stop();
-    }
-    else
-    {
-        mTimerSet = true;
-        mTimer.Start(aFinish);
-        mTimerIntermediate = TimerMilli::GetNow() + aIntermediate;
-    }
-}
-
-#if (MBEDTLS_VERSION_NUMBER >= 0x03000000)
-
-void Dtls::HandleMbedtlsExportKeys(void                       *aContext,
-                                   mbedtls_ssl_key_export_type aType,
-                                   const unsigned char        *aMasterSecret,
-                                   size_t                      aMasterSecretLen,
-                                   const unsigned char         aClientRandom[32],
-                                   const unsigned char         aServerRandom[32],
-                                   mbedtls_tls_prf_types       aTlsPrfType)
-{
-    static_cast<Dtls *>(aContext)->HandleMbedtlsExportKeys(aType, aMasterSecret, aMasterSecretLen, aClientRandom,
-                                                           aServerRandom, aTlsPrfType);
-}
-
-void Dtls::HandleMbedtlsExportKeys(mbedtls_ssl_key_export_type aType,
-                                   const unsigned char        *aMasterSecret,
-                                   size_t                      aMasterSecretLen,
-                                   const unsigned char         aClientRandom[32],
-                                   const unsigned char         aServerRandom[32],
-                                   mbedtls_tls_prf_types       aTlsPrfType)
-{
-    Crypto::Sha256::Hash kek;
-    Crypto::Sha256       sha256;
-    unsigned char        keyBlock[kDtlsKeyBlockSize];
-    unsigned char        randBytes[2 * kDtlsRandomBufferSize];
-
-    VerifyOrExit(mCipherSuites[0] == MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8);
-    VerifyOrExit(aType == MBEDTLS_SSL_KEY_EXPORT_TLS12_MASTER_SECRET);
-
-    memcpy(randBytes, aServerRandom, kDtlsRandomBufferSize);
-    memcpy(randBytes + kDtlsRandomBufferSize, aClientRandom, kDtlsRandomBufferSize);
-
-    // Retrieve the Key block from Master secret
-    mbedtls_ssl_tls_prf(aTlsPrfType, aMasterSecret, aMasterSecretLen, "key expansion", randBytes, sizeof(randBytes),
-                        keyBlock, sizeof(keyBlock));
-
-    sha256.Start();
-    sha256.Update(keyBlock, kDtlsKeyBlockSize);
-    sha256.Finish(kek);
-
-    LogDebg("Generated KEK");
-    Get<KeyManager>().SetKek(kek.GetBytes());
-
-exit:
-    return;
-}
-
-#else
-
-int Dtls::HandleMbedtlsExportKeys(void                *aContext,
-                                  const unsigned char *aMasterSecret,
-                                  const unsigned char *aKeyBlock,
-                                  size_t               aMacLength,
-                                  size_t               aKeyLength,
-                                  size_t               aIvLength)
-{
-    return static_cast<Dtls *>(aContext)->HandleMbedtlsExportKeys(aMasterSecret, aKeyBlock, aMacLength, aKeyLength,
-                                                                  aIvLength);
-}
-
-int Dtls::HandleMbedtlsExportKeys(const unsigned char *aMasterSecret,
-                                  const unsigned char *aKeyBlock,
-                                  size_t               aMacLength,
-                                  size_t               aKeyLength,
-                                  size_t               aIvLength)
-{
-    OT_UNUSED_VARIABLE(aMasterSecret);
-
-    Crypto::Sha256::Hash kek;
-    Crypto::Sha256       sha256;
-
-    VerifyOrExit(mCipherSuites[0] == MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8);
-
-    sha256.Start();
-    sha256.Update(aKeyBlock, 2 * static_cast<uint16_t>(aMacLength + aKeyLength + aIvLength));
-    sha256.Finish(kek);
-
-    LogDebg("Generated KEK");
-    Get<KeyManager>().SetKek(kek.GetBytes());
-
-exit:
-    return 0;
-}
-
-#endif // (MBEDTLS_VERSION_NUMBER >= 0x03000000)
-
-void Dtls::HandleTimer(Timer &aTimer)
-{
-    static_cast<Dtls *>(static_cast<TimerMilliContext &>(aTimer).GetContext())->HandleTimer();
-}
-
-void Dtls::HandleTimer(void)
-{
-    switch (mState)
-    {
-    case kStateConnecting:
-    case kStateConnected:
-        Process();
-        break;
-
-    case kStateCloseNotify:
-        mState = kStateOpen;
-        mTimer.Stop();
-        mConnectedCallback.InvokeIfSet(false);
-        break;
-
-    default:
-        OT_ASSERT(false);
-        OT_UNREACHABLE_CODE(break);
-    }
-}
-
-void Dtls::Process(void)
-{
-    uint8_t buf[OPENTHREAD_CONFIG_DTLS_MAX_CONTENT_LEN];
-    bool    shouldDisconnect = false;
-    int     rval;
-
-    while ((mState == kStateConnecting) || (mState == kStateConnected))
-    {
-        if (mState == kStateConnecting)
-        {
-            rval = mbedtls_ssl_handshake(&mSsl);
-
-            if (mSsl.MBEDTLS_PRIVATE(state) == MBEDTLS_SSL_HANDSHAKE_OVER)
-            {
-                mState = kStateConnected;
-                mConnectedCallback.InvokeIfSet(true);
-            }
-        }
-        else
-        {
-            rval = mbedtls_ssl_read(&mSsl, buf, sizeof(buf));
-        }
-
-        if (rval > 0)
-        {
-            mReceiveCallback.InvokeIfSet(buf, static_cast<uint16_t>(rval));
-        }
-        else if (rval == 0 || rval == MBEDTLS_ERR_SSL_WANT_READ || rval == MBEDTLS_ERR_SSL_WANT_WRITE)
-        {
-            break;
-        }
-        else
-        {
-            switch (rval)
-            {
-            case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY:
-                mbedtls_ssl_close_notify(&mSsl);
-                ExitNow(shouldDisconnect = true);
-                OT_UNREACHABLE_CODE(break);
-
-            case MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED:
-                break;
-
-            case MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE:
-                mbedtls_ssl_close_notify(&mSsl);
-                ExitNow(shouldDisconnect = true);
-                OT_UNREACHABLE_CODE(break);
-
-            case MBEDTLS_ERR_SSL_INVALID_MAC:
-                if (mSsl.MBEDTLS_PRIVATE(state) != MBEDTLS_SSL_HANDSHAKE_OVER)
-                {
-                    mbedtls_ssl_send_alert_message(&mSsl, MBEDTLS_SSL_ALERT_LEVEL_FATAL,
-                                                   MBEDTLS_SSL_ALERT_MSG_BAD_RECORD_MAC);
-                    ExitNow(shouldDisconnect = true);
-                }
-
-                break;
-
-            default:
-                if (mSsl.MBEDTLS_PRIVATE(state) != MBEDTLS_SSL_HANDSHAKE_OVER)
-                {
-                    mbedtls_ssl_send_alert_message(&mSsl, MBEDTLS_SSL_ALERT_LEVEL_FATAL,
-                                                   MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE);
-                    ExitNow(shouldDisconnect = true);
-                }
-
-                break;
-            }
-
-            mbedtls_ssl_session_reset(&mSsl);
-            if (mCipherSuites[0] == MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8)
-            {
-                mbedtls_ssl_set_hs_ecjpake_password(&mSsl, mPsk, mPskLength);
-            }
-            break;
-        }
-    }
-
-exit:
-
-    if (shouldDisconnect)
-    {
-        Disconnect();
-    }
-}
-
-void Dtls::HandleMbedtlsDebug(void *aContext, int aLevel, const char *aFile, int aLine, const char *aStr)
-{
-    static_cast<Dtls *>(aContext)->HandleMbedtlsDebug(aLevel, aFile, aLine, aStr);
-}
-
-void Dtls::HandleMbedtlsDebug(int aLevel, const char *aFile, int aLine, const char *aStr)
-{
-    OT_UNUSED_VARIABLE(aStr);
-    OT_UNUSED_VARIABLE(aFile);
-    OT_UNUSED_VARIABLE(aLine);
-
-    switch (aLevel)
-    {
-    case 1:
-        LogCrit("[%u] %s", mSocket.GetSockName().mPort, aStr);
-        break;
-
-    case 2:
-        LogWarn("[%u] %s", mSocket.GetSockName().mPort, aStr);
-        break;
-
-    case 3:
-        LogInfo("[%u] %s", mSocket.GetSockName().mPort, aStr);
-        break;
-
-    case 4:
-    default:
-        LogDebg("[%u] %s", mSocket.GetSockName().mPort, aStr);
-        break;
-    }
-}
-
-Error Dtls::HandleDtlsSend(const uint8_t *aBuf, uint16_t aLength, Message::SubType aMessageSubType)
-{
-    Error        error   = kErrorNone;
-    ot::Message *message = nullptr;
-
-    VerifyOrExit((message = mSocket.NewMessage()) != nullptr, error = kErrorNoBufs);
-    message->SetSubType(aMessageSubType);
-    message->SetLinkSecurityEnabled(mLayerTwoSecurity);
-
-    SuccessOrExit(error = message->AppendBytes(aBuf, aLength));
-
-    // Set message sub type in case Joiner Finalize Response is appended to the message.
-    if (aMessageSubType != Message::kSubTypeNone)
-    {
-        message->SetSubType(aMessageSubType);
-    }
-
-    if (mTransportCallback.IsSet())
-    {
-        SuccessOrExit(error = mTransportCallback.Invoke(*message, mMessageInfo));
-    }
-    else
-    {
-        SuccessOrExit(error = mSocket.SendTo(*message, mMessageInfo));
-    }
-
-exit:
-    FreeMessageOnError(message, error);
-    return error;
-}
-
-} // namespace MeshCoP
-} // namespace ot
-
-#endif // OPENTHREAD_CONFIG_DTLS_ENABLE
diff --git a/src/core/meshcop/dtls.hpp b/src/core/meshcop/dtls.hpp
deleted file mode 100644
index 2a4df72..0000000
--- a/src/core/meshcop/dtls.hpp
+++ /dev/null
@@ -1,515 +0,0 @@
-/*
- *  Copyright (c) 2016, The OpenThread Authors.
- *  All rights reserved.
- *
- *  Redistribution and use in source and binary forms, with or without
- *  modification, are permitted provided that the following conditions are met:
- *  1. Redistributions of source code must retain the above copyright
- *     notice, this list of conditions and the following disclaimer.
- *  2. Redistributions in binary form must reproduce the above copyright
- *     notice, this list of conditions and the following disclaimer in the
- *     documentation and/or other materials provided with the distribution.
- *  3. Neither the name of the copyright holder nor the
- *     names of its contributors may be used to endorse or promote products
- *     derived from this software without specific prior written permission.
- *
- *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- *  POSSIBILITY OF SUCH DAMAGE.
- */
-
-/**
- * @file
- *   This file includes definitions for using mbedTLS.
- */
-
-#ifndef DTLS_HPP_
-#define DTLS_HPP_
-
-#include "openthread-core-config.h"
-
-#include <mbedtls/net_sockets.h>
-#include <mbedtls/ssl.h>
-#include <mbedtls/ssl_cookie.h>
-#include <mbedtls/version.h>
-
-#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
-#if defined(MBEDTLS_BASE64_C) && defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
-#include <mbedtls/base64.h>
-#endif
-#ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
-#include <mbedtls/x509.h>
-#include <mbedtls/x509_crl.h>
-#include <mbedtls/x509_crt.h>
-#include <mbedtls/x509_csr.h>
-#endif
-#endif
-
-#include "common/callback.hpp"
-#include "common/locator.hpp"
-#include "common/message.hpp"
-#include "common/random.hpp"
-#include "common/timer.hpp"
-#include "crypto/sha256.hpp"
-#include "meshcop/meshcop_tlvs.hpp"
-#include "net/socket.hpp"
-#include "net/udp6.hpp"
-
-namespace ot {
-
-namespace MeshCoP {
-
-class Dtls : public InstanceLocator
-{
-public:
-    static constexpr uint8_t kPskMaxLength = 32; ///< Maximum PSK length.
-
-    /**
-     * Initializes the DTLS object.
-     *
-     * @param[in]  aInstance            A reference to the OpenThread instance.
-     * @param[in]  aLayerTwoSecurity    Specifies whether to use layer two security or not.
-     *
-     */
-    explicit Dtls(Instance &aInstance, bool aLayerTwoSecurity);
-
-    /**
-     * Pointer is called when a connection is established or torn down.
-     *
-     * @param[in]  aContext    A pointer to application-specific context.
-     * @param[in]  aConnected  TRUE if a connection was established, FALSE otherwise.
-     *
-     */
-    typedef void (*ConnectedHandler)(void *aContext, bool aConnected);
-
-    /**
-     * Pointer is called when data is received from the DTLS session.
-     *
-     * @param[in]  aContext  A pointer to application-specific context.
-     * @param[in]  aBuf      A pointer to the received data buffer.
-     * @param[in]  aLength   Number of bytes in the received data buffer.
-     *
-     */
-    typedef void (*ReceiveHandler)(void *aContext, uint8_t *aBuf, uint16_t aLength);
-
-    /**
-     * Pointer is called when secure CoAP server want to send encrypted message.
-     *
-     * @param[in]  aContext      A pointer to arbitrary context information.
-     * @param[in]  aMessage      A reference to the message to send.
-     * @param[in]  aMessageInfo  A reference to the message info associated with @p aMessage.
-     *
-     */
-    typedef Error (*TransportCallback)(void *aContext, ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
-
-    /**
-     * Opens the DTLS socket.
-     *
-     * @param[in]  aReceiveHandler      A pointer to a function that is called to receive DTLS payload.
-     * @param[in]  aConnectedHandler    A pointer to a function that is called when connected or disconnected.
-     * @param[in]  aContext             A pointer to arbitrary context information.
-     *
-     * @retval kErrorNone     Successfully opened the socket.
-     * @retval kErrorAlready  The DTLS is already open.
-     *
-     */
-    Error Open(ReceiveHandler aReceiveHandler, ConnectedHandler aConnectedHandler, void *aContext);
-
-    /**
-     * Binds this DTLS to a UDP port.
-     *
-     * @param[in]  aPort              The port to bind.
-     *
-     * @retval kErrorNone           Successfully bound the DTLS socket.
-     * @retval kErrorInvalidState   The DTLS socket is not open.
-     * @retval kErrorAlready        Already bound.
-     *
-     */
-    Error Bind(uint16_t aPort);
-
-    /**
-     * Gets the UDP port of this session.
-     *
-     * @returns  UDP port number.
-     *
-     */
-    uint16_t GetUdpPort(void) const;
-
-    /**
-     * Binds this DTLS with a transport callback.
-     *
-     * @param[in]  aCallback  A pointer to a function for sending messages.
-     * @param[in]  aContext   A pointer to arbitrary context information.
-     *
-     * @retval kErrorNone           Successfully bound the DTLS socket.
-     * @retval kErrorInvalidState   The DTLS socket is not open.
-     * @retval kErrorAlready        Already bound.
-     *
-     */
-    Error Bind(TransportCallback aCallback, void *aContext);
-
-    /**
-     * Establishes a DTLS session.
-     *
-     * For CoAP Secure API do first:
-     * Set X509 Pk and Cert for use DTLS mode ECDHE ECDSA with AES 128 CCM 8 or
-     * set PreShared Key for use DTLS mode PSK with AES 128 CCM 8.
-     *
-     * @param[in]  aSockAddr               A reference to the remote sockaddr.
-     *
-     * @retval kErrorNone          Successfully started DTLS handshake.
-     * @retval kErrorInvalidState  The DTLS socket is not open.
-     *
-     */
-    Error Connect(const Ip6::SockAddr &aSockAddr);
-
-    /**
-     * Indicates whether or not the DTLS session is active.
-     *
-     * @retval TRUE  If DTLS session is active.
-     * @retval FALSE If DTLS session is not active.
-     *
-     */
-    bool IsConnectionActive(void) const { return mState >= kStateConnecting; }
-
-    /**
-     * Indicates whether or not the DTLS session is connected.
-     *
-     * @retval TRUE   The DTLS session is connected.
-     * @retval FALSE  The DTLS session is not connected.
-     *
-     */
-    bool IsConnected(void) const { return mState == kStateConnected; }
-
-    /**
-     * Disconnects the DTLS session.
-     *
-     */
-    void Disconnect(void);
-
-    /**
-     * Closes the DTLS socket.
-     *
-     */
-    void Close(void);
-
-    /**
-     * Sets the PSK.
-     *
-     * @param[in]  aPsk  A pointer to the PSK.
-     *
-     * @retval kErrorNone          Successfully set the PSK.
-     * @retval kErrorInvalidArgs   The PSK is invalid.
-     *
-     */
-    Error SetPsk(const uint8_t *aPsk, uint8_t aPskLength);
-
-#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
-#ifdef MBEDTLS_KEY_EXCHANGE_PSK_ENABLED
-    /**
-     * Sets the Pre-Shared Key (PSK) for DTLS sessions-
-     * identified by a PSK.
-     *
-     * DTLS mode "PSK with AES 128 CCM 8" for Application CoAPS.
-     *
-     * @param[in]  aPsk          A pointer to the PSK.
-     * @param[in]  aPskLength    The PSK char length.
-     * @param[in]  aPskIdentity  The Identity Name for the PSK.
-     * @param[in]  aPskIdLength  The PSK Identity Length.
-     *
-     * @retval kErrorNone  Successfully set the PSK.
-     *
-     */
-    void SetPreSharedKey(const uint8_t *aPsk, uint16_t aPskLength, const uint8_t *aPskIdentity, uint16_t aPskIdLength);
-
-#endif
-
-#ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
-    /**
-     * Sets a reference to the own x509 certificate with corresponding private key.
-     *
-     * DTLS mode "ECDHE ECDSA with AES 128 CCM 8" for Application CoAPS.
-     *
-     * @param[in]  aX509Certificate   A pointer to the PEM formatted X509 certificate.
-     * @param[in]  aX509CertLength    The length of certificate.
-     * @param[in]  aPrivateKey        A pointer to the PEM formatted private key.
-     * @param[in]  aPrivateKeyLength  The length of the private key.
-     *
-     */
-    void SetCertificate(const uint8_t *aX509Certificate,
-                        uint32_t       aX509CertLength,
-                        const uint8_t *aPrivateKey,
-                        uint32_t       aPrivateKeyLength);
-
-    /**
-     * Sets the trusted top level CAs. It is needed for validate the
-     * certificate of the peer.
-     *
-     * DTLS mode "ECDHE ECDSA with AES 128 CCM 8" for Application CoAPS.
-     *
-     * @param[in]  aX509CaCertificateChain  A pointer to the PEM formatted X509 CA chain.
-     * @param[in]  aX509CaCertChainLength   The length of chain.
-     *
-     */
-    void SetCaCertificateChain(const uint8_t *aX509CaCertificateChain, uint32_t aX509CaCertChainLength);
-#endif // MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
-
-#if defined(MBEDTLS_BASE64_C) && defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
-    /**
-     * Returns the peer x509 certificate base64 encoded.
-     *
-     * DTLS mode "ECDHE ECDSA with AES 128 CCM 8" for Application CoAPS.
-     *
-     * @param[out]  aPeerCert        A pointer to the base64 encoded certificate buffer.
-     * @param[out]  aCertLength      The length of the base64 encoded peer certificate.
-     * @param[in]   aCertBufferSize  The buffer size of aPeerCert.
-     *
-     * @retval kErrorInvalidState   Not connected yet.
-     * @retval kErrorNone           Successfully get the peer certificate.
-     * @retval kErrorNoBufs         Can't allocate memory for certificate.
-     *
-     */
-    Error GetPeerCertificateBase64(unsigned char *aPeerCert, size_t *aCertLength, size_t aCertBufferSize);
-#endif // defined(MBEDTLS_BASE64_C) && defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
-
-    /**
-     * Set the authentication mode for a dtls connection.
-     *
-     * Disable or enable the verification of peer certificate.
-     * Must called before start.
-     *
-     * @param[in]  aVerifyPeerCertificate  true, if the peer certificate should verify.
-     *
-     */
-    void SetSslAuthMode(bool aVerifyPeerCertificate) { mVerifyPeerCertificate = aVerifyPeerCertificate; }
-#endif // OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
-
-#ifdef MBEDTLS_SSL_SRV_C
-    /**
-     * Sets the Client ID used for generating the Hello Cookie.
-     *
-     * @param[in]  aClientId  A pointer to the Client ID.
-     * @param[in]  aLength    Number of bytes in the Client ID.
-     *
-     * @retval kErrorNone  Successfully set the Client ID.
-     *
-     */
-    Error SetClientId(const uint8_t *aClientId, uint8_t aLength);
-#endif
-
-    /**
-     * Sends data within the DTLS session.
-     *
-     * @param[in]  aMessage  A message to send via DTLS.
-     * @param[in]  aLength   Number of bytes in the data buffer.
-     *
-     * @retval kErrorNone     Successfully sent the data via the DTLS session.
-     * @retval kErrorNoBufs   A message is too long.
-     *
-     */
-    Error Send(Message &aMessage, uint16_t aLength);
-
-    /**
-     * Provides a received DTLS message to the DTLS object.
-     *
-     * @param[in]  aMessage  A reference to the message.
-     *
-     */
-    void Receive(Message &aMessage);
-
-    /**
-     * Sets the default message sub-type that will be used for all messages without defined
-     * sub-type.
-     *
-     * @param[in]  aMessageSubType  The default message sub-type.
-     *
-     */
-    void SetDefaultMessageSubType(Message::SubType aMessageSubType) { mMessageDefaultSubType = aMessageSubType; }
-
-    /**
-     * Returns the DTLS session's peer address.
-     *
-     * @return DTLS session's message info.
-     *
-     */
-    const Ip6::MessageInfo &GetMessageInfo(void) const { return mMessageInfo; }
-
-    void HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
-
-private:
-    enum State : uint8_t
-    {
-        kStateClosed,       // UDP socket is closed.
-        kStateOpen,         // UDP socket is open.
-        kStateInitializing, // The DTLS service is initializing.
-        kStateConnecting,   // The DTLS service is establishing a connection.
-        kStateConnected,    // The DTLS service has a connection established.
-        kStateCloseNotify,  // The DTLS service is closing a connection.
-    };
-
-    static constexpr uint32_t kGuardTimeNewConnectionMilli = 2000;
-
-#if !OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
-    static constexpr uint16_t kApplicationDataMaxLength = 1152;
-#else
-    static constexpr uint16_t         kApplicationDataMaxLength = OPENTHREAD_CONFIG_DTLS_APPLICATION_DATA_MAX_LENGTH;
-#endif
-
-    static constexpr size_t kDtlsKeyBlockSize     = 40;
-    static constexpr size_t kDtlsRandomBufferSize = 32;
-
-    void  FreeMbedtls(void);
-    Error Setup(bool aClient);
-
-#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
-    /**
-     * Set keys and/or certificates for dtls session dependent of used cipher suite.
-     *
-     * @retval mbedtls error, 0 if successfully.
-     *
-     */
-    int SetApplicationCoapSecureKeys(void);
-#endif
-
-    static void HandleMbedtlsDebug(void *aContext, int aLevel, const char *aFile, int aLine, const char *aStr);
-    void        HandleMbedtlsDebug(int aLevel, const char *aFile, int aLine, const char *aStr);
-
-    static int HandleMbedtlsGetTimer(void *aContext);
-    int        HandleMbedtlsGetTimer(void);
-
-    static void HandleMbedtlsSetTimer(void *aContext, uint32_t aIntermediate, uint32_t aFinish);
-    void        HandleMbedtlsSetTimer(uint32_t aIntermediate, uint32_t aFinish);
-
-    static int HandleMbedtlsReceive(void *aContext, unsigned char *aBuf, size_t aLength);
-    int        HandleMbedtlsReceive(unsigned char *aBuf, size_t aLength);
-
-    static int HandleMbedtlsTransmit(void *aContext, const unsigned char *aBuf, size_t aLength);
-    int        HandleMbedtlsTransmit(const unsigned char *aBuf, size_t aLength);
-
-#ifdef MBEDTLS_SSL_EXPORT_KEYS
-#if (MBEDTLS_VERSION_NUMBER >= 0x03000000)
-
-    static void HandleMbedtlsExportKeys(void                       *aContext,
-                                        mbedtls_ssl_key_export_type aType,
-                                        const unsigned char        *aMasterSecret,
-                                        size_t                      aMasterSecretLen,
-                                        const unsigned char         aClientRandom[32],
-                                        const unsigned char         aServerRandom[32],
-                                        mbedtls_tls_prf_types       aTlsPrfType);
-
-    void HandleMbedtlsExportKeys(mbedtls_ssl_key_export_type aType,
-                                 const unsigned char        *aMasterSecret,
-                                 size_t                      aMasterSecretLen,
-                                 const unsigned char         aClientRandom[32],
-                                 const unsigned char         aServerRandom[32],
-                                 mbedtls_tls_prf_types       aTlsPrfType);
-
-#else
-
-    static int       HandleMbedtlsExportKeys(void                *aContext,
-                                             const unsigned char *aMasterSecret,
-                                             const unsigned char *aKeyBlock,
-                                             size_t               aMacLength,
-                                             size_t               aKeyLength,
-                                             size_t               aIvLength);
-    int              HandleMbedtlsExportKeys(const unsigned char *aMasterSecret,
-                                             const unsigned char *aKeyBlock,
-                                             size_t               aMacLength,
-                                             size_t               aKeyLength,
-                                             size_t               aIvLength);
-
-#endif // (MBEDTLS_VERSION_NUMBER >= 0x03000000)
-#endif // MBEDTLS_SSL_EXPORT_KEYS
-
-    static void HandleTimer(Timer &aTimer);
-    void        HandleTimer(void);
-
-    static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo);
-
-    void  HandleDtlsReceive(const uint8_t *aBuf, uint16_t aLength);
-    Error HandleDtlsSend(const uint8_t *aBuf, uint16_t aLength, Message::SubType aMessageSubType);
-
-    void Process(void);
-
-    State mState;
-
-    int     mCipherSuites[2];
-    uint8_t mPsk[kPskMaxLength];
-    uint8_t mPskLength;
-
-#if (MBEDTLS_VERSION_NUMBER >= 0x03010000)
-    static const uint16_t sGroups[];
-#else
-    static const mbedtls_ecp_group_id sCurves[];
-#endif
-
-#if defined(MBEDTLS_KEY_EXCHANGE__WITH_CERT__ENABLED) || defined(MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED)
-#if (MBEDTLS_VERSION_NUMBER >= 0x03020000)
-    static const uint16_t sSignatures[];
-#else
-    static const int sHashes[];
-#endif
-#endif
-
-#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
-#ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
-    const uint8_t     *mCaChainSrc;
-    uint32_t           mCaChainLength;
-    const uint8_t     *mOwnCertSrc;
-    uint32_t           mOwnCertLength;
-    const uint8_t     *mPrivateKeySrc;
-    uint32_t           mPrivateKeyLength;
-    mbedtls_x509_crt   mCaChain;
-    mbedtls_x509_crt   mOwnCert;
-    mbedtls_pk_context mPrivateKey;
-#endif
-#ifdef MBEDTLS_KEY_EXCHANGE_PSK_ENABLED
-    const uint8_t *mPreSharedKey;
-    const uint8_t *mPreSharedKeyIdentity;
-    uint16_t       mPreSharedKeyLength;
-    uint16_t       mPreSharedKeyIdLength;
-#endif
-#endif
-
-    bool mVerifyPeerCertificate;
-
-    mbedtls_ssl_context mSsl;
-    mbedtls_ssl_config  mConf;
-
-#ifdef MBEDTLS_SSL_COOKIE_C
-    mbedtls_ssl_cookie_ctx mCookieCtx;
-#endif
-
-    TimerMilliContext mTimer;
-
-    TimeMilli mTimerIntermediate;
-    bool      mTimerSet : 1;
-
-    bool mLayerTwoSecurity : 1;
-
-    Message *mReceiveMessage;
-
-    Callback<ConnectedHandler> mConnectedCallback;
-    Callback<ReceiveHandler>   mReceiveCallback;
-
-    Ip6::MessageInfo mMessageInfo;
-    Ip6::Udp::Socket mSocket;
-
-    Callback<TransportCallback> mTransportCallback;
-
-    Message::SubType mMessageSubType;
-    Message::SubType mMessageDefaultSubType;
-};
-
-} // namespace MeshCoP
-} // namespace ot
-
-#endif // DTLS_HPP_
diff --git a/src/core/meshcop/energy_scan_client.cpp b/src/core/meshcop/energy_scan_client.cpp
index beea65c..41f0838 100644
--- a/src/core/meshcop/energy_scan_client.cpp
+++ b/src/core/meshcop/energy_scan_client.cpp
@@ -66,10 +66,9 @@
                                   otCommissionerEnergyReportCallback aCallback,
                                   void                              *aContext)
 {
-    Error                   error = kErrorNone;
-    MeshCoP::ChannelMaskTlv channelMask;
-    Tmf::MessageInfo        messageInfo(GetInstance());
-    Coap::Message          *message = nullptr;
+    Error            error = kErrorNone;
+    Tmf::MessageInfo messageInfo(GetInstance());
+    Coap::Message   *message = nullptr;
 
     VerifyOrExit(Get<MeshCoP::Commissioner>().IsActive(), error = kErrorInvalidState);
     VerifyOrExit((message = Get<Tmf::Agent>().NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
@@ -80,9 +79,7 @@
     SuccessOrExit(
         error = Tlv::Append<MeshCoP::CommissionerSessionIdTlv>(*message, Get<MeshCoP::Commissioner>().GetSessionId()));
 
-    channelMask.Init();
-    channelMask.SetChannelMask(aChannelMask);
-    SuccessOrExit(error = channelMask.AppendTo(*message));
+    SuccessOrExit(error = MeshCoP::ChannelMaskTlv::AppendTo(*message, aChannelMask));
 
     SuccessOrExit(error = Tlv::Append<MeshCoP::CountTlv>(*message, aCount));
     SuccessOrExit(error = Tlv::Append<MeshCoP::PeriodTlv>(*message, aPeriod));
@@ -110,7 +107,7 @@
 
     LogInfo("Received %s", UriToString<kUriEnergyReport>());
 
-    VerifyOrExit((mask = MeshCoP::ChannelMaskTlv::GetChannelMask(aMessage)) != 0);
+    SuccessOrExit(MeshCoP::ChannelMaskTlv::FindIn(aMessage, mask));
 
     SuccessOrExit(MeshCoP::Tlv::FindTlv(aMessage, MeshCoP::Tlv::kEnergyList, sizeof(energyListTlv), energyListTlv));
 
diff --git a/src/core/meshcop/joiner.cpp b/src/core/meshcop/joiner.cpp
index 50b02e9..4df5f96 100644
--- a/src/core/meshcop/joiner.cpp
+++ b/src/core/meshcop/joiner.cpp
@@ -68,7 +68,7 @@
 {
     SetIdFromIeeeEui64();
     mDiscerner.Clear();
-    memset(mJoinerRouters, 0, sizeof(mJoinerRouters));
+    ClearAllBytes(mJoinerRouters);
 }
 
 void Joiner::SetIdFromIeeeEui64(void)
@@ -185,7 +185,7 @@
         FreeJoinerFinalizeMessage();
     }
 
-    LogError("start joiner", error);
+    LogWarnOnError(error, "start joiner");
     return error;
 }
 
@@ -378,7 +378,7 @@
     SetState(kStateConnect);
 
 exit:
-    LogError("start secure joiner connection", error);
+    LogWarnOnError(error, "start secure joiner connection");
     return error;
 }
 
@@ -543,7 +543,7 @@
     mTimer.Start(kConfigExtAddressDelay);
 
 exit:
-    LogError("process joiner entrust", error);
+    LogWarnOnError(error, "process joiner entrust");
 }
 
 void Joiner::SendJoinerEntrustResponse(const Coap::Message &aRequest, const Ip6::MessageInfo &aRequestInfo)
diff --git a/src/core/meshcop/joiner.hpp b/src/core/meshcop/joiner.hpp
index 1b42c6a..ce430a7 100644
--- a/src/core/meshcop/joiner.hpp
+++ b/src/core/meshcop/joiner.hpp
@@ -49,9 +49,9 @@
 #include "common/message.hpp"
 #include "common/non_copyable.hpp"
 #include "mac/mac_types.hpp"
-#include "meshcop/dtls.hpp"
 #include "meshcop/meshcop.hpp"
 #include "meshcop/meshcop_tlvs.hpp"
+#include "meshcop/secure_transport.hpp"
 #include "thread/discover_scanner.hpp"
 #include "thread/tmf.hpp"
 
diff --git a/src/core/meshcop/joiner_router.cpp b/src/core/meshcop/joiner_router.cpp
index acadf1a..a5df413 100644
--- a/src/core/meshcop/joiner_router.cpp
+++ b/src/core/meshcop/joiner_router.cpp
@@ -232,7 +232,7 @@
 
 exit:
     FreeMessageOnError(message, error);
-    LogError("schedule joiner entrust", error);
+    LogWarnOnError(error, "schedule joiner entrust");
 }
 
 void JoinerRouter::HandleTimer(void) { SendDelayedJoinerEntrust(); }
diff --git a/src/core/meshcop/meshcop.cpp b/src/core/meshcop/meshcop.cpp
index 35ad539..8bce745 100644
--- a/src/core/meshcop/meshcop.cpp
+++ b/src/core/meshcop/meshcop.cpp
@@ -34,6 +34,7 @@
 
 #include "meshcop.hpp"
 
+#include "common/clearable.hpp"
 #include "common/crc16.hpp"
 #include "common/debug.hpp"
 #include "common/locator_getters.hpp"
@@ -191,7 +192,7 @@
 {
     OT_ASSERT(aLength <= kMaxLength);
     mLength = aLength;
-    memset(m8, 0, sizeof(m8));
+    ClearAllBytes(m8);
 }
 
 void SteeringData::SetToPermitAllJoiners(void)
@@ -324,7 +325,7 @@
                      (networkNameLen <= OT_NETWORK_NAME_MAX_SIZE),
                  error = kErrorInvalidArgs);
 
-    memset(salt, 0, sizeof(salt));
+    ClearAllBytes(salt);
     memcpy(salt, saltPrefix, sizeof(saltPrefix) - 1);
     saltLen += static_cast<uint16_t>(sizeof(saltPrefix) - 1);
 
@@ -334,23 +335,13 @@
     memcpy(salt + saltLen, aNetworkName.GetAsCString(), networkNameLen);
     saltLen += networkNameLen;
 
-    otPlatCryptoPbkdf2GenerateKey(reinterpret_cast<const uint8_t *>(aPassPhrase), passphraseLen, salt, saltLen, 16384,
-                                  OT_PSKC_MAX_SIZE, aPskc.m8);
+    error = otPlatCryptoPbkdf2GenerateKey(reinterpret_cast<const uint8_t *>(aPassPhrase), passphraseLen, salt, saltLen,
+                                          16384, OT_PSKC_MAX_SIZE, aPskc.m8);
 
 exit:
     return error;
 }
 #endif // OPENTHREAD_FTD
 
-#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN)
-void LogError(const char *aActionText, Error aError)
-{
-    if (aError != kErrorNone && aError != kErrorAlready)
-    {
-        LogWarn("Failed to %s: %s", aActionText, ErrorToString(aError));
-    }
-}
-#endif
-
 } // namespace MeshCoP
 } // namespace ot
diff --git a/src/core/meshcop/meshcop.hpp b/src/core/meshcop/meshcop.hpp
index 339872b..3620927 100644
--- a/src/core/meshcop/meshcop.hpp
+++ b/src/core/meshcop/meshcop.hpp
@@ -561,22 +561,6 @@
  */
 void ComputeJoinerId(const Mac::ExtAddress &aEui64, Mac::ExtAddress &aJoinerId);
 
-#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN)
-/**
- * Emits a log message indicating an error during a MeshCoP action.
- *
- * Note that log message is emitted only if there is an error, i.e. @p aError is not `kErrorNone`. The log
- * message will have the format "Failed to {aActionText} : {ErrorString}".
- *
- * @param[in] aActionText   A string representing the failed action.
- * @param[in] aError        The error in sending the message.
- *
- */
-void LogError(const char *aActionText, Error aError);
-#else
-inline void LogError(const char *, Error) {}
-#endif
-
 } // namespace MeshCoP
 
 DefineCoreType(otJoinerPskd, MeshCoP::JoinerPskd);
diff --git a/src/core/meshcop/meshcop_leader.cpp b/src/core/meshcop/meshcop_leader.cpp
index 64c028d..4fc8385 100644
--- a/src/core/meshcop/meshcop_leader.cpp
+++ b/src/core/meshcop/meshcop_leader.cpp
@@ -125,7 +125,7 @@
 
 exit:
     FreeMessageOnError(message, error);
-    LogError("send petition response", error);
+    LogWarnOnError(error, "send petition response");
 }
 
 template <> void Leader::HandleTmf<kUriLeaderKeepAlive>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
@@ -192,7 +192,7 @@
 
 exit:
     FreeMessageOnError(message, error);
-    LogError("send keep alive response", error);
+    LogWarnOnError(error, "send keep alive response");
 }
 
 void Leader::SendDatasetChanged(const Ip6::Address &aAddress)
@@ -211,7 +211,7 @@
 
 exit:
     FreeMessageOnError(message, error);
-    LogError("send dataset changed", error);
+    LogWarnOnError(error, "send dataset changed");
 }
 
 Error Leader::SetDelayTimerMinimal(uint32_t aDelayTimerMinimal)
diff --git a/src/core/meshcop/meshcop_tlvs.cpp b/src/core/meshcop/meshcop_tlvs.cpp
index 446846f..151abcd 100644
--- a/src/core/meshcop/meshcop_tlvs.cpp
+++ b/src/core/meshcop/meshcop_tlvs.cpp
@@ -65,9 +65,9 @@
     case Tlv::kMeshLocalPrefix:
         minLength = sizeof(MeshLocalPrefixTlv::ValueType);
         break;
-
     case Tlv::kChannel:
-        isValid = As<ChannelTlv>(aTlv).IsValid();
+        VerifyOrExit(aTlv.GetLength() >= sizeof(ChannelTlvValue), isValid = false);
+        isValid = aTlv.ReadValueAs<ChannelTlv>().IsValid();
         break;
     case Tlv::kNetworkName:
         isValid = As<NetworkNameTlv>(aTlv).IsValid();
@@ -90,6 +90,7 @@
         isValid = (aTlv.GetLength() >= minLength);
     }
 
+exit:
     return isValid;
 }
 
@@ -143,51 +144,6 @@
     aSecurityPolicy.GetFlags(mFlags, sizeof(mFlags));
 }
 
-bool ChannelTlv::IsValid(void) const
-{
-    bool ret = false;
-
-    VerifyOrExit(GetLength() == sizeof(*this) - sizeof(Tlv));
-    VerifyOrExit(mChannelPage < BitSizeOf(uint32_t));
-    VerifyOrExit((1U << mChannelPage) & Radio::kSupportedChannelPages);
-    VerifyOrExit(Radio::kChannelMin <= GetChannel() && GetChannel() <= Radio::kChannelMax);
-    ret = true;
-
-exit:
-    return ret;
-}
-
-void ChannelTlv::SetChannel(uint16_t aChannel)
-{
-    uint8_t channelPage = OT_RADIO_CHANNEL_PAGE_0;
-
-#if OPENTHREAD_CONFIG_RADIO_2P4GHZ_OQPSK_SUPPORT
-    if ((OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MIN <= aChannel) && (aChannel <= OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MAX))
-    {
-        channelPage = OT_RADIO_CHANNEL_PAGE_0;
-    }
-#endif
-
-#if OPENTHREAD_CONFIG_RADIO_915MHZ_OQPSK_SUPPORT
-    if ((OT_RADIO_915MHZ_OQPSK_CHANNEL_MIN <= aChannel) && (aChannel <= OT_RADIO_915MHZ_OQPSK_CHANNEL_MAX))
-    {
-        channelPage = OT_RADIO_CHANNEL_PAGE_2;
-    }
-#endif
-
-#if OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_SUPPORT
-    if ((OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_MIN == aChannel) ||
-        ((OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_MIN < aChannel) &&
-         (aChannel <= OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_MAX)))
-    {
-        channelPage = OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_PAGE;
-    }
-#endif
-
-    SetChannelPage(channelPage);
-    mChannel = BigEndian::HostSwap16(aChannel);
-}
-
 const char *StateTlv::StateToString(State aState)
 {
     static const char *const kStateStrings[] = {
@@ -202,191 +158,140 @@
     return aState == kReject ? kStateStrings[2] : kStateStrings[aState];
 }
 
-bool ChannelMaskBaseTlv::IsValid(void) const
+bool ChannelMaskTlv::IsValid(void) const
 {
-    const ChannelMaskEntryBase *cur = GetFirstEntry();
-    const ChannelMaskEntryBase *end = reinterpret_cast<const ChannelMaskEntryBase *>(GetNext());
-    bool                        ret = false;
+    uint32_t channelMask;
 
-    VerifyOrExit(cur != nullptr);
-
-    while (cur < end)
-    {
-        uint8_t channelPage;
-
-        VerifyOrExit((cur + 1) <= end && cur->GetNext() <= end);
-
-        channelPage = cur->GetChannelPage();
-
-#if OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_SUPPORT
-        if (channelPage == OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_PAGE)
-#else
-        if ((channelPage == OT_RADIO_CHANNEL_PAGE_0) || (channelPage == OT_RADIO_CHANNEL_PAGE_2))
-#endif
-        {
-            VerifyOrExit(static_cast<const ChannelMaskEntry *>(cur)->IsValid());
-        }
-
-        cur = cur->GetNext();
-    }
-
-    ret = true;
-
-exit:
-    return ret;
+    return (ReadChannelMask(channelMask) == kErrorNone);
 }
 
-const ChannelMaskEntryBase *ChannelMaskBaseTlv::GetFirstEntry(void) const
+Error ChannelMaskTlv::ReadChannelMask(uint32_t &aChannelMask) const
 {
-    const ChannelMaskEntryBase *entry = nullptr;
+    EntriesData entriesData;
 
-    VerifyOrExit(GetLength() >= sizeof(ChannelMaskEntryBase));
+    entriesData.Clear();
+    entriesData.mData   = &mEntriesStart;
+    entriesData.mLength = GetLength();
 
-    entry = reinterpret_cast<const ChannelMaskEntryBase *>(GetValue());
-    VerifyOrExit(GetLength() >= entry->GetEntrySize(), entry = nullptr);
-
-exit:
-    return entry;
+    return entriesData.Parse(aChannelMask);
 }
 
-ChannelMaskEntryBase *ChannelMaskBaseTlv::GetFirstEntry(void) { return AsNonConst(AsConst(this)->GetFirstEntry()); }
-
-void ChannelMaskTlv::SetChannelMask(uint32_t aChannelMask)
+Error ChannelMaskTlv::FindIn(const Message &aMessage, uint32_t &aChannelMask)
 {
-    uint8_t           length = 0;
-    ChannelMaskEntry *entry;
+    Error       error;
+    EntriesData entriesData;
 
-    entry = static_cast<ChannelMaskEntry *>(GetFirstEntry());
+    entriesData.Clear();
+    entriesData.mMessage = &aMessage;
 
-#if OPENTHREAD_CONFIG_RADIO_915MHZ_OQPSK_SUPPORT
-    if (aChannelMask & OT_RADIO_915MHZ_OQPSK_CHANNEL_MASK)
-    {
-        OT_ASSERT(entry != nullptr);
-        entry->Init();
-        entry->SetChannelPage(OT_RADIO_CHANNEL_PAGE_2);
-        entry->SetMask(aChannelMask & OT_RADIO_915MHZ_OQPSK_CHANNEL_MASK);
-
-        length += sizeof(ChannelMaskEntry);
-
-        entry = static_cast<ChannelMaskEntry *>(entry->GetNext());
-    }
-#endif
-
-#if OPENTHREAD_CONFIG_RADIO_2P4GHZ_OQPSK_SUPPORT
-    if (aChannelMask & OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MASK)
-    {
-        OT_ASSERT(entry != nullptr);
-        entry->Init();
-        entry->SetChannelPage(OT_RADIO_CHANNEL_PAGE_0);
-        entry->SetMask(aChannelMask & OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MASK);
-
-        length += sizeof(ChannelMaskEntry);
-    }
-#endif
-
-#if OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_SUPPORT
-    if (aChannelMask & OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_MASK)
-    {
-        OT_ASSERT(entry != nullptr);
-        entry->Init();
-        entry->SetChannelPage(OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_PAGE);
-        entry->SetMask(aChannelMask & OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_MASK);
-
-        length += sizeof(ChannelMaskEntry);
-    }
-#endif
-
-    SetLength(length);
-}
-
-uint32_t ChannelMaskTlv::GetChannelMask(void) const
-{
-    const ChannelMaskEntryBase *cur  = GetFirstEntry();
-    const ChannelMaskEntryBase *end  = reinterpret_cast<const ChannelMaskEntryBase *>(GetNext());
-    uint32_t                    mask = 0;
-
-    VerifyOrExit(cur != nullptr);
-
-    while (cur < end)
-    {
-        uint8_t channelPage;
-
-        VerifyOrExit((cur + 1) <= end && cur->GetNext() <= end);
-
-        channelPage = cur->GetChannelPage();
-
-#if OPENTHREAD_CONFIG_RADIO_915MHZ_OQPSK_SUPPORT
-        if (channelPage == OT_RADIO_CHANNEL_PAGE_2)
-        {
-            mask |= static_cast<const ChannelMaskEntry *>(cur)->GetMask() & OT_RADIO_915MHZ_OQPSK_CHANNEL_MASK;
-        }
-#endif
-
-#if OPENTHREAD_CONFIG_RADIO_2P4GHZ_OQPSK_SUPPORT
-        if (channelPage == OT_RADIO_CHANNEL_PAGE_0)
-        {
-            mask |= static_cast<const ChannelMaskEntry *>(cur)->GetMask() & OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MASK;
-        }
-#endif
-
-#if OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_SUPPORT
-        if (channelPage == OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_PAGE)
-        {
-            mask |= static_cast<const ChannelMaskEntry *>(cur)->GetMask() &
-                    OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_MASK;
-        }
-#endif
-
-        cur = cur->GetNext();
-    }
+    SuccessOrExit(error = FindTlvValueOffset(aMessage, Tlv::kChannelMask, entriesData.mOffset, entriesData.mLength));
+    error = entriesData.Parse(aChannelMask);
 
 exit:
-    return mask;
+    return error;
 }
 
-uint32_t ChannelMaskTlv::GetChannelMask(const Message &aMessage)
+Error ChannelMaskTlv::EntriesData::Parse(uint32_t &aChannelMask)
 {
-    uint32_t mask = 0;
-    uint16_t offset;
-    uint16_t end;
+    // Validates and parses the Channel Mask TLV entries for each
+    // channel page and if successful updates `aChannelMask` to
+    // return the combined mask for all channel pages supported by
+    // radio. The entries can be either contained in `mMessage` from
+    // `mOffset` (when `mMessage` is non-null) or be in a buffer
+    // `mData`. `mLength` gives the number of bytes for all entries.
 
-    SuccessOrExit(FindTlvValueStartEndOffsets(aMessage, kChannelMask, offset, end));
+    Error        error = kErrorParse;
+    Entry        readEntry;
+    const Entry *entry;
+    uint16_t     size;
 
-    while (offset + sizeof(ChannelMaskEntryBase) <= end)
+    aChannelMask = 0;
+
+    VerifyOrExit(mLength > 0); // At least one entry.
+
+    while (mLength > 0)
     {
-        ChannelMaskEntry entry;
+        VerifyOrExit(mLength > kEntryHeaderSize);
 
-        IgnoreError(aMessage.Read(offset, entry));
-        VerifyOrExit(offset + entry.GetEntrySize() <= end);
-
-        switch (entry.GetChannelPage())
+        if (mMessage != nullptr)
         {
-#if OPENTHREAD_CONFIG_RADIO_2P4GHZ_OQPSK_SUPPORT
-        case OT_RADIO_CHANNEL_PAGE_0:
-            IgnoreError(aMessage.Read(offset, entry));
-            mask |= entry.GetMask() & OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MASK;
-            break;
-#endif
+            // We first read the entry's header only and after
+            // validating the entry and that the entry's channel page
+            // is supported by radio, we read the full `Entry`.
 
-#if OPENTHREAD_CONFIG_RADIO_915MHZ_OQPSK_SUPPORT
-        case OT_RADIO_CHANNEL_PAGE_2:
-            IgnoreError(aMessage.Read(offset, entry));
-            mask |= entry.GetMask() & OT_RADIO_915MHZ_OQPSK_CHANNEL_MASK;
-            break;
-#endif
-
-#if OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_SUPPORT
-        case OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_PAGE:
-            IgnoreError(aMessage.Read(offset, entry));
-            mask |= entry.GetMask() & OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_MASK;
-            break;
-#endif
+            mMessage->ReadBytes(mOffset, &readEntry, kEntryHeaderSize);
+            entry = &readEntry;
         }
-        offset += entry.GetEntrySize();
+        else
+        {
+            entry = reinterpret_cast<const Entry *>(mData);
+        }
+
+        size = kEntryHeaderSize + entry->GetMaskLength();
+
+        VerifyOrExit(size <= mLength);
+
+        if (Radio::SupportsChannelPage(entry->GetChannelPage()))
+        {
+            // Currently supported channel pages all use `uint32_t`
+            // channel mask.
+
+            VerifyOrExit(entry->GetMaskLength() == kMaskLength);
+
+            if (mMessage != nullptr)
+            {
+                IgnoreError(mMessage->Read(mOffset, readEntry));
+            }
+
+            aChannelMask |= (entry->GetMask() & Radio::ChannelMaskForPage(entry->GetChannelPage()));
+        }
+
+        mLength -= size;
+
+        if (mMessage != nullptr)
+        {
+            mOffset += size;
+        }
+        else
+        {
+            mData += size;
+        }
     }
 
+    error = kErrorNone;
+
 exit:
-    return mask;
+    return error;
+}
+
+void ChannelMaskTlv::PrepareValue(Value &aValue, uint32_t aChannelMask)
+{
+    Entry *entry = reinterpret_cast<Entry *>(aValue.mData);
+
+    aValue.mLength = 0;
+
+    for (uint8_t page : Radio::kSupportedChannelPages)
+    {
+        uint32_t mask = (Radio::ChannelMaskForPage(page) & aChannelMask);
+
+        if (mask != 0)
+        {
+            entry->SetChannelPage(page);
+            entry->SetMaskLength(kMaskLength);
+            entry->SetMask(mask);
+
+            aValue.mLength += sizeof(Entry);
+            entry++;
+        }
+    }
+}
+
+Error ChannelMaskTlv::AppendTo(Message &aMessage, uint32_t aChannelMask)
+{
+    Value value;
+
+    PrepareValue(value, aChannelMask);
+    return Tlv::Append<ChannelMaskTlv>(aMessage, value.mData, value.mLength);
 }
 
 } // namespace MeshCoP
diff --git a/src/core/meshcop/meshcop_tlvs.hpp b/src/core/meshcop/meshcop_tlvs.hpp
index c9c324e..2f2b111 100644
--- a/src/core/meshcop/meshcop_tlvs.hpp
+++ b/src/core/meshcop/meshcop_tlvs.hpp
@@ -54,6 +54,7 @@
 #include "net/ip6_address.hpp"
 #include "radio/radio.hpp"
 #include "thread/key_manager.hpp"
+#include "thread/mle_tlvs.hpp"
 #include "thread/mle_types.hpp"
 
 namespace ot {
@@ -245,75 +246,22 @@
 typedef UintTlvInfo<Tlv::kScanDuration, uint16_t> ScanDurationTlv;
 
 /**
- * Defines Commissioner ID TLV constants and type.s
+ * Defines Commissioner ID TLV constants and types.
  *
  */
 typedef StringTlvInfo<Tlv::kCommissionerId, Tlv::kMaxCommissionerIdLength> CommissionerIdTlv;
 
 /**
- * Implements Channel TLV generation and parsing.
+ * Implements Channel TLV value format.
  *
  */
-OT_TOOL_PACKED_BEGIN
-class ChannelTlv : public Tlv, public TlvInfo<Tlv::kChannel>
-{
-public:
-    /**
-     * Initializes the TLV.
-     *
-     */
-    void Init(void)
-    {
-        SetType(kChannel);
-        SetLength(sizeof(*this) - sizeof(Tlv));
-    }
+typedef Mle::ChannelTlvValue ChannelTlvValue;
 
-    /**
-     * Indicates whether or not the TLV appears to be well-formed.
-     *
-     * @retval TRUE   If the TLV appears to be well-formed.
-     * @retval FALSE  If the TLV does not appear to be well-formed.
-     *
-     */
-    bool IsValid(void) const;
-
-    /**
-     * Returns the ChannelPage value.
-     *
-     * @returns The ChannelPage value.
-     *
-     */
-    uint8_t GetChannelPage(void) const { return mChannelPage; }
-
-    /**
-     * Sets the ChannelPage value.
-     *
-     * @param[in]  aChannelPage  The ChannelPage value.
-     *
-     */
-    void SetChannelPage(uint8_t aChannelPage) { mChannelPage = aChannelPage; }
-
-    /**
-     * Returns the Channel value.
-     *
-     * @returns The Channel value.
-     *
-     */
-    uint16_t GetChannel(void) const { return BigEndian::HostSwap16(mChannel); }
-
-    /**
-     * Sets the Channel value.
-     * Note: This method also sets the channel page according to the channel value.
-     *
-     * @param[in]  aChannel  The Channel value.
-     *
-     */
-    void SetChannel(uint16_t aChannel);
-
-private:
-    uint8_t  mChannelPage;
-    uint16_t mChannel;
-} OT_TOOL_PACKED_END;
+/**
+ * Defines Channel TLV constants and types.
+ *
+ */
+typedef SimpleTlvInfo<Tlv::kChannel, ChannelTlvValue> ChannelTlv;
 
 /**
  * Defines PAN ID TLV constants and types.
@@ -665,258 +613,121 @@
  */
 typedef UintTlvInfo<Tlv::kDelayTimer, uint32_t> DelayTimerTlv;
 
-// forward declare ChannelMaskTlv
-class ChannelMaskTlv;
-
-/**
- * Implements Channel Mask Entry generation and parsing.
- *
- */
-OT_TOOL_PACKED_BEGIN
-class ChannelMaskEntryBase
-{
-public:
-    /**
-     * Gets the ChannelPage value.
-     *
-     * @returns The ChannelPage value.
-     *
-     */
-    uint8_t GetChannelPage(void) const { return mChannelPage; }
-
-    /**
-     * Sets the ChannelPage value.
-     *
-     * @param[in]  aChannelPage  The ChannelPage value.
-     *
-     */
-    void SetChannelPage(uint8_t aChannelPage) { mChannelPage = aChannelPage; }
-
-    /**
-     * Gets the MaskLength value.
-     *
-     * @returns The MaskLength value.
-     *
-     */
-    uint8_t GetMaskLength(void) const { return mMaskLength; }
-
-    /**
-     * Sets the MaskLength value.
-     *
-     * @param[in]  aMaskLength  The MaskLength value.
-     *
-     */
-    void SetMaskLength(uint8_t aMaskLength) { mMaskLength = aMaskLength; }
-
-    /**
-     * Returns the total size of this Channel Mask Entry including the mask.
-     *
-     * @returns The total size of this entry (number of bytes).
-     *
-     */
-    uint16_t GetEntrySize(void) const { return sizeof(ChannelMaskEntryBase) + mMaskLength; }
-
-    /**
-     * Clears the bit corresponding to @p aChannel in ChannelMask.
-     *
-     * @param[in]  aChannel  The channel in ChannelMask to clear.
-     *
-     */
-    void ClearChannel(uint8_t aChannel)
-    {
-        uint8_t *mask = reinterpret_cast<uint8_t *>(this) + sizeof(*this);
-        mask[aChannel / 8] &= ~(0x80 >> (aChannel % 8));
-    }
-
-    /**
-     * Sets the bit corresponding to @p aChannel in ChannelMask.
-     *
-     * @param[in]  aChannel  The channel in ChannelMask to set.
-     *
-     */
-    void SetChannel(uint8_t aChannel)
-    {
-        uint8_t *mask = reinterpret_cast<uint8_t *>(this) + sizeof(*this);
-        mask[aChannel / 8] |= 0x80 >> (aChannel % 8);
-    }
-
-    /**
-     * Indicates whether or not the bit corresponding to @p aChannel in ChannelMask is set.
-     *
-     * @param[in]  aChannel  The channel in ChannelMask to get.
-     *
-     */
-    bool IsChannelSet(uint8_t aChannel) const
-    {
-        const uint8_t *mask = reinterpret_cast<const uint8_t *>(this) + sizeof(*this);
-        return (aChannel < (mMaskLength * 8)) ? ((mask[aChannel / 8] & (0x80 >> (aChannel % 8))) != 0) : false;
-    }
-
-    /**
-     * Gets the next Channel Mask Entry in a Channel Mask TLV.
-     *
-     * @returns A pointer to next Channel Mask Entry.
-     *
-     */
-    const ChannelMaskEntryBase *GetNext(void) const
-    {
-        return reinterpret_cast<const ChannelMaskEntryBase *>(reinterpret_cast<const uint8_t *>(this) + GetEntrySize());
-    }
-
-    /**
-     * Gets the next Channel Mask Entry in a Channel Mask TLV.
-     *
-     * @returns A pointer to next Channel Mask Entry.
-     *
-     */
-    ChannelMaskEntryBase *GetNext(void) { return AsNonConst(AsConst(this)->GetNext()); }
-
-private:
-    uint8_t mChannelPage;
-    uint8_t mMaskLength;
-} OT_TOOL_PACKED_END;
-
-/**
- * Implements Channel Mask Entry Page 0 generation and parsing.
- *
- */
-OT_TOOL_PACKED_BEGIN
-class ChannelMaskEntry : public ChannelMaskEntryBase
-{
-public:
-    /**
-     * Initializes the entry.
-     *
-     */
-    void Init(void)
-    {
-        SetChannelPage(0);
-        SetMaskLength(sizeof(mMask));
-    }
-
-    /**
-     * Indicates whether or not the entry appears to be well-formed.
-     *
-     * @retval TRUE   If the entry appears to be well-formed.
-     * @retval FALSE  If the entry does not appear to be well-formed.
-     *
-     */
-    bool IsValid(void) const { return GetMaskLength() == sizeof(mMask); }
-
-    /**
-     * Returns the Channel Mask value as a `uint32_t` bit mask.
-     *
-     * @returns The Channel Mask value.
-     *
-     */
-    uint32_t GetMask(void) const { return Reverse32(BigEndian::HostSwap32(mMask)); }
-
-    /**
-     * Sets the Channel Mask value.
-     *
-     * @param[in]  aMask  The Channel Mask value.
-     *
-     */
-    void SetMask(uint32_t aMask) { mMask = BigEndian::HostSwap32(Reverse32(aMask)); }
-
-private:
-    uint32_t mMask;
-} OT_TOOL_PACKED_END;
-
 /**
  * Implements Channel Mask TLV generation and parsing.
  *
  */
 OT_TOOL_PACKED_BEGIN
-class ChannelMaskBaseTlv : public Tlv, public TlvInfo<Tlv::kChannelMask>
+class ChannelMaskTlv : public Tlv, public TlvInfo<Tlv::kChannelMask>
 {
+    static constexpr uint8_t kEntryHeaderSize = 2; // Two bytes: mChannelPage and mMaskLength
+    static constexpr uint8_t kEntrySize       = kEntryHeaderSize + sizeof(uint32_t);
+
 public:
     /**
-     * Initializes the TLV.
+     * Represents Channel Mask TLV value to append.
      *
      */
-    void Init(void)
+    struct Value
     {
-        SetType(kChannelMask);
-        SetLength(sizeof(*this) - sizeof(Tlv));
-    }
+        static constexpr uint16_t kMaxLength = (kEntrySize * Radio::kNumChannelPages); ///< Max value length.
+
+        uint8_t mData[kMaxLength]; ///< Array to store TLV value (encoded as one or more Channel Mask TLV Entry)
+        uint8_t mLength;           ///< Value length in bytes.
+    };
+
+    ChannelMaskTlv(void) = delete;
 
     /**
-     * Indicates whether or not the TLV appears to be well-formed.
+     * Parses the Channel Mask TLV value and validates that all the included entries are well-formed.
      *
-     * @retval TRUE   If the TLV appears to be well-formed.
-     * @retval FALSE  If the TLV does not appear to be well-formed.
+     * @returns TRUE if the TLV is well-formed, FALSE otherwise.
      *
      */
     bool IsValid(void) const;
 
     /**
-     * Gets the first Channel Mask Entry in the Channel Mask TLV.
+     * Parses and retrieves the combined channel mask for all supported channel pages from entries in the TLV.
      *
-     * @returns A pointer to first Channel Mask Entry or `nullptr` if not found.
+     * @param[out] aChannelMask  A reference to return the channel mask.
+     *
+     * @retval kErrorNone   Successfully parsed the TLV value, @p aChannelMask is updated.
+     * @retval kErrorParse  TLV value is not well-formed.
      *
      */
-    const ChannelMaskEntryBase *GetFirstEntry(void) const;
+    Error ReadChannelMask(uint32_t &aChannelMask) const;
 
     /**
-     * Gets the first Channel Mask Entry in the Channel Mask TLV.
+     * Searches within a given message for Channel Mask TLV, parses and validates the TLV value and returns the
+     * combined channel mask for all supported channel pages included in the TLV.
      *
-     * @returns A pointer to first Channel Mask Entry or `nullptr` if not found.
+     * @param[in]  aMessage      The message to search in.
+     * @param[out] aChannelMask  A reference to return the channel mask.
+     *
+     * @retval kErrorNone       Found the TLV, successfully parsed its value, @p aChannelMask is updated.
+     * @retval kErrorNotFound   No Channel Mask TLV found in the @p aMessage.
+     * @retval kErrorParse      Found the TLV, but failed to parse it.
      *
      */
-    ChannelMaskEntryBase *GetFirstEntry(void);
-} OT_TOOL_PACKED_END;
-
-/**
- * Implements Channel Mask TLV generation and parsing.
- *
- */
-OT_TOOL_PACKED_BEGIN
-class ChannelMaskTlv : public ChannelMaskBaseTlv
-{
-public:
-    /**
-     * Initializes the TLV.
-     *
-     */
-    void Init(void)
-    {
-        SetType(kChannelMask);
-        SetLength(sizeof(*this) - sizeof(Tlv));
-        memset(mEntries, 0, sizeof(mEntries));
-    }
+    static Error FindIn(const Message &aMessage, uint32_t &aChannelMask);
 
     /**
-     * Sets the Channel Mask Entries.
+     * Prepares Channel Mask TLV value for appending/writing.
      *
-     * @param[in]  aChannelMask  The Channel Mask value.
+     * @param[out] aValue        A reference to `Value` structure to populate.
+     * @param[in]  aChannelMask  The combined channel mask for all supported channel pages.
      *
      */
-    void SetChannelMask(uint32_t aChannelMask);
+    static void PrepareValue(Value &aValue, uint32_t aChannelMask);
 
     /**
-     * Returns the Channel Mask value as a `uint32_t` bit mask.
+     * Prepares a Channel Mask TLV value and appends the TLV to a given message.
      *
-     * @returns The Channel Mask or 0 if not found.
+     * @param[in] aMessage       The message to append to.
+     * @param[in] aChannelMask   The combined channel mask for all supported channel pages.
+     *
+     * @retval kErrorNone    Successfully prepared the Channel Mask TLV and appended it to @p aMessage.
+     * @retval kErrorNoBufs  Insufficient available buffers to grow the message.
      *
      */
-    uint32_t GetChannelMask(void) const;
-
-    /**
-     * Reads message and returns the Channel Mask value as a `uint32_t` bit mask.
-     *
-     * @param[in]   aMessage     A reference to the message.
-     *
-     * @returns The Channel Mask or 0 if not found.
-     *
-     */
-    static uint32_t GetChannelMask(const Message &aMessage);
+    static Error AppendTo(Message &aMessage, uint32_t aChannelMask);
 
 private:
-    static constexpr uint8_t kNumMaskEntries = Radio::kNumChannelPages;
+    static constexpr uint8_t kMaskLength = sizeof(uint32_t);
 
-    ChannelMaskEntry mEntries[kNumMaskEntries];
-} OT_TOOL_PACKED_END;
+    OT_TOOL_PACKED_BEGIN
+    class Entry
+    {
+    public:
+        uint8_t  GetChannelPage(void) const { return mChannelPage; }
+        void     SetChannelPage(uint8_t aChannelPage) { mChannelPage = aChannelPage; }
+        uint8_t  GetMaskLength(void) const { return mMaskLength; }
+        void     SetMaskLength(uint8_t aMaskLength) { mMaskLength = aMaskLength; }
+        uint32_t GetMask(void) const { return Reverse32(BigEndian::HostSwap32(mMask)); }
+        void     SetMask(uint32_t aMask) { mMask = BigEndian::HostSwap32(Reverse32(aMask)); }
+
+    private:
+        uint8_t  mChannelPage;
+        uint8_t  mMaskLength;
+        uint32_t mMask;
+    } OT_TOOL_PACKED_END;
+
+    struct EntriesData : public Clearable<EntriesData>
+    {
+        // Represents received Channel Mask TLV Entries data which
+        // is either contained in `mData` buffer, or in `mMessage`
+        // at `mOffset`.
+
+        Error Parse(uint32_t &aChannelMask);
+
+        const uint8_t *mData;
+        const Message *mMessage;
+        uint16_t       mOffset;
+        uint16_t       mLength;
+    };
+
+    uint8_t mEntriesStart;
+} OT_TOOL_PACKED_BEGIN;
 
 /**
  * Implements Energy List TLV generation and parsing.
diff --git a/src/core/meshcop/panid_query_client.cpp b/src/core/meshcop/panid_query_client.cpp
index 431e8c2..01a3e9a 100644
--- a/src/core/meshcop/panid_query_client.cpp
+++ b/src/core/meshcop/panid_query_client.cpp
@@ -62,10 +62,9 @@
                                   otCommissionerPanIdConflictCallback aCallback,
                                   void                               *aContext)
 {
-    Error                   error = kErrorNone;
-    MeshCoP::ChannelMaskTlv channelMask;
-    Tmf::MessageInfo        messageInfo(GetInstance());
-    Coap::Message          *message = nullptr;
+    Error            error = kErrorNone;
+    Tmf::MessageInfo messageInfo(GetInstance());
+    Coap::Message   *message = nullptr;
 
     VerifyOrExit(Get<MeshCoP::Commissioner>().IsActive(), error = kErrorInvalidState);
     VerifyOrExit((message = Get<Tmf::Agent>().NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
@@ -76,9 +75,7 @@
     SuccessOrExit(
         error = Tlv::Append<MeshCoP::CommissionerSessionIdTlv>(*message, Get<MeshCoP::Commissioner>().GetSessionId()));
 
-    channelMask.Init();
-    channelMask.SetChannelMask(aChannelMask);
-    SuccessOrExit(error = channelMask.AppendTo(*message));
+    SuccessOrExit(error = MeshCoP::ChannelMaskTlv::AppendTo(*message, aChannelMask));
 
     SuccessOrExit(error = Tlv::Append<MeshCoP::PanIdTlv>(*message, aPanId));
 
@@ -106,7 +103,7 @@
 
     SuccessOrExit(Tlv::Find<MeshCoP::PanIdTlv>(aMessage, panId));
 
-    VerifyOrExit((mask = MeshCoP::ChannelMaskTlv::GetChannelMask(aMessage)) != 0);
+    SuccessOrExit(MeshCoP::ChannelMaskTlv::FindIn(aMessage, mask));
 
     mCallback.InvokeIfSet(panId, mask);
 
diff --git a/src/core/meshcop/secure_transport.cpp b/src/core/meshcop/secure_transport.cpp
new file mode 100644
index 0000000..530ba5c
--- /dev/null
+++ b/src/core/meshcop/secure_transport.cpp
@@ -0,0 +1,1251 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ *   This file implements the necessary hooks for mbedTLS.
+ */
+
+#include "secure_transport.hpp"
+
+#include <mbedtls/debug.h>
+#ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
+#include <mbedtls/pem.h>
+#endif
+
+#include <openthread/platform/radio.h>
+
+#include "common/as_core_type.hpp"
+#include "common/clearable.hpp"
+#include "common/code_utils.hpp"
+#include "common/debug.hpp"
+#include "common/encoding.hpp"
+#include "common/locator_getters.hpp"
+#include "common/log.hpp"
+#include "common/timer.hpp"
+#include "crypto/mbedtls.hpp"
+#include "crypto/sha256.hpp"
+#include "instance/instance.hpp"
+#include "thread/thread_netif.hpp"
+
+#if OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE
+
+namespace ot {
+namespace MeshCoP {
+
+RegisterLogModule("SecTransport");
+
+#if (MBEDTLS_VERSION_NUMBER >= 0x03010000)
+const uint16_t SecureTransport::sGroups[] = {MBEDTLS_SSL_IANA_TLS_GROUP_SECP256R1, MBEDTLS_SSL_IANA_TLS_GROUP_NONE};
+#else
+const mbedtls_ecp_group_id SecureTransport::sCurves[] = {MBEDTLS_ECP_DP_SECP256R1, MBEDTLS_ECP_DP_NONE};
+#endif
+
+#if defined(MBEDTLS_KEY_EXCHANGE__WITH_CERT__ENABLED) || defined(MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED)
+#if (MBEDTLS_VERSION_NUMBER >= 0x03020000)
+const uint16_t SecureTransport::sSignatures[] = {MBEDTLS_TLS1_3_SIG_ECDSA_SECP256R1_SHA256, MBEDTLS_TLS1_3_SIG_NONE};
+#else
+const int SecureTransport::sHashes[] = {MBEDTLS_MD_SHA256, MBEDTLS_MD_NONE};
+#endif
+#endif
+
+SecureTransport::SecureTransport(Instance &aInstance, bool aLayerTwoSecurity, bool aDatagramTransport)
+    : InstanceLocator(aInstance)
+    , mState(kStateClosed)
+    , mPskLength(0)
+    , mVerifyPeerCertificate(true)
+    , mTimer(aInstance, SecureTransport::HandleTimer, this)
+    , mTimerIntermediate(0)
+    , mTimerSet(false)
+    , mLayerTwoSecurity(aLayerTwoSecurity)
+    , mDatagramTransport(aDatagramTransport)
+    , mMaxConnectionAttempts(0)
+    , mRemainingConnectionAttempts(0)
+    , mReceiveMessage(nullptr)
+    , mSocket(aInstance)
+    , mMessageSubType(Message::kSubTypeNone)
+    , mMessageDefaultSubType(Message::kSubTypeNone)
+{
+#if OPENTHREAD_CONFIG_TLS_API_ENABLE
+#ifdef MBEDTLS_KEY_EXCHANGE_PSK_ENABLED
+    mPreSharedKey         = nullptr;
+    mPreSharedKeyIdentity = nullptr;
+    mPreSharedKeyIdLength = 0;
+    mPreSharedKeyLength   = 0;
+#endif
+
+#ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
+    mCaChainSrc       = nullptr;
+    mCaChainLength    = 0;
+    mOwnCertSrc       = nullptr;
+    mOwnCertLength    = 0;
+    mPrivateKeySrc    = nullptr;
+    mPrivateKeyLength = 0;
+    ClearAllBytes(mCaChain);
+    ClearAllBytes(mOwnCert);
+    ClearAllBytes(mPrivateKey);
+#endif
+#endif
+
+    ClearAllBytes(mCipherSuites);
+    ClearAllBytes(mPsk);
+    ClearAllBytes(mSsl);
+    ClearAllBytes(mConf);
+
+#ifdef MBEDTLS_SSL_COOKIE_C
+    ClearAllBytes(mCookieCtx);
+#endif
+}
+
+void SecureTransport::FreeMbedtls(void)
+{
+#if defined(MBEDTLS_SSL_SRV_C) && defined(MBEDTLS_SSL_COOKIE_C)
+    if (mDatagramTransport)
+    {
+        mbedtls_ssl_cookie_free(&mCookieCtx);
+    }
+#endif
+#if OPENTHREAD_CONFIG_TLS_API_ENABLE
+#ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
+    mbedtls_x509_crt_free(&mCaChain);
+    mbedtls_x509_crt_free(&mOwnCert);
+    mbedtls_pk_free(&mPrivateKey);
+#endif
+#endif
+    mbedtls_ssl_config_free(&mConf);
+    mbedtls_ssl_free(&mSsl);
+}
+
+void SecureTransport::SetState(State aState)
+{
+    VerifyOrExit(mState != aState);
+
+    LogInfo("State: %s -> %s", StateToString(mState), StateToString(aState));
+    mState = aState;
+
+exit:
+    return;
+}
+
+Error SecureTransport::Open(ReceiveHandler aReceiveHandler, ConnectedHandler aConnectedHandler, void *aContext)
+{
+    Error error;
+
+    VerifyOrExit(IsStateClosed(), error = kErrorAlready);
+
+    SuccessOrExit(error = mSocket.Open(&SecureTransport::HandleReceive, this));
+
+    mConnectedCallback.Set(aConnectedHandler, aContext);
+    mReceiveCallback.Set(aReceiveHandler, aContext);
+
+    mRemainingConnectionAttempts = mMaxConnectionAttempts;
+
+    SetState(kStateOpen);
+
+exit:
+    return error;
+}
+
+Error SecureTransport::SetMaxConnectionAttempts(uint16_t aMaxAttempts, AutoCloseCallback aCallback, void *aContext)
+{
+    Error error = kErrorNone;
+
+    VerifyOrExit(IsStateClosed(), error = kErrorInvalidState);
+
+    mMaxConnectionAttempts = aMaxAttempts;
+    mAutoCloseCallback.Set(aCallback, aContext);
+
+exit:
+    return error;
+}
+
+Error SecureTransport::Connect(const Ip6::SockAddr &aSockAddr)
+{
+    Error error;
+
+    VerifyOrExit(IsStateOpen(), error = kErrorInvalidState);
+
+    if (mRemainingConnectionAttempts > 0)
+    {
+        mRemainingConnectionAttempts--;
+    }
+
+    mMessageInfo.SetPeerAddr(aSockAddr.GetAddress());
+    mMessageInfo.SetPeerPort(aSockAddr.mPort);
+
+    error = Setup(true);
+
+exit:
+    return error;
+}
+
+void SecureTransport::HandleReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
+{
+    static_cast<SecureTransport *>(aContext)->HandleReceive(AsCoreType(aMessage), AsCoreType(aMessageInfo));
+}
+
+void SecureTransport::HandleReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
+{
+    VerifyOrExit(!IsStateClosed());
+
+    if (IsStateOpen())
+    {
+        if (mRemainingConnectionAttempts > 0)
+        {
+            mRemainingConnectionAttempts--;
+        }
+
+        IgnoreError(mSocket.Connect(Ip6::SockAddr(aMessageInfo.GetPeerAddr(), aMessageInfo.GetPeerPort())));
+
+        mMessageInfo.SetPeerAddr(aMessageInfo.GetPeerAddr());
+        mMessageInfo.SetPeerPort(aMessageInfo.GetPeerPort());
+        mMessageInfo.SetIsHostInterface(aMessageInfo.IsHostInterface());
+
+        if (Get<ThreadNetif>().HasUnicastAddress(aMessageInfo.GetSockAddr()))
+        {
+            mMessageInfo.SetSockAddr(aMessageInfo.GetSockAddr());
+        }
+
+        mMessageInfo.SetSockPort(aMessageInfo.GetSockPort());
+
+        SuccessOrExit(Setup(false));
+    }
+    else
+    {
+        // Once DTLS session is started, communicate only with a peer.
+        VerifyOrExit((mMessageInfo.GetPeerAddr() == aMessageInfo.GetPeerAddr()) &&
+                     (mMessageInfo.GetPeerPort() == aMessageInfo.GetPeerPort()));
+    }
+
+#ifdef MBEDTLS_SSL_SRV_C
+    if (IsStateConnecting())
+    {
+        IgnoreError(SetClientId(mMessageInfo.GetPeerAddr().mFields.m8, sizeof(mMessageInfo.GetPeerAddr().mFields)));
+    }
+#endif
+
+    Receive(aMessage);
+
+exit:
+    return;
+}
+
+uint16_t SecureTransport::GetUdpPort(void) const { return mSocket.GetSockName().GetPort(); }
+
+Error SecureTransport::Bind(uint16_t aPort)
+{
+    Error error;
+
+    VerifyOrExit(IsStateOpen(), error = kErrorInvalidState);
+    VerifyOrExit(!mTransportCallback.IsSet(), error = kErrorAlready);
+
+    SuccessOrExit(error = mSocket.Bind(aPort, Ip6::kNetifUnspecified));
+
+exit:
+    return error;
+}
+
+Error SecureTransport::Bind(TransportCallback aCallback, void *aContext)
+{
+    Error error = kErrorNone;
+
+    VerifyOrExit(IsStateOpen(), error = kErrorInvalidState);
+    VerifyOrExit(!mSocket.IsBound(), error = kErrorAlready);
+    VerifyOrExit(!mTransportCallback.IsSet(), error = kErrorAlready);
+
+    mTransportCallback.Set(aCallback, aContext);
+
+exit:
+    return error;
+}
+
+Error SecureTransport::Setup(bool aClient)
+{
+    int rval;
+
+    // do not handle new connection before guard time expired
+    VerifyOrExit(IsStateOpen(), rval = MBEDTLS_ERR_SSL_TIMEOUT);
+
+    SetState(kStateInitializing);
+
+    mbedtls_ssl_init(&mSsl);
+    mbedtls_ssl_config_init(&mConf);
+#if OPENTHREAD_CONFIG_TLS_API_ENABLE
+#ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
+    mbedtls_x509_crt_init(&mCaChain);
+    mbedtls_x509_crt_init(&mOwnCert);
+    mbedtls_pk_init(&mPrivateKey);
+#endif
+#endif
+#if defined(MBEDTLS_SSL_SRV_C) && defined(MBEDTLS_SSL_COOKIE_C)
+    if (mDatagramTransport)
+    {
+        mbedtls_ssl_cookie_init(&mCookieCtx);
+    }
+#endif
+
+    rval = mbedtls_ssl_config_defaults(
+        &mConf, aClient ? MBEDTLS_SSL_IS_CLIENT : MBEDTLS_SSL_IS_SERVER,
+        mDatagramTransport ? MBEDTLS_SSL_TRANSPORT_DATAGRAM : MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT);
+    VerifyOrExit(rval == 0);
+
+#if OPENTHREAD_CONFIG_TLS_API_ENABLE
+    if (mVerifyPeerCertificate && (mCipherSuites[0] == MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 ||
+                                   mCipherSuites[0] == MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256))
+    {
+        mbedtls_ssl_conf_authmode(&mConf, MBEDTLS_SSL_VERIFY_REQUIRED);
+    }
+    else
+    {
+        mbedtls_ssl_conf_authmode(&mConf, MBEDTLS_SSL_VERIFY_NONE);
+    }
+#else
+    OT_UNUSED_VARIABLE(mVerifyPeerCertificate);
+#endif
+
+    mbedtls_ssl_conf_rng(&mConf, Crypto::MbedTls::CryptoSecurePrng, nullptr);
+#if (MBEDTLS_VERSION_NUMBER >= 0x03020000)
+    mbedtls_ssl_conf_min_tls_version(&mConf, MBEDTLS_SSL_VERSION_TLS1_2);
+    mbedtls_ssl_conf_max_tls_version(&mConf, MBEDTLS_SSL_VERSION_TLS1_2);
+#else
+    mbedtls_ssl_conf_min_version(&mConf, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3);
+    mbedtls_ssl_conf_max_version(&mConf, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3);
+#endif
+
+    OT_ASSERT(mCipherSuites[1] == 0);
+    mbedtls_ssl_conf_ciphersuites(&mConf, mCipherSuites);
+    if (mCipherSuites[0] == MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8)
+    {
+#if (MBEDTLS_VERSION_NUMBER >= 0x03010000)
+        mbedtls_ssl_conf_groups(&mConf, sGroups);
+#else
+        mbedtls_ssl_conf_curves(&mConf, sCurves);
+#endif
+#if defined(MBEDTLS_KEY_EXCHANGE__WITH_CERT__ENABLED) || defined(MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED)
+#if (MBEDTLS_VERSION_NUMBER >= 0x03020000)
+        mbedtls_ssl_conf_sig_algs(&mConf, sSignatures);
+#else
+        mbedtls_ssl_conf_sig_hashes(&mConf, sHashes);
+#endif
+#endif
+    }
+
+#if (MBEDTLS_VERSION_NUMBER >= 0x03000000)
+    mbedtls_ssl_set_export_keys_cb(&mSsl, HandleMbedtlsExportKeys, this);
+#else
+    mbedtls_ssl_conf_export_keys_cb(&mConf, HandleMbedtlsExportKeys, this);
+#endif
+
+    mbedtls_ssl_conf_handshake_timeout(&mConf, 8000, 60000);
+    mbedtls_ssl_conf_dbg(&mConf, HandleMbedtlsDebug, this);
+
+#if defined(MBEDTLS_SSL_SRV_C) && defined(MBEDTLS_SSL_COOKIE_C)
+    if (!aClient && mDatagramTransport)
+    {
+        rval = mbedtls_ssl_cookie_setup(&mCookieCtx, Crypto::MbedTls::CryptoSecurePrng, nullptr);
+        VerifyOrExit(rval == 0);
+
+        mbedtls_ssl_conf_dtls_cookies(&mConf, mbedtls_ssl_cookie_write, mbedtls_ssl_cookie_check, &mCookieCtx);
+    }
+#endif
+
+    rval = mbedtls_ssl_setup(&mSsl, &mConf);
+    VerifyOrExit(rval == 0);
+
+    mbedtls_ssl_set_bio(&mSsl, this, &SecureTransport::HandleMbedtlsTransmit, HandleMbedtlsReceive, nullptr);
+
+    if (mDatagramTransport)
+    {
+        mbedtls_ssl_set_timer_cb(&mSsl, this, &SecureTransport::HandleMbedtlsSetTimer, HandleMbedtlsGetTimer);
+    }
+
+    if (mCipherSuites[0] == MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8)
+    {
+        rval = mbedtls_ssl_set_hs_ecjpake_password(&mSsl, mPsk, mPskLength);
+    }
+#if OPENTHREAD_CONFIG_TLS_API_ENABLE
+    else
+    {
+        rval = SetApplicationSecureKeys();
+    }
+#endif
+    VerifyOrExit(rval == 0);
+
+    mReceiveMessage = nullptr;
+    mMessageSubType = Message::kSubTypeNone;
+
+    if (mCipherSuites[0] == MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8)
+    {
+        LogInfo("DTLS started");
+    }
+#if OPENTHREAD_CONFIG_TLS_API_ENABLE
+    else
+    {
+        LogInfo("Application Secure (D)TLS started");
+    }
+#endif
+
+    SetState(kStateConnecting);
+
+    Process();
+
+exit:
+    if (IsStateInitializing() && (rval != 0))
+    {
+        if ((mMaxConnectionAttempts > 0) && (mRemainingConnectionAttempts == 0))
+        {
+            Close();
+            mAutoCloseCallback.InvokeIfSet();
+        }
+        else
+        {
+            SetState(kStateOpen);
+            FreeMbedtls();
+        }
+    }
+
+    return Crypto::MbedTls::MapError(rval);
+}
+
+#if OPENTHREAD_CONFIG_TLS_API_ENABLE
+int SecureTransport::SetApplicationSecureKeys(void)
+{
+    int rval = 0;
+
+    switch (mCipherSuites[0])
+    {
+    case MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8:
+    case MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+
+#ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
+        if (mCaChainSrc != nullptr)
+        {
+            rval = mbedtls_x509_crt_parse(&mCaChain, static_cast<const unsigned char *>(mCaChainSrc),
+                                          static_cast<size_t>(mCaChainLength));
+            VerifyOrExit(rval == 0);
+            mbedtls_ssl_conf_ca_chain(&mConf, &mCaChain, nullptr);
+        }
+
+        if (mOwnCertSrc != nullptr && mPrivateKeySrc != nullptr)
+        {
+            rval = mbedtls_x509_crt_parse(&mOwnCert, static_cast<const unsigned char *>(mOwnCertSrc),
+                                          static_cast<size_t>(mOwnCertLength));
+            VerifyOrExit(rval == 0);
+
+#if (MBEDTLS_VERSION_NUMBER >= 0x03000000)
+            rval = mbedtls_pk_parse_key(&mPrivateKey, static_cast<const unsigned char *>(mPrivateKeySrc),
+                                        static_cast<size_t>(mPrivateKeyLength), nullptr, 0,
+                                        Crypto::MbedTls::CryptoSecurePrng, nullptr);
+#else
+            rval = mbedtls_pk_parse_key(&mPrivateKey, static_cast<const unsigned char *>(mPrivateKeySrc),
+                                        static_cast<size_t>(mPrivateKeyLength), nullptr, 0);
+#endif
+            VerifyOrExit(rval == 0);
+            rval = mbedtls_ssl_conf_own_cert(&mConf, &mOwnCert, &mPrivateKey);
+            VerifyOrExit(rval == 0);
+        }
+#endif
+        break;
+
+    case MBEDTLS_TLS_PSK_WITH_AES_128_CCM_8:
+#ifdef MBEDTLS_KEY_EXCHANGE_PSK_ENABLED
+        rval = mbedtls_ssl_conf_psk(&mConf, static_cast<const unsigned char *>(mPreSharedKey), mPreSharedKeyLength,
+                                    static_cast<const unsigned char *>(mPreSharedKeyIdentity), mPreSharedKeyIdLength);
+        VerifyOrExit(rval == 0);
+#endif
+        break;
+
+    default:
+        LogCrit("Application Coap Secure: Not supported cipher.");
+        rval = MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
+        ExitNow();
+        break;
+    }
+
+exit:
+    return rval;
+}
+
+#endif // OPENTHREAD_CONFIG_TLS_API_ENABLE
+
+void SecureTransport::Close(void)
+{
+    Disconnect();
+
+    SetState(kStateClosed);
+    mTimerSet = false;
+    mTransportCallback.Clear();
+
+    IgnoreError(mSocket.Close());
+    mTimer.Stop();
+}
+
+void SecureTransport::Disconnect(void)
+{
+    VerifyOrExit(IsStateConnectingOrConnected());
+
+    mbedtls_ssl_close_notify(&mSsl);
+    SetState(kStateCloseNotify);
+    mTimer.Start(kGuardTimeNewConnectionMilli);
+
+    mMessageInfo.Clear();
+    IgnoreError(mSocket.Connect());
+
+    FreeMbedtls();
+
+exit:
+    return;
+}
+
+Error SecureTransport::SetPsk(const uint8_t *aPsk, uint8_t aPskLength)
+{
+    Error error = kErrorNone;
+
+    VerifyOrExit(aPskLength <= sizeof(mPsk), error = kErrorInvalidArgs);
+
+    memcpy(mPsk, aPsk, aPskLength);
+    mPskLength       = aPskLength;
+    mCipherSuites[0] = MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8;
+    mCipherSuites[1] = 0;
+
+exit:
+    return error;
+}
+
+#if OPENTHREAD_CONFIG_TLS_API_ENABLE
+#ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
+
+void SecureTransport::SetCertificate(const uint8_t *aX509Certificate,
+                                     uint32_t       aX509CertLength,
+                                     const uint8_t *aPrivateKey,
+                                     uint32_t       aPrivateKeyLength)
+{
+    OT_ASSERT(aX509CertLength > 0);
+    OT_ASSERT(aX509Certificate != nullptr);
+
+    OT_ASSERT(aPrivateKeyLength > 0);
+    OT_ASSERT(aPrivateKey != nullptr);
+
+    mOwnCertSrc       = aX509Certificate;
+    mOwnCertLength    = aX509CertLength;
+    mPrivateKeySrc    = aPrivateKey;
+    mPrivateKeyLength = aPrivateKeyLength;
+
+    if (mDatagramTransport)
+    {
+        mCipherSuites[0] = MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8;
+    }
+    else
+    {
+        mCipherSuites[0] = MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256;
+    }
+
+    mCipherSuites[1] = 0;
+}
+
+void SecureTransport::SetCaCertificateChain(const uint8_t *aX509CaCertificateChain, uint32_t aX509CaCertChainLength)
+{
+    OT_ASSERT(aX509CaCertChainLength > 0);
+    OT_ASSERT(aX509CaCertificateChain != nullptr);
+
+    mCaChainSrc    = aX509CaCertificateChain;
+    mCaChainLength = aX509CaCertChainLength;
+}
+
+#endif // MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
+
+#ifdef MBEDTLS_KEY_EXCHANGE_PSK_ENABLED
+void SecureTransport::SetPreSharedKey(const uint8_t *aPsk,
+                                      uint16_t       aPskLength,
+                                      const uint8_t *aPskIdentity,
+                                      uint16_t       aPskIdLength)
+{
+    OT_ASSERT(aPsk != nullptr);
+    OT_ASSERT(aPskIdentity != nullptr);
+    OT_ASSERT(aPskLength > 0);
+    OT_ASSERT(aPskIdLength > 0);
+
+    mPreSharedKey         = aPsk;
+    mPreSharedKeyLength   = aPskLength;
+    mPreSharedKeyIdentity = aPskIdentity;
+    mPreSharedKeyIdLength = aPskIdLength;
+
+    mCipherSuites[0] = MBEDTLS_TLS_PSK_WITH_AES_128_CCM_8;
+    mCipherSuites[1] = 0;
+}
+#endif
+
+#if defined(MBEDTLS_BASE64_C) && defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
+Error SecureTransport::GetPeerCertificateBase64(unsigned char *aPeerCert, size_t *aCertLength, size_t aCertBufferSize)
+{
+    Error error = kErrorNone;
+
+    VerifyOrExit(IsStateConnected(), error = kErrorInvalidState);
+
+#if (MBEDTLS_VERSION_NUMBER >= 0x03010000)
+    VerifyOrExit(mbedtls_base64_encode(aPeerCert, aCertBufferSize, aCertLength,
+                                       mSsl.MBEDTLS_PRIVATE(session)->MBEDTLS_PRIVATE(peer_cert)->raw.p,
+                                       mSsl.MBEDTLS_PRIVATE(session)->MBEDTLS_PRIVATE(peer_cert)->raw.len) == 0,
+                 error = kErrorNoBufs);
+#else
+    VerifyOrExit(
+        mbedtls_base64_encode(
+            aPeerCert, aCertBufferSize, aCertLength,
+            mSsl.MBEDTLS_PRIVATE(session)->MBEDTLS_PRIVATE(peer_cert)->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(p),
+            mSsl.MBEDTLS_PRIVATE(session)->MBEDTLS_PRIVATE(peer_cert)->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(len)) == 0,
+        error = kErrorNoBufs);
+#endif
+
+exit:
+    return error;
+}
+#endif // defined(MBEDTLS_BASE64_C) && defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
+
+#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
+Error SecureTransport::GetPeerSubjectAttributeByOid(const char *aOid,
+                                                    size_t      aOidLength,
+                                                    uint8_t    *aAttributeBuffer,
+                                                    size_t     *aAttributeLength,
+                                                    int        *aAsn1Type)
+{
+    Error                          error = kErrorNone;
+    const mbedtls_asn1_named_data *data;
+    size_t                         length;
+    size_t                         attributeBufferSize;
+    mbedtls_x509_crt              *peerCert = const_cast<mbedtls_x509_crt *>(mbedtls_ssl_get_peer_cert(&mSsl));
+
+    VerifyOrExit(aAttributeLength != nullptr, error = kErrorInvalidArgs);
+    attributeBufferSize = *aAttributeLength;
+    *aAttributeLength   = 0;
+
+    VerifyOrExit(aAttributeBuffer != nullptr, error = kErrorNoBufs);
+    VerifyOrExit(peerCert != nullptr, error = kErrorInvalidState);
+    data = mbedtls_asn1_find_named_data(&peerCert->subject, aOid, aOidLength);
+    VerifyOrExit(data != nullptr, error = kErrorNotFound);
+    length = data->val.len;
+    VerifyOrExit(length <= attributeBufferSize, error = kErrorNoBufs);
+
+    if (aAttributeLength != nullptr)
+    {
+        *aAttributeLength = length;
+    }
+
+    if (aAsn1Type != nullptr)
+    {
+        *aAsn1Type = data->val.tag;
+    }
+
+    memcpy(aAttributeBuffer, data->val.p, length);
+
+exit:
+    return error;
+}
+
+Error SecureTransport::GetThreadAttributeFromPeerCertificate(int      aThreadOidDescriptor,
+                                                             uint8_t *aAttributeBuffer,
+                                                             size_t  *aAttributeLength)
+{
+    const mbedtls_x509_crt *cert = mbedtls_ssl_get_peer_cert(&mSsl);
+
+    return GetThreadAttributeFromCertificate(cert, aThreadOidDescriptor, aAttributeBuffer, aAttributeLength);
+}
+
+#endif // defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
+
+Error SecureTransport::GetThreadAttributeFromOwnCertificate(int      aThreadOidDescriptor,
+                                                            uint8_t *aAttributeBuffer,
+                                                            size_t  *aAttributeLength)
+{
+    const mbedtls_x509_crt *cert = &mOwnCert;
+
+    return GetThreadAttributeFromCertificate(cert, aThreadOidDescriptor, aAttributeBuffer, aAttributeLength);
+}
+
+Error SecureTransport::GetThreadAttributeFromCertificate(const mbedtls_x509_crt *aCert,
+                                                         int                     aThreadOidDescriptor,
+                                                         uint8_t                *aAttributeBuffer,
+                                                         size_t                 *aAttributeLength)
+{
+    Error            error  = kErrorNotFound;
+    char             oid[9] = {0x2B, 0x06, 0x01, 0x04, 0x01, static_cast<char>(0x82), static_cast<char>(0xDF),
+                               0x2A, 0x00}; // 1.3.6.1.4.1.44970.0
+    mbedtls_x509_buf v3_ext;
+    unsigned char   *p, *end, *endExtData;
+    size_t           len;
+    size_t           attributeBufferSize;
+    mbedtls_x509_buf extnOid;
+    int              ret, isCritical;
+
+    VerifyOrExit(aAttributeLength != nullptr, error = kErrorInvalidArgs);
+    attributeBufferSize = *aAttributeLength;
+    *aAttributeLength   = 0;
+
+    VerifyOrExit(aCert != nullptr, error = kErrorInvalidState);
+    v3_ext = aCert->v3_ext;
+    p      = v3_ext.p;
+    VerifyOrExit(p != nullptr, error = kErrorInvalidState);
+    end = p + v3_ext.len;
+    VerifyOrExit(mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) == 0,
+                 error = kErrorParse);
+    VerifyOrExit(end == p + len, error = kErrorParse);
+
+    VerifyOrExit(aThreadOidDescriptor < 128, error = kErrorNotImplemented);
+    oid[sizeof(oid) - 1] = static_cast<char>(aThreadOidDescriptor);
+
+    while (p < end)
+    {
+        isCritical = 0;
+        VerifyOrExit(mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) == 0,
+                     error = kErrorParse);
+        endExtData = p + len;
+
+        // Get extension ID
+        VerifyOrExit(mbedtls_asn1_get_tag(&p, endExtData, &extnOid.len, MBEDTLS_ASN1_OID) == 0, error = kErrorParse);
+        extnOid.tag = MBEDTLS_ASN1_OID;
+        extnOid.p   = p;
+        p += extnOid.len;
+
+        // Get optional critical
+        ret = mbedtls_asn1_get_bool(&p, endExtData, &isCritical);
+        VerifyOrExit(ret == 0 || ret == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG, error = kErrorParse);
+
+        // Data should be octet string type
+        VerifyOrExit(mbedtls_asn1_get_tag(&p, endExtData, &len, MBEDTLS_ASN1_OCTET_STRING) == 0, error = kErrorParse);
+        VerifyOrExit(endExtData == p + len, error = kErrorParse);
+
+        if (isCritical || extnOid.len != sizeof(oid))
+        {
+            continue;
+        }
+
+        if (memcmp(extnOid.p, oid, sizeof(oid)) == 0)
+        {
+            *aAttributeLength = len;
+
+            if (aAttributeBuffer != nullptr)
+            {
+                VerifyOrExit(len <= attributeBufferSize, error = kErrorNoBufs);
+                memcpy(aAttributeBuffer, p, len);
+            }
+
+            error = kErrorNone;
+            break;
+        }
+    }
+
+exit:
+    return error;
+}
+
+#endif // OPENTHREAD_CONFIG_TLS_API_ENABLE
+
+#ifdef MBEDTLS_SSL_SRV_C
+Error SecureTransport::SetClientId(const uint8_t *aClientId, uint8_t aLength)
+{
+    int rval = mbedtls_ssl_set_client_transport_id(&mSsl, aClientId, aLength);
+    return Crypto::MbedTls::MapError(rval);
+}
+#endif
+
+Error SecureTransport::Send(Message &aMessage, uint16_t aLength)
+{
+    Error   error = kErrorNone;
+    uint8_t buffer[kApplicationDataMaxLength];
+
+    VerifyOrExit(aLength <= kApplicationDataMaxLength, error = kErrorNoBufs);
+
+    // Store message specific sub type.
+    if (aMessage.GetSubType() != Message::kSubTypeNone)
+    {
+        mMessageSubType = aMessage.GetSubType();
+    }
+
+    aMessage.ReadBytes(0, buffer, aLength);
+
+    SuccessOrExit(error = Crypto::MbedTls::MapError(mbedtls_ssl_write(&mSsl, buffer, aLength)));
+
+    aMessage.Free();
+
+exit:
+    return error;
+}
+
+void SecureTransport::Receive(Message &aMessage)
+{
+    mReceiveMessage = &aMessage;
+
+    Process();
+
+    mReceiveMessage = nullptr;
+}
+
+int SecureTransport::HandleMbedtlsTransmit(void *aContext, const unsigned char *aBuf, size_t aLength)
+{
+    return static_cast<SecureTransport *>(aContext)->HandleMbedtlsTransmit(aBuf, aLength);
+}
+
+int SecureTransport::HandleMbedtlsTransmit(const unsigned char *aBuf, size_t aLength)
+{
+    Error error;
+    int   rval = 0;
+
+    if (mCipherSuites[0] == MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8)
+    {
+        LogDebg("HandleMbedtlsTransmit DTLS");
+    }
+#if OPENTHREAD_CONFIG_TLS_API_ENABLE
+    else
+    {
+        LogDebg("HandleMbedtlsTransmit TLS");
+    }
+#endif
+
+    error = HandleSecureTransportSend(aBuf, static_cast<uint16_t>(aLength), mMessageSubType);
+
+    // Restore default sub type.
+    mMessageSubType = mMessageDefaultSubType;
+
+    switch (error)
+    {
+    case kErrorNone:
+        rval = static_cast<int>(aLength);
+        break;
+
+    case kErrorNoBufs:
+        rval = MBEDTLS_ERR_SSL_WANT_WRITE;
+        break;
+
+    default:
+        LogWarn("HandleMbedtlsTransmit: %s error", ErrorToString(error));
+        rval = MBEDTLS_ERR_NET_SEND_FAILED;
+        break;
+    }
+
+    return rval;
+}
+
+int SecureTransport::HandleMbedtlsReceive(void *aContext, unsigned char *aBuf, size_t aLength)
+{
+    return static_cast<SecureTransport *>(aContext)->HandleMbedtlsReceive(aBuf, aLength);
+}
+
+int SecureTransport::HandleMbedtlsReceive(unsigned char *aBuf, size_t aLength)
+{
+    int rval;
+
+    if (mCipherSuites[0] == MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8)
+    {
+        LogDebg("HandleMbedtlsReceive DTLS");
+    }
+#if OPENTHREAD_CONFIG_TLS_API_ENABLE
+    else
+    {
+        LogDebg("HandleMbedtlsReceive TLS");
+    }
+#endif
+
+    VerifyOrExit(mReceiveMessage != nullptr && (rval = mReceiveMessage->GetLength() - mReceiveMessage->GetOffset()) > 0,
+                 rval = MBEDTLS_ERR_SSL_WANT_READ);
+
+    if (aLength > static_cast<size_t>(rval))
+    {
+        aLength = static_cast<size_t>(rval);
+    }
+
+    rval = mReceiveMessage->ReadBytes(mReceiveMessage->GetOffset(), aBuf, static_cast<uint16_t>(aLength));
+    mReceiveMessage->MoveOffset(rval);
+
+exit:
+    return rval;
+}
+
+int SecureTransport::HandleMbedtlsGetTimer(void *aContext)
+{
+    return static_cast<SecureTransport *>(aContext)->HandleMbedtlsGetTimer();
+}
+
+int SecureTransport::HandleMbedtlsGetTimer(void)
+{
+    int rval;
+
+    if (mCipherSuites[0] == MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8)
+    {
+        LogDebg("HandleMbedtlsGetTimer");
+    }
+#if OPENTHREAD_CONFIG_TLS_API_ENABLE
+    else
+    {
+        LogDebg("HandleMbedtlsGetTimer");
+    }
+#endif
+
+    if (!mTimerSet)
+    {
+        rval = -1;
+    }
+    else if (!mTimer.IsRunning())
+    {
+        rval = 2;
+    }
+    else if (mTimerIntermediate <= TimerMilli::GetNow())
+    {
+        rval = 1;
+    }
+    else
+    {
+        rval = 0;
+    }
+
+    return rval;
+}
+
+void SecureTransport::HandleMbedtlsSetTimer(void *aContext, uint32_t aIntermediate, uint32_t aFinish)
+{
+    static_cast<SecureTransport *>(aContext)->HandleMbedtlsSetTimer(aIntermediate, aFinish);
+}
+
+void SecureTransport::HandleMbedtlsSetTimer(uint32_t aIntermediate, uint32_t aFinish)
+{
+    if (mCipherSuites[0] == MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8)
+    {
+        LogDebg("SetTimer DTLS");
+    }
+#if OPENTHREAD_CONFIG_TLS_API_ENABLE
+    else
+    {
+        LogDebg("SetTimer TLS");
+    }
+#endif
+
+    if (aFinish == 0)
+    {
+        mTimerSet = false;
+        mTimer.Stop();
+    }
+    else
+    {
+        mTimerSet = true;
+        mTimer.Start(aFinish);
+        mTimerIntermediate = TimerMilli::GetNow() + aIntermediate;
+    }
+}
+
+#if (MBEDTLS_VERSION_NUMBER >= 0x03000000)
+
+void SecureTransport::HandleMbedtlsExportKeys(void                       *aContext,
+                                              mbedtls_ssl_key_export_type aType,
+                                              const unsigned char        *aMasterSecret,
+                                              size_t                      aMasterSecretLen,
+                                              const unsigned char         aClientRandom[32],
+                                              const unsigned char         aServerRandom[32],
+                                              mbedtls_tls_prf_types       aTlsPrfType)
+{
+    static_cast<SecureTransport *>(aContext)->HandleMbedtlsExportKeys(aType, aMasterSecret, aMasterSecretLen,
+                                                                      aClientRandom, aServerRandom, aTlsPrfType);
+}
+
+void SecureTransport::HandleMbedtlsExportKeys(mbedtls_ssl_key_export_type aType,
+                                              const unsigned char        *aMasterSecret,
+                                              size_t                      aMasterSecretLen,
+                                              const unsigned char         aClientRandom[32],
+                                              const unsigned char         aServerRandom[32],
+                                              mbedtls_tls_prf_types       aTlsPrfType)
+{
+    Crypto::Sha256::Hash kek;
+    Crypto::Sha256       sha256;
+    unsigned char        keyBlock[kSecureTransportKeyBlockSize];
+    unsigned char        randBytes[2 * kSecureTransportRandomBufferSize];
+
+    VerifyOrExit(mCipherSuites[0] == MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8);
+    VerifyOrExit(aType == MBEDTLS_SSL_KEY_EXPORT_TLS12_MASTER_SECRET);
+
+    memcpy(randBytes, aServerRandom, kSecureTransportRandomBufferSize);
+    memcpy(randBytes + kSecureTransportRandomBufferSize, aClientRandom, kSecureTransportRandomBufferSize);
+
+    // Retrieve the Key block from Master secret
+    mbedtls_ssl_tls_prf(aTlsPrfType, aMasterSecret, aMasterSecretLen, "key expansion", randBytes, sizeof(randBytes),
+                        keyBlock, sizeof(keyBlock));
+
+    sha256.Start();
+    sha256.Update(keyBlock, kSecureTransportKeyBlockSize);
+    sha256.Finish(kek);
+
+    LogDebg("Generated KEK");
+    Get<KeyManager>().SetKek(kek.GetBytes());
+
+exit:
+    return;
+}
+
+#else
+
+int SecureTransport::HandleMbedtlsExportKeys(void                *aContext,
+                                             const unsigned char *aMasterSecret,
+                                             const unsigned char *aKeyBlock,
+                                             size_t               aMacLength,
+                                             size_t               aKeyLength,
+                                             size_t               aIvLength)
+{
+    return static_cast<SecureTransport *>(aContext)->HandleMbedtlsExportKeys(aMasterSecret, aKeyBlock, aMacLength,
+                                                                             aKeyLength, aIvLength);
+}
+
+int SecureTransport::HandleMbedtlsExportKeys(const unsigned char *aMasterSecret,
+                                             const unsigned char *aKeyBlock,
+                                             size_t               aMacLength,
+                                             size_t               aKeyLength,
+                                             size_t               aIvLength)
+{
+    OT_UNUSED_VARIABLE(aMasterSecret);
+
+    Crypto::Sha256::Hash kek;
+    Crypto::Sha256       sha256;
+
+    VerifyOrExit(mCipherSuites[0] == MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8);
+
+    sha256.Start();
+    sha256.Update(aKeyBlock, 2 * static_cast<uint16_t>(aMacLength + aKeyLength + aIvLength));
+    sha256.Finish(kek);
+
+    LogDebg("Generated KEK");
+    Get<KeyManager>().SetKek(kek.GetBytes());
+
+exit:
+    return 0;
+}
+
+#endif // (MBEDTLS_VERSION_NUMBER >= 0x03000000)
+
+void SecureTransport::HandleTimer(Timer &aTimer)
+{
+    static_cast<SecureTransport *>(static_cast<TimerMilliContext &>(aTimer).GetContext())->HandleTimer();
+}
+
+void SecureTransport::HandleTimer(void)
+{
+    if (IsStateConnectingOrConnected())
+    {
+        Process();
+    }
+    else if (IsStateCloseNotify())
+    {
+        if ((mMaxConnectionAttempts > 0) && (mRemainingConnectionAttempts == 0))
+        {
+            Close();
+            mConnectedCallback.InvokeIfSet(false);
+            mAutoCloseCallback.InvokeIfSet();
+        }
+        else
+        {
+            SetState(kStateOpen);
+            mTimer.Stop();
+            mConnectedCallback.InvokeIfSet(false);
+        }
+    }
+}
+
+void SecureTransport::Process(void)
+{
+    uint8_t buf[OPENTHREAD_CONFIG_DTLS_MAX_CONTENT_LEN];
+    bool    shouldDisconnect = false;
+    int     rval;
+
+    while (IsStateConnectingOrConnected())
+    {
+        if (IsStateConnecting())
+        {
+            rval = mbedtls_ssl_handshake(&mSsl);
+
+            if (mSsl.MBEDTLS_PRIVATE(state) == MBEDTLS_SSL_HANDSHAKE_OVER)
+            {
+                SetState(kStateConnected);
+                mConnectedCallback.InvokeIfSet(true);
+            }
+        }
+        else
+        {
+            rval = mbedtls_ssl_read(&mSsl, buf, sizeof(buf));
+        }
+
+        if (rval > 0)
+        {
+            mReceiveCallback.InvokeIfSet(buf, static_cast<uint16_t>(rval));
+        }
+        else if (rval == 0 || rval == MBEDTLS_ERR_SSL_WANT_READ || rval == MBEDTLS_ERR_SSL_WANT_WRITE)
+        {
+            break;
+        }
+        else
+        {
+            switch (rval)
+            {
+            case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY:
+                mbedtls_ssl_close_notify(&mSsl);
+                ExitNow(shouldDisconnect = true);
+                OT_UNREACHABLE_CODE(break);
+
+            case MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED:
+                break;
+
+            case MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE:
+                mbedtls_ssl_close_notify(&mSsl);
+                ExitNow(shouldDisconnect = true);
+                OT_UNREACHABLE_CODE(break);
+
+            case MBEDTLS_ERR_SSL_INVALID_MAC:
+                if (mSsl.MBEDTLS_PRIVATE(state) != MBEDTLS_SSL_HANDSHAKE_OVER)
+                {
+                    mbedtls_ssl_send_alert_message(&mSsl, MBEDTLS_SSL_ALERT_LEVEL_FATAL,
+                                                   MBEDTLS_SSL_ALERT_MSG_BAD_RECORD_MAC);
+                    ExitNow(shouldDisconnect = true);
+                }
+
+                break;
+
+            default:
+                if (mSsl.MBEDTLS_PRIVATE(state) != MBEDTLS_SSL_HANDSHAKE_OVER)
+                {
+                    mbedtls_ssl_send_alert_message(&mSsl, MBEDTLS_SSL_ALERT_LEVEL_FATAL,
+                                                   MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE);
+                    ExitNow(shouldDisconnect = true);
+                }
+
+                break;
+            }
+
+            mbedtls_ssl_session_reset(&mSsl);
+            if (mCipherSuites[0] == MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8)
+            {
+                mbedtls_ssl_set_hs_ecjpake_password(&mSsl, mPsk, mPskLength);
+            }
+            break;
+        }
+    }
+
+exit:
+
+    if (shouldDisconnect)
+    {
+        Disconnect();
+    }
+}
+
+void SecureTransport::HandleMbedtlsDebug(void *aContext, int aLevel, const char *aFile, int aLine, const char *aStr)
+{
+    static_cast<SecureTransport *>(aContext)->HandleMbedtlsDebug(aLevel, aFile, aLine, aStr);
+}
+
+void SecureTransport::HandleMbedtlsDebug(int aLevel, const char *aFile, int aLine, const char *aStr)
+{
+    OT_UNUSED_VARIABLE(aStr);
+    OT_UNUSED_VARIABLE(aFile);
+    OT_UNUSED_VARIABLE(aLine);
+
+    switch (aLevel)
+    {
+    case 1:
+        LogCrit("[%u] %s", mSocket.GetSockName().mPort, aStr);
+        break;
+
+    case 2:
+        LogWarn("[%u] %s", mSocket.GetSockName().mPort, aStr);
+        break;
+
+    case 3:
+        LogInfo("[%u] %s", mSocket.GetSockName().mPort, aStr);
+        break;
+
+    case 4:
+    default:
+        LogDebg("[%u] %s", mSocket.GetSockName().mPort, aStr);
+        break;
+    }
+}
+
+Error SecureTransport::HandleSecureTransportSend(const uint8_t   *aBuf,
+                                                 uint16_t         aLength,
+                                                 Message::SubType aMessageSubType)
+{
+    Error        error   = kErrorNone;
+    ot::Message *message = nullptr;
+
+    VerifyOrExit((message = mSocket.NewMessage()) != nullptr, error = kErrorNoBufs);
+    message->SetSubType(aMessageSubType);
+    message->SetLinkSecurityEnabled(mLayerTwoSecurity);
+
+    SuccessOrExit(error = message->AppendBytes(aBuf, aLength));
+
+    // Set message sub type in case Joiner Finalize Response is appended to the message.
+    if (aMessageSubType != Message::kSubTypeNone)
+    {
+        message->SetSubType(aMessageSubType);
+    }
+
+    if (mTransportCallback.IsSet())
+    {
+        SuccessOrExit(error = mTransportCallback.Invoke(*message, mMessageInfo));
+    }
+    else
+    {
+        SuccessOrExit(error = mSocket.SendTo(*message, mMessageInfo));
+    }
+
+exit:
+    FreeMessageOnError(message, error);
+    return error;
+}
+
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
+
+const char *SecureTransport::StateToString(State aState)
+{
+    static const char *const kStateStrings[] = {
+        "Closed",       // (0) kStateClosed
+        "Open",         // (1) kStateOpen
+        "Initializing", // (2) kStateInitializing
+        "Connecting",   // (3) kStateConnecting
+        "Connected",    // (4) kStateConnected
+        "CloseNotify",  // (5) kStateCloseNotify
+    };
+
+    static_assert(0 == kStateClosed, "kStateClosed valid is incorrect");
+    static_assert(1 == kStateOpen, "kStateOpen valid is incorrect");
+    static_assert(2 == kStateInitializing, "kStateInitializing valid is incorrect");
+    static_assert(3 == kStateConnecting, "kStateConnecting valid is incorrect");
+    static_assert(4 == kStateConnected, "kStateConnected valid is incorrect");
+    static_assert(5 == kStateCloseNotify, "kStateCloseNotify valid is incorrect");
+
+    return kStateStrings[aState];
+}
+
+#endif
+
+} // namespace MeshCoP
+} // namespace ot
+
+#endif // OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE
diff --git a/src/core/meshcop/secure_transport.hpp b/src/core/meshcop/secure_transport.hpp
new file mode 100644
index 0000000..4641ef5
--- /dev/null
+++ b/src/core/meshcop/secure_transport.hpp
@@ -0,0 +1,678 @@
+/*
+ *  Copyright (c) 2016, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ *   This file includes definitions for using mbedTLS.
+ */
+
+#ifndef SECURE_TRANSPORT_HPP_
+#define SECURE_TRANSPORT_HPP_
+
+#include "openthread-core-config.h"
+
+#ifdef OPENTHREAD_CONFIG_TLS_API_ENABLE
+#error `OPENTHREAD_CONFIG_TLS_API_ENABLE` must not be defined directly, it is determined from `COAP_SECURE_API_ENABLE` and `BLE_TCAT_ENABLE`
+#endif
+
+#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE || OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
+#define OPENTHREAD_CONFIG_TLS_API_ENABLE 1
+#else
+#define OPENTHREAD_CONFIG_TLS_API_ENABLE 0
+#endif
+
+#include <mbedtls/net_sockets.h>
+#include <mbedtls/ssl.h>
+#include <mbedtls/ssl_cookie.h>
+#include <mbedtls/version.h>
+
+#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
+#ifndef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
+#error OPENTHREAD_CONFIG_BLE_TCAT_ENABLE requires MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
+#endif
+#endif
+
+#if OPENTHREAD_CONFIG_TLS_API_ENABLE
+#if defined(MBEDTLS_BASE64_C) && defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
+#include <mbedtls/base64.h>
+#endif
+#ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
+#include <mbedtls/x509.h>
+#include <mbedtls/x509_crl.h>
+#include <mbedtls/x509_crt.h>
+#include <mbedtls/x509_csr.h>
+#endif
+#endif
+
+#include "common/callback.hpp"
+#include "common/locator.hpp"
+#include "common/log.hpp"
+#include "common/message.hpp"
+#include "common/random.hpp"
+#include "common/timer.hpp"
+#include "crypto/sha256.hpp"
+#include "meshcop/meshcop_tlvs.hpp"
+#include "net/socket.hpp"
+#include "net/udp6.hpp"
+
+namespace ot {
+
+namespace MeshCoP {
+
+class SecureTransport : public InstanceLocator
+{
+public:
+    static constexpr uint8_t kPskMaxLength = 32; ///< Maximum PSK length.
+
+    /**
+     * Initializes the SecureTransport object.
+     *
+     * @param[in]  aInstance            A reference to the OpenThread instance.
+     * @param[in]  aLayerTwoSecurity    Specifies whether to use layer two security or not.
+     * @param[in]  aDatagramTransport   Specifies if dtls of tls connection should be used.
+     *
+     */
+    explicit SecureTransport(Instance &aInstance, bool aLayerTwoSecurity, bool aDatagramTransport = true);
+
+    /**
+     * Pointer is called when a connection is established or torn down.
+     *
+     * @param[in]  aContext    A pointer to application-specific context.
+     * @param[in]  aConnected  TRUE if a connection was established, FALSE otherwise.
+     *
+     */
+    typedef void (*ConnectedHandler)(void *aContext, bool aConnected);
+
+    /**
+     * Pointer is called when data is received from the session.
+     *
+     * @param[in]  aContext  A pointer to application-specific context.
+     * @param[in]  aBuf      A pointer to the received data buffer.
+     * @param[in]  aLength   Number of bytes in the received data buffer.
+     *
+     */
+    typedef void (*ReceiveHandler)(void *aContext, uint8_t *aBuf, uint16_t aLength);
+
+    /**
+     * Pointer is called when secure CoAP server want to send encrypted message.
+     *
+     * @param[in]  aContext      A pointer to arbitrary context information.
+     * @param[in]  aMessage      A reference to the message to send.
+     * @param[in]  aMessageInfo  A reference to the message info associated with @p aMessage.
+     *
+     */
+    typedef Error (*TransportCallback)(void *aContext, ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
+
+    /**
+     * Callback to notify when the socket is automatically closed due to reaching the maximum number of connection
+     * attempts (set from `SetMaxConnectionAttempts()`).
+     *
+     * @param[in] aContext    A pointer to arbitrary context information.
+     *
+     */
+    typedef void (*AutoCloseCallback)(void *aContext);
+
+    /**
+     * Opens the socket.
+     *
+     * @param[in]  aReceiveHandler      A pointer to a function that is called to receive payload.
+     * @param[in]  aConnectedHandler    A pointer to a function that is called when connected or disconnected.
+     * @param[in]  aContext             A pointer to arbitrary context information.
+     *
+     * @retval kErrorNone     Successfully opened the socket.
+     * @retval kErrorAlready  The connection is already open.
+     *
+     */
+    Error Open(ReceiveHandler aReceiveHandler, ConnectedHandler aConnectedHandler, void *aContext);
+
+    /**
+     * Sets the maximum number of allowed connection requests before socket is automatically closed.
+     *
+     * This method can be called when socket is closed. Otherwise `kErrorInvalidSatet` is returned.
+     *
+     * If @p aMaxAttempts is zero, no limit is applied and connections are allowed until the socket is closed. This is
+     * the default behavior if `SetMaxConnectionAttempts()` is not called.
+     *
+     * @param[in] aMaxAttempts    Maximum number of allowed connection attempts.
+     * @param[in] aCallback       Callback to notify when max number of attempts has reached and socket is closed.
+     * @param[in] aContext        A pointer to arbitrary context to use with `AutoCloseCallback`.
+     *
+     * @retval kErrorNone          Successfully set the maximum allowed connection attempts and callback.
+     * @retval kErrorInvalidState  Socket is not closed.
+     *
+     */
+    Error SetMaxConnectionAttempts(uint16_t aMaxAttempts, AutoCloseCallback aCallback, void *aContext);
+
+    /**
+     * Binds this DTLS to a UDP port.
+     *
+     * @param[in]  aPort              The port to bind.
+     *
+     * @retval kErrorNone           Successfully bound the socket.
+     * @retval kErrorInvalidState   The socket is not open.
+     * @retval kErrorAlready        Already bound.
+     *
+     */
+    Error Bind(uint16_t aPort);
+
+    /**
+     * Gets the UDP port of this session.
+     *
+     * @returns  UDP port number.
+     *
+     */
+    uint16_t GetUdpPort(void) const;
+
+    /**
+     * Binds with a transport callback.
+     *
+     * @param[in]  aCallback  A pointer to a function for sending messages.
+     * @param[in]  aContext   A pointer to arbitrary context information.
+     *
+     * @retval kErrorNone           Successfully bound the socket.
+     * @retval kErrorInvalidState   The socket is not open.
+     * @retval kErrorAlready        Already bound.
+     *
+     */
+    Error Bind(TransportCallback aCallback, void *aContext);
+
+    /**
+     * Establishes a secure session.
+     *
+     * For CoAP Secure API do first:
+     * Set X509 Pk and Cert for use DTLS mode ECDHE ECDSA with AES 128 CCM 8 or
+     * set PreShared Key for use DTLS mode PSK with AES 128 CCM 8.
+     *
+     * @param[in]  aSockAddr               A reference to the remote sockaddr.
+     *
+     * @retval kErrorNone          Successfully started handshake.
+     * @retval kErrorInvalidState  The socket is not open.
+     *
+     */
+    Error Connect(const Ip6::SockAddr &aSockAddr);
+
+    /**
+     * Indicates whether or not the session is active.
+     *
+     * @retval TRUE  If session is active.
+     * @retval FALSE If session is not active.
+     *
+     */
+    bool IsConnectionActive(void) const { return mState >= kStateConnecting; }
+
+    /**
+     * Indicates whether or not the session is connected.
+     *
+     * @retval TRUE   The session is connected.
+     * @retval FALSE  The session is not connected.
+     *
+     */
+    bool IsConnected(void) const { return mState == kStateConnected; }
+
+    /**
+     * Indicates whether or not the session is closed.
+     *
+     * @retval TRUE   The session is closed.
+     * @retval FALSE  The session is not closed.
+     *
+     */
+    bool IsClosed(void) const { return mState == kStateClosed; }
+
+    /**
+     * Disconnects the session.
+     *
+     */
+    void Disconnect(void);
+
+    /**
+     * Closes the socket.
+     *
+     */
+    void Close(void);
+
+    /**
+     * Sets the PSK.
+     *
+     * @param[in]  aPsk  A pointer to the PSK.
+     *
+     * @retval kErrorNone          Successfully set the PSK.
+     * @retval kErrorInvalidArgs   The PSK is invalid.
+     *
+     */
+    Error SetPsk(const uint8_t *aPsk, uint8_t aPskLength);
+
+#if OPENTHREAD_CONFIG_TLS_API_ENABLE
+#ifdef MBEDTLS_KEY_EXCHANGE_PSK_ENABLED
+    /**
+     * Sets the Pre-Shared Key (PSK) for sessions-
+     * identified by a PSK.
+     *
+     * DTLS mode "PSK with AES 128 CCM 8" for Application CoAPS.
+     *
+     * @param[in]  aPsk          A pointer to the PSK.
+     * @param[in]  aPskLength    The PSK char length.
+     * @param[in]  aPskIdentity  The Identity Name for the PSK.
+     * @param[in]  aPskIdLength  The PSK Identity Length.
+     *
+     * @retval kErrorNone  Successfully set the PSK.
+     *
+     */
+    void SetPreSharedKey(const uint8_t *aPsk, uint16_t aPskLength, const uint8_t *aPskIdentity, uint16_t aPskIdLength);
+
+#endif
+
+#ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
+    /**
+     * Sets a reference to the own x509 certificate with corresponding private key.
+     *
+     * DTLS mode "ECDHE ECDSA with AES 128 CCM 8" for Application CoAPS.
+     *
+     * @param[in]  aX509Certificate   A pointer to the PEM formatted X509 certificate.
+     * @param[in]  aX509CertLength    The length of certificate.
+     * @param[in]  aPrivateKey        A pointer to the PEM formatted private key.
+     * @param[in]  aPrivateKeyLength  The length of the private key.
+     *
+     */
+    void SetCertificate(const uint8_t *aX509Certificate,
+                        uint32_t       aX509CertLength,
+                        const uint8_t *aPrivateKey,
+                        uint32_t       aPrivateKeyLength);
+
+    /**
+     * Sets the trusted top level CAs. It is needed for validate the
+     * certificate of the peer.
+     *
+     * DTLS mode "ECDHE ECDSA with AES 128 CCM 8" for Application CoAPS.
+     *
+     * @param[in]  aX509CaCertificateChain  A pointer to the PEM formatted X509 CA chain.
+     * @param[in]  aX509CaCertChainLength   The length of chain.
+     *
+     */
+    void SetCaCertificateChain(const uint8_t *aX509CaCertificateChain, uint32_t aX509CaCertChainLength);
+#endif // MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
+
+#if defined(MBEDTLS_BASE64_C) && defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
+    /**
+     * Returns the peer x509 certificate base64 encoded.
+     *
+     * DTLS mode "ECDHE ECDSA with AES 128 CCM 8" for Application CoAPS.
+     *
+     * @param[out]  aPeerCert        A pointer to the base64 encoded certificate buffer.
+     * @param[out]  aCertLength      The length of the base64 encoded peer certificate.
+     * @param[in]   aCertBufferSize  The buffer size of aPeerCert.
+     *
+     * @retval kErrorInvalidState   Not connected yet.
+     * @retval kErrorNone           Successfully get the peer certificate.
+     * @retval kErrorNoBufs         Can't allocate memory for certificate.
+     *
+     */
+    Error GetPeerCertificateBase64(unsigned char *aPeerCert, size_t *aCertLength, size_t aCertBufferSize);
+#endif // defined(MBEDTLS_BASE64_C) && defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
+
+#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
+    /**
+     * Returns an attribute value identified by its OID from the subject
+     * of the peer x509 certificate. The peer OID is provided in binary format.
+     * The attribute length is set if the attribute was successfully read or zero
+     * if unsuccessful. The ASN.1 type as is set as defineded in the ITU-T X.690 standard
+     * if the attribute was successfully read.
+     *
+     * @param[in]     aOid                A pointer to the OID to be found.
+     * @param[in]     aOidLength          The length of the OID.
+     * @param[out]    aAttributeBuffer    A pointer to the attribute buffer.
+     * @param[in,out] aAttributeLength    On input, the size the max size of @p aAttributeBuffer.
+     *                                    On output, the length of the attribute written to the buffer.
+     * @param[out]    aAsn1Type           A pointer to the ASN.1 type of the attribute written to the buffer.
+     *
+     * @retval kErrorInvalidState   Not connected yet.
+     * @retval kErrorInvalidArgs    Invalid attribute length.
+     * @retval kErrorNone           Successfully read attribute.
+     * @retval kErrorNoBufs         Insufficient memory for storing the attribute value.
+     *
+     */
+    Error GetPeerSubjectAttributeByOid(const char *aOid,
+                                       size_t      aOidLength,
+                                       uint8_t    *aAttributeBuffer,
+                                       size_t     *aAttributeLength,
+                                       int        *aAsn1Type);
+
+    /**
+     * Returns an attribute value for the OID 1.3.6.1.4.1.44970.x from the v3 extensions of
+     * the peer x509 certificate, where the last digit x is set to aThreadOidDescriptor.
+     * The attribute length is set if the attribute was successfully read or zero if unsuccessful.
+     * Requires a connection to be active.
+     *
+     * @param[in]      aThreadOidDescriptor  The last digit of the Thread attribute OID.
+     * @param[out]     aAttributeBuffer      A pointer to the attribute buffer.
+     * @param[in,out]  aAttributeLength      On input, the size the max size of @p aAttributeBuffer.
+     *                                           On output, the length of the attribute written to the buffer.
+     *
+     * @retval kErrorNone             Successfully read attribute.
+     * @retval kErrorInvalidArgs      Invalid attribute length.
+     * @retval kErrorNotFound         The requested attribute was not found.
+     * @retval kErrorNoBufs           Insufficient memory for storing the attribute value.
+     * @retval kErrorInvalidState     Not connected yet.
+     * @retval kErrorNotImplemented   The value of aThreadOidDescriptor is >127.
+     * @retval kErrorParse            The certificate extensions could not be parsed.
+     *
+     */
+    Error GetThreadAttributeFromPeerCertificate(int      aThreadOidDescriptor,
+                                                uint8_t *aAttributeBuffer,
+                                                size_t  *aAttributeLength);
+#endif // defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
+
+    /**
+     * Returns an attribute value for the OID 1.3.6.1.4.1.44970.x from the v3 extensions of
+     * the own x509 certificate, where the last digit x is set to aThreadOidDescriptor.
+     * The attribute length is set if the attribute was successfully read or zero if unsuccessful.
+     * Requires a connection to be active.
+     *
+     * @param[in]      aThreadOidDescriptor  The last digit of the Thread attribute OID.
+     * @param[out]     aAttributeBuffer      A pointer to the attribute buffer.
+     * @param[in,out]  aAttributeLength      On input, the size the max size of @p aAttributeBuffer.
+     *                                       On output, the length of the attribute written to the buffer.
+     *
+     * @retval kErrorNone             Successfully read attribute.
+     * @retval kErrorInvalidArgs      Invalid attribute length.
+     * @retval kErrorNotFound         The requested attribute was not found.
+     * @retval kErrorNoBufs           Insufficient memory for storing the attribute value.
+     * @retval kErrorInvalidState     Not connected yet.
+     * @retval kErrorNotImplemented   The value of aThreadOidDescriptor is >127.
+     * @retval kErrorParse            The certificate extensions could not be parsed.
+     *
+     */
+    Error GetThreadAttributeFromOwnCertificate(int      aThreadOidDescriptor,
+                                               uint8_t *aAttributeBuffer,
+                                               size_t  *aAttributeLength);
+
+    /**
+     * Set the authentication mode for a connection.
+     *
+     * Disable or enable the verification of peer certificate.
+     * Must called before start.
+     *
+     * @param[in]  aVerifyPeerCertificate  true, if the peer certificate should verify.
+     *
+     */
+    void SetSslAuthMode(bool aVerifyPeerCertificate) { mVerifyPeerCertificate = aVerifyPeerCertificate; }
+#endif // OPENTHREAD_CONFIG_TLS_API_ENABLE
+
+#ifdef MBEDTLS_SSL_SRV_C
+    /**
+     * Sets the Client ID used for generating the Hello Cookie.
+     *
+     * @param[in]  aClientId  A pointer to the Client ID.
+     * @param[in]  aLength    Number of bytes in the Client ID.
+     *
+     * @retval kErrorNone  Successfully set the Client ID.
+     *
+     */
+    Error SetClientId(const uint8_t *aClientId, uint8_t aLength);
+#endif
+
+    /**
+     * Sends data within the session.
+     *
+     * @param[in]  aMessage  A message to send via connection.
+     * @param[in]  aLength   Number of bytes in the data buffer.
+     *
+     * @retval kErrorNone     Successfully sent the data via the session.
+     * @retval kErrorNoBufs   A message is too long.
+     *
+     */
+    Error Send(Message &aMessage, uint16_t aLength);
+
+    /**
+     * Provides a received message to the SecureTransport object.
+     *
+     * @param[in]  aMessage  A reference to the message.
+     *
+     */
+    void Receive(Message &aMessage);
+
+    /**
+     * Sets the default message sub-type that will be used for all messages without defined
+     * sub-type.
+     *
+     * @param[in]  aMessageSubType  The default message sub-type.
+     *
+     */
+    void SetDefaultMessageSubType(Message::SubType aMessageSubType) { mMessageDefaultSubType = aMessageSubType; }
+
+    /**
+     * Returns the session's peer address.
+     *
+     * @return session's message info.
+     *
+     */
+    const Ip6::MessageInfo &GetMessageInfo(void) const { return mMessageInfo; }
+
+    /**
+     * Checks and handles a received message provided to the SecureTransport object. If checks based on
+     * the message info and current connection state pass, the message is processed.
+     *
+     * @param[in]  aMessage  A reference to the message to receive.
+     * @param[in]  aMessageInfo A reference to the message info associated with @p aMessage.
+     *
+     */
+    void HandleReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
+
+private:
+    enum State : uint8_t
+    {
+        kStateClosed,       // UDP socket is closed.
+        kStateOpen,         // UDP socket is open.
+        kStateInitializing, // The service is initializing.
+        kStateConnecting,   // The service is establishing a connection.
+        kStateConnected,    // The service has a connection established.
+        kStateCloseNotify,  // The service is closing a connection.
+    };
+
+    static constexpr uint32_t kGuardTimeNewConnectionMilli = 2000;
+
+#if !OPENTHREAD_CONFIG_TLS_API_ENABLE
+    static constexpr uint16_t kApplicationDataMaxLength = 1152;
+#else
+    static constexpr uint16_t         kApplicationDataMaxLength = OPENTHREAD_CONFIG_DTLS_APPLICATION_DATA_MAX_LENGTH;
+#endif
+
+    static constexpr size_t kSecureTransportKeyBlockSize     = 40;
+    static constexpr size_t kSecureTransportRandomBufferSize = 32;
+
+    bool IsStateClosed(void) const { return mState == kStateClosed; }
+    bool IsStateOpen(void) const { return mState == kStateOpen; }
+    bool IsStateInitializing(void) const { return mState == kStateInitializing; }
+    bool IsStateConnecting(void) const { return mState == kStateConnecting; }
+    bool IsStateConnected(void) const { return mState == kStateConnected; }
+    bool IsStateCloseNotify(void) const { return mState == kStateCloseNotify; }
+    bool IsStateConnectingOrConnected(void) const { return mState == kStateConnecting || mState == kStateConnected; }
+    void SetState(State aState);
+
+    void  FreeMbedtls(void);
+    Error Setup(bool aClient);
+
+#if OPENTHREAD_CONFIG_TLS_API_ENABLE
+    /**
+     * Set keys and/or certificates for dtls session dependent of used cipher suite.
+     *
+     * @retval mbedtls error, 0 if successfully.
+     *
+     */
+    int SetApplicationSecureKeys(void);
+
+    Error GetThreadAttributeFromCertificate(const mbedtls_x509_crt *aCert,
+                                            int                     aThreadOidDescriptor,
+                                            uint8_t                *aAttributeBuffer,
+                                            size_t                 *aAttributeLength);
+#endif
+
+    static void HandleMbedtlsDebug(void *aContext, int aLevel, const char *aFile, int aLine, const char *aStr);
+    void        HandleMbedtlsDebug(int aLevel, const char *aFile, int aLine, const char *aStr);
+
+    static int HandleMbedtlsGetTimer(void *aContext);
+    int        HandleMbedtlsGetTimer(void);
+
+    static void HandleMbedtlsSetTimer(void *aContext, uint32_t aIntermediate, uint32_t aFinish);
+    void        HandleMbedtlsSetTimer(uint32_t aIntermediate, uint32_t aFinish);
+
+    static int HandleMbedtlsReceive(void *aContext, unsigned char *aBuf, size_t aLength);
+    int        HandleMbedtlsReceive(unsigned char *aBuf, size_t aLength);
+
+    static int HandleMbedtlsTransmit(void *aContext, const unsigned char *aBuf, size_t aLength);
+    int        HandleMbedtlsTransmit(const unsigned char *aBuf, size_t aLength);
+
+#ifdef MBEDTLS_SSL_EXPORT_KEYS
+#if (MBEDTLS_VERSION_NUMBER >= 0x03000000)
+
+    static void HandleMbedtlsExportKeys(void                       *aContext,
+                                        mbedtls_ssl_key_export_type aType,
+                                        const unsigned char        *aMasterSecret,
+                                        size_t                      aMasterSecretLen,
+                                        const unsigned char         aClientRandom[32],
+                                        const unsigned char         aServerRandom[32],
+                                        mbedtls_tls_prf_types       aTlsPrfType);
+
+    void HandleMbedtlsExportKeys(mbedtls_ssl_key_export_type aType,
+                                 const unsigned char        *aMasterSecret,
+                                 size_t                      aMasterSecretLen,
+                                 const unsigned char         aClientRandom[32],
+                                 const unsigned char         aServerRandom[32],
+                                 mbedtls_tls_prf_types       aTlsPrfType);
+
+#else
+
+    static int       HandleMbedtlsExportKeys(void                *aContext,
+                                             const unsigned char *aMasterSecret,
+                                             const unsigned char *aKeyBlock,
+                                             size_t               aMacLength,
+                                             size_t               aKeyLength,
+                                             size_t               aIvLength);
+    int              HandleMbedtlsExportKeys(const unsigned char *aMasterSecret,
+                                             const unsigned char *aKeyBlock,
+                                             size_t               aMacLength,
+                                             size_t               aKeyLength,
+                                             size_t               aIvLength);
+
+#endif // (MBEDTLS_VERSION_NUMBER >= 0x03000000)
+#endif // MBEDTLS_SSL_EXPORT_KEYS
+
+    static void HandleTimer(Timer &aTimer);
+    void        HandleTimer(void);
+
+    static void HandleReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo);
+
+    void  HandleReceive(const uint8_t *aBuf, uint16_t aLength);
+    Error HandleSecureTransportSend(const uint8_t *aBuf, uint16_t aLength, Message::SubType aMessageSubType);
+
+    void Process(void);
+
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
+    static const char *StateToString(State aState);
+#endif
+
+    State mState;
+
+    int     mCipherSuites[2];
+    uint8_t mPsk[kPskMaxLength];
+    uint8_t mPskLength;
+
+#if (MBEDTLS_VERSION_NUMBER >= 0x03010000)
+    static const uint16_t sGroups[];
+#else
+    static const mbedtls_ecp_group_id sCurves[];
+#endif
+
+#if defined(MBEDTLS_KEY_EXCHANGE__WITH_CERT__ENABLED) || defined(MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED)
+#if (MBEDTLS_VERSION_NUMBER >= 0x03020000)
+    static const uint16_t sSignatures[];
+#else
+    static const int sHashes[];
+#endif
+#endif
+
+#if OPENTHREAD_CONFIG_TLS_API_ENABLE
+#ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
+    const uint8_t     *mCaChainSrc;
+    uint32_t           mCaChainLength;
+    const uint8_t     *mOwnCertSrc;
+    uint32_t           mOwnCertLength;
+    const uint8_t     *mPrivateKeySrc;
+    uint32_t           mPrivateKeyLength;
+    mbedtls_x509_crt   mCaChain;
+    mbedtls_x509_crt   mOwnCert;
+    mbedtls_pk_context mPrivateKey;
+#endif
+#ifdef MBEDTLS_KEY_EXCHANGE_PSK_ENABLED
+    const uint8_t *mPreSharedKey;
+    const uint8_t *mPreSharedKeyIdentity;
+    uint16_t       mPreSharedKeyLength;
+    uint16_t       mPreSharedKeyIdLength;
+#endif
+#endif
+
+    bool mVerifyPeerCertificate;
+
+    mbedtls_ssl_context mSsl;
+    mbedtls_ssl_config  mConf;
+
+#ifdef MBEDTLS_SSL_COOKIE_C
+    mbedtls_ssl_cookie_ctx mCookieCtx;
+#endif
+
+    TimerMilliContext mTimer;
+
+    TimeMilli mTimerIntermediate;
+    bool      mTimerSet : 1;
+
+    bool mLayerTwoSecurity : 1;
+    bool mDatagramTransport : 1;
+
+    uint16_t                    mMaxConnectionAttempts;
+    uint16_t                    mRemainingConnectionAttempts;
+    Callback<AutoCloseCallback> mAutoCloseCallback;
+
+    Message *mReceiveMessage;
+
+    Callback<ConnectedHandler> mConnectedCallback;
+    Callback<ReceiveHandler>   mReceiveCallback;
+    void                      *mContext;
+
+    Ip6::MessageInfo mMessageInfo;
+    Ip6::Udp::Socket mSocket;
+
+    Callback<TransportCallback> mTransportCallback;
+    void                       *mTransportContext;
+
+    Message::SubType mMessageSubType;
+    Message::SubType mMessageDefaultSubType;
+};
+
+} // namespace MeshCoP
+} // namespace ot
+
+#endif // SECURE_TRANSPORT_HPP_
diff --git a/src/core/meshcop/tcat_agent.cpp b/src/core/meshcop/tcat_agent.cpp
new file mode 100644
index 0000000..f8e4596
--- /dev/null
+++ b/src/core/meshcop/tcat_agent.cpp
@@ -0,0 +1,506 @@
+/*
+ *  Copyright (c) 2023, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ *   This file implements the TCAT Agent service.
+ */
+
+#include "tcat_agent.hpp"
+
+#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
+
+#include <stdio.h>
+
+#include "common/array.hpp"
+#include "common/code_utils.hpp"
+#include "common/debug.hpp"
+#include "common/encoding.hpp"
+#include "common/locator_getters.hpp"
+#include "common/string.hpp"
+#include "instance/instance.hpp"
+#include "radio/radio.hpp"
+#include "thread/thread_netif.hpp"
+#include "thread/uri_paths.hpp"
+#include "utils/otns.hpp"
+
+namespace ot {
+namespace MeshCoP {
+
+RegisterLogModule("TcatAgent");
+
+bool TcatAgent::VendorInfo::IsValid(void) const
+{
+    return mProvisioningUrl == nullptr || IsValidUtf8String(mProvisioningUrl) || mPskdString != nullptr;
+}
+
+TcatAgent::TcatAgent(Instance &aInstance)
+    : InstanceLocator(aInstance)
+    , mVendorInfo(nullptr)
+    , mCurrentApplicationProtocol(kApplicationProtocolNone)
+    , mState(kStateDisabled)
+    , mAlreadyCommissioned(false)
+    , mCommissionerHasNetworkName(false)
+    , mCommissionerHasDomainName(false)
+    , mCommissionerHasExtendedPanId(false)
+{
+    mJoinerPskd.Clear();
+    mCurrentServiceName[0] = 0;
+}
+
+Error TcatAgent::Start(const TcatAgent::VendorInfo &aVendorInfo,
+                       AppDataReceiveCallback       aAppDataReceiveCallback,
+                       JoinCallback                 aHandler,
+                       void                        *aContext)
+{
+    Error error = kErrorNone;
+
+    LogInfo("Starting");
+
+    VerifyOrExit(aVendorInfo.IsValid(), error = kErrorInvalidArgs);
+    SuccessOrExit(error = mJoinerPskd.SetFrom(aVendorInfo.mPskdString));
+
+    mAppDataReceiveCallback.Set(aAppDataReceiveCallback, aContext);
+    mJoinCallback.Set(aHandler, aContext);
+
+    mVendorInfo                 = &aVendorInfo;
+    mCurrentApplicationProtocol = kApplicationProtocolNone;
+    mState                      = kStateEnabled;
+    mAlreadyCommissioned        = false;
+
+exit:
+    LogWarnOnError(error, "start TCAT agent");
+    return error;
+}
+
+void TcatAgent::Stop(void)
+{
+    mCurrentApplicationProtocol = kApplicationProtocolNone;
+    mState                      = kStateDisabled;
+    mAlreadyCommissioned        = false;
+    mAppDataReceiveCallback.Clear();
+    mJoinCallback.Clear();
+    LogInfo("TCAT agent stopped");
+}
+
+Error TcatAgent::Connected(MeshCoP::SecureTransport &aTlsContext)
+{
+    size_t len;
+    Error  error;
+
+    VerifyOrExit(IsEnabled(), error = kErrorInvalidState);
+    len = sizeof(mCommissionerAuthorizationField);
+    SuccessOrExit(
+        error = aTlsContext.GetThreadAttributeFromPeerCertificate(
+            kCertificateAuthorizationField, reinterpret_cast<uint8_t *>(&mCommissionerAuthorizationField), &len));
+    VerifyOrExit(len == sizeof(mCommissionerAuthorizationField), error = kErrorParse);
+    VerifyOrExit((mCommissionerAuthorizationField.mHeader & kCommissionerFlag) == 1, error = kErrorParse);
+
+    len = sizeof(mDeviceAuthorizationField);
+    SuccessOrExit(error = aTlsContext.GetThreadAttributeFromOwnCertificate(
+                      kCertificateAuthorizationField, reinterpret_cast<uint8_t *>(&mDeviceAuthorizationField), &len));
+    VerifyOrExit(len == sizeof(mDeviceAuthorizationField), error = kErrorParse);
+    VerifyOrExit((mDeviceAuthorizationField.mHeader & kCommissionerFlag) == 0, error = kErrorParse);
+
+    mCommissionerHasDomainName    = false;
+    mCommissionerHasNetworkName   = false;
+    mCommissionerHasExtendedPanId = false;
+
+    len = sizeof(mCommissionerDomainName) - 1;
+    if (aTlsContext.GetThreadAttributeFromPeerCertificate(
+            kCertificateDomainName, reinterpret_cast<uint8_t *>(&mCommissionerDomainName), &len) == kErrorNone)
+    {
+        mCommissionerDomainName.m8[len] = '\0';
+        mCommissionerHasDomainName      = true;
+    }
+
+    len = sizeof(mCommissionerNetworkName) - 1;
+    if (aTlsContext.GetThreadAttributeFromPeerCertificate(
+            kCertificateNetworkName, reinterpret_cast<uint8_t *>(&mCommissionerNetworkName), &len) == kErrorNone)
+    {
+        mCommissionerNetworkName.m8[len] = '\0';
+        mCommissionerHasNetworkName      = true;
+    }
+
+    len = sizeof(mCommissionerExtendedPanId);
+    if (aTlsContext.GetThreadAttributeFromPeerCertificate(
+            kCertificateExtendedPanId, reinterpret_cast<uint8_t *>(&mCommissionerExtendedPanId), &len) == kErrorNone)
+    {
+        if (len == sizeof(mCommissionerExtendedPanId))
+        {
+            mCommissionerHasExtendedPanId = true;
+        }
+    }
+
+    mCurrentApplicationProtocol = kApplicationProtocolNone;
+    mCurrentServiceName[0]      = 0;
+    mState                      = kStateConnected;
+    mAlreadyCommissioned        = Get<ActiveDatasetManager>().IsCommissioned();
+    LogInfo("TCAT agent connected");
+
+exit:
+    return error;
+}
+
+void TcatAgent::Disconnected(void)
+{
+    mCurrentApplicationProtocol = kApplicationProtocolNone;
+    mAlreadyCommissioned        = false;
+
+    if (mState != kStateDisabled)
+    {
+        mState = kStateEnabled;
+    }
+
+    LogInfo("TCAT agent disconnected");
+}
+
+bool TcatAgent::CheckCommandClassAuthorizationFlags(CommandClassFlags aCommissionerCommandClassFlags,
+                                                    CommandClassFlags aDeviceCommandClassFlags,
+                                                    Dataset          *aDataset) const
+{
+    bool authorized                     = false;
+    bool additionalDeviceRequirementMet = false;
+    bool domainNamesMatch               = false;
+    bool networkNamesMatch              = false;
+    bool extendedPanIdsMatch            = false;
+
+    VerifyOrExit(IsConnected());
+    VerifyOrExit(aCommissionerCommandClassFlags & kAccessFlag);
+
+    if (aDeviceCommandClassFlags & kAccessFlag)
+    {
+        additionalDeviceRequirementMet = true;
+    }
+
+    if (aDeviceCommandClassFlags & kPskdFlag)
+    {
+        additionalDeviceRequirementMet = true;
+    }
+
+    if (aDeviceCommandClassFlags & kPskcFlag)
+    {
+        additionalDeviceRequirementMet = true;
+    }
+
+    if (mCommissionerHasNetworkName || mCommissionerHasExtendedPanId)
+    {
+        Dataset::Info datasetInfo;
+        Error         datasetError = kErrorNone;
+
+        if (aDataset == nullptr)
+        {
+            datasetError = Get<ActiveDatasetManager>().Read(datasetInfo);
+        }
+        else
+        {
+            aDataset->ConvertTo(datasetInfo);
+        }
+
+        if (datasetError == kErrorNone)
+        {
+            if (datasetInfo.IsNetworkNamePresent() && mCommissionerHasNetworkName &&
+                (datasetInfo.GetNetworkName() == mCommissionerNetworkName))
+            {
+                networkNamesMatch = true;
+            }
+
+            if (datasetInfo.IsExtendedPanIdPresent() && mCommissionerHasExtendedPanId &&
+                (datasetInfo.GetExtendedPanId() == mCommissionerExtendedPanId))
+            {
+                extendedPanIdsMatch = true;
+            }
+        }
+    }
+
+    if (!networkNamesMatch)
+    {
+        VerifyOrExit((aCommissionerCommandClassFlags & kNetworkNameFlag) == 0);
+    }
+    else if (aDeviceCommandClassFlags & kNetworkNameFlag)
+    {
+        additionalDeviceRequirementMet = true;
+    }
+
+    if (!extendedPanIdsMatch)
+    {
+        VerifyOrExit((aCommissionerCommandClassFlags & kExtendedPanIdFlag) == 0);
+    }
+    else if (aDeviceCommandClassFlags & kExtendedPanIdFlag)
+    {
+        additionalDeviceRequirementMet = true;
+    }
+
+#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
+    VerifyOrExit((aCommissionerCommandClassFlags & kThreadDomainFlag) == 0);
+#endif
+
+    if (!domainNamesMatch)
+    {
+        VerifyOrExit((aCommissionerCommandClassFlags & kThreadDomainFlag) == 0);
+    }
+    else if (aDeviceCommandClassFlags & kThreadDomainFlag)
+    {
+        additionalDeviceRequirementMet = true;
+    }
+
+    if (additionalDeviceRequirementMet)
+    {
+        authorized = true;
+    }
+
+exit:
+    return authorized;
+}
+
+bool TcatAgent::IsCommandClassAuthorized(CommandClass aCommandClass) const
+{
+    bool authorized = false;
+
+    switch (aCommandClass)
+    {
+    case kGeneral:
+        authorized = true;
+        break;
+
+    case kCommissioning:
+        authorized = CheckCommandClassAuthorizationFlags(mCommissionerAuthorizationField.mCommissioningFlags,
+                                                         mDeviceAuthorizationField.mCommissioningFlags, nullptr);
+        break;
+
+    case kExtraction:
+        authorized = CheckCommandClassAuthorizationFlags(mCommissionerAuthorizationField.mExtractionFlags,
+                                                         mDeviceAuthorizationField.mExtractionFlags, nullptr);
+        break;
+
+    case kTlvDecommissioning:
+        authorized = CheckCommandClassAuthorizationFlags(mCommissionerAuthorizationField.mDecommissioningFlags,
+                                                         mDeviceAuthorizationField.mDecommissioningFlags, nullptr);
+        break;
+
+    case kApplication:
+        authorized = CheckCommandClassAuthorizationFlags(mCommissionerAuthorizationField.mApplicationFlags,
+                                                         mDeviceAuthorizationField.mApplicationFlags, nullptr);
+        break;
+
+    case kInvalid:
+        authorized = false;
+        break;
+    }
+
+    return authorized;
+}
+
+TcatAgent::CommandClass TcatAgent::GetCommandClass(uint8_t aTlvType) const
+{
+    static constexpr int kGeneralTlvs            = 0x1F;
+    static constexpr int kCommissioningTlvs      = 0x3F;
+    static constexpr int kExtractionTlvs         = 0x5F;
+    static constexpr int kTlvDecommissioningTlvs = 0x7F;
+    static constexpr int kApplicationTlvs        = 0x9F;
+
+    if (aTlvType <= kGeneralTlvs)
+    {
+        return kGeneral;
+    }
+    else if (aTlvType <= kCommissioningTlvs)
+    {
+        return kCommissioning;
+    }
+    else if (aTlvType <= kExtractionTlvs)
+    {
+        return kExtraction;
+    }
+    else if (aTlvType <= kTlvDecommissioningTlvs)
+    {
+        return kTlvDecommissioning;
+    }
+    else if (aTlvType <= kApplicationTlvs)
+    {
+        return kApplication;
+    }
+    else
+    {
+        return kInvalid;
+    }
+}
+
+bool TcatAgent::CanProcessTlv(uint8_t aTlvType) const
+{
+    CommandClass tlvCommandClass = GetCommandClass(aTlvType);
+    return IsCommandClassAuthorized(tlvCommandClass);
+}
+
+Error TcatAgent::HandleSingleTlv(const Message &aIncommingMessage, Message &aOutgoingMessage)
+{
+    Error    error = kErrorParse;
+    ot::Tlv  tlv;
+    uint16_t offset = aIncommingMessage.GetOffset();
+    uint16_t length;
+    bool     response = false;
+
+    VerifyOrExit(IsConnected(), error = kErrorInvalidState);
+    SuccessOrExit(error = aIncommingMessage.Read(offset, tlv));
+
+    if (tlv.IsExtended())
+    {
+        ot::ExtendedTlv extTlv;
+        SuccessOrExit(error = aIncommingMessage.Read(offset, extTlv));
+        length = extTlv.GetLength();
+        offset += sizeof(ot::ExtendedTlv);
+    }
+    else
+    {
+        length = tlv.GetLength();
+        offset += sizeof(ot::Tlv);
+    }
+
+    if (!CanProcessTlv(tlv.GetType()))
+    {
+        error = kErrorRejected;
+    }
+    else
+    {
+        switch (tlv.GetType())
+        {
+        case kTlvDisconnect:
+            error = kErrorAbort;
+            break;
+
+        case kTlvSetActiveOperationalDataset:
+            error = HandleSetActiveOperationalDataset(aIncommingMessage, offset, length);
+            break;
+
+        case kTlvStartThreadInterface:
+            error = HandleStartThreadInterface();
+            break;
+
+        case kTlvStopThreadInterface:
+            error = otThreadSetEnabled(&GetInstance(), false);
+            break;
+
+        case kTlvSendApplicationData:
+            LogInfo("Application data len:%d, offset:%d", length, offset);
+            mAppDataReceiveCallback.InvokeIfSet(&GetInstance(), &aIncommingMessage, offset,
+                                                MapEnum(mCurrentApplicationProtocol), mCurrentServiceName);
+            response = true;
+            error    = kErrorNone;
+            break;
+
+        default:
+            error = kErrorInvalidCommand;
+        }
+    }
+
+    if (!response)
+    {
+        StatusCode statusCode;
+
+        switch (error)
+        {
+        case kErrorNone:
+            statusCode = kStatusSuccess;
+            break;
+
+        case kErrorInvalidState:
+            statusCode = kStatusUndefined;
+            break;
+
+        case kErrorParse:
+            statusCode = kStatusParseError;
+            break;
+
+        case kErrorInvalidCommand:
+            statusCode = kStatusUnsupported;
+            break;
+
+        case kErrorRejected:
+            statusCode = kStatusUnauthorized;
+            break;
+
+        case kErrorNotImplemented:
+            statusCode = kStatusUnsupported;
+            break;
+
+        default:
+            statusCode = kStatusGeneralError;
+            break;
+        }
+
+        SuccessOrExit(error = ot::Tlv::Append<ResponseWithStatusTlv>(aOutgoingMessage, statusCode));
+    }
+
+exit:
+    return error;
+}
+
+Error TcatAgent::HandleSetActiveOperationalDataset(const Message &aIncommingMessage, uint16_t aOffset, uint16_t aLength)
+{
+    Dataset       dataset;
+    Dataset::Tlvs datasetTlvs;
+    Error         error;
+
+    SuccessOrExit(error = dataset.ReadFromMessage(aIncommingMessage, aOffset, aLength));
+
+    if (!CheckCommandClassAuthorizationFlags(mCommissionerAuthorizationField.mApplicationFlags,
+                                             mDeviceAuthorizationField.mApplicationFlags, &dataset))
+    {
+        error = kErrorRejected;
+        ExitNow();
+    }
+
+    dataset.ConvertTo(datasetTlvs);
+    error = Get<ActiveDatasetManager>().Save(datasetTlvs);
+
+exit:
+    return error;
+}
+
+Error TcatAgent::HandleStartThreadInterface(void)
+{
+    Error         error;
+    Dataset::Info datasetInfo;
+
+    VerifyOrExit(Get<ActiveDatasetManager>().Read(datasetInfo) == kErrorNone, error = kErrorInvalidState);
+    VerifyOrExit(datasetInfo.IsNetworkKeyPresent(), error = kErrorInvalidState);
+
+#if OPENTHREAD_CONFIG_LINK_RAW_ENABLE
+    VerifyOrExit(!Get<Mac::LinkRaw>().IsEnabled(), error = kErrorInvalidState);
+#endif
+
+    Get<ThreadNetif>().Up();
+    error = Get<Mle::MleRouter>().Start();
+
+exit:
+    return error;
+}
+
+} // namespace MeshCoP
+} // namespace ot
+
+#endif // OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
diff --git a/src/core/meshcop/tcat_agent.hpp b/src/core/meshcop/tcat_agent.hpp
new file mode 100644
index 0000000..16cf8f9
--- /dev/null
+++ b/src/core/meshcop/tcat_agent.hpp
@@ -0,0 +1,368 @@
+/*
+ *  Copyright (c) 2023, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ *  Implements the TCAT Agent service.
+ */
+
+#ifndef TCAT_AGENT_HPP_
+#define TCAT_AGENT_HPP_
+
+#include "openthread-core-config.h"
+
+#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
+
+#include <openthread/tcat.h>
+#include <openthread/platform/ble.h>
+
+#include "common/as_core_type.hpp"
+#include "common/callback.hpp"
+#include "common/locator.hpp"
+#include "common/log.hpp"
+#include "common/message.hpp"
+#include "common/non_copyable.hpp"
+#include "mac/mac_types.hpp"
+#include "meshcop/dataset.hpp"
+#include "meshcop/meshcop.hpp"
+#include "meshcop/meshcop_tlvs.hpp"
+#include "meshcop/secure_transport.hpp"
+
+namespace ot {
+
+namespace Ble {
+class BleSecure;
+}
+
+namespace MeshCoP {
+
+class TcatAgent : public InstanceLocator, private NonCopyable
+{
+public:
+    /**
+     * Pointer to call when application data was received over the TLS connection.
+     *
+     *  Please see otHandleTcatApplicationDataReceive for details.
+     *
+     */
+    typedef otHandleTcatApplicationDataReceive AppDataReceiveCallback;
+
+    /**
+     * Pointer to call to notify the completion of a join operation.
+     *
+     * Please see otHandleTcatJoin for details.
+     *
+     */
+    typedef otHandleTcatJoin JoinCallback;
+
+    /**
+     * Represents a TCAT command class.
+     *
+     */
+    enum CommandClass
+    {
+        kGeneral            = OT_TCAT_COMMAND_CLASS_GENERAL,         ///< TCAT commands related to general operations
+        kCommissioning      = OT_TCAT_COMMAND_CLASS_COMMISSIONING,   ///< TCAT commands related to commissioning
+        kExtraction         = OT_TCAT_COMMAND_CLASS_EXTRACTION,      ///< TCAT commands related to key extraction
+        kTlvDecommissioning = OT_TCAT_COMMAND_CLASS_DECOMMISSIONING, ///< TCAT commands related to de-commissioning
+        kApplication        = OT_TCAT_COMMAND_CLASS_APPLICATION,     ///< TCAT commands related to application layer
+        kInvalid ///< TCAT command belongs to reserved pool or is invalid
+    };
+
+    /**
+     * The certificate authorization field header type to indicate the type and version of the certificate.
+     *
+     */
+    enum CertificateAuthorizationFieldHeader : uint8_t
+    {
+        kCommissionerFlag = 1 << 0, ///< TCAT commissioner ('1') or device ('0')
+        kHeaderVersion    = 0xD0,   ///< Header version (3 bits)
+    };
+
+    /**
+     * The command class flag type to indicate which requirements apply for a given command class.
+     *
+     */
+    enum CommandClassFlags : uint8_t
+    {
+        kAccessFlag        = 1 << 0, ///< Access to the command class (device: without without additional requirements).
+        kPskdFlag          = 1 << 1, ///< Access requires proof-of-possession of the device's PSKd
+        kNetworkNameFlag   = 1 << 2, ///< Access requires matching network name
+        kExtendedPanIdFlag = 1 << 3, ///< Access requires matching XPANID
+        kThreadDomainFlag  = 1 << 4, ///< Access requires matching XPANID
+        kPskcFlag          = 1 << 5, ///< Access requires proof-of-possession of the device's PSKc
+    };
+
+    /**
+     *
+     * Represents a data structure for storing TCAT Commissioner authorization information in the
+     * certificate ASN.1 field 1.3.6.1.4.1.44970.3.
+     *
+     */
+    OT_TOOL_PACKED_BEGIN
+    struct CertificateAuthorizationField
+    {
+        CertificateAuthorizationFieldHeader mHeader;               ///< Typ and version
+        CommandClassFlags                   mCommissioningFlags;   ///< Command class flags
+        CommandClassFlags                   mExtractionFlags;      ///< Command class flags
+        CommandClassFlags                   mDecommissioningFlags; ///< Command class flags
+        CommandClassFlags                   mApplicationFlags;     ///< Command class flags
+
+    } OT_TOOL_PACKED_END;
+
+    typedef CertificateAuthorizationField CertificateAuthorizationField;
+
+    /**
+     * Represents the TCAT vendor information.
+     *
+     */
+    class VendorInfo : public otTcatVendorInfo
+    {
+    public:
+        /**
+         * Validates whether the TCAT vendor information is valid.
+         *
+         * @returns Whether the parameters are valid.
+         *
+         */
+        bool IsValid(void) const;
+    };
+
+    /**
+     * TCAT TLV Types.
+     *
+     */
+    enum TlvType : uint8_t
+    {
+        // Command Class General
+        kTlvResponseWithStatus        = 1,  ///< TCAT response with status value TLV
+        kTlvResponseWithPayload       = 2,  ///< TCAT response with payload TLV
+        kTlvResponseEvent             = 3,  ///< TCAT response event TLV (reserved)
+        kTlvGetNetworkName            = 8,  ///< TCAT network name query TLV
+        kTlvDisconnect                = 9,  ///< TCAT disconnect request TLV
+        kTlvPing                      = 10, ///< TCAT ping request TLV
+        kTlvGetDeviceId               = 11, ///< TCAT device ID query TLV
+        kTlvGetExtendedPanID          = 12, ///< TCAT extended PAN ID query TLV
+        kTlvPresentPskdHash           = 16, ///< TCAT commissioner rights elevation request TLV using PSKd hash
+        kTlvPresentPskcHash           = 17, ///< TCAT commissioner rights elevation request TLV using PSKc hash
+        kTlvPresentInstallCodeHash    = 18, ///< TCAT commissioner rights elevation request TLV using install code
+        kTlvRequestRandomNumChallenge = 19, ///< TCAT random number challenge query TLV
+        kTlvRequestPskdHash           = 20, ///< TCAT PSKd hash request TLV
+
+        // Command Class Commissioning
+        kTlvSetActiveOperationalDataset            = 32, ///< TCAT active operational dataset TLV
+        kTlvSetActiveOperationalDatasetAlternative = 33, ///< TCAT active operational dataset alternative #1 TLV
+        kTlvGetProvisioningTlvs                    = 36, ///< TCAT provisioning TLVs query TLV
+        kTlvGetCommissionerCertificate             = 37, ///< TCAT commissioner certificate query TLV
+        kTlvGetDiagnosticTlvs                      = 38, ///< TCAT diagnostics TLVs query TLV
+        kTlvStartThreadInterface                   = 39, ///< TCAT start thread interface request TLV
+        kTlvStopThreadInterface                    = 40, ///< TCAT stop thread interface request TLV
+
+        // Command Class Extraction
+        kTlvGetActiveOperationalDataset            = 64, ///< TCAT active oerational dataset query TLV
+        kTlvGetActiveOperationalDatasetAlternative = 65, ///< TCAT active oerational dataset alternative #1 query TLV
+
+        // Command Class Decommissioning
+        kTlvDecommission = 96, ///< TCAT decommission request TLV
+
+        // Command Class Application
+        kTlvSelectApplicationLayerUdp = 128, ///< TCAT select UDP protocol application layer request TLV
+        kTlvSelectApplicationLayerTcp = 129, ///< TCAT select TCP protocol application layer request TLV
+        kTlvSendApplicationData       = 130, ///< TCAT send application data TLV
+        kTlvSendVendorSpecificData    = 159, ///< TCAT send vendor specific command or data TLV
+
+        // Command Class CCM
+        kTlvSetLDevIdOperationalCert = 160, ///< TCAT LDevID operational certificate TLV
+        kTlvSetLDevIdPrivateKey      = 161, ///< TCAT LDevID operational certificate pricate key TLV
+        kTlvSetDomainCaCert          = 162, ///< TCAT domain CA certificate TLV
+    };
+
+    /**
+     * TCAT Response Types.
+     *
+     */
+    enum StatusCode : uint8_t
+    {
+        kStatusSuccess      = OT_TCAT_STATUS_SUCCESS,       ///< Command or request was successfully processed
+        kStatusUnsupported  = OT_TCAT_STATUS_UNSUPPORTED,   ///< Requested command or received TLV is not supported
+        kStatusParseError   = OT_TCAT_STATUS_PARSE_ERROR,   ///< Request / command could not be parsed correctly
+        kStatusValueError   = OT_TCAT_STATUS_VALUE_ERROR,   ///< The value of the transmitted TLV has an error
+        kStatusGeneralError = OT_TCAT_STATUS_GENERAL_ERROR, ///< An error not matching any other category occurred
+        kStatusBusy         = OT_TCAT_STATUS_BUSY,          ///< Command cannot be executed because the resource is busy
+        kStatusUndefined    = OT_TCAT_STATUS_UNDEFINED,     ///< The requested value, data or service is not defined
+                                                            ///< (currently) or not present
+        kStatusHashError = OT_TCAT_STATUS_HASH_ERROR, ///< The hash value presented by the commissioner was incorrect
+        kStatusUnauthorized =
+            OT_TCAT_STATUS_UNAUTHORIZED, ///< Sender does not have sufficient authorization for the given command
+    };
+
+    /**
+     * Represents TCAT application protocol.
+     *
+     */
+    enum TcatApplicationProtocol : uint8_t
+    {
+        kApplicationProtocolNone =
+            OT_TCAT_APPLICATION_PROTOCOL_NONE, ///< Message which has been sent without activating the TCAT agent
+        kApplicationProtocolUdp = OT_TCAT_APPLICATION_PROTOCOL_STATUS, ///< Message directed to a UDP service
+        kApplicationProtocolTcp = OT_TCAT_APPLICATION_PROTOCOL_TCP,    ///< Message directed to a TCP service
+    };
+
+    /**
+     * Represents a TCAT certificate V3 extension attribute (OID 1.3.6.1.4.1.44970.x).
+     *
+     */
+    enum TcatCertificateAttribute
+    {
+        kCertificateDomainName         = 1,
+        kCertificateAuthorizationField = 3,
+        kCertificateNetworkName        = 4,
+        kCertificateExtendedPanId      = 5,
+    };
+
+    /**
+     * Represents TCAT status.
+     *
+     */
+    enum State : uint8_t
+    {
+        kStateDisabled,
+        kStateEnabled,
+        kStateConnected,
+    };
+
+    /**
+     * Initializes the Joiner object.
+     *
+     * @param[in]  aInstance     A reference to the OpenThread instance.
+     *
+     */
+    explicit TcatAgent(Instance &aInstance);
+
+    /**
+     * Enables the TCAT protocol.
+     *
+     * @param[in] aVendorInfo               A pointer to the Vendor Information (must remain valid after the method
+     * call, may be NULL).
+     * @param[in] aAppDataReceiveCallback   A pointer to a function that is called when the user data is received.
+     * @param[in] aHandler                  A pointer to a function that is called when the join operation completes.
+     * @param[in] aContext                  A context pointer.
+     *
+     * @retval kErrorNone           Successfully started the TCAT agent.
+     * @retval kErrorInvalidArgs    The aVendorInfo is invalid.
+     *
+     */
+    Error Start(const VendorInfo      &aVendorInfo,
+                AppDataReceiveCallback aAppDataReceiveCallback,
+                JoinCallback           aHandler,
+                void                  *aContext);
+
+    /**
+     * Stops the TCAT protocol.
+     *
+     */
+    void Stop(void);
+
+    /**
+     * Indicates whether or not the TCAT agent is enabled.
+     *
+     * @retval TRUE   The TCAT agent is enabled.
+     * @retval FALSE  The TCAT agent is not enabled.
+     *
+     */
+    bool IsEnabled(void) const { return mState != kStateDisabled; }
+
+    /**
+     * Indicates whether or not the TCAT agent is connected.
+     *
+     * @retval TRUE   The TCAT agent is connected.
+     * @retval FALSE  The TCAT agent is not connected.
+     *
+     */
+    bool IsConnected(void) const { return mState == kStateConnected; }
+
+    /**
+     * Indicates whether or not a command class is authorized.
+     *
+     * @param[in] aCommandClass Command class to subject for authorization check.
+     *
+     * @retval TRUE   The command class is authorized.
+     * @retval FALSE  The command class is not authorized.
+     *
+     */
+    bool IsCommandClassAuthorized(CommandClass aCommandClass) const;
+
+private:
+    Error Connected(MeshCoP::SecureTransport &aTlsContext);
+    void  Disconnected(void);
+
+    Error HandleSingleTlv(const Message &aIncommingMessage, Message &aOutgoingMessage);
+    Error HandleSetActiveOperationalDataset(const Message &aIncommingMessage, uint16_t aOffset, uint16_t aLength);
+    Error HandleStartThreadInterface(void);
+
+    bool         CheckCommandClassAuthorizationFlags(CommandClassFlags aCommissionerCommandClassFlags,
+                                                     CommandClassFlags aDeviceCommandClassFlags,
+                                                     Dataset          *aDataset) const;
+    bool         CanProcessTlv(uint8_t aTlvType) const;
+    CommandClass GetCommandClass(uint8_t aTlvType) const;
+
+    static constexpr uint16_t kJoinerUdpPort = OPENTHREAD_CONFIG_JOINER_UDP_PORT;
+
+    JoinerPskd                       mJoinerPskd;
+    const VendorInfo                *mVendorInfo;
+    Callback<JoinCallback>           mJoinCallback;
+    Callback<AppDataReceiveCallback> mAppDataReceiveCallback;
+    CertificateAuthorizationField    mCommissionerAuthorizationField;
+    CertificateAuthorizationField    mDeviceAuthorizationField;
+    TcatApplicationProtocol          mCurrentApplicationProtocol;
+    NetworkName                      mCommissionerNetworkName;
+    NetworkName                      mCommissionerDomainName;
+    ExtendedPanId                    mCommissionerExtendedPanId;
+    char                             mCurrentServiceName[OT_TCAT_MAX_SERVICE_NAME_LENGTH + 1];
+    State                            mState;
+    bool                             mAlreadyCommissioned : 1;
+    bool                             mCommissionerHasNetworkName : 1;
+    bool                             mCommissionerHasDomainName : 1;
+    bool                             mCommissionerHasExtendedPanId : 1;
+
+    friend class Ble::BleSecure;
+};
+
+} // namespace MeshCoP
+
+DefineCoreType(otTcatVendorInfo, MeshCoP::TcatAgent::VendorInfo);
+
+DefineMapEnum(otTcatApplicationProtocol, MeshCoP::TcatAgent::TcatApplicationProtocol);
+
+typedef UintTlvInfo<MeshCoP::TcatAgent::kTlvResponseWithStatus, uint8_t> ResponseWithStatusTlv;
+
+} // namespace ot
+
+#endif // OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
+
+#endif // TCAT_AGENT_HPP_
diff --git a/src/core/mtd.cmake b/src/core/mtd.cmake
index e9b831c..ae8d9e3 100644
--- a/src/core/mtd.cmake
+++ b/src/core/mtd.cmake
@@ -29,7 +29,9 @@
 add_library(openthread-mtd)
 
 target_compile_definitions(openthread-mtd PRIVATE
+    OPENTHREAD_FTD=0
     OPENTHREAD_MTD=1
+    OPENTHREAD_RADIO=0
 )
 
 target_compile_options(openthread-mtd PRIVATE
diff --git a/src/core/net/dhcp6_client.cpp b/src/core/net/dhcp6_client.cpp
index 680ea04..56654fb 100644
--- a/src/core/net/dhcp6_client.cpp
+++ b/src/core/net/dhcp6_client.cpp
@@ -57,7 +57,7 @@
     , mStartTime(0)
     , mIdentityAssociationCurrent(nullptr)
 {
-    memset(mIdentityAssociations, 0, sizeof(mIdentityAssociations));
+    ClearAllBytes(mIdentityAssociations);
 }
 
 bool Client::MatchNetifAddressWithPrefix(const Ip6::Netif::UnicastAddress &aNetifAddress, const Ip6::Prefix &aIp6Prefix)
@@ -288,7 +288,7 @@
     if (error != kErrorNone)
     {
         FreeMessage(message);
-        LogWarn("Failed to send DHCPv6 Solicit: %s", ErrorToString(error));
+        LogWarnOnError(error, "send DHCPv6 Solicit");
     }
 }
 
diff --git a/src/core/net/dhcp6_server.cpp b/src/core/net/dhcp6_server.cpp
index c268e1c..ea95707 100644
--- a/src/core/net/dhcp6_server.cpp
+++ b/src/core/net/dhcp6_server.cpp
@@ -56,7 +56,7 @@
     , mPrefixAgentsCount(0)
     , mPrefixAgentsMask(0)
 {
-    memset(mPrefixAgents, 0, sizeof(mPrefixAgents));
+    ClearAllBytes(mPrefixAgents);
 }
 
 Error Server::UpdateService(void)
@@ -172,11 +172,8 @@
     mPrefixAgentsCount++;
 
 exit:
-
-    if (error != kErrorNone)
-    {
-        LogNote("Failed to add DHCPv6 prefix agent: %s", ErrorToString(error));
-    }
+    LogWarnOnError(error, "add DHCPv6 prefix agent");
+    OT_UNUSED_VARIABLE(error);
 }
 
 void Server::HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
diff --git a/src/core/net/dns_client.cpp b/src/core/net/dns_client.cpp
index 366a657..95a87b7 100644
--- a/src/core/net/dns_client.cpp
+++ b/src/core/net/dns_client.cpp
@@ -799,7 +799,7 @@
     Error                       error;
     otTcpEndpointInitializeArgs endpointArgs;
 
-    memset(&endpointArgs, 0x00, sizeof(endpointArgs));
+    ClearAllBytes(endpointArgs);
     endpointArgs.mSendDoneCallback         = HandleTcpSendDoneCallback;
     endpointArgs.mEstablishedCallback      = HandleTcpEstablishedCallback;
     endpointArgs.mReceiveAvailableCallback = HandleTcpReceiveAvailableCallback;
@@ -1634,7 +1634,7 @@
 
     PopulateResponse(response, aQuery, aResponseMessage);
 
-    memset(&serviceInfo, 0, sizeof(serviceInfo));
+    ClearAllBytes(serviceInfo);
     serviceInfo.mHostNameBuffer     = hostName;
     serviceInfo.mHostNameBufferSize = sizeof(hostName);
     SuccessOrExit(response.ReadServiceInfo(Response::kAnswerSection, Name(aQuery, kNameOffsetInQuery), serviceInfo));
diff --git a/src/core/net/dns_types.cpp b/src/core/net/dns_types.cpp
index aa19c73..eecc9ff 100644
--- a/src/core/net/dns_types.cpp
+++ b/src/core/net/dns_types.cpp
@@ -100,6 +100,69 @@
     return error;
 }
 
+bool Name::Matches(const char *aFirstLabel, const char *aLabels, const char *aDomain) const
+{
+    bool matches = false;
+
+    VerifyOrExit(!IsEmpty());
+
+    if (IsFromCString())
+    {
+        const char *namePtr = mString;
+
+        if (aFirstLabel != nullptr)
+        {
+            matches = CompareAndSkipLabels(namePtr, aFirstLabel, kLabelSeparatorChar);
+            VerifyOrExit(matches);
+        }
+
+        matches = CompareAndSkipLabels(namePtr, aLabels, kLabelSeparatorChar);
+        VerifyOrExit(matches);
+
+        matches = CompareAndSkipLabels(namePtr, aDomain, kNullChar);
+    }
+    else
+    {
+        uint16_t offset = mOffset;
+
+        if (aFirstLabel != nullptr)
+        {
+            SuccessOrExit(CompareLabel(*mMessage, offset, aFirstLabel));
+        }
+
+        SuccessOrExit(CompareMultipleLabels(*mMessage, offset, aLabels));
+        SuccessOrExit(CompareName(*mMessage, offset, aDomain));
+        matches = true;
+    }
+
+exit:
+    return matches;
+}
+
+bool Name::CompareAndSkipLabels(const char *&aNamePtr, const char *aLabels, char aExpectedNextChar)
+{
+    // Compares `aNamePtr` to the label string `aLabels` followed by
+    // the `aExpectedNextChar`(using case-insensitive match). Upon
+    // successful comparison, `aNamePtr` is advanced to point after
+    // the matched portion.
+
+    bool     matches = false;
+    uint16_t len     = StringLength(aLabels, kMaxNameSize);
+
+    VerifyOrExit(len < kMaxNameSize);
+
+    VerifyOrExit(StringStartsWith(aNamePtr, aLabels, kStringCaseInsensitiveMatch));
+    aNamePtr += len;
+
+    VerifyOrExit(*aNamePtr == aExpectedNextChar);
+    aNamePtr++;
+
+    matches = true;
+
+exit:
+    return matches;
+}
+
 Error Name::AppendTo(Message &aMessage) const
 {
     Error error;
@@ -369,6 +432,27 @@
     return error;
 }
 
+Error Name::CompareMultipleLabels(const Message &aMessage, uint16_t &aOffset, const char *aLabels)
+{
+    Error         error;
+    LabelIterator iterator(aMessage, aOffset);
+
+    while (true)
+    {
+        SuccessOrExit(error = iterator.GetNextLabel());
+        VerifyOrExit(iterator.CompareLabel(aLabels, !kIsSingleLabel), error = kErrorNotFound);
+
+        if (*aLabels == kNullChar)
+        {
+            aOffset = iterator.mNextLabelOffset;
+            ExitNow();
+        }
+    }
+
+exit:
+    return error;
+}
+
 Error Name::CompareName(const Message &aMessage, uint16_t &aOffset, const char *aName)
 {
     Error         error;
diff --git a/src/core/net/dns_types.hpp b/src/core/net/dns_types.hpp
index 9f8a930..e590a48 100644
--- a/src/core/net/dns_types.hpp
+++ b/src/core/net/dns_types.hpp
@@ -511,9 +511,25 @@
      */
     static constexpr uint8_t kMaxLabelLength = kMaxLabelSize - 1;
 
+    /**
+     * Dot character separating labels in a name.
+     *
+     */
     static constexpr char kLabelSeparatorChar = '.';
 
     /**
+     * Represents a string buffer (with `kMaxNameSize`) intended to hold a DNS name.
+     *
+     */
+    typedef char Buffer[kMaxNameSize];
+
+    /**
+     * Represents a string buffer (with `kMaxLabelSize`) intended to hold a DNS label.
+     *
+     */
+    typedef char LabelBuffer[kMaxLabelSize];
+
+    /**
      * Represents the name type.
      *
      */
@@ -647,6 +663,32 @@
     }
 
     /**
+     * Matches the `Name` with a given set of labels and domain name.
+     *
+     * This method allows the caller to specify name components separately, enabling scenarios like comparing "service
+     * instance name" with separate instance label (which can include dot character), service type, and domain strings.
+     *
+     * @p aFirstLabel can be `nullptr` if not needed. But if non-null, it is treated as a single label and can itself
+     * include dot `.` character.
+     *
+     * The @p aLabels MUST NOT be `nullptr` and MUST follow  "<label1>.<label2>.<label3>", i.e., a sequence of one or
+     * more labels separated by dot '.' char, and it MUST NOT end with dot `.`.
+     *
+     * @p aDomain MUST NOT be `nullptr` and MUST have at least one label and MUST always end with a dot `.` character.
+     *
+     * If the above conditions are not satisfied, the behavior of this method is undefined.
+     *
+     * @param[in] aFirstLabel     A first label to check. Can be `nullptr`.
+     * @param[in] aLabels         A string of dot separated labels, MUST NOT end with dot.
+     * @param[in] aDomain         Domain name. MUST end with dot.
+     *
+     * @retval TRUE   The name matches the given components.
+     * @retval FALSE  The name does not match the given components.
+     *
+     */
+    bool Matches(const char *aFirstLabel, const char *aLabels, const char *aDomain) const;
+
+    /**
      * Encodes and appends the name to a message.
      *
      * If the name is empty (not specified), then root "." is appended to @p aMessage. If the name is from a C string
@@ -822,6 +864,35 @@
     static Error ReadName(const Message &aMessage, uint16_t &aOffset, char *aNameBuffer, uint16_t aNameBufferSize);
 
     /**
+     * Reads a full name from a message.
+     *
+     * On successful read, the read name follows  "<label1>.<label2>.<label3>.", i.e., a sequence of labels separated by
+     * dot '.' character. The read name will ALWAYS end with a dot.
+     *
+     * Verifies that the labels after the first label in message do not contain any dot character. If they do,
+     * returns `kErrorParse`.
+     *
+     * @tparam kNameBufferSize         Size of the string buffer array.
+     *
+     * @param[in]     aMessage         The message to read the name from. `aMessage.GetOffset()` MUST point to
+     *                                 the start of DNS header (this is used to handle compressed names).
+     * @param[in,out] aOffset          On input, the offset in @p aMessage pointing to the start of the name field.
+     *                                 On exit (when parsed successfully), @p aOffset is updated to point to the byte
+     *                                 after the end of name field.
+     * @param[out]    aNameBuffer      Reference to a name string buffer to output the read name.
+     *
+     * @retval kErrorNone         Successfully read the name, @p aNameBuffer and @p Offset are updated.
+     * @retval kErrorParse        Name could not be parsed (invalid format).
+     * @retval kErrorNoBufs       Name could not fit in @p aNameBuffer.
+     *
+     */
+    template <uint16_t kNameBufferSize>
+    static Error ReadName(const Message &aMessage, uint16_t &aOffset, char (&aNameBuffer)[kNameBufferSize])
+    {
+        return ReadName(aMessage, aOffset, aNameBuffer, kNameBufferSize);
+    }
+
+    /**
      * Compares a single name label from a message with a given label string.
      *
      * Can be used to compare labels one by one. It checks whether the label read from @p aMessage matches
@@ -846,6 +917,29 @@
     static Error CompareLabel(const Message &aMessage, uint16_t &aOffset, const char *aLabel);
 
     /**
+     * Parses and compares multiple name labels from a message.
+     *
+     * Can be used to read and compare a group of labels from an encoded DNS name in a message with possibly more
+     * labels remaining to read.
+     *
+     * The @p aLabels must follow  "<label1>.<label2>.<label3>", i.e., a sequence of labels separated by dot '.' char.
+     *
+     * @param[in]     aMessage        The message to read the labels from to compare. `aMessage.GetOffset()` MUST point
+     *                                to the start of DNS header (this is used to handle compressed names).
+     * @param[in,out] aOffset         On input, the offset in @p aMessage pointing to the start of the labels to read.
+     *                                On exit and only when all labels are successfully read and match @p aLabels,
+     *                                @p aOffset is updated to point to the start of the next label.
+     * @param[in]     aLabels         A pointer to a null terminated string containing the labels to compare with.
+     *
+     * @retval kErrorNone          The labels from @p aMessage matches @p aLabels. @p aOffset is updated.
+     * @retval kErrorNotFound      The labels from @p aMessage does not match @p aLabel (note that @p aOffset is not
+     *                             updated in this case).
+     * @retval kErrorParse         Name could not be parsed (invalid format).
+     *
+     */
+    static Error CompareMultipleLabels(const Message &aMessage, uint16_t &aOffset, const char *aLabels);
+
+    /**
      * Parses and compares a full name from a message with a given name.
      *
      * Checks whether the encoded name in a message matches a given name string (using case-insensitive
@@ -928,14 +1022,14 @@
     static Error CompareName(const Message &aMessage, uint16_t &aOffset, const Name &aName);
 
     /**
-     * Extracts label(s) from a full name by checking that it contains a given suffix name (e.g., suffix name can be
+     * Extracts label(s) from a name by checking that it contains a given suffix name (e.g., suffix name can be
      * a domain name) and removing it.
      *
-     * Both @p aName and @p aSuffixName must be full DNS name and end with ('.'), otherwise the behavior of this method
-     * is undefined.
+     * Both @p aName and @p aSuffixName MUST follow the same style regarding inclusion of trailing dot ('.'). Otherwise
+     * `kErrorParse` is returned.
      *
-     * @param[in]   aName           The full name to extract labels from.
-     * @param[in]   aSuffixName     The suffix name (e.g. can be domain name).
+     * @param[in]   aName           The name to extract labels from.
+     * @param[in]   aSuffixName     The suffix name (e.g., can be domain name).
      * @param[out]  aLabels         Pointer to buffer to copy the extracted labels.
      * @param[in]   aLabelsSize     Size of @p aLabels buffer.
      *
@@ -947,6 +1041,30 @@
     static Error ExtractLabels(const char *aName, const char *aSuffixName, char *aLabels, uint16_t aLabelsSize);
 
     /**
+     * Extracts label(s) from a name by checking that it contains a given suffix name (e.g., suffix name can be
+     * a domain name) and removing it.
+     *
+     * Both @p aName and @p aSuffixName MUST follow the same style regarding inclusion of trailing dot ('.'). Otherwise
+     * `kErrorParse` is returned.
+     *
+     * @tparam      kLabelsBufferSize   Size of the buffer string.
+     *
+     * @param[in]   aName           The name to extract labels from.
+     * @param[in]   aSuffixName     The suffix name (e.g., can be domain name).
+     * @param[out]  aLabelsBuffer   A buffer to copy the extracted labels.
+     *
+     * @retval kErrorNone     Successfully extracted the labels, @p aLabels is updated.
+     * @retval kErrorParse    @p aName does not contain @p aSuffixName.
+     * @retval kErrorNoBufs   Could not fit the labels in @p aLabels.
+     *
+     */
+    template <uint16_t kLabelsBufferSize>
+    static Error ExtractLabels(const char *aName, const char *aSuffixName, char (&aLabels)[kLabelsBufferSize])
+    {
+        return ExtractLabels(aName, aSuffixName, aLabels, kLabelsBufferSize);
+    }
+
+    /**
      * Tests if a DNS name is a sub-domain of a given domain.
      *
      * Both @p aName and @p aDomain can end without dot ('.').
@@ -1025,6 +1143,7 @@
     {
     }
 
+    static bool  CompareAndSkipLabels(const char *&aNamePtr, const char *aLabels, char aExpectedNextChar);
     static Error AppendLabel(const char *aLabel, uint8_t aLength, Message &aMessage);
 
     const char    *mString;  // String containing the name or `nullptr` if name is not from string.
@@ -1660,6 +1779,36 @@
                       char          *aNameBuffer,
                       uint16_t       aNameBufferSize) const;
 
+    /**
+     * Parses and reads the PTR name from a message.
+     *
+     * This is a template variation of the previous method with name and label buffer sizes as template parameters.
+     *
+     * @tparam kLabelBufferSize          The size of label buffer.
+     * @tparam kNameBufferSize           The size of name buffer.
+     *
+     * @param[in]      aMessage          The message to read from. `aMessage.GetOffset()` MUST point to the start of
+     *                                   DNS header.
+     * @param[in,out]  aOffset           On input, the offset in @p aMessage to the start of PTR name field.
+     *                                   On exit, when successfully read, @p aOffset is updated to point to the byte
+     *                                   after the entire PTR record (skipping over the record).
+     * @param[out]     aLabelBuffer      A char array buffer to output the first label as a null-terminated C string.
+     * @param[out]     aNameBuffer       A char array to output the rest of name (after first label).
+     *
+     * @retval kErrorNone    The PTR name was read successfully. @p aOffset, @aLabelBuffer and @aNameBuffer are updated.
+     * @retval kErrorParse   The PTR record in @p aMessage could not be parsed (invalid format).
+     * @retval kErrorNoBufs  Either label or name could not fit in the related given buffers.
+     *
+     */
+    template <uint16_t kLabelBufferSize, uint16_t kNameBufferSize>
+    Error ReadPtrName(const Message &aMessage,
+                      uint16_t      &aOffset,
+                      char (&aLabelBuffer)[kLabelBufferSize],
+                      char (&aNameBuffer)[kNameBufferSize]) const
+    {
+        return ReadPtrName(aMessage, aOffset, aLabelBuffer, kLabelBufferSize, aNameBuffer, kNameBufferSize);
+    }
+
 } OT_TOOL_PACKED_END;
 
 /**
@@ -1868,6 +2017,32 @@
                                         /* aSkipRecord */ true);
     }
 
+    /**
+     * Parses and reads the SRV target host name from a message.
+     *
+     * Also verifies that the SRV record is well-formed (e.g., the record data length `GetLength()` matches
+     * the SRV encoded name).
+     *
+     * @tparam         kNameBufferSize  Size of the name buffer.
+     *
+     * @param[in]      aMessage         The message to read from. `aMessage.GetOffset()` MUST point to the start of
+     *                                  DNS header.
+     * @param[in,out]  aOffset          On input, the offset in @p aMessage to start of target host name field.
+     *                                  On exit when successfully read, @p aOffset is updated to point to the byte
+     *                                  after the entire SRV record (skipping over the record).
+     * @param[out]     aNameBuffer      A char array to output the read name as a null-terminated C string
+     *
+     * @retval kErrorNone            The host name was read successfully. @p aOffset and @p aNameBuffer are updated.
+     * @retval kErrorParse           The SRV record in @p aMessage could not be parsed (invalid format).
+     * @retval kErrorNoBufs          Name could not fit in @p aNameBuffer.
+     *
+     */
+    template <uint16_t kNameBufferSize>
+    Error ReadTargetHostName(const Message &aMessage, uint16_t &aOffset, char (&aNameBuffer)[kNameBufferSize]) const
+    {
+        return ReadTargetHostName(aMessage, aOffset, aNameBuffer, kNameBufferSize);
+    }
+
 private:
     uint16_t mPriority;
     uint16_t mWeight;
@@ -2079,13 +2254,21 @@
     bool IsValid(void) const;
 
     /**
-     * Returns the ECDSA P-256 public kek.
+     * Returns the ECDSA P-256 public key.
      *
      * @returns  A reference to the public key.
      *
      */
     const Crypto::Ecdsa::P256::PublicKey &GetKey(void) const { return mKey; }
 
+    /**
+     * Sets the ECDSA P-256 public key.
+     *
+     * @param[in] aKey  The public key.
+     *
+     */
+    void SetKey(const Crypto::Ecdsa::P256::PublicKey &aKey) { mKey = aKey; }
+
 private:
     Crypto::Ecdsa::P256::PublicKey mKey;
 } OT_TOOL_PACKED_END;
diff --git a/src/core/net/dnssd.cpp b/src/core/net/dnssd.cpp
new file mode 100644
index 0000000..c795fa8
--- /dev/null
+++ b/src/core/net/dnssd.cpp
@@ -0,0 +1,158 @@
+/*
+ *  Copyright (c) 2023, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ *   This file implements infrastructure DNS-SD (mDNS) platform APIs.
+ */
+
+#include "dnssd.hpp"
+
+#if OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE
+
+#include "common/code_utils.hpp"
+#include "common/locator_getters.hpp"
+#include "instance/instance.hpp"
+
+namespace ot {
+
+//---------------------------------------------------------------------------------------------------------------------
+// Dnssd::RequestIdRange
+
+void Dnssd::RequestIdRange::Add(RequestId aId)
+{
+    if (IsEmpty())
+    {
+        mStart = aId;
+        mEnd   = aId + 1;
+    }
+    else if (SerialNumber::IsLess(aId, mStart)) // Equivalent to (aId < mStart)
+    {
+        mStart = aId;
+    }
+    else if (!SerialNumber::IsLess(aId, mEnd)) // Equivalent to !(aId < mEd) -> (aId >= mEnd)
+    {
+        mEnd = aId + 1;
+    }
+}
+
+void Dnssd::RequestIdRange::Remove(RequestId aId)
+{
+    if (!IsEmpty())
+    {
+        if (aId == mStart)
+        {
+            mStart++;
+        }
+        else if (aId + 1 == mEnd)
+        {
+            mEnd--;
+        }
+    }
+}
+
+bool Dnssd::RequestIdRange::Contains(RequestId aId) const
+{
+    // Equivalent to `(aId >= mStart) && (aId < mEnd)`
+
+    return (!SerialNumber::IsLess(aId, mStart) && SerialNumber::IsLess(aId, mEnd));
+}
+
+//---------------------------------------------------------------------------------------------------------------------
+// Dnssd
+
+Dnssd::Dnssd(Instance &aInstance)
+    : InstanceLocator(aInstance)
+{
+}
+
+Dnssd::State Dnssd::GetState(void) const { return MapEnum(otPlatDnssdGetState(&GetInstance())); }
+
+void Dnssd::RegisterService(const Service &aService, RequestId aRequestId, RegisterCallback aCallback)
+{
+    if (IsReady())
+    {
+        otPlatDnssdRegisterService(&GetInstance(), &aService, aRequestId, aCallback);
+    }
+}
+
+void Dnssd::UnregisterService(const Service &aService, RequestId aRequestId, RegisterCallback aCallback)
+{
+    if (IsReady())
+    {
+        otPlatDnssdUnregisterService(&GetInstance(), &aService, aRequestId, aCallback);
+    }
+}
+
+void Dnssd::RegisterHost(const Host &aHost, RequestId aRequestId, RegisterCallback aCallback)
+{
+    if (IsReady())
+    {
+        otPlatDnssdRegisterHost(&GetInstance(), &aHost, aRequestId, aCallback);
+    }
+}
+
+void Dnssd::UnregisterHost(const Host &aHost, RequestId aRequestId, RegisterCallback aCallback)
+{
+    if (IsReady())
+    {
+        otPlatDnssdUnregisterHost(&GetInstance(), &aHost, aRequestId, aCallback);
+    }
+}
+
+void Dnssd::RegisterKey(const Key &aKey, RequestId aRequestId, RegisterCallback aCallback)
+{
+    if (IsReady())
+    {
+        otPlatDnssdRegisterKey(&GetInstance(), &aKey, aRequestId, aCallback);
+    }
+}
+
+void Dnssd::UnregisterKey(const Key &aKey, RequestId aRequestId, RegisterCallback aCallback)
+{
+    if (IsReady())
+    {
+        otPlatDnssdUnregisterKey(&GetInstance(), &aKey, aRequestId, aCallback);
+    }
+}
+
+void Dnssd::HandleStateChange(void)
+{
+#if OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE
+    Get<Srp::AdvertisingProxy>().HandleDnssdPlatformStateChange();
+#endif
+}
+
+extern "C" void otPlatDnssdStateHandleStateChange(otInstance *aInstance)
+{
+    AsCoreType(aInstance).Get<Dnssd>().HandleStateChange();
+}
+
+} // namespace ot
+
+#endif // OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE
diff --git a/src/core/net/dnssd.hpp b/src/core/net/dnssd.hpp
new file mode 100644
index 0000000..dc6f6b6
--- /dev/null
+++ b/src/core/net/dnssd.hpp
@@ -0,0 +1,282 @@
+/*
+ *  Copyright (c) 2023, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ *   This file includes definitions for infrastructure DNS-SD (mDNS) platform.
+ */
+
+#ifndef DNSSD_HPP_
+#define DNSSD_HPP_
+
+#include "openthread-core-config.h"
+
+#if OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE
+
+#include <openthread/platform/dnssd.h>
+
+#include "common/clearable.hpp"
+#include "common/locator.hpp"
+#include "common/non_copyable.hpp"
+#include "net/ip6_address.hpp"
+
+namespace ot {
+
+/**
+ * @addtogroup core-dns
+ *
+ * @brief
+ *   This module includes definitions for DNS-SD (mDNS) platform.
+ *
+ * @{
+ *
+ */
+
+extern "C" void otPlatDnssdStateHandleStateChange(otInstance *aInstance);
+
+/**
+ * Represents DNS-SD (mDNS) platform.
+ *
+ */
+class Dnssd : public InstanceLocator, private NonCopyable
+{
+    friend void otPlatDnssdStateHandleStateChange(otInstance *aInstance);
+
+public:
+    /**
+     * Represents state of DNS-SD platform.
+     *
+     */
+    enum State : uint8_t
+    {
+        kStopped = OT_PLAT_DNSSD_STOPPED, ///< Stopped and unable to register any service or host.
+        kReady   = OT_PLAT_DNSSD_READY    ///< Running and ready to register service or host.
+    };
+
+    typedef otPlatDnssdRequestId        RequestId;        ///< A request ID.
+    typedef otPlatDnssdRegisterCallback RegisterCallback; ///< The registration request callback
+
+    class Host : public otPlatDnssdHost, public Clearable<Host> ///< Host information.
+    {
+    };
+
+    class Service : public otPlatDnssdService, public Clearable<Service> ///< Service information.
+    {
+    };
+
+    class Key : public otPlatDnssdKey, public Clearable<Key> ///< Key information
+    {
+    };
+
+    /**
+     * Represents a range of `RequestId` values.
+     *
+     * The range is stored using start and end ID values. The implementation handles the case when ID values roll over.
+     *
+     */
+    struct RequestIdRange : public Clearable<RequestIdRange>
+    {
+        /**
+         * Initializes a range as empty.
+         *
+         */
+        RequestIdRange(void)
+            : mStart(0)
+            , mEnd(0)
+        {
+        }
+
+        /**
+         * Adds a request ID to the range.
+         *
+         * @param[in] aId   The ID to add to the range.
+         *
+         */
+        void Add(RequestId aId);
+
+        /**
+         * Removes a request ID from the range.
+         *
+         * @param[in] aId   The ID to remove from the range.
+         *
+         */
+        void Remove(RequestId aId);
+
+        /**
+         * Indicates whether or not a given ID is contained within the range.
+         *
+         * @param[in] aId   The ID to check.
+         *
+         * @retval TRUE  The @p aID is contained within the range.
+         * @retval FALSE The @p aId is not contained within the range.
+         *
+         */
+        bool Contains(RequestId aId) const;
+
+        /**
+         * Indicates whether or not the range is empty.
+         *
+         * @retval TRUE  The range is empty.
+         * @retval FALSE The range is not empty.
+         *
+         */
+        bool IsEmpty(void) const { return (mStart == mEnd); }
+
+    private:
+        // The range is represented as all `RequestId` values from
+        // `mStart` up to, but not including, `mEnd`. It uses serial
+        // number arithmetic logic when comparing `RequestId` values,
+        // so `Contains()` and other methods work correctly even when
+        // the ID value rolls over.
+
+        RequestId mStart;
+        RequestId mEnd;
+    };
+
+    /**
+     * Initializes `Dnssd` object.
+     *
+     * @param[in]  aInstance  The OpenThread instance.
+     *
+     */
+    explicit Dnssd(Instance &aInstance);
+
+    /**
+     * Gets the current state of DNS-SD platform module.
+     *
+     * @returns The current state of DNS-SD platform.
+     *
+     */
+    State GetState(void) const;
+
+    /**
+     * Indicates whether or not DNS-SD platform is ready (in `kReady` state).
+     *
+     * @retval TRUE   The DNS-SD platform is ready.
+     * @retval FALSE  The DNS-SD platform is not ready.
+     *
+     */
+    bool IsReady(void) const { return GetState() == kReady; }
+
+    /**
+     * Registers or updates a service on the infrastructure network's DNS-SD module.
+     *
+     * Refer to the documentation for `otPlatDnssdRegisterService()`, for a more detailed description of the behavior
+     * of this method.
+     *
+     * @param[in] aService     Information about service to unregister.
+     * @param[in] aRequestId   The ID associated with this request.
+     * @param[in] aCallback    The callback function pointer to report the outcome (may be `nullptr`).
+     *
+     */
+    void RegisterService(const Service &aService, RequestId aRequestId, RegisterCallback aCallback);
+
+    /**
+     * Unregisters a service on the infrastructure network's DNS-SD module.
+     *
+     * Refer to the documentation for `otPlatDnssdUnregisterService()`, for a more detailed description of the behavior
+     * of this method.
+     *
+     * @param[in] aService      Information about service to unregister.
+     * @param[in] aRequestId    The ID associated with this request.
+     * @param[in] aCallback     The callback function pointer to report the outcome (may be `nullptr`).
+     *
+     */
+    void UnregisterService(const Service &aService, RequestId aRequestId, RegisterCallback aCallback);
+
+    /**
+     * Registers or updates a host on the infrastructure network's DNS-SD module.
+     *
+     * Refer to the documentation for `otPlatDnssdRegisterHost()`, for a more detailed description of the behavior
+     * of this method.
+     *
+     * @param[in] aHost         Information about host to register.
+     * @param[in] aRequestId    The ID associated with this request.
+     * @param[in] aCallback     The callback function pointer to report the outcome (may be `nullptr`).
+     *
+     */
+    void RegisterHost(const Host &aHost, RequestId aRequestId, RegisterCallback aCallback);
+
+    /**
+     * Unregisters a host on the infrastructure network's DNS-SD module.
+     *
+     * Refer to the documentation for `otPlatDnssdUnregisterHost()`, for a more detailed description of the behavior
+     * of this method.
+     *
+     * @param[in] aHost         Information about the host to unregister.
+     * @param[in] aRequestId    The ID associated with this request.
+     * @param[in] aCallback     The callback function pointer to report the outcome (may be NULL if no callback needed).
+     *
+     */
+    void UnregisterHost(const Host &aHost, RequestId aRequestId, RegisterCallback aCallback);
+
+    /**
+     * Registers or updates a key record on the infrastructure network's DNS-SD module.
+     *
+     * Refer to the documentation for `otPlatDnssdRegisterKey()`, for a more detailed description of the behavior
+     * of this method.
+     *
+     * @param[in] aKey          Information about the key to register.
+     * @param[in] aRequestId    The ID associated with this request.
+     * @param[in] aCallback     The callback function pointer to report the outcome (may be `nullptr`).
+     *
+     */
+    void RegisterKey(const Key &aKey, RequestId aRequestId, RegisterCallback aCallback);
+
+    /**
+     * Unregisters a key record on the infrastructure network's DNS-SD module.
+     *
+     * Refer to the documentation for `otPlatDnssdUnregisterKey()`, for a more detailed description of the behavior
+     * of this method.
+     *
+     * @param[in] aKey          Information about the key to unregister.
+     * @param[in] aRequestId    The ID associated with this request.
+     * @param[in] aCallback     The callback function pointer to report the outcome (may be NULL if no callback needed).
+     *
+     */
+    void UnregisterKey(const Key &aKey, RequestId aRequestId, RegisterCallback aCallback);
+
+private:
+    void HandleStateChange(void);
+};
+
+/**
+ * @}
+ *
+ */
+
+DefineMapEnum(otPlatDnssdState, Dnssd::State);
+DefineCoreType(otPlatDnssdService, Dnssd::Service);
+DefineCoreType(otPlatDnssdHost, Dnssd::Host);
+DefineCoreType(otPlatDnssdKey, Dnssd::Key);
+
+} // namespace ot
+
+#endif // OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE
+
+#endif // DNSSD_HPP_
diff --git a/src/core/net/dnssd_server.cpp b/src/core/net/dnssd_server.cpp
index ea59c08..e05a7d9 100644
--- a/src/core/net/dnssd_server.cpp
+++ b/src/core/net/dnssd_server.cpp
@@ -170,7 +170,7 @@
             ExitNow();
         }
 
-        LogWarn("Error forwarding to upstream: %s", ErrorToString(error));
+        LogWarnOnError(error, "forwarding to upstream");
 
         rcode = Header::kResponseServerFailure;
 
@@ -412,12 +412,12 @@
     // Parses and validates the query name and updates
     // the name compression offsets.
 
-    Error    error = kErrorNone;
-    DnsName  name;
-    uint16_t offset;
+    Error        error = kErrorNone;
+    Name::Buffer name;
+    uint16_t     offset;
 
     offset = sizeof(Header);
-    SuccessOrExit(error = Name::ReadName(*mMessage, offset, name, sizeof(name)));
+    SuccessOrExit(error = Name::ReadName(*mMessage, offset, name));
 
     switch (mType)
     {
@@ -446,9 +446,9 @@
 
     while (true)
     {
-        DnsLabel label;
-        uint8_t  labelLength = sizeof(label);
-        uint16_t comapreOffset;
+        Name::LabelBuffer label;
+        uint8_t           labelLength = sizeof(label);
+        uint16_t          comapreOffset;
 
         SuccessOrExit(error = Name::ReadLabel(*mMessage, offset, label, labelLength));
 
@@ -472,7 +472,7 @@
     return error;
 }
 
-void Server::Response::ReadQueryName(DnsName &aName) const { Server::ReadQueryName(*mMessage, aName); }
+void Server::Response::ReadQueryName(Name::Buffer &aName) const { Server::ReadQueryName(*mMessage, aName); }
 
 bool Server::Response::QueryNameMatches(const char *aName) const { return Server::QueryNameMatches(*mMessage, aName); }
 
@@ -526,12 +526,12 @@
                                         uint16_t    aWeight,
                                         uint16_t    aPort)
 {
-    Error     error = kErrorNone;
-    SrvRecord srvRecord;
-    uint16_t  recordOffset;
-    DnsName   hostLabels;
+    Error        error = kErrorNone;
+    SrvRecord    srvRecord;
+    uint16_t     recordOffset;
+    Name::Buffer hostLabels;
 
-    SuccessOrExit(error = Name::ExtractLabels(aHostName, kDefaultDomainName, hostLabels, sizeof(hostLabels)));
+    SuccessOrExit(error = Name::ExtractLabels(aHostName, kDefaultDomainName, hostLabels));
 
     srvRecord.Init();
     srvRecord.SetTtl(aTtl);
@@ -674,7 +674,7 @@
 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
 void Server::Response::Log(void) const
 {
-    DnsName name;
+    Name::Buffer name;
 
     ReadQueryName(name);
     LogInfo("%s query for '%s'", QueryTypeToString(mType), name);
@@ -830,16 +830,16 @@
 #if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE
 bool Server::ShouldForwardToUpstream(const Request &aRequest)
 {
-    bool     shouldForward = false;
-    uint16_t readOffset;
-    DnsName  name;
+    bool         shouldForward = false;
+    uint16_t     readOffset;
+    Name::Buffer name;
 
     VerifyOrExit(aRequest.mHeader.IsRecursionDesiredFlagSet());
     readOffset = sizeof(Header);
 
     for (uint16_t i = 0; i < aRequest.mHeader.GetQuestionCount(); i++)
     {
-        SuccessOrExit(Name::ReadName(*aRequest.mMessage, readOffset, name, sizeof(name)));
+        SuccessOrExit(Name::ReadName(*aRequest.mMessage, readOffset, name));
         readOffset += sizeof(Question);
 
         VerifyOrExit(!Name::IsSubDomainOf(name, kDefaultDomainName));
@@ -866,6 +866,10 @@
     {
         error = mSocket.SendTo(*aResponseMessage, aQueryTransaction.GetMessageInfo());
     }
+    else
+    {
+        error = kErrorResponseTimeout;
+    }
 
     ResetUpstreamQueryTransaction(aQueryTransaction, error);
 
@@ -886,7 +890,7 @@
         }
     }
 
-    VerifyOrExit(newTxn != nullptr);
+    VerifyOrExit(newTxn != nullptr, mCounters.mUpstreamDnsCounters.mFailures++);
 
     newTxn->Init(aMessageInfo);
     LogInfo("Upstream query transaction %d initialized.", static_cast<int>(newTxn - mUpstreamQueryTransactions));
@@ -905,6 +909,7 @@
     VerifyOrExit(txn != nullptr, error = kErrorNoBufs);
 
     otPlatDnsStartUpstreamQuery(&GetInstance(), txn, aRequest.mMessage);
+    mCounters.mUpstreamDnsCounters.mQueries++;
 
 exit:
     return error;
@@ -915,7 +920,7 @@
 {
     ProxyQuery    *query;
     ProxyQueryInfo info;
-    DnsName        name;
+    Name::Buffer   name;
 
     VerifyOrExit(mQuerySubscribe.IsSet());
 
@@ -950,11 +955,11 @@
     return;
 }
 
-void Server::ReadQueryName(const Message &aQuery, DnsName &aName)
+void Server::ReadQueryName(const Message &aQuery, Name::Buffer &aName)
 {
     uint16_t offset = sizeof(Header);
 
-    IgnoreError(Name::ReadName(aQuery, offset, aName, sizeof(aName)));
+    IgnoreError(Name::ReadName(aQuery, offset, aName));
 }
 
 bool Server::QueryNameMatches(const Message &aQuery, const char *aName)
@@ -979,20 +984,20 @@
     aQuery.Write(aQuery.GetLength() - sizeof(ProxyQueryInfo), *this);
 }
 
-Error Server::Response::ExtractServiceInstanceLabel(const char *aInstanceName, DnsLabel &aLabel)
+Error Server::Response::ExtractServiceInstanceLabel(const char *aInstanceName, Name::LabelBuffer &aLabel)
 {
-    uint16_t offset;
-    DnsName  serviceName;
+    uint16_t     offset;
+    Name::Buffer serviceName;
 
     offset = mOffsets.mServiceName;
-    IgnoreError(Name::ReadName(*mMessage, offset, serviceName, sizeof(serviceName)));
+    IgnoreError(Name::ReadName(*mMessage, offset, serviceName));
 
-    return Name::ExtractLabels(aInstanceName, serviceName, aLabel, sizeof(aLabel));
+    return Name::ExtractLabels(aInstanceName, serviceName, aLabel);
 }
 
 void Server::RemoveQueryAndPrepareResponse(ProxyQuery &aQuery, const ProxyQueryInfo &aInfo, Response &aResponse)
 {
-    DnsName name;
+    Name::Buffer name;
 
     mProxyQueries.Dequeue(aQuery);
     aInfo.RemoveFrom(aQuery);
@@ -1021,7 +1026,7 @@
 
     if (mType == kPtrQuery)
     {
-        DnsLabel instanceLabel;
+        Name::LabelBuffer instanceLabel;
 
         SuccessOrExit(error = ExtractServiceInstanceLabel(aInstanceInfo.mFullName, instanceLabel));
         mSection = kAnswerSection;
@@ -1148,7 +1153,7 @@
     return (query == nullptr) ? mProxyQueries.GetHead() : query->GetNext();
 }
 
-Server::DnsQueryType Server::GetQueryTypeAndName(const otDnssdQuery *aQuery, char (&aName)[Name::kMaxNameSize])
+Server::DnsQueryType Server::GetQueryTypeAndName(const otDnssdQuery *aQuery, Dns::Name::Buffer &aName)
 {
     const ProxyQuery *query = static_cast<const ProxyQuery *>(aQuery);
     ProxyQueryInfo    info;
@@ -1277,10 +1282,12 @@
     OT_UNUSED_VARIABLE(index);
     if (aError == kErrorNone)
     {
+        mCounters.mUpstreamDnsCounters.mResponses++;
         LogInfo("Upstream query transaction %d completed.", index);
     }
     else
     {
+        mCounters.mUpstreamDnsCounters.mFailures++;
         LogWarn("Upstream query transaction %d closed: %s.", index, ErrorToString(aError));
     }
     aTxn.Reset();
diff --git a/src/core/net/dnssd_server.hpp b/src/core/net/dnssd_server.hpp
index de01d55..3b6b679 100644
--- a/src/core/net/dnssd_server.hpp
+++ b/src/core/net/dnssd_server.hpp
@@ -258,7 +258,7 @@
      * @returns The DNS-SD query type.
      *
      */
-    static DnsQueryType GetQueryTypeAndName(const otDnssdQuery *aQuery, char (&aName)[Name::kMaxNameSize]);
+    static DnsQueryType GetQueryTypeAndName(const otDnssdQuery *aQuery, Dns::Name::Buffer &aName);
 
     /**
      * Returns the counters of the DNS-SD server.
@@ -297,8 +297,6 @@
     static constexpr uint16_t kMaxConcurrentUpstreamQueries = 32;
 
     typedef Header::Response ResponseCode;
-    typedef char             DnsName[Name::kMaxNameSize];
-    typedef char             DnsLabel[Name::kMaxLabelSize];
 
     typedef Message      ProxyQuery;
     typedef MessageQueue ProxyQueryList;
@@ -347,7 +345,7 @@
         void         SetResponseCode(ResponseCode aResponseCode) { mHeader.SetResponseCode(aResponseCode); }
         ResponseCode AddQuestionsFrom(const Request &aRequest);
         Error        ParseQueryName(void);
-        void         ReadQueryName(DnsName &aName) const;
+        void         ReadQueryName(Name::Buffer &aName) const;
         bool         QueryNameMatches(const char *aName) const;
         Error        AppendQueryName(void);
         Error        AppendPtrRecord(const char *aInstanceLabel, uint32_t aTtl);
@@ -367,7 +365,7 @@
         void         Send(const Ip6::MessageInfo &aMessageInfo);
         void         Answer(const HostInfo &aHostInfo, const Ip6::MessageInfo &aMessageInfo);
         void         Answer(const ServiceInstanceInfo &aInstanceInfo, const Ip6::MessageInfo &aMessageInfo);
-        Error        ExtractServiceInstanceLabel(const char *aInstanceName, DnsLabel &aLabel);
+        Error        ExtractServiceInstanceLabel(const char *aInstanceName, Name::LabelBuffer &aLabel);
 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
         Error ResolveBySrp(void);
         bool  QueryNameMatchesService(const Srp::Server::Service &aService) const;
@@ -408,7 +406,7 @@
     void        ResolveByProxy(Response &aResponse, const Ip6::MessageInfo &aMessageInfo);
     void        RemoveQueryAndPrepareResponse(ProxyQuery &aQuery, const ProxyQueryInfo &aInfo, Response &aResponse);
     void        Finalize(ProxyQuery &aQuery, ResponseCode aResponseCode);
-    static void ReadQueryName(const Message &aQuery, DnsName &aName);
+    static void ReadQueryName(const Message &aQuery, Name::Buffer &aName);
     static bool QueryNameMatches(const Message &aQuery, const char *aName);
 
 #if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE
diff --git a/src/core/net/ip4_types.cpp b/src/core/net/ip4_types.cpp
index 7f03a3e..472dee0 100644
--- a/src/core/net/ip4_types.cpp
+++ b/src/core/net/ip4_types.cpp
@@ -66,6 +66,17 @@
     return error;
 }
 
+Error Address::ExtractFromIp4MappedIp6Address(const Ip6::Address &aIp6Address)
+{
+    Error error = kErrorNone;
+
+    VerifyOrExit(aIp6Address.IsIp4Mapped(), error = kErrorParse);
+    SetBytes(&aIp6Address.GetBytes()[12]);
+
+exit:
+    return error;
+}
+
 void Address::ExtractFromIp6Address(uint8_t aPrefixLength, const Ip6::Address &aIp6Address)
 {
     // The prefix length must be 32, 40, 48, 56, 64, 96. IPv4 bytes are added
diff --git a/src/core/net/ip4_types.hpp b/src/core/net/ip4_types.hpp
index 8095a81..169fe3d 100644
--- a/src/core/net/ip4_types.hpp
+++ b/src/core/net/ip4_types.hpp
@@ -123,6 +123,17 @@
     void SetBytes(const uint8_t *aBuffer) { memcpy(mFields.m8, aBuffer, kSize); }
 
     /**
+     * Sets the IPv4 address from a given IPv4-mapped IPv6 address.
+     *
+     * @param[in] aIp6Address  An IPv6 address.
+     *
+     * @retval kErrorNone  Set the IPv4 address successfully.
+     * @retval kErrorPase  The @p aIp6Address does not follow the IPv4-mapped IPv6 address format.
+     *
+     */
+    Error ExtractFromIp4MappedIp6Address(const Ip6::Address &aIp6Address);
+
+    /**
      * Sets the IPv4 address by performing NAT64 address translation from a given IPv6 address as specified
      * in RFC 6052.
      *
diff --git a/src/core/net/ip6.cpp b/src/core/net/ip6.cpp
index d5ddeec..d5b2e27 100644
--- a/src/core/net/ip6.cpp
+++ b/src/core/net/ip6.cpp
@@ -616,7 +616,7 @@
     return error;
 }
 
-Error Ip6::HandleFragment(Message &aMessage, MessageInfo &aMessageInfo)
+Error Ip6::HandleFragment(Message &aMessage)
 {
     Error          error = kErrorNone;
     Header         header, headerBuffer;
@@ -703,8 +703,7 @@
 
         mReassemblyList.Dequeue(*message);
 
-        IgnoreError(HandleDatagram(OwnedPtr<Message>(message), aMessageInfo.mLinkInfo,
-                                   /* aIsReassembled */ true));
+        IgnoreError(HandleDatagram(OwnedPtr<Message>(message), /* aIsReassembled */ true));
     }
 
 exit:
@@ -715,7 +714,7 @@
             mReassemblyList.DequeueAndFree(*message);
         }
 
-        LogWarn("Reassembly failed: %s", ErrorToString(error));
+        LogWarnOnError(error, "reassemble");
     }
 
     if (isFragmented)
@@ -766,16 +765,12 @@
     messageInfo.SetPeerAddr(header.GetSource());
     messageInfo.SetSockAddr(header.GetDestination());
     messageInfo.SetHopLimit(header.GetHopLimit());
-    messageInfo.SetLinkInfo(nullptr);
 
     error = mIcmp.SendError(aIcmpType, aIcmpCode, messageInfo, aMessage);
 
 exit:
-
-    if (error != kErrorNone)
-    {
-        LogWarn("Failed to send ICMP error: %s", ErrorToString(error));
-    }
+    LogWarnOnError(error, "send ICMP");
+    OT_UNUSED_VARIABLE(error);
 }
 
 #else
@@ -788,10 +783,8 @@
     return kErrorNone;
 }
 
-Error Ip6::HandleFragment(Message &aMessage, MessageInfo &aMessageInfo)
+Error Ip6::HandleFragment(Message &aMessage)
 {
-    OT_UNUSED_VARIABLE(aMessageInfo);
-
     Error          error = kErrorNone;
     FragmentHeader fragmentHeader;
 
@@ -829,7 +822,7 @@
         case kProtoFragment:
             IgnoreError(PassToHost(aMessagePtr, aMessageInfo, aNextHeader,
                                    /* aApplyFilter */ false, aReceive, Message::kCopyToUse));
-            SuccessOrExit(error = HandleFragment(*aMessagePtr, aMessageInfo));
+            SuccessOrExit(error = HandleFragment(*aMessagePtr));
             break;
 
         case kProtoDstOpts:
@@ -920,11 +913,7 @@
     }
 
 exit:
-    if (error != kErrorNone)
-    {
-        LogNote("Failed to handle payload: %s", ErrorToString(error));
-    }
-
+    LogWarnOnError(error, "handle payload");
     return error;
 }
 
@@ -959,17 +948,6 @@
 
     if (mIsReceiveIp6FilterEnabled && aApplyFilter)
     {
-#if !OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE
-        // Do not pass messages sent to an RLOC/ALOC, except
-        // Service Locator
-
-        bool isLocator = Get<Mle::Mle>().IsMeshLocalAddress(aMessageInfo.GetSockAddr()) &&
-                         aMessageInfo.GetSockAddr().GetIid().IsLocator();
-
-        VerifyOrExit(!isLocator || aMessageInfo.GetSockAddr().GetIid().IsAnycastServiceLocator(),
-                     error = kErrorNoRoute);
-#endif
-
         switch (aIpProto)
         {
         case kProtoIcmp6:
@@ -1037,6 +1015,19 @@
     }
 #endif
 
+#if OPENTHREAD_CONFIG_IP6_RESTRICT_FORWARDING_LARGER_SCOPE_MCAST_WITH_LOCAL_SRC
+    // Some platforms (e.g. Android) currently doesn't restrict link-local/mesh-local source
+    // addresses when forwarding multicast packets.
+    // For a multicast packet sent from link-local/mesh-local address to scope larger
+    // than realm-local, set the hop limit to 1 before sending to host, so this packet
+    // will not be forwarded by host.
+    if (aMessageInfo.GetSockAddr().IsMulticastLargerThanRealmLocal() &&
+        (aMessageInfo.GetPeerAddr().IsLinkLocal() || (Get<Mle::Mle>().IsMeshLocalAddress(aMessageInfo.GetPeerAddr()))))
+    {
+        messagePtr->Write<uint8_t>(Header::kHopLimitFieldOffset, 1);
+    }
+#endif
+
     // Pass message to callback transferring its ownership.
     mReceiveIp6DatagramCallback.Invoke(messagePtr.Release());
 
@@ -1081,7 +1072,7 @@
     return error;
 }
 
-Error Ip6::HandleDatagram(OwnedPtr<Message> aMessagePtr, const void *aLinkMessageInfo, bool aIsReassembled)
+Error Ip6::HandleDatagram(OwnedPtr<Message> aMessagePtr, bool aIsReassembled)
 {
     Error       error;
     MessageInfo messageInfo;
@@ -1102,7 +1093,6 @@
     messageInfo.SetSockAddr(header.GetDestination());
     messageInfo.SetHopLimit(header.GetHopLimit());
     messageInfo.SetEcn(header.GetEcn());
-    messageInfo.SetLinkInfo(aLinkMessageInfo);
 
     // Determine `forwardThread`, `forwardHost` and `receive`
     // based on the destination address.
@@ -1121,7 +1111,8 @@
         }
 #endif
 
-        forwardHost = header.GetDestination().IsMulticastLargerThanRealmLocal();
+        // Always forward multicast packets to host network stack
+        forwardHost = true;
 
         if ((aMessagePtr->IsOriginThreadNetif() || aMessagePtr->GetMulticastLoop()) &&
             Get<ThreadNetif>().IsMulticastSubscribed(header.GetDestination()))
@@ -1189,7 +1180,7 @@
 
         Get<MeshForwarder>().LogMessage(MeshForwarder::kMessageReceive, *messagePtr);
 
-        IgnoreError(HandleDatagram(messagePtr.PassOwnership(), aLinkMessageInfo, aIsReassembled));
+        IgnoreError(HandleDatagram(messagePtr.PassOwnership(), aIsReassembled));
 
         receive     = false;
         forwardHost = false;
@@ -1308,6 +1299,7 @@
 
     for (const Netif::UnicastAddress &addr : Get<ThreadNetif>().GetUnicastAddresses())
     {
+        bool    newAddrIsPreferred = false;
         uint8_t matchLen;
         uint8_t overrideScope;
 
@@ -1329,80 +1321,55 @@
             overrideScope = destScope;
         }
 
-        if (bestAddr == nullptr)
-        {
-            // Rule 0: Prefer any address
-            bestAddr     = &addr;
-            bestMatchLen = matchLen;
-        }
-        else if (addr.GetAddress() == aDestination)
+        if (addr.GetAddress() == aDestination)
         {
             // Rule 1: Prefer same address
             bestAddr = &addr;
             ExitNow();
         }
+
+        if (bestAddr == nullptr)
+        {
+            newAddrIsPreferred = true;
+        }
         else if (addr.GetScope() < bestAddr->GetScope())
         {
             // Rule 2: Prefer appropriate scope
-            if (addr.GetScope() >= overrideScope)
-            {
-                bestAddr     = &addr;
-                bestMatchLen = matchLen;
-            }
-            else
-            {
-                continue;
-            }
+            newAddrIsPreferred = (addr.GetScope() >= overrideScope);
         }
         else if (addr.GetScope() > bestAddr->GetScope())
         {
-            if (bestAddr->GetScope() < overrideScope)
-            {
-                bestAddr     = &addr;
-                bestMatchLen = matchLen;
-            }
-            else
-            {
-                continue;
-            }
+            newAddrIsPreferred = (bestAddr->GetScope() < overrideScope);
         }
         else if (addr.mPreferred != bestAddr->mPreferred)
         {
             // Rule 3: Avoid deprecated addresses
-
-            if (addr.mPreferred)
-            {
-                bestAddr     = &addr;
-                bestMatchLen = matchLen;
-            }
-            else
-            {
-                continue;
-            }
+            newAddrIsPreferred = addr.mPreferred;
         }
         else if (matchLen > bestMatchLen)
         {
             // Rule 6: Prefer matching label
             // Rule 7: Prefer public address
             // Rule 8: Use longest prefix matching
-            bestAddr     = &addr;
-            bestMatchLen = matchLen;
+
+            newAddrIsPreferred = true;
         }
         else if ((matchLen == bestMatchLen) && (destIsRloc == Get<Mle::Mle>().IsRoutingLocator(addr.GetAddress())))
         {
             // Additional rule: Prefer RLOC source for RLOC destination, EID source for anything else
-            bestAddr     = &addr;
-            bestMatchLen = matchLen;
-        }
-        else
-        {
-            continue;
+            newAddrIsPreferred = true;
         }
 
-        // infer destination scope based on prefix match
-        if (bestMatchLen >= bestAddr->mPrefixLength)
+        if (newAddrIsPreferred)
         {
-            destScope = bestAddr->GetScope();
+            bestAddr     = &addr;
+            bestMatchLen = matchLen;
+
+            // Infer destination scope based on prefix match
+            if (bestMatchLen >= bestAddr->mPrefixLength)
+            {
+                destScope = bestAddr->GetScope();
+            }
         }
     }
 
@@ -1456,7 +1423,9 @@
 #if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE
 void Ip6::UpdateBorderRoutingCounters(const Header &aHeader, uint16_t aMessageLength, bool aIsInbound)
 {
-    otPacketsAndBytes *counter = nullptr;
+    static constexpr uint8_t kPrefixLength   = 48;
+    otPacketsAndBytes       *counter         = nullptr;
+    otPacketsAndBytes       *internetCounter = nullptr;
 
     VerifyOrExit(!aHeader.GetSource().IsLinkLocal());
     VerifyOrExit(!aHeader.GetDestination().IsLinkLocal());
@@ -1466,7 +1435,10 @@
     if (aIsInbound)
     {
         VerifyOrExit(!Get<Netif>().HasUnicastAddress(aHeader.GetSource()));
-
+        if (!aHeader.GetSource().MatchesPrefix(aHeader.GetDestination().GetPrefix().m8, kPrefixLength))
+        {
+            internetCounter = &mBorderRoutingCounters.mInboundInternet;
+        }
         if (aHeader.GetDestination().IsMulticast())
         {
             VerifyOrExit(aHeader.GetDestination().IsMulticastLargerThanRealmLocal());
@@ -1480,7 +1452,10 @@
     else
     {
         VerifyOrExit(!Get<Netif>().HasUnicastAddress(aHeader.GetDestination()));
-
+        if (!aHeader.GetSource().MatchesPrefix(aHeader.GetDestination().GetPrefix().m8, kPrefixLength))
+        {
+            internetCounter = &mBorderRoutingCounters.mOutboundInternet;
+        }
         if (aHeader.GetDestination().IsMulticast())
         {
             VerifyOrExit(aHeader.GetDestination().IsMulticastLargerThanRealmLocal());
@@ -1499,6 +1474,11 @@
         counter->mPackets += 1;
         counter->mBytes += aMessageLength;
     }
+    if (internetCounter)
+    {
+        internetCounter->mPackets += 1;
+        internetCounter->mBytes += aMessageLength;
+    }
 }
 #endif
 
diff --git a/src/core/net/ip6.hpp b/src/core/net/ip6.hpp
index 1c700b5..de04eef 100644
--- a/src/core/net/ip6.hpp
+++ b/src/core/net/ip6.hpp
@@ -207,7 +207,6 @@
      * Processes a received IPv6 datagram.
      *
      * @param[in]  aMessage          An owned pointer to a message.
-     * @param[in]  aLinkMessageInfo  A pointer to link-specific message information.
      *
      * @retval kErrorNone     Successfully processed the message.
      * @retval kErrorDrop     Message was well-formed but not fully processed due to packet processing rules.
@@ -216,9 +215,7 @@
      * @retval kErrorParse    Encountered a malformed header when processing the message.
      *
      */
-    Error HandleDatagram(OwnedPtr<Message> aMessagePtr,
-                         const void       *aLinkMessageInfo = nullptr,
-                         bool              aIsReassembled   = false);
+    Error HandleDatagram(OwnedPtr<Message> aMessagePtr, bool aIsReassembled = false);
 
     /**
      * Registers a callback to provide received raw IPv6 datagrams.
@@ -350,7 +347,7 @@
      * Resets the Border Routing counters.
      *
      */
-    void ResetBorderRoutingCounters(void) { memset(&mBorderRoutingCounters, 0, sizeof(mBorderRoutingCounters)); }
+    void ResetBorderRoutingCounters(void) { ClearAllBytes(mBorderRoutingCounters); }
 #endif
 
 private:
@@ -378,7 +375,7 @@
                                  uint8_t           &aNextHeader,
                                  bool              &aReceive);
     Error FragmentDatagram(Message &aMessage, uint8_t aIpProto);
-    Error HandleFragment(Message &aMessage, MessageInfo &aMessageInfo);
+    Error HandleFragment(Message &aMessage);
 #if OPENTHREAD_CONFIG_IP6_FRAGMENTATION_ENABLE
     void CleanupFragmentationBuffer(void);
     void HandleTimeTick(void);
diff --git a/src/core/net/ip6_address.cpp b/src/core/net/ip6_address.cpp
index 4c09226..8e5cdc8 100644
--- a/src/core/net/ip6_address.cpp
+++ b/src/core/net/ip6_address.cpp
@@ -388,6 +388,18 @@
 
 void Address::SetToRealmLocalAllMplForwarders(void) { *this = GetRealmLocalAllMplForwarders(); }
 
+bool Address::IsIp4Mapped(void) const
+{
+    return (mFields.m32[0] == 0) && (mFields.m32[1] == 0) && (mFields.m32[2] == BigEndian::HostSwap32(0xffff));
+}
+
+void Address::SetToIp4Mapped(const Ip4::Address &aIp4Address)
+{
+    Clear();
+    mFields.m16[5] = 0xffff;
+    memcpy(&mFields.m8[12], aIp4Address.GetBytes(), sizeof(Ip4::Address));
+}
+
 bool Address::MatchesPrefix(const Prefix &aPrefix) const
 {
     return Prefix::MatchLength(mFields.m8, aPrefix.GetBytes(), aPrefix.GetBytesSize()) >= aPrefix.GetLength();
diff --git a/src/core/net/ip6_address.hpp b/src/core/net/ip6_address.hpp
index b066557..d7dfe49 100644
--- a/src/core/net/ip6_address.hpp
+++ b/src/core/net/ip6_address.hpp
@@ -809,6 +809,27 @@
     }
 
     /**
+     * Indicates whether or not the IPv6 address follows the IPv4-mapped format.
+     *
+     * An IPv4-mapped IPv6 address consists of an 80-bit prefix of zeros, the next 16 bits set to  ones, and the
+     * remaining, least-significant 32 bits contain the IPv4 address, e.g., `::ffff:192.0.2.128` representing
+     * `192.0.2.128` IPv4 address.
+     *
+     * @retval TRUE   If the IPv6 address follows the IPv4-mapped format.
+     * @retval FALSE  If the IPv6 address does not follow the IPv4-mapped format.
+     *
+     */
+    bool IsIp4Mapped(void) const;
+
+    /**
+     * Sets the IPv6 address to follow the IPv4-mapped IPv6 address for a given IPv4 address.
+     *
+     * @param[in] aIp4Address  An IPv4 address.
+     *
+     */
+    void SetToIp4Mapped(const Ip4::Address &aIp4Address);
+
+    /**
      * Returns the Network Prefix of the IPv6 address (most significant 64 bits of the address).
      *
      * @returns A reference to the Network Prefix.
diff --git a/src/core/net/ip6_mpl.cpp b/src/core/net/ip6_mpl.cpp
index 78922ef..61be9ea 100644
--- a/src/core/net/ip6_mpl.cpp
+++ b/src/core/net/ip6_mpl.cpp
@@ -52,7 +52,7 @@
     , mRetransmissionTimer(aInstance)
 #endif
 {
-    memset(mSeedSet, 0, sizeof(mSeedSet));
+    ClearAllBytes(mSeedSet);
 }
 
 void MplOption::Init(SeedIdLength aSeedIdLength)
diff --git a/src/core/net/nd6.cpp b/src/core/net/nd6.cpp
index e66fb04..d73aea1 100644
--- a/src/core/net/nd6.cpp
+++ b/src/core/net/nd6.cpp
@@ -36,6 +36,7 @@
 
 #include "common/as_core_type.hpp"
 #include "common/code_utils.hpp"
+#include "instance/instance.hpp"
 
 namespace ot {
 namespace Ip6 {
@@ -181,9 +182,9 @@
 }
 
 //----------------------------------------------------------------------------------------------------------------------
-// RouterAdverMessage::Header
+// RouterAdver::Header
 
-void RouterAdvertMessage::Header::SetToDefault(void)
+void RouterAdvert::Header::SetToDefault(void)
 {
     OT_UNUSED_VARIABLE(mCode);
     OT_UNUSED_VARIABLE(mCurHopLimit);
@@ -194,21 +195,21 @@
     mType = Icmp::Header::kTypeRouterAdvert;
 }
 
-RoutePreference RouterAdvertMessage::Header::GetDefaultRouterPreference(void) const
+RoutePreference RouterAdvert::Header::GetDefaultRouterPreference(void) const
 {
     return NetworkData::RoutePreferenceFromValue((mFlags & kPreferenceMask) >> kPreferenceOffset);
 }
 
-void RouterAdvertMessage::Header::SetDefaultRouterPreference(RoutePreference aPreference)
+void RouterAdvert::Header::SetDefaultRouterPreference(RoutePreference aPreference)
 {
     mFlags &= ~kPreferenceMask;
     mFlags |= (NetworkData::RoutePreferenceToValue(aPreference) << kPreferenceOffset) & kPreferenceMask;
 }
 
 //----------------------------------------------------------------------------------------------------------------------
-// RouterAdverMessage
+// RouterAdver::TxMessage
 
-Option *RouterAdvertMessage::AppendOption(uint16_t aOptionSize)
+Option *RouterAdvert::TxMessage::AppendOption(uint16_t aOptionSize)
 {
     // This method appends an option with a given size to the RA
     // message by reserving space in the data buffer if there is
@@ -217,21 +218,39 @@
     // initialized and populated by the caller.
 
     Option  *option    = nullptr;
-    uint32_t newLength = mData.GetLength();
+    uint16_t oldLength = mArray.GetLength();
 
-    newLength += aOptionSize;
-    VerifyOrExit(newLength <= mMaxLength);
-
-    option = reinterpret_cast<Option *>(AsNonConst(GetDataEnd()));
-    mData.SetLength(static_cast<uint16_t>(newLength));
+    SuccessOrExit(AppendBytes(nullptr, aOptionSize));
+    option = reinterpret_cast<Option *>(&mArray[oldLength]);
 
 exit:
     return option;
 }
 
-Error RouterAdvertMessage::AppendPrefixInfoOption(const Prefix &aPrefix,
-                                                  uint32_t      aValidLifetime,
-                                                  uint32_t      aPreferredLifetime)
+Error RouterAdvert::TxMessage::AppendBytes(const uint8_t *aBytes, uint16_t aLength)
+{
+    Error error = kErrorNone;
+
+    for (; aLength > 0; aLength--)
+    {
+        uint8_t byte;
+
+        byte = (aBytes == nullptr) ? 0 : *aBytes++;
+        SuccessOrExit(error = mArray.PushBack(byte));
+    }
+
+exit:
+    return error;
+}
+
+Error RouterAdvert::TxMessage::AppendHeader(const Header &aHeader)
+{
+    return AppendBytes(reinterpret_cast<const uint8_t *>(&aHeader), sizeof(Header));
+}
+
+Error RouterAdvert::TxMessage::AppendPrefixInfoOption(const Prefix &aPrefix,
+                                                      uint32_t      aValidLifetime,
+                                                      uint32_t      aPreferredLifetime)
 {
     Error             error = kErrorNone;
     PrefixInfoOption *pio;
@@ -250,9 +269,9 @@
     return error;
 }
 
-Error RouterAdvertMessage::AppendRouteInfoOption(const Prefix   &aPrefix,
-                                                 uint32_t        aRouteLifetime,
-                                                 RoutePreference aPreference)
+Error RouterAdvert::TxMessage::AppendRouteInfoOption(const Prefix   &aPrefix,
+                                                     uint32_t        aRouteLifetime,
+                                                     RoutePreference aPreference)
 {
     Error            error = kErrorNone;
     RouteInfoOption *rio;
@@ -269,7 +288,7 @@
     return error;
 }
 
-Error RouterAdvertMessage::AppendFlagsExtensionOption(bool aStubRouterFlag)
+Error RouterAdvert::TxMessage::AppendFlagsExtensionOption(bool aStubRouterFlag)
 {
     Error             error = kErrorNone;
     RaFlagsExtOption *flagsOption;
diff --git a/src/core/net/nd6.hpp b/src/core/net/nd6.hpp
index f4a6d5a..4178f18 100644
--- a/src/core/net/nd6.hpp
+++ b/src/core/net/nd6.hpp
@@ -47,6 +47,7 @@
 #include "common/const_cast.hpp"
 #include "common/encoding.hpp"
 #include "common/equatable.hpp"
+#include "common/heap_array.hpp"
 #include "net/icmp6.hpp"
 #include "net/ip6.hpp"
 #include "thread/network_data_types.hpp"
@@ -67,7 +68,7 @@
 OT_TOOL_PACKED_BEGIN
 class Option
 {
-    friend class RouterAdvertMessage;
+    friend class RouterAdvert;
 
 public:
     enum Type : uint8_t
@@ -525,14 +526,14 @@
 static_assert(sizeof(RaFlagsExtOption) == 8, "invalid RaFlagsExtOption structure");
 
 /**
- * Represents a Router Advertisement message.
+ * Defines Router Advertisement components.
  *
  */
-class RouterAdvertMessage
+class RouterAdvert
 {
 public:
     /**
-     * Implements the RA message header.
+     * Represent an RA message header.
      *
      * See section 2.2 of RFC 4191 [https://datatracker.ietf.org/doc/html/rfc4191]
      *
@@ -673,135 +674,170 @@
     typedef Data<kWithUint16Length> Icmp6Packet; ///< A data buffer containing an ICMPv6 packet.
 
     /**
-     * Initializes the RA message from a received packet data buffer.
-     *
-     * @param[in] aPacket   A received packet data.
+     * Represents a received RA message.
      *
      */
-    explicit RouterAdvertMessage(const Icmp6Packet &aPacket)
-        : mData(aPacket)
-        , mMaxLength(0)
+    class RxMessage
     {
-    }
+    public:
+        /**
+         * Initializes the RA message from a received packet data buffer.
+         *
+         * @param[in] aPacket   A received packet data.
+         *
+         */
+        explicit RxMessage(const Icmp6Packet &aPacket)
+            : mData(aPacket)
+        {
+        }
+
+        /**
+         * Gets the RA message as an `Icmp6Packet`.
+         *
+         * @returns The RA message as an `Icmp6Packet`.
+         *
+         */
+        const Icmp6Packet &GetAsPacket(void) const { return mData; }
+
+        /**
+         * Indicates whether or not the received RA message is valid.
+         *
+         * @retval TRUE   If the RA message is valid.
+         * @retval FALSE  If the RA message is not valid.
+         *
+         */
+        bool IsValid(void) const
+        {
+            return (mData.GetBytes() != nullptr) && (mData.GetLength() >= sizeof(Header)) &&
+                   (GetHeader().GetType() == Icmp::Header::kTypeRouterAdvert);
+        }
+
+        /**
+         * Gets the RA message's header.
+         *
+         * @returns The RA message's header.
+         *
+         */
+        const Header &GetHeader(void) const { return *reinterpret_cast<const Header *>(mData.GetBytes()); }
+
+        /**
+         * Indicates whether or not the received RA message contains any options.
+         *
+         * @retval TRUE   If the RA message contains at least one option.
+         * @retval FALSE  If the RA message contains no options.
+         *
+         */
+        bool ContainsAnyOptions(void) const { return (mData.GetLength() > sizeof(Header)); }
+
+        // The following methods are intended to support range-based `for`
+        // loop iteration over `Option`s in the RA message.
+
+        Option::Iterator begin(void) const { return Option::Iterator(GetOptionStart(), GetDataEnd()); }
+        Option::Iterator end(void) const { return Option::Iterator(); }
+
+    private:
+        const uint8_t *GetOptionStart(void) const { return (mData.GetBytes() + sizeof(Header)); }
+        const uint8_t *GetDataEnd(void) const { return mData.GetBytes() + mData.GetLength(); }
+
+        Data<kWithUint16Length> mData;
+    };
 
     /**
-     * This template constructor initializes the RA message with a given header using a given buffer to store the RA
-     * message.
-     *
-     * @tparam kBufferSize   The size of the buffer used to store the RA message.
-     *
-     * @param[in] aHeader    The RA message header.
-     * @param[in] aBuffer    The data buffer to store the RA message in.
+     * Represents an RA message to be sent.
      *
      */
-    template <uint16_t kBufferSize>
-    RouterAdvertMessage(const Header &aHeader, uint8_t (&aBuffer)[kBufferSize])
-        : mMaxLength(kBufferSize)
+    class TxMessage
     {
-        static_assert(kBufferSize >= sizeof(Header), "Buffer for RA msg is too small");
+    public:
+        /**
+         * Gets the prepared RA message as an `Icmp6Packet`.
+         *
+         * @param[out] aPacket   A reference to an `Icmp6Packet`.
+         *
+         */
+        void GetAsPacket(Icmp6Packet &aPacket) const { aPacket.Init(mArray.AsCArray(), mArray.GetLength()); }
 
-        memcpy(aBuffer, &aHeader, sizeof(Header));
-        mData.Init(aBuffer, sizeof(Header));
-    }
+        /**
+         * Appends the RA header.
+         *
+         * @param[in] aHeader  The RA header.
+         *
+         * @retval kErrorNone    Header is written successfully.
+         * @retval kErrorNoBufs  Insufficient available buffers to grow the message.
+         *
+         */
+        Error AppendHeader(const Header &aHeader);
 
-    /**
-     * Gets the RA message as an `Icmp6Packet`.
-     *
-     * @returns The RA message as an `Icmp6Packet`.
-     *
-     */
-    const Icmp6Packet &GetAsPacket(void) const { return mData; }
+        /**
+         * Appends a Prefix Info Option to the RA message.
+         *
+         * The appended Prefix Info Option will have both on-link (L) and autonomous address-configuration (A) flags
+         * set.
+         *
+         * @param[in] aPrefix             The prefix.
+         * @param[in] aValidLifetime      The valid lifetime in seconds.
+         * @param[in] aPreferredLifetime  The preferred lifetime in seconds.
+         *
+         * @retval kErrorNone    Option is appended successfully.
+         * @retval kErrorNoBufs  Insufficient available buffers to grow the message.
+         *
+         */
+        Error AppendPrefixInfoOption(const Prefix &aPrefix, uint32_t aValidLifetime, uint32_t aPreferredLifetime);
 
-    /**
-     * Indicates whether or not the RA message is valid.
-     *
-     * @retval TRUE   If the RA message is valid.
-     * @retval FALSE  If the RA message is not valid.
-     *
-     */
-    bool IsValid(void) const
-    {
-        return (mData.GetBytes() != nullptr) && (mData.GetLength() >= sizeof(Header)) &&
-               (GetHeader().GetType() == Icmp::Header::kTypeRouterAdvert);
-    }
+        /**
+         * Appends a Route Info Option to the RA message.
+         *
+         * @param[in] aPrefix             The prefix.
+         * @param[in] aRouteLifetime      The route lifetime in seconds.
+         * @param[in] aPreference         The route preference.
+         *
+         * @retval kErrorNone    Option is appended successfully.
+         * @retval kErrorNoBufs  Insufficient available buffers to grow the message.
+         *
+         */
+        Error AppendRouteInfoOption(const Prefix &aPrefix, uint32_t aRouteLifetime, RoutePreference aPreference);
 
-    /**
-     * Gets the RA message's header.
-     *
-     * @returns The RA message's header.
-     *
-     */
-    const Header &GetHeader(void) const { return *reinterpret_cast<const Header *>(mData.GetBytes()); }
+        /**
+         * Appends a Flags Extension Option to the RA message.
+         *
+         * @param[in] aStubRouterFlag    The stub router flag.
+         *
+         * @retval kErrorNone    Option is appended successfully.
+         * @retval kErrorNoBufs  Insufficient available buffers to grow the message.
+         *
+         */
+        Error AppendFlagsExtensionOption(bool aStubRouterFlag);
 
-    /**
-     * Gets the RA message's header.
-     *
-     * @returns The RA message's header.
-     *
-     */
-    Header &GetHeader(void) { return *reinterpret_cast<Header *>(AsNonConst(mData.GetBytes())); }
+        /**
+         * Appends bytes from a given buffer to the RA message.
+         *
+         * @param[in] aBytes     A pointer to the buffer containing the bytes to append.
+         * @param[in] aLength    The buffer length.
+         *
+         * @retval kErrorNone    Bytes are appended successfully.
+         * @retval kErrorNoBufs  Insufficient available buffers to grow the message.
+         *
+         */
+        Error AppendBytes(const uint8_t *aBytes, uint16_t aLength);
 
-    /**
-     * Appends a Prefix Info Option to the RA message.
-     *
-     * The appended Prefix Info Option will have both on-link (L) and autonomous address-configuration (A) flags set.
-     *
-     * @param[in] aPrefix             The prefix.
-     * @param[in] aValidLifetime      The valid lifetime in seconds.
-     * @param[in] aPreferredLifetime  The preferred lifetime in seconds.
-     *
-     * @retval kErrorNone    Option is appended successfully.
-     * @retval kErrorNoBufs  No more space in the buffer to append the option.
-     *
-     */
-    Error AppendPrefixInfoOption(const Prefix &aPrefix, uint32_t aValidLifetime, uint32_t aPreferredLifetime);
+        /**
+         * Indicates whether or not the received RA message contains any options.
+         *
+         * @retval TRUE   If the RA message contains at least one option.
+         * @retval FALSE  If the RA message contains no options.
+         *
+         */
+        bool ContainsAnyOptions(void) const { return (mArray.GetLength() > sizeof(Header)); }
 
-    /**
-     * Appends a Route Info Option to the RA message.
-     *
-     * @param[in] aPrefix             The prefix.
-     * @param[in] aRouteLifetime      The route lifetime in seconds.
-     * @param[in] aPreference         The route preference.
-     *
-     * @retval kErrorNone    Option is appended successfully.
-     * @retval kErrorNoBufs  No more space in the buffer to append the option.
-     *
-     */
-    Error AppendRouteInfoOption(const Prefix &aPrefix, uint32_t aRouteLifetime, RoutePreference aPreference);
+    private:
+        static constexpr uint16_t kCapacityIncrement = 256;
 
-    /**
-     * Appends a Flags Extension Option to the RA message.
-     *
-     * @param[in] aStubRouterFlag    The stub router flag.
-     *
-     * @retval kErrorNone    Option is appended successfully.
-     * @retval kErrorNoBufs  No more space in the buffer to append the option.
-     *
-     */
-    Error AppendFlagsExtensionOption(bool aStubRouterFlag);
+        Option *AppendOption(uint16_t aOptionSize);
 
-    /**
-     * Indicates whether or not the RA message contains any options.
-     *
-     * @retval TRUE   If the RA message contains at least one option.
-     * @retval FALSE  If the RA message contains no options.
-     *
-     */
-    bool ContainsAnyOptions(void) const { return (mData.GetLength() > sizeof(Header)); }
+        Heap::Array<uint8_t, kCapacityIncrement> mArray;
+    };
 
-    // The following methods are intended to support range-based `for`
-    // loop iteration over `Option`s in the RA message.
-
-    Option::Iterator begin(void) const { return Option::Iterator(GetOptionStart(), GetDataEnd()); }
-    Option::Iterator end(void) const { return Option::Iterator(); }
-
-private:
-    const uint8_t *GetOptionStart(void) const { return (mData.GetBytes() + sizeof(Header)); }
-    const uint8_t *GetDataEnd(void) const { return mData.GetBytes() + mData.GetLength(); }
-    Option        *AppendOption(uint16_t aOptionSize);
-
-    Data<kWithUint16Length> mData;
-    uint16_t                mMaxLength;
+    RouterAdvert(void) = delete;
 };
 
 /**
diff --git a/src/core/net/netif.cpp b/src/core/net/netif.cpp
index ff39891..3e10331 100644
--- a/src/core/net/netif.cpp
+++ b/src/core/net/netif.cpp
@@ -253,6 +253,7 @@
         info.mPrefixLength = kMulticastPrefixLength;
         info.mScope        = aAddress.GetAddress().GetScope();
         info.mPreferred    = false;
+        info.mMeshLocal    = false;
 
         mAddressCallback.Invoke(&info, aEvent);
     }
@@ -377,15 +378,29 @@
     return;
 }
 
-void Netif::RemoveUnicastAddress(const UnicastAddress &aAddress)
+void Netif::RemoveUnicastAddress(UnicastAddress &aAddress)
 {
     SuccessOrExit(mUnicastAddresses.Remove(aAddress));
+    aAddress.mSrpRegistered = false;
     SignalUnicastAddressChange(kAddressRemoved, aAddress);
 
 exit:
     return;
 }
 
+void Netif::UpdatePreferredFlagOn(UnicastAddress &aAddress, bool aPreferred)
+{
+    VerifyOrExit(HasUnicastAddress(aAddress));
+    VerifyOrExit(aAddress.mPreferred != aPreferred);
+
+    SignalUnicastAddressChange(kAddressRemoved, aAddress);
+    aAddress.mPreferred = aPreferred;
+    SignalUnicastAddressChange(kAddressAdded, aAddress);
+
+exit:
+    return;
+}
+
 void Netif::SignalUnicastAddressChange(AddressEvent aEvent, const UnicastAddress &aAddress)
 {
     Event event;
@@ -413,6 +428,7 @@
         info.mPrefixLength = aAddress.mPrefixLength;
         info.mScope        = aAddress.GetScope();
         info.mPreferred    = aAddress.mPreferred;
+        info.mMeshLocal    = aAddress.mMeshLocal;
 
         mAddressCallback.Invoke(&info, aEvent);
     }
@@ -443,9 +459,10 @@
     entry = mExtUnicastAddressPool.Allocate();
     VerifyOrExit(entry != nullptr, error = kErrorNoBufs);
 
-    *entry            = aAddress;
-    entry->mRloc      = false;
-    entry->mMeshLocal = false;
+    *entry                = aAddress;
+    entry->mRloc          = false;
+    entry->mMeshLocal     = false;
+    entry->mSrpRegistered = false;
 
     mUnicastAddresses.Push(*entry);
     SignalUnicastAddressChange(kAddressAdded, *entry);
@@ -504,6 +521,7 @@
         if (address.mMeshLocal)
         {
             SignalUnicastAddressChange(kAddressRemoved, address);
+            address.mSrpRegistered = false;
             address.GetAddress().SetPrefix(Get<Mle::Mle>().GetMeshLocalPrefix());
             SignalUnicastAddressChange(kAddressAdded, address);
         }
@@ -523,12 +541,12 @@
 //---------------------------------------------------------------------------------------------------------------------
 // Netif::UnicastAddress
 
-void Netif::UnicastAddress::InitAsThreadOrigin(bool aPreferred)
+void Netif::UnicastAddress::InitAsThreadOrigin(void)
 {
     Clear();
     mPrefixLength  = NetworkPrefix::kLength;
     mAddressOrigin = kOriginThread;
-    mPreferred     = aPreferred;
+    mPreferred     = true;
     mValid         = true;
 }
 
diff --git a/src/core/net/netif.hpp b/src/core/net/netif.hpp
index 0e50571..a736df8 100644
--- a/src/core/net/netif.hpp
+++ b/src/core/net/netif.hpp
@@ -116,10 +116,8 @@
          * Clears and initializes the unicast address as a preferred, valid, thread-origin address with
          * 64-bit prefix length.
          *
-         * @param[in]   aPreferred  Whether to initialize as a preferred address.
-         *
          */
-        void InitAsThreadOrigin(bool aPreferred = false);
+        void InitAsThreadOrigin(void);
 
         /**
          * Clears and initializes the unicast address as a valid (but not preferred), thread-origin,
@@ -398,6 +396,14 @@
     const LinkedList<UnicastAddress> &GetUnicastAddresses(void) const { return mUnicastAddresses; }
 
     /**
+     * Returns the linked list of unicast addresses.
+     *
+     * @returns The linked list of unicast addresses.
+     *
+     */
+    LinkedList<UnicastAddress> &GetUnicastAddresses(void) { return mUnicastAddresses; }
+
+    /**
      * Adds a unicast address to the network interface.
      *
      * Is intended for addresses internal to OpenThread. The @p aAddress instance is directly added in the
@@ -421,7 +427,19 @@
      * @param[in]  aAddress  A reference to the unicast address.
      *
      */
-    void RemoveUnicastAddress(const UnicastAddress &aAddress);
+    void RemoveUnicastAddress(UnicastAddress &aAddress);
+
+    /**
+     * Updates the preferred flag on a previously added (internal to OpenThread core) unicast address.
+     *
+     * If the address is not added to the network interface or the current preferred flag of @p aAddress is the same as
+     * the given @p aPreferred, no action is performed.
+     *
+     * @param[in] aAddress        The unicast address
+     * @param[in] aPreferred The new value for preferred flag.
+     *
+     */
+    void UpdatePreferredFlagOn(UnicastAddress &aAddress, bool aPreferred);
 
     /**
      * Indicates whether or not an address is assigned to the interface.
diff --git a/src/core/net/sntp_client.cpp b/src/core/net/sntp_client.cpp
index 5897a5f..7718b02 100644
--- a/src/core/net/sntp_client.cpp
+++ b/src/core/net/sntp_client.cpp
@@ -193,7 +193,7 @@
     if (error != kErrorNone)
     {
         FreeMessage(messageCopy);
-        LogWarn("Failed to send SNTP request: %s", ErrorToString(error));
+        LogWarnOnError(error, "send SNTP request");
     }
 }
 
diff --git a/src/core/net/socket.hpp b/src/core/net/socket.hpp
index fb4e928..e1ca9ac 100644
--- a/src/core/net/socket.hpp
+++ b/src/core/net/socket.hpp
@@ -181,30 +181,6 @@
     void SetMulticastLoop(bool aMulticastLoop) { mMulticastLoop = aMulticastLoop; }
 
     /**
-     * Returns a pointer to the link-specific information object.
-     *
-     * @returns A pointer to the link-specific information object.
-     *
-     */
-    const void *GetLinkInfo(void) const { return mLinkInfo; }
-
-    /**
-     * Sets the pointer to the link-specific information object.
-     *
-     * @param[in]  aLinkInfo  A pointer to the link-specific information object.
-     *
-     */
-    void SetLinkInfo(const void *aLinkInfo) { mLinkInfo = aLinkInfo; }
-
-    /**
-     * Returns a pointer to the link-specific information as a `ThreadLinkInfo`.
-     *
-     * @returns A pointer to to the link-specific information object as `ThreadLinkInfo`.
-     *
-     */
-    const ThreadLinkInfo *GetThreadLinkInfo(void) const { return reinterpret_cast<const ThreadLinkInfo *>(mLinkInfo); }
-
-    /**
      * Gets the ECN status.
      *
      * @returns The ECN status, as represented in the IP header.
diff --git a/src/core/net/srp_advertising_proxy.cpp b/src/core/net/srp_advertising_proxy.cpp
new file mode 100644
index 0000000..8a2451d
--- /dev/null
+++ b/src/core/net/srp_advertising_proxy.cpp
@@ -0,0 +1,1393 @@
+/*
+ *  Copyright (c) 2023, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ *   This file includes implementation of SRP Advertising Proxy.
+ */
+
+#include "srp_advertising_proxy.hpp"
+
+#if OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE
+
+#include "common/as_core_type.hpp"
+#include "common/debug.hpp"
+#include "common/locator_getters.hpp"
+#include "common/log.hpp"
+#include "common/serial_number.hpp"
+#include "common/type_traits.hpp"
+#include "instance/instance.hpp"
+
+namespace ot {
+namespace Srp {
+
+RegisterLogModule("SrpAdvProxy");
+
+//---------------------------------------------------------------------------------------------------------------------
+// AdvertisingProxy
+
+AdvertisingProxy::AdvertisingProxy(Instance &aInstance)
+    : InstanceLocator(aInstance)
+    , mState(kStateStopped)
+    , mCurrentRequestId(0)
+    , mAdvTimeout(kAdvTimeout)
+    , mTimer(aInstance)
+    , mTasklet(aInstance)
+{
+    mCounters.Clear();
+}
+
+void AdvertisingProxy::Start(void)
+{
+    VerifyOrExit(mState != kStateRunning);
+
+    mState = kStateRunning;
+    mCounters.mStateChanges++;
+    LogInfo("Started");
+
+    // Advertise all existing and committed entries on SRP sever.
+
+    for (Host &host : Get<Server>().mHosts)
+    {
+        LogInfo("Adv existing host '%s'", host.GetFullName());
+        Advertise(host);
+    }
+
+exit:
+    return;
+}
+
+void AdvertisingProxy::Stop(void)
+{
+    VerifyOrExit(mState != kStateStopped);
+
+    mState = kStateStopped;
+    mCounters.mStateChanges++;
+
+    while (true)
+    {
+        OwnedPtr<AdvInfo> advPtr = mAdvInfoList.Pop();
+
+        if (advPtr.IsNull())
+        {
+            break;
+        }
+
+        mCounters.mAdvRejected++;
+
+        UnregisterHostAndItsServicesAndKeys(advPtr->mHost);
+
+        advPtr->mError = kErrorAbort;
+        advPtr->mHost.mAdvIdRange.Clear();
+        advPtr->mBlockingAdv = nullptr;
+        advPtr->SignalServerToCommit();
+    }
+
+    for (Host &host : Get<Server>().GetHosts())
+    {
+        UnregisterHostAndItsServicesAndKeys(host);
+
+        host.mAdvIdRange.Clear();
+        host.mAdvId        = kInvalidRequestId;
+        host.mIsRegistered = false;
+
+        for (Service &service : host.mServices)
+        {
+            service.mAdvId        = kInvalidRequestId;
+            service.mIsRegistered = false;
+        }
+    }
+
+    LogInfo("Stopped");
+
+exit:
+    return;
+}
+
+void AdvertisingProxy::UpdateState(void)
+{
+    if (!Get<Dnssd>().IsReady() || !Get<BorderRouter::InfraIf>().IsRunning())
+    {
+        Stop();
+        ExitNow();
+    }
+
+    switch (Get<Server>().GetState())
+    {
+    case Server::kStateDisabled:
+    case Server::kStateStopped:
+        Stop();
+        break;
+
+    case Server::kStateRunning:
+        Start();
+        break;
+    }
+
+exit:
+    return;
+}
+
+AdvertisingProxy::RequestId AdvertisingProxy::AllocateNextRequestId(void)
+{
+    mCurrentRequestId++;
+
+    if (kInvalidRequestId == mCurrentRequestId)
+    {
+        mCurrentRequestId++;
+    }
+
+    return mCurrentRequestId;
+}
+
+// NOLINTNEXTLINE(readability-inconsistent-declaration-parameter-name)
+template <> void AdvertisingProxy::UpdateAdvIdRangeOn(Host &aHost)
+{
+    // Determine and update `mAdvIdRange` on `aHost` based on
+    // `mAdvId` and `mKeyAdvId` of host and its services.
+
+    aHost.mAdvIdRange.Clear();
+
+    for (const Service &service : aHost.mServices)
+    {
+        if (service.mKeyAdvId != kInvalidRequestId)
+        {
+            aHost.mAdvIdRange.Add(service.mKeyAdvId);
+        }
+
+        if (service.mAdvId != kInvalidRequestId)
+        {
+            aHost.mAdvIdRange.Add(service.mAdvId);
+        }
+    }
+
+    if (aHost.mKeyAdvId != kInvalidRequestId)
+    {
+        aHost.mAdvIdRange.Add(aHost.mKeyAdvId);
+    }
+
+    if (aHost.mAdvId != kInvalidRequestId)
+    {
+        aHost.mAdvIdRange.Add(aHost.mAdvId);
+    }
+
+    if (aHost.mAdvIdRange.IsEmpty())
+    {
+        mTasklet.Post();
+    }
+}
+
+// NOLINTNEXTLINE(readability-inconsistent-declaration-parameter-name)
+template <> void AdvertisingProxy::UpdateAdvIdRangeOn(Service &aService)
+{
+    // Updates `mAdvIdRange` on the `Host` associated with
+    // `aService`.
+
+    UpdateAdvIdRangeOn<Host>(*aService.mHost);
+}
+
+void AdvertisingProxy::AdvertiseRemovalOf(Host &aHost)
+{
+    LogInfo("Adv removal of host '%s'", aHost.GetFullName());
+    mCounters.mAdvHostRemovals++;
+
+    VerifyOrExit(mState == kStateRunning);
+    VerifyOrExit(aHost.IsDeleted());
+
+    aHost.mShouldAdvertise = aHost.mIsRegistered;
+
+    for (Service &service : aHost.mServices)
+    {
+        if (!service.mIsDeleted)
+        {
+            service.mIsDeleted = true;
+        }
+
+        service.mShouldAdvertise = service.mIsRegistered;
+    }
+
+    // Reject any outstanding `AdvInfo` that matches `aHost` that is
+    // being removed.
+
+    for (AdvInfo &adv : mAdvInfoList)
+    {
+        Host &advHost = adv.mHost;
+
+        if (!aHost.Matches(advHost.GetFullName()) || advHost.IsDeleted())
+        {
+            continue;
+        }
+
+        for (Service &advService : advHost.mServices)
+        {
+            Service *service;
+
+            service = aHost.FindService(advService.GetInstanceName());
+
+            if (service == nullptr)
+            {
+                // `AdvInfo` contains a service that is not present in
+                // `aHost`, we unregister the service and its key.
+
+                if (!advService.IsDeleted())
+                {
+                    UnregisterService(advService);
+                }
+
+                UnregisterKey(advService);
+            }
+            else
+            {
+                service->mShouldAdvertise = true;
+
+                if (aHost.mKeyLease == 0)
+                {
+                    advService.mIsKeyRegistered = false;
+                }
+            }
+
+            advService.mAdvId      = kInvalidRequestId;
+            advService.mKeyAdvId   = kInvalidRequestId;
+            advService.mIsReplaced = true;
+        }
+
+        if (aHost.mKeyLease == 0)
+        {
+            advHost.mIsKeyRegistered = false;
+        }
+
+        advHost.mAdvId      = kInvalidRequestId;
+        advHost.mKeyAdvId   = kInvalidRequestId;
+        advHost.mIsReplaced = true;
+        advHost.mAdvIdRange.Clear();
+
+        adv.mError = kErrorAbort;
+        mTasklet.Post();
+    }
+
+    for (Service &service : aHost.mServices)
+    {
+        if (service.mShouldAdvertise)
+        {
+            UnregisterService(service);
+        }
+
+        if (aHost.mKeyLease == 0)
+        {
+            UnregisterKey(service);
+        }
+    }
+
+    if (aHost.mShouldAdvertise)
+    {
+        UnregisterHost(aHost);
+    }
+
+    if (aHost.mKeyLease == 0)
+    {
+        UnregisterKey(aHost);
+    }
+
+exit:
+    return;
+}
+
+void AdvertisingProxy::AdvertiseRemovalOf(Service &aService)
+{
+    LogInfo("Adv removal of service '%s' '%s'", aService.GetInstanceLabel(), aService.GetServiceName());
+    mCounters.mAdvServiceRemovals++;
+
+    VerifyOrExit(mState == kStateRunning);
+
+    aService.mShouldAdvertise = aService.mIsRegistered;
+
+    // Check if any outstanding `AdvInfo` is re-adding the `aService`
+    // (which is being removed), and if so, skip unregistering the
+    // service and its key.
+
+    for (const AdvInfo &adv : mAdvInfoList)
+    {
+        const Host    &advHost = adv.mHost;
+        const Service *advService;
+
+        if (!aService.mHost->Matches(advHost.GetFullName()))
+        {
+            continue;
+        }
+
+        if (advHost.IsDeleted())
+        {
+            break;
+        }
+
+        advService = advHost.FindService(aService.GetInstanceName());
+
+        if ((advService != nullptr) && !advService->IsDeleted())
+        {
+            ExitNow();
+        }
+    }
+
+    if (aService.mShouldAdvertise)
+    {
+        UnregisterService(aService);
+    }
+
+    if (aService.mKeyLease == 0)
+    {
+        UnregisterKey(aService);
+    }
+
+exit:
+    return;
+}
+
+void AdvertisingProxy::Advertise(Host &aHost, const Server::MessageMetadata &aMetadata)
+{
+    AdvInfo *advPtr = nullptr;
+    Host    *existingHost;
+
+    LogInfo("Adv update for '%s'", aHost.GetFullName());
+
+    mCounters.mAdvTotal++;
+
+    VerifyOrExit(mState == kStateRunning);
+
+    advPtr = AdvInfo::Allocate(aHost, aMetadata, mAdvTimeout);
+    VerifyOrExit(advPtr != nullptr);
+    mAdvInfoList.Push(*advPtr);
+
+    // Compare the new `aHost` with outstanding advertisements and
+    // already committed entries on server.
+
+    for (AdvInfo &adv : mAdvInfoList)
+    {
+        if (!aHost.Matches(adv.mHost.GetFullName()))
+        {
+            continue;
+        }
+
+        if (CompareAndUpdateHostAndServices(aHost, adv.mHost))
+        {
+            // If the new `aHost` replaces an entry in the outstanding
+            // `adv`, we mark the new advertisement as blocked so
+            // that it is not committed before the earlier one. This
+            // ensures that SRP Updates are committed in the order
+            // they are advertised, avoiding issues such as re-adding
+            // a removed entry due to a delay in registration on
+            // infra DNS-SD.
+
+            if (advPtr->mBlockingAdv == nullptr)
+            {
+                mCounters.mAdvReplaced++;
+                advPtr->mBlockingAdv = &adv;
+            }
+        }
+    }
+
+    existingHost = Get<Server>().mHosts.FindMatching(aHost.GetFullName());
+
+    if (existingHost != nullptr)
+    {
+        CompareAndUpdateHostAndServices(aHost, *existingHost);
+    }
+
+    Advertise(aHost);
+
+exit:
+    if (advPtr != nullptr)
+    {
+        if (advPtr->IsCompleted())
+        {
+            mTasklet.Post();
+        }
+        else
+        {
+            mTimer.FireAtIfEarlier(advPtr->mExpireTime);
+        }
+    }
+    else
+    {
+        LogInfo("Adv skipped '%s'", aHost.GetFullName());
+        mCounters.mAdvSkipped++;
+        Get<Server>().CommitSrpUpdate(kErrorNone, aHost, aMetadata);
+    }
+}
+
+template <typename Entry> bool AdvertisingProxy::IsKeyRegisteredOrRegistering(const Entry &aEntry) const
+{
+    return (aEntry.mIsKeyRegistered || (aEntry.mKeyAdvId != kInvalidRequestId));
+}
+
+template <typename Entry> bool AdvertisingProxy::IsRegisteredOrRegistering(const Entry &aEntry) const
+{
+    return (aEntry.mIsRegistered || (aEntry.mAdvId != kInvalidRequestId));
+}
+
+template <typename Entry>
+void AdvertisingProxy::DecideToAdvertise(Entry &aEntry, bool aUnregisterEntry, bool aUnregisterKey)
+{
+    // Decides whether to advertise `aEntry` or register its key.
+
+    if (!aUnregisterKey && !IsKeyRegisteredOrRegistering(aEntry))
+    {
+        aEntry.mShouldRegisterKey = true;
+        aEntry.mKeyAdvId          = AllocateNextRequestId();
+    }
+
+    VerifyOrExit(!aEntry.mShouldAdvertise);
+
+    if (aUnregisterEntry || aEntry.IsDeleted())
+    {
+        aEntry.mShouldAdvertise = aEntry.mIsRegistered;
+    }
+    else if (!IsRegisteredOrRegistering(aEntry))
+    {
+        aEntry.mShouldAdvertise = true;
+        aEntry.mAdvId           = AllocateNextRequestId();
+    }
+
+exit:
+    return;
+}
+
+void AdvertisingProxy::Advertise(Host &aHost)
+{
+    bool shouldUnregisterHostAndServices = aHost.IsDeleted();
+    bool shouldUnregisterKeys            = (aHost.mKeyLease == 0);
+
+    DecideToAdvertise(aHost, shouldUnregisterHostAndServices, shouldUnregisterKeys);
+
+    for (Service &service : aHost.mServices)
+    {
+        DecideToAdvertise(service, shouldUnregisterHostAndServices, shouldUnregisterKeys);
+    }
+
+    // We call `UpdateAdvIdRangeOn()` to determine the `mAdvIdRange`
+    // on `aHost` before we call any of `UnregisterHost()`,
+    // `UnregisterService()`, or `UnregisterKey()` methods, and
+    // and receive any `HandleRegistered()` callbacks. The DNS-SD
+    // platform may invoke `HandleRegistered()` callbacks from within
+    // the `Register{Host/Service/Key}()` calls.
+
+    UpdateAdvIdRangeOn(aHost);
+
+    if (shouldUnregisterKeys)
+    {
+        UnregisterKey(aHost);
+    }
+    else if (aHost.mShouldRegisterKey)
+    {
+        RegisterKey(aHost);
+    }
+
+    // We register host first before any of its services.
+    // But if we need to unregister host, it is done after
+    // all services.
+
+    if (aHost.mShouldAdvertise && !shouldUnregisterHostAndServices)
+    {
+        RegisterHost(aHost);
+    }
+
+    for (Service &service : aHost.mServices)
+    {
+        if (shouldUnregisterKeys)
+        {
+            UnregisterKey(service);
+        }
+        else if (service.mShouldRegisterKey)
+        {
+            RegisterKey(service);
+        }
+
+        if (service.mShouldAdvertise)
+        {
+            if (shouldUnregisterHostAndServices || service.IsDeleted())
+            {
+                UnregisterService(service);
+            }
+            else
+            {
+                RegisterService(service);
+            }
+        }
+    }
+
+    if (aHost.mShouldAdvertise && shouldUnregisterHostAndServices)
+    {
+        UnregisterHost(aHost);
+    }
+}
+
+void AdvertisingProxy::UnregisterHostAndItsServicesAndKeys(Host &aHost)
+{
+    for (Service &service : aHost.mServices)
+    {
+        if (service.mIsKeyRegistered)
+        {
+            UnregisterKey(service);
+        }
+
+        if (!service.mIsReplaced && IsRegisteredOrRegistering(service))
+        {
+            UnregisterService(service);
+        }
+    }
+
+    if (aHost.mIsKeyRegistered)
+    {
+        UnregisterKey(aHost);
+    }
+
+    if (!aHost.mIsReplaced && IsRegisteredOrRegistering(aHost))
+    {
+        UnregisterHost(aHost);
+    }
+}
+
+bool AdvertisingProxy::CompareAndUpdateHostAndServices(Host &aHost, Host &aExistingHost)
+{
+    // This method compares and updates flags used by `AdvertisingProxy`
+    // on new `aHost` and `aExistingHost` and their services.
+    //
+    // It returns a boolean indicating whether the new `aHost` replaced
+    // any of the entries on the `aExistingHost`.
+    //
+    // The `AdvertisingProxy` uses the following flags and variables
+    // on `Host` and `Service` entries:
+    //
+    // - `mIsRegistered` indicates whether or not the entry has been
+    //   successfully registered by the proxy.
+    //
+    // - `mIsKeyRegistered` indicates whether or not a key record
+    //   associated with the entry name has been successfully
+    //   registered by the proxy on infrastructure DNS-SD.
+    //
+    // - `mAdvId` specifies the ongoing registration request ID
+    //   associated with this entry by the proxy. A value of zero or
+    //   `kInvalidRequestId` indicates that there is no ongoing
+    //   registration for this entry.
+    //
+    // - `mKeyAdvId` is similar to `mAdvId` but for registering the
+    //   key record.
+    //
+    // - `mIsReplaced` tracks whether this entry has been replaced by
+    //   a newer advertisement request that changes some of its
+    //   parameters. For example, the address list could have been
+    //   changed on a `Host`, or TXT Data, or the list of sub-types,
+    //   or port number could have been changed on a `Service`.
+    //
+    // - `mShouldAdvertise` is only used in the `Advertise()` call
+    //   chain to track whether we need to advertise the entry.
+    //
+    // - `mShouldRegisterKey` is similar to `mShouldAdvertise` and
+    //   only used in `Advertise()` call chain.
+
+    bool replaced = false;
+
+    VerifyOrExit(&aHost != &aExistingHost);
+
+    replaced = CompareAndUpdateHost(aHost, aExistingHost);
+
+    // Compare services of `aHost` against services of
+    // `aExistingHost`.
+
+    for (Service &service : aHost.mServices)
+    {
+        Service *existingService = aExistingHost.mServices.FindMatching(service.GetInstanceName());
+
+        if (existingService != nullptr)
+        {
+            replaced |= CompareAndUpdateService(service, *existingService);
+        }
+    }
+
+exit:
+    return replaced;
+}
+
+template <typename Entry> void AdvertisingProxy::UpdateKeyRegistrationStatus(Entry &aEntry, const Entry &aExistingEntry)
+{
+    // Updates key registration status on `aEntry` based
+    // on its state on `aExistingEntry`.
+
+    static_assert(TypeTraits::IsSame<Entry, Host>::kValue || TypeTraits::IsSame<Entry, Service>::kValue,
+                  "`Entry` must be `Host` or `Service` types");
+
+    // If the new `aEntry` has a zero key lease, we always unregister
+    // it, just to be safe. Therefore, there is no need to check the
+    // key registration status of the existing `aExistingEntry`.
+
+    VerifyOrExit(aEntry.GetKeyLease() != 0);
+
+    VerifyOrExit(!IsKeyRegisteredOrRegistering(aEntry));
+
+    if (aExistingEntry.mIsKeyRegistered)
+    {
+        aEntry.mIsKeyRegistered = true;
+    }
+    else
+    {
+        // Use the key registration request ID by `aExistingEntry` for
+        // the new `aEntry` if there is any. If there is none the
+        // `mKeyAdvId` remains as `kInvalidRequestId`.
+
+        aEntry.mKeyAdvId = aExistingEntry.mKeyAdvId;
+    }
+
+exit:
+    return;
+}
+
+// NOLINTNEXTLINE(readability-inconsistent-declaration-parameter-name)
+template <> bool AdvertisingProxy::EntriesMatch(const Host &aFirstHost, const Host &aSecondHost)
+{
+    bool match = false;
+
+    VerifyOrExit(aFirstHost.IsDeleted() == aSecondHost.IsDeleted());
+
+    if (aFirstHost.IsDeleted())
+    {
+        match = true;
+        ExitNow();
+    }
+
+    VerifyOrExit(aFirstHost.mAddresses.GetLength() == aSecondHost.mAddresses.GetLength());
+
+    for (const Ip6::Address &address : aFirstHost.mAddresses)
+    {
+        VerifyOrExit(aSecondHost.mAddresses.Contains(address));
+    }
+
+    match = true;
+
+exit:
+    return match;
+}
+
+// NOLINTNEXTLINE(readability-inconsistent-declaration-parameter-name)
+template <> bool AdvertisingProxy::EntriesMatch(const Service &aFirstService, const Service &aSecondService)
+{
+    bool match = false;
+
+    VerifyOrExit(aFirstService.IsDeleted() == aSecondService.IsDeleted());
+
+    if (aFirstService.IsDeleted())
+    {
+        match = true;
+        ExitNow();
+    }
+
+    VerifyOrExit(aFirstService.GetPort() == aSecondService.GetPort());
+    VerifyOrExit(aFirstService.GetWeight() == aSecondService.GetWeight());
+    VerifyOrExit(aFirstService.GetPriority() == aSecondService.GetPriority());
+    VerifyOrExit(aFirstService.GetTtl() == aSecondService.GetTtl());
+
+    VerifyOrExit(aFirstService.GetNumberOfSubTypes() == aSecondService.GetNumberOfSubTypes());
+
+    for (uint16_t index = 0; index < aFirstService.GetNumberOfSubTypes(); index++)
+    {
+        VerifyOrExit(aSecondService.HasSubTypeServiceName(aFirstService.GetSubTypeServiceNameAt(index)));
+    }
+
+    VerifyOrExit(aFirstService.GetTxtDataLength() == aSecondService.GetTxtDataLength());
+    VerifyOrExit(!memcmp(aFirstService.GetTxtData(), aSecondService.GetTxtData(), aFirstService.GetTxtDataLength()));
+
+    match = true;
+
+exit:
+    return match;
+}
+
+template <typename Entry> bool AdvertisingProxy::CompareAndUpdate(Entry &aEntry, Entry &aExistingEntry)
+{
+    // This is called when the new `aEntry` is not deleted.
+
+    bool replaced = false;
+
+    // If we previously determined that `aEntry` is registered,
+    // nothing else to do.
+
+    VerifyOrExit(!aEntry.mIsRegistered);
+
+    if (aEntry.mShouldAdvertise || aExistingEntry.mIsReplaced || !EntriesMatch(aEntry, aExistingEntry))
+    {
+        // If we previously determined that we should advertise the
+        // new `aEntry`, we enter this block to mark `aExistingEntry`
+        // as being replaced.
+        //
+        // If `aExistingEntry` was already marked as replaced, we
+        // cannot compare it to the new `aEntry`. Therefore, we assume
+        // that there may be a change and always advertise the new
+        // `aEntry`. Otherwise, we compare it to the new `aEntry` using
+        // `EntriesMatch()` and only if there are any differences, we
+        // mark that `aEntry` needs to be advertised.
+
+        aExistingEntry.mIsReplaced = true;
+        replaced                   = true;
+
+        if (aEntry.mAdvId == kInvalidRequestId)
+        {
+            aEntry.mShouldAdvertise = true;
+            aEntry.mAdvId           = AllocateNextRequestId();
+        }
+
+        // If there is an outstanding registration request for
+        // `aExistingEntry` we replace it with the request ID of the
+        // new `aEntry` registration.
+
+        if (aExistingEntry.mAdvId != kInvalidRequestId)
+        {
+            aExistingEntry.mAdvId = aEntry.mAdvId;
+            UpdateAdvIdRangeOn(aExistingEntry);
+        }
+
+        ExitNow();
+    }
+
+    // `aEntry` fully matches `aExistingEntry` and `aExistingEntry` was
+    // not replaced.
+
+    VerifyOrExit(aEntry.mAdvId == kInvalidRequestId);
+
+    if (aExistingEntry.mIsRegistered)
+    {
+        aEntry.mIsRegistered = true;
+    }
+    else if (aExistingEntry.mAdvId != kInvalidRequestId)
+    {
+        // There is an outstanding registration request for
+        // `aExistingEntry`. We use the same ID for the new `aEntry`.
+
+        aEntry.mAdvId = aExistingEntry.mAdvId;
+    }
+    else
+    {
+        // The earlier advertisement of `aExistingEntry` seems to have
+        // failed since there is no outstanding registration request
+        // (no ID) and it is not marked as registered. We mark the
+        // new `aEntry` to be advertised (to try again).
+
+        aEntry.mShouldAdvertise    = true;
+        aEntry.mAdvId              = AllocateNextRequestId();
+        aExistingEntry.mIsReplaced = true;
+    }
+
+exit:
+    return replaced;
+}
+
+bool AdvertisingProxy::CompareAndUpdateHost(Host &aHost, Host &aExistingHost)
+{
+    bool replaced = false;
+
+    UpdateKeyRegistrationStatus(aHost, aExistingHost);
+
+    if (!aHost.IsDeleted())
+    {
+        replaced = CompareAndUpdate(aHost, aExistingHost);
+        ExitNow();
+    }
+
+    // The new `aHost` is removing the host and all its services.
+
+    if (aExistingHost.IsDeleted())
+    {
+        // If `aHost` has zero key-lease (fully removed),
+        // we need to unregister keys for any services on
+        // existing host that are not present in `aHost`.
+
+        if (aHost.mKeyLease == 0)
+        {
+            for (Service &existingService : aExistingHost.mServices)
+            {
+                if (!aHost.HasService(existingService.GetInstanceName()))
+                {
+                    UnregisterKey(existingService);
+                }
+            }
+        }
+
+        ExitNow();
+    }
+
+    // `aExistingHost` is updating the same host that is being
+    // removed by the new `aHost` update. We need to advertise
+    // the new `aHost` to make sure it is unregistered.
+
+    aHost.mShouldAdvertise = true;
+
+    // We unregister any services that were registered by
+    // `aExistingHost` but are not included in the now being
+    // removed `aHost`, and unregister any registered keys when
+    // `aHost` has zero key lease.
+
+    for (Service &existingService : aExistingHost.mServices)
+    {
+        if (existingService.IsDeleted())
+        {
+            if (aHost.GetKeyLease() == 0)
+            {
+                existingService.mIsReplaced = true;
+                UnregisterKey(existingService);
+            }
+
+            continue;
+        }
+
+        if (aHost.HasService(existingService.GetInstanceName()))
+        {
+            // The `existingService` that are contained in `aHost`
+            // are updated in `CompareAndUpdateService()`.
+            continue;
+        }
+
+        UnregisterService(existingService);
+
+        existingService.mIsReplaced = true;
+
+        if (aHost.GetKeyLease() == 0)
+        {
+            UnregisterKey(existingService);
+        }
+    }
+
+    aExistingHost.mAdvId      = kInvalidRequestId;
+    aExistingHost.mIsReplaced = true;
+    replaced                  = true;
+
+    if (aHost.GetKeyLease() == 0)
+    {
+        UnregisterKey(aExistingHost);
+    }
+
+    UpdateAdvIdRangeOn(aExistingHost);
+
+exit:
+    return replaced;
+}
+
+bool AdvertisingProxy::CompareAndUpdateService(Service &aService, Service &aExistingService)
+{
+    bool replaced = false;
+
+    UpdateKeyRegistrationStatus(aService, aExistingService);
+
+    if (!aService.IsDeleted())
+    {
+        replaced = CompareAndUpdate(aService, aExistingService);
+        ExitNow();
+    }
+
+    if (aExistingService.IsDeleted())
+    {
+        ExitNow();
+    }
+
+    aService.mShouldAdvertise = true;
+
+    aExistingService.mIsReplaced = true;
+    replaced                     = true;
+
+    if (aExistingService.mAdvId != kInvalidRequestId)
+    {
+        // If there is an outstanding registration request for the
+        // existing service, clear its request ID.
+
+        aExistingService.mAdvId = kInvalidRequestId;
+
+        UpdateAdvIdRangeOn(*aExistingService.mHost);
+    }
+
+exit:
+    return replaced;
+}
+
+void AdvertisingProxy::RegisterHost(Host &aHost)
+{
+    Error                     error = kErrorNone;
+    Dnssd::Host               hostInfo;
+    DnsName                   hostName;
+    Heap::Array<Ip6::Address> hostAddresses;
+
+    aHost.mShouldAdvertise = false;
+
+    CopyNameAndRemoveDomain(hostName, aHost.GetFullName());
+
+    SuccessOrExit(error = hostAddresses.ReserveCapacity(aHost.mAddresses.GetLength()));
+
+    for (const Ip6::Address &address : aHost.mAddresses)
+    {
+        if (!address.IsLinkLocal() && !Get<Mle::Mle>().IsMeshLocalAddress(address))
+        {
+            IgnoreError(hostAddresses.PushBack(address));
+        }
+    }
+
+    LogInfo("Registering host '%s', id:%lu", hostName, ToUlong(aHost.mAdvId));
+
+    hostInfo.Clear();
+    hostInfo.mHostName        = hostName;
+    hostInfo.mAddresses       = hostAddresses.AsCArray();
+    hostInfo.mAddressesLength = hostAddresses.GetLength();
+    hostInfo.mTtl             = aHost.GetTtl();
+    hostInfo.mInfraIfIndex    = Get<BorderRouter::InfraIf>().GetIfIndex();
+
+    Get<Dnssd>().RegisterHost(hostInfo, aHost.mAdvId, HandleRegistered);
+
+exit:
+    if (error != kErrorNone)
+    {
+        LogWarn("Error %s registering host '%s'", ErrorToString(error), hostName);
+    }
+}
+
+void AdvertisingProxy::UnregisterHost(Host &aHost)
+{
+    Dnssd::Host hostInfo;
+    DnsName     hostName;
+
+    aHost.mShouldAdvertise = false;
+    aHost.mIsRegistered    = false;
+    aHost.mAdvId           = false;
+
+    CopyNameAndRemoveDomain(hostName, aHost.GetFullName());
+
+    LogInfo("Unregistering host '%s'", hostName);
+
+    hostInfo.Clear();
+    hostInfo.mHostName     = hostName;
+    hostInfo.mInfraIfIndex = Get<BorderRouter::InfraIf>().GetIfIndex();
+
+    Get<Dnssd>().UnregisterHost(hostInfo, 0, nullptr);
+}
+
+void AdvertisingProxy::RegisterService(Service &aService)
+{
+    Error                     error = kErrorNone;
+    Dnssd::Service            serviceInfo;
+    DnsName                   hostName;
+    DnsName                   serviceName;
+    Heap::Array<Heap::String> subTypeHeapStrings;
+    Heap::Array<const char *> subTypeLabels;
+
+    aService.mShouldAdvertise = false;
+
+    CopyNameAndRemoveDomain(hostName, aService.GetHost().GetFullName());
+    CopyNameAndRemoveDomain(serviceName, aService.GetServiceName());
+
+    SuccessOrExit(error = subTypeHeapStrings.ReserveCapacity(aService.mSubTypes.GetLength()));
+    SuccessOrExit(error = subTypeLabels.ReserveCapacity(aService.mSubTypes.GetLength()));
+
+    for (const Heap::String &subTypeName : aService.mSubTypes)
+    {
+        char         label[Dns::Name::kMaxLabelSize];
+        Heap::String labelString;
+
+        IgnoreError(Server::Service::ParseSubTypeServiceName(subTypeName.AsCString(), label, sizeof(label)));
+        SuccessOrExit(error = labelString.Set(label));
+        IgnoreError(subTypeHeapStrings.PushBack(static_cast<Heap::String &&>(labelString)));
+        IgnoreError(subTypeLabels.PushBack(subTypeHeapStrings.Back()->AsCString()));
+    }
+
+    LogInfo("Registering service '%s' '%s' on '%s', id:%lu", aService.GetInstanceLabel(), serviceName, hostName,
+            ToUlong(aService.mAdvId));
+
+    serviceInfo.Clear();
+    serviceInfo.mHostName            = hostName;
+    serviceInfo.mServiceInstance     = aService.GetInstanceLabel();
+    serviceInfo.mServiceType         = serviceName;
+    serviceInfo.mSubTypeLabels       = subTypeLabels.AsCArray();
+    serviceInfo.mSubTypeLabelsLength = subTypeLabels.GetLength();
+    serviceInfo.mTxtData             = aService.GetTxtData();
+    serviceInfo.mTxtDataLength       = aService.GetTxtDataLength();
+    serviceInfo.mPort                = aService.GetPort();
+    serviceInfo.mWeight              = aService.GetWeight();
+    serviceInfo.mPriority            = aService.GetPriority();
+    serviceInfo.mTtl                 = aService.GetTtl();
+    serviceInfo.mInfraIfIndex        = Get<BorderRouter::InfraIf>().GetIfIndex();
+
+    Get<Dnssd>().RegisterService(serviceInfo, aService.mAdvId, HandleRegistered);
+
+exit:
+    if (error != kErrorNone)
+    {
+        LogWarn("Error %s registering service '%s' '%s'", ErrorToString(error), aService.GetInstanceLabel(),
+                serviceName);
+    }
+}
+
+void AdvertisingProxy::UnregisterService(Service &aService)
+{
+    Dnssd::Service serviceInfo;
+    DnsName        hostName;
+    DnsName        serviceName;
+
+    aService.mShouldAdvertise = false;
+    aService.mIsRegistered    = false;
+    aService.mAdvId           = kInvalidRequestId;
+
+    CopyNameAndRemoveDomain(hostName, aService.GetHost().GetFullName());
+    CopyNameAndRemoveDomain(serviceName, aService.GetServiceName());
+
+    LogInfo("Unregistering service '%s' '%s' on '%s'", aService.GetInstanceLabel(), serviceName, hostName);
+
+    serviceInfo.Clear();
+    serviceInfo.mHostName        = hostName;
+    serviceInfo.mServiceInstance = aService.GetInstanceLabel();
+    serviceInfo.mServiceType     = serviceName;
+    serviceInfo.mInfraIfIndex    = Get<BorderRouter::InfraIf>().GetIfIndex();
+
+    Get<Dnssd>().UnregisterService(serviceInfo, 0, nullptr);
+}
+
+void AdvertisingProxy::RegisterKey(Host &aHost)
+{
+    DnsName hostName;
+
+    aHost.mShouldRegisterKey = false;
+
+    CopyNameAndRemoveDomain(hostName, aHost.GetFullName());
+
+    LogInfo("Registering key for host '%s', id:%lu", hostName, ToUlong(aHost.mKeyAdvId));
+
+    RegisterKey(hostName, /* aServiceType */ nullptr, aHost.mKey, aHost.mKeyAdvId, aHost.GetTtl());
+}
+
+void AdvertisingProxy::RegisterKey(Service &aService)
+{
+    DnsName serviceType;
+
+    aService.mShouldRegisterKey = false;
+
+    CopyNameAndRemoveDomain(serviceType, aService.GetServiceName());
+
+    LogInfo("Registering key for service '%s' '%s', id:%lu", aService.GetInstanceLabel(), serviceType,
+            ToUlong(aService.mKeyAdvId));
+
+    RegisterKey(aService.GetInstanceLabel(), serviceType, aService.mHost->mKey, aService.mKeyAdvId, aService.GetTtl());
+}
+
+void AdvertisingProxy::RegisterKey(const char      *aName,
+                                   const char      *aServiceType,
+                                   const Host::Key &aKey,
+                                   RequestId        aRequestId,
+                                   uint32_t         aTtl)
+{
+    Dnssd::Key             keyInfo;
+    Dns::Ecdsa256KeyRecord keyRecord;
+
+    keyRecord.Init();
+    keyRecord.SetFlags(Dns::KeyRecord::kAuthConfidPermitted, Dns::KeyRecord::kOwnerNonZone,
+                       Dns::KeyRecord::kSignatoryFlagGeneral);
+    keyRecord.SetProtocol(Dns::KeyRecord::kProtocolDnsSec);
+    keyRecord.SetAlgorithm(Dns::KeyRecord::kAlgorithmEcdsaP256Sha256);
+    keyRecord.SetLength(sizeof(Dns::Ecdsa256KeyRecord) - sizeof(Dns::ResourceRecord));
+    keyRecord.SetKey(aKey);
+
+    keyInfo.Clear();
+    keyInfo.mName          = aName;
+    keyInfo.mServiceType   = aServiceType;
+    keyInfo.mKeyData       = reinterpret_cast<uint8_t *>(&keyRecord) + sizeof(Dns::ResourceRecord);
+    keyInfo.mKeyDataLength = keyRecord.GetLength();
+    keyInfo.mClass         = Dns::ResourceRecord::kClassInternet;
+    keyInfo.mTtl           = aTtl;
+    keyInfo.mInfraIfIndex  = Get<BorderRouter::InfraIf>().GetIfIndex();
+
+    Get<Dnssd>().RegisterKey(keyInfo, aRequestId, HandleRegistered);
+}
+
+void AdvertisingProxy::UnregisterKey(Host &aHost)
+{
+    DnsName hostName;
+
+    aHost.mIsKeyRegistered = false;
+    aHost.mKeyAdvId        = kInvalidRequestId;
+
+    CopyNameAndRemoveDomain(hostName, aHost.GetFullName());
+
+    LogInfo("Unregistering key for host '%s'", hostName);
+
+    UnregisterKey(hostName, /* aServiceType */ nullptr);
+}
+
+void AdvertisingProxy::UnregisterKey(Service &aService)
+{
+    DnsName serviceType;
+
+    aService.mIsKeyRegistered = false;
+    aService.mKeyAdvId        = kInvalidRequestId;
+
+    CopyNameAndRemoveDomain(serviceType, aService.GetServiceName());
+
+    LogInfo("Unregistering key for service '%s' '%s'", aService.GetInstanceLabel(), serviceType);
+
+    UnregisterKey(aService.GetInstanceLabel(), serviceType);
+}
+
+void AdvertisingProxy::UnregisterKey(const char *aName, const char *aServiceType)
+{
+    Dnssd::Key keyInfo;
+
+    keyInfo.Clear();
+    keyInfo.mName         = aName;
+    keyInfo.mServiceType  = aServiceType;
+    keyInfo.mInfraIfIndex = Get<BorderRouter::InfraIf>().GetIfIndex();
+
+    Get<Dnssd>().UnregisterKey(keyInfo, 0, nullptr);
+}
+
+void AdvertisingProxy::CopyNameAndRemoveDomain(DnsName &aName, const char *aFullName)
+{
+    IgnoreError(Dns::Name::ExtractLabels(aFullName, Get<Server>().GetDomain(), aName, sizeof(aName)));
+}
+
+void AdvertisingProxy::HandleRegistered(otInstance *aInstance, otPlatDnssdRequestId aRequestId, otError aError)
+{
+    AsCoreType(aInstance).Get<AdvertisingProxy>().HandleRegistered(aRequestId, aError);
+}
+
+void AdvertisingProxy::HandleRegistered(RequestId aRequestId, Error aError)
+{
+    LogInfo("Register callback, id:%lu, error:%s", ToUlong(aRequestId), ErrorToString(aError));
+
+    VerifyOrExit(mState == kStateRunning);
+
+    for (Host &host : Get<Server>().mHosts)
+    {
+        HandleRegisteredRequestIdOn(host, aRequestId, aError);
+    }
+
+    for (AdvInfo &adv : mAdvInfoList)
+    {
+        if (HandleRegisteredRequestIdOn(adv.mHost, aRequestId, aError))
+        {
+            if (adv.mError == kErrorNone)
+            {
+                adv.mError = aError;
+            }
+
+            if (adv.IsCompleted())
+            {
+                mTasklet.Post();
+            }
+        }
+    }
+
+exit:
+    return;
+}
+
+bool AdvertisingProxy::HandleRegisteredRequestIdOn(Host &aHost, RequestId aRequestId, Error aError)
+{
+    // Handles "registration request callback" for `aRequestId` on a
+    // given `aHost`. Returns `true`, if the ID matched an entry
+    // on `aHost` and `aHost` was updated, `false` otherwise.
+
+    bool didUpdate = false;
+
+    VerifyOrExit(aHost.mAdvIdRange.Contains(aRequestId));
+
+    if (aHost.mAdvId == aRequestId)
+    {
+        aHost.mAdvId        = kInvalidRequestId;
+        aHost.mIsRegistered = (aError == kErrorNone);
+        didUpdate           = true;
+    }
+
+    if (aHost.mKeyAdvId == aRequestId)
+    {
+        aHost.mKeyAdvId        = kInvalidRequestId;
+        aHost.mIsKeyRegistered = true;
+        didUpdate              = true;
+    }
+
+    for (Service &service : aHost.mServices)
+    {
+        if (service.mAdvId == aRequestId)
+        {
+            service.mAdvId        = kInvalidRequestId;
+            service.mIsRegistered = (aError == kErrorNone);
+            didUpdate             = true;
+        }
+
+        if (service.mKeyAdvId == aRequestId)
+        {
+            service.mKeyAdvId        = kInvalidRequestId;
+            service.mIsKeyRegistered = true;
+            didUpdate                = true;
+        }
+    }
+
+    UpdateAdvIdRangeOn(aHost);
+
+exit:
+    return didUpdate;
+}
+
+void AdvertisingProxy::HandleTimer(void)
+{
+    TimeMilli           now      = TimerMilli::GetNow();
+    TimeMilli           nextTime = now.GetDistantFuture();
+    OwningList<AdvInfo> expiredList;
+
+    VerifyOrExit(mState == kStateRunning);
+
+    mAdvInfoList.RemoveAllMatching(AdvInfo::ExpirationChecker(now), expiredList);
+
+    for (AdvInfo &adv : mAdvInfoList)
+    {
+        nextTime = Min(adv.mExpireTime, nextTime);
+    }
+
+    if (nextTime != now.GetDistantFuture())
+    {
+        mTimer.FireAtIfEarlier(nextTime);
+    }
+
+    for (AdvInfo &adv : expiredList)
+    {
+        adv.mError       = kErrorResponseTimeout;
+        adv.mBlockingAdv = nullptr;
+        adv.mHost.mAdvIdRange.Clear();
+        SignalAdvCompleted(adv);
+    }
+
+exit:
+    return;
+}
+
+void AdvertisingProxy::HandleTasklet(void)
+{
+    VerifyOrExit(mState == kStateRunning);
+
+    while (true)
+    {
+        OwningList<AdvInfo> completedList;
+
+        mAdvInfoList.RemoveAllMatching(AdvInfo::CompletionChecker(), completedList);
+
+        VerifyOrExit(!completedList.IsEmpty());
+
+        // `RemoveAllMatching()` reverses the order of removed entries
+        // from `mAdvInfoList` (which itself keeps the later requests
+        // towards the head of the list). This means that the
+        // `completedList` will be sorted from earliest request to
+        // latest and this is the order that we want to notify
+        // `Srp::Server`.
+
+        for (AdvInfo &adv : completedList)
+        {
+            SignalAdvCompleted(adv);
+        }
+
+        completedList.Clear();
+    }
+
+exit:
+    return;
+}
+
+void AdvertisingProxy::SignalAdvCompleted(AdvInfo &aAdvInfo)
+{
+    // Check if any outstanding advertisements in the list
+    // is blocked by `aAdvInfo` and unblock.
+
+    for (AdvInfo &adv : mAdvInfoList)
+    {
+        if (adv.mBlockingAdv == &aAdvInfo)
+        {
+            adv.mBlockingAdv = nullptr;
+
+            if (adv.IsCompleted())
+            {
+                mTasklet.Post();
+            }
+        }
+    }
+
+    switch (aAdvInfo.mError)
+    {
+    case kErrorNone:
+        mCounters.mAdvSuccessful++;
+        break;
+    case kErrorResponseTimeout:
+        mCounters.mAdvTimeout++;
+        break;
+    default:
+        mCounters.mAdvRejected++;
+        break;
+    }
+
+    aAdvInfo.SignalServerToCommit();
+}
+
+//---------------------------------------------------------------------------------------------------------------------
+// AdvertisingProxy::AdvInfo
+
+AdvertisingProxy::AdvInfo::AdvInfo(Host &aHost, const Server::MessageMetadata &aMetadata, uint32_t aTimeout)
+    : mNext(nullptr)
+    , mBlockingAdv(nullptr)
+    , mHost(aHost)
+    , mExpireTime(TimerMilli::GetNow() + aTimeout)
+    , mMessageMetadata(aMetadata)
+    , mError(kErrorNone)
+{
+    if (aMetadata.mMessageInfo != nullptr)
+    {
+        // If `mMessageInfo` is not null in the given `aMetadata` keep
+        // a copy of it in `AdvInfo` structure and update the
+        // `mMessageMetadata` to point to the local copy instead.
+
+        mMessageInfo                  = *aMetadata.mMessageInfo;
+        mMessageMetadata.mMessageInfo = &mMessageInfo;
+    }
+}
+
+void AdvertisingProxy::AdvInfo::SignalServerToCommit(void)
+{
+    LogInfo("Adv done '%s', error:%s", mHost.GetFullName(), ErrorToString(mError));
+    Get<Server>().CommitSrpUpdate(mError, mHost, mMessageMetadata);
+}
+
+bool AdvertisingProxy::AdvInfo::IsCompleted(void) const
+{
+    bool isCompleted = false;
+
+    VerifyOrExit(mBlockingAdv == nullptr);
+    isCompleted = (mError != kErrorNone) || mHost.mAdvIdRange.IsEmpty();
+
+exit:
+    return isCompleted;
+}
+
+} // namespace Srp
+} // namespace ot
+
+#endif // OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE
diff --git a/src/core/net/srp_advertising_proxy.hpp b/src/core/net/srp_advertising_proxy.hpp
new file mode 100644
index 0000000..19fa82d
--- /dev/null
+++ b/src/core/net/srp_advertising_proxy.hpp
@@ -0,0 +1,322 @@
+/*
+ *  Copyright (c) 2023, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ *   This file includes definitions for Advertising Proxy.
+ */
+
+#ifndef SRP_ADVERTISING_PROXY_HPP_
+#define SRP_ADVERTISING_PROXY_HPP_
+
+#include "openthread-core-config.h"
+
+#if OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE
+
+#if !OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE
+#error "OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE requires OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE"
+#endif
+
+#if !OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
+#error "OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE requires OPENTHREAD_CONFIG_SRP_SERVER_ENABLE"
+#endif
+
+#if !OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
+#error "OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE requires OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE"
+#endif
+
+#include "common/clearable.hpp"
+#include "common/heap_allocatable.hpp"
+#include "common/locator.hpp"
+#include "common/non_copyable.hpp"
+#include "common/owning_list.hpp"
+#include "common/tasklet.hpp"
+#include "common/timer.hpp"
+#include "net/dnssd.hpp"
+#include "net/srp_server.hpp"
+
+namespace ot {
+namespace Srp {
+
+/**
+ * Implements SRP Advertising Proxy.
+ *
+ */
+class AdvertisingProxy : public InstanceLocator, private NonCopyable
+{
+public:
+    typedef Server::Host    Host;    ///< An SRP server host registration.
+    typedef Server::Service Service; ///< An SRP server service registration.
+
+    /**
+     * Represents counters for Advertising Proxy.
+     *
+     */
+    struct Counters : public Clearable<Counters>
+    {
+        uint32_t mAdvTotal;           ///< Total number of advertisement requests, i.e., calls to `Advertise()`.
+        uint32_t mAdvReplaced;        ///< Number of advertisements that were replaced by a newer one.
+        uint32_t mAdvSkipped;         ///< Number of advertisement that were skipped (DNS-SD platform not yet ready).
+        uint32_t mAdvSuccessful;      ///< Number of successful adv (all requests registered successfully).
+        uint32_t mAdvRejected;        ///< Number of rejected adv (at least one request was rejected by DNS-SD plat).
+        uint32_t mAdvTimeout;         ///< Number of advertisements that timed out (no response from DNS-SD platform).
+        uint32_t mAdvHostRemovals;    ///< Number of host removal adv, i.e., calls to `AdvertiseRemovalOf(Host &)`
+        uint32_t mAdvServiceRemovals; ///< Number of service removal adv, i.e., calls to `AdvertiseRemovalOf(Service &)`
+        uint32_t mStateChanges;       ///< Number of state changes of Advertising Proxy.
+    };
+
+    /**
+     * Initializes the `AdvertisingProxy` object.
+     *
+     * @param[in] aInstance  The OpenThread instance
+     *
+     */
+    explicit AdvertisingProxy(Instance &aInstance);
+
+    /**
+     * Indicates whether or not the Advertising Proxy is running.
+     *
+     * @retval TRUE   The Advertising Proxy is running.
+     * @retval FALSE  The Advertising Proxy is not running (it is stopped).
+     *
+     */
+    bool IsRunning(void) const { return mState == kStateRunning; }
+
+    /**
+     * Requests advertisement of a newly received SRP Update message.
+     *
+     * Once advertisement is completed, `AdvertisingProxy` notifies server by invoking `Server::CommitSrpUpdate()`
+     * using the same `aHost` and `aMetadata` as input parameters along with an `Error` indicating the outcome of the
+     * advertisement.
+     *
+     * The `aHost` instance ownership is passed to `AdvertisingProxy` until it is passed back to the `Server` in the
+     * `CommitSrpUpdate()` call. The call to `CommitSrpUpdate()` may happen before this method returns, for example,
+     * if the proxy is not running and therefore the advertisement is skipped.
+     *
+     * @param[in] aHost     The `aHost` instance constructed from processing a newly received SRP Update message.
+     * @param[in] aMetadata The `MessageMetadata` associated with the received SRP Update message by server.
+     *
+     */
+    void Advertise(Host &aHost, const Server::MessageMetadata &aMetadata);
+
+    /**
+     * Requests advertisement of removal of an already committed host and all its services, for example, due to its
+     * lease expiration.
+     *
+     * The removal does not use any callback to notify the SRP server since the server always immediately commits the
+     * removed entries.
+     *
+     * If there is an outstanding advertisement request (an earlier call to `Advertise()` that has not yet completed)
+     * that is registering the same host name as @p aHost that is being removed, the outstanding advertisement is
+     * rejected using the `kErrorAbort` error. This situation can happen if the client tries to refresh or update its
+     * registration close to its lease expiration time. By rejecting any outstanding advertisements, we ensure that an
+     * expired host is not re-added by mistake due to a delay in registration by the DNS-SD platform. The error is
+     * passed back to the client, triggering it to retry its registration.
+     *
+     * @param[in] aHost  The host which is being removed.
+     *
+     */
+    void AdvertiseRemovalOf(Host &aHost);
+
+    /**
+     * Requests advertisement of removal of an already committed service, for example, due to its lease expiration.
+     *
+     * The removal does not use any callback to notify the SRP server since the server always immediately commits the
+     * removed services.
+     *
+     * If there is an outstanding advertisement request (an earlier call to `Advertise()` that has not yet completed)
+     * that is registering the same service as @p aService that is being removed, we skip the advertisement of service
+     * removal (do not unregister the service on infrastructure DNS-SD). This ensures that when the outstanding
+     * advertisement is completed, the service is re-added successfully (and it is still being advertised by proxy).
+     * This behavior is different from `AdvertiseRemovalOf(Host &)`, where the outstanding advertisement is rejected
+     * because service removals are individual, compared to when removing a host where the host and all its associated
+     * services are removed.
+     *
+     * @param[in] aHost  The host which is being removed.
+     *
+     */
+    void AdvertiseRemovalOf(Service &aService);
+
+    /**
+     * Gets the set of counters.
+     *
+     * @returns The `AdvertisingProxy` counter.
+     *
+     */
+    const Counters &GetCounters(void) const { return mCounters; }
+
+    /**
+     * Resets the counters
+     *
+     */
+    void ResetCounters(void) { mCounters.Clear(); }
+
+    /**
+     * Gets the advertisement timeout (in msec).
+     *
+     * The default value of `OPENTHREAD_CONFIG_SRP_SERVER_SERVICE_UPDATE_TIMEOUT` is used when not explicitly set.
+     *
+     * @returns The advertisement timeout (in msec).
+     *
+     */
+    uint32_t GetAdvTimeout(void) const { return mAdvTimeout; }
+
+    /**
+     * Sets the advertisement timeout.
+     *
+     * Changing the timeout is intended for testing purposes only. This allows tests to use a long timeout to validate
+     * the behavior of `AdvertisingProxy` when new `Advertise()` requests replace entries in earlier requests.
+     *
+     * @param[in] aTimeout   The advertisement timeout (in msec).
+     *
+     */
+    void SetAdvTimeout(uint32_t aTimeout) { mAdvTimeout = Max(aTimeout, kAdvTimeout); }
+
+    /**
+     * Notifies `AdvertisingProxy` that SRP sever state changed.
+     *
+     */
+    void HandleServerStateChange(void) { UpdateState(); }
+
+    /**
+     * Notifies `AdvertisingProxy` that DND-SD platform state changed.
+     *
+     */
+    void HandleDnssdPlatformStateChange(void) { UpdateState(); }
+
+    /**
+     * Notifies `AdvertisingProxy` that `InfraIf` state changed.
+     *
+     */
+    void HandleInfraIfStateChanged(void) { UpdateState(); }
+
+private:
+    typedef Dnssd::RequestId RequestId;
+    typedef char             DnsName[Dns::Name::kMaxNameSize];
+
+    static constexpr RequestId kInvalidRequestId = Server::kInvalidRequestId;
+
+    static constexpr uint32_t kAdvTimeout = OPENTHREAD_CONFIG_SRP_SERVER_SERVICE_UPDATE_TIMEOUT; // in msec
+
+    enum State : uint8_t
+    {
+        kStateStopped,
+        kStateRunning,
+    };
+
+    struct AdvInfo : public Heap::Allocatable<AdvInfo>, public LinkedListEntry<AdvInfo>, public GetProvider<AdvInfo>
+    {
+        struct CompletionChecker
+        {
+            // Used in `Matches()` to check if advertisement is
+            // completed (successfully or failed).
+        };
+
+        struct ExpirationChecker
+        {
+            explicit ExpirationChecker(TimeMilli aNow)
+                : mNow(aNow)
+            {
+            }
+
+            TimeMilli mNow;
+        };
+
+        AdvInfo(Host &aHost, const Server::MessageMetadata &aMetadata, uint32_t aTimeout);
+        void      SignalServerToCommit(void);
+        bool      IsCompleted(void) const;
+        bool      Matches(const CompletionChecker &) const { return IsCompleted(); }
+        bool      Matches(const ExpirationChecker &aChecker) const { return (mExpireTime <= aChecker.mNow); }
+        Instance &GetInstance(void) const { return mHost.GetInstance(); }
+
+        AdvInfo                *mNext;
+        AdvInfo                *mBlockingAdv;
+        Host                   &mHost;
+        TimeMilli               mExpireTime;
+        Server::MessageMetadata mMessageMetadata;
+        Ip6::MessageInfo        mMessageInfo;
+        Error                   mError;
+    };
+
+    template <typename Entry> void UpdateAdvIdRangeOn(Entry &aEntry);
+    template <typename Entry> bool IsRegisteredOrRegistering(const Entry &aEntry) const;
+    template <typename Entry> bool IsKeyRegisteredOrRegistering(const Entry &aEntry) const;
+    template <typename Entry> void DecideToAdvertise(Entry &aEntry, bool aUnregisterEntry, bool aUnregisterKey);
+    template <typename Entry> void UpdateKeyRegistrationStatus(Entry &aEntry, const Entry &aExistingEntry);
+    template <typename Entry> bool CompareAndUpdate(Entry &aEntry, Entry &aExistingEntry);
+    template <typename Entry> bool EntriesMatch(const Entry &aFirstEntry, const Entry &aSecondEntry);
+
+    void        Start(void);
+    void        Stop(void);
+    void        UpdateState(void);
+    RequestId   AllocateNextRequestId(void);
+    void        Advertise(Host &aHost);
+    void        UnregisterHostAndItsServicesAndKeys(Host &aHost);
+    bool        CompareAndUpdateHostAndServices(Host &aHost, Host &aExistingHost);
+    bool        CompareAndUpdateHost(Host &aHost, Host &aExistingHost);
+    bool        CompareAndUpdateService(Service &aService, Service &aExistingService);
+    void        RegisterHost(Host &aHost);
+    void        UnregisterHost(Host &aHost);
+    void        RegisterService(Service &aService);
+    void        UnregisterService(Service &aService);
+    void        RegisterKey(Host &aHost);
+    void        RegisterKey(Service &aService);
+    void        RegisterKey(const char      *aName,
+                            const char      *aServiceType,
+                            const Host::Key &aKey,
+                            RequestId        aRequestId,
+                            uint32_t         aTtl);
+    void        UnregisterKey(Service &aService);
+    void        UnregisterKey(Host &aHost);
+    void        UnregisterKey(const char *aName, const char *aServiceType);
+    void        CopyNameAndRemoveDomain(DnsName &aName, const char *aFullName);
+    static void HandleRegistered(otInstance *aInstance, otPlatDnssdRequestId aRequestId, otError aError);
+    void        HandleRegistered(RequestId aRequestId, Error aError);
+    bool        HandleRegisteredRequestIdOn(Host &aHost, RequestId aRequestId, Error aError);
+    void        HandleTimer(void);
+    void        HandleTasklet(void);
+    void        SignalAdvCompleted(AdvInfo &aAdvInfo);
+
+    using AdvTimer   = TimerMilliIn<AdvertisingProxy, &AdvertisingProxy::HandleTimer>;
+    using AdvTasklet = TaskletIn<AdvertisingProxy, &AdvertisingProxy::HandleTasklet>;
+
+    State               mState;
+    RequestId           mCurrentRequestId;
+    uint32_t            mAdvTimeout;
+    OwningList<AdvInfo> mAdvInfoList;
+    AdvTimer            mTimer;
+    AdvTasklet          mTasklet;
+    Counters            mCounters;
+};
+
+} // namespace Srp
+} // namespace ot
+
+#endif // OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE
+
+#endif // SRP_ADVERTISING_PROXY_HPP_
diff --git a/src/core/net/srp_client.cpp b/src/core/net/srp_client.cpp
index 617091c..fc4a417 100644
--- a/src/core/net/srp_client.cpp
+++ b/src/core/net/srp_client.cpp
@@ -244,13 +244,13 @@
     , mState(kStateStopped)
     , mTxFailureRetryCount(0)
     , mShouldRemoveKeyLease(false)
-    , mAutoHostAddressAddedMeshLocal(false)
     , mSingleServiceMode(false)
 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
     , mServiceKeyRecordEnabled(false)
     , mUseShortLeaseOption(false)
 #endif
     , mUpdateMessageId(0)
+    , mAutoHostAddressCount(0)
     , mRetryWaitInterval(kMinRetryWaitInterval)
     , mTtl(0)
     , mLease(0)
@@ -421,20 +421,11 @@
     }
 #endif
 
-    if (mHostInfo.IsAutoAddressEnabled())
+    if (aEvents.ContainsAny(kEventIp6AddressAdded | kEventIp6AddressRemoved | kEventThreadMeshLocalAddrChanged) &&
+        ShouldUpdateHostAutoAddresses())
     {
-        Events::Flags eventFlags = (kEventIp6AddressAdded | kEventIp6AddressRemoved);
-
-        if (mAutoHostAddressAddedMeshLocal)
-        {
-            eventFlags |= kEventThreadMeshLocalAddrChanged;
-        }
-
-        if (aEvents.ContainsAny(eventFlags))
-        {
-            IgnoreError(UpdateHostInfoStateOnAddressChange());
-            UpdateState();
-        }
+        IgnoreError(UpdateHostInfoStateOnAddressChange());
+        UpdateState();
     }
 }
 
@@ -494,6 +485,13 @@
     VerifyOrExit(!mHostInfo.IsAutoAddressEnabled());
     SuccessOrExit(error = UpdateHostInfoStateOnAddressChange());
 
+    for (Ip6::Netif::UnicastAddress &unicastAddress : Get<ThreadNetif>().GetUnicastAddresses())
+    {
+        unicastAddress.mSrpRegistered = false;
+    }
+
+    mAutoHostAddressCount = 0;
+
     mHostInfo.EnableAutoAddress();
     UpdateState();
 
@@ -515,6 +513,68 @@
     return error;
 }
 
+bool Client::ShouldUpdateHostAutoAddresses(void) const
+{
+    bool                        shouldUpdate    = false;
+    uint16_t                    registeredCount = 0;
+    Ip6::Netif::UnicastAddress &ml64            = Get<Mle::Mle>().GetMeshLocal64UnicastAddress();
+
+    VerifyOrExit(mHostInfo.IsAutoAddressEnabled());
+
+    // Check all addresses on `ThreadNetif` excluding the mesh local
+    // EID (`ml64`). If any address should be registered but is not,
+    // or if any address was registered earlier but no longer should
+    // be, the host information needs to be re-registered to update
+    // the addresses. If there is no eligible address, then `ml64`
+    // should be registered, so its status is checked. Finally, the
+    // number of addresses that should be registered is verified
+    // against the previous value `mAutoHostAddressCount` to handle
+    // the case where an earlier registered address is now removed.
+
+    for (const Ip6::Netif::UnicastAddress &unicastAddress : Get<ThreadNetif>().GetUnicastAddresses())
+    {
+        if (&unicastAddress == &ml64)
+        {
+            continue;
+        }
+
+        if (ShouldHostAutoAddressRegister(unicastAddress) != unicastAddress.mSrpRegistered)
+        {
+            ExitNow(shouldUpdate = true);
+        }
+
+        if (unicastAddress.mSrpRegistered)
+        {
+            registeredCount++;
+        }
+    }
+
+    if (registeredCount == 0)
+    {
+        ExitNow(shouldUpdate = !ml64.mSrpRegistered);
+    }
+
+    shouldUpdate = (registeredCount != mAutoHostAddressCount);
+
+exit:
+    return shouldUpdate;
+}
+
+bool Client::ShouldHostAutoAddressRegister(const Ip6::Netif::UnicastAddress &aUnicastAddress) const
+{
+    bool shouldRegister = false;
+
+    VerifyOrExit(aUnicastAddress.mValid);
+    VerifyOrExit(aUnicastAddress.mPreferred);
+    VerifyOrExit(!aUnicastAddress.GetAddress().IsLinkLocal());
+    VerifyOrExit(!Get<Mle::Mle>().IsMeshLocalAddress(aUnicastAddress.GetAddress()));
+
+    shouldRegister = true;
+
+exit:
+    return shouldRegister;
+}
+
 Error Client::UpdateHostInfoStateOnAddressChange(void)
 {
     Error error = kErrorNone;
@@ -901,19 +961,16 @@
 
     if (error == kErrorNone)
     {
-        Crypto::Ecdsa::P256::PublicKey publicKey;
-
-        if (keyPair.GetPublicKey(publicKey) == kErrorNone)
+        if (aKeyRef.ImportKeyPair(keyPair) != kErrorNone)
         {
-            SuccessOrExit(error = aKeyRef.ImportKeyPair(keyPair));
-            IgnoreError(Get<Settings>().Delete<Settings::SrpEcdsaKey>());
-            ExitNow();
+            SuccessOrExit(error = aKeyRef.Generate());
         }
         IgnoreError(Get<Settings>().Delete<Settings::SrpEcdsaKey>());
     }
-
-    error = aKeyRef.Generate();
-
+    else
+    {
+        SuccessOrExit(error = aKeyRef.Generate());
+    }
 exit:
     return error;
 }
@@ -1244,25 +1301,29 @@
         // and mesh-local addresses. If no address is appended, we include
         // the mesh local EID.
 
-        mAutoHostAddressAddedMeshLocal = true;
+        mAutoHostAddressCount = 0;
 
-        for (const Ip6::Netif::UnicastAddress &unicastAddress : Get<ThreadNetif>().GetUnicastAddresses())
+        for (Ip6::Netif::UnicastAddress &unicastAddress : Get<ThreadNetif>().GetUnicastAddresses())
         {
-            const Ip6::Address &address = unicastAddress.GetAddress();
-
-            if (address.IsLinkLocal() || Get<Mle::Mle>().IsMeshLocalAddress(address) || !unicastAddress.mPreferred ||
-                !unicastAddress.mValid)
+            if (ShouldHostAutoAddressRegister(unicastAddress))
             {
-                continue;
+                SuccessOrExit(error = AppendAaaaRecord(unicastAddress.GetAddress(), aMessage, aInfo));
+                unicastAddress.mSrpRegistered = true;
+                mAutoHostAddressCount++;
             }
-
-            SuccessOrExit(error = AppendAaaaRecord(address, aMessage, aInfo));
-            mAutoHostAddressAddedMeshLocal = false;
+            else
+            {
+                unicastAddress.mSrpRegistered = false;
+            }
         }
 
-        if (mAutoHostAddressAddedMeshLocal)
+        if (mAutoHostAddressCount == 0)
         {
-            SuccessOrExit(error = AppendAaaaRecord(Get<Mle::Mle>().GetMeshLocal64(), aMessage, aInfo));
+            Ip6::Netif::UnicastAddress &ml64 = Get<Mle::Mle>().GetMeshLocal64UnicastAddress();
+
+            SuccessOrExit(error = AppendAaaaRecord(ml64.GetAddress(), aMessage, aInfo));
+            ml64.mSrpRegistered = true;
+            mAutoHostAddressCount++;
         }
     }
     else
diff --git a/src/core/net/srp_client.hpp b/src/core/net/srp_client.hpp
index 5260ea6..32b1ea0 100644
--- a/src/core/net/srp_client.hpp
+++ b/src/core/net/srp_client.hpp
@@ -1001,6 +1001,8 @@
     void  Pause(void);
     void  HandleNotifierEvents(Events aEvents);
     void  HandleRoleChanged(void);
+    bool  ShouldUpdateHostAutoAddresses(void) const;
+    bool  ShouldHostAutoAddressRegister(const Ip6::Netif::UnicastAddress &aUnicastAddress) const;
     Error UpdateHostInfoStateOnAddressChange(void);
     void  UpdateServiceStateToRemove(Service &aService);
     State GetState(void) const { return mState; }
@@ -1065,7 +1067,6 @@
     State   mState;
     uint8_t mTxFailureRetryCount : 4;
     bool    mShouldRemoveKeyLease : 1;
-    bool    mAutoHostAddressAddedMeshLocal : 1;
     bool    mSingleServiceMode : 1;
 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
     bool mServiceKeyRecordEnabled : 1;
@@ -1073,6 +1074,7 @@
 #endif
 
     uint16_t mUpdateMessageId;
+    uint16_t mAutoHostAddressCount;
     uint32_t mRetryWaitInterval;
 
     TimeMilli mLeaseRenewTime;
diff --git a/src/core/net/srp_server.cpp b/src/core/net/srp_server.cpp
index aaaaaa7..635cbed 100644
--- a/src/core/net/srp_server.cpp
+++ b/src/core/net/srp_server.cpp
@@ -91,7 +91,7 @@
     , mOutstandingUpdatesTimer(aInstance)
     , mCompletedUpdateTask(aInstance)
     , mServiceUpdateId(Random::NonCrypto::GetUint32())
-    , mPort(kUdpPortMin)
+    , mPort(kUninitializedPort)
     , mState(kStateDisabled)
     , mAddressMode(kDefaultAddressMode)
     , mAnycastSequenceNumber(0)
@@ -329,7 +329,7 @@
 
     if (mServiceUpdateHandler.IsSet())
     {
-        uint32_t updateId = AllocateId();
+        uint32_t updateId = AllocateServiceUpdateId();
 
         LogInfo("SRP update handler is notified (updatedId = %lu)", ToUlong(updateId));
         mServiceUpdateHandler.Invoke(updateId, aHost, static_cast<uint32_t>(kDefaultEventsHandlerTimeout));
@@ -339,6 +339,12 @@
         // only when there is system failure of the platform mDNS implementation
         // and in which case the host is not expected to be still registered.
     }
+#if OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE
+    else
+    {
+        Get<AdvertisingProxy>().AdvertiseRemovalOf(*aHost);
+    }
+#endif
 
     if (!aRetainName)
     {
@@ -456,7 +462,7 @@
     uint32_t grantedKeyLease = 0;
     bool     useShortLease   = aHost.ShouldUseShortLeaseOption();
 
-    if (aError != kErrorNone)
+    if (aError != kErrorNone || (mState != kStateRunning))
     {
         aHost.Free();
         ExitNow();
@@ -525,7 +531,27 @@
             if (!aHost.HasService(existingService->GetInstanceName()))
             {
                 aHost.AddService(*existingService);
-                existingService->Log(Service::kKeepUnchanged);
+
+                // If host is deleted we make sure to add any existing
+                // service that is not already included as deleted.
+                // When processing an SRP update message that removes
+                // a host, we construct `aHost` and add any existing
+                // services that we have for the same host name as
+                // deleted. However, due to asynchronous nature
+                // of "update handler" (advertising proxy), by the
+                // time we get the callback to commit `aHost`, there
+                // may be new services added that were not included
+                // when constructing `aHost`.
+
+                if (aHost.IsDeleted() && !existingService->IsDeleted())
+                {
+                    existingService->mIsDeleted = true;
+                    existingService->Log(Service::kRemoveButRetainName);
+                }
+                else
+                {
+                    existingService->Log(Service::kKeepUnchanged);
+                }
             }
             else
             {
@@ -569,7 +595,7 @@
     }
 }
 
-void Server::SelectPort(void)
+void Server::InitPort(void)
 {
     mPort = kUdpPortMin;
 
@@ -579,31 +605,51 @@
 
         if (Get<Settings>().Read(info) == kErrorNone)
         {
-            mPort = info.GetPort() + 1;
-            if (mPort < kUdpPortMin || mPort > kUdpPortMax)
-            {
-                mPort = kUdpPortMin;
-            }
+            mPort = info.GetPort();
         }
     }
 #endif
+}
+
+void Server::SelectPort(void)
+{
+    if (mPort == kUninitializedPort)
+    {
+        InitPort();
+    }
+    ++mPort;
+    if (mPort < kUdpPortMin || mPort > kUdpPortMax)
+    {
+        mPort = kUdpPortMin;
+    }
 
     LogInfo("Selected port %u", mPort);
 }
 
 void Server::Start(void)
 {
+    Error error = kErrorNone;
+
     VerifyOrExit(mState == kStateStopped);
 
     mState = kStateRunning;
-    PrepareSocket();
+    SuccessOrExit(error = PrepareSocket());
     LogInfo("Start listening on port %u", mPort);
 
+#if OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE
+    Get<AdvertisingProxy>().HandleServerStateChange();
+#endif
+
 exit:
-    return;
+    // Re-enable server to select a new port.
+    if (error != kErrorNone)
+    {
+        Disable();
+        Enable();
+    }
 }
 
-void Server::PrepareSocket(void)
+Error Server::PrepareSocket(void)
 {
     Error error = kErrorNone;
 
@@ -628,9 +674,12 @@
 exit:
     if (error != kErrorNone)
     {
-        LogCrit("Failed to prepare socket: %s", ErrorToString(error));
+        LogWarnOnError(error, "prepare socket");
+        IgnoreError(mSocket.Close());
         Stop();
     }
+
+    return error;
 }
 
 Ip6::Udp::Socket &Server::GetSocket(void)
@@ -659,7 +708,7 @@
 
     if (mState == kStateRunning)
     {
-        PrepareSocket();
+        IgnoreError(PrepareSocket());
     }
 }
 
@@ -708,6 +757,10 @@
     IgnoreError(mSocket.Close());
     mHasRegisteredAnyService = false;
 
+#if OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE
+    Get<AdvertisingProxy>().HandleServerStateChange();
+#endif
+
 exit:
     return;
 }
@@ -777,6 +830,13 @@
     // Parse lease time and validate signature.
     SuccessOrExit(error = ProcessAdditionalSection(host, aMessage, aMetadata));
 
+#if OPENTHREAD_FTD
+    if (aMetadata.IsDirectRxFromClient())
+    {
+        UpdateAddrResolverCacheTable(*aMetadata.mMessageInfo, *host);
+    }
+#endif
+
     HandleUpdate(*host, aMetadata);
 
 exit:
@@ -796,13 +856,13 @@
 
 Error Server::ProcessZoneSection(const Message &aMessage, MessageMetadata &aMetadata) const
 {
-    Error    error = kErrorNone;
-    char     name[Dns::Name::kMaxNameSize];
-    uint16_t offset = aMetadata.mOffset;
+    Error             error = kErrorNone;
+    Dns::Name::Buffer name;
+    uint16_t          offset = aMetadata.mOffset;
 
     VerifyOrExit(aMetadata.mDnsHeader.GetZoneRecordCount() == 1, error = kErrorParse);
 
-    SuccessOrExit(error = Dns::Name::ReadName(aMessage, offset, name, sizeof(name)));
+    SuccessOrExit(error = Dns::Name::ReadName(aMessage, offset, name));
     // TODO: return `Dns::kResponseNotAuth` for not authorized zone names.
     VerifyOrExit(StringMatch(name, GetDomain(), kStringCaseInsensitiveMatch), error = kErrorSecurity);
     SuccessOrExit(error = aMessage.Read(offset, aMetadata.mDnsZone));
@@ -812,11 +872,7 @@
     aMetadata.mOffset = offset;
 
 exit:
-    if (error != kErrorNone)
-    {
-        LogWarn("Failed to process DNS Zone section: %s", ErrorToString(error));
-    }
-
+    LogWarnOnError(error, "process DNS Zone section");
     return error;
 }
 
@@ -842,11 +898,7 @@
     VerifyOrExit(!HasNameConflictsWith(aHost), error = kErrorDuplicated);
 
 exit:
-    if (error != kErrorNone)
-    {
-        LogWarn("Failed to process DNS Update section: %s", ErrorToString(error));
-    }
-
+    LogWarnOnError(error, "Process DNS Update section");
     return error;
 }
 
@@ -861,10 +913,10 @@
 
     for (uint16_t numRecords = aMetadata.mDnsHeader.GetUpdateRecordCount(); numRecords > 0; numRecords--)
     {
-        char                name[Dns::Name::kMaxNameSize];
+        Dns::Name::Buffer   name;
         Dns::ResourceRecord record;
 
-        SuccessOrExit(error = Dns::Name::ReadName(aMessage, offset, name, sizeof(name)));
+        SuccessOrExit(error = Dns::Name::ReadName(aMessage, offset, name));
 
         SuccessOrExit(error = aMessage.Read(offset, record));
 
@@ -935,11 +987,7 @@
     // the host is being removed or registered.
 
 exit:
-    if (error != kErrorNone)
-    {
-        LogWarn("Failed to process Host Description instructions: %s", ErrorToString(error));
-    }
-
+    LogWarnOnError(error, "process Host Description instructions");
     return error;
 }
 
@@ -952,9 +1000,9 @@
 
     for (uint16_t numRecords = aMetadata.mDnsHeader.GetUpdateRecordCount(); numRecords > 0; numRecords--)
     {
-        char                            serviceName[Dns::Name::kMaxNameSize];
-        char                            instanceLabel[Dns::Name::kMaxLabelSize];
-        char                            instanceServiceName[Dns::Name::kMaxNameSize];
+        Dns::Name::Buffer               serviceName;
+        Dns::Name::LabelBuffer          instanceLabel;
+        Dns::Name::Buffer               instanceServiceName;
         String<Dns::Name::kMaxNameSize> instanceName;
         Dns::PtrRecord                  ptrRecord;
         const char                     *subServiceName;
@@ -962,7 +1010,7 @@
         bool                            isSubType;
         bool                            isDelete;
 
-        SuccessOrExit(error = Dns::Name::ReadName(aMessage, offset, serviceName, sizeof(serviceName)));
+        SuccessOrExit(error = Dns::Name::ReadName(aMessage, offset, serviceName));
         VerifyOrExit(Dns::Name::IsSubDomainOf(serviceName, GetDomain()), error = kErrorSecurity);
 
         error = Dns::ResourceRecord::ReadRecord(aMessage, offset, ptrRecord);
@@ -977,8 +1025,7 @@
 
         SuccessOrExit(error);
 
-        SuccessOrExit(error = ptrRecord.ReadPtrName(aMessage, offset, instanceLabel, sizeof(instanceLabel),
-                                                    instanceServiceName, sizeof(instanceServiceName)));
+        SuccessOrExit(error = ptrRecord.ReadPtrName(aMessage, offset, instanceLabel, instanceServiceName));
         instanceName.Append("%s.%s", instanceLabel, instanceServiceName);
 
         // Class None indicates "Delete an RR from an RRset".
@@ -1059,11 +1106,7 @@
     }
 
 exit:
-    if (error != kErrorNone)
-    {
-        LogWarn("Failed to process Service Discovery instructions: %s", ErrorToString(error));
-    }
-
+    LogWarnOnError(error, "process Service Discovery instructions");
     return error;
 }
 
@@ -1076,11 +1119,11 @@
 
     for (uint16_t numRecords = aMetadata.mDnsHeader.GetUpdateRecordCount(); numRecords > 0; numRecords--)
     {
-        char                name[Dns::Name::kMaxNameSize];
+        Dns::Name::Buffer   name;
         Dns::ResourceRecord record;
         Service            *service;
 
-        SuccessOrExit(error = Dns::Name::ReadName(aMessage, offset, name, sizeof(name)));
+        SuccessOrExit(error = Dns::Name::ReadName(aMessage, offset, name));
         SuccessOrExit(error = aMessage.Read(offset, record));
 
         if (record.GetClass() == Dns::ResourceRecord::kClassAny)
@@ -1102,9 +1145,8 @@
 
         if (record.GetType() == Dns::ResourceRecord::kTypeSrv)
         {
-            Dns::SrvRecord srvRecord;
-            char           hostName[Dns::Name::kMaxNameSize];
-            uint16_t       hostNameLength = sizeof(hostName);
+            Dns::SrvRecord    srvRecord;
+            Dns::Name::Buffer hostName;
 
             VerifyOrExit(record.GetClass() == aMetadata.mDnsZone.GetClass(), error = kErrorFailed);
 
@@ -1113,7 +1155,7 @@
             SuccessOrExit(error = aMessage.Read(offset, srvRecord));
             offset += sizeof(srvRecord);
 
-            SuccessOrExit(error = Dns::Name::ReadName(aMessage, offset, hostName, hostNameLength));
+            SuccessOrExit(error = Dns::Name::ReadName(aMessage, offset, hostName));
             VerifyOrExit(Dns::Name::IsSubDomainOf(name, GetDomain()), error = kErrorSecurity);
             VerifyOrExit(aHost.Matches(hostName), error = kErrorFailed);
 
@@ -1163,11 +1205,7 @@
     aMetadata.mOffset = offset;
 
 exit:
-    if (error != kErrorNone)
-    {
-        LogWarn("Failed to process Service Description instructions: %s", ErrorToString(error));
-    }
-
+    LogWarnOnError(error, "process Service Description instructions");
     return error;
 }
 
@@ -1179,22 +1217,22 @@
 
 Error Server::ProcessAdditionalSection(Host *aHost, const Message &aMessage, MessageMetadata &aMetadata) const
 {
-    Error            error = kErrorNone;
-    Dns::OptRecord   optRecord;
-    Dns::LeaseOption leaseOption;
-    Dns::SigRecord   sigRecord;
-    char             name[2]; // The root domain name (".") is expected.
-    uint16_t         offset = aMetadata.mOffset;
-    uint16_t         sigOffset;
-    uint16_t         sigRdataOffset;
-    char             signerName[Dns::Name::kMaxNameSize];
-    uint16_t         signatureLength;
+    Error             error = kErrorNone;
+    Dns::OptRecord    optRecord;
+    Dns::LeaseOption  leaseOption;
+    Dns::SigRecord    sigRecord;
+    char              name[2]; // The root domain name (".") is expected.
+    uint16_t          offset = aMetadata.mOffset;
+    uint16_t          sigOffset;
+    uint16_t          sigRdataOffset;
+    Dns::Name::Buffer signerName;
+    uint16_t          signatureLength;
 
     VerifyOrExit(aMetadata.mDnsHeader.GetAdditionalRecordCount() == 2, error = kErrorFailed);
 
     // EDNS(0) Update Lease Option.
 
-    SuccessOrExit(error = Dns::Name::ReadName(aMessage, offset, name, sizeof(name)));
+    SuccessOrExit(error = Dns::Name::ReadName(aMessage, offset, name));
     SuccessOrExit(error = aMessage.Read(offset, optRecord));
 
     SuccessOrExit(error = leaseOption.ReadFrom(aMessage, offset + sizeof(optRecord), optRecord.GetLength()));
@@ -1204,24 +1242,31 @@
     aHost->SetLease(leaseOption.GetLeaseInterval());
     aHost->SetKeyLease(leaseOption.GetKeyLeaseInterval());
 
+    for (Service &service : aHost->mServices)
+    {
+        if (aHost->GetLease() == 0)
+        {
+            service.mIsDeleted = true;
+        }
+
+        service.mLease    = service.mIsDeleted ? 0 : leaseOption.GetLeaseInterval();
+        service.mKeyLease = leaseOption.GetKeyLeaseInterval();
+    }
+
     // If the client included the short variant of Lease Option,
     // server must also use the short variant in its response.
     aHost->SetUseShortLeaseOption(leaseOption.IsShortVariant());
 
     if (aHost->GetLease() > 0)
     {
-        uint8_t hostAddressesNum;
-
-        aHost->GetAddresses(hostAddressesNum);
-
         // There MUST be at least one valid address if we have nonzero lease.
-        VerifyOrExit(hostAddressesNum > 0, error = kErrorFailed);
+        VerifyOrExit(aHost->mAddresses.GetLength() > 0, error = kErrorFailed);
     }
 
     // SIG(0).
 
     sigOffset = offset;
-    SuccessOrExit(error = Dns::Name::ReadName(aMessage, offset, name, sizeof(name)));
+    SuccessOrExit(error = Dns::Name::ReadName(aMessage, offset, name));
     SuccessOrExit(error = aMessage.Read(offset, sigRecord));
     VerifyOrExit(sigRecord.IsValid(), error = kErrorParse);
 
@@ -1232,7 +1277,7 @@
     // implemented because the end device may not be able to get
     // the synchronized date/time.
 
-    SuccessOrExit(error = Dns::Name::ReadName(aMessage, offset, signerName, sizeof(signerName)));
+    SuccessOrExit(error = Dns::Name::ReadName(aMessage, offset, signerName));
 
     signatureLength = sigRecord.GetLength() - (offset - sigRdataOffset);
     offset += signatureLength;
@@ -1249,11 +1294,7 @@
     aMetadata.mOffset = offset;
 
 exit:
-    if (error != kErrorNone)
-    {
-        LogWarn("Failed to process DNS Additional section: %s", ErrorToString(error));
-    }
-
+    LogWarnOnError(error, "process DNS Additional section");
     return error;
 }
 
@@ -1300,11 +1341,7 @@
     error = aKey.Verify(hash, signature);
 
 exit:
-    if (error != kErrorNone)
-    {
-        LogWarn("Failed to verify message signature: %s", ErrorToString(error));
-    }
-
+    LogWarnOnError(error, "verify message signature");
     FreeMessage(signerNameMessage);
     return error;
 }
@@ -1342,6 +1379,7 @@
 
         SuccessOrExit(error = service->mServiceName.Set(existingService.GetServiceName()));
         service->mIsDeleted = true;
+        service->mKeyLease  = existingService.mKeyLease;
     }
 
 exit:
@@ -1383,9 +1421,9 @@
 
             for (const Heap::String &subType : service.mSubTypes)
             {
-                char label[Dns::Name::kMaxLabelSize];
+                Dns::Name::LabelBuffer label;
 
-                IgnoreError(Service::ParseSubTypeServiceName(subType.AsCString(), label, sizeof(label)));
+                IgnoreError(Service::ParseSubTypeServiceName(subType.AsCString(), label));
                 LogInfo("      sub-type: %s", label);
             }
         }
@@ -1412,6 +1450,17 @@
 
         aError = kErrorNoBufs;
     }
+#if OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE
+    else if (aError == kErrorNone)
+    {
+        // Ask `AdvertisingProxy` to advertise the new update.
+        // Proxy will report the outcome by calling
+        // `Server::CommitSrpUpdate()` directly.
+
+        Get<AdvertisingProxy>().Advertise(aHost, aMetadata);
+        ExitNow();
+    }
+#endif
 
     CommitSrpUpdate(aError, aHost, aMetadata);
 
@@ -1423,10 +1472,12 @@
                           Dns::UpdateHeader::Response aResponseCode,
                           const Ip6::MessageInfo     &aMessageInfo)
 {
-    Error             error;
+    Error             error    = kErrorNone;
     Message          *response = nullptr;
     Dns::UpdateHeader header;
 
+    VerifyOrExit(mState == kStateRunning, error = kErrorInvalidState);
+
     response = GetSocket().NewMessage();
     VerifyOrExit(response != nullptr, error = kErrorNoBufs);
 
@@ -1450,11 +1501,8 @@
     UpdateResponseCounters(aResponseCode);
 
 exit:
-    if (error != kErrorNone)
-    {
-        LogWarn("Failed to send response: %s", ErrorToString(error));
-        FreeMessage(response);
-    }
+    LogWarnOnError(error, "send response");
+    FreeMessageOnError(response, error);
 }
 
 void Server::SendResponse(const Dns::UpdateHeader &aHeader,
@@ -1509,11 +1557,8 @@
     UpdateResponseCounters(Dns::UpdateHeader::kResponseSuccess);
 
 exit:
-    if (error != kErrorNone)
-    {
-        LogWarn("Failed to send response: %s", ErrorToString(error));
-        FreeMessage(response);
-    }
+    LogWarnOnError(error, "send response");
+    FreeMessageOnError(response, error);
 }
 
 void Server::HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
@@ -1525,10 +1570,8 @@
 {
     Error error = ProcessMessage(aMessage, aMessageInfo);
 
-    if (error != kErrorNone)
-    {
-        LogInfo("Failed to handle DNS message: %s", ErrorToString(error));
-    }
+    LogWarnOnError(error, "handle DNS message");
+    OT_UNUSED_VARIABLE(error);
 }
 
 Error Server::ProcessMessage(Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
@@ -1736,6 +1779,41 @@
     }
 }
 
+#if OPENTHREAD_FTD
+void Server::UpdateAddrResolverCacheTable(const Ip6::MessageInfo &aMessageInfo, const Host &aHost)
+{
+    // If message is from a client on mesh, we add all registered
+    // addresses as snooped entries in the address resolver cache
+    // table. We associate the registered addresses with the same
+    // RLOC16 (if any) as the received message's peer IPv6 address.
+
+    uint16_t rloc16;
+
+    VerifyOrExit(aHost.GetLease() != 0);
+    VerifyOrExit(aHost.GetTtl() > 0);
+
+    // If the `LookUp()` call succeeds, the cache entry will be marked
+    // as "cached and in-use". We can mark it as "in-use" early since
+    // the entry will be needed and used soon when sending the SRP
+    // response. This also prevents a snooped cache entry (added for
+    // `GetPeerAddr()` due to rx of the SRP update message) from
+    // being overwritten by `UpdateSnoopedCacheEntry()` calls when
+    // there are limited snoop entries available.
+
+    rloc16 = Get<AddressResolver>().LookUp(aMessageInfo.GetPeerAddr());
+
+    VerifyOrExit(rloc16 != Mac::kShortAddrInvalid);
+
+    for (const Ip6::Address &address : aHost.mAddresses)
+    {
+        Get<AddressResolver>().UpdateSnoopedCacheEntry(address, rloc16, Get<Mle::Mle>().GetRloc16());
+    }
+
+exit:
+    return;
+}
+#endif
+
 //---------------------------------------------------------------------------------------------------------------------
 // Server::Service
 
@@ -1754,6 +1832,15 @@
     mUpdateTime  = aUpdateTime;
     mIsDeleted   = false;
     mIsCommitted = false;
+#if OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE
+    mIsRegistered      = false;
+    mIsKeyRegistered   = false;
+    mIsReplaced        = false;
+    mShouldAdvertise   = false;
+    mShouldRegisterKey = false;
+    mAdvId             = kInvalidRequestId;
+    mKeyAdvId          = kInvalidRequestId;
+#endif
 
     mParsedDeleteAllRrset = false;
     mParsedSrv            = false;
@@ -1892,9 +1979,9 @@
 
         for (const Heap::String &subType : mSubTypes)
         {
-            char label[Dns::Name::kMaxLabelSize];
+            Dns::Name::LabelBuffer label;
 
-            IgnoreError(ParseSubTypeServiceName(subType.AsCString(), label, sizeof(label)));
+            IgnoreError(ParseSubTypeServiceName(subType.AsCString(), label));
             LogInfo("  sub-type: %s", subType.AsCString());
         }
     }
@@ -1932,6 +2019,15 @@
     , mUpdateTime(aUpdateTime)
     , mParsedKey(false)
     , mUseShortLeaseOption(false)
+#if OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE
+    , mIsRegistered(false)
+    , mIsKeyRegistered(false)
+    , mIsReplaced(false)
+    , mShouldAdvertise(false)
+    , mShouldRegisterKey(false)
+    , mAdvId(kInvalidRequestId)
+    , mKeyAdvId(kInvalidRequestId)
+#endif
 {
 }
 
@@ -2043,12 +2139,18 @@
     VerifyOrExit(aService != nullptr);
 
     aService->mIsDeleted = true;
+    aService->mLease     = 0;
+
+    if (!aRetainName)
+    {
+        aService->mKeyLease = 0;
+    }
 
     aService->Log(aRetainName ? Service::kRemoveButRetainName : Service::kFullyRemove);
 
     if (aNotifyServiceHandler && server.mServiceUpdateHandler.IsSet())
     {
-        uint32_t updateId = server.AllocateId();
+        uint32_t updateId = server.AllocateServiceUpdateId();
 
         LogInfo("SRP update handler is notified (updatedId = %lu)", ToUlong(updateId));
         server.mServiceUpdateHandler.Invoke(updateId, this, static_cast<uint32_t>(kDefaultEventsHandlerTimeout));
@@ -2058,6 +2160,12 @@
         // failure of the platform mDNS implementation and in which case the
         // service is not expected to be still registered.
     }
+#if OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE
+    else if (aNotifyServiceHandler)
+    {
+        Get<AdvertisingProxy>().AdvertiseRemovalOf(*aService);
+    }
+#endif
 
     if (!aRetainName)
     {
@@ -2121,7 +2229,7 @@
     , mNext(nullptr)
     , mExpireTime(TimerMilli::GetNow() + kDefaultEventsHandlerTimeout)
     , mDnsHeader(aMessageMetadata.mDnsHeader)
-    , mId(Get<Server>().AllocateId())
+    , mId(Get<Server>().AllocateServiceUpdateId())
     , mTtlConfig(aMessageMetadata.mTtlConfig)
     , mLeaseConfig(aMessageMetadata.mLeaseConfig)
     , mHost(aHost)
diff --git a/src/core/net/srp_server.hpp b/src/core/net/srp_server.hpp
index bb73c15..ebe67d8 100644
--- a/src/core/net/srp_server.hpp
+++ b/src/core/net/srp_server.hpp
@@ -72,6 +72,7 @@
 #include "common/timer.hpp"
 #include "crypto/ecdsa.hpp"
 #include "net/dns_types.hpp"
+#include "net/dnssd.hpp"
 #include "net/ip6.hpp"
 #include "net/ip6_address.hpp"
 #include "net/udp6.hpp"
@@ -101,6 +102,8 @@
 
 namespace Srp {
 
+class AdvertisingProxy;
+
 /**
  * Implements the SRP server.
  *
@@ -112,6 +115,7 @@
     friend class Service;
     friend class Host;
     friend class Dns::ServiceDiscovery::Server;
+    friend class AdvertisingProxy;
 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
     friend class BorderRouter::RoutingManager;
 #endif
@@ -128,6 +132,10 @@
         kNotifyServiceHandler      = true,
     };
 
+#if OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE
+    static constexpr Dnssd::RequestId kInvalidRequestId = 0;
+#endif
+
 public:
     static constexpr uint16_t kUdpPortMin = OPENTHREAD_CONFIG_SRP_SERVER_UDP_PORT_MIN; ///< The reserved min port.
     static constexpr uint16_t kUdpPortMax = OPENTHREAD_CONFIG_SRP_SERVER_UDP_PORT_MAX; ///< The reserved max port.
@@ -185,6 +193,7 @@
         friend class LinkedList<Service>;
         friend class LinkedListEntry<Service>;
         friend class Heap::Allocatable<Service>;
+        friend class AdvertisingProxy;
 
     public:
         /**
@@ -407,6 +416,12 @@
         bool  Matches(const char *aInstanceName) const;
         void  Log(Action aAction) const;
 
+        template <uint16_t kLabelSize>
+        static Error ParseSubTypeServiceName(const char *aSubTypeServiceName, char (&aLabel)[kLabelSize])
+        {
+            return ParseSubTypeServiceName(aSubTypeServiceName, aLabel, kLabelSize);
+        }
+
         Service                  *mNext;
         Heap::String              mInstanceName;
         Heap::String              mInstanceLabel;
@@ -426,6 +441,15 @@
         bool                      mParsedDeleteAllRrset : 1;
         bool                      mParsedSrv : 1;
         bool                      mParsedTxt : 1;
+#if OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE
+        bool             mIsRegistered : 1;
+        bool             mIsKeyRegistered : 1;
+        bool             mIsReplaced : 1;
+        bool             mShouldAdvertise : 1;
+        bool             mShouldRegisterKey : 1;
+        Dnssd::RequestId mAdvId;
+        Dnssd::RequestId mKeyAdvId;
+#endif
     };
 
     /**
@@ -441,6 +465,7 @@
         friend class Server;
         friend class LinkedListEntry<Host>;
         friend class Heap::Allocatable<Host>;
+        friend class AdvertisingProxy;
 
     public:
         typedef Crypto::Ecdsa::P256::PublicKey Key; ///< Host key (public ECDSA P256 key).
@@ -597,6 +622,16 @@
         LinkedList<Service>       mServices;
         bool                      mParsedKey : 1;
         bool                      mUseShortLeaseOption : 1; // Use short lease option (lease only 4 bytes).
+#if OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE
+        bool                  mIsRegistered : 1;
+        bool                  mIsKeyRegistered : 1;
+        bool                  mIsReplaced : 1;
+        bool                  mShouldAdvertise : 1;
+        bool                  mShouldRegisterKey : 1;
+        Dnssd::RequestId      mAdvId;
+        Dnssd::RequestId      mKeyAdvId;
+        Dnssd::RequestIdRange mAdvIdRange;
+#endif
     };
 
     /**
@@ -877,6 +912,7 @@
     static constexpr AddressMode kDefaultAddressMode =
         static_cast<AddressMode>(OPENTHREAD_CONFIG_SRP_SERVER_DEFAULT_ADDRESS_MODE);
 
+    static constexpr uint16_t kUninitializedPort      = 0;
     static constexpr uint16_t kAnycastAddressModePort = 53;
 
     // Metadata for a received SRP Update message.
@@ -936,9 +972,11 @@
     void              Disable(void);
     void              Start(void);
     void              Stop(void);
+    void              InitPort(void);
     void              SelectPort(void);
-    void              PrepareSocket(void);
+    Error             PrepareSocket(void);
     Ip6::Udp::Socket &GetSocket(void);
+    LinkedList<Host> &GetHosts(void) { return mHosts; }
 
 #if OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE
     void  HandleDnssdServerStateChange(void);
@@ -947,7 +985,7 @@
 
     void HandleNetDataPublisherEvent(NetworkData::Publisher::Event aEvent);
 
-    ServiceUpdateId AllocateId(void) { return mServiceUpdateId++; }
+    ServiceUpdateId AllocateServiceUpdateId(void) { return mServiceUpdateId++; }
 
     void  InformUpdateHandlerOrCommit(Error aError, Host &aHost, const MessageMetadata &aMetadata);
     void  CommitSrpUpdate(Error aError, Host &aHost, const MessageMetadata &aMessageMetadata);
@@ -1007,6 +1045,7 @@
     static const char    *AddressModeToString(AddressMode aMode);
 
     void UpdateResponseCounters(Dns::Header::Response aResponseCode);
+    void UpdateAddrResolverCacheTable(const Ip6::MessageInfo &aMessageInfo, const Host &aHost);
 
     using LeaseTimer           = TimerMilliIn<Server, &Server::HandleLeaseTimer>;
     using UpdateTimer          = TimerMilliIn<Server, &Server::HandleOutstandingUpdatesTimer>;
diff --git a/src/core/net/tcp6.cpp b/src/core/net/tcp6.cpp
index d9712a3..470e92a 100644
--- a/src/core/net/tcp6.cpp
+++ b/src/core/net/tcp6.cpp
@@ -81,7 +81,7 @@
     Error         error;
     struct tcpcb &tp = GetTcb();
 
-    memset(&tp, 0x00, sizeof(tp));
+    ClearAllBytes(tp);
 
     SuccessOrExit(error = aInstance.Get<Tcp>().mEndpoints.Add(*this));
 
@@ -92,8 +92,8 @@
     mReceiveAvailableCallback = aArgs.mReceiveAvailableCallback;
     mDisconnectedCallback     = aArgs.mDisconnectedCallback;
 
-    memset(mTimers, 0x00, sizeof(mTimers));
-    memset(&mSockAddr, 0x00, sizeof(mSockAddr));
+    ClearAllBytes(mTimers);
+    ClearAllBytes(mSockAddr);
     mPendingCallbacks = 0;
 
     /*
@@ -549,7 +549,7 @@
     mAcceptReadyCallback = aArgs.mAcceptReadyCallback;
     mAcceptDoneCallback  = aArgs.mAcceptDoneCallback;
 
-    memset(tpl, 0x00, sizeof(struct tcpcb_listen));
+    ClearAllBytes(*tpl);
     tpl->instance = &aInstance;
 
 exit:
@@ -579,7 +579,7 @@
 {
     struct tcpcb_listen *tpl = &GetTcbListen();
 
-    memset(&tpl->laddr, 0x00, sizeof(tpl->laddr));
+    ClearAllBytes(tpl->laddr);
     tpl->lport   = 0;
     tpl->t_state = TCP6S_CLOSED;
     return kErrorNone;
@@ -670,7 +670,7 @@
         otLinkedBuffer *priorHead    = lbuf_head(&tp->sendbuf);
         size_t          priorBacklog = endpoint->GetSendBufferBytes() - endpoint->GetInFlightBytes();
 
-        memset(&sig, 0x00, sizeof(sig));
+        ClearAllBytes(sig);
         nextAction = tcp_input(ip6Header, tcpHeader, &aMessage, tp, nullptr, &sig);
         if (nextAction != RELOOKUP_REQUIRED)
         {
@@ -685,7 +685,7 @@
     {
         struct tcpcb_listen *tpl = &listener->GetTcbListen();
 
-        memset(&sig, 0x00, sizeof(sig));
+        ClearAllBytes(sig);
         nextAction = tcp_input(ip6Header, tcpHeader, &aMessage, nullptr, tpl, &sig);
         OT_ASSERT(nextAction != RELOOKUP_REQUIRED);
         if (sig.accepted_connection != nullptr)
diff --git a/src/core/openthread-core-config.h b/src/core/openthread-core-config.h
index ea238fb..009b671 100644
--- a/src/core/openthread-core-config.h
+++ b/src/core/openthread-core-config.h
@@ -41,7 +41,9 @@
 #define OT_THREAD_VERSION_1_1 2
 #define OT_THREAD_VERSION_1_2 3
 #define OT_THREAD_VERSION_1_3 4
+// Support projects on legacy "1.3.1" version, which is now "1.4"
 #define OT_THREAD_VERSION_1_3_1 5
+#define OT_THREAD_VERSION_1_4 5
 
 #define OPENTHREAD_CORE_CONFIG_H_IN
 
@@ -88,7 +90,6 @@
 #include "config/dns_client.h"
 #include "config/dns_dso.h"
 #include "config/dnssd_server.h"
-#include "config/dtls.h"
 #include "config/history_tracker.h"
 #include "config/ip6.h"
 #include "config/joiner.h"
@@ -109,11 +110,13 @@
 #include "config/platform.h"
 #include "config/power_calibration.h"
 #include "config/radio_link.h"
+#include "config/secure_transport.h"
 #include "config/sntp_client.h"
 #include "config/srp_client.h"
 #include "config/srp_server.h"
 #include "config/time_sync.h"
 #include "config/tmf.h"
+#include "config/trel.h"
 
 #undef OPENTHREAD_CORE_CONFIG_H_IN
 
diff --git a/src/core/radio.cmake b/src/core/radio.cmake
index 95ae915..a416052 100644
--- a/src/core/radio.cmake
+++ b/src/core/radio.cmake
@@ -29,6 +29,8 @@
 add_library(openthread-radio)
 
 target_compile_definitions(openthread-radio PRIVATE
+    OPENTHREAD_FTD=0
+    OPENTHREAD_MTD=0
     OPENTHREAD_RADIO=1
     OPENTHREAD_RADIO_CLI=0
 )
diff --git a/src/core/radio/ble_secure.cpp b/src/core/radio/ble_secure.cpp
new file mode 100644
index 0000000..8f4a0a9
--- /dev/null
+++ b/src/core/radio/ble_secure.cpp
@@ -0,0 +1,555 @@
+/*
+ *  Copyright (c) 2023, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "ble_secure.hpp"
+
+#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
+
+#include <openthread/platform/ble.h>
+#include "common/locator_getters.hpp"
+#include "common/log.hpp"
+#include "common/tlvs.hpp"
+#include "instance/instance.hpp"
+#include "meshcop/secure_transport.hpp"
+
+using namespace ot;
+
+/**
+ * @file
+ *   This file implements the secure Ble agent.
+ */
+
+namespace ot {
+namespace Ble {
+
+RegisterLogModule("BleSecure");
+
+BleSecure::BleSecure(Instance &aInstance)
+    : InstanceLocator(aInstance)
+    , mTls(aInstance, false, false)
+    , mTcatAgent(aInstance)
+    , mTlvMode(false)
+    , mReceivedMessage(nullptr)
+    , mSendMessage(nullptr)
+    , mTransmitTask(aInstance)
+    , mBleState(kStopped)
+    , mMtuSize(kInitialMtuSize)
+{
+}
+
+Error BleSecure::Start(ConnectCallback aConnectHandler, ReceiveCallback aReceiveHandler, bool aTlvMode, void *aContext)
+{
+    Error error = kErrorNone;
+
+    VerifyOrExit(mBleState == kStopped, error = kErrorAlready);
+
+    mConnectCallback.Set(aConnectHandler, aContext);
+    mReceiveCallback.Set(aReceiveHandler, aContext);
+    mTlvMode = aTlvMode;
+    mMtuSize = kInitialMtuSize;
+
+    SuccessOrExit(error = otPlatBleEnable(&GetInstance()));
+    SuccessOrExit(error = otPlatBleGapAdvStart(&GetInstance(), OT_BLE_ADV_INTERVAL_DEFAULT));
+    SuccessOrExit(error = mTls.Open(&BleSecure::HandleTlsReceive, &BleSecure::HandleTlsConnected, this));
+    SuccessOrExit(error = mTls.Bind(HandleTransport, this));
+
+exit:
+    if (error == kErrorNone)
+    {
+        mBleState = kAdvertising;
+    }
+    return error;
+}
+
+Error BleSecure::TcatStart(const MeshCoP::TcatAgent::VendorInfo &aVendorInfo,
+                           MeshCoP::TcatAgent::JoinCallback      aJoinHandler)
+{
+    Error error;
+
+    VerifyOrExit(mBleState != kStopped, error = kErrorInvalidState);
+
+    error = mTcatAgent.Start(aVendorInfo, mReceiveCallback.GetHandler(), aJoinHandler, mReceiveCallback.GetContext());
+
+exit:
+    return error;
+}
+
+void BleSecure::Stop(void)
+{
+    VerifyOrExit(mBleState != kStopped);
+    SuccessOrExit(otPlatBleGapAdvStop(&GetInstance()));
+    SuccessOrExit(otPlatBleDisable(&GetInstance()));
+    mBleState = kStopped;
+    mMtuSize  = kInitialMtuSize;
+
+    if (mTcatAgent.IsEnabled())
+    {
+        mTcatAgent.Stop();
+    }
+
+    mTls.Close();
+
+    mTransmitQueue.DequeueAndFreeAll();
+
+    mConnectCallback.Clear();
+    mReceiveCallback.Clear();
+
+    FreeMessage(mReceivedMessage);
+    mReceivedMessage = nullptr;
+    FreeMessage(mSendMessage);
+    mSendMessage = nullptr;
+
+exit:
+    return;
+}
+
+Error BleSecure::Connect(void)
+{
+    Ip6::SockAddr sockaddr;
+    Error         error;
+
+    VerifyOrExit(mBleState == kConnected, error = kErrorInvalidState);
+
+    error = mTls.Connect(sockaddr);
+
+exit:
+    return error;
+}
+
+void BleSecure::Disconnect(void)
+{
+    if (mTls.IsConnected())
+    {
+        mTls.Disconnect();
+    }
+
+    if (mBleState == kConnected)
+    {
+        mBleState = kAdvertising;
+        IgnoreReturnValue(otPlatBleGapDisconnect(&GetInstance()));
+    }
+
+    mConnectCallback.InvokeIfSet(&GetInstance(), false, false);
+}
+
+void BleSecure::SetPsk(const MeshCoP::JoinerPskd &aPskd)
+{
+    static_assert(static_cast<uint16_t>(MeshCoP::JoinerPskd::kMaxLength) <=
+                      static_cast<uint16_t>(MeshCoP::SecureTransport::kPskMaxLength),
+                  "The maximum length of TLS PSK is smaller than joiner PSKd");
+
+    SuccessOrAssert(mTls.SetPsk(reinterpret_cast<const uint8_t *>(aPskd.GetAsCString()), aPskd.GetLength()));
+}
+
+#if defined(MBEDTLS_BASE64_C) && defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
+Error BleSecure::GetPeerCertificateBase64(unsigned char *aPeerCert, size_t *aCertLength)
+{
+    Error error;
+
+    VerifyOrExit(aCertLength != nullptr, error = kErrorInvalidArgs);
+
+    error = mTls.GetPeerCertificateBase64(aPeerCert, aCertLength, *aCertLength);
+
+exit:
+    return error;
+}
+#endif // defined(MBEDTLS_BASE64_C) && defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
+
+Error BleSecure::SendMessage(ot::Message &aMessage)
+{
+    Error error = kErrorNone;
+
+    VerifyOrExit(IsConnected(), error = kErrorInvalidState);
+    if (mSendMessage == nullptr)
+    {
+        mSendMessage = Get<MessagePool>().Allocate(Message::kTypeBle);
+        VerifyOrExit(mSendMessage != nullptr, error = kErrorNoBufs);
+    }
+    SuccessOrExit(error = mSendMessage->AppendBytesFromMessage(aMessage, 0, aMessage.GetLength()));
+    SuccessOrExit(error = Flush());
+
+exit:
+    aMessage.Free();
+    return error;
+}
+
+Error BleSecure::Send(uint8_t *aBuf, uint16_t aLength)
+{
+    Error error = kErrorNone;
+
+    VerifyOrExit(IsConnected(), error = kErrorInvalidState);
+    if (mSendMessage == nullptr)
+    {
+        mSendMessage = Get<MessagePool>().Allocate(Message::kTypeBle);
+        VerifyOrExit(mSendMessage != nullptr, error = kErrorNoBufs);
+    }
+    SuccessOrExit(error = mSendMessage->AppendBytes(aBuf, aLength));
+
+exit:
+    return error;
+}
+
+Error BleSecure::SendApplicationTlv(uint8_t *aBuf, uint16_t aLength)
+{
+    Error error = kErrorNone;
+    if (aLength > Tlv::kBaseTlvMaxLength)
+    {
+        ot::ExtendedTlv tlv;
+
+        tlv.SetType(ot::MeshCoP::TcatAgent::kTlvSendApplicationData);
+        tlv.SetLength(aLength);
+        SuccessOrExit(error = Send(reinterpret_cast<uint8_t *>(&tlv), sizeof(tlv)));
+    }
+    else
+    {
+        ot::Tlv tlv;
+
+        tlv.SetType(ot::MeshCoP::TcatAgent::kTlvSendApplicationData);
+        tlv.SetLength((uint8_t)aLength);
+        SuccessOrExit(error = Send(reinterpret_cast<uint8_t *>(&tlv), sizeof(tlv)));
+    }
+
+    error = Send(aBuf, aLength);
+exit:
+    return error;
+}
+
+Error BleSecure::Flush(void)
+{
+    Error error = kErrorNone;
+
+    VerifyOrExit(IsConnected(), error = kErrorInvalidState);
+    VerifyOrExit(mSendMessage->GetLength() != 0, error = kErrorNone);
+
+    mTransmitQueue.Enqueue(*mSendMessage);
+    mTransmitTask.Post();
+
+    mSendMessage = nullptr;
+
+exit:
+    return error;
+}
+
+Error BleSecure::HandleBleReceive(uint8_t *aBuf, uint16_t aLength)
+{
+    ot::Message     *message = nullptr;
+    Ip6::MessageInfo messageInfo;
+    Error            error = kErrorNone;
+
+    if ((message = Get<MessagePool>().Allocate(Message::kTypeBle, 0)) == nullptr)
+    {
+        error = kErrorNoBufs;
+        ExitNow();
+    }
+    SuccessOrExit(error = message->AppendBytes(aBuf, aLength));
+
+    // Cannot call Receive(..) directly because Setup(..) and mState are private
+    mTls.HandleReceive(*message, messageInfo);
+
+exit:
+    FreeMessage(message);
+    return error;
+}
+
+void BleSecure::HandleBleConnected(uint16_t aConnectionId)
+{
+    OT_UNUSED_VARIABLE(aConnectionId);
+
+    mBleState = kConnected;
+
+    IgnoreReturnValue(otPlatBleGattMtuGet(&GetInstance(), &mMtuSize));
+
+    mConnectCallback.InvokeIfSet(&GetInstance(), IsConnected(), true);
+}
+
+void BleSecure::HandleBleDisconnected(uint16_t aConnectionId)
+{
+    OT_UNUSED_VARIABLE(aConnectionId);
+
+    mBleState = kAdvertising;
+    mMtuSize  = kInitialMtuSize;
+
+    Disconnect(); // Stop TLS connection
+}
+
+Error BleSecure::HandleBleMtuUpdate(uint16_t aMtu)
+{
+    Error error = kErrorNone;
+
+    if (aMtu <= OT_BLE_ATT_MTU_MAX)
+    {
+        mMtuSize = aMtu;
+    }
+    else
+    {
+        mMtuSize = OT_BLE_ATT_MTU_MAX;
+        error    = kErrorInvalidArgs;
+    }
+
+    return error;
+}
+
+void BleSecure::HandleTlsConnected(void *aContext, bool aConnected)
+{
+    return static_cast<BleSecure *>(aContext)->HandleTlsConnected(aConnected);
+}
+
+void BleSecure::HandleTlsConnected(bool aConnected)
+{
+    if (aConnected)
+    {
+        if (mReceivedMessage == nullptr)
+        {
+            mReceivedMessage = Get<MessagePool>().Allocate(Message::kTypeBle);
+        }
+
+        if (mTcatAgent.IsEnabled())
+        {
+            IgnoreReturnValue(mTcatAgent.Connected(mTls));
+        }
+    }
+    else
+    {
+        FreeMessage(mReceivedMessage);
+        mReceivedMessage = nullptr;
+
+        if (mTcatAgent.IsEnabled())
+        {
+            mTcatAgent.Disconnected();
+        }
+    }
+
+    mConnectCallback.InvokeIfSet(&GetInstance(), aConnected, true);
+}
+
+void BleSecure::HandleTlsReceive(void *aContext, uint8_t *aBuf, uint16_t aLength)
+{
+    return static_cast<BleSecure *>(aContext)->HandleTlsReceive(aBuf, aLength);
+}
+
+void BleSecure::HandleTlsReceive(uint8_t *aBuf, uint16_t aLength)
+{
+    VerifyOrExit(mReceivedMessage != nullptr);
+
+    if (!mTlvMode)
+    {
+        SuccessOrExit(mReceivedMessage->AppendBytes(aBuf, aLength));
+        mReceiveCallback.InvokeIfSet(&GetInstance(), mReceivedMessage, 0, OT_TCAT_APPLICATION_PROTOCOL_NONE, "");
+        IgnoreReturnValue(mReceivedMessage->SetLength(0));
+    }
+    else
+    {
+        ot::Tlv  tlv;
+        uint32_t requiredBytes = sizeof(Tlv);
+        uint32_t offset;
+
+        while (aLength > 0)
+        {
+            if (mReceivedMessage->GetLength() < requiredBytes)
+            {
+                uint32_t missingBytes = requiredBytes - mReceivedMessage->GetLength();
+
+                if (missingBytes > aLength)
+                {
+                    SuccessOrExit(mReceivedMessage->AppendBytes(aBuf, aLength));
+                    break;
+                }
+                else
+                {
+                    SuccessOrExit(mReceivedMessage->AppendBytes(aBuf, (uint16_t)missingBytes));
+                    aLength -= missingBytes;
+                    aBuf += missingBytes;
+                }
+            }
+
+            IgnoreReturnValue(mReceivedMessage->Read(0, tlv));
+
+            if (tlv.IsExtended())
+            {
+                ot::ExtendedTlv extTlv;
+                requiredBytes = sizeof(extTlv);
+
+                if (mReceivedMessage->GetLength() < requiredBytes)
+                {
+                    continue;
+                }
+
+                IgnoreReturnValue(mReceivedMessage->Read(0, extTlv));
+                requiredBytes = extTlv.GetSize();
+                offset        = sizeof(extTlv);
+            }
+            else
+            {
+                requiredBytes = tlv.GetSize();
+                offset        = sizeof(tlv);
+            }
+
+            if (mReceivedMessage->GetLength() < requiredBytes)
+            {
+                continue;
+            }
+
+            // TLV fully loaded
+
+            if (mTcatAgent.IsEnabled())
+            {
+                ot::Message *message;
+                Error        error = kErrorNone;
+
+                message = Get<MessagePool>().Allocate(Message::kTypeBle);
+                VerifyOrExit(message != nullptr, error = kErrorNoBufs);
+
+                error = mTcatAgent.HandleSingleTlv(*mReceivedMessage, *message);
+                if (message->GetLength() != 0)
+                {
+                    IgnoreReturnValue(SendMessage(*message));
+                }
+
+                if (error == kErrorAbort)
+                {
+                    Disconnect();
+                    Stop();
+                    ExitNow();
+                }
+            }
+            else
+            {
+                mReceivedMessage->SetOffset((uint16_t)offset);
+                mReceiveCallback.InvokeIfSet(&GetInstance(), mReceivedMessage, (int32_t)offset,
+                                             OT_TCAT_APPLICATION_PROTOCOL_NONE, "");
+            }
+
+            SuccessOrExit(mReceivedMessage->SetLength(0)); // also sets the offset to 0
+            requiredBytes = sizeof(Tlv);
+        }
+    }
+
+exit:
+    return;
+}
+
+void BleSecure::HandleTransmit(void)
+{
+    Error        error   = kErrorNone;
+    ot::Message *message = mTransmitQueue.GetHead();
+
+    VerifyOrExit(message != nullptr);
+    mTransmitQueue.Dequeue(*message);
+
+    if (mTransmitQueue.GetHead() != nullptr)
+    {
+        mTransmitTask.Post();
+    }
+
+    SuccessOrExit(error = mTls.Send(*message, message->GetLength()));
+
+exit:
+    if (error != kErrorNone)
+    {
+        LogNote("Transmit: %s", ErrorToString(error));
+        message->Free();
+    }
+    else
+    {
+        LogDebg("Transmit: %s", ErrorToString(error));
+    }
+}
+
+Error BleSecure::HandleTransport(void *aContext, ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
+{
+    OT_UNUSED_VARIABLE(aMessageInfo);
+    return static_cast<BleSecure *>(aContext)->HandleTransport(aMessage);
+}
+
+Error BleSecure::HandleTransport(ot::Message &aMessage)
+{
+    otBleRadioPacket packet;
+    uint16_t         len    = aMessage.GetLength();
+    uint16_t         offset = 0;
+    Error            error  = kErrorNone;
+
+    while (len > 0)
+    {
+        if (len <= mMtuSize - kGattOverhead)
+        {
+            packet.mLength = len;
+        }
+        else
+        {
+            packet.mLength = mMtuSize - kGattOverhead;
+        }
+
+        if (packet.mLength > kPacketBufferSize)
+        {
+            packet.mLength = kPacketBufferSize;
+        }
+
+        IgnoreReturnValue(aMessage.Read(offset, mPacketBuffer, packet.mLength));
+        packet.mValue = mPacketBuffer;
+        packet.mPower = OT_BLE_DEFAULT_POWER;
+
+        SuccessOrExit(error = otPlatBleGattServerIndicate(&GetInstance(), kTxBleHandle, &packet));
+
+        len -= packet.mLength;
+        offset += packet.mLength;
+    }
+
+    aMessage.Free();
+exit:
+    return error;
+}
+
+} // namespace Ble
+} // namespace ot
+
+void otPlatBleGattServerOnWriteRequest(otInstance *aInstance, uint16_t aHandle, const otBleRadioPacket *aPacket)
+{
+    OT_UNUSED_VARIABLE(aHandle); // Only a single handle is expected for RX
+
+    VerifyOrExit(aPacket != nullptr);
+    IgnoreReturnValue(AsCoreType(aInstance).Get<Ble::BleSecure>().HandleBleReceive(aPacket->mValue, aPacket->mLength));
+exit:
+    return;
+}
+
+void otPlatBleGapOnConnected(otInstance *aInstance, uint16_t aConnectionId)
+{
+    AsCoreType(aInstance).Get<Ble::BleSecure>().HandleBleConnected(aConnectionId);
+}
+
+void otPlatBleGapOnDisconnected(otInstance *aInstance, uint16_t aConnectionId)
+{
+    AsCoreType(aInstance).Get<Ble::BleSecure>().HandleBleDisconnected(aConnectionId);
+}
+
+void otPlatBleGattOnMtuUpdate(otInstance *aInstance, uint16_t aMtu)
+{
+    IgnoreReturnValue(AsCoreType(aInstance).Get<Ble::BleSecure>().HandleBleMtuUpdate(aMtu));
+}
+
+#endif // OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
diff --git a/src/core/radio/ble_secure.hpp b/src/core/radio/ble_secure.hpp
new file mode 100644
index 0000000..63a86aa
--- /dev/null
+++ b/src/core/radio/ble_secure.hpp
@@ -0,0 +1,489 @@
+/*
+ *  Copyright (c) 2023, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef BLE_SECURE_HPP_
+#define BLE_SECURE_HPP_
+
+#include "openthread-core-config.h"
+
+#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
+
+#include <openthread/ble_secure.h>
+
+#include "meshcop/meshcop.hpp"
+#include "meshcop/secure_transport.hpp"
+#include "meshcop/tcat_agent.hpp"
+
+/**
+ * @file
+ *   Includes definitions for the secure BLE agent.
+ */
+
+namespace ot {
+
+namespace Ble {
+
+class BleSecure : public InstanceLocator, private NonCopyable
+{
+public:
+    /**
+     * Pointer to call when the secure BLE connection state changes.
+     *
+     *  Please see otHandleBleSecureConnect for details.
+     *
+     */
+    typedef otHandleBleSecureConnect ConnectCallback;
+
+    /**
+     * Pointer to call when data was received over the TLS connection.
+     * If line mode is activated the function is called only after EOL has been received.
+     *
+     *  Please see otHandleBleSecureReceive for details.
+     *
+     */
+    typedef otHandleBleSecureReceive ReceiveCallback;
+
+    /**
+     * Represents a TCAT command class.
+     *
+     */
+    typedef MeshCoP::TcatAgent::CommandClass CommandClass;
+
+    /**
+     * Constructor initializes the object.
+     *
+     * @param[in]  aInstance    A reference to the OpenThread instance.
+     *
+     */
+    explicit BleSecure(Instance &aInstance);
+
+    /**
+     * Starts the secure BLE agent.
+     *
+     * @param[in]  aConnectHandler  A pointer to a function that will be called when the connection
+     *                              state changes.
+     * @param[in]  aReceiveHandler  A pointer to a function that will be called once data has been received
+     *                              over the TLS connection.
+     * @param[in]  aTlvMode         A boolean value indicating if line mode shall be activated.
+     * @param[in]  aContext         A pointer to arbitrary context information. May be NULL if not used.
+     *
+     * @retval kErrorNone       Successfully started the BLE agent.
+     * @retval kErrorAlready    Already started.
+     *
+     */
+    Error Start(ConnectCallback aConnectHandler, ReceiveCallback aReceiveHandler, bool aTlvMode, void *aContext);
+
+    /**
+     * Enables the TCAT protocol over BLE Secure.
+     *
+     * @param[in]  aVendorInfo       A reference to the Vendor Information (must remain valid after the method call)
+     * @param[in]  aHandler          Callback to a function that is called when the join operation completes.
+     *
+     * @retval kErrorNone           Successfully started the BLE Secure Joiner role.
+     * @retval kErrorInvalidArgs    The aVendorInfo is invalid.
+     * @retval kErrorInvaidState    The BLE function has not been started or line mode is not selected.
+     *
+     */
+    Error TcatStart(const MeshCoP::TcatAgent::VendorInfo &aVendorInfo, MeshCoP::TcatAgent::JoinCallback aHandler);
+
+    /**
+     * Stops the secure BLE agent.
+     *
+     */
+    void Stop(void);
+
+    /**
+     * Initializes TLS session with a peer using an already open BLE connection.
+     *
+     * @retval kErrorNone  Successfully started TLS connection.
+     *
+     */
+    Error Connect(void);
+
+    /**
+     * Stops the BLE and TLS connection.
+     *
+     */
+    void Disconnect(void);
+
+    /**
+     * Indicates whether or not the TLS session is active (connected or conneting).
+     *
+     * @retval TRUE  If TLS session is active.
+     * @retval FALSE If TLS session is not active.
+     *
+     */
+    bool IsConnectionActive(void) const { return mTls.IsConnectionActive(); }
+
+    /**
+     * Indicates whether or not the TLS session is connected.
+     *
+     * @retval TRUE   The TLS session is connected.
+     * @retval FALSE  The TLS session is not connected.
+     *
+     */
+    bool IsConnected(void) const { return mTls.IsConnected(); }
+
+    /**
+     * Indicates whether or not the TCAT agent is enabled.
+     *
+     * @retval TRUE   The TCAT agent is enabled.
+     * @retval FALSE  The TCAT agent is not enabled.
+     *
+     */
+    bool IsTcatEnabled(void) const { return mTcatAgent.IsEnabled(); }
+
+    /**
+     * Indicates whether or not a TCAT command class is authorized.
+     *
+     * @param[in]  aInstance  A pointer to an OpenThread instance.
+     * @param[in]  aCommandClass  A command class to check.
+     *
+     * @retval TRUE   The command class is authorized.
+     * @retval FALSE  The command class is not authorized.
+     *
+     */
+    bool IsCommandClassAuthorized(CommandClass aCommandClass) const
+    {
+        return mTcatAgent.IsCommandClassAuthorized(aCommandClass);
+    }
+
+    /**
+     * Sets the PSK.
+     *
+     * @param[in]  aPsk        A pointer to the PSK.
+     * @param[in]  aPskLength  The PSK length.
+     *
+     * @retval kErrorNone         Successfully set the PSK.
+     * @retval kErrorInvalidArgs  The PSK is invalid.
+     *
+     */
+    Error SetPsk(const uint8_t *aPsk, uint8_t aPskLength) { return mTls.SetPsk(aPsk, aPskLength); }
+
+    /**
+     * Sets the PSK.
+     *
+     * @param[in]  aPskd  A Joiner PSKd.
+     *
+     */
+    void SetPsk(const MeshCoP::JoinerPskd &aPskd);
+
+#ifdef MBEDTLS_KEY_EXCHANGE_PSK_ENABLED
+    /**
+     * Sets the Pre-Shared Key (PSK) for TLS sessions identified by a PSK.
+     *
+     * TLS mode "TLS with AES 128 CCM 8" for secure BLE.
+     *
+     * @param[in]  aPsk          A pointer to the PSK.
+     * @param[in]  aPskLength    The PSK char length.
+     * @param[in]  aPskIdentity  The Identity Name for the PSK.
+     * @param[in]  aPskIdLength  The PSK Identity Length.
+     *
+     */
+    void SetPreSharedKey(const uint8_t *aPsk, uint16_t aPskLength, const uint8_t *aPskIdentity, uint16_t aPskIdLength)
+    {
+        mTls.SetPreSharedKey(aPsk, aPskLength, aPskIdentity, aPskIdLength);
+    }
+#endif // MBEDTLS_KEY_EXCHANGE_PSK_ENABLED
+
+#ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
+    /**
+     * Sets a X509 certificate with corresponding private key for TLS session.
+     *
+     * TLS mode "ECDHE ECDSA with AES 128 CCM 8" for secure BLE.
+     *
+     * @param[in]  aX509Cert          A pointer to the PEM formatted X509 PEM certificate.
+     * @param[in]  aX509Length        The length of certificate.
+     * @param[in]  aPrivateKey        A pointer to the PEM formatted private key.
+     * @param[in]  aPrivateKeyLength  The length of the private key.
+     *
+     */
+    void SetCertificate(const uint8_t *aX509Cert,
+                        uint32_t       aX509Length,
+                        const uint8_t *aPrivateKey,
+                        uint32_t       aPrivateKeyLength)
+    {
+        mTls.SetCertificate(aX509Cert, aX509Length, aPrivateKey, aPrivateKeyLength);
+    }
+
+    /**
+     * Sets the trusted top level CAs. It is needed for validate the certificate of the peer.
+     *
+     * TLS mode "ECDHE ECDSA with AES 128 CCM 8" for secure BLE.
+     *
+     * @param[in]  aX509CaCertificateChain  A pointer to the PEM formatted X509 CA chain.
+     * @param[in]  aX509CaCertChainLength   The length of chain.
+     *
+     */
+    void SetCaCertificateChain(const uint8_t *aX509CaCertificateChain, uint32_t aX509CaCertChainLength)
+    {
+        mTls.SetCaCertificateChain(aX509CaCertificateChain, aX509CaCertChainLength);
+    }
+#endif // MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
+
+#if defined(MBEDTLS_BASE64_C) && defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
+    /**
+     * Returns the peer x509 certificate base64 encoded.
+     *
+     * TLS mode "ECDHE ECDSA with AES 128 CCM 8" for secure BLE.
+     *
+     * @param[out]  aPeerCert        A pointer to the base64 encoded certificate buffer.
+     * @param[out]  aCertLength      On input, the size the max size of @p aPeerCert.
+     *                               On output, the length of the base64 encoded peer certificate.
+     *
+     * @retval kErrorNone           Successfully get the peer certificate.
+     * @retval kErrorInvalidArgs    @p aInstance or @p aCertLength is invalid.
+     * @retval kErrorInvalidState   Not connected yet.
+     * @retval kErrorNoBufs         Can't allocate memory for certificate.
+     *
+     */
+    Error GetPeerCertificateBase64(unsigned char *aPeerCert, size_t *aCertLength);
+#endif // defined(MBEDTLS_BASE64_C) && defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
+
+#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
+    /**
+     * Returns an attribute value identified by its OID from the subject
+     * of the peer x509 certificate. The peer OID is provided in binary format.
+     * The attribute length is set if the attribute was successfully read or zero
+     * if unsuccessful. The ASN.1 type as is set as defineded in the ITU-T X.690 standard
+     * if the attribute was successfully read.
+     *
+     * @param[in]      aOid                  A pointer to the OID to be found.
+     * @param[in]      aOidLength            The length of the OID.
+     * @param[out]     aAttributeBuffer      A pointer to the attribute buffer.
+     * @param[in,out]  aAttributeLength      On input, the size the max size of @p aAttributeBuffer.
+     *                                           On output, the length of the attribute written to the buffer.
+     * @param[out]  aAsn1Type                A pointer to the ASN.1 type of the attribute written to the buffer.
+     *
+     * @retval kErrorInvalidState   Not connected yet.
+     * @retval kErrorNone           Successfully read attribute.
+     * @retval kErrorNoBufs         Insufficient memory for storing the attribute value.
+     *
+     */
+    Error GetPeerSubjectAttributeByOid(const char *aOid,
+                                       size_t      aOidLength,
+                                       uint8_t    *aAttributeBuffer,
+                                       size_t     *aAttributeLength,
+                                       int        *aAsn1Type)
+    {
+        return mTls.GetPeerSubjectAttributeByOid(aOid, aOidLength, aAttributeBuffer, aAttributeLength, aAsn1Type);
+    }
+
+    /**
+     * Returns an attribute value for the OID 1.3.6.1.4.1.44970.x from the v3 extensions of
+     * the peer x509 certificate, where the last digit x is set to aThreadOidDescriptor.
+     * The attribute length is set if the attribute was successfully read or zero if unsuccessful.
+     * Requires a connection to be active.
+     *
+     * @param[in]      aThreadOidDescriptor  The last digit of the Thread attribute OID.
+     * @param[out]     aAttributeBuffer      A pointer to the attribute buffer.
+     * @param[in,out]  aAttributeLength      On input, the size the max size of @p aAttributeBuffer.
+     *                                       On output, the length of the attribute written to the buffer.
+     *
+     * @retval kErrorNone             Successfully read attribute.
+     * @retval kErrorNotFound         The requested attribute was not found.
+     * @retval kErrorNoBufs           Insufficient memory for storing the attribute value.
+     * @retval kErrorInvalidState     Not connected yet.
+     * @retval kErrorNotImplemented   The value of aThreadOidDescriptor is >127.
+     * @retval kErrorParse            The certificate extensions could not be parsed.
+     *
+     */
+    Error GetThreadAttributeFromPeerCertificate(int      aThreadOidDescriptor,
+                                                uint8_t *aAttributeBuffer,
+                                                size_t  *aAttributeLength)
+    {
+        return mTls.GetThreadAttributeFromPeerCertificate(aThreadOidDescriptor, aAttributeBuffer, aAttributeLength);
+    }
+#endif // defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
+
+    /**
+     * Returns an attribute value for the OID 1.3.6.1.4.1.44970.x from the v3 extensions of
+     * the own x509 certificate, where the last digit x is set to aThreadOidDescriptor.
+     * The attribute length is set if the attribute was successfully read or zero if unsuccessful.
+     * Requires a connection to be active.
+     *
+     * @param[in]      aThreadOidDescriptor  The last digit of the Thread attribute OID.
+     * @param[out]     aAttributeBuffer      A pointer to the attribute buffer.
+     * @param[in,out]  aAttributeLength      On input, the size the max size of @p aAttributeBuffer.
+     *                                       On output, the length of the attribute written to the buffer.
+     *
+     * @retval kErrorNone             Successfully read attribute.
+     * @retval kErrorNotFound         The requested attribute was not found.
+     * @retval kErrorNoBufs           Insufficient memory for storing the attribute value.
+     * @retval kErrorInvalidState     Not connected yet.
+     * @retval kErrorNotImplemented   The value of aThreadOidDescriptor is >127.
+     * @retval kErrorParse            The certificate extensions could not be parsed.
+     *
+     */
+    Error GetThreadAttributeFromOwnCertificate(int      aThreadOidDescriptor,
+                                               uint8_t *aAttributeBuffer,
+                                               size_t  *aAttributeLength)
+    {
+        return mTls.GetThreadAttributeFromOwnCertificate(aThreadOidDescriptor, aAttributeBuffer, aAttributeLength);
+    }
+
+    /**
+     * Sets the authentication mode for the BLE secure connection. It disables or enables the verification
+     * of peer certificate.
+     *
+     * @param[in]  aVerifyPeerCertificate  true, if the peer certificate should be verified
+     *
+     */
+    void SetSslAuthMode(bool aVerifyPeerCertificate) { mTls.SetSslAuthMode(aVerifyPeerCertificate); }
+
+    /**
+     * Sends a secure BLE message.
+     *
+     * @param[in]  aMessage        A pointer to the message to send.
+     *
+     * If the return value is kErrorNone, OpenThread takes ownership of @p aMessage, and the caller should no longer
+     * reference @p aMessage. If the return value is not kErrorNone, the caller retains ownership of @p aMessage,
+     * including freeing @p aMessage if the message buffer is no longer needed.
+     *
+     * @retval kErrorNone          Successfully sent message.
+     * @retval kErrorNoBufs        Failed to allocate buffer memory.
+     * @retval kErrorInvalidState  TLS connection was not initialized.
+     *
+     */
+    Error SendMessage(Message &aMessage);
+
+    /**
+     * Sends a secure BLE data packet.
+     *
+     * @param[in]  aBuf            A pointer to the data to send as the Value of the TCAT Send Application Data TLV.
+     * @param[in]  aLength         A number indicating the length of the data buffer.
+     *
+     * @retval kErrorNone          Successfully sent data.
+     * @retval kErrorNoBufs        Failed to allocate buffer memory.
+     * @retval kErrorInvalidState  TLS connection was not initialized.
+     *
+     */
+    Error Send(uint8_t *aBuf, uint16_t aLength);
+
+    /**
+     * Sends a secure BLE data packet containing a TCAT Send Application Data TLV.
+     *
+     * @param[in]  aBuf            A pointer to the data to send as the Value of the TCAT Send Application Data TLV.
+     * @param[in]  aLength         A number indicating the length of the data buffer.
+     *
+     * @retval kErrorNone          Successfully sent data.
+     * @retval kErrorNoBufs        Failed to allocate buffer memory.
+     * @retval kErrorInvalidState  TLS connection was not initialized.
+     *
+     */
+    Error SendApplicationTlv(uint8_t *aBuf, uint16_t aLength);
+
+    /**
+     * Sends all remaining bytes in the send buffer.
+     *
+     * @retval kErrorNone          Successfully enqueued data into the output interface.
+     * @retval kErrorNoBufs        Failed to allocate buffer memory.
+     * @retval kErrorInvalidState  TLS connection was not initialized.
+     *
+     */
+    Error Flush(void);
+
+    /**
+     * Used to pass data received over a BLE link to the secure BLE server.
+     *
+     * @param[in]  aBuf            A pointer to the data received.
+     * @param[in]  aLength         A number indicating the length of the data buffer.
+     *
+     */
+    Error HandleBleReceive(uint8_t *aBuf, uint16_t aLength);
+
+    /**
+     * Used to notify the secure BLE server that a BLE Device has been connected.
+     *
+     * @param[in]  aConnectionId    The identifier of the open connection.
+     *
+     */
+    void HandleBleConnected(uint16_t aConnectionId);
+
+    /**
+     * Used to notify the secure BLE server that the BLE Device has been disconnected.
+     *
+     * @param[in]  aConnectionId    The identifier of the open connection.
+     *
+     */
+    void HandleBleDisconnected(uint16_t aConnectionId);
+
+    /**
+     * Used to notify the secure BLE server that the BLE Device has updated ATT_MTU size.
+     *
+     * @param[in]  aMtu             The updated ATT_MTU value.
+     *
+     */
+    Error HandleBleMtuUpdate(uint16_t aMtu);
+
+private:
+    enum BleState : uint8_t
+    {
+        kStopped     = 0, // Ble secure not started.
+        kAdvertising = 1, // Ble secure not advertising.
+        kConnected   = 2, // Ble secure not connected.
+    };
+
+    static constexpr uint8_t  kInitialMtuSize   = 23; // ATT_MTU
+    static constexpr uint8_t  kGattOverhead     = 3;  // BLE GATT payload fits MTU size - 3 bytes
+    static constexpr uint8_t  kPacketBufferSize = OT_BLE_ATT_MTU_MAX - kGattOverhead;
+    static constexpr uint16_t kTxBleHandle      = 0; // Characteristics Handle for TX (not used)
+
+    static void HandleTlsConnected(void *aContext, bool aConnected);
+    void        HandleTlsConnected(bool aConnected);
+
+    static void HandleTlsReceive(void *aContext, uint8_t *aBuf, uint16_t aLength);
+    void        HandleTlsReceive(uint8_t *aBuf, uint16_t aLength);
+
+    void HandleTransmit(void);
+
+    static Error HandleTransport(void *aContext, ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
+    Error        HandleTransport(ot::Message &aMessage);
+
+    using TxTask = TaskletIn<BleSecure, &BleSecure::HandleTransmit>;
+
+    MeshCoP::SecureTransport  mTls;
+    MeshCoP::TcatAgent        mTcatAgent;
+    Callback<ConnectCallback> mConnectCallback;
+    Callback<ReceiveCallback> mReceiveCallback;
+    bool                      mTlvMode;
+    ot::Message              *mReceivedMessage;
+    ot::Message              *mSendMessage;
+    ot::MessageQueue          mTransmitQueue;
+    TxTask                    mTransmitTask;
+    uint8_t                   mPacketBuffer[kPacketBufferSize];
+    BleState                  mBleState;
+    uint16_t                  mMtuSize;
+};
+
+} // namespace Ble
+} // namespace ot
+
+#endif // OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
+
+#endif // BLE_SECURE_HPP_
diff --git a/src/core/radio/radio.cpp b/src/core/radio/radio.cpp
index bdc46be..2356e14 100644
--- a/src/core/radio/radio.cpp
+++ b/src/core/radio/radio.cpp
@@ -36,12 +36,24 @@
 
 namespace ot {
 
+const uint8_t Radio::kSupportedChannelPages[kNumChannelPages] = {
+#if OPENTHREAD_CONFIG_RADIO_2P4GHZ_OQPSK_SUPPORT
+    kChannelPage0,
+#endif
+#if OPENTHREAD_CONFIG_RADIO_915MHZ_OQPSK_SUPPORT
+    kChannelPage2,
+#endif
+#if OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_SUPPORT
+    OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_PAGE,
+#endif
+};
+
 #if OPENTHREAD_RADIO
 void Radio::Init(void)
 {
 #if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
-    SuccessOrAssert(EnableCsl(0, Mac::kShortAddrInvalid, nullptr));
+    SuccessOrAssert(ResetCsl());
 #endif
 
     EnableSrcMatch(false);
diff --git a/src/core/radio/radio.hpp b/src/core/radio/radio.hpp
index 3321abe..c8bb938 100644
--- a/src/core/radio/radio.hpp
+++ b/src/core/radio/radio.hpp
@@ -37,11 +37,12 @@
 #include "openthread-core-config.h"
 
 #include <openthread/radio_stats.h>
+#include <openthread/platform/crypto.h>
 #include <openthread/platform/radio.h>
 
-#include <openthread/platform/crypto.h>
 #include "common/locator.hpp"
 #include "common/non_copyable.hpp"
+#include "common/numeric_limits.hpp"
 #include "common/time.hpp"
 #include "mac/mac_frame.hpp"
 
@@ -136,33 +137,33 @@
     static constexpr uint32_t kSymbolTime      = OT_RADIO_SYMBOL_TIME;
     static constexpr uint8_t  kSymbolsPerOctet = OT_RADIO_SYMBOLS_PER_OCTET;
     static constexpr uint32_t kPhyUsPerByte    = kSymbolsPerOctet * kSymbolTime;
+    static constexpr uint8_t  kChannelPage0    = OT_RADIO_CHANNEL_PAGE_0;
+    static constexpr uint8_t  kChannelPage2    = OT_RADIO_CHANNEL_PAGE_2;
 #if (OPENTHREAD_CONFIG_RADIO_2P4GHZ_OQPSK_SUPPORT && OPENTHREAD_CONFIG_RADIO_915MHZ_OQPSK_SUPPORT)
     static constexpr uint16_t kNumChannelPages = 2;
     static constexpr uint32_t kSupportedChannels =
         OT_RADIO_915MHZ_OQPSK_CHANNEL_MASK | OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MASK;
-    static constexpr uint8_t  kChannelMin            = OT_RADIO_915MHZ_OQPSK_CHANNEL_MIN;
-    static constexpr uint8_t  kChannelMax            = OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MAX;
-    static constexpr uint32_t kSupportedChannelPages = OT_RADIO_CHANNEL_PAGE_0_MASK | OT_RADIO_CHANNEL_PAGE_2_MASK;
+    static constexpr uint8_t kChannelMin = OT_RADIO_915MHZ_OQPSK_CHANNEL_MIN;
+    static constexpr uint8_t kChannelMax = OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MAX;
 #elif OPENTHREAD_CONFIG_RADIO_915MHZ_OQPSK_SUPPORT
-    static constexpr uint16_t kNumChannelPages       = 1;
-    static constexpr uint32_t kSupportedChannels     = OT_RADIO_915MHZ_OQPSK_CHANNEL_MASK;
-    static constexpr uint8_t  kChannelMin            = OT_RADIO_915MHZ_OQPSK_CHANNEL_MIN;
-    static constexpr uint8_t  kChannelMax            = OT_RADIO_915MHZ_OQPSK_CHANNEL_MAX;
-    static constexpr uint32_t kSupportedChannelPages = OT_RADIO_CHANNEL_PAGE_2_MASK;
+    static constexpr uint16_t kNumChannelPages   = 1;
+    static constexpr uint32_t kSupportedChannels = OT_RADIO_915MHZ_OQPSK_CHANNEL_MASK;
+    static constexpr uint8_t  kChannelMin        = OT_RADIO_915MHZ_OQPSK_CHANNEL_MIN;
+    static constexpr uint8_t  kChannelMax        = OT_RADIO_915MHZ_OQPSK_CHANNEL_MAX;
 #elif OPENTHREAD_CONFIG_RADIO_2P4GHZ_OQPSK_SUPPORT
-    static constexpr uint16_t kNumChannelPages       = 1;
-    static constexpr uint32_t kSupportedChannels     = OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MASK;
-    static constexpr uint8_t  kChannelMin            = OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MIN;
-    static constexpr uint8_t  kChannelMax            = OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MAX;
-    static constexpr uint32_t kSupportedChannelPages = OT_RADIO_CHANNEL_PAGE_0_MASK;
+    static constexpr uint16_t kNumChannelPages   = 1;
+    static constexpr uint32_t kSupportedChannels = OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MASK;
+    static constexpr uint8_t  kChannelMin        = OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MIN;
+    static constexpr uint8_t  kChannelMax        = OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MAX;
 #elif OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_SUPPORT
-    static constexpr uint16_t kNumChannelPages       = 1;
-    static constexpr uint32_t kSupportedChannels     = OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_MASK;
-    static constexpr uint8_t  kChannelMin            = OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_MIN;
-    static constexpr uint8_t  kChannelMax            = OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_MAX;
-    static constexpr uint32_t kSupportedChannelPages = (1 << OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_PAGE);
+    static constexpr uint16_t kNumChannelPages   = 1;
+    static constexpr uint32_t kSupportedChannels = OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_MASK;
+    static constexpr uint8_t  kChannelMin        = OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_MIN;
+    static constexpr uint8_t  kChannelMax        = OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_MAX;
 #endif
 
+    static const uint8_t kSupportedChannelPages[kNumChannelPages];
+
     static constexpr int8_t kInvalidRssi = OT_RADIO_RSSI_INVALID; ///< Invalid RSSI value.
 
     static constexpr int8_t kDefaultReceiveSensitivity = -110; ///< Default receive sensitivity (in dBm).
@@ -523,7 +524,8 @@
      */
     Error ReceiveAt(uint8_t aChannel, uint32_t aStart, uint32_t aDuration);
 
-    /** Enables CSL sampling in radio.
+    /**
+     * Enables CSL sampling in radio.
      *
      * @param[in]  aCslPeriod    CSL period, 0 for disabling CSL.
      * @param[in]  aShortAddr    The short source address of CSL receiver's peer.
@@ -537,6 +539,16 @@
      *
      */
     Error EnableCsl(uint32_t aCslPeriod, otShortAddress aShortAddr, const otExtAddress *aExtAddr);
+
+    /**
+     * Resets CSL receiver in radio.
+     *
+     * @retval  kErrorNotImplemented Radio driver doesn't support CSL.
+     * @retval  kErrorFailed         Other platform specific errors.
+     * @retval  kErrorNone           Successfully disabled CSL.
+     *
+     */
+    Error ResetCsl(void);
 #endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
 
 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
@@ -771,6 +783,63 @@
      */
     Error GetRegion(uint16_t &aRegionCode) const { return otPlatRadioGetRegion(GetInstancePtr(), &aRegionCode); }
 
+    /**
+     * Indicates whether a given channel page is supported based on the current configurations.
+     *
+     * @param[in] aChannelPage The channel page to check.
+     *
+     * @retval TRUE    The @p aChannelPage is supported by radio.
+     * @retval FALASE  The @p aChannelPage is not supported by radio.
+     *
+     */
+    static constexpr bool SupportsChannelPage(uint8_t aChannelPage)
+    {
+#if OPENTHREAD_CONFIG_RADIO_2P4GHZ_OQPSK_SUPPORT && OPENTHREAD_CONFIG_RADIO_915MHZ_OQPSK_SUPPORT
+        return (aChannelPage == kChannelPage0) || (aChannelPage == kChannelPage2);
+#elif OPENTHREAD_CONFIG_RADIO_2P4GHZ_OQPSK_SUPPORT
+        return (aChannelPage == kChannelPage0);
+#elif OPENTHREAD_CONFIG_RADIO_915MHZ_OQPSK_SUPPORT
+        return (aChannelPage == kChannelPage2);
+#elif OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_SUPPORT
+        return (aChannelPage == OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_PAGE);
+#endif
+    }
+
+    /**
+     * Returns the channel mask for a given channel page if supported by the radio.
+     *
+     * @param[in] aChannelPage   The channel page.
+     *
+     * @returns The channel mask for @p aChannelPage if page is supported by the radio, otherwise zero.
+     *
+     */
+    static uint32_t ChannelMaskForPage(uint8_t aChannelPage)
+    {
+        uint32_t mask = 0;
+
+#if OPENTHREAD_CONFIG_RADIO_2P4GHZ_OQPSK_SUPPORT
+        if (aChannelPage == kChannelPage0)
+        {
+            mask = OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MASK;
+        }
+#endif
+
+#if OPENTHREAD_CONFIG_RADIO_915MHZ_OQPSK_SUPPORT
+        if (aChannelPage == kChannelPage1)
+        {
+            mask = OT_RADIO_915MHZ_OQPSK_CHANNEL_MASK;
+        }
+#endif
+
+#if OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_SUPPORT
+        if (aChannelPage == OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_PAGE)
+        {
+            mask = OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_MASK;
+        }
+#endif
+        return mask;
+    }
+
 private:
     otInstance *GetInstancePtr(void) const { return reinterpret_cast<otInstance *>(&InstanceLocator::GetInstance()); }
 
@@ -901,6 +970,8 @@
 {
     return otPlatRadioEnableCsl(GetInstancePtr(), aCslPeriod, aShortAddr, aExtAddr);
 }
+
+inline Error Radio::ResetCsl(void) { return otPlatRadioResetCsl(GetInstancePtr()); }
 #endif
 
 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
@@ -1007,12 +1078,14 @@
 {
     return kErrorNotImplemented;
 }
+
+inline Error Radio::ResetCsl(void) { return kErrorNotImplemented; }
 #endif
 
 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
-inline uint8_t Radio::GetCslAccuracy(void) { return UINT8_MAX; }
+inline uint8_t Radio::GetCslAccuracy(void) { return NumericLimits<uint8_t>::kMax; }
 
-inline uint8_t Radio::GetCslUncertainty(void) { return UINT8_MAX; }
+inline uint8_t Radio::GetCslUncertainty(void) { return NumericLimits<uint8_t>::kMax; }
 #endif
 
 inline Mac::TxFrame &Radio::GetTransmitBuffer(void)
diff --git a/src/core/radio/radio_platform.cpp b/src/core/radio/radio_platform.cpp
index b71a0fb..0f60012 100644
--- a/src/core/radio/radio_platform.cpp
+++ b/src/core/radio/radio_platform.cpp
@@ -250,18 +250,25 @@
     return 0;
 }
 
+#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
+OT_TOOL_WEAK otError otPlatRadioResetCsl(otInstance *aInstance)
+{
+    return otPlatRadioEnableCsl(aInstance, 0, Mac::kShortAddrInvalid, nullptr);
+}
+#endif
+
 OT_TOOL_WEAK uint8_t otPlatRadioGetCslAccuracy(otInstance *aInstance)
 {
     OT_UNUSED_VARIABLE(aInstance);
 
-    return UINT8_MAX;
+    return NumericLimits<uint8_t>::kMax;
 }
 
 OT_TOOL_WEAK uint8_t otPlatRadioGetCslUncertainty(otInstance *aInstance)
 {
     OT_UNUSED_VARIABLE(aInstance);
 
-    return UINT8_MAX;
+    return NumericLimits<uint8_t>::kMax;
 }
 
 OT_TOOL_WEAK otError otPlatRadioGetFemLnaGain(otInstance *aInstance, int8_t *aGain)
diff --git a/src/core/radio/trel_interface.cpp b/src/core/radio/trel_interface.cpp
index d253477..dc94b45 100644
--- a/src/core/radio/trel_interface.cpp
+++ b/src/core/radio/trel_interface.cpp
@@ -352,6 +352,10 @@
     mPeerTable.PopBack();
 }
 
+const Counters *Interface::GetCounters(void) const { return otPlatTrelGetCounters(&GetInstance()); }
+
+void Interface::ResetCounters(void) { otPlatTrelResetCounters(&GetInstance()); }
+
 Error Interface::Send(const Packet &aPacket, bool aIsDiscovery)
 {
     Error error = kErrorNone;
diff --git a/src/core/radio/trel_interface.hpp b/src/core/radio/trel_interface.hpp
index 9ef09b2..70b4eb0 100644
--- a/src/core/radio/trel_interface.hpp
+++ b/src/core/radio/trel_interface.hpp
@@ -60,6 +60,12 @@
 extern "C" void otPlatTrelHandleDiscoveredPeerInfo(otInstance *aInstance, const otPlatTrelPeerInfo *aInfo);
 
 /**
+ * Represents a group of TREL counters.
+ *
+ */
+typedef otTrelCounters Counters;
+
+/**
  * Represents a TREL link interface.
  *
  */
@@ -206,6 +212,14 @@
     const Peer *GetNextPeer(PeerIterator &aIterator) const;
 
     /**
+     * Returns the number of TREL peers.
+     *
+     * @returns  The number of TREL peers.
+     *
+     */
+    uint16_t GetNumberOfPeers(void) const { return mPeerTable.GetLength(); }
+
+    /**
      * Sets the filter mode (enables/disables filtering).
      *
      * When filtering is enabled, any rx and tx traffic through TREL interface is silently dropped. This is mainly
@@ -228,10 +242,27 @@
      */
     bool IsFilterEnabled(void) const { return mFiltered; }
 
+    /**
+     * Gets the TREL counters.
+     *
+     * The counters are initialized to zero when the TREL platform is initialized.
+     *
+     */
+    const Counters *GetCounters(void) const;
+
+    /**
+     * Resets the TREL counters.
+     *
+     */
+    void ResetCounters(void);
+
 private:
+#if OPENTHREAD_CONFIG_TREL_PEER_TABLE_SIZE != 0
+    static constexpr uint16_t kPeerTableSize = OPENTHREAD_CONFIG_TREL_PEER_TABLE_SIZE;
+#else
     static constexpr uint16_t kPeerTableExtraEntries = 32;
     static constexpr uint16_t kPeerTableSize         = Mle::kMaxRouters + Mle::kMaxChildren + kPeerTableExtraEntries;
-
+#endif
     static const char kTxtRecordExtAddressKey[];
     static const char kTxtRecordExtPanIdKey[];
 
diff --git a/src/core/radio/trel_link.cpp b/src/core/radio/trel_link.cpp
index b976765..0b47987 100644
--- a/src/core/radio/trel_link.cpp
+++ b/src/core/radio/trel_link.cpp
@@ -55,9 +55,9 @@
     , mTimer(aInstance)
     , mInterface(aInstance)
 {
-    memset(&mTxFrame, 0, sizeof(mTxFrame));
-    memset(&mRxFrame, 0, sizeof(mRxFrame));
-    memset(mAckFrameBuffer, 0, sizeof(mAckFrameBuffer));
+    ClearAllBytes(mTxFrame);
+    ClearAllBytes(mRxFrame);
+    ClearAllBytes(mAckFrameBuffer);
 
     mTxFrame.mPsdu = &mTxPacketBuffer[kMaxHeaderSize];
     mTxFrame.SetLength(0);
diff --git a/src/core/radio_cli.cmake b/src/core/radio_cli.cmake
index 31e7b6d..cec70bf 100644
--- a/src/core/radio_cli.cmake
+++ b/src/core/radio_cli.cmake
@@ -29,6 +29,8 @@
 add_library(openthread-radio-cli)
 
 target_compile_definitions(openthread-radio-cli PRIVATE
+    OPENTHREAD_FTD=0
+    OPENTHREAD_MTD=0
     OPENTHREAD_RADIO=1
     OPENTHREAD_RADIO_CLI=1
 )
diff --git a/src/core/thread/address_resolver.cpp b/src/core/thread/address_resolver.cpp
index a779a11..28f6483 100644
--- a/src/core/thread/address_resolver.cpp
+++ b/src/core/thread/address_resolver.cpp
@@ -785,7 +785,7 @@
     SuccessOrExit(error = Tlv::Find<ThreadTargetTlv>(aMessage, target));
     SuccessOrExit(error = Tlv::Find<ThreadMeshLocalEidTlv>(aMessage, meshLocalIid));
 
-    for (const Ip6::Netif::UnicastAddress &address : Get<ThreadNetif>().GetUnicastAddresses())
+    for (Ip6::Netif::UnicastAddress &address : Get<ThreadNetif>().GetUnicastAddresses())
     {
         if (address.GetAddress() == target && Get<Mle::MleRouter>().GetMeshLocal64().GetIid() != meshLocalIid)
         {
diff --git a/src/core/thread/address_resolver.hpp b/src/core/thread/address_resolver.hpp
index 880d436..8b5d0a4 100644
--- a/src/core/thread/address_resolver.hpp
+++ b/src/core/thread/address_resolver.hpp
@@ -199,7 +199,10 @@
     /**
      * Looks up the RLOC16 for a given EID in the address cache.
      *
-     * @param[in]   aEid                A reference to the EID.
+     * When a cache entry is successfully looked up using this method, it will be marked as "cached and in-use".
+     * Specifically, a snooped entry (`kStateSnooped`) will be marked as cached (`kStateCached`).
+     *
+     * @param[in]   aEid   A reference to the EID to lookup.
      *
      * @returns The RLOC16 mapping to @p aEid or `Mac::kShortAddrInvalid` if it is not found in the address cache.
      *
diff --git a/src/core/thread/announce_begin_server.cpp b/src/core/thread/announce_begin_server.cpp
index 4db912d..b243d90 100644
--- a/src/core/thread/announce_begin_server.cpp
+++ b/src/core/thread/announce_begin_server.cpp
@@ -71,7 +71,7 @@
     uint16_t period;
 
     VerifyOrExit(aMessage.IsPostRequest());
-    VerifyOrExit((mask = MeshCoP::ChannelMaskTlv::GetChannelMask(aMessage)) != 0);
+    SuccessOrExit(MeshCoP::ChannelMaskTlv::FindIn(aMessage, mask));
 
     SuccessOrExit(Tlv::Find<MeshCoP::CountTlv>(aMessage, count));
     SuccessOrExit(Tlv::Find<MeshCoP::PeriodTlv>(aMessage, period));
diff --git a/src/core/thread/child.cpp b/src/core/thread/child.cpp
index 008173b..92a0b05 100644
--- a/src/core/thread/child.cpp
+++ b/src/core/thread/child.cpp
@@ -112,14 +112,14 @@
 {
     Instance &instance = GetInstance();
 
-    memset(reinterpret_cast<void *>(this), 0, sizeof(Child));
+    ClearAllBytes(*this);
     Init(instance);
 }
 
 void Child::ClearIp6Addresses(void)
 {
     mMeshLocalIid.Clear();
-    memset(mIp6Address, 0, sizeof(mIp6Address));
+    ClearAllBytes(mIp6Address);
 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
     mMlrToRegisterMask.Clear();
     mMlrRegisteredMask.Clear();
diff --git a/src/core/thread/child_table.cpp b/src/core/thread/child_table.cpp
index cd9cb65..7fb557d 100644
--- a/src/core/thread/child_table.cpp
+++ b/src/core/thread/child_table.cpp
@@ -245,6 +245,7 @@
         child->SetTimeout(childInfo.GetTimeout());
         child->SetDeviceMode(Mle::DeviceMode(childInfo.GetMode()));
         child->SetState(Neighbor::kStateRestored);
+        child->GenerateChallenge();
         child->SetLastHeard(TimerMilli::GetNow());
         child->SetVersion(childInfo.GetVersion());
         Get<IndirectSender>().SetChildUseShortAddress(*child, true);
diff --git a/src/core/thread/csl_tx_scheduler.hpp b/src/core/thread/csl_tx_scheduler.hpp
index 77eebcd..7b3d129 100644
--- a/src/core/thread/csl_tx_scheduler.hpp
+++ b/src/core/thread/csl_tx_scheduler.hpp
@@ -113,7 +113,7 @@
          * containing the CSL IE was transmitted until the next channel sample,
          * see IEEE 802.15.4-2015, section 6.12.2.
          *
-         * The Thread standard further defines the CSL phase (see Thread 1.3.1,
+         * The Thread standard further defines the CSL phase (see Thread 1.4,
          * section 3.2.6.3.4, also conforming to IEEE 802.15.4-2020, section
          * 6.12.2.1):
          *  * The "first symbol" from the definition SHALL be interpreted as the
diff --git a/src/core/thread/discover_scanner.cpp b/src/core/thread/discover_scanner.cpp
index 2425e1e..9cd9978 100644
--- a/src/core/thread/discover_scanner.cpp
+++ b/src/core/thread/discover_scanner.cpp
@@ -205,7 +205,7 @@
     return frame;
 }
 
-void DiscoverScanner::HandleDiscoveryRequestFrameTxDone(Message &aMessage)
+void DiscoverScanner::HandleDiscoveryRequestFrameTxDone(Message &aMessage, Error aError)
 {
     switch (mState)
     {
@@ -213,15 +213,28 @@
         break;
 
     case kStateScanning:
-        // Mark the Discovery Request message for direct tx to ensure it
-        // is not dequeued and freed by `MeshForwarder` and is ready for
-        // the next scan channel. Also pause message tx on `MeshForwarder`
-        // while listening to receive Discovery Responses.
-        aMessage.SetDirectTransmission();
-        aMessage.SetTimestampToNow();
-        Get<MeshForwarder>().PauseMessageTransmissions();
-        mTimer.Start(kDefaultScanDuration);
-        break;
+        if ((aError == kErrorNone) || (aError == kErrorChannelAccessFailure))
+        {
+            // Mark the Discovery Request message for direct tx to ensure it
+            // is not dequeued and freed by `MeshForwarder` and is ready for
+            // the next scan channel. Also pause message tx on `MeshForwarder`
+            // while listening to receive Discovery Responses.
+            aMessage.SetDirectTransmission();
+            aMessage.SetTimestampToNow();
+            Get<MeshForwarder>().PauseMessageTransmissions();
+            mTimer.Start(kDefaultScanDuration);
+            break;
+        }
+
+        // If we encounter other error failures (e.g., `kErrorDrop` due
+        // to queue management dropping the message or if message being
+        // evicted), `aMessage` may be immediately freed. This prevents
+        // us from reusing it to request a scan on the next scan channel.
+        // As a result, we stop the scan operation in such cases.
+
+        mState = kStateScanDone;
+
+        OT_FALL_THROUGH;
 
     case kStateScanDone:
         HandleDiscoverComplete();
@@ -295,8 +308,7 @@
 
 void DiscoverScanner::HandleDiscoveryResponse(Mle::RxInfo &aRxInfo) const
 {
-    Error                         error    = kErrorNone;
-    const ThreadLinkInfo         *linkInfo = aRxInfo.mMessageInfo.GetThreadLinkInfo();
+    Error                         error = kErrorNone;
     MeshCoP::Tlv                  meshcopTlv;
     MeshCoP::DiscoveryResponseTlv discoveryResponse;
     MeshCoP::NetworkNameTlv       networkName;
@@ -312,12 +324,12 @@
     // Find MLE Discovery TLV
     SuccessOrExit(error = Tlv::FindTlvValueStartEndOffsets(aRxInfo.mMessage, Tlv::kDiscovery, offset, end));
 
-    memset(&result, 0, sizeof(result));
+    ClearAllBytes(result);
     result.mDiscover = true;
-    result.mPanId    = linkInfo->mPanId;
-    result.mChannel  = linkInfo->mChannel;
-    result.mRssi     = linkInfo->mRss;
-    result.mLqi      = linkInfo->mLqi;
+    result.mPanId    = aRxInfo.mMessage.GetPanId();
+    result.mChannel  = aRxInfo.mMessage.GetChannel();
+    result.mRssi     = aRxInfo.mMessage.GetAverageRss();
+    result.mLqi      = aRxInfo.mMessage.GetAverageLqi();
 
     aRxInfo.mMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(AsCoreType(&result.mExtAddress));
 
diff --git a/src/core/thread/discover_scanner.hpp b/src/core/thread/discover_scanner.hpp
index d9bdd13..4753802 100644
--- a/src/core/thread/discover_scanner.hpp
+++ b/src/core/thread/discover_scanner.hpp
@@ -165,7 +165,7 @@
 
     // Methods used by `MeshForwarder`
     Mac::TxFrame *PrepareDiscoveryRequestFrame(Mac::TxFrame &aFrame);
-    void          HandleDiscoveryRequestFrameTxDone(Message &aMessage);
+    void          HandleDiscoveryRequestFrameTxDone(Message &aMessage, Error aError);
     void          Stop(void) { HandleDiscoverComplete(); }
 
     // Methods used from `Mle`
diff --git a/src/core/thread/dua_manager.cpp b/src/core/thread/dua_manager.cpp
index 7d78879..9f98b07 100644
--- a/src/core/thread/dua_manager.cpp
+++ b/src/core/thread/dua_manager.cpp
@@ -158,7 +158,7 @@
     }
     else
     {
-        LogWarn("Generate DUA: %s", ErrorToString(error));
+        LogWarnOnError(error, "generate DUA");
     }
 
     return error;
@@ -548,7 +548,7 @@
         UpdateCheckDelay(kNoBufDelay);
     }
 
-    LogInfo("PerformNextRegistration: %s", ErrorToString(error));
+    LogWarnOnError(error, "perform next registration");
     FreeMessageOnError(message, error);
 }
 
diff --git a/src/core/thread/energy_scan_server.cpp b/src/core/thread/energy_scan_server.cpp
index c390141..c79c7d8 100644
--- a/src/core/thread/energy_scan_server.cpp
+++ b/src/core/thread/energy_scan_server.cpp
@@ -63,12 +63,11 @@
 template <>
 void EnergyScanServer::HandleTmf<kUriEnergyScan>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
 {
-    uint8_t                 count;
-    uint16_t                period;
-    uint16_t                scanDuration;
-    uint32_t                mask;
-    MeshCoP::Tlv            tlv;
-    MeshCoP::ChannelMaskTlv channelMaskTlv;
+    uint8_t      count;
+    uint16_t     period;
+    uint16_t     scanDuration;
+    uint32_t     mask;
+    MeshCoP::Tlv tlv;
 
     VerifyOrExit(aMessage.IsPostRequest());
 
@@ -76,15 +75,13 @@
     SuccessOrExit(Tlv::Find<MeshCoP::PeriodTlv>(aMessage, period));
     SuccessOrExit(Tlv::Find<MeshCoP::ScanDurationTlv>(aMessage, scanDuration));
 
-    VerifyOrExit((mask = MeshCoP::ChannelMaskTlv::GetChannelMask(aMessage)) != 0);
+    SuccessOrExit(MeshCoP::ChannelMaskTlv::FindIn(aMessage, mask));
 
     FreeMessage(mReportMessage);
     mReportMessage = Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(kUriEnergyReport);
     VerifyOrExit(mReportMessage != nullptr);
 
-    channelMaskTlv.Init();
-    channelMaskTlv.SetChannelMask(mask);
-    SuccessOrExit(channelMaskTlv.AppendTo(*mReportMessage));
+    SuccessOrExit(MeshCoP::ChannelMaskTlv::AppendTo(*mReportMessage, mask));
 
     tlv.SetType(MeshCoP::Tlv::kEnergyList);
     SuccessOrExit(mReportMessage->Append(tlv));
@@ -201,7 +198,7 @@
 
 exit:
     FreeMessageOnError(mReportMessage, error);
-    MeshCoP::LogError("send scan results", error);
+    LogWarnOnError(error, "send scan results");
     mReportMessage = nullptr;
 }
 
diff --git a/src/core/thread/key_manager.cpp b/src/core/thread/key_manager.cpp
index 4d73444..d38fdb8 100644
--- a/src/core/thread/key_manager.cpp
+++ b/src/core/thread/key_manager.cpp
@@ -60,6 +60,9 @@
                                                'r', 'I', 'n', 'f', 'r', 'a', 'K', 'e', 'y'};
 #endif
 
+//---------------------------------------------------------------------------------------------------------------------
+// SecurityPolicy
+
 void SecurityPolicy::SetToDefault(void)
 {
     mRotationTime = kDefaultKeyRotationTime;
@@ -163,6 +166,9 @@
     return;
 }
 
+//---------------------------------------------------------------------------------------------------------------------
+// KeyManager
+
 KeyManager::KeyManager(Instance &aInstance)
     : InstanceLocator(aInstance)
     , mKeySequence(0)
@@ -171,7 +177,7 @@
     , mStoredMleFrameCounter(0)
     , mHoursSinceKeyRotation(0)
     , mKeySwitchGuardTime(kDefaultKeySwitchGuardTime)
-    , mKeySwitchGuardEnabled(false)
+    , mKeySwitchGuardTimer(0)
     , mKeyRotationTimer(aInstance)
     , mKekFrameCounter(0)
     , mIsPskcSet(false)
@@ -198,8 +204,8 @@
 
 void KeyManager::Start(void)
 {
-    mKeySwitchGuardEnabled = false;
-    StartKeyRotationTimer();
+    mKeySwitchGuardTimer = 0;
+    ResetKeyRotationTimer();
 }
 
 void KeyManager::Stop(void) { mKeyRotationTimer.Stop(); }
@@ -362,20 +368,13 @@
 #endif
 }
 
-void KeyManager::SetCurrentKeySequence(uint32_t aKeySequence)
+void KeyManager::SetCurrentKeySequence(uint32_t aKeySequence, KeySequenceUpdateMode aUpdateMode)
 {
     VerifyOrExit(aKeySequence != mKeySequence, Get<Notifier>().SignalIfFirst(kEventThreadKeySeqCounterChanged));
 
-    if ((aKeySequence == (mKeySequence + 1)) && mKeyRotationTimer.IsRunning())
+    if (aUpdateMode == kApplyKeySwitchGuard)
     {
-        if (mKeySwitchGuardEnabled)
-        {
-            // Check if the guard timer has expired if key rotation is requested.
-            VerifyOrExit(mHoursSinceKeyRotation >= mKeySwitchGuardTime);
-            StartKeyRotationTimer();
-        }
-
-        mKeySwitchGuardEnabled = true;
+        VerifyOrExit(mKeySwitchGuardTimer == 0);
     }
 
     mKeySequence = aKeySequence;
@@ -384,6 +383,9 @@
     SetAllMacFrameCounters(0, /* aSetIfLarger */ false);
     mMleFrameCounter = 0;
 
+    ResetKeyRotationTimer();
+    mKeySwitchGuardTimer = mKeySwitchGuardTime;
+
     Get<Notifier>().Signal(kEventThreadKeySeqCounterChanged);
 
 exit:
@@ -476,47 +478,64 @@
 
 void KeyManager::SetSecurityPolicy(const SecurityPolicy &aSecurityPolicy)
 {
-    if (aSecurityPolicy.mRotationTime < SecurityPolicy::kMinKeyRotationTime)
+    SecurityPolicy newPolicy = aSecurityPolicy;
+
+    if (newPolicy.mRotationTime < SecurityPolicy::kMinKeyRotationTime)
     {
-        LogNote("Key Rotation Time too small: %d", aSecurityPolicy.mRotationTime);
-        ExitNow();
+        newPolicy.mRotationTime = SecurityPolicy::kMinKeyRotationTime;
+        LogNote("Key Rotation Time in SecurityPolicy is set to min allowed value of %u", newPolicy.mRotationTime);
     }
 
-    IgnoreError(Get<Notifier>().Update(mSecurityPolicy, aSecurityPolicy, kEventSecurityPolicyChanged));
+    if (newPolicy.mRotationTime != mSecurityPolicy.mRotationTime)
+    {
+        uint32_t newGuardTime = newPolicy.mRotationTime;
 
-exit:
-    return;
+        // Calculations are done using a `uint32_t` variable to prevent
+        // potential overflow.
+
+        newGuardTime *= kKeySwitchGuardTimePercentage;
+        newGuardTime /= 100;
+
+        mKeySwitchGuardTime = static_cast<uint16_t>(newGuardTime);
+    }
+
+    IgnoreError(Get<Notifier>().Update(mSecurityPolicy, newPolicy, kEventSecurityPolicyChanged));
+
+    CheckForKeyRotation();
 }
 
-void KeyManager::StartKeyRotationTimer(void)
+void KeyManager::ResetKeyRotationTimer(void)
 {
     mHoursSinceKeyRotation = 0;
-    mKeyRotationTimer.Start(kOneHourIntervalInMsec);
+    mKeyRotationTimer.Start(Time::kOneHourInMsec);
 }
 
 void KeyManager::HandleKeyRotationTimer(void)
 {
+    mKeyRotationTimer.Start(Time::kOneHourInMsec);
+
     mHoursSinceKeyRotation++;
 
-    // Order of operations below is important. We should restart the timer (from
-    // last fire time for one hour interval) before potentially calling
-    // `SetCurrentKeySequence()`. `SetCurrentKeySequence()` uses the fact that
-    // timer is running to decide to check for the guard time and to reset the
-    // rotation timer (and the `mHoursSinceKeyRotation`) if it updates the key
-    // sequence.
+    if (mKeySwitchGuardTimer > 0)
+    {
+        mKeySwitchGuardTimer--;
+    }
 
-    mKeyRotationTimer.StartAt(mKeyRotationTimer.GetFireTime(), kOneHourIntervalInMsec);
+    CheckForKeyRotation();
+}
 
+void KeyManager::CheckForKeyRotation(void)
+{
     if (mHoursSinceKeyRotation >= mSecurityPolicy.mRotationTime)
     {
-        SetCurrentKeySequence(mKeySequence + 1);
+        SetCurrentKeySequence(mKeySequence + 1, kForceUpdate);
     }
 }
 
 void KeyManager::GetNetworkKey(NetworkKey &aNetworkKey) const
 {
 #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
-    if (Crypto::Storage::IsKeyRefValid(mNetworkKeyRef))
+    if (Crypto::Storage::HasKey(mNetworkKeyRef))
     {
         size_t keyLen;
 
@@ -535,7 +554,7 @@
 void KeyManager::GetPskc(Pskc &aPskc) const
 {
 #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
-    if (Crypto::Storage::IsKeyRefValid(mPskcRef))
+    if (Crypto::Storage::HasKey(mPskcRef))
     {
         size_t keyLen;
 
diff --git a/src/core/thread/key_manager.hpp b/src/core/thread/key_manager.hpp
index 18f11f2..099854c 100644
--- a/src/core/thread/key_manager.hpp
+++ b/src/core/thread/key_manager.hpp
@@ -77,8 +77,17 @@
      */
     static constexpr uint8_t kVersionThresholdOffsetVersion = 3;
 
-    static constexpr uint16_t kMinKeyRotationTime     = 1;   ///< The minimum Key Rotation Time in hours.
-    static constexpr uint16_t kDefaultKeyRotationTime = 672; ///< Default Key Rotation Time (in unit of hours).
+    /**
+     * Default Key Rotation Time (in unit of hours).
+     *
+     */
+    static constexpr uint16_t kDefaultKeyRotationTime = 672;
+
+    /**
+     * Minimum Key Rotation Time (in unit of hours).
+     *
+     */
+    static constexpr uint16_t kMinKeyRotationTime = 2;
 
     /**
      * Initializes the object with default Key Rotation Time
@@ -212,6 +221,18 @@
 {
 public:
     /**
+     * Determines whether to apply or ignore key switch guard when updating the key sequence.
+     *
+     * Used as input by `SetCurrentKeySequence()`.
+     *
+     */
+    enum KeySequenceUpdateMode : uint8_t
+    {
+        kApplyKeySwitchGuard, ///< Apply key switch guard check before setting the new key sequence.
+        kForceUpdate,         ///< Ignore key switch guard check and forcibly update the key sequence to new value.
+    };
+
+    /**
      * Initializes the object.
      *
      * @param[in]  aInstance     A reference to the OpenThread instance.
@@ -321,10 +342,14 @@
     /**
      * Sets the current key sequence value.
      *
-     * @param[in]  aKeySequence  The key sequence value.
+     * If @p aMode is `kApplyKeySwitchGuard`, the current key switch guard timer is checked and only if it is zero, key
+     * sequence will be updated.
+     *
+     * @param[in]  aKeySequence    The key sequence value.
+     * @param[in]  aUpdateMode     Whether or not to apply the key switch guard.
      *
      */
-    void SetCurrentKeySequence(uint32_t aKeySequence);
+    void SetCurrentKeySequence(uint32_t aKeySequence, KeySequenceUpdateMode aUpdateMode);
 
 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
     /**
@@ -500,17 +525,19 @@
      * @returns The KeySwitchGuardTime value in hours.
      *
      */
-    uint32_t GetKeySwitchGuardTime(void) const { return mKeySwitchGuardTime; }
+    uint16_t GetKeySwitchGuardTime(void) const { return mKeySwitchGuardTime; }
 
     /**
      * Sets the KeySwitchGuardTime.
      *
      * The KeySwitchGuardTime is the time interval during which key rotation procedure is prevented.
      *
-     * @param[in]  aKeySwitchGuardTime  The KeySwitchGuardTime value in hours.
+     * Intended for testing only. Changing the guard time will render device non-compliant with the Thread spec.
+     *
+     * @param[in]  aGuardTime  The KeySwitchGuardTime value in hours.
      *
      */
-    void SetKeySwitchGuardTime(uint32_t aKeySwitchGuardTime) { mKeySwitchGuardTime = aKeySwitchGuardTime; }
+    void SetKeySwitchGuardTime(uint16_t aGuardTime) { mKeySwitchGuardTime = aGuardTime; }
 
     /**
      * Returns the Security Policy.
@@ -565,9 +592,13 @@
 #endif
 
 private:
-    static constexpr uint32_t kDefaultKeySwitchGuardTime = 624;
-    static constexpr uint32_t kOneHourIntervalInMsec     = 3600u * 1000u;
-    static constexpr bool     kExportableMacKeys         = OPENTHREAD_CONFIG_PLATFORM_MAC_KEYS_EXPORTABLE_ENABLE;
+    static constexpr uint16_t kDefaultKeySwitchGuardTime    = 624; // ~ 93% of 672 (default key rotation time)
+    static constexpr uint32_t kKeySwitchGuardTimePercentage = 93;  // Percentage of key rotation time.
+    static constexpr bool     kExportableMacKeys            = OPENTHREAD_CONFIG_PLATFORM_MAC_KEYS_EXPORTABLE_ENABLE;
+
+    static_assert(kDefaultKeySwitchGuardTime ==
+                      SecurityPolicy::kDefaultKeyRotationTime * kKeySwitchGuardTimePercentage / 100,
+                  "Default key switch guard time value is not correct");
 
     OT_TOOL_PACKED_BEGIN
     struct Keys
@@ -591,8 +622,9 @@
     void ComputeTrelKey(uint32_t aKeySequence, Mac::Key &aKey) const;
 #endif
 
-    void StartKeyRotationTimer(void);
+    void ResetKeyRotationTimer(void);
     void HandleKeyRotationTimer(void);
+    void CheckForKeyRotation(void);
 
 #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
     void StoreNetworkKey(const NetworkKey &aNetworkKey, bool aOverWriteExisting);
@@ -630,9 +662,9 @@
     uint32_t               mStoredMacFrameCounter;
     uint32_t               mStoredMleFrameCounter;
 
-    uint32_t      mHoursSinceKeyRotation;
-    uint32_t      mKeySwitchGuardTime;
-    bool          mKeySwitchGuardEnabled;
+    uint16_t      mHoursSinceKeyRotation;
+    uint16_t      mKeySwitchGuardTime;
+    uint16_t      mKeySwitchGuardTimer;
     RotationTimer mKeyRotationTimer;
 
 #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
diff --git a/src/core/thread/link_quality.cpp b/src/core/thread/link_quality.cpp
index bf6b35b..6747540 100644
--- a/src/core/thread/link_quality.cpp
+++ b/src/core/thread/link_quality.cpp
@@ -38,6 +38,7 @@
 #include "common/code_utils.hpp"
 #include "common/locator_getters.hpp"
 #include "common/num_utils.hpp"
+#include "common/numeric_limits.hpp"
 #include "instance/instance.hpp"
 
 namespace ot {
@@ -116,16 +117,20 @@
 
 void LqiAverager::Add(uint8_t aLqi)
 {
-    uint8_t count;
+    uint8_t  count;
+    uint16_t newAverage;
 
-    if (mCount < UINT8_MAX)
+    if (mCount < NumericLimits<uint8_t>::kMax)
     {
         mCount++;
     }
 
     count = Min(static_cast<uint8_t>(1 << kCoeffBitShift), mCount);
 
-    mAverage = static_cast<uint8_t>(((mAverage * (count - 1)) + aLqi) / count);
+    newAverage = mAverage;
+    newAverage = (newAverage * (count - 1) + aLqi) / count;
+
+    mAverage = static_cast<uint8_t>(newAverage);
 }
 
 void LinkQualityInfo::Clear(void)
diff --git a/src/core/thread/mesh_forwarder.cpp b/src/core/thread/mesh_forwarder.cpp
index 4355447..4b5b4b2 100644
--- a/src/core/thread/mesh_forwarder.cpp
+++ b/src/core/thread/mesh_forwarder.cpp
@@ -202,7 +202,7 @@
     aFrame.SetPayloadLength(0);
 }
 
-void MeshForwarder::RemoveMessage(Message &aMessage)
+void MeshForwarder::EvictMessage(Message &aMessage)
 {
     PriorityQueue *queue = aMessage.GetPriorityQueue();
 
@@ -217,6 +217,8 @@
         }
 #endif
 
+        FinalizeMessageDirectTx(aMessage, kErrorNoBufs);
+
         if (mSendMessage == &aMessage)
         {
             mSendMessage = nullptr;
@@ -387,7 +389,7 @@
         mTxQueueStats.UpdateFor(aMessage);
 #endif
         LogMessage(kMessageQueueMgmtDrop, aMessage);
-        aMessage.ClearDirectTransmission();
+        FinalizeMessageDirectTx(aMessage, kErrorDrop);
         RemoveMessageIfNoPendingTx(aMessage);
     }
 
@@ -518,7 +520,7 @@
 #endif
 
     LogMessage(kMessageFullQueueDrop, aMessage);
-    aMessage.ClearDirectTransmission();
+    FinalizeMessageDirectTx(aMessage, kErrorDrop);
     RemoveMessageIfNoPendingTx(aMessage);
 
 exit:
@@ -642,6 +644,7 @@
             mTxQueueStats.UpdateFor(*curMessage);
 #endif
             LogMessage(kMessageDrop, *curMessage, error);
+            FinalizeMessageDirectTx(*curMessage, error);
             mSendQueue.DequeueAndFree(*curMessage);
             continue;
         }
@@ -1261,9 +1264,6 @@
 
     txError = aFrameTxError;
 
-    mSendMessage->ClearDirectTransmission();
-    mSendMessage->SetOffset(0);
-
     if (aNeighbor != nullptr)
     {
         aNeighbor->GetLinkInfo().AddMessageTxStatus(mSendMessage->GetTxSuccess());
@@ -1289,39 +1289,54 @@
 #endif
 
     LogMessage(kMessageTransmit, *mSendMessage, txError, &aMacDest);
+    FinalizeMessageDirectTx(*mSendMessage, txError);
+    RemoveMessageIfNoPendingTx(*mSendMessage);
 
-    if (mSendMessage->GetType() == Message::kTypeIp6)
+exit:
+    mScheduleTransmissionTask.Post();
+}
+
+void MeshForwarder::FinalizeMessageDirectTx(Message &aMessage, Error aError)
+{
+    // Finalizes the direct transmission of `aMessage`. This can be
+    // triggered by successful delivery (all fragments reaching the
+    // destination), failure of any fragment, queue management
+    // dropping the message, or eviction of message to accommodate
+    // higher priority messages.
+
+    VerifyOrExit(aMessage.IsDirectTransmission());
+
+    aMessage.ClearDirectTransmission();
+    aMessage.SetOffset(0);
+
+    if (aError != kErrorNone)
     {
-        if (mSendMessage->GetTxSuccess())
-        {
-            mIpCounters.mTxSuccess++;
-        }
-        else
-        {
-            mIpCounters.mTxFailure++;
-        }
+        aMessage.SetTxSuccess(false);
     }
 
-    switch (mSendMessage->GetSubType())
+    if (aMessage.GetType() == Message::kTypeIp6)
+    {
+        aMessage.GetTxSuccess() ? mIpCounters.mTxSuccess++ : mIpCounters.mTxFailure++;
+    }
+
+    switch (aMessage.GetSubType())
     {
     case Message::kSubTypeMleDiscoverRequest:
         // Note that `HandleDiscoveryRequestFrameTxDone()` may update
-        // `mSendMessage` and mark it again for direct transmission.
-        Get<Mle::DiscoverScanner>().HandleDiscoveryRequestFrameTxDone(*mSendMessage);
+        // `aMessage` and mark it again for direct transmission.
+        Get<Mle::DiscoverScanner>().HandleDiscoveryRequestFrameTxDone(aMessage, aError);
         break;
 
     case Message::kSubTypeMleChildIdRequest:
-        Get<Mle::Mle>().HandleChildIdRequestTxDone(*mSendMessage);
+        Get<Mle::Mle>().HandleChildIdRequestTxDone(aMessage);
         break;
 
     default:
         break;
     }
 
-    RemoveMessageIfNoPendingTx(*mSendMessage);
-
 exit:
-    mScheduleTransmissionTask.Post();
+    return;
 }
 
 bool MeshForwarder::RemoveMessageIfNoPendingTx(Message &aMessage)
@@ -1462,7 +1477,7 @@
 
         message->SetDatagramTag(fragmentHeader.GetDatagramTag());
         message->SetTimestampToNow();
-        message->SetLinkInfo(aLinkInfo);
+        message->UpdateLinkInfoFrom(aLinkInfo);
 
         VerifyOrExit(Get<Ip6::Filter>().Accept(*message), error = kErrorDrop);
 
@@ -1515,9 +1530,7 @@
         message->WriteData(message->GetOffset(), aFrameData);
         message->MoveOffset(aFrameData.GetLength());
         message->AddRss(aLinkInfo.GetRss());
-#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
         message->AddLqi(aLinkInfo.GetLqi());
-#endif
         message->SetTimestampToNow();
     }
 
@@ -1528,7 +1541,7 @@
         if (message->GetOffset() >= message->GetLength())
         {
             mReassemblyList.Dequeue(*message);
-            IgnoreError(HandleDatagram(*message, aLinkInfo, aMacAddrs.mSource));
+            IgnoreError(HandleDatagram(*message, aMacAddrs.mSource));
         }
     }
     else
@@ -1628,7 +1641,7 @@
 
     SuccessOrExit(error = FrameToMessage(aFrameData, 0, aMacAddrs, message));
 
-    message->SetLinkInfo(aLinkInfo);
+    message->UpdateLinkInfoFrom(aLinkInfo);
 
     VerifyOrExit(Get<Ip6::Filter>().Accept(*message), error = kErrorDrop);
 
@@ -1640,7 +1653,7 @@
 
     if (error == kErrorNone)
     {
-        IgnoreError(HandleDatagram(*message, aLinkInfo, aMacAddrs.mSource));
+        IgnoreError(HandleDatagram(*message, aMacAddrs.mSource));
     }
     else
     {
@@ -1649,7 +1662,7 @@
     }
 }
 
-Error MeshForwarder::HandleDatagram(Message &aMessage, const ThreadLinkInfo &aLinkInfo, const Mac::Address &aMacSource)
+Error MeshForwarder::HandleDatagram(Message &aMessage, const Mac::Address &aMacSource)
 {
 #if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
     Get<Utils::HistoryTracker>().RecordRxMessage(aMessage, aMacSource);
@@ -1665,7 +1678,7 @@
     aMessage.SetLoopbackToHostAllowed(true);
     aMessage.SetOrigin(Message::kOriginThreadNetif);
 
-    return Get<Ip6::Ip6>().HandleDatagram(OwnedPtr<Message>(&aMessage), &aLinkInfo);
+    return Get<Ip6::Ip6>().HandleDatagram(OwnedPtr<Message>(&aMessage));
 }
 
 Error MeshForwarder::GetFramePriority(const FrameData      &aFrameData,
diff --git a/src/core/thread/mesh_forwarder.hpp b/src/core/thread/mesh_forwarder.hpp
index c27029d..1ccd0d0 100644
--- a/src/core/thread/mesh_forwarder.hpp
+++ b/src/core/thread/mesh_forwarder.hpp
@@ -306,7 +306,7 @@
      * Resets the IP level counters.
      *
      */
-    void ResetCounters(void) { memset(&mIpCounters, 0, sizeof(mIpCounters)); }
+    void ResetCounters(void) { ClearAllBytes(mIpCounters); }
 
 #if OPENTHREAD_CONFIG_TX_QUEUE_STATISTICS_ENABLE
     /**
@@ -550,9 +550,9 @@
                                  uint16_t                aFragmentLength,
                                  uint16_t                aSrcRloc16,
                                  Message::Priority       aPriority);
-    Error HandleDatagram(Message &aMessage, const ThreadLinkInfo &aLinkInfo, const Mac::Address &aMacSource);
+    Error HandleDatagram(Message &aMessage, const Mac::Address &aMacSource);
     void  ClearReassemblyList(void);
-    void  RemoveMessage(Message &aMessage);
+    void  EvictMessage(Message &aMessage);
     void  HandleDiscoverComplete(void);
 
     void          HandleReceivedFrame(Mac::RxFrame &aFrame);
@@ -564,6 +564,7 @@
     void UpdateNeighborLinkFailures(Neighbor &aNeighbor, Error aError, bool aAllowNeighborRemove, uint8_t aFailLimit);
     void HandleSentFrame(Mac::TxFrame &aFrame, Error aError);
     void UpdateSendMessage(Error aFrameTxError, Mac::Address &aMacDest, Neighbor *aNeighbor);
+    void FinalizeMessageDirectTx(Message &aMessage, Error aError);
     bool RemoveMessageIfNoPendingTx(Message &aMessage);
 
     void HandleTimeTick(void);
diff --git a/src/core/thread/mesh_forwarder_ftd.cpp b/src/core/thread/mesh_forwarder_ftd.cpp
index 4001bbe..d4911b9 100644
--- a/src/core/thread/mesh_forwarder_ftd.cpp
+++ b/src/core/thread/mesh_forwarder_ftd.cpp
@@ -165,6 +165,7 @@
         if (aError != kErrorNone)
         {
             LogMessage(kMessageDrop, message, kErrorAddressQuery);
+            FinalizeMessageDirectTx(message, kErrorAddressQuery);
             mSendQueue.DequeueAndFree(message);
             continue;
         }
@@ -259,7 +260,7 @@
 exit:
     if ((error == kErrorNone) && (evict != nullptr))
     {
-        RemoveMessage(*evict);
+        EvictMessage(*evict);
     }
 
     return error;
@@ -342,6 +343,7 @@
         }
 
         LogMessage(kMessageDrop, message);
+        FinalizeMessageDirectTx(message, kErrorDrop);
         mSendQueue.DequeueAndFree(message);
     }
 }
@@ -625,10 +627,16 @@
 
     error = ip6Headers.DecompressFrom(aFrameData, aMeshAddrs, GetInstance());
 
-    if (error == kErrorNotFound)
+    switch (error)
     {
+    case kErrorNone:
+        break;
+    case kErrorNotFound:
         // Frame may not contain an IPv6 header.
-        ExitNow(error = kErrorNone);
+        error = kErrorNone;
+        OT_FALL_THROUGH;
+    default:
+        ExitNow();
     }
 
     error = Get<Mle::MleRouter>().CheckReachability(aMeshAddrs.mDestination.GetShort(), ip6Headers.GetIp6Header());
@@ -704,13 +712,7 @@
         SuccessOrExit(error = meshHeader.AppendTo(*messagePtr));
         SuccessOrExit(error = messagePtr->AppendData(aFrameData));
 
-        messagePtr->SetLinkInfo(aLinkInfo);
-
-#if OPENTHREAD_CONFIG_MULTI_RADIO
-        // We set the received radio type on the message in order for it
-        // to be logged correctly from LogMessage().
-        messagePtr->SetRadioType(static_cast<Mac::RadioType>(aLinkInfo.mRadioType));
-#endif
+        messagePtr->UpdateLinkInfoFrom(aLinkInfo);
 
         LogMessage(kMessageReceive, *messagePtr, kErrorNone, &aMacSource);
 
diff --git a/src/core/thread/mesh_forwarder_mtd.cpp b/src/core/thread/mesh_forwarder_mtd.cpp
index b88c773..2bdca2b 100644
--- a/src/core/thread/mesh_forwarder_mtd.cpp
+++ b/src/core/thread/mesh_forwarder_mtd.cpp
@@ -68,7 +68,7 @@
 
     if (message->GetPriority() < static_cast<uint8_t>(aPriority))
     {
-        RemoveMessage(*message);
+        EvictMessage(*message);
         ExitNow(error = kErrorNone);
     }
 
diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp
index cad9cac..99e6809 100644
--- a/src/core/thread/mle.cpp
+++ b/src/core/thread/mle.cpp
@@ -92,6 +92,9 @@
     , mDataRequestAttempts(0)
     , mAnnounceChannel(0)
     , mAlternateChannel(0)
+#if OPENTHREAD_FTD
+    , mLinkRequestAttempts(0)
+#endif
     , mRloc16(Mac::kShortAddrInvalid)
     , mPreviousParentRloc(Mac::kShortAddrInvalid)
     , mAttachCounter(0)
@@ -120,7 +123,7 @@
     mParentCandidate.Clear();
     ResetCounters();
 
-    mLinkLocal64.InitAsThreadOrigin(/* aPreferred */ true);
+    mLinkLocal64.InitAsThreadOrigin();
     mLinkLocal64.GetAddress().SetToLinkLocalAddress(Get<Mac::Mac>().GetExtAddress());
 
     mMeshLocal64.InitAsThreadOriginMeshLocal();
@@ -262,7 +265,7 @@
 
 void Mle::ResetCounters(void)
 {
-    memset(&mCounters, 0, sizeof(mCounters));
+    ClearAllBytes(mCounters);
 #if OPENTHREAD_CONFIG_UPTIME_ENABLE
     mLastUpdatedTimestamp = Get<Uptime>().GetUptime();
 #endif
@@ -375,7 +378,7 @@
 
     SuccessOrExit(Get<Settings>().Read(networkInfo));
 
-    Get<KeyManager>().SetCurrentKeySequence(networkInfo.GetKeySequence());
+    Get<KeyManager>().SetCurrentKeySequence(networkInfo.GetKeySequence(), KeyManager::kForceUpdate);
     Get<KeyManager>().SetMleFrameCounter(networkInfo.GetMleFrameCounter());
     Get<KeyManager>().SetAllMacFrameCounters(networkInfo.GetMacFrameCounter(), /* aSetIfLarger */ false);
 
@@ -451,7 +454,8 @@
     mWasLeader = networkInfo.GetRole() == kRoleLeader;
 #endif
 
-    // Successfully restored the network information from non-volatile settings after boot.
+    // Successfully restored the network information from
+    // non-volatile settings after boot.
     mHasRestored = true;
 
 exit:
@@ -530,7 +534,7 @@
 
     VerifyOrExit(!IsDetached() || mAttachState != kAttachStateStart);
 
-    // not in reattach stage after reset
+    // Not in reattach stage after reset
     if (mReattachState == kReattachStop)
     {
         Get<MeshCoP::PendingDatasetManager>().HandleDetach();
@@ -1090,7 +1094,7 @@
 {
     aRxInfo.mMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(aNeighbor.GetExtAddress());
     aNeighbor.GetLinkInfo().Clear();
-    aNeighbor.GetLinkInfo().AddRss(aRxInfo.mMessageInfo.GetThreadLinkInfo()->GetRss());
+    aNeighbor.GetLinkInfo().AddRss(aRxInfo.mMessage.GetAverageRss());
     aNeighbor.ResetLinkFailures();
     aNeighbor.SetLastHeard(TimerMilli::GetNow());
 }
@@ -1117,7 +1121,6 @@
     {
         if (!Get<ThreadNetif>().HasUnicastAddress(mMeshLocal64.GetAddress()))
         {
-            // Mesh Local EID was removed, choose a new one and add it back
             mMeshLocal64.GetAddress().GetIid().GenerateRandom();
 
             Get<ThreadNetif>().AddUnicastAddress(mMeshLocal64);
@@ -1132,9 +1135,12 @@
 
     if (aEvents.ContainsAny(kEventIp6MulticastSubscribed | kEventIp6MulticastUnsubscribed))
     {
-        // When multicast subscription changes, SED always notifies its parent as it depends on its
-        // parent for indirect transmission. Since Thread 1.2, MED MAY also notify its parent of 1.2
-        // or higher version as it could depend on its parent to perform Multicast Listener Report.
+        // When multicast subscription changes, SED always notifies
+        // its parent as it depends on its parent for indirect
+        // transmission. Since Thread 1.2, MED MAY also notify its
+        // parent of 1.2 or higher version as it could depend on its
+        // parent to perform Multicast Listener Report.
+
         if (IsChild() && !IsFullThreadDevice() &&
             (!IsRxOnWhenIdle()
 #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
@@ -1574,7 +1580,7 @@
         }
         else if (!IsRxOnWhenIdle())
         {
-            // return to sleepy operation
+            // Return to sleepy operation
             Get<DataPollSender>().SetAttachMode(false);
             Get<MeshForwarder>().SetRxOnWhenIdle(false);
         }
@@ -1650,7 +1656,6 @@
 exit:
     if (error != kErrorNone)
     {
-        // do not use `FreeMessageOnError()` to avoid null check on nonnull pointer
         aMessage.Free();
     }
 }
@@ -1779,9 +1784,13 @@
         {
             // Invalidate stale parent state.
             //
-            // Parent state is not normally invalidated after becoming a Router/Leader (see #1875).  When trying to
-            // attach to a better partition, invalidating old parent state (especially when in kStateRestored) ensures
-            // that FindNeighbor() returns mParentCandidate when processing the Child ID Response.
+            // Parent state is not normally invalidated after becoming
+            // a Router/Leader (see #1875).  When trying to attach to
+            // a better partition, invalidating old parent state
+            // (especially when in `kStateRestored`) ensures that
+            // `FindNeighbor()` returns `mParentCandidate` when
+            // processing the Child ID Response.
+
             mParent.SetState(Neighbor::kStateInvalid);
         }
     }
@@ -1799,7 +1808,7 @@
     {
         SuccessOrExit(error = message->AppendAddressRegistrationTlv(mAddressRegistrationMode));
 
-        // no need to request the last Route64 TLV for MTD
+        // No need to request the last Route64 TLV for MTD
         tlvsLen -= 1;
     }
 
@@ -2008,9 +2017,8 @@
     case kChildUpdateRequestPending:
         if (Get<Notifier>().IsPending())
         {
-            // Here intentionally delay another kChildUpdateRequestPendingDelay
-            // cycle to ensure we only send a Child Update Request after we
-            // know there are no more pending changes.
+            // Add another delay to ensures the Child Update Request is sent
+            // only after all pending changes are incorporated.
             ScheduleMessageTransmissionTimer();
             ExitNow();
         }
@@ -2049,8 +2057,13 @@
         ExitNow();
     }
 
-    mChildUpdateRequestState = kChildUpdateRequestActive;
-    ScheduleMessageTransmissionTimer();
+    if (aMode != kAppendZeroTimeout)
+    {
+        // Enable MLE retransmissions on all Child Update Request
+        // messages, except when actively detaching.
+        mChildUpdateRequestState = kChildUpdateRequestActive;
+        ScheduleMessageTransmissionTimer();
+    }
 
     VerifyOrExit((message = NewMleMessage(kCommandChildUpdateRequest)) != nullptr, error = kErrorNoBufs);
     SuccessOrExit(error = message->AppendModeTlv(mDeviceMode));
@@ -2428,7 +2441,7 @@
 
     LogDebg("Receive MLE message");
 
-    VerifyOrExit(aMessageInfo.GetLinkInfo() != nullptr);
+    VerifyOrExit(aMessage.GetOrigin() == Message::kOriginThreadNetif);
     VerifyOrExit(aMessageInfo.GetHopLimit() == kMleHopLimit, error = kErrorParse);
 
     SuccessOrExit(error = aMessage.Read(aMessage.GetOffset(), securitySuite));
@@ -2639,13 +2652,13 @@
     case kCommandChildIdRequest:
         Get<MleRouter>().HandleChildIdRequest(rxInfo);
         break;
+#endif // OPENTHREAD_FTD
 
 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
     case kCommandTimeSync:
-        Get<MleRouter>().HandleTimeSync(rxInfo);
+        HandleTimeSync(rxInfo);
         break;
 #endif
-#endif // OPENTHREAD_FTD
 
 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
     case kCommandLinkMetricsManagementRequest:
@@ -2696,7 +2709,7 @@
     // We skip logging failures for broadcast MLE messages since it
     // can be common to receive such messages from adjacent Thread
     // networks.
-    if (!aMessageInfo.GetSockAddr().IsMulticast() || !aMessageInfo.GetThreadLinkInfo()->IsDstPanIdBroadcast())
+    if (!aMessageInfo.GetSockAddr().IsMulticast() || !aMessage.IsDstPanIdBroadcast())
     {
         LogProcessError(kTypeGenericUdp, error);
     }
@@ -2723,7 +2736,7 @@
     switch (aRxInfo.mClass)
     {
     case RxInfo::kAuthoritativeMessage:
-        Get<KeyManager>().SetCurrentKeySequence(aRxInfo.mKeySequence);
+        Get<KeyManager>().SetCurrentKeySequence(aRxInfo.mKeySequence, KeyManager::kForceUpdate);
         break;
 
     case RxInfo::kPeerMessage:
@@ -2731,7 +2744,7 @@
         {
             if (aRxInfo.mKeySequence - Get<KeyManager>().GetCurrentKeySequence() == 1)
             {
-                Get<KeyManager>().SetCurrentKeySequence(aRxInfo.mKeySequence);
+                Get<KeyManager>().SetCurrentKeySequence(aRxInfo.mKeySequence, KeyManager::kApplyKeySwitchGuard);
             }
             else
             {
@@ -2872,11 +2885,8 @@
 
     if (mDataRequestState == kDataRequestNone && !IsRxOnWhenIdle())
     {
-        // Here simply stops fast data poll request by Mle Data Request.
-        // Note that in some cases fast data poll may continue after below stop operation until
-        // running out the specified number. E.g. other component also trigger fast poll, and
-        // is waiting for response; or the corner case where multiple Mle Data Request attempts
-        // happened due to the retransmission mechanism.
+        // Stop fast data poll request by MLE since we received
+        // the response.
         Get<DataPollSender>().StopFastPolls();
     }
 
@@ -2910,7 +2920,6 @@
     uint16_t                  pendingDatasetLength = 0;
     bool                      dataRequest          = false;
 
-    // Leader Data
     SuccessOrExit(error = aRxInfo.mMessage.ReadLeaderDataTlv(leaderData));
 
     if ((leaderData.GetPartitionId() != mLeaderData.GetPartitionId()) ||
@@ -2931,7 +2940,6 @@
         VerifyOrExit(IsNetworkDataNewer(leaderData));
     }
 
-    // Active Timestamp
     switch (Tlv::Find<ActiveTimestampTlv>(aRxInfo.mMessage, activeTimestamp))
     {
     case kErrorNone:
@@ -2939,8 +2947,9 @@
 
         timestamp = Get<MeshCoP::ActiveDatasetManager>().GetTimestamp();
 
-        // if received timestamp does not match the local value and message does not contain the dataset,
-        // send MLE Data Request
+        // Send an MLE Data Request if the received timestamp
+        // mismatches the local value and the message does not
+        // include the dataset.
         if (!IsLeader() && (MeshCoP::Timestamp::Compare(&activeTimestamp, timestamp) != 0) &&
             (Tlv::FindTlvValueOffset(aRxInfo.mMessage, Tlv::kActiveDataset, activeDatasetOffset, activeDatasetLength) !=
              kErrorNone))
@@ -2957,7 +2966,6 @@
         ExitNow(error = kErrorParse);
     }
 
-    // Pending Timestamp
     switch (Tlv::Find<PendingTimestampTlv>(aRxInfo.mMessage, pendingTimestamp))
     {
     case kErrorNone:
@@ -2965,8 +2973,6 @@
 
         timestamp = Get<MeshCoP::PendingDatasetManager>().GetTimestamp();
 
-        // if received timestamp does not match the local value and message does not contain the dataset,
-        // send MLE Data Request
         if (!IsLeader() && (MeshCoP::Timestamp::Compare(&pendingTimestamp, timestamp) != 0) &&
             (Tlv::FindTlvValueOffset(aRxInfo.mMessage, Tlv::kPendingDataset, pendingDatasetOffset,
                                      pendingDatasetLength) != kErrorNone))
@@ -3004,7 +3010,6 @@
     else
 #endif
     {
-        // Active Dataset
         if (hasActiveTimestamp)
         {
             if (activeDatasetOffset > 0)
@@ -3014,7 +3019,6 @@
             }
         }
 
-        // Pending Dataset
         if (hasPendingTimestamp)
         {
             if (pendingDatasetOffset > 0)
@@ -3129,7 +3133,7 @@
 void Mle::HandleParentResponse(RxInfo &aRxInfo)
 {
     Error            error = kErrorNone;
-    int8_t           rss   = aRxInfo.mMessageInfo.GetThreadLinkInfo()->GetRss();
+    int8_t           rss   = aRxInfo.mMessage.GetAverageRss();
     RxChallenge      response;
     uint16_t         version;
     uint16_t         sourceAddress;
@@ -3146,16 +3150,13 @@
     TimeParameterTlv timeParameterTlv;
 #endif
 
-    // Source Address
     SuccessOrExit(error = Tlv::Find<SourceAddressTlv>(aRxInfo.mMessage, sourceAddress));
 
     Log(kMessageReceive, kTypeParentResponse, aRxInfo.mMessageInfo.GetPeerAddr(), sourceAddress);
 
-    // Version
     SuccessOrExit(error = Tlv::Find<VersionTlv>(aRxInfo.mMessage, version));
     VerifyOrExit(version >= kThreadVersion1p1, error = kErrorParse);
 
-    // Response
     SuccessOrExit(error = aRxInfo.mMessage.ReadResponseTlv(response));
     VerifyOrExit(response == mParentRequestChallenge, error = kErrorParse);
 
@@ -3166,20 +3167,16 @@
         mReceivedResponseFromParent = true;
     }
 
-    // Leader Data
     SuccessOrExit(error = aRxInfo.mMessage.ReadLeaderDataTlv(leaderData));
 
-    // Link Margin
     SuccessOrExit(error = Tlv::Find<LinkMarginTlv>(aRxInfo.mMessage, linkMarginFromTlv));
     linkMargin  = Min(Get<Mac::Mac>().ComputeLinkMargin(rss), linkMarginFromTlv);
     linkQuality = LinkQualityForLinkMargin(linkMargin);
 
-    // Connectivity
     SuccessOrExit(error = Tlv::FindTlv(aRxInfo.mMessage, connectivityTlv));
     VerifyOrExit(connectivityTlv.IsValid(), error = kErrorParse);
 
 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
-    // CSL Accuracy
     switch (aRxInfo.mMessage.ReadCslClockAccuracyTlv(cslAccuracy))
     {
     case kErrorNone:
@@ -3252,7 +3249,7 @@
 
     if (mParentCandidate.IsStateParentResponse() && (mParentCandidate.GetExtAddress() != extAddress))
     {
-        // if already have a candidate parent, only seek a better parent
+        // If already have a candidate parent, only seek a better parent
 
         int compare = 0;
 
@@ -3263,23 +3260,21 @@
                                                    mParentCandidate.mIsSingleton, mParentCandidate.mLeaderData);
         }
 
-        // only consider partitions that are the same or better
+        // Only consider partitions that are the same or better
         VerifyOrExit(compare >= 0);
 #endif
 
-        // only consider better parents if the partitions are the same
+        // Only consider better parents if the partitions are the same
         if (compare == 0)
         {
             VerifyOrExit(IsBetterParent(sourceAddress, linkQuality, linkMargin, connectivityTlv, version, cslAccuracy));
         }
     }
 
-    // Link/MLE Frame Counters
     SuccessOrExit(error = aRxInfo.mMessage.ReadFrameCounterTlvs(linkFrameCounter, mleFrameCounter));
 
 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
 
-    // Time Parameter
     if (Tlv::FindTlv(aRxInfo.mMessage, timeParameterTlv) == kErrorNone)
     {
         VerifyOrExit(timeParameterTlv.IsValid());
@@ -3291,14 +3286,13 @@
 #if OPENTHREAD_CONFIG_TIME_SYNC_REQUIRED
     else
     {
-        // If the time sync feature is required, don't choose the parent which doesn't support it.
+        // If the time sync feature is required, don't choose the
+        // parent which doesn't support it.
         ExitNow();
     }
-
-#endif // OPENTHREAD_CONFIG_TIME_SYNC_REQUIRED
+#endif
 #endif // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
 
-    // Challenge
     SuccessOrExit(error = aRxInfo.mMessage.ReadChallengeTlv(mParentCandidate.mRxChallenge));
 
     InitNeighbor(mParentCandidate, aRxInfo);
@@ -3343,7 +3337,6 @@
     uint16_t           offset;
     uint16_t           length;
 
-    // Source Address
     SuccessOrExit(error = Tlv::Find<SourceAddressTlv>(aRxInfo.mMessage, sourceAddress));
 
     Log(kMessageReceive, kTypeChildIdResponse, aRxInfo.mMessageInfo.GetPeerAddr(), sourceAddress);
@@ -3352,22 +3345,17 @@
 
     VerifyOrExit(mAttachState == kAttachStateChildIdRequest);
 
-    // ShortAddress
     SuccessOrExit(error = Tlv::Find<Address16Tlv>(aRxInfo.mMessage, shortAddress));
     VerifyOrExit(RouterIdMatch(sourceAddress, shortAddress), error = kErrorRejected);
 
-    // Leader Data
     SuccessOrExit(error = aRxInfo.mMessage.ReadLeaderDataTlv(leaderData));
 
-    // Network Data
     SuccessOrExit(
         error = Tlv::FindTlvValueOffset(aRxInfo.mMessage, Tlv::kNetworkData, networkDataOffset, networkDataLength));
 
-    // Active Timestamp
     switch (Tlv::Find<ActiveTimestampTlv>(aRxInfo.mMessage, timestamp))
     {
     case kErrorNone:
-        // Active Dataset
         if (Tlv::FindTlvValueOffset(aRxInfo.mMessage, Tlv::kActiveDataset, offset, length) == kErrorNone)
         {
             SuccessOrExit(error =
@@ -3382,17 +3370,15 @@
         ExitNow(error = kErrorParse);
     }
 
-    // clear Pending Dataset if device succeed to reattach using stored Pending Dataset
+    // Clear Pending Dataset if device succeed to reattach using stored Pending Dataset
     if (mReattachState == kReattachPending)
     {
         Get<MeshCoP::PendingDatasetManager>().Clear();
     }
 
-    // Pending Timestamp
     switch (Tlv::Find<PendingTimestampTlv>(aRxInfo.mMessage, timestamp))
     {
     case kErrorNone:
-        // Pending Dataset
         if (Tlv::FindTlvValueOffset(aRxInfo.mMessage, Tlv::kPendingDataset, offset, length) == kErrorNone)
         {
             IgnoreError(Get<MeshCoP::PendingDatasetManager>().Save(timestamp, aRxInfo.mMessage, offset, length));
@@ -3408,7 +3394,6 @@
     }
 
 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
-    // Sync to Thread network time
     if (aRxInfo.mMessage.GetTimeSyncSeq() != OT_TIME_SYNC_INVALID_SEQ)
     {
         Get<TimeSync>().HandleTimeSyncMessage(aRxInfo.mMessage);
@@ -3464,12 +3449,10 @@
     TlvList     requestedTlvList;
     TlvList     tlvList;
 
-    // Source Address
     SuccessOrExit(error = Tlv::Find<SourceAddressTlv>(aRxInfo.mMessage, sourceAddress));
 
     Log(kMessageReceive, kTypeChildUpdateRequestAsChild, aRxInfo.mMessageInfo.GetPeerAddr(), sourceAddress);
 
-    // Challenge
     switch (aRxInfo.mMessage.ReadChallengeTlv(challenge))
     {
     case kErrorNone:
@@ -3505,7 +3488,6 @@
             ExitNow();
         }
 
-        // Leader Data, Network Data, Active Timestamp, Pending Timestamp
         SuccessOrExit(error = HandleLeaderData(aRxInfo));
 
 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
@@ -3514,7 +3496,8 @@
 
             if (aRxInfo.mMessage.ReadCslClockAccuracyTlv(cslAccuracy) == kErrorNone)
             {
-                // MUST include CSL timeout TLV when request includes CSL accuracy
+                // MUST include CSL timeout TLV when request includes
+                // CSL accuracy
                 tlvList.Add(Tlv::kCslTimeout);
             }
         }
@@ -3522,11 +3505,10 @@
     }
     else
     {
-        // this device is not a child of the Child Update Request source
+        // This device is not a child of the Child Update Request source
         tlvList.Add(Tlv::kStatus);
     }
 
-    // TLV Request
     switch (aRxInfo.mMessage.ReadTlvRequestTlv(requestedTlvList))
     {
     case kErrorNone:
@@ -3548,7 +3530,8 @@
     }
 #endif
 
-    // Send the response to the requester, regardless if it's this device's parent or not
+    // Send the response to the requester, regardless if it's this
+    // device's parent or not.
     SuccessOrExit(error = SendChildUpdateResponse(tlvList, challenge, aRxInfo.mMessageInfo.GetPeerAddr()));
 
 exit:
@@ -3593,14 +3576,12 @@
         OT_ASSERT(false);
     }
 
-    // Status
     if (Tlv::Find<StatusTlv>(aRxInfo.mMessage, status) == kErrorNone)
     {
         IgnoreError(BecomeDetached());
         ExitNow();
     }
 
-    // Mode
     SuccessOrExit(error = Tlv::Find<ModeTlv>(aRxInfo.mMessage, mode));
     VerifyOrExit(DeviceMode(mode) == mDeviceMode, error = kErrorDrop);
 
@@ -3628,7 +3609,6 @@
         OT_FALL_THROUGH;
 
     case kRoleChild:
-        // Source Address
         SuccessOrExit(error = Tlv::Find<SourceAddressTlv>(aRxInfo.mMessage, sourceAddress));
 
         if (RouterIdFromRloc16(sourceAddress) != RouterIdFromRloc16(GetRloc16()))
@@ -3637,10 +3617,8 @@
             ExitNow();
         }
 
-        // Leader Data, Network Data, Active Timestamp, Pending Timestamp
         SuccessOrExit(error = HandleLeaderData(aRxInfo));
 
-        // Timeout optional
         switch (Tlv::Find<TimeoutTlv>(aRxInfo.mMessage, timeout))
         {
         case kErrorNone:
@@ -3663,7 +3641,6 @@
         {
             Mac::CslAccuracy cslAccuracy;
 
-            // CSL Accuracy
             switch (aRxInfo.mMessage.ReadCslClockAccuracyTlv(cslAccuracy))
             {
             case kErrorNone:
@@ -3812,8 +3789,23 @@
 exit:
     LogProcessError(kTypeLinkMetricsManagementRequest, error);
 }
+#endif
 
-#endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
+#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
+void Mle::HandleTimeSync(RxInfo &aRxInfo)
+{
+    Log(kMessageReceive, kTypeTimeSync, aRxInfo.mMessageInfo.GetPeerAddr());
+
+    VerifyOrExit(aRxInfo.IsNeighborStateValid());
+
+    aRxInfo.mClass = RxInfo::kPeerMessage;
+
+    Get<TimeSync>().HandleTimeSyncMessage(aRxInfo.mMessage);
+
+exit:
+    return;
+}
+#endif
 
 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
 void Mle::HandleLinkMetricsManagementResponse(RxInfo &aRxInfo)
@@ -3832,7 +3824,7 @@
 exit:
     LogProcessError(kTypeLinkMetricsManagementResponse, error);
 }
-#endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
+#endif
 
 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
 void Mle::HandleLinkProbe(RxInfo &aRxInfo)
@@ -3853,7 +3845,7 @@
 exit:
     LogProcessError(kTypeLinkProbe, error);
 }
-#endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
+#endif
 
 void Mle::ProcessAnnounce(void)
 {
@@ -3913,22 +3905,6 @@
 
 bool Mle::IsMeshLocalAddress(const Ip6::Address &aAddress) const { return (aAddress.GetPrefix() == mMeshLocalPrefix); }
 
-Error Mle::CheckReachability(uint16_t aMeshDest, const Ip6::Header &aIp6Header)
-{
-    Error error;
-
-    if ((aMeshDest != GetRloc16()) || Get<ThreadNetif>().HasUnicastAddress(aIp6Header.GetDestination()))
-    {
-        error = kErrorNone;
-    }
-    else
-    {
-        error = kErrorNoRoute;
-    }
-
-    return error;
-}
-
 #if OPENTHREAD_CONFIG_MLE_INFORM_PREVIOUS_PARENT_ON_REATTACH
 void Mle::InformPreviousParent(void)
 {
@@ -3948,13 +3924,8 @@
     LogNote("Sending message to inform previous parent 0x%04x", mPreviousParentRloc);
 
 exit:
-
-    if (error != kErrorNone)
-    {
-        LogWarn("Failed to inform previous parent: %s", ErrorToString(error));
-
-        FreeMessage(message);
-    }
+    LogWarnOnError(error, "inform previous parent");
+    FreeMessageOnError(message, error);
 }
 #endif // OPENTHREAD_CONFIG_MLE_INFORM_PREVIOUS_PARENT_ON_REATTACH
 
@@ -4155,14 +4126,14 @@
         "Link Reject",             // (25) kTypeLinkReject
         "Link Request",            // (26) kTypeLinkRequest
         "Parent Request",          // (27) kTypeParentRequest
-#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
-        "Time Sync", // (28) kTypeTimeSync
-#endif
 #endif
 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
-        "Link Metrics Management Request",  // (29) kTypeLinkMetricsManagementRequest
-        "Link Metrics Management Response", // (30) kTypeLinkMetricsManagementResponse
-        "Link Probe",                       // (31) kTypeLinkProbe
+        "Link Metrics Management Request",  // (28) kTypeLinkMetricsManagementRequest
+        "Link Metrics Management Response", // (29) kTypeLinkMetricsManagementResponse
+        "Link Probe",                       // (30) kTypeLinkProbe
+#endif
+#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
+        "Time Sync", // (31) kTypeTimeSync
 #endif
     };
 
@@ -4195,25 +4166,30 @@
     static_assert(kTypeLinkReject == 25, "kTypeLinkReject value is incorrect");
     static_assert(kTypeLinkRequest == 26, "kTypeLinkRequest value is incorrect");
     static_assert(kTypeParentRequest == 27, "kTypeParentRequest value is incorrect");
-#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
-    static_assert(kTypeTimeSync == 28, "kTypeTimeSync value is incorrect");
-#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
-    static_assert(kTypeLinkMetricsManagementRequest == 29, "kTypeLinkMetricsManagementRequest value is incorrect)");
-    static_assert(kTypeLinkMetricsManagementResponse == 30, "kTypeLinkMetricsManagementResponse value is incorrect)");
-    static_assert(kTypeLinkProbe == 31, "kTypeLinkProbe value is incorrect)");
-#endif
-#else // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
     static_assert(kTypeLinkMetricsManagementRequest == 28, "kTypeLinkMetricsManagementRequest value is incorrect)");
     static_assert(kTypeLinkMetricsManagementResponse == 29, "kTypeLinkMetricsManagementResponse value is incorrect)");
     static_assert(kTypeLinkProbe == 30, "kTypeLinkProbe value is incorrect)");
+#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
+    static_assert(kTypeTimeSync == 31, "kTypeTimeSync value is incorrect");
 #endif
-#endif // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
-#else  // OPENTHREAD_FTD
+#else
+#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
+    static_assert(kTypeTimeSync == 28, "kTypeTimeSync value is incorrect");
+#endif
+#endif
+#else // OPENTHREAD_FTD
 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
     static_assert(kTypeLinkMetricsManagementRequest == 16, "kTypeLinkMetricsManagementRequest value is incorrect)");
     static_assert(kTypeLinkMetricsManagementResponse == 17, "kTypeLinkMetricsManagementResponse value is incorrect)");
     static_assert(kTypeLinkProbe == 18, "kTypeLinkProbe value is incorrect)");
+#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
+    static_assert(kTypeTimeSync == 19, "kTypeTimeSync value is incorrect");
+#endif
+#else
+#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
+    static_assert(kTypeTimeSync == 16, "kTypeTimeSync value is incorrect");
+#endif
 #endif
 #endif // OPENTHREAD_FTD
 
@@ -5094,7 +5070,7 @@
 {
     Instance &instance = GetInstance();
 
-    memset(reinterpret_cast<void *>(this), 0, sizeof(ParentCandidate));
+    ClearAllBytes(*this);
     Init(instance);
 }
 
diff --git a/src/core/thread/mle.hpp b/src/core/thread/mle.hpp
index 8f3e9ea..873e338 100644
--- a/src/core/thread/mle.hpp
+++ b/src/core/thread/mle.hpp
@@ -531,6 +531,14 @@
     const Ip6::Address &GetMeshLocal64(void) const { return mMeshLocal64.GetAddress(); }
 
     /**
+     * Returns a reference to the ML-EID as a `Netif::UnicastAddress`.
+     *
+     * @returns A reference to the ML-EID.
+     *
+     */
+    Ip6::Netif::UnicastAddress &GetMeshLocal64UnicastAddress(void) { return mMeshLocal64; }
+
+    /**
      * Returns the Router ID of the Leader.
      *
      * @returns The Router ID of the Leader.
@@ -965,15 +973,15 @@
         kTypeLinkReject,
         kTypeLinkRequest,
         kTypeParentRequest,
-#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
-        kTypeTimeSync,
-#endif
 #endif
 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
         kTypeLinkMetricsManagementRequest,
         kTypeLinkMetricsManagementResponse,
         kTypeLinkProbe,
 #endif
+#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
+        kTypeTimeSync,
+#endif
     };
 
     //------------------------------------------------------------------------------------------------------------------
@@ -1232,7 +1240,6 @@
     void       SetAttachState(AttachState aState);
     void       InitNeighbor(Neighbor &aNeighbor, const RxInfo &aRxInfo);
     void       ClearParentCandidate(void) { mParentCandidate.Clear(); }
-    Error      CheckReachability(uint16_t aMeshDest, const Ip6::Header &aIp6Header);
     Error      SendDataRequest(const Ip6::Address &aDestination);
     void       HandleNotifierEvents(Events aEvents);
     void       SendDelayedResponse(TxMessage &aMessage, const DelayedResponseMetadata &aMetadata);
@@ -1306,6 +1313,10 @@
     void         UpdateServiceAlocs(void);
 #endif
 
+#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
+    void HandleTimeSync(RxInfo &aRxInfo);
+#endif
+
 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
     void  HandleLinkMetricsManagementRequest(RxInfo &aRxInfo);
     void  HandleLinkProbe(RxInfo &aRxInfo);
diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp
index b97a7ec..b55341f 100644
--- a/src/core/thread/mle_router.cpp
+++ b/src/core/thread/mle_router.cpp
@@ -363,15 +363,22 @@
 
     case kAnyPartition:
     case kBetterParent:
-        // If attach was started due to receiving MLE Announce Messages, all rx-on-when-idle devices would
-        // start attach immediately when receiving such Announce message as in Thread 1.1 specification,
-        // Section 4.8.1,
-        // "If the received value is newer and the channel and/or PAN ID in the Announce message differ
-        //  from those currently in use, the receiving device attempts to attach using the channel and
-        //  PAN ID received from the Announce message."
+
+        // If attach was initiated due to receiving an MLE Announce
+        // message, all rx-on-when-idle devices will immediately
+        // attempt to attach as well. This aligns with the Thread 1.1
+        // specification (Section 4.8.1):
         //
-        // That is, Parent-child relationship is highly unlikely to be kept in the new partition, so here
-        // removes all children, leaving whether to become router according to the new partition status.
+        // "If the received value is newer and the channel and/or PAN
+        //  ID in the Announce message differ from those currently in
+        //  use, the receiving device attempts to attach using the
+        //  channel and PAN ID received from the Announce message."
+        //
+        // Since parent-child relationships are unlikely to persist in
+        // the new partition, we remove all children here. The
+        // decision to become router is determined based on the new
+        // partition's status.
+
         if (IsAnnounceAttach() && HasChildren())
         {
             RemoveChildren();
@@ -442,7 +449,7 @@
         Get<AddressResolver>().Clear();
     }
 
-    // Remove children that do not have matching RLOC16
+    // Remove children that do not have a matching RLOC16
     for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValidOrRestoring))
     {
         if (RouterIdFromRloc16(child.GetRloc16()) != mRouterId)
@@ -521,17 +528,19 @@
     Ip6::Address destination;
     TxMessage   *message = nullptr;
 
-    // Suppress MLE Advertisements when trying to attach to a better partition.
-    //
-    // Without this suppression, a device may send an MLE Advertisement before receiving the MLE Child ID Response.
-    // The candidate parent then removes the attaching device because the Source Address TLV includes an RLOC16 that
-    // indicates a Router role (i.e. a Child ID equal to zero).
+    // Suppress MLE Advertisements when trying to attach to a better
+    // partition. Without this, a candidate parent might incorrectly
+    // interpret this advertisement (Source Address TLV containing an
+    // RLOC16 indicating device is acting as router) and reject the
+    // attaching device.
+
     VerifyOrExit(!IsAttaching());
 
-    // Suppress MLE Advertisements when transitioning to the router role.
-    //
-    // When trying to attach to a new partition, sending out advertisements as a REED can cause already-attached
-    // children to detach.
+    // Suppress MLE Advertisements when attempting to transition to
+    // router role. Advertisements as a REED while attaching to a new
+    // partition can cause existing children to detach
+    // unnecessarily.
+
     VerifyOrExit(!mAddressSolicitPending);
 
     VerifyOrExit((message = NewMleMessage(kCommandAdvertisement)) != nullptr, error = kErrorNoBufs);
@@ -665,14 +674,11 @@
 
     VerifyOrExit(!IsAttaching(), error = kErrorInvalidState);
 
-    // Challenge
     SuccessOrExit(error = aRxInfo.mMessage.ReadChallengeTlv(challenge));
 
-    // Version
     SuccessOrExit(error = Tlv::Find<VersionTlv>(aRxInfo.mMessage, version));
     VerifyOrExit(version >= kThreadVersion1p1, error = kErrorParse);
 
-    // Leader Data
     switch (aRxInfo.mMessage.ReadLeaderDataTlv(leaderData))
     {
     case kErrorNone:
@@ -684,7 +690,6 @@
         ExitNow(error = kErrorParse);
     }
 
-    // Source Address
     switch (Tlv::Find<SourceAddressTlv>(aRxInfo.mMessage, sourceAddress))
     {
     case kErrorNone:
@@ -711,7 +716,8 @@
         break;
 
     case kErrorNotFound:
-        // lack of source address indicates router coming out of reset
+        // A missing source address indicates that the router was
+        // recently reset.
         VerifyOrExit(aRxInfo.IsNeighborStateValid() && IsActiveRouter(aRxInfo.mNeighbor->GetRloc16()),
                      error = kErrorDrop);
         neighbor = aRxInfo.mNeighbor;
@@ -721,7 +727,6 @@
         ExitNow(error = kErrorParse);
     }
 
-    // TLV Request
     switch (aRxInfo.mMessage.ReadTlvRequestTlv(requestedTlvList))
     {
     case kErrorNone:
@@ -748,16 +753,16 @@
     aRxInfo.mClass = RxInfo::kPeerMessage;
     ProcessKeySequence(aRxInfo);
 
-    SuccessOrExit(error = SendLinkAccept(aRxInfo.mMessageInfo, neighbor, requestedTlvList, challenge));
+    SuccessOrExit(error = SendLinkAccept(aRxInfo, neighbor, requestedTlvList, challenge));
 
 exit:
     LogProcessError(kTypeLinkRequest, error);
 }
 
-Error MleRouter::SendLinkAccept(const Ip6::MessageInfo &aMessageInfo,
-                                Neighbor               *aNeighbor,
-                                const TlvList          &aRequestedTlvList,
-                                const RxChallenge      &aChallenge)
+Error MleRouter::SendLinkAccept(const RxInfo      &aRxInfo,
+                                Neighbor          *aNeighbor,
+                                const TlvList     &aRequestedTlvList,
+                                const RxChallenge &aChallenge)
 {
     static const uint8_t kRouterTlvs[] = {Tlv::kLinkMargin};
 
@@ -775,9 +780,7 @@
     SuccessOrExit(error = message->AppendLinkFrameCounterTlv());
     SuccessOrExit(error = message->AppendMleFrameCounterTlv());
 
-    // always append a link margin, regardless of whether or not it was requested
-    linkMargin = Get<Mac::Mac>().ComputeLinkMargin(aMessageInfo.GetThreadLinkInfo()->GetRss());
-
+    linkMargin = Get<Mac::Mac>().ComputeLinkMargin(aRxInfo.mMessage.GetAverageRss());
     SuccessOrExit(error = message->AppendLinkMarginTlv(linkMargin));
 
     if (aNeighbor != nullptr && IsActiveRouter(aNeighbor->GetRloc16()))
@@ -823,20 +826,20 @@
     }
 #endif
 
-    if (aMessageInfo.GetSockAddr().IsMulticast())
+    if (aRxInfo.mMessageInfo.GetSockAddr().IsMulticast())
     {
-        SuccessOrExit(error = message->SendAfterDelay(aMessageInfo.GetPeerAddr(),
+        SuccessOrExit(error = message->SendAfterDelay(aRxInfo.mMessageInfo.GetPeerAddr(),
                                                       1 + Random::NonCrypto::GetUint16InRange(0, kMaxLinkAcceptDelay)));
 
         Log(kMessageDelay, (command == kCommandLinkAccept) ? kTypeLinkAccept : kTypeLinkAcceptAndRequest,
-            aMessageInfo.GetPeerAddr());
+            aRxInfo.mMessageInfo.GetPeerAddr());
     }
     else
     {
-        SuccessOrExit(error = message->SendTo(aMessageInfo.GetPeerAddr()));
+        SuccessOrExit(error = message->SendTo(aRxInfo.mMessageInfo.GetPeerAddr()));
 
         Log(kMessageSend, (command == kCommandLinkAccept) ? kTypeLinkAccept : kTypeLinkAcceptAndRequest,
-            aMessageInfo.GetPeerAddr());
+            aRxInfo.mMessageInfo.GetPeerAddr());
     }
 
 exit:
@@ -874,7 +877,6 @@
     LeaderData      leaderData;
     uint8_t         linkMargin;
 
-    // Source Address
     SuccessOrExit(error = Tlv::Find<SourceAddressTlv>(aRxInfo.mMessage, sourceAddress));
 
     Log(kMessageReceive, aRequest ? kTypeLinkAcceptAndRequest : kTypeLinkAccept, aRxInfo.mMessageInfo.GetPeerAddr(),
@@ -886,10 +888,8 @@
     router        = mRouterTable.FindRouterById(routerId);
     neighborState = (router != nullptr) ? router->GetState() : Neighbor::kStateInvalid;
 
-    // Response
     SuccessOrExit(error = aRxInfo.mMessage.ReadResponseTlv(response));
 
-    // verify response
     switch (neighborState)
     {
     case Neighbor::kStateLinkRequest:
@@ -915,22 +915,20 @@
         RemoveNeighbor(*aRxInfo.mNeighbor);
     }
 
-    // Version
     SuccessOrExit(error = Tlv::Find<VersionTlv>(aRxInfo.mMessage, version));
     VerifyOrExit(version >= kThreadVersion1p1, error = kErrorParse);
 
-    // Link and MLE Frame Counters
     SuccessOrExit(error = aRxInfo.mMessage.ReadFrameCounterTlvs(linkFrameCounter, mleFrameCounter));
 
-    // Link Margin
     switch (Tlv::Find<LinkMarginTlv>(aRxInfo.mMessage, linkMargin))
     {
     case kErrorNone:
         break;
     case kErrorNotFound:
-        // Link Margin TLV may be skipped in Router Synchronization process after Reset
+        // The Link Margin TLV may be omitted after a reset. We wait
+        // for MLE Advertisements to establish the routing cost to
+        // the neighbor
         VerifyOrExit(IsDetached(), error = kErrorNotFound);
-        // Wait for an MLE Advertisement to establish a routing cost to the neighbor
         linkMargin = 0;
         break;
     default:
@@ -940,15 +938,12 @@
     switch (mRole)
     {
     case kRoleDetached:
-        // Address16
         SuccessOrExit(error = Tlv::Find<Address16Tlv>(aRxInfo.mMessage, address16));
         VerifyOrExit(GetRloc16() == address16, error = kErrorDrop);
 
-        // Leader Data
         SuccessOrExit(error = aRxInfo.mMessage.ReadLeaderDataTlv(leaderData));
         SetLeaderData(leaderData.GetPartitionId(), leaderData.GetWeighting(), leaderData.GetLeaderRouterId());
 
-        // Route
         mRouterTable.Clear();
         SuccessOrExit(error = aRxInfo.mMessage.ReadRouteTlv(routeTlv));
         SuccessOrExit(error = ProcessRouteTlv(routeTlv, aRxInfo));
@@ -964,7 +959,7 @@
             SetStateRouter(GetRloc16());
         }
 
-        mLinkRequestAttempts    = 0; // completed router sync after reset, no more link request to retransmit
+        mLinkRequestAttempts    = 0;
         mRetrieveNewNetworkData = true;
         IgnoreError(SendDataRequest(aRxInfo.mMessageInfo.GetPeerAddr()));
 
@@ -981,7 +976,6 @@
     case kRoleLeader:
         VerifyOrExit(router != nullptr);
 
-        // Leader Data
         SuccessOrExit(error = aRxInfo.mMessage.ReadLeaderDataTlv(leaderData));
         VerifyOrExit(leaderData.GetPartitionId() == mLeaderData.GetPartitionId());
 
@@ -992,7 +986,6 @@
             IgnoreError(SendDataRequest(aRxInfo.mMessageInfo.GetPeerAddr()));
         }
 
-        // Route (optional)
         switch (aRxInfo.mMessage.ReadRouteTlv(routeTlv))
         {
         case kErrorNone:
@@ -1026,7 +1019,6 @@
         OT_ASSERT(false);
     }
 
-    // finish link synchronization
     InitNeighbor(*router, aRxInfo);
     router->SetRloc16(sourceAddress);
     router->GetLinkFrameCounters().SetAll(linkFrameCounter);
@@ -1049,10 +1041,8 @@
         RxChallenge challenge;
         TlvList     requestedTlvList;
 
-        // Challenge
         SuccessOrExit(error = aRxInfo.mMessage.ReadChallengeTlv(challenge));
 
-        // TLV Request
         switch (aRxInfo.mMessage.ReadTlvRequestTlv(requestedTlvList))
         {
         case kErrorNone:
@@ -1062,7 +1052,7 @@
             ExitNow(error = kErrorParse);
         }
 
-        SuccessOrExit(error = SendLinkAccept(aRxInfo.mMessageInfo, router, requestedTlvList, challenge));
+        SuccessOrExit(error = SendLinkAccept(aRxInfo, router, requestedTlvList, challenge));
     }
 
 exit:
@@ -1178,7 +1168,7 @@
     // - `aLeaderData` is the read value from `LeaderDataTlv`.
 
     Error    error      = kErrorNone;
-    uint8_t  linkMargin = Get<Mac::Mac>().ComputeLinkMargin(aRxInfo.mMessageInfo.GetThreadLinkInfo()->GetRss());
+    uint8_t  linkMargin = Get<Mac::Mac>().ComputeLinkMargin(aRxInfo.mMessage.GetAverageRss());
     RouteTlv routeTlv;
     Router  *router;
     uint8_t  routerId;
@@ -1218,7 +1208,7 @@
 
         if (ComparePartitions(routeTlv.IsSingleton(), aLeaderData, IsSingleton(), mLeaderData) > 0
 #if OPENTHREAD_CONFIG_TIME_SYNC_REQUIRED
-            // if time sync is required, it will only migrate to a better network which also enables time sync.
+            // Allow a better partition if it also enables time sync.
             && aRxInfo.mMessage.GetTimeSyncSeq() != OT_TIME_SYNC_INVALID_SEQ
 #endif
         )
@@ -1333,7 +1323,9 @@
         Get<AddressResolver>().ReplaceEntriesForRloc16(aRxInfo.mNeighbor->GetRloc16(), router->GetRloc16());
     }
 
-    // Send unicast link request if no link to router and no unicast/multicast link request in progress
+    // Send unicast link request if no link to router and no
+    // unicast/multicast link request in progress
+
     if (!router->IsStateValid() && !router->IsStateLinkRequest() && (mChallengeTimeout == 0) &&
         (linkMargin >= kLinkRequestMinMargin))
     {
@@ -1350,7 +1342,6 @@
 exit:
     if (aRxInfo.mNeighbor && aRxInfo.mNeighbor->GetRloc16() != aSourceAddress)
     {
-        // Remove stale neighbors
         RemoveNeighbor(*aRxInfo.mNeighbor);
     }
 
@@ -1395,11 +1386,9 @@
 
     aRxInfo.mMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(extAddr);
 
-    // Version
     SuccessOrExit(error = Tlv::Find<VersionTlv>(aRxInfo.mMessage, version));
     VerifyOrExit(version >= kThreadVersion1p1, error = kErrorParse);
 
-    // Scan Mask
     SuccessOrExit(error = Tlv::Find<ScanMaskTlv>(aRxInfo.mMessage, scanMask));
 
     switch (mRole)
@@ -1419,7 +1408,6 @@
         break;
     }
 
-    // Challenge
     SuccessOrExit(error = aRxInfo.mMessage.ReadChallengeTlv(challenge));
 
     child = mChildTable.FindChild(extAddr, Child::kInStateAnyExceptInvalid);
@@ -1428,7 +1416,6 @@
     {
         VerifyOrExit((child = mChildTable.GetNewChild()) != nullptr, error = kErrorNoBufs);
 
-        // MAC Address
         InitNeighbor(*child, aRxInfo);
         child->SetState(Neighbor::kStateParentRequest);
 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
@@ -1510,6 +1497,9 @@
         mPreviousPartitionIdTimeout--;
     }
 
+    //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+    // Role transitions
+
     roleTransitionTimeoutExpired = mRouterRoleTransition.HandleTimeTick();
 
     switch (mRole)
@@ -1532,7 +1522,6 @@
             }
             else
             {
-                // send announce after decided to stay in REED if needed
                 InformPreviousChannel();
             }
 
@@ -1549,11 +1538,11 @@
         OT_FALL_THROUGH;
 
     case kRoleRouter:
-        LogDebg("network id timeout = %lu", ToUlong(mRouterTable.GetLeaderAge()));
+        LogDebg("Leader age %lu", ToUlong(mRouterTable.GetLeaderAge()));
 
         if ((mRouterTable.GetActiveRouterCount() > 0) && (mRouterTable.GetLeaderAge() >= mNetworkIdTimeout))
         {
-            LogInfo("Router ID Sequence timeout");
+            LogInfo("Leader age timeout");
             Attach(kSamePartition);
         }
 
@@ -1578,7 +1567,9 @@
         OT_ASSERT(false);
     }
 
-    // update children state
+    //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+    // Update `ChildTable`
+
     for (Child &child : Get<ChildTable>().Iterate(Child::kInStateAnyExceptInvalid))
     {
         uint32_t timeout = 0;
@@ -1605,7 +1596,7 @@
         if (child.IsCslSynchronized() &&
             TimerMilli::GetNow() - child.GetCslLastHeard() >= Time::SecToMsec(child.GetCslTimeout()))
         {
-            LogInfo("Child CSL synchronization expired");
+            LogInfo("Child 0x%04x CSL synchronization expired", child.GetRloc16());
             child.SetCslSynchronized(false);
             Get<CslTxScheduler>().Update();
         }
@@ -1613,7 +1604,7 @@
 
         if (TimerMilli::GetNow() - child.GetLastHeard() >= timeout)
         {
-            LogInfo("Child timeout expired");
+            LogInfo("Child 0x%04x timeout expired", child.GetRloc16());
             RemoveNeighbor(child);
         }
         else if (IsRouterOrLeader() && child.IsStateRestored())
@@ -1622,7 +1613,9 @@
         }
     }
 
-    // update router state
+    //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+    // Update `RouterTable`
+
     for (Router &router : Get<RouterTable>())
     {
         uint32_t age;
@@ -1635,53 +1628,35 @@
 
         age = TimerMilli::GetNow() - router.GetLastHeard();
 
-        if (router.IsStateValid())
+        if (router.IsStateValid() && (age >= kMaxNeighborAge))
         {
-#if OPENTHREAD_CONFIG_MLE_SEND_LINK_REQUEST_ON_ADV_TIMEOUT == 0
-
-            if (age >= kMaxNeighborAge)
+#if OPENTHREAD_CONFIG_MLE_SEND_LINK_REQUEST_ON_ADV_TIMEOUT
+            if (age < kMaxNeighborAge + kMaxTxCount * kUnicastRetxDelay)
             {
-                LogInfo("Router timeout expired");
-                RemoveNeighbor(router);
-                continue;
+                LogInfo("No Adv from router 0x%04x - sending Link Request", router.GetRloc16());
+                IgnoreError(SendLinkRequest(&router));
             }
-
-#else
-
-            if (age >= kMaxNeighborAge)
-            {
-                if (age < kMaxNeighborAge + kMaxTxCount * kUnicastRetxDelay)
-                {
-                    LogInfo("Router timeout expired");
-                    IgnoreError(SendLinkRequest(&router));
-                }
-                else
-                {
-                    RemoveNeighbor(router);
-                    continue;
-                }
-            }
-
+            else
 #endif
-        }
-        else if (router.IsStateLinkRequest())
-        {
-            if (age >= kLinkRequestTimeout)
             {
-                LogInfo("Link Request timeout expired");
+                LogInfo("Router 0x%04x timeout expired", router.GetRloc16());
                 RemoveNeighbor(router);
                 continue;
             }
         }
 
-        if (IsLeader())
+        if (router.IsStateLinkRequest() && (age >= kLinkRequestTimeout))
         {
-            if (mRouterTable.FindNextHopOf(router) == nullptr && mRouterTable.GetLinkCost(router) >= kMaxRouteCost &&
-                age >= kMaxLeaderToRouterTimeout)
-            {
-                LogInfo("Router ID timeout expired (no route)");
-                IgnoreError(mRouterTable.Release(router.GetRouterId()));
-            }
+            LogInfo("Router 0x%04x - Link Request timeout expired", router.GetRloc16());
+            RemoveNeighbor(router);
+            continue;
+        }
+
+        if (IsLeader() && (mRouterTable.FindNextHopOf(router) == nullptr) &&
+            (mRouterTable.GetLinkCost(router) >= kMaxRouteCost) && (age >= kMaxLeaderToRouterTimeout))
+        {
+            LogInfo("Router 0x%04x ID timeout expired (no route)", router.GetRloc16());
+            IgnoreError(mRouterTable.Release(router.GetRouterId()));
         }
     }
 
@@ -1727,7 +1702,6 @@
         SuccessOrExit(error = message->AppendCslClockAccuracyTlv());
     }
 #endif
-
     aChild->GenerateChallenge();
     SuccessOrExit(error = message->AppendChallengeTlv(aChild->GetChallenge()));
     SuccessOrExit(error = message->AppendLinkMarginTlv(aChild->GetLinkInfo().GetLinkMargin()));
@@ -1808,7 +1782,6 @@
 #endif
 
 #if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
-    // Retrieve registered multicast addresses of the Child
     if (aChild.HasAnyMlrRegisteredAddress())
     {
         OT_ASSERT(aChild.IsStateValid());
@@ -1872,7 +1845,8 @@
 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
         if (mMaxChildIpAddresses > 0 && storedCount >= mMaxChildIpAddresses)
         {
-            // Skip remaining address registration entries but keep logging skipped addresses.
+            // Skip remaining address registration entries but keep logging
+            // skipped addresses.
             error = kErrorNoBufs;
         }
         else
@@ -1918,7 +1892,7 @@
             }
             else
             {
-                // if not able to store DUA, then assume child does not have one
+                // It cannot store DUA, then assume child does not have one.
                 hasNewDua = false;
             }
         }
@@ -2000,43 +1974,33 @@
 
     VerifyOrExit(IsRouterEligible(), error = kErrorInvalidState);
 
-    // only process message when operating as a child, router, or leader
     VerifyOrExit(IsAttached(), error = kErrorInvalidState);
 
-    // Find Child
     aRxInfo.mMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(extAddr);
 
     child = mChildTable.FindChild(extAddr, Child::kInStateAnyExceptInvalid);
     VerifyOrExit(child != nullptr, error = kErrorAlready);
 
-    // Version
     SuccessOrExit(error = Tlv::Find<VersionTlv>(aRxInfo.mMessage, version));
     VerifyOrExit(version >= kThreadVersion1p1, error = kErrorParse);
 
-    // Response
     SuccessOrExit(error = aRxInfo.mMessage.ReadResponseTlv(response));
     VerifyOrExit(response == child->GetChallenge(), error = kErrorSecurity);
 
-    // Remove existing MLE messages
     Get<MeshForwarder>().RemoveMessages(*child, Message::kSubTypeMleGeneral);
     Get<MeshForwarder>().RemoveMessages(*child, Message::kSubTypeMleChildIdRequest);
     Get<MeshForwarder>().RemoveMessages(*child, Message::kSubTypeMleChildUpdateRequest);
     Get<MeshForwarder>().RemoveMessages(*child, Message::kSubTypeMleDataResponse);
 
-    // Link-Layer and MLE Frame Counters
     SuccessOrExit(error = aRxInfo.mMessage.ReadFrameCounterTlvs(linkFrameCounter, mleFrameCounter));
 
-    // Mode
     SuccessOrExit(error = Tlv::Find<ModeTlv>(aRxInfo.mMessage, modeBitmask));
     mode.Set(modeBitmask);
 
-    // Timeout
     SuccessOrExit(error = Tlv::Find<TimeoutTlv>(aRxInfo.mMessage, timeout));
 
-    // Requested TLVs
     SuccessOrExit(error = aRxInfo.mMessage.ReadTlvRequestTlv(tlvList));
 
-    // Supervision interval
     switch (Tlv::Find<SupervisionIntervalTlv>(aRxInfo.mMessage, supervisionInterval))
     {
     case kErrorNone:
@@ -2049,7 +2013,6 @@
         ExitNow(error = kErrorParse);
     }
 
-    // Active Timestamp
     switch (Tlv::Find<ActiveTimestampTlv>(aRxInfo.mMessage, timestamp))
     {
     case kErrorNone:
@@ -2068,7 +2031,6 @@
         ExitNow(error = kErrorParse);
     }
 
-    // Pending Timestamp
     switch (Tlv::Find<PendingTimestampTlv>(aRxInfo.mMessage, timestamp))
     {
     case kErrorNone:
@@ -2094,12 +2056,10 @@
         SuccessOrExit(error = ProcessAddressRegistrationTlv(aRxInfo, *child));
     }
 
-    // Remove from router table
     router = mRouterTable.FindRouter(extAddr);
 
     if (router != nullptr)
     {
-        // The `router` here can be invalid
         RemoveNeighbor(*router);
     }
 
@@ -2119,7 +2079,7 @@
     child->SetKeySequence(aRxInfo.mKeySequence);
     child->SetDeviceMode(mode);
     child->SetVersion(version);
-    child->GetLinkInfo().AddRss(aRxInfo.mMessageInfo.GetThreadLinkInfo()->GetRss());
+    child->GetLinkInfo().AddRss(aRxInfo.mMessage.GetAverageRss());
     child->SetTimeout(timeout);
     child->SetSupervisionInterval(supervisionInterval);
 #if OPENTHREAD_CONFIG_MULTI_RADIO
@@ -2180,11 +2140,9 @@
 
     Log(kMessageReceive, kTypeChildUpdateRequestOfChild, aRxInfo.mMessageInfo.GetPeerAddr());
 
-    // Mode
     SuccessOrExit(error = Tlv::Find<ModeTlv>(aRxInfo.mMessage, modeBitmask));
     mode.Set(modeBitmask);
 
-    // Challenge
     switch (aRxInfo.mMessage.ReadChallengeTlv(challenge))
     {
     case kErrorNone:
@@ -2237,7 +2195,6 @@
         tlvList.Add(Tlv::kLinkFrameCounter);
     }
 
-    // IPv6 Address TLV
     switch (ProcessAddressRegistrationTlv(aRxInfo, *child))
     {
     case kErrorNone:
@@ -2249,7 +2206,6 @@
         ExitNow(error = kErrorParse);
     }
 
-    // Leader Data
     switch (aRxInfo.mMessage.ReadLeaderDataTlv(leaderData))
     {
     case kErrorNone:
@@ -2261,7 +2217,6 @@
         ExitNow(error = kErrorParse);
     }
 
-    // Timeout
     switch (Tlv::Find<TimeoutTlv>(aRxInfo.mMessage, timeout))
     {
     case kErrorNone:
@@ -2281,7 +2236,6 @@
         ExitNow(error = kErrorParse);
     }
 
-    // Supervision interval
     switch (Tlv::Find<SupervisionIntervalTlv>(aRxInfo.mMessage, supervisionInterval))
     {
     case kErrorNone:
@@ -2299,7 +2253,6 @@
 
     child->SetSupervisionInterval(supervisionInterval);
 
-    // TLV Request
     switch (aRxInfo.mMessage.ReadTlvRequestTlv(requestedTlvList))
     {
     case kErrorNone:
@@ -2409,7 +2362,6 @@
 
     child = static_cast<Child *>(aRxInfo.mNeighbor);
 
-    // Response
     switch (aRxInfo.mMessage.ReadResponseTlv(response))
     {
     case kErrorNone:
@@ -2425,7 +2377,6 @@
 
     Log(kMessageReceive, kTypeChildUpdateResponseOfChild, aRxInfo.mMessageInfo.GetPeerAddr(), child->GetRloc16());
 
-    // Source Address
     switch (Tlv::Find<SourceAddressTlv>(aRxInfo.mMessage, sourceAddress))
     {
     case kErrorNone:
@@ -2444,7 +2395,6 @@
         ExitNow(error = kErrorParse);
     }
 
-    // Status
     switch (Tlv::Find<StatusTlv>(aRxInfo.mMessage, status))
     {
     case kErrorNone:
@@ -2456,8 +2406,6 @@
         ExitNow(error = kErrorParse);
     }
 
-    // Link-Layer Frame Counter
-
     switch (Tlv::Find<LinkFrameCounterTlv>(aRxInfo.mMessage, linkFrameCounter))
     {
     case kErrorNone:
@@ -2470,7 +2418,6 @@
         ExitNow(error = kErrorParse);
     }
 
-    // MLE Frame Counter
     switch (Tlv::Find<MleFrameCounterTlv>(aRxInfo.mMessage, mleFrameCounter))
     {
     case kErrorNone:
@@ -2482,7 +2429,6 @@
         ExitNow(error = kErrorNone);
     }
 
-    // Timeout
     switch (Tlv::Find<TimeoutTlv>(aRxInfo.mMessage, timeout))
     {
     case kErrorNone:
@@ -2509,7 +2455,6 @@
         }
     }
 
-    // IPv6 Address
     switch (ProcessAddressRegistrationTlv(aRxInfo, *child))
     {
     case kErrorNone:
@@ -2519,7 +2464,6 @@
         ExitNow(error = kErrorParse);
     }
 
-    // Leader Data
     switch (aRxInfo.mMessage.ReadLeaderDataTlv(leaderData))
     {
     case kErrorNone:
@@ -2534,7 +2478,7 @@
     SetChildStateToValid(*child);
     child->SetLastHeard(TimerMilli::GetNow());
     child->SetKeySequence(aRxInfo.mKeySequence);
-    child->GetLinkInfo().AddRss(aRxInfo.mMessageInfo.GetThreadLinkInfo()->GetRss());
+    child->GetLinkInfo().AddRss(aRxInfo.mMessage.GetAverageRss());
 
     aRxInfo.mClass = response.IsEmpty() ? RxInfo::kPeerMessage : RxInfo::kAuthoritativeMessage;
 
@@ -2552,10 +2496,8 @@
 
     VerifyOrExit(aRxInfo.IsNeighborStateValid(), error = kErrorSecurity);
 
-    // TLV Request
     SuccessOrExit(error = aRxInfo.mMessage.ReadTlvRequestTlv(tlvList));
 
-    // Active Timestamp
     switch (Tlv::Find<ActiveTimestampTlv>(aRxInfo.mMessage, timestamp))
     {
     case kErrorNone:
@@ -2574,7 +2516,6 @@
         ExitNow(error = kErrorParse);
     }
 
-    // Pending Timestamp
     switch (Tlv::Find<PendingTimestampTlv>(aRxInfo.mMessage, timestamp))
     {
     case kErrorNone:
@@ -2686,7 +2627,6 @@
 
     discoveryRequestTlv.SetLength(0);
 
-    // only Routers and REEDs respond
     VerifyOrExit(IsRouterEligible(), error = kErrorInvalidState);
 
     SuccessOrExit(error = Tlv::FindTlvValueStartEndOffsets(aRxInfo.mMessage, Tlv::kDiscovery, offset, end));
@@ -2768,13 +2708,11 @@
     message->SetRadioType(aDiscoverRequestMessage.GetRadioType());
 #endif
 
-    // Discovery TLV
     tlv.SetType(Tlv::kDiscovery);
     SuccessOrExit(error = message->Append(tlv));
 
     startOffset = message->GetLength();
 
-    // Discovery Response TLV
     discoveryResponseTlv.Init();
     discoveryResponseTlv.SetVersion(kThreadVersion);
 
@@ -2799,11 +2737,9 @@
 
     SuccessOrExit(error = discoveryResponseTlv.AppendTo(*message));
 
-    // Extended PAN ID TLV
     SuccessOrExit(
         error = Tlv::Append<MeshCoP::ExtendedPanIdTlv>(*message, Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId()));
 
-    // Network Name TLV
     networkNameTlv.Init();
     networkNameTlv.SetNetworkName(Get<MeshCoP::NetworkNameManager>().GetNetworkName().GetAsData());
     SuccessOrExit(error = networkNameTlv.AppendTo(*message));
@@ -2844,7 +2780,7 @@
     {
         uint16_t rloc16;
 
-        // pick next Child ID that is not being used
+        // Pick next Child ID that is not being used
         do
         {
             mNextChildId++;
@@ -2858,7 +2794,6 @@
 
         } while (mChildTable.FindChild(rloc16, Child::kInStateAnyExceptInvalid) != nullptr);
 
-        // allocate Child ID
         aChild.SetRloc16(rloc16);
     }
 
@@ -2938,14 +2873,16 @@
         {
             if (msg.GetChildMask(childIndex) && msg.GetSubType() == Message::kSubTypeMleChildUpdateRequest)
             {
-                // No need to send the resync "Child Update Request" to the sleepy child
-                // if there is one already queued.
+                // No need to send the resync "Child Update Request"
+                // to the sleepy child if there is one already
+                // queued.
                 if (aChild.IsStateRestoring())
                 {
                     ExitNow();
                 }
 
-                // Remove queued outdated "Child Update Request" when there is newer Network Data is to send.
+                // Remove queued outdated "Child Update Request" when
+                // there is newer Network Data is to send.
                 Get<MeshForwarder>().RemoveMessages(aChild, Message::kSubTypeMleChildUpdateRequest);
                 break;
             }
@@ -2962,7 +2899,24 @@
     if (!aChild.IsStateValid())
     {
         SuccessOrExit(error = message->AppendTlvRequestTlv(kTlvs));
-        aChild.GenerateChallenge();
+
+        if (!aChild.IsStateRestored())
+        {
+            // A random challenge is generated and saved when `aChild`
+            // is first initialized in `kStateRestored`. We will use
+            // the saved challenge here. This prevents overwriting
+            // the saved challenge when the child is also detached
+            // and happens to send a "Parent Request" in the window
+            // where the parent transitions to the router/leader role
+            // and before the parent sends the "Child Update Request".
+            // This ensures that the same random challenge is
+            // included in both "Parent Response" and "Child Update
+            // Response," guaranteeing proper acceptance of the
+            // child's "Child ID request".
+
+            aChild.GenerateChallenge();
+        }
+
         SuccessOrExit(error = message->AppendChallengeTlv(aChild.GetChallenge()));
     }
 
@@ -3139,10 +3093,8 @@
 
     if (aDelay)
     {
-        // Remove MLE Data Responses from Send Message Queue.
         Get<MeshForwarder>().RemoveDataResponseMessages();
 
-        // Remove multicast MLE Data Response from Delayed Message Queue.
         RemoveDelayedDataResponseMessage();
 
         SuccessOrExit(error = message->SendAfterDelay(aDestination, aDelay));
@@ -3224,7 +3176,6 @@
 
         if (aNeighbor.IsFullThreadDevice())
         {
-            // Clear all EID-to-RLOC entries associated with the child.
             Get<AddressResolver>().RemoveEntriesForRloc16(aNeighbor.GetRloc16());
         }
 
@@ -3275,11 +3226,9 @@
         ExitNow();
     }
 
-    // loop exists
     router = mRouterTable.FindRouterByRloc16(aDestRloc16);
     VerifyOrExit(router != nullptr);
 
-    // invalidate next hop
     router->SetNextHopToInvalid();
     ResetAdvertiseInterval();
 
@@ -3289,46 +3238,39 @@
 
 Error MleRouter::CheckReachability(uint16_t aMeshDest, const Ip6::Header &aIp6Header)
 {
-    Error error = kErrorNone;
+    bool isReachable = false;
 
     if (IsChild())
     {
-        error = Mle::CheckReachability(aMeshDest, aIp6Header);
+        if (aMeshDest == GetRloc16())
+        {
+            isReachable = Get<ThreadNetif>().HasUnicastAddress(aIp6Header.GetDestination());
+        }
+        else
+        {
+            isReachable = true;
+        }
+
         ExitNow();
     }
 
-    if (aMeshDest == Get<Mac::Mac>().GetShortAddress())
+    if (aMeshDest == GetRloc16())
     {
-        // mesh destination is this device
-        if (Get<ThreadNetif>().HasUnicastAddress(aIp6Header.GetDestination()))
-        {
-            // IPv6 destination is this device
-            ExitNow();
-        }
-        else if (mNeighborTable.FindNeighbor(aIp6Header.GetDestination()) != nullptr)
-        {
-            // IPv6 destination is an RFD child
-            ExitNow();
-        }
-    }
-    else if (RouterIdFromRloc16(aMeshDest) == mRouterId)
-    {
-        // mesh destination is a child of this device
-        if (mChildTable.FindChild(aMeshDest, Child::kInStateValidOrRestoring))
-        {
-            ExitNow();
-        }
-    }
-    else if (GetNextHop(aMeshDest) != Mac::kShortAddrInvalid)
-    {
-        // forwarding to another router and route is known
+        isReachable = Get<ThreadNetif>().HasUnicastAddress(aIp6Header.GetDestination()) ||
+                      (mNeighborTable.FindNeighbor(aIp6Header.GetDestination()) != nullptr);
         ExitNow();
     }
 
-    error = kErrorNoRoute;
+    if (RouterIdFromRloc16(aMeshDest) == mRouterId)
+    {
+        isReachable = (mChildTable.FindChild(aMeshDest, Child::kInStateValidOrRestoring) != nullptr);
+        ExitNow();
+    }
+
+    isReachable = (GetNextHop(aMeshDest) != Mac::kShortAddrInvalid);
 
 exit:
-    return error;
+    return isReachable ? kErrorNone : kErrorNoRoute;
 }
 
 Error MleRouter::SendAddressSolicit(ThreadStatusTlv::Status aStatus)
@@ -3442,7 +3384,6 @@
     SuccessOrExit(Tlv::FindTlv(*aMessage, routerMaskTlv));
     VerifyOrExit(routerMaskTlv.IsValid());
 
-    // assign short address
     SetRouterId(routerId);
 
     SetStateRouter(Rloc16FromRouterId(mRouterId));
@@ -3505,7 +3446,6 @@
     }
 
 exit:
-    // Send announce after received address solicit reply if needed
     InformPreviousChannel();
 }
 
@@ -3566,7 +3506,6 @@
     }
 #endif
 
-    // Check if allocation already exists
     router = mRouterTable.FindRouter(extAddress);
 
     if (router != nullptr)
@@ -3658,7 +3597,10 @@
 
     // If assigning a new RLOC16 (e.g., on promotion of a child to
     // router role) we clear any address cache entries associated
-    // with the old RLOC16.
+    // with the old RLOC16 unless the sender is a direct child. For
+    // direct children, we retain the cache entries to allow
+    // association with the promoted router's new RLOC16 upon
+    // receiving its Link Advertisement.
 
     if ((aResponseStatus == ThreadStatusTlv::kSuccess) && (aRouter != nullptr))
     {
@@ -3668,6 +3610,7 @@
         oldRloc16 = aMessageInfo.GetPeerAddr().GetIid().GetLocator();
 
         VerifyOrExit(oldRloc16 != aRouter->GetRloc16());
+        VerifyOrExit(!RouterIdMatch(oldRloc16, GetRloc16()));
         Get<AddressResolver>().RemoveEntriesForRloc16(oldRloc16);
     }
 
@@ -3744,13 +3687,11 @@
     {
         if (router.GetRloc16() == GetRloc16())
         {
-            // skip self
             continue;
         }
 
         if (!router.IsStateValid())
         {
-            // skip non-neighbor routers
             continue;
         }
 
@@ -3950,20 +3891,6 @@
 }
 
 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
-void MleRouter::HandleTimeSync(RxInfo &aRxInfo)
-{
-    Log(kMessageReceive, kTypeTimeSync, aRxInfo.mMessageInfo.GetPeerAddr());
-
-    VerifyOrExit(aRxInfo.IsNeighborStateValid());
-
-    aRxInfo.mClass = RxInfo::kPeerMessage;
-
-    Get<TimeSync>().HandleTimeSyncMessage(aRxInfo.mMessage);
-
-exit:
-    return;
-}
-
 Error MleRouter::SendTimeSync(void)
 {
     Error        error = kErrorNone;
diff --git a/src/core/thread/mle_router.hpp b/src/core/thread/mle_router.hpp
index 65bfe11..955674c 100644
--- a/src/core/thread/mle_router.hpp
+++ b/src/core/thread/mle_router.hpp
@@ -649,9 +649,6 @@
     void  HandleDataRequest(RxInfo &aRxInfo);
     void  HandleNetworkDataUpdateRouter(void);
     void  HandleDiscoveryRequest(RxInfo &aRxInfo);
-#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
-    void HandleTimeSync(RxInfo &aRxInfo);
-#endif
 
     Error ProcessRouteTlv(const RouteTlv &aRouteTlv, RxInfo &aRxInfo);
     Error ReadAndProcessRouteTlvOnFed(RxInfo &aRxInfo, uint8_t aParentId);
@@ -666,10 +663,10 @@
                                      const Ip6::MessageInfo &aMessageInfo);
     void  SendAddressRelease(void);
     void  SendAdvertisement(void);
-    Error SendLinkAccept(const Ip6::MessageInfo &aMessageInfo,
-                         Neighbor               *aNeighbor,
-                         const TlvList          &aRequestedTlvList,
-                         const RxChallenge      &aChallenge);
+    Error SendLinkAccept(const RxInfo      &aRxInfo,
+                         Neighbor          *aNeighbor,
+                         const TlvList     &aRequestedTlvList,
+                         const RxChallenge &aChallenge);
     void  SendParentResponse(Child *aChild, const RxChallenge &aChallenge, bool aRoutersOnlyRequest);
     Error SendChildIdResponse(Child &aChild);
     Error SendChildUpdateRequest(Child &aChild);
diff --git a/src/core/thread/mle_tlvs.cpp b/src/core/thread/mle_tlvs.cpp
index 1908d61..8c0ac13 100644
--- a/src/core/thread/mle_tlvs.cpp
+++ b/src/core/thread/mle_tlvs.cpp
@@ -33,7 +33,10 @@
 
 #include "mle_tlvs.hpp"
 
+#include "common/clearable.hpp"
 #include "common/code_utils.hpp"
+#include "common/numeric_limits.hpp"
+#include "radio/radio.hpp"
 
 namespace ot {
 namespace Mle {
@@ -45,7 +48,7 @@
     SetType(kRoute);
     SetLength(sizeof(*this) - sizeof(Tlv));
     mRouterIdMask.Clear();
-    memset(mRouteData, 0, sizeof(mRouteData));
+    ClearAllBytes(mRouteData);
 }
 
 bool RouteTlv::IsValid(void) const
@@ -94,5 +97,45 @@
     mFlags = static_cast<uint8_t>(Preference::To2BitUint(aParentPriority) << kFlagsParentPriorityOffset);
 }
 
+void ChannelTlvValue::SetChannelAndPage(uint16_t aChannel)
+{
+    uint8_t channelPage = OT_RADIO_CHANNEL_PAGE_0;
+
+#if OPENTHREAD_CONFIG_RADIO_915MHZ_OQPSK_SUPPORT
+    if ((OT_RADIO_915MHZ_OQPSK_CHANNEL_MIN <= aChannel) && (aChannel <= OT_RADIO_915MHZ_OQPSK_CHANNEL_MAX))
+    {
+        channelPage = OT_RADIO_CHANNEL_PAGE_2;
+    }
+#endif
+
+#if OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_SUPPORT
+    if ((OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_MIN == aChannel) ||
+        ((OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_MIN < aChannel) &&
+         (aChannel <= OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_MAX)))
+    {
+        channelPage = OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_PAGE;
+    }
+#endif
+
+    SetChannelPage(channelPage);
+    SetChannel(aChannel);
+}
+
+bool ChannelTlvValue::IsValid(void) const
+{
+    bool     isValid = false;
+    uint16_t channel;
+
+    VerifyOrExit(Radio::SupportsChannelPage(mChannelPage));
+
+    channel = GetChannel();
+    VerifyOrExit((Radio::kChannelMin <= channel) && (channel <= Radio::kChannelMax));
+
+    isValid = true;
+
+exit:
+    return isValid;
+}
+
 } // namespace Mle
 } // namespace ot
diff --git a/src/core/thread/mle_tlvs.hpp b/src/core/thread/mle_tlvs.hpp
index d4be8ff..68f62a4 100644
--- a/src/core/thread/mle_tlvs.hpp
+++ b/src/core/thread/mle_tlvs.hpp
@@ -1096,6 +1096,23 @@
      */
     void SetChannel(uint16_t aChannel) { mChannel = BigEndian::HostSwap16(aChannel); }
 
+    /**
+     * Sets the Channel and determines and sets the Channel Page from the given channel.
+     *
+     * @param[in]  aChannel  The Channel value.
+     *
+     */
+    void SetChannelAndPage(uint16_t aChannel);
+
+    /**
+     * Indicates whether or not the Channel and Channel Page values are valid.
+     *
+     * @retval TRUE   If the Channel and Channel Page values are valid.
+     * @retval FALSE  If the Channel and Channel Page values are not valid.
+     *
+     */
+    bool IsValid(void) const;
+
 private:
     uint8_t  mChannelPage;
     uint16_t mChannel;
diff --git a/src/core/thread/mle_types.hpp b/src/core/thread/mle_types.hpp
index ffc6a42..fd08cdc 100644
--- a/src/core/thread/mle_types.hpp
+++ b/src/core/thread/mle_types.hpp
@@ -434,16 +434,10 @@
 };
 
 OT_TOOL_PACKED_BEGIN
-class RouterIdSet : public Equatable<RouterIdSet>
+class RouterIdSet : public Equatable<RouterIdSet>, public Clearable<RouterIdSet>
 {
 public:
     /**
-     * Clears the Router Id Set.
-     *
-     */
-    void Clear(void) { memset(mRouterIdSet, 0, sizeof(mRouterIdSet)); }
-
-    /**
      * Indicates whether or not a Router ID bit is set.
      *
      * @param[in]  aRouterId  The Router ID.
diff --git a/src/core/thread/network_data.cpp b/src/core/thread/network_data.cpp
index 2599d65..28675ad 100644
--- a/src/core/thread/network_data.cpp
+++ b/src/core/thread/network_data.cpp
@@ -50,6 +50,9 @@
 
 RegisterLogModule("NetworkData");
 
+//---------------------------------------------------------------------------------------------------------------------
+// NetworkData
+
 Error NetworkData::CopyNetworkData(Type aType, uint8_t *aData, uint8_t &aDataLength) const
 {
     Error              error;
@@ -402,153 +405,6 @@
     return contains;
 }
 
-void MutableNetworkData::RemoveTemporaryData(void)
-{
-    NetworkDataTlv *cur = GetTlvsStart();
-
-    while (cur < GetTlvsEnd())
-    {
-        switch (cur->GetType())
-        {
-        case NetworkDataTlv::kTypePrefix:
-        {
-            PrefixTlv *prefix = As<PrefixTlv>(cur);
-
-            RemoveTemporaryDataIn(*prefix);
-
-            if (prefix->GetSubTlvsLength() == 0)
-            {
-                RemoveTlv(cur);
-                continue;
-            }
-
-            break;
-        }
-
-        case NetworkDataTlv::kTypeService:
-        {
-            ServiceTlv *service = As<ServiceTlv>(cur);
-
-            RemoveTemporaryDataIn(*service);
-
-            if (service->GetSubTlvsLength() == 0)
-            {
-                RemoveTlv(cur);
-                continue;
-            }
-
-            break;
-        }
-
-        default:
-            // remove temporary tlv
-            if (!cur->IsStable())
-            {
-                RemoveTlv(cur);
-                continue;
-            }
-
-            break;
-        }
-
-        cur = cur->GetNext();
-    }
-}
-
-void MutableNetworkData::RemoveTemporaryDataIn(PrefixTlv &aPrefix)
-{
-    NetworkDataTlv *cur = aPrefix.GetSubTlvs();
-
-    while (cur < aPrefix.GetNext())
-    {
-        if (cur->IsStable())
-        {
-            switch (cur->GetType())
-            {
-            case NetworkDataTlv::kTypeBorderRouter:
-            {
-                BorderRouterTlv *borderRouter = As<BorderRouterTlv>(cur);
-                ContextTlv      *context      = aPrefix.FindSubTlv<ContextTlv>();
-
-                // Replace p_border_router_16
-                for (BorderRouterEntry *entry = borderRouter->GetFirstEntry(); entry <= borderRouter->GetLastEntry();
-                     entry                    = entry->GetNext())
-                {
-                    if ((entry->IsDhcp() || entry->IsConfigure()) && (context != nullptr))
-                    {
-                        entry->SetRloc(0xfc00 | context->GetContextId());
-                    }
-                    else
-                    {
-                        entry->SetRloc(0xfffe);
-                    }
-                }
-
-                break;
-            }
-
-            case NetworkDataTlv::kTypeHasRoute:
-            {
-                HasRouteTlv *hasRoute = As<HasRouteTlv>(cur);
-
-                // Replace r_border_router_16
-                for (HasRouteEntry *entry = hasRoute->GetFirstEntry(); entry <= hasRoute->GetLastEntry();
-                     entry                = entry->GetNext())
-                {
-                    entry->SetRloc(0xfffe);
-                }
-
-                break;
-            }
-
-            default:
-                break;
-            }
-
-            // keep stable tlv
-            cur = cur->GetNext();
-        }
-        else
-        {
-            // remove temporary tlv
-            uint8_t subTlvSize = cur->GetSize();
-            RemoveTlv(cur);
-            aPrefix.SetSubTlvsLength(aPrefix.GetSubTlvsLength() - subTlvSize);
-        }
-    }
-}
-
-void MutableNetworkData::RemoveTemporaryDataIn(ServiceTlv &aService)
-{
-    NetworkDataTlv *cur = aService.GetSubTlvs();
-
-    while (cur < aService.GetNext())
-    {
-        if (cur->IsStable())
-        {
-            switch (cur->GetType())
-            {
-            case NetworkDataTlv::kTypeServer:
-                As<ServerTlv>(cur)->SetServer16(Mle::ServiceAlocFromId(aService.GetServiceId()));
-                break;
-
-            default:
-                break;
-            }
-
-            // keep stable tlv
-            cur = cur->GetNext();
-        }
-        else
-        {
-            // remove temporary tlv
-            uint8_t subTlvSize = cur->GetSize();
-            RemoveTlv(cur);
-            aService.SetSubTlvsLength(aService.GetSubTlvsLength() - subTlvSize);
-        }
-    }
-}
-
 const PrefixTlv *NetworkData::FindPrefix(const uint8_t *aPrefix, uint8_t aPrefixLength) const
 {
     TlvIterator      tlvIterator(mTlvs, mLength);
@@ -639,6 +495,251 @@
     return match;
 }
 
+void NetworkData::FindRlocs(BorderRouterFilter aBrFilter, RoleFilter aRoleFilter, Rlocs &aRlocs) const
+{
+    Iterator            iterator = kIteratorInit;
+    OnMeshPrefixConfig  prefix;
+    ExternalRouteConfig route;
+    ServiceConfig       service;
+    Config              config;
+
+    aRlocs.Clear();
+
+    while (true)
+    {
+        config.mOnMeshPrefix  = &prefix;
+        config.mExternalRoute = &route;
+        config.mService       = &service;
+        config.mLowpanContext = nullptr;
+
+        SuccessOrExit(Iterate(iterator, Mac::kShortAddrBroadcast, config));
+
+        if (config.mOnMeshPrefix != nullptr)
+        {
+            bool matches = true;
+
+            switch (aBrFilter)
+            {
+            case kAnyBrOrServer:
+                break;
+            case kBrProvidingExternalIpConn:
+                matches = prefix.mOnMesh && (prefix.mDefaultRoute || prefix.mDp);
+                break;
+            }
+
+            if (matches)
+            {
+                AddRloc16ToRlocs(prefix.mRloc16, aRlocs, aRoleFilter);
+            }
+        }
+        else if (config.mExternalRoute != nullptr)
+        {
+            AddRloc16ToRlocs(route.mRloc16, aRlocs, aRoleFilter);
+        }
+        else if (config.mService != nullptr)
+        {
+            switch (aBrFilter)
+            {
+            case kAnyBrOrServer:
+                AddRloc16ToRlocs(service.mServerConfig.mRloc16, aRlocs, aRoleFilter);
+                break;
+            case kBrProvidingExternalIpConn:
+                break;
+            }
+        }
+    }
+
+exit:
+    return;
+}
+
+uint8_t NetworkData::CountBorderRouters(RoleFilter aRoleFilter) const
+{
+    Rlocs rlocs;
+
+    FindRlocs(kBrProvidingExternalIpConn, aRoleFilter, rlocs);
+
+    return rlocs.GetLength();
+}
+
+bool NetworkData::ContainsBorderRouterWithRloc(uint16_t aRloc16) const
+{
+    Rlocs rlocs;
+
+    FindRlocs(kBrProvidingExternalIpConn, kAnyRole, rlocs);
+
+    return rlocs.Contains(aRloc16);
+}
+
+void NetworkData::AddRloc16ToRlocs(uint16_t aRloc16, Rlocs &aRlocs, RoleFilter aRoleFilter)
+{
+    switch (aRoleFilter)
+    {
+    case kAnyRole:
+        break;
+
+    case kRouterRoleOnly:
+        VerifyOrExit(Mle::IsActiveRouter(aRloc16));
+        break;
+
+    case kChildRoleOnly:
+        VerifyOrExit(!Mle::IsActiveRouter(aRloc16));
+        break;
+    }
+
+    VerifyOrExit(!aRlocs.Contains(aRloc16));
+    IgnoreError(aRlocs.PushBack(aRloc16));
+
+exit:
+    return;
+}
+
+Error NetworkData::FindDomainIdFor(const Ip6::Prefix &aPrefix, uint8_t &aDomainId) const
+{
+    Error            error     = kErrorNone;
+    const PrefixTlv *prefixTlv = FindPrefix(aPrefix);
+
+    VerifyOrExit(prefixTlv != nullptr, error = kErrorNotFound);
+    aDomainId = prefixTlv->GetDomainId();
+
+exit:
+    return error;
+}
+
+//---------------------------------------------------------------------------------------------------------------------
+// MutableNetworkData
+
+void MutableNetworkData::RemoveTemporaryData(void)
+{
+    NetworkDataTlv *cur = GetTlvsStart();
+
+    while (cur < GetTlvsEnd())
+    {
+        bool shouldRemove = false;
+
+        switch (cur->GetType())
+        {
+        case NetworkDataTlv::kTypePrefix:
+            shouldRemove = RemoveTemporaryDataIn(*As<PrefixTlv>(cur));
+            break;
+
+        case NetworkDataTlv::kTypeService:
+            shouldRemove = RemoveTemporaryDataIn(*As<ServiceTlv>(cur));
+            break;
+
+        default:
+            shouldRemove = !cur->IsStable();
+            break;
+        }
+
+        if (shouldRemove)
+        {
+            RemoveTlv(cur);
+            continue;
+        }
+
+        cur = cur->GetNext();
+    }
+}
+
+bool MutableNetworkData::RemoveTemporaryDataIn(PrefixTlv &aPrefix)
+{
+    NetworkDataTlv *cur = aPrefix.GetSubTlvs();
+
+    while (cur < aPrefix.GetNext())
+    {
+        if (cur->IsStable())
+        {
+            switch (cur->GetType())
+            {
+            case NetworkDataTlv::kTypeBorderRouter:
+            {
+                BorderRouterTlv *borderRouter = As<BorderRouterTlv>(cur);
+                ContextTlv      *context      = aPrefix.FindSubTlv<ContextTlv>();
+
+                // Replace p_border_router_16
+                for (BorderRouterEntry *entry = borderRouter->GetFirstEntry(); entry <= borderRouter->GetLastEntry();
+                     entry                    = entry->GetNext())
+                {
+                    if ((entry->IsDhcp() || entry->IsConfigure()) && (context != nullptr))
+                    {
+                        entry->SetRloc(0xfc00 | context->GetContextId());
+                    }
+                    else
+                    {
+                        entry->SetRloc(0xfffe);
+                    }
+                }
+
+                break;
+            }
+
+            case NetworkDataTlv::kTypeHasRoute:
+            {
+                HasRouteTlv *hasRoute = As<HasRouteTlv>(cur);
+
+                // Replace r_border_router_16
+                for (HasRouteEntry *entry = hasRoute->GetFirstEntry(); entry <= hasRoute->GetLastEntry();
+                     entry                = entry->GetNext())
+                {
+                    entry->SetRloc(0xfffe);
+                }
+
+                break;
+            }
+
+            default:
+                break;
+            }
+
+            // keep stable tlv
+            cur = cur->GetNext();
+        }
+        else
+        {
+            // remove temporary tlv
+            uint8_t subTlvSize = cur->GetSize();
+            RemoveTlv(cur);
+            aPrefix.SetSubTlvsLength(aPrefix.GetSubTlvsLength() - subTlvSize);
+        }
+    }
+
+    return (aPrefix.GetSubTlvsLength() == 0);
+}
+
+bool MutableNetworkData::RemoveTemporaryDataIn(ServiceTlv &aService)
+{
+    NetworkDataTlv *cur = aService.GetSubTlvs();
+
+    while (cur < aService.GetNext())
+    {
+        if (cur->IsStable())
+        {
+            switch (cur->GetType())
+            {
+            case NetworkDataTlv::kTypeServer:
+                As<ServerTlv>(cur)->SetServer16(Mle::ServiceAlocFromId(aService.GetServiceId()));
+                break;
+
+            default:
+                break;
+            }
+
+            // keep stable tlv
+            cur = cur->GetNext();
+        }
+        else
+        {
+            // remove temporary tlv
+            uint8_t subTlvSize = cur->GetSize();
+            RemoveTlv(cur);
+            aService.SetSubTlvsLength(aService.GetSubTlvsLength() - subTlvSize);
+        }
+    }
+
+    return (aService.GetSubTlvsLength() == 0);
+}
+
 NetworkDataTlv *MutableNetworkData::AppendTlv(uint16_t aTlvSize)
 {
     NetworkDataTlv *tlv;
@@ -675,178 +776,5 @@
 
 void MutableNetworkData::RemoveTlv(NetworkDataTlv *aTlv) { Remove(aTlv, aTlv->GetSize()); }
 
-Error NetworkData::GetNextServer(Iterator &aIterator, uint16_t &aRloc16) const
-{
-    Error               error;
-    OnMeshPrefixConfig  prefixConfig;
-    ExternalRouteConfig routeConfig;
-    ServiceConfig       serviceConfig;
-    Config              config;
-
-    config.mOnMeshPrefix  = &prefixConfig;
-    config.mExternalRoute = &routeConfig;
-    config.mService       = &serviceConfig;
-    config.mLowpanContext = nullptr;
-
-    SuccessOrExit(error = Iterate(aIterator, Mac::kShortAddrBroadcast, config));
-
-    if (config.mOnMeshPrefix != nullptr)
-    {
-        aRloc16 = config.mOnMeshPrefix->mRloc16;
-    }
-    else if (config.mExternalRoute != nullptr)
-    {
-        aRloc16 = config.mExternalRoute->mRloc16;
-    }
-    else if (config.mService != nullptr)
-    {
-        aRloc16 = config.mService->mServerConfig.mRloc16;
-    }
-    else
-    {
-        OT_ASSERT(false);
-    }
-
-exit:
-    return error;
-}
-
-Error NetworkData::FindBorderRouters(RoleFilter aRoleFilter, uint16_t aRlocs[], uint8_t &aRlocsLength) const
-{
-    class Rlocs // Wrapper over an array of RLOC16s.
-    {
-    public:
-        Rlocs(RoleFilter aRoleFilter, uint16_t *aRlocs, uint8_t aRlocsMaxLength)
-            : mRoleFilter(aRoleFilter)
-            , mRlocs(aRlocs)
-            , mLength(0)
-            , mMaxLength(aRlocsMaxLength)
-        {
-        }
-
-        uint8_t GetLength(void) const { return mLength; }
-
-        Error AddRloc16(uint16_t aRloc16)
-        {
-            // Add `aRloc16` into the array if it matches `RoleFilter` and
-            // it is not in the array already. If we need to add the `aRloc16`
-            // but there is no more room in the array, return `kErrorNoBufs`.
-
-            Error   error = kErrorNone;
-            uint8_t index;
-
-            switch (mRoleFilter)
-            {
-            case kAnyRole:
-                break;
-
-            case kRouterRoleOnly:
-                VerifyOrExit(Mle::IsActiveRouter(aRloc16));
-                break;
-
-            case kChildRoleOnly:
-                VerifyOrExit(!Mle::IsActiveRouter(aRloc16));
-                break;
-            }
-
-            for (index = 0; index < mLength; index++)
-            {
-                if (mRlocs[index] == aRloc16)
-                {
-                    break;
-                }
-            }
-
-            if (index == mLength)
-            {
-                VerifyOrExit(mLength < mMaxLength, error = kErrorNoBufs);
-                mRlocs[mLength++] = aRloc16;
-            }
-
-        exit:
-            return error;
-        }
-
-    private:
-        RoleFilter mRoleFilter;
-        uint16_t  *mRlocs;
-        uint8_t    mLength;
-        uint8_t    mMaxLength;
-    };
-
-    Error               error = kErrorNone;
-    Rlocs               rlocs(aRoleFilter, aRlocs, aRlocsLength);
-    Iterator            iterator = kIteratorInit;
-    ExternalRouteConfig route;
-    OnMeshPrefixConfig  prefix;
-
-    while (GetNextExternalRoute(iterator, route) == kErrorNone)
-    {
-        SuccessOrExit(error = rlocs.AddRloc16(route.mRloc16));
-    }
-
-    iterator = kIteratorInit;
-
-    while (GetNextOnMeshPrefix(iterator, prefix) == kErrorNone)
-    {
-        if (!prefix.mDefaultRoute || !prefix.mOnMesh)
-        {
-            continue;
-        }
-
-        SuccessOrExit(error = rlocs.AddRloc16(prefix.mRloc16));
-    }
-
-exit:
-    aRlocsLength = rlocs.GetLength();
-    return error;
-}
-
-uint8_t NetworkData::CountBorderRouters(RoleFilter aRoleFilter) const
-{
-    // We use an over-estimate of max number of border routers in the
-    // Network Data using the facts that network data is limited to 254
-    // bytes and that an external route entry uses at minimum 3 bytes
-    // for RLOC16 and flag, so `ceil(254/3) = 85`.
-
-    static constexpr uint16_t kMaxRlocs = 85;
-
-    uint16_t rlocs[kMaxRlocs];
-    uint8_t  rlocsLength = kMaxRlocs;
-
-    SuccessOrAssert(FindBorderRouters(aRoleFilter, rlocs, rlocsLength));
-
-    return rlocsLength;
-}
-
-bool NetworkData::ContainsBorderRouterWithRloc(uint16_t aRloc16) const
-{
-    bool                contains = false;
-    Iterator            iterator = kIteratorInit;
-    ExternalRouteConfig route;
-    OnMeshPrefixConfig  prefix;
-
-    while (GetNextExternalRoute(iterator, route) == kErrorNone)
-    {
-        if (route.mRloc16 == aRloc16)
-        {
-            ExitNow(contains = true);
-        }
-    }
-
-    iterator = kIteratorInit;
-
-    while (GetNextOnMeshPrefix(iterator, prefix) == kErrorNone)
-    {
-        if ((prefix.mRloc16 == aRloc16) && prefix.mOnMesh && (prefix.mDefaultRoute || prefix.mDp))
-        {
-            ExitNow(contains = true);
-        }
-    }
-
-exit:
-    return contains;
-}
-
 } // namespace NetworkData
 } // namespace ot
diff --git a/src/core/thread/network_data.hpp b/src/core/thread/network_data.hpp
index 40294cf..d1d8270 100644
--- a/src/core/thread/network_data.hpp
+++ b/src/core/thread/network_data.hpp
@@ -325,42 +325,42 @@
     bool ContainsEntriesFrom(const NetworkData &aCompare, uint16_t aRloc16) const;
 
     /**
-     * Provides the next server RLOC16 in the Thread Network Data.
+     * Finds and returns Domain ID associated with a given prefix in the Thread Network data.
      *
-     * @param[in,out]  aIterator  A reference to the Network Data iterator.
-     * @param[out]     aRloc16    The RLOC16 value.
+     * @param[in]  aPrefix     The prefix to search for.
+     * @param[out] aDomainId   A reference to return the Domain ID.
      *
-     * @retval kErrorNone       Successfully found the next server.
-     * @retval kErrorNotFound   No subsequent server exists in the Thread Network Data.
+     * @retval kErrorNone      Successfully found @p aPrefix in the Network Data and updated @p aDomainId.
+     * @retval kErrorNotFound  Could not find @p aPrefix in the Network Data.
      *
      */
-    Error GetNextServer(Iterator &aIterator, uint16_t &aRloc16) const;
+    Error FindDomainIdFor(const Ip6::Prefix &aPrefix, uint8_t &aDomainId) const;
 
     /**
-     * Finds and returns the list of RLOCs of border routers providing external IP connectivity.
+     * Finds border routers and servers in the Network Data matching specified filters, returning their RLOC16s.
      *
-     * A border router is considered to provide external IP connectivity if it has added at least one external route
-     * entry, or an on-mesh prefix with default-route and on-mesh flags set.
+     * @p aBrFilter can be used to filter the type of BRs. It can be set to `kAnyBrOrServer` to include all BRs and
+     * servers. `kBrProvidingExternalIpConn` restricts it to BRs providing external IP connectivity where at least one
+     * the below conditions hold:
+     *
+     * - It has added at least one external route entry.
+     * - It has added at least one prefix entry with default-route and on-mesh flags set.
+     * - It has added at least one domain prefix (domain and on-mesh flags set).
      *
      * Should be used when the RLOC16s are present in the Network Data (when the Network Data contains the
      * full set and not the stable subset).
      *
-     * @param[in]      aRoleFilter   Indicates which devices to include (any role, router role only, or child only).
-     * @param[out]     aRlocs        Array to output the list of RLOCs.
-     * @param[in,out]  aRlocsLength  On entry, @p aRlocs array length (max number of elements).
-     *                               On exit, number RLOC16 entries added in @p aRlocs.
-     *
-     * @retval kErrorNone     Successfully found all RLOC16s and updated @p aRlocs and @p aRlocsLength.
-     * @retval kErrorNoBufs   Ran out of space in @p aRlocs array. @p aRlocs and @p aRlocsLength are still updated up
-     *                        to the maximum array length.
+     * @param[in]  aBrFilter    Indicates BR filter.
+     * @param[in]  aRoleFilter  Indicates role filter (any role, router role only, or child only).
+     * @param[out] aRlocs       Array to output the list of RLOC16s.
      *
      */
-    Error FindBorderRouters(RoleFilter aRoleFilter, uint16_t aRlocs[], uint8_t &aRlocsLength) const;
+    void FindRlocs(BorderRouterFilter aBrFilter, RoleFilter aRoleFilter, Rlocs &aRlocs) const;
 
     /**
      * Counts the number of border routers providing external IP connectivity.
      *
-     * A border router is considered to provide external IP connectivity if at least one of the below conditions hold
+     * A border router is considered to provide external IP connectivity if at least one of the below conditions hold:
      *
      * - It has added at least one external route entry.
      * - It has added at least one prefix entry with default-route and on-mesh flags set.
@@ -579,6 +579,8 @@
                              const ServiceData &aServiceData,
                              ServiceMatchMode   aServiceMatchMode);
 
+    static void AddRloc16ToRlocs(uint16_t aRloc16, Rlocs &aRlocs, RoleFilter aRoleFilter);
+
     const uint8_t *mTlvs;
     uint8_t        mLength;
 };
@@ -767,8 +769,8 @@
     void RemoveTemporaryData(void);
 
 private:
-    void RemoveTemporaryDataIn(PrefixTlv &aPrefix);
-    void RemoveTemporaryDataIn(ServiceTlv &aService);
+    bool RemoveTemporaryDataIn(PrefixTlv &aPrefix);
+    bool RemoveTemporaryDataIn(ServiceTlv &aService);
 
     uint8_t mSize;
 };
diff --git a/src/core/thread/network_data_leader.cpp b/src/core/thread/network_data_leader.cpp
index 66a29a8..0c08a61 100644
--- a/src/core/thread/network_data_leader.cpp
+++ b/src/core/thread/network_data_leader.cpp
@@ -279,6 +279,22 @@
         }
     }
 
+#if OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE
+    {
+        // The `Slaac` module keeps track of the associated Domain IDs
+        // for deprecating SLAAC prefixes, even if the related
+        // Prefix TLV has already been removed from the Network
+        // Data.
+
+        uint8_t domainId;
+
+        if (Get<Utils::Slaac>().FindDomainIdFor(aSource, domainId) == kErrorNone)
+        {
+            error = ExternalRouteLookup(domainId, aDestination, aRloc16);
+        }
+    }
+#endif
+
 exit:
     return error;
 }
diff --git a/src/core/thread/network_data_leader_ftd.cpp b/src/core/thread/network_data_leader_ftd.cpp
index fe423ef..5cfc44e 100644
--- a/src/core/thread/network_data_leader_ftd.cpp
+++ b/src/core/thread/network_data_leader_ftd.cpp
@@ -680,10 +680,7 @@
     if (!mIsClone)
 #endif
     {
-        if (error != kErrorNone)
-        {
-            LogNote("Failed to register network data: %s", ErrorToString(error));
-        }
+        LogWarnOnError(error, "register network data");
     }
 }
 
@@ -1218,10 +1215,10 @@
 {
     const PrefixTlv *prefix;
     TlvIterator      tlvIterator(GetTlvsStart(), GetTlvsEnd());
-    Iterator         iterator = kIteratorInit;
     ChangedFlags     flags;
     uint16_t         rloc16;
     uint16_t         sessionId;
+    Rlocs            rlocs;
 
     mWaitingForNetDataSync = false;
 
@@ -1232,16 +1229,13 @@
     // got the chance to send the updated Network Data to other
     // routers.
 
-    while (GetNextServer(iterator, rloc16) == kErrorNone)
-    {
-        if (!Get<RouterTable>().IsAllocated(Mle::RouterIdFromRloc16(rloc16)))
-        {
-            // After we `RemoveRloc()` the Network Data gets changed
-            // and the `iterator` will not be valid anymore. So we set
-            // it to `kIteratorInit` to restart the loop.
+    FindRlocs(kAnyBrOrServer, kAnyRole, rlocs);
 
-            RemoveRloc(rloc16, kMatchModeRouterId, flags);
-            iterator = kIteratorInit;
+    for (uint16_t rloc : rlocs)
+    {
+        if (!Get<RouterTable>().IsAllocated(Mle::RouterIdFromRloc16(rloc)))
+        {
+            RemoveRloc(rloc, kMatchModeRouterId, flags);
         }
     }
 
diff --git a/src/core/thread/network_data_notifier.cpp b/src/core/thread/network_data_notifier.cpp
index 750b379..e451a73 100644
--- a/src/core/thread/network_data_notifier.cpp
+++ b/src/core/thread/network_data_notifier.cpp
@@ -129,13 +129,14 @@
     // - `kErrorNoBufs` if could not allocate message to send message.
     // - `kErrorNotFound` if no stale child entries were found.
 
-    Error    error    = kErrorNotFound;
-    Iterator iterator = kIteratorInit;
-    uint16_t rloc16;
+    Error error = kErrorNotFound;
+    Rlocs rlocs;
 
     VerifyOrExit(Get<Mle::MleRouter>().IsRouterOrLeader());
 
-    while (Get<Leader>().GetNextServer(iterator, rloc16) == kErrorNone)
+    Get<Leader>().FindRlocs(kAnyBrOrServer, kAnyRole, rlocs);
+
+    for (uint16_t rloc16 : rlocs)
     {
         if (!Mle::IsActiveRouter(rloc16) && Mle::RouterIdMatch(Get<Mle::MleRouter>().GetRloc16(), rloc16) &&
             Get<ChildTable>().FindChild(rloc16, Child::kInStateValid) == nullptr)
diff --git a/src/core/thread/network_data_publisher.cpp b/src/core/thread/network_data_publisher.cpp
index 6094edd..a64f0c3 100644
--- a/src/core/thread/network_data_publisher.cpp
+++ b/src/core/thread/network_data_publisher.cpp
@@ -658,10 +658,30 @@
         desiredNumEntries = kDesiredNumAnycast;
         break;
 
-    case kTypeUnicast:
     case kTypeUnicastMeshLocalEid:
-        CountUnicastEntries(numEntries, numPreferredEntries);
+    {
+        Service::DnsSrpAnycast::Info anycastInfo;
+        bool                         hasServiceDataEntry;
+
+        CountServerDataUnicastEntries(numEntries, numPreferredEntries, hasServiceDataEntry);
         desiredNumEntries = kDesiredNumUnicast;
+
+        if (hasServiceDataEntry || (Get<Service::Manager>().FindPreferredDnsSrpAnycastInfo(anycastInfo) == kErrorNone))
+        {
+            // If there is any service data unicast entry or anycast
+            // entry, we set the desired number of server data
+            // unicast entries to zero to remove any such previously
+            // added unicast entry.
+
+            desiredNumEntries = 0;
+        }
+
+        break;
+    }
+
+    case kTypeUnicast:
+        desiredNumEntries = kDesiredNumUnicast;
+        CountServiceDataUnicastEntries(numEntries, numPreferredEntries);
         break;
     }
 
@@ -702,9 +722,53 @@
     }
 }
 
-void Publisher::DnsSrpServiceEntry::CountUnicastEntries(uint8_t &aNumEntries, uint8_t &aNumPreferredEntries) const
+void Publisher::DnsSrpServiceEntry::CountServerDataUnicastEntries(uint8_t &aNumEntries,
+                                                                  uint8_t &aNumPreferredEntries,
+                                                                  bool    &aHasServiceDataEntry) const
 {
-    // Count the number of "DNS/SRP Unicast" service entries in
+    // Count the number of server data DNS/SRP unicast entries in the
+    // Network Data. Also determine whether there is any service data
+    // DNS/SRP unicast entry (update `aHasServiceDataEntry`).
+
+    const ServiceTlv *serviceTlv = nullptr;
+    ServiceData       data;
+
+    aHasServiceDataEntry = false;
+
+    data.InitFrom(Service::DnsSrpUnicast::kServiceData);
+
+    while ((serviceTlv = Get<Leader>().FindNextThreadService(serviceTlv, data, NetworkData::kServicePrefixMatch)) !=
+           nullptr)
+    {
+        TlvIterator      subTlvIterator(*serviceTlv);
+        const ServerTlv *serverSubTlv;
+
+        if (serviceTlv->GetServiceDataLength() >= sizeof(Service::DnsSrpUnicast::ServiceData))
+        {
+            aHasServiceDataEntry = true;
+        }
+
+        while (((serverSubTlv = subTlvIterator.Iterate<ServerTlv>())) != nullptr)
+        {
+            if (serverSubTlv->GetServerDataLength() < sizeof(Service::DnsSrpUnicast::ServerData))
+            {
+                continue;
+            }
+
+            aNumEntries++;
+
+            if (IsPreferred(serverSubTlv->GetServer16()))
+            {
+                aNumPreferredEntries++;
+            }
+        }
+    }
+}
+
+void Publisher::DnsSrpServiceEntry::CountServiceDataUnicastEntries(uint8_t &aNumEntries,
+                                                                   uint8_t &aNumPreferredEntries) const
+{
+    // Count the number of service data DNS/SRP unicast entries in
     // the Network Data.
 
     const ServiceTlv *serviceTlv = nullptr;
@@ -718,45 +782,18 @@
         TlvIterator      subTlvIterator(*serviceTlv);
         const ServerTlv *serverSubTlv;
 
+        if (serviceTlv->GetServiceDataLength() < sizeof(Service::DnsSrpUnicast::ServiceData))
+        {
+            continue;
+        }
+
         while (((serverSubTlv = subTlvIterator.Iterate<ServerTlv>())) != nullptr)
         {
-            if (serviceTlv->GetServiceDataLength() >= sizeof(Service::DnsSrpUnicast::ServiceData))
+            aNumEntries++;
+
+            if (IsPreferred(serverSubTlv->GetServer16()))
             {
-                aNumEntries++;
-
-                // Generally, we prefer entries where the SRP/DNS server
-                // address/port info is included in the service TLV data
-                // over the ones where the info is included in the
-                // server TLV data (i.e., we prefer infra-provided
-                // SRP/DNS entry over a BR local one using ML-EID). If
-                // our entry itself uses the service TLV data, then we
-                // prefer based on the associated RLOC16.
-
-                if (GetType() == kTypeUnicast)
-                {
-                    if (IsPreferred(serverSubTlv->GetServer16()))
-                    {
-                        aNumPreferredEntries++;
-                    }
-                }
-                else
-                {
-                    aNumPreferredEntries++;
-                }
-            }
-
-            if (serverSubTlv->GetServerDataLength() >= sizeof(Service::DnsSrpUnicast::ServerData))
-            {
-                aNumEntries++;
-
-                // If our entry also uses the server TLV data (with
-                // ML-EID address), then the we prefer based on the
-                // associated RLOC16.
-
-                if ((GetType() == kTypeUnicastMeshLocalEid) && IsPreferred(serverSubTlv->GetServer16()))
-                {
-                    aNumPreferredEntries++;
-                }
+                aNumPreferredEntries++;
             }
         }
     }
diff --git a/src/core/thread/network_data_publisher.hpp b/src/core/thread/network_data_publisher.hpp
index 3ac2c82..4503efb 100644
--- a/src/core/thread/network_data_publisher.hpp
+++ b/src/core/thread/network_data_publisher.hpp
@@ -437,7 +437,10 @@
         void Notify(Event aEvent) const;
         void Process(void);
         void CountAnycastEntries(uint8_t &aNumEntries, uint8_t &aNumPreferredEntries) const;
-        void CountUnicastEntries(uint8_t &aNumEntries, uint8_t &aNumPreferredEntries) const;
+        void CountServiceDataUnicastEntries(uint8_t &aNumEntries, uint8_t &aNumPreferredEntries) const;
+        void CountServerDataUnicastEntries(uint8_t &aNumEntries,
+                                           uint8_t &aNumPreferredEntries,
+                                           bool    &aHasServiceDataEntry) const;
 
         Info                            mInfo;
         Callback<DnsSrpServiceCallback> mCallback;
diff --git a/src/core/thread/network_data_types.hpp b/src/core/thread/network_data_types.hpp
index 1457aa2..af63ff5 100644
--- a/src/core/thread/network_data_types.hpp
+++ b/src/core/thread/network_data_types.hpp
@@ -38,6 +38,7 @@
 
 #include <openthread/netdata.h>
 
+#include "common/array.hpp"
 #include "common/as_core_type.hpp"
 #include "common/clearable.hpp"
 #include "common/data.hpp"
@@ -108,6 +109,38 @@
 };
 
 /**
+ * Represents the entry filter used when searching for RLOC16 of border routers or servers in the Network Data.
+ *
+ * Regarding `kBrProvidingExternalIpConn`, a border router is considered to provide external IP connectivity if at
+ * least one of the below conditions hold:
+ *
+ * - It has added at least one external route entry.
+ * - It has added at least one prefix entry with default-route and on-mesh flags set.
+ * - It has added at least one domain prefix (domain and on-mesh flags set).
+ *
+ */
+enum BorderRouterFilter : uint8_t
+{
+    kAnyBrOrServer,             ///< Include any border router or server entry.
+    kBrProvidingExternalIpConn, ///< Include border routers providing external IP connectivity.
+};
+
+/**
+ * Maximum length of `Rlocs` array containing RLOC16 of all border routers and servers in the Network Data.
+ *
+ * This limit is derived from the maximum Network Data size (254 bytes) and the minimum size of an external route entry
+ * (3 bytes including the RLOC16 and flags) as `ceil(254/3) = 85`.
+ *
+ */
+static constexpr uint8_t kMaxRlocs = 85;
+
+/**
+ * An array containing RLOC16 of all border routers and server in the Network Data.
+ *
+ */
+typedef Array<uint16_t, kMaxRlocs> Rlocs;
+
+/**
  * Indicates whether a given `int8_t` preference value is a valid route preference (i.e., one of the
  * values from `RoutePreference` enumeration).
  *
diff --git a/src/core/thread/network_diagnostic.cpp b/src/core/thread/network_diagnostic.cpp
index 634574b..f32e781 100644
--- a/src/core/thread/network_diagnostic.cpp
+++ b/src/core/thread/network_diagnostic.cpp
@@ -61,6 +61,7 @@
 const char Server::kVendorName[]      = OPENTHREAD_CONFIG_NET_DIAG_VENDOR_NAME;
 const char Server::kVendorModel[]     = OPENTHREAD_CONFIG_NET_DIAG_VENDOR_MODEL;
 const char Server::kVendorSwVersion[] = OPENTHREAD_CONFIG_NET_DIAG_VENDOR_SW_VERSION;
+const char Server::kVendorAppUrl[]    = OPENTHREAD_CONFIG_NET_DIAG_VENDOR_APP_URL;
 
 //---------------------------------------------------------------------------------------------------------------------
 // Server
@@ -71,11 +72,13 @@
     static_assert(sizeof(kVendorName) <= sizeof(VendorNameTlv::StringType), "VENDOR_NAME is too long");
     static_assert(sizeof(kVendorModel) <= sizeof(VendorModelTlv::StringType), "VENDOR_MODEL is too long");
     static_assert(sizeof(kVendorSwVersion) <= sizeof(VendorSwVersionTlv::StringType), "VENDOR_SW_VERSION is too long");
+    static_assert(sizeof(kVendorAppUrl) <= sizeof(VendorAppUrlTlv::StringType), "VENDOR_APP_URL is too long");
 
 #if OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE
     memcpy(mVendorName, kVendorName, sizeof(kVendorName));
     memcpy(mVendorModel, kVendorModel, sizeof(kVendorModel));
     memcpy(mVendorSwVersion, kVendorSwVersion, sizeof(kVendorSwVersion));
+    memcpy(mVendorAppUrl, kVendorAppUrl, sizeof(kVendorAppUrl));
 #endif
 }
 
@@ -96,6 +99,11 @@
     return StringCopy(mVendorSwVersion, aVendorSwVersion, kStringCheckUtf8Encoding);
 }
 
+Error Server::SetVendorAppUrl(const char *aVendorAppUrl)
+{
+    return StringCopy(mVendorAppUrl, aVendorAppUrl, kStringCheckUtf8Encoding);
+}
+
 #endif
 
 void Server::PrepareMessageInfoForDest(const Ip6::Address &aDestination, Tmf::MessageInfo &aMessageInfo) const
@@ -212,7 +220,7 @@
     MacCountersTlv       tlv;
     const otMacCounters &counters = Get<Mac::Mac>().GetCounters();
 
-    memset(&tlv, 0, sizeof(tlv));
+    ClearAllBytes(tlv);
 
     tlv.Init();
     tlv.SetIfInUnknownProtos(counters.mRxOther);
@@ -267,6 +275,15 @@
         error = Tlv::Append<ModeTlv>(aMessage, Get<Mle::MleRouter>().GetDeviceMode().Get());
         break;
 
+    case Tlv::kEui64:
+    {
+        Mac::ExtAddress eui64;
+
+        Get<Radio>().GetIeeeEui64(eui64);
+        error = Tlv::Append<Eui64Tlv>(aMessage, eui64);
+        break;
+    }
+
     case Tlv::kVersion:
         error = Tlv::Append<VersionTlv>(aMessage, kThreadVersion);
         break;
@@ -299,6 +316,15 @@
         error = AppendMacCounters(aMessage);
         break;
 
+    case Tlv::kMleCounters:
+    {
+        MleCountersTlv tlv;
+
+        tlv.Init(Get<Mle::Mle>().GetCounters());
+        error = tlv.AppendTo(aMessage);
+        break;
+    }
+
     case Tlv::kVendorName:
         error = Tlv::Append<VendorNameTlv>(aMessage, GetVendorName());
         break;
@@ -311,6 +337,10 @@
         error = Tlv::Append<VendorSwVersionTlv>(aMessage, GetVendorSwVersion());
         break;
 
+    case Tlv::kVendorAppUrl:
+        error = Tlv::Append<VendorAppUrlTlv>(aMessage, GetVendorAppUrl());
+        break;
+
     case Tlv::kThreadStackVersion:
         error = Tlv::Append<ThreadStackVersionTlv>(aMessage, otGetVersionString());
         break;
@@ -322,12 +352,9 @@
 
         tlv.Init();
 
-        for (uint8_t page = 0; page < static_cast<uint8_t>(BitSizeOf(Radio::kSupportedChannelPages)); page++)
+        for (uint8_t page : Radio::kSupportedChannelPages)
         {
-            if (Radio::kSupportedChannelPages & (1 << page))
-            {
-                tlv.GetChannelPages()[length++] = page;
-            }
+            tlv.GetChannelPages()[length++] = page;
         }
 
         tlv.SetLength(length);
@@ -1190,6 +1217,10 @@
             SuccessOrExit(error = Tlv::Read<MaxChildTimeoutTlv>(aMessage, offset, aTlvInfo.mData.mMaxChildTimeout));
             break;
 
+        case Tlv::kEui64:
+            SuccessOrExit(error = Tlv::Read<Eui64Tlv>(aMessage, offset, AsCoreType(&aTlvInfo.mData.mEui64)));
+            break;
+
         case Tlv::kVersion:
             SuccessOrExit(error = Tlv::Read<VersionTlv>(aMessage, offset, aTlvInfo.mData.mVersion));
             break;
@@ -1206,6 +1237,10 @@
             SuccessOrExit(error = Tlv::Read<VendorSwVersionTlv>(aMessage, offset, aTlvInfo.mData.mVendorSwVersion));
             break;
 
+        case Tlv::kVendorAppUrl:
+            SuccessOrExit(error = Tlv::Read<VendorAppUrlTlv>(aMessage, offset, aTlvInfo.mData.mVendorAppUrl));
+            break;
+
         case Tlv::kThreadStackVersion:
             SuccessOrExit(error =
                               Tlv::Read<ThreadStackVersionTlv>(aMessage, offset, aTlvInfo.mData.mThreadStackVersion));
diff --git a/src/core/thread/network_diagnostic.hpp b/src/core/thread/network_diagnostic.hpp
index de16b7c..fb9d2b8 100644
--- a/src/core/thread/network_diagnostic.hpp
+++ b/src/core/thread/network_diagnostic.hpp
@@ -141,10 +141,30 @@
      */
     Error SetVendorSwVersion(const char *aVendorSwVersion);
 
+    /**
+     * Returns the vendor app URL string.
+     *
+     * @returns the vendor app URL string.
+     *
+     */
+    const char *GetVendorAppUrl(void) const { return mVendorAppUrl; }
+
+    /**
+     * Sets the vendor app URL string.
+     *
+     * @param[in] aVendorAppUrl     The vendor app URL string
+     *
+     * @retval kErrorNone         Successfully set the vendor app URL.
+     * @retval kErrorInvalidArgs  @p aVendorAppUrl is not valid (too long or not UTF8).
+     *
+     */
+    Error SetVendorAppUrl(const char *aVendorAppUrl);
+
 #else
     const char *GetVendorName(void) const { return kVendorName; }
     const char *GetVendorModel(void) const { return kVendorModel; }
     const char *GetVendorSwVersion(void) const { return kVendorSwVersion; }
+    const char *GetVendorAppUrl(void) const { return kVendorAppUrl; }
 #endif // OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE
 
 private:
@@ -173,6 +193,7 @@
     static const char kVendorName[];
     static const char kVendorModel[];
     static const char kVendorSwVersion[];
+    static const char kVendorAppUrl[];
 
     Error AppendDiagTlv(uint8_t aTlvType, Message &aMessage);
     Error AppendIp6AddressList(Message &aMessage);
@@ -211,6 +232,7 @@
     VendorNameTlv::StringType      mVendorName;
     VendorModelTlv::StringType     mVendorModel;
     VendorSwVersionTlv::StringType mVendorSwVersion;
+    VendorAppUrlTlv::StringType    mVendorAppUrl;
 #endif
 
 #if OPENTHREAD_FTD
diff --git a/src/core/thread/network_diagnostic_tlvs.hpp b/src/core/thread/network_diagnostic_tlvs.hpp
index e867e0f..acbaf5c 100644
--- a/src/core/thread/network_diagnostic_tlvs.hpp
+++ b/src/core/thread/network_diagnostic_tlvs.hpp
@@ -84,6 +84,7 @@
         kChannelPages        = OT_NETWORK_DIAGNOSTIC_TLV_CHANNEL_PAGES,
         kTypeList            = OT_NETWORK_DIAGNOSTIC_TLV_TYPE_LIST,
         kMaxChildTimeout     = OT_NETWORK_DIAGNOSTIC_TLV_MAX_CHILD_TIMEOUT,
+        kEui64               = OT_NETWORK_DIAGNOSTIC_TLV_EUI64,
         kVersion             = OT_NETWORK_DIAGNOSTIC_TLV_VERSION,
         kVendorName          = OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_NAME,
         kVendorModel         = OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_MODEL,
@@ -95,6 +96,7 @@
         kAnswer              = OT_NETWORK_DIAGNOSTIC_TLV_ANSWER,
         kQueryId             = OT_NETWORK_DIAGNOSTIC_TLV_QUERY_ID,
         kMleCounters         = OT_NETWORK_DIAGNOSTIC_TLV_MLE_COUNTERS,
+        kVendorAppUrl        = OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_APP_URL,
     };
 
     /**
@@ -122,6 +124,12 @@
     static constexpr uint8_t kMaxThreadStackVersionLength = OT_NETWORK_DIAGNOSTIC_MAX_THREAD_STACK_VERSION_TLV_LENGTH;
 
     /**
+     * Maximum length of Vendor SW Version TLV.
+     *
+     */
+    static constexpr uint8_t kMaxVendorAppUrlLength = OT_NETWORK_DIAGNOSTIC_MAX_VENDOR_APP_URL_TLV_LENGTH;
+
+    /**
      * Returns the Type value.
      *
      * @returns The Type value.
@@ -200,6 +208,12 @@
 typedef UintTlvInfo<Tlv::kMaxChildTimeout, uint32_t> MaxChildTimeoutTlv;
 
 /**
+ * Defines Eui64 TLV constants and types.
+ *
+ */
+typedef SimpleTlvInfo<Tlv::kEui64, Mac::ExtAddress> Eui64Tlv;
+
+/**
  * Defines Version TLV constants and types.
  *
  */
@@ -230,6 +244,12 @@
 typedef StringTlvInfo<Tlv::kThreadStackVersion, Tlv::kMaxThreadStackVersionLength> ThreadStackVersionTlv;
 
 /**
+ * Defines Vendor App URL TLV constants and types.
+ *
+ */
+typedef StringTlvInfo<Tlv::kVendorAppUrl, Tlv::kMaxVendorAppUrlLength> VendorAppUrlTlv;
+
+/**
  * Defines Child IPv6 Address List TLV constants and types.
  *
  */
diff --git a/src/core/thread/panid_query_server.cpp b/src/core/thread/panid_query_server.cpp
index 19c40c4..59dd591 100644
--- a/src/core/thread/panid_query_server.cpp
+++ b/src/core/thread/panid_query_server.cpp
@@ -64,7 +64,7 @@
     uint32_t mask;
 
     VerifyOrExit(aMessage.IsPostRequest());
-    VerifyOrExit((mask = MeshCoP::ChannelMaskTlv::GetChannelMask(aMessage)) != 0);
+    SuccessOrExit(MeshCoP::ChannelMaskTlv::FindIn(aMessage, mask));
 
     SuccessOrExit(Tlv::Find<MeshCoP::PanIdTlv>(aMessage, panId));
 
@@ -105,17 +105,14 @@
 
 void PanIdQueryServer::SendConflict(void)
 {
-    Error                   error = kErrorNone;
-    MeshCoP::ChannelMaskTlv channelMask;
-    Tmf::MessageInfo        messageInfo(GetInstance());
-    Coap::Message          *message;
+    Error            error = kErrorNone;
+    Tmf::MessageInfo messageInfo(GetInstance());
+    Coap::Message   *message;
 
     message = Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(kUriPanIdConflict);
     VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
-    channelMask.Init();
-    channelMask.SetChannelMask(mChannelMask);
-    SuccessOrExit(error = channelMask.AppendTo(*message));
+    SuccessOrExit(error = MeshCoP::ChannelMaskTlv::AppendTo(*message, mChannelMask));
 
     SuccessOrExit(error = Tlv::Append<MeshCoP::PanIdTlv>(*message, mPanId));
 
@@ -127,7 +124,7 @@
 
 exit:
     FreeMessageOnError(message, error);
-    MeshCoP::LogError("send panid conflict", error);
+    LogWarnOnError(error, "send panid conflict");
 }
 
 void PanIdQueryServer::HandleTimer(void)
diff --git a/src/core/thread/radio_selector.cpp b/src/core/thread/radio_selector.cpp
index ea7ea74..4268bb0 100644
--- a/src/core/thread/radio_selector.cpp
+++ b/src/core/thread/radio_selector.cpp
@@ -63,7 +63,7 @@
 
 void RadioSelector::NeighborInfo::PopulateMultiRadioInfo(MultiRadioInfo &aInfo)
 {
-    memset(&aInfo, 0, sizeof(MultiRadioInfo));
+    ClearAllBytes(aInfo);
 
 #if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
     if (GetSupportedRadioTypes().Contains(Mac::kRadioTypeIeee802154))
diff --git a/src/core/thread/router.cpp b/src/core/thread/router.cpp
index d16d635..e130b69 100644
--- a/src/core/thread/router.cpp
+++ b/src/core/thread/router.cpp
@@ -71,7 +71,7 @@
 {
     Instance &instance = GetInstance();
 
-    memset(reinterpret_cast<void *>(this), 0, sizeof(Router));
+    ClearAllBytes(*this);
     Init(instance);
 }
 
@@ -92,7 +92,7 @@
 {
     Instance &instance = GetInstance();
 
-    memset(reinterpret_cast<void *>(this), 0, sizeof(Parent));
+    ClearAllBytes(*this);
     Init(instance);
 }
 
diff --git a/src/core/thread/router_table.hpp b/src/core/thread/router_table.hpp
index bf11573..8f2b8f2 100644
--- a/src/core/thread/router_table.hpp
+++ b/src/core/thread/router_table.hpp
@@ -465,7 +465,7 @@
     void HandleTableChanged(void);
     void LogRouteTable(void) const;
 
-    class RouterIdMap
+    class RouterIdMap : public Clearable<RouterIdMap>
     {
     public:
         // The `RouterIdMap` tracks which Router IDs are allocated.
@@ -474,7 +474,6 @@
         // remaining reuse delay time (in seconds).
 
         RouterIdMap(void) { Clear(); }
-        void    Clear(void) { memset(mIndexes, 0, sizeof(mIndexes)); }
         bool    IsAllocated(uint8_t aRouterId) const { return (mIndexes[aRouterId] & kAllocatedFlag); }
         uint8_t GetIndex(uint8_t aRouterId) const { return (mIndexes[aRouterId] & kIndexMask); }
         void    SetIndex(uint8_t aRouterId, uint8_t aIndex) { mIndexes[aRouterId] = kAllocatedFlag | aIndex; }
diff --git a/src/core/thread/thread_netif.cpp b/src/core/thread/thread_netif.cpp
index bba16de..8e68488 100644
--- a/src/core/thread/thread_netif.cpp
+++ b/src/core/thread/thread_netif.cpp
@@ -97,7 +97,7 @@
 #if OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE
     Get<Dns::ServiceDiscovery::Server>().Stop();
 #endif
-#if OPENTHREAD_CONFIG_DTLS_ENABLE
+#if OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE
     Get<Tmf::SecureAgent>().Stop();
 #endif
     IgnoreError(Get<Tmf::Agent>().Stop());
diff --git a/src/core/thread/tmf.cpp b/src/core/thread/tmf.cpp
index 583f6e2..477e4d1 100644
--- a/src/core/thread/tmf.cpp
+++ b/src/core/thread/tmf.cpp
@@ -271,7 +271,7 @@
     return priority;
 }
 
-#if OPENTHREAD_CONFIG_DTLS_ENABLE
+#if OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE
 
 SecureAgent::SecureAgent(Instance &aInstance)
     : Coap::CoapSecure(aInstance)
@@ -329,7 +329,7 @@
     return didHandle;
 }
 
-#endif // OPENTHREAD_CONFIG_DTLS_ENABLE
+#endif // OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE
 
 } // namespace Tmf
 } // namespace ot
diff --git a/src/core/thread/tmf.hpp b/src/core/thread/tmf.hpp
index 3b38544..c90e242 100644
--- a/src/core/thread/tmf.hpp
+++ b/src/core/thread/tmf.hpp
@@ -214,7 +214,7 @@
     static Error Filter(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, void *aContext);
 };
 
-#if OPENTHREAD_CONFIG_DTLS_ENABLE
+#if OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE
 
 /**
  * Implements functionality of the secure TMF agent.
diff --git a/src/core/thread/version.hpp b/src/core/thread/version.hpp
index c3584aa..0277ede 100644
--- a/src/core/thread/version.hpp
+++ b/src/core/thread/version.hpp
@@ -42,10 +42,12 @@
 
 constexpr uint16_t kThreadVersion = OPENTHREAD_CONFIG_THREAD_VERSION; ///< Thread Version of this device.
 
-constexpr uint16_t kThreadVersion1p1   = OT_THREAD_VERSION_1_1;   ///< Thread Version 1.1
-constexpr uint16_t kThreadVersion1p2   = OT_THREAD_VERSION_1_2;   ///< Thread Version 1.2
-constexpr uint16_t kThreadVersion1p3   = OT_THREAD_VERSION_1_3;   ///< Thread Version 1.3
+constexpr uint16_t kThreadVersion1p1 = OT_THREAD_VERSION_1_1; ///< Thread Version 1.1
+constexpr uint16_t kThreadVersion1p2 = OT_THREAD_VERSION_1_2; ///< Thread Version 1.2
+constexpr uint16_t kThreadVersion1p3 = OT_THREAD_VERSION_1_3; ///< Thread Version 1.3
+// Support projects on legacy "1.3.1" version, which is now "1.4"
 constexpr uint16_t kThreadVersion1p3p1 = OT_THREAD_VERSION_1_3_1; ///< Thread Version 1.3.1
+constexpr uint16_t kThreadVersion1p4   = OT_THREAD_VERSION_1_4;   ///< Thread Version 1.4
 
 } // namespace ot
 
diff --git a/src/core/utils/channel_manager.cpp b/src/core/utils/channel_manager.cpp
index 7ed7df4..788a077 100644
--- a/src/core/utils/channel_manager.cpp
+++ b/src/core/utils/channel_manager.cpp
@@ -34,7 +34,9 @@
 
 #include "channel_manager.hpp"
 
-#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && OPENTHREAD_FTD
+#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && \
+    (OPENTHREAD_FTD ||                          \
+     (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE))
 
 #include "common/code_utils.hpp"
 #include "common/locator_getters.hpp"
@@ -54,26 +56,51 @@
     : InstanceLocator(aInstance)
     , mSupportedChannelMask(0)
     , mFavoredChannelMask(0)
+#if OPENTHREAD_FTD
     , mDelay(kMinimumDelay)
+#endif
     , mChannel(0)
+    , mChannelSelected(0)
     , mState(kStateIdle)
     , mTimer(aInstance)
     , mAutoSelectInterval(kDefaultAutoSelectInterval)
+#if OPENTHREAD_FTD
     , mAutoSelectEnabled(false)
+#endif
+#if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
+    , mAutoSelectCslEnabled(false)
+#endif
     , mCcaFailureRateThreshold(kCcaFailureRateThreshold)
 {
 }
 
 void ChannelManager::RequestChannelChange(uint8_t aChannel)
 {
-    LogInfo("Request to change to channel %d with delay %d sec", aChannel, mDelay);
+#if OPENTHREAD_FTD
+    if (Get<Mle::Mle>().IsFullThreadDevice() && Get<Mle::Mle>().IsRxOnWhenIdle() && mAutoSelectEnabled)
+    {
+        RequestNetworkChannelChange(aChannel);
+    }
+#endif
+#if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
+    if (mAutoSelectCslEnabled)
+    {
+        ChangeCslChannel(aChannel);
+    }
+#endif
+}
 
+#if OPENTHREAD_FTD
+void ChannelManager::RequestNetworkChannelChange(uint8_t aChannel)
+{
+    // Check requested channel != current channel
     if (aChannel == Get<Mac::Mac>().GetPanChannel())
     {
         LogInfo("Already operating on the requested channel %d", aChannel);
         ExitNow();
     }
 
+    LogInfo("Request to change to channel %d with delay %d sec", aChannel, mDelay);
     if (mState == kStateChangeInProgress)
     {
         VerifyOrExit(mChannel != aChannel);
@@ -89,7 +116,36 @@
 exit:
     return;
 }
+#endif
 
+#if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
+void ChannelManager::ChangeCslChannel(uint8_t aChannel)
+{
+    if (!(!Get<Mle::Mle>().IsRxOnWhenIdle() && Get<Mac::Mac>().IsCslEnabled()))
+    {
+        // cannot select or use other channel
+        ExitNow();
+    }
+
+    if (aChannel == Get<Mac::Mac>().GetCslChannel())
+    {
+        LogInfo("Already operating on the requested channel %d", aChannel);
+        ExitNow();
+    }
+
+    VerifyOrExit(Radio::IsCslChannelValid(aChannel));
+
+    LogInfo("Change to Csl channel %d now.", aChannel);
+
+    mChannel = aChannel;
+    Get<Mac::Mac>().SetCslChannel(aChannel);
+
+exit:
+    return;
+}
+#endif // (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
+
+#if OPENTHREAD_FTD
 Error ChannelManager::SetDelay(uint16_t aDelay)
 {
     Error error = kErrorNone;
@@ -153,6 +209,7 @@
     mState = kStateIdle;
     StartAutoSelectTimer();
 }
+#endif // OPENTHREAD_FTD
 
 void ChannelManager::HandleTimer(void)
 {
@@ -160,12 +217,14 @@
     {
     case kStateIdle:
         LogInfo("Auto-triggered channel select");
-        IgnoreError(RequestChannelSelect(false));
+        IgnoreError(RequestAutoChannelSelect(false));
         StartAutoSelectTimer();
         break;
 
     case kStateChangeRequested:
+#if OPENTHREAD_FTD
         StartDatasetUpdate();
+#endif
         break;
 
     case kStateChangeInProgress:
@@ -236,6 +295,53 @@
     return shouldAttempt;
 }
 
+#if OPENTHREAD_FTD
+Error ChannelManager::RequestNetworkChannelSelect(bool aSkipQualityCheck)
+{
+    Error error = kErrorNone;
+
+    SuccessOrExit(error = RequestChannelSelect(aSkipQualityCheck));
+    RequestNetworkChannelChange(mChannelSelected);
+
+exit:
+    if ((error == kErrorAbort) || (error == kErrorAlready))
+    {
+        // ignore aborted channel change
+        error = kErrorNone;
+    }
+    return error;
+}
+#endif
+
+#if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
+Error ChannelManager::RequestCslChannelSelect(bool aSkipQualityCheck)
+{
+    Error error = kErrorNone;
+
+    SuccessOrExit(error = RequestChannelSelect(aSkipQualityCheck));
+    ChangeCslChannel(mChannelSelected);
+
+exit:
+    if ((error == kErrorAbort) || (error == kErrorAlready))
+    {
+        // ignore aborted channel change
+        error = kErrorNone;
+    }
+    return error;
+}
+#endif
+
+Error ChannelManager::RequestAutoChannelSelect(bool aSkipQualityCheck)
+{
+    Error error = kErrorNone;
+
+    SuccessOrExit(error = RequestChannelSelect(aSkipQualityCheck));
+    RequestChannelChange(mChannelSelected);
+
+exit:
+    return error;
+}
+
 Error ChannelManager::RequestChannelSelect(bool aSkipQualityCheck)
 {
     Error    error = kErrorNone;
@@ -246,17 +352,27 @@
 
     VerifyOrExit(!Get<Mle::Mle>().IsDisabled(), error = kErrorInvalidState);
 
-    VerifyOrExit(aSkipQualityCheck || ShouldAttemptChannelChange());
+    VerifyOrExit(aSkipQualityCheck || ShouldAttemptChannelChange(), error = kErrorAbort);
 
     SuccessOrExit(error = FindBetterChannel(newChannel, newOccupancy));
 
-    curChannel   = Get<Mac::Mac>().GetPanChannel();
+#if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
+    if (Get<Mac::Mac>().IsCslEnabled() && (Get<Mac::Mac>().GetCslChannel() != 0))
+    {
+        curChannel = Get<Mac::Mac>().GetCslChannel();
+    }
+    else
+#endif
+    {
+        curChannel = Get<Mac::Mac>().GetPanChannel();
+    }
+
     curOccupancy = Get<ChannelMonitor>().GetChannelOccupancy(curChannel);
 
     if (newChannel == curChannel)
     {
         LogInfo("Already on best possible channel %d", curChannel);
-        ExitNow();
+        ExitNow(error = kErrorAlready);
     }
 
     LogInfo("Cur channel %d, occupancy 0x%04x - Best channel %d, occupancy 0x%04x", curChannel, curOccupancy,
@@ -269,18 +385,13 @@
         (static_cast<uint16_t>(curOccupancy - newOccupancy) < kThresholdToChangeChannel))
     {
         LogInfo("Occupancy rate diff too small to change channel");
-        ExitNow();
+        ExitNow(error = kErrorAbort);
     }
 
-    RequestChannelChange(newChannel);
+    mChannelSelected = newChannel;
 
 exit:
-
-    if (error != kErrorNone)
-    {
-        LogInfo("Request to select better channel failed, error: %s", ErrorToString(error));
-    }
-
+    LogWarnOnError(error, "select better channel");
     return error;
 }
 #endif // OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
@@ -289,7 +400,14 @@
 {
     VerifyOrExit(mState == kStateIdle);
 
+#if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && \
+     OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
+    if (mAutoSelectEnabled || mAutoSelectCslEnabled)
+#elif OPENTHREAD_FTD
     if (mAutoSelectEnabled)
+#elif (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
+    if (mAutoSelectCslEnabled)
+#endif
     {
         mTimer.Start(Time::SecToMsec(mAutoSelectInterval));
     }
@@ -302,15 +420,29 @@
     return;
 }
 
-void ChannelManager::SetAutoChannelSelectionEnabled(bool aEnabled)
+#if OPENTHREAD_FTD
+void ChannelManager::SetAutoNetworkChannelSelectionEnabled(bool aEnabled)
 {
     if (aEnabled != mAutoSelectEnabled)
     {
         mAutoSelectEnabled = aEnabled;
-        IgnoreError(RequestChannelSelect(false));
+        IgnoreError(RequestNetworkChannelSelect(false));
         StartAutoSelectTimer();
     }
 }
+#endif
+
+#if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
+void ChannelManager::SetAutoCslChannelSelectionEnabled(bool aEnabled)
+{
+    if (aEnabled != mAutoSelectCslEnabled)
+    {
+        mAutoSelectCslEnabled = aEnabled;
+        IgnoreError(RequestAutoChannelSelect(false));
+        StartAutoSelectTimer();
+    }
+}
+#endif
 
 Error ChannelManager::SetAutoChannelSelectionInterval(uint32_t aInterval)
 {
@@ -321,9 +453,19 @@
 
     mAutoSelectInterval = aInterval;
 
-    if (mAutoSelectEnabled && (mState == kStateIdle) && mTimer.IsRunning() && (prevInterval != aInterval))
+#if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && \
+     OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
+    if (mAutoSelectEnabled || mAutoSelectCslEnabled)
+#elif OPENTHREAD_FTD
+    if (mAutoSelectEnabled)
+#elif (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
+    if (mAutoSelectCslEnabled)
+#endif
     {
-        mTimer.StartAt(mTimer.GetFireTime() - Time::SecToMsec(prevInterval), Time::SecToMsec(aInterval));
+        if ((mState == kStateIdle) && mTimer.IsRunning() && (prevInterval != aInterval))
+        {
+            mTimer.StartAt(mTimer.GetFireTime() - Time::SecToMsec(prevInterval), Time::SecToMsec(aInterval));
+        }
     }
 
 exit:
diff --git a/src/core/utils/channel_manager.hpp b/src/core/utils/channel_manager.hpp
index ad46141..02d9305 100644
--- a/src/core/utils/channel_manager.hpp
+++ b/src/core/utils/channel_manager.hpp
@@ -36,7 +36,9 @@
 
 #include "openthread-core-config.h"
 
-#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && OPENTHREAD_FTD
+#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && \
+    (OPENTHREAD_FTD ||                          \
+     (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE))
 
 #include <openthread/platform/radio.h>
 
@@ -64,11 +66,13 @@
 class ChannelManager : public InstanceLocator, private NonCopyable
 {
 public:
+#if OPENTHREAD_FTD
     /**
      * Minimum delay (in seconds) used for network channel change.
      *
      */
     static constexpr uint16_t kMinimumDelay = OPENTHREAD_CONFIG_CHANNEL_MANAGER_MINIMUM_DELAY;
+#endif
 
     /**
      * Initializes a `ChanelManager` object.
@@ -78,6 +82,7 @@
      */
     explicit ChannelManager(Instance &aInstance);
 
+#if OPENTHREAD_FTD
     /**
      * Requests a Thread network channel change.
      *
@@ -91,16 +96,18 @@
      * @param[in] aChannel             The new channel for the Thread network.
      *
      */
-    void RequestChannelChange(uint8_t aChannel);
+    void RequestNetworkChannelChange(uint8_t aChannel);
+#endif
 
     /**
-     * Gets the channel from the last successful call to `RequestChannelChange()`.
+     * Gets the channel from the last successful call to `RequestNetworkChannelChange()` or `ChangeCslChannel()`.
      *
      * @returns The last requested channel, or zero if there has been no channel change request yet.
      *
      */
     uint8_t GetRequestedChannel(void) const { return mChannel; }
 
+#if OPENTHREAD_FTD
     /**
      * Gets the delay (in seconds) used for a channel change.
      *
@@ -122,9 +129,11 @@
      *
      */
     Error SetDelay(uint16_t aDelay);
+#endif // OPENTHREAD_FTD
 
+#if OPENTHREAD_FTD
     /**
-     * Requests that `ChannelManager` checks and selects a new channel and starts a channel change.
+     * Requests that `ChannelManager` checks and selects a new network channel and starts a network channel change.
      *
      * Unlike the `RequestChannelChange()`  where the channel must be given as a parameter, this method asks the
      * `ChannelManager` to select a channel by itself (based on the collected channel quality info).
@@ -142,7 +151,7 @@
      *    (@sa SetSupportedChannels, @sa SetFavoredChannels).
      *
      * 3) If the newly selected channel is different from the current channel, `ChannelManager` requests/starts the
-     *    channel change process (internally invoking a `RequestChannelChange()`).
+     *    channel change process (internally invoking a `RequestNetworkChannelChange()`).
      *
      *
      * @param[in] aSkipQualityCheck        Indicates whether the quality check (step 1) should be skipped.
@@ -152,8 +161,40 @@
      * @retval kErrorInvalidState      Thread is not enabled or not enough data to select new channel.
      *
      */
-    Error RequestChannelSelect(bool aSkipQualityCheck);
+    Error RequestNetworkChannelSelect(bool aSkipQualityCheck);
+#endif // OPENTHREAD_FTD
 
+#if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
+    /**
+     * Requests that `ChannelManager` checks and selects a new Csl channel and starts a channel change.
+     *
+     * Once called, the `ChannelManager` will perform the following 3 steps:
+     *
+     * 1) `ChannelManager` decides if the channel change would be helpful. This check can be skipped if
+     *    `aSkipQualityCheck` is set to true (forcing a channel selection to happen and skipping the quality check).
+     *    This step uses the collected link quality metrics on the device (such as CCA failure rate, frame and message
+     *    error rates per neighbor, etc.) to determine if the current channel quality is at the level that justifies
+     *    a channel change.
+     *
+     * 2) If the first step passes, then `ChannelManager` selects a potentially better channel. It uses the collected
+     *    channel occupancy data by `ChannelMonitor` module. The supported and favored channels are used at this step.
+     *    (@sa SetSupportedChannels, @sa SetFavoredChannels).
+     *
+     * 3) If the newly selected channel is different from the current Csl channel, `ChannelManager` starts the
+     *    channel change process (internally invoking a `ChangeCslChannel()`).
+     *
+     *
+     * @param[in] aSkipQualityCheck        Indicates whether the quality check (step 1) should be skipped.
+     *
+     * @retval kErrorNone              Channel selection finished successfully.
+     * @retval kErrorNotFound          Supported channels is empty, therefore could not select a channel.
+     * @retval kErrorInvalidState      Thread is not enabled or not enough data to select new channel.
+     *
+     */
+    Error RequestCslChannelSelect(bool aSkipQualityCheck);
+#endif // (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
+
+#if OPENTHREAD_FTD
     /**
      * Enables/disables the auto-channel-selection functionality.
      *
@@ -163,7 +204,7 @@
      * @param[in]  aEnabled  Indicates whether to enable or disable this functionality.
      *
      */
-    void SetAutoChannelSelectionEnabled(bool aEnabled);
+    void SetAutoNetworkChannelSelectionEnabled(bool aEnabled);
 
     /**
      * Indicates whether the auto-channel-selection functionality is enabled or not.
@@ -171,7 +212,29 @@
      * @returns TRUE if enabled, FALSE if disabled.
      *
      */
-    bool GetAutoChannelSelectionEnabled(void) const { return mAutoSelectEnabled; }
+    bool GetAutoNetworkChannelSelectionEnabled(void) const { return mAutoSelectEnabled; }
+#endif
+
+#if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
+    /**
+     * Enables/disables the auto-channel-selection functionality.
+     *
+     * When enabled, `ChannelManager` will periodically invoke a `RequestChannelSelect(false)`. The period interval
+     * can be set by `SetAutoChannelSelectionInterval()`.
+     *
+     * @param[in]  aEnabled  Indicates whether to enable or disable this functionality.
+     *
+     */
+    void SetAutoCslChannelSelectionEnabled(bool aEnabled);
+
+    /**
+     * Indicates whether the auto-channel-selection functionality is enabled or not.
+     *
+     * @returns TRUE if enabled, FALSE if disabled.
+     *
+     */
+    bool GetAutoCslChannelSelectionEnabled(void) const { return mAutoSelectCslEnabled; }
+#endif
 
     /**
      * Sets the period interval (in seconds) used by auto-channel-selection functionality.
@@ -244,7 +307,7 @@
     // Retry interval to resend Pending Dataset in case of tx failure (in ms).
     static constexpr uint32_t kPendingDatasetTxRetryInterval = 20000;
 
-    // Maximum jitter/wait time to start a requested channel change (in ms).
+    // Maximum jitter/wait time to start a requested network channel change (in ms).
     static constexpr uint32_t kRequestStartJitterInterval = 10000;
 
     // The minimum number of RSSI samples required before using the collected data (by `ChannelMonitor`) to select
@@ -273,28 +336,45 @@
         kStateChangeInProgress,
     };
 
+#if OPENTHREAD_FTD
     void        StartDatasetUpdate(void);
     static void HandleDatasetUpdateDone(Error aError, void *aContext);
     void        HandleDatasetUpdateDone(Error aError);
-    void        HandleTimer(void);
-    void        StartAutoSelectTimer(void);
+#endif
+    void  HandleTimer(void);
+    void  StartAutoSelectTimer(void);
+    Error RequestChannelSelect(bool aSkipQualityCheck);
+    Error RequestAutoChannelSelect(bool aSkipQualityCheck);
+    void  RequestChannelChange(uint8_t aChannel);
 
 #if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
     Error FindBetterChannel(uint8_t &aNewChannel, uint16_t &aOccupancy);
     bool  ShouldAttemptChannelChange(void);
 #endif
 
+#if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
+    void ChangeCslChannel(uint8_t aChannel);
+#endif
+
     using ManagerTimer = TimerMilliIn<ChannelManager, &ChannelManager::HandleTimer>;
 
     Mac::ChannelMask mSupportedChannelMask;
     Mac::ChannelMask mFavoredChannelMask;
-    uint16_t         mDelay;
-    uint8_t          mChannel;
-    State            mState;
-    ManagerTimer     mTimer;
-    uint32_t         mAutoSelectInterval;
-    bool             mAutoSelectEnabled;
-    uint16_t         mCcaFailureRateThreshold;
+#if OPENTHREAD_FTD
+    uint16_t mDelay;
+#endif
+    uint8_t      mChannel;
+    uint8_t      mChannelSelected;
+    State        mState;
+    ManagerTimer mTimer;
+    uint32_t     mAutoSelectInterval;
+#if OPENTHREAD_FTD
+    bool mAutoSelectEnabled;
+#endif
+#if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
+    bool mAutoSelectCslEnabled;
+#endif
+    uint16_t mCcaFailureRateThreshold;
 };
 
 /**
diff --git a/src/core/utils/channel_monitor.cpp b/src/core/utils/channel_monitor.cpp
index b707a79..f10dd93 100644
--- a/src/core/utils/channel_monitor.cpp
+++ b/src/core/utils/channel_monitor.cpp
@@ -66,7 +66,7 @@
     , mSampleCount(0)
     , mTimer(aInstance)
 {
-    memset(mChannelOccupancy, 0, sizeof(mChannelOccupancy));
+    ClearAllBytes(mChannelOccupancy);
 }
 
 Error ChannelMonitor::Start(void)
@@ -98,7 +98,7 @@
 {
     mChannelMaskIndex = 0;
     mSampleCount      = 0;
-    memset(mChannelOccupancy, 0, sizeof(mChannelOccupancy));
+    ClearAllBytes(mChannelOccupancy);
 
     LogDebg("Clearing data");
 }
diff --git a/src/core/utils/heap.hpp b/src/core/utils/heap.hpp
index c4125b1..2286844 100644
--- a/src/core/utils/heap.hpp
+++ b/src/core/utils/heap.hpp
@@ -227,7 +227,7 @@
     size_t GetFreeSize(void) const { return mMemory.mFreeSize; }
 
 private:
-#if OPENTHREAD_CONFIG_TLS_ENABLE || OPENTHREAD_CONFIG_DTLS_ENABLE
+#if OPENTHREAD_CONFIG_TLS_ENABLE || OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE
     static constexpr uint16_t kMemorySize = OPENTHREAD_CONFIG_HEAP_INTERNAL_SIZE;
 #else
     static constexpr uint16_t kMemorySize = OPENTHREAD_CONFIG_HEAP_INTERNAL_SIZE_NO_DTLS;
diff --git a/src/core/utils/history_tracker.cpp b/src/core/utils/history_tracker.cpp
index 74d8cfb..1b2a4fd 100644
--- a/src/core/utils/history_tracker.cpp
+++ b/src/core/utils/history_tracker.cpp
@@ -61,7 +61,7 @@
     mTimer.Start(kAgeCheckPeriod);
 
 #if OPENTHREAD_FTD && (OPENTHREAD_CONFIG_HISTORY_TRACKER_ROUTER_LIST_SIZE > 0)
-    memset(mRouterEntries, 0, sizeof(mRouterEntries));
+    ClearAllBytes(mRouterEntries);
 #endif
 }
 
diff --git a/src/core/utils/otns.cpp b/src/core/utils/otns.cpp
index f19185f..a130ca3 100644
--- a/src/core/utils/otns.cpp
+++ b/src/core/utils/otns.cpp
@@ -166,11 +166,9 @@
 
     EmitStatus("coap=send,%d,%d,%d,%s,%s,%d", aMessage.GetMessageId(), aMessage.GetType(), aMessage.GetCode(), uriPath,
                aMessageInfo.GetPeerAddr().ToString().AsCString(), aMessageInfo.GetPeerPort());
+
 exit:
-    if (error != kErrorNone)
-    {
-        LogWarn("EmitCoapSend failed: %s", ErrorToString(error));
-    }
+    LogWarnOnError(error, "EmitCoapSend");
 }
 
 void Otns::EmitCoapReceive(const Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
@@ -183,10 +181,7 @@
     EmitStatus("coap=recv,%d,%d,%d,%s,%s,%d", aMessage.GetMessageId(), aMessage.GetType(), aMessage.GetCode(), uriPath,
                aMessageInfo.GetPeerAddr().ToString().AsCString(), aMessageInfo.GetPeerPort());
 exit:
-    if (error != kErrorNone)
-    {
-        LogWarn("EmitCoapReceive failed: %s", ErrorToString(error));
-    }
+    LogWarnOnError(error, "EmitCoapReceive");
 }
 
 void Otns::EmitCoapSendFailure(Error aError, Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
@@ -200,10 +195,7 @@
                uriPath, aMessageInfo.GetPeerAddr().ToString().AsCString(), aMessageInfo.GetPeerPort(),
                ErrorToString(aError));
 exit:
-    if (error != kErrorNone)
-    {
-        LogWarn("EmitCoapSendFailure failed: %s", ErrorToString(error));
-    }
+    LogWarnOnError(error, "EmitCoapSendFailure");
 }
 
 } // namespace Utils
diff --git a/src/core/utils/slaac_address.cpp b/src/core/utils/slaac_address.cpp
index 7420bdf..95478e3 100644
--- a/src/core/utils/slaac_address.cpp
+++ b/src/core/utils/slaac_address.cpp
@@ -55,8 +55,9 @@
     : InstanceLocator(aInstance)
     , mEnabled(true)
     , mFilter(nullptr)
+    , mTimer(aInstance)
 {
-    memset(mAddresses, 0, sizeof(mAddresses));
+    ClearAllBytes(mSlaacAddresses);
 }
 
 void Slaac::Enable(void)
@@ -77,6 +78,8 @@
     VerifyOrExit(mEnabled);
 
     RemoveAllAddresses();
+    mTimer.Stop();
+
     LogInfo("Disabled");
     mEnabled = false;
 
@@ -92,29 +95,43 @@
     LogInfo("Filter %s", (mFilter != nullptr) ? "updated" : "disabled");
 
     VerifyOrExit(mEnabled);
-    RemoveAddresses();
+    RemoveOrDeprecateAddresses();
     AddAddresses();
 
 exit:
     return;
 }
 
-bool Slaac::ShouldUseForSlaac(const NetworkData::OnMeshPrefixConfig &aConfig) const
+Error Slaac::FindDomainIdFor(const Ip6::Address &aAddress, uint8_t &aDomainId) const
 {
-    bool shouldUse = false;
+    Error error = kErrorNotFound;
 
-    VerifyOrExit(aConfig.mSlaac && !aConfig.mDp);
-    VerifyOrExit(aConfig.GetPrefix().GetLength() == Ip6::NetworkPrefix::kLength);
-
-    if (mFilter != nullptr)
+    for (const SlaacAddress &slaacAddr : mSlaacAddresses)
     {
-        VerifyOrExit(!mFilter(&GetInstance(), &aConfig.GetPrefix()));
+        if (!slaacAddr.IsInUse() || !slaacAddr.IsDeprecating())
+        {
+            continue;
+        }
+
+        if (aAddress.PrefixMatch(slaacAddr.GetAddress()) >= Ip6::NetworkPrefix::kLength)
+        {
+            aDomainId = slaacAddr.GetDomainId();
+            error     = kErrorNone;
+            break;
+        }
     }
 
-    shouldUse = true;
+    return error;
+}
 
-exit:
-    return shouldUse;
+bool Slaac::IsSlaac(const NetworkData::OnMeshPrefixConfig &aConfig) const
+{
+    return aConfig.mSlaac && !aConfig.mDp && (aConfig.GetPrefix().GetLength() == Ip6::NetworkPrefix::kLength);
+}
+
+bool Slaac::IsFiltered(const NetworkData::OnMeshPrefixConfig &aConfig) const
+{
+    return (mFilter != nullptr) ? mFilter(&GetInstance(), &aConfig.GetPrefix()) : false;
 }
 
 void Slaac::HandleNotifierEvents(Events aEvents)
@@ -123,7 +140,7 @@
 
     if (aEvents.Contains(kEventThreadNetdataChanged))
     {
-        RemoveAddresses();
+        RemoveOrDeprecateAddresses();
         AddAddresses();
         ExitNow();
     }
@@ -154,17 +171,18 @@
             (aAddr.GetAddress().MatchesPrefix(aConfig.GetPrefix())));
 }
 
-void Slaac::RemoveAddresses(void)
+void Slaac::RemoveOrDeprecateAddresses(void)
 {
-    // Remove any SLAAC addresses with no matching on-mesh prefix.
+    // Remove or deprecate any SLAAC addresses with no matching on-mesh
+    // prefix in Network Data.
 
-    for (Ip6::Netif::UnicastAddress &slaacAddr : mAddresses)
+    for (SlaacAddress &slaacAddr : mSlaacAddresses)
     {
         NetworkData::Iterator           iterator;
         NetworkData::OnMeshPrefixConfig config;
         bool                            found = false;
 
-        if (!slaacAddr.mValid)
+        if (!slaacAddr.IsInUse())
         {
             continue;
         }
@@ -173,37 +191,61 @@
 
         while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, config) == kErrorNone)
         {
-            if (ShouldUseForSlaac(config) && DoesConfigMatchNetifAddr(config, slaacAddr))
+            if (IsSlaac(config) && DoesConfigMatchNetifAddr(config, slaacAddr))
             {
                 found = true;
                 break;
             }
         }
 
-        if (!found)
+        if (found)
         {
-            RemoveAddress(slaacAddr);
+            if (IsFiltered(config))
+            {
+                RemoveAddress(slaacAddr);
+            }
+        }
+        else if (!slaacAddr.IsDeprecating())
+        {
+            if (slaacAddr.mPreferred)
+            {
+                DeprecateAddress(slaacAddr);
+            }
+            else
+            {
+                RemoveAddress(slaacAddr);
+            }
         }
     }
 }
 
+void Slaac::DeprecateAddress(SlaacAddress &aAddress)
+{
+    LogAddress(kDeprecating, aAddress);
+
+    aAddress.SetExpirationTime(TimerMilli::GetNow() + kDeprecationInterval);
+    mTimer.FireAtIfEarlier(aAddress.GetExpirationTime());
+
+    Get<ThreadNetif>().UpdatePreferredFlagOn(aAddress, false);
+}
+
 void Slaac::RemoveAllAddresses(void)
 {
-    for (Ip6::Netif::UnicastAddress &slaacAddr : mAddresses)
+    for (SlaacAddress &slaacAddr : mSlaacAddresses)
     {
-        if (slaacAddr.mValid)
+        if (slaacAddr.IsInUse())
         {
             RemoveAddress(slaacAddr);
         }
     }
 }
 
-void Slaac::RemoveAddress(Ip6::Netif::UnicastAddress &aAddress)
+void Slaac::RemoveAddress(SlaacAddress &aAddress)
 {
-    LogInfo("Removing %s", aAddress.GetAddress().ToString().AsCString());
+    LogAddress(kRemoving, aAddress);
 
     Get<ThreadNetif>().RemoveUnicastAddress(aAddress);
-    aAddress.mValid = false;
+    aAddress.MarkAsNotInUse();
 }
 
 void Slaac::AddAddresses(void)
@@ -219,7 +261,27 @@
     {
         bool found = false;
 
-        if (!ShouldUseForSlaac(config))
+        if (!IsSlaac(config) || IsFiltered(config))
+        {
+            continue;
+        }
+
+        for (SlaacAddress &slaacAddr : mSlaacAddresses)
+        {
+            if (slaacAddr.IsInUse() && DoesConfigMatchNetifAddr(config, slaacAddr))
+            {
+                if (slaacAddr.IsDeprecating() && config.mPreferred)
+                {
+                    slaacAddr.MarkAsNotDeprecating();
+                    Get<ThreadNetif>().UpdatePreferredFlagOn(slaacAddr, true);
+                }
+
+                found = true;
+                break;
+            }
+        }
+
+        if (found)
         {
             continue;
         }
@@ -242,30 +304,52 @@
 
 void Slaac::AddAddressFor(const NetworkData::OnMeshPrefixConfig &aConfig)
 {
-    Ip6::Netif::UnicastAddress *newAddress = nullptr;
-    uint8_t                     dadCounter = 0;
+    SlaacAddress *newAddress = nullptr;
+    uint8_t       dadCounter = 0;
+    uint8_t       domainId   = 0;
 
-    for (Ip6::Netif::UnicastAddress &slaacAddr : mAddresses)
+    for (SlaacAddress &slaacAddr : mSlaacAddresses)
     {
-        if (!slaacAddr.mValid)
+        // If all address entries are in-use, and we have any
+        // deprecating addresses, we select one with earliest
+        // expiration time.
+
+        if (!slaacAddr.IsInUse())
         {
             newAddress = &slaacAddr;
             break;
         }
+
+        if (slaacAddr.IsDeprecating())
+        {
+            if ((newAddress == nullptr) || slaacAddr.GetExpirationTime() < newAddress->GetExpirationTime())
+            {
+                newAddress = &slaacAddr;
+            }
+        }
     }
 
     if (newAddress == nullptr)
     {
-        LogWarn("Failed to add - already have max %u addresses", kNumAddresses);
+        LogWarn("Failed to add - already have max %u addresses", kNumSlaacAddresses);
         ExitNow();
     }
 
+    if (newAddress->IsInUse())
+    {
+        RemoveAddress(*newAddress);
+    }
+
+    newAddress->MarkAsNotDeprecating();
     newAddress->InitAsSlaacOrigin(aConfig.mOnMesh ? aConfig.GetPrefix().mLength : 128, aConfig.mPreferred);
     newAddress->GetAddress().SetPrefix(aConfig.GetPrefix());
 
+    IgnoreError(Get<NetworkData::Leader>().FindDomainIdFor(aConfig.GetPrefix(), domainId));
+    newAddress->SetDomainId(domainId);
+
     IgnoreError(GenerateIid(*newAddress, dadCounter));
 
-    LogInfo("Adding address %s", newAddress->GetAddress().ToString().AsCString());
+    LogAddress(kAdding, *newAddress);
 
     Get<ThreadNetif>().AddUnicastAddress(*newAddress);
 
@@ -273,6 +357,34 @@
     return;
 }
 
+void Slaac::HandleTimer(void)
+{
+    TimeMilli now      = TimerMilli::GetNow();
+    TimeMilli nextTime = now.GetDistantFuture();
+
+    for (SlaacAddress &slaacAddr : mSlaacAddresses)
+    {
+        if (!slaacAddr.IsInUse() || !slaacAddr.IsDeprecating())
+        {
+            continue;
+        }
+
+        if (slaacAddr.GetExpirationTime() <= now)
+        {
+            RemoveAddress(slaacAddr);
+        }
+        else
+        {
+            nextTime = Min(nextTime, slaacAddr.GetExpirationTime());
+        }
+    }
+
+    if (nextTime != now.GetDistantFuture())
+    {
+        mTimer.FireAtIfEarlier(nextTime);
+    }
+}
+
 Error Slaac::GenerateIid(Ip6::Netif::UnicastAddress &aAddress, uint8_t &aDadCounter) const
 {
     /*
@@ -329,6 +441,25 @@
     return error;
 }
 
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
+void Slaac::LogAddress(Action aAction, const SlaacAddress &aAddress)
+{
+    static const char *const kActionStrings[] = {
+        "Adding",      // (0) kAdding
+        "Removing",    // (1) kRemoving
+        "Deprecating", // (2) kDeprecating
+    };
+
+    static_assert(kAdding == 0, "kAdding value is incorrect");
+    static_assert(kRemoving == 1, "kRemoving value is incorrect");
+    static_assert(kDeprecating == 2, "kDeprecating value is incorrect");
+
+    LogInfo("%s %s", kActionStrings[aAction], aAddress.GetAddress().ToString().AsCString());
+}
+#else
+void Slaac::LogAddress(Action, const SlaacAddress &) {}
+#endif
+
 void Slaac::GetIidSecretKey(IidSecretKey &aKey) const
 {
     Error error;
diff --git a/src/core/utils/slaac_address.hpp b/src/core/utils/slaac_address.hpp
index 99feca2..f741ee2 100644
--- a/src/core/utils/slaac_address.hpp
+++ b/src/core/utils/slaac_address.hpp
@@ -142,25 +142,86 @@
      */
     Error GenerateIid(Ip6::Netif::UnicastAddress &aAddress, uint8_t &aDadCounter) const;
 
+    /**
+     * Searches in the list of deprecating SLAAC prefixes for a match to a given address and if found, returns the
+     * Domain ID from the Prefix TLV in the Network Data for this SLAAC prefix.
+     *
+     * The `Slaac` module keeps track of the associated Domain IDs for deprecating SLAAC prefixes, even if the related
+     * Prefix TLV has already been removed from the Network Data. This information is used during external route lookup
+     * if a deprecating SLAAC address is used as the source address in an outbound message.
+     *
+     * @param[in]  aAddress   The address to search for.
+     * @param[out] aDomainId  A reference to return the Domain ID.
+     *
+     * @retval kErrorNone       Found a match for @p aAddress and updated @p aDomainId.
+     * @retval kErrorNotFound   Could not find a match for @p aAddress in deprecating SLAAC prefixes.
+     *
+     */
+    Error FindDomainIdFor(const Ip6::Address &aAddress, uint8_t &aDomainId) const;
+
 private:
-    static constexpr uint16_t kNumAddresses = OPENTHREAD_CONFIG_IP6_SLAAC_NUM_ADDRESSES;
+    static constexpr uint16_t kNumSlaacAddresses = OPENTHREAD_CONFIG_IP6_SLAAC_NUM_ADDRESSES;
 
     static constexpr uint16_t kMaxIidCreationAttempts = 256; // Maximum number of attempts when generating IID.
 
-    bool        ShouldUseForSlaac(const NetworkData::OnMeshPrefixConfig &aConfig) const;
-    void        RemoveAddresses(void);
+    static constexpr uint32_t kDeprecationInterval =
+        TimeMilli::SecToMsec(OPENTHREAD_CONFIG_IP6_SLAAC_DEPRECATION_INTERVAL);
+
+    enum Action : uint8_t
+    {
+        kAdding,
+        kRemoving,
+        kDeprecating,
+    };
+
+    class SlaacAddress : public Ip6::Netif::UnicastAddress
+    {
+    public:
+        bool      IsInUse(void) const { return mValid; }
+        void      MarkAsNotInUse(void) { mValid = false; }
+        uint8_t   GetDomainId(void) const { return mDomainId; }
+        void      SetDomainId(uint8_t aDomainId) { mDomainId = aDomainId; }
+        bool      IsDeprecating(void) const { return (mExpirationTime.GetValue() != kNotDeprecated); };
+        void      MarkAsNotDeprecating(void) { mExpirationTime.SetValue(kNotDeprecated); }
+        TimeMilli GetExpirationTime(void) const { return mExpirationTime; }
+        void      SetExpirationTime(TimeMilli aTime)
+        {
+            mExpirationTime = aTime;
+
+            if (mExpirationTime.GetValue() == kNotDeprecated)
+            {
+                mExpirationTime.SetValue(kNotDeprecated + 1);
+            }
+        }
+
+    private:
+        static constexpr uint32_t kNotDeprecated = 0; // Special `mExpirationTime` value to indicate not deprecated.
+
+        uint8_t   mDomainId;
+        TimeMilli mExpirationTime;
+    };
+
+    bool        IsSlaac(const NetworkData::OnMeshPrefixConfig &aConfig) const;
+    bool        IsFiltered(const NetworkData::OnMeshPrefixConfig &aConfig) const;
+    void        RemoveOrDeprecateAddresses(void);
     void        RemoveAllAddresses(void);
     void        AddAddresses(void);
-    void        RemoveAddress(Ip6::Netif::UnicastAddress &aAddress);
+    void        DeprecateAddress(SlaacAddress &aAddress);
+    void        RemoveAddress(SlaacAddress &aAddress);
     void        AddAddressFor(const NetworkData::OnMeshPrefixConfig &aConfig);
+    void        HandleTimer(void);
     void        GetIidSecretKey(IidSecretKey &aKey) const;
     void        HandleNotifierEvents(Events aEvents);
+    void        LogAddress(Action aAction, const SlaacAddress &aAddress);
     static bool DoesConfigMatchNetifAddr(const NetworkData::OnMeshPrefixConfig &aConfig,
                                          const Ip6::Netif::UnicastAddress      &aAddr);
 
-    bool                       mEnabled;
-    PrefixFilter               mFilter;
-    Ip6::Netif::UnicastAddress mAddresses[kNumAddresses];
+    using ExpireTimer = TimerMilliIn<Slaac, &Slaac::HandleTimer>;
+
+    bool         mEnabled;
+    PrefixFilter mFilter;
+    ExpireTimer  mTimer;
+    SlaacAddress mSlaacAddresses[kNumSlaacAddresses];
 };
 
 /**
diff --git a/src/lib/platform/reset_util.h b/src/lib/platform/reset_util.h
index 52f5681..56e75da 100644
--- a/src/lib/platform/reset_util.h
+++ b/src/lib/platform/reset_util.h
@@ -32,8 +32,8 @@
 #if defined(OPENTHREAD_ENABLE_COVERAGE) && OPENTHREAD_ENABLE_COVERAGE && defined(__GNUC__)
 #if __GNUC__ >= 11 || (defined(__clang__) && (defined(__APPLE__) && (__clang_major__ >= 13)) || \
                        (!defined(__APPLE__) && (__clang_major__ >= 12)))
-void __gcov_dump();
-void __gcov_reset();
+void __gcov_dump(void);
+void __gcov_reset(void);
 
 static void flush_gcov(void)
 {
diff --git a/src/lib/spinel/BUILD.gn b/src/lib/spinel/BUILD.gn
index f03d945..e3911c0 100644
--- a/src/lib/spinel/BUILD.gn
+++ b/src/lib/spinel/BUILD.gn
@@ -34,6 +34,8 @@
 
 spinel_sources = [
   "openthread-spinel-config.h",
+  "logger.hpp",
+  "logger.cpp",
   "multi_frame_buffer.hpp",
   "radio_spinel.cpp",
   "radio_spinel.hpp",
diff --git a/src/lib/spinel/CMakeLists.txt b/src/lib/spinel/CMakeLists.txt
index e6cf16f..1c21d6e 100644
--- a/src/lib/spinel/CMakeLists.txt
+++ b/src/lib/spinel/CMakeLists.txt
@@ -30,11 +30,23 @@
 add_library(openthread-spinel-ncp)
 add_library(openthread-spinel-rcp)
 
+target_compile_definitions(openthread-radio-spinel PRIVATE
+    OPENTHREAD_FTD=0
+    OPENTHREAD_MTD=0
+    OPENTHREAD_RADIO=0
+)
+
 target_compile_definitions(openthread-spinel-ncp PRIVATE
+    OPENTHREAD_FTD=0
+    OPENTHREAD_MTD=0
+    OPENTHREAD_RADIO=0
     PUBLIC OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE=1
 )
 
 target_compile_definitions(openthread-spinel-rcp PRIVATE
+    OPENTHREAD_FTD=0
+    OPENTHREAD_MTD=0
+    OPENTHREAD_RADIO=0
     PUBLIC OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE=0
 )
 
@@ -62,11 +74,26 @@
     spinel_encoder.cpp
 )
 
+set(OT_SPINEL_VENDOR_HOOK_SOURCE "" CACHE STRING "set vendor hook source file for Spinel")
+set(OT_SPINEL_VENDOR_HOOK_HEADER "" CACHE STRING "set vendor hook header file for Spinel")
+if(OT_SPINEL_VENDOR_HOOK_SOURCE)
+    target_compile_definitions(openthread-spinel-rcp PUBLIC "OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_ENABLE=1")
+    target_compile_definitions(openthread-spinel-ncp PUBLIC "OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_ENABLE=1")
+    target_compile_definitions(openthread-spinel-rcp PUBLIC "OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_HEADER=\"${OT_SPINEL_VENDOR_HOOK_HEADER}\"")
+    target_compile_definitions(openthread-spinel-ncp PUBLIC "OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_HEADER=\"${OT_SPINEL_VENDOR_HOOK_HEADER}\"")
+    list(APPEND COMMON_SOURCES ${OT_SPINEL_VENDOR_HOOK_SOURCE_DIR}${OT_SPINEL_VENDOR_HOOK_SOURCE})
+    list(APPEND COMMON_INCLUDES ${OT_SPINEL_VENDOR_HOOK_SOURCE_DIR})
+endif()
+
 target_include_directories(openthread-radio-spinel PUBLIC ${OT_PUBLIC_INCLUDES} PRIVATE ${COMMON_INCLUDES})
 target_include_directories(openthread-spinel-ncp PUBLIC ${OT_PUBLIC_INCLUDES} PRIVATE ${COMMON_INCLUDES})
 target_include_directories(openthread-spinel-rcp PUBLIC ${OT_PUBLIC_INCLUDES} PRIVATE ${COMMON_INCLUDES})
 
-target_sources(openthread-radio-spinel PRIVATE radio_spinel.cpp)
+target_sources(openthread-radio-spinel
+    PRIVATE
+        logger.cpp
+        radio_spinel.cpp
+)
 target_sources(openthread-spinel-ncp PRIVATE ${COMMON_SOURCES})
 target_sources(openthread-spinel-rcp PRIVATE ${COMMON_SOURCES})
 
diff --git a/src/lib/spinel/example_vendor_hook.cpp b/src/lib/spinel/example_vendor_hook.cpp
new file mode 100644
index 0000000..c43961b
--- /dev/null
+++ b/src/lib/spinel/example_vendor_hook.cpp
@@ -0,0 +1,67 @@
+/*
+ *    Copyright (c) 2023, The OpenThread Authors.
+ *    All rights reserved.
+ *
+ *    Redistribution and use in source and binary forms, with or without
+ *    modification, are permitted provided that the following conditions are met:
+ *    1. Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *    2. Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *    3. Neither the name of the copyright holder nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ *    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ *    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ *    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+ *    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ *    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ *    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ *    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ *   This file shows how to implement the Radio Spinel vendor hook.
+ */
+
+#if OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_ENABLE
+
+#include OPENTRHEAD_SPINEL_CONFIG_VENDOR_HOOK_HEADER
+#include "common/log.hpp"
+#include "lib/platform/exit_code.h"
+
+namespace ot {
+namespace Spinel {
+
+otError RadioSpinel::VendorHandleValueIs(spinel_prop_key_t aPropKey)
+{
+    otError error = OT_ERROR_NONE;
+
+    switch (aPropKey)
+    {
+        // TODO: Implement your property get handlers here.
+        //
+        // Get handler should retrieve the property value and then encode and write the
+        // value into the NCP buffer. If the "get" operation itself fails, handler should
+        // write a `LAST_STATUS` with the error status into the NCP buffer. `OT_ERROR_NO_BUFS`
+        // should be returned if NCP buffer is full and response cannot be written.
+    default:
+        error = OT_ERROR_NOT_FOUND;
+        break;
+    }
+exit:
+    return error;
+}
+
+} // namespace Spinel
+} // namespace ot
+
+extern ot::Spinel::RadioSpinel &GetRadioSpinel(void);
+
+#endif // OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_ENABLE
diff --git a/src/lib/spinel/example_vendor_hook.hpp b/src/lib/spinel/example_vendor_hook.hpp
new file mode 100644
index 0000000..df35e8b
--- /dev/null
+++ b/src/lib/spinel/example_vendor_hook.hpp
@@ -0,0 +1,59 @@
+/*
+ *    Copyright (c) 2023, The OpenThread Authors.
+ *    All rights reserved.
+ *
+ *    Redistribution and use in source and binary forms, with or without
+ *    modification, are permitted provided that the following conditions are met:
+ *    1. Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *    2. Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *    3. Neither the name of the copyright holder nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ *    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ *    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ *    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+ *    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ *    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ *    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ *    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ *   This file shows how to implement the Radio Spinel vendor hook.
+ */
+
+#ifndef SPINEL_EXTENSION_H
+#define SPINEL_EXTENSION_H
+
+#if OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_ENABLE
+
+#include <openthread/thread_ftd.h>
+#include "lib/spinel/radio_spinel.hpp"
+
+namespace ot {
+namespace Spinel {
+
+class VendorRadioSpinel : public ot::Spinel::RadioSpinel
+{
+public:
+    VendorRadioSpinel(void)
+        : ot::Spinel::RadioSpinel()
+    {
+    }
+
+    // Add public/private methods or member variables
+};
+
+} // namespace Spinel
+} // namespace ot
+
+#endif // OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_ENABLE
+#endif // SPINEL_EXTENSION_H
diff --git a/src/lib/spinel/logger.cpp b/src/lib/spinel/logger.cpp
new file mode 100644
index 0000000..46636c7
--- /dev/null
+++ b/src/lib/spinel/logger.cpp
@@ -0,0 +1,748 @@
+/*
+ *  Copyright (c) 2024, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "logger.hpp"
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <openthread/error.h>
+#include <openthread/logging.h>
+#include <openthread/platform/radio.h>
+
+#include "common/code_utils.hpp"
+#include "common/num_utils.hpp"
+#include "lib/spinel/spinel.h"
+
+namespace ot {
+namespace Spinel {
+
+Logger::Logger(const char *aModuleName)
+    : mModuleName(aModuleName)
+{
+}
+
+void Logger::LogIfFail(const char *aText, otError aError)
+{
+    OT_UNUSED_VARIABLE(aText);
+
+    if (aError != OT_ERROR_NONE && aError != OT_ERROR_NO_ACK)
+    {
+        LogWarn("%s: %s", aText, otThreadErrorToString(aError));
+    }
+}
+
+void Logger::LogCrit(const char *aFormat, ...)
+{
+    va_list args;
+
+    va_start(args, aFormat);
+    otLogPlatArgs(OT_LOG_LEVEL_CRIT, mModuleName, aFormat, args);
+    va_end(args);
+}
+
+void Logger::LogWarn(const char *aFormat, ...)
+{
+    va_list args;
+
+    va_start(args, aFormat);
+    otLogPlatArgs(OT_LOG_LEVEL_WARN, mModuleName, aFormat, args);
+    va_end(args);
+}
+
+void Logger::LogNote(const char *aFormat, ...)
+{
+    va_list args;
+
+    va_start(args, aFormat);
+    otLogPlatArgs(OT_LOG_LEVEL_NOTE, mModuleName, aFormat, args);
+    va_end(args);
+}
+
+void Logger::LogInfo(const char *aFormat, ...)
+{
+    va_list args;
+
+    va_start(args, aFormat);
+    otLogPlatArgs(OT_LOG_LEVEL_INFO, mModuleName, aFormat, args);
+    va_end(args);
+}
+
+void Logger::LogDebg(const char *aFormat, ...)
+{
+    va_list args;
+
+    va_start(args, aFormat);
+    otLogPlatArgs(OT_LOG_LEVEL_DEBG, mModuleName, aFormat, args);
+    va_end(args);
+}
+
+uint32_t Logger::Snprintf(char *aDest, uint32_t aSize, const char *aFormat, ...)
+{
+    int     len;
+    va_list args;
+
+    va_start(args, aFormat);
+    len = vsnprintf(aDest, static_cast<size_t>(aSize), aFormat, args);
+    va_end(args);
+
+    return (len < 0) ? 0 : Min(static_cast<uint32_t>(len), aSize - 1);
+}
+
+void Logger::LogSpinelFrame(const uint8_t *aFrame, uint16_t aLength, bool aTx)
+{
+    otError           error                               = OT_ERROR_NONE;
+    char              buf[OPENTHREAD_CONFIG_LOG_MAX_SIZE] = {0};
+    spinel_ssize_t    unpacked;
+    uint8_t           header;
+    uint32_t          cmd;
+    spinel_prop_key_t key;
+    uint8_t          *data;
+    spinel_size_t     len;
+    const char       *prefix = nullptr;
+    char             *start  = buf;
+    char             *end    = buf + sizeof(buf);
+
+    VerifyOrExit(otLoggingGetLevel() >= OT_LOG_LEVEL_DEBG);
+
+    prefix   = aTx ? "Sent spinel frame" : "Received spinel frame";
+    unpacked = spinel_datatype_unpack(aFrame, aLength, "CiiD", &header, &cmd, &key, &data, &len);
+    VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+
+    start += Snprintf(start, static_cast<uint32_t>(end - start), "%s, flg:0x%x, iid:%d, tid:%u, cmd:%s", prefix,
+                      SPINEL_HEADER_GET_FLAG(header), SPINEL_HEADER_GET_IID(header), SPINEL_HEADER_GET_TID(header),
+                      spinel_command_to_cstr(cmd));
+    VerifyOrExit(cmd != SPINEL_CMD_RESET);
+
+    start += Snprintf(start, static_cast<uint32_t>(end - start), ", key:%s", spinel_prop_key_to_cstr(key));
+    VerifyOrExit(cmd != SPINEL_CMD_PROP_VALUE_GET);
+
+    switch (key)
+    {
+    case SPINEL_PROP_LAST_STATUS:
+    {
+        spinel_status_t status;
+
+        unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT_PACKED_S, &status);
+        VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+        start += Snprintf(start, static_cast<uint32_t>(end - start), ", status:%s", spinel_status_to_cstr(status));
+    }
+    break;
+
+    case SPINEL_PROP_MAC_RAW_STREAM_ENABLED:
+    case SPINEL_PROP_MAC_SRC_MATCH_ENABLED:
+    case SPINEL_PROP_PHY_ENABLED:
+    case SPINEL_PROP_RADIO_COEX_ENABLE:
+    {
+        bool enabled;
+
+        unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_BOOL_S, &enabled);
+        VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+        start += Snprintf(start, static_cast<uint32_t>(end - start), ", enabled:%u", enabled);
+    }
+    break;
+
+    case SPINEL_PROP_PHY_CCA_THRESHOLD:
+    case SPINEL_PROP_PHY_FEM_LNA_GAIN:
+    case SPINEL_PROP_PHY_RX_SENSITIVITY:
+    case SPINEL_PROP_PHY_RSSI:
+    case SPINEL_PROP_PHY_TX_POWER:
+    {
+        const char *name = nullptr;
+        int8_t      value;
+
+        unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_INT8_S, &value);
+        VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+
+        switch (key)
+        {
+        case SPINEL_PROP_PHY_TX_POWER:
+            name = "power";
+            break;
+        case SPINEL_PROP_PHY_CCA_THRESHOLD:
+            name = "threshold";
+            break;
+        case SPINEL_PROP_PHY_FEM_LNA_GAIN:
+            name = "gain";
+            break;
+        case SPINEL_PROP_PHY_RX_SENSITIVITY:
+            name = "sensitivity";
+            break;
+        case SPINEL_PROP_PHY_RSSI:
+            name = "rssi";
+            break;
+        }
+
+        start += Snprintf(start, static_cast<uint32_t>(end - start), ", %s:%d", name, value);
+    }
+    break;
+
+    case SPINEL_PROP_MAC_PROMISCUOUS_MODE:
+    case SPINEL_PROP_MAC_SCAN_STATE:
+    case SPINEL_PROP_PHY_CHAN:
+    case SPINEL_PROP_RCP_CSL_ACCURACY:
+    case SPINEL_PROP_RCP_CSL_UNCERTAINTY:
+    {
+        const char *name = nullptr;
+        uint8_t     value;
+
+        unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT8_S, &value);
+        VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+
+        switch (key)
+        {
+        case SPINEL_PROP_MAC_SCAN_STATE:
+            name = "state";
+            break;
+        case SPINEL_PROP_RCP_CSL_ACCURACY:
+            name = "accuracy";
+            break;
+        case SPINEL_PROP_RCP_CSL_UNCERTAINTY:
+            name = "uncertainty";
+            break;
+        case SPINEL_PROP_MAC_PROMISCUOUS_MODE:
+            name = "mode";
+            break;
+        case SPINEL_PROP_PHY_CHAN:
+            name = "channel";
+            break;
+        }
+
+        start += Snprintf(start, static_cast<uint32_t>(end - start), ", %s:%u", name, value);
+    }
+    break;
+
+    case SPINEL_PROP_MAC_15_4_PANID:
+    case SPINEL_PROP_MAC_15_4_SADDR:
+    case SPINEL_PROP_MAC_SCAN_PERIOD:
+    case SPINEL_PROP_PHY_REGION_CODE:
+    {
+        const char *name = nullptr;
+        uint16_t    value;
+
+        unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT16_S, &value);
+        VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+
+        switch (key)
+        {
+        case SPINEL_PROP_MAC_SCAN_PERIOD:
+            name = "period";
+            break;
+        case SPINEL_PROP_PHY_REGION_CODE:
+            name = "region";
+            break;
+        case SPINEL_PROP_MAC_15_4_SADDR:
+            name = "saddr";
+            break;
+        case SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES:
+            name = "saddr";
+            break;
+        case SPINEL_PROP_MAC_15_4_PANID:
+            name = "panid";
+            break;
+        }
+
+        start += Snprintf(start, static_cast<uint32_t>(end - start), ", %s:0x%04x", name, value);
+    }
+    break;
+
+    case SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES:
+    {
+        uint16_t saddr;
+
+        start += Snprintf(start, static_cast<uint32_t>(end - start), ", saddr:");
+
+        if (len < sizeof(saddr))
+        {
+            start += Snprintf(start, static_cast<uint32_t>(end - start), "none");
+        }
+        else
+        {
+            while (len >= sizeof(saddr))
+            {
+                unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT16_S, &saddr);
+                VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+                data += unpacked;
+                len -= static_cast<spinel_size_t>(unpacked);
+                start += Snprintf(start, static_cast<uint32_t>(end - start), "0x%04x ", saddr);
+            }
+        }
+    }
+    break;
+
+    case SPINEL_PROP_RCP_MAC_FRAME_COUNTER:
+    case SPINEL_PROP_RCP_TIMESTAMP:
+    {
+        const char *name;
+        uint32_t    value;
+
+        unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT32_S, &value);
+        VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+
+        name = (key == SPINEL_PROP_RCP_TIMESTAMP) ? "timestamp" : "counter";
+        start += Snprintf(start, static_cast<uint32_t>(end - start), ", %s:%u", name, value);
+    }
+    break;
+
+    case SPINEL_PROP_RADIO_CAPS:
+    case SPINEL_PROP_RCP_API_VERSION:
+    case SPINEL_PROP_RCP_MIN_HOST_API_VERSION:
+    {
+        const char  *name;
+        unsigned int value;
+
+        unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT_PACKED_S, &value);
+        VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+
+        switch (key)
+        {
+        case SPINEL_PROP_RADIO_CAPS:
+            name = "caps";
+            break;
+        case SPINEL_PROP_RCP_API_VERSION:
+            name = "version";
+            break;
+        case SPINEL_PROP_RCP_MIN_HOST_API_VERSION:
+            name = "min-host-version";
+            break;
+        default:
+            name = "";
+            break;
+        }
+
+        start += Snprintf(start, static_cast<uint32_t>(end - start), ", %s:%u", name, value);
+    }
+    break;
+
+    case SPINEL_PROP_MAC_ENERGY_SCAN_RESULT:
+    case SPINEL_PROP_PHY_CHAN_MAX_POWER:
+    {
+        const char *name;
+        uint8_t     channel;
+        int8_t      value;
+
+        unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_INT8_S, &channel, &value);
+        VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+
+        name = (key == SPINEL_PROP_MAC_ENERGY_SCAN_RESULT) ? "rssi" : "power";
+        start += Snprintf(start, static_cast<uint32_t>(end - start), ", channel:%u, %s:%d", channel, name, value);
+    }
+    break;
+
+    case SPINEL_PROP_CAPS:
+    {
+        unsigned int capability;
+
+        start += Snprintf(start, static_cast<uint32_t>(end - start), ", caps:");
+
+        while (len > 0)
+        {
+            unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT_PACKED_S, &capability);
+            VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+            data += unpacked;
+            len -= static_cast<spinel_size_t>(unpacked);
+            start += Snprintf(start, static_cast<uint32_t>(end - start), "%s ", spinel_capability_to_cstr(capability));
+        }
+    }
+    break;
+
+    case SPINEL_PROP_PROTOCOL_VERSION:
+    {
+        unsigned int major;
+        unsigned int minor;
+
+        unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT_PACKED_S SPINEL_DATATYPE_UINT_PACKED_S,
+                                          &major, &minor);
+        VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+        start += Snprintf(start, static_cast<uint32_t>(end - start), ", major:%u, minor:%u", major, minor);
+    }
+    break;
+
+    case SPINEL_PROP_PHY_CHAN_PREFERRED:
+    case SPINEL_PROP_PHY_CHAN_SUPPORTED:
+    {
+        uint8_t        maskBuffer[kChannelMaskBufferSize];
+        uint32_t       channelMask = 0;
+        const uint8_t *maskData    = maskBuffer;
+        spinel_size_t  maskLength  = sizeof(maskBuffer);
+
+        unpacked = spinel_datatype_unpack_in_place(data, len, SPINEL_DATATYPE_DATA_S, maskBuffer, &maskLength);
+        VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+
+        while (maskLength > 0)
+        {
+            uint8_t channel;
+
+            unpacked = spinel_datatype_unpack(maskData, maskLength, SPINEL_DATATYPE_UINT8_S, &channel);
+            VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+            VerifyOrExit(channel < kChannelMaskBufferSize, error = OT_ERROR_PARSE);
+            channelMask |= (1UL << channel);
+
+            maskData += unpacked;
+            maskLength -= static_cast<spinel_size_t>(unpacked);
+        }
+
+        start += Snprintf(start, static_cast<uint32_t>(end - start), ", channelMask:0x%08x", channelMask);
+    }
+    break;
+
+    case SPINEL_PROP_NCP_VERSION:
+    {
+        const char *version;
+
+        unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UTF8_S, &version);
+        VerifyOrExit(unpacked >= 0, error = OT_ERROR_PARSE);
+        start += Snprintf(start, static_cast<uint32_t>(end - start), ", version:%s", version);
+    }
+    break;
+
+    case SPINEL_PROP_STREAM_RAW:
+    {
+        otRadioFrame frame;
+
+        if (cmd == SPINEL_CMD_PROP_VALUE_IS)
+        {
+            uint16_t     flags;
+            int8_t       noiseFloor;
+            unsigned int receiveError;
+
+            unpacked = spinel_datatype_unpack(data, len,
+                                              SPINEL_DATATYPE_DATA_WLEN_S                          // Frame
+                                                  SPINEL_DATATYPE_INT8_S                           // RSSI
+                                                      SPINEL_DATATYPE_INT8_S                       // Noise Floor
+                                                          SPINEL_DATATYPE_UINT16_S                 // Flags
+                                                              SPINEL_DATATYPE_STRUCT_S(            // PHY-data
+                                                                  SPINEL_DATATYPE_UINT8_S          // 802.15.4 channel
+                                                                      SPINEL_DATATYPE_UINT8_S      // 802.15.4 LQI
+                                                                          SPINEL_DATATYPE_UINT64_S // Timestamp (us).
+                                                                  ) SPINEL_DATATYPE_STRUCT_S(      // Vendor-data
+                                                                  SPINEL_DATATYPE_UINT_PACKED_S    // Receive error
+                                                                  ),
+                                              &frame.mPsdu, &frame.mLength, &frame.mInfo.mRxInfo.mRssi, &noiseFloor,
+                                              &flags, &frame.mChannel, &frame.mInfo.mRxInfo.mLqi,
+                                              &frame.mInfo.mRxInfo.mTimestamp, &receiveError);
+            VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+            start += Snprintf(start, static_cast<uint32_t>(end - start), ", len:%u, rssi:%d ...", frame.mLength,
+                              frame.mInfo.mRxInfo.mRssi);
+            OT_UNUSED_VARIABLE(start); // Avoid static analysis error
+            LogDebg("%s", buf);
+
+            start = buf;
+            start += Snprintf(start, static_cast<uint32_t>(end - start),
+                              "... noise:%d, flags:0x%04x, channel:%u, lqi:%u, timestamp:%lu, rxerr:%u", noiseFloor,
+                              flags, frame.mChannel, frame.mInfo.mRxInfo.mLqi,
+                              static_cast<unsigned long>(frame.mInfo.mRxInfo.mTimestamp), receiveError);
+        }
+        else if (cmd == SPINEL_CMD_PROP_VALUE_SET)
+        {
+            bool csmaCaEnabled;
+            bool isHeaderUpdated;
+            bool isARetx;
+            bool skipAes;
+
+            unpacked = spinel_datatype_unpack(
+                data, len,
+                SPINEL_DATATYPE_DATA_WLEN_S                                   // Frame data
+                    SPINEL_DATATYPE_UINT8_S                                   // Channel
+                        SPINEL_DATATYPE_UINT8_S                               // MaxCsmaBackoffs
+                            SPINEL_DATATYPE_UINT8_S                           // MaxFrameRetries
+                                SPINEL_DATATYPE_BOOL_S                        // CsmaCaEnabled
+                                    SPINEL_DATATYPE_BOOL_S                    // IsHeaderUpdated
+                                        SPINEL_DATATYPE_BOOL_S                // IsARetx
+                                            SPINEL_DATATYPE_BOOL_S            // SkipAes
+                                                SPINEL_DATATYPE_UINT32_S      // TxDelay
+                                                    SPINEL_DATATYPE_UINT32_S, // TxDelayBaseTime
+                &frame.mPsdu, &frame.mLength, &frame.mChannel, &frame.mInfo.mTxInfo.mMaxCsmaBackoffs,
+                &frame.mInfo.mTxInfo.mMaxFrameRetries, &csmaCaEnabled, &isHeaderUpdated, &isARetx, &skipAes,
+                &frame.mInfo.mTxInfo.mTxDelay, &frame.mInfo.mTxInfo.mTxDelayBaseTime);
+
+            VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+            start += Snprintf(start, static_cast<uint32_t>(end - start),
+                              ", len:%u, channel:%u, maxbackoffs:%u, maxretries:%u ...", frame.mLength, frame.mChannel,
+                              frame.mInfo.mTxInfo.mMaxCsmaBackoffs, frame.mInfo.mTxInfo.mMaxFrameRetries);
+            OT_UNUSED_VARIABLE(start); // Avoid static analysis error
+            LogDebg("%s", buf);
+
+            start = buf;
+            start += Snprintf(start, static_cast<uint32_t>(end - start),
+                              "... csmaCaEnabled:%u, isHeaderUpdated:%u, isARetx:%u, skipAes:%u"
+                              ", txDelay:%u, txDelayBase:%u",
+                              csmaCaEnabled, isHeaderUpdated, isARetx, skipAes, frame.mInfo.mTxInfo.mTxDelay,
+                              frame.mInfo.mTxInfo.mTxDelayBaseTime);
+        }
+    }
+    break;
+
+    case SPINEL_PROP_STREAM_DEBUG:
+    {
+        char          debugString[OPENTHREAD_CONFIG_NCP_SPINEL_LOG_MAX_SIZE + 1];
+        spinel_size_t stringLength = sizeof(debugString);
+
+        unpacked = spinel_datatype_unpack_in_place(data, len, SPINEL_DATATYPE_DATA_S, debugString, &stringLength);
+        assert(stringLength < sizeof(debugString));
+        VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+        debugString[stringLength] = '\0';
+        start += Snprintf(start, static_cast<uint32_t>(end - start), ", debug:%s", debugString);
+    }
+    break;
+
+    case SPINEL_PROP_STREAM_LOG:
+    {
+        const char *logString;
+        uint8_t     logLevel;
+
+        unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UTF8_S, &logString);
+        VerifyOrExit(unpacked >= 0, error = OT_ERROR_PARSE);
+        data += unpacked;
+        len -= static_cast<spinel_size_t>(unpacked);
+
+        unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT8_S, &logLevel);
+        VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+        start += Snprintf(start, static_cast<uint32_t>(end - start), ", level:%u, log:%s", logLevel, logString);
+    }
+    break;
+
+    case SPINEL_PROP_NEST_STREAM_MFG:
+    {
+        const char *output;
+        size_t      outputLen;
+
+        unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UTF8_S, &output, &outputLen);
+        VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+        start += Snprintf(start, static_cast<uint32_t>(end - start), ", diag:%s", output);
+    }
+    break;
+
+    case SPINEL_PROP_RCP_MAC_KEY:
+    {
+        uint8_t      keyIdMode;
+        uint8_t      keyId;
+        otMacKey     prevKey;
+        unsigned int prevKeyLen = sizeof(otMacKey);
+        otMacKey     currKey;
+        unsigned int currKeyLen = sizeof(otMacKey);
+        otMacKey     nextKey;
+        unsigned int nextKeyLen = sizeof(otMacKey);
+
+        unpacked = spinel_datatype_unpack(data, len,
+                                          SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_DATA_WLEN_S
+                                              SPINEL_DATATYPE_DATA_WLEN_S SPINEL_DATATYPE_DATA_WLEN_S,
+                                          &keyIdMode, &keyId, prevKey.m8, &prevKeyLen, currKey.m8, &currKeyLen,
+                                          nextKey.m8, &nextKeyLen);
+        VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+        start += Snprintf(start, static_cast<uint32_t>(end - start),
+                          ", keyIdMode:%u, keyId:%u, prevKey:***, currKey:***, nextKey:***", keyIdMode, keyId);
+    }
+    break;
+
+    case SPINEL_PROP_HWADDR:
+    case SPINEL_PROP_MAC_15_4_LADDR:
+    {
+        const char *name                    = nullptr;
+        uint8_t     m8[OT_EXT_ADDRESS_SIZE] = {0};
+
+        unpacked = spinel_datatype_unpack_in_place(data, len, SPINEL_DATATYPE_EUI64_S, &m8[0]);
+        VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+
+        name = (key == SPINEL_PROP_HWADDR) ? "eui64" : "laddr";
+        start += Snprintf(start, static_cast<uint32_t>(end - start), ", %s:%02x%02x%02x%02x%02x%02x%02x%02x", name,
+                          m8[0], m8[1], m8[2], m8[3], m8[4], m8[5], m8[6], m8[7]);
+    }
+    break;
+
+    case SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES:
+    {
+        uint8_t m8[OT_EXT_ADDRESS_SIZE];
+
+        start += Snprintf(start, static_cast<uint32_t>(end - start), ", extaddr:");
+
+        if (len < sizeof(m8))
+        {
+            start += Snprintf(start, static_cast<uint32_t>(end - start), "none");
+        }
+        else
+        {
+            while (len >= sizeof(m8))
+            {
+                unpacked = spinel_datatype_unpack_in_place(data, len, SPINEL_DATATYPE_EUI64_S, m8);
+                VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+                data += unpacked;
+                len -= static_cast<spinel_size_t>(unpacked);
+                start += Snprintf(start, static_cast<uint32_t>(end - start), "%02x%02x%02x%02x%02x%02x%02x%02x ", m8[0],
+                                  m8[1], m8[2], m8[3], m8[4], m8[5], m8[6], m8[7]);
+            }
+        }
+    }
+    break;
+
+    case SPINEL_PROP_RADIO_COEX_METRICS:
+    {
+        otRadioCoexMetrics metrics;
+        unpacked = spinel_datatype_unpack(
+            data, len,
+            SPINEL_DATATYPE_STRUCT_S(                                    // Tx Coex Metrics Structure
+                SPINEL_DATATYPE_UINT32_S                                 // NumTxRequest
+                    SPINEL_DATATYPE_UINT32_S                             // NumTxGrantImmediate
+                        SPINEL_DATATYPE_UINT32_S                         // NumTxGrantWait
+                            SPINEL_DATATYPE_UINT32_S                     // NumTxGrantWaitActivated
+                                SPINEL_DATATYPE_UINT32_S                 // NumTxGrantWaitTimeout
+                                    SPINEL_DATATYPE_UINT32_S             // NumTxGrantDeactivatedDuringRequest
+                                        SPINEL_DATATYPE_UINT32_S         // NumTxDelayedGrant
+                                            SPINEL_DATATYPE_UINT32_S     // AvgTxRequestToGrantTime
+                ) SPINEL_DATATYPE_STRUCT_S(                              // Rx Coex Metrics Structure
+                SPINEL_DATATYPE_UINT32_S                                 // NumRxRequest
+                    SPINEL_DATATYPE_UINT32_S                             // NumRxGrantImmediate
+                        SPINEL_DATATYPE_UINT32_S                         // NumRxGrantWait
+                            SPINEL_DATATYPE_UINT32_S                     // NumRxGrantWaitActivated
+                                SPINEL_DATATYPE_UINT32_S                 // NumRxGrantWaitTimeout
+                                    SPINEL_DATATYPE_UINT32_S             // NumRxGrantDeactivatedDuringRequest
+                                        SPINEL_DATATYPE_UINT32_S         // NumRxDelayedGrant
+                                            SPINEL_DATATYPE_UINT32_S     // AvgRxRequestToGrantTime
+                                                SPINEL_DATATYPE_UINT32_S // NumRxGrantNone
+                ) SPINEL_DATATYPE_BOOL_S                                 // Stopped
+                SPINEL_DATATYPE_UINT32_S,                                // NumGrantGlitch
+            &metrics.mNumTxRequest, &metrics.mNumTxGrantImmediate, &metrics.mNumTxGrantWait,
+            &metrics.mNumTxGrantWaitActivated, &metrics.mNumTxGrantWaitTimeout,
+            &metrics.mNumTxGrantDeactivatedDuringRequest, &metrics.mNumTxDelayedGrant,
+            &metrics.mAvgTxRequestToGrantTime, &metrics.mNumRxRequest, &metrics.mNumRxGrantImmediate,
+            &metrics.mNumRxGrantWait, &metrics.mNumRxGrantWaitActivated, &metrics.mNumRxGrantWaitTimeout,
+            &metrics.mNumRxGrantDeactivatedDuringRequest, &metrics.mNumRxDelayedGrant,
+            &metrics.mAvgRxRequestToGrantTime, &metrics.mNumRxGrantNone, &metrics.mStopped, &metrics.mNumGrantGlitch);
+
+        VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+
+        LogDebg("%s ...", buf);
+        LogDebg(" txRequest:%lu", ToUlong(metrics.mNumTxRequest));
+        LogDebg(" txGrantImmediate:%lu", ToUlong(metrics.mNumTxGrantImmediate));
+        LogDebg(" txGrantWait:%lu", ToUlong(metrics.mNumTxGrantWait));
+        LogDebg(" txGrantWaitActivated:%lu", ToUlong(metrics.mNumTxGrantWaitActivated));
+        LogDebg(" txGrantWaitTimeout:%lu", ToUlong(metrics.mNumTxGrantWaitTimeout));
+        LogDebg(" txGrantDeactivatedDuringRequest:%lu", ToUlong(metrics.mNumTxGrantDeactivatedDuringRequest));
+        LogDebg(" txDelayedGrant:%lu", ToUlong(metrics.mNumTxDelayedGrant));
+        LogDebg(" avgTxRequestToGrantTime:%lu", ToUlong(metrics.mAvgTxRequestToGrantTime));
+        LogDebg(" rxRequest:%lu", ToUlong(metrics.mNumRxRequest));
+        LogDebg(" rxGrantImmediate:%lu", ToUlong(metrics.mNumRxGrantImmediate));
+        LogDebg(" rxGrantWait:%lu", ToUlong(metrics.mNumRxGrantWait));
+        LogDebg(" rxGrantWaitActivated:%lu", ToUlong(metrics.mNumRxGrantWaitActivated));
+        LogDebg(" rxGrantWaitTimeout:%lu", ToUlong(metrics.mNumRxGrantWaitTimeout));
+        LogDebg(" rxGrantDeactivatedDuringRequest:%lu", ToUlong(metrics.mNumRxGrantDeactivatedDuringRequest));
+        LogDebg(" rxDelayedGrant:%lu", ToUlong(metrics.mNumRxDelayedGrant));
+        LogDebg(" avgRxRequestToGrantTime:%lu", ToUlong(metrics.mAvgRxRequestToGrantTime));
+        LogDebg(" rxGrantNone:%lu", ToUlong(metrics.mNumRxGrantNone));
+        LogDebg(" stopped:%u", metrics.mStopped);
+
+        start = buf;
+        start += Snprintf(start, static_cast<uint32_t>(end - start), " grantGlitch:%u", metrics.mNumGrantGlitch);
+    }
+    break;
+
+    case SPINEL_PROP_MAC_SCAN_MASK:
+    {
+        constexpr uint8_t kNumChannels = 16;
+        uint8_t           channels[kNumChannels];
+        spinel_size_t     size;
+
+        unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_DATA_S, channels, &size);
+        VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+        start += Snprintf(start, static_cast<uint32_t>(end - start), ", channels:");
+
+        for (spinel_size_t i = 0; i < size; i++)
+        {
+            start += Snprintf(start, static_cast<uint32_t>(end - start), "%u ", channels[i]);
+        }
+    }
+    break;
+
+    case SPINEL_PROP_RCP_ENH_ACK_PROBING:
+    {
+        uint16_t saddr;
+        uint8_t  m8[OT_EXT_ADDRESS_SIZE];
+        uint8_t  flags;
+
+        unpacked = spinel_datatype_unpack(
+            data, len, SPINEL_DATATYPE_UINT16_S SPINEL_DATATYPE_EUI64_S SPINEL_DATATYPE_UINT8_S, &saddr, m8, &flags);
+
+        VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+        start += Snprintf(start, static_cast<uint32_t>(end - start),
+                          ", saddr:%04x, extaddr:%02x%02x%02x%02x%02x%02x%02x%02x, flags:0x%02x", saddr, m8[0], m8[1],
+                          m8[2], m8[3], m8[4], m8[5], m8[6], m8[7], flags);
+    }
+    break;
+
+    case SPINEL_PROP_PHY_CALIBRATED_POWER:
+    {
+        if (cmd == SPINEL_CMD_PROP_VALUE_INSERT)
+        {
+            uint8_t      channel;
+            int16_t      actualPower;
+            uint8_t     *rawPowerSetting;
+            unsigned int rawPowerSettingLength;
+
+            unpacked = spinel_datatype_unpack(
+                data, len, SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_INT16_S SPINEL_DATATYPE_DATA_WLEN_S, &channel,
+                &actualPower, &rawPowerSetting, &rawPowerSettingLength);
+            VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+
+            start += Snprintf(start, static_cast<uint32_t>(end - start),
+                              ", ch:%u, actualPower:%d, rawPowerSetting:", channel, actualPower);
+            for (unsigned int i = 0; i < rawPowerSettingLength; i++)
+            {
+                start += Snprintf(start, static_cast<uint32_t>(end - start), "%02x", rawPowerSetting[i]);
+            }
+        }
+    }
+    break;
+
+    case SPINEL_PROP_PHY_CHAN_TARGET_POWER:
+    {
+        uint8_t channel;
+        int16_t targetPower;
+
+        unpacked =
+            spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_INT16_S, &channel, &targetPower);
+        VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+        start += Snprintf(start, static_cast<uint32_t>(end - start), ", ch:%u, targetPower:%d", channel, targetPower);
+    }
+    break;
+    }
+
+exit:
+    OT_UNUSED_VARIABLE(start); // Avoid static analysis error
+    if (error == OT_ERROR_NONE)
+    {
+        LogDebg("%s", buf);
+    }
+    else if (prefix != nullptr)
+    {
+        LogDebg("%s, failed to parse spinel frame !", prefix);
+    }
+}
+
+} // namespace Spinel
+} // namespace ot
diff --git a/src/lib/spinel/logger.hpp b/src/lib/spinel/logger.hpp
new file mode 100644
index 0000000..ea86f4d
--- /dev/null
+++ b/src/lib/spinel/logger.hpp
@@ -0,0 +1,69 @@
+/*
+ *  Copyright (c) 2024, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SPINEL_LOGGER_HPP_
+#define SPINEL_LOGGER_HPP_
+
+#include "openthread-core-config.h"
+
+#include <openthread/error.h>
+#include <openthread/platform/toolchain.h>
+
+#include "ncp/ncp_config.h"
+
+namespace ot {
+namespace Spinel {
+
+class Logger
+{
+protected:
+    explicit Logger(const char *aModuleName);
+
+    void LogIfFail(const char *aText, otError aError);
+
+    void LogCrit(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(2, 3);
+    void LogWarn(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(2, 3);
+    void LogNote(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(2, 3);
+    void LogInfo(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(2, 3);
+    void LogDebg(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(2, 3);
+
+    uint32_t Snprintf(char *aDest, uint32_t aSize, const char *aFormat, ...);
+    void     LogSpinelFrame(const uint8_t *aFrame, uint16_t aLength, bool aTx);
+
+    enum
+    {
+        kChannelMaskBufferSize = 32, ///< Max buffer size used to store `SPINEL_PROP_PHY_CHAN_SUPPORTED` value.
+    };
+
+    const char *mModuleName;
+};
+
+} // namespace Spinel
+} // namespace ot
+
+#endif // SPINEL_LOG_HPP_
diff --git a/src/lib/spinel/multi_frame_buffer.hpp b/src/lib/spinel/multi_frame_buffer.hpp
index ab1ef83..61945a5 100644
--- a/src/lib/spinel/multi_frame_buffer.hpp
+++ b/src/lib/spinel/multi_frame_buffer.hpp
@@ -371,7 +371,7 @@
 
         aFrame = (aFrame == nullptr) ? mBuffer : aFrame + aLength;
 
-        if (aFrame != mWriteFrameStart)
+        if (HasSavedFrame() && (aFrame != mWriteFrameStart))
         {
             uint16_t totalLength = LittleEndian::ReadUint16(aFrame + kHeaderTotalLengthOffset);
             uint16_t skipLength  = LittleEndian::ReadUint16(aFrame + kHeaderSkipLengthOffset);
diff --git a/src/lib/spinel/openthread-spinel-config.h b/src/lib/spinel/openthread-spinel-config.h
index 6b3316a..75dd496 100644
--- a/src/lib/spinel/openthread-spinel-config.h
+++ b/src/lib/spinel/openthread-spinel-config.h
@@ -89,4 +89,34 @@
 #define OPENTHREAD_SPINEL_CONFIG_BROADCAST_IID SPINEL_HEADER_IID_3
 #endif
 
+/**
+ * @def OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_ENABLE
+ *
+ * Enables compilation of vendor specific code for Spinel
+ *
+ */
+#ifndef OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_ENABLE
+#define OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_ENABLE 0
+#endif
+
+/**
+ * @def OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_HEADER
+ *
+ * Header file defining class VendorRadioSpinel
+ *
+ */
+#ifndef OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_HEADER
+#define OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_HEADER "lib/spinel/example_vendor_hook.hpp"
+#endif
+
+/**
+ * @def OPENTHREAD_SPINEL_CONFIG_RCP_TX_WAIT_TIME_SECS
+ *
+ * Defines the Tx wait duration in seconds.
+ *
+ */
+#ifndef OPENTHREAD_SPINEL_CONFIG_RCP_TX_WAIT_TIME_SECS
+#define OPENTHREAD_SPINEL_CONFIG_RCP_TX_WAIT_TIME_SECS 5
+#endif
+
 #endif // OPENTHREAD_SPINEL_CONFIG_H_
diff --git a/src/lib/spinel/radio_spinel.cpp b/src/lib/spinel/radio_spinel.cpp
index 2cd1dea..b250f09 100644
--- a/src/lib/spinel/radio_spinel.cpp
+++ b/src/lib/spinel/radio_spinel.cpp
@@ -46,6 +46,7 @@
 #include "common/encoding.hpp"
 #include "common/new.hpp"
 #include "lib/platform/exit_code.h"
+#include "lib/spinel/logger.hpp"
 #include "lib/spinel/spinel_decoder.hpp"
 
 namespace ot {
@@ -81,7 +82,8 @@
 }
 
 RadioSpinel::RadioSpinel(void)
-    : mInstance(nullptr)
+    : Logger("RadioSpinel")
+    , mInstance(nullptr)
     , mSpinelInterface(nullptr)
     , mCmdTidsInUse(0)
     , mCmdNextTid(1)
@@ -122,6 +124,10 @@
     , mTxRadioEndUs(UINT64_MAX)
     , mRadioTimeRecalcStart(UINT64_MAX)
     , mRadioTimeOffset(UINT64_MAX)
+#if OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_ENABLE
+    , mVendorRestorePropertiesCallback(nullptr)
+    , mVendorRestorePropertiesContext(nullptr)
+#endif
 {
     memset(mIidList, SPINEL_HEADER_INVALID_IID, sizeof(mIidList));
     memset(&mRadioSpinelMetrics, 0, sizeof(mRadioSpinelMetrics));
@@ -666,6 +672,9 @@
                 ExitNow();
             }
 
+            // this clear is necessary in case the RCP has sent messages between disable and reset
+            mRxFrameBuffer.Clear();
+
             LogInfo("RCP reset: %s", spinel_status_to_cstr(status));
             sIsReady = true;
         }
@@ -747,12 +756,27 @@
             break;
         }
     }
+#if OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_ENABLE
+    else if (aKey >= SPINEL_PROP_VENDOR__BEGIN && aKey < SPINEL_PROP_VENDOR__END)
+    {
+        error = VendorHandleValueIs(aKey);
+    }
+#endif
 
 exit:
     UpdateParseErrorCount(error);
     LogIfFail("Failed to handle ValueIs", error);
 }
 
+#if OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_ENABLE
+void RadioSpinel::SetVendorRestorePropertiesCallback(otRadioSpinelVendorRestorePropertiesCallback aCallback,
+                                                     void                                        *aContext)
+{
+    mVendorRestorePropertiesCallback = aCallback;
+    mVendorRestorePropertiesContext  = aContext;
+}
+#endif
+
 otError RadioSpinel::ParseRadioFrame(otRadioFrame   &aFrame,
                                      const uint8_t  *aBuffer,
                                      uint16_t        aLength,
@@ -1757,8 +1781,18 @@
     }
 
 exit:
-    mState   = kStateTransmitDone;
-    mTxError = error;
+    // A parse error indicates an RCP misbehavior, so recover the RCP immediately.
+    mState = kStateTransmitDone;
+    if (error != OT_ERROR_PARSE)
+    {
+        mTxError = error;
+    }
+    else
+    {
+        mTxError = kErrorAbort;
+        HandleRcpTimeout();
+        RecoverFromRcpFailure();
+    }
     UpdateParseErrorCount(error);
     LogIfFail("Handle transmit done failed", error);
 }
@@ -2122,12 +2156,22 @@
     case kStateSleep:
         break;
     case kStateReceive:
+#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
+        // In case multiple PANs are running, don't force RCP to receive state.
+        IgnoreError(Set(SPINEL_PROP_MAC_RAW_STREAM_ENABLED, SPINEL_DATATYPE_BOOL_S, true));
+#else
         SuccessOrDie(Set(SPINEL_PROP_MAC_RAW_STREAM_ENABLED, SPINEL_DATATYPE_BOOL_S, true));
+#endif
         mState = kStateReceive;
         break;
     case kStateTransmitting:
     case kStateTransmitDone:
+#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
+        // In case multiple PANs are running, don't force RCP to receive state.
+        IgnoreError(Set(SPINEL_PROP_MAC_RAW_STREAM_ENABLED, SPINEL_DATATYPE_BOOL_S, true));
+#else
         SuccessOrDie(Set(SPINEL_PROP_MAC_RAW_STREAM_ENABLED, SPINEL_DATATYPE_BOOL_S, true));
+#endif
         mTxError = OT_ERROR_ABORT;
         mState   = kStateTransmitDone;
         break;
@@ -2152,7 +2196,12 @@
     SuccessOrDie(Set(SPINEL_PROP_MAC_15_4_PANID, SPINEL_DATATYPE_UINT16_S, mPanId));
     SuccessOrDie(Set(SPINEL_PROP_MAC_15_4_SADDR, SPINEL_DATATYPE_UINT16_S, mShortAddress));
     SuccessOrDie(Set(SPINEL_PROP_MAC_15_4_LADDR, SPINEL_DATATYPE_EUI64_S, mExtendedAddress.m8));
+#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
+    // In case multiple PANs are running, don't force RCP to change channel.
+    IgnoreError(Set(SPINEL_PROP_PHY_CHAN, SPINEL_DATATYPE_UINT8_S, mChannel));
+#else
     SuccessOrDie(Set(SPINEL_PROP_PHY_CHAN, SPINEL_DATATYPE_UINT8_S, mChannel));
+#endif
 
     if (mMacKeySet)
     {
@@ -2236,6 +2285,13 @@
         SuccessOrDie(Set(SPINEL_PROP_MAC_RX_ON_WHEN_IDLE_MODE, SPINEL_DATATYPE_BOOL_S, mRxOnWhenIdle));
     }
 
+#if OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_ENABLE
+    if (mVendorRestorePropertiesCallback)
+    {
+        mVendorRestorePropertiesCallback(mVendorRestorePropertiesContext);
+    }
+#endif
+
     CalcRcpTimeOffset();
 }
 #endif // OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
@@ -2397,647 +2453,6 @@
 }
 #endif // OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
 
-uint32_t RadioSpinel::Snprintf(char *aDest, uint32_t aSize, const char *aFormat, ...)
-{
-    int     len;
-    va_list args;
-
-    va_start(args, aFormat);
-    len = vsnprintf(aDest, static_cast<size_t>(aSize), aFormat, args);
-    va_end(args);
-
-    return (len < 0) ? 0 : Min(static_cast<uint32_t>(len), aSize - 1);
-}
-
-void RadioSpinel::LogSpinelFrame(const uint8_t *aFrame, uint16_t aLength, bool aTx)
-{
-    otError           error                               = OT_ERROR_NONE;
-    char              buf[OPENTHREAD_CONFIG_LOG_MAX_SIZE] = {0};
-    spinel_ssize_t    unpacked;
-    uint8_t           header;
-    uint32_t          cmd;
-    spinel_prop_key_t key;
-    uint8_t          *data;
-    spinel_size_t     len;
-    const char       *prefix = nullptr;
-    char             *start  = buf;
-    char             *end    = buf + sizeof(buf);
-
-    VerifyOrExit(otLoggingGetLevel() >= OT_LOG_LEVEL_DEBG);
-
-    prefix   = aTx ? "Sent spinel frame" : "Received spinel frame";
-    unpacked = spinel_datatype_unpack(aFrame, aLength, "CiiD", &header, &cmd, &key, &data, &len);
-    VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
-
-    start += Snprintf(start, static_cast<uint32_t>(end - start), "%s, flg:0x%x, iid:%d, tid:%u, cmd:%s", prefix,
-                      SPINEL_HEADER_GET_FLAG(header), SPINEL_HEADER_GET_IID(header), SPINEL_HEADER_GET_TID(header),
-                      spinel_command_to_cstr(cmd));
-    VerifyOrExit(cmd != SPINEL_CMD_RESET);
-
-    start += Snprintf(start, static_cast<uint32_t>(end - start), ", key:%s", spinel_prop_key_to_cstr(key));
-    VerifyOrExit(cmd != SPINEL_CMD_PROP_VALUE_GET);
-
-    switch (key)
-    {
-    case SPINEL_PROP_LAST_STATUS:
-    {
-        spinel_status_t status;
-
-        unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT_PACKED_S, &status);
-        VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
-        start += Snprintf(start, static_cast<uint32_t>(end - start), ", status:%s", spinel_status_to_cstr(status));
-    }
-    break;
-
-    case SPINEL_PROP_MAC_RAW_STREAM_ENABLED:
-    case SPINEL_PROP_MAC_SRC_MATCH_ENABLED:
-    case SPINEL_PROP_PHY_ENABLED:
-    case SPINEL_PROP_RADIO_COEX_ENABLE:
-    {
-        bool enabled;
-
-        unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_BOOL_S, &enabled);
-        VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
-        start += Snprintf(start, static_cast<uint32_t>(end - start), ", enabled:%u", enabled);
-    }
-    break;
-
-    case SPINEL_PROP_PHY_CCA_THRESHOLD:
-    case SPINEL_PROP_PHY_FEM_LNA_GAIN:
-    case SPINEL_PROP_PHY_RX_SENSITIVITY:
-    case SPINEL_PROP_PHY_RSSI:
-    case SPINEL_PROP_PHY_TX_POWER:
-    {
-        const char *name = nullptr;
-        int8_t      value;
-
-        unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_INT8_S, &value);
-        VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
-
-        switch (key)
-        {
-        case SPINEL_PROP_PHY_TX_POWER:
-            name = "power";
-            break;
-        case SPINEL_PROP_PHY_CCA_THRESHOLD:
-            name = "threshold";
-            break;
-        case SPINEL_PROP_PHY_FEM_LNA_GAIN:
-            name = "gain";
-            break;
-        case SPINEL_PROP_PHY_RX_SENSITIVITY:
-            name = "sensitivity";
-            break;
-        case SPINEL_PROP_PHY_RSSI:
-            name = "rssi";
-            break;
-        }
-
-        start += Snprintf(start, static_cast<uint32_t>(end - start), ", %s:%d", name, value);
-    }
-    break;
-
-    case SPINEL_PROP_MAC_PROMISCUOUS_MODE:
-    case SPINEL_PROP_MAC_SCAN_STATE:
-    case SPINEL_PROP_PHY_CHAN:
-    case SPINEL_PROP_RCP_CSL_ACCURACY:
-    case SPINEL_PROP_RCP_CSL_UNCERTAINTY:
-    {
-        const char *name = nullptr;
-        uint8_t     value;
-
-        unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT8_S, &value);
-        VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
-
-        switch (key)
-        {
-        case SPINEL_PROP_MAC_SCAN_STATE:
-            name = "state";
-            break;
-        case SPINEL_PROP_RCP_CSL_ACCURACY:
-            name = "accuracy";
-            break;
-        case SPINEL_PROP_RCP_CSL_UNCERTAINTY:
-            name = "uncertainty";
-            break;
-        case SPINEL_PROP_MAC_PROMISCUOUS_MODE:
-            name = "mode";
-            break;
-        case SPINEL_PROP_PHY_CHAN:
-            name = "channel";
-            break;
-        }
-
-        start += Snprintf(start, static_cast<uint32_t>(end - start), ", %s:%u", name, value);
-    }
-    break;
-
-    case SPINEL_PROP_MAC_15_4_PANID:
-    case SPINEL_PROP_MAC_15_4_SADDR:
-    case SPINEL_PROP_MAC_SCAN_PERIOD:
-    case SPINEL_PROP_PHY_REGION_CODE:
-    {
-        const char *name = nullptr;
-        uint16_t    value;
-
-        unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT16_S, &value);
-        VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
-
-        switch (key)
-        {
-        case SPINEL_PROP_MAC_SCAN_PERIOD:
-            name = "period";
-            break;
-        case SPINEL_PROP_PHY_REGION_CODE:
-            name = "region";
-            break;
-        case SPINEL_PROP_MAC_15_4_SADDR:
-            name = "saddr";
-            break;
-        case SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES:
-            name = "saddr";
-            break;
-        case SPINEL_PROP_MAC_15_4_PANID:
-            name = "panid";
-            break;
-        }
-
-        start += Snprintf(start, static_cast<uint32_t>(end - start), ", %s:0x%04x", name, value);
-    }
-    break;
-
-    case SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES:
-    {
-        uint16_t saddr;
-
-        start += Snprintf(start, static_cast<uint32_t>(end - start), ", saddr:");
-
-        if (len < sizeof(saddr))
-        {
-            start += Snprintf(start, static_cast<uint32_t>(end - start), "none");
-        }
-        else
-        {
-            while (len >= sizeof(saddr))
-            {
-                unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT16_S, &saddr);
-                VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
-                data += unpacked;
-                len -= static_cast<spinel_size_t>(unpacked);
-                start += Snprintf(start, static_cast<uint32_t>(end - start), "0x%04x ", saddr);
-            }
-        }
-    }
-    break;
-
-    case SPINEL_PROP_RCP_MAC_FRAME_COUNTER:
-    case SPINEL_PROP_RCP_TIMESTAMP:
-    {
-        const char *name;
-        uint32_t    value;
-
-        unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT32_S, &value);
-        VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
-
-        name = (key == SPINEL_PROP_RCP_TIMESTAMP) ? "timestamp" : "counter";
-        start += Snprintf(start, static_cast<uint32_t>(end - start), ", %s:%u", name, value);
-    }
-    break;
-
-    case SPINEL_PROP_RADIO_CAPS:
-    case SPINEL_PROP_RCP_API_VERSION:
-    case SPINEL_PROP_RCP_MIN_HOST_API_VERSION:
-    {
-        const char  *name;
-        unsigned int value;
-
-        unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT_PACKED_S, &value);
-        VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
-
-        switch (key)
-        {
-        case SPINEL_PROP_RADIO_CAPS:
-            name = "caps";
-            break;
-        case SPINEL_PROP_RCP_API_VERSION:
-            name = "version";
-            break;
-        case SPINEL_PROP_RCP_MIN_HOST_API_VERSION:
-            name = "min-host-version";
-            break;
-        default:
-            name = "";
-            break;
-        }
-
-        start += Snprintf(start, static_cast<uint32_t>(end - start), ", %s:%u", name, value);
-    }
-    break;
-
-    case SPINEL_PROP_MAC_ENERGY_SCAN_RESULT:
-    case SPINEL_PROP_PHY_CHAN_MAX_POWER:
-    {
-        const char *name;
-        uint8_t     channel;
-        int8_t      value;
-
-        unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_INT8_S, &channel, &value);
-        VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
-
-        name = (key == SPINEL_PROP_MAC_ENERGY_SCAN_RESULT) ? "rssi" : "power";
-        start += Snprintf(start, static_cast<uint32_t>(end - start), ", channel:%u, %s:%d", channel, name, value);
-    }
-    break;
-
-    case SPINEL_PROP_CAPS:
-    {
-        unsigned int capability;
-
-        start += Snprintf(start, static_cast<uint32_t>(end - start), ", caps:");
-
-        while (len > 0)
-        {
-            unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT_PACKED_S, &capability);
-            VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
-            data += unpacked;
-            len -= static_cast<spinel_size_t>(unpacked);
-            start += Snprintf(start, static_cast<uint32_t>(end - start), "%s ", spinel_capability_to_cstr(capability));
-        }
-    }
-    break;
-
-    case SPINEL_PROP_PROTOCOL_VERSION:
-    {
-        unsigned int major;
-        unsigned int minor;
-
-        unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT_PACKED_S SPINEL_DATATYPE_UINT_PACKED_S,
-                                          &major, &minor);
-        VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
-        start += Snprintf(start, static_cast<uint32_t>(end - start), ", major:%u, minor:%u", major, minor);
-    }
-    break;
-
-    case SPINEL_PROP_PHY_CHAN_PREFERRED:
-    case SPINEL_PROP_PHY_CHAN_SUPPORTED:
-    {
-        uint8_t        maskBuffer[kChannelMaskBufferSize];
-        uint32_t       channelMask = 0;
-        const uint8_t *maskData    = maskBuffer;
-        spinel_size_t  maskLength  = sizeof(maskBuffer);
-
-        unpacked = spinel_datatype_unpack_in_place(data, len, SPINEL_DATATYPE_DATA_S, maskBuffer, &maskLength);
-        VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
-
-        while (maskLength > 0)
-        {
-            uint8_t channel;
-
-            unpacked = spinel_datatype_unpack(maskData, maskLength, SPINEL_DATATYPE_UINT8_S, &channel);
-            VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
-            VerifyOrExit(channel < kChannelMaskBufferSize, error = OT_ERROR_PARSE);
-            channelMask |= (1UL << channel);
-
-            maskData += unpacked;
-            maskLength -= static_cast<spinel_size_t>(unpacked);
-        }
-
-        start += Snprintf(start, static_cast<uint32_t>(end - start), ", channelMask:0x%08x", channelMask);
-    }
-    break;
-
-    case SPINEL_PROP_NCP_VERSION:
-    {
-        const char *version;
-
-        unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UTF8_S, &version);
-        VerifyOrExit(unpacked >= 0, error = OT_ERROR_PARSE);
-        start += Snprintf(start, static_cast<uint32_t>(end - start), ", version:%s", version);
-    }
-    break;
-
-    case SPINEL_PROP_STREAM_RAW:
-    {
-        otRadioFrame frame;
-
-        if (cmd == SPINEL_CMD_PROP_VALUE_IS)
-        {
-            uint16_t     flags;
-            int8_t       noiseFloor;
-            unsigned int receiveError;
-
-            unpacked = spinel_datatype_unpack(data, len,
-                                              SPINEL_DATATYPE_DATA_WLEN_S                          // Frame
-                                                  SPINEL_DATATYPE_INT8_S                           // RSSI
-                                                      SPINEL_DATATYPE_INT8_S                       // Noise Floor
-                                                          SPINEL_DATATYPE_UINT16_S                 // Flags
-                                                              SPINEL_DATATYPE_STRUCT_S(            // PHY-data
-                                                                  SPINEL_DATATYPE_UINT8_S          // 802.15.4 channel
-                                                                      SPINEL_DATATYPE_UINT8_S      // 802.15.4 LQI
-                                                                          SPINEL_DATATYPE_UINT64_S // Timestamp (us).
-                                                                  ) SPINEL_DATATYPE_STRUCT_S(      // Vendor-data
-                                                                  SPINEL_DATATYPE_UINT_PACKED_S    // Receive error
-                                                                  ),
-                                              &frame.mPsdu, &frame.mLength, &frame.mInfo.mRxInfo.mRssi, &noiseFloor,
-                                              &flags, &frame.mChannel, &frame.mInfo.mRxInfo.mLqi,
-                                              &frame.mInfo.mRxInfo.mTimestamp, &receiveError);
-            VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
-            start += Snprintf(start, static_cast<uint32_t>(end - start), ", len:%u, rssi:%d ...", frame.mLength,
-                              frame.mInfo.mRxInfo.mRssi);
-            OT_UNUSED_VARIABLE(start); // Avoid static analysis error
-            LogDebg("%s", buf);
-
-            start = buf;
-            start += Snprintf(start, static_cast<uint32_t>(end - start),
-                              "... noise:%d, flags:0x%04x, channel:%u, lqi:%u, timestamp:%lu, rxerr:%u", noiseFloor,
-                              flags, frame.mChannel, frame.mInfo.mRxInfo.mLqi,
-                              static_cast<unsigned long>(frame.mInfo.mRxInfo.mTimestamp), receiveError);
-        }
-        else if (cmd == SPINEL_CMD_PROP_VALUE_SET)
-        {
-            bool csmaCaEnabled;
-            bool isHeaderUpdated;
-            bool isARetx;
-            bool skipAes;
-
-            unpacked = spinel_datatype_unpack(
-                data, len,
-                SPINEL_DATATYPE_DATA_WLEN_S                                   // Frame data
-                    SPINEL_DATATYPE_UINT8_S                                   // Channel
-                        SPINEL_DATATYPE_UINT8_S                               // MaxCsmaBackoffs
-                            SPINEL_DATATYPE_UINT8_S                           // MaxFrameRetries
-                                SPINEL_DATATYPE_BOOL_S                        // CsmaCaEnabled
-                                    SPINEL_DATATYPE_BOOL_S                    // IsHeaderUpdated
-                                        SPINEL_DATATYPE_BOOL_S                // IsARetx
-                                            SPINEL_DATATYPE_BOOL_S            // SkipAes
-                                                SPINEL_DATATYPE_UINT32_S      // TxDelay
-                                                    SPINEL_DATATYPE_UINT32_S, // TxDelayBaseTime
-                &frame.mPsdu, &frame.mLength, &frame.mChannel, &frame.mInfo.mTxInfo.mMaxCsmaBackoffs,
-                &frame.mInfo.mTxInfo.mMaxFrameRetries, &csmaCaEnabled, &isHeaderUpdated, &isARetx, &skipAes,
-                &frame.mInfo.mTxInfo.mTxDelay, &frame.mInfo.mTxInfo.mTxDelayBaseTime);
-
-            VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
-            start += Snprintf(start, static_cast<uint32_t>(end - start),
-                              ", len:%u, channel:%u, maxbackoffs:%u, maxretries:%u ...", frame.mLength, frame.mChannel,
-                              frame.mInfo.mTxInfo.mMaxCsmaBackoffs, frame.mInfo.mTxInfo.mMaxFrameRetries);
-            OT_UNUSED_VARIABLE(start); // Avoid static analysis error
-            LogDebg("%s", buf);
-
-            start = buf;
-            start += Snprintf(start, static_cast<uint32_t>(end - start),
-                              "... csmaCaEnabled:%u, isHeaderUpdated:%u, isARetx:%u, skipAes:%u"
-                              ", txDelay:%u, txDelayBase:%u",
-                              csmaCaEnabled, isHeaderUpdated, isARetx, skipAes, frame.mInfo.mTxInfo.mTxDelay,
-                              frame.mInfo.mTxInfo.mTxDelayBaseTime);
-        }
-    }
-    break;
-
-    case SPINEL_PROP_STREAM_DEBUG:
-    {
-        char          debugString[OPENTHREAD_CONFIG_NCP_SPINEL_LOG_MAX_SIZE + 1];
-        spinel_size_t stringLength = sizeof(debugString);
-
-        unpacked = spinel_datatype_unpack_in_place(data, len, SPINEL_DATATYPE_DATA_S, debugString, &stringLength);
-        assert(stringLength < sizeof(debugString));
-        VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
-        debugString[stringLength] = '\0';
-        start += Snprintf(start, static_cast<uint32_t>(end - start), ", debug:%s", debugString);
-    }
-    break;
-
-    case SPINEL_PROP_STREAM_LOG:
-    {
-        const char *logString;
-        uint8_t     logLevel;
-
-        unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UTF8_S, &logString);
-        VerifyOrExit(unpacked >= 0, error = OT_ERROR_PARSE);
-        data += unpacked;
-        len -= static_cast<spinel_size_t>(unpacked);
-
-        unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT8_S, &logLevel);
-        VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
-        start += Snprintf(start, static_cast<uint32_t>(end - start), ", level:%u, log:%s", logLevel, logString);
-    }
-    break;
-
-    case SPINEL_PROP_NEST_STREAM_MFG:
-    {
-        const char *output;
-        size_t      outputLen;
-
-        unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UTF8_S, &output, &outputLen);
-        VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
-        start += Snprintf(start, static_cast<uint32_t>(end - start), ", diag:%s", output);
-    }
-    break;
-
-    case SPINEL_PROP_RCP_MAC_KEY:
-    {
-        uint8_t      keyIdMode;
-        uint8_t      keyId;
-        otMacKey     prevKey;
-        unsigned int prevKeyLen = sizeof(otMacKey);
-        otMacKey     currKey;
-        unsigned int currKeyLen = sizeof(otMacKey);
-        otMacKey     nextKey;
-        unsigned int nextKeyLen = sizeof(otMacKey);
-
-        unpacked = spinel_datatype_unpack(data, len,
-                                          SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_DATA_WLEN_S
-                                              SPINEL_DATATYPE_DATA_WLEN_S SPINEL_DATATYPE_DATA_WLEN_S,
-                                          &keyIdMode, &keyId, prevKey.m8, &prevKeyLen, currKey.m8, &currKeyLen,
-                                          nextKey.m8, &nextKeyLen);
-        VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
-        start += Snprintf(start, static_cast<uint32_t>(end - start),
-                          ", keyIdMode:%u, keyId:%u, prevKey:***, currKey:***, nextKey:***", keyIdMode, keyId);
-    }
-    break;
-
-    case SPINEL_PROP_HWADDR:
-    case SPINEL_PROP_MAC_15_4_LADDR:
-    {
-        const char *name                    = nullptr;
-        uint8_t     m8[OT_EXT_ADDRESS_SIZE] = {0};
-
-        unpacked = spinel_datatype_unpack_in_place(data, len, SPINEL_DATATYPE_EUI64_S, &m8[0]);
-        VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
-
-        name = (key == SPINEL_PROP_HWADDR) ? "eui64" : "laddr";
-        start += Snprintf(start, static_cast<uint32_t>(end - start), ", %s:%02x%02x%02x%02x%02x%02x%02x%02x", name,
-                          m8[0], m8[1], m8[2], m8[3], m8[4], m8[5], m8[6], m8[7]);
-    }
-    break;
-
-    case SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES:
-    {
-        uint8_t m8[OT_EXT_ADDRESS_SIZE];
-
-        start += Snprintf(start, static_cast<uint32_t>(end - start), ", extaddr:");
-
-        if (len < sizeof(m8))
-        {
-            start += Snprintf(start, static_cast<uint32_t>(end - start), "none");
-        }
-        else
-        {
-            while (len >= sizeof(m8))
-            {
-                unpacked = spinel_datatype_unpack_in_place(data, len, SPINEL_DATATYPE_EUI64_S, m8);
-                VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
-                data += unpacked;
-                len -= static_cast<spinel_size_t>(unpacked);
-                start += Snprintf(start, static_cast<uint32_t>(end - start), "%02x%02x%02x%02x%02x%02x%02x%02x ", m8[0],
-                                  m8[1], m8[2], m8[3], m8[4], m8[5], m8[6], m8[7]);
-            }
-        }
-    }
-    break;
-
-    case SPINEL_PROP_RADIO_COEX_METRICS:
-    {
-        otRadioCoexMetrics metrics;
-        unpacked = spinel_datatype_unpack(
-            data, len,
-            SPINEL_DATATYPE_STRUCT_S(                                    // Tx Coex Metrics Structure
-                SPINEL_DATATYPE_UINT32_S                                 // NumTxRequest
-                    SPINEL_DATATYPE_UINT32_S                             // NumTxGrantImmediate
-                        SPINEL_DATATYPE_UINT32_S                         // NumTxGrantWait
-                            SPINEL_DATATYPE_UINT32_S                     // NumTxGrantWaitActivated
-                                SPINEL_DATATYPE_UINT32_S                 // NumTxGrantWaitTimeout
-                                    SPINEL_DATATYPE_UINT32_S             // NumTxGrantDeactivatedDuringRequest
-                                        SPINEL_DATATYPE_UINT32_S         // NumTxDelayedGrant
-                                            SPINEL_DATATYPE_UINT32_S     // AvgTxRequestToGrantTime
-                ) SPINEL_DATATYPE_STRUCT_S(                              // Rx Coex Metrics Structure
-                SPINEL_DATATYPE_UINT32_S                                 // NumRxRequest
-                    SPINEL_DATATYPE_UINT32_S                             // NumRxGrantImmediate
-                        SPINEL_DATATYPE_UINT32_S                         // NumRxGrantWait
-                            SPINEL_DATATYPE_UINT32_S                     // NumRxGrantWaitActivated
-                                SPINEL_DATATYPE_UINT32_S                 // NumRxGrantWaitTimeout
-                                    SPINEL_DATATYPE_UINT32_S             // NumRxGrantDeactivatedDuringRequest
-                                        SPINEL_DATATYPE_UINT32_S         // NumRxDelayedGrant
-                                            SPINEL_DATATYPE_UINT32_S     // AvgRxRequestToGrantTime
-                                                SPINEL_DATATYPE_UINT32_S // NumRxGrantNone
-                ) SPINEL_DATATYPE_BOOL_S                                 // Stopped
-                SPINEL_DATATYPE_UINT32_S,                                // NumGrantGlitch
-            &metrics.mNumTxRequest, &metrics.mNumTxGrantImmediate, &metrics.mNumTxGrantWait,
-            &metrics.mNumTxGrantWaitActivated, &metrics.mNumTxGrantWaitTimeout,
-            &metrics.mNumTxGrantDeactivatedDuringRequest, &metrics.mNumTxDelayedGrant,
-            &metrics.mAvgTxRequestToGrantTime, &metrics.mNumRxRequest, &metrics.mNumRxGrantImmediate,
-            &metrics.mNumRxGrantWait, &metrics.mNumRxGrantWaitActivated, &metrics.mNumRxGrantWaitTimeout,
-            &metrics.mNumRxGrantDeactivatedDuringRequest, &metrics.mNumRxDelayedGrant,
-            &metrics.mAvgRxRequestToGrantTime, &metrics.mNumRxGrantNone, &metrics.mStopped, &metrics.mNumGrantGlitch);
-
-        VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
-
-        LogDebg("%s ...", buf);
-        LogDebg(" txRequest:%lu", ToUlong(metrics.mNumTxRequest));
-        LogDebg(" txGrantImmediate:%lu", ToUlong(metrics.mNumTxGrantImmediate));
-        LogDebg(" txGrantWait:%lu", ToUlong(metrics.mNumTxGrantWait));
-        LogDebg(" txGrantWaitActivated:%lu", ToUlong(metrics.mNumTxGrantWaitActivated));
-        LogDebg(" txGrantWaitTimeout:%lu", ToUlong(metrics.mNumTxGrantWaitTimeout));
-        LogDebg(" txGrantDeactivatedDuringRequest:%lu", ToUlong(metrics.mNumTxGrantDeactivatedDuringRequest));
-        LogDebg(" txDelayedGrant:%lu", ToUlong(metrics.mNumTxDelayedGrant));
-        LogDebg(" avgTxRequestToGrantTime:%lu", ToUlong(metrics.mAvgTxRequestToGrantTime));
-        LogDebg(" rxRequest:%lu", ToUlong(metrics.mNumRxRequest));
-        LogDebg(" rxGrantImmediate:%lu", ToUlong(metrics.mNumRxGrantImmediate));
-        LogDebg(" rxGrantWait:%lu", ToUlong(metrics.mNumRxGrantWait));
-        LogDebg(" rxGrantWaitActivated:%lu", ToUlong(metrics.mNumRxGrantWaitActivated));
-        LogDebg(" rxGrantWaitTimeout:%lu", ToUlong(metrics.mNumRxGrantWaitTimeout));
-        LogDebg(" rxGrantDeactivatedDuringRequest:%lu", ToUlong(metrics.mNumRxGrantDeactivatedDuringRequest));
-        LogDebg(" rxDelayedGrant:%lu", ToUlong(metrics.mNumRxDelayedGrant));
-        LogDebg(" avgRxRequestToGrantTime:%lu", ToUlong(metrics.mAvgRxRequestToGrantTime));
-        LogDebg(" rxGrantNone:%lu", ToUlong(metrics.mNumRxGrantNone));
-        LogDebg(" stopped:%u", metrics.mStopped);
-
-        start = buf;
-        start += Snprintf(start, static_cast<uint32_t>(end - start), " grantGlitch:%u", metrics.mNumGrantGlitch);
-    }
-    break;
-
-    case SPINEL_PROP_MAC_SCAN_MASK:
-    {
-        constexpr uint8_t kNumChannels = 16;
-        uint8_t           channels[kNumChannels];
-        spinel_size_t     size;
-
-        unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_DATA_S, channels, &size);
-        VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
-        start += Snprintf(start, static_cast<uint32_t>(end - start), ", channels:");
-
-        for (spinel_size_t i = 0; i < size; i++)
-        {
-            start += Snprintf(start, static_cast<uint32_t>(end - start), "%u ", channels[i]);
-        }
-    }
-    break;
-
-    case SPINEL_PROP_RCP_ENH_ACK_PROBING:
-    {
-        uint16_t saddr;
-        uint8_t  m8[OT_EXT_ADDRESS_SIZE];
-        uint8_t  flags;
-
-        unpacked = spinel_datatype_unpack(
-            data, len, SPINEL_DATATYPE_UINT16_S SPINEL_DATATYPE_EUI64_S SPINEL_DATATYPE_UINT8_S, &saddr, m8, &flags);
-
-        VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
-        start += Snprintf(start, static_cast<uint32_t>(end - start),
-                          ", saddr:%04x, extaddr:%02x%02x%02x%02x%02x%02x%02x%02x, flags:0x%02x", saddr, m8[0], m8[1],
-                          m8[2], m8[3], m8[4], m8[5], m8[6], m8[7], flags);
-    }
-    break;
-
-    case SPINEL_PROP_PHY_CALIBRATED_POWER:
-    {
-        if (cmd == SPINEL_CMD_PROP_VALUE_INSERT)
-        {
-            uint8_t      channel;
-            int16_t      actualPower;
-            uint8_t     *rawPowerSetting;
-            unsigned int rawPowerSettingLength;
-
-            unpacked = spinel_datatype_unpack(
-                data, len, SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_INT16_S SPINEL_DATATYPE_DATA_WLEN_S, &channel,
-                &actualPower, &rawPowerSetting, &rawPowerSettingLength);
-            VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
-
-            start += Snprintf(start, static_cast<uint32_t>(end - start),
-                              ", ch:%u, actualPower:%d, rawPowerSetting:", channel, actualPower);
-            for (unsigned int i = 0; i < rawPowerSettingLength; i++)
-            {
-                start += Snprintf(start, static_cast<uint32_t>(end - start), "%02x", rawPowerSetting[i]);
-            }
-        }
-    }
-    break;
-
-    case SPINEL_PROP_PHY_CHAN_TARGET_POWER:
-    {
-        uint8_t channel;
-        int16_t targetPower;
-
-        unpacked =
-            spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_INT16_S, &channel, &targetPower);
-        VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
-        start += Snprintf(start, static_cast<uint32_t>(end - start), ", ch:%u, targetPower:%d", channel, targetPower);
-    }
-    break;
-    }
-
-exit:
-    OT_UNUSED_VARIABLE(start); // Avoid static analysis error
-    if (error == OT_ERROR_NONE)
-    {
-        LogDebg("%s", buf);
-    }
-    else if (prefix != nullptr)
-    {
-        LogDebg("%s, failed to parse spinel frame !", prefix);
-    }
-}
-
 otError RadioSpinel::SpinelStatusToOtError(spinel_status_t aStatus)
 {
     otError ret;
@@ -3115,62 +2530,5 @@
     return ret;
 }
 
-void RadioSpinel::LogIfFail(const char *aText, otError aError)
-{
-    OT_UNUSED_VARIABLE(aText);
-
-    if (aError != OT_ERROR_NONE && aError != OT_ERROR_NO_ACK)
-    {
-        LogWarn("%s: %s", aText, otThreadErrorToString(aError));
-    }
-}
-
-static const char kModuleName[] = "RadioSpinel";
-
-void RadioSpinel::LogCrit(const char *aFormat, ...)
-{
-    va_list args;
-
-    va_start(args, aFormat);
-    otLogPlatArgs(OT_LOG_LEVEL_CRIT, kModuleName, aFormat, args);
-    va_end(args);
-}
-
-void RadioSpinel::LogWarn(const char *aFormat, ...)
-{
-    va_list args;
-
-    va_start(args, aFormat);
-    otLogPlatArgs(OT_LOG_LEVEL_WARN, kModuleName, aFormat, args);
-    va_end(args);
-}
-
-void RadioSpinel::LogNote(const char *aFormat, ...)
-{
-    va_list args;
-
-    va_start(args, aFormat);
-    otLogPlatArgs(OT_LOG_LEVEL_NOTE, kModuleName, aFormat, args);
-    va_end(args);
-}
-
-void RadioSpinel::LogInfo(const char *aFormat, ...)
-{
-    va_list args;
-
-    va_start(args, aFormat);
-    otLogPlatArgs(OT_LOG_LEVEL_INFO, kModuleName, aFormat, args);
-    va_end(args);
-}
-
-void RadioSpinel::LogDebg(const char *aFormat, ...)
-{
-    va_list args;
-
-    va_start(args, aFormat);
-    otLogPlatArgs(OT_LOG_LEVEL_DEBG, kModuleName, aFormat, args);
-    va_end(args);
-}
-
 } // namespace Spinel
 } // namespace ot
diff --git a/src/lib/spinel/radio_spinel.hpp b/src/lib/spinel/radio_spinel.hpp
index da0b652..5f55c75 100644
--- a/src/lib/spinel/radio_spinel.hpp
+++ b/src/lib/spinel/radio_spinel.hpp
@@ -38,6 +38,7 @@
 
 #include "openthread-spinel-config.h"
 #include "core/radio/max_power_table.hpp"
+#include "lib/spinel/logger.hpp"
 #include "lib/spinel/radio_spinel_metrics.h"
 #include "lib/spinel/spinel.h"
 #include "lib/spinel/spinel_interface.hpp"
@@ -148,7 +149,7 @@
  * co-processor(RCP).
  *
  */
-class RadioSpinel
+class RadioSpinel : private Logger
 {
 public:
     /**
@@ -1065,6 +1066,44 @@
      */
     void RestoreProperties(void);
 #endif
+#if OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_ENABLE
+    /**
+     * Defines a vendor "set property handler" hook to process vendor spinel properties.
+     *
+     * The vendor handler should return `OT_ERROR_NOT_FOUND` status if it does not support "set" operation for the
+     * given property key. Otherwise, the vendor handler should behave like other property set handlers, i.e., it
+     * should first decode the value from the input spinel frame and then perform the corresponding set operation. The
+     * handler should not prepare the spinel response and therefore should not write anything to the NCP buffer. The
+     * `otError` returned from handler (other than `OT_ERROR_NOT_FOUND`) indicates the error in either parsing of the
+     * input or the error of the set operation. In case of a successful "set", `NcpBase` set command handler will call
+     * the `VendorGetPropertyHandler()` for the same property key to prepare the response.
+     *
+     * @param[in] aPropKey  The spinel property key.
+     *
+     * @returns OT_ERROR_NOT_FOUND if it does not support the given property key, otherwise the error in either parsing
+     *          of the input or the "set" operation.
+     *
+     */
+    otError VendorHandleValueIs(spinel_prop_key_t aPropKey);
+
+    /**
+     *  A callback type for restoring vendor properties.
+     *
+     */
+    typedef void (*otRadioSpinelVendorRestorePropertiesCallback)(void *context);
+
+    /**
+     * Registers a callback to restore vendor properties.
+     *
+     * This function is used to register a callback for vendor properties recovery. When an event which needs to restore
+     * properties occurs (such as an unexpected RCP reset), the user can restore the vendor properties via the callback.
+     *
+     * @param[in] aCallback The callback.
+     * @param[in] aContext  The context.
+     *
+     */
+    void SetVendorRestorePropertiesCallback(otRadioSpinelVendorRestorePropertiesCallback aCallback, void *aContext);
+#endif
 
 private:
     enum
@@ -1085,9 +1124,12 @@
         kStateTransmitDone, ///< Radio indicated frame transmission is done.
     };
 
-    static constexpr uint32_t kUsPerMs = 1000; ///< Microseconds per millisecond.
+    static constexpr uint32_t kUsPerMs  = 1000;                 ///< Microseconds per millisecond.
+    static constexpr uint32_t kMsPerSec = 1000;                 ///< Milliseconds per second.
+    static constexpr uint32_t kUsPerSec = kUsPerMs * kMsPerSec; ///< Microseconds per second.
     static constexpr uint64_t kTxWaitUs =
-        5000000; ///< Maximum time of waiting for `TransmitDone` event, in microseconds.
+        OPENTHREAD_SPINEL_CONFIG_RCP_TX_WAIT_TIME_SECS *
+        kUsPerSec; ///< Maximum time of waiting for `TransmitDone` event, in microseconds.
 
     typedef otError (RadioSpinel::*ResponseHandler)(const uint8_t *aBuffer, uint16_t aLength);
 
@@ -1197,17 +1239,6 @@
     static otError ReadMacKey(const otMacKeyMaterial &aKeyMaterial, otMacKey &aKey);
 #endif
 
-    static void LogIfFail(const char *aText, otError aError);
-
-    static void LogCrit(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(1, 2);
-    static void LogWarn(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(1, 2);
-    static void LogNote(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(1, 2);
-    static void LogInfo(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(1, 2);
-    static void LogDebg(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(1, 2);
-
-    uint32_t Snprintf(char *aDest, uint32_t aSize, const char *aFormat, ...);
-    void     LogSpinelFrame(const uint8_t *aFrame, uint16_t aLength, bool aTx);
-
     otInstance *mInstance;
 
     SpinelInterface::RxFrameBuffer mRxFrameBuffer;
@@ -1256,8 +1287,7 @@
 
 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
 
-    enum
-    {
+    enum {
         kRcpFailureNone,
         kRcpFailureTimeout,
         kRcpFailureUnexpectedReset,
@@ -1308,6 +1338,11 @@
     MaxPowerTable mMaxPowerTable;
 
     otRadioSpinelMetrics mRadioSpinelMetrics;
+
+#if OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_ENABLE
+    otRadioSpinelVendorRestorePropertiesCallback mVendorRestorePropertiesCallback;
+    void                                        *mVendorRestorePropertiesContext;
+#endif
 };
 
 } // namespace Spinel
diff --git a/src/lib/spinel/spinel.c b/src/lib/spinel/spinel.c
index 82ea555..c85d244 100644
--- a/src/lib/spinel/spinel.c
+++ b/src/lib/spinel/spinel.c
@@ -1500,11 +1500,9 @@
 const char *spinel_net_role_to_cstr(uint8_t net_role)
 {
     static const struct spinel_cstr spinel_net_cstr[] = {
-        {SPINEL_NET_ROLE_DETACHED, "NET_ROLE_DETACHED"},
-        {SPINEL_NET_ROLE_CHILD, "NET_ROLE_CHILD"},
-        {SPINEL_NET_ROLE_ROUTER, "NET_ROLE_ROUTER"},
-        {SPINEL_NET_ROLE_LEADER, "NET_ROLE_LEADER"},
-        {0, NULL},
+        {SPINEL_NET_ROLE_DETACHED, "NET_ROLE_DETACHED"}, {SPINEL_NET_ROLE_CHILD, "NET_ROLE_CHILD"},
+        {SPINEL_NET_ROLE_ROUTER, "NET_ROLE_ROUTER"},     {SPINEL_NET_ROLE_LEADER, "NET_ROLE_LEADER"},
+        {SPINEL_NET_ROLE_DISABLED, "NET_ROLE_DISABLED"}, {0, NULL},
     };
 
     return spinel_to_cstr(spinel_net_cstr, net_role);
diff --git a/src/lib/spinel/spinel.h b/src/lib/spinel/spinel.h
index 5d8094d..d91a039 100644
--- a/src/lib/spinel/spinel.h
+++ b/src/lib/spinel/spinel.h
@@ -598,6 +598,7 @@
     SPINEL_NET_ROLE_CHILD    = 1,
     SPINEL_NET_ROLE_ROUTER   = 2,
     SPINEL_NET_ROLE_LEADER   = 3,
+    SPINEL_NET_ROLE_DISABLED = 4,
 } spinel_net_role_t;
 
 typedef enum
@@ -2296,6 +2297,7 @@
      *  SPINEL_NET_ROLE_CHILD    = 1,
      *  SPINEL_NET_ROLE_ROUTER   = 2,
      *  SPINEL_NET_ROLE_LEADER   = 3,
+     *  SPINEL_NET_ROLE_DISABLED = 4,
      *
      */
     SPINEL_PROP_NET_ROLE = SPINEL_PROP_NET__BEGIN + 3,
diff --git a/src/lib/spinel/spinel_encoder.cpp b/src/lib/spinel/spinel_encoder.cpp
index 91a9ec3..6cd3904 100644
--- a/src/lib/spinel/spinel_encoder.cpp
+++ b/src/lib/spinel/spinel_encoder.cpp
@@ -291,5 +291,7 @@
     return error;
 }
 
+void Encoder::ClearNcpBuffer(void) { mNcpBuffer.Clear(); }
+
 } // namespace Spinel
 } // namespace ot
diff --git a/src/lib/spinel/spinel_encoder.hpp b/src/lib/spinel/spinel_encoder.hpp
index 6b9ea98..c678519 100644
--- a/src/lib/spinel/spinel_encoder.hpp
+++ b/src/lib/spinel/spinel_encoder.hpp
@@ -676,6 +676,12 @@
      */
     otError ResetToSaved(void);
 
+    /**
+     * Clear NCP buffer on reset command.
+     *
+     */
+    void ClearNcpBuffer(void);
+
 private:
     enum
     {
diff --git a/src/ncp/ftd.cmake b/src/ncp/ftd.cmake
index 43b5f4f..fbf230a 100644
--- a/src/ncp/ftd.cmake
+++ b/src/ncp/ftd.cmake
@@ -30,6 +30,8 @@
 
 target_compile_definitions(openthread-ncp-ftd PRIVATE
     OPENTHREAD_FTD=1
+    OPENTHREAD_MTD=0
+    OPENTHREAD_RADIO=0
     OPENTHREAD_CONFIG_NCP_HDLC_ENABLE=1
 )
 
diff --git a/src/ncp/mtd.cmake b/src/ncp/mtd.cmake
index 6d8de8e..5bcf378 100644
--- a/src/ncp/mtd.cmake
+++ b/src/ncp/mtd.cmake
@@ -29,7 +29,9 @@
 add_library(openthread-ncp-mtd)
 
 target_compile_definitions(openthread-ncp-mtd PRIVATE
+    OPENTHREAD_FTD=0
     OPENTHREAD_MTD=1
+    OPENTHREAD_RADIO=0
     OPENTHREAD_CONFIG_NCP_HDLC_ENABLE=1
 )
 
diff --git a/src/ncp/ncp_base.cpp b/src/ncp/ncp_base.cpp
index e0b4c18..a1ad3ad 100644
--- a/src/ncp/ncp_base.cpp
+++ b/src/ncp/ncp_base.cpp
@@ -1285,6 +1285,8 @@
 
         ResetCounters();
 
+        mEncoder.ClearNcpBuffer();
+
         SuccessOrAssert(error = WriteLastStatusFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_TX_NOTIFICATION_IID,
                                                      SPINEL_STATUS_RESET_POWER_ON));
     }
diff --git a/src/ncp/ncp_base_dispatcher.cpp b/src/ncp/ncp_base_dispatcher.cpp
index e653f83..48bd97e 100644
--- a/src/ncp/ncp_base_dispatcher.cpp
+++ b/src/ncp/ncp_base_dispatcher.cpp
@@ -433,7 +433,9 @@
         OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_MAC_PROMISCUOUS_MODE),
 #if OPENTHREAD_MTD || OPENTHREAD_FTD
         OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_MAC_DATA_POLL_PERIOD),
+#endif
         OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_MAC_RX_ON_WHEN_IDLE_MODE),
+#if OPENTHREAD_MTD || OPENTHREAD_FTD
         OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_NET_IF_UP),
         OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_NET_STACK_UP),
         OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_NET_ROLE),
diff --git a/src/ncp/ncp_base_mtd.cpp b/src/ncp/ncp_base_mtd.cpp
index f5b06dd..aa87efa 100644
--- a/src/ncp/ncp_base_mtd.cpp
+++ b/src/ncp/ncp_base_mtd.cpp
@@ -536,6 +536,9 @@
     switch (otThreadGetDeviceRole(mInstance))
     {
     case OT_DEVICE_ROLE_DISABLED:
+        role = SPINEL_NET_ROLE_DISABLED;
+        break;
+
     case OT_DEVICE_ROLE_DETACHED:
         role = SPINEL_NET_ROLE_DETACHED;
         break;
@@ -687,7 +690,7 @@
 
     SuccessOrExit(error = mDecoder.ReadUint32(keyGuardTime));
 
-    otThreadSetKeySwitchGuardTime(mInstance, keyGuardTime);
+    otThreadSetKeySwitchGuardTime(mInstance, static_cast<uint16_t>(keyGuardTime));
 
 exit:
     return error;
@@ -2266,6 +2269,7 @@
     // STREAM_NET requires layer 2 security.
     message = otIp6NewMessageFromBuffer(mInstance, framePtr, frameLen, nullptr);
     VerifyOrExit(message != nullptr, error = OT_ERROR_NO_BUFS);
+    otMessageSetOrigin(message, OT_MESSAGE_ORIGIN_HOST_UNTRUSTED);
 
     error = otIp6Send(mInstance, message);
 
@@ -3300,6 +3304,7 @@
     // STREAM_NET_INSECURE packets are not secured at layer 2.
     message = otIp6NewMessageFromBuffer(mInstance, framePtr, frameLen, &msgSettings);
     VerifyOrExit(message != nullptr, error = OT_ERROR_NO_BUFS);
+    otMessageSetOrigin(message, OT_MESSAGE_ORIGIN_HOST_UNTRUSTED);
 
     // Ensure the insecure message is forwarded using direct transmission.
     otMessageSetDirectTransmission(message, true);
diff --git a/src/ncp/radio.cmake b/src/ncp/radio.cmake
index 1350a28..23ec4e0 100644
--- a/src/ncp/radio.cmake
+++ b/src/ncp/radio.cmake
@@ -29,6 +29,8 @@
 add_library(openthread-rcp)
 
 target_compile_definitions(openthread-rcp PRIVATE
+    OPENTHREAD_FTD=0
+    OPENTHREAD_MTD=0
     OPENTHREAD_RADIO=1
     OPENTHREAD_RADIO_CLI=0
 )
diff --git a/src/posix/CMakeLists.txt b/src/posix/CMakeLists.txt
index 2563451..d8081d6 100644
--- a/src/posix/CMakeLists.txt
+++ b/src/posix/CMakeLists.txt
@@ -34,6 +34,8 @@
     ${PROJECT_SOURCE_DIR}/src/posix/platform/include
 )
 
+ot_option(OT_PLATFORM_POSIX_CONFIG_FILE OPENTHREAD_POSIX_CONFIG_CONFIGURATION_FILE_ENABLE "posix config file")
+
 set(OT_READLINE_VALUES
     "readline"
     "edit"
@@ -83,6 +85,19 @@
     endif()
 endif()
 
+set(OT_POSIX_FACTORY_CONFIG "" CACHE STRING "OpenThread factory config file")
+set(OT_POSIX_PRODUCT_CONFIG "" CACHE STRING "OpenThread product config file")
+
+if (OT_POSIX_FACTORY_CONFIG)
+    target_compile_definitions(ot-config INTERFACE "OPENTHREAD_POSIX_CONFIG_FACTORY_CONFIG_FILE=\"${OT_POSIX_FACTORY_CONFIG}\"")
+    message(STATUS "OT_POSIX_FACTORY_CONFIG=\"${OT_POSIX_FACTORY_CONFIG}\"")
+endif()
+
+if (OT_POSIX_PRODUCT_CONFIG)
+    target_compile_definitions(ot-config INTERFACE "OPENTHREAD_POSIX_CONFIG_PRODUCT_CONFIG_FILE=\"${OT_POSIX_PRODUCT_CONFIG}\"")
+    message(STATUS "OT_POSIX_PRODUCT_CONFIG=\"${OT_POSIX_PRODUCT_CONFIG}\"")
+endif()
+
 if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
     set(CPACK_GENERATOR "DEB")
     set(CPACK_DEBIAN_PACKAGE_MAINTAINER "OpenThread Authors (https://github.com/openthread/openthread)")
diff --git a/src/posix/platform/CMakeLists.txt b/src/posix/platform/CMakeLists.txt
index 6dd3288..e479583 100644
--- a/src/posix/platform/CMakeLists.txt
+++ b/src/posix/platform/CMakeLists.txt
@@ -125,6 +125,7 @@
 add_library(openthread-posix
     alarm.cpp
     backtrace.cpp
+    ble.cpp
     configuration.cpp
     config_file.cpp
     daemon.cpp
@@ -184,6 +185,7 @@
     PRIVATE
         OPENTHREAD_FTD=1
         OPENTHREAD_MTD=0
+        OPENTHREAD_RADIO=0
 )
 
 target_compile_options(openthread-posix PRIVATE
@@ -195,6 +197,7 @@
     ${PROJECT_SOURCE_DIR}/src
     ${PROJECT_SOURCE_DIR}/src/core
     ${PROJECT_SOURCE_DIR}/third_party/mbedtls/repo/include
+    ${OT_SPINEL_VENDOR_HOOK_SOURCE_DIR}
     PUBLIC
         ${PROJECT_SOURCE_DIR}/src/posix/platform/include
 )
diff --git a/src/posix/platform/ble.cpp b/src/posix/platform/ble.cpp
new file mode 100644
index 0000000..2fe3c64
--- /dev/null
+++ b/src/posix/platform/ble.cpp
@@ -0,0 +1,75 @@
+/*
+ *  Copyright (c) 2023, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <openthread/platform/ble.h>
+
+otError otPlatBleEnable(otInstance *aInstance)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+    return OT_ERROR_NOT_IMPLEMENTED;
+}
+
+otError otPlatBleDisable(otInstance *aInstance)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+    return OT_ERROR_NOT_IMPLEMENTED;
+}
+
+otError otPlatBleGapAdvStart(otInstance *aInstance, uint16_t aInterval)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+    OT_UNUSED_VARIABLE(aInterval);
+    return OT_ERROR_NOT_IMPLEMENTED;
+}
+
+otError otPlatBleGapAdvStop(otInstance *aInstance)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+    return OT_ERROR_NOT_IMPLEMENTED;
+}
+
+otError otPlatBleGapDisconnect(otInstance *aInstance)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+    return OT_ERROR_NOT_IMPLEMENTED;
+}
+
+otError otPlatBleGattMtuGet(otInstance *aInstance, uint16_t *aMtu)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+    OT_UNUSED_VARIABLE(aMtu);
+    return OT_ERROR_NOT_IMPLEMENTED;
+}
+
+otError otPlatBleGattServerIndicate(otInstance *aInstance, uint16_t aHandle, const otBleRadioPacket *aPacket)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+    OT_UNUSED_VARIABLE(aHandle);
+    OT_UNUSED_VARIABLE(aPacket);
+    return OT_ERROR_NOT_IMPLEMENTED;
+}
diff --git a/src/posix/platform/config_file.hpp b/src/posix/platform/config_file.hpp
index 51d16ca..1590ed2 100644
--- a/src/posix/platform/config_file.hpp
+++ b/src/posix/platform/config_file.hpp
@@ -26,8 +26,8 @@
  *  POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef POSIX_PLATFORM_CONFIG_FILE_HPP_
-#define POSIX_PLATFORM_CONFIG_FILE_HPP_
+#ifndef OT_POSIX_PLATFORM_CONFIG_FILE_HPP_
+#define OT_POSIX_PLATFORM_CONFIG_FILE_HPP_
 
 #include <assert.h>
 #include <stdint.h>
@@ -125,4 +125,4 @@
 } // namespace Posix
 } // namespace ot
 
-#endif // POSIX_PLATFORM_CONFIG_FILE_HPP_
+#endif // OT_POSIX_PLATFORM_CONFIG_FILE_HPP_
diff --git a/src/posix/platform/configuration.cpp b/src/posix/platform/configuration.cpp
index 07f1208..6311bdc 100644
--- a/src/posix/platform/configuration.cpp
+++ b/src/posix/platform/configuration.cpp
@@ -43,6 +43,8 @@
 namespace ot {
 namespace Posix {
 
+const char Configuration::kLogModuleName[] = "Config";
+
 #if OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
 const char Configuration::kKeyCalibratedPower[] = "calibrated_power";
 #endif
@@ -74,12 +76,12 @@
 exit:
     if (error == OT_ERROR_NONE)
     {
-        otLogInfoPlat("Successfully set region \"%c%c\"", (aRegionCode >> 8) & 0xff, (aRegionCode & 0xff));
+        LogInfo("Successfully set region \"%c%c\"", (aRegionCode >> 8) & 0xff, (aRegionCode & 0xff));
     }
     else
     {
-        otLogCritPlat("Failed to set region \"%c%c\": %s", (aRegionCode >> 8) & 0xff, (aRegionCode & 0xff),
-                      otThreadErrorToString(error));
+        LogCrit("Failed to set region \"%c%c\": %s", (aRegionCode >> 8) & 0xff, (aRegionCode & 0xff),
+                otThreadErrorToString(error));
     }
 
     return error;
@@ -112,7 +114,7 @@
 exit:
     if (error != OT_ERROR_NONE)
     {
-        otLogCritPlat("Failed to get power domain: %s", otThreadErrorToString(error));
+        LogCrit("Failed to get power domain: %s", otThreadErrorToString(error));
     }
 
     return error;
@@ -165,7 +167,7 @@
 exit:
     if (error != OT_ERROR_NONE)
     {
-        otLogCritPlat("Failed to update channel mask: %s", otThreadErrorToString(error));
+        LogCrit("Failed to update channel mask: %s", otThreadErrorToString(error));
     }
 
     return error;
@@ -182,7 +184,7 @@
 
     while (GetNextTargetPower(aDomain, iterator, targetPower) == OT_ERROR_NONE)
     {
-        otLogInfoPlat("Update target power: %s\r\n", targetPower.ToString().AsCString());
+        LogInfo("Update target power: %s\r\n", targetPower.ToString().AsCString());
 
         for (uint8_t ch = targetPower.GetChannelStart(); ch <= targetPower.GetChannelEnd(); ch++)
         {
@@ -193,7 +195,7 @@
 exit:
     if (error != OT_ERROR_NONE)
     {
-        otLogCritPlat("Failed to update target power: %s", otThreadErrorToString(error));
+        LogCrit("Failed to update target power: %s", otThreadErrorToString(error));
     }
 
     return error;
@@ -222,7 +224,7 @@
     while (calibrationFile->Get(kKeyCalibratedPower, iterator, value, sizeof(value)) == OT_ERROR_NONE)
     {
         SuccessOrExit(error = calibratedPower.FromString(value));
-        otLogInfoPlat("Update calibrated power: %s\r\n", calibratedPower.ToString().AsCString());
+        LogInfo("Update calibrated power: %s\r\n", calibratedPower.ToString().AsCString());
 
         for (uint8_t ch = calibratedPower.GetChannelStart(); ch <= calibratedPower.GetChannelEnd(); ch++)
         {
@@ -235,7 +237,7 @@
 exit:
     if (error != OT_ERROR_NONE)
     {
-        otLogCritPlat("Failed to update calibrated power table: %s", otThreadErrorToString(error));
+        LogCrit("Failed to update calibrated power table: %s", otThreadErrorToString(error));
     }
 
     return error;
@@ -259,7 +261,7 @@
 
         if ((error = aTargetPower.FromString(psave)) != OT_ERROR_NONE)
         {
-            otLogCritPlat("Failed to read target power: %s", otThreadErrorToString(error));
+            LogCrit("Failed to read target power: %s", otThreadErrorToString(error));
         }
         break;
     }
diff --git a/src/posix/platform/configuration.hpp b/src/posix/platform/configuration.hpp
index 71c479d..e2a2942 100644
--- a/src/posix/platform/configuration.hpp
+++ b/src/posix/platform/configuration.hpp
@@ -26,8 +26,8 @@
  *  POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef POSIX_PLATFORM_CONFIGURATION_HPP_
-#define POSIX_PLATFORM_CONFIGURATION_HPP_
+#ifndef OT_POSIX_PLATFORM_CONFIGURATION_HPP_
+#define OT_POSIX_PLATFORM_CONFIGURATION_HPP_
 
 #include "openthread-posix-config.h"
 
@@ -41,7 +41,9 @@
 #include <openthread/platform/radio.h>
 
 #include "config_file.hpp"
+#include "logger.hpp"
 #include "power.hpp"
+
 #include "common/code_utils.hpp"
 
 namespace ot {
@@ -51,9 +53,11 @@
  * Updates the target power table and calibrated power table to the RCP.
  *
  */
-class Configuration
+class Configuration : public Logger<Configuration>
 {
 public:
+    static const char kLogModuleName[]; ///< Module name used for logging.
+
     Configuration(void)
         : mFactoryConfigFile(OPENTHREAD_POSIX_CONFIG_FACTORY_CONFIG_FILE)
         , mProductConfigFile(OPENTHREAD_POSIX_CONFIG_PRODUCT_CONFIG_FILE)
@@ -150,4 +154,4 @@
 } // namespace ot
 
 #endif // OPENTHREAD_POSIX_CONFIG_CONFIGURATION_FILE_ENABLE
-#endif // POSIX_PLATFORM_CONFIGURATION_HPP_
+#endif // OT_POSIX_PLATFORM_CONFIGURATION_HPP_
diff --git a/src/posix/platform/daemon.cpp b/src/posix/platform/daemon.cpp
index 5a85b15..1436e45 100644
--- a/src/posix/platform/daemon.cpp
+++ b/src/posix/platform/daemon.cpp
@@ -28,6 +28,9 @@
 
 #include "posix/platform/daemon.hpp"
 
+#if defined(__ANDROID__) && !OPENTHREAD_CONFIG_ANDROID_NDK_ENABLE
+#include <cutils/sockets.h>
+#endif
 #include <fcntl.h>
 #include <signal.h>
 #include <stdarg.h>
@@ -72,6 +75,8 @@
 
 } // namespace
 
+const char Daemon::kLogModuleName[] = "Daemon";
+
 int Daemon::OutputFormat(const char *aFormat, ...)
 {
     int     ret;
@@ -94,7 +99,7 @@
                   "OPENTHREAD_CONFIG_CLI_MAX_LINE_LENGTH is too short!");
 
     rval = vsnprintf(buf, sizeof(buf), aFormat, aArguments);
-    VerifyOrExit(rval >= 0, otLogWarnPlat("Failed to format CLI output: %s", strerror(errno)));
+    VerifyOrExit(rval >= 0, LogWarn("Failed to format CLI output: %s", strerror(errno)));
 
     if (rval >= static_cast<int>(sizeof(buf)))
     {
@@ -104,7 +109,7 @@
 
     VerifyOrExit(mSessionSocket != -1);
 
-#if defined(__linux__)
+#ifdef __linux__
     // Don't die on SIGPIPE
     rval = send(mSessionSocket, buf, static_cast<size_t>(rval), MSG_NOSIGNAL);
 #else
@@ -113,7 +118,7 @@
 
     if (rval < 0)
     {
-        otLogWarnPlat("Failed to write CLI output: %s", strerror(errno));
+        LogWarn("Failed to write CLI output: %s", strerror(errno));
         close(mSessionSocket);
         mSessionSocket = -1;
     }
@@ -157,7 +162,7 @@
 exit:
     if (rval == -1)
     {
-        otLogWarnPlat("Failed to initialize session socket: %s", strerror(errno));
+        LogWarn("Failed to initialize session socket: %s", strerror(errno));
         if (newSessionSocket != -1)
         {
             close(newSessionSocket);
@@ -165,14 +170,33 @@
     }
     else
     {
-        otLogInfoPlat("Session socket is ready");
+        LogInfo("Session socket is ready");
     }
 }
 
-void Daemon::SetUp(void)
+#if defined(__ANDROID__) && !OPENTHREAD_CONFIG_ANDROID_NDK_ENABLE
+void Daemon::createListenSocketOrDie(void)
+{
+    Filename socketFile;
+
+    // Don't use OPENTHREAD_POSIX_DAEMON_SOCKET_NAME because android_get_control_socket
+    // below already assumes parent /dev/socket dir
+    GetFilename(socketFile, "ot-daemon/%s.sock");
+
+    // This returns the init-managed stream socket which is already bind to
+    // /dev/socket/ot-daemon/<interface-name>.sock
+    mListenSocket = android_get_control_socket(socketFile);
+
+    if (mListenSocket == -1)
+    {
+        DieNowWithMessage("android_get_control_socket", OT_EXIT_ERROR_ERRNO);
+    }
+}
+#else
+void Daemon::createListenSocketOrDie(void)
 {
     struct sockaddr_un sockname;
-    int                ret;
+    int ret;
 
     class AllowAllGuard
     {
@@ -180,7 +204,7 @@
         AllowAllGuard(void)
         {
             const char *allowAll = getenv("OT_DAEMON_ALLOW_ALL");
-            mAllowAll            = (allowAll != nullptr && strcmp("1", allowAll) == 0);
+            mAllowAll = (allowAll != nullptr && strcmp("1", allowAll) == 0);
 
             if (mAllowAll)
             {
@@ -196,13 +220,10 @@
         }
 
     private:
-        bool   mAllowAll = false;
-        mode_t mMode     = 0;
+        bool mAllowAll = false;
+        mode_t mMode = 0;
     };
 
-    // This allows implementing pseudo reset.
-    VerifyOrExit(mListenSocket == -1);
-
     mListenSocket = SocketWithCloseExec(AF_UNIX, SOCK_STREAM, 0, kSocketNonBlock);
 
     if (mListenSocket == -1)
@@ -246,6 +267,16 @@
     {
         DieNowWithMessage("bind", OT_EXIT_ERROR_ERRNO);
     }
+}
+#endif // defined(__ANDROID__) && !OPENTHREAD_CONFIG_ANDROID_NDK_ENABLE
+
+void Daemon::SetUp(void)
+{
+    int ret;
+
+    // This allows implementing pseudo reset.
+    VerifyOrExit(mListenSocket == -1);
+    createListenSocketOrDie();
 
     //
     // only accept 1 connection.
@@ -256,18 +287,13 @@
         DieNowWithMessage("listen", OT_EXIT_ERROR_ERRNO);
     }
 
+exit:
 #if OPENTHREAD_POSIX_CONFIG_DAEMON_CLI_ENABLE
-    otCliInit(
-        gInstance,
-        [](void *aContext, const char *aFormat, va_list aArguments) -> int {
-            return static_cast<Daemon *>(aContext)->OutputFormatV(aFormat, aArguments);
-        },
-        this);
+    otSysCliInitUsingDaemon(gInstance);
 #endif
 
     Mainloop::Manager::Get().Add(*this);
 
-exit:
     return;
 }
 
@@ -281,6 +307,8 @@
         mSessionSocket = -1;
     }
 
+#if !defined(__ANDROID__) || OPENTHREAD_CONFIG_ANDROID_NDK_ENABLE
+    // The `mListenSocket` is managed by `init` on Android
     if (mListenSocket != -1)
     {
         close(mListenSocket);
@@ -292,7 +320,7 @@
         Filename sockfile;
 
         GetFilename(sockfile, OPENTHREAD_POSIX_DAEMON_SOCKET_NAME);
-        otLogDebgPlat("Removing daemon socket: %s", sockfile);
+        LogDebg("Removing daemon socket: %s", sockfile);
         (void)unlink(sockfile);
     }
 
@@ -302,6 +330,7 @@
         close(mDaemonLock);
         mDaemonLock = -1;
     }
+#endif
 }
 
 void Daemon::Update(otSysMainloopContext &aContext)
@@ -373,7 +402,7 @@
         {
             if (rval < 0)
             {
-                otLogWarnPlat("Daemon read: %s", strerror(errno));
+                LogWarn("Daemon read: %s", strerror(errno));
             }
             close(mSessionSocket);
             mSessionSocket = -1;
diff --git a/src/posix/platform/daemon.hpp b/src/posix/platform/daemon.hpp
index 4fdd3b3..be3738b 100644
--- a/src/posix/platform/daemon.hpp
+++ b/src/posix/platform/daemon.hpp
@@ -31,24 +31,29 @@
 #include "openthread-posix-config.h"
 
 #include "core/common/non_copyable.hpp"
-#include "posix/platform/mainloop.hpp"
+
+#include "logger.hpp"
+#include "mainloop.hpp"
 
 namespace ot {
 namespace Posix {
 
-class Daemon : public Mainloop::Source, private NonCopyable
+class Daemon : public Mainloop::Source, public Logger<Daemon>, private NonCopyable
 {
 public:
+    static const char kLogModuleName[];
+
     static Daemon &Get(void);
 
     void SetUp(void);
     void TearDown(void);
     void Update(otSysMainloopContext &aContext) override;
     void Process(const otSysMainloopContext &aContext) override;
+    int  OutputFormatV(const char *aFormat, va_list aArguments);
 
 private:
     int  OutputFormat(const char *aFormat, ...);
-    int  OutputFormatV(const char *aFormat, va_list aArguments);
+    void createListenSocketOrDie(void);
     void InitializeSessionSocket(void);
 
     int mListenSocket  = -1;
diff --git a/src/posix/platform/firewall.cpp b/src/posix/platform/firewall.cpp
index 19ad8c5..7e9d47e 100644
--- a/src/posix/platform/firewall.cpp
+++ b/src/posix/platform/firewall.cpp
@@ -124,7 +124,7 @@
 exit:
     if (error != OT_ERROR_NONE)
     {
-        otLogWarnPlat("Failed to update ipsets: %s", otThreadErrorToString(error));
+        otLogWarnPlat("Firewall - failed to update ipsets: %s", otThreadErrorToString(error));
     }
 }
 
diff --git a/src/posix/platform/hdlc_interface.cpp b/src/posix/platform/hdlc_interface.cpp
index d2c45e4..12726fc 100644
--- a/src/posix/platform/hdlc_interface.cpp
+++ b/src/posix/platform/hdlc_interface.cpp
@@ -128,6 +128,8 @@
 namespace ot {
 namespace Posix {
 
+const char HdlcInterface::kLogModuleName[] = "HdlcIntface";
+
 HdlcInterface::HdlcInterface(const Url::Url &aRadioUrl)
     : mReceiveFrameCallback(nullptr)
     , mReceiveFrameContext(nullptr)
@@ -164,7 +166,7 @@
 #endif // OPENTHREAD_POSIX_CONFIG_RCP_PTY_ENABLE
     else
     {
-        otLogCritPlat("Radio file '%s' not supported", mRadioUrl.GetPath());
+        LogCrit("Radio file '%s' not supported", mRadioUrl.GetPath());
         ExitNow(error = OT_ERROR_FAILED);
     }
 
@@ -714,7 +716,7 @@
     {
         mInterfaceMetrics.mTransferredGarbageFrameCount++;
         mReceiveFrameBuffer->DiscardFrame();
-        otLogWarnPlat("Error decoding hdlc frame: %s", otThreadErrorToString(aError));
+        LogWarn("Error decoding hdlc frame: %s", otThreadErrorToString(aError));
     }
 
 exit:
@@ -742,7 +744,7 @@
             usleep(static_cast<useconds_t>(kOpenFileDelay) * US_PER_MS);
         } while (end > otPlatTimeGet());
 
-        otLogCritPlat("Failed to reopen UART connection after resetting the RCP device.");
+        LogCrit("Failed to reopen UART connection after resetting the RCP device.");
         error = OT_ERROR_FAILED;
     }
 
diff --git a/src/posix/platform/hdlc_interface.hpp b/src/posix/platform/hdlc_interface.hpp
index 6078135..c6e01c7 100644
--- a/src/posix/platform/hdlc_interface.hpp
+++ b/src/posix/platform/hdlc_interface.hpp
@@ -31,9 +31,10 @@
  *   This file includes definitions for the HDLC interface to radio (RCP).
  */
 
-#ifndef POSIX_PLATFORM_HDLC_INTERFACE_HPP_
-#define POSIX_PLATFORM_HDLC_INTERFACE_HPP_
+#ifndef OT_POSIX_PLATFORM_HDLC_INTERFACE_HPP_
+#define OT_POSIX_PLATFORM_HDLC_INTERFACE_HPP_
 
+#include "logger.hpp"
 #include "openthread-posix-config.h"
 #include "platform-posix.h"
 #include "lib/hdlc/hdlc.hpp"
@@ -48,9 +49,11 @@
  * Defines an HDLC interface to the Radio Co-processor (RCP)
  *
  */
-class HdlcInterface : public ot::Spinel::SpinelInterface
+class HdlcInterface : public ot::Spinel::SpinelInterface, public Logger<HdlcInterface>
 {
 public:
+    static const char kLogModuleName[]; ///< Module name used for logging.
+
     /**
      * Initializes the object.
      *
@@ -272,4 +275,5 @@
 
 } // namespace Posix
 } // namespace ot
-#endif // POSIX_PLATFORM_HDLC_INTERFACE_HPP_
+
+#endif // OT_POSIX_PLATFORM_HDLC_INTERFACE_HPP_
diff --git a/src/posix/platform/include/openthread/openthread-system.h b/src/posix/platform/include/openthread/openthread-system.h
index 61a0741..7f11349 100644
--- a/src/posix/platform/include/openthread/openthread-system.h
+++ b/src/posix/platform/include/openthread/openthread-system.h
@@ -254,6 +254,19 @@
  */
 bool otSysInfraIfIsRunning(void);
 
+/**
+ * Initializes the CLI module using the daemon.
+ *
+ * This function initializes the CLI module, and assigns the daemon to handle
+ * the CLI output. This function can be invoked multiple times. The typical use case
+ * is that, after OTBR/vendor_server's CLI output redirection, it uses this API to
+ * restore the original daemon's CLI output.
+ *
+ * @param[in] aInstance  The OpenThread instance structure.
+ *
+ */
+void otSysCliInitUsingDaemon(otInstance *aInstance);
+
 #ifdef __cplusplus
 } // end of extern "C"
 #endif
diff --git a/src/posix/platform/infra_if.cpp b/src/posix/platform/infra_if.cpp
index 6e16b3f..0c97852 100644
--- a/src/posix/platform/infra_if.cpp
+++ b/src/posix/platform/infra_if.cpp
@@ -128,6 +128,8 @@
 namespace ot {
 namespace Posix {
 
+const char InfraNetif::kLogModuleName[] = "InfraNetif";
+
 int InfraNetif::CreateIcmp6Socket(const char *aInfraIfName)
 {
     int                 sock;
@@ -174,7 +176,7 @@
 #ifdef __linux__
     rval = setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, aInfraIfName, strlen(aInfraIfName));
 #else  // __NetBSD__ || __FreeBSD__ || __APPLE__
-    rval = setsockopt(mInfraIfIcmp6Socket, IPPROTO_IPV6, IPV6_BOUND_IF, &mInfraIfIndex, sizeof(mInfraIfIndex));
+    rval = setsockopt(sock, IPPROTO_IPV6, IPV6_BOUND_IF, aInfraIfName, strlen(aInfraIfName));
 #endif // __linux__
     VerifyOrDie(rval == 0, OT_EXIT_ERROR_ERRNO);
 
@@ -190,6 +192,7 @@
 
 bool IsAddressGlobalUnicast(const in6_addr &aAddress) { return (aAddress.s6_addr[0] & 0xe0) == 0x20; }
 
+#ifdef __linux__
 // Create a net-link socket that subscribes to link & addresses events.
 int CreateNetLinkSocket(void)
 {
@@ -209,6 +212,7 @@
 
     return sock;
 }
+#endif // #ifdef __linux__
 
 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
 otError InfraNetif::SendIcmp6Nd(uint32_t            aInfraIfIndex,
@@ -269,15 +273,16 @@
     memcpy(CMSG_DATA(cmsgPointer), &hopLimit, sizeof(hopLimit));
 
     rval = sendmsg(mInfraIfIcmp6Socket, &msgHeader, 0);
+
     if (rval < 0)
     {
-        otLogWarnPlat("failed to send ICMPv6 message: %s", strerror(errno));
+        LogWarn("failed to send ICMPv6 message: %s", strerror(errno));
         ExitNow(error = OT_ERROR_FAILED);
     }
 
     if (static_cast<size_t>(rval) != iov.iov_len)
     {
-        otLogWarnPlat("failed to send ICMPv6 message: partially sent");
+        LogWarn("failed to send ICMPv6 message: partially sent");
         ExitNow(error = OT_ERROR_FAILED);
     }
 
@@ -309,7 +314,7 @@
     if (ioctl(sock, SIOCGIFFLAGS, &ifReq) == -1)
     {
 #if OPENTHREAD_POSIX_CONFIG_EXIT_ON_INFRA_NETIF_LOST_ENABLE
-        otLogCritPlat("The infra link %s may be lost. Exiting.", mInfraIfName);
+        LogCrit("The infra link %s may be lost. Exiting.", mInfraIfName);
         DieNow(OT_EXIT_ERROR_ERRNO);
 #endif
         ExitNow();
@@ -332,7 +337,7 @@
 
     if (getifaddrs(&ifAddrs) < 0)
     {
-        otLogWarnPlat("failed to get netif addresses: %s", strerror(errno));
+        LogWarn("failed to get netif addresses: %s", strerror(errno));
         ExitNow();
     }
 
@@ -379,7 +384,7 @@
 
     if (getifaddrs(&ifAddrs) < 0)
     {
-        otLogCritPlat("failed to get netif addresses: %s", strerror(errno));
+        LogCrit("failed to get netif addresses: %s", strerror(errno));
         DieNow(OT_EXIT_ERROR_ERRNO);
     }
 
@@ -405,7 +410,12 @@
     return hasLla;
 }
 
-void InfraNetif::Init(void) { mNetLinkSocket = CreateNetLinkSocket(); }
+void InfraNetif::Init(void)
+{
+#ifdef __linux__
+    mNetLinkSocket = CreateNetLinkSocket();
+#endif
+}
 
 void InfraNetif::SetInfraNetif(const char *aIfName, int aIcmp6Socket)
 {
@@ -414,7 +424,9 @@
     OT_UNUSED_VARIABLE(aIcmp6Socket);
 
     OT_ASSERT(gInstance != nullptr);
+#ifdef __linux__
     VerifyOrDie(mNetLinkSocket != -1, OT_EXIT_INVALID_STATE);
+#endif
 
 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
     SetInfraNetifIcmp6SocketForBorderRouting(aIcmp6Socket);
@@ -425,7 +437,7 @@
 
     if (aIfName == nullptr || aIfName[0] == '\0')
     {
-        otLogWarnPlat("Border Routing/Backbone Router feature is disabled: infra interface is missing");
+        LogWarn("Border Routing/Backbone Router feature is disabled: infra interface is missing");
         ExitNow();
     }
 
@@ -436,7 +448,7 @@
     ifIndex = if_nametoindex(aIfName);
     if (ifIndex == 0)
     {
-        otLogCritPlat("Failed to get the index for infra interface %s", aIfName);
+        LogCrit("Failed to get the index for infra interface %s", aIfName);
         DieNow(OT_EXIT_INVALID_ARGUMENTS);
     }
 
@@ -449,7 +461,9 @@
 void InfraNetif::SetUp(void)
 {
     OT_ASSERT(gInstance != nullptr);
+#ifdef __linux__
     VerifyOrExit(mNetLinkSocket != -1);
+#endif
 
 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
     SuccessOrDie(otBorderRoutingInit(gInstance, mInfraIfIndex, otSysInfraIfIsRunning()));
@@ -461,6 +475,9 @@
 #endif
 
     Mainloop::Manager::Get().Add(*this);
+
+    ExitNow(); // To silence unused `exit` label warning.
+
 exit:
     return;
 }
@@ -488,11 +505,13 @@
     }
 #endif
 
+#ifdef __linux__
     if (mNetLinkSocket != -1)
     {
         close(mNetLinkSocket);
         mNetLinkSocket = -1;
     }
+#endif
 
     mInfraIfName[0] = '\0';
     mInfraIfIndex   = 0;
@@ -500,7 +519,9 @@
 
 void InfraNetif::Update(otSysMainloopContext &aContext)
 {
+#ifdef __linux__
     VerifyOrExit(mNetLinkSocket != -1);
+#endif
 
 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
     VerifyOrExit(mInfraIfIcmp6Socket != -1);
@@ -509,13 +530,17 @@
     aContext.mMaxFd = OT_MAX(aContext.mMaxFd, mInfraIfIcmp6Socket);
 #endif
 
+#ifdef __linux__
     FD_SET(mNetLinkSocket, &aContext.mReadFdSet);
     aContext.mMaxFd = OT_MAX(aContext.mMaxFd, mNetLinkSocket);
+#endif
 
 exit:
     return;
 }
 
+#ifdef __linux__
+
 void InfraNetif::ReceiveNetLinkMessage(void)
 {
     const size_t kMaxNetLinkBufSize = 8192;
@@ -529,7 +554,7 @@
     len = recv(mNetLinkSocket, msgBuffer.mBuffer, sizeof(msgBuffer.mBuffer), 0);
     if (len < 0)
     {
-        otLogCritPlat("Failed to receive netlink message: %s", strerror(errno));
+        LogCrit("Failed to receive netlink message: %s", strerror(errno));
         ExitNow();
     }
 
@@ -554,7 +579,7 @@
             struct nlmsgerr *errMsg = reinterpret_cast<struct nlmsgerr *>(NLMSG_DATA(header));
 
             OT_UNUSED_VARIABLE(errMsg);
-            otLogWarnPlat("netlink NLMSG_ERROR response: seq=%u, error=%d", header->nlmsg_seq, errMsg->error);
+            LogWarn("netlink NLMSG_ERROR response: seq=%u, error=%d", header->nlmsg_seq, errMsg->error);
             break;
         }
         default:
@@ -566,6 +591,8 @@
     return;
 }
 
+#endif // #ifdef __linux__
+
 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
 void InfraNetif::ReceiveIcmp6Message(void)
 {
@@ -599,7 +626,7 @@
     rval = recvmsg(mInfraIfIcmp6Socket, &msg, 0);
     if (rval < 0)
     {
-        otLogWarnPlat("Failed to receive ICMPv6 message: %s", strerror(errno));
+        LogWarn("Failed to receive ICMPv6 message: %s", strerror(errno));
         ExitNow(error = OT_ERROR_DROP);
     }
 
@@ -635,7 +662,7 @@
 exit:
     if (error != OT_ERROR_NONE)
     {
-        otLogDebgPlat("Failed to handle ICMPv6 message: %s", otThreadErrorToString(error));
+        LogDebg("Failed to handle ICMPv6 message: %s", otThreadErrorToString(error));
     }
 }
 #endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
@@ -655,7 +682,7 @@
 
     VerifyOrExit((char *)req->ar_name == kWellKnownIpv4OnlyName);
 
-    otLogInfoPlat("Handling host address response for %s", kWellKnownIpv4OnlyName);
+    LogInfo("Handling host address response for %s", kWellKnownIpv4OnlyName);
 
     // We extract the first valid NAT64 prefix from the address look-up response.
     for (struct addrinfo *rp = res; rp != NULL && prefix.mLength == 0; rp = rp->ai_next)
@@ -754,10 +781,10 @@
 
     if (status != 0)
     {
-        otLogNotePlat("getaddrinfo_a failed: %s", gai_strerror(status));
+        LogNote("getaddrinfo_a failed: %s", gai_strerror(status));
         ExitNow(error = OT_ERROR_FAILED);
     }
-    otLogInfoPlat("getaddrinfo_a requested for %s", kWellKnownIpv4OnlyName);
+    LogInfo("getaddrinfo_a requested for %s", kWellKnownIpv4OnlyName);
 exit:
     if (error != OT_ERROR_NONE)
     {
@@ -792,7 +819,10 @@
 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
     VerifyOrExit(mInfraIfIcmp6Socket != -1);
 #endif
+
+#ifdef __linux__
     VerifyOrExit(mNetLinkSocket != -1);
+#endif
 
 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
     if (FD_ISSET(mInfraIfIcmp6Socket, &aContext.mReadFdSet))
@@ -801,10 +831,12 @@
     }
 #endif
 
+#ifdef __linux__
     if (FD_ISSET(mNetLinkSocket, &aContext.mReadFdSet))
     {
         ReceiveNetLinkMessage();
     }
+#endif
 
 exit:
     return;
diff --git a/src/posix/platform/infra_if.hpp b/src/posix/platform/infra_if.hpp
index e8aedd2..3eee248 100644
--- a/src/posix/platform/infra_if.hpp
+++ b/src/posix/platform/infra_if.hpp
@@ -31,15 +31,20 @@
  *   This file implements the infrastructure interface for posix.
  */
 
+#ifndef OT_POSIX_PLATFORM_INFRA_IF_HPP_
+#define OT_POSIX_PLATFORM_INFRA_IF_HPP_
+
 #include "openthread-posix-config.h"
 
 #include <net/if.h>
 #include <openthread/nat64.h>
 #include <openthread/openthread-system.h>
 
-#include "multicast_routing.hpp"
 #include "core/common/non_copyable.hpp"
-#include "posix/platform/mainloop.hpp"
+
+#include "logger.hpp"
+#include "mainloop.hpp"
+#include "multicast_routing.hpp"
 
 #if OPENTHREAD_POSIX_CONFIG_INFRA_IF_ENABLE
 
@@ -50,9 +55,11 @@
  * Manages infrastructure network interface.
  *
  */
-class InfraNetif : public Mainloop::Source, private NonCopyable
+class InfraNetif : public Mainloop::Source, public Logger<InfraNetif>, private NonCopyable
 {
 public:
+    static const char kLogModuleName[]; ///< Module name used for logging.
+
     /**
      * Updates the fd_set and timeout for mainloop.
      *
@@ -220,8 +227,12 @@
     static const uint8_t      kValidNat64PrefixLength[];
 
     char     mInfraIfName[IFNAMSIZ];
-    uint32_t mInfraIfIndex  = 0;
-    int      mNetLinkSocket = -1;
+    uint32_t mInfraIfIndex = 0;
+
+#ifdef __linux__
+    int mNetLinkSocket = -1;
+#endif
+
 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
     int mInfraIfIcmp6Socket = -1;
 #endif
@@ -229,7 +240,10 @@
     MulticastRoutingManager mMulticastRoutingManager;
 #endif
 
-    void        ReceiveNetLinkMessage(void);
+#ifdef __linux__
+    void ReceiveNetLinkMessage(void);
+#endif
+
     bool        HasLinkLocalAddress(void) const;
     static void DiscoverNat64PrefixDone(union sigval sv);
 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
@@ -241,3 +255,5 @@
 } // namespace Posix
 } // namespace ot
 #endif // OPENTHREAD_POSIX_CONFIG_INFRA_IF_ENABLE
+
+#endif // OT_POSIX_PLATFORM_INFRA_IF_HPP_
diff --git a/src/posix/platform/ip6_utils.hpp b/src/posix/platform/ip6_utils.hpp
index 39c7bc1..841c317 100644
--- a/src/posix/platform/ip6_utils.hpp
+++ b/src/posix/platform/ip6_utils.hpp
@@ -26,16 +26,83 @@
  *  POSSIBILITY OF SUCH DAMAGE.
  */
 
+#ifndef OT_POSIX_PLATFORM_IP6_UTILS_HPP_
+#define OT_POSIX_PLATFORM_IP6_UTILS_HPP_
+
 #include "openthread-posix-config.h"
 #include "platform-posix.h"
 
 #include <arpa/inet.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <openthread/ip6.h>
 
 namespace ot {
 namespace Posix {
 namespace Ip6Utils {
 
 /**
+ * Indicates whether or not the IPv6 address scope is Link-Local.
+ *
+ * @param[in] aAddress   The IPv6 address to check.
+ *
+ * @retval TRUE   If the IPv6 address scope is Link-Local.
+ * @retval FALSE  If the IPv6 address scope is not Link-Local.
+ *
+ */
+inline bool IsIp6AddressLinkLocal(const otIp6Address &aAddress)
+{
+    return (aAddress.mFields.m8[0] == 0xfe) && ((aAddress.mFields.m8[1] & 0xc0) == 0x80);
+}
+
+/**
+ * Indicates whether or not the IPv6 address is multicast.
+ *
+ * @param[in] aAddress   The IPv6 address to check.
+ *
+ * @retval TRUE   If the IPv6 address scope is multicast.
+ * @retval FALSE  If the IPv6 address scope is not multicast.
+ *
+ */
+inline bool IsIp6AddressMulticast(const otIp6Address &aAddress) { return (aAddress.mFields.m8[0] == 0xff); }
+
+/**
+ * Indicates whether or not the IPv6 address is unspecified.
+ *
+ * @param[in] aAddress   The IPv6 address to check.
+ *
+ * @retval TRUE   If the IPv6 address scope is unspecified.
+ * @retval FALSE  If the IPv6 address scope is not unspecified.
+ *
+ */
+inline bool IsIp6AddressUnspecified(const otIp6Address &aAddress) { return otIp6IsAddressUnspecified(&aAddress); }
+
+/**
+ * Copies the IPv6 address bytes into a given buffer.
+ *
+ * @param[in] aAddress  The IPv6 address to copy.
+ * @param[in] aBuffer   A pointer to buffer to copy the address to.
+ *
+ */
+inline void CopyIp6AddressTo(const otIp6Address &aAddress, void *aBuffer)
+{
+    memcpy(aBuffer, &aAddress, sizeof(otIp6Address));
+}
+
+/**
+ * Reads and set the the IPv6 address bytes from a given buffer.
+ *
+ * @param[in] aBuffer    A pointer to buffer to read from.
+ * @param[out] aAddress  A reference to populate with the read IPv6 address.
+ *
+ */
+inline void ReadIp6AddressFrom(const void *aBuffer, otIp6Address &aAddress)
+{
+    memcpy(&aAddress, aBuffer, sizeof(otIp6Address));
+}
+
+/**
  * This utility class converts binary IPv6 address to text format.
  *
  */
@@ -68,3 +135,5 @@
 } // namespace Ip6Utils
 } // namespace Posix
 } // namespace ot
+
+#endif // OT_POSIX_PLATFORM_IP6_UTILS_HPP_
diff --git a/src/posix/platform/logger.hpp b/src/posix/platform/logger.hpp
new file mode 100644
index 0000000..e907b87
--- /dev/null
+++ b/src/posix/platform/logger.hpp
@@ -0,0 +1,141 @@
+/*
+ *  Copyright (c) 2024, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ *   This file implements the `Logger` class for use by POSIX platform module.
+ */
+
+#ifndef OT_POSIX_PLATFORM_LOGGER_HPP_
+#define OT_POSIX_PLATFORM_LOGGER_HPP_
+
+#include "openthread-posix-config.h"
+
+#include <openthread/logging.h>
+
+namespace ot {
+namespace Posix {
+
+/**
+ * Provides logging methods for a specific POSIX module.
+ *
+ * The `Type` class MUST provide a `static const char kLogModuleName[]` which specifies the POSIX log module name to
+ * include in the platform logs (using `otLogPlatArgs()`).
+ *
+ * Users of this class should follow CRTP-style inheritance, i.e., the `Type` class itself should inherit from
+ * `Logger<Type>`.
+ *
+ */
+template <typename Type> class Logger
+{
+public:
+    /**
+     * Emits a log message at critical log level.
+     *
+     * @param[in]  aFormat  The format string.
+     * @param[in]  ...      Arguments for the format specification.
+     *
+     */
+    static void LogCrit(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(1, 2)
+    {
+        va_list args;
+
+        va_start(args, aFormat);
+        otLogPlatArgs(OT_LOG_LEVEL_CRIT, Type::kLogModuleName, aFormat, args);
+        va_end(args);
+    }
+
+    /**
+     * Emits a log message at warning log level.
+     *
+     * @param[in]  aFormat  The format string.
+     * @param[in]  ...      Arguments for the format specification.
+     *
+     */
+    static void LogWarn(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(1, 2)
+    {
+        va_list args;
+
+        va_start(args, aFormat);
+        otLogPlatArgs(OT_LOG_LEVEL_WARN, Type::kLogModuleName, aFormat, args);
+        va_end(args);
+    }
+
+    /**
+     * Emits a log message at note log level.
+     *
+     * @param[in]  aFormat  The format string.
+     * @param[in]  ...      Arguments for the format specification.
+     *
+     */
+    static void LogNote(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(1, 2)
+    {
+        va_list args;
+
+        va_start(args, aFormat);
+        otLogPlatArgs(OT_LOG_LEVEL_NOTE, Type::kLogModuleName, aFormat, args);
+        va_end(args);
+    }
+
+    /**
+     * Emits a log message at info log level.
+     *
+     * @param[in]  aFormat  The format string.
+     * @param[in]  ...      Arguments for the format specification.
+     *
+     */
+    static void LogInfo(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(1, 2)
+    {
+        va_list args;
+
+        va_start(args, aFormat);
+        otLogPlatArgs(OT_LOG_LEVEL_INFO, Type::kLogModuleName, aFormat, args);
+        va_end(args);
+    }
+
+    /**
+     * Emits a log message at debug log level.
+     *
+     * @param[in]  aFormat  The format string.
+     * @param[in]  ...      Arguments for the format specification.
+     *
+     */
+    static void LogDebg(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(1, 2)
+    {
+        va_list args;
+
+        va_start(args, aFormat);
+        otLogPlatArgs(OT_LOG_LEVEL_DEBG, Type::kLogModuleName, aFormat, args);
+        va_end(args);
+    }
+};
+
+} // namespace Posix
+} // namespace ot
+
+#endif // OT_POSIX_PLATFORM_LOGGER_HPP_
diff --git a/src/posix/platform/mainloop.hpp b/src/posix/platform/mainloop.hpp
index 6e11e56..a9ce981 100644
--- a/src/posix/platform/mainloop.hpp
+++ b/src/posix/platform/mainloop.hpp
@@ -28,7 +28,7 @@
 
 /**
  * @file
- *   This file includes definitions for the SPI interface to radio (RCP).
+ *   This file includes definitions for the mainloop events and manager.
  */
 
 #ifndef OT_POSIX_PLATFORM_MAINLOOP_HPP_
diff --git a/src/posix/platform/multicast_routing.cpp b/src/posix/platform/multicast_routing.cpp
index 40af863..864f984 100644
--- a/src/posix/platform/multicast_routing.cpp
+++ b/src/posix/platform/multicast_routing.cpp
@@ -39,7 +39,7 @@
 #include <sys/socket.h>
 #include <sys/types.h>
 #include <unistd.h>
-#if __linux__
+#ifdef __linux__
 #include <linux/mroute6.h>
 #else
 #error "Multicast Routing feature is not ported to non-Linux platforms yet."
@@ -54,19 +54,21 @@
 namespace ot {
 namespace Posix {
 
-#define LogResult(aError, ...)                                                                                      \
-    do                                                                                                              \
-    {                                                                                                               \
-        otError _err = (aError);                                                                                    \
-                                                                                                                    \
-        if (_err == OT_ERROR_NONE)                                                                                  \
-        {                                                                                                           \
-            otLogInfoPlat(OT_FIRST_ARG(__VA_ARGS__) ": %s" OT_REST_ARGS(__VA_ARGS__), otThreadErrorToString(_err)); \
-        }                                                                                                           \
-        else                                                                                                        \
-        {                                                                                                           \
-            otLogWarnPlat(OT_FIRST_ARG(__VA_ARGS__) ": %s" OT_REST_ARGS(__VA_ARGS__), otThreadErrorToString(_err)); \
-        }                                                                                                           \
+const char MulticastRoutingManager::kLogModuleName[] = "McastRtMgr";
+
+#define LogResult(aError, ...)                                                                                \
+    do                                                                                                        \
+    {                                                                                                         \
+        otError _err = (aError);                                                                              \
+                                                                                                              \
+        if (_err == OT_ERROR_NONE)                                                                            \
+        {                                                                                                     \
+            LogInfo(OT_FIRST_ARG(__VA_ARGS__) ": %s" OT_REST_ARGS(__VA_ARGS__), otThreadErrorToString(_err)); \
+        }                                                                                                     \
+        else                                                                                                  \
+        {                                                                                                     \
+            LogWarn(OT_FIRST_ARG(__VA_ARGS__) ": %s" OT_REST_ARGS(__VA_ARGS__), otThreadErrorToString(_err)); \
+        }                                                                                                     \
     } while (false)
 
 void MulticastRoutingManager::SetUp(void)
@@ -114,7 +116,7 @@
 
     InitMulticastRouterSock();
 
-    LogResult(OT_ERROR_NONE, "MulticastRoutingManager: %s", __FUNCTION__);
+    LogResult(OT_ERROR_NONE, "%s", __FUNCTION__);
 exit:
     return;
 }
@@ -123,7 +125,7 @@
 {
     FinalizeMulticastRouterSock();
 
-    LogResult(OT_ERROR_NONE, "MulticastRoutingManager: %s", __FUNCTION__);
+    LogResult(OT_ERROR_NONE, "%s", __FUNCTION__);
 }
 
 void MulticastRoutingManager::Add(const Ip6::Address &aAddress)
@@ -133,7 +135,7 @@
     UnblockInboundMulticastForwardingCache(aAddress);
     UpdateMldReport(aAddress, true);
 
-    LogResult(OT_ERROR_NONE, "MulticastRoutingManager: %s: %s", __FUNCTION__, aAddress.ToString().AsCString());
+    LogResult(OT_ERROR_NONE, "%s: %s", __FUNCTION__, aAddress.ToString().AsCString());
 
 exit:
     return;
@@ -148,7 +150,7 @@
     RemoveInboundMulticastForwardingCache(aAddress);
     UpdateMldReport(aAddress, false);
 
-    LogResult(error, "MulticastRoutingManager: %s: %s", __FUNCTION__, aAddress.ToString().AsCString());
+    LogResult(error, "%s: %s", __FUNCTION__, aAddress.ToString().AsCString());
 
 exit:
     return;
@@ -166,8 +168,7 @@
                  ? OT_ERROR_FAILED
                  : OT_ERROR_NONE);
 
-    LogResult(error, "MulticastRoutingManager: %s: address %s %s", __FUNCTION__, aAddress.ToString().AsCString(),
-              (isAdd ? "Added" : "Removed"));
+    LogResult(error, "%s: address %s %s", __FUNCTION__, aAddress.ToString().AsCString(), (isAdd ? "Added" : "Removed"));
 }
 
 bool MulticastRoutingManager::HasMulticastListener(const Ip6::Address &aAddress) const
@@ -283,7 +284,7 @@
     error = AddMulticastForwardingCache(src, dst, static_cast<MifIndex>(mrt6msg->im6_mif));
 
 exit:
-    LogResult(error, "MulticastRoutingManager: %s", __FUNCTION__);
+    LogResult(error, "%s", __FUNCTION__);
 }
 
 otError MulticastRoutingManager::AddMulticastForwardingCache(const Ip6::Address &aSrcAddr,
@@ -340,9 +341,8 @@
 
     SaveMulticastForwardingCache(aSrcAddr, aGroupAddr, aIif, forwardMif);
 exit:
-    LogResult(error, "MulticastRoutingManager: %s: add dynamic route: %s %s => %s %s", __FUNCTION__,
-              MifIndexToString(aIif), aSrcAddr.ToString().AsCString(), aGroupAddr.ToString().AsCString(),
-              MifIndexToString(forwardMif));
+    LogResult(error, "%s: add dynamic route: %s %s => %s %s", __FUNCTION__, MifIndexToString(aIif),
+              aSrcAddr.ToString().AsCString(), aGroupAddr.ToString().AsCString(), MifIndexToString(forwardMif));
 
     return error;
 }
@@ -377,7 +377,7 @@
 
         mfc.Set(kMifIndexBackbone, kMifIndexThread);
 
-        LogResult(error, "MulticastRoutingManager: %s: %s %s => %s %s", __FUNCTION__, MifIndexToString(mfc.mIif),
+        LogResult(error, "%s: %s %s => %s %s", __FUNCTION__, MifIndexToString(mfc.mIif),
                   mfc.mSrcAddr.ToString().AsCString(), mfc.mGroupAddr.ToString().AsCString(),
                   MifIndexToString(kMifIndexThread));
     }
@@ -439,9 +439,9 @@
     {
         unsigned long validPktCnt;
 
-        otLogDebgPlat("MulticastRoutingManager: %s: SIOCGETSGCNT_IN6 %s => %s: bytecnt=%lu, pktcnt=%lu, wrong_if=%lu",
-                      __FUNCTION__, aMfc.mSrcAddr.ToString().AsCString(), aMfc.mGroupAddr.ToString().AsCString(),
-                      sioc_sg_req6.bytecnt, sioc_sg_req6.pktcnt, sioc_sg_req6.wrong_if);
+        LogDebg("%s: SIOCGETSGCNT_IN6 %s => %s: bytecnt=%lu, pktcnt=%lu, wrong_if=%lu", __FUNCTION__,
+                aMfc.mSrcAddr.ToString().AsCString(), aMfc.mGroupAddr.ToString().AsCString(), sioc_sg_req6.bytecnt,
+                sioc_sg_req6.pktcnt, sioc_sg_req6.wrong_if);
 
         validPktCnt = sioc_sg_req6.pktcnt - sioc_sg_req6.wrong_if;
         if (validPktCnt != aMfc.mValidPktCnt)
@@ -453,8 +453,8 @@
     }
     else
     {
-        otLogDebgPlat("MulticastRoutingManager: %s: SIOCGETSGCNT_IN6 %s => %s failed: %s", __FUNCTION__,
-                      aMfc.mSrcAddr.ToString().AsCString(), aMfc.mGroupAddr.ToString().AsCString(), strerror(errno));
+        LogDebg("%s: SIOCGETSGCNT_IN6 %s => %s failed: %s", __FUNCTION__, aMfc.mSrcAddr.ToString().AsCString(),
+                aMfc.mGroupAddr.ToString().AsCString(), strerror(errno));
     }
 
     return updated;
@@ -483,19 +483,18 @@
 void MulticastRoutingManager::DumpMulticastForwardingCache(void) const
 {
 #if OPENTHREAD_CONFIG_LOG_PLATFORM && (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_DEBG)
-    otLogDebgPlat("MulticastRoutingManager: ==================== MFC ENTRIES ====================");
+    LogDebg("==================== MFC ENTRIES ====================");
 
     for (const MulticastForwardingCache &mfc : mMulticastForwardingCacheTable)
     {
         if (mfc.IsValid())
         {
-            otLogDebgPlat("MulticastRoutingManager: %s %s => %s %s", MifIndexToString(mfc.mIif),
-                          mfc.mSrcAddr.ToString().AsCString(), mfc.mGroupAddr.ToString().AsCString(),
-                          MifIndexToString(mfc.mOif));
+            LogDebg("%s %s => %s %s", MifIndexToString(mfc.mIif), mfc.mSrcAddr.ToString().AsCString(),
+                    mfc.mGroupAddr.ToString().AsCString(), MifIndexToString(mfc.mOif));
         }
     }
 
-    otLogDebgPlat("MulticastRoutingManager: =====================================================");
+    LogDebg("=====================================================");
 #endif
 }
 
@@ -605,7 +604,7 @@
                 ? OT_ERROR_NONE
                 : OT_ERROR_FAILED;
 
-    LogResult(error, "MulticastRoutingManager: %s: %s %s => %s %s", __FUNCTION__, MifIndexToString(aMfc.mIif),
+    LogResult(error, "%s: %s %s => %s %s", __FUNCTION__, MifIndexToString(aMfc.mIif),
               aMfc.mSrcAddr.ToString().AsCString(), aMfc.mGroupAddr.ToString().AsCString(),
               MifIndexToString(aMfc.mOif));
 
diff --git a/src/posix/platform/multicast_routing.hpp b/src/posix/platform/multicast_routing.hpp
index c4d7e32..9d4e049 100644
--- a/src/posix/platform/multicast_routing.hpp
+++ b/src/posix/platform/multicast_routing.hpp
@@ -39,18 +39,21 @@
 #include <openthread/backbone_router_ftd.h>
 #include <openthread/openthread-system.h>
 
+#include "logger.hpp"
+#include "mainloop.hpp"
 #include "platform-posix.h"
 #include "core/common/non_copyable.hpp"
 #include "core/net/ip6_address.hpp"
 #include "lib/url/url.hpp"
-#include "posix/platform/mainloop.hpp"
 
 namespace ot {
 namespace Posix {
 
-class MulticastRoutingManager : public Mainloop::Source, private NonCopyable
+class MulticastRoutingManager : public Mainloop::Source, public Logger<MulticastRoutingManager>, private NonCopyable
 {
 public:
+    static const char kLogModuleName[];
+
     explicit MulticastRoutingManager()
 
         : mLastExpireTime(0)
diff --git a/src/posix/platform/netif.cpp b/src/posix/platform/netif.cpp
index 3d546eb..e0a4c49 100644
--- a/src/posix/platform/netif.cpp
+++ b/src/posix/platform/netif.cpp
@@ -147,14 +147,14 @@
 #include <openthread/message.h>
 #include <openthread/nat64.h>
 #include <openthread/netdata.h>
+#include <openthread/thread.h>
 #include <openthread/platform/border_routing.h>
 #include <openthread/platform/misc.h>
 
-#include "common/code_utils.hpp"
-#include "common/debug.hpp"
-#include "net/ip6_address.hpp"
-
+#include "ip6_utils.hpp"
+#include "logger.hpp"
 #include "resolver.hpp"
+#include "common/code_utils.hpp"
 
 unsigned int gNetifIndex = 0;
 char         gNetifName[IFNAMSIZ];
@@ -167,10 +167,10 @@
 unsigned int otSysGetThreadNetifIndex(void) { return gNetifIndex; }
 
 #if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE
+
 #if OPENTHREAD_POSIX_CONFIG_FIREWALL_ENABLE
 #include "firewall.hpp"
 #endif
-#include "posix/platform/ip6_utils.hpp"
 
 using namespace ot::Posix::Ip6Utils;
 
@@ -191,20 +191,20 @@
 #define OPENTHREAD_POSIX_TUN_DEVICE "/dev/net/tun"
 #endif
 
-#endif // OPENTHREAD_TUN_DEVICE
+#endif // OPENTHREAD_POSIX_TUN_DEVICE
 
-#if defined(__linux__)
+#ifdef __linux__
 static uint32_t sNetlinkSequence = 0; ///< Netlink message sequence.
 #endif
 
-#if OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE && __linux__
+#if OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE && defined(__linux__)
 static constexpr uint32_t kOmrRoutesPriority = OPENTHREAD_POSIX_CONFIG_OMR_ROUTES_PRIORITY;
 static constexpr uint8_t  kMaxOmrRoutesNum   = OPENTHREAD_POSIX_CONFIG_MAX_OMR_ROUTES_NUM;
 static uint8_t            sAddedOmrRoutesNum = 0;
 static otIp6Prefix        sAddedOmrRoutes[kMaxOmrRoutesNum];
 #endif
 
-#if OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE && __linux__
+#if OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE && defined(__linux__)
 static constexpr uint32_t kExternalRoutePriority  = OPENTHREAD_POSIX_CONFIG_EXTERNAL_ROUTE_PRIORITY;
 static constexpr uint8_t  kMaxExternalRoutesNum   = OPENTHREAD_POSIX_CONFIG_MAX_EXTERNAL_ROUTE_NUM;
 static uint8_t            sAddedExternalRoutesNum = 0;
@@ -291,35 +291,106 @@
 
 #define OPENTHREAD_POSIX_LOG_TUN_PACKETS 0
 
+static const char kLogModuleName[] = "Netif";
+
+static void LogCrit(const char *aFormat, ...)
+{
+    va_list args;
+
+    va_start(args, aFormat);
+    otLogPlatArgs(OT_LOG_LEVEL_CRIT, kLogModuleName, aFormat, args);
+    va_end(args);
+}
+
+static void LogWarn(const char *aFormat, ...)
+{
+    va_list args;
+
+    va_start(args, aFormat);
+    otLogPlatArgs(OT_LOG_LEVEL_WARN, kLogModuleName, aFormat, args);
+    va_end(args);
+}
+
+static void LogNote(const char *aFormat, ...)
+{
+    va_list args;
+
+    va_start(args, aFormat);
+    otLogPlatArgs(OT_LOG_LEVEL_NOTE, kLogModuleName, aFormat, args);
+    va_end(args);
+}
+
+static void LogInfo(const char *aFormat, ...)
+{
+    va_list args;
+
+    va_start(args, aFormat);
+    otLogPlatArgs(OT_LOG_LEVEL_INFO, kLogModuleName, aFormat, args);
+    va_end(args);
+}
+
+static void LogDebg(const char *aFormat, ...)
+{
+    va_list args;
+
+    va_start(args, aFormat);
+    otLogPlatArgs(OT_LOG_LEVEL_DEBG, kLogModuleName, aFormat, args);
+    va_end(args);
+}
+
 #if defined(__APPLE__) || defined(__NetBSD__) || defined(__FreeBSD__)
-static const uint8_t allOnes[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
-                                  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+
+static const uint8_t kAllOnes[] = {
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+};
 
 #define BITS_PER_BYTE 8
 #define MAX_PREFIX_LENGTH (OT_IP6_ADDRESS_SIZE * BITS_PER_BYTE)
 
+static void CopyBits(uint8_t *aDst, const uint8_t *aSrc, uint8_t aNumBits)
+{
+    // Copies `aNumBits` from `aSrc` into `aDst` handling
+    // the case where `aNumBits` may not be a multiple of 8.
+    // Leaves the remaining bits beyond `aNumBits` in `aDst`
+    // unchanged.
+
+    uint8_t numBytes  = aNumBits / BITS_PER_BYTE;
+    uint8_t extraBits = aNumBits % BITS_PER_BYTE;
+
+    memcpy(aDst, aSrc, numBytes);
+
+    if (extraBits > 0)
+    {
+        uint8_t mask = ((0x80 >> (extraBits - 1)) - 1);
+
+        aDst[numBytes] &= mask;
+        aDst[numBytes] |= (aSrc[numBytes] & ~mask);
+    }
+}
+
 static void InitNetaskWithPrefixLength(struct in6_addr *address, uint8_t prefixLen)
 {
-    ot::Ip6::Address addr;
+    otIp6Address addr;
 
     if (prefixLen > MAX_PREFIX_LENGTH)
     {
         prefixLen = MAX_PREFIX_LENGTH;
     }
 
-    addr.Clear();
-    addr.SetPrefix(allOnes, prefixLen);
-    memcpy(address, addr.mFields.m8, sizeof(addr.mFields.m8));
+    memset(&addr, 0, sizeof(otIp6Address));
+    CopyBits(addr.mFields.m8, kAllOnes, prefixLen);
+    CopyIp6AddressTo(addr, address);
 }
 
 static uint8_t NetmaskToPrefixLength(const struct sockaddr_in6 *netmask)
 {
     return otIp6PrefixMatch(reinterpret_cast<const otIp6Address *>(netmask->sin6_addr.s6_addr),
-                            reinterpret_cast<const otIp6Address *>(allOnes));
+                            reinterpret_cast<const otIp6Address *>(kAllOnes));
 }
-#endif
 
-#if defined(__linux__)
+#endif // defined(__APPLE__) || defined(__NetBSD__) || defined(__FreeBSD__)
+
+#ifdef __linux__
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wcast-align"
 
@@ -375,7 +446,7 @@
     memset(&req, 0, sizeof(req));
 
     req.nh.nlmsg_len   = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
-    req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL;
+    req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | (aIsAdded ? (NLM_F_CREATE | NLM_F_EXCL) : 0);
     req.nh.nlmsg_type  = aIsAdded ? RTM_NEWADDR : RTM_DELADDR;
     req.nh.nlmsg_pid   = 0;
     req.nh.nlmsg_seq   = ++sNetlinkSequence;
@@ -388,7 +459,7 @@
 
     AddRtAttr(&req.nh, sizeof(req), IFA_LOCAL, aAddressInfo.mAddress, sizeof(*aAddressInfo.mAddress));
 
-    if (!aAddressInfo.mPreferred)
+    if (!aAddressInfo.mPreferred || aAddressInfo.mMeshLocal)
     {
         struct ifa_cacheinfo cacheinfo;
 
@@ -412,7 +483,9 @@
 #endif
     {
 #if OPENTHREAD_POSIX_CONFIG_NETIF_PREFIX_ROUTE_METRIC > 0
-        if (aAddressInfo.mScope > ot::Ip6::Address::kLinkLocalScope)
+        static constexpr kLinkLocalScope = 2;
+
+        if (aAddressInfo.mScope > kLinkLocalScope)
         {
             AddRtAttrUint32(&req.nh, sizeof(req), IFA_RT_PRIORITY, OPENTHREAD_POSIX_CONFIG_NETIF_PREFIX_ROUTE_METRIC);
         }
@@ -421,18 +494,18 @@
 
     if (send(sNetlinkFd, &req, req.nh.nlmsg_len, 0) != -1)
     {
-        otLogInfoPlat("[netif] Sent request#%u to %s %s/%u", sNetlinkSequence, (aIsAdded ? "add" : "remove"),
-                      Ip6AddressString(aAddressInfo.mAddress).AsCString(), aAddressInfo.mPrefixLength);
+        LogInfo("Sent request#%u to %s %s/%u", sNetlinkSequence, (aIsAdded ? "add" : "remove"),
+                Ip6AddressString(aAddressInfo.mAddress).AsCString(), aAddressInfo.mPrefixLength);
     }
     else
     {
-        otLogWarnPlat("[netif] Failed to send request#%u to %s %s/%u", sNetlinkSequence, (aIsAdded ? "add" : "remove"),
-                      Ip6AddressString(aAddressInfo.mAddress).AsCString(), aAddressInfo.mPrefixLength);
+        LogWarn("Failed to send request#%u to %s %s/%u", sNetlinkSequence, (aIsAdded ? "add" : "remove"),
+                Ip6AddressString(aAddressInfo.mAddress).AsCString(), aAddressInfo.mPrefixLength);
     }
 }
 
 #pragma GCC diagnostic pop
-#endif // defined(__linux__)
+#endif // __linux__
 
 static void UpdateUnicast(otInstance *aInstance, const otIp6AddressInfo &aAddressInfo, bool aIsAdded)
 {
@@ -441,7 +514,7 @@
     assert(gInstance == aInstance);
     assert(sIpFd >= 0);
 
-#if defined(__linux__)
+#ifdef __linux__
     UpdateUnicastLinux(aInstance, aAddressInfo, aIsAdded);
 #elif defined(__APPLE__) || defined(__NetBSD__) || defined(__FreeBSD__)
     {
@@ -456,25 +529,25 @@
         ifr6.ifra_prefixmask.sin6_family = AF_INET6;
         ifr6.ifra_prefixmask.sin6_len    = sizeof(ifr6.ifra_prefixmask);
         InitNetaskWithPrefixLength(&ifr6.ifra_prefixmask.sin6_addr, aAddressInfo.mPrefixLength);
-        ifr6.ifra_lifetime.ia6t_vltime    = ND6_INFINITE_LIFETIME;
-        ifr6.ifra_lifetime.ia6t_pltime    = ND6_INFINITE_LIFETIME;
+        ifr6.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
+        ifr6.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
 
 #if defined(__APPLE__)
-        ifr6.ifra_lifetime.ia6t_expire    = ND6_INFINITE_LIFETIME;
-        ifr6.ifra_lifetime.ia6t_preferred = (aAddressInfo.mPreferred ? ND6_INFINITE_LIFETIME : 0);
+        ifr6.ifra_lifetime.ia6t_expire = ND6_INFINITE_LIFETIME;
+        ifr6.ifra_lifetime.ia6t_preferred =
+            (aAddressInfo.mPreferred && !aAddressInfo.mMeshLocal ? ND6_INFINITE_LIFETIME : 0);
 #endif
 
         rval = ioctl(sIpFd, aIsAdded ? SIOCAIFADDR_IN6 : SIOCDIFADDR_IN6, &ifr6);
         if (rval == 0)
         {
-            otLogInfoPlat("[netif] %s %s/%u", (aIsAdded ? "Added" : "Removed"),
-                          Ip6AddressString(aAddressInfo.mAddress).AsCString(), aAddressInfo.mPrefixLength);
+            LogInfo("%s %s/%u", (aIsAdded ? "Added" : "Removed"), Ip6AddressString(aAddressInfo.mAddress).AsCString(),
+                    aAddressInfo.mPrefixLength);
         }
         else if (errno != EALREADY)
         {
-            otLogWarnPlat("[netif] Failed to %s %s/%u: %s", (aIsAdded ? "add" : "remove"),
-                          Ip6AddressString(aAddressInfo.mAddress).AsCString(), aAddressInfo.mPrefixLength,
-                          strerror(errno));
+            LogWarn("Failed to %s %s/%u: %s", (aIsAdded ? "add" : "remove"),
+                    Ip6AddressString(aAddressInfo.mAddress).AsCString(), aAddressInfo.mPrefixLength, strerror(errno));
         }
     }
 #endif
@@ -506,21 +579,20 @@
         char addressString[INET6_ADDRSTRLEN + 1];
 
         inet_ntop(AF_INET6, mreq.ipv6mr_multiaddr.s6_addr, addressString, sizeof(addressString));
-        otLogWarnPlat("[netif] Ignoring %s failure (EINVAL) for MC LINKLOCAL address (%s)",
-                      aIsAdded ? "IPV6_JOIN_GROUP" : "IPV6_LEAVE_GROUP", addressString);
+        LogWarn("Ignoring %s failure (EINVAL) for MC LINKLOCAL address (%s)",
+                aIsAdded ? "IPV6_JOIN_GROUP" : "IPV6_LEAVE_GROUP", addressString);
         err = 0;
     }
 #endif
 
     if (err != 0)
     {
-        otLogWarnPlat("[netif] %s failure (%d)", aIsAdded ? "IPV6_JOIN_GROUP" : "IPV6_LEAVE_GROUP", errno);
+        LogWarn("%s failure (%d)", aIsAdded ? "IPV6_JOIN_GROUP" : "IPV6_LEAVE_GROUP", errno);
         error = OT_ERROR_FAILED;
         ExitNow();
     }
 
-    otLogInfoPlat("[netif] %s multicast address %s", aIsAdded ? "Added" : "Removed",
-                  Ip6AddressString(&aAddress).AsCString());
+    LogInfo("%s multicast address %s", aIsAdded ? "Added" : "Removed", Ip6AddressString(&aAddress).AsCString());
 
 exit:
     SuccessOrDie(error);
@@ -543,8 +615,8 @@
 
     ifState = ((ifr.ifr_flags & IFF_UP) == IFF_UP) ? true : false;
 
-    otLogNotePlat("[netif] Changing interface state to %s%s.", aState ? "up" : "down",
-                  (ifState == aState) ? " (already done, ignoring)" : "");
+    LogNote("Changing interface state to %s%s.", aState ? "up" : "down",
+            (ifState == aState) ? " (already done, ignoring)" : "");
 
     if (ifState != aState)
     {
@@ -559,7 +631,7 @@
 exit:
     if (error != OT_ERROR_NONE)
     {
-        otLogWarnPlat("[netif] Failed to update state %s", otThreadErrorToString(error));
+        LogWarn("Failed to update state %s", otThreadErrorToString(error));
     }
 }
 
@@ -569,7 +641,7 @@
     SetLinkState(aInstance, otIp6IsEnabled(aInstance));
 }
 
-#if defined(__linux__)
+#ifdef __linux__
 template <size_t N> otError AddRoute(const uint8_t (&aAddress)[N], uint8_t aPrefixLen, uint32_t aPriority)
 {
     constexpr unsigned int kBufSize = 128;
@@ -722,15 +794,14 @@
         otIp6PrefixToString(&sAddedOmrRoutes[i], prefixString, sizeof(prefixString));
         if ((error = DeleteRoute(sAddedOmrRoutes[i])) != OT_ERROR_NONE)
         {
-            otLogWarnPlat("[netif] Failed to delete an OMR route %s in kernel: %s", prefixString,
-                          otThreadErrorToString(error));
+            LogWarn("Failed to delete an OMR route %s in kernel: %s", prefixString, otThreadErrorToString(error));
         }
         else
         {
             sAddedOmrRoutes[i] = sAddedOmrRoutes[sAddedOmrRoutesNum - 1];
             --sAddedOmrRoutesNum;
             --i;
-            otLogInfoPlat("[netif] Successfully deleted an OMR route %s in kernel", prefixString);
+            LogInfo("Successfully deleted an OMR route %s in kernel", prefixString);
         }
     }
 
@@ -745,13 +816,12 @@
         otIp6PrefixToString(&config.mPrefix, prefixString, sizeof(prefixString));
         if ((error = AddOmrRoute(config.mPrefix)) != OT_ERROR_NONE)
         {
-            otLogWarnPlat("[netif] Failed to add an OMR route %s in kernel: %s", prefixString,
-                          otThreadErrorToString(error));
+            LogWarn("Failed to add an OMR route %s in kernel: %s", prefixString, otThreadErrorToString(error));
         }
         else
         {
             sAddedOmrRoutes[sAddedOmrRoutesNum++] = config.mPrefix;
-            otLogInfoPlat("[netif] Successfully added an OMR route %s in kernel", prefixString);
+            LogInfo("Successfully added an OMR route %s in kernel", prefixString);
         }
     }
 }
@@ -818,15 +888,14 @@
         otIp6PrefixToString(&sAddedExternalRoutes[i], prefixString, sizeof(prefixString));
         if ((error = DeleteRoute(sAddedExternalRoutes[i])) != OT_ERROR_NONE)
         {
-            otLogWarnPlat("[netif] Failed to delete an external route %s in kernel: %s", prefixString,
-                          otThreadErrorToString(error));
+            LogWarn("Failed to delete an external route %s in kernel: %s", prefixString, otThreadErrorToString(error));
         }
         else
         {
             sAddedExternalRoutes[i] = sAddedExternalRoutes[sAddedExternalRoutesNum - 1];
             --sAddedExternalRoutesNum;
             --i;
-            otLogWarnPlat("[netif] Successfully deleted an external route %s in kernel", prefixString);
+            LogWarn("Successfully deleted an external route %s in kernel", prefixString);
         }
     }
 
@@ -837,18 +906,17 @@
             continue;
         }
         VerifyOrExit(sAddedExternalRoutesNum < kMaxExternalRoutesNum,
-                     otLogWarnPlat("[netif] No buffer to add more external routes in kernel"));
+                     LogWarn("No buffer to add more external routes in kernel"));
 
         otIp6PrefixToString(&config.mPrefix, prefixString, sizeof(prefixString));
         if ((error = AddExternalRoute(config.mPrefix)) != OT_ERROR_NONE)
         {
-            otLogWarnPlat("[netif] Failed to add an external route %s in kernel: %s", prefixString,
-                          otThreadErrorToString(error));
+            LogWarn("Failed to add an external route %s in kernel: %s", prefixString, otThreadErrorToString(error));
         }
         else
         {
             sAddedExternalRoutes[sAddedExternalRoutesNum++] = config.mPrefix;
-            otLogWarnPlat("[netif] Successfully added an external route %s in kernel", prefixString);
+            LogWarn("Successfully added an external route %s in kernel", prefixString);
         }
     }
 exit:
@@ -867,7 +935,7 @@
     return DeleteRoute(aIp4Cidr.mAddress.mFields.m8, aIp4Cidr.mLength);
 }
 #endif
-#endif // defined(__linux__)
+#endif // __linux__
 
 static void processAddressChange(const otIp6AddressInfo *aAddressInfo, bool aIsAdded, void *aContext)
 {
@@ -913,30 +981,30 @@
         {
             if ((error = DeleteIp4Route(sActiveNat64Cidr)) != OT_ERROR_NONE)
             {
-                otLogWarnPlat("[netif] failed to delete route for NAT64: %s", otThreadErrorToString(error));
+                LogWarn("failed to delete route for NAT64: %s", otThreadErrorToString(error));
             }
         }
         sActiveNat64Cidr = translatorCidr;
 
         otIp4CidrToString(&translatorCidr, cidrString, sizeof(cidrString));
-        otLogInfoPlat("[netif] NAT64 CIDR updated to %s.", cidrString);
+        LogInfo("NAT64 CIDR updated to %s.", cidrString);
     }
 
     if (otNat64GetTranslatorState(gInstance) == OT_NAT64_STATE_ACTIVE)
     {
         if ((error = AddIp4Route(sActiveNat64Cidr, kNat64RoutePriority)) != OT_ERROR_NONE)
         {
-            otLogWarnPlat("[netif] failed to add route for NAT64: %s", otThreadErrorToString(error));
+            LogWarn("failed to add route for NAT64: %s", otThreadErrorToString(error));
         }
-        otLogInfoPlat("[netif] Adding route for NAT64");
+        LogInfo("Adding route for NAT64");
     }
     else if (sActiveNat64Cidr.mLength > 0) // Translator is not active.
     {
         if ((error = DeleteIp4Route(sActiveNat64Cidr)) != OT_ERROR_NONE)
         {
-            otLogWarnPlat("[netif] failed to delete route for NAT64: %s", otThreadErrorToString(error));
+            LogWarn("failed to delete route for NAT64: %s", otThreadErrorToString(error));
         }
-        otLogInfoPlat("[netif] Deleting route for NAT64");
+        LogInfo("Deleting route for NAT64");
     }
 
 exit:
@@ -952,10 +1020,10 @@
     }
     if (OT_CHANGED_THREAD_NETDATA & aFlags)
     {
-#if OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE && __linux__
+#if OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE && defined(__linux__)
         UpdateOmrRoutes(aInstance);
 #endif
-#if OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE && __linux__
+#if OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE && defined(__linux__)
         UpdateExternalRoutes(aInstance);
 #endif
 #if OPENTHREAD_POSIX_CONFIG_FIREWALL_ENABLE
@@ -992,7 +1060,7 @@
     VerifyOrExit(otMessageRead(aMessage, 0, &packet[offset], maxLength) == length, error = OT_ERROR_NO_BUFS);
 
 #if OPENTHREAD_POSIX_LOG_TUN_PACKETS
-    otLogInfoPlat("[netif] Packet from NCP (%u bytes)", static_cast<uint16_t>(length));
+    LogInfo("Packet from NCP (%u bytes)", static_cast<uint16_t>(length));
     otDumpInfoPlat("", &packet[offset], length);
 #endif
 
@@ -1011,7 +1079,7 @@
 
     if (error != OT_ERROR_NONE)
     {
-        otLogWarnPlat("[netif] Failed to receive, error:%s", otThreadErrorToString(error));
+        LogWarn("Failed to receive, error:%s", otThreadErrorToString(error));
     }
 }
 
@@ -1068,7 +1136,7 @@
     VerifyOrExit(ra != nullptr, error = OT_ERROR_INVALID_ARGS);
 
 #if OPENTHREAD_POSIX_LOG_TUN_PACKETS
-    otLogInfoPlat("[netif] RA to BorderRouting (%hu bytes)", static_cast<uint16_t>(length));
+    LogInfo("RA to BorderRouting (%hu bytes)", static_cast<uint16_t>(length));
     otDumpInfoPlat("", data, static_cast<size_t>(length));
 #endif
 
@@ -1080,6 +1148,46 @@
 }
 #endif // OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
 
+#ifdef __linux__
+/**
+ * Returns whether the address is a required anycast address (RFC2373, 2.6.1).
+ *
+ */
+static bool isRequiredAnycast(const uint8_t *aAddress, uint8_t aPrefixLength)
+{
+    bool    isRequiredAnycast = false;
+    uint8_t firstBytePos      = aPrefixLength / 8;
+    uint8_t remainingBits     = aPrefixLength % 8;
+
+    if (aPrefixLength == OT_IP6_ADDRESS_BITSIZE)
+    {
+        ExitNow();
+    }
+
+    if (remainingBits != 0)
+    {
+        if ((aAddress[firstBytePos] & ((1 << remainingBits) - 1)) != 0)
+        {
+            ExitNow();
+        }
+        firstBytePos++;
+    }
+
+    for (int i = firstBytePos; i < OT_IP6_ADDRESS_SIZE; ++i)
+    {
+        if (aAddress[i] != 0)
+        {
+            ExitNow();
+        }
+    }
+
+    isRequiredAnycast = true;
+
+exit:
+    return isRequiredAnycast;
+}
+#endif // __linux__
+
 static void processTransmit(otInstance *aInstance)
 {
     otMessage *message = nullptr;
@@ -1124,12 +1232,11 @@
         message = otIp6NewMessage(aInstance, &settings);
 #endif
         VerifyOrExit(message != nullptr, error = OT_ERROR_NO_BUFS);
-        otMessageSetLoopbackToHostAllowed(message, true);
         otMessageSetOrigin(message, OT_MESSAGE_ORIGIN_HOST_UNTRUSTED);
     }
 
 #if OPENTHREAD_POSIX_LOG_TUN_PACKETS
-    otLogInfoPlat("[netif] Packet to NCP (%hu bytes)", static_cast<uint16_t>(rval));
+    LogInfo("Packet to NCP (%hu bytes)", static_cast<uint16_t>(rval));
     otDumpInfoPlat("", &packet[offset], static_cast<size_t>(rval));
 #endif
 
@@ -1152,37 +1259,37 @@
     {
         if (error == OT_ERROR_DROP)
         {
-            otLogInfoPlat("[netif] Message dropped by Thread");
+            LogInfo("Message dropped by Thread");
         }
         else
         {
-            otLogWarnPlat("[netif] Failed to transmit, error:%s", otThreadErrorToString(error));
+            LogWarn("Failed to transmit, error:%s", otThreadErrorToString(error));
         }
     }
 }
 
-static void logAddrEvent(bool isAdd, const ot::Ip6::Address &aAddress, otError error)
+static void logAddrEvent(bool isAdd, const otIp6Address &aAddress, otError error)
 {
     OT_UNUSED_VARIABLE(aAddress);
 
     if ((error == OT_ERROR_NONE) || ((isAdd) && (error == OT_ERROR_ALREADY || error == OT_ERROR_REJECTED)) ||
         ((!isAdd) && (error == OT_ERROR_NOT_FOUND || error == OT_ERROR_REJECTED)))
     {
-        otLogInfoPlat("[netif] %s [%s] %s%s", isAdd ? "ADD" : "DEL", aAddress.IsMulticast() ? "M" : "U",
-                      aAddress.ToString().AsCString(),
-                      error == OT_ERROR_ALREADY     ? " (already subscribed, ignored)"
-                      : error == OT_ERROR_REJECTED  ? " (rejected)"
-                      : error == OT_ERROR_NOT_FOUND ? " (not found, ignored)"
-                                                    : "");
+        LogInfo("%s [%s] %s%s", isAdd ? "ADD" : "DEL", IsIp6AddressMulticast(aAddress) ? "M" : "U",
+                Ip6AddressString(&aAddress).AsCString(),
+                error == OT_ERROR_ALREADY     ? " (already subscribed, ignored)"
+                : error == OT_ERROR_REJECTED  ? " (rejected)"
+                : error == OT_ERROR_NOT_FOUND ? " (not found, ignored)"
+                                              : "");
     }
     else
     {
-        otLogWarnPlat("[netif] %s [%s] %s failed (%s)", isAdd ? "ADD" : "DEL", aAddress.IsMulticast() ? "M" : "U",
-                      aAddress.ToString().AsCString(), otThreadErrorToString(error));
+        LogWarn("%s [%s] %s failed (%s)", isAdd ? "ADD" : "DEL", IsIp6AddressMulticast(aAddress) ? "M" : "U",
+                Ip6AddressString(&aAddress).AsCString(), otThreadErrorToString(error));
     }
 }
 
-#if defined(__linux__)
+#ifdef __linux__
 
 static void processNetifAddrEvent(otInstance *aInstance, struct nlmsghdr *aNetlinkMessage)
 {
@@ -1206,16 +1313,26 @@
         case IFA_ANYCAST:
         case IFA_MULTICAST:
         {
-            ot::Ip6::Address addr;
-            memcpy(&addr, RTA_DATA(rta), sizeof(addr));
+            otIp6Address addr;
+
+            ReadIp6AddressFrom(RTA_DATA(rta), addr);
 
             memset(&addr6, 0, sizeof(addr6));
             addr6.sin6_family = AF_INET6;
             memcpy(&addr6.sin6_addr, RTA_DATA(rta), sizeof(addr6.sin6_addr));
 
+            // Linux allows adding an IPv6 required anycast address to an interface,
+            // which blocks openthread deriving an address by SLAAC and will cause routing issues.
+            // Ignore the required anycast addresses here to allow OpenThread stack generate one when necessary,
+            // and Linux will prefer the non-required anycast address on the interface.
+            if (isRequiredAnycast(addr.mFields.m8, ifaddr->ifa_prefixlen))
+            {
+                continue;
+            }
+
             if (aNetlinkMessage->nlmsg_type == RTM_NEWADDR)
             {
-                if (!addr.IsMulticast())
+                if (!IsIp6AddressMulticast(addr))
                 {
                     otNetifAddress netAddr;
 
@@ -1234,6 +1351,7 @@
                 }
 
                 logAddrEvent(/* isAdd */ true, addr, error);
+
                 if (error == OT_ERROR_ALREADY || error == OT_ERROR_REJECTED)
                 {
                     error = OT_ERROR_NONE;
@@ -1243,7 +1361,7 @@
             }
             else if (aNetlinkMessage->nlmsg_type == RTM_DELADDR)
             {
-                if (!addr.IsMulticast())
+                if (!IsIp6AddressMulticast(addr))
                 {
                     error = otIp6RemoveUnicastAddress(aInstance, &addr);
                 }
@@ -1253,6 +1371,7 @@
                 }
 
                 logAddrEvent(/* isAdd */ false, addr, error);
+
                 if (error == OT_ERROR_NOT_FOUND || error == OT_ERROR_REJECTED)
                 {
                     error = OT_ERROR_NONE;
@@ -1268,7 +1387,7 @@
         }
 
         default:
-            otLogDebgPlat("[netif] Unexpected address type (%d).", (int)rta->rta_type);
+            LogDebg("Unexpected address type (%d).", (int)rta->rta_type);
             break;
         }
     }
@@ -1276,7 +1395,7 @@
 exit:
     if (error != OT_ERROR_NONE)
     {
-        otLogWarnPlat("[netif] Failed to process event, error:%s", otThreadErrorToString(error));
+        LogWarn("Failed to process event, error:%s", otThreadErrorToString(error));
     }
 }
 
@@ -1290,13 +1409,13 @@
 
     isUp = ((ifinfo->ifi_flags & IFF_UP) != 0);
 
-    otLogInfoPlat("[netif] Host netif is %s", isUp ? "up" : "down");
+    LogInfo("Host netif is %s", isUp ? "up" : "down");
 
 #if defined(RTM_NEWLINK) && defined(RTM_DELLINK)
     if (sIsSyncingState)
     {
         VerifyOrExit(isUp == otIp6IsEnabled(aInstance),
-                     otLogWarnPlat("[netif] Host netif state notification is unexpected (ignore)"));
+                     LogWarn("Host netif state notification is unexpected (ignore)"));
         sIsSyncingState = false;
     }
     else
@@ -1304,16 +1423,16 @@
         if (isUp != otIp6IsEnabled(aInstance))
     {
         SuccessOrExit(error = otIp6SetEnabled(aInstance, isUp));
-        otLogInfoPlat("[netif] Succeeded to sync netif state with host");
+        LogInfo("Succeeded to sync netif state with host");
     }
 
 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE && OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
-    if (isUp && sActiveNat64Cidr.mLength > 0)
+    if (isUp && otNat64GetTranslatorState(gInstance) == OT_NAT64_STATE_ACTIVE)
     {
         // Recover NAT64 route.
         if ((error = AddIp4Route(sActiveNat64Cidr, kNat64RoutePriority)) != OT_ERROR_NONE)
         {
-            otLogWarnPlat("[netif] failed to add route for NAT64: %s", otThreadErrorToString(error));
+            LogWarn("failed to add route for NAT64: %s", otThreadErrorToString(error));
         }
     }
 #endif
@@ -1321,10 +1440,10 @@
 exit:
     if (error != OT_ERROR_NONE)
     {
-        otLogWarnPlat("[netif] Failed to sync netif state with host: %s", otThreadErrorToString(error));
+        LogWarn("Failed to sync netif state with host: %s", otThreadErrorToString(error));
     }
 }
-#endif // defined(__linux__)
+#endif // __linux__
 
 #if defined(__APPLE__) || defined(__NetBSD__) || defined(__FreeBSD__)
 
@@ -1406,7 +1525,10 @@
 
     if (addr6.sin6_family == AF_INET6)
     {
+        otIp6Address addr;
+
         is_link_local = false;
+
         if (IN6_IS_ADDR_LINKLOCAL(&addr6.sin6_addr))
         {
             is_link_local = true;
@@ -1418,8 +1540,7 @@
             addr6.sin6_addr.s6_addr[3] = 0;
         }
 
-        ot::Ip6::Address addr;
-        memcpy(&addr, &addr6.sin6_addr, sizeof(addr));
+        ReadIp6AddressFrom(&addr6.sin6_addr, addr);
 
         if (rtm->rtm_type == RTM_NEWADDR
 #ifdef RTM_NEWMADDR
@@ -1427,7 +1548,7 @@
 #endif
         )
         {
-            if (!addr.IsMulticast())
+            if (!IsIp6AddressMulticast(addr))
             {
                 otNetifAddress netAddr;
 
@@ -1470,16 +1591,14 @@
                         err = ioctl(sIpFd, SIOCDIFADDR_IN6, &ifr6);
                         if (err != 0)
                         {
-                            otLogWarnPlat(
-                                "[netif] Error (%d) removing stack-addded link-local address %s", errno,
-                                inet_ntop(AF_INET6, addr6.sin6_addr.s6_addr, addressString, sizeof(addressString)));
+                            LogWarn("Error (%d) removing stack-addded link-local address %s", errno,
+                                    inet_ntop(AF_INET6, addr6.sin6_addr.s6_addr, addressString, sizeof(addressString)));
                             error = OT_ERROR_FAILED;
                         }
                         else
                         {
-                            otLogNotePlat(
-                                "[netif]        %s (removed stack-added link-local)",
-                                inet_ntop(AF_INET6, addr6.sin6_addr.s6_addr, addressString, sizeof(addressString)));
+                            LogNote("       %s (removed stack-added link-local)",
+                                    inet_ntop(AF_INET6, addr6.sin6_addr.s6_addr, addressString, sizeof(addressString)));
                             error = OT_ERROR_NONE;
                         }
                     }
@@ -1487,6 +1606,7 @@
                     {
                         error = otIp6AddUnicastAddress(aInstance, &netAddr);
                         logAddrEvent(/* isAdd */ true, addr, error);
+
                         if (error == OT_ERROR_ALREADY)
                         {
                             error = OT_ERROR_NONE;
@@ -1502,6 +1622,7 @@
 
                 error = otIp6SubscribeMulticastAddress(aInstance, &addr);
                 logAddrEvent(/* isAdd */ true, addr, error);
+
                 if (error == OT_ERROR_ALREADY || error == OT_ERROR_REJECTED)
                 {
                     error = OT_ERROR_NONE;
@@ -1515,10 +1636,11 @@
 #endif
         )
         {
-            if (!addr.IsMulticast())
+            if (!IsIp6AddressMulticast(addr))
             {
                 error = otIp6RemoveUnicastAddress(aInstance, &addr);
                 logAddrEvent(/* isAdd */ false, addr, error);
+
                 if (error == OT_ERROR_NOT_FOUND)
                 {
                     error = OT_ERROR_NONE;
@@ -1528,11 +1650,13 @@
             {
                 error = otIp6UnsubscribeMulticastAddress(aInstance, &addr);
                 logAddrEvent(/* isAdd */ false, addr, error);
+
                 if (error == OT_ERROR_NOT_FOUND)
                 {
                     error = OT_ERROR_NONE;
                 }
             }
+
             SuccessOrExit(error);
         }
     }
@@ -1552,13 +1676,13 @@
 exit:
     if (error != OT_ERROR_NONE)
     {
-        otLogWarnPlat("[netif] Failed to process info event: %s", otThreadErrorToString(error));
+        LogWarn("Failed to process info event: %s", otThreadErrorToString(error));
     }
 }
 
 #endif
 
-#if defined(__linux__)
+#ifdef __linux__
 
 #define ERR_RTA(errmsg, requestPayloadLength) \
     ((struct rtattr *)((char *)(errmsg)) + NLMSG_ALIGN(sizeof(struct nlmsgerr)) + NLMSG_ALIGN(requestPayloadLength))
@@ -1587,7 +1711,7 @@
 
     if (msg->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr)))
     {
-        otLogWarnPlat("[netif] Truncated netlink reply of request#%u", requestSeq);
+        LogWarn("Truncated netlink reply of request#%u", requestSeq);
         ExitNow();
     }
 
@@ -1596,7 +1720,7 @@
 
     if (err->error == 0)
     {
-        otLogInfoPlat("[netif] Succeeded to process request#%u", requestSeq);
+        LogInfo("Succeeded to process request#%u", requestSeq);
         ExitNow();
     }
 
@@ -1622,17 +1746,17 @@
         }
         else
         {
-            otLogDebgPlat("[netif] Ignoring netlink response attribute %d (request#%u)", rta->rta_type, requestSeq);
+            LogDebg("Ignoring netlink response attribute %d (request#%u)", rta->rta_type, requestSeq);
         }
     }
 
-    otLogWarnPlat("[netif] Failed to process request#%u: %s", requestSeq, errorMsg);
+    LogWarn("Failed to process request#%u: %s", requestSeq, errorMsg);
 
 exit:
     return;
 }
 
-#endif // defined(__linux__)
+#endif // __linux__
 
 static void processNetlinkEvent(otInstance *aInstance)
 {
@@ -1641,7 +1765,7 @@
 
     union
     {
-#if defined(__linux__)
+#ifdef __linux__
         nlmsghdr nlMsg;
 #else
         rt_msghdr rtMsg;
@@ -1651,7 +1775,7 @@
 
     length = recv(sNetlinkFd, msgBuffer.buffer, sizeof(msgBuffer.buffer), 0);
 
-#if defined(__linux__)
+#ifdef __linux__
 #define HEADER_SIZE sizeof(nlmsghdr)
 #else
 #define HEADER_SIZE sizeof(rt_msghdr)
@@ -1660,11 +1784,11 @@
     // Ensures full netlink header is received
     if (length < static_cast<ssize_t>(HEADER_SIZE))
     {
-        otLogWarnPlat("[netif] Unexpected netlink recv() result: %ld", static_cast<long>(length));
+        LogWarn("Unexpected netlink recv() result: %ld", static_cast<long>(length));
         ExitNow();
     }
 
-#if defined(__linux__)
+#ifdef __linux__
     for (struct nlmsghdr *msg = &msgBuffer.nlMsg; NLMSG_OK(msg, static_cast<size_t>(length));
          msg                  = NLMSG_NEXT(msg, length))
     {
@@ -1680,7 +1804,7 @@
 #endif
         switch (msg->nlmsg_type)
         {
-#if defined(__linux__)
+#ifdef __linux__
         case NLMSG_DONE:
             // NLMSG_DONE indicates the end of the netlink message, exits now
             ExitNow();
@@ -1705,7 +1829,7 @@
             break;
 #endif
 
-#if !defined(__linux__)
+#ifndef __linux__
         case RTM_IFINFO:
             processNetifInfoEvent(aInstance, msg);
             break;
@@ -1718,7 +1842,7 @@
 
 #if defined(ROUTE_FILTER) || defined(RO_MSGFILTER) || defined(__linux__)
         default:
-            otLogWarnPlat("[netif] Unhandled/Unexpected netlink/route message (%d).", (int)msg->nlmsg_type);
+            LogWarn("Unhandled/Unexpected netlink/route message (%d).", (int)msg->nlmsg_type);
             break;
 #else
             // this platform doesn't support filtering, so we expect messages of other types...we just ignore them
@@ -1742,7 +1866,7 @@
     memcpy(&mreq6.ipv6mr_multiaddr, kMLDv2MulticastAddress.mFields.m8, sizeof(kMLDv2MulticastAddress.mFields.m8));
 
     VerifyOrDie(setsockopt(sMLDMonitorFd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq6, sizeof(mreq6)) == 0, OT_EXIT_FAILURE);
-#if defined(__linux__)
+#ifdef __linux__
     VerifyOrDie(setsockopt(sMLDMonitorFd, SOL_SOCKET, SO_BINDTODEVICE, gNetifName,
                            static_cast<socklen_t>(strnlen(gNetifName, IFNAMSIZ))) == 0,
                 OT_EXIT_FAILURE);
@@ -1799,11 +1923,13 @@
         {
             MLDv2Record *record = reinterpret_cast<MLDv2Record *>(&buffer[offset]);
 
-            otError          err;
-            ot::Ip6::Address address;
+            otError      err;
+            otIp6Address address;
 
-            memcpy(&address.mFields.m8, &record->mMulticastAddress, sizeof(address.mFields.m8));
+            ReadIp6AddressFrom(&record->mMulticastAddress, address);
+
             inet_ntop(AF_INET6, &record->mMulticastAddress, addressString, sizeof(addressString));
+
             if (record->mRecordType == kICMPv6MLDv2RecordChangeToIncludeType)
             {
                 err = otIp6SubscribeMulticastAddress(aInstance, &address);
@@ -1827,7 +1953,7 @@
 }
 #endif
 
-#if defined(__linux__)
+#ifdef __linux__
 static void SetAddrGenModeToNone(void)
 {
     struct
@@ -1863,11 +1989,11 @@
 
     if (send(sNetlinkFd, &req, req.nh.nlmsg_len, 0) != -1)
     {
-        otLogInfoPlat("[netif] Sent request#%u to set addr_gen_mode to %d", sNetlinkSequence, mode);
+        LogInfo("Sent request#%u to set addr_gen_mode to %d", sNetlinkSequence, mode);
     }
     else
     {
-        otLogWarnPlat("[netif] Failed to send request#%u to set addr_gen_mode to %d", sNetlinkSequence, mode);
+        LogWarn("Failed to send request#%u to set addr_gen_mode to %d", sNetlinkSequence, mode);
     }
 }
 
@@ -1948,7 +2074,7 @@
     err        = getsockopt(sTunFd, SYSPROTO_CONTROL, UTUN_OPT_IFNAME, gNetifName, &devNameLen);
     VerifyOrDie(err == 0, OT_EXIT_ERROR_ERRNO);
 
-    otLogInfoPlat("[netif] Tunnel device name = '%s'", gNetifName);
+    LogInfo("Tunnel device name = '%s'", gNetifName);
 }
 #endif
 
@@ -2004,7 +2130,7 @@
 
 static void platformConfigureNetLink(void)
 {
-#if defined(__linux__)
+#ifdef __linux__
     sNetlinkFd = SocketWithCloseExec(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE, kSocketNonBlock);
 #elif defined(__APPLE__) || defined(__NetBSD__) || defined(__FreeBSD__)
     sNetlinkFd = SocketWithCloseExec(PF_ROUTE, SOCK_RAW, 0, kSocketNonBlock);
@@ -2020,19 +2146,19 @@
 #if defined(NETLINK_EXT_ACK)
         if (setsockopt(sNetlinkFd, SOL_NETLINK, NETLINK_EXT_ACK, &enable, sizeof(enable)) != 0)
         {
-            otLogWarnPlat("[netif] Failed to enable NETLINK_EXT_ACK: %s", strerror(errno));
+            LogWarn("Failed to enable NETLINK_EXT_ACK: %s", strerror(errno));
         }
 #endif
 #if defined(NETLINK_CAP_ACK)
         if (setsockopt(sNetlinkFd, SOL_NETLINK, NETLINK_CAP_ACK, &enable, sizeof(enable)) != 0)
         {
-            otLogWarnPlat("[netif] Failed to enable NETLINK_CAP_ACK: %s", strerror(errno));
+            LogWarn("Failed to enable NETLINK_CAP_ACK: %s", strerror(errno));
         }
 #endif
     }
 #endif
 
-#if defined(__linux__)
+#ifdef __linux__
     {
         struct sockaddr_nl sa;
 
@@ -2071,6 +2197,13 @@
 
 void platformNetifInit(otPlatformConfig *aPlatformConfig)
 {
+    // To silence "unused function" warning.
+    (void)LogCrit;
+    (void)LogWarn;
+    (void)LogInfo;
+    (void)LogNote;
+    (void)LogDebg;
+
     sIpFd = SocketWithCloseExec(AF_INET6, SOCK_DGRAM, IPPROTO_IP, kSocketNonBlock);
     VerifyOrDie(sIpFd >= 0, OT_EXIT_ERROR_ERRNO);
 
@@ -2084,7 +2217,7 @@
     mldListenerInit();
 #endif
 
-#if __linux__
+#ifdef __linux__
     SetAddrGenModeToNone();
 #endif
 }
@@ -2099,19 +2232,19 @@
     {
         if ((error = otNat64SetIp4Cidr(gInstance, &cidr)) != OT_ERROR_NONE)
         {
-            otLogWarnPlat("[netif] failed to set CIDR for NAT64: %s", otThreadErrorToString(error));
+            LogWarn("failed to set CIDR for NAT64: %s", otThreadErrorToString(error));
         }
     }
     else
     {
-        otLogInfoPlat("[netif] No default NAT64 CIDR provided.");
+        LogInfo("No default NAT64 CIDR provided.");
     }
 }
 #endif
 
 void platformNetifSetUp(void)
 {
-    OT_ASSERT(gInstance != nullptr);
+    assert(gInstance != nullptr);
 
     otIp6SetReceiveFilterEnabled(gInstance, true);
 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
diff --git a/src/posix/platform/openthread-core-posix-config.h b/src/posix/platform/openthread-core-posix-config.h
index 2b3297a..ebe784f 100644
--- a/src/posix/platform/openthread-core-posix-config.h
+++ b/src/posix/platform/openthread-core-posix-config.h
@@ -34,6 +34,8 @@
 #ifndef OPENTHREAD_CORE_POSIX_CONFIG_H_
 #define OPENTHREAD_CORE_POSIX_CONFIG_H_
 
+#include "openthread-posix-daemon-config.h"
+
 #ifndef OPENTHREAD_CONFIG_NUM_MESSAGE_BUFFERS
 #define OPENTHREAD_CONFIG_NUM_MESSAGE_BUFFERS 256
 #endif
diff --git a/src/posix/platform/openthread-posix-config.h b/src/posix/platform/openthread-posix-config.h
index cc8afe5..ade8ab2 100644
--- a/src/posix/platform/openthread-posix-config.h
+++ b/src/posix/platform/openthread-posix-config.h
@@ -26,8 +26,8 @@
  *  POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef OPENTHREAD_PLATFORM_CONFIG_H_
-#define OPENTHREAD_PLATFORM_CONFIG_H_
+#ifndef OPENTHREAD_PLATFORM_POSIX_CONFIG_H_
+#define OPENTHREAD_PLATFORM_POSIX_CONFIG_H_
 
 #include "openthread-core-config.h"
 
@@ -52,40 +52,6 @@
 #endif
 
 /**
- * @def OPENTHREAD_POSIX_CONFIG_DAEMON_SOCKET_BASENAME
- *
- * Define socket basename used by POSIX app daemon.
- *
- */
-#ifndef OPENTHREAD_POSIX_CONFIG_DAEMON_SOCKET_BASENAME
-#ifdef __linux__
-#define OPENTHREAD_POSIX_CONFIG_DAEMON_SOCKET_BASENAME "/run/openthread-%s"
-#else
-#define OPENTHREAD_POSIX_CONFIG_DAEMON_SOCKET_BASENAME "/tmp/openthread-%s"
-#endif
-#endif
-
-/**
- * @def OPENTHREAD_POSIX_CONFIG_DAEMON_ENABLE
- *
- * Define to 1 to enable POSIX daemon.
- *
- */
-#ifndef OPENTHREAD_POSIX_CONFIG_DAEMON_ENABLE
-#define OPENTHREAD_POSIX_CONFIG_DAEMON_ENABLE 0
-#endif
-
-/**
- * @def OPENTHREAD_POSIX_CONFIG_DAEMON_CLI_ENABLE
- *
- * Define to 1 to enable CLI for the posix daemon.
- *
- */
-#ifndef OPENTHREAD_POSIX_CONFIG_DAEMON_CLI_ENABLE
-#define OPENTHREAD_POSIX_CONFIG_DAEMON_CLI_ENABLE 1
-#endif
-
-/**
  * @def OPENTHREAD_POSIX_CONFIG_RCP_BUS
  *
  * This setting configures what type of RCP bus to use.
@@ -453,4 +419,14 @@
 #define OPENTHREAD_POSIX_CONFIG_EXIT_ON_INFRA_NETIF_LOST_ENABLE 1
 #endif
 
-#endif // OPENTHREAD_PLATFORM_CONFIG_H_
+/**
+ * @def OPENTHREAD_POSIX_CONFIG_TREL_TX_PACKET_POOL_SIZE
+ *
+ * This setting configures the capacity of TREL packet pool for transmission.
+ *
+ */
+#ifndef OPENTHREAD_POSIX_CONFIG_TREL_TX_PACKET_POOL_SIZE
+#define OPENTHREAD_POSIX_CONFIG_TREL_TX_PACKET_POOL_SIZE 5
+#endif
+
+#endif // OPENTHREAD_PLATFORM_POSIX_CONFIG_H_
diff --git a/src/core/config/dtls.h b/src/posix/platform/openthread-posix-daemon-config.h
similarity index 61%
copy from src/core/config/dtls.h
copy to src/posix/platform/openthread-posix-daemon-config.h
index 09e8337..d7e8b6c 100644
--- a/src/core/config/dtls.h
+++ b/src/posix/platform/openthread-posix-daemon-config.h
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2021, The OpenThread Authors.
+ *  Copyright (c) 2023, The OpenThread Authors.
  *  All rights reserved.
  *
  *  Redistribution and use in source and binary forms, with or without
@@ -26,40 +26,47 @@
  *  POSSIBILITY OF SUCH DAMAGE.
  */
 
+#ifndef OPENTHREAD_POSIX_DAEMON_CONFIG_H_
+#define OPENTHREAD_POSIX_DAEMON_CONFIG_H_
+
 /**
  * @file
- *   This file includes compile-time configurations for DTLS.
- *
+ * @brief
+ *   This file includes the POSIX daemon specific configurations.
  */
 
-#ifndef CONFIG_DTLS_H_
-#define CONFIG_DTLS_H_
-
-#include "config/border_agent.h"
-#include "config/coap.h"
-#include "config/commissioner.h"
-#include "config/joiner.h"
-
 /**
- * @def OPENTHREAD_CONFIG_DTLS_MAX_CONTENT_LEN
+ * @def OPENTHREAD_POSIX_CONFIG_DAEMON_SOCKET_BASENAME
  *
- * The max length of the OpenThread dtls content buffer.
+ * Define socket basename used by POSIX app daemon.
  *
  */
-#ifndef OPENTHREAD_CONFIG_DTLS_MAX_CONTENT_LEN
-#define OPENTHREAD_CONFIG_DTLS_MAX_CONTENT_LEN MBEDTLS_SSL_IN_CONTENT_LEN
+#ifndef OPENTHREAD_POSIX_CONFIG_DAEMON_SOCKET_BASENAME
+#ifdef __linux__
+#define OPENTHREAD_POSIX_CONFIG_DAEMON_SOCKET_BASENAME "/run/openthread-%s"
+#else
+#define OPENTHREAD_POSIX_CONFIG_DAEMON_SOCKET_BASENAME "/tmp/openthread-%s"
+#endif
 #endif
 
 /**
- * @def OPENTHREAD_CONFIG_DTLS_ENABLE
+ * @def OPENTHREAD_POSIX_CONFIG_DAEMON_ENABLE
  *
- *  Define to 1 to enable DTLS.
+ * Define to 1 to enable POSIX daemon.
  *
  */
-#ifndef OPENTHREAD_CONFIG_DTLS_ENABLE
-#define OPENTHREAD_CONFIG_DTLS_ENABLE                                                     \
-    (OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE || OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE || \
-     OPENTHREAD_CONFIG_COMMISSIONER_ENABLE || OPENTHREAD_CONFIG_JOINER_ENABLE)
+#ifndef OPENTHREAD_POSIX_CONFIG_DAEMON_ENABLE
+#define OPENTHREAD_POSIX_CONFIG_DAEMON_ENABLE 0
 #endif
 
-#endif // CONFIG_DTLS_H_
+/**
+ * @def OPENTHREAD_POSIX_CONFIG_DAEMON_CLI_ENABLE
+ *
+ * Define to 1 to enable CLI for the posix daemon.
+ *
+ */
+#ifndef OPENTHREAD_POSIX_CONFIG_DAEMON_CLI_ENABLE
+#define OPENTHREAD_POSIX_CONFIG_DAEMON_CLI_ENABLE 1
+#endif
+
+#endif // OPENTHREAD_POSIX_DAEMON_CONFIG_H_
diff --git a/src/posix/platform/platform-posix.h b/src/posix/platform/platform-posix.h
index 912ceb5..a5d3a30 100644
--- a/src/posix/platform/platform-posix.h
+++ b/src/posix/platform/platform-posix.h
@@ -32,8 +32,8 @@
  *   This file includes the platform-specific initializers.
  */
 
-#ifndef PLATFORM_POSIX_H_
-#define PLATFORM_POSIX_H_
+#ifndef OT_PLATFORM_POSIX_H_
+#define OT_PLATFORM_POSIX_H_
 
 #include "openthread-posix-config.h"
 
@@ -424,4 +424,4 @@
 #ifdef __cplusplus
 }
 #endif
-#endif // PLATFORM_POSIX_H_
+#endif // OT_PLATFORM_POSIX_H_
diff --git a/src/posix/platform/power.hpp b/src/posix/platform/power.hpp
index 4edbcb2..f4481f1 100644
--- a/src/posix/platform/power.hpp
+++ b/src/posix/platform/power.hpp
@@ -26,8 +26,8 @@
  *  POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef POSIX_PLATFORM_POWER_H
-#define POSIX_PLATFORM_POWER_H
+#ifndef OT_POSIX_PLATFORM_POWER_HPP_
+#define OT_POSIX_PLATFORM_POWER_HPP_
 
 #include <assert.h>
 #include <stdio.h>
@@ -286,4 +286,4 @@
 };
 } // namespace Power
 } // namespace ot
-#endif // POSIX_PLATFORM_POWER_H
+#endif // OT_POSIX_PLATFORM_POWER_HPP_
diff --git a/src/posix/platform/radio.cpp b/src/posix/platform/radio.cpp
index 7cf2737..f5eb17c 100644
--- a/src/posix/platform/radio.cpp
+++ b/src/posix/platform/radio.cpp
@@ -57,6 +57,8 @@
 extern "C" void platformRadioInit(const char *aUrl) { sRadio.Init(aUrl); }
 } // namespace
 
+const char Radio::kLogModuleName[] = "Radio";
+
 Radio::Radio(void)
     : mRadioUrl(nullptr)
     , mRadioSpinel()
@@ -98,7 +100,7 @@
 
     mRadioSpinel.SetCallbacks(callbacks);
     mRadioSpinel.Init(*mSpinelInterface, resetRadio, skipCompatibilityCheck, iidList, OT_ARRAY_LENGTH(iidList));
-    otLogDebgPlat("instance init:%p - iid = %d", (void *)&mRadioSpinel, iidList[0]);
+    LogDebg("instance init:%p - iid = %d", (void *)&mRadioSpinel, iidList[0]);
 
     ProcessRadioUrl(mRadioUrl);
 }
@@ -145,7 +147,7 @@
 #endif
     else
     {
-        otLogCritPlat("The Spinel interface name \"%s\" is not supported!", aInterfaceName);
+        LogCrit("The Spinel interface name \"%s\" is not supported!", aInterfaceName);
         DieNow(OT_ERROR_FAILED);
     }
 
@@ -159,7 +161,7 @@
 
     if (aRadioUrl.HasParam("ncp-dataset"))
     {
-        otLogCritPlat("The argument \"ncp-dataset\" is no longer supported");
+        LogCrit("The argument \"ncp-dataset\" is no longer supported");
         DieNow(OT_ERROR_FAILED);
     }
 
@@ -220,7 +222,7 @@
         VerifyOrDie((error == OT_ERROR_NONE) || (error == OT_ERROR_NOT_IMPLEMENTED), OT_EXIT_FAILURE);
         if (error == OT_ERROR_NOT_IMPLEMENTED)
         {
-            otLogWarnPlat("The RCP doesn't support setting the max transmit power");
+            LogWarn("The RCP doesn't support setting the max transmit power");
         }
 
         ++channel;
@@ -233,7 +235,7 @@
         VerifyOrDie((error == OT_ERROR_NONE) || (error == OT_ERROR_NOT_IMPLEMENTED), OT_ERROR_FAILED);
         if (error == OT_ERROR_NOT_IMPLEMENTED)
         {
-            otLogWarnPlat("The RCP doesn't support setting the max transmit power");
+            LogWarn("The RCP doesn't support setting the max transmit power");
         }
 
         ++channel;
@@ -296,7 +298,7 @@
 } // namespace Posix
 } // namespace ot
 
-static ot::Spinel::RadioSpinel &GetRadioSpinel(void) { return sRadio.GetRadioSpinel(); }
+ot::Spinel::RadioSpinel &GetRadioSpinel(void) { return sRadio.GetRadioSpinel(); }
 
 void platformRadioDeinit(void) { GetRadioSpinel().Deinit(); }
 
diff --git a/src/posix/platform/radio.hpp b/src/posix/platform/radio.hpp
index 439483f..e4a8b1a 100644
--- a/src/posix/platform/radio.hpp
+++ b/src/posix/platform/radio.hpp
@@ -26,15 +26,21 @@
  *  POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef POSIX_PLATFORM_RADIO_HPP_
-#define POSIX_PLATFORM_RADIO_HPP_
+#ifndef OT_POSIX_PLATFORM_RADIO_HPP_
+#define OT_POSIX_PLATFORM_RADIO_HPP_
 
+#include "hdlc_interface.hpp"
+#include "logger.hpp"
+#include "radio_url.hpp"
+#include "spi_interface.hpp"
+#include "vendor_interface.hpp"
 #include "common/code_utils.hpp"
 #include "lib/spinel/radio_spinel.hpp"
-#include "posix/platform/hdlc_interface.hpp"
-#include "posix/platform/radio_url.hpp"
-#include "posix/platform/spi_interface.hpp"
-#include "posix/platform/vendor_interface.hpp"
+#if OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_ENABLE
+#ifdef OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_HEADER
+#include OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_HEADER
+#endif
+#endif
 
 namespace ot {
 namespace Posix {
@@ -43,9 +49,11 @@
  * Manages Thread radio.
  *
  */
-class Radio
+class Radio : public Logger<Radio>
 {
 public:
+    static const char kLogModuleName[]; ///< Module name used for logging.
+
     /**
      * Creates the radio manager.
      *
@@ -104,8 +112,12 @@
 #error "No Spinel interface is specified!"
 #endif
 
-    RadioUrl                 mRadioUrl;
-    Spinel::RadioSpinel      mRadioSpinel;
+    RadioUrl mRadioUrl;
+#if OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_ENABLE
+    Spinel::VendorRadioSpinel mRadioSpinel;
+#else
+    Spinel::RadioSpinel     mRadioSpinel;
+#endif
     Spinel::SpinelInterface *mSpinelInterface;
 
     OT_DEFINE_ALIGNED_VAR(mSpinelInterfaceRaw, kSpinelInterfaceRawSize, uint64_t);
@@ -114,4 +126,4 @@
 } // namespace Posix
 } // namespace ot
 
-#endif // POSIX_PLATFORM_RADIO_HPP_
+#endif // OT_POSIX_PLATFORM_RADIO_HPP_
diff --git a/src/posix/platform/radio_url.hpp b/src/posix/platform/radio_url.hpp
index 0246070..0e088cb 100644
--- a/src/posix/platform/radio_url.hpp
+++ b/src/posix/platform/radio_url.hpp
@@ -26,8 +26,8 @@
  *  POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef POSIX_PLATFORM_RADIO_URL_HPP_
-#define POSIX_PLATFORM_RADIO_URL_HPP_
+#ifndef OT_POSIX_PLATFORM_RADIO_URL_HPP_
+#define OT_POSIX_PLATFORM_RADIO_URL_HPP_
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -78,4 +78,4 @@
 } // namespace Posix
 } // namespace ot
 
-#endif // POSIX_PLATFORM_RADIO_URL_HPP_
+#endif // OT_POSIX_PLATFORM_RADIO_URL_HPP_
diff --git a/src/posix/platform/resolver.cpp b/src/posix/platform/resolver.cpp
index c8d80e2..642d771 100644
--- a/src/posix/platform/resolver.cpp
+++ b/src/posix/platform/resolver.cpp
@@ -61,6 +61,8 @@
 namespace ot {
 namespace Posix {
 
+const char Resolver::kLogModuleName[] = "Resolver";
+
 void Resolver::Init(void)
 {
     memset(mUpstreamTransaction, 0, sizeof(mUpstreamTransaction));
@@ -95,8 +97,7 @@
 
             if (inet_pton(AF_INET, &line.c_str()[sizeof(kNameserverItem)], &addr) == 1)
             {
-                otLogInfoPlat("Got nameserver #%d: %s", mUpstreamDnsServerCount,
-                              &line.c_str()[sizeof(kNameserverItem)]);
+                LogInfo("Got nameserver #%d: %s", mUpstreamDnsServerCount, &line.c_str()[sizeof(kNameserverItem)]);
                 mUpstreamDnsServerList[mUpstreamDnsServerCount] = addr;
                 mUpstreamDnsServerCount++;
             }
@@ -105,7 +106,7 @@
 
     if (mUpstreamDnsServerCount == 0)
     {
-        otLogCritPlat("No domain name servers found in %s, default to 127.0.0.1", kResolvConfFullPath);
+        LogCrit("No domain name servers found in %s, default to 127.0.0.1", kResolvConfFullPath);
     }
 
     mUpstreamDnsServerListFreshness = otPlatTimeGet();
@@ -137,12 +138,12 @@
             sendto(txn->mUdpFd, packet, length, MSG_DONTWAIT, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) > 0,
             error = OT_ERROR_NO_ROUTE);
     }
-    otLogInfoPlat("Forwarded DNS query %p to %d server(s).", static_cast<void *>(aTxn), mUpstreamDnsServerCount);
+    LogInfo("Forwarded DNS query %p to %d server(s).", static_cast<void *>(aTxn), mUpstreamDnsServerCount);
 
 exit:
     if (error != OT_ERROR_NONE)
     {
-        otLogCritPlat("Failed to forward DNS query %p to server: %d", static_cast<void *>(aTxn), error);
+        LogCrit("Failed to forward DNS query %p to server: %d", static_cast<void *>(aTxn), error);
     }
     return;
 }
@@ -171,7 +172,7 @@
             fdOrError = socket(AF_INET, SOCK_DGRAM, 0);
             if (fdOrError < 0)
             {
-                otLogInfoPlat("Failed to create socket for upstream resolver: %d", fdOrError);
+                LogInfo("Failed to create socket for upstream resolver: %d", fdOrError);
                 break;
             }
             ret             = &txn;
@@ -203,11 +204,11 @@
 exit:
     if (readSize < 0)
     {
-        otLogInfoPlat("Failed to read response from upstream resolver socket: %d", errno);
+        LogInfo("Failed to read response from upstream resolver socket: %d", errno);
     }
     if (error != OT_ERROR_NONE)
     {
-        otLogInfoPlat("Failed to forward upstream DNS response: %s", otThreadErrorToString(error));
+        LogInfo("Failed to forward upstream DNS response: %s", otThreadErrorToString(error));
     }
     if (message != nullptr)
     {
diff --git a/src/posix/platform/resolver.hpp b/src/posix/platform/resolver.hpp
index 8446a47..fae0cf9 100644
--- a/src/posix/platform/resolver.hpp
+++ b/src/posix/platform/resolver.hpp
@@ -26,8 +26,8 @@
  *  POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef POSIX_PLATFORM_RESOLVER_HPP_
-#define POSIX_PLATFORM_RESOLVER_HPP_
+#ifndef OT_POSIX_PLATFORM_RESOLVER_HPP_
+#define OT_POSIX_PLATFORM_RESOLVER_HPP_
 
 #include <openthread/openthread-system.h>
 #include <openthread/platform/dns.h>
@@ -35,14 +35,18 @@
 #include <arpa/inet.h>
 #include <sys/select.h>
 
+#include "logger.hpp"
+
 #if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE
 
 namespace ot {
 namespace Posix {
 
-class Resolver
+class Resolver : public Logger<Resolver>
 {
 public:
+    static const char kLogModuleName[]; ///< Module name used for logging.
+
     constexpr static ssize_t kMaxDnsMessageSize           = 512;
     constexpr static ssize_t kMaxUpstreamTransactionCount = 16;
     constexpr static ssize_t kMaxUpstreamServerCount      = 3;
@@ -73,10 +77,7 @@
     /**
      * Updates the file descriptor sets with file descriptors used by the radio driver.
      *
-     * @param[in,out]  aReadFdSet   A reference to the read file descriptors.
-     * @param[in,out]  aErrorFdSet  A reference to the error file descriptors.
-     * @param[in,out]  aMaxFd       A reference to the max file descriptor.
-     * @param[in,out]  aTimeout     A reference to the timeout.
+     * @param[in,out]  aContext  The mainloop context.
      *
      */
     void UpdateFdSet(otSysMainloopContext &aContext);
@@ -84,8 +85,7 @@
     /**
      * Handles the result of select.
      *
-     * @param[in]  aReadFdSet   A reference to the read file descriptors.
-     * @param[in]  aErrorFdSet  A reference to the error file descriptors.
+     * @param[in]  aContext  The mainloop context.
      *
      */
     void Process(const otSysMainloopContext &aContext);
@@ -122,4 +122,4 @@
 
 #endif // OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE
 
-#endif // POSIX_PLATFORM_RESOLVER_HPP_
+#endif // OT_POSIX_PLATFORM_RESOLVER_HPP_
diff --git a/src/posix/platform/settings.hpp b/src/posix/platform/settings.hpp
index d2009aa..bf2cabd 100644
--- a/src/posix/platform/settings.hpp
+++ b/src/posix/platform/settings.hpp
@@ -26,8 +26,8 @@
  *  POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef POSIX_PLATFORM_SETTINGS_HPP_
-#define POSIX_PLATFORM_SETTINGS_HPP_
+#ifndef OT_POSIX_PLATFORM_SETTINGS_HPP_
+#define OT_POSIX_PLATFORM_SETTINGS_HPP_
 
 namespace ot {
 namespace Posix {
@@ -100,4 +100,4 @@
 } // namespace Posix
 } // namespace ot
 
-#endif // POSIX_PLATFORM_SETTINGS_HPP_
+#endif // OT_POSIX_PLATFORM_SETTINGS_HPP_
diff --git a/src/posix/platform/spi_interface.cpp b/src/posix/platform/spi_interface.cpp
index 167f4b9..9d7860b 100644
--- a/src/posix/platform/spi_interface.cpp
+++ b/src/posix/platform/spi_interface.cpp
@@ -62,6 +62,8 @@
 namespace ot {
 namespace Posix {
 
+const char SpiInterface::kLogModuleName[] = "SpiIntface";
+
 SpiInterface::SpiInterface(const Url::Url &aRadioUrl)
     : mReceiveFrameCallback(nullptr)
     , mReceiveFrameContext(nullptr)
@@ -153,7 +155,7 @@
     }
     else
     {
-        otLogNotePlat("SPI interface enters polling mode.");
+        LogNote("SPI interface enters polling mode.");
     }
 
     InitResetPin(spiGpioResetDevice, spiGpioResetLine);
@@ -254,7 +256,7 @@
     char label[] = "SOC_THREAD_RESET";
     int  fd;
 
-    otLogDebgPlat("InitResetPin: charDev=%s, line=%" PRIu8, aCharDev, aLine);
+    LogDebg("InitResetPin: charDev=%s, line=%" PRIu8, aCharDev, aLine);
 
     VerifyOrDie(aCharDev != nullptr, OT_EXIT_INVALID_ARGUMENTS);
     VerifyOrDie((fd = open(aCharDev, O_RDWR)) != -1, OT_EXIT_ERROR_ERRNO);
@@ -268,7 +270,7 @@
     char label[] = "THREAD_SOC_INT";
     int  fd;
 
-    otLogDebgPlat("InitIntPin: charDev=%s, line=%" PRIu8, aCharDev, aLine);
+    LogDebg("InitIntPin: charDev=%s, line=%" PRIu8, aCharDev, aLine);
 
     VerifyOrDie(aCharDev != nullptr, OT_EXIT_INVALID_ARGUMENTS);
     VerifyOrDie((fd = open(aCharDev, O_RDWR)) != -1, OT_EXIT_ERROR_ERRNO);
@@ -283,7 +285,7 @@
     const uint8_t wordBits = kSpiBitsPerWord;
     int           fd;
 
-    otLogDebgPlat("InitSpiDev: path=%s, mode=%" PRIu8 ", speed=%" PRIu32, aPath, aMode, aSpeed);
+    LogDebg("InitSpiDev: path=%s, mode=%" PRIu8 ", speed=%" PRIu32, aPath, aMode, aSpeed);
 
     VerifyOrDie((aPath != nullptr) && (aMode <= kSpiModeMax), OT_EXIT_INVALID_ARGUMENTS);
     VerifyOrDie((fd = open(aPath, O_RDWR | O_CLOEXEC)) != -1, OT_EXIT_ERROR_ERRNO);
@@ -314,7 +316,7 @@
     // Set Reset pin to high level.
     SetGpioValue(mResetGpioValueFd, 1);
 
-    otLogNotePlat("Triggered hardware reset");
+    LogNote("Triggered hardware reset");
 }
 
 uint8_t *SpiInterface::GetRealRxFrameStart(uint8_t *aSpiRxFrameBuffer, uint8_t aAlignAllowance, uint16_t &aSkipLength)
@@ -453,12 +455,12 @@
 
     if (error != OT_ERROR_NONE)
     {
-        otLogCritPlat("PushPullSpi:DoSpiTransfer: errno=%s", strerror(errno));
+        LogCrit("PushPullSpi:DoSpiTransfer: errno=%s", strerror(errno));
 
         // Print out a helpful error message for a common error.
         if ((mSpiCsDelayUs != 0) && (errno == EINVAL))
         {
-            otLogWarnPlat("SPI ioctl failed with EINVAL. Try adding `--spi-cs-delay=0` to command line arguments.");
+            LogWarn("SPI ioctl failed with EINVAL. Try adding `--spi-cs-delay=0` to command line arguments.");
         }
 
         LogStats();
@@ -471,10 +473,10 @@
     {
         Spinel::SpiFrame rxFrame(spiRxFrame);
 
-        otLogDebgPlat("spi_transfer TX: H:%02X ACCEPT:%" PRIu16 " DATA:%" PRIu16, txFrame.GetHeaderFlagByte(),
-                      txFrame.GetHeaderAcceptLen(), txFrame.GetHeaderDataLen());
-        otLogDebgPlat("spi_transfer RX: H:%02X ACCEPT:%" PRIu16 " DATA:%" PRIu16, rxFrame.GetHeaderFlagByte(),
-                      rxFrame.GetHeaderAcceptLen(), rxFrame.GetHeaderDataLen());
+        LogDebg("spi_transfer TX: H:%02X ACCEPT:%" PRIu16 " DATA:%" PRIu16, txFrame.GetHeaderFlagByte(),
+                txFrame.GetHeaderAcceptLen(), txFrame.GetHeaderDataLen());
+        LogDebg("spi_transfer RX: H:%02X ACCEPT:%" PRIu16 " DATA:%" PRIu16, rxFrame.GetHeaderFlagByte(),
+                rxFrame.GetHeaderAcceptLen(), rxFrame.GetHeaderDataLen());
 
         slaveHeader = rxFrame.GetHeaderFlagByte();
         if ((slaveHeader == 0xFF) || (slaveHeader == 0x00))
@@ -485,11 +487,11 @@
                 // Device is off or in a bad state. In some cases may be induced by flow control.
                 if (mSpiSlaveDataLen == 0)
                 {
-                    otLogDebgPlat("Slave did not respond to frame. (Header was all 0x%02X)", slaveHeader);
+                    LogDebg("Slave did not respond to frame. (Header was all 0x%02X)", slaveHeader);
                 }
                 else
                 {
-                    otLogWarnPlat("Slave did not respond to frame. (Header was all 0x%02X)", slaveHeader);
+                    LogWarn("Slave did not respond to frame. (Header was all 0x%02X)", slaveHeader);
                 }
 
                 mSpiUnresponsiveFrameCount++;
@@ -499,8 +501,8 @@
                 // Header is full of garbage
                 mInterfaceMetrics.mTransferredGarbageFrameCount++;
 
-                otLogWarnPlat("Garbage in header : %02X %02X %02X %02X %02X", spiRxFrame[0], spiRxFrame[1],
-                              spiRxFrame[2], spiRxFrame[3], spiRxFrame[4]);
+                LogWarn("Garbage in header : %02X %02X %02X %02X %02X", spiRxFrame[0], spiRxFrame[1], spiRxFrame[2],
+                        spiRxFrame[3], spiRxFrame[4]);
                 otDumpDebgPlat("SPI-TX", mSpiTxFrameBuffer, spiTransferBytes);
                 otDumpDebgPlat("SPI-RX", spiRxFrameBuffer, spiTransferBytes);
             }
@@ -518,8 +520,8 @@
             mSpiTxRefusedCount++;
             mSpiSlaveDataLen = 0;
 
-            otLogWarnPlat("Garbage in header : %02X %02X %02X %02X %02X", spiRxFrame[0], spiRxFrame[1], spiRxFrame[2],
-                          spiRxFrame[3], spiRxFrame[4]);
+            LogWarn("Garbage in header : %02X %02X %02X %02X %02X", spiRxFrame[0], spiRxFrame[1], spiRxFrame[2],
+                    spiRxFrame[3], spiRxFrame[4]);
             otDumpDebgPlat("SPI-TX", mSpiTxFrameBuffer, spiTransferBytes);
             otDumpDebgPlat("SPI-RX", spiRxFrameBuffer, spiTransferBytes);
 
@@ -532,7 +534,7 @@
         {
             mSlaveResetCount++;
 
-            otLogNotePlat("Slave did reset (%" PRIu64 " resets so far)", mSlaveResetCount);
+            LogNote("Slave did reset (%" PRIu64 " resets so far)", mSlaveResetCount);
             LogStats();
         }
 
@@ -634,7 +636,7 @@
             // Interrupt pin is asserted, set the timeout to be 0.
             timeout.tv_sec  = 0;
             timeout.tv_usec = 0;
-            otLogDebgPlat("UpdateFdSet(): Interrupt.");
+            LogDebg("UpdateFdSet(): Interrupt.");
         }
         else
         {
@@ -677,7 +679,7 @@
         {
             // To avoid printing out this message over and over, we only print it out once the refused count is at two
             // or higher when we actually have something to send the slave. And then, we only print it once.
-            otLogInfoPlat("Slave is rate limiting transactions");
+            LogInfo("Slave is rate limiting transactions");
 
             mDidPrintRateLimitLog = true;
         }
@@ -686,7 +688,7 @@
         {
             // Ua-oh. The slave hasn't given us a chance to send it anything for over thirty frames. If this ever
             // happens, print out a warning to the logs.
-            otLogWarnPlat("Slave seems stuck.");
+            LogWarn("Slave seems stuck.");
         }
         else if (mSpiTxRefusedCount == kSpiTxRefuseExitCount)
         {
@@ -716,7 +718,7 @@
     {
         struct gpioevent_data event;
 
-        otLogDebgPlat("Process(): Interrupt.");
+        LogDebg("Process(): Interrupt.");
 
         // Read event data to clear interrupt.
         VerifyOrDie(read(mIntGpioValueFd, &event, sizeof(event)) != -1, OT_EXIT_ERROR_ERRNO);
@@ -804,21 +806,21 @@
 void SpiInterface::LogError(const char *aString)
 {
     OT_UNUSED_VARIABLE(aString);
-    otLogWarnPlat("%s: %s", aString, strerror(errno));
+    LogWarn("%s: %s", aString, strerror(errno));
 }
 
 void SpiInterface::LogStats(void)
 {
-    otLogInfoPlat("INFO: SlaveResetCount=%" PRIu64, mSlaveResetCount);
-    otLogInfoPlat("INFO: SpiDuplexFrameCount=%" PRIu64, mSpiDuplexFrameCount);
-    otLogInfoPlat("INFO: SpiUnresponsiveFrameCount=%" PRIu64, mSpiUnresponsiveFrameCount);
-    otLogInfoPlat("INFO: TransferredFrameCount=%" PRIu64, mInterfaceMetrics.mTransferredFrameCount);
-    otLogInfoPlat("INFO: TransferredValidFrameCount=%" PRIu64, mInterfaceMetrics.mTransferredValidFrameCount);
-    otLogInfoPlat("INFO: TransferredGarbageFrameCount=%" PRIu64, mInterfaceMetrics.mTransferredGarbageFrameCount);
-    otLogInfoPlat("INFO: RxFrameCount=%" PRIu64, mInterfaceMetrics.mRxFrameCount);
-    otLogInfoPlat("INFO: RxFrameByteCount=%" PRIu64, mInterfaceMetrics.mRxFrameByteCount);
-    otLogInfoPlat("INFO: TxFrameCount=%" PRIu64, mInterfaceMetrics.mTxFrameCount);
-    otLogInfoPlat("INFO: TxFrameByteCount=%" PRIu64, mInterfaceMetrics.mTxFrameByteCount);
+    LogInfo("INFO: SlaveResetCount=%" PRIu64, mSlaveResetCount);
+    LogInfo("INFO: SpiDuplexFrameCount=%" PRIu64, mSpiDuplexFrameCount);
+    LogInfo("INFO: SpiUnresponsiveFrameCount=%" PRIu64, mSpiUnresponsiveFrameCount);
+    LogInfo("INFO: TransferredFrameCount=%" PRIu64, mInterfaceMetrics.mTransferredFrameCount);
+    LogInfo("INFO: TransferredValidFrameCount=%" PRIu64, mInterfaceMetrics.mTransferredValidFrameCount);
+    LogInfo("INFO: TransferredGarbageFrameCount=%" PRIu64, mInterfaceMetrics.mTransferredGarbageFrameCount);
+    LogInfo("INFO: RxFrameCount=%" PRIu64, mInterfaceMetrics.mRxFrameCount);
+    LogInfo("INFO: RxFrameByteCount=%" PRIu64, mInterfaceMetrics.mRxFrameByteCount);
+    LogInfo("INFO: TxFrameCount=%" PRIu64, mInterfaceMetrics.mTxFrameCount);
+    LogInfo("INFO: TxFrameByteCount=%" PRIu64, mInterfaceMetrics.mTxFrameByteCount);
 }
 } // namespace Posix
 } // namespace ot
diff --git a/src/posix/platform/spi_interface.hpp b/src/posix/platform/spi_interface.hpp
index 095214a..94b5507 100644
--- a/src/posix/platform/spi_interface.hpp
+++ b/src/posix/platform/spi_interface.hpp
@@ -31,11 +31,12 @@
  *   This file includes definitions for the SPI interface to radio (RCP).
  */
 
-#ifndef POSIX_PLATFORM_SPI_INTERFACE_HPP_
-#define POSIX_PLATFORM_SPI_INTERFACE_HPP_
+#ifndef OT_POSIX_PLATFORM_SPI_INTERFACE_HPP_
+#define OT_POSIX_PLATFORM_SPI_INTERFACE_HPP_
 
 #include "openthread-posix-config.h"
 
+#include "logger.hpp"
 #include "platform-posix.h"
 #include "lib/hdlc/hdlc.hpp"
 #include "lib/spinel/multi_frame_buffer.hpp"
@@ -51,9 +52,11 @@
  * Defines an SPI interface to the Radio Co-processor (RCP).
  *
  */
-class SpiInterface : public ot::Spinel::SpinelInterface
+class SpiInterface : public ot::Spinel::SpinelInterface, public Logger<SpiInterface>
 {
 public:
+    static const char kLogModuleName[]; ///< Module name used for logging.
+
     /**
      * Initializes the object.
      *
@@ -258,4 +261,4 @@
 } // namespace Posix
 } // namespace ot
 
-#endif // POSIX_PLATFORM_SPI_INTERFACE_HPP_
+#endif // OT_POSIX_PLATFORM_SPI_INTERFACE_HPP_
diff --git a/src/posix/platform/system.cpp b/src/posix/platform/system.cpp
index 9b0aa9c..4a86fd6 100644
--- a/src/posix/platform/system.cpp
+++ b/src/posix/platform/system.cpp
@@ -40,6 +40,7 @@
 
 #include <openthread-core-config.h>
 #include <openthread/border_router.h>
+#include <openthread/cli.h>
 #include <openthread/heap.h>
 #include <openthread/tasklet.h>
 #include <openthread/platform/alarm-milli.h>
@@ -406,3 +407,15 @@
 }
 
 bool IsSystemDryRun(void) { return gDryRun; }
+
+#if OPENTHREAD_POSIX_CONFIG_DAEMON_ENABLE && OPENTHREAD_POSIX_CONFIG_DAEMON_CLI_ENABLE
+void otSysCliInitUsingDaemon(otInstance *aInstance)
+{
+    otCliInit(
+        aInstance,
+        [](void *aContext, const char *aFormat, va_list aArguments) -> int {
+            return static_cast<ot::Posix::Daemon *>(aContext)->OutputFormatV(aFormat, aArguments);
+        },
+        &ot::Posix::Daemon::Get());
+}
+#endif
diff --git a/src/posix/platform/trel.cpp b/src/posix/platform/trel.cpp
index a925cb8..6b6e6c0 100644
--- a/src/posix/platform/trel.cpp
+++ b/src/posix/platform/trel.cpp
@@ -45,34 +45,82 @@
 #include <openthread/logging.h>
 #include <openthread/platform/trel.h>
 
+#include "logger.hpp"
 #include "radio_url.hpp"
 #include "system.hpp"
 #include "common/code_utils.hpp"
 
 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
 
-#define TREL_MAX_PACKET_SIZE 1400
-#define TREL_PACKET_POOL_SIZE 5
+static constexpr uint16_t kMaxPacketSize = 1400; // The max size of a TREL packet.
 
 typedef struct TxPacket
 {
     struct TxPacket *mNext;
-    uint8_t          mBuffer[TREL_MAX_PACKET_SIZE];
+    uint8_t          mBuffer[kMaxPacketSize];
     uint16_t         mLength;
     otSockAddr       mDestSockAddr;
 } TxPacket;
 
-static uint8_t   sRxPacketBuffer[TREL_MAX_PACKET_SIZE];
-static uint16_t  sRxPacketLength;
-static TxPacket  sTxPacketPool[TREL_PACKET_POOL_SIZE];
-static TxPacket *sFreeTxPacketHead;  // A singly linked list of free/available `TxPacket` from pool.
-static TxPacket *sTxPacketQueueTail; // A circular linked list for queued tx packets.
+static uint8_t            sRxPacketBuffer[kMaxPacketSize];
+static uint16_t           sRxPacketLength;
+static TxPacket           sTxPacketPool[OPENTHREAD_POSIX_CONFIG_TREL_TX_PACKET_POOL_SIZE];
+static TxPacket          *sFreeTxPacketHead;  // A singly linked list of free/available `TxPacket` from pool.
+static TxPacket          *sTxPacketQueueTail; // A circular linked list for queued tx packets.
+static otPlatTrelCounters sCounters;
 
 static char sInterfaceName[IFNAMSIZ + 1];
 static bool sInitialized = false;
 static bool sEnabled     = false;
 static int  sSocket      = -1;
 
+static const char kLogModuleName[] = "Trel";
+
+static void LogCrit(const char *aFormat, ...)
+{
+    va_list args;
+
+    va_start(args, aFormat);
+    otLogPlatArgs(OT_LOG_LEVEL_CRIT, kLogModuleName, aFormat, args);
+    va_end(args);
+}
+
+static void LogWarn(const char *aFormat, ...)
+{
+    va_list args;
+
+    va_start(args, aFormat);
+    otLogPlatArgs(OT_LOG_LEVEL_WARN, kLogModuleName, aFormat, args);
+    va_end(args);
+}
+
+static void LogNote(const char *aFormat, ...)
+{
+    va_list args;
+
+    va_start(args, aFormat);
+    otLogPlatArgs(OT_LOG_LEVEL_NOTE, kLogModuleName, aFormat, args);
+    va_end(args);
+}
+
+static void LogInfo(const char *aFormat, ...)
+{
+    va_list args;
+
+    va_start(args, aFormat);
+    otLogPlatArgs(OT_LOG_LEVEL_INFO, kLogModuleName, aFormat, args);
+    va_end(args);
+}
+
+static void LogDebg(const char *aFormat, ...)
+{
+    va_list args;
+
+    va_start(args, aFormat);
+    otLogPlatArgs(OT_LOG_LEVEL_DEBG, kLogModuleName, aFormat, args);
+    va_end(args);
+}
+
 static const char *Ip6AddrToString(const void *aAddress)
 {
     static char string[INET6_ADDRSTRLEN];
@@ -121,7 +169,7 @@
     struct sockaddr_in6 sockAddr;
     socklen_t           sockLen;
 
-    otLogDebgPlat("[trel] PrepareSocket()");
+    LogDebg("PrepareSocket()");
 
     sSocket = SocketWithCloseExec(AF_INET6, SOCK_DGRAM, 0, kSocketNonBlock);
     VerifyOrDie(sSocket >= 0, OT_EXIT_ERROR_ERRNO);
@@ -141,7 +189,7 @@
 
     if (bind(sSocket, (struct sockaddr *)&sockAddr, sizeof(sockAddr)) == -1)
     {
-        otLogCritPlat("[trel] Failed to bind socket");
+        LogCrit("Failed to bind socket");
         DieNow(OT_EXIT_ERROR_ERRNO);
     }
 
@@ -149,7 +197,7 @@
 
     if (getsockname(sSocket, (struct sockaddr *)&sockAddr, &sockLen) == -1)
     {
-        otLogCritPlat("[trel] Failed to get the socket name");
+        LogCrit("Failed to get the socket name");
         DieNow(OT_EXIT_ERROR_ERRNO);
     }
 
@@ -173,7 +221,7 @@
 
     if (ret != aLength)
     {
-        otLogDebgPlat("[trel] SendPacket() -- sendto() failed errno %d", errno);
+        LogDebg("SendPacket() -- sendto() failed errno %d", errno);
 
         switch (errno)
         {
@@ -187,11 +235,19 @@
             error = OT_ERROR_INVALID_STATE;
         }
     }
+    else
+    {
+        ++sCounters.mTxPackets;
+        sCounters.mTxBytes += aLength;
+    }
 
 exit:
-    otLogDebgPlat("[trel] SendPacket([%s]:%u) err:%s pkt:%s", Ip6AddrToString(&aDestSockAddr->mAddress),
-                  aDestSockAddr->mPort, otThreadErrorToString(error), BufferToString(aBuffer, aLength));
-
+    LogDebg("SendPacket([%s]:%u) err:%s pkt:%s", Ip6AddrToString(&aDestSockAddr->mAddress), aDestSockAddr->mPort,
+            otThreadErrorToString(error), BufferToString(aBuffer, aLength));
+    if (error != OT_ERROR_NONE)
+    {
+        ++sCounters.mTxFailure;
+    }
     return error;
 }
 
@@ -214,11 +270,13 @@
         sRxPacketLength = sizeof(sRxPacketLength);
     }
 
-    otLogDebgPlat("[trel] ReceivePacket() - received from [%s]:%d, id:%d, pkt:%s", Ip6AddrToString(&sockAddr.sin6_addr),
-                  ntohs(sockAddr.sin6_port), sockAddr.sin6_scope_id, BufferToString(sRxPacketBuffer, sRxPacketLength));
+    LogDebg("ReceivePacket() - received from [%s]:%d, id:%d, pkt:%s", Ip6AddrToString(&sockAddr.sin6_addr),
+            ntohs(sockAddr.sin6_port), sockAddr.sin6_scope_id, BufferToString(sRxPacketBuffer, sRxPacketLength));
 
     if (sEnabled)
     {
+        ++sCounters.mRxPackets;
+        sCounters.mRxBytes += sRxPacketLength;
         otPlatTrelHandleReceived(aInstance, sRxPacketBuffer, sRxPacketLength);
     }
 }
@@ -247,7 +305,7 @@
 
         if (SendPacket(packet->mBuffer, packet->mLength, &packet->mDestSockAddr) == OT_ERROR_INVALID_STATE)
         {
-            otLogDebgPlat("[trel] SendQueuedPackets() - SendPacket() would block");
+            LogDebg("SendQueuedPackets() - SendPacket() would block");
             break;
         }
 
@@ -277,7 +335,7 @@
     // Allocate an available packet entry (from the free packet list)
     // and copy the packet content into it.
 
-    VerifyOrExit(sFreeTxPacketHead != NULL, otLogWarnPlat("[trel] EnqueuePacket failed, queue is full"));
+    VerifyOrExit(sFreeTxPacketHead != NULL, LogWarn("EnqueuePacket failed, queue is full"));
     packet            = sFreeTxPacketHead;
     sFreeTxPacketHead = sFreeTxPacketHead->mNext;
 
@@ -299,13 +357,15 @@
         sTxPacketQueueTail        = packet;
     }
 
-    otLogDebgPlat("[trel] EnqueuePacket([%s]:%u) - %s", Ip6AddrToString(&aDestSockAddr->mAddress), aDestSockAddr->mPort,
-                  BufferToString(aBuffer, aLength));
+    LogDebg("EnqueuePacket([%s]:%u) - %s", Ip6AddrToString(&aDestSockAddr->mAddress), aDestSockAddr->mPort,
+            BufferToString(aBuffer, aLength));
 
 exit:
     return;
 }
 
+static void ResetCounters() { memset(&sCounters, 0, sizeof(sCounters)); }
+
 //---------------------------------------------------------------------------------------------------------------------
 // trelDnssd
 //
@@ -456,7 +516,7 @@
 
     VerifyOrExit(sEnabled);
 
-    assert(aUdpPayloadLen <= TREL_MAX_PACKET_SIZE);
+    assert(aUdpPayloadLen <= kMaxPacketSize);
 
     // We try to send the packet immediately. If it fails (e.g.,
     // network is down) `SendPacket()` returns `OT_ERROR_ABORT`. If
@@ -486,12 +546,34 @@
     return;
 }
 
+// We keep counters at the platform layer because TREL failures can only be captured accurately within
+// the platform layer as the platform sometimes only queues the packet and the packet will be sent later
+// and the error is only known after sent.
+const otPlatTrelCounters *otPlatTrelGetCounters(otInstance *aInstance)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+    return &sCounters;
+}
+
+void otPlatTrelResetCounters(otInstance *aInstance)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+    ResetCounters();
+}
+
 //---------------------------------------------------------------------------------------------------------------------
 // platformTrel system
 
 void platformTrelInit(const char *aTrelUrl)
 {
-    otLogDebgPlat("[trel] platformTrelInit(aTrelUrl:\"%s\")", aTrelUrl != nullptr ? aTrelUrl : "");
+    // To silence "unused function" warning.
+    (void)LogCrit;
+    (void)LogWarn;
+    (void)LogInfo;
+    (void)LogNote;
+    (void)LogDebg;
+
+    LogDebg("platformTrelInit(aTrelUrl:\"%s\")", aTrelUrl != nullptr ? aTrelUrl : "");
 
     assert(!sInitialized);
 
@@ -507,6 +589,8 @@
 
     InitPacketQueue();
     sInitialized = true;
+
+    ResetCounters();
 }
 
 void platformTrelDeinit(void)
@@ -516,7 +600,7 @@
     otPlatTrelDisable(nullptr);
     sInterfaceName[0] = '\0';
     sInitialized      = false;
-    otLogDebgPlat("[trel] platformTrelDeinit()");
+    LogDebg("platformTrelDeinit()");
 
 exit:
     return;
diff --git a/src/posix/platform/udp.cpp b/src/posix/platform/udp.cpp
index 6d7c880..89c6d3d 100644
--- a/src/posix/platform/udp.cpp
+++ b/src/posix/platform/udp.cpp
@@ -69,10 +69,6 @@
 
 int FdFromHandle(void *aHandle) { return static_cast<int>(reinterpret_cast<long>(aHandle)); }
 
-bool IsLinkLocal(const struct in6_addr &aAddress) { return aAddress.s6_addr[0] == 0xfe && aAddress.s6_addr[1] == 0x80; }
-
-bool IsMulticast(const otIp6Address &aAddress) { return aAddress.mFields.m8[0] == 0xff; }
-
 otError transmitPacket(int aFd, uint8_t *aPayload, uint16_t aLength, const otMessageInfo &aMessageInfo)
 {
 #ifdef __APPLE__
@@ -93,9 +89,9 @@
     memset(&peerAddr, 0, sizeof(peerAddr));
     peerAddr.sin6_port   = htons(aMessageInfo.mPeerPort);
     peerAddr.sin6_family = AF_INET6;
-    memcpy(&peerAddr.sin6_addr, &aMessageInfo.mPeerAddr, sizeof(peerAddr.sin6_addr));
+    CopyIp6AddressTo(aMessageInfo.mPeerAddr, &peerAddr.sin6_addr);
 
-    if (IsLinkLocal(peerAddr.sin6_addr) && !aMessageInfo.mIsHostInterface)
+    if (IsIp6AddressLinkLocal(aMessageInfo.mPeerAddr) && !aMessageInfo.mIsHostInterface)
     {
         // sin6_scope_id only works for link local destinations
         peerAddr.sin6_scope_id = gNetifIndex;
@@ -127,8 +123,7 @@
         controlLength += CMSG_SPACE(sizeof(int));
     }
 
-    if (!IsMulticast(aMessageInfo.mSockAddr) &&
-        memcmp(&aMessageInfo.mSockAddr, &in6addr_any, sizeof(aMessageInfo.mSockAddr)))
+    if (!IsIp6AddressMulticast(aMessageInfo.mSockAddr) && !IsIp6AddressUnspecified(aMessageInfo.mSockAddr))
     {
         struct in6_pktinfo pktinfo;
 
@@ -139,7 +134,7 @@
 
         pktinfo.ipi6_ifindex = aMessageInfo.mIsHostInterface ? 0 : gNetifIndex;
 
-        memcpy(&pktinfo.ipi6_addr, &aMessageInfo.mSockAddr, sizeof(pktinfo.ipi6_addr));
+        CopyIp6AddressTo(aMessageInfo.mSockAddr, &pktinfo.ipi6_addr);
         memcpy(CMSG_DATA(cmsg), &pktinfo, sizeof(pktinfo));
 
         controlLength += CMSG_SPACE(sizeof(pktinfo));
@@ -206,13 +201,13 @@
                 memcpy(&pktinfo, CMSG_DATA(cmsg), sizeof(pktinfo));
 
                 aMessageInfo.mIsHostInterface = (pktinfo.ipi6_ifindex != gNetifIndex);
-                memcpy(&aMessageInfo.mSockAddr, &pktinfo.ipi6_addr, sizeof(aMessageInfo.mSockAddr));
+                ReadIp6AddressFrom(&pktinfo.ipi6_addr, aMessageInfo.mSockAddr);
             }
         }
     }
 
     aMessageInfo.mPeerPort = ntohs(peerAddr.sin6_port);
-    memcpy(&aMessageInfo.mPeerAddr, &peerAddr.sin6_addr, sizeof(aMessageInfo.mPeerAddr));
+    ReadIp6AddressFrom(&peerAddr.sin6_addr, aMessageInfo.mPeerAddr);
 
 exit:
     return rval > 0 ? OT_ERROR_NONE : OT_ERROR_FAILED;
@@ -270,7 +265,8 @@
         memset(&sin6, 0, sizeof(struct sockaddr_in6));
         sin6.sin6_port   = htons(aUdpSocket->mSockName.mPort);
         sin6.sin6_family = AF_INET6;
-        memcpy(&sin6.sin6_addr, &aUdpSocket->mSockName.mAddress, sizeof(sin6.sin6_addr));
+        CopyIp6AddressTo(aUdpSocket->mSockName.mAddress, &sin6.sin6_addr);
+
         VerifyOrExit(0 == bind(fd, reinterpret_cast<struct sockaddr *>(&sin6), sizeof(sin6)), error = OT_ERROR_FAILED);
     }
 
@@ -283,7 +279,7 @@
 exit:
     if (error == OT_ERROR_FAILED)
     {
-        otLogCritPlat("Failed to bind UDP socket: %s", strerror(errno));
+        ot::Posix::Udp::LogCrit("Failed to bind UDP socket: %s", strerror(errno));
     }
 
     return error;
@@ -323,7 +319,12 @@
     case OT_NETIF_BACKBONE:
     {
 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
-#if __linux__
+        if (otSysGetInfraNetifName() == nullptr || otSysGetInfraNetifName()[0] == '\0')
+        {
+            ot::Posix::Udp::LogWarn("No backbone interface given, %s fails.", __func__);
+            ExitNow(error = OT_ERROR_INVALID_ARGS);
+        }
+#ifdef __linux__
         VerifyOrExit(setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, otSysGetInfraNetifName(),
                                 strlen(otSysGetInfraNetifName())) == 0,
                      error = OT_ERROR_FAILED);
@@ -353,8 +354,7 @@
     otError             error = OT_ERROR_NONE;
     struct sockaddr_in6 sin6;
     int                 fd;
-    bool isDisconnect = memcmp(&aUdpSocket->mPeerName.mAddress, &in6addr_any, sizeof(in6addr_any)) == 0 &&
-                        aUdpSocket->mPeerName.mPort == 0;
+    bool isDisconnect = IsIp6AddressUnspecified(aUdpSocket->mPeerName.mAddress) && (aUdpSocket->mPeerName.mPort == 0);
 
     VerifyOrExit(aUdpSocket->mHandle != nullptr, error = OT_ERROR_INVALID_ARGS);
 
@@ -362,10 +362,11 @@
 
     memset(&sin6, 0, sizeof(struct sockaddr_in6));
     sin6.sin6_port = htons(aUdpSocket->mPeerName.mPort);
+
     if (!isDisconnect)
     {
         sin6.sin6_family = AF_INET6;
-        memcpy(&sin6.sin6_addr, &aUdpSocket->mPeerName.mAddress, sizeof(sin6.sin6_addr));
+        CopyIp6AddressTo(aUdpSocket->mPeerName.mAddress, &sin6.sin6_addr);
     }
     else
     {
@@ -377,7 +378,7 @@
 
         if (getsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &netifName, &len) != 0)
         {
-                      otLogWarnPlat("Failed to read socket bound device: %s", strerror(errno));
+                      ot::Posix::Udp::LogWarn("Failed to read socket bound device: %s", strerror(errno));
                       len = 0;
         }
 
@@ -391,7 +392,7 @@
         {
                       fd = FdFromHandle(aUdpSocket->mHandle);
                       VerifyOrExit(setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &netifName, len) == 0, {
-                          otLogWarnPlat("Failed to bind to device: %s", strerror(errno));
+                          ot::Posix::Udp::LogWarn("Failed to bind to device: %s", strerror(errno));
                           error = OT_ERROR_FAILED;
                       });
         }
@@ -405,8 +406,9 @@
 #ifdef __APPLE__
         VerifyOrExit(errno == EAFNOSUPPORT && isDisconnect);
 #endif
-        otLogWarnPlat("Failed to connect to [%s]:%u: %s", Ip6AddressString(&aUdpSocket->mPeerName.mAddress).AsCString(),
-                      aUdpSocket->mPeerName.mPort, strerror(errno));
+        ot::Posix::Udp::LogWarn("Failed to connect to [%s]:%u: %s",
+                                Ip6AddressString(&aUdpSocket->mPeerName.mAddress).AsCString(),
+                                aUdpSocket->mPeerName.mPort, strerror(errno));
         error = OT_ERROR_FAILED;
     }
 
@@ -463,7 +465,7 @@
     VerifyOrExit(aUdpSocket->mHandle != nullptr, error = OT_ERROR_INVALID_ARGS);
     fd = FdFromHandle(aUdpSocket->mHandle);
 
-    memcpy(&mreq.ipv6mr_multiaddr, aAddress->mFields.m8, sizeof(mreq.ipv6mr_multiaddr));
+    CopyIp6AddressTo(*aAddress, &mreq.ipv6mr_multiaddr);
 
     switch (aNetifIdentifier)
     {
@@ -487,8 +489,9 @@
 exit:
     if (error != OT_ERROR_NONE)
     {
-        otLogCritPlat("IPV6_JOIN_GROUP failed: %s", strerror(errno));
+        ot::Posix::Udp::LogCrit("IPV6_JOIN_GROUP failed: %s", strerror(errno));
     }
+
     return error;
 }
 
@@ -503,7 +506,7 @@
     VerifyOrExit(aUdpSocket->mHandle != nullptr, error = OT_ERROR_INVALID_ARGS);
     fd = FdFromHandle(aUdpSocket->mHandle);
 
-    memcpy(&mreq.ipv6mr_multiaddr, aAddress->mFields.m8, sizeof(mreq.ipv6mr_multiaddr));
+    CopyIp6AddressTo(*aAddress, &mreq.ipv6mr_multiaddr);
 
     switch (aNetifIdentifier)
     {
@@ -527,14 +530,17 @@
 exit:
     if (error != OT_ERROR_NONE)
     {
-        otLogCritPlat("IPV6_LEAVE_GROUP failed: %s", strerror(errno));
+        ot::Posix::Udp::LogCrit("IPV6_LEAVE_GROUP failed: %s", strerror(errno));
     }
+
     return error;
 }
 
 namespace ot {
 namespace Posix {
 
+const char Udp::kLogModuleName[] = "Udp";
+
 void Udp::Update(otSysMainloopContext &aContext)
 {
     VerifyOrExit(gNetifIndex != 0);
diff --git a/src/posix/platform/udp.hpp b/src/posix/platform/udp.hpp
index adc252a..f22a8cf 100644
--- a/src/posix/platform/udp.hpp
+++ b/src/posix/platform/udp.hpp
@@ -29,14 +29,18 @@
 #define OT_POSIX_PLATFORM_UDP_HPP_
 
 #include "core/common/non_copyable.hpp"
-#include "posix/platform/mainloop.hpp"
+
+#include "logger.hpp"
+#include "mainloop.hpp"
 
 namespace ot {
 namespace Posix {
 
-class Udp : public Mainloop::Source, private NonCopyable
+class Udp : public Mainloop::Source, public Logger<Udp>, private NonCopyable
 {
 public:
+    static const char kLogModuleName[];
+
     static Udp &Get(void);
 
     void Init(const char *aIfName);
diff --git a/src/posix/platform/vendor_interface.hpp b/src/posix/platform/vendor_interface.hpp
index 4ee7a1a..449ebe5 100644
--- a/src/posix/platform/vendor_interface.hpp
+++ b/src/posix/platform/vendor_interface.hpp
@@ -31,8 +31,8 @@
  *   This file includes definitions for the vendor interface to radio (RCP).
  */
 
-#ifndef POSIX_APP_VENDOR_INTERFACE_HPP_
-#define POSIX_APP_VENDOR_INTERFACE_HPP_
+#ifndef OT_POSIX_APP_VENDOR_INTERFACE_HPP_
+#define OT_POSIX_APP_VENDOR_INTERFACE_HPP_
 
 #include "openthread-posix-config.h"
 
@@ -171,4 +171,4 @@
 } // namespace Posix
 } // namespace ot
 
-#endif // POSIX_APP_VENDOR_INTERFACE_HPP_
+#endif // OT_POSIX_APP_VENDOR_INTERFACE_HPP_
diff --git a/tests/fuzz/CMakeLists.txt b/tests/fuzz/CMakeLists.txt
index 39ca67b..c2d5da2 100644
--- a/tests/fuzz/CMakeLists.txt
+++ b/tests/fuzz/CMakeLists.txt
@@ -33,6 +33,8 @@
 
 set(COMMON_COMPILE_OPTIONS
     -DOPENTHREAD_FTD=1
+    -DOPENTHREAD_MTD=0
+    -DOPENTHREAD_RADIO=0
     -DOPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE=1
 )
 
diff --git a/tests/fuzz/oss-fuzz-build b/tests/fuzz/oss-fuzz-build
index c8bb407..e83c5c8 100755
--- a/tests/fuzz/oss-fuzz-build
+++ b/tests/fuzz/oss-fuzz-build
@@ -68,6 +68,7 @@
         -DOT_SERVICE=ON \
         -DOT_SLAAC=ON \
         -DOT_SNTP_CLIENT=ON \
+        -DOT_SRN_ADV_PROXY=ON \
         -DOT_SRP_CLIENT=ON \
         -DOT_SRP_SERVER=ON \
         -DOT_THREAD_VERSION=1.3 \
diff --git a/tests/scripts/expect/cli-debug.exp b/tests/scripts/expect/cli-debug.exp
new file mode 100644
index 0000000..6c5ced7
--- /dev/null
+++ b/tests/scripts/expect/cli-debug.exp
@@ -0,0 +1,35 @@
+#!/usr/bin/expect -f
+#
+#  Copyright (c) 2023, The OpenThread Authors.
+#  All rights reserved.
+#
+#  Redistribution and use in source and binary forms, with or without
+#  modification, are permitted provided that the following conditions are met:
+#  1. Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#  2. Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#  3. Neither the name of the copyright holder nor the
+#     names of its contributors may be used to endorse or promote products
+#     derived from this software without specific prior written permission.
+#
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+#  POSSIBILITY OF SUCH DAMAGE.
+#
+
+source "tests/scripts/expect/_common.exp"
+
+spawn_node 1
+
+send "debug\n"
+expect_line "Done"
diff --git a/tests/scripts/expect/cli-tcat.exp b/tests/scripts/expect/cli-tcat.exp
new file mode 100755
index 0000000..00ce087
--- /dev/null
+++ b/tests/scripts/expect/cli-tcat.exp
@@ -0,0 +1,61 @@
+#!/usr/bin/expect -f
+#
+#  Copyright (c) 2022, The OpenThread Authors.
+#  All rights reserved.
+#
+#  Redistribution and use in source and binary forms, with or without
+#  modification, are permitted provided that the following conditions are met:
+#  1. Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#  2. Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#  3. Neither the name of the copyright holder nor the
+#     names of its contributors may be used to endorse or promote products
+#     derived from this software without specific prior written permission.
+#
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+#  POSSIBILITY OF SUCH DAMAGE.
+#
+
+source "tests/scripts/expect/_common.exp"
+
+spawn_node 1 "cli"
+
+switch_node 1
+send "tcat start\n"
+expect_line "Done"
+
+spawn python "tools/tcat_ble_client/bbtc.py" --simulation 1 --cert_path "tools/tcat_ble_client/auth"
+set py_client "$spawn_id"
+expect_line "Done"
+send "commission\n"
+expect_line "\tTYPE:\tRESPONSE_W_STATUS"
+expect_line "\tVALUE:\t0x00"
+
+send "thread start\n"
+expect_line "\tTYPE:\tRESPONSE_W_STATUS"
+expect_line "\tVALUE:\t0x00"
+
+send "exit\n"
+expect eof
+
+switch_node 1
+send "tcat stop\n"
+expect_line "Done"
+
+send "networkkey\n"
+expect_line "fda7c771a27202e232ecd04cf934f476"
+expect_line "Done"
+
+wait_for "state" "leader"
+expect_line "Done"
diff --git a/tests/scripts/expect/posix-rcp-local-host.exp b/tests/scripts/expect/posix-rcp-local-host.exp
new file mode 100755
index 0000000..e8c8201
--- /dev/null
+++ b/tests/scripts/expect/posix-rcp-local-host.exp
@@ -0,0 +1,44 @@
+#!/usr/bin/expect -f
+#
+#  Copyright (c) 2024, The OpenThread Authors.
+#  All rights reserved.
+#
+#  Redistribution and use in source and binary forms, with or without
+#  modification, are permitted provided that the following conditions are met:
+#  1. Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#  2. Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#  3. Neither the name of the copyright holder nor the
+#     names of its contributors may be used to endorse or promote products
+#     derived from this software without specific prior written permission.
+#
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+#  POSSIBILITY OF SUCH DAMAGE.
+#
+
+source "tests/scripts/expect/_common.exp"
+
+spawn_node 1 rcp "spinel+hdlc+uart://$::env(OT_SIMULATION_APPS)/ncp/ot-rcp?forkpty-arg=-Llo&forkpty-arg=1"
+send "factoryreset\n"
+wait_for "state" "disabled"
+setup_default_network
+attach
+
+spawn_node 2 rcp "spinel+hdlc+uart://$::env(OT_SIMULATION_APPS)/ncp/ot-rcp?forkpty-arg=--local-host=127.0.0.1&forkpty-arg=2"
+send "factoryreset\n"
+wait_for "state" "disabled"
+setup_default_network
+attach child
+
+dispose_all
diff --git a/tests/scripts/thread-cert/Cert_5_6_06_NetworkDataExpiration.py b/tests/scripts/thread-cert/Cert_5_6_06_NetworkDataExpiration.py
index 5452f20..d29640a 100755
--- a/tests/scripts/thread-cert/Cert_5_6_06_NetworkDataExpiration.py
+++ b/tests/scripts/thread-cert/Cert_5_6_06_NetworkDataExpiration.py
@@ -118,7 +118,7 @@
 
         self.nodes[ROUTER].remove_prefix('2001:2:0:3::/64')
         self.nodes[ROUTER].register_netdata()
-        self.simulator.go(10)
+        self.simulator.go(310)
 
         addrs = self.nodes[ED1].get_addrs()
         self.assertTrue(any('2001:2:0:1' in addr[0:10] for addr in addrs))
diff --git a/tests/scripts/thread-cert/addon_test_channel_manager_autocsl.py b/tests/scripts/thread-cert/addon_test_channel_manager_autocsl.py
new file mode 100755
index 0000000..2eaff4f
--- /dev/null
+++ b/tests/scripts/thread-cert/addon_test_channel_manager_autocsl.py
@@ -0,0 +1,143 @@
+#!/usr/bin/env python3
+#
+#  Copyright (c) 2023, The OpenThread Authors.
+#  All rights reserved.
+#
+#  Redistribution and use in source and binary forms, with or without
+#  modification, are permitted provided that the following conditions are met:
+#  1. Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#  2. Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#  3. Neither the name of the copyright holder nor the
+#     names of its contributors may be used to endorse or promote products
+#     derived from this software without specific prior written permission.
+#
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS'
+#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+#  POSSIBILITY OF SUCH DAMAGE.
+#
+
+import unittest
+
+import config
+import mle
+import thread_cert
+from pktverify import consts
+
+LEADER = 1
+ED = 2
+SSED = 3
+
+
+class SSED_CslChannelManager(thread_cert.TestCase):
+    TOPOLOGY = {
+        LEADER: {
+            'version': '1.2',
+        },
+        ED: {
+            'version': '1.2',
+            'is_mtd': False,
+            'mode': 'rn',
+        },
+        SSED: {
+            'version': '1.2',
+            'is_mtd': True,
+            'mode': '-',
+        },
+    }
+    """All nodes are created with default configurations"""
+
+    def test(self):
+
+        self.nodes[SSED].set_csl_period(consts.CSL_DEFAULT_PERIOD)
+        self.nodes[SSED].set_csl_timeout(consts.CSL_DEFAULT_TIMEOUT)
+
+        self.nodes[SSED].get_csl_info()
+
+        self.nodes[LEADER].start()
+        self.simulator.go(config.LEADER_STARTUP_DELAY)
+        self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
+        channel = self.nodes[LEADER].get_channel()
+
+        self.nodes[SSED].start()
+        self.simulator.go(7)
+        self.assertEqual(self.nodes[SSED].get_state(), 'child')
+
+        csl_channel = 0
+        csl_config = self.nodes[SSED].get_csl_info()
+        self.assertTrue(int(csl_config['channel']) == csl_channel)
+        self.assertTrue(csl_config['period'] == '500000us')
+
+        print('SSED rloc:%s' % self.nodes[SSED].get_rloc())
+        self.assertTrue(self.nodes[LEADER].ping(self.nodes[SSED].get_rloc()))
+
+        # let channel monitor collect >970 sample counts
+        self.simulator.go(980 * 41)
+        results = self.nodes[SSED].get_channel_monitor_info()
+        self.assertTrue(int(results['count']) > 970)
+
+        # Configure channel manager channel masks
+        # Set cca threshold to 0 as we cannot change cca assessment in simulation.
+        # and shorten interval to speedup test
+        all_channels_mask = int('0x7fff800', 0)
+        chan_12_to_15_mask = int('0x000f000', 0)
+        interval = 30
+        self.nodes[SSED].set_channel_manager_supported(all_channels_mask)
+        self.nodes[SSED].set_channel_manager_favored(chan_12_to_15_mask)
+        self.nodes[SSED].set_channel_manager_cca_threshold('0x0000')
+        self.nodes[SSED].set_channel_manager_interval(interval)
+
+        # enable channel manager auto-select and check
+        # network channel is not changed by channel manager on SSED
+        # and also csl_channel is unchanged
+        self.nodes[SSED].set_channel_manager_auto_enable(True)
+        self.simulator.go(interval + 1)
+        results = self.nodes[SSED].get_channel_manager_config()
+        self.assertTrue(int(results['auto']) == 1)
+        self.assertTrue(results['cca threshold'] == '0x0000')
+        self.assertTrue(int(results['interval']) == interval)
+        self.assertTrue('11-26' in results['supported'])
+        self.simulator.go(1)
+        self.assertTrue(self.nodes[SSED].get_channel() == channel)
+        csl_config = self.nodes[SSED].get_csl_info()
+        self.assertTrue(int(csl_config['channel']) == csl_channel)
+
+        # check SSED can change csl channel
+        csl_channel = 25
+        self.flush_all()
+        self.nodes[SSED].set_csl_channel(csl_channel)
+        self.simulator.go(1)
+        ssed_messages = self.simulator.get_messages_sent_by(SSED)
+        self.assertIsNotNone(ssed_messages.next_mle_message(mle.CommandType.CHILD_UPDATE_REQUEST))
+        self.simulator.go(1)
+        csl_config = self.nodes[SSED].get_csl_info()
+        self.assertTrue(int(csl_config['channel']) == csl_channel)
+        self.simulator.go(1)
+        self.assertTrue(self.nodes[LEADER].ping(self.nodes[SSED].get_rloc()))
+
+        # enable channel manager autocsl-select in addition
+        # and check csl channel changed to best favored channel 12
+        csl_channel = 12
+        self.nodes[SSED].set_channel_manager_autocsl_enable(True)
+        self.simulator.go(interval + 1)
+        results = self.nodes[SSED].get_channel_manager_config()
+        self.assertTrue(int(results['autocsl']) == 1)
+        self.assertTrue(int(results['channel']) == csl_channel)
+        csl_config = self.nodes[SSED].get_csl_info()
+        self.assertTrue(int(csl_config['channel']) == csl_channel)
+        self.simulator.go(1)
+        self.assertTrue(self.nodes[LEADER].ping(self.nodes[SSED].get_rloc()))
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/scripts/thread-cert/border_router/nat64/test_multi_border_routers.py b/tests/scripts/thread-cert/border_router/nat64/test_multi_border_routers.py
index 46896bc..32ef64a 100755
--- a/tests/scripts/thread-cert/border_router/nat64/test_multi_border_routers.py
+++ b/tests/scripts/thread-cert/border_router/nat64/test_multi_border_routers.py
@@ -131,8 +131,8 @@
         self.assertNotEqual(br2_local_nat64_prefix, br2.get_br_favored_nat64_prefix())
         br2_infra_nat64_prefix = br2.get_br_favored_nat64_prefix()
 
-        self.assertEqual(len(br1.get_netdata_nat64_prefix()), 1)
-        nat64_prefix = br1.get_netdata_nat64_prefix()[0]
+        self.assertEqual(len(br1.get_netdata_nat64_routes()), 1)
+        nat64_prefix = br1.get_netdata_nat64_routes()[0]
         self.assertEqual(nat64_prefix, br2_infra_nat64_prefix)
         self.assertNotEqual(nat64_prefix, br1_local_nat64_prefix)
         self.assertDictIncludes(br1.nat64_state, {
@@ -151,8 +151,8 @@
         br2.nat64_set_enabled(False)
         self.simulator.go(10)
 
-        self.assertEqual(len(br1.get_netdata_nat64_prefix()), 1)
-        nat64_prefix = br1.get_netdata_nat64_prefix()[0]
+        self.assertEqual(len(br1.get_netdata_nat64_routes()), 1)
+        nat64_prefix = br1.get_netdata_nat64_routes()[0]
         self.assertEqual(nat64_prefix, br1_local_nat64_prefix)
         self.assertDictIncludes(br1.nat64_state, {
             'PrefixManager': NAT64_STATE_ACTIVE,
@@ -174,8 +174,8 @@
         self.simulator.go(10)
         self.assertEqual(br2_local_nat64_prefix, br2.get_br_favored_nat64_prefix())
 
-        self.assertEqual(len(br1.get_netdata_nat64_prefix()), 1)
-        nat64_prefix = br1.get_netdata_nat64_prefix()[0]
+        self.assertEqual(len(br1.get_netdata_nat64_routes()), 1)
+        nat64_prefix = br1.get_netdata_nat64_routes()[0]
         self.assertEqual(nat64_prefix, br1_local_nat64_prefix)
         self.assertNotEqual(nat64_prefix, br2_local_nat64_prefix)
         self.assertDictIncludes(br1.nat64_state, {
@@ -194,8 +194,8 @@
         br1.nat64_set_enabled(False)
 
         self.simulator.go(10)
-        self.assertEqual(len(br1.get_netdata_nat64_prefix()), 1)
-        nat64_prefix = br1.get_netdata_nat64_prefix()[0]
+        self.assertEqual(len(br1.get_netdata_nat64_routes()), 1)
+        nat64_prefix = br1.get_netdata_nat64_routes()[0]
         self.assertEqual(br2_local_nat64_prefix, nat64_prefix)
         self.assertNotEqual(br1_local_nat64_prefix, nat64_prefix)
         self.assertDictIncludes(br1.nat64_state, {
@@ -214,8 +214,8 @@
         br1.nat64_set_enabled(True)
 
         self.simulator.go(10)
-        self.assertEqual(len(br1.get_netdata_nat64_prefix()), 1)
-        nat64_prefix = br1.get_netdata_nat64_prefix()[0]
+        self.assertEqual(len(br1.get_netdata_nat64_routes()), 1)
+        nat64_prefix = br1.get_netdata_nat64_routes()[0]
         self.assertEqual(br2_local_nat64_prefix, nat64_prefix)
         self.assertNotEqual(br1_local_nat64_prefix, nat64_prefix)
         self.assertDictIncludes(br1.nat64_state, {
@@ -233,8 +233,8 @@
         #
         br2.disable_br()
         self.simulator.go(10)
-        self.assertEqual(len(br1.get_netdata_nat64_prefix()), 1)
-        nat64_prefix = br1.get_netdata_nat64_prefix()[0]
+        self.assertEqual(len(br1.get_netdata_nat64_routes()), 1)
+        nat64_prefix = br1.get_netdata_nat64_routes()[0]
         self.assertEqual(br1_local_nat64_prefix, nat64_prefix)
         self.assertNotEqual(br2_local_nat64_prefix, nat64_prefix)
         self.assertDictIncludes(br1.nat64_state, {
@@ -252,8 +252,8 @@
         #
         br2.enable_br()
         self.simulator.go(10)
-        self.assertEqual(len(br1.get_netdata_nat64_prefix()), 1)
-        nat64_prefix = br1.get_netdata_nat64_prefix()[0]
+        self.assertEqual(len(br1.get_netdata_nat64_routes()), 1)
+        nat64_prefix = br1.get_netdata_nat64_routes()[0]
         self.assertEqual(br1_local_nat64_prefix, nat64_prefix)
         self.assertNotEqual(br2_local_nat64_prefix, nat64_prefix)
         self.assertDictIncludes(br1.nat64_state, {
diff --git a/tests/scripts/thread-cert/border_router/nat64/test_single_border_router.py b/tests/scripts/thread-cert/border_router/nat64/test_single_border_router.py
index 82f27eb..33d010e 100755
--- a/tests/scripts/thread-cert/border_router/nat64/test_single_border_router.py
+++ b/tests/scripts/thread-cert/border_router/nat64/test_single_border_router.py
@@ -137,8 +137,8 @@
         self.simulator.go(5)
         local_nat64_prefix = br.get_br_nat64_prefix()
 
-        self.assertEqual(len(br.get_netdata_nat64_prefix()), 1)
-        nat64_prefix = br.get_netdata_nat64_prefix()[0]
+        self.assertEqual(len(br.get_netdata_nat64_routes()), 1)
+        nat64_prefix = br.get_netdata_nat64_routes()[0]
         self.assertEqual(nat64_prefix, local_nat64_prefix)
 
         #
@@ -150,15 +150,15 @@
         br.register_netdata()
         self.simulator.go(5)
 
-        self.assertEqual(len(br.get_netdata_nat64_prefix()), 1)
-        self.assertNotEqual(local_nat64_prefix, br.get_netdata_nat64_prefix()[0])
+        self.assertEqual(len(br.get_netdata_nat64_routes()), 1)
+        self.assertNotEqual(local_nat64_prefix, br.get_netdata_nat64_routes()[0])
 
         br.remove_route(SMALL_NAT64_PREFIX)
         br.register_netdata()
         self.simulator.go(10)
 
-        self.assertEqual(len(br.get_netdata_nat64_prefix()), 1)
-        self.assertEqual(local_nat64_prefix, br.get_netdata_nat64_prefix()[0])
+        self.assertEqual(len(br.get_netdata_nat64_routes()), 1)
+        self.assertEqual(local_nat64_prefix, br.get_netdata_nat64_routes()[0])
 
         #
         # Case 3.
@@ -169,15 +169,15 @@
         br.register_netdata()
         self.simulator.go(5)
 
-        self.assertEqual(len(br.get_netdata_nat64_prefix()), 1)
-        self.assertNotEqual(local_nat64_prefix, br.get_netdata_nat64_prefix()[0])
+        self.assertEqual(len(br.get_netdata_nat64_routes()), 1)
+        self.assertNotEqual(local_nat64_prefix, br.get_netdata_nat64_routes()[0])
 
         br.remove_route(LARGE_NAT64_PREFIX)
         br.register_netdata()
         self.simulator.go(10)
 
-        self.assertEqual(len(br.get_netdata_nat64_prefix()), 1)
-        self.assertEqual(local_nat64_prefix, br.get_netdata_nat64_prefix()[0])
+        self.assertEqual(len(br.get_netdata_nat64_routes()), 1)
+        self.assertEqual(local_nat64_prefix, br.get_netdata_nat64_routes()[0])
 
         #
         # Case 4. Disable and re-enable border routing on the border router.
@@ -186,7 +186,7 @@
         self.simulator.go(5)
 
         # NAT64 prefix is withdrawn from Network Data.
-        self.assertEqual(len(br.get_netdata_nat64_prefix()), 0)
+        self.assertEqual(len(br.get_netdata_nat64_routes()), 0)
 
         br.enable_br()
         self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY)
@@ -256,17 +256,17 @@
         self.simulator.go(5)
 
         # NAT64 prefix is withdrawn from Network Data.
-        self.assertEqual(len(br.get_netdata_nat64_prefix()), 0)
+        self.assertEqual(len(br.get_netdata_nat64_routes()), 0)
 
         br.enable_ether()
         self.simulator.go(80)
 
         # Same NAT64 prefix is advertised to Network Data.
-        self.assertEqual(len(br.get_netdata_nat64_prefix()), 1)
-        self.assertEqual(nat64_prefix, br.get_netdata_nat64_prefix()[0])
+        self.assertEqual(len(br.get_netdata_nat64_routes()), 1)
+        self.assertEqual(nat64_prefix, br.get_netdata_nat64_routes()[0])
         # Same NAT64 prefix is advertised to Network Data.
-        self.assertEqual(len(br.get_netdata_nat64_prefix()), 1)
-        self.assertEqual(nat64_prefix, br.get_netdata_nat64_prefix()[0])
+        self.assertEqual(len(br.get_netdata_nat64_routes()), 1)
+        self.assertEqual(nat64_prefix, br.get_netdata_nat64_routes()[0])
 
 
 if __name__ == '__main__':
diff --git a/tests/scripts/thread-cert/border_router/nat64/test_with_infrastructure_prefix.py b/tests/scripts/thread-cert/border_router/nat64/test_with_infrastructure_prefix.py
index 905e6f2..21ab2ba 100755
--- a/tests/scripts/thread-cert/border_router/nat64/test_with_infrastructure_prefix.py
+++ b/tests/scripts/thread-cert/border_router/nat64/test_with_infrastructure_prefix.py
@@ -94,8 +94,8 @@
         # Case 1 No infra-derived OMR prefix. BR publishes its local prefix.
         local_nat64_prefix = br.get_br_nat64_prefix()
 
-        self.assertEqual(len(br.get_netdata_nat64_prefix()), 1)
-        nat64_prefix = br.get_netdata_nat64_prefix()[0]
+        self.assertEqual(len(br.get_netdata_nat64_routes()), 1)
+        nat64_prefix = br.get_netdata_nat64_routes()[0]
         self.assertEqual(nat64_prefix, local_nat64_prefix)
 
         self.assertDictIncludes(br.nat64_state, {
@@ -112,8 +112,8 @@
         self.assertNotEqual(favored_nat64_prefix, local_nat64_prefix)
         infra_nat64_prefix = favored_nat64_prefix
 
-        self.assertEqual(len(br.get_netdata_nat64_prefix()), 1)
-        nat64_prefix = br.get_netdata_nat64_prefix()[0]
+        self.assertEqual(len(br.get_netdata_nat64_routes()), 1)
+        nat64_prefix = br.get_netdata_nat64_routes()[0]
         self.assertEqual(nat64_prefix, infra_nat64_prefix)
         self.assertDictIncludes(br.nat64_state, {
             'PrefixManager': NAT64_STATE_ACTIVE,
@@ -126,8 +126,8 @@
         br.register_netdata()
         self.simulator.go(5)
 
-        self.assertEqual(len(br.get_netdata_nat64_prefix()), 1)
-        self.assertNotEqual(infra_nat64_prefix, br.get_netdata_nat64_prefix()[0])
+        self.assertEqual(len(br.get_netdata_nat64_routes()), 1)
+        self.assertNotEqual(infra_nat64_prefix, br.get_netdata_nat64_routes()[0])
         self.assertDictIncludes(br.nat64_state, {
             'PrefixManager': NAT64_STATE_IDLE,
             'Translator': NAT64_STATE_NOT_RUNNING
@@ -137,7 +137,7 @@
         br.register_netdata()
         self.simulator.go(10)
 
-        self.assertEqual(len(br.get_netdata_nat64_prefix()), 1)
+        self.assertEqual(len(br.get_netdata_nat64_routes()), 1)
         self.assertEqual(nat64_prefix, infra_nat64_prefix)
 
         # Case 4 No change when a smaller prefix in low preference is present
@@ -145,8 +145,8 @@
         br.register_netdata()
         self.simulator.go(5)
 
-        self.assertEqual(len(br.get_netdata_nat64_prefix()), 2)
-        self.assertEqual(br.get_netdata_nat64_prefix(), [infra_nat64_prefix, SMALL_NAT64_PREFIX])
+        self.assertEqual(len(br.get_netdata_nat64_routes()), 2)
+        self.assertEqual(br.get_netdata_nat64_routes(), [infra_nat64_prefix, SMALL_NAT64_PREFIX])
         self.assertDictIncludes(br.nat64_state, {
             'PrefixManager': NAT64_STATE_ACTIVE,
             'Translator': NAT64_STATE_NOT_RUNNING
@@ -162,8 +162,8 @@
 
         local_nat64_prefix = br.get_br_nat64_prefix()
         self.assertNotEqual(local_nat64_prefix, infra_nat64_prefix)
-        self.assertEqual(len(br.get_netdata_nat64_prefix()), 1)
-        self.assertEqual(br.get_netdata_nat64_prefix()[0], local_nat64_prefix)
+        self.assertEqual(len(br.get_netdata_nat64_routes()), 1)
+        self.assertEqual(br.get_netdata_nat64_routes()[0], local_nat64_prefix)
         self.assertDictIncludes(br.nat64_state, {
             'PrefixManager': NAT64_STATE_ACTIVE,
             'Translator': NAT64_STATE_ACTIVE
@@ -174,8 +174,8 @@
         self.simulator.go(NAT64_PREFIX_REFRESH_DELAY)
 
         self.assertEqual(br.get_br_favored_nat64_prefix(), infra_nat64_prefix)
-        self.assertEqual(len(br.get_netdata_nat64_prefix()), 1)
-        self.assertEqual(br.get_netdata_nat64_prefix()[0], infra_nat64_prefix)
+        self.assertEqual(len(br.get_netdata_nat64_routes()), 1)
+        self.assertEqual(br.get_netdata_nat64_routes()[0], infra_nat64_prefix)
         self.assertDictIncludes(br.nat64_state, {
             'PrefixManager': NAT64_STATE_ACTIVE,
             'Translator': NAT64_STATE_NOT_RUNNING
@@ -188,8 +188,8 @@
         self.simulator.go(NAT64_PREFIX_REFRESH_DELAY)
 
         self.assertEqual(br.get_br_favored_nat64_prefix(), SMALL_NAT64_PREFIX)
-        self.assertEqual(len(br.get_netdata_nat64_prefix()), 1)
-        self.assertEqual(br.get_netdata_nat64_prefix()[0], SMALL_NAT64_PREFIX)
+        self.assertEqual(len(br.get_netdata_nat64_routes()), 1)
+        self.assertEqual(br.get_netdata_nat64_routes()[0], SMALL_NAT64_PREFIX)
         self.assertDictIncludes(br.nat64_state, {
             'PrefixManager': NAT64_STATE_ACTIVE,
             'Translator': NAT64_STATE_NOT_RUNNING
diff --git a/tests/scripts/thread-cert/border_router/test_advertising_proxy.py b/tests/scripts/thread-cert/border_router/test_advertising_proxy.py
index 5c66bf9..2ae828b 100755
--- a/tests/scripts/thread-cert/border_router/test_advertising_proxy.py
+++ b/tests/scripts/thread-cert/border_router/test_advertising_proxy.py
@@ -27,7 +27,6 @@
 #  POSSIBILITY OF SUCH DAMAGE.
 #
 import ipaddress
-import logging
 import unittest
 
 import config
@@ -81,6 +80,12 @@
         host.start(start_radvd=False)
         self.simulator.go(5)
 
+        # Reserve UDP ports to verify that SRP server can skip the unavailable
+        # ports correctly
+        server.reserve_udp_port(53535)
+        server.reserve_udp_port(53536)
+        server.reserve_udp_port(53537)
+
         self.assertEqual(server.srp_server_get_state(), 'disabled')
         server.srp_server_set_enabled(True)
         server.srp_server_set_lease_range(LEASE, LEASE, KEY_LEASE, KEY_LEASE)
@@ -88,6 +93,7 @@
         self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY)
         self.assertEqual('leader', server.get_state())
         self.assertEqual(server.srp_server_get_state(), 'running')
+        self.assertNotIn(server.get_srp_server_port(), [53535, 53536, 53537])
 
         client.start()
         self.simulator.go(config.ROUTER_STARTUP_DELAY)
diff --git a/tests/scripts/thread-cert/border_router/test_manual_maddress.py b/tests/scripts/thread-cert/border_router/test_manual_maddress.py
index 536d3ad..78edac8 100755
--- a/tests/scripts/thread-cert/border_router/test_manual_maddress.py
+++ b/tests/scripts/thread-cert/border_router/test_manual_maddress.py
@@ -86,11 +86,12 @@
 
         # TD registers for multicast address, MA1, at BR_1.
         td.add_ipmaddr_tun(MA1)
-        self.simulator.go(5)
+        self.simulator.go(10)
 
         # Host sends a ping packet to the multicast address, MA1. TD should
         # respond to the ping request.
-        host.ping(MA1, backbone=True, ttl=10, interface=host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0])
+        self.assertTrue(
+            host.ping(MA1, backbone=True, ttl=10, interface=host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0]))
         self.simulator.go(5)
 
     def verify(self, pv: pktverify.packet_verifier.PacketVerifier):
diff --git a/tests/scripts/thread-cert/border_router/test_multi_border_routers.py b/tests/scripts/thread-cert/border_router/test_multi_border_routers.py
index ab354d1..201222f 100755
--- a/tests/scripts/thread-cert/border_router/test_multi_border_routers.py
+++ b/tests/scripts/thread-cert/border_router/test_multi_border_routers.py
@@ -141,10 +141,10 @@
         self.assertEqual(br1_omr_prefix, br1.get_netdata_omr_prefixes()[0])
 
         # Each BR should independently register an external route for the on-link prefix.
-        self.assertEqual(len(br1.get_netdata_non_nat64_prefixes()), 2)
-        self.assertEqual(len(router1.get_netdata_non_nat64_prefixes()), 2)
-        self.assertEqual(len(br2.get_netdata_non_nat64_prefixes()), 2)
-        self.assertEqual(len(router2.get_netdata_non_nat64_prefixes()), 2)
+        self.assertEqual(len(br1.get_netdata_non_nat64_routes()), 2)
+        self.assertEqual(len(router1.get_netdata_non_nat64_routes()), 2)
+        self.assertEqual(len(br2.get_netdata_non_nat64_routes()), 2)
+        self.assertEqual(len(router2.get_netdata_non_nat64_routes()), 2)
 
         br1_on_link_prefix = br1.get_br_on_link_prefix()
 
@@ -170,7 +170,7 @@
 
         br1.disable_br()
 
-        self.simulator.go(15)
+        self.simulator.go(315)
         self.collect_ipaddrs()
 
         logging.info("BR1     addrs: %r", br1.get_addrs())
@@ -192,10 +192,10 @@
         # There should be no changes to the external route for the
         # on-link prefix, given that the on-link prefix is derived
         # from the Extended PAN ID.
-        self.assertEqual(len(br1.get_netdata_non_nat64_prefixes()), 1)
-        self.assertEqual(len(router1.get_netdata_non_nat64_prefixes()), 1)
-        self.assertEqual(len(br2.get_netdata_non_nat64_prefixes()), 1)
-        self.assertEqual(len(router2.get_netdata_non_nat64_prefixes()), 1)
+        self.assertEqual(len(br1.get_netdata_non_nat64_routes()), 1)
+        self.assertEqual(len(router1.get_netdata_non_nat64_routes()), 1)
+        self.assertEqual(len(br2.get_netdata_non_nat64_routes()), 1)
+        self.assertEqual(len(router2.get_netdata_non_nat64_routes()), 1)
 
         br2_on_link_prefix = br2.get_br_on_link_prefix()
 
diff --git a/tests/scripts/thread-cert/border_router/test_multi_thread_networks.py b/tests/scripts/thread-cert/border_router/test_multi_thread_networks.py
index eaa1e13..7db19de 100755
--- a/tests/scripts/thread-cert/border_router/test_multi_thread_networks.py
+++ b/tests/scripts/thread-cert/border_router/test_multi_thread_networks.py
@@ -130,10 +130,10 @@
 
         # Each BR should independently register an external route for the on-link prefix
         # and OMR prefix in another Thread Network.
-        self.assertEqual(br1.get_netdata_non_nat64_prefixes(), ['fc00::/7'])
-        self.assertEqual(router1.get_netdata_non_nat64_prefixes(), ['fc00::/7'])
-        self.assertEqual(br2.get_netdata_non_nat64_prefixes(), ['fc00::/7'])
-        self.assertEqual(router2.get_netdata_non_nat64_prefixes(), ['fc00::/7'])
+        self.assertEqual(br1.get_netdata_non_nat64_routes(), ['fc00::/7'])
+        self.assertEqual(router1.get_netdata_non_nat64_routes(), ['fc00::/7'])
+        self.assertEqual(br2.get_netdata_non_nat64_routes(), ['fc00::/7'])
+        self.assertEqual(router2.get_netdata_non_nat64_routes(), ['fc00::/7'])
 
         self.assertTrue(len(router1.get_ip6_address(config.ADDRESS_TYPE.OMR)) == 1)
         self.assertTrue(len(router2.get_ip6_address(config.ADDRESS_TYPE.OMR)) == 1)
diff --git a/tests/scripts/thread-cert/border_router/test_on_link_prefix.py b/tests/scripts/thread-cert/border_router/test_on_link_prefix.py
index 4067236..0f56baa 100755
--- a/tests/scripts/thread-cert/border_router/test_on_link_prefix.py
+++ b/tests/scripts/thread-cert/border_router/test_on_link_prefix.py
@@ -126,7 +126,7 @@
         logging.info("ROUTER1 addrs: %r", router1.get_addrs())
         logging.info("HOST    addrs: %r", host.get_addrs())
 
-        self.assertEqual(len(br1.get_netdata_non_nat64_prefixes()), 1)
+        self.assertEqual(len(br1.get_netdata_non_nat64_routes()), 1)
 
         host_on_link_addr = host.get_matched_ula_addresses(ON_LINK_PREFIX)[0]
         self.assertTrue(router1.ping(host_on_link_addr))
@@ -166,10 +166,10 @@
         # but don't remove the external routes for the radvd on-link prefix
         # immediately, because the SLAAC addresses are still valid.
 
-        self.assertEqual(len(br1.get_netdata_non_nat64_prefixes()), 1)
-        self.assertEqual(len(router1.get_netdata_non_nat64_prefixes()), 1)
-        self.assertEqual(len(br2.get_netdata_non_nat64_prefixes()), 1)
-        self.assertEqual(len(router2.get_netdata_non_nat64_prefixes()), 1)
+        self.assertEqual(len(br1.get_netdata_non_nat64_routes()), 1)
+        self.assertEqual(len(router1.get_netdata_non_nat64_routes()), 1)
+        self.assertEqual(len(br2.get_netdata_non_nat64_routes()), 1)
+        self.assertEqual(len(router2.get_netdata_non_nat64_routes()), 1)
 
         br2_on_link_prefix = br2.get_br_on_link_prefix()
 
diff --git a/tests/scripts/thread-cert/border_router/test_plat_udp_accessiblity.py b/tests/scripts/thread-cert/border_router/test_plat_udp_accessiblity.py
index 5ed84bc..ea491a9 100755
--- a/tests/scripts/thread-cert/border_router/test_plat_udp_accessiblity.py
+++ b/tests/scripts/thread-cert/border_router/test_plat_udp_accessiblity.py
@@ -74,6 +74,7 @@
         router.start()
         self.simulator.go(config.ROUTER_STARTUP_DELAY)
         self.assertEqual('router', router.get_state())
+        router.srp_client_stop()
 
         # Router1 can ping to/from the Host on infra link.
         self.assertTrue(router.ping(br.get_rloc()))
diff --git a/tests/scripts/thread-cert/border_router/test_publish_meshcop_service.py b/tests/scripts/thread-cert/border_router/test_publish_meshcop_service.py
index d0dafad..6756794 100755
--- a/tests/scripts/thread-cert/border_router/test_publish_meshcop_service.py
+++ b/tests/scripts/thread-cert/border_router/test_publish_meshcop_service.py
@@ -105,6 +105,7 @@
         br1.set_active_dataset(updateExisting=True, network_name='ot-br1-1')
         br1.start()
         self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY)
+        self.simulator.go(5)  # Needs to wait extra some time to update meshcop service on state changes.
         self.check_meshcop_service(br1, host)
 
         # verify that there are two meshcop services
@@ -147,6 +148,7 @@
         }
 
         br1.set_active_dataset(**dataset)
+        self.simulator.go(10)
 
         self.assertEqual(len(host.browse_mdns_services('_meshcop._udp')), 2)
         self.check_meshcop_service(br1, host)
diff --git a/tests/scripts/thread-cert/border_router/test_radvd_coexist.py b/tests/scripts/thread-cert/border_router/test_radvd_coexist.py
index 2fffd60..6dc7a13 100755
--- a/tests/scripts/thread-cert/border_router/test_radvd_coexist.py
+++ b/tests/scripts/thread-cert/border_router/test_radvd_coexist.py
@@ -102,8 +102,8 @@
 
         self.assertEqual(len(br.get_netdata_omr_prefixes()), 1)
         self.assertEqual(len(router.get_netdata_omr_prefixes()), 1)
-        self.assertEqual(len(br.get_netdata_non_nat64_prefixes()), 1)
-        self.assertEqual(len(router.get_netdata_non_nat64_prefixes()), 1)
+        self.assertEqual(len(br.get_netdata_non_nat64_routes()), 1)
+        self.assertEqual(len(router.get_netdata_non_nat64_routes()), 1)
 
         self.assertEqual(len(br.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1)
         self.assertEqual(len(router.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1)
@@ -122,8 +122,8 @@
 
         self.assertEqual(len(br.get_netdata_omr_prefixes()), 1)
         self.assertEqual(len(router.get_netdata_omr_prefixes()), 1)
-        self.assertEqual(len(br.get_netdata_non_nat64_prefixes()), 1)
-        self.assertEqual(len(router.get_netdata_non_nat64_prefixes()), 1)
+        self.assertEqual(len(br.get_netdata_non_nat64_routes()), 1)
+        self.assertEqual(len(router.get_netdata_non_nat64_routes()), 1)
 
         self.assertEqual(len(br.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1)
         self.assertEqual(len(router.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1)
diff --git a/tests/scripts/thread-cert/border_router/test_single_border_router.py b/tests/scripts/thread-cert/border_router/test_single_border_router.py
index a660345..e72e557 100755
--- a/tests/scripts/thread-cert/border_router/test_single_border_router.py
+++ b/tests/scripts/thread-cert/border_router/test_single_border_router.py
@@ -105,8 +105,8 @@
 
         self.assertEqual(len(br.get_netdata_omr_prefixes()), 1)
         self.assertEqual(len(router.get_netdata_omr_prefixes()), 1)
-        self.assertEqual(len(br.get_netdata_non_nat64_prefixes()), 1)
-        self.assertEqual(len(router.get_netdata_non_nat64_prefixes()), 1)
+        self.assertEqual(len(br.get_netdata_non_nat64_routes()), 1)
+        self.assertEqual(len(router.get_netdata_non_nat64_routes()), 1)
 
         omr_prefix = br.get_br_omr_prefix()
         on_link_prefix = br.get_br_on_link_prefix()
@@ -145,8 +145,8 @@
 
         self.assertEqual(len(br.get_netdata_omr_prefixes()), 2)
         self.assertEqual(len(router.get_netdata_omr_prefixes()), 2)
-        self.assertEqual(len(br.get_netdata_non_nat64_prefixes()), 1)
-        self.assertEqual(len(router.get_netdata_non_nat64_prefixes()), 1)
+        self.assertEqual(len(br.get_netdata_non_nat64_routes()), 1)
+        self.assertEqual(len(router.get_netdata_non_nat64_routes()), 1)
 
         self.assertEqual(len(br.get_ip6_address(config.ADDRESS_TYPE.OMR)), 2)
         self.assertEqual(len(router.get_ip6_address(config.ADDRESS_TYPE.OMR)), 2)
@@ -162,7 +162,7 @@
         br.remove_prefix(ON_MESH_PREFIX2)
         br.register_netdata()
 
-        self.simulator.go(10)
+        self.simulator.go(310)
         self.collect_ipaddrs()
 
         logging.info("BR     addrs: %r", br.get_addrs())
@@ -171,8 +171,8 @@
 
         self.assertEqual(len(br.get_netdata_omr_prefixes()), 1)
         self.assertEqual(len(router.get_netdata_omr_prefixes()), 1)
-        self.assertEqual(len(br.get_netdata_non_nat64_prefixes()), 1)
-        self.assertEqual(len(router.get_netdata_non_nat64_prefixes()), 1)
+        self.assertEqual(len(br.get_netdata_non_nat64_routes()), 1)
+        self.assertEqual(len(router.get_netdata_non_nat64_routes()), 1)
 
         # The same local OMR and on-link prefix should be re-register.
         self.assertEqual(br.get_netdata_omr_prefixes(), [omr_prefix])
@@ -223,8 +223,8 @@
 
         self.assertEqual(len(br.get_netdata_omr_prefixes()), 1)
         self.assertEqual(len(router.get_netdata_omr_prefixes()), 1)
-        self.assertEqual(len(br.get_netdata_non_nat64_prefixes()), 1)
-        self.assertEqual(len(router.get_netdata_non_nat64_prefixes()), 1)
+        self.assertEqual(len(br.get_netdata_non_nat64_routes()), 1)
+        self.assertEqual(len(router.get_netdata_non_nat64_routes()), 1)
 
         # The same local OMR and on-link prefix should be re-registered.
         self.assertEqual(br.get_netdata_omr_prefixes(), [omr_prefix])
@@ -275,8 +275,8 @@
 
         self.assertEqual(len(br.get_netdata_omr_prefixes()), 1)
         self.assertEqual(len(router.get_netdata_omr_prefixes()), 1)
-        self.assertEqual(len(br.get_netdata_non_nat64_prefixes()), 1)
-        self.assertEqual(len(router.get_netdata_non_nat64_prefixes()), 1)
+        self.assertEqual(len(br.get_netdata_non_nat64_routes()), 1)
+        self.assertEqual(len(router.get_netdata_non_nat64_routes()), 1)
 
         # The same local OMR and on-link prefix should be re-registered.
         self.assertEqual(br.get_netdata_omr_prefixes(), [omr_prefix])
@@ -314,8 +314,8 @@
         br.start_radvd_service(prefix=config.ONLINK_GUA_PREFIX, slaac=True)
         self.simulator.go(5)
 
-        self.assertEqual(len(br.get_netdata_non_nat64_prefixes()), 1)
-        self.assertEqual(len(router.get_netdata_non_nat64_prefixes()), 1)
+        self.assertEqual(len(br.get_netdata_non_nat64_routes()), 1)
+        self.assertEqual(len(router.get_netdata_non_nat64_routes()), 1)
 
         self.assertTrue(router.ping(host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_GUA)[0]))
         self.assertTrue(router.ping(host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0]))
diff --git a/tests/scripts/thread-cert/border_router/test_trel_connectivity.py b/tests/scripts/thread-cert/border_router/test_trel_connectivity.py
index 33487d8..20ed039 100755
--- a/tests/scripts/thread-cert/border_router/test_trel_connectivity.py
+++ b/tests/scripts/thread-cert/border_router/test_trel_connectivity.py
@@ -143,6 +143,23 @@
         self.assertTrue(med1.ping(router2_mleid))
         self.assertTrue(sed1.ping(router2_mleid))
 
+        counters = br1.get_trel_counters()
+        print('br1 trel counters', counters)
+        self.assertTrue(counters['Inbound']['packets'] > 0)
+        self.assertTrue(counters['Inbound']['bytes'] > 0)
+        self.assertTrue(counters['Outbound']['packets'] > 0)
+        self.assertTrue(counters['Outbound']['bytes'] > 0)
+        self.assertTrue(counters['Outbound']['failures'] >= 0)
+
+        br1.reset_trel_counters()
+        counters = br1.get_trel_counters()
+        print('br1 trel counters after reset', counters)
+        self.assertTrue(counters['Inbound']['packets'] == 0)
+        self.assertTrue(counters['Inbound']['bytes'] == 0)
+        self.assertTrue(counters['Outbound']['packets'] == 0)
+        self.assertTrue(counters['Outbound']['bytes'] == 0)
+        self.assertTrue(counters['Outbound']['failures'] == 0)
+
     def verify(self, pv: PacketVerifier):
         pkts: PacketFilter = pv.pkts
         BR1_RLOC16 = pv.vars['BR1_RLOC16']
diff --git a/tests/scripts/thread-cert/node.py b/tests/scripts/thread-cert/node.py
index 25e254a..9f5d337 100755
--- a/tests/scripts/thread-cert/node.py
+++ b/tests/scripts/thread-cert/node.py
@@ -197,6 +197,9 @@
         self.pexpect.wait()
         self.pexpect.proc.kill()
 
+    def reserve_udp_port(self, port):
+        self.bash(f'socat -u UDP6-LISTEN:{port},bindtodevice=wpan0 - &')
+
     def destroy(self):
         logging.info("Destroying %s", self)
         self._shutdown_docker()
@@ -807,6 +810,18 @@
         results = [line for line in output if self._match_pattern(line, pattern)]
         return results
 
+    def _expect_key_value_pairs(self, pattern, separator=': '):
+        """Expect 'key: value' in multiple lines.
+
+        Returns:
+            Dictionary of the key:value pairs.
+        """
+        result = {}
+        for line in self._expect_results(pattern):
+            key, val = line.split(separator)
+            result.update({key: val})
+        return result
+
     @staticmethod
     def _match_pattern(line, pattern):
         if isinstance(pattern, str):
@@ -1262,7 +1277,7 @@
         self._expect_done()
 
     def srp_client_disable_auto_start_mode(self):
-        self.send_command(f'srp client autostart able')
+        self.send_command(f'srp client autostart disable')
         self._expect_done()
 
     def srp_client_get_server_address(self):
@@ -1390,6 +1405,30 @@
 
             raise
 
+    def get_trel_counters(self):
+        cmd = 'trel counters'
+        self.send_command(cmd)
+        result = self._expect_command_output()
+
+        counters = {}
+        for line in result:
+            m = re.match(r'(\w+)\:[^\d]+(\d+)[^\d]+(\d+)(?:[^\d]+(\d+))?', line)
+            if m:
+                groups = m.groups()
+                sub_counters = {
+                    'packets': int(groups[1]),
+                    'bytes': int(groups[2]),
+                }
+                if groups[3]:
+                    sub_counters['failures'] = int(groups[3])
+                counters[groups[0]] = sub_counters
+        return counters
+
+    def reset_trel_counters(self):
+        cmd = 'trel counters reset'
+        self.send_command(cmd)
+        self._expect_done()
+
     def _encode_txt_entry(self, entry):
         """Encodes the TXT entry to the DNS-SD TXT record format as a HEX string.
 
@@ -1676,6 +1715,10 @@
         self.send_command(cmd)
         self._expect_done()
 
+    def get_key_switch_guardtime(self):
+        self.send_command('keysequence guardtime')
+        return int(self._expect_result(r'\d+'))
+
     def set_key_switch_guardtime(self, key_switch_guardtime):
         cmd = 'keysequence guardtime %d' % key_switch_guardtime
         self.send_command(cmd)
@@ -1771,7 +1814,7 @@
 
     def get_csl_info(self):
         self.send_command('csl')
-        self._expect_done()
+        return self._expect_key_value_pairs(r'\S+')
 
     def set_csl_channel(self, csl_channel):
         self.send_command('csl channel %d' % csl_channel)
@@ -2177,13 +2220,21 @@
         self.send_command(cmd)
         return self._expect_command_output()[0]
 
-    def get_netdata_non_nat64_prefixes(self):
-        prefixes = []
+    def get_netdata_non_nat64_routes(self):
+        nat64_routes = []
         routes = self.get_routes()
         for route in routes:
             if 'n' not in route.split(' ')[1]:
-                prefixes.append(route.split(' ')[0])
-        return prefixes
+                nat64_routes.append(route.split(' ')[0])
+        return nat64_routes
+
+    def get_netdata_nat64_routes(self):
+        nat64_routes = []
+        routes = self.get_routes()
+        for route in routes:
+            if 'n' in route.split(' ')[1]:
+                nat64_routes.append(route.split(' ')[0])
+        return nat64_routes
 
     def get_br_nat64_prefix(self):
         cmd = 'br nat64prefix local'
@@ -2302,14 +2353,6 @@
                 continue
         return {'protocol': protocol_counters, 'errors': error_counters}
 
-    def get_netdata_nat64_prefix(self):
-        prefixes = []
-        routes = self.get_routes()
-        for route in routes:
-            if 'n' in route.split(' ')[1]:
-                prefixes.append(route.split(' ')[0])
-        return prefixes
-
     def get_prefixes(self):
         return self.get_netdata()['Prefixes']
 
@@ -2659,7 +2702,7 @@
         self.send_command('dataset commit pending')
         self._expect_done()
 
-    def start_dataset_updater(self, panid=None, channel=None):
+    def start_dataset_updater(self, panid=None, channel=None, security_policy=None, delay=None):
         self.send_command('dataset clear')
         self._expect_done()
 
@@ -2673,6 +2716,18 @@
             self.send_command(cmd)
             self._expect_done()
 
+        if security_policy is not None:
+            cmd = 'dataset securitypolicy %d %s ' % (security_policy[0], security_policy[1])
+            if (len(security_policy) >= 3):
+                cmd += '%d ' % (security_policy[2])
+            self.send_command(cmd)
+            self._expect_done()
+
+        if delay is not None:
+            cmd = 'dataset delay %d ' % delay
+            self.send_command(cmd)
+            self._expect_done()
+
         self.send_command('dataset updater start')
         self._expect_done()
 
@@ -3558,6 +3613,81 @@
         line = self._expect_command_output()[0]
         return [int(item) for item in line.split()]
 
+    def get_channel_monitor_info(self) -> Dict:
+        """
+        Returns:
+            Dict of channel monitor info, e.g. 
+                {'enabled': '1',
+                 'interval': '41000',
+                 'threshold': '-75',
+                 'window': '960',
+                 'count': '985',
+                 'occupancies': {
+                    '11': '0.00%',
+                    '12': '3.50%',
+                    '13': '9.89%',
+                    '14': '15.36%',
+                    '15': '20.02%',
+                    '16': '21.95%',
+                    '17': '32.71%',
+                    '18': '35.76%',
+                    '19': '37.97%',
+                    '20': '43.68%',
+                    '21': '48.95%',
+                    '22': '54.05%',
+                    '23': '58.65%',
+                    '24': '68.26%',
+                    '25': '66.73%',
+                    '26': '73.12%'
+                    }
+                }
+        """
+        config = {}
+        self.send_command('channel monitor')
+
+        for line in self._expect_results(r'\S+'):
+            if re.match(r'.*:\s.*', line):
+                key, val = line.split(':')
+                config.update({key: val.strip()})
+            elif re.match(r'.*:', line):  # occupancy
+                occ_key, val = line.split(':')
+                val = {}
+                config.update({occ_key: val})
+            elif 'busy' in line:
+                # channel occupancies
+                key = line.split()[1]
+                val = line.split()[3]
+                config[occ_key].update({key: val})
+        return config
+
+    def set_channel_manager_auto_enable(self, enable: bool):
+        self.send_command(f'channel manager auto {int(enable)}')
+        self._expect_done()
+
+    def set_channel_manager_autocsl_enable(self, enable: bool):
+        self.send_command(f'channel manager autocsl {int(enable)}')
+        self._expect_done()
+
+    def set_channel_manager_supported(self, channel_mask: int):
+        self.send_command(f'channel manager supported {int(channel_mask)}')
+        self._expect_done()
+
+    def set_channel_manager_favored(self, channel_mask: int):
+        self.send_command(f'channel manager favored {int(channel_mask)}')
+        self._expect_done()
+
+    def set_channel_manager_interval(self, interval: int):
+        self.send_command(f'channel manager interval {interval}')
+        self._expect_done()
+
+    def set_channel_manager_cca_threshold(self, hex_value: str):
+        self.send_command(f'channel manager threshold {hex_value}')
+        self._expect_done()
+
+    def get_channel_manager_config(self):
+        self.send_command('channel manager')
+        return self._expect_key_value_pairs(r'\S+')
+
 
 class Node(NodeImpl, OtCli):
     pass
diff --git a/tests/scripts/thread-cert/requirements.txt b/tests/scripts/thread-cert/requirements.txt
index 99b9797..8afe42d 100644
--- a/tests/scripts/thread-cert/requirements.txt
+++ b/tests/scripts/thread-cert/requirements.txt
@@ -16,7 +16,7 @@
     # via pexpect
 py==1.11.0
     # via pyshark
-pycryptodome==3.17
+pycryptodome==3.19.1
     # via -r requirements.in
 pyshark==0.4.6
     # via -r requirements.in
diff --git a/tests/scripts/thread-cert/test_detach.py b/tests/scripts/thread-cert/test_detach.py
index 954df98..c32f8b3 100755
--- a/tests/scripts/thread-cert/test_detach.py
+++ b/tests/scripts/thread-cert/test_detach.py
@@ -105,7 +105,7 @@
         self.assertFalse(list(filter(lambda x: x[1]['rloc16'] == router1_rloc16, leader.router_table().items())))
 
         router1.start()
-        self.simulator.go(5)
+        self.simulator.go(config.ROUTER_STARTUP_DELAY)
         self.assertEqual(router1.get_state(), 'router')
 
         child1.start()
@@ -121,7 +121,7 @@
         self.assertEqual(child1.get_state(), 'disabled')
 
         router1.start()
-        self.simulator.go(5)
+        self.simulator.go(config.ROUTER_STARTUP_DELAY)
         self.assertEqual(router1.get_state(), 'router')
 
         child1.start()
diff --git a/tests/scripts/thread-cert/test_dnssd.py b/tests/scripts/thread-cert/test_dnssd.py
index 0110c1e..38c4f85 100755
--- a/tests/scripts/thread-cert/test_dnssd.py
+++ b/tests/scripts/thread-cert/test_dnssd.py
@@ -237,10 +237,6 @@
                                     weight,
                                     addrs,
                                     subtypes=''):
-        client.netdata_show()
-        srp_server_port = client.get_srp_server_port()
-
-        client.srp_client_start(server.get_mleid(), srp_server_port)
         client.srp_client_set_host_name(hostname)
         client.srp_client_set_host_address(*addrs)
         client.srp_client_add_service(instancename, SERVICE + subtypes, port, priority, weight)
diff --git a/tests/scripts/thread-cert/test_key_rotation_and_key_guard_time.py b/tests/scripts/thread-cert/test_key_rotation_and_key_guard_time.py
new file mode 100755
index 0000000..595ffea
--- /dev/null
+++ b/tests/scripts/thread-cert/test_key_rotation_and_key_guard_time.py
@@ -0,0 +1,171 @@
+#!/usr/bin/env python3
+#
+#  Copyright (c) 2024, The OpenThread Authors.
+#  All rights reserved.
+#
+#  Redistribution and use in source and binary forms, with or without
+#  modification, are permitted provided that the following conditions are met:
+#  1. Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#  2. Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#  3. Neither the name of the copyright holder nor the
+#     names of its contributors may be used to endorse or promote products
+#     derived from this software without specific prior written permission.
+#
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+#  POSSIBILITY OF SUCH DAMAGE.
+#
+
+import ipaddress
+import unittest
+import math
+
+import command
+import config
+import thread_cert
+
+# Test description:
+#
+#   This test verifies key rotation and key guard time mechanisms.
+#
+#
+# Topology:
+#
+#   leader ---  router
+#    |    \
+#    |     \
+#  child   reed
+#
+
+LEADER = 1
+CHILD = 2
+REED = 3
+ROUTER = 4
+
+
+class MleMsgKeySeqJump(thread_cert.TestCase):
+    USE_MESSAGE_FACTORY = False
+    SUPPORT_NCP = False
+
+    TOPOLOGY = {
+        LEADER: {
+            'name': 'LEADER',
+            'mode': 'rdn',
+        },
+        CHILD: {
+            'name': 'CHILD',
+            'is_mtd': True,
+            'mode': 'rn',
+        },
+        REED: {
+            'name': 'REED',
+            'mode': 'rn'
+        },
+        ROUTER: {
+            'name': 'ROUTER',
+            'mode': 'rdn',
+        },
+    }
+
+    def test(self):
+        leader = self.nodes[LEADER]
+        child = self.nodes[CHILD]
+        reed = self.nodes[REED]
+        router = self.nodes[ROUTER]
+
+        nodes = [leader, child, reed, router]
+
+        #-------------------------------------------------------------------
+        # Form the network.
+
+        for node in nodes:
+            node.set_key_sequence_counter(0)
+
+        leader.start()
+        self.simulator.go(config.LEADER_STARTUP_DELAY)
+        self.assertEqual(leader.get_state(), 'leader')
+
+        child.start()
+        reed.start()
+        self.simulator.go(5)
+        self.assertEqual(child.get_state(), 'child')
+        self.assertEqual(reed.get_state(), 'child')
+
+        router.start()
+        self.simulator.go(config.ROUTER_STARTUP_DELAY)
+        self.assertEqual(router.get_state(), 'router')
+
+        #-------------------------------------------------------------------
+        # Validate the initial key seq counter and key switch guard time
+
+        for node in nodes:
+            self.assertEqual(node.get_key_sequence_counter(), 0)
+            self.assertEqual(node.get_key_switch_guardtime(), 624)
+
+        #-------------------------------------------------------------------
+        # Change the key rotation time a bunch of times and make sure that
+        # the key switch guard time is properly changed (should be set
+        # to 93% of the rotation time).
+
+        for rotation_time in [100, 1, 10, 888, 2]:
+            reed.start_dataset_updater(security_policy=[rotation_time, 'onrc'])
+            guardtime = math.floor(rotation_time * 93 / 100) if rotation_time >= 2 else 1
+            self.simulator.go(100)
+            for node in nodes:
+                self.assertEqual(node.get_key_switch_guardtime(), guardtime)
+
+        #-------------------------------------------------------------------
+        # Wait for key rotation time (2 hours) and check that all nodes
+        # moved to the next key seq counter
+
+        self.simulator.go(2 * 60 * 60)
+        for node in nodes:
+            self.assertEqual(node.get_key_sequence_counter(), 1)
+
+        #-------------------------------------------------------------------
+        # Manually increment the key sequence counter on leader and make
+        # sure other nodes are not updated due to key guard time.
+
+        router.set_key_sequence_counter(2)
+
+        self.simulator.go(50 * 60)
+
+        self.assertEqual(router.get_key_sequence_counter(), 2)
+
+        for node in [leader, reed, child]:
+            self.assertEqual(node.get_key_sequence_counter(), 1)
+
+        #-------------------------------------------------------------------
+        # Make sure nodes can communicate with each other.
+
+        self.assertTrue(leader.ping(router.get_mleid()))
+        self.assertTrue(router.ping(child.get_mleid()))
+
+        #-------------------------------------------------------------------
+        # Wait for rotation time to expire. Validate that the `router`
+        # has moved to key seq `3` and all other nodes also followed.
+
+        self.simulator.go(75 * 60)
+
+        self.assertEqual(router.get_key_sequence_counter(), 3)
+
+        for node in nodes:
+            self.assertEqual(node.get_key_sequence_counter(), 3)
+
+        self.assertTrue(leader.ping(router.get_mleid()))
+        self.assertTrue(router.ping(child.get_mleid()))
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/scripts/thread-cert/test_mle_msg_key_seq_jump.py b/tests/scripts/thread-cert/test_mle_msg_key_seq_jump.py
index d400557..2463434 100755
--- a/tests/scripts/thread-cert/test_mle_msg_key_seq_jump.py
+++ b/tests/scripts/thread-cert/test_mle_msg_key_seq_jump.py
@@ -221,20 +221,20 @@
         self.assertEqual(reed.get_key_sequence_counter(), 20)
 
         #-------------------------------------------------------------------
-        # Move forward the key seq counter by one on router. Wait for max
+        # Move forward the key seq counter by two on router. Wait for max
         # time between advertisements. Validate that leader adopts the higher
         # counter value.
 
-        router.set_key_sequence_counter(21)
-        self.assertEqual(router.get_key_sequence_counter(), 21)
+        router.set_key_sequence_counter(22)
+        self.assertEqual(router.get_key_sequence_counter(), 22)
 
         self.simulator.go(52)
-        self.assertEqual(leader.get_key_sequence_counter(), 21)
-        self.assertEqual(reed.get_key_sequence_counter(), 21)
+        self.assertEqual(leader.get_key_sequence_counter(), 22)
+        self.assertEqual(reed.get_key_sequence_counter(), 22)
 
         child.set_mode('r')
         self.simulator.go(2)
-        self.assertEqual(child.get_key_sequence_counter(), 21)
+        self.assertEqual(child.get_key_sequence_counter(), 22)
 
         #-------------------------------------------------------------------
         # Force a reattachment from the child with a higher key seq counter,
@@ -247,6 +247,7 @@
 
         child.factory_reset()
         self.assertEqual(child.get_state(), 'disabled')
+        child.set_mode('r')
 
         child.set_active_dataset(channel=leader.get_channel(),
                                  network_key=leader.get_networkkey(),
diff --git a/tests/scripts/thread-cert/test_netdata_publisher.py b/tests/scripts/thread-cert/test_netdata_publisher.py
index f1a1c5d..c31ac6e 100755
--- a/tests/scripts/thread-cert/test_netdata_publisher.py
+++ b/tests/scripts/thread-cert/test_netdata_publisher.py
@@ -70,7 +70,7 @@
 
 # The desired number of entries (based on related config).
 DESIRED_NUM_DNSSRP_ANYCAST = 8
-DESIRED_NUM_DNSSRP_UNCIAST = 2
+DESIRED_NUM_DNSSRP_UNICAST = 2
 DESIRED_NUM_ON_MESH_PREFIX = 3
 DESIRED_NUM_EXTERNAL_ROUTE = 10
 
@@ -263,48 +263,123 @@
             self.verify_anycast_services(services)
 
         #---------------------------------------------------------------------------------
-        # DNS/SRP unicast entries
+        # DNS/SRP service data unicast entries
 
-        # Publish DNS/SRP unicast address on all routers, first using
-        # MLE-EID address, then change to use specific address. Verify
-        # that number of entries in network data is correct in each step
-        # and that entries are switched correctly.
         num = 0
         for node in routers:
-            node.netdata_publish_dnssrp_unicast_mleid(DNSSRP_PORT)
-            self.simulator.go(WAIT_TIME)
-            num += 1
-            services = leader.get_services()
-            self.assertEqual(len(services), min(num, DESIRED_NUM_DNSSRP_UNCIAST))
-            self.verify_unicast_services(services)
-
-        for node in routers:
             node.netdata_publish_dnssrp_unicast(DNSSRP_ADDRESS, DNSSRP_PORT)
             self.simulator.go(WAIT_TIME)
+            num += 1
             services = leader.get_services()
-            self.assertEqual(len(services), min(num, DESIRED_NUM_DNSSRP_UNCIAST))
+            self.assertEqual(len(services), min(num, DESIRED_NUM_DNSSRP_UNICAST))
             self.verify_unicast_services(services)
 
         for node in routers:
             node.srp_server_set_enabled(True)
             self.simulator.go(WAIT_TIME)
+
         self.assertEqual(sum(node.srp_server_get_state() == 'running' for node in routers),
-                         min(len(routers), DESIRED_NUM_DNSSRP_UNCIAST))
+                         min(len(routers), DESIRED_NUM_DNSSRP_UNICAST))
         self.assertEqual(sum(node.srp_server_get_state() == 'stopped' for node in routers),
-                         max(len(routers) - DESIRED_NUM_DNSSRP_UNCIAST, 0))
+                         max(len(routers) - DESIRED_NUM_DNSSRP_UNICAST, 0))
 
         for node in routers:
             node.netdata_unpublish_dnssrp()
             self.simulator.go(WAIT_TIME)
             num -= 1
             services = leader.get_services()
-            self.assertEqual(len(services), min(num, DESIRED_NUM_DNSSRP_UNCIAST))
+            self.assertEqual(len(services), min(num, DESIRED_NUM_DNSSRP_UNICAST))
             self.verify_unicast_services(services)
         for node in routers:
             node.srp_server_set_enabled(False)
             self.assertEqual(node.srp_server_get_state(), 'disabled')
 
         #---------------------------------------------------------------------------------
+        # DNS/SRP server data unicast entries
+
+        num = 0
+        for node in routers:
+            node.netdata_publish_dnssrp_unicast_mleid(DNSSRP_PORT)
+            self.simulator.go(WAIT_TIME)
+            num += 1
+            services = leader.get_services()
+            self.assertEqual(len(services), min(num, DESIRED_NUM_DNSSRP_UNICAST))
+            self.verify_unicast_services(services)
+
+        for node in routers:
+            node.srp_server_set_enabled(True)
+            self.simulator.go(WAIT_TIME)
+        self.assertEqual(sum(node.srp_server_get_state() == 'running' for node in routers),
+                         min(len(routers), DESIRED_NUM_DNSSRP_UNICAST))
+        self.assertEqual(sum(node.srp_server_get_state() == 'stopped' for node in routers),
+                         max(len(routers) - DESIRED_NUM_DNSSRP_UNICAST, 0))
+
+        for node in routers:
+            node.netdata_unpublish_dnssrp()
+            self.simulator.go(WAIT_TIME)
+            num -= 1
+            services = leader.get_services()
+            self.assertEqual(len(services), min(num, DESIRED_NUM_DNSSRP_UNICAST))
+            self.verify_unicast_services(services)
+        for node in routers:
+            node.srp_server_set_enabled(False)
+            self.assertEqual(node.srp_server_get_state(), 'disabled')
+
+        #---------------------------------------------------------------------------------
+        # DNS/SRP server data unicast vs anycast
+
+        num = 0
+        for node in routers:
+            node.netdata_publish_dnssrp_unicast_mleid(DNSSRP_PORT)
+            self.simulator.go(WAIT_TIME)
+            num += 1
+            services = leader.get_services()
+            self.assertEqual(len(services), min(num, DESIRED_NUM_DNSSRP_UNICAST))
+            self.verify_unicast_services(services)
+
+        # Verify that publishing an anycast entry will update the
+        # limit for the server data unicast address entry and all are
+        # removed.
+
+        leader.netdata_publish_dnssrp_anycast(ANYCAST_SEQ_NUM)
+        self.simulator.go(WAIT_TIME)
+        services = leader.get_services()
+        self.assertEqual(len(services), 1)
+        self.verify_anycast_services(services)
+
+        # Removing the anycast entry will cause the lower priority
+        # server data unicast entries to be added again.
+
+        leader.netdata_unpublish_dnssrp()
+        self.simulator.go(WAIT_TIME)
+
+        services = leader.get_services()
+        self.assertEqual(len(services), min(num, DESIRED_NUM_DNSSRP_UNICAST))
+        self.verify_unicast_services(services)
+
+        #---------------------------------------------------------------------------------
+        # DNS/SRP server data unicast vs service data unicast
+
+        leader.netdata_publish_dnssrp_unicast(DNSSRP_ADDRESS, DNSSRP_PORT)
+        self.simulator.go(WAIT_TIME)
+        services = leader.get_services()
+        self.assertEqual(len(services), 1)
+        self.verify_unicast_services(services)
+
+        # Removing the service data unicast entry will cause the lower
+        # priority server data unicast entries to be added again.
+
+        leader.netdata_unpublish_dnssrp()
+        self.simulator.go(WAIT_TIME)
+
+        services = leader.get_services()
+        self.assertEqual(len(services), min(num, DESIRED_NUM_DNSSRP_UNICAST))
+        self.verify_unicast_services(services)
+
+        for node in routers:
+            node.netdata_unpublish_dnssrp()
+
+        #---------------------------------------------------------------------------------
         # DNS/SRP entries: Verify publisher preference when removing
         # entries.
         #
@@ -463,15 +538,15 @@
 
         # Replace the published route on leader with '::/0'.
         leader.netdata_publish_replace(EXTERNAL_ROUTE, '::/0', EXTERNAL_FLAGS, 'med')
-        self.simulator.go(0.2)
+        self.simulator.go(1)
         routes = leader.get_routes()
         self.assertEqual([route.split(' ')[0] == '::/0' for route in routes].count(True), 1)
-        self.check_num_of_routes(routes, num - 1, 1, 0)
 
         # Replace it back to the original route.
         leader.netdata_publish_replace('::/0', EXTERNAL_ROUTE, EXTERNAL_FLAGS, 'high')
         self.simulator.go(WAIT_TIME)
         routes = leader.get_routes()
+        self.assertEqual([route.split(' ')[0] == '::/0' for route in routes].count(True), 0)
         self.check_num_of_routes(routes, num - 1, 0, 1)
 
         # Publish the same prefix on leader as an on-mesh prefix. Make
diff --git a/tests/scripts/thread-cert/test_srp_auto_host_address.py b/tests/scripts/thread-cert/test_srp_auto_host_address.py
index f714e6c..1612a30 100755
--- a/tests/scripts/thread-cert/test_srp_auto_host_address.py
+++ b/tests/scripts/thread-cert/test_srp_auto_host_address.py
@@ -86,10 +86,8 @@
         self.simulator.go(5)
 
         #-------------------------------------------------------------------
-        # Enable auto start mode on SRP client
+        # Check auto start mode on SRP client
 
-        self.assertEqual(client.srp_client_get_state(), 'Disabled')
-        client.srp_client_enable_auto_start_mode()
         self.assertEqual(client.srp_client_get_auto_start_mode(), 'Enabled')
         self.simulator.go(2)
 
@@ -169,16 +167,14 @@
         self.check_registered_addresses(client, server)
 
         #-------------------------------------------------------------------
-        # Remove the on-mesh prefix (which will trigger an address to be
-        # removed) and check that the SRP client re-registered and updated
-        # server with the remaining address.
+        # Remove the on-mesh prefix and check that the SRP client
+        # re-registered and updated server with the remaining address.
 
         client.remove_prefix('fd00:abba:cafe:bee::/64')
         client.register_netdata()
+
         self.simulator.go(5)
 
-        slaac_addr = [addr.strip() for addr in client.get_addrs() if addr.strip().startswith('fd00:abba:cafe:bee:')]
-        self.assertEqual(len(slaac_addr), 0)
         self.check_registered_addresses(client, server)
 
         #-------------------------------------------------------------------
@@ -187,10 +183,9 @@
 
         client.remove_prefix('fd00:9:8:7::/64')
         client.register_netdata()
+
         self.simulator.go(5)
 
-        slaac_addr = [addr.strip() for addr in client.get_addrs() if addr.strip().startswith('fd00:9:8:7:')]
-        self.assertEqual(len(slaac_addr), 0)
         self.check_registered_addresses(client, server)
 
         #-------------------------------------------------------------------
diff --git a/tests/scripts/thread-cert/test_srp_auto_start_mode.py b/tests/scripts/thread-cert/test_srp_auto_start_mode.py
index 8fbcdda..ee0cfab 100755
--- a/tests/scripts/thread-cert/test_srp_auto_start_mode.py
+++ b/tests/scripts/thread-cert/test_srp_auto_start_mode.py
@@ -107,10 +107,8 @@
         self.simulator.go(5)
 
         #-------------------------------------------------------------------
-        # Enable auto start mode on client and check that server1 is selected
+        # Check auto start mode on client and check that server1 is selected
 
-        self.assertEqual(client.srp_client_get_state(), 'Disabled')
-        client.srp_client_enable_auto_start_mode()
         self.assertEqual(client.srp_client_get_auto_start_mode(), 'Enabled')
         self.simulator.go(2)
 
diff --git a/tests/scripts/thread-cert/test_srp_client_change_lease.py b/tests/scripts/thread-cert/test_srp_client_change_lease.py
index 37b03d3..e55df64 100755
--- a/tests/scripts/thread-cert/test_srp_client_change_lease.py
+++ b/tests/scripts/thread-cert/test_srp_client_change_lease.py
@@ -89,7 +89,6 @@
         self.assertEqual(server.get_state(), 'leader')
         self.simulator.go(5)
 
-        client.srp_server_set_enabled(False)
         client.start()
         self.simulator.go(config.ROUTER_STARTUP_DELAY)
         self.assertEqual(client.get_state(), 'router')
@@ -98,9 +97,10 @@
         # 1. Register a single service and verify that it works.
         #
 
+        self.assertEqual(client.srp_client_get_auto_start_mode(), 'Enabled')
+
         client.srp_client_set_host_name('my-host')
         client.srp_client_set_host_address('2001::1')
-        client.srp_client_start(server.get_addrs()[0], client.get_srp_server_port())
         client.srp_client_add_service('my-service', '_ipps._tcp', 12345)
         self.simulator.go(2)
 
diff --git a/tests/scripts/thread-cert/test_srp_client_save_server_info.py b/tests/scripts/thread-cert/test_srp_client_save_server_info.py
index cb277e5..5018aad 100755
--- a/tests/scripts/thread-cert/test_srp_client_save_server_info.py
+++ b/tests/scripts/thread-cert/test_srp_client_save_server_info.py
@@ -105,8 +105,6 @@
         client.srp_client_set_host_address('2001::1')
         client.srp_client_add_service('my-service', '_ipps._tcp', 12345)
 
-        self.assertEqual(client.srp_client_get_state(), 'Disabled')
-        client.srp_client_enable_auto_start_mode()
         self.assertEqual(client.srp_client_get_auto_start_mode(), 'Enabled')
         self.simulator.go(WAIT_TIME)
 
diff --git a/tests/scripts/thread-cert/test_srp_lease.py b/tests/scripts/thread-cert/test_srp_lease.py
index 6d14711..b259c21 100755
--- a/tests/scripts/thread-cert/test_srp_lease.py
+++ b/tests/scripts/thread-cert/test_srp_lease.py
@@ -83,7 +83,6 @@
         self.assertEqual(server.get_state(), 'leader')
         self.simulator.go(5)
 
-        client.srp_server_set_enabled(False)
         client.start()
         self.simulator.go(config.ROUTER_STARTUP_DELAY)
         self.assertEqual(client.get_state(), 'router')
@@ -92,9 +91,10 @@
         # 1. Register a single service and verify that it works.
         #
 
+        self.assertEqual(client.srp_client_get_auto_start_mode(), 'Enabled')
+
         client.srp_client_set_host_name('my-host')
         client.srp_client_set_host_address('2001::1')
-        client.srp_client_start(server.get_addrs()[0], client.get_srp_server_port())
         client.srp_client_add_service('my-service', '_ipps._tcp', 12345)
         self.simulator.go(2)
 
@@ -113,7 +113,7 @@
         self.assertEqual(server.srp_server_get_service('my-service', '_ipps._tcp')['deleted'], 'true')
 
         # Start the client again, the same service should be successfully registered.
-        client.srp_client_start(server.get_addrs()[0], client.get_srp_server_port())
+        client.srp_client_enable_auto_start_mode()
         self.simulator.go(2)
 
         self.check_host_and_service(server, client)
@@ -131,7 +131,7 @@
         self.assertEqual(len(server.srp_server_get_services()), 0)
 
         # Start the client again, the same service should be successfully registered.
-        client.srp_client_start(server.get_addrs()[0], client.get_srp_server_port())
+        client.srp_client_enable_auto_start_mode()
         self.simulator.go(2)
 
         self.check_host_and_service(server, client)
diff --git a/tests/scripts/thread-cert/test_srp_name_conflicts.py b/tests/scripts/thread-cert/test_srp_name_conflicts.py
index 4451eab..984547f 100755
--- a/tests/scripts/thread-cert/test_srp_name_conflicts.py
+++ b/tests/scripts/thread-cert/test_srp_name_conflicts.py
@@ -101,9 +101,10 @@
         # 1. Register a single service and verify that it works.
         #
 
+        self.assertEqual(client_1.srp_client_get_auto_start_mode(), 'Enabled')
+
         client_1.srp_client_set_host_name('my-host-1')
         client_1.srp_client_set_host_address('2001::1')
-        client_1.srp_client_start(server.get_addrs()[0], client_1.get_srp_server_port())
         client_1.srp_client_add_service('my-service-1', '_ipps._tcp', 12345)
         self.simulator.go(2)
 
@@ -139,9 +140,10 @@
         # 2. Register with the same host name from the second client and it should fail.
         #
 
+        self.assertEqual(client_2.srp_client_get_auto_start_mode(), 'Enabled')
+
         client_2.srp_client_set_host_name('my-host-1')
         client_2.srp_client_set_host_address('2001::2')
-        client_2.srp_client_start(server.get_addrs()[0], client_2.get_srp_server_port())
         client_2.srp_client_add_service('my-service-2', '_ipps._tcp', 12345)
         self.simulator.go(2)
 
@@ -160,9 +162,9 @@
         # 3. Register with the same service name from the second client and it should fail.
         #
 
+        client_2.srp_client_enable_auto_start_mode()
         client_2.srp_client_set_host_name('my-host-2')
         client_2.srp_client_set_host_address('2001::2')
-        client_2.srp_client_start(server.get_addrs()[0], client_2.get_srp_server_port())
         client_2.srp_client_add_service('my-service-1', '_ipps._tcp', 12345)
         self.simulator.go(2)
 
@@ -182,9 +184,9 @@
         # from the second client and it should pass.
         #
 
+        client_2.srp_client_enable_auto_start_mode()
         client_2.srp_client_set_host_name('my-host-2')
         client_2.srp_client_set_host_address('2001::2')
-        client_2.srp_client_start(server.get_addrs()[0], client_2.get_srp_server_port())
         client_2.srp_client_add_service('my-service-1', '_ipps2._tcp', 12345)
         self.simulator.go(2)
 
@@ -209,9 +211,9 @@
         # 5. Register with different host & service instance name, it should succeed.
         #
 
+        client_2.srp_client_enable_auto_start_mode()
         client_2.srp_client_set_host_name('my-host-2')
         client_2.srp_client_set_host_address('2001::2')
-        client_2.srp_client_start(server.get_addrs()[0], client_2.get_srp_server_port())
         client_2.srp_client_add_service('my-service-2', '_ipps._tcp', 12345)
         self.simulator.go(2)
 
@@ -241,9 +243,9 @@
         client_1.srp_client_remove_service('my-service-1', '_ipps._tcp')
         self.simulator.go(2)
 
+        client_2.srp_client_enable_auto_start_mode()
         client_2.srp_client_set_host_name('my-host-2')
         client_2.srp_client_set_host_address('2001::2')
-        client_2.srp_client_start(server.get_addrs()[0], client_2.get_srp_server_port())
         client_2.srp_client_add_service('my-service-1', '_ipps._tcp', 12345)
         self.simulator.go(2)
 
@@ -284,10 +286,10 @@
 
         # Client 2 registers the same host & service instance name with Client 1.
         client_2.srp_client_stop()
+        client_2.srp_client_enable_auto_start_mode()
         client_2.srp_client_clear_host()
         client_2.srp_client_set_host_name('my-host-1')
         client_2.srp_client_set_host_address('2001::2')
-        client_2.srp_client_start(server.get_addrs()[0], client_2.get_srp_server_port())
         client_2.srp_client_add_service('my-service-1', '_ipps._tcp', 12345)
         self.simulator.go(2)
 
diff --git a/tests/scripts/thread-cert/test_srp_register_500_services.py b/tests/scripts/thread-cert/test_srp_register_500_services.py
index 54719dd..e69fc3e 100644
--- a/tests/scripts/thread-cert/test_srp_register_500_services.py
+++ b/tests/scripts/thread-cert/test_srp_register_500_services.py
@@ -132,13 +132,11 @@
         for fed in feds():
             self.assertEqual(fed.get_state(), 'child')
 
-        server_addr = server.get_rloc()
-
         for clientid in CLIENT_IDS:
             client = self.nodes[clientid]
+            self.assertEqual(client.srp_client_get_auto_start_mode(), 'Enabled')
             client.srp_client_set_host_name(f'client{clientid}')
             client.srp_client_set_host_address(f'2001::{clientid}')
-            client.srp_client_start(server_addr, client.get_srp_server_port())
 
             for instanceid in INSTANCE_IDS:
                 client.srp_client_add_service(f'client{clientid}_{instanceid}', SERVICE_NAME, SERVICE_PORT)
diff --git a/tests/scripts/thread-cert/test_srp_register_single_service.py b/tests/scripts/thread-cert/test_srp_register_single_service.py
index 621ad57..33d298b 100755
--- a/tests/scripts/thread-cert/test_srp_register_single_service.py
+++ b/tests/scripts/thread-cert/test_srp_register_single_service.py
@@ -89,9 +89,10 @@
         # 1. Register a single service and verify that it works.
         #
 
+        self.assertEqual(client.srp_client_get_auto_start_mode(), 'Enabled')
+
         client.srp_client_set_host_name('my-host')
         client.srp_client_set_host_address('2001::1')
-        client.srp_client_start(server.get_addrs()[0], client.get_srp_server_port())
         client.srp_client_add_service('my-service', '_ipps._tcp', 12345, 0, 0, ['abc', 'def=', 'xyz=XYZ'])
         self.simulator.go(2)
 
diff --git a/tests/scripts/thread-cert/test_srp_server_reboot_port.py b/tests/scripts/thread-cert/test_srp_server_reboot_port.py
index 411b598..b38bcfe 100755
--- a/tests/scripts/thread-cert/test_srp_server_reboot_port.py
+++ b/tests/scripts/thread-cert/test_srp_server_reboot_port.py
@@ -84,11 +84,9 @@
         self.assertEqual(server.get_state(), 'router')
 
         #
-        # 1. Enable auto start mode on client and check that server is used.
+        # 1. Check auto start mode on client and check that server is used.
         #
 
-        self.assertEqual(client.srp_client_get_state(), 'Disabled')
-        client.srp_client_enable_auto_start_mode()
         self.assertEqual(client.srp_client_get_auto_start_mode(), 'Enabled')
         self.simulator.go(2)
         self.assertEqual(client.srp_client_get_state(), 'Enabled')
@@ -96,14 +94,14 @@
 
         #
         # 2. Reboot the server without any service registered. The server should
-        # listen to the same port after the reboot.
+        # switch to a new port after re-enabling.
         #
         old_port = server.get_srp_server_port()
         server.srp_server_set_enabled(False)
         self.simulator.go(5)
         server.srp_server_set_enabled(True)
         self.simulator.go(5)
-        self.assertEqual(old_port, server.get_srp_server_port())
+        self.assertNotEqual(old_port, server.get_srp_server_port())
 
         #
         # 3. Register a service
diff --git a/tests/scripts/thread-cert/test_srp_ttl.py b/tests/scripts/thread-cert/test_srp_ttl.py
index 93cc818..9c3a149 100755
--- a/tests/scripts/thread-cert/test_srp_ttl.py
+++ b/tests/scripts/thread-cert/test_srp_ttl.py
@@ -85,9 +85,10 @@
         self.simulator.go(config.ROUTER_STARTUP_DELAY)
         self.assertEqual(client.get_state(), 'router')
 
+        self.assertEqual(client.srp_client_get_auto_start_mode(), 'Enabled')
+
         client.srp_client_set_host_name('my-host')
         client.srp_client_set_host_address('2001::1')
-        client.srp_client_start(server.get_addrs()[0], client.get_srp_server_port())
         client.srp_client_add_service('my-service', '_ipps._tcp', 12345)
         self.simulator.go(2)
 
diff --git a/tests/scripts/thread-cert/v1_2_router_5_1_1.py b/tests/scripts/thread-cert/v1_2_router_5_1_1.py
index 6f8b01c..146b2b5 100755
--- a/tests/scripts/thread-cert/v1_2_router_5_1_1.py
+++ b/tests/scripts/thread-cert/v1_2_router_5_1_1.py
@@ -79,7 +79,7 @@
         msg.assertMleMessageContainsTlv(mle.Challenge)
         msg.assertMleMessageContainsTlv(mle.ScanMask)
         msg.assertMleMessageContainsTlv(mle.Version)
-        assert msg.get_mle_message_tlv(mle.Version).version >= config.THREAD_VERSION_1_2
+        self.assertGreaterEqual(msg.get_mle_message_tlv(mle.Version).version, config.THREAD_VERSION_1_2)
 
         scan_mask_tlv = msg.get_mle_message_tlv(mle.ScanMask)
         self.assertEqual(1, scan_mask_tlv.router)
@@ -97,7 +97,7 @@
         msg.assertMleMessageContainsTlv(mle.LinkMargin)
         msg.assertMleMessageContainsTlv(mle.Connectivity)
         msg.assertMleMessageContainsTlv(mle.Version)
-        assert msg.get_mle_message_tlv(mle.Version).version >= config.THREAD_VERSION_1_2
+        self.assertGreaterEqual(msg.get_mle_message_tlv(mle.Version).version, config.THREAD_VERSION_1_2)
 
         # 4 - Router_1 receives the MLE Parent Response and sends a Child ID Request
         msg = router_messages.next_mle_message(mle.CommandType.CHILD_ID_REQUEST)
@@ -110,7 +110,7 @@
         msg.assertMleMessageContainsTlv(mle.Version)
         msg.assertMleMessageContainsTlv(mle.TlvRequest)
         msg.assertMleMessageDoesNotContainTlv(mle.AddressRegistration)
-        assert msg.get_mle_message_tlv(mle.Version).version >= config.THREAD_VERSION_1_2
+        self.assertGreaterEqual(msg.get_mle_message_tlv(mle.Version).version, config.THREAD_VERSION_1_2)
 
         # 5 - Leader responds with a Child ID Response
         msg = leader_messages.next_mle_message(mle.CommandType.CHILD_ID_RESPONSE)
diff --git a/tests/scripts/thread-cert/v1_2_test_backbone_router_service.py b/tests/scripts/thread-cert/v1_2_test_backbone_router_service.py
index 36d5264..888a6d9 100755
--- a/tests/scripts/thread-cert/v1_2_test_backbone_router_service.py
+++ b/tests/scripts/thread-cert/v1_2_test_backbone_router_service.py
@@ -128,7 +128,7 @@
         WAIT_TIME = BBR_REGISTRATION_JITTER + WAIT_REDUNDANCE
         self.simulator.go(WAIT_TIME)
         self.assertEqual(self.nodes[BBR_1].get_backbone_router_state(), 'Primary')
-        assert self.nodes[BBR_1].get_backbone_router()['seqno'] == 2
+        self.assertEqual(self.nodes[BBR_1].get_backbone_router()['seqno'], 2)
 
         # 3) Reset BBR_1 and bring it back after its original router id is released
         # 200s (100s MaxNeighborAge + 90s InfiniteCost + 10s redundance)
@@ -186,7 +186,7 @@
         # Check no SRV_DATA.ntf.
         messages = self.simulator.get_messages_sent_by(BBR_2)
         msg = messages.next_coap_message('0.02', '/a/sd', False)
-        assert (msg is None), "Error: %d sent unexpected SRV_DATA.ntf when there is PBbr already"
+        self.assertIsNone(msg)
 
         # Flush relative message queue.
         self.flush_nodes([BBR_1])
@@ -203,7 +203,7 @@
         messages.next_coap_message('0.02', '/a/sd', True)
         self.assertEqual(self.nodes[BBR_1].get_backbone_router_state(), 'Secondary')
         # Verify Sequence number increases when become Secondary from Primary.
-        assert self.nodes[BBR_1].get_backbone_router()['seqno'] == (BBR_1_SEQNO + 1)
+        self.assertEqual(self.nodes[BBR_1].get_backbone_router()['seqno'], BBR_1_SEQNO + 1)
 
         # 4a) Check communication via DUA.
         bbr2_dua = self.nodes[BBR_2].get_addr(config.DOMAIN_PREFIX)
@@ -238,7 +238,7 @@
 
         # 6a) Check the uniqueness of DUA by comparing the one in above 4a).
         bbr2_dua2 = self.nodes[BBR_2].get_addr(config.DOMAIN_PREFIX)
-        assert bbr2_dua == bbr2_dua2, 'Error: Unexpected different DUA ({} v.s. {})'.format(bbr2_dua, bbr2_dua2)
+        self.assertEqual(bbr2_dua, bbr2_dua2)
 
         # 6b) Check communication via DUA
         self.assertTrue(self.nodes[BBR_1].ping(bbr2_dua))
diff --git a/tests/scripts/thread-cert/v1_2_test_domain_unicast_address.py b/tests/scripts/thread-cert/v1_2_test_domain_unicast_address.py
index 7a98680..81c9054 100755
--- a/tests/scripts/thread-cert/v1_2_test_domain_unicast_address.py
+++ b/tests/scripts/thread-cert/v1_2_test_domain_unicast_address.py
@@ -242,10 +242,8 @@
         WAIT_TIME = WAIT_REDUNDANCE
         self.simulator.go(WAIT_TIME)
         dua = self.nodes[MED_1_2].get_addr(config.DOMAIN_PREFIX)
-        assert ipaddress.ip_address(dua) == ipaddress.ip_address(
-            med_1_2_dua), 'Error: Expected SLAAC DUA not generated'
-        assert ipaddress.ip_address(med_1_2_dua) == ipaddress.ip_address(
-            dua), 'Error: Expected same SLAAC DUA not generated'
+        self.assertEqual(ipaddress.ip_address(dua), ipaddress.ip_address(med_1_2_dua))
+        self.assertEqual(ipaddress.ip_address(med_1_2_dua), ipaddress.ip_address(dua))
 
         self.__check_dua_registration(MED_1_2, med_1_2_dua_iid, domain_prefix_cid)
 
@@ -269,8 +267,7 @@
         self.simulator.go(WAIT_TIME)
         dua = self.nodes[MED_1_2].get_addr(config.DOMAIN_PREFIX)
         assert dua, 'Error: Expected DUA not found'
-        assert ipaddress.ip_address(med_1_2_dua) == ipaddress.ip_address(
-            dua), 'Error: Expected same SLAAC DUA not generated'
+        self.assertEqual(ipaddress.ip_address(med_1_2_dua), ipaddress.ip_address(dua))
 
         self.__check_dua_registration(MED_1_2, med_1_2_dua_iid, domain_prefix_cid)
 
@@ -299,8 +296,7 @@
         self.simulator.go(WAIT_TIME)
         dua = self.nodes[MED_1_2].get_addr(config.DOMAIN_PREFIX)
         assert dua, 'Error: Expected DUA not found'
-        assert ipaddress.ip_address(med_1_2_dua) == ipaddress.ip_address(
-            dua), 'Error: Expected same SLAAC DUA not generated'
+        self.assertEqual(ipaddress.ip_address(med_1_2_dua), ipaddress.ip_address(dua))
 
         self.__check_dua_registration(MED_1_2, med_1_2_dua_iid, domain_prefix_cid)
 
diff --git a/tests/scripts/thread-cert/v1_2_test_domain_unicast_address_registration.py b/tests/scripts/thread-cert/v1_2_test_domain_unicast_address_registration.py
index 4053fec..76772ab 100755
--- a/tests/scripts/thread-cert/v1_2_test_domain_unicast_address_registration.py
+++ b/tests/scripts/thread-cert/v1_2_test_domain_unicast_address_registration.py
@@ -276,7 +276,7 @@
 
         dua2 = self.nodes[ROUTER_1_2].get_addr(config.DOMAIN_PREFIX)
         assert dua2, 'Error: Expected DUA ({}) not found'.format(dua2)
-        assert dua2 != dua, 'Error: Expected Different DUA not found, same DUA {}'.format(dua2)
+        self.assertNotEqual(dua2, dua)
 
         # e) (repeated) Configure BBR_1 to respond with per remaining error status:
         #   - increase BBR seqno to trigger reregistration
diff --git a/tests/scripts/thread-cert/v1_2_test_multicast_listener_registration.py b/tests/scripts/thread-cert/v1_2_test_multicast_listener_registration.py
index 1887536..ac974de 100755
--- a/tests/scripts/thread-cert/v1_2_test_multicast_listener_registration.py
+++ b/tests/scripts/thread-cert/v1_2_test_multicast_listener_registration.py
@@ -704,7 +704,7 @@
 
     def __check_renewing(self, id, parent_id, addr, expect_mlr_req=True, expect_mlr_req_proxied=False):
         """Check if MLR works that a node can renew it's registered MAs"""
-        assert self.pbbr_id == BBR_1
+        self.assertEqual(self.pbbr_id, BBR_1)
         self.flush_all()
         self.simulator.go(MLR_TIMEOUT + WAIT_REDUNDANCE)
 
@@ -748,7 +748,7 @@
     def __check_rereg_pbbr_change(self, id, parent_id, addr, expect_mlr_req=True, expect_mlr_req_proxied=False):
         """Check if MLR works that a node can do MLR reregistration when PBBR changes"""
         # Make BBR_2 to be Primary and expect MLR.req within REREG_DELAY
-        assert self.pbbr_id == BBR_1
+        self.assertEqual(self.pbbr_id, BBR_1)
 
         self.flush_all()
         self.nodes[BBR_1].disable_backbone_router()
diff --git a/tests/scripts/thread-cert/v1_2_test_parent_selection.py b/tests/scripts/thread-cert/v1_2_test_parent_selection.py
index 7b55b91..caf979f 100755
--- a/tests/scripts/thread-cert/v1_2_test_parent_selection.py
+++ b/tests/scripts/thread-cert/v1_2_test_parent_selection.py
@@ -139,8 +139,8 @@
         assert (parent_cmp), "Error: Expected parent response not found"
 
         # Known that link margin for link quality 3 is 80 and link quality 2 is 15
-        assert ((parent_prefer.get_mle_message_tlv(mle.LinkMargin).link_margin -
-                 parent_cmp.get_mle_message_tlv(mle.LinkMargin).link_margin) > 20)
+        self.assertGreater((parent_prefer.get_mle_message_tlv(mle.LinkMargin).link_margin -
+                            parent_cmp.get_mle_message_tlv(mle.LinkMargin).link_margin), 20)
 
         # Check Child Id Request
         messages = self.simulator.get_messages_sent_by(REED_1_2)
@@ -174,8 +174,9 @@
         parent_cmp = messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
         assert (parent_cmp), "Error: Expected parent response not found"
 
-        assert (parent_prefer.get_mle_message_tlv(mle.LinkMargin).link_margin == parent_cmp.get_mle_message_tlv(
-            mle.LinkMargin).link_margin)
+        self.assertEqual(
+            parent_prefer.get_mle_message_tlv(mle.LinkMargin).link_margin,
+            parent_cmp.get_mle_message_tlv(mle.LinkMargin).link_margin)
 
         # Check Child Id Request
         messages = self.simulator.get_messages_sent_by(ROUTER_1_2)
@@ -204,11 +205,13 @@
         parent_cmp = messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
         assert (parent_cmp), "Error: Expected parent response not found"
 
-        assert (parent_prefer.get_mle_message_tlv(mle.LinkMargin).link_margin == parent_cmp.get_mle_message_tlv(
-            mle.LinkMargin).link_margin)
+        self.assertEqual(
+            parent_prefer.get_mle_message_tlv(mle.LinkMargin).link_margin,
+            parent_cmp.get_mle_message_tlv(mle.LinkMargin).link_margin)
 
-        assert (parent_prefer.get_mle_message_tlv(mle.Connectivity).pp > parent_cmp.get_mle_message_tlv(
-            mle.Connectivity).pp)
+        self.assertGreater(
+            parent_prefer.get_mle_message_tlv(mle.Connectivity).pp,
+            parent_cmp.get_mle_message_tlv(mle.Connectivity).pp)
 
         # Check Child Id Request
         messages = self.simulator.get_messages_sent_by(REED_1_1)
@@ -239,12 +242,15 @@
         parent_cmp = messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
         assert (parent_cmp), "Error: Expected parent response not found"
 
-        assert (parent_prefer.get_mle_message_tlv(mle.LinkMargin).link_margin == parent_cmp.get_mle_message_tlv(
-            mle.LinkMargin).link_margin)
-        assert (parent_prefer.get_mle_message_tlv(mle.Connectivity).pp == parent_cmp.get_mle_message_tlv(
-            mle.Connectivity).pp)
-        assert (parent_prefer.get_mle_message_tlv(mle.Connectivity).link_quality_3 > parent_cmp.get_mle_message_tlv(
-            mle.Connectivity).link_quality_3)
+        self.assertEqual(
+            parent_prefer.get_mle_message_tlv(mle.LinkMargin).link_margin,
+            parent_cmp.get_mle_message_tlv(mle.LinkMargin).link_margin)
+        self.assertEqual(
+            parent_prefer.get_mle_message_tlv(mle.Connectivity).pp,
+            parent_cmp.get_mle_message_tlv(mle.Connectivity).pp)
+        self.assertGreater(
+            parent_prefer.get_mle_message_tlv(mle.Connectivity).link_quality_3,
+            parent_cmp.get_mle_message_tlv(mle.Connectivity).link_quality_3)
 
         # Check Child Id Request
         messages = self.simulator.get_messages_sent_by(MED_1_1)
@@ -272,14 +278,18 @@
         parent_cmp = messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
         assert (parent_cmp), "Error: Expected parent response not found"
 
-        assert (parent_prefer.get_mle_message_tlv(mle.LinkMargin).link_margin == parent_cmp.get_mle_message_tlv(
-            mle.LinkMargin).link_margin)
-        assert (parent_prefer.get_mle_message_tlv(mle.Connectivity).pp == parent_cmp.get_mle_message_tlv(
-            mle.Connectivity).pp)
-        assert (parent_prefer.get_mle_message_tlv(mle.Connectivity).link_quality_3 == parent_cmp.get_mle_message_tlv(
-            mle.Connectivity).link_quality_3)
-        assert (parent_prefer.get_mle_message_tlv(mle.Version).version > parent_cmp.get_mle_message_tlv(
-            mle.Version).version)
+        self.assertEqual(
+            parent_prefer.get_mle_message_tlv(mle.LinkMargin).link_margin,
+            parent_cmp.get_mle_message_tlv(mle.LinkMargin).link_margin)
+        self.assertEqual(
+            parent_prefer.get_mle_message_tlv(mle.Connectivity).pp,
+            parent_cmp.get_mle_message_tlv(mle.Connectivity).pp)
+        self.assertEqual(
+            parent_prefer.get_mle_message_tlv(mle.Connectivity).link_quality_3,
+            parent_cmp.get_mle_message_tlv(mle.Connectivity).link_quality_3)
+        self.assertGreater(
+            parent_prefer.get_mle_message_tlv(mle.Version).version,
+            parent_cmp.get_mle_message_tlv(mle.Version).version)
 
         # Check Child Id Request
         messages = self.simulator.get_messages_sent_by(MED_1_2)
diff --git a/tests/toranj/build.sh b/tests/toranj/build.sh
index 0c9a698..4b91cd4 100755
--- a/tests/toranj/build.sh
+++ b/tests/toranj/build.sh
@@ -113,8 +113,9 @@
         echo "==================================================================================================="
         cd "${top_builddir}" || die "cd failed"
         cmake -GNinja -DOT_PLATFORM=simulation -DOT_COMPILE_WARNING_AS_ERROR=ON -DOT_COVERAGE=${ot_coverage} \
-            -DOT_THREAD_VERSION=1.3.1 -DOT_APP_CLI=OFF -DOT_APP_NCP=ON -DOT_APP_RCP=OFF \
+            -DOT_THREAD_VERSION=1.4 -DOT_APP_CLI=OFF -DOT_APP_NCP=ON -DOT_APP_RCP=OFF \
             -DOT_OPERATIONAL_DATASET_AUTO_INIT=ON -DOT_PLATFORM_KEY_REF=${ot_plat_key_ref} \
+            -DOT_BORDER_ROUTING=OFF \
             -DOT_PROJECT_CONFIG=../tests/toranj/openthread-core-toranj-config-simulation.h \
             "${top_srcdir}" || die
         ninja || die
@@ -126,8 +127,9 @@
         echo "==================================================================================================="
         cd "${top_builddir}" || die "cd failed"
         cmake -GNinja -DOT_PLATFORM=simulation -DOT_COMPILE_WARNING_AS_ERROR=ON -DOT_COVERAGE=${ot_coverage} \
-            -DOT_THREAD_VERSION=1.3.1 -DOT_APP_CLI=OFF -DOT_APP_NCP=ON -DOT_APP_RCP=OFF \
+            -DOT_THREAD_VERSION=1.4 -DOT_APP_CLI=OFF -DOT_APP_NCP=ON -DOT_APP_RCP=OFF \
             -DOT_15_4=ON -DOT_TREL=OFF -DOT_OPERATIONAL_DATASET_AUTO_INIT=ON \
+            -DOT_BORDER_ROUTING=OFF \
             -DOT_PLATFORM_KEY_REF=${ot_plat_key_ref} \
             -DOT_PROJECT_CONFIG=../tests/toranj/openthread-core-toranj-config-simulation.h \
             "${top_srcdir}" || die
@@ -141,8 +143,9 @@
         echo "==================================================================================================="
         cd "${top_builddir}" || die "cd failed"
         cmake -GNinja -DOT_PLATFORM=simulation -DOT_COMPILE_WARNING_AS_ERROR=ON -DOT_COVERAGE=${ot_coverage} \
-            -DOT_THREAD_VERSION=1.3.1 -DOT_APP_CLI=OFF -DOT_APP_NCP=ON -DOT_APP_RCP=OFF \
+            -DOT_THREAD_VERSION=1.4 -DOT_APP_CLI=OFF -DOT_APP_NCP=ON -DOT_APP_RCP=OFF \
             -DOT_15_4=OFF -DOT_TREL=ON -DOT_OPERATIONAL_DATASET_AUTO_INIT=ON \
+            -DOT_BORDER_ROUTING=OFF \
             -DOT_PLATFORM_KEY_REF=${ot_plat_key_ref} \
             -DOT_PROJECT_CONFIG=../tests/toranj/openthread-core-toranj-config-simulation.h \
             "${top_srcdir}" || die
@@ -156,8 +159,9 @@
         echo "==================================================================================================="
         cd "${top_builddir}" || die "cd failed"
         cmake -GNinja -DOT_PLATFORM=simulation -DOT_COMPILE_WARNING_AS_ERROR=ON -DOT_COVERAGE=${ot_coverage} \
-            -DOT_THREAD_VERSION=1.3.1 -DOT_APP_CLI=OFF -DOT_APP_NCP=ON -DOT_APP_RCP=OFF \
+            -DOT_THREAD_VERSION=1.4 -DOT_APP_CLI=OFF -DOT_APP_NCP=ON -DOT_APP_RCP=OFF \
             -DOT_15_4=ON -DOT_TREL=ON -DOT_OPERATIONAL_DATASET_AUTO_INIT=ON \
+            -DOT_BORDER_ROUTING=OFF \
             -DOT_PLATFORM_KEY_REF=${ot_plat_key_ref} \
             -DOT_PROJECT_CONFIG=../tests/toranj/openthread-core-toranj-config-simulation.h \
             "${top_srcdir}" || die
@@ -171,7 +175,7 @@
         echo "==================================================================================================="
         cd "${top_builddir}" || die "cd failed"
         cmake -GNinja -DOT_PLATFORM=simulation -DOT_COMPILE_WARNING_AS_ERROR=ON -DOT_COVERAGE=${ot_coverage} \
-            -DOT_THREAD_VERSION=1.3.1 -DOT_APP_CLI=ON -DOT_APP_NCP=OFF -DOT_APP_RCP=OFF \
+            -DOT_THREAD_VERSION=1.4 -DOT_APP_CLI=ON -DOT_APP_NCP=OFF -DOT_APP_RCP=OFF \
             -DOT_PLATFORM_KEY_REF=${ot_plat_key_ref} \
             -DOT_PROJECT_CONFIG=../tests/toranj/openthread-core-toranj-config-simulation.h \
             "${top_srcdir}" || die
@@ -184,7 +188,7 @@
         echo "==================================================================================================="
         cd "${top_builddir}" || die "cd failed"
         cmake -GNinja -DOT_PLATFORM=simulation -DOT_COMPILE_WARNING_AS_ERROR=ON -DOT_COVERAGE=${ot_coverage} \
-            -DOT_THREAD_VERSION=1.3.1 -DOT_APP_CLI=ON -DOT_APP_NCP=OFF -DOT_APP_RCP=OFF \
+            -DOT_THREAD_VERSION=1.4 -DOT_APP_CLI=ON -DOT_APP_NCP=OFF -DOT_APP_RCP=OFF \
             -DOT_15_4=ON -DOT_TREL=OFF \
             -DOT_PLATFORM_KEY_REF=${ot_plat_key_ref} \
             -DOT_PROJECT_CONFIG=../tests/toranj/openthread-core-toranj-config-simulation.h \
@@ -199,7 +203,7 @@
         echo "==================================================================================================="
         cd "${top_builddir}" || die "cd failed"
         cmake -GNinja -DOT_PLATFORM=simulation -DOT_COMPILE_WARNING_AS_ERROR=ON -DOT_COVERAGE=${ot_coverage} \
-            -DOT_THREAD_VERSION=1.3.1 -DOT_APP_CLI=ON -DOT_APP_NCP=OFF -DOT_APP_RCP=OFF \
+            -DOT_THREAD_VERSION=1.4 -DOT_APP_CLI=ON -DOT_APP_NCP=OFF -DOT_APP_RCP=OFF \
             -DOT_15_4=OFF -DOT_TREL=ON \
             -DOT_PLATFORM_KEY_REF=${ot_plat_key_ref} \
             -DOT_PROJECT_CONFIG=../tests/toranj/openthread-core-toranj-config-simulation.h \
@@ -214,7 +218,7 @@
         echo "==================================================================================================="
         cd "${top_builddir}" || die "cd failed"
         cmake -GNinja -DOT_PLATFORM=simulation -DOT_COMPILE_WARNING_AS_ERROR=ON -DOT_COVERAGE=${ot_coverage} \
-            -DOT_THREAD_VERSION=1.3.1 -DOT_APP_CLI=ON -DOT_APP_NCP=OFF -DOT_APP_RCP=OFF \
+            -DOT_THREAD_VERSION=1.4 -DOT_APP_CLI=ON -DOT_APP_NCP=OFF -DOT_APP_RCP=OFF \
             -DOT_15_4=ON -DOT_TREL=ON \
             -DOT_PLATFORM_KEY_REF=${ot_plat_key_ref} \
             -DOT_PROJECT_CONFIG=../tests/toranj/openthread-core-toranj-config-simulation.h \
@@ -229,7 +233,7 @@
         echo "===================================================================================================="
         cd "${top_builddir}" || die "cd failed"
         cmake -GNinja -DOT_PLATFORM=simulation -DOT_COMPILE_WARNING_AS_ERROR=ON -DOT_COVERAGE=${ot_coverage} \
-            -DOT_THREAD_VERSION=1.3.1 -DOT_APP_CLI=OFF -DOT_APP_NCP=OFF -DOT_APP_RCP=ON \
+            -DOT_THREAD_VERSION=1.4 -DOT_APP_CLI=OFF -DOT_APP_NCP=OFF -DOT_APP_RCP=ON \
             -DOT_PLATFORM_KEY_REF=${ot_plat_key_ref} \
             -DOT_PROJECT_CONFIG=../tests/toranj/openthread-core-toranj-config-simulation.h \
             "${top_srcdir}" || die
@@ -242,7 +246,7 @@
         echo "===================================================================================================="
         cd "${top_builddir}" || die "cd failed"
         cmake -GNinja -DOT_PLATFORM=posix -DOT_COMPILE_WARNING_AS_ERROR=ON -DOT_COVERAGE=${ot_coverage} \
-            -DOT_THREAD_VERSION=1.3.1 -DOT_APP_CLI=ON -DOT_APP_NCP=ON -DOT_APP_RCP=OFF \
+            -DOT_THREAD_VERSION=1.4 -DOT_APP_CLI=ON -DOT_APP_NCP=ON -DOT_APP_RCP=OFF \
             -DOT_PLATFORM_KEY_REF=${ot_plat_key_ref} \
             -DOT_PROJECT_CONFIG=../tests/toranj/openthread-core-toranj-config-posix.h \
             "${top_srcdir}" || die
@@ -255,7 +259,7 @@
         echo "===================================================================================================="
         cd "${top_builddir}" || die "cd failed"
         cmake -GNinja -DOT_PLATFORM=posix -DOT_COMPILE_WARNING_AS_ERROR=ON -DOT_COVERAGE=${ot_coverage} \
-            -DOT_THREAD_VERSION=1.3.1 -DOT_APP_CLI=ON -DOT_APP_NCP=ON -DOT_APP_RCP=OFF \
+            -DOT_THREAD_VERSION=1.4 -DOT_APP_CLI=ON -DOT_APP_NCP=ON -DOT_APP_RCP=OFF \
             -DOT_15_4=ON -DOT_TREL=OFF \
             -DOT_PLATFORM_KEY_REF=${ot_plat_key_ref} \
             -DOT_PROJECT_CONFIG=../tests/toranj/openthread-core-toranj-config-posix.h \
@@ -269,7 +273,7 @@
         echo "===================================================================================================="
         cd "${top_builddir}" || die "cd failed"
         cmake -GNinja -DOT_PLATFORM=posix -DOT_COMPILE_WARNING_AS_ERROR=ON -DOT_COVERAGE=${ot_coverage} \
-            -DOT_THREAD_VERSION=1.3.1 -DOT_APP_CLI=ON -DOT_APP_NCP=ON -DOT_APP_RCP=OFF \
+            -DOT_THREAD_VERSION=1.4 -DOT_APP_CLI=ON -DOT_APP_NCP=ON -DOT_APP_RCP=OFF \
             -DOT_15_4=OFF -DOT_TREL=ON \
             -DOT_PLATFORM_KEY_REF=${ot_plat_key_ref} \
             -DOT_PROJECT_CONFIG=../tests/toranj/openthread-core-toranj-config-posix.h \
@@ -283,7 +287,7 @@
         echo "===================================================================================================="
         cd "${top_builddir}" || die "cd failed"
         cmake -GNinja -DOT_PLATFORM=posix -DOT_COMPILE_WARNING_AS_ERROR=ON -DOT_COVERAGE=${ot_coverage} \
-            -DOT_THREAD_VERSION=1.3.1 -DOT_APP_CLI=ON -DOT_APP_NCP=ON -DOT_APP_RCP=OFF \
+            -DOT_THREAD_VERSION=1.4 -DOT_APP_CLI=ON -DOT_APP_NCP=ON -DOT_APP_RCP=OFF \
             -DOT_15_4=ON -DOT_TREL=ON \
             -DOT_PLATFORM_KEY_REF=${ot_plat_key_ref} \
             -DOT_PROJECT_CONFIG=../tests/toranj/openthread-core-toranj-config-posix.h \
@@ -297,7 +301,7 @@
         echo "===================================================================================================="
         cd "${top_builddir}" || die "cd failed"
         cmake -GNinja -DOT_PLATFORM=simulation -DOT_COMPILE_WARNING_AS_ERROR=ON -DOT_COVERAGE=${ot_coverage} \
-            -DOT_THREAD_VERSION=1.3.1 -DOT_APP_CLI=ON -DOT_APP_NCP=ON -DOT_APP_RCP=ON \
+            -DOT_THREAD_VERSION=1.4 -DOT_APP_CLI=ON -DOT_APP_NCP=ON -DOT_APP_RCP=ON \
             -DOT_PLATFORM_KEY_REF=${ot_plat_key_ref} \
             -DOT_PROJECT_CONFIG=../tests/toranj/openthread-core-toranj-config-simulation.h \
             "${top_srcdir}" || die
diff --git a/tests/toranj/cli/cli.py b/tests/toranj/cli/cli.py
index e5533ad..8f278f4 100644
--- a/tests/toranj/cli/cli.py
+++ b/tests/toranj/cli/cli.py
@@ -223,6 +223,17 @@
     def set_channel(self, channel):
         self._cli_no_output('channel', channel)
 
+    def get_csl_config(self):
+        outputs = self.cli('csl')
+        result = {}
+        for line in outputs:
+            fields = line.split(':')
+            result[fields[0].strip()] = fields[1].strip()
+        return result
+
+    def set_csl_period(self, period):
+        self._cli_no_output('csl period', period)
+
     def get_ext_addr(self):
         return self._cli_single_output('extaddr')
 
@@ -301,8 +312,8 @@
     def get_rloc16(self):
         return self._cli_single_output('rloc16')
 
-    def get_ip_addrs(self):
-        return self.cli('ipaddr')
+    def get_ip_addrs(self, verbose=None):
+        return self.cli('ipaddr', verbose)
 
     def add_ip_addr(self, address):
         self._cli_no_output('ipaddr add', address)
@@ -378,22 +389,32 @@
     def set_vendor_sw_version(self, version):
         return self._cli_no_output('vendor swversion', version)
 
+    def get_vendor_app_url(self):
+        return self._cli_single_output('vendor appurl')
+
+    def set_vendor_app_url(self, url):
+        return self._cli_no_output('vendor appurl', url)
+
     #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
     # netdata
 
-    def get_netdata(self):
-        outputs = self.cli('netdata show')
+    def get_netdata(self, rloc16=None):
+        outputs = self.cli('netdata show', rloc16)
         outputs = [line.strip() for line in outputs]
         routes_index = outputs.index('Routes:')
         services_index = outputs.index('Services:')
-        contexts_index = outputs.index('Contexts:')
-        commissioning_index = outputs.index('Commissioning:')
+        if rloc16 is None:
+            contexts_index = outputs.index('Contexts:')
+            commissioning_index = outputs.index('Commissioning:')
         result = {}
         result['prefixes'] = outputs[1:routes_index]
         result['routes'] = outputs[routes_index + 1:services_index]
-        result['services'] = outputs[services_index + 1:contexts_index]
-        result['contexts'] = outputs[contexts_index + 1:commissioning_index]
-        result['commissioning'] = outputs[commissioning_index + 1:]
+        if rloc16 is None:
+            result['services'] = outputs[services_index + 1:contexts_index]
+            result['contexts'] = outputs[contexts_index + 1:commissioning_index]
+            result['commissioning'] = outputs[commissioning_index + 1:]
+        else:
+            result['services'] = outputs[services_index + 1:]
 
         return result
 
@@ -466,6 +487,24 @@
         return self._cli_single_output('mleadvimax')
 
     #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+    # Border Agent
+
+    def ba_get_state(self):
+        return self._cli_single_output('ba state')
+
+    def ba_get_port(self):
+        return self._cli_single_output('ba port')
+
+    def ba_is_ephemeral_key_active(self):
+        return self._cli_single_output('ba ephemeralkey')
+
+    def ba_set_ephemeral_key(self, keystring, timeout=None, port=None):
+        self._cli_no_output('ba ephemeralkey set', keystring, timeout, port)
+
+    def ba_clear_ephemeral_key(self):
+        self._cli_no_output('ba ephemeralkey clear')
+
+    #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
     # UDP
 
     def udp_open(self):
@@ -618,6 +657,12 @@
     def srp_server_disable(self):
         self._cli_no_output('srp server disable')
 
+    def srp_server_auto_enable(self):
+        self._cli_no_output('srp server auto enable')
+
+    def srp_server_auto_disable(self):
+        self._cli_no_output('srp server auto disable')
+
     def srp_server_set_lease(self, min_lease, max_lease, min_key_lease, max_key_lease):
         self._cli_no_output('srp server lease', min_lease, max_lease, min_key_lease, max_key_lease)
 
@@ -736,6 +781,18 @@
     def br_get_state(self):
         return self._cli_single_output('br state')
 
+    def br_get_favored_omrprefix(self):
+        return self._cli_single_output('br omrprefix favored')
+
+    def br_get_local_omrprefix(self):
+        return self._cli_single_output('br omrprefix local')
+
+    def br_get_favored_onlinkprefix(self):
+        return self._cli_single_output('br onlinkprefix favored')
+
+    def br_get_local_onlinkprefix(self):
+        return self._cli_single_output('br onlinkprefix local')
+
     def br_get_routeprf(self):
         return self._cli_single_output('br routeprf')
 
@@ -745,6 +802,9 @@
     def br_clear_routeprf(self):
         self._cli_no_output('br routeprf clear')
 
+    def br_get_routers(self):
+        return self.cli('br routers')
+
     # ------------------------------------------------------------------------------------------------------------------
     # Helper methods
 
@@ -926,7 +986,8 @@
         except VerifyError as e:
             if time.time() - start_time > wait_time:
                 print('Took too long to pass the condition ({}>{} sec)'.format(time.time() - start_time, wait_time))
-                print(e.message)
+                if hasattr(e, 'message'):
+                    print(e.message)
                 raise e
         except BaseException:
             raise
diff --git a/tests/toranj/cli/test-011-network-data-timeout.py b/tests/toranj/cli/test-011-network-data-timeout.py
index a3a4086..bf52487 100755
--- a/tests/toranj/cli/test-011-network-data-timeout.py
+++ b/tests/toranj/cli/test-011-network-data-timeout.py
@@ -89,11 +89,6 @@
 # -----------------------------------------------------------------------------------------------------------------------
 # Test Implementation
 
-common_prefix = 'fd00:cafe::'
-prefix1 = 'fd00:1::'
-prefix2 = 'fd00:2::'
-prefix3 = 'fd00:3::'
-
 # Each node adds its own prefix.
 r1.add_prefix('fd00:1::/64', 'paros', 'med')
 r2.add_prefix('fd00:2::/64', 'paros', 'med')
@@ -104,6 +99,9 @@
 r2.add_prefix('fd00:abba::/64', 'paros', 'med')
 c2.add_prefix('fd00:abba::/64', 'paros', 'low')
 
+r1.add_route('fd00:cafe::/64', 's', 'med')
+r2.add_route('fd00:cafe::/64', 's', 'med')
+
 r1.register_netdata()
 r2.register_netdata()
 c2.register_netdata()
@@ -112,12 +110,19 @@
 def check_netdata_on_all_nodes():
     for node in nodes:
         netdata = node.get_netdata()
-        prefixes = netdata['prefixes']
-        verify(len(prefixes) == 6)
+        verify(len(netdata['prefixes']) == 6)
+        verify(len(netdata['routes']) == 2)
 
 
 verify_within(check_netdata_on_all_nodes, 10)
 
+# Check netdata filtering for r1 entries only.
+
+r1_rloc = int(r1.get_rloc16(), 16)
+netdata = r1.get_netdata(r1_rloc)
+verify(len(netdata['prefixes']) == 2)
+verify(len(netdata['routes']) == 1)
+
 # Remove `r2`. This should trigger all the prefixes added by it or its
 # child to timeout and be removed.
 
@@ -127,8 +132,11 @@
 
 def check_netdata_on_r1():
     netdata = r1.get_netdata()
-    prefixes = netdata['prefixes']
-    verify(len(prefixes) == 2)
+    verify(len(netdata['prefixes']) == 2)
+    verify(len(netdata['routes']) == 1)
+    netdata = r1.get_netdata(r1_rloc)
+    verify(len(netdata['prefixes']) == 2)
+    verify(len(netdata['routes']) == 1)
 
 
 verify_within(check_netdata_on_r1, 120)
diff --git a/tests/toranj/cli/test-020-net-diag-vendor-info.py b/tests/toranj/cli/test-020-net-diag-vendor-info.py
index 6f205cd..463854a 100755
--- a/tests/toranj/cli/test-020-net-diag-vendor-info.py
+++ b/tests/toranj/cli/test-020-net-diag-vendor-info.py
@@ -68,17 +68,21 @@
 VENDOR_MODEL_TLV = 26
 VENDOR_SW_VERSION_TLV = 27
 THREAD_STACK_VERSION_TLV = 28
+MLE_COUNTERS_TLV = 34
+VENDOR_APP_URL = 35
 
 #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 # Check setting vendor name, model, ans sw version
 
 r1.set_vendor_name('nest')
 r1.set_vendor_model('marble')
-r1.set_vendor_sw_version('ot-1.3.1')
+r1.set_vendor_sw_version('ot-1.4')
+r1.set_vendor_app_url('https://example.com/vendor-app')
 
 verify(r1.get_vendor_name() == 'nest')
 verify(r1.get_vendor_model() == 'marble')
-verify(r1.get_vendor_sw_version() == 'ot-1.3.1')
+verify(r1.get_vendor_sw_version() == 'ot-1.4')
+verify(r1.get_vendor_app_url() == 'https://example.com/vendor-app')
 
 #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 # Check invalid names (too long)
@@ -125,6 +129,8 @@
 
 verify(errored)
 
+r2.set_vendor_app_url("https://example.com/vendor-app")
+
 #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 # Perform net diag query
 
@@ -152,6 +158,13 @@
 verify(result[1].startswith("Vendor SW Version:"))
 verify(result[1].split(':')[1].strip() == r1.get_vendor_sw_version())
 
+# Get vendor app URL (TLV 35)
+
+result = r2.cli('networkdiagnostic get', r1_rloc, VENDOR_APP_URL)
+verify(len(result) == 2)
+verify(result[1].startswith("Vendor App URL:"))
+verify(result[1].split(':', 1)[1].strip() == r1.get_vendor_app_url())
+
 # Get thread stack version (TLV 30)
 
 result = r2.cli('networkdiagnostic get', r1_rloc, THREAD_STACK_VERSION_TLV)
@@ -162,8 +175,8 @@
 # Get all three TLVs (now from `r1`)
 
 result = r1.cli('networkdiagnostic get', r2_rloc, VENDOR_NAME_TLV, VENDOR_MODEL_TLV, VENDOR_SW_VERSION_TLV,
-                THREAD_STACK_VERSION_TLV)
-verify(len(result) == 5)
+                THREAD_STACK_VERSION_TLV, VENDOR_APP_URL)
+verify(len(result) == 6)
 for line in result[1:]:
     if line.startswith("Vendor Name:"):
         verify(line.split(':')[1].strip() == r2.get_vendor_name())
@@ -171,11 +184,17 @@
         verify(line.split(':')[1].strip() == r2.get_vendor_model())
     elif line.startswith("Vendor SW Version:"):
         verify(line.split(':')[1].strip() == r2.get_vendor_sw_version())
+    elif line.startswith("Vendor App URL:"):
+        verify(line.split(':', 1)[1].strip() == r2.get_vendor_app_url())
     elif line.startswith("Thread Stack Version:"):
         verify(r2.get_version().startswith(line.split(':', 1)[1].strip()))
     else:
         verify(False)
 
+result = r2.cli('networkdiagnostic get', r1_rloc, MLE_COUNTERS_TLV)
+print(len(result) >= 1)
+verify(result[1].startswith("MLE Counters:"))
+
 # -----------------------------------------------------------------------------------------------------------------------
 # Test finished
 
diff --git a/tests/toranj/cli/test-026-coaps-conn-limit.py b/tests/toranj/cli/test-026-coaps-conn-limit.py
new file mode 100755
index 0000000..6684dbe
--- /dev/null
+++ b/tests/toranj/cli/test-026-coaps-conn-limit.py
@@ -0,0 +1,104 @@
+#!/usr/bin/env python3
+#
+#  Copyright (c) 2024, The OpenThread Authors.
+#  All rights reserved.
+#
+#  Redistribution and use in source and binary forms, with or without
+#  modification, are permitted provided that the following conditions are met:
+#  1. Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#  2. Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#  3. Neither the name of the copyright holder nor the
+#     names of its contributors may be used to endorse or promote products
+#     derived from this software without specific prior written permission.
+#
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+#  POSSIBILITY OF SUCH DAMAGE.
+
+from cli import verify
+from cli import verify_within
+import cli
+import time
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Test description:
+#
+# Validate CoaP secure agent related to the maximum number of allowed connection attempts before the socket is a
+# automatically closed and the agent is stopped.
+#
+#
+# Network topology
+#
+#    r1 ---- r2
+#
+
+test_name = __file__[:-3] if __file__.endswith('.py') else __file__
+print('-' * 120)
+print('Starting \'{}\''.format(test_name))
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Creating `cli.Node` instances
+
+speedup = 40
+cli.Node.set_time_speedup_factor(speedup)
+
+r1 = cli.Node()
+r2 = cli.Node()
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Form topology
+
+r1.form('coaps-conn-lmt')
+r2.join(r1)
+
+verify(r1.get_state() == 'leader')
+verify(r2.get_state() == 'router')
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Test Implementation
+
+r1.cli('coaps psk RIGHT 1234')
+
+# Set max connection attempts to 3
+r1.cli('coaps start 3')
+
+r1_mleid = r1.get_mleid_ip_addr()
+
+r2.cli('coaps psk WRONG 1234')
+r2.cli('coaps start')
+
+# First connection attempt
+r2.cli('coaps connect', r1_mleid)
+time.sleep(0.1)
+verify(r1.cli('coaps isconnactive')[-1] == 'no')
+verify(r1.cli('coaps isclosed')[-1] == 'no')
+
+# Second connection attempt
+r2.cli('coaps connect', r1_mleid)
+time.sleep(0.1)
+verify(r1.cli('coaps isconnactive')[-1] == 'no')
+verify(r1.cli('coaps isclosed')[-1] == 'no')
+
+# Third connection attempt
+r2.cli('coaps connect', r1_mleid)
+time.sleep(0.1)
+verify(r1.cli('coaps isconnactive')[-1] == 'no')
+verify(r1.cli('coaps isclosed')[-1] == 'yes')
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Test finished
+
+cli.Node.finalize_all_nodes()
+
+print('\'{}\' passed.'.format(test_name))
diff --git a/tests/toranj/cli/test-027-slaac-address.py b/tests/toranj/cli/test-027-slaac-address.py
new file mode 100755
index 0000000..7d42623
--- /dev/null
+++ b/tests/toranj/cli/test-027-slaac-address.py
@@ -0,0 +1,381 @@
+#!/usr/bin/env python3
+#
+#  Copyright (c) 2023, The OpenThread Authors.
+#  All rights reserved.
+#
+#  Redistribution and use in source and binary forms, with or without
+#  modification, are permitted provided that the following conditions are met:
+#  1. Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#  2. Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#  3. Neither the name of the copyright holder nor the
+#     names of its contributors may be used to endorse or promote products
+#     derived from this software without specific prior written permission.
+#
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+#  POSSIBILITY OF SUCH DAMAGE.
+
+from cli import verify
+from cli import verify_within
+import cli
+import time
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Test description: Validate SLAAC module, addition/deprecation/removal of SLAAC addresses.
+#
+# Network topology
+#
+#    r1 ---- r2
+#
+
+test_name = __file__[:-3] if __file__.endswith('.py') else __file__
+print('-' * 120)
+print('Starting \'{}\''.format(test_name))
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Creating `cli.Node` instances
+
+speedup = 40
+cli.Node.set_time_speedup_factor(speedup)
+
+r1 = cli.Node()
+r2 = cli.Node()
+c2 = cli.Node()
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  - - - - - - - - - - - - - - - - - - - - - - -
+
+
+def verify_slaac_address_for_prefix(prefix, preferred, nodes=[r1, r2], origin='slaac'):
+    # Verify that both nodes have SLAAC address based on `prefix`.
+    for node in nodes:
+        ip_addrs = node.get_ip_addrs('-v')
+        matched_addrs = [addr for addr in ip_addrs if addr.startswith(prefix)]
+        verify(len(matched_addrs) == 1)
+        addr = matched_addrs[0]
+        verify('origin:' + origin in addr)
+        verify('plen:64' in addr)
+        verify('valid:1' in addr)
+        if (preferred):
+            verify('preferred:1' in addr)
+        else:
+            verify('preferred:0' in addr)
+
+
+def verify_no_slaac_address_for_prefix(prefix):
+    for node in [r1, r2]:
+        ip_addrs = node.get_ip_addrs('-v')
+        matched_addrs = [addr for addr in ip_addrs if addr.startswith(prefix)]
+        verify(len(matched_addrs) == 0)
+
+
+def check_num_netdata_prefixes(num_prefixes):
+    for node in [r1, r2]:
+        verify(len(node.get_netdata_prefixes()) == num_prefixes)
+
+
+def check_num_netdata_routes(num_routes):
+    for node in [r1, r2]:
+        verify(len(node.get_netdata_routes()) == num_routes)
+
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Form topology
+
+r1.allowlist_node(r2)
+r2.allowlist_node(r1)
+r2.allowlist_node(c2)
+c2.allowlist_node(r2)
+
+r1.form('slaac')
+r2.join(r1)
+c2.join(r2, cli.JOIN_TYPE_END_DEVICE)
+
+verify(r1.get_state() == 'leader')
+verify(r2.get_state() == 'router')
+verify(c2.get_state() == 'child')
+
+# Ensure `c2` is attached to `r2` as its parent
+verify(int(c2.get_parent_info()['Rloc'], 16) == int(r2.get_rloc16(), 16))
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Test Implementation
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Add first prefix `fd00:11::/64` and check that SLAAC
+# addressed are added based on it on all nodes.
+
+r1.add_prefix("fd00:11:0:0::/64", "paos")
+r1.register_netdata()
+verify_within(check_num_netdata_prefixes, 100, 1)
+
+verify_slaac_address_for_prefix('fd00:11:0:0:', preferred=True)
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Add `fd00:22::/64` and check its related SLAAC addresses.
+
+r2.add_prefix("fd00:22:0:0::/64", "paos")
+r2.register_netdata()
+
+verify_within(check_num_netdata_prefixes, 100, 2)
+
+verify_slaac_address_for_prefix('fd00:11:0:0:', preferred=True)
+verify_slaac_address_for_prefix('fd00:22:0:0:', preferred=True)
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Add prefix `fd00:11::/64` now from `r2`, and since this was
+# previously added by `r1`, there should not change to SLAAC
+# addresses.
+
+r2.add_prefix("fd00:11:0:0::/64", "paos")
+r2.register_netdata()
+
+verify_within(check_num_netdata_prefixes, 100, 3)
+
+verify_slaac_address_for_prefix('fd00:11:0:0:', preferred=True)
+verify_slaac_address_for_prefix('fd00:22:0:0:', preferred=True)
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Remove prefix `fd00:11::/64` from `r2`, and since this is
+# also added by `r1`, there should not change to SLAAC addresses.
+
+r2.remove_prefix("fd00:11:0:0::/64")
+r2.register_netdata()
+
+verify_within(check_num_netdata_prefixes, 100, 2)
+
+verify_slaac_address_for_prefix('fd00:11:0:0:', preferred=True)
+verify_slaac_address_for_prefix('fd00:22:0:0:', preferred=True)
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Remove all prefixes and validate that all SLAAC addresses
+# start deprecating.
+
+r1.remove_prefix("fd00:11:0:0::/64")
+r1.register_netdata()
+r2.remove_prefix("fd00:22:0:0::/64")
+r2.register_netdata()
+
+verify_within(check_num_netdata_prefixes, 100, 0)
+
+verify_slaac_address_for_prefix('fd00:11:0:0:', preferred=False)
+verify_slaac_address_for_prefix('fd00:22:0:0:', preferred=False)
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Validate that the deprecating address can still be used
+# and proper route look up happens when it is used.
+
+# Add an off-mesh route `::/0` (default route) on `r1`.
+
+r1.add_route('::/0', 's', 'med')
+r1.register_netdata()
+
+verify_within(check_num_netdata_routes, 10, 1)
+
+# Now we ping from `r2` an off-mesh address, `r2` should pick one of
+# its deprecating addresses as the source address for this message.
+# Since the SLAAC module tracks the associated Domain ID from the
+# original Prefix TLV, the route lookup for this message should
+# successfully match to `r1` off-mesh route. We validate that the
+# message is indeed delivered to `r1` and passed to host.
+
+r1_counter = r1.get_br_counter_unicast_outbound_packets()
+
+r2.ping('fd00:ee::1', verify_success=False)
+
+verify(r1_counter + 1 == r1.get_br_counter_unicast_outbound_packets())
+
+# Repeat the same process but now ping from `c2` which is an
+# MTD child. `c2` would send the message to its parent `r2` which
+# should be able to successfully do route look up for the deprecating
+# prefix. The message should be delivered to `r1` and passed to host.
+
+r1_counter = r1.get_br_counter_unicast_outbound_packets()
+
+c2.ping('fd00:ff::1', verify_success=False)
+
+verify(r1_counter + 1 == r1.get_br_counter_unicast_outbound_packets())
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Before the address is expired, re-add ``fd00:11::/64` prefix
+# and check that the address is added back as preferred.
+
+r2.add_prefix("fd00:11:0:0::/64", "paors")
+r2.register_netdata()
+
+verify_within(check_num_netdata_prefixes, 100, 1)
+
+verify_slaac_address_for_prefix('fd00:11:0:0:', preferred=True)
+verify_slaac_address_for_prefix('fd00:22:0:0:', preferred=False)
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Make sure prefix 2 is expired and removed
+
+
+def check_prefix_2_addr_expire():
+    verify_no_slaac_address_for_prefix('fd00:22:0:0:')
+
+
+verify_within(check_prefix_2_addr_expire, 100)
+
+verify_slaac_address_for_prefix('fd00:11:0:0:', preferred=True)
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Manually add an address for prefix `fd00:33::/64` on `r1`
+# and `r2` before adding  a related on-mesh prefix. Validate that
+# the SLAAC module does not add addresses.
+
+r1.add_ip_addr('fd00:33::1')
+r2.add_ip_addr('fd00:33::2')
+
+r1.add_prefix("fd00:33:0:0::/64", "paos")
+r1.register_netdata()
+
+verify_within(check_num_netdata_prefixes, 100, 2)
+
+verify_slaac_address_for_prefix('fd00:11:0:0:', preferred=True)
+verify_slaac_address_for_prefix('fd00:33:0:0:', preferred=True, origin='manual')
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Remove the manually added address on `r2` which should trigger
+# SLAAC module to add its own SLAAC address based on the prefix.
+
+r2.remove_ip_addr('fd00:33::2')
+time.sleep(0.1)
+
+verify_slaac_address_for_prefix('fd00:11:0:0:', preferred=True)
+verify_slaac_address_for_prefix('fd00:33:0:0:', preferred=True, nodes=[r2], origin='slaac')
+verify_slaac_address_for_prefix('fd00:33:0:0:', preferred=True, nodes=[r1], origin='manual')
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Remove the prefix and make sure the manually added address
+# is not impacted, but the one added by SLAAC on `r2` should
+# be deprecating.
+
+r1.remove_prefix("fd00:33:0:0::/64")
+r1.register_netdata()
+
+verify_within(check_num_netdata_prefixes, 100, 1)
+
+verify_slaac_address_for_prefix('fd00:11:0:0:', preferred=True)
+verify_slaac_address_for_prefix('fd00:33:0:0:', preferred=False, nodes=[r2], origin='slaac')
+verify_slaac_address_for_prefix('fd00:33:0:0:', preferred=True, nodes=[r1], origin='manual')
+
+r1.remove_ip_addr('fd00:33::1')
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Toranj config sets the max number of SLAAC addresses to 4
+# Check that the max limit is applied by adding 5 prefixes.
+
+r1.add_prefix("fd00:22:0:0::/64", "paos")
+r1.add_prefix("fd00:33:0:0::/64", "paos")
+r1.add_prefix("fd00:44:0:0::/64", "paos")
+r1.register_netdata()
+
+verify_within(check_num_netdata_prefixes, 100, 4)
+
+verify_slaac_address_for_prefix('fd00:11:0:0:', preferred=True)
+verify_slaac_address_for_prefix('fd00:22:0:0:', preferred=True)
+verify_slaac_address_for_prefix('fd00:33:0:0:', preferred=True)
+verify_slaac_address_for_prefix('fd00:44:0:0:', preferred=True)
+
+r1.add_prefix("fd00:55:0:0::/64", "paos")
+r1.register_netdata()
+
+verify_within(check_num_netdata_prefixes, 100, 5)
+
+verify_slaac_address_for_prefix('fd00:11:0:0:', preferred=True)
+verify_slaac_address_for_prefix('fd00:22:0:0:', preferred=True)
+verify_slaac_address_for_prefix('fd00:33:0:0:', preferred=True)
+verify_slaac_address_for_prefix('fd00:44:0:0:', preferred=True)
+verify_no_slaac_address_for_prefix('fd00:55:0:0::/64')
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Remove one of the prefixes which should deprecate an
+# existing SLAAC address which should then be evicted to
+# add address for the new `fd00:55::/64` prefix.
+
+r1.remove_prefix("fd00:44:0:0::/64")
+r1.register_netdata()
+
+verify_within(check_num_netdata_prefixes, 100, 4)
+
+verify_slaac_address_for_prefix('fd00:11:0:0:', preferred=True)
+verify_slaac_address_for_prefix('fd00:22:0:0:', preferred=True)
+verify_slaac_address_for_prefix('fd00:33:0:0:', preferred=True)
+verify_slaac_address_for_prefix('fd00:55:0:0:', preferred=True)
+verify_no_slaac_address_for_prefix('fd00:44:0:0::/64')
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Validate oldest entry is evicted when multiple addresses are deprecating
+# and new prefix is added.
+
+r1.remove_prefix("fd00:33:0:0::/64")
+r1.register_netdata()
+time.sleep(0.05)
+r1.remove_prefix("fd00:22:0:0::/64")
+r1.register_netdata()
+time.sleep(0.05)
+r1.remove_prefix("fd00:55:0:0::/64")
+r1.register_netdata()
+time.sleep(0.05)
+
+verify_within(check_num_netdata_prefixes, 100, 1)
+
+verify_slaac_address_for_prefix('fd00:11:0:0:', preferred=True)
+verify_slaac_address_for_prefix('fd00:22:0:0:', preferred=False)
+verify_slaac_address_for_prefix('fd00:33:0:0:', preferred=False)
+verify_slaac_address_for_prefix('fd00:55:0:0:', preferred=False)
+
+# Now add a new prefix `fd00:66::/64`, the `fd00:33::` address
+# which was removed first should be evicted to make room for
+# the SLAAC address for the new prefix.
+
+r1.add_prefix("fd00:66:0:0::/64", "paos")
+r1.register_netdata()
+
+verify_within(check_num_netdata_prefixes, 100, 2)
+
+verify_slaac_address_for_prefix('fd00:11:0:0:', preferred=True)
+verify_slaac_address_for_prefix('fd00:22:0:0:', preferred=False)
+verify_no_slaac_address_for_prefix('fd00:33:0:0:')
+verify_slaac_address_for_prefix('fd00:55:0:0:', preferred=False)
+verify_slaac_address_for_prefix('fd00:66:0:0:', preferred=True)
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Validate re-adding of a prefix before its address is deprecated.
+
+r1.add_prefix("fd00:22:0:0::/64", "paos")
+r1.register_netdata()
+
+verify_within(check_num_netdata_prefixes, 100, 3)
+
+verify_slaac_address_for_prefix('fd00:11:0:0:', preferred=True)
+verify_slaac_address_for_prefix('fd00:22:0:0:', preferred=True)
+verify_no_slaac_address_for_prefix('fd00:33:0:0:')
+verify_slaac_address_for_prefix('fd00:55:0:0:', preferred=False)
+verify_slaac_address_for_prefix('fd00:66:0:0:', preferred=True)
+
+
+def check_prefix_5_addr_expire():
+    verify_no_slaac_address_for_prefix('fd00:55:0:0:')
+
+
+verify_within(check_prefix_5_addr_expire, 100)
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Test finished
+
+cli.Node.finalize_all_nodes()
+
+print('\'{}\' passed.'.format(test_name))
diff --git a/tests/toranj/cli/test-028-border-agent-ephemeral-key.py b/tests/toranj/cli/test-028-border-agent-ephemeral-key.py
new file mode 100755
index 0000000..47a02ee
--- /dev/null
+++ b/tests/toranj/cli/test-028-border-agent-ephemeral-key.py
@@ -0,0 +1,96 @@
+#!/usr/bin/env python3
+#
+#  Copyright (c) 2023, The OpenThread Authors.
+#  All rights reserved.
+#
+#  Redistribution and use in source and binary forms, with or without
+#  modification, are permitted provided that the following conditions are met:
+#  1. Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#  2. Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#  3. Neither the name of the copyright holder nor the
+#     names of its contributors may be used to endorse or promote products
+#     derived from this software without specific prior written permission.
+#
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+#  POSSIBILITY OF SUCH DAMAGE.
+
+from cli import verify
+from cli import verify_within
+import cli
+import time
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Test description:
+#
+# Validate changes to `IntervalMax` for MLE Advertisement Trickle Timer based on number of
+# router neighbors of the device.
+#
+
+test_name = __file__[:-3] if __file__.endswith('.py') else __file__
+print('-' * 120)
+print('Starting \'{}\''.format(test_name))
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Creating `cli.Node` instances
+
+speedup = 20
+cli.Node.set_time_speedup_factor(speedup)
+
+leader = cli.Node()
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Test Implementation
+
+leader.form('ba-ephemeral')
+
+verify(leader.get_state() == 'leader')
+
+verify(leader.ba_is_ephemeral_key_active() == 'inactive')
+
+port = int(leader.ba_get_port())
+
+leader.ba_set_ephemeral_key('password', 10000, 1234)
+
+time.sleep(0.1)
+
+verify(leader.ba_is_ephemeral_key_active() == 'active')
+verify(int(leader.ba_get_port()) == 1234)
+
+leader.ba_set_ephemeral_key('password2', 200, 45678)
+
+time.sleep(0.100 / speedup)
+verify(leader.ba_is_ephemeral_key_active() == 'active')
+verify(int(leader.ba_get_port()) == 45678)
+
+time.sleep(0.150 / speedup)
+verify(leader.ba_is_ephemeral_key_active() == 'inactive')
+verify(int(leader.ba_get_port()) == port)
+
+leader.ba_set_ephemeral_key('newkey')
+verify(leader.ba_is_ephemeral_key_active() == 'active')
+
+time.sleep(0.1)
+verify(leader.ba_is_ephemeral_key_active() == 'active')
+
+leader.ba_clear_ephemeral_key()
+verify(leader.ba_is_ephemeral_key_active() == 'inactive')
+verify(int(leader.ba_get_port()) == port)
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Test finished
+
+cli.Node.finalize_all_nodes()
+
+print('\'{}\' passed.'.format(test_name))
diff --git a/tests/toranj/cli/test-400-srp-client-server.py b/tests/toranj/cli/test-400-srp-client-server.py
index a74d261..0e3edb2 100755
--- a/tests/toranj/cli/test-400-srp-client-server.py
+++ b/tests/toranj/cli/test-400-srp-client-server.py
@@ -61,11 +61,10 @@
 verify(server.srp_server_get_state() == 'disabled')
 verify(server.srp_server_get_addr_mode() == 'unicast')
 verify(client.srp_client_get_state() == 'Disabled')
-verify(client.srp_client_get_auto_start_mode() == 'Disabled')
+verify(client.srp_client_get_auto_start_mode() == 'Enabled')
 
 # Start server and client and register single service
 server.srp_server_enable()
-client.srp_client_enable_auto_start_mode()
 
 client.srp_client_set_host_name('host')
 client.srp_client_set_host_address('fd00::cafe')
@@ -122,6 +121,22 @@
 verify(service['host'] == 'host')
 verify(service['addresses'] == ['fd00:0:0:0:0:0:0:cafe'])
 
+# Check the client address is added in EID cache table (snoop).
+
+cache_table = server.get_eidcache()
+client_rloc = int(client.get_rloc16(), 16)
+found_entry = False
+
+for entry in cache_table:
+    fields = entry.strip().split(' ')
+    if (fields[0] == 'fd00:0:0:0:0:0:0:cafe'):
+        verify(int(fields[1], 16) == client_rloc)
+        verify(fields[2] == 'snoop')
+        found_entry = True
+        break
+
+verify(found_entry)
+
 # -----------------------------------------------------------------------------------------------------------------------
 # Test finished
 
diff --git a/tests/toranj/cli/test-401-srp-server-address-cache-snoop.py b/tests/toranj/cli/test-401-srp-server-address-cache-snoop.py
new file mode 100755
index 0000000..2462d0a
--- /dev/null
+++ b/tests/toranj/cli/test-401-srp-server-address-cache-snoop.py
@@ -0,0 +1,213 @@
+#!/usr/bin/env python3
+#
+#  Copyright (c) 2024, The OpenThread Authors.
+#  All rights reserved.
+#
+#  Redistribution and use in source and binary forms, with or without
+#  modification, are permitted provided that the following conditions are met:
+#  1. Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#  2. Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#  3. Neither the name of the copyright holder nor the
+#     names of its contributors may be used to endorse or promote products
+#     derived from this software without specific prior written permission.
+#
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+#  POSSIBILITY OF SUCH DAMAGE.
+
+from cli import verify
+from cli import verify_within
+import cli
+import time
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Test description:
+#
+# Validate registered host addresses are properly added in address cache table
+# on SRP server.
+#
+#    r1 (leader) ----- r2 ------ r3
+#     /  |                        \
+#    /   |                         \
+#   fed1  sed1                     sed2
+#
+
+test_name = __file__[:-3] if __file__.endswith('.py') else __file__
+print('-' * 120)
+print('Starting \'{}\''.format(test_name))
+
+#-----------------------------------------------------------------------------------------------------------------------
+# Creating `cli.Nodes` instances
+
+speedup = 10
+cli.Node.set_time_speedup_factor(speedup)
+
+r1 = cli.Node()
+r2 = cli.Node()
+r3 = cli.Node()
+fed1 = cli.Node()
+sed1 = cli.Node()
+sed2 = cli.Node()
+
+WAIT_TIME = 5
+
+#-----------------------------------------------------------------------------------------------------------------------
+# Form topology
+
+r1.allowlist_node(r2)
+r1.allowlist_node(fed1)
+r1.allowlist_node(sed1)
+
+r2.allowlist_node(r1)
+r2.allowlist_node(r3)
+r2.allowlist_node(sed2)
+
+r3.allowlist_node(r2)
+r3.allowlist_node(sed2)
+
+fed1.allowlist_node(r1)
+sed1.allowlist_node(r1)
+
+sed2.allowlist_node(r3)
+
+r1.form('srp-snoop')
+r2.join(r1)
+r3.join(r2)
+fed1.join(r1, cli.JOIN_TYPE_REED)
+sed1.join(r1, cli.JOIN_TYPE_SLEEPY_END_DEVICE)
+sed2.join(r1, cli.JOIN_TYPE_SLEEPY_END_DEVICE)
+sed1.set_pollperiod(500)
+sed2.set_pollperiod(500)
+
+verify(r1.get_state() == 'leader')
+verify(r2.get_state() == 'router')
+verify(r2.get_state() == 'router')
+verify(sed1.get_state() == 'child')
+verify(sed2.get_state() == 'child')
+verify(fed1.get_state() == 'child')
+
+r2_rloc = int(r2.get_rloc16(), 16)
+r3_rloc = int(r3.get_rloc16(), 16)
+fed1_rloc = int(fed1.get_rloc16(), 16)
+
+# Start server and client and register single service
+r1.srp_server_enable()
+
+r2.srp_client_enable_auto_start_mode()
+r2.srp_client_set_host_name('r2')
+r2.srp_client_set_host_address('fd00::2')
+r2.srp_client_add_service('srv2', '_test._udp', 222, 0, 0)
+
+
+def check_server_has_host(num_hosts):
+    verify(len(r1.srp_server_get_hosts()) >= num_hosts)
+
+
+verify_within(check_server_has_host, WAIT_TIME, arg=1)
+
+cache_table = r1.get_eidcache()
+
+for entry in cache_table:
+    fields = entry.strip().split(' ')
+    if (fields[0] == 'fd00:0:0:0:0:0:0:2'):
+        verify(int(fields[1], 16) == r2_rloc)
+        verify(fields[2] == 'snoop')
+        break
+else:
+    verify(False)  # did not find cache entry
+
+# Register from r3 which one hop away from r1 server.
+
+r3.srp_client_enable_auto_start_mode()
+r3.srp_client_set_host_name('r3')
+r3.srp_client_set_host_address('fd00::3')
+r3.srp_client_add_service('srv3', '_test._udp', 333, 0, 0)
+
+verify_within(check_server_has_host, WAIT_TIME, arg=2)
+
+cache_table = r1.get_eidcache()
+
+for entry in cache_table:
+    fields = entry.strip().split(' ')
+    if (fields[0] == 'fd00:0:0:0:0:0:0:3'):
+        verify(int(fields[1], 16) == r3_rloc)
+        verify(fields[2] == 'snoop')
+        break
+else:
+    verify(False)  # did not find cache entry
+
+# Register from sed2 which child of r3. The cache table should
+# use the `r3` as the parent of sed2.
+
+sed2.srp_client_enable_auto_start_mode()
+sed2.srp_client_set_host_name('sed2')
+sed2.srp_client_set_host_address('fd00::1:3')
+sed2.srp_client_add_service('srv4', '_test._udp', 333, 0, 0)
+
+verify_within(check_server_has_host, WAIT_TIME, arg=3)
+
+cache_table = r1.get_eidcache()
+
+for entry in cache_table:
+    fields = entry.strip().split(' ')
+    if (fields[0] == 'fd00:0:0:0:0:0:1:3'):
+        verify(int(fields[1], 16) == r3_rloc)
+        verify(fields[2] == 'snoop')
+        break
+else:
+    verify(False)  # did not find cache entry
+
+# Register from fed1 which child of server (r1) itself. The cache table should
+# be properly updated
+
+fed1.srp_client_enable_auto_start_mode()
+fed1.srp_client_set_host_name('fed1')
+fed1.srp_client_set_host_address('fd00::2:3')
+fed1.srp_client_add_service('srv5', '_test._udp', 555, 0, 0)
+
+verify_within(check_server_has_host, WAIT_TIME, arg=4)
+
+cache_table = r1.get_eidcache()
+
+for entry in cache_table:
+    fields = entry.strip().split(' ')
+    if (fields[0] == 'fd00:0:0:0:0:0:2:3'):
+        verify(int(fields[1], 16) == fed1_rloc)
+        verify(fields[2] == 'snoop')
+        break
+else:
+    verify(False)  # did not find cache entry
+
+# Register from sed1 which is a sleepy child of server (r1).
+# The cache table should not be updated for sleepy child.
+
+sed1.srp_client_enable_auto_start_mode()
+sed1.srp_client_set_host_name('sed1')
+sed1.srp_client_set_host_address('fd00::3:4')
+sed1.srp_client_add_service('srv5', '_test._udp', 555, 0, 0)
+
+verify_within(check_server_has_host, WAIT_TIME, arg=4)
+
+cache_table = r1.get_eidcache()
+
+for entry in cache_table:
+    fields = entry.strip().split(' ')
+    verify(fields[0] != 'fd00:0:0:0:0:0:3:4')
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Test finished
+
+cli.Node.finalize_all_nodes()
+
+print('\'{}\' passed.'.format(test_name))
diff --git a/tests/toranj/cli/test-500-two-brs-two-networks.py b/tests/toranj/cli/test-500-two-brs-two-networks.py
new file mode 100755
index 0000000..12c61a7
--- /dev/null
+++ b/tests/toranj/cli/test-500-two-brs-two-networks.py
@@ -0,0 +1,111 @@
+#!/usr/bin/env python3
+#
+#  Copyright (c) 2024, The OpenThread Authors.
+#  All rights reserved.
+#
+#  Redistribution and use in source and binary forms, with or without
+#  modification, are permitted provided that the following conditions are met:
+#  1. Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#  2. Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#  3. Neither the name of the copyright holder nor the
+#     names of its contributors may be used to endorse or promote products
+#     derived from this software without specific prior written permission.
+#
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+#  POSSIBILITY OF SUCH DAMAGE.
+
+from cli import verify
+from cli import verify_within
+import cli
+import time
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Test description:
+#
+# Two BRs on two different Thread networks.
+
+test_name = __file__[:-3] if __file__.endswith('.py') else __file__
+print('-' * 120)
+print('Starting \'{}\''.format(test_name))
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Creating `cli.Nodes` instances
+
+speedup = 60
+cli.Node.set_time_speedup_factor(speedup)
+
+br1 = cli.Node()
+br2 = cli.Node()
+
+WAIT_TIME = 5
+IF_INDEX = 1
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Test implementation
+
+# Start first BR with its own Thread network
+
+br1.form('net1')
+verify(br1.get_state() == 'leader')
+
+br1.br_init(IF_INDEX, 1)
+br1.br_enable()
+
+time.sleep(1)
+verify(br1.br_get_state() == 'running')
+
+br1_local_omr = br1.br_get_local_omrprefix()
+br1_favored_omr = br1.br_get_favored_omrprefix().split()[0]
+verify(br1_local_omr == br1_favored_omr)
+
+br1_local_onlink = br1.br_get_local_onlinkprefix()
+br1_favored_onlink = br1.br_get_favored_onlinkprefix().split()[0]
+verify(br1_local_onlink == br1_favored_onlink)
+
+# Start second BR with its own Thread network.
+
+br2.form('net2')
+verify(br2.get_state() == 'leader')
+
+br2.br_init(IF_INDEX, 1)
+br2.br_enable()
+
+time.sleep(1)
+verify(br2.br_get_state() == 'running')
+
+br2_local_omr = br2.br_get_local_omrprefix()
+br2_favored_omr = br2.br_get_favored_omrprefix().split()[0]
+verify(br2_local_omr == br2_favored_omr)
+
+br2_local_onlink = br2.br_get_local_onlinkprefix()
+br2_favored_onlink = br2.br_get_favored_onlinkprefix().split()[0]
+verify(br2_local_onlink != br2_favored_onlink)
+
+# BR2 should see and favor the on-link prefix already advertised by BR1.
+
+verify(br1_favored_onlink == br2_favored_onlink)
+
+br1_routers = br1.br_get_routers()
+br2_routers = br2.br_get_routers()
+
+verify(len(br1_routers) > 0)
+verify(len(br2_routers) > 0)
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Test finished
+
+cli.Node.finalize_all_nodes()
+
+print('\'{}\' passed.'.format(test_name))
diff --git a/tests/toranj/cli/test-501-multi-br-failure-recovery.py b/tests/toranj/cli/test-501-multi-br-failure-recovery.py
new file mode 100755
index 0000000..ecf41b9
--- /dev/null
+++ b/tests/toranj/cli/test-501-multi-br-failure-recovery.py
@@ -0,0 +1,210 @@
+#!/usr/bin/env python3
+#
+#  Copyright (c) 2024, The OpenThread Authors.
+#  All rights reserved.
+#
+#  Redistribution and use in source and binary forms, with or without
+#  modification, are permitted provided that the following conditions are met:
+#  1. Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#  2. Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#  3. Neither the name of the copyright holder nor the
+#     names of its contributors may be used to endorse or promote products
+#     derived from this software without specific prior written permission.
+#
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+#  POSSIBILITY OF SUCH DAMAGE.
+
+from cli import verify
+from cli import verify_within
+import cli
+import time
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Test description:
+#
+# Network with two BRs, none of them acting as leader. Removing BR1 ensuring BR2 taking over.
+#
+#      ________________
+#     /                \
+#   br1 --- leader --- br2
+#   /         / \        \
+#  c1       c2  c3       c4
+#
+
+test_name = __file__[:-3] if __file__.endswith('.py') else __file__
+print('-' * 120)
+print('Starting \'{}\''.format(test_name))
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Creating `cli.Nodes` instances
+
+speedup = 60
+cli.Node.set_time_speedup_factor(speedup)
+
+leader = cli.Node()
+br1 = cli.Node()
+br2 = cli.Node()
+c1 = cli.Node()
+c2 = cli.Node()
+c3 = cli.Node()
+c4 = cli.Node()
+
+IF_INDEX = 1
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Form topology
+
+leader.allowlist_node(br1)
+leader.allowlist_node(br2)
+leader.allowlist_node(c2)
+leader.allowlist_node(c3)
+
+br1.allowlist_node(leader)
+br1.allowlist_node(br2)
+br1.allowlist_node(c1)
+
+br2.allowlist_node(leader)
+br2.allowlist_node(br1)
+br2.allowlist_node(c4)
+
+c1.allowlist_node(br1)
+
+c2.allowlist_node(leader)
+c3.allowlist_node(leader)
+
+c4.allowlist_node(br2)
+
+leader.form("multi-br")
+br1.join(leader)
+br2.join(leader)
+c1.join(leader, cli.JOIN_TYPE_END_DEVICE)
+c2.join(leader, cli.JOIN_TYPE_END_DEVICE)
+c3.join(leader, cli.JOIN_TYPE_END_DEVICE)
+c4.join(leader, cli.JOIN_TYPE_END_DEVICE)
+
+verify(leader.get_state() == 'leader')
+verify(br1.get_state() == 'router')
+verify(br2.get_state() == 'router')
+verify(c1.get_state() == 'child')
+verify(c2.get_state() == 'child')
+verify(c3.get_state() == 'child')
+verify(c4.get_state() == 'child')
+
+nodes_non_br = [leader, c1, c2, c3, c4]
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Test implementation
+
+# Start the first BR
+
+br1.srp_server_set_addr_mode('unicast')
+br1.srp_server_auto_enable()
+
+br1.br_init(IF_INDEX, 1)
+br1.br_enable()
+
+time.sleep(1)
+verify(br1.br_get_state() == 'running')
+
+br1_local_omr = br1.br_get_local_omrprefix()
+br1_favored_omr = br1.br_get_favored_omrprefix().split()[0]
+verify(br1_local_omr == br1_favored_omr)
+
+br1_local_onlink = br1.br_get_local_onlinkprefix()
+br1_favored_onlink = br1.br_get_favored_onlinkprefix().split()[0]
+verify(br1_local_onlink == br1_favored_onlink)
+
+# Start the second BR
+
+br2.br_init(IF_INDEX, 1)
+br2.br_enable()
+
+time.sleep(1)
+verify(br2.br_get_state() == 'running')
+
+br2_local_omr = br2.br_get_local_omrprefix()
+br2_favored_omr = br2.br_get_favored_omrprefix().split()[0]
+verify(br2_favored_omr == br1_favored_omr)
+
+br2_favored_onlink = br2.br_get_favored_onlinkprefix().split()[0]
+verify(br2_favored_onlink == br1_favored_onlink)
+
+verify(br1.srp_server_get_state() == 'running')
+verify(br2.srp_server_get_state() == 'disabled')
+
+# Register SRP services on all nodes
+
+for node in nodes_non_br:
+    verify(node.srp_client_get_auto_start_mode() == 'Enabled')
+    node.srp_client_set_host_name('host' + str(node.index))
+    node.srp_client_enable_auto_host_address()
+    node.srp_client_add_service('srv' + str(node.index), '_test._udp', 777, 0, 0)
+
+time.sleep(1)
+
+hosts = br1.srp_server_get_hosts()
+verify(len(hosts) == len(nodes_non_br))
+
+services = br1.srp_server_get_services()
+verify(len(services) == len(nodes_non_br))
+
+# Ensure that all registered addresses are derived from BR1 OMR.
+
+for host in hosts:
+    verify(host['addresses'][0].startswith(br1_local_omr[:-4]))
+
+# Start SRP server on BR2
+
+br2.srp_server_set_addr_mode('unicast')
+br2.srp_server_auto_enable()
+
+time.sleep(1)
+
+verify(br2.srp_server_get_state() == 'running')
+
+# De-activate BR1
+
+br1.br_disable()
+br1.thread_stop()
+br1.interface_down()
+del br1
+
+c1.allowlist_node(br2)
+br2.allowlist_node(c1)
+
+# Wait long enough for BR2 to take over
+
+time.sleep(5)
+
+# Validate that everything is registered with BR2
+
+hosts = br2.srp_server_get_hosts()
+verify(len(hosts) == len(nodes_non_br))
+
+services = br2.srp_server_get_services()
+verify(len(services) == len(nodes_non_br))
+
+# Ensure that all registered addresses are now derived from BR2
+# OMR prefix.
+
+for host in hosts:
+    verify(host['addresses'][0].startswith(br2_local_omr[:-4]))
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Test finished
+
+cli.Node.finalize_all_nodes()
+
+print('\'{}\' passed.'.format(test_name))
diff --git a/tests/toranj/cli/test-502-multi-br-leader-failure-recovery.py b/tests/toranj/cli/test-502-multi-br-leader-failure-recovery.py
new file mode 100755
index 0000000..71d2ebc
--- /dev/null
+++ b/tests/toranj/cli/test-502-multi-br-leader-failure-recovery.py
@@ -0,0 +1,210 @@
+#!/usr/bin/env python3
+#
+#  Copyright (c) 2024, The OpenThread Authors.
+#  All rights reserved.
+#
+#  Redistribution and use in source and binary forms, with or without
+#  modification, are permitted provided that the following conditions are met:
+#  1. Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#  2. Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#  3. Neither the name of the copyright holder nor the
+#     names of its contributors may be used to endorse or promote products
+#     derived from this software without specific prior written permission.
+#
+#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+#  POSSIBILITY OF SUCH DAMAGE.
+
+from cli import verify
+from cli import verify_within
+import cli
+import time
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Test description:
+#
+# Network with two BRs, BR1 acting as leader. Removing BR1 ensuring BR2 taking over.
+#
+#      ________________
+#     /                \
+#   br1 --- router --- br2
+#   /        / \        \
+#  c1       c2  c3      c4
+#
+
+test_name = __file__[:-3] if __file__.endswith('.py') else __file__
+print('-' * 120)
+print('Starting \'{}\''.format(test_name))
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Creating `cli.Nodes` instances
+
+speedup = 60
+cli.Node.set_time_speedup_factor(speedup)
+
+br1 = cli.Node()
+br2 = cli.Node()
+router = cli.Node()
+c1 = cli.Node()
+c2 = cli.Node()
+c3 = cli.Node()
+c4 = cli.Node()
+
+IF_INDEX = 1
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Form topology
+
+br1.allowlist_node(router)
+br1.allowlist_node(br2)
+br1.allowlist_node(c1)
+
+br2.allowlist_node(router)
+br2.allowlist_node(br1)
+br2.allowlist_node(c4)
+
+router.allowlist_node(br1)
+router.allowlist_node(br2)
+router.allowlist_node(c2)
+router.allowlist_node(c3)
+
+c1.allowlist_node(br1)
+
+c2.allowlist_node(router)
+c3.allowlist_node(router)
+
+c4.allowlist_node(br2)
+
+br1.form("multi-br")
+br2.join(br1)
+router.join(br1)
+c1.join(br1, cli.JOIN_TYPE_END_DEVICE)
+c2.join(br1, cli.JOIN_TYPE_END_DEVICE)
+c3.join(br1, cli.JOIN_TYPE_END_DEVICE)
+c4.join(br1, cli.JOIN_TYPE_END_DEVICE)
+
+verify(br1.get_state() == 'leader')
+verify(br2.get_state() == 'router')
+verify(router.get_state() == 'router')
+verify(c1.get_state() == 'child')
+verify(c2.get_state() == 'child')
+verify(c3.get_state() == 'child')
+verify(c4.get_state() == 'child')
+
+nodes_non_br = [router, c1, c2, c3, c4]
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Test implementation
+
+# Start the first BR
+
+br1.srp_server_set_addr_mode('unicast')
+br1.srp_server_auto_enable()
+
+br1.br_init(IF_INDEX, 1)
+br1.br_enable()
+
+time.sleep(1)
+verify(br1.br_get_state() == 'running')
+
+br1_local_omr = br1.br_get_local_omrprefix()
+br1_favored_omr = br1.br_get_favored_omrprefix().split()[0]
+verify(br1_local_omr == br1_favored_omr)
+
+br1_local_onlink = br1.br_get_local_onlinkprefix()
+br1_favored_onlink = br1.br_get_favored_onlinkprefix().split()[0]
+verify(br1_local_onlink == br1_favored_onlink)
+
+# Start the second BR
+
+br2.br_init(IF_INDEX, 1)
+br2.br_enable()
+
+time.sleep(1)
+verify(br2.br_get_state() == 'running')
+
+br2_local_omr = br2.br_get_local_omrprefix()
+br2_favored_omr = br2.br_get_favored_omrprefix().split()[0]
+verify(br2_favored_omr == br1_favored_omr)
+
+br2_favored_onlink = br2.br_get_favored_onlinkprefix().split()[0]
+verify(br2_favored_onlink == br1_favored_onlink)
+
+verify(br1.srp_server_get_state() == 'running')
+verify(br2.srp_server_get_state() == 'disabled')
+
+# Register SRP services on all nodes
+
+for node in nodes_non_br:
+    verify(node.srp_client_get_auto_start_mode() == 'Enabled')
+    node.srp_client_set_host_name('host' + str(node.index))
+    node.srp_client_enable_auto_host_address()
+    node.srp_client_add_service('srv' + str(node.index), '_test._udp', 777, 0, 0)
+
+time.sleep(1)
+
+hosts = br1.srp_server_get_hosts()
+verify(len(hosts) == len(nodes_non_br))
+
+services = br1.srp_server_get_services()
+verify(len(services) == len(nodes_non_br))
+
+# Ensure that all registered addresses are derived from BR1 OMR.
+
+for host in hosts:
+    verify(host['addresses'][0].startswith(br1_local_omr[:-4]))
+
+# Start SRP server on BR2
+
+br2.srp_server_set_addr_mode('unicast')
+br2.srp_server_auto_enable()
+
+time.sleep(1)
+
+verify(br2.srp_server_get_state() == 'running')
+
+# De-activate BR1
+
+br1.br_disable()
+br1.thread_stop()
+br1.interface_down()
+del br1
+
+c1.allowlist_node(br2)
+br2.allowlist_node(c1)
+
+# Wait long enough for BR2 to take over
+
+time.sleep(5)
+
+# Validate that everything is registered with BR2
+
+hosts = br2.srp_server_get_hosts()
+verify(len(hosts) == len(nodes_non_br))
+
+services = br2.srp_server_get_services()
+verify(len(services) == len(nodes_non_br))
+
+# Ensure that all registered addresses are now derived from BR2
+# OMR prefix.
+
+for host in hosts:
+    verify(host['addresses'][0].startswith(br2_local_omr[:-4]))
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Test finished
+
+cli.Node.finalize_all_nodes()
+
+print('\'{}\' passed.'.format(test_name))
diff --git a/tests/toranj/cli/test-602-channel-manager-channel-select.py b/tests/toranj/cli/test-602-channel-manager-channel-select.py
index 0ddf358..596cfdc 100755
--- a/tests/toranj/cli/test-602-channel-manager-channel-select.py
+++ b/tests/toranj/cli/test-602-channel-manager-channel-select.py
@@ -66,6 +66,10 @@
     verify(int(node.get_channel()) == channel)
 
 
+delay = int(node.cli('channel manager delay')[0])
+# add kRequestStartJitterInterval=10000ms to expected channel manager delay
+delay += 10 / speedup
+
 check_channel()
 
 all_channels_mask = int('0x7fff800', 0)
@@ -99,7 +103,7 @@
 result = cli.Node.parse_list(node.cli('channel manager'))
 verify(result['channel'] == '11')
 channel = 11
-verify_within(check_channel, 2)
+verify_within(check_channel, delay)
 
 # Set channels 12-15 as favorable and request a channel select, verify
 # that channel is switched to 12.
@@ -112,13 +116,13 @@
 
 channel = 25
 node.cli('channel manager change', channel)
-verify_within(check_channel, 2)
+verify_within(check_channel, delay)
 
 node.cli('channel manager select 1')
 result = cli.Node.parse_list(node.cli('channel manager'))
 verify(result['channel'] == '12')
 channel = 12
-verify_within(check_channel, 2)
+verify_within(check_channel, delay)
 
 # Set channels 15-17 as favorables and request a channel select,
 # verify that channel is switched to 11.
@@ -129,7 +133,7 @@
 
 channel = 25
 node.cli('channel manager change', channel)
-verify_within(check_channel, 2)
+verify_within(check_channel, delay)
 
 node.cli('channel manager favored', chan_15_to_17_mask)
 
@@ -137,7 +141,7 @@
 result = cli.Node.parse_list(node.cli('channel manager'))
 verify(result['channel'] == '11')
 channel = 11
-verify_within(check_channel, 2)
+verify_within(check_channel, delay)
 
 # Set channels 12-15 as favorable and request a channel select, verify
 # that channel is not switched.
@@ -145,10 +149,11 @@
 node.cli('channel manager favored', chan_12_to_15_mask)
 
 node.cli('channel manager select 1')
+
 result = cli.Node.parse_list(node.cli('channel manager'))
 verify(result['channel'] == '11')
 channel = 11
-verify_within(check_channel, 2)
+verify_within(check_channel, delay)
 
 # Starting from channel 12 and issuing a channel select (which would
 # pick 11 as best channel). However, since quality difference between
@@ -157,14 +162,60 @@
 
 channel = 12
 node.cli('channel manager change', channel)
-verify_within(check_channel, 2)
+verify_within(check_channel, delay)
 
 node.cli('channel manager favored', all_channels_mask)
 
 node.cli('channel manager select 1')
 result = cli.Node.parse_list(node.cli('channel manager'))
 verify(result['channel'] == '12')
-verify_within(check_channel, 2)
+verify_within(check_channel, delay)
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Auto Select
+
+# Set channel manager cca failure rate threshold to 0
+# as we cannot control cca success in simulation
+node.cli('channel manager threshold 0')
+
+# Set short channel selection interval to speedup
+interval = 30
+node.cli(f'channel manager interval {interval}')
+
+# Set channels 15-17 as favorable and request a channel select, verify
+# that channel is switched to 11.
+
+channel = 25
+node.cli('channel manager change', channel)
+verify_within(check_channel, delay)
+node.cli('channel manager favored', chan_15_to_17_mask)
+
+# Active auto channel selection
+node.cli('channel manager auto 1')
+
+channel = 11
+result = cli.Node.parse_list(node.cli('channel manager'))
+verify(result['auto'] == '1')
+verify(result['channel'] == str(channel))
+
+verify_within(check_channel, delay)
+
+# while channel selection timer is running change to channel 25,
+# set channels 12-15 as favorable, wait for auto channel selection
+# and verify that channel is switched to 12.
+
+node.cli('channel manager favored', chan_12_to_15_mask)
+channel = 25
+node.cli('channel manager change', channel)
+
+# wait for timeout of auto selection timer
+time.sleep(2 * interval / speedup)
+
+channel = 12
+result = cli.Node.parse_list(node.cli('channel manager'))
+verify(result['channel'] == str(channel))
+
+verify_within(check_channel, delay)
 
 # -----------------------------------------------------------------------------------------------------------------------
 # Test finished
diff --git a/tests/toranj/openthread-core-toranj-config-posix.h b/tests/toranj/openthread-core-toranj-config-posix.h
index 60e8677..2fced00 100644
--- a/tests/toranj/openthread-core-toranj-config-posix.h
+++ b/tests/toranj/openthread-core-toranj-config-posix.h
@@ -39,6 +39,16 @@
 
 #define OPENTHREAD_CONFIG_PLATFORM_INFO "POSIX-toranj"
 
+#ifdef __linux__
+#define OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE 1
+#endif
+
+#ifndef OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE
+#define OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE 1
+#endif
+
+#define OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE 1
+
 #define OPENTHREAD_CONFIG_LOG_OUTPUT OPENTHREAD_CONFIG_LOG_OUTPUT_PLATFORM_DEFINED
 
 #define OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE 1
diff --git a/tests/toranj/openthread-core-toranj-config-simulation.h b/tests/toranj/openthread-core-toranj-config-simulation.h
index 8a1063c..f564e0b 100644
--- a/tests/toranj/openthread-core-toranj-config-simulation.h
+++ b/tests/toranj/openthread-core-toranj-config-simulation.h
@@ -45,6 +45,10 @@
 
 #endif
 
+#define OPENTHREAD_CONFIG_COAP_API_ENABLE 1
+
+#define OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE 1
+
 #define OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE 0
 
 #define OPENTHREAD_CONFIG_PLATFORM_FLASH_API_ENABLE 1
@@ -57,10 +61,16 @@
 
 #define OPENTHREAD_CONFIG_DNS_DSO_ENABLE 1
 
+#define OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
+
+#define OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE 1
+
 #define OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE 1
 
 #define OPENTHREAD_CONFIG_BORDER_ROUTING_USE_HEAP_ENABLE 1
 
+#define OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE 1
+
 #define OPENTHREAD_CONFIG_RADIO_STATS_ENABLE 0
 
 #endif /* OPENTHREAD_CORE_TORANJ_CONFIG_SIMULATION_H_ */
diff --git a/tests/toranj/openthread-core-toranj-config.h b/tests/toranj/openthread-core-toranj-config.h
index 7e2d4c7..c1a9689 100644
--- a/tests/toranj/openthread-core-toranj-config.h
+++ b/tests/toranj/openthread-core-toranj-config.h
@@ -49,22 +49,32 @@
 
 #define OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE 1
 
+#ifndef OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
 #define OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE 1
+#endif
 
-#define OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE 1
+#define OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
 
 #define OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE 1
 
 #define OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE 1
 
+#define OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE 1
+
 #define OPENTHREAD_CONFIG_MESH_DIAG_ENABLE 1
 
+#define OPENTHREAD_CONFIG_BLE_TCAT_ENABLE 1
+
 #define OPENTHREAD_CONFIG_COMMISSIONER_ENABLE 1
 
 #define OPENTHREAD_CONFIG_COMMISSIONER_MAX_JOINER_ENTRIES 4
 
 #define OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE 1
 
+#define OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE 1
+
+#define OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE 1
+
 #define OPENTHREAD_CONFIG_DIAG_ENABLE 1
 
 #define OPENTHREAD_CONFIG_JOINER_ENABLE 1
@@ -107,6 +117,12 @@
 
 #define OPENTHREAD_CONFIG_IP6_MAX_EXT_MCAST_ADDRS 4
 
+#define OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE 1
+
+#define OPENTHREAD_CONFIG_IP6_SLAAC_NUM_ADDRESSES 4
+
+#define OPENTHREAD_CONFIG_IP6_SLAAC_DEPRECATION_INTERVAL 30
+
 #define OPENTHREAD_CONFIG_MAC_FILTER_ENABLE 1
 
 #define OPENTHREAD_CONFIG_MAC_FILTER_SIZE 80
@@ -164,8 +180,6 @@
 
 #define OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE 1
 
-#define OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE 1
-
 #define OPENTHREAD_CONFIG_CLI_REGISTER_IP6_RECV_CALLBACK 1
 
 #define OPENTHREAD_CONFIG_MLE_PARENT_RESPONSE_CALLBACK_API_ENABLE 1
diff --git a/tests/toranj/start.sh b/tests/toranj/start.sh
index 3e07304..04cef96 100755
--- a/tests/toranj/start.sh
+++ b/tests/toranj/start.sh
@@ -190,7 +190,14 @@
     run cli/test-023-mesh-diag.py
     run cli/test-024-mle-adv-imax-change.py
     run cli/test-025-mesh-local-prefix-change.py
+    run cli/test-026-coaps-conn-limit.py
+    run cli/test-027-slaac-address.py
+    run cli/test-028-border-agent-ephemeral-key.py
     run cli/test-400-srp-client-server.py
+    run cli/test-401-srp-server-address-cache-snoop.py
+    run cli/test-500-two-brs-two-networks.py
+    run cli/test-501-multi-br-failure-recovery.py
+    run cli/test-502-multi-br-leader-failure-recovery.py
     run cli/test-601-channel-manager-channel-change.py
     # Skip the "channel-select" test on a TREL only radio link, since it
     # requires energy scan which is not supported in this case.
@@ -240,7 +247,7 @@
 run ncp/test-027-child-mode-change.py
 run ncp/test-028-router-leader-reset-recovery.py
 run ncp/test-029-data-poll-interval.py
-run ncp/test-030-slaac-address-ncp.py
+# run ncp/test-030-slaac-address-ncp.py
 run ncp/test-031-meshcop-joiner-commissioner.py
 run ncp/test-032-child-attach-with-multiple-ip-addresses.py
 run ncp/test-033-mesh-local-prefix-change.py
diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt
index e14e30a..92337b3 100644
--- a/tests/unit/CMakeLists.txt
+++ b/tests/unit/CMakeLists.txt
@@ -39,10 +39,14 @@
 
 set(COMMON_COMPILE_OPTIONS
     -DOPENTHREAD_FTD=1
+    -DOPENTHREAD_MTD=0
+    -DOPENTHREAD_RADIO=0
     -DOPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE=1
 )
 
 set(COMMON_COMPILE_OPTIONS_RCP
+    -DOPENTHREAD_FTD=0
+    -DOPENTHREAD_MTD=0
     -DOPENTHREAD_RADIO=1
     -DOPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE=1
     -DOPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE=0
@@ -1112,6 +1116,27 @@
 
 add_test(NAME ot-test-serial-number COMMAND ot-test-serial-number)
 
+add_executable(ot-test-srp-adv-proxy
+    test_srp_adv_proxy.cpp
+)
+
+target_include_directories(ot-test-srp-adv-proxy
+    PRIVATE
+        ${COMMON_INCLUDES}
+)
+
+target_compile_options(ot-test-srp-adv-proxy
+    PRIVATE
+        ${COMMON_COMPILE_OPTIONS}
+)
+
+target_link_libraries(ot-test-srp-adv-proxy
+    PRIVATE
+        ${COMMON_LIBS}
+)
+
+add_test(NAME ot-test-srp-adv-proxy COMMAND ot-test-srp-adv-proxy)
+
 add_executable(ot-test-srp-server
     test_srp_server.cpp
 )
@@ -1155,6 +1180,27 @@
 
 add_test(NAME ot-test-string COMMAND ot-test-string)
 
+add_executable(ot-test-tcat
+    test_tcat.cpp
+)
+
+target_include_directories(ot-test-tcat
+    PRIVATE
+        ${COMMON_INCLUDES}
+)
+
+target_compile_options(ot-test-tcat
+    PRIVATE
+        ${COMMON_COMPILE_OPTIONS}
+)
+
+target_link_libraries(ot-test-tcat
+    PRIVATE
+        ${COMMON_LIBS}
+)
+
+add_test(NAME ot-test-tcat COMMAND ot-test-tcat)
+
 add_executable(ot-test-timer
     test_timer.cpp
 )
diff --git a/tests/unit/test_dns.cpp b/tests/unit/test_dns.cpp
index a8408aa..4566cde 100644
--- a/tests/unit/test_dns.cpp
+++ b/tests/unit/test_dns.cpp
@@ -56,20 +56,30 @@
         const char    *mExpectedReadName;
     };
 
-    Instance    *instance;
-    MessagePool *messagePool;
-    Message     *message;
-    uint8_t      buffer[kMaxSize];
-    uint16_t     len;
-    uint16_t     offset;
-    char         label[Dns::Name::kMaxLabelSize];
-    uint8_t      labelLength;
-    char         name[Dns::Name::kMaxNameSize];
-    const char  *subDomain;
-    const char  *domain;
-    const char  *domain2;
-    const char  *fullName;
-    const char  *suffixName;
+    struct TestMatches
+    {
+        const char *mFullName;
+        const char *mFirstLabel;
+        const char *mLabels;
+        const char *mDomain;
+        bool        mShouldMatch;
+    };
+
+    Instance              *instance;
+    MessagePool           *messagePool;
+    Message               *message;
+    uint8_t                buffer[kMaxSize];
+    uint16_t               len;
+    uint16_t               offset;
+    Dns::Name::LabelBuffer label;
+    uint8_t                labelLength;
+    Dns::Name::Buffer      name;
+    const char            *subDomain;
+    const char            *domain;
+    const char            *domain2;
+    const char            *fullName;
+    const char            *suffixName;
+    Dns::Name              dnsName;
 
     static const uint8_t kEncodedName1[] = {7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 3, 'c', 'o', 'm', 0};
     static const uint8_t kEncodedName2[] = {3, 'f', 'o', 'o', 1, 'a', 2, 'b', 'b', 3, 'e', 'd', 'u', 0};
@@ -139,6 +149,20 @@
     static const char kBadLabel[] = "badlabel";
     static const char kBadName[]  = "bad.name";
 
+    static const TestMatches kTestMatches[] = {
+        {"foo.bar.local.", "foo", "bar", "local.", true},
+        {"foo.bar.local.", nullptr, "foo.bar", "local.", true},
+        {"foo.bar.local.", "foo", "ba", "local.", false},
+        {"foo.bar.local.", "fooooo", "bar", "local.", false},
+        {"foo.bar.local.", "foo", "bar", "locall.", false},
+        {"foo.bar.local.", "f", "bar", "local.", false},
+        {"foo.bar.local.", "foo", "barr", "local.", false},
+        {"foo.bar.local.", "foo", "bar", ".local.", false},
+        {"My Lovely Instance._mt._udp.local.", "mY lovely instancE", "_mt._udp", "local.", true},
+        {"My Lovely Instance._mt._udp.local.", nullptr, "mY lovely instancE._mt._udp", "local.", true},
+        {"_s1._sub._srv._udp.default.service.arpa.", "_s1", "_sub._srv._udp", "default.service.arpa.", true},
+    };
+
     printf("================================================================\n");
     printf("TestDnsName()\n");
 
@@ -254,42 +278,68 @@
 
     fullName   = "my-service._ipps._tcp.default.service.arpa.";
     suffixName = "default.service.arpa.";
-    SuccessOrQuit(Dns::Name::ExtractLabels(fullName, suffixName, name, sizeof(name)));
+    SuccessOrQuit(Dns::Name::ExtractLabels(fullName, suffixName, name));
     VerifyOrQuit(strcmp(name, "my-service._ipps._tcp") == 0);
 
+    fullName   = "my-service._ipps._tcp.default.service.arpa";
+    suffixName = "default.service.arpa";
+    SuccessOrQuit(Dns::Name::ExtractLabels(fullName, suffixName, name));
+    VerifyOrQuit(strcmp(name, "my-service._ipps._tcp") == 0);
+
+    fullName   = "my-service._ipps._tcp.default.service.arpa";
+    suffixName = "default.service.arpa.";
+    VerifyOrQuit(Dns::Name::ExtractLabels(fullName, suffixName, name) == kErrorParse);
+
+    fullName   = "my-service._ipps._tcp.default.service.arpa.";
+    suffixName = "default.service.arpa";
+    VerifyOrQuit(Dns::Name::ExtractLabels(fullName, suffixName, name) == kErrorParse);
+
     fullName   = "my.service._ipps._tcp.default.service.arpa.";
     suffixName = "_ipps._tcp.default.service.arpa.";
-    SuccessOrQuit(Dns::Name::ExtractLabels(fullName, suffixName, name, sizeof(name)));
+    SuccessOrQuit(Dns::Name::ExtractLabels(fullName, suffixName, name));
     VerifyOrQuit(strcmp(name, "my.service") == 0);
 
     fullName   = "my-service._ipps._tcp.default.service.arpa.";
     suffixName = "DeFault.SerVice.ARPA.";
-    SuccessOrQuit(Dns::Name::ExtractLabels(fullName, suffixName, name, sizeof(name)));
+    SuccessOrQuit(Dns::Name::ExtractLabels(fullName, suffixName, name));
+    VerifyOrQuit(strcmp(name, "my-service._ipps._tcp") == 0);
+
+    fullName   = "my-service._ipps._tcp.default.service.arpa";
+    suffixName = "DeFault.SerVice.ARPA";
+    SuccessOrQuit(Dns::Name::ExtractLabels(fullName, suffixName, name));
     VerifyOrQuit(strcmp(name, "my-service._ipps._tcp") == 0);
 
     fullName   = "my-service._ipps._tcp.default.service.arpa.";
     suffixName = "efault.service.arpa.";
-    VerifyOrQuit(Dns::Name::ExtractLabels(fullName, suffixName, name, sizeof(name)) == kErrorParse);
+    VerifyOrQuit(Dns::Name::ExtractLabels(fullName, suffixName, name) == kErrorParse);
+
+    fullName   = "my-service._ipps._tcp.default.service.arpa";
+    suffixName = "efault.service.arpa";
+    VerifyOrQuit(Dns::Name::ExtractLabels(fullName, suffixName, name) == kErrorParse);
 
     fullName   = "my-service._ipps._tcp.default.service.arpa.";
     suffixName = "xdefault.service.arpa.";
-    VerifyOrQuit(Dns::Name::ExtractLabels(fullName, suffixName, name, sizeof(name)) == kErrorParse);
+    VerifyOrQuit(Dns::Name::ExtractLabels(fullName, suffixName, name) == kErrorParse);
 
     fullName   = "my-service._ipps._tcp.default.service.arpa.";
     suffixName = ".default.service.arpa.";
-    VerifyOrQuit(Dns::Name::ExtractLabels(fullName, suffixName, name, sizeof(name)) == kErrorParse);
+    VerifyOrQuit(Dns::Name::ExtractLabels(fullName, suffixName, name) == kErrorParse);
 
     fullName   = "my-service._ipps._tcp.default.service.arpa.";
     suffixName = "default.service.arp.";
-    VerifyOrQuit(Dns::Name::ExtractLabels(fullName, suffixName, name, sizeof(name)) == kErrorParse);
+    VerifyOrQuit(Dns::Name::ExtractLabels(fullName, suffixName, name) == kErrorParse);
 
     fullName   = "default.service.arpa.";
     suffixName = "default.service.arpa.";
-    VerifyOrQuit(Dns::Name::ExtractLabels(fullName, suffixName, name, sizeof(name)) == kErrorParse);
+    VerifyOrQuit(Dns::Name::ExtractLabels(fullName, suffixName, name) == kErrorParse);
+
+    fullName   = "default.service.arpa";
+    suffixName = "default.service.arpa";
+    VerifyOrQuit(Dns::Name::ExtractLabels(fullName, suffixName, name) == kErrorParse);
 
     fullName   = "efault.service.arpa.";
     suffixName = "default.service.arpa.";
-    VerifyOrQuit(Dns::Name::ExtractLabels(fullName, suffixName, name, sizeof(name)) == kErrorParse);
+    VerifyOrQuit(Dns::Name::ExtractLabels(fullName, suffixName, name) == kErrorParse);
 
     fullName   = "my-service._ipps._tcp.default.service.arpa.";
     suffixName = "default.service.arpa.";
@@ -342,7 +392,7 @@
 
         // Read entire name
         offset = 0;
-        SuccessOrQuit(Dns::Name::ReadName(*message, offset, name, sizeof(name)));
+        SuccessOrQuit(Dns::Name::ReadName(*message, offset, name));
 
         printf("Read name =\"%s\"\n", name);
 
@@ -496,6 +546,53 @@
         VerifyOrQuit(memcmp(buffer, test.mEncodedData, len) == 0, "Encoded name data does not match expected data");
     }
 
+    printf("----------------------------------------------------------------\n");
+    printf("Name::Matches() variations\n");
+
+    for (const TestMatches &test : kTestMatches)
+    {
+        printf(" \"%s\"\n", test.mFullName);
+
+        dnsName.Set(test.mFullName);
+        VerifyOrQuit(dnsName.Matches(test.mFirstLabel, test.mLabels, test.mDomain) == test.mShouldMatch);
+
+        IgnoreError(message->SetLength(0));
+        SuccessOrQuit(dnsName.AppendTo(*message));
+
+        dnsName.SetFromMessage(*message, 0);
+        VerifyOrQuit(dnsName.Matches(test.mFirstLabel, test.mLabels, test.mDomain) == test.mShouldMatch);
+    }
+
+    IgnoreError(message->SetLength(0));
+    dnsName.SetFromMessage(*message, 0);
+    SuccessOrQuit(Dns::Name::AppendLabel("Name.With.Dot", *message));
+    SuccessOrQuit(Dns::Name::AppendName("_srv._udp.local.", *message));
+
+    VerifyOrQuit(dnsName.Matches("Name.With.Dot", "_srv._udp", "local."));
+    VerifyOrQuit(dnsName.Matches("nAme.with.dOT", "_srv._udp", "local."));
+    VerifyOrQuit(dnsName.Matches("Name.With.Dot", "_srv", "_udp.local."));
+
+    VerifyOrQuit(!dnsName.Matches("Name", "With.Dot._srv._udp", "local."));
+    VerifyOrQuit(!dnsName.Matches("Name.", "With.Dot._srv._udp", "local."));
+    VerifyOrQuit(!dnsName.Matches("Name.With", "Dot._srv._udp", "local."));
+
+    VerifyOrQuit(!dnsName.Matches("Name.With.Dott", "_srv._udp", "local."));
+    VerifyOrQuit(!dnsName.Matches("Name.With.Dot.", "_srv._udp", "local."));
+    VerifyOrQuit(!dnsName.Matches("Name.With.Dot", "_srv._tcp", "local."));
+    VerifyOrQuit(!dnsName.Matches("Name.With.Dot", "_srv._udp", "arpa."));
+
+    offset = 0;
+    SuccessOrQuit(Dns::Name::ReadName(*message, offset, name));
+    dnsName.Set(name);
+
+    VerifyOrQuit(dnsName.Matches("Name.With.Dot", "_srv._udp", "local."));
+    VerifyOrQuit(dnsName.Matches("nAme.with.dOT", "_srv._udp", "local."));
+    VerifyOrQuit(dnsName.Matches("Name.With.Dot", "_srv", "_udp.local."));
+    VerifyOrQuit(!dnsName.Matches("Name.With.Dott", "_srv._udp", "local."));
+    VerifyOrQuit(!dnsName.Matches("Name.With.Dot.", "_srv._udp", "local."));
+    VerifyOrQuit(!dnsName.Matches("Name.With.Dot", "_srv._tcp", "local."));
+    VerifyOrQuit(!dnsName.Matches("Name.With.Dot", "_srv._udp", "arpa."));
+
     message->Free();
     testFreeInstance(instance);
 }
@@ -528,6 +625,13 @@
     static const char *kName3Labels[] = {"ISI", "ARPA"};
     static const char *kName4Labels[] = {"Human.Readable", "F", "ISI", "ARPA"};
 
+    static const char *kName1MultiLabels[]  = {"F.ISI", "ARPA"};
+    static const char *kName2MultiLabels1[] = {"FOO", "F.ISI.ARPA."};
+    static const char *kName2MultiLabels2[] = {"FOO.F.", "ISI.ARPA."};
+
+    static const char kName1BadMultiLabels[] = "F.ISI.ARPA.MORE";
+    static const char kName2BadMultiLabels[] = "FOO.F.IS";
+
     static const char kExpectedReadName1[] = "F.ISI.ARPA.";
     static const char kExpectedReadName2[] = "FOO.F.ISI.ARPA.";
     static const char kExpectedReadName3[] = "ISI.ARPA.";
@@ -633,17 +737,41 @@
                  "Name::ReadLabel() failed at end of the name");
 
     offset = name1Offset;
-    SuccessOrQuit(Dns::Name::ReadName(*message, offset, name, sizeof(name)));
+    SuccessOrQuit(Dns::Name::ReadName(*message, offset, name));
     printf("Read name =\"%s\"\n", name);
     VerifyOrQuit(strcmp(name, kExpectedReadName1) == 0, "Name::ReadName() did not return expected name");
     VerifyOrQuit(offset == name1Offset + sizeof(kEncodedName), "Name::ReadName() returned incorrect offset");
 
     offset = name1Offset;
-
     for (const char *nameLabel : kName1Labels)
     {
         SuccessOrQuit(Dns::Name::CompareLabel(*message, offset, nameLabel));
     }
+    SuccessOrQuit(Dns::Name::CompareName(*message, offset, "."));
+
+    offset = name1Offset;
+    for (const char *nameLabel : kName1Labels)
+    {
+        SuccessOrQuit(Dns::Name::CompareMultipleLabels(*message, offset, nameLabel));
+    }
+    SuccessOrQuit(Dns::Name::CompareName(*message, offset, "."));
+
+    offset = name1Offset;
+    SuccessOrQuit(Dns::Name::CompareMultipleLabels(*message, offset, kExpectedReadName1));
+    SuccessOrQuit(Dns::Name::CompareName(*message, offset, "."));
+
+    offset = name1Offset;
+    VerifyOrQuit(Dns::Name::CompareMultipleLabels(*message, offset, kBadName) == kErrorNotFound);
+
+    offset = name1Offset;
+    VerifyOrQuit(Dns::Name::CompareMultipleLabels(*message, offset, kName1BadMultiLabels) == kErrorNotFound);
+
+    offset = name1Offset;
+    for (const char *nameLabels : kName1MultiLabels)
+    {
+        SuccessOrQuit(Dns::Name::CompareMultipleLabels(*message, offset, nameLabels));
+    }
+    SuccessOrQuit(Dns::Name::CompareName(*message, offset, "."));
 
     offset = name1Offset;
     SuccessOrQuit(Dns::Name::CompareName(*message, offset, kExpectedReadName1));
@@ -690,17 +818,48 @@
                  "Name::ReadLabel() failed at end of the name");
 
     offset = name2Offset;
-    SuccessOrQuit(Dns::Name::ReadName(*message, offset, name, sizeof(name)));
+    SuccessOrQuit(Dns::Name::ReadName(*message, offset, name));
     printf("Read name =\"%s\"\n", name);
     VerifyOrQuit(strcmp(name, kExpectedReadName2) == 0, "Name::ReadName() did not return expected name");
     VerifyOrQuit(offset == name2Offset + kName2EncodedSize, "Name::ReadName() returned incorrect offset");
 
     offset = name2Offset;
-
     for (const char *nameLabel : kName2Labels)
     {
         SuccessOrQuit(Dns::Name::CompareLabel(*message, offset, nameLabel));
     }
+    SuccessOrQuit(Dns::Name::CompareName(*message, offset, "."));
+
+    offset = name2Offset;
+    for (const char *nameLabel : kName2Labels)
+    {
+        SuccessOrQuit(Dns::Name::CompareMultipleLabels(*message, offset, nameLabel));
+    }
+    SuccessOrQuit(Dns::Name::CompareName(*message, offset, "."));
+
+    offset = name2Offset;
+    SuccessOrQuit(Dns::Name::CompareMultipleLabels(*message, offset, kExpectedReadName2));
+    SuccessOrQuit(Dns::Name::CompareName(*message, offset, "."));
+
+    offset = name2Offset;
+    VerifyOrQuit(Dns::Name::CompareMultipleLabels(*message, offset, kBadName) == kErrorNotFound);
+
+    offset = name2Offset;
+    VerifyOrQuit(Dns::Name::CompareMultipleLabels(*message, offset, kName2BadMultiLabels) == kErrorNotFound);
+
+    offset = name2Offset;
+    for (const char *nameLabels : kName2MultiLabels1)
+    {
+        SuccessOrQuit(Dns::Name::CompareMultipleLabels(*message, offset, nameLabels));
+    }
+    SuccessOrQuit(Dns::Name::CompareName(*message, offset, "."));
+
+    offset = name2Offset;
+    for (const char *nameLabels : kName2MultiLabels2)
+    {
+        SuccessOrQuit(Dns::Name::CompareMultipleLabels(*message, offset, nameLabels));
+    }
+    SuccessOrQuit(Dns::Name::CompareName(*message, offset, "."));
 
     offset = name2Offset;
     SuccessOrQuit(Dns::Name::CompareName(*message, offset, kExpectedReadName2));
@@ -747,17 +906,28 @@
                  "Name::ReadLabel() failed at end of the name");
 
     offset = name3Offset;
-    SuccessOrQuit(Dns::Name::ReadName(*message, offset, name, sizeof(name)));
+    SuccessOrQuit(Dns::Name::ReadName(*message, offset, name));
     printf("Read name =\"%s\"\n", name);
     VerifyOrQuit(strcmp(name, kExpectedReadName3) == 0, "Name::ReadName() did not return expected name");
     VerifyOrQuit(offset == name3Offset + kName3EncodedSize, "Name::ReadName() returned incorrect offset");
 
     offset = name3Offset;
-
     for (const char *nameLabel : kName3Labels)
     {
         SuccessOrQuit(Dns::Name::CompareLabel(*message, offset, nameLabel));
     }
+    SuccessOrQuit(Dns::Name::CompareName(*message, offset, "."));
+
+    offset = name3Offset;
+    for (const char *nameLabel : kName3Labels)
+    {
+        SuccessOrQuit(Dns::Name::CompareMultipleLabels(*message, offset, nameLabel));
+    }
+    SuccessOrQuit(Dns::Name::CompareName(*message, offset, "."));
+
+    offset = name3Offset;
+    SuccessOrQuit(Dns::Name::CompareMultipleLabels(*message, offset, kExpectedReadName3));
+    SuccessOrQuit(Dns::Name::CompareName(*message, offset, "."));
 
     offset = name3Offset;
     SuccessOrQuit(Dns::Name::CompareName(*message, offset, kExpectedReadName3));
@@ -801,17 +971,24 @@
 
     // `ReadName()` for name-4 should still succeed since only the first label contains dot char
     offset = name4Offset;
-    SuccessOrQuit(Dns::Name::ReadName(*message, offset, name, sizeof(name)));
+    SuccessOrQuit(Dns::Name::ReadName(*message, offset, name));
     printf("Read name =\"%s\"\n", name);
     VerifyOrQuit(strcmp(name, kExpectedReadName4) == 0, "Name::ReadName() did not return expected name");
     VerifyOrQuit(offset == name4Offset + kName4EncodedSize, "Name::ParseName() returned incorrect offset");
 
     offset = name4Offset;
-
     for (const char *nameLabel : kName4Labels)
     {
         SuccessOrQuit(Dns::Name::CompareLabel(*message, offset, nameLabel));
     }
+    SuccessOrQuit(Dns::Name::CompareName(*message, offset, "."));
+
+    offset = name4Offset;
+    for (const char *nameLabel : kName4Labels)
+    {
+        SuccessOrQuit(Dns::Name::CompareMultipleLabels(*message, offset, nameLabel));
+    }
+    SuccessOrQuit(Dns::Name::CompareName(*message, offset, "."));
 
     offset = name4Offset;
     SuccessOrQuit(Dns::Name::CompareName(*message, offset, *message, offset), "Name::CompareName() with itself failed");
@@ -848,11 +1025,11 @@
     SuccessOrQuit(Dns::Name::CompareName(*message2, offset, dnsName4));
 
     offset = 0;
-    SuccessOrQuit(Dns::Name::ReadName(*message2, offset, name, sizeof(name)));
+    SuccessOrQuit(Dns::Name::ReadName(*message2, offset, name));
     printf("- Name1 after `AppendTo()`: \"%s\"\n", name);
-    SuccessOrQuit(Dns::Name::ReadName(*message2, offset, name, sizeof(name)));
+    SuccessOrQuit(Dns::Name::ReadName(*message2, offset, name));
     printf("- Name2 after `AppendTo()`: \"%s\"\n", name);
-    SuccessOrQuit(Dns::Name::ReadName(*message2, offset, name, sizeof(name)));
+    SuccessOrQuit(Dns::Name::ReadName(*message2, offset, name));
     printf("- Name3 after `AppendTo()`: \"%s\"\n", name);
     // `ReadName()` for name-4 will fail due to first label containing dot char.
 
@@ -913,9 +1090,9 @@
     Dns::ResourceRecord record;
     Ip6::Address        hostAddress;
 
-    char    label[Dns::Name::kMaxLabelSize];
-    char    name[Dns::Name::kMaxNameSize];
-    uint8_t buffer[kMaxSize];
+    Dns::Name::LabelBuffer label;
+    Dns::Name::Buffer      name;
+    uint8_t                buffer[kMaxSize];
 
     printf("================================================================\n");
     printf("TestHeaderAndResourceRecords()\n");
@@ -1050,7 +1227,7 @@
         SuccessOrQuit(Dns::ResourceRecord::ReadRecord(*message, offset, ptrRecord));
         VerifyOrQuit(ptrRecord.GetTtl() == kTtl, "Read PTR is incorrect");
 
-        SuccessOrQuit(ptrRecord.ReadPtrName(*message, offset, label, sizeof(label), name, sizeof(name)));
+        SuccessOrQuit(ptrRecord.ReadPtrName(*message, offset, label, name));
         VerifyOrQuit(strcmp(label, instanceLabel) == 0, "Inst label is incorrect");
         VerifyOrQuit(strcmp(name, kServiceName) == 0);
 
@@ -1077,7 +1254,7 @@
         VerifyOrQuit(numRecords == prevNumRecords - 1, "Incorrect num records");
         SuccessOrQuit(Dns::ResourceRecord::ReadRecord(*message, offset, ptrRecord));
         VerifyOrQuit(ptrRecord.GetTtl() == kTtl, "Read PTR is incorrect");
-        SuccessOrQuit(ptrRecord.ReadPtrName(*message, offset, label, sizeof(label), name, sizeof(name)));
+        SuccessOrQuit(ptrRecord.ReadPtrName(*message, offset, label, name));
         printf("    \"%s\" PTR %u %d inst:\"%s\" at \"%s\"\n", kServiceName, ptrRecord.GetTtl(), ptrRecord.GetLength(),
                label, name);
     }
@@ -1125,7 +1302,7 @@
         VerifyOrQuit(srvRecord.GetPort() == kSrvPort);
         VerifyOrQuit(srvRecord.GetWeight() == kSrvWeight);
         VerifyOrQuit(srvRecord.GetPriority() == kSrvPriority);
-        SuccessOrQuit(srvRecord.ReadTargetHostName(*message, offset, name, sizeof(name)));
+        SuccessOrQuit(srvRecord.ReadTargetHostName(*message, offset, name));
         VerifyOrQuit(strcmp(name, kHostName) == 0);
         printf("    \"%s\" SRV %u %d %d %d %d \"%s\"\n", instanceName, srvRecord.GetTtl(), srvRecord.GetLength(),
                srvRecord.GetPort(), srvRecord.GetWeight(), srvRecord.GetPriority(), name);
diff --git a/tests/unit/test_dns_client.cpp b/tests/unit/test_dns_client.cpp
index 59efab7..74c5598 100644
--- a/tests/unit/test_dns_client.cpp
+++ b/tests/unit/test_dns_client.cpp
@@ -357,10 +357,10 @@
 {
     void Reset(void) { mCallbackCount = 0; }
 
-    uint16_t mCallbackCount;
-    Error    mError;
-    char     mServiceName[Dns::Name::kMaxNameSize];
-    uint16_t mNumInstances;
+    uint16_t          mCallbackCount;
+    Error             mError;
+    Dns::Name::Buffer mServiceName;
+    uint16_t          mNumInstances;
 };
 
 static BrowseInfo sBrowseInfo;
@@ -384,8 +384,8 @@
 
     for (uint16_t index = 0;; index++)
     {
-        char  instLabel[Dns::Name::kMaxLabelSize];
-        Error error;
+        Dns::Name::LabelBuffer instLabel;
+        Error                  error;
 
         error = response.GetServiceInstance(index, instLabel, sizeof(instLabel));
 
@@ -423,7 +423,7 @@
     uint16_t                 mCallbackCount;
     Error                    mError;
     Dns::Client::ServiceInfo mInfo;
-    char                     mNameBuffer[Dns::Name::kMaxNameSize];
+    Dns::Name::Buffer        mNameBuffer;
     uint8_t                  mTxtBuffer[kMaxTxtBuffer];
     Ip6::Address             mHostAddresses[kMaxHostAddresses];
     uint8_t                  mNumHostAddresses;
@@ -434,8 +434,8 @@
 void ServiceCallback(otError aError, const otDnsServiceResponse *aResponse, void *aContext)
 {
     const Dns::Client::ServiceResponse &response = AsCoreType(aResponse);
-    char                                instLabel[Dns::Name::kMaxLabelSize];
-    char                                serviceName[Dns::Name::kMaxNameSize];
+    Dns::Name::LabelBuffer              instLabel;
+    Dns::Name::Buffer                   serviceName;
 
     Log("ServiceCallback");
     Log("   Error: %s", ErrorToString(aError));
@@ -912,8 +912,8 @@
 
 //----------------------------------------------------------------------------------------------------------------------
 
-char sLastSubscribeName[Dns::Name::kMaxNameSize];
-char sLastUnsubscribeName[Dns::Name::kMaxNameSize];
+Dns::Name::Buffer sLastSubscribeName;
+Dns::Name::Buffer sLastUnsubscribeName;
 
 void QuerySubscribe(void *aContext, const char *aFullName)
 {
diff --git a/tests/unit/test_heap_string.cpp b/tests/unit/test_heap_string.cpp
index cc72d5a..3feb4b6 100644
--- a/tests/unit/test_heap_string.cpp
+++ b/tests/unit/test_heap_string.cpp
@@ -171,11 +171,15 @@
 
     PrintData(aData);
 
+    VerifyOrQuit(aData.Matches(aBytes, aLength));
+    VerifyOrQuit(!aData.Matches(aBytes, aLength + 1));
+
     if (aLength == 0)
     {
         VerifyOrQuit(aData.IsNull());
         VerifyOrQuit(aData.GetBytes() == nullptr);
         VerifyOrQuit(aData.GetLength() == 0);
+        VerifyOrQuit(aData.Matches(nullptr, 0));
     }
     else
     {
@@ -186,6 +190,10 @@
 
         aData.CopyBytesTo(buffer);
         VerifyOrQuit(memcmp(buffer, aBytes, aLength) == 0, "CopyBytesTo() failed");
+
+        VerifyOrQuit(aData.Matches(buffer, aLength));
+        buffer[aLength - 1]++;
+        VerifyOrQuit(!aData.Matches(buffer, aLength));
     }
 }
 
@@ -223,6 +231,10 @@
     printf("After constructor\n");
     VerifyData(data, nullptr, 0);
 
+    VerifyOrQuit(data.Matches(nullptr, 0));
+    VerifyOrQuit(data.Matches(kData1, 0));
+    VerifyOrQuit(!data.Matches(kData1, 1));
+
     printf("------------------------------------------------------------------------------------\n");
     printf("SetFrom(aBuffer, aLength)\n");
 
diff --git a/tests/unit/test_ip_address.cpp b/tests/unit/test_ip_address.cpp
index 746ac02..2210026 100644
--- a/tests/unit/test_ip_address.cpp
+++ b/tests/unit/test_ip_address.cpp
@@ -758,6 +758,44 @@
     }
 }
 
+void TestIp4MappedIp6Address(void)
+{
+    const uint8_t kIp4Address[] = {192, 0, 2, 33};
+
+    const char *kInvalidIp4MappedFormats[] = {
+        "8000::ffff:192.0.2.23",     "0:400::ffff:192.0.2.23", "0:0:1::ffff:192.0.2.23", "0:0:0:4:0:ffff:192.0.2.23",
+        "0:0:0:0:1:ffff:192.0.2.23", "::fffe:192.0.2.23",      "::efff:192.0.2.23",
+    };
+
+    Ip4::Address expectedIp4Address;
+    Ip4::Address ip4Address;
+    Ip6::Address expectedIp6Address;
+    Ip6::Address ip6Address;
+
+    printf("\nTestIp4MappedIp6Address()\n");
+
+    expectedIp4Address.SetBytes(kIp4Address);
+
+    SuccessOrQuit(expectedIp6Address.FromString("::ffff:192.0.2.33"));
+    ip6Address.SetToIp4Mapped(expectedIp4Address);
+
+    printf("IPv4-mapped IPv6 address: %s\n", ip6Address.ToString().AsCString());
+
+    VerifyOrQuit(ip6Address.IsIp4Mapped());
+    VerifyOrQuit(ip6Address == expectedIp6Address);
+
+    SuccessOrQuit(ip4Address.ExtractFromIp4MappedIp6Address(ip6Address));
+    VerifyOrQuit(ip4Address == expectedIp4Address);
+
+    for (const char *invalidIp4MappedAddr : kInvalidIp4MappedFormats)
+    {
+        SuccessOrQuit(ip6Address.FromString(invalidIp4MappedAddr));
+        printf("Invalid IPv4-mapped IPv6 address: %s -> %s\n", invalidIp4MappedAddr, ip6Address.ToString().AsCString());
+        VerifyOrQuit(!ip6Address.IsIp4Mapped());
+        VerifyOrQuit(ip4Address.ExtractFromIp4MappedIp6Address(ip6Address) != kErrorNone);
+    }
+}
+
 void TestIp4Ip6Translation(void)
 {
     struct TestCase
@@ -884,6 +922,7 @@
     ot::TestIp6PrefixFromString();
     ot::TestIp6Prefix();
     ot::TestIp6PrefixTidy();
+    ot::TestIp4MappedIp6Address();
     ot::TestIp4Ip6Translation();
     ot::TestIp4Cidr();
     ot::TestIp4CidrFromString();
diff --git a/tests/unit/test_network_data.cpp b/tests/unit/test_network_data.cpp
index 2868709..4740a2f 100644
--- a/tests/unit/test_network_data.cpp
+++ b/tests/unit/test_network_data.cpp
@@ -86,32 +86,32 @@
            (aConfig1.mDefaultRoute == aConfig2.mDefaultRoute) && (aConfig1.mOnMesh == aConfig2.mOnMesh);
 }
 
-template <uint8_t kLength>
-void VerifyRlocsArray(const uint16_t *aRlocs, uint16_t aRlocsLength, const uint16_t (&aExpectedRlocs)[kLength])
+template <uint8_t kLength> void VerifyRlocsArray(const Rlocs &aRlocs, const uint16_t (&aExpectedRlocs)[kLength])
 {
-    VerifyOrQuit(aRlocsLength == kLength);
+    VerifyOrQuit(aRlocs.GetLength() == kLength);
 
     printf("\nRLOCs: { ");
 
-    for (uint16_t index = 0; index < aRlocsLength; index++)
+    for (uint16_t rloc : aRlocs)
     {
-        VerifyOrQuit(aRlocs[index] == aExpectedRlocs[index]);
-        printf("0x%04x ", aRlocs[index]);
+        printf("0x%04x ", rloc);
     }
 
     printf("}");
+
+    for (uint16_t index = 0; index < kLength; index++)
+    {
+        VerifyOrQuit(aRlocs.Contains(aExpectedRlocs[index]));
+    }
 }
 
 void TestNetworkDataIterator(void)
 {
-    static constexpr uint8_t kMaxRlocsArray = 10;
-
     Instance           *instance;
     Iterator            iter = kIteratorInit;
     ExternalRouteConfig rconfig;
     OnMeshPrefixConfig  pconfig;
-    uint16_t            rlocs[kMaxRlocsArray];
-    uint8_t             rlocsLength;
+    Rlocs               rlocs;
 
     instance = testInitInstance();
     VerifyOrQuit(instance != nullptr);
@@ -168,19 +168,25 @@
             VerifyOrQuit(CompareExternalRouteConfig(rconfig, route));
         }
 
-        rlocsLength = GetArrayLength(rlocs);
-        SuccessOrQuit(netData.FindBorderRouters(kAnyRole, rlocs, rlocsLength));
-        VerifyRlocsArray(rlocs, rlocsLength, kRlocs);
+        netData.FindRlocs(kAnyBrOrServer, kAnyRole, rlocs);
+        VerifyRlocsArray(rlocs, kRlocs);
+
+        netData.FindRlocs(kAnyBrOrServer, kRouterRoleOnly, rlocs);
+        VerifyRlocsArray(rlocs, kRlocs);
+
+        netData.FindRlocs(kAnyBrOrServer, kChildRoleOnly, rlocs);
+        VerifyOrQuit(rlocs.GetLength() == 0);
+
+        netData.FindRlocs(kBrProvidingExternalIpConn, kAnyRole, rlocs);
+        VerifyRlocsArray(rlocs, kRlocs);
         VerifyOrQuit(netData.CountBorderRouters(kAnyRole) == GetArrayLength(kRlocs));
 
-        rlocsLength = GetArrayLength(rlocs);
-        SuccessOrQuit(netData.FindBorderRouters(kRouterRoleOnly, rlocs, rlocsLength));
-        VerifyRlocsArray(rlocs, rlocsLength, kRlocs);
+        netData.FindRlocs(kBrProvidingExternalIpConn, kRouterRoleOnly, rlocs);
+        VerifyRlocsArray(rlocs, kRlocs);
         VerifyOrQuit(netData.CountBorderRouters(kRouterRoleOnly) == GetArrayLength(kRlocs));
 
-        rlocsLength = GetArrayLength(rlocs);
-        SuccessOrQuit(netData.FindBorderRouters(kChildRoleOnly, rlocs, rlocsLength));
-        VerifyOrQuit(rlocsLength == 0);
+        netData.FindRlocs(kBrProvidingExternalIpConn, kChildRoleOnly, rlocs);
+        VerifyOrQuit(rlocs.GetLength() == 0);
         VerifyOrQuit(netData.CountBorderRouters(kChildRoleOnly) == 0);
 
         for (uint16_t rloc16 : kRlocs)
@@ -284,33 +290,29 @@
             VerifyOrQuit(CompareExternalRouteConfig(rconfig, route));
         }
 
-        rlocsLength = GetArrayLength(rlocs);
-        SuccessOrQuit(netData.FindBorderRouters(kAnyRole, rlocs, rlocsLength));
-        VerifyRlocsArray(rlocs, rlocsLength, kRlocsAnyRole);
+        netData.FindRlocs(kAnyBrOrServer, kAnyRole, rlocs);
+        VerifyRlocsArray(rlocs, kRlocsAnyRole);
+
+        netData.FindRlocs(kAnyBrOrServer, kRouterRoleOnly, rlocs);
+        VerifyRlocsArray(rlocs, kRlocsRouterRole);
+
+        netData.FindRlocs(kAnyBrOrServer, kChildRoleOnly, rlocs);
+        VerifyRlocsArray(rlocs, kRlocsChildRole);
+
+        netData.FindRlocs(kBrProvidingExternalIpConn, kAnyRole, rlocs);
+        VerifyRlocsArray(rlocs, kRlocsAnyRole);
         VerifyOrQuit(netData.CountBorderRouters(kAnyRole) == GetArrayLength(kRlocsAnyRole));
 
-        rlocsLength = GetArrayLength(rlocs);
-        SuccessOrQuit(netData.FindBorderRouters(kRouterRoleOnly, rlocs, rlocsLength));
-        VerifyRlocsArray(rlocs, rlocsLength, kRlocsRouterRole);
+        netData.FindRlocs(kBrProvidingExternalIpConn, kRouterRoleOnly, rlocs);
+        VerifyRlocsArray(rlocs, kRlocsRouterRole);
         VerifyOrQuit(netData.CountBorderRouters(kRouterRoleOnly) == GetArrayLength(kRlocsRouterRole));
 
-        rlocsLength = GetArrayLength(rlocs);
-        SuccessOrQuit(netData.FindBorderRouters(kChildRoleOnly, rlocs, rlocsLength));
-        VerifyRlocsArray(rlocs, rlocsLength, kRlocsChildRole);
+        netData.FindRlocs(kBrProvidingExternalIpConn, kChildRoleOnly, rlocs);
+        VerifyRlocsArray(rlocs, kRlocsChildRole);
         VerifyOrQuit(netData.CountBorderRouters(kChildRoleOnly) == GetArrayLength(kRlocsChildRole));
 
-        // Test failure case when given array is smaller than number of RLOCs.
-        rlocsLength = GetArrayLength(kRlocsAnyRole) - 1;
-        VerifyOrQuit(netData.FindBorderRouters(kAnyRole, rlocs, rlocsLength) == kErrorNoBufs);
-        VerifyOrQuit(rlocsLength == GetArrayLength(kRlocsAnyRole) - 1);
-        for (uint8_t index = 0; index < rlocsLength; index++)
-        {
-            VerifyOrQuit(rlocs[index] == kRlocsAnyRole[index]);
-        }
-
-        rlocsLength = GetArrayLength(kRlocsAnyRole);
-        SuccessOrQuit(netData.FindBorderRouters(kAnyRole, rlocs, rlocsLength));
-        VerifyRlocsArray(rlocs, rlocsLength, kRlocsAnyRole);
+        netData.FindRlocs(kBrProvidingExternalIpConn, kAnyRole, rlocs);
+        VerifyRlocsArray(rlocs, kRlocsAnyRole);
 
         for (uint16_t rloc16 : kRlocsAnyRole)
         {
@@ -434,10 +436,13 @@
             },
         };
 
-        const uint16_t kRlocsAnyRole[]     = {0xec00, 0x2801, 0x2800};
-        const uint16_t kRlocsRouterRole[]  = {0xec00, 0x2800};
-        const uint16_t kRlocsChildRole[]   = {0x2801};
-        const uint16_t kNonExistingRlocs[] = {0x6000, 0x0000, 0x2806, 0x4c00};
+        const uint16_t kRlocsAnyRole[]      = {0xec00, 0x2801, 0x2800, 0x4c00};
+        const uint16_t kRlocsRouterRole[]   = {0xec00, 0x2800, 0x4c00};
+        const uint16_t kRlocsChildRole[]    = {0x2801};
+        const uint16_t kBrRlocsAnyRole[]    = {0xec00, 0x2801, 0x2800};
+        const uint16_t kBrRlocsRouterRole[] = {0xec00, 0x2800};
+        const uint16_t kBrRlocsChildRole[]  = {0x2801};
+        const uint16_t kNonExistingRlocs[]  = {0x6000, 0x0000, 0x2806, 0x4c00};
 
         NetworkData netData(*instance, kNetworkData, sizeof(kNetworkData));
 
@@ -462,22 +467,28 @@
             VerifyOrQuit(CompareOnMeshPrefixConfig(pconfig, prefix));
         }
 
-        rlocsLength = GetArrayLength(rlocs);
-        SuccessOrQuit(netData.FindBorderRouters(kAnyRole, rlocs, rlocsLength));
-        VerifyRlocsArray(rlocs, rlocsLength, kRlocsAnyRole);
-        VerifyOrQuit(netData.CountBorderRouters(kAnyRole) == GetArrayLength(kRlocsAnyRole));
+        netData.FindRlocs(kAnyBrOrServer, kAnyRole, rlocs);
+        VerifyRlocsArray(rlocs, kRlocsAnyRole);
 
-        rlocsLength = GetArrayLength(rlocs);
-        SuccessOrQuit(netData.FindBorderRouters(kRouterRoleOnly, rlocs, rlocsLength));
-        VerifyRlocsArray(rlocs, rlocsLength, kRlocsRouterRole);
-        VerifyOrQuit(netData.CountBorderRouters(kRouterRoleOnly) == GetArrayLength(kRlocsRouterRole));
+        netData.FindRlocs(kAnyBrOrServer, kRouterRoleOnly, rlocs);
+        VerifyRlocsArray(rlocs, kRlocsRouterRole);
 
-        rlocsLength = GetArrayLength(rlocs);
-        SuccessOrQuit(netData.FindBorderRouters(kChildRoleOnly, rlocs, rlocsLength));
-        VerifyRlocsArray(rlocs, rlocsLength, kRlocsChildRole);
-        VerifyOrQuit(netData.CountBorderRouters(kChildRoleOnly) == GetArrayLength(kRlocsChildRole));
+        netData.FindRlocs(kAnyBrOrServer, kChildRoleOnly, rlocs);
+        VerifyRlocsArray(rlocs, kRlocsChildRole);
 
-        for (uint16_t rloc16 : kRlocsAnyRole)
+        netData.FindRlocs(kBrProvidingExternalIpConn, kAnyRole, rlocs);
+        VerifyRlocsArray(rlocs, kBrRlocsAnyRole);
+        VerifyOrQuit(netData.CountBorderRouters(kAnyRole) == GetArrayLength(kBrRlocsAnyRole));
+
+        netData.FindRlocs(kBrProvidingExternalIpConn, kRouterRoleOnly, rlocs);
+        VerifyRlocsArray(rlocs, kBrRlocsRouterRole);
+        VerifyOrQuit(netData.CountBorderRouters(kRouterRoleOnly) == GetArrayLength(kBrRlocsRouterRole));
+
+        netData.FindRlocs(kBrProvidingExternalIpConn, kChildRoleOnly, rlocs);
+        VerifyRlocsArray(rlocs, kBrRlocsChildRole);
+        VerifyOrQuit(netData.CountBorderRouters(kChildRoleOnly) == GetArrayLength(kBrRlocsChildRole));
+
+        for (uint16_t rloc16 : kBrRlocsAnyRole)
         {
             VerifyOrQuit(netData.ContainsBorderRouterWithRloc(rloc16));
         }
@@ -681,17 +692,34 @@
             {"fdde:ad00:beef:0:0:ff:fe00:6c00", 0xcd12, Service::DnsSrpUnicast::kFromServerData, 0x6c00},
         };
 
+        const uint16_t kExpectedRlocs[] = {0x6c00, 0x2800, 0x4c00, 0x0000};
+
         const uint8_t kPreferredAnycastEntryIndex = 2;
 
         Service::Manager            &manager = instance->Get<Service::Manager>();
         Service::Manager::Iterator   iterator;
         Service::DnsSrpAnycast::Info anycastInfo;
         Service::DnsSrpUnicast::Info unicastInfo;
+        Rlocs                        rlocs;
 
         reinterpret_cast<TestLeader &>(instance->Get<Leader>()).Populate(kNetworkData, sizeof(kNetworkData));
 
         DumpBuffer("netdata", kNetworkData, sizeof(kNetworkData));
 
+        // Verify `FindRlocs()`
+
+        instance->Get<Leader>().FindRlocs(kAnyBrOrServer, kAnyRole, rlocs);
+        VerifyRlocsArray(rlocs, kExpectedRlocs);
+
+        instance->Get<Leader>().FindRlocs(kAnyBrOrServer, kRouterRoleOnly, rlocs);
+        VerifyRlocsArray(rlocs, kExpectedRlocs);
+
+        instance->Get<Leader>().FindRlocs(kAnyBrOrServer, kChildRoleOnly, rlocs);
+        VerifyOrQuit(rlocs.GetLength() == 0);
+
+        instance->Get<Leader>().FindRlocs(kBrProvidingExternalIpConn, kAnyRole, rlocs);
+        VerifyOrQuit(rlocs.GetLength() == 0);
+
         // Verify all the "DNS/SRP Anycast Service" entries in Network Data
 
         printf("\n- - - - - - - - - - - - - - - - - - - -");
diff --git a/tests/unit/test_platform.cpp b/tests/unit/test_platform.cpp
index 8c339d4..2904f60 100644
--- a/tests/unit/test_platform.cpp
+++ b/tests/unit/test_platform.cpp
@@ -35,6 +35,9 @@
 
 #include <stdio.h>
 #include <sys/time.h>
+#ifdef OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
+#include <openthread/platform/ble.h>
+#endif
 
 enum
 {
@@ -401,6 +404,8 @@
     return OT_ERROR_NONE;
 }
 
+OT_TOOL_WEAK otError otPlatRadioResetCsl(otInstance *) { return OT_ERROR_NONE; }
+
 OT_TOOL_WEAK void otPlatRadioUpdateCslSampleTime(otInstance *, uint32_t) {}
 
 OT_TOOL_WEAK uint8_t otPlatRadioGetCslAccuracy(otInstance *)
@@ -421,6 +426,10 @@
 OT_TOOL_WEAK void otPlatTrelSend(otInstance *, const uint8_t *, uint16_t, const otSockAddr *) {}
 
 OT_TOOL_WEAK void otPlatTrelRegisterService(otInstance *, uint16_t, const uint8_t *, uint8_t) {}
+
+OT_TOOL_WEAK const otPlatTrelCounters *otPlatTrelGetCounters(otInstance *) { return nullptr; }
+
+OT_TOOL_WEAK void otPlatTrelResetCounters(otInstance *) {}
 #endif
 
 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
@@ -681,5 +690,129 @@
 
 OT_TOOL_WEAK otError otPlatSetMcuPowerState(otInstance *aInstance, otPlatMcuPowerState aState) { return OT_ERROR_NONE; }
 #endif // OPENTHREAD_CONFIG_NCP_ENABLE_MCU_POWER_STATE_CONTROL
+#ifdef OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
+otError otPlatBleEnable(otInstance *aInstance)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+    return OT_ERROR_NONE;
+}
+
+otError otPlatBleDisable(otInstance *aInstance)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+    return OT_ERROR_NONE;
+}
+
+otError otPlatBleGapAdvStart(otInstance *aInstance, uint16_t aInterval)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+    OT_UNUSED_VARIABLE(aInterval);
+    return OT_ERROR_NONE;
+}
+
+otError otPlatBleGapAdvStop(otInstance *aInstance)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+    return OT_ERROR_NONE;
+}
+
+otError otPlatBleGapDisconnect(otInstance *aInstance)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+    return OT_ERROR_NONE;
+}
+
+otError otPlatBleGattMtuGet(otInstance *aInstance, uint16_t *aMtu)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+    OT_UNUSED_VARIABLE(aMtu);
+    return OT_ERROR_NONE;
+}
+
+otError otPlatBleGattServerIndicate(otInstance *aInstance, uint16_t aHandle, const otBleRadioPacket *aPacket)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+    OT_UNUSED_VARIABLE(aHandle);
+    OT_UNUSED_VARIABLE(aPacket);
+    return OT_ERROR_NONE;
+}
+#endif // OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
+
+#if OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE
+
+OT_TOOL_WEAK otPlatDnssdState otPlatDnssdGetState(otInstance *aInstance)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+
+    return OT_PLAT_DNSSD_STOPPED;
+}
+
+OT_TOOL_WEAK void otPlatDnssdRegisterService(otInstance                 *aInstance,
+                                             const otPlatDnssdService   *aService,
+                                             otPlatDnssdRequestId        aRequestId,
+                                             otPlatDnssdRegisterCallback aCallback)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+    OT_UNUSED_VARIABLE(aService);
+    OT_UNUSED_VARIABLE(aRequestId);
+    OT_UNUSED_VARIABLE(aCallback);
+}
+
+OT_TOOL_WEAK void otPlatDnssdUnregisterService(otInstance                 *aInstance,
+                                               const otPlatDnssdService   *aService,
+                                               otPlatDnssdRequestId        aRequestId,
+                                               otPlatDnssdRegisterCallback aCallback)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+    OT_UNUSED_VARIABLE(aService);
+    OT_UNUSED_VARIABLE(aRequestId);
+    OT_UNUSED_VARIABLE(aCallback);
+}
+
+OT_TOOL_WEAK void otPlatDnssdRegisterHost(otInstance                 *aInstance,
+                                          const otPlatDnssdHost      *aHost,
+                                          otPlatDnssdRequestId        aRequestId,
+                                          otPlatDnssdRegisterCallback aCallback)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+    OT_UNUSED_VARIABLE(aHost);
+    OT_UNUSED_VARIABLE(aRequestId);
+    OT_UNUSED_VARIABLE(aCallback);
+}
+
+OT_TOOL_WEAK void otPlatDnssdUnregisterHost(otInstance                 *aInstance,
+                                            const otPlatDnssdHost      *aHost,
+                                            otPlatDnssdRequestId        aRequestId,
+                                            otPlatDnssdRegisterCallback aCallback)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+    OT_UNUSED_VARIABLE(aHost);
+    OT_UNUSED_VARIABLE(aRequestId);
+    OT_UNUSED_VARIABLE(aCallback);
+}
+
+OT_TOOL_WEAK void otPlatDnssdRegisterKey(otInstance                 *aInstance,
+                                         const otPlatDnssdKey       *aKey,
+                                         otPlatDnssdRequestId        aRequestId,
+                                         otPlatDnssdRegisterCallback aCallback)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+    OT_UNUSED_VARIABLE(aKey);
+    OT_UNUSED_VARIABLE(aRequestId);
+    OT_UNUSED_VARIABLE(aCallback);
+}
+
+OT_TOOL_WEAK void otPlatDnssdUnregisterKey(otInstance                 *aInstance,
+                                           const otPlatDnssdKey       *aKey,
+                                           otPlatDnssdRequestId        aRequestId,
+                                           otPlatDnssdRegisterCallback aCallback)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+    OT_UNUSED_VARIABLE(aKey);
+    OT_UNUSED_VARIABLE(aRequestId);
+    OT_UNUSED_VARIABLE(aCallback);
+}
+
+#endif // OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE
 
 } // extern "C"
diff --git a/tests/unit/test_platform.h b/tests/unit/test_platform.h
index 97a9d79..f1c7eb4 100644
--- a/tests/unit/test_platform.h
+++ b/tests/unit/test_platform.h
@@ -34,12 +34,14 @@
 #include <openthread/config.h>
 #include <openthread/platform/alarm-milli.h>
 #include <openthread/platform/dns.h>
+#include <openthread/platform/dnssd.h>
 #include <openthread/platform/dso_transport.h>
 #include <openthread/platform/entropy.h>
 #include <openthread/platform/logging.h>
 #include <openthread/platform/misc.h>
 #include <openthread/platform/multipan.h>
 #include <openthread/platform/radio.h>
+#include <openthread/platform/trel.h>
 
 #include "common/code_utils.hpp"
 #include "instance/instance.hpp"
diff --git a/tests/unit/test_pskc.cpp b/tests/unit/test_pskc.cpp
index 2515637..b1a3877 100644
--- a/tests/unit/test_pskc.cpp
+++ b/tests/unit/test_pskc.cpp
@@ -34,71 +34,81 @@
 #include "test_util.h"
 
 namespace ot {
+namespace MeshCoP {
 
 #if OPENTHREAD_FTD
 
 void TestMinimumPassphrase(void)
 {
-    ot::Pskc              pskc;
-    const uint8_t         expectedPskc[] = {0x44, 0x98, 0x8e, 0x22, 0xcf, 0x65, 0x2e, 0xee,
-                                            0xcc, 0xd1, 0xe4, 0xc0, 0x1d, 0x01, 0x54, 0xf8};
-    const otExtendedPanId xpanid         = {{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}};
-    const char            passphrase[]   = "123456";
-    otInstance           *instance       = testInitInstance();
-    SuccessOrQuit(ot::MeshCoP::GeneratePskc(passphrase,
-                                            *reinterpret_cast<const ot::MeshCoP::NetworkName *>("OpenThread"),
-                                            static_cast<const ot::MeshCoP::ExtendedPanId &>(xpanid), pskc));
-    VerifyOrQuit(memcmp(pskc.m8, expectedPskc, OT_PSKC_MAX_SIZE) == 0);
+    static const otExtendedPanId kExtPanId     = {{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}};
+    static const otNetworkName   kNetworkName  = {{'O', 'p', 'e', 'n', 'T', 'h', 'r', 'e', 'a', 'd', '\0'}};
+    static const char            kPassphrase[] = "123456";
+
+    static const otPskc kExpectedPskc = {
+        {0x44, 0x98, 0x8e, 0x22, 0xcf, 0x65, 0x2e, 0xee, 0xcc, 0xd1, 0xe4, 0xc0, 0x1d, 0x01, 0x54, 0xf8}};
+
+    Instance *instance = testInitInstance();
+    Pskc      pskc;
+
+    SuccessOrQuit(GeneratePskc(kPassphrase, AsCoreType(&kNetworkName), AsCoreType(&kExtPanId), pskc));
+    VerifyOrQuit(pskc == AsCoreType(&kExpectedPskc));
+
     testFreeInstance(instance);
 }
 
 void TestMaximumPassphrase(void)
 {
-    ot::Pskc              pskc;
-    const uint8_t         expectedPskc[] = {0x9e, 0x81, 0xbd, 0x35, 0xa2, 0x53, 0x76, 0x2f,
-                                            0x80, 0xee, 0x04, 0xff, 0x2f, 0xa2, 0x85, 0xe9};
-    const otExtendedPanId xpanid         = {{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}};
-    const char            passphrase[]   = "1234567812345678"
-                                           "1234567812345678"
-                                           "1234567812345678"
-                                           "1234567812345678"
-                                           "1234567812345678"
-                                           "1234567812345678"
-                                           "1234567812345678"
-                                           "1234567812345678"
-                                           "1234567812345678"
-                                           "1234567812345678"
-                                           "1234567812345678"
-                                           "1234567812345678"
-                                           "1234567812345678"
-                                           "1234567812345678"
-                                           "1234567812345678"
-                                           "123456781234567";
+    static const otExtendedPanId kExtPanId    = {{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}};
+    static const otNetworkName   kNetworkName = {{'O', 'p', 'e', 'n', 'T', 'h', 'r', 'e', 'a', 'd', '\0'}};
 
-    otInstance *instance = testInitInstance();
-    SuccessOrQuit(ot::MeshCoP::GeneratePskc(passphrase,
-                                            *reinterpret_cast<const ot::MeshCoP::NetworkName *>("OpenThread"),
-                                            static_cast<const ot::MeshCoP::ExtendedPanId &>(xpanid), pskc));
-    VerifyOrQuit(memcmp(pskc.m8, expectedPskc, sizeof(pskc.m8)) == 0);
+    static const char kPassphrase[] = "1234567812345678"
+                                      "1234567812345678"
+                                      "1234567812345678"
+                                      "1234567812345678"
+                                      "1234567812345678"
+                                      "1234567812345678"
+                                      "1234567812345678"
+                                      "1234567812345678"
+                                      "1234567812345678"
+                                      "1234567812345678"
+                                      "1234567812345678"
+                                      "1234567812345678"
+                                      "1234567812345678"
+                                      "1234567812345678"
+                                      "1234567812345678"
+                                      "123456781234567";
+
+    static const otPskc kExpectedPskc = {
+        {0x9e, 0x81, 0xbd, 0x35, 0xa2, 0x53, 0x76, 0x2f, 0x80, 0xee, 0x04, 0xff, 0x2f, 0xa2, 0x85, 0xe9}};
+
+    Instance *instance = testInitInstance();
+    Pskc      pskc;
+
+    SuccessOrQuit(GeneratePskc(kPassphrase, AsCoreType(&kNetworkName), AsCoreType(&kExtPanId), pskc));
+    VerifyOrQuit(pskc == AsCoreType(&kExpectedPskc));
+
     testFreeInstance(instance);
 }
 
 void TestExampleInSpec(void)
 {
-    ot::Pskc              pskc;
-    const uint8_t         expectedPskc[] = {0xc3, 0xf5, 0x93, 0x68, 0x44, 0x5a, 0x1b, 0x61,
-                                            0x06, 0xbe, 0x42, 0x0a, 0x70, 0x6d, 0x4c, 0xc9};
-    const otExtendedPanId xpanid         = {{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}};
-    const char            passphrase[]   = "12SECRETPASSWORD34";
+    static const otExtendedPanId kExtPanId     = {{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}};
+    static const otNetworkName   kNetworkName  = {{'T', 'e', 's', 't', ' ', 'N', 'e', 't', 'w', 'o', 'r', 'k', '\0'}};
+    static const char            kPassphrase[] = "12SECRETPASSWORD34";
 
-    otInstance *instance = testInitInstance();
-    SuccessOrQuit(ot::MeshCoP::GeneratePskc(passphrase,
-                                            *reinterpret_cast<const ot::MeshCoP::NetworkName *>("Test Network"),
-                                            static_cast<const ot::MeshCoP::ExtendedPanId &>(xpanid), pskc));
-    VerifyOrQuit(memcmp(pskc.m8, expectedPskc, sizeof(pskc.m8)) == 0);
+    static const otPskc kExpectedPskc = {
+        {0xc3, 0xf5, 0x93, 0x68, 0x44, 0x5a, 0x1b, 0x61, 0x06, 0xbe, 0x42, 0x0a, 0x70, 0x6d, 0x4c, 0xc9}};
+
+    Instance *instance = testInitInstance();
+    Pskc      pskc;
+
+    SuccessOrQuit(GeneratePskc(kPassphrase, AsCoreType(&kNetworkName), AsCoreType(&kExtPanId), pskc));
+    VerifyOrQuit(pskc == AsCoreType(&kExpectedPskc));
+
     testFreeInstance(instance);
 }
 
+} // namespace MeshCoP
 } // namespace ot
 
 #endif // OPENTHREAD_FTD
@@ -106,9 +116,9 @@
 int main(void)
 {
 #if OPENTHREAD_FTD
-    ot::TestMinimumPassphrase();
-    ot::TestMaximumPassphrase();
-    ot::TestExampleInSpec();
+    ot::MeshCoP::TestMinimumPassphrase();
+    ot::MeshCoP::TestMaximumPassphrase();
+    ot::MeshCoP::TestExampleInSpec();
     printf("All tests passed\n");
 #else
     printf("PSKc generation is not supported on non-ftd build\n");
diff --git a/tests/unit/test_routing_manager.cpp b/tests/unit/test_routing_manager.cpp
index ef2a53b..a474de4 100644
--- a/tests/unit/test_routing_manager.cpp
+++ b/tests/unit/test_routing_manager.cpp
@@ -38,6 +38,7 @@
 #include "border_router/routing_manager.hpp"
 #include "common/arg_macros.hpp"
 #include "common/array.hpp"
+#include "common/numeric_limits.hpp"
 #include "common/time.hpp"
 #include "instance/instance.hpp"
 #include "net/icmp6.hpp"
@@ -57,6 +58,10 @@
 
 static constexpr uint32_t kValidLitime       = 2000;
 static constexpr uint32_t kPreferredLifetime = 1800;
+static constexpr uint32_t kInfiniteLifetime  = NumericLimits<uint32_t>::kMax;
+
+static constexpr uint32_t kRioValidLifetime       = 1800;
+static constexpr uint32_t kRioDeprecatingLifetime = 300;
 
 static constexpr uint16_t kMaxRaSize              = 800;
 static constexpr uint16_t kMaxDeprecatingPrefixes = 16;
@@ -121,7 +126,7 @@
 static uint8_t      sRadioTxFramePsdu[OT_RADIO_FRAME_MAX_SIZE];
 static bool         sRadioTxOngoing = false;
 
-using Icmp6Packet = Ip6::Nd::RouterAdvertMessage::Icmp6Packet;
+using Icmp6Packet = Ip6::Nd::RouterAdvert::Icmp6Packet;
 
 enum ExpectedPio
 {
@@ -155,6 +160,12 @@
 ExpectedPio sExpectedPio;    // Expected PIO in the emitted RA by BR (MUST be seen in RA to set `sRaValidated`).
 uint32_t    sOnLinkLifetime; // Valid lifetime for local on-link prefix from the last processed RA.
 
+// Indicate whether or not to check the emitted RA header (default route) lifetime
+bool sCheckRaHeaderLifetime;
+
+// Expected default route lifetime in emitted RA header by BR.
+uint32_t sExpectedRaHeaderLifetime;
+
 enum ExpectedRaHeaderFlags
 {
     kRaHeaderFlagsSkipChecking, // Skip checking the RA header flags.
@@ -224,6 +235,8 @@
 void        SendNeighborAdvert(const Ip6::Address &aAddress, const Ip6::Nd::NeighborAdvertMessage &aNaMessage);
 void        DiscoverNat64Prefix(const Ip6::Prefix &aPrefix);
 
+extern "C" {
+
 #if OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_PLATFORM_DEFINED
 void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, ...)
 {
@@ -243,8 +256,6 @@
 //----------------------------------------------------------------------------------------------------------------------
 // `otPlatRadio
 
-extern "C" {
-
 otRadioCaps otPlatRadioGetCaps(otInstance *) { return OT_RADIO_CAPS_ACK_TIMEOUT | OT_RADIO_CAPS_CSMA_BACKOFF; }
 
 otError otPlatRadioTransmit(otInstance *, otRadioFrame *)
@@ -339,6 +350,36 @@
     return OT_ERROR_NONE;
 }
 
+//----------------------------------------------------------------------------------------------------------------------
+
+Array<void *, 500> sHeapAllocatedPtrs;
+
+#if OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE
+
+void *otPlatCAlloc(size_t aNum, size_t aSize)
+{
+    void *ptr = calloc(aNum, aSize);
+
+    SuccessOrQuit(sHeapAllocatedPtrs.PushBack(ptr));
+
+    return ptr;
+}
+
+void otPlatFree(void *aPtr)
+{
+    if (aPtr != nullptr)
+    {
+        void **entry = sHeapAllocatedPtrs.Find(aPtr);
+
+        VerifyOrQuit(entry != nullptr, "A heap allocated item is freed twice");
+        sHeapAllocatedPtrs.Remove(*entry);
+    }
+
+    free(aPtr);
+}
+
+#endif
+
 } // extern "C"
 
 //---------------------------------------------------------------------------------------------------------------------
@@ -379,7 +420,7 @@
 {
     constexpr uint8_t kMaxPrefixes = 16;
 
-    Ip6::Nd::RouterAdvertMessage     raMsg(aPacket);
+    Ip6::Nd::RouterAdvert::RxMessage raMsg(aPacket);
     bool                             sawExpectedPio = false;
     Array<Ip6::Prefix, kMaxPrefixes> pioPrefixes;
     Array<Ip6::Prefix, kMaxPrefixes> rioPrefixes;
@@ -389,7 +430,10 @@
 
     VerifyOrQuit(raMsg.IsValid());
 
-    VerifyOrQuit(raMsg.GetHeader().GetRouterLifetime() == 0);
+    if (sCheckRaHeaderLifetime)
+    {
+        VerifyOrQuit(raMsg.GetHeader().GetRouterLifetime() == sExpectedRaHeaderLifetime);
+    }
 
     switch (sExpectedRaHeaderFlags)
     {
@@ -533,38 +577,9 @@
 
 //----------------------------------------------------------------------------------------------------------------------
 
-Array<void *, 500> sHeapAllocatedPtrs;
-
-#if OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE
-
-void *otPlatCAlloc(size_t aNum, size_t aSize)
-{
-    void *ptr = calloc(aNum, aSize);
-
-    SuccessOrQuit(sHeapAllocatedPtrs.PushBack(ptr));
-
-    return ptr;
-}
-
-void otPlatFree(void *aPtr)
-{
-    if (aPtr != nullptr)
-    {
-        void **entry = sHeapAllocatedPtrs.Find(aPtr);
-
-        VerifyOrQuit(entry != nullptr, "A heap allocated item is freed twice");
-        sHeapAllocatedPtrs.Remove(*entry);
-    }
-
-    free(aPtr);
-}
-#endif
-
-//----------------------------------------------------------------------------------------------------------------------
-
 void LogRouterAdvert(const Icmp6Packet &aPacket)
 {
-    Ip6::Nd::RouterAdvertMessage raMsg(aPacket);
+    Ip6::Nd::RouterAdvert::RxMessage raMsg(aPacket);
 
     VerifyOrQuit(raMsg.IsValid());
 
@@ -848,17 +863,15 @@
     bool mStubRouterFlag;
 };
 
-template <size_t N>
-uint16_t BuildRouterAdvert(uint8_t (&aBuffer)[N],
-                           const Pio          *aPios,
-                           uint16_t            aNumPios,
-                           const Rio          *aRios,
-                           uint16_t            aNumRios,
-                           const DefaultRoute &aDefaultRoute,
-                           const RaFlags      &aRaFlags)
+void BuildRouterAdvert(Ip6::Nd::RouterAdvert::TxMessage &aRaMsg,
+                       const Pio                        *aPios,
+                       uint16_t                          aNumPios,
+                       const Rio                        *aRios,
+                       uint16_t                          aNumRios,
+                       const DefaultRoute               &aDefaultRoute,
+                       const RaFlags                    &aRaFlags)
 {
-    Ip6::Nd::RouterAdvertMessage::Header header;
-    uint16_t                             length;
+    Ip6::Nd::RouterAdvert::Header header;
 
     header.SetRouterLifetime(aDefaultRoute.mLifetime);
     header.SetDefaultRouterPreference(aDefaultRoute.mPreference);
@@ -873,29 +886,22 @@
         header.SetOtherConfigFlag();
     }
 
+    SuccessOrQuit(aRaMsg.AppendHeader(header));
+
+    if (aRaFlags.mStubRouterFlag)
     {
-        Ip6::Nd::RouterAdvertMessage raMsg(header, aBuffer);
-
-        if (aRaFlags.mStubRouterFlag)
-        {
-            SuccessOrQuit(raMsg.AppendFlagsExtensionOption(/* aStubRouterFlag */ true));
-        }
-
-        for (; aNumPios > 0; aPios++, aNumPios--)
-        {
-            SuccessOrQuit(
-                raMsg.AppendPrefixInfoOption(aPios->mPrefix, aPios->mValidLifetime, aPios->mPreferredLifetime));
-        }
-
-        for (; aNumRios > 0; aRios++, aNumRios--)
-        {
-            SuccessOrQuit(raMsg.AppendRouteInfoOption(aRios->mPrefix, aRios->mValidLifetime, aRios->mPreference));
-        }
-
-        length = raMsg.GetAsPacket().GetLength();
+        SuccessOrQuit(aRaMsg.AppendFlagsExtensionOption(/* aStubRouterFlag */ true));
     }
 
-    return length;
+    for (; aNumPios > 0; aPios++, aNumPios--)
+    {
+        SuccessOrQuit(aRaMsg.AppendPrefixInfoOption(aPios->mPrefix, aPios->mValidLifetime, aPios->mPreferredLifetime));
+    }
+
+    for (; aNumRios > 0; aRios++, aNumRios--)
+    {
+        SuccessOrQuit(aRaMsg.AppendRouteInfoOption(aRios->mPrefix, aRios->mValidLifetime, aRios->mPreference));
+    }
 }
 
 void SendRouterAdvert(const Ip6::Address &aRouterAddress,
@@ -906,12 +912,15 @@
                       const DefaultRoute &aDefaultRoute,
                       const RaFlags      &aRaFlags)
 {
-    uint8_t  buffer[kMaxRaSize];
-    uint16_t length = BuildRouterAdvert(buffer, aPios, aNumPios, aRios, aNumRios, aDefaultRoute, aRaFlags);
+    Ip6::Nd::RouterAdvert::TxMessage raMsg;
+    Icmp6Packet                      packet;
 
-    SendRouterAdvert(aRouterAddress, buffer, length);
+    BuildRouterAdvert(raMsg, aPios, aNumPios, aRios, aNumRios, aDefaultRoute, aRaFlags);
+    raMsg.GetAsPacket(packet);
+
+    SendRouterAdvert(aRouterAddress, packet);
     Log("Sending RA from router %s", aRouterAddress.ToString().AsCString());
-    LogRouterAdvert(buffer, length);
+    LogRouterAdvert(packet);
 }
 
 template <uint16_t kNumPios, uint16_t kNumRios>
@@ -957,13 +966,16 @@
 
 template <uint16_t kNumPios> void SendRouterAdvertToBorderRoutingProcessIcmp6Ra(const Pio (&aPios)[kNumPios])
 {
-    uint8_t  buffer[kMaxRaSize];
-    uint16_t length = BuildRouterAdvert(buffer, aPios, kNumPios, nullptr, 0,
-                                        DefaultRoute(0, NetworkData::kRoutePreferenceMedium), RaFlags());
+    Ip6::Nd::RouterAdvert::TxMessage raMsg;
+    Icmp6Packet                      packet;
 
-    otPlatBorderRoutingProcessIcmp6Ra(sInstance, buffer, length);
+    BuildRouterAdvert(raMsg, aPios, kNumPios, nullptr, 0, DefaultRoute(0, NetworkData::kRoutePreferenceMedium),
+                      RaFlags());
+    raMsg.GetAsPacket(packet);
+
+    otPlatBorderRoutingProcessIcmp6Ra(sInstance, packet.GetBytes(), packet.GetLength());
     Log("Passing RA to otPlatBorderRoutingProcessIcmp6Ra");
-    LogRouterAdvert(buffer, length);
+    LogRouterAdvert(packet);
 }
 
 struct OnLinkPrefix : public Pio
@@ -1185,8 +1197,10 @@
     sRaValidated = false;
     sExpectedPio = kNoPio;
     sExpectedRios.Clear();
-    sRespondToNs           = true;
-    sExpectedRaHeaderFlags = kRaHeaderFlagsNone;
+    sRespondToNs              = true;
+    sExpectedRaHeaderFlags    = kRaHeaderFlagsNone;
+    sCheckRaHeaderLifetime    = true;
+    sExpectedRaHeaderLifetime = 0;
 
     //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
     // Ensure device starts as leader.
@@ -1382,6 +1396,8 @@
     VerifyOrQuit(sRsEmitted);
     VerifyOrQuit(sRaValidated);
     VerifyOrQuit(sExpectedRios.SawAll());
+    VerifyOrQuit(sExpectedRios[0].mLifetime == kRioValidLifetime);
+
     Log("Received RA was validated");
 
     //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -1409,17 +1425,20 @@
     AdvanceTime(100);
 
     //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-    // Make sure BR emits RA with new OMR prefix now.
+    // Make sure BR emits RA with the new OMR prefix now, and deprecates the old OMR prefix.
 
     sRaValidated = false;
     sExpectedPio = kPioAdvertisingLocalOnLink;
     sExpectedRios.Clear();
     sExpectedRios.Add(omrPrefix);
+    sExpectedRios.Add(localOmr);
 
     AdvanceTime(20000);
 
     VerifyOrQuit(sRaValidated);
     VerifyOrQuit(sExpectedRios.SawAll());
+    VerifyOrQuit(sExpectedRios[0].mLifetime == kRioValidLifetime);
+    VerifyOrQuit(sExpectedRios[1].mLifetime <= kRioDeprecatingLifetime);
 
     //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
     // Check Network Data. We should now see that the local OMR prefix
@@ -1437,16 +1456,20 @@
     AdvanceTime(100);
 
     //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-    // Make sure BR emits RA with local OMR prefix again.
+    // Make sure BR emits RA with local OMR prefix again and start
+    // deprecating the previously added OMR prefix.
 
     sRaValidated = false;
     sExpectedRios.Clear();
+    sExpectedRios.Add(omrPrefix);
     sExpectedRios.Add(localOmr);
 
     AdvanceTime(20000);
 
     VerifyOrQuit(sRaValidated);
     VerifyOrQuit(sExpectedRios.SawAll());
+    VerifyOrQuit(sExpectedRios[0].mLifetime <= kRioDeprecatingLifetime);
+    VerifyOrQuit(sExpectedRios[1].mLifetime == kRioValidLifetime);
 
     //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
     // Check Network Data. We should see that the local OMR prefix is
@@ -1456,6 +1479,21 @@
     VerifyExternalRouteInNetData(kUlaRoute, kWithAdvPioFlagSet);
 
     //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+    // Wait enough for old deprecating OMR prefix deprecating to expire.
+
+    sRaValidated = false;
+    sExpectedRios.Clear();
+    sExpectedRios.Add(omrPrefix);
+    sExpectedRios.Add(localOmr);
+
+    AdvanceTime(310000);
+
+    VerifyOrQuit(sRaValidated);
+    VerifyOrQuit(sExpectedRios.SawAll());
+    VerifyOrQuit(sExpectedRios[0].mLifetime == 0);
+    VerifyOrQuit(sExpectedRios[1].mLifetime == kRioValidLifetime);
+
+    //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
     SuccessOrQuit(sInstance->Get<BorderRouter::RoutingManager>().SetEnabled(false));
     VerifyOrQuit(heapAllocations == sHeapAllocatedPtrs.GetLength());
@@ -1848,7 +1886,7 @@
     // Send an RA from router A with a new on-link (PIO) which is preferred over
     // the local on-link prefix.
 
-    SendRouterAdvert(routerAddressA, {Pio(onLinkPrefix, kValidLitime, kPreferredLifetime)});
+    SendRouterAdvert(routerAddressA, {Pio(onLinkPrefix, kInfiniteLifetime, kInfiniteLifetime)});
 
     //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
     // Check that the local on-link prefix is now deprecating in the new RA.
@@ -2023,7 +2061,10 @@
 
     VerifyOrQuit(sExpectedRios[1].mPrefix == localOmr);
     VerifyOrQuit(sExpectedRios[1].mSawInRa);
-    VerifyOrQuit(sExpectedRios[1].mLifetime == 0);
+    VerifyOrQuit(sExpectedRios[1].mLifetime <= kRioDeprecatingLifetime);
+
+    // Wait long enough for deprecating RIO prefix to expire
+    AdvanceTime(3200000);
 
     sRaValidated = false;
     sExpectedPio = kPioAdvertisingLocalOnLink;
@@ -2920,6 +2961,93 @@
     FinalizeTest();
 }
 
+void TestLearnRaHeader(void)
+{
+    Ip6::Prefix localOnLink;
+    Ip6::Prefix localOmr;
+    Ip6::Prefix onLinkPrefix = PrefixFromString("2000:abba:baba::", 64);
+    uint16_t    heapAllocations;
+
+    Log("--------------------------------------------------------------------------------------------");
+    Log("TestLearnRaHeader");
+
+    InitTest();
+
+    //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+    // Start Routing Manager. Check emitted RS and RA messages.
+
+    sRsEmitted   = false;
+    sRaValidated = false;
+    sExpectedPio = kPioAdvertisingLocalOnLink;
+    sExpectedRios.Clear();
+
+    heapAllocations = sHeapAllocatedPtrs.GetLength();
+    SuccessOrQuit(sInstance->Get<BorderRouter::RoutingManager>().SetEnabled(true));
+
+    SuccessOrQuit(sInstance->Get<BorderRouter::RoutingManager>().GetOnLinkPrefix(localOnLink));
+    SuccessOrQuit(sInstance->Get<BorderRouter::RoutingManager>().GetOmrPrefix(localOmr));
+
+    Log("Local on-link prefix is %s", localOnLink.ToString().AsCString());
+    Log("Local OMR prefix is %s", localOmr.ToString().AsCString());
+
+    sExpectedRios.Add(localOmr);
+
+    AdvanceTime(30000);
+
+    VerifyOrQuit(sRsEmitted);
+    VerifyOrQuit(sRaValidated);
+    VerifyOrQuit(sExpectedRios.SawAll());
+    Log("Received RA was validated");
+
+    VerifyDiscoveredRoutersIsEmpty();
+
+    //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+    // Send an RA from the same address (another entity on the device)
+    // advertising a default route.
+
+    SendRouterAdvert(sInfraIfAddress, DefaultRoute(1000, NetworkData::kRoutePreferenceLow));
+
+    AdvanceTime(1);
+    VerifyDiscoveredRouters({InfraRouter(sInfraIfAddress, /* M */ false, /* O */ false, /* StubRouter */ false)});
+
+    //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+    // RoutingManager should learn the header from the
+    // received RA (from same address) and start advertising
+    // the same default route lifetime in the emitted RAs.
+
+    sRaValidated              = false;
+    sCheckRaHeaderLifetime    = true;
+    sExpectedRaHeaderLifetime = 1000;
+
+    AdvanceTime(30 * 1000);
+    VerifyOrQuit(sRaValidated);
+
+    //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+    // Wait for longer than entry lifetime (for it to expire) and
+    // make sure `RoutingManager` stops advertising default route.
+
+    sCheckRaHeaderLifetime = false;
+
+    AdvanceTime(1000 * 1000);
+
+    sRaValidated              = false;
+    sCheckRaHeaderLifetime    = true;
+    sExpectedRaHeaderLifetime = 0;
+
+    AdvanceTime(700 * 1000);
+    VerifyOrQuit(sRaValidated);
+
+    //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+    SuccessOrQuit(sInstance->Get<BorderRouter::RoutingManager>().SetEnabled(false));
+    VerifyDiscoveredRoutersIsEmpty();
+
+    VerifyOrQuit(heapAllocations == sHeapAllocatedPtrs.GetLength());
+
+    Log("End of TestLearnRaHeader");
+    FinalizeTest();
+}
+
 void TestConflictingPrefix(void)
 {
     static const otExtendedPanId kExtPanId1 = {{0x01, 0x02, 0x03, 0x04, 0x05, 0x6, 0x7, 0x08}};
@@ -3891,6 +4019,7 @@
     ot::TestConflictingPrefix();
     ot::TestRouterNsProbe();
     ot::TestLearningAndCopyingOfFlags();
+    ot::TestLearnRaHeader();
 #if OPENTHREAD_CONFIG_PLATFORM_FLASH_API_ENABLE
     ot::TestSavedOnLinkPrefixes();
 #endif
diff --git a/tests/unit/test_srp_adv_proxy.cpp b/tests/unit/test_srp_adv_proxy.cpp
new file mode 100644
index 0000000..9fe8809
--- /dev/null
+++ b/tests/unit/test_srp_adv_proxy.cpp
@@ -0,0 +1,2781 @@
+/*
+ *  Copyright (c) 2023, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <openthread/config.h>
+
+#include "test_platform.h"
+#include "test_util.hpp"
+
+#include <openthread/dataset_ftd.h>
+#include <openthread/srp_client.h>
+#include <openthread/srp_server.h>
+#include <openthread/thread.h>
+
+#include "common/arg_macros.hpp"
+#include "common/array.hpp"
+#include "common/string.hpp"
+#include "common/time.hpp"
+#include "instance/instance.hpp"
+
+#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE && OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE &&                   \
+    OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE && !OPENTHREAD_CONFIG_TIME_SYNC_ENABLE && \
+    !OPENTHREAD_PLATFORM_POSIX
+#define ENABLE_ADV_PROXY_TEST 1
+#else
+#define ENABLE_ADV_PROXY_TEST 0
+#endif
+
+#if ENABLE_ADV_PROXY_TEST
+
+using namespace ot;
+
+// Logs a message and adds current time (sNow) as "<hours>:<min>:<secs>.<msec>"
+#define Log(...)                                                                                          \
+    printf("%02u:%02u:%02u.%03u " OT_FIRST_ARG(__VA_ARGS__) "\n", (sNow / 36000000), (sNow / 60000) % 60, \
+           (sNow / 1000) % 60, sNow % 1000 OT_REST_ARGS(__VA_ARGS__))
+
+static constexpr uint16_t kMaxRaSize = 800;
+
+static ot::Instance *sInstance;
+
+static uint32_t sNow = 0;
+static uint32_t sAlarmTime;
+static bool     sAlarmOn = false;
+
+static otRadioFrame sRadioTxFrame;
+static uint8_t      sRadioTxFramePsdu[OT_RADIO_FRAME_MAX_SIZE];
+static bool         sRadioTxOngoing = false;
+
+//----------------------------------------------------------------------------------------------------------------------
+// Function prototypes
+
+void ProcessRadioTxAndTasklets(void);
+void AdvanceTime(uint32_t aDuration);
+
+//----------------------------------------------------------------------------------------------------------------------
+// `otPlatRadio`
+
+extern "C" {
+
+otRadioCaps otPlatRadioGetCaps(otInstance *) { return OT_RADIO_CAPS_ACK_TIMEOUT | OT_RADIO_CAPS_CSMA_BACKOFF; }
+
+otError otPlatRadioTransmit(otInstance *, otRadioFrame *)
+{
+    sRadioTxOngoing = true;
+
+    return OT_ERROR_NONE;
+}
+
+otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *) { return &sRadioTxFrame; }
+
+//----------------------------------------------------------------------------------------------------------------------
+// `otPlatAlarm`
+
+void otPlatAlarmMilliStop(otInstance *) { sAlarmOn = false; }
+
+void otPlatAlarmMilliStartAt(otInstance *, uint32_t aT0, uint32_t aDt)
+{
+    sAlarmOn   = true;
+    sAlarmTime = aT0 + aDt;
+}
+
+uint32_t otPlatAlarmMilliGetNow(void) { return sNow; }
+
+//----------------------------------------------------------------------------------------------------------------------
+// `otPlatDnssd`
+
+static constexpr uint16_t kDnssdArraySize = 128;
+
+struct DnssdRequest
+{
+    DnssdRequest(void) = default;
+
+    DnssdRequest(otPlatDnssdRequestId aId, otPlatDnssdRegisterCallback aCallback)
+        : mId(aId)
+        , mCallback(aCallback)
+    {
+    }
+
+    otPlatDnssdRequestId        mId;
+    otPlatDnssdRegisterCallback mCallback;
+};
+
+static Array<DnssdRequest, kDnssdArraySize> sDnssdRegHostRequests;
+static Array<DnssdRequest, kDnssdArraySize> sDnssdUnregHostRequests;
+static Array<DnssdRequest, kDnssdArraySize> sDnssdRegServiceRequests;
+static Array<DnssdRequest, kDnssdArraySize> sDnssdUnregServiceRequests;
+static Array<DnssdRequest, kDnssdArraySize> sDnssdRegKeyRequests;
+static Array<DnssdRequest, kDnssdArraySize> sDnssdUnregKeyRequests;
+
+static bool             sDnssdShouldCheckWithClient = true;
+static Error            sDnssdCallbackError         = kErrorPending;
+static otPlatDnssdState sDnssdState                 = OT_PLAT_DNSSD_READY;
+static uint16_t         sDnssdNumHostAddresses      = 0;
+
+constexpr uint32_t kInfraIfIndex = 1;
+
+otPlatDnssdState otPlatDnssdGetState(otInstance *aInstance)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+
+    return sDnssdState;
+}
+
+void otPlatDnssdRegisterService(otInstance                 *aInstance,
+                                const otPlatDnssdService   *aService,
+                                otPlatDnssdRequestId        aRequestId,
+                                otPlatDnssdRegisterCallback aCallback)
+{
+    Log("otPlatDnssdRegisterService(aRequestId: %lu)", ToUlong(aRequestId));
+    Log("   hostName       : %s", aService->mHostName);
+    Log("   serviceInstance: %s", aService->mServiceInstance);
+    Log("   serviceType    : %s", aService->mServiceType);
+    Log("   num sub-types  : %u", aService->mSubTypeLabelsLength);
+
+    for (uint16_t index = 0; index < aService->mSubTypeLabelsLength; index++)
+    {
+        Log("   sub-type %-4u  : %s", index, aService->mSubTypeLabels[index]);
+    }
+
+    Log("   TXT data len   : %u", aService->mTxtDataLength);
+    Log("   port           : %u", aService->mPort);
+    Log("   priority       : %u", aService->mPriority);
+    Log("   weight         : %u", aService->mWeight);
+    Log("   TTL            : %u", aService->mTtl);
+    Log("   Infra-if index : %u", aService->mInfraIfIndex);
+
+    VerifyOrQuit(aInstance == sInstance);
+    VerifyOrQuit(aService->mInfraIfIndex == kInfraIfIndex);
+
+    if (sDnssdShouldCheckWithClient)
+    {
+        Srp::Client &srpClient = AsCoreType(aInstance).Get<Srp::Client>();
+        bool         didFind   = false;
+
+        VerifyOrQuit(StringMatch(srpClient.GetHostInfo().GetName(), aService->mHostName));
+
+        didFind = false;
+
+        for (const Srp::Client::Service &service : srpClient.GetServices())
+        {
+            if (StringMatch(service.GetInstanceName(), aService->mServiceInstance))
+            {
+                didFind = true;
+                VerifyOrQuit(StringMatch(service.GetName(), aService->mServiceType));
+                VerifyOrQuit(service.GetPort() == aService->mPort);
+                VerifyOrQuit(service.GetWeight() == aService->mWeight);
+                VerifyOrQuit(service.GetPriority() == aService->mPriority);
+                VerifyOrQuit(service.HasSubType() == (aService->mSubTypeLabelsLength != 0));
+            }
+        }
+
+        VerifyOrQuit(didFind);
+    }
+
+    SuccessOrQuit(sDnssdRegServiceRequests.PushBack(DnssdRequest(aRequestId, aCallback)));
+
+    if ((sDnssdCallbackError != kErrorPending) && (aCallback != nullptr))
+    {
+        aCallback(aInstance, aRequestId, sDnssdCallbackError);
+    }
+}
+
+void otPlatDnssdUnregisterService(otInstance                 *aInstance,
+                                  const otPlatDnssdService   *aService,
+                                  otPlatDnssdRequestId        aRequestId,
+                                  otPlatDnssdRegisterCallback aCallback)
+{
+    Log("otPlatDnssdUnregisterService(aRequestId: %lu)", ToUlong(aRequestId));
+    Log("   hostName       : %s", aService->mHostName);
+    Log("   serviceInstance: %s", aService->mServiceInstance);
+    Log("   serviceName    : %s", aService->mServiceType);
+    Log("   Infra-if index : %u", aService->mInfraIfIndex);
+
+    VerifyOrQuit(aInstance == sInstance);
+    VerifyOrQuit(aService->mInfraIfIndex == kInfraIfIndex);
+
+    if (sDnssdShouldCheckWithClient)
+    {
+        // Validate the received service info matches one of the services
+        // on SRP client.
+
+        Srp::Client &srpClient = AsCoreType(aInstance).Get<Srp::Client>();
+        bool         didFind   = false;
+
+        VerifyOrQuit(StringMatch(srpClient.GetHostInfo().GetName(), aService->mHostName));
+
+        for (const Srp::Client::Service &service : srpClient.GetServices())
+        {
+            if (StringMatch(service.GetInstanceName(), aService->mServiceInstance))
+            {
+                didFind = true;
+                VerifyOrQuit(StringMatch(service.GetName(), aService->mServiceType));
+            }
+        }
+
+        VerifyOrQuit(didFind);
+    }
+
+    SuccessOrQuit(sDnssdUnregServiceRequests.PushBack(DnssdRequest(aRequestId, aCallback)));
+
+    if ((sDnssdCallbackError != kErrorPending) && (aCallback != nullptr))
+    {
+        aCallback(aInstance, aRequestId, sDnssdCallbackError);
+    }
+}
+
+void otPlatDnssdRegisterHost(otInstance                 *aInstance,
+                             const otPlatDnssdHost      *aHost,
+                             otPlatDnssdRequestId        aRequestId,
+                             otPlatDnssdRegisterCallback aCallback)
+{
+    Log("otPlatDnssdRegisterHost(aRequestId: %lu)", ToUlong(aRequestId));
+    Log("   hostName       : %s", aHost->mHostName);
+    Log("   numAddresses   : %u", aHost->mAddressesLength);
+
+    for (uint16_t index = 0; index < aHost->mAddressesLength; index++)
+    {
+        Log("   Address %-4u   : %s", index, AsCoreType(&aHost->mAddresses[index]).ToString().AsCString());
+    }
+
+    Log("   TTL            : %u", aHost->mTtl);
+    Log("   Infra-if index : %u", aHost->mInfraIfIndex);
+
+    VerifyOrQuit(aInstance == sInstance);
+    VerifyOrQuit(aHost->mInfraIfIndex == kInfraIfIndex);
+
+    sDnssdNumHostAddresses = aHost->mAddressesLength;
+
+    if (sDnssdShouldCheckWithClient)
+    {
+        VerifyOrQuit(StringMatch(AsCoreType(aInstance).Get<Srp::Client>().GetHostInfo().GetName(), aHost->mHostName));
+    }
+
+    SuccessOrQuit(sDnssdRegHostRequests.PushBack(DnssdRequest(aRequestId, aCallback)));
+
+    if ((sDnssdCallbackError != kErrorPending) && (aCallback != nullptr))
+    {
+        aCallback(aInstance, aRequestId, sDnssdCallbackError);
+    }
+}
+
+void otPlatDnssdUnregisterHost(otInstance                 *aInstance,
+                               const otPlatDnssdHost      *aHost,
+                               otPlatDnssdRequestId        aRequestId,
+                               otPlatDnssdRegisterCallback aCallback)
+{
+    Log("otPlatDnssdUnregisterHost(aRequestId: %lu)", ToUlong(aRequestId));
+    Log("   hostName       : %s", aHost->mHostName);
+    Log("   Infra-if index : %u", aHost->mInfraIfIndex);
+
+    VerifyOrQuit(sInstance == aInstance);
+    VerifyOrQuit(aHost->mInfraIfIndex == kInfraIfIndex);
+
+    if (sDnssdShouldCheckWithClient)
+    {
+        VerifyOrQuit(StringMatch(AsCoreType(aInstance).Get<Srp::Client>().GetHostInfo().GetName(), aHost->mHostName));
+    }
+
+    SuccessOrQuit(sDnssdUnregHostRequests.PushBack(DnssdRequest(aRequestId, aCallback)));
+
+    if ((sDnssdCallbackError != kErrorPending) && (aCallback != nullptr))
+    {
+        aCallback(aInstance, aRequestId, sDnssdCallbackError);
+    }
+}
+
+void otPlatDnssdRegisterKey(otInstance                 *aInstance,
+                            const otPlatDnssdKey       *aKey,
+                            otPlatDnssdRequestId        aRequestId,
+                            otPlatDnssdRegisterCallback aCallback)
+{
+    Log("otPlatDnssdRegisterKey(aRequestId: %lu)", ToUlong(aRequestId));
+    Log("   name           : %s", aKey->mName);
+    Log("   serviceType    : %s", aKey->mServiceType == nullptr ? "(null)" : aKey->mServiceType);
+    Log("   key data-len   : %u", aKey->mKeyDataLength);
+    Log("   TTL            : %u", aKey->mTtl);
+
+    VerifyOrQuit(aInstance == sInstance);
+    VerifyOrQuit(aKey->mInfraIfIndex == kInfraIfIndex);
+
+    if (sDnssdShouldCheckWithClient)
+    {
+        if (aKey->mServiceType == nullptr)
+        {
+            VerifyOrQuit(StringMatch(AsCoreType(aInstance).Get<Srp::Client>().GetHostInfo().GetName(), aKey->mName));
+        }
+        else
+        {
+            bool didFind = false;
+
+            for (const Srp::Client::Service &service : AsCoreType(aInstance).Get<Srp::Client>().GetServices())
+            {
+                if (StringMatch(service.GetInstanceName(), aKey->mName))
+                {
+                    didFind = true;
+                    VerifyOrQuit(StringMatch(service.GetName(), aKey->mServiceType));
+                }
+            }
+
+            VerifyOrQuit(didFind);
+        }
+    }
+
+    SuccessOrQuit(sDnssdRegKeyRequests.PushBack(DnssdRequest(aRequestId, aCallback)));
+
+    if ((sDnssdCallbackError != kErrorPending) && (aCallback != nullptr))
+    {
+        aCallback(aInstance, aRequestId, sDnssdCallbackError);
+    }
+}
+
+void otPlatDnssdUnregisterKey(otInstance                 *aInstance,
+                              const otPlatDnssdKey       *aKey,
+                              otPlatDnssdRequestId        aRequestId,
+                              otPlatDnssdRegisterCallback aCallback)
+{
+    Log("otPlatDnssdUnregisterKey(aRequestId: %lu)", ToUlong(aRequestId));
+    Log("   name           : %s", aKey->mName);
+
+    VerifyOrQuit(aInstance == sInstance);
+    VerifyOrQuit(aKey->mInfraIfIndex == kInfraIfIndex);
+
+    if (sDnssdShouldCheckWithClient)
+    {
+        if (aKey->mServiceType == nullptr)
+        {
+            VerifyOrQuit(StringMatch(AsCoreType(aInstance).Get<Srp::Client>().GetHostInfo().GetName(), aKey->mName));
+        }
+        else
+        {
+            bool didFind = false;
+
+            for (const Srp::Client::Service &service : AsCoreType(aInstance).Get<Srp::Client>().GetServices())
+            {
+                if (StringMatch(service.GetInstanceName(), aKey->mName))
+                {
+                    didFind = true;
+                    VerifyOrQuit(StringMatch(service.GetName(), aKey->mServiceType));
+                }
+            }
+
+            VerifyOrQuit(didFind);
+        }
+    }
+
+    SuccessOrQuit(sDnssdUnregKeyRequests.PushBack(DnssdRequest(aRequestId, aCallback)));
+
+    if ((sDnssdCallbackError != kErrorPending) && (aCallback != nullptr))
+    {
+        aCallback(aInstance, aRequestId, sDnssdCallbackError);
+    }
+}
+
+// Number of times we expect each `otPlatDnssd` request to be called
+struct DnssdRequestCounts : public Clearable<DnssdRequestCounts>
+{
+    DnssdRequestCounts(void) { Clear(); }
+
+    uint16_t mKeyReg;
+    uint16_t mHostReg;
+    uint16_t mServiceReg;
+    uint16_t mKeyUnreg;
+    uint16_t mHostUnreg;
+    uint16_t mServiceUnreg;
+};
+
+void VerifyDnnsdRequests(const DnssdRequestCounts &aRequestCounts, bool aAllowMoreUnregs = false)
+{
+    VerifyOrQuit(sDnssdRegKeyRequests.GetLength() == aRequestCounts.mKeyReg);
+    VerifyOrQuit(sDnssdRegHostRequests.GetLength() == aRequestCounts.mHostReg);
+    VerifyOrQuit(sDnssdRegServiceRequests.GetLength() == aRequestCounts.mServiceReg);
+
+    if (aAllowMoreUnregs)
+    {
+        VerifyOrQuit(sDnssdUnregKeyRequests.GetLength() >= aRequestCounts.mKeyUnreg);
+        VerifyOrQuit(sDnssdUnregHostRequests.GetLength() >= aRequestCounts.mHostUnreg);
+        VerifyOrQuit(sDnssdUnregServiceRequests.GetLength() >= aRequestCounts.mServiceUnreg);
+    }
+    else
+    {
+        VerifyOrQuit(sDnssdUnregKeyRequests.GetLength() == aRequestCounts.mKeyUnreg);
+        VerifyOrQuit(sDnssdUnregHostRequests.GetLength() == aRequestCounts.mHostUnreg);
+        VerifyOrQuit(sDnssdUnregServiceRequests.GetLength() == aRequestCounts.mServiceUnreg);
+    }
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+Array<void *, 500> sHeapAllocatedPtrs;
+
+#if OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE
+void *otPlatCAlloc(size_t aNum, size_t aSize)
+{
+    void *ptr = calloc(aNum, aSize);
+
+    SuccessOrQuit(sHeapAllocatedPtrs.PushBack(ptr));
+
+    return ptr;
+}
+
+void otPlatFree(void *aPtr)
+{
+    if (aPtr != nullptr)
+    {
+        void **entry = sHeapAllocatedPtrs.Find(aPtr);
+
+        VerifyOrQuit(entry != nullptr, "A heap allocated item is freed twice");
+        sHeapAllocatedPtrs.Remove(*entry);
+    }
+
+    free(aPtr);
+}
+#endif
+
+#if OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_PLATFORM_DEFINED
+void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, ...)
+{
+    OT_UNUSED_VARIABLE(aLogLevel);
+    OT_UNUSED_VARIABLE(aLogRegion);
+
+    va_list args;
+
+    printf("   ");
+    va_start(args, aFormat);
+    vprintf(aFormat, args);
+    va_end(args);
+    printf("\n");
+}
+#endif
+
+} // extern "C"
+
+//---------------------------------------------------------------------------------------------------------------------
+
+void ProcessRadioTxAndTasklets(void)
+{
+    do
+    {
+        if (sRadioTxOngoing)
+        {
+            sRadioTxOngoing = false;
+            otPlatRadioTxStarted(sInstance, &sRadioTxFrame);
+            otPlatRadioTxDone(sInstance, &sRadioTxFrame, nullptr, OT_ERROR_NONE);
+        }
+
+        otTaskletsProcess(sInstance);
+    } while (otTaskletsArePending(sInstance));
+}
+
+void AdvanceTime(uint32_t aDuration)
+{
+    uint32_t time = sNow + aDuration;
+
+    Log("AdvanceTime for %u.%03u", aDuration / 1000, aDuration % 1000);
+
+    while (TimeMilli(sAlarmTime) <= TimeMilli(time))
+    {
+        ProcessRadioTxAndTasklets();
+        sNow = sAlarmTime;
+        otPlatAlarmMilliFired(sInstance);
+    }
+
+    ProcessRadioTxAndTasklets();
+    sNow = time;
+}
+
+void InitTest(void)
+{
+    //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+    // Initialize OT instance.
+
+    sNow      = 0;
+    sAlarmOn  = false;
+    sInstance = static_cast<Instance *>(testInitInstance());
+
+    memset(&sRadioTxFrame, 0, sizeof(sRadioTxFrame));
+    sRadioTxFrame.mPsdu = sRadioTxFramePsdu;
+    sRadioTxOngoing     = false;
+
+    sDnssdShouldCheckWithClient = true;
+    sDnssdState                 = OT_PLAT_DNSSD_READY;
+    sDnssdCallbackError         = kErrorPending;
+    sDnssdRegHostRequests.Clear();
+    sDnssdUnregHostRequests.Clear();
+    sDnssdRegServiceRequests.Clear();
+    sDnssdUnregServiceRequests.Clear();
+
+    //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+    // Initialize Border Router and start Thread operation.
+
+    otOperationalDataset     dataset;
+    otOperationalDatasetTlvs datasetTlvs;
+
+    SuccessOrQuit(otDatasetCreateNewNetwork(sInstance, &dataset));
+    SuccessOrQuit(otDatasetConvertToTlvs(&dataset, &datasetTlvs));
+    SuccessOrQuit(otDatasetSetActiveTlvs(sInstance, &datasetTlvs));
+
+    SuccessOrQuit(otIp6SetEnabled(sInstance, true));
+    SuccessOrQuit(otThreadSetEnabled(sInstance, true));
+
+    //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+    // Ensure device starts as leader.
+
+    AdvanceTime(10000);
+
+    VerifyOrQuit(otThreadGetDeviceRole(sInstance) == OT_DEVICE_ROLE_LEADER);
+}
+
+void FinalizeTest(void)
+{
+    SuccessOrQuit(otIp6SetEnabled(sInstance, false));
+    SuccessOrQuit(otThreadSetEnabled(sInstance, false));
+    SuccessOrQuit(otInstanceErasePersistentInfo(sInstance));
+    testFreeInstance(sInstance);
+}
+
+//---------------------------------------------------------------------------------------------------------------------
+// SRP Client callback
+
+static bool  sProcessedClientCallback = false;
+static Error sLastClientCallbackError = kErrorNone;
+
+void HandleSrpClientCallback(otError                    aError,
+                             const otSrpClientHostInfo *aHostInfo,
+                             const otSrpClientService  *aServices,
+                             const otSrpClientService  *aRemovedServices,
+                             void                      *aContext)
+{
+    Log("HandleSrpClientCallback() called with error %s", ErrorToString(aError));
+
+    VerifyOrQuit(aContext == sInstance);
+
+    sProcessedClientCallback = true;
+    sLastClientCallbackError = aError;
+
+    OT_UNUSED_VARIABLE(aHostInfo);
+    OT_UNUSED_VARIABLE(aServices);
+    OT_UNUSED_VARIABLE(aRemovedServices);
+}
+
+static const char kHostName[] = "awesomehost";
+
+void PrepareService1(Srp::Client::Service &aService)
+{
+    static const char          kServiceName[]   = "_srv1._udp";
+    static const char          kInstanceLabel[] = "service1";
+    static const char          kSub1[]          = "_sub1";
+    static const char          kSub2[]          = "_sub2";
+    static const char          kSub3[]          = "_sub3";
+    static const char         *kSubLabels[]     = {kSub1, kSub2, kSub3, nullptr};
+    static const char          kTxtKey1[]       = "ABCD";
+    static const uint8_t       kTxtValue1[]     = {'a', '0'};
+    static const char          kTxtKey2[]       = "Z0";
+    static const uint8_t       kTxtValue2[]     = {'1', '2', '3'};
+    static const char          kTxtKey3[]       = "D";
+    static const uint8_t       kTxtValue3[]     = {0};
+    static const otDnsTxtEntry kTxtEntries[]    = {
+           {kTxtKey1, kTxtValue1, sizeof(kTxtValue1)},
+           {kTxtKey2, kTxtValue2, sizeof(kTxtValue2)},
+           {kTxtKey3, kTxtValue3, sizeof(kTxtValue3)},
+    };
+
+    memset(&aService, 0, sizeof(aService));
+    aService.mName          = kServiceName;
+    aService.mInstanceName  = kInstanceLabel;
+    aService.mSubTypeLabels = kSubLabels;
+    aService.mTxtEntries    = kTxtEntries;
+    aService.mNumTxtEntries = 3;
+    aService.mPort          = 777;
+    aService.mWeight        = 1;
+    aService.mPriority      = 2;
+}
+
+void PrepareService2(Srp::Client::Service &aService)
+{
+    static const char  kService2Name[]   = "_matter._udp";
+    static const char  kInstance2Label[] = "service2";
+    static const char  kSub4[]           = "_44444444";
+    static const char *kSubLabels2[]     = {kSub4, nullptr};
+
+    memset(&aService, 0, sizeof(aService));
+    aService.mName          = kService2Name;
+    aService.mInstanceName  = kInstance2Label;
+    aService.mSubTypeLabels = kSubLabels2;
+    aService.mTxtEntries    = nullptr;
+    aService.mNumTxtEntries = 0;
+    aService.mPort          = 555;
+    aService.mWeight        = 0;
+    aService.mPriority      = 3;
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+
+typedef Dnssd::RequestId      RequestId;
+typedef Dnssd::RequestIdRange RequestIdRange;
+
+void ValidateRequestIdRange(const RequestIdRange &aIdRange, RequestId aStart, RequestId aEnd)
+{
+    RequestId maxId         = NumericLimits<RequestId>::kMax;
+    bool      shouldContain = false;
+
+    VerifyOrQuit(!aIdRange.IsEmpty());
+
+    for (RequestId id = aStart - 5; id != aEnd + 6; id++)
+    {
+        // `idRange` should contain IDs within range `[aStart, aEnd]`
+
+        if (id == aStart)
+        {
+            shouldContain = true;
+        }
+
+        if (id == aEnd + 1)
+        {
+            shouldContain = false;
+        }
+
+        VerifyOrQuit(aIdRange.Contains(id) == shouldContain);
+    }
+
+    // Test values that half the range apart
+
+    for (RequestId id = aStart + maxId / 2 - 10; id != aEnd + maxId / 2 + 10; id++)
+    {
+        VerifyOrQuit(!aIdRange.Contains(id));
+    }
+}
+
+void TestDnssdRequestIdRange(void)
+{
+    RequestId      maxId = NumericLimits<RequestId>::kMax;
+    RequestIdRange idRange;
+
+    Log("--------------------------------------------------------------------------------------------");
+    Log("TestDnssdRequestIdRange");
+
+    VerifyOrQuit(idRange.IsEmpty());
+
+    idRange.Add(5);
+    ValidateRequestIdRange(idRange, 5, 5);
+
+    idRange.Remove(4);
+    ValidateRequestIdRange(idRange, 5, 5);
+
+    idRange.Remove(6);
+    ValidateRequestIdRange(idRange, 5, 5);
+
+    idRange.Remove(5);
+    VerifyOrQuit(idRange.IsEmpty());
+    VerifyOrQuit(!idRange.Contains(5));
+
+    // Adding and removing multiple IDs
+
+    idRange.Add(10);
+    idRange.Add(15);
+    ValidateRequestIdRange(idRange, 10, 15);
+
+    idRange.Add(12);
+    ValidateRequestIdRange(idRange, 10, 15);
+    idRange.Add(15);
+    ValidateRequestIdRange(idRange, 10, 15);
+    idRange.Add(10);
+    ValidateRequestIdRange(idRange, 10, 15);
+
+    idRange.Add(9);
+    ValidateRequestIdRange(idRange, 9, 15);
+    idRange.Add(16);
+    ValidateRequestIdRange(idRange, 9, 16);
+
+    idRange.Remove(10);
+    ValidateRequestIdRange(idRange, 9, 16);
+    idRange.Remove(15);
+    ValidateRequestIdRange(idRange, 9, 16);
+
+    idRange.Remove(8);
+    ValidateRequestIdRange(idRange, 9, 16);
+    idRange.Remove(17);
+    ValidateRequestIdRange(idRange, 9, 16);
+
+    idRange.Remove(9);
+    ValidateRequestIdRange(idRange, 10, 16);
+    idRange.Remove(16);
+    ValidateRequestIdRange(idRange, 10, 15);
+
+    idRange.Clear();
+    VerifyOrQuit(idRange.IsEmpty());
+    VerifyOrQuit(!idRange.Contains(10));
+
+    // Ranges close to roll-over max value
+
+    idRange.Add(maxId);
+    ValidateRequestIdRange(idRange, maxId, maxId);
+
+    idRange.Remove(0);
+    ValidateRequestIdRange(idRange, maxId, maxId);
+    idRange.Remove(maxId - 1);
+    ValidateRequestIdRange(idRange, maxId, maxId);
+
+    idRange.Add(0);
+    ValidateRequestIdRange(idRange, maxId, 0);
+
+    idRange.Add(maxId - 2);
+    ValidateRequestIdRange(idRange, maxId - 2, 0);
+
+    idRange.Add(3);
+    ValidateRequestIdRange(idRange, maxId - 2, 3);
+    idRange.Add(3);
+    ValidateRequestIdRange(idRange, maxId - 2, 3);
+
+    idRange.Remove(4);
+    ValidateRequestIdRange(idRange, maxId - 2, 3);
+    idRange.Remove(maxId - 3);
+    ValidateRequestIdRange(idRange, maxId - 2, 3);
+
+    idRange.Remove(3);
+    ValidateRequestIdRange(idRange, maxId - 2, 2);
+
+    idRange.Remove(maxId - 2);
+    ValidateRequestIdRange(idRange, maxId - 1, 2);
+
+    Log("End of TestDnssdRequestIdRange");
+}
+
+void TestSrpAdvProxy(void)
+{
+    NetworkData::OnMeshPrefixConfig prefixConfig;
+    Srp::Server                    *srpServer;
+    Srp::Client                    *srpClient;
+    Srp::AdvertisingProxy          *advProxy;
+    Srp::Client::Service            service1;
+    Srp::Client::Service            service2;
+    DnssdRequestCounts              dnssdCounts;
+    uint16_t                        heapAllocations;
+
+    Log("--------------------------------------------------------------------------------------------");
+    Log("TestSrpAdvProxy");
+
+    InitTest();
+
+    srpServer = &sInstance->Get<Srp::Server>();
+    srpClient = &sInstance->Get<Srp::Client>();
+    advProxy  = &sInstance->Get<Srp::AdvertisingProxy>();
+
+    heapAllocations = sHeapAllocatedPtrs.GetLength();
+
+    PrepareService1(service1);
+    PrepareService2(service2);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Add an on-mesh prefix (with SLAAC) to network data");
+
+    prefixConfig.Clear();
+    SuccessOrQuit(AsCoreType(&prefixConfig.mPrefix.mPrefix).FromString("fd00:cafe:beef::"));
+    prefixConfig.mPrefix.mLength = 64;
+    prefixConfig.mStable         = true;
+    prefixConfig.mSlaac          = true;
+    prefixConfig.mPreferred      = true;
+    prefixConfig.mOnMesh         = true;
+    prefixConfig.mDefaultRoute   = false;
+    prefixConfig.mPreference     = NetworkData::kRoutePreferenceMedium;
+
+    SuccessOrQuit(otBorderRouterAddOnMeshPrefix(sInstance, &prefixConfig));
+    SuccessOrQuit(otBorderRouterRegister(sInstance));
+
+    // Configure Dnssd platform API behavior
+
+    sDnssdRegHostRequests.Clear();
+    sDnssdRegServiceRequests.Clear();
+    sDnssdUnregHostRequests.Clear();
+    sDnssdUnregServiceRequests.Clear();
+    sDnssdRegKeyRequests.Clear();
+    sDnssdUnregKeyRequests.Clear();
+
+    sDnssdState                 = OT_PLAT_DNSSD_READY;
+    sDnssdShouldCheckWithClient = true;
+    sDnssdCallbackError         = kErrorNone; // Invoke callback directly from dnssd APIs
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Start SRP server");
+
+    SuccessOrQuit(otBorderRoutingInit(sInstance, /* aInfraIfIndex */ kInfraIfIndex, /* aInfraIfIsRunning */ true));
+
+    SuccessOrQuit(srpServer->SetAddressMode(Srp::Server::kAddressModeUnicast));
+    VerifyOrQuit(srpServer->GetAddressMode() == Srp::Server::kAddressModeUnicast);
+
+    VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateDisabled);
+
+    srpServer->SetServiceHandler(nullptr, sInstance);
+
+    srpServer->SetEnabled(true);
+    VerifyOrQuit(srpServer->GetState() != Srp::Server::kStateDisabled);
+
+    AdvanceTime(10000);
+    VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateRunning);
+    VerifyOrQuit(advProxy->IsRunning());
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Start SRP client");
+
+    srpClient->SetCallback(HandleSrpClientCallback, sInstance);
+    srpClient->SetLeaseInterval(180);
+
+    srpClient->EnableAutoStartMode(nullptr, nullptr);
+    VerifyOrQuit(srpClient->IsAutoStartModeEnabled());
+
+    AdvanceTime(2000);
+    VerifyOrQuit(srpClient->IsRunning());
+
+    SuccessOrQuit(srpClient->SetHostName(kHostName));
+    SuccessOrQuit(srpClient->EnableAutoHostAddress());
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Register a service");
+
+    SuccessOrQuit(srpClient->AddService(service1));
+
+    sProcessedClientCallback = false;
+
+    AdvanceTime(2 * 1000);
+
+    dnssdCounts.mKeyReg += 2;
+    dnssdCounts.mHostReg++;
+    dnssdCounts.mServiceReg++;
+    VerifyDnnsdRequests(dnssdCounts);
+
+    VerifyOrQuit(sProcessedClientCallback);
+    VerifyOrQuit(sLastClientCallbackError == kErrorNone);
+
+    VerifyOrQuit(service1.GetState() == Srp::Client::kRegistered);
+
+    VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 1);
+    VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 1);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Register a second service");
+
+    SuccessOrQuit(srpClient->AddService(service2));
+
+    sProcessedClientCallback = false;
+
+    AdvanceTime(2 * 1000);
+
+    // This time we should only see the new service and its key being
+    // registered as the host is same as before and already registered
+
+    dnssdCounts.mKeyReg++;
+    dnssdCounts.mServiceReg++;
+    VerifyDnnsdRequests(dnssdCounts);
+
+    VerifyOrQuit(sProcessedClientCallback);
+    VerifyOrQuit(sLastClientCallbackError == kErrorNone);
+
+    VerifyOrQuit(service1.GetState() == Srp::Client::kRegistered);
+    VerifyOrQuit(service2.GetState() == Srp::Client::kRegistered);
+
+    VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 2);
+    VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 2);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Wait for longer than lease interval for client to refresh");
+
+    sProcessedClientCallback = false;
+
+    AdvanceTime(181 * 1000);
+
+    VerifyOrQuit(sProcessedClientCallback);
+
+    // Validate that adv-proxy does not update any of registration on
+    // DNS-SD platform since there is no change.
+
+    VerifyDnnsdRequests(dnssdCounts);
+
+    VerifyOrQuit(advProxy->GetCounters().mAdvTotal >= 3);
+    VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == advProxy->GetCounters().mAdvTotal);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Add a new on-mesh prefix so to get a new host address");
+
+    prefixConfig.Clear();
+    SuccessOrQuit(AsCoreType(&prefixConfig.mPrefix.mPrefix).FromString("fd00:abba::"));
+    prefixConfig.mPrefix.mLength = 64;
+    prefixConfig.mStable         = true;
+    prefixConfig.mSlaac          = true;
+    prefixConfig.mPreferred      = true;
+    prefixConfig.mOnMesh         = true;
+    prefixConfig.mDefaultRoute   = false;
+    prefixConfig.mPreference     = NetworkData::kRoutePreferenceMedium;
+
+    SuccessOrQuit(otBorderRouterAddOnMeshPrefix(sInstance, &prefixConfig));
+    SuccessOrQuit(otBorderRouterRegister(sInstance));
+
+    sProcessedClientCallback = false;
+
+    AdvanceTime(5 * 1000);
+
+    // This time we should only see new host registration
+    // since that's the only thing that changes
+
+    dnssdCounts.mHostReg++;
+    VerifyDnnsdRequests(dnssdCounts);
+
+    VerifyOrQuit(sProcessedClientCallback);
+    VerifyOrQuit(sLastClientCallbackError == kErrorNone);
+
+    VerifyOrQuit(service1.GetState() == Srp::Client::kRegistered);
+    VerifyOrQuit(service2.GetState() == Srp::Client::kRegistered);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Remove the first service on client");
+
+    SuccessOrQuit(srpClient->RemoveService(service1));
+
+    sProcessedClientCallback = false;
+
+    AdvanceTime(2 * 1000);
+
+    // We should see the service being unregistered
+    // by advertising proxy on DNS-SD platform but its key
+    // remains registered.
+
+    dnssdCounts.mServiceUnreg++;
+    VerifyDnnsdRequests(dnssdCounts);
+
+    VerifyOrQuit(sProcessedClientCallback);
+    VerifyOrQuit(sLastClientCallbackError == kErrorNone);
+
+    VerifyOrQuit(service1.GetState() == Srp::Client::kRemoved);
+    VerifyOrQuit(service2.GetState() == Srp::Client::kRegistered);
+
+    // Wait for more than lease interval again and make sure
+    // there is no change in DNS-SD platform API calls.
+
+    sProcessedClientCallback = false;
+
+    AdvanceTime(181 * 1000);
+
+    VerifyOrQuit(sProcessedClientCallback);
+    VerifyOrQuit(sLastClientCallbackError == kErrorNone);
+
+    VerifyDnnsdRequests(dnssdCounts);
+
+    VerifyOrQuit(service1.GetState() == Srp::Client::kRemoved);
+    VerifyOrQuit(service2.GetState() == Srp::Client::kRegistered);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Change service 2 on client, remove its sub-type");
+
+    SuccessOrQuit(srpClient->ClearService(service2));
+    PrepareService2(service2);
+    service2.mSubTypeLabels = nullptr;
+
+    SuccessOrQuit(srpClient->AddService(service2));
+
+    sProcessedClientCallback = false;
+
+    AdvanceTime(2 * 1000);
+
+    // Since the service is now changed, advertising proxy
+    // should update it (re-register it) on DNS-SD APIs.
+
+    dnssdCounts.mServiceReg++;
+    VerifyDnnsdRequests(dnssdCounts);
+
+    VerifyOrQuit(sProcessedClientCallback);
+    VerifyOrQuit(sLastClientCallbackError == kErrorNone);
+
+    VerifyOrQuit(service1.GetState() == Srp::Client::kRemoved);
+    VerifyOrQuit(service2.GetState() == Srp::Client::kRegistered);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Remove the host on client");
+
+    SuccessOrQuit(srpClient->RemoveHostAndServices(/* aShouldRemoveKeyLease */ false));
+
+    sProcessedClientCallback = false;
+
+    AdvanceTime(2 * 1000);
+
+    // We should see the host and service being unregistered
+    // on DNS-SD APIs but keys remain unchanged.
+
+    dnssdCounts.mHostUnreg++;
+    dnssdCounts.mServiceUnreg++;
+    VerifyDnnsdRequests(dnssdCounts);
+
+    VerifyOrQuit(sProcessedClientCallback);
+    VerifyOrQuit(sLastClientCallbackError == kErrorNone);
+
+    VerifyOrQuit(service1.GetState() == Srp::Client::kRemoved);
+    VerifyOrQuit(service2.GetState() == Srp::Client::kRemoved);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Remove the host on client again and force an update to be sent to server");
+
+    SuccessOrQuit(srpClient->SetHostName(kHostName));
+    SuccessOrQuit(srpClient->RemoveHostAndServices(/* aShouldRemoveKeyLease */ false, /* aSendUnregToServer */ true));
+
+    sProcessedClientCallback = false;
+
+    AdvanceTime(2 * 1000);
+
+    // We should see no changes (no calls) to DNS-SD APIs.
+
+    VerifyDnnsdRequests(dnssdCounts);
+
+    VerifyOrQuit(sProcessedClientCallback);
+    VerifyOrQuit(sLastClientCallbackError == kErrorNone);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Re-add service 1 on client and register with server");
+
+    SuccessOrQuit(srpClient->SetHostName(kHostName));
+    SuccessOrQuit(srpClient->EnableAutoHostAddress());
+    PrepareService1(service1);
+    SuccessOrQuit(srpClient->AddService(service1));
+
+    sProcessedClientCallback = false;
+
+    AdvanceTime(2 * 1000);
+
+    // We should see one host register and one service register
+    // on DNS-SD APIs. Keys are already registered.
+
+    dnssdCounts.mHostReg++;
+    dnssdCounts.mServiceReg++;
+    VerifyDnnsdRequests(dnssdCounts);
+
+    VerifyOrQuit(sProcessedClientCallback);
+    VerifyOrQuit(sLastClientCallbackError == kErrorNone);
+
+    VerifyOrQuit(service1.GetState() == Srp::Client::kRegistered);
+
+    // Wait for more than lease interval again and make sure
+    // there is no change in DNS-SD platform API calls.
+
+    sProcessedClientCallback = false;
+
+    AdvanceTime(181 * 1000);
+
+    VerifyOrQuit(sProcessedClientCallback);
+    VerifyOrQuit(sLastClientCallbackError == kErrorNone);
+
+    VerifyDnnsdRequests(dnssdCounts);
+
+    VerifyOrQuit(service1.GetState() == Srp::Client::kRegistered);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Disable SRP client and wait for lease time to expire");
+
+    srpClient->ClearHostAndServices(); // does not signal removal to server
+
+    // Since we clear everything on SRP client, we disable
+    // matching the services with client from `otPlatDnssd`
+    // APIs.
+    sDnssdShouldCheckWithClient = false;
+
+    AdvanceTime(181 * 1000);
+
+    // Make sure host and service are unregistered.
+
+    dnssdCounts.mHostUnreg++;
+    dnssdCounts.mServiceUnreg++;
+    VerifyDnnsdRequests(dnssdCounts);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Disable SRP server");
+
+    // Verify that all heap allocations by SRP server
+    // and Advertising Proxy are freed.
+
+    srpServer->SetEnabled(false);
+    AdvanceTime(100);
+    VerifyOrQuit(!advProxy->IsRunning());
+
+    VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == advProxy->GetCounters().mAdvTotal);
+    VerifyOrQuit(advProxy->GetCounters().mAdvTimeout == 0);
+    VerifyOrQuit(advProxy->GetCounters().mAdvRejected == 0);
+    VerifyOrQuit(advProxy->GetCounters().mAdvSkipped == 0);
+    VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 0);
+
+    dnssdCounts.mKeyUnreg += 3;
+    VerifyDnnsdRequests(dnssdCounts);
+
+    VerifyOrQuit(heapAllocations == sHeapAllocatedPtrs.GetLength());
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Finalize OT instance and validate all heap allocations are freed");
+
+    FinalizeTest();
+
+    VerifyOrQuit(sHeapAllocatedPtrs.IsEmpty());
+
+    Log("End of TestSrpAdvProxy");
+}
+
+void TestSrpAdvProxyDnssdStateChange(void)
+{
+    NetworkData::OnMeshPrefixConfig prefixConfig;
+    Srp::Server                    *srpServer;
+    Srp::Client                    *srpClient;
+    Srp::AdvertisingProxy          *advProxy;
+    Srp::Client::Service            service1;
+    Srp::Client::Service            service2;
+    DnssdRequestCounts              dnssdCounts;
+    uint16_t                        heapAllocations;
+
+    Log("--------------------------------------------------------------------------------------------");
+    Log("TestSrpAdvProxyDnssdStateChange");
+
+    InitTest();
+
+    srpServer = &sInstance->Get<Srp::Server>();
+    srpClient = &sInstance->Get<Srp::Client>();
+    advProxy  = &sInstance->Get<Srp::AdvertisingProxy>();
+
+    heapAllocations = sHeapAllocatedPtrs.GetLength();
+
+    PrepareService1(service1);
+    PrepareService2(service2);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Add an on-mesh prefix (with SLAAC) to network data");
+
+    prefixConfig.Clear();
+    SuccessOrQuit(AsCoreType(&prefixConfig.mPrefix.mPrefix).FromString("fd00:cafe:beef::"));
+    prefixConfig.mPrefix.mLength = 64;
+    prefixConfig.mStable         = true;
+    prefixConfig.mSlaac          = true;
+    prefixConfig.mPreferred      = true;
+    prefixConfig.mOnMesh         = true;
+    prefixConfig.mDefaultRoute   = false;
+    prefixConfig.mPreference     = NetworkData::kRoutePreferenceMedium;
+
+    SuccessOrQuit(otBorderRouterAddOnMeshPrefix(sInstance, &prefixConfig));
+    SuccessOrQuit(otBorderRouterRegister(sInstance));
+
+    // Configure Dnssd platform API behavior
+
+    sDnssdRegHostRequests.Clear();
+    sDnssdRegServiceRequests.Clear();
+    sDnssdUnregHostRequests.Clear();
+    sDnssdUnregServiceRequests.Clear();
+    sDnssdRegKeyRequests.Clear();
+    sDnssdUnregKeyRequests.Clear();
+
+    sDnssdState                 = OT_PLAT_DNSSD_STOPPED;
+    sDnssdShouldCheckWithClient = true;
+    sDnssdCallbackError         = kErrorNone; // Invoke callback directly
+
+    VerifyOrQuit(!advProxy->IsRunning());
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Start SRP server");
+
+    SuccessOrQuit(otBorderRoutingInit(sInstance, /* aInfraIfIndex */ kInfraIfIndex, /* aInfraIfIsRunning */ true));
+
+    SuccessOrQuit(srpServer->SetAddressMode(Srp::Server::kAddressModeUnicast));
+    VerifyOrQuit(srpServer->GetAddressMode() == Srp::Server::kAddressModeUnicast);
+
+    VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateDisabled);
+
+    srpServer->SetServiceHandler(nullptr, sInstance);
+
+    srpServer->SetEnabled(true);
+    VerifyOrQuit(srpServer->GetState() != Srp::Server::kStateDisabled);
+
+    AdvanceTime(10000);
+    VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateRunning);
+    VerifyOrQuit(!advProxy->IsRunning());
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Start SRP client");
+
+    srpClient->SetCallback(HandleSrpClientCallback, sInstance);
+    srpClient->SetLeaseInterval(180);
+
+    srpClient->EnableAutoStartMode(nullptr, nullptr);
+    VerifyOrQuit(srpClient->IsAutoStartModeEnabled());
+
+    AdvanceTime(2000);
+    VerifyOrQuit(srpClient->IsRunning());
+
+    SuccessOrQuit(srpClient->SetHostName(kHostName));
+    SuccessOrQuit(srpClient->EnableAutoHostAddress());
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Register a services");
+
+    SuccessOrQuit(srpClient->AddService(service1));
+
+    sProcessedClientCallback = false;
+
+    AdvanceTime(2 * 1000);
+
+    VerifyOrQuit(sProcessedClientCallback);
+    VerifyOrQuit(sLastClientCallbackError == kErrorNone);
+
+    VerifyOrQuit(service1.GetState() == Srp::Client::kRegistered);
+
+    VerifyDnnsdRequests(dnssdCounts);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Register a second service");
+
+    SuccessOrQuit(srpClient->AddService(service2));
+
+    sProcessedClientCallback = false;
+
+    AdvanceTime(2 * 1000);
+
+    VerifyOrQuit(sProcessedClientCallback);
+    VerifyOrQuit(sLastClientCallbackError == kErrorNone);
+
+    VerifyOrQuit(service1.GetState() == Srp::Client::kRegistered);
+    VerifyOrQuit(service2.GetState() == Srp::Client::kRegistered);
+
+    // None of the DNS-SD APIs should be called since its state
+    // `OT_PLAT_DNSSD_STOPPED` (`dnssdCounts` is all zeros).
+    VerifyDnnsdRequests(dnssdCounts);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Update DNS-SD state and signal that state is changed");
+
+    sDnssdState = OT_PLAT_DNSSD_READY;
+    otPlatDnssdStateHandleStateChange(sInstance);
+
+    AdvanceTime(5);
+
+    VerifyOrQuit(advProxy->IsRunning());
+    VerifyOrQuit(advProxy->GetCounters().mStateChanges == 1);
+
+    // Now the host and two services should be registered on
+    // DNS-SD platform
+
+    dnssdCounts.mHostReg++;
+    dnssdCounts.mServiceReg += 2;
+    dnssdCounts.mKeyReg += 3;
+    VerifyDnnsdRequests(dnssdCounts);
+
+    VerifyOrQuit(service1.GetState() == Srp::Client::kRegistered);
+    VerifyOrQuit(service2.GetState() == Srp::Client::kRegistered);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Wait for longer than lease interval for client to refresh");
+
+    sProcessedClientCallback = false;
+
+    AdvanceTime(181 * 1000);
+
+    VerifyOrQuit(sProcessedClientCallback);
+
+    // Validate that adv-proxy does not update any of registration on
+    // DNS-SD platform since there is no change.
+    VerifyDnnsdRequests(dnssdCounts);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Update DNS-SD state to `STOPPED` and signal its change");
+
+    sDnssdState = OT_PLAT_DNSSD_STOPPED;
+    otPlatDnssdStateHandleStateChange(sInstance);
+
+    AdvanceTime(5);
+
+    VerifyOrQuit(!advProxy->IsRunning());
+    VerifyOrQuit(advProxy->GetCounters().mStateChanges == 2);
+
+    // Since DNS-SD platform signal that it is stopped,
+    // there should be no calls to any of APIs.
+
+    VerifyDnnsdRequests(dnssdCounts);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Wait for longer than lease interval for client to refresh");
+
+    sProcessedClientCallback = false;
+
+    AdvanceTime(181 * 1000);
+
+    VerifyOrQuit(sProcessedClientCallback);
+    VerifyOrQuit(service1.GetState() == Srp::Client::kRegistered);
+    VerifyOrQuit(service2.GetState() == Srp::Client::kRegistered);
+
+    // The DNS-SD API counters should remain unchanged
+
+    VerifyDnnsdRequests(dnssdCounts);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Update DNS-SD state to `READY` and signal its change");
+
+    sDnssdState = OT_PLAT_DNSSD_READY;
+    otPlatDnssdStateHandleStateChange(sInstance);
+
+    AdvanceTime(5);
+
+    VerifyOrQuit(advProxy->IsRunning());
+    VerifyOrQuit(advProxy->GetCounters().mStateChanges == 3);
+
+    // Check that the host and two services are again registered
+    // on DNS-SD platform by advertising proxy.
+
+    dnssdCounts.mHostReg++;
+    dnssdCounts.mServiceReg += 2;
+    dnssdCounts.mKeyReg += 3;
+    VerifyDnnsdRequests(dnssdCounts);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Update DNS-SD state to `STOPPED` and signal its change");
+
+    sDnssdState = OT_PLAT_DNSSD_STOPPED;
+    otPlatDnssdStateHandleStateChange(sInstance);
+
+    AdvanceTime(5);
+
+    VerifyOrQuit(!advProxy->IsRunning());
+    VerifyOrQuit(advProxy->GetCounters().mStateChanges == 4);
+
+    // Since DNS-SD platform signal that it is stopped,
+    // there should be no calls to any of APIs.
+
+    VerifyDnnsdRequests(dnssdCounts);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Remove the first service on client");
+
+    SuccessOrQuit(srpClient->RemoveService(service1));
+
+    sProcessedClientCallback = false;
+
+    AdvanceTime(2 * 1000);
+
+    VerifyOrQuit(sProcessedClientCallback);
+    VerifyOrQuit(sLastClientCallbackError == kErrorNone);
+    VerifyOrQuit(service1.GetState() == Srp::Client::kRemoved);
+    VerifyOrQuit(service2.GetState() == Srp::Client::kRegistered);
+
+    // No changes to DNS-SD API counters (since it is stopped)
+
+    VerifyDnnsdRequests(dnssdCounts);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Update DNS-SD state to `READY` and signal its change #2");
+
+    // Since the already removed `service1` is no longer available
+    // on SRP client, we disable checking the services with client
+    // from `otPlatDnssd` APIs.
+    sDnssdShouldCheckWithClient = false;
+
+    sDnssdState = OT_PLAT_DNSSD_READY;
+    otPlatDnssdStateHandleStateChange(sInstance);
+
+    AdvanceTime(5);
+
+    VerifyOrQuit(advProxy->IsRunning());
+    VerifyOrQuit(advProxy->GetCounters().mStateChanges == 5);
+
+    // We should see the host and `service2` registered again.
+    // And all 3 keys (even for removed `service1`) to be
+    // registered.
+
+    dnssdCounts.mHostReg++;
+    dnssdCounts.mServiceReg++;
+    dnssdCounts.mKeyReg += 3;
+    VerifyDnnsdRequests(dnssdCounts);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Disable SRP server");
+
+    // Verify that all heap allocations by SRP server
+    // and Advertising Proxy are freed.
+
+    srpServer->SetEnabled(false);
+    AdvanceTime(100);
+
+    VerifyOrQuit(!advProxy->IsRunning());
+    VerifyOrQuit(advProxy->GetCounters().mStateChanges == 6);
+    VerifyOrQuit(advProxy->GetCounters().mAdvSkipped > 0);
+    VerifyOrQuit(advProxy->GetCounters().mAdvTotal ==
+                 (advProxy->GetCounters().mAdvSuccessful + advProxy->GetCounters().mAdvSkipped));
+    VerifyOrQuit(advProxy->GetCounters().mAdvTimeout == 0);
+    VerifyOrQuit(advProxy->GetCounters().mAdvRejected == 0);
+    VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 0);
+
+    dnssdCounts.mHostUnreg++;
+    dnssdCounts.mServiceUnreg++;
+    dnssdCounts.mKeyUnreg += 3;
+    VerifyDnnsdRequests(dnssdCounts);
+
+    VerifyOrQuit(heapAllocations == sHeapAllocatedPtrs.GetLength());
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Finalize OT instance and validate all heap allocations are freed");
+
+    FinalizeTest();
+
+    VerifyOrQuit(sHeapAllocatedPtrs.IsEmpty());
+
+    Log("End of TestSrpAdvProxyDnssdStateChange");
+}
+
+void TestSrpAdvProxyDelayedCallback(void)
+{
+    NetworkData::OnMeshPrefixConfig prefixConfig;
+    Srp::Server                    *srpServer;
+    Srp::Client                    *srpClient;
+    Srp::AdvertisingProxy          *advProxy;
+    Srp::Client::Service            service1;
+    Srp::Client::Service            service2;
+    DnssdRequestCounts              dnssdCounts;
+    uint16_t                        heapAllocations;
+    const DnssdRequest             *request;
+
+    Log("--------------------------------------------------------------------------------------------");
+    Log("TestSrpAdvProxyDelayedCallback");
+
+    InitTest();
+
+    srpServer = &sInstance->Get<Srp::Server>();
+    srpClient = &sInstance->Get<Srp::Client>();
+    advProxy  = &sInstance->Get<Srp::AdvertisingProxy>();
+
+    heapAllocations = sHeapAllocatedPtrs.GetLength();
+
+    PrepareService1(service1);
+    PrepareService2(service2);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Add an on-mesh prefix (with SLAAC) to network data");
+
+    prefixConfig.Clear();
+    SuccessOrQuit(AsCoreType(&prefixConfig.mPrefix.mPrefix).FromString("fd00:cafe:beef::"));
+    prefixConfig.mPrefix.mLength = 64;
+    prefixConfig.mStable         = true;
+    prefixConfig.mSlaac          = true;
+    prefixConfig.mPreferred      = true;
+    prefixConfig.mOnMesh         = true;
+    prefixConfig.mDefaultRoute   = false;
+    prefixConfig.mPreference     = NetworkData::kRoutePreferenceMedium;
+
+    SuccessOrQuit(otBorderRouterAddOnMeshPrefix(sInstance, &prefixConfig));
+    SuccessOrQuit(otBorderRouterRegister(sInstance));
+
+    // Configure Dnssd platform API behavior
+
+    sDnssdRegHostRequests.Clear();
+    sDnssdRegServiceRequests.Clear();
+    sDnssdUnregHostRequests.Clear();
+    sDnssdUnregServiceRequests.Clear();
+    sDnssdRegKeyRequests.Clear();
+    sDnssdUnregKeyRequests.Clear();
+
+    sDnssdState                 = OT_PLAT_DNSSD_READY;
+    sDnssdShouldCheckWithClient = true;
+    sDnssdCallbackError         = kErrorPending; // Do not call the callbacks directly
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Start SRP server");
+
+    SuccessOrQuit(otBorderRoutingInit(sInstance, /* aInfraIfIndex */ kInfraIfIndex, /* aInfraIfIsRunning */ true));
+
+    SuccessOrQuit(srpServer->SetAddressMode(Srp::Server::kAddressModeUnicast));
+    VerifyOrQuit(srpServer->GetAddressMode() == Srp::Server::kAddressModeUnicast);
+
+    VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateDisabled);
+
+    srpServer->SetServiceHandler(nullptr, sInstance);
+
+    srpServer->SetEnabled(true);
+    VerifyOrQuit(srpServer->GetState() != Srp::Server::kStateDisabled);
+
+    AdvanceTime(10000);
+    VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateRunning);
+    VerifyOrQuit(advProxy->IsRunning());
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Start SRP client");
+
+    srpClient->SetCallback(HandleSrpClientCallback, sInstance);
+    srpClient->SetLeaseInterval(180);
+
+    srpClient->EnableAutoStartMode(nullptr, nullptr);
+    VerifyOrQuit(srpClient->IsAutoStartModeEnabled());
+
+    AdvanceTime(2000);
+    VerifyOrQuit(srpClient->IsRunning());
+
+    SuccessOrQuit(srpClient->SetHostName(kHostName));
+    SuccessOrQuit(srpClient->EnableAutoHostAddress());
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Register a service, invoke the registration callback after some delay");
+
+    SuccessOrQuit(srpClient->AddService(service1));
+
+    sProcessedClientCallback = false;
+
+    AdvanceTime(1000);
+
+    dnssdCounts.mHostReg++;
+    dnssdCounts.mServiceReg++;
+    dnssdCounts.mKeyReg += 2;
+    VerifyDnnsdRequests(dnssdCounts);
+
+    VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 1);
+
+    VerifyOrQuit(!sProcessedClientCallback);
+    VerifyOrQuit(srpServer->GetNextHost(nullptr) == nullptr);
+
+    // Invoke the service and key callbacks first
+    request = &sDnssdRegServiceRequests[0];
+    VerifyOrQuit(request->mCallback != nullptr);
+    request->mCallback(sInstance, request->mId, kErrorNone);
+
+    request = &sDnssdRegKeyRequests[0];
+    VerifyOrQuit(request->mCallback != nullptr);
+    request->mCallback(sInstance, request->mId, kErrorNone);
+
+    request = &sDnssdRegKeyRequests[1];
+    VerifyOrQuit(request->mCallback != nullptr);
+    request->mCallback(sInstance, request->mId, kErrorNone);
+
+    AdvanceTime(10);
+    VerifyOrQuit(!sProcessedClientCallback);
+    VerifyOrQuit(srpServer->GetNextHost(nullptr) == nullptr);
+
+    // Invoke the host registration callback next
+    request = &sDnssdRegHostRequests[0];
+    VerifyOrQuit(request->mCallback != nullptr);
+    request->mCallback(sInstance, request->mId, kErrorNone);
+
+    AdvanceTime(10);
+    VerifyOrQuit(srpServer->GetNextHost(nullptr) != nullptr);
+
+    AdvanceTime(100);
+    VerifyOrQuit(sProcessedClientCallback);
+    VerifyOrQuit(sLastClientCallbackError == kErrorNone);
+    VerifyOrQuit(service1.GetState() == Srp::Client::kRegistered);
+
+    VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 1);
+    VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 1);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Register a second service, invoke registration callback with `kErrorDuplicated`");
+
+    SuccessOrQuit(srpClient->AddService(service2));
+
+    sProcessedClientCallback = false;
+
+    AdvanceTime(1000);
+
+    VerifyOrQuit(!sProcessedClientCallback);
+
+    dnssdCounts.mServiceReg++;
+    dnssdCounts.mKeyReg++;
+    VerifyDnnsdRequests(dnssdCounts);
+
+    VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 2);
+    VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 1);
+
+    // Invoke the service callback with kErrorDuplicated
+
+    request = &sDnssdRegServiceRequests[1];
+    VerifyOrQuit(request->mCallback != nullptr);
+    request->mCallback(sInstance, request->mId, kErrorDuplicated);
+
+    AdvanceTime(100);
+
+    VerifyOrQuit(sProcessedClientCallback);
+    VerifyOrQuit(sLastClientCallbackError == kErrorDuplicated);
+
+    VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 2);
+    VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 1);
+    VerifyOrQuit(advProxy->GetCounters().mAdvRejected == 1);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Try registering service again from client, invoke callback with success");
+
+    SuccessOrQuit(srpClient->ClearService(service2));
+    PrepareService2(service2);
+    SuccessOrQuit(srpClient->AddService(service2));
+
+    sProcessedClientCallback = false;
+
+    AdvanceTime(1000);
+
+    VerifyOrQuit(!sProcessedClientCallback);
+
+    // We should see a new service registration request.
+
+    dnssdCounts.mServiceReg++;
+    dnssdCounts.mKeyReg++;
+    VerifyDnnsdRequests(dnssdCounts);
+
+    VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 3);
+    VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 1);
+    VerifyOrQuit(advProxy->GetCounters().mAdvRejected == 1);
+
+    // Invoked the service and key callback with success.
+
+    request = &sDnssdRegKeyRequests[sDnssdRegKeyRequests.GetLength() - 1];
+    VerifyOrQuit(request->mCallback != nullptr);
+    request->mCallback(sInstance, request->mId, kErrorNone);
+
+    request = &sDnssdRegServiceRequests[sDnssdRegServiceRequests.GetLength() - 1];
+    VerifyOrQuit(request->mCallback != nullptr);
+    request->mCallback(sInstance, request->mId, kErrorNone);
+
+    AdvanceTime(100);
+
+    VerifyOrQuit(sProcessedClientCallback);
+    VerifyOrQuit(sLastClientCallbackError == kErrorNone);
+    VerifyOrQuit(service1.GetState() == Srp::Client::kRegistered);
+    VerifyOrQuit(service2.GetState() == Srp::Client::kRegistered);
+
+    VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 3);
+    VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 2);
+    VerifyOrQuit(advProxy->GetCounters().mAdvRejected == 1);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Change the service and register again, but ignore the registration callback");
+
+    SuccessOrQuit(srpClient->ClearService(service2));
+    PrepareService2(service2);
+    service2.mSubTypeLabels = nullptr;
+    SuccessOrQuit(srpClient->AddService(service2));
+
+    sProcessedClientCallback = false;
+
+    AdvanceTime(1000);
+
+    VerifyOrQuit(!sProcessedClientCallback);
+
+    // We should see a new service registration request.
+
+    dnssdCounts.mServiceReg++;
+    VerifyDnnsdRequests(dnssdCounts);
+
+    // Wait for advertising proxy timeout (there will be no callback from
+    // platform) so validate that registration failure is reported to
+    // the SRP client.
+
+    AdvanceTime(2 * 1000);
+
+    VerifyOrQuit(sProcessedClientCallback);
+    VerifyOrQuit(sLastClientCallbackError != kErrorNone);
+
+    VerifyOrQuit(advProxy->GetCounters().mAdvTimeout == 1);
+
+    // Wait for longer than client retry time.
+
+    AdvanceTime(3 * 1000);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Disable SRP server");
+
+    // Verify that all heap allocations by SRP server
+    // and Advertising Proxy are freed.
+
+    srpServer->SetEnabled(false);
+    AdvanceTime(100);
+
+    // Make sure the host and two services are unregistered
+    // (even though the second service was not successfully
+    // registered yet).
+
+    VerifyOrQuit(sDnssdRegHostRequests.GetLength() == 1);
+    VerifyOrQuit(sDnssdRegServiceRequests.GetLength() >= 4);
+    VerifyOrQuit(sDnssdRegKeyRequests.GetLength() >= 3);
+    VerifyOrQuit(sDnssdUnregHostRequests.GetLength() == 1);
+    VerifyOrQuit(sDnssdUnregServiceRequests.GetLength() == 2);
+    VerifyOrQuit(sDnssdUnregKeyRequests.GetLength() == 3);
+
+    VerifyOrQuit(heapAllocations == sHeapAllocatedPtrs.GetLength());
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Finalize OT instance and validate all heap allocations are freed");
+
+    FinalizeTest();
+
+    VerifyOrQuit(sHeapAllocatedPtrs.IsEmpty());
+
+    Log("End of TestSrpAdvProxyDelayedCallback");
+}
+
+void TestSrpAdvProxyReplacedEntries(void)
+{
+    NetworkData::OnMeshPrefixConfig prefixConfig;
+    Srp::Server                    *srpServer;
+    Srp::Client                    *srpClient;
+    Srp::AdvertisingProxy          *advProxy;
+    Srp::Client::Service            service1;
+    Srp::Client::Service            service2;
+    DnssdRequestCounts              dnssdCounts;
+    uint16_t                        heapAllocations;
+    const DnssdRequest             *request;
+    uint16_t                        numServices;
+
+    Log("--------------------------------------------------------------------------------------------");
+    Log("TestSrpAdvProxyReplacedEntries");
+
+    InitTest();
+
+    srpServer = &sInstance->Get<Srp::Server>();
+    srpClient = &sInstance->Get<Srp::Client>();
+    advProxy  = &sInstance->Get<Srp::AdvertisingProxy>();
+
+    heapAllocations = sHeapAllocatedPtrs.GetLength();
+
+    PrepareService1(service1);
+    PrepareService2(service2);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Add an on-mesh prefix (with SLAAC) to network data");
+
+    prefixConfig.Clear();
+    SuccessOrQuit(AsCoreType(&prefixConfig.mPrefix.mPrefix).FromString("fd00:cafe:beef::"));
+    prefixConfig.mPrefix.mLength = 64;
+    prefixConfig.mStable         = true;
+    prefixConfig.mSlaac          = true;
+    prefixConfig.mPreferred      = true;
+    prefixConfig.mOnMesh         = true;
+    prefixConfig.mDefaultRoute   = false;
+    prefixConfig.mPreference     = NetworkData::kRoutePreferenceMedium;
+
+    SuccessOrQuit(otBorderRouterAddOnMeshPrefix(sInstance, &prefixConfig));
+    SuccessOrQuit(otBorderRouterRegister(sInstance));
+
+    // Configure Dnssd platform API behavior
+
+    sDnssdRegHostRequests.Clear();
+    sDnssdRegServiceRequests.Clear();
+    sDnssdUnregHostRequests.Clear();
+    sDnssdUnregServiceRequests.Clear();
+    sDnssdRegKeyRequests.Clear();
+    sDnssdUnregKeyRequests.Clear();
+
+    sDnssdState                 = OT_PLAT_DNSSD_READY;
+    sDnssdShouldCheckWithClient = true;
+    sDnssdCallbackError         = kErrorPending; // Do not call the callbacks directly
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Start SRP server");
+
+    SuccessOrQuit(otBorderRoutingInit(sInstance, /* aInfraIfIndex */ kInfraIfIndex, /* aInfraIfIsRunning */ true));
+
+    SuccessOrQuit(srpServer->SetAddressMode(Srp::Server::kAddressModeUnicast));
+    VerifyOrQuit(srpServer->GetAddressMode() == Srp::Server::kAddressModeUnicast);
+
+    VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateDisabled);
+
+    srpServer->SetServiceHandler(nullptr, sInstance);
+
+    srpServer->SetEnabled(true);
+    VerifyOrQuit(srpServer->GetState() != Srp::Server::kStateDisabled);
+
+    AdvanceTime(10000);
+    VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateRunning);
+    VerifyOrQuit(advProxy->IsRunning());
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Set AdvTimeout to 5 minutes on AdvProxy");
+
+    // Change the timeout on AvdertisingProxy to 5 minutes
+    // so that we can send multiple SRP updates and create
+    // situations where previous advertisement are replaced.
+
+    advProxy->SetAdvTimeout(5 * 60 * 1000);
+    VerifyOrQuit(advProxy->GetAdvTimeout() == 5 * 60 * 1000);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Start SRP client");
+
+    srpClient->SetCallback(HandleSrpClientCallback, sInstance);
+
+    srpClient->EnableAutoStartMode(nullptr, nullptr);
+    VerifyOrQuit(srpClient->IsAutoStartModeEnabled());
+
+    AdvanceTime(2000);
+    VerifyOrQuit(srpClient->IsRunning());
+
+    SuccessOrQuit(srpClient->SetHostName(kHostName));
+    SuccessOrQuit(srpClient->EnableAutoHostAddress());
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Register a service and do not invoke the registration request callbacks");
+
+    SuccessOrQuit(srpClient->AddService(service1));
+
+    sProcessedClientCallback = false;
+
+    AdvanceTime(1200);
+
+    dnssdCounts.mHostReg++;
+    dnssdCounts.mServiceReg++;
+    dnssdCounts.mKeyReg += 2;
+    VerifyDnnsdRequests(dnssdCounts);
+
+    VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 1);
+    VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 0);
+
+    VerifyOrQuit(!sProcessedClientCallback);
+    VerifyOrQuit(srpServer->GetNextHost(nullptr) == nullptr);
+
+    // SRP client min retry is 1800 msec, we wait for longer
+    // to make sure client retries.
+
+    AdvanceTime(2000);
+
+    VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 2);
+    VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 0);
+
+    // We should see no new service or host registrations on
+    // DNS-SD platform APIs as the requests should be same
+    // and fully matching the outstanding ones.
+
+    VerifyDnnsdRequests(dnssdCounts);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Invoke the DNS-SD API callbacks");
+
+    request = &sDnssdRegServiceRequests[0];
+    VerifyOrQuit(request->mCallback != nullptr);
+    request->mCallback(sInstance, request->mId, kErrorNone);
+
+    request = &sDnssdRegHostRequests[0];
+    VerifyOrQuit(request->mCallback != nullptr);
+    request->mCallback(sInstance, request->mId, kErrorNone);
+
+    for (uint16_t index = 0; index < 2; index++)
+    {
+        request = &sDnssdRegKeyRequests[index];
+        VerifyOrQuit(request->mCallback != nullptr);
+        request->mCallback(sInstance, request->mId, kErrorNone);
+    }
+
+    AdvanceTime(100);
+
+    VerifyOrQuit(sProcessedClientCallback);
+    VerifyOrQuit(sLastClientCallbackError == kErrorNone);
+    VerifyOrQuit(srpServer->GetNextHost(nullptr) != nullptr);
+
+    VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 2);
+    VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 2);
+    VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 0);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Check outstanding Adv being replaced");
+
+    // Change service 1
+    SuccessOrQuit(srpClient->ClearService(service1));
+    PrepareService1(service1);
+    service1.mSubTypeLabels = nullptr; // No sub-types
+    SuccessOrQuit(srpClient->AddService(service1));
+
+    sProcessedClientCallback = false;
+
+    AdvanceTime(1200);
+
+    VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 3);
+    VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 2);
+    VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 0);
+
+    // We should see the changed service registered on DNS-SD
+    // platform APIs.
+
+    dnssdCounts.mServiceReg++;
+    VerifyDnnsdRequests(dnssdCounts);
+
+    // Change service 1 again (add sub-types back).
+    SuccessOrQuit(srpClient->ClearService(service1));
+    PrepareService1(service1);
+    SuccessOrQuit(srpClient->AddService(service1));
+
+    AdvanceTime(1200);
+
+    VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 4);
+    VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 2);
+    VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 1);
+
+    // We should see the changed service registered on DNS-SD
+    // platform APIs again.
+
+    dnssdCounts.mServiceReg++;
+    VerifyDnnsdRequests(dnssdCounts);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Invoke the replaced entry DNS-SD API callback");
+
+    request = &sDnssdRegServiceRequests[1];
+    VerifyOrQuit(request->mCallback != nullptr);
+    request->mCallback(sInstance, request->mId, kErrorNone);
+
+    AdvanceTime(100);
+
+    // Since adv is replaced invoking the old registration callback
+    // should not complete it.
+
+    VerifyOrQuit(!sProcessedClientCallback);
+    VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 4);
+    VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 2);
+    VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 1);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Invoke the new entry DNS-SD API callback");
+
+    request = &sDnssdRegServiceRequests[2];
+    VerifyOrQuit(request->mCallback != nullptr);
+    request->mCallback(sInstance, request->mId, kErrorNone);
+
+    AdvanceTime(100);
+
+    VerifyOrQuit(sProcessedClientCallback);
+    VerifyOrQuit(sLastClientCallbackError == kErrorNone);
+
+    VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 4);
+    VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 4);
+    VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 1);
+
+    // Make sure the service entry on the SRP server is the
+    // last (most recent) request with three sub-types
+
+    VerifyOrQuit(srpServer->GetNextHost(nullptr)->GetServices().GetHead() != nullptr);
+    VerifyOrQuit(srpServer->GetNextHost(nullptr)->GetServices().GetHead()->GetNumberOfSubTypes() == 3);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Check replacing Adv being blocked till old Adv is completed");
+
+    // Change service 1 and add service 2
+    SuccessOrQuit(srpClient->ClearService(service1));
+    PrepareService1(service1);
+    service1.mSubTypeLabels = nullptr; // No sub-types
+    SuccessOrQuit(srpClient->AddService(service1));
+    SuccessOrQuit(srpClient->AddService(service2));
+
+    sProcessedClientCallback = false;
+
+    AdvanceTime(1200);
+
+    // We should see a new Adv with two new service registrations
+    // on DNS-SD APIs.
+
+    VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 5);
+    VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 4);
+    VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 1);
+
+    dnssdCounts.mServiceReg += 2;
+    dnssdCounts.mKeyReg++;
+    VerifyDnnsdRequests(dnssdCounts);
+
+    // Invoke the key registration callback
+
+    request = &sDnssdRegKeyRequests[sDnssdRegKeyRequests.GetLength() - 1];
+    VerifyOrQuit(request->mCallback != nullptr);
+    request->mCallback(sInstance, request->mId, kErrorNone);
+
+    // Now have SRP client send a new SRP update message
+    // just changing `service2`. We clear `servcie1` on client
+    // so it is not included in new SRP update message.
+
+    SuccessOrQuit(srpClient->ClearService(service1));
+    SuccessOrQuit(srpClient->ClearService(service2));
+    PrepareService2(service2);
+    service2.mPort = 2222; // Use a different port number
+    SuccessOrQuit(srpClient->AddService(service2));
+
+    AdvanceTime(1200);
+
+    // We should see the new Adv (total increasing) and
+    // also replacing the outstanding one
+
+    VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 6);
+    VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 4);
+    VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 2);
+
+    // We should see new registration for the changed `service2`
+
+    dnssdCounts.mServiceReg++;
+    VerifyDnnsdRequests(dnssdCounts);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Invoke the callback for new registration replacing old one first");
+
+    request = &sDnssdRegServiceRequests[5];
+    VerifyOrQuit(request->mCallback != nullptr);
+    request->mCallback(sInstance, request->mId, kErrorNone);
+
+    AdvanceTime(100);
+
+    // This should not change anything, since the new Avd should
+    // be still blocked by the earlier Adv that it replaced.
+
+    VerifyOrQuit(!sProcessedClientCallback);
+
+    VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 6);
+    VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 4);
+    VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 2);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Invoke the callback for replaced Adv services");
+
+    request = &sDnssdRegServiceRequests[4];
+    VerifyOrQuit(request->mCallback != nullptr);
+    request->mCallback(sInstance, request->mId, kErrorNone);
+
+    request = &sDnssdRegServiceRequests[3];
+    VerifyOrQuit(request->mCallback != nullptr);
+    request->mCallback(sInstance, request->mId, kErrorNone);
+
+    AdvanceTime(100);
+
+    // This should trigger both Adv to complete.
+
+    VerifyOrQuit(sProcessedClientCallback);
+    VerifyOrQuit(sLastClientCallbackError == kErrorNone);
+
+    VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 6);
+    VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 6);
+    VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 2);
+
+    VerifyOrQuit(service2.GetState() == Srp::Client::kRegistered);
+
+    // Make sure the `service2` entry on the SRP server is the
+    // last (most recent) request with new port number.
+
+    VerifyOrQuit(srpServer->GetNextHost(nullptr)->GetServices().GetHead() != nullptr);
+
+    numServices = 0;
+
+    for (const Srp::Server::Service &service : srpServer->GetNextHost(nullptr)->GetServices())
+    {
+        numServices++;
+
+        if (StringMatch(service.GetInstanceLabel(), service2.GetInstanceName(), kStringCaseInsensitiveMatch))
+        {
+            VerifyOrQuit(service.GetPort() == service2.GetPort());
+        }
+        else if (StringMatch(service.GetInstanceLabel(), service1.GetInstanceName(), kStringCaseInsensitiveMatch))
+        {
+            // Service 1 was changed to have no sub-types
+            VerifyOrQuit(service.GetPort() == service1.GetPort());
+            VerifyOrQuit(service.GetNumberOfSubTypes() == 0);
+        }
+        else
+        {
+            VerifyOrQuit(false); // Unexpected extra service on SRP server.
+        }
+    }
+
+    VerifyOrQuit(numServices == 2);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Check replacing Adv being blocked till old Adv is completed when removing services");
+
+    // Change and re-add both services so they are both
+    // included in a new SRP update message from client.
+
+    SuccessOrQuit(srpClient->ClearService(service2));
+    PrepareService1(service1);
+    PrepareService2(service2);
+    SuccessOrQuit(srpClient->AddService(service1));
+    SuccessOrQuit(srpClient->AddService(service2));
+
+    sProcessedClientCallback = false;
+
+    AdvanceTime(1200);
+
+    // We should see a new Adv with two new service registrations
+    // on DNS-SD APIs.
+
+    VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 7);
+    VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 6);
+    VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 2);
+
+    dnssdCounts.mServiceReg += 2;
+    VerifyDnnsdRequests(dnssdCounts);
+
+    // Now have SRP client send a new SRP update message
+    // just removing `service1`. We clear `servcie2` on client
+    // so it is not included in new SRP update message.
+
+    SuccessOrQuit(srpClient->RemoveService(service1));
+    SuccessOrQuit(srpClient->ClearService(service2));
+
+    AdvanceTime(1200);
+
+    // We should see a new Adv added replacing the outstanding
+    // one.
+
+    VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 8);
+    VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 6);
+    VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 3);
+
+    dnssdCounts.mServiceUnreg++;
+    VerifyDnnsdRequests(dnssdCounts);
+
+    // Even though the new SRP update which removed `servcie2`
+    // is already unregistered, it should be blocked by the
+    // earlier Adv.
+
+    VerifyOrQuit(!sProcessedClientCallback);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Invoke the callback for replaced Adv services");
+
+    request = &sDnssdRegServiceRequests[6];
+    VerifyOrQuit(request->mCallback != nullptr);
+    request->mCallback(sInstance, request->mId, kErrorNone);
+
+    request = &sDnssdRegServiceRequests[7];
+    VerifyOrQuit(request->mCallback != nullptr);
+    request->mCallback(sInstance, request->mId, kErrorNone);
+
+    AdvanceTime(100);
+
+    // This should trigger both Adv to complete, and first one
+    // should be committed before the second one removing the
+    // `service2`.
+
+    VerifyOrQuit(sProcessedClientCallback);
+    VerifyOrQuit(sLastClientCallbackError == kErrorNone);
+
+    VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 8);
+    VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 8);
+    VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 3);
+
+    VerifyOrQuit(service1.GetState() == Srp::Client::kRemoved);
+
+    // Check services on server and make sure `service2`
+    // is marked as deleted.
+
+    VerifyOrQuit(srpServer->GetNextHost(nullptr)->GetServices().GetHead() != nullptr);
+
+    numServices = 0;
+
+    for (const Srp::Server::Service &service : srpServer->GetNextHost(nullptr)->GetServices())
+    {
+        numServices++;
+
+        if (StringMatch(service.GetInstanceLabel(), service1.GetInstanceName(), kStringCaseInsensitiveMatch))
+        {
+            VerifyOrQuit(service.IsDeleted());
+        }
+        else if (StringMatch(service.GetInstanceLabel(), service2.GetInstanceName(), kStringCaseInsensitiveMatch))
+        {
+            VerifyOrQuit(!service.IsDeleted());
+        }
+        else
+        {
+            VerifyOrQuit(false); // Unexpected extra service on SRP server.
+        }
+    }
+
+    VerifyOrQuit(numServices == 2);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Disable SRP server");
+
+    sDnssdShouldCheckWithClient = false;
+
+    // Verify that all heap allocations by SRP server
+    // and Advertising Proxy are freed.
+
+    srpServer->SetEnabled(false);
+    AdvanceTime(100);
+
+    VerifyOrQuit(heapAllocations == sHeapAllocatedPtrs.GetLength());
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Finalize OT instance and validate all heap allocations are freed");
+
+    FinalizeTest();
+
+    VerifyOrQuit(sHeapAllocatedPtrs.IsEmpty());
+
+    Log("End of TestSrpAdvProxyReplacedEntries");
+}
+
+void TestSrpAdvProxyHostWithOffMeshRoutableAddress(void)
+{
+    NetworkData::OnMeshPrefixConfig prefixConfig;
+    Srp::Server                    *srpServer;
+    Srp::Client                    *srpClient;
+    Srp::AdvertisingProxy          *advProxy;
+    Srp::Client::Service            service1;
+    Srp::Client::Service            service2;
+    DnssdRequestCounts              dnssdCounts;
+    uint16_t                        heapAllocations;
+    const DnssdRequest             *request;
+
+    Log("--------------------------------------------------------------------------------------------");
+    Log("TestSrpAdvProxyHostWithOffMeshRoutableAddress");
+
+    InitTest();
+
+    srpServer = &sInstance->Get<Srp::Server>();
+    srpClient = &sInstance->Get<Srp::Client>();
+    advProxy  = &sInstance->Get<Srp::AdvertisingProxy>();
+
+    heapAllocations = sHeapAllocatedPtrs.GetLength();
+
+    PrepareService1(service1);
+    PrepareService2(service2);
+
+    // Configure Dnssd platform API behavior
+
+    sDnssdRegHostRequests.Clear();
+    sDnssdRegServiceRequests.Clear();
+    sDnssdUnregHostRequests.Clear();
+    sDnssdUnregServiceRequests.Clear();
+    sDnssdRegKeyRequests.Clear();
+    sDnssdUnregKeyRequests.Clear();
+
+    sDnssdState                 = OT_PLAT_DNSSD_READY;
+    sDnssdShouldCheckWithClient = true;
+    sDnssdCallbackError         = kErrorNone; // Invoke callback directly from dnssd APIs
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Start SRP server");
+
+    SuccessOrQuit(otBorderRoutingInit(sInstance, /* aInfraIfIndex */ kInfraIfIndex, /* aInfraIfIsRunning */ true));
+
+    SuccessOrQuit(srpServer->SetAddressMode(Srp::Server::kAddressModeUnicast));
+    VerifyOrQuit(srpServer->GetAddressMode() == Srp::Server::kAddressModeUnicast);
+
+    VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateDisabled);
+
+    srpServer->SetServiceHandler(nullptr, sInstance);
+
+    srpServer->SetEnabled(true);
+    VerifyOrQuit(srpServer->GetState() != Srp::Server::kStateDisabled);
+
+    AdvanceTime(10000);
+    VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateRunning);
+    VerifyOrQuit(advProxy->IsRunning());
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Start SRP client");
+
+    srpClient->SetCallback(HandleSrpClientCallback, sInstance);
+    srpClient->SetLeaseInterval(400);
+
+    srpClient->EnableAutoStartMode(nullptr, nullptr);
+    VerifyOrQuit(srpClient->IsAutoStartModeEnabled());
+
+    AdvanceTime(2000);
+    VerifyOrQuit(srpClient->IsRunning());
+
+    SuccessOrQuit(srpClient->SetHostName(kHostName));
+    SuccessOrQuit(srpClient->EnableAutoHostAddress());
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Register a service");
+
+    SuccessOrQuit(srpClient->AddService(service1));
+
+    sProcessedClientCallback = false;
+
+    AdvanceTime(2 * 1000);
+
+    dnssdCounts.mHostReg++;
+    dnssdCounts.mServiceReg++;
+    dnssdCounts.mKeyReg += 2;
+
+    VerifyDnnsdRequests(dnssdCounts);
+    VerifyOrQuit(sDnssdNumHostAddresses == 0);
+
+    VerifyOrQuit(sProcessedClientCallback);
+    VerifyOrQuit(sLastClientCallbackError == kErrorNone);
+
+    VerifyOrQuit(service1.GetState() == Srp::Client::kRegistered);
+
+    VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 1);
+    VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 1);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Register a second service");
+
+    SuccessOrQuit(srpClient->AddService(service2));
+
+    sProcessedClientCallback = false;
+
+    AdvanceTime(2 * 1000);
+
+    dnssdCounts.mServiceReg++;
+    dnssdCounts.mKeyReg++;
+    VerifyDnnsdRequests(dnssdCounts);
+
+    VerifyOrQuit(sProcessedClientCallback);
+    VerifyOrQuit(sLastClientCallbackError == kErrorNone);
+
+    VerifyOrQuit(service1.GetState() == Srp::Client::kRegistered);
+    VerifyOrQuit(service2.GetState() == Srp::Client::kRegistered);
+
+    VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 2);
+    VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 2);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Disable SRP server");
+
+    // Verify that all heap allocations by SRP server
+    // and Advertising Proxy are freed.
+
+    srpServer->SetEnabled(false);
+    AdvanceTime(100);
+    VerifyOrQuit(!advProxy->IsRunning());
+
+    VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == advProxy->GetCounters().mAdvTotal);
+    VerifyOrQuit(advProxy->GetCounters().mAdvTimeout == 0);
+    VerifyOrQuit(advProxy->GetCounters().mAdvRejected == 0);
+    VerifyOrQuit(advProxy->GetCounters().mAdvSkipped == 0);
+    VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 0);
+
+    VerifyOrQuit(heapAllocations == sHeapAllocatedPtrs.GetLength());
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Finalize OT instance and validate all heap allocations are freed");
+
+    FinalizeTest();
+
+    VerifyOrQuit(sHeapAllocatedPtrs.IsEmpty());
+
+    Log("End of TestSrpAdvProxyHostWithOffMeshRoutableAddress");
+}
+
+void TestSrpAdvProxyRemoveBeforeCommitted(void)
+{
+    NetworkData::OnMeshPrefixConfig prefixConfig;
+    Srp::Server                    *srpServer;
+    Srp::Client                    *srpClient;
+    Srp::AdvertisingProxy          *advProxy;
+    Srp::Client::Service            service1;
+    Srp::Client::Service            service2;
+    DnssdRequestCounts              dnssdCounts;
+    uint16_t                        heapAllocations;
+    const DnssdRequest             *request;
+
+    Log("--------------------------------------------------------------------------------------------");
+    Log("TestSrpAdvProxyRemoveBeforeCommitted");
+
+    InitTest();
+
+    srpServer = &sInstance->Get<Srp::Server>();
+    srpClient = &sInstance->Get<Srp::Client>();
+    advProxy  = &sInstance->Get<Srp::AdvertisingProxy>();
+
+    heapAllocations = sHeapAllocatedPtrs.GetLength();
+
+    PrepareService1(service1);
+    PrepareService2(service2);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Add an on-mesh prefix (with SLAAC) to network data");
+
+    prefixConfig.Clear();
+    SuccessOrQuit(AsCoreType(&prefixConfig.mPrefix.mPrefix).FromString("fd00:cafe:beef::"));
+    prefixConfig.mPrefix.mLength = 64;
+    prefixConfig.mStable         = true;
+    prefixConfig.mSlaac          = true;
+    prefixConfig.mPreferred      = true;
+    prefixConfig.mOnMesh         = true;
+    prefixConfig.mDefaultRoute   = false;
+    prefixConfig.mPreference     = NetworkData::kRoutePreferenceMedium;
+
+    SuccessOrQuit(otBorderRouterAddOnMeshPrefix(sInstance, &prefixConfig));
+    SuccessOrQuit(otBorderRouterRegister(sInstance));
+
+    // Configure Dnssd platform API behavior
+
+    sDnssdRegHostRequests.Clear();
+    sDnssdRegServiceRequests.Clear();
+    sDnssdUnregHostRequests.Clear();
+    sDnssdUnregServiceRequests.Clear();
+    sDnssdRegKeyRequests.Clear();
+    sDnssdUnregKeyRequests.Clear();
+
+    sDnssdState                 = OT_PLAT_DNSSD_READY;
+    sDnssdShouldCheckWithClient = true;
+    sDnssdCallbackError         = kErrorNone; // Do not call the callbacks directly
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Start SRP server");
+
+    SuccessOrQuit(otBorderRoutingInit(sInstance, /* aInfraIfIndex */ kInfraIfIndex, /* aInfraIfIsRunning */ true));
+
+    SuccessOrQuit(srpServer->SetAddressMode(Srp::Server::kAddressModeUnicast));
+    VerifyOrQuit(srpServer->GetAddressMode() == Srp::Server::kAddressModeUnicast);
+
+    VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateDisabled);
+
+    srpServer->SetServiceHandler(nullptr, sInstance);
+
+    srpServer->SetEnabled(true);
+    VerifyOrQuit(srpServer->GetState() != Srp::Server::kStateDisabled);
+
+    AdvanceTime(10000);
+    VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateRunning);
+    VerifyOrQuit(advProxy->IsRunning());
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Start SRP client");
+
+    srpClient->SetCallback(HandleSrpClientCallback, sInstance);
+
+    srpClient->EnableAutoStartMode(nullptr, nullptr);
+    VerifyOrQuit(srpClient->IsAutoStartModeEnabled());
+
+    AdvanceTime(2000);
+    VerifyOrQuit(srpClient->IsRunning());
+
+    SuccessOrQuit(srpClient->SetHostName(kHostName));
+    SuccessOrQuit(srpClient->EnableAutoHostAddress());
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Register host and one service");
+
+    SuccessOrQuit(srpClient->AddService(service1));
+
+    sProcessedClientCallback = false;
+
+    AdvanceTime(2000);
+
+    dnssdCounts.mHostReg++;
+    dnssdCounts.mServiceReg++;
+    dnssdCounts.mKeyReg += 2;
+    VerifyDnnsdRequests(dnssdCounts);
+
+    VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 1);
+    VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 1);
+    VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 0);
+
+    VerifyOrQuit(sProcessedClientCallback);
+    VerifyOrQuit(sLastClientCallbackError == kErrorNone);
+
+    VerifyOrQuit(service1.GetState() == Srp::Client::kRegistered);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Set AdvTimeout to 5 minutes on AdvProxy");
+
+    // Change the timeout on AvdertisingProxy to 5 minutes
+    // so that we can send multiple SRP updates and create
+    // situations where previous advertisement are replaced.
+
+    advProxy->SetAdvTimeout(5 * 60 * 1000);
+    VerifyOrQuit(advProxy->GetAdvTimeout() == 5 * 60 * 1000);
+
+    sDnssdCallbackError = kErrorPending; // Do not invoke callback
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Remove service1 while adding a new service2 and do not invoke callback from DNSSD plat");
+
+    SuccessOrQuit(srpClient->RemoveService(service1));
+    SuccessOrQuit(srpClient->AddService(service2));
+
+    sProcessedClientCallback = false;
+
+    AdvanceTime(1000);
+
+    dnssdCounts.mServiceReg++;
+    dnssdCounts.mServiceUnreg++;
+    dnssdCounts.mKeyReg++;
+    VerifyDnnsdRequests(dnssdCounts);
+
+    VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 2);
+    VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 1);
+    VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 0);
+
+    VerifyOrQuit(!sProcessedClientCallback);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Remove host and its services without removing key-lease");
+
+    SuccessOrQuit(srpClient->RemoveHostAndServices(/* aShouldRemoveKeyLease */ false));
+
+    AdvanceTime(1000);
+
+    // Proxy will unregister both services again
+    // (to be safe).
+
+    dnssdCounts.mHostUnreg++;
+    dnssdCounts.mServiceUnreg++;
+    VerifyDnnsdRequests(dnssdCounts, /* aAllowMoreUnregs */ true);
+
+    VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 3);
+    VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 1);
+    VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 1);
+
+    VerifyOrQuit(!sProcessedClientCallback);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Invoke callback for last key registration");
+
+    // This should be enough for all `AdvInfo` entries to be finished.
+
+    request = &sDnssdRegKeyRequests[sDnssdRegKeyRequests.GetLength() - 1];
+    VerifyOrQuit(request->mCallback != nullptr);
+    request->mCallback(sInstance, request->mId, kErrorNone);
+
+    AdvanceTime(50);
+
+    VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 3);
+    VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 3);
+    VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 1);
+
+    VerifyOrQuit(sProcessedClientCallback);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Disable SRP server");
+
+    sDnssdShouldCheckWithClient = false;
+
+    // Verify that all heap allocations by SRP server
+    // and Advertising Proxy are freed.
+
+    srpServer->SetEnabled(false);
+    AdvanceTime(100);
+
+    VerifyOrQuit(heapAllocations == sHeapAllocatedPtrs.GetLength());
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Finalize OT instance and validate all heap allocations are freed");
+
+    FinalizeTest();
+
+    VerifyOrQuit(sHeapAllocatedPtrs.IsEmpty());
+
+    Log("End of TestSrpAdvProxyRemoveBeforeCommitted");
+}
+
+void TestSrpAdvProxyFullyRemoveBeforeCommitted(void)
+{
+    NetworkData::OnMeshPrefixConfig prefixConfig;
+    Srp::Server                    *srpServer;
+    Srp::Client                    *srpClient;
+    Srp::AdvertisingProxy          *advProxy;
+    Srp::Client::Service            service1;
+    Srp::Client::Service            service2;
+    DnssdRequestCounts              dnssdCounts;
+    uint16_t                        heapAllocations;
+    const DnssdRequest             *request;
+
+    Log("--------------------------------------------------------------------------------------------");
+    Log("TestSrpAdvProxyFullyRemoveBeforeCommitted");
+
+    InitTest();
+
+    srpServer = &sInstance->Get<Srp::Server>();
+    srpClient = &sInstance->Get<Srp::Client>();
+    advProxy  = &sInstance->Get<Srp::AdvertisingProxy>();
+
+    heapAllocations = sHeapAllocatedPtrs.GetLength();
+
+    PrepareService1(service1);
+    PrepareService2(service2);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Add an on-mesh prefix (with SLAAC) to network data");
+
+    prefixConfig.Clear();
+    SuccessOrQuit(AsCoreType(&prefixConfig.mPrefix.mPrefix).FromString("fd00:cafe:beef::"));
+    prefixConfig.mPrefix.mLength = 64;
+    prefixConfig.mStable         = true;
+    prefixConfig.mSlaac          = true;
+    prefixConfig.mPreferred      = true;
+    prefixConfig.mOnMesh         = true;
+    prefixConfig.mDefaultRoute   = false;
+    prefixConfig.mPreference     = NetworkData::kRoutePreferenceMedium;
+
+    SuccessOrQuit(otBorderRouterAddOnMeshPrefix(sInstance, &prefixConfig));
+    SuccessOrQuit(otBorderRouterRegister(sInstance));
+
+    // Configure Dnssd platform API behavior
+
+    sDnssdRegHostRequests.Clear();
+    sDnssdRegServiceRequests.Clear();
+    sDnssdUnregHostRequests.Clear();
+    sDnssdUnregServiceRequests.Clear();
+    sDnssdRegKeyRequests.Clear();
+    sDnssdUnregKeyRequests.Clear();
+
+    sDnssdState                 = OT_PLAT_DNSSD_READY;
+    sDnssdShouldCheckWithClient = true;
+    sDnssdCallbackError         = kErrorNone; // Do not call the callbacks directly
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Start SRP server");
+
+    SuccessOrQuit(otBorderRoutingInit(sInstance, /* aInfraIfIndex */ kInfraIfIndex, /* aInfraIfIsRunning */ true));
+
+    SuccessOrQuit(srpServer->SetAddressMode(Srp::Server::kAddressModeUnicast));
+    VerifyOrQuit(srpServer->GetAddressMode() == Srp::Server::kAddressModeUnicast);
+
+    VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateDisabled);
+
+    srpServer->SetServiceHandler(nullptr, sInstance);
+
+    srpServer->SetEnabled(true);
+    VerifyOrQuit(srpServer->GetState() != Srp::Server::kStateDisabled);
+
+    AdvanceTime(10000);
+    VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateRunning);
+    VerifyOrQuit(advProxy->IsRunning());
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Start SRP client");
+
+    srpClient->SetCallback(HandleSrpClientCallback, sInstance);
+
+    srpClient->EnableAutoStartMode(nullptr, nullptr);
+    VerifyOrQuit(srpClient->IsAutoStartModeEnabled());
+
+    AdvanceTime(2000);
+    VerifyOrQuit(srpClient->IsRunning());
+
+    SuccessOrQuit(srpClient->SetHostName(kHostName));
+    SuccessOrQuit(srpClient->EnableAutoHostAddress());
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Register host and one service");
+
+    SuccessOrQuit(srpClient->AddService(service1));
+
+    sProcessedClientCallback = false;
+
+    AdvanceTime(2000);
+
+    dnssdCounts.mHostReg++;
+    dnssdCounts.mServiceReg++;
+    dnssdCounts.mKeyReg += 2;
+    VerifyDnnsdRequests(dnssdCounts);
+
+    VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 1);
+    VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 1);
+    VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 0);
+
+    VerifyOrQuit(sProcessedClientCallback);
+    VerifyOrQuit(sLastClientCallbackError == kErrorNone);
+
+    VerifyOrQuit(service1.GetState() == Srp::Client::kRegistered);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Set AdvTimeout to 5 minutes on AdvProxy");
+
+    // Change the timeout on AvdertisingProxy to 5 minutes
+    // so that we can send multiple SRP updates and create
+    // situations where previous advertisement are replaced.
+
+    advProxy->SetAdvTimeout(5 * 60 * 1000);
+    VerifyOrQuit(advProxy->GetAdvTimeout() == 5 * 60 * 1000);
+
+    sDnssdCallbackError = kErrorPending; // Do not invoke callback
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Remove service1 while adding a new service2 and do not invoke callback from DNSSD plat");
+
+    SuccessOrQuit(srpClient->RemoveService(service1));
+    SuccessOrQuit(srpClient->AddService(service2));
+
+    sProcessedClientCallback = false;
+
+    AdvanceTime(1000);
+
+    dnssdCounts.mServiceReg++;
+    dnssdCounts.mServiceUnreg++;
+    dnssdCounts.mKeyReg++;
+    VerifyDnnsdRequests(dnssdCounts);
+
+    VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 2);
+    VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 1);
+    VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 0);
+
+    VerifyOrQuit(!sProcessedClientCallback);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Remove host and its services and remove key-lease");
+
+    SuccessOrQuit(srpClient->RemoveHostAndServices(/* aShouldRemoveKeyLease */ true));
+
+    AdvanceTime(1000);
+
+    // Proxy should unregister everything.
+    //  Keys may be unregistered multiple times.
+
+    dnssdCounts.mHostUnreg++;
+    dnssdCounts.mServiceUnreg++;
+    dnssdCounts.mKeyUnreg += 3;
+    VerifyDnnsdRequests(dnssdCounts, /* aAllowMoreUnregs */ true);
+
+    VerifyOrQuit(advProxy->GetCounters().mAdvTotal == 3);
+    VerifyOrQuit(advProxy->GetCounters().mAdvSuccessful == 3);
+    VerifyOrQuit(advProxy->GetCounters().mAdvReplaced == 1);
+
+    VerifyOrQuit(sProcessedClientCallback);
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Disable SRP server");
+
+    sDnssdShouldCheckWithClient = false;
+
+    // Verify that all heap allocations by SRP server
+    // and Advertising Proxy are freed.
+
+    srpServer->SetEnabled(false);
+    AdvanceTime(100);
+
+    VerifyOrQuit(heapAllocations == sHeapAllocatedPtrs.GetLength());
+
+    Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+    Log("Finalize OT instance and validate all heap allocations are freed");
+
+    FinalizeTest();
+
+    VerifyOrQuit(sHeapAllocatedPtrs.IsEmpty());
+
+    Log("End of TestSrpAdvProxyFullyRemoveBeforeCommitted");
+}
+
+#endif // ENABLE_ADV_PROXY_TEST
+
+int main(void)
+{
+#if ENABLE_ADV_PROXY_TEST
+    TestDnssdRequestIdRange();
+    TestSrpAdvProxy();
+    TestSrpAdvProxyDnssdStateChange();
+    TestSrpAdvProxyDelayedCallback();
+    TestSrpAdvProxyReplacedEntries();
+    TestSrpAdvProxyHostWithOffMeshRoutableAddress();
+    TestSrpAdvProxyRemoveBeforeCommitted();
+    TestSrpAdvProxyFullyRemoveBeforeCommitted();
+
+    printf("All tests passed\n");
+#else
+    printf("SRP_ADV_PROXY feature is not enabled\n");
+#endif
+
+    return 0;
+}
diff --git a/tests/unit/test_tcat.cpp b/tests/unit/test_tcat.cpp
new file mode 100644
index 0000000..6767a0f
--- /dev/null
+++ b/tests/unit/test_tcat.cpp
@@ -0,0 +1,170 @@
+/*
+ *  Copyright (c) 2024, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "openthread-core-config.h"
+
+#include "test_platform.h"
+#include "test_util.h"
+
+#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
+
+#include <openthread/ble_secure.h>
+
+#define OT_TCAT_X509_CERT                                                  \
+    "-----BEGIN CERTIFICATE-----\r\n"                                      \
+    "MIIBmDCCAT+gAwIBAgIEAQIDBDAKBggqhkjOPQQDAjBvMQswCQYDVQQGEwJYWDEQ\r\n" \
+    "MA4GA1UECBMHTXlTdGF0ZTEPMA0GA1UEBxMGTXlDaXR5MQ8wDQYDVQQLEwZNeVVu\r\n" \
+    "aXQxETAPBgNVBAoTCE15VmVuZG9yMRkwFwYDVQQDExB3d3cubXl2ZW5kb3IuY29t\r\n" \
+    "MB4XDTIzMTAxNjEwMzk1NFoXDTI0MTAxNjEwMzk1NFowIjEgMB4GA1UEAxMXbXl2\r\n" \
+    "ZW5kb3IuY29tL3RjYXQvbXlkZXYwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQB\r\n" \
+    "aWwFDNj1bpQIdN+Kp2cHWw55U/+fa+OmZnoy1B4BOT+822jdwPBuyXWAQoBdYdQJ\r\n" \
+    "ff4RgmhczyV4PhArPIuAoxYwFDASBgkrBgEEAYLfKgMEBQABAQEBMAoGCCqGSM49\r\n" \
+    "BAMCA0cAMEQCIBEHxiEDij26y6V77Q311Gj4CZAuZuPGXZpnzL2BLk7bAiAlFk6G\r\n" \
+    "mYGzkcrYyssFI9HlPgrisWoMmgummaTtCuvrEw==\r\n"                         \
+    "-----END CERTIFICATE-----\r\n"
+
+#define OT_TCAT_PRIV_KEY                                                   \
+    "-----BEGIN EC PRIVATE KEY-----\r\n"                                   \
+    "MHcCAQEEIDeJ6lVQKiOIBxKwTZp6TkU5QVHt9pvXOR9CGpPBI3DhoAoGCCqGSM49\r\n" \
+    "AwEHoUQDQgAEAWlsBQzY9W6UCHTfiqdnB1sOeVP/n2vjpmZ6MtQeATk/vNto3cDw\r\n" \
+    "bsl1gEKAXWHUCX3+EYJoXM8leD4QKzyLgA==\r\n"                             \
+    "-----END EC PRIVATE KEY-----\r\n"
+
+#define OT_TCAT_TRUSTED_ROOT_CERTIFICATE                                   \
+    "-----BEGIN CERTIFICATE-----\r\n"                                      \
+    "MIICCDCCAa2gAwIBAgIJAIKxygBXoH+5MAoGCCqGSM49BAMCMG8xCzAJBgNVBAYT\r\n" \
+    "AlhYMRAwDgYDVQQIEwdNeVN0YXRlMQ8wDQYDVQQHEwZNeUNpdHkxDzANBgNVBAsT\r\n" \
+    "Bk15VW5pdDERMA8GA1UEChMITXlWZW5kb3IxGTAXBgNVBAMTEHd3dy5teXZlbmRv\r\n" \
+    "ci5jb20wHhcNMjMxMDE2MTAzMzE1WhcNMjYxMDE2MTAzMzE1WjBvMQswCQYDVQQG\r\n" \
+    "EwJYWDEQMA4GA1UECBMHTXlTdGF0ZTEPMA0GA1UEBxMGTXlDaXR5MQ8wDQYDVQQL\r\n" \
+    "EwZNeVVuaXQxETAPBgNVBAoTCE15VmVuZG9yMRkwFwYDVQQDExB3d3cubXl2ZW5k\r\n" \
+    "b3IuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEWdyzPAXGKeZY94OhHAWX\r\n" \
+    "HzJfQIjGSyaOzlgL9OEFw2SoUDncLKPGwfPAUSfuMyEkzszNDM0HHkBsDLqu4n25\r\n" \
+    "/6MyMDAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU4EynoSw9eDKZEVPkums2\r\n" \
+    "IWLAJCowCgYIKoZIzj0EAwIDSQAwRgIhAMYGGL9xShyE6P9wEU+MAYF6W3CzdrwV\r\n" \
+    "kuerX1encIH2AiEA5rq490NUobM1Au43roxJq1T6Z43LscPVbGZfULD1Jq0=\r\n"     \
+    "-----END CERTIFICATE-----\r\n"
+
+namespace ot {
+
+class TestBleSecure
+{
+public:
+    TestBleSecure(void)
+        : mIsConnected(false)
+        , mIsBleConnectionOpen(false)
+    {
+    }
+
+    void HandleBleSecureConnect(bool aConnected, bool aBleConnectionOpen)
+    {
+        mIsConnected         = aConnected;
+        mIsBleConnectionOpen = aBleConnectionOpen;
+    }
+
+    bool IsConnected(void) const { return mIsConnected; }
+    bool IsBleConnectionOpen(void) const { return mIsBleConnectionOpen; }
+
+private:
+    bool mIsConnected;
+    bool mIsBleConnectionOpen;
+};
+
+static void HandleBleSecureConnect(otInstance *aInstance, bool aConnected, bool aBleConnectionOpen, void *aContext)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+
+    static_cast<TestBleSecure *>(aContext)->HandleBleSecureConnect(aConnected, aBleConnectionOpen);
+}
+
+void TestTcat(void)
+{
+    const char         kPskdVendor[] = "J01NM3";
+    const char         kUrl[]        = "dummy_url";
+    constexpr uint16_t kConnectionId = 0;
+
+    TestBleSecure ble;
+    Instance     *instance = testInitInstance();
+
+    otTcatVendorInfo vendorInfo = {.mProvisioningUrl = kUrl, .mPskdString = kPskdVendor};
+
+    otBleSecureSetCertificate(instance, reinterpret_cast<const uint8_t *>(OT_TCAT_X509_CERT), sizeof(OT_TCAT_X509_CERT),
+                              reinterpret_cast<const uint8_t *>(OT_TCAT_PRIV_KEY), sizeof(OT_TCAT_PRIV_KEY));
+
+    otBleSecureSetCaCertificateChain(instance, reinterpret_cast<const uint8_t *>(OT_TCAT_TRUSTED_ROOT_CERTIFICATE),
+                                     sizeof(OT_TCAT_TRUSTED_ROOT_CERTIFICATE));
+
+    otBleSecureSetSslAuthMode(instance, true);
+
+    // Validate BLE secure and Tcat start APIs
+    VerifyOrQuit(otBleSecureTcatStart(instance, &vendorInfo, nullptr) == kErrorInvalidState);
+    SuccessOrQuit(otBleSecureStart(instance, HandleBleSecureConnect, nullptr, true, &ble));
+    VerifyOrQuit(otBleSecureStart(instance, HandleBleSecureConnect, nullptr, true, nullptr) == kErrorAlready);
+    SuccessOrQuit(otBleSecureTcatStart(instance, &vendorInfo, nullptr));
+
+    // Validate connection callbacks when platform informs that peer has connected/disconnected
+    otPlatBleGapOnConnected(instance, kConnectionId);
+    VerifyOrQuit(!ble.IsConnected() && ble.IsBleConnectionOpen());
+    otPlatBleGapOnDisconnected(instance, kConnectionId);
+    VerifyOrQuit(!ble.IsConnected() && !ble.IsBleConnectionOpen());
+
+    // Validate connection callbacks when calling `otBleSecureDisconnect()`
+    otPlatBleGapOnConnected(instance, kConnectionId);
+    VerifyOrQuit(!ble.IsConnected() && ble.IsBleConnectionOpen());
+    otBleSecureDisconnect(instance);
+    VerifyOrQuit(!ble.IsConnected() && !ble.IsBleConnectionOpen());
+
+    // Validate TLS connection can be started only when peer is connected
+    otPlatBleGapOnConnected(instance, kConnectionId);
+    SuccessOrQuit(otBleSecureConnect(instance));
+    otBleSecureDisconnect(instance);
+    VerifyOrQuit(otBleSecureConnect(instance) == kErrorInvalidState);
+
+    // Validate Tcat state changes after stopping BLE secure
+    VerifyOrQuit(otBleSecureIsTcatEnabled(instance));
+    otBleSecureStop(instance);
+    VerifyOrQuit(!otBleSecureIsTcatEnabled(instance));
+
+    testFreeInstance(instance);
+}
+
+} // namespace ot
+
+#endif // OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
+
+int main(void)
+{
+#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
+    ot::TestTcat();
+    printf("All tests passed\n");
+#else
+    printf("Tcat is not enabled\n");
+    return -1;
+#endif
+    return 0;
+}
diff --git a/third_party/mbedtls/README.md b/third_party/mbedtls/README.md
index 2af292d..176bf93 100644
--- a/third_party/mbedtls/README.md
+++ b/third_party/mbedtls/README.md
@@ -2,11 +2,11 @@
 
 ## URL
 
-https://github.com/ARMmbed/mbedtls/releases/tag/mbedtls-2.28.0
+https://github.com/ARMmbed/mbedtls/releases/tag/mbedtls-2.28.7
 
 ## Version
 
-2.28.0
+2.28.7
 
 ## License
 
diff --git a/third_party/mbedtls/mbedtls-config.h b/third_party/mbedtls/mbedtls-config.h
index fcb1739..29aa49e 100644
--- a/third_party/mbedtls/mbedtls-config.h
+++ b/third_party/mbedtls/mbedtls-config.h
@@ -92,6 +92,11 @@
 #define MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
 #endif
 
+#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
+#define MBEDTLS_SSL_KEEP_PEER_CERTIFICATE
+#define MBEDTLS_GCM_C
+#endif
+
 #ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
 #define MBEDTLS_BASE64_C
 #define MBEDTLS_ECDH_C
@@ -128,7 +133,9 @@
 #define MBEDTLS_MEMORY_BUFFER_ALLOC_C
 #endif
 
-#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
+#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
+#define MBEDTLS_SSL_MAX_CONTENT_LEN      2000 /**< Maxium fragment length in bytes */
+#elif OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
 #define MBEDTLS_SSL_MAX_CONTENT_LEN      900 /**< Maxium fragment length in bytes */
 #else
 #define MBEDTLS_SSL_MAX_CONTENT_LEN      768 /**< Maxium fragment length in bytes */
diff --git a/third_party/mbedtls/repo/.gitattributes b/third_party/mbedtls/repo/.gitattributes
new file mode 100644
index 0000000..ceb59d7
--- /dev/null
+++ b/third_party/mbedtls/repo/.gitattributes
@@ -0,0 +1,2 @@
+# Classify all '.function' files as C for syntax highlighting purposes
+*.function linguist-language=C
diff --git a/third_party/mbedtls/repo/.github/issue_template.md b/third_party/mbedtls/repo/.github/issue_template.md
index 370066f..d4573a4 100644
--- a/third_party/mbedtls/repo/.github/issue_template.md
+++ b/third_party/mbedtls/repo/.github/issue_template.md
@@ -20,7 +20,7 @@
 **OS**  
 Mbed OS|linux|windows|
 
-**mbed TLS build:**  
+**Mbed TLS build:**  
 Version: x.x.x or git commit id  
 OS version: x.x.x  
 Configuration: please attach config.h file where possible  
diff --git a/third_party/mbedtls/repo/.github/pull_request_template.md b/third_party/mbedtls/repo/.github/pull_request_template.md
index c580443..94fb020 100644
--- a/third_party/mbedtls/repo/.github/pull_request_template.md
+++ b/third_party/mbedtls/repo/.github/pull_request_template.md
@@ -1,36 +1,19 @@
-Notes:
-* Pull requests cannot be accepted until the PR follows the [contributing guidelines](../CONTRIBUTING.md). In particular, each commit must have at least one `Signed-off-by:` line from the committer to certify that the contribution is made under the terms of the [Developer Certificate of Origin](../dco.txt).
-* This is just a template, so feel free to use/remove the unnecessary things
 ## Description
-A few sentences describing the overall goals of the pull request's commits.
+
+Please write a few sentences describing the overall goals of the pull request's commits.
 
 
-## Status
-**READY/IN DEVELOPMENT/HOLD**
 
-## Requires Backporting
-When there is a bug fix, it should be backported to all maintained and supported branches.
-Changes do not have to be backported if:
-- This PR is a new feature\enhancement
-- This PR contains changes in the API. If this is true, and there is a need for the fix to be backported, the fix should be handled differently in the legacy branch
+## Gatekeeper checklist
 
-Yes | NO  
-Which branch?
-
-## Migrations
-If there is any API change, what's the incentive and logic for it.
-
-YES | NO
-
-## Additional comments
-Any additional information that could be of interest
-
-## Todos
-- [ ] Tests
-- [ ] Documentation
-- [ ] Changelog updated
-- [ ] Backported
+- [ ] **changelog** provided, or not required
+- [ ] **backport** done, or not required
+- [ ] **tests** provided, or not required
 
 
-## Steps to test or reproduce
-Outline the steps to test or reproduce the PR here.
+
+## Notes for the submitter
+
+Please refer to the [contributing guidelines](../CONTRIBUTING.md), especially the
+checklist for PR contributors.
+
diff --git a/third_party/mbedtls/repo/.gitignore b/third_party/mbedtls/repo/.gitignore
index 9b185c6..54b3c54 100644
--- a/third_party/mbedtls/repo/.gitignore
+++ b/third_party/mbedtls/repo/.gitignore
@@ -1,5 +1,7 @@
 # Random seed file created by test scripts and sample programs
 seedfile
+# MBEDTLS_PSA_INJECT_ENTROPY seed file created by the test framework
+00000000ffffff52.psa_its
 
 # CMake build artifacts:
 CMakeCache.txt
@@ -23,6 +25,14 @@
 # generated by scripts/memory.sh
 massif-*
 
+# Eclipse project files
+.cproject
+.project
+/.settings
+
+# Unix-like build artifacts:
+*.o
+
 # MSVC build artifacts:
 *.exe
 *.pdb
@@ -60,3 +70,6 @@
 /TAGS
 /cscope*.out
 /tags
+
+# Clangd compilation database
+compile_commands.json
diff --git a/third_party/mbedtls/repo/.pylintrc b/third_party/mbedtls/repo/.pylintrc
index d217ff6..10c93f8 100644
--- a/third_party/mbedtls/repo/.pylintrc
+++ b/third_party/mbedtls/repo/.pylintrc
@@ -73,3 +73,7 @@
 # Allow unused variables if their name starts with an underscore.
 # [unused-argument]
 dummy-variables-rgx=_.*
+
+[SIMILARITIES]
+# Ignore imports when computing similarities.
+ignore-imports=yes
diff --git a/third_party/mbedtls/repo/.readthedocs.yaml b/third_party/mbedtls/repo/.readthedocs.yaml
new file mode 100644
index 0000000..47bec76
--- /dev/null
+++ b/third_party/mbedtls/repo/.readthedocs.yaml
@@ -0,0 +1,26 @@
+# .readthedocs.yaml
+# Read the Docs configuration file
+# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
+
+# Required
+version: 2
+
+# Set the version of Python and other tools you might need
+build:
+  os: ubuntu-20.04
+  tools:
+    python: "3.9"
+  jobs:
+    pre_build:
+    - ./scripts/apidoc_full.sh
+    - breathe-apidoc -o docs/api apidoc/xml
+
+# Build documentation in the docs/ directory with Sphinx
+sphinx:
+  builder: dirhtml
+  configuration: docs/conf.py
+
+# Optionally declare the Python requirements required to build your docs
+python:
+  install:
+  - requirements: docs/requirements.txt
diff --git a/third_party/mbedtls/repo/.travis.yml b/third_party/mbedtls/repo/.travis.yml
index 3354607..3b4132e 100644
--- a/third_party/mbedtls/repo/.travis.yml
+++ b/third_party/mbedtls/repo/.travis.yml
@@ -1,52 +1,24 @@
-language: c
-compiler: gcc
-sudo: false
+# Declare python as our language. This way we get our chosen Python version,
+# and pip is available. Gcc and clang are available anyway.
+dist: jammy
+os: linux
+language: python
+python: 3.10
+
 cache: ccache
 
-jobs:
-  include:
-    - name: basic checks and reference configurations
-      addons:
-        apt:
-          packages:
-          - gnutls-bin
-          - doxygen
-          - graphviz
-          - gcc-arm-none-eabi
-          - libnewlib-arm-none-eabi
-          - gcc-arm-linux-gnueabi
-          - libc6-dev-armel-cross
-      language: python # Needed to get pip for Python 3
-      python: 3.5 # version from Ubuntu 16.04
-      install:
-        - scripts/min_requirements.py
-      script:
-        - tests/scripts/all.sh -k 'check_*'
-        - tests/scripts/all.sh -k test_default_out_of_box
-        - tests/scripts/all.sh -k test_ref_configs
-        - tests/scripts/all.sh -k build_arm_linux_gnueabi_gcc_arm5vte build_arm_none_eabi_gcc_m0plus
+branches:
+  only:
+    coverity_scan
 
-    - name: full configuration
-      script:
-        - tests/scripts/all.sh -k test_full_cmake_gcc_asan
-
-    - name: Windows
-      os: windows
-      script:
-        - scripts/windows_msbuild.bat v141 # Visual Studio 2017
-
-after_failure:
-- tests/scripts/travis-log-failure.sh
+install:
+  - $PYTHON scripts/min_requirements.py
 
 env:
   global:
     - SEED=1
-    - secure: "FrI5d2s+ckckC17T66c8jm2jV6i2DkBPU5nyWzwbedjmEBeocREfQLd/x8yKpPzLDz7ghOvr+/GQvsPPn0dVkGlNzm3Q+hGHc/ujnASuUtGrcuMM+0ALnJ3k4rFr9xEvjJeWb4SmhJO5UCAZYvTItW4k7+bj9L+R6lt3TzQbXzg="
-
+    - secure: "GF/Fde5fkm15T/RNykrjrPV5Uh1KJ70cP308igL6Xkk3eJmqkkmWCe9JqRH12J3TeWw2fu9PYPHt6iFSg6jasgqysfUyg+W03knRT5QNn3h5eHgt36cQJiJr6t3whPrRaiM6U9omE0evm+c0cAwlkA3GGSMw8Z+na4EnKI6OFCo="
 addons:
-  apt:
-    packages:
-    - gnutls-bin
   coverity_scan:
     project:
       name: "ARMmbed/mbedtls"
diff --git a/third_party/mbedtls/repo/.uncrustify.cfg b/third_party/mbedtls/repo/.uncrustify.cfg
new file mode 100644
index 0000000..8dc9db0
--- /dev/null
+++ b/third_party/mbedtls/repo/.uncrustify.cfg
@@ -0,0 +1,240 @@
+# Configuration options for Uncrustify specifying the Mbed TLS code style.
+#
+# Note: The code style represented by this file has not yet been introduced
+# to Mbed TLS.
+#
+# Copyright The Mbed TLS Contributors
+# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+
+# Wrap lines at 100 characters
+code_width = 100
+
+# Allow splitting long for statements between the condition statements
+ls_for_split_full = true
+
+# Allow splitting function calls between arguments
+ls_func_split_full = true
+
+input_tab_size = 4
+
+# Spaces-only indentation
+indent_with_tabs = 0
+
+indent_columns = 4
+
+# Indent 'case' 1 level from 'switch'
+indent_switch_case = indent_columns
+
+# Line-up strings broken by '\'
+indent_align_string = true
+
+# Braces on the same line (Egyptian-style braces)
+nl_enum_brace = remove
+nl_union_brace = remove
+nl_struct_brace = remove
+nl_do_brace = remove
+nl_if_brace = remove
+nl_for_brace = remove
+nl_else_brace = remove
+nl_while_brace = remove
+nl_switch_brace = remove
+
+# Braces on same line as keywords that follow them - 'else' and the 'while' in 'do {} while ()';
+nl_brace_else = remove
+nl_brace_while = remove
+# Space before else on the same line
+sp_brace_else = add
+# If else is on the same line as '{', force exactly 1 space between them
+sp_else_brace = force
+
+# Functions are the exception and have braces on the next line
+nl_fcall_brace = add
+nl_fdef_brace = add
+
+# Force exactly one space between ')' and '{' in statements
+sp_sparen_brace = force
+
+# At least 1 space around assignment
+sp_assign = add
+
+# Remove spaces around the preprocessor '##' token-concatenate
+sp_pp_concat = ignore
+
+# At least 1 space around '||' and '&&'
+sp_bool = add
+
+# But no space after the '!' operator
+sp_not = remove
+
+# No space after the bitwise-not '~' operator
+sp_inv = remove
+
+# No space after the addressof '&' operator
+sp_addr = remove
+
+# No space around the member '.' and '->' operators
+sp_member = remove
+
+# No space after the dereference '*' operator
+sp_deref = remove
+
+# No space after a unary negation '-'
+sp_sign = remove
+
+# No space between the '++'/'--' operator and its operand
+sp_incdec = remove
+
+# At least 1 space around comparison operators
+sp_compare = add
+
+# Remove spaces inside all kinds of parentheses:
+
+# Remove spaces inside parentheses
+sp_inside_paren = remove
+
+# No spaces inside statement parentheses
+sp_inside_sparen = remove
+
+# No spaces inside cast parentheses '( char )x' -> '(char)x'
+sp_inside_paren_cast = remove
+
+# No spaces inside function parentheses
+sp_inside_fparen = remove
+# (The case where the function has no parameters/arguments)
+sp_inside_fparens = remove
+
+# No spaces inside the first parentheses in a function type
+sp_inside_tparen = remove
+
+# (Uncrustify >= 0.74.0) No spaces inside parens in for statements
+sp_inside_for = remove
+
+# Remove spaces between nested parentheses '( (' -> '(('
+sp_paren_paren = remove
+# (Uncrustify >= 0.74.0)
+sp_sparen_paren = remove
+
+# Remove spaces between ')' and adjacent '('
+sp_cparen_oparen = remove
+
+# (Uncrustify >= 0.73.0) space between 'do' and '{'
+sp_do_brace_open = force
+
+# (Uncrustify >= 0.73.0) space between '}' and 'while'
+sp_brace_close_while = force
+
+# At least 1 space before a '*' pointer star
+sp_before_ptr_star = add
+
+# Remove spaces between pointer stars
+sp_between_ptr_star = remove
+
+# No space after a pointer star
+sp_after_ptr_star = remove
+
+# But allow a space in the case of e.g. char * const x;
+sp_after_ptr_star_qualifier = ignore
+
+# Remove space after star in a function return type
+sp_after_ptr_star_func = remove
+
+# At least 1 space after a type in variable definition etc
+sp_after_type = add
+
+# Force exactly 1 space between a statement keyword (e.g. 'if') and an opening parenthesis
+sp_before_sparen = force
+
+# Remove a space before a ';'
+sp_before_semi = remove
+# (Uncrustify >= 0.73.0) Remove space before a semi in a non-empty for
+sp_before_semi_for = remove
+# (Uncrustify >= 0.73.0) Remove space in empty first statement of a for
+sp_before_semi_for_empty = remove
+# (Uncrustify >= 0.74.0) Remove space in empty middle statement of a for
+sp_between_semi_for_empty = remove
+
+# Add a space after a ';' (unless a comment follows)
+sp_after_semi = add
+# (Uncrustify >= 0.73.0) Add a space after a semi in non-empty for statements
+sp_after_semi_for = add
+# (Uncrustify >= 0.73.0) No space after final semi in empty for statements
+sp_after_semi_for_empty = remove
+
+# Remove spaces on the inside of square brackets '[]'
+sp_inside_square = remove
+
+# Must have at least 1 space after a comma
+sp_after_comma = add
+
+# Must not have a space before a comma
+sp_before_comma = remove
+
+# No space before the ':' in a case statement
+sp_before_case_colon = remove
+
+# Must have space after a cast - '(char)x' -> '(char) x'
+sp_after_cast = add
+
+# No space between 'sizeof' and '('
+sp_sizeof_paren = remove
+
+# At least 1 space inside '{ }'
+sp_inside_braces = add
+
+# At least 1 space inside '{ }' in an enum
+sp_inside_braces_enum = add
+
+# At least 1 space inside '{ }' in a struct
+sp_inside_braces_struct = add
+
+# At least 1 space between a function return type and the function name
+sp_type_func = add
+
+# No space between a function name and its arguments/parameters
+sp_func_proto_paren = remove
+sp_func_def_paren = remove
+sp_func_call_paren = remove
+
+# No space between '__attribute__' and '('
+sp_attribute_paren = remove
+
+# No space between 'defined' and '(' in preprocessor conditions
+sp_defined_paren = remove
+
+# At least 1 space between a macro's name and its definition
+sp_macro = add
+sp_macro_func = add
+
+# Force exactly 1 space between a '}' and the name of a typedef if on the same line
+sp_brace_typedef = force
+
+# At least 1 space before a '\' line continuation
+sp_before_nl_cont = add
+
+# At least 1 space around '?' and ':' in ternary statements
+sp_cond_colon = add
+sp_cond_question = add
+
+# Space between #else/#endif and comment afterwards
+sp_endif_cmt = add
+
+# Remove newlines at the start of a file
+nl_start_of_file = remove
+
+# At least 1 newline at the end of a file
+nl_end_of_file = add
+nl_end_of_file_min = 1
+
+# Add braces in single-line statements
+mod_full_brace_do = add
+mod_full_brace_for = add
+mod_full_brace_if = add
+mod_full_brace_while = add
+
+# Remove parentheses from return statements
+mod_paren_on_return = remove
+
+# Disable removal of leading spaces in a multi-line comment if the first and
+# last lines are the same length
+cmt_multi_check_last = false
diff --git a/third_party/mbedtls/repo/3rdparty/everest/.gitignore b/third_party/mbedtls/repo/3rdparty/everest/.gitignore
index 6eb25f6..f3c7a7c 100644
--- a/third_party/mbedtls/repo/3rdparty/everest/.gitignore
+++ b/third_party/mbedtls/repo/3rdparty/everest/.gitignore
@@ -1,2 +1 @@
-*.o
 Makefile
diff --git a/third_party/mbedtls/repo/3rdparty/everest/README.md b/third_party/mbedtls/repo/3rdparty/everest/README.md
index 0e25466..bcf12c0 100644
--- a/third_party/mbedtls/repo/3rdparty/everest/README.md
+++ b/third_party/mbedtls/repo/3rdparty/everest/README.md
@@ -2,4 +2,4 @@
 
 This is a formally verified implementation of Curve25519-based handshakes. The C code is automatically derived from the (verified) [original implementation](https://github.com/project-everest/hacl-star/tree/master/code/curve25519) in the [F* language](https://github.com/fstarlang/fstar) by [KreMLin](https://github.com/fstarlang/kremlin). In addition to the improved safety and security of the implementation, it is also significantly faster than the default implementation of Curve25519 in mbedTLS.
 
-The caveat is that not all platforms are supported, although the version in `everest/library/legacy` should work on most systems. The main issue is that some platforms do not provide a 128-bit integer type and KreMLin therefore has to use additional (also verified) code to simulate them, resulting in less of a performance gain overall. Explictly supported platforms are currently `x86` and `x86_64` using gcc or clang, and Visual C (2010 and later).
+The caveat is that not all platforms are supported, although the version in `everest/library/legacy` should work on most systems. The main issue is that some platforms do not provide a 128-bit integer type and KreMLin therefore has to use additional (also verified) code to simulate them, resulting in less of a performance gain overall. Explicitly supported platforms are currently `x86` and `x86_64` using gcc or clang, and Visual C (2010 and later).
diff --git a/third_party/mbedtls/repo/3rdparty/everest/include/everest/everest.h b/third_party/mbedtls/repo/3rdparty/everest/include/everest/everest.h
index 5806500..392e792 100644
--- a/third_party/mbedtls/repo/3rdparty/everest/include/everest/everest.h
+++ b/third_party/mbedtls/repo/3rdparty/everest/include/everest/everest.h
@@ -96,7 +96,7 @@
                                  void *p_rng );
 
 /**
- * \brief           This function parses and processes a TLS ServerKeyExhange
+ * \brief           This function parses and processes a TLS ServerKeyExchange
  *                  payload.
  *
  *                  This is the first function used by a TLS client for ECDHE
@@ -116,7 +116,7 @@
                                  const unsigned char **buf, const unsigned char *end );
 
 /**
- * \brief           This function parses and processes a TLS ServerKeyExhange
+ * \brief           This function parses and processes a TLS ServerKeyExchange
  *                  payload.
  *
  *                  This is the first function used by a TLS client for ECDHE
diff --git a/third_party/mbedtls/repo/3rdparty/everest/include/everest/vs2010/inttypes.h b/third_party/mbedtls/repo/3rdparty/everest/include/everest/vs2010/inttypes.h
index d53f87f..77003be 100644
--- a/third_party/mbedtls/repo/3rdparty/everest/include/everest/vs2010/inttypes.h
+++ b/third_party/mbedtls/repo/3rdparty/everest/include/everest/vs2010/inttypes.h
@@ -17,7 +17,7 @@
  *  See the License for the specific language governing permissions and
  *  limitations under the License.
  *
- *  This file is part of mbed TLS (https://tls.mbed.org)
+ *  This file is part of Mbed TLS (https://tls.mbed.org)
  */
 
 #ifndef _INTTYPES_H_VS2010
diff --git a/third_party/mbedtls/repo/3rdparty/everest/include/everest/vs2010/stdbool.h b/third_party/mbedtls/repo/3rdparty/everest/include/everest/vs2010/stdbool.h
index 5b7039c..dcae6d8 100644
--- a/third_party/mbedtls/repo/3rdparty/everest/include/everest/vs2010/stdbool.h
+++ b/third_party/mbedtls/repo/3rdparty/everest/include/everest/vs2010/stdbool.h
@@ -17,7 +17,7 @@
  *  See the License for the specific language governing permissions and
  *  limitations under the License.
  *
- *  This file is part of mbed TLS (https://tls.mbed.org)
+ *  This file is part of Mbed TLS (https://tls.mbed.org)
  */
 
 #ifndef _STDBOOL_H_VS2010
diff --git a/third_party/mbedtls/repo/3rdparty/everest/include/everest/x25519.h b/third_party/mbedtls/repo/3rdparty/everest/include/everest/x25519.h
index 7a973dc..ef314d2 100644
--- a/third_party/mbedtls/repo/3rdparty/everest/include/everest/x25519.h
+++ b/third_party/mbedtls/repo/3rdparty/everest/include/everest/x25519.h
@@ -16,7 +16,7 @@
  *  See the License for the specific language governing permissions and
  *  limitations under the License.
  *
- *  This file is part of mbed TLS (https://tls.mbed.org)
+ *  This file is part of Mbed TLS (https://tls.mbed.org)
  */
 
 #ifndef MBEDTLS_X25519_H
diff --git a/third_party/mbedtls/repo/3rdparty/everest/library/Hacl_Curve25519_joined.c b/third_party/mbedtls/repo/3rdparty/everest/library/Hacl_Curve25519_joined.c
index ee62be1..b7d0c92 100644
--- a/third_party/mbedtls/repo/3rdparty/everest/library/Hacl_Curve25519_joined.c
+++ b/third_party/mbedtls/repo/3rdparty/everest/library/Hacl_Curve25519_joined.c
@@ -16,7 +16,7 @@
  *  See the License for the specific language governing permissions and
  *  limitations under the License.
  *
- *  This file is part of mbed TLS (https://tls.mbed.org)
+ *  This file is part of Mbed TLS (https://tls.mbed.org)
  */
 
 #include "common.h"
diff --git a/third_party/mbedtls/repo/3rdparty/everest/library/everest.c b/third_party/mbedtls/repo/3rdparty/everest/library/everest.c
index 82c4e03..fefc6a2 100644
--- a/third_party/mbedtls/repo/3rdparty/everest/library/everest.c
+++ b/third_party/mbedtls/repo/3rdparty/everest/library/everest.c
@@ -28,12 +28,7 @@
 #include "everest/x25519.h"
 #include "everest/everest.h"
 
-#if defined(MBEDTLS_PLATFORM_C)
 #include "mbedtls/platform.h"
-#else
-#define mbedtls_calloc calloc
-#define mbedtls_free   free
-#endif
 
 #if defined(MBEDTLS_ECDH_VARIANT_EVEREST_ENABLED)
 
diff --git a/third_party/mbedtls/repo/3rdparty/everest/library/x25519.c b/third_party/mbedtls/repo/3rdparty/everest/library/x25519.c
index 9faa9ab..83064dc 100644
--- a/third_party/mbedtls/repo/3rdparty/everest/library/x25519.c
+++ b/third_party/mbedtls/repo/3rdparty/everest/library/x25519.c
@@ -16,7 +16,7 @@
  *  See the License for the specific language governing permissions and
  *  limitations under the License.
  *
- *  This file is part of mbed TLS (https://tls.mbed.org)
+ *  This file is part of Mbed TLS (https://tls.mbed.org)
  */
 
 #include "common.h"
diff --git a/third_party/mbedtls/repo/BRANCHES.md b/third_party/mbedtls/repo/BRANCHES.md
index ee91f76..28a2c6a 100644
--- a/third_party/mbedtls/repo/BRANCHES.md
+++ b/third_party/mbedtls/repo/BRANCHES.md
@@ -2,10 +2,10 @@
 
 At any point in time, we have a number of maintained branches consisting of:
 
-- The [`master`](https://github.com/ARMmbed/mbedtls/tree/master) branch:
+- The [`master`](https://github.com/Mbed-TLS/mbedtls/tree/master) branch:
   this always contains the latest release, including all publicly available
   security fixes.
-- The [`development`](https://github.com/ARMmbed/mbedtls/tree/development) branch:
+- The [`development`](https://github.com/Mbed-TLS/mbedtls/tree/development) branch:
   this is where new features land,
   as well as bug fixes and security fixes.
 - One or more long-time support (LTS) branches:
@@ -16,7 +16,12 @@
 also maintain ABI compatibility within LTS branches; see the next section for
 details.
 
-## Backwards Compatibility
+Every major version will become an LTS branch when the next major version is
+released. We may occasionally create LTS branches from other releases at our
+discretion.
+When a new LTS branch is created, it usually remains supported for three years.
+
+## Backwards Compatibility for application code
 
 We maintain API compatibility in released versions of Mbed TLS. If you have
 code that's working and secure with Mbed TLS x.y.z and does not rely on
@@ -24,6 +29,14 @@
 modification with any later release x.y'.z' with the same major version
 number, and your code will still build, be secure, and work.
 
+Note that this guarantee only applies if you either use the default
+compile-time configuration (`mbedtls/config.h`) or the same modified
+compile-time configuration. Changing compile-time configuration options can
+result in an incompatible API or ABI, although features will generally not
+affect unrelated features (for example, enabling or disabling a
+cryptographic algorithm does not break code that does not use that
+algorithm).
+
 There are rare exceptions: code that was relying on something that became
 insecure in the meantime (for example, crypto that was found to be weak) may
 need to be changed. In case security comes in conflict with backwards
@@ -40,15 +53,29 @@
 increase code size for a security fix.)
 
 For contributors, see the [Backwards Compatibility section of
-CONTRIBUTING](CONTRIBUTING.md#cackwords-compatibility).
+CONTRIBUTING](CONTRIBUTING.md#backwards-compatibility).
+
+## Backward compatibility for the key store
+
+We maintain backward compatibility with previous versions of the
+PSA Crypto persistent storage since Mbed TLS 2.25.0, provided that the
+storage backend (PSA ITS implementation) is configured in a compatible way.
+We intend to maintain this backward compatibility throughout a major version
+of Mbed TLS (for example, all Mbed TLS 3.y versions will be able to read
+keys written under any Mbed TLS 3.x with x <= y).
+
+Mbed TLS 3.x can also read keys written by Mbed TLS 2.25.0 through 2.28.x
+LTS, but future major version upgrades (for example from 2.28.x/3.x to 4.y)
+may require the use of an upgrade tool.
 
 ## Current Branches
 
 The following branches are currently maintained:
 
-- [master](https://github.com/ARMmbed/mbedtls/tree/master)
-- [`development`](https://github.com/ARMmbed/mbedtls/)
-- [`mbedtls-2.28`](https://github.com/ARMmbed/mbedtls/tree/mbedtls-2.28)
- maintained until at least the end of 2024.
+- [master](https://github.com/Mbed-TLS/mbedtls/tree/master)
+- [`development`](https://github.com/Mbed-TLS/mbedtls/)
+- [`mbedtls-2.28`](https://github.com/Mbed-TLS/mbedtls/tree/mbedtls-2.28)
+ maintained until at least the end of 2024, see
+  <https://github.com/Mbed-TLS/mbedtls/releases/tag/v2.28.7>.
 
 Users are urged to always use the latest version of a maintained branch.
diff --git a/third_party/mbedtls/repo/BUGS.md b/third_party/mbedtls/repo/BUGS.md
index e8705ff..47bde07 100644
--- a/third_party/mbedtls/repo/BUGS.md
+++ b/third_party/mbedtls/repo/BUGS.md
@@ -1,6 +1,6 @@
 ## Known issues
 
-Known issues in Mbed TLS are [tracked on GitHub](https://github.com/ARMmbed/mbedtls/issues).
+Known issues in Mbed TLS are [tracked on GitHub](https://github.com/Mbed-TLS/mbedtls/issues).
 
 ## Reporting a bug
 
@@ -9,12 +9,12 @@
 1. Make sure you're using the latest version of a
    [maintained branch](BRANCHES.md): `master`, `development`,
    or a long-time support branch.
-2. Check [GitHub](https://github.com/ARMmbed/mbedtls/issues) to see if
+2. Check [GitHub](https://github.com/Mbed-TLS/mbedtls/issues) to see if
    your issue has already been reported. If not, …
 3. If the issue is a security risk (for example: buffer overflow,
    data leak), please report it confidentially as described in
    [`SECURITY.md`](SECURITY.md). If not, …
-4. Please [create an issue on on GitHub](https://github.com/ARMmbed/mbedtls/issues).
+4. Please [create an issue on on GitHub](https://github.com/Mbed-TLS/mbedtls/issues).
 
 Please do not use GitHub for support questions. If you want to know
 how to do something with Mbed TLS, please see [`SUPPORT.md`](SUPPORT.md) for available documentation and support channels.
diff --git a/third_party/mbedtls/repo/CMakeLists.txt b/third_party/mbedtls/repo/CMakeLists.txt
index 14ca7b6..b001bb7 100644
--- a/third_party/mbedtls/repo/CMakeLists.txt
+++ b/third_party/mbedtls/repo/CMakeLists.txt
@@ -6,9 +6,9 @@
 #     command but rather at the target level using the
 #     target_include_directories command. That way, it is easier to guarantee
 #     that targets are built using the proper list of include directories.
-#   + Use the PUBLIC and PRIVATE keywords to specifiy the scope of include
+#   + Use the PUBLIC and PRIVATE keywords to specify the scope of include
 #     directories. That way, a target linking to a library (using the
-#     target_link_librairies command) inherits from the library PUBLIC include
+#     target_link_libraries command) inherits from the library PUBLIC include
 #     directories and not from the PRIVATE ones.
 #   + Note: there is currently one remaining include_directories command in the
 #     CMake files. It is related to ZLIB support which is planned to be removed.
@@ -34,18 +34,18 @@
 cmake_policy(SET CMP0012 NEW)
 
 if(TEST_CPP)
-    project("mbed TLS" C CXX)
+    project("Mbed TLS" C CXX)
 else()
-    project("mbed TLS" C)
+    project("Mbed TLS" C)
 endif()
 
 # Set the project root directory.
 set(MBEDTLS_DIR ${CMAKE_CURRENT_SOURCE_DIR})
 
-option(USE_PKCS11_HELPER_LIBRARY "Build mbed TLS with the pkcs11-helper library." OFF)
-option(ENABLE_ZLIB_SUPPORT "Build mbed TLS with zlib library." OFF)
+option(USE_PKCS11_HELPER_LIBRARY "Build Mbed TLS with the pkcs11-helper library." OFF)
+option(ENABLE_ZLIB_SUPPORT "Build Mbed TLS with zlib library." OFF)
 
-option(ENABLE_PROGRAMS "Build mbed TLS programs." ON)
+option(ENABLE_PROGRAMS "Build Mbed TLS programs." ON)
 
 option(UNSAFE_BUILD "Allow unsafe builds. These builds ARE NOT SECURE." OFF)
 option(MBEDTLS_FATAL_WARNINGS "Compiler warnings treated as errors" ON)
@@ -57,9 +57,9 @@
 
 # the test suites currently have compile errors with MSVC
 if(CMAKE_COMPILER_IS_MSVC)
-    option(ENABLE_TESTING "Build mbed TLS tests." OFF)
+    option(ENABLE_TESTING "Build Mbed TLS tests." OFF)
 else()
-    option(ENABLE_TESTING "Build mbed TLS tests." ON)
+    option(ENABLE_TESTING "Build Mbed TLS tests." ON)
 endif()
 
 # Warning string - created as a list for compatibility with CMake 2.8
@@ -135,37 +135,38 @@
         FORCE)
 endif()
 
+# Make MBEDTLS_CONFIG_FILE and MBEDTLS_USER_CONFIG_FILE into PATHs
+set(MBEDTLS_CONFIG_FILE "" CACHE FILEPATH "Mbed TLS config file (overrides default).")
+set(MBEDTLS_USER_CONFIG_FILE "" CACHE FILEPATH "Mbed TLS user config file (appended to default).")
+
 # Create a symbolic link from ${base_name} in the binary directory
 # to the corresponding path in the source directory.
+# Note: Copies the file(s) on Windows.
 function(link_to_source base_name)
-    # Get OS dependent path to use in `execute_process`
-    if (CMAKE_HOST_WIN32)
-        #mklink is an internal command of cmd.exe it can only work with \
-        string(REPLACE "/" "\\" link "${CMAKE_CURRENT_BINARY_DIR}/${base_name}")
-        string(REPLACE "/" "\\" target "${CMAKE_CURRENT_SOURCE_DIR}/${base_name}")
-    else()
-        set(link "${CMAKE_CURRENT_BINARY_DIR}/${base_name}")
-        set(target "${CMAKE_CURRENT_SOURCE_DIR}/${base_name}")
-    endif()
+    set(link "${CMAKE_CURRENT_BINARY_DIR}/${base_name}")
+    set(target "${CMAKE_CURRENT_SOURCE_DIR}/${base_name}")
 
     if (NOT EXISTS ${link})
         if (CMAKE_HOST_UNIX)
-            set(command ln -s ${target} ${link})
+            execute_process(COMMAND ln -s ${target} ${link}
+                RESULT_VARIABLE result
+                ERROR_VARIABLE output)
+
+            if (NOT ${result} EQUAL 0)
+                message(FATAL_ERROR "Could not create symbolic link for: ${target} --> ${output}")
+            endif()
         else()
             if (IS_DIRECTORY ${target})
-                set(command cmd.exe /c mklink /j ${link} ${target})
+                file(GLOB_RECURSE files FOLLOW_SYMLINKS RELATIVE ${target} "${target}/*")
+                foreach(file IN LISTS files)
+                    if(NOT IS_DIRECTORY "${target}/${file}")
+                        configure_file("${target}/${file}" "${link}/${file}" COPYONLY)
+                    endif()
+                endforeach(file)
             else()
-                set(command cmd.exe /c mklink /h ${link} ${target})
+                configure_file(${target} ${link} COPYONLY)
             endif()
         endif()
-
-        execute_process(COMMAND ${command}
-            RESULT_VARIABLE result
-            ERROR_VARIABLE output)
-
-        if (NOT ${result} EQUAL 0)
-            message(FATAL_ERROR "Could not create symbolic link for: ${target} --> ${output}")
-        endif()
     endif()
 endfunction(link_to_source)
 
@@ -222,12 +223,14 @@
 endif(CMAKE_COMPILER_IS_CLANG)
 
 if(CMAKE_COMPILER_IS_IAR)
-    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --warn_about_c_style_casts --warnings_are_errors -Ohz")
+    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --warn_about_c_style_casts")
+    set(CMAKE_C_FLAGS_RELEASE     "-Ohz")
+    set(CMAKE_C_FLAGS_DEBUG       "--debug -On")
 endif(CMAKE_COMPILER_IS_IAR)
 
 if(CMAKE_COMPILER_IS_MSVC)
-    # Strictest warnings
-    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W3")
+    # Strictest warnings, UTF-8 source and execution charset
+    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W3 /utf-8")
 endif(CMAKE_COMPILER_IS_MSVC)
 
 if(MBEDTLS_FATAL_WARNINGS)
@@ -243,6 +246,10 @@
             set(CMAKE_C_FLAGS_ASANDBG "${CMAKE_C_FLAGS_ASANDBG} -Wno-error=cpp")
         endif(UNSAFE_BUILD)
     endif(CMAKE_COMPILER_IS_CLANG OR CMAKE_COMPILER_IS_GNU)
+
+    if (CMAKE_COMPILER_IS_IAR)
+        set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --warnings_are_errors")
+    endif(CMAKE_COMPILER_IS_IAR)
 endif(MBEDTLS_FATAL_WARNINGS)
 
 if(CMAKE_BUILD_TYPE STREQUAL "Coverage")
@@ -286,12 +293,37 @@
 # to define the test executables.
 #
 if(ENABLE_TESTING OR ENABLE_PROGRAMS)
-    file(GLOB MBEDTLS_TEST_FILES ${CMAKE_CURRENT_SOURCE_DIR}/tests/src/*.c ${CMAKE_CURRENT_SOURCE_DIR}/tests/src/drivers/*.c)
+    file(GLOB MBEDTLS_TEST_FILES
+         ${CMAKE_CURRENT_SOURCE_DIR}/tests/src/*.c
+         ${CMAKE_CURRENT_SOURCE_DIR}/tests/src/drivers/*.c)
     add_library(mbedtls_test OBJECT ${MBEDTLS_TEST_FILES})
     target_include_directories(mbedtls_test
         PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/tests/include
         PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
         PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/library)
+
+    file(GLOB MBEDTLS_TEST_HELPER_FILES
+         ${CMAKE_CURRENT_SOURCE_DIR}/tests/src/test_helpers/*.c)
+    add_library(mbedtls_test_helpers OBJECT ${MBEDTLS_TEST_HELPER_FILES})
+    target_include_directories(mbedtls_test_helpers
+        PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/tests/include
+        PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
+        PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/library
+        PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/everest/include)
+
+    # Pass-through MBEDTLS_CONFIG_FILE and MBEDTLS_USER_CONFIG_FILE
+    if(MBEDTLS_CONFIG_FILE)
+        target_compile_definitions(mbedtls_test
+            PUBLIC MBEDTLS_CONFIG_FILE="${MBEDTLS_CONFIG_FILE}")
+        target_compile_definitions(mbedtls_test_helpers
+            PUBLIC MBEDTLS_CONFIG_FILE="${MBEDTLS_CONFIG_FILE}")
+    endif()
+    if(MBEDTLS_USER_CONFIG_FILE)
+        target_compile_definitions(mbedtls_test
+            PUBLIC MBEDTLS_USER_CONFIG_FILE="${MBEDTLS_USER_CONFIG_FILE}")
+        target_compile_definitions(mbedtls_test_helpers
+            PUBLIC MBEDTLS_USER_CONFIG_FILE="${MBEDTLS_USER_CONFIG_FILE}")
+    endif()
 endif()
 
 if(ENABLE_PROGRAMS)
@@ -310,22 +342,15 @@
     # additional convenience targets for Unix only
     if(UNIX)
 
-        ADD_CUSTOM_TARGET(covtest
-            COMMAND make test
-            COMMAND programs/test/selftest
-            COMMAND tests/compat.sh
-            COMMAND tests/ssl-opt.sh
-        )
-
+        # For coverage testing:
+        # 1. Build with:
+        #         cmake -D CMAKE_BUILD_TYPE=Coverage /path/to/source && make
+        # 2. Run the relevant tests for the part of the code you're interested in.
+        #    For the reference coverage measurement, see
+        #    tests/scripts/basic-build-test.sh
+        # 3. Run scripts/lcov.sh to generate an HTML report.
         ADD_CUSTOM_TARGET(lcov
-            COMMAND rm -rf Coverage
-            COMMAND lcov --capture --initial --directory library/CMakeFiles/mbedtls.dir -o files.info
-            COMMAND lcov --capture --directory library/CMakeFiles/mbedtls.dir -o tests.info
-            COMMAND lcov --add-tracefile files.info --add-tracefile tests.info -o all.info
-            COMMAND lcov --remove all.info -o final.info '*.h'
-            COMMAND gendesc tests/Descriptions.txt -o descriptions
-            COMMAND genhtml --title "mbed TLS" --description-file descriptions --keep-descriptions --legend --no-branch-coverage -o Coverage final.info
-            COMMAND rm -f files.info tests.info all.info final.info descriptions
+            COMMAND scripts/lcov.sh
         )
 
         ADD_CUSTOM_TARGET(memcheck
diff --git a/third_party/mbedtls/repo/CONTRIBUTING.md b/third_party/mbedtls/repo/CONTRIBUTING.md
index b3a9547..f9dd499 100644
--- a/third_party/mbedtls/repo/CONTRIBUTING.md
+++ b/third_party/mbedtls/repo/CONTRIBUTING.md
@@ -1,39 +1,40 @@
 Contributing
 ============
-We gratefully accept bug reports and contributions from the community. There are some requirements we need to fulfill in order to be able to integrate contributions:
+We gratefully accept bug reports and contributions from the community. All PRs are reviewed by the project team / community, and may need some modifications to
+be accepted.
 
- - As with any open source project, contributions will be reviewed by the project team and community and may need some modifications to be accepted.
- - The contribution should not break API or ABI, unless there is a real justification for that. If there is an API change, the contribution, if accepted, will be merged only when there will be a major release.
+Quick Checklist for PR contributors
+-----------------------------------
+More details on all of these points may be found in the sections below.
+
+- [Sign-off](#license-and-copyright): all commits must be signed off.
+- [Tests](#tests): please ensure the PR includes adequate tests.
+- [Changelog](#documentation): if needed, please provide a changelog entry.
+- [Backports](#long-term-support-branches): provide a backport if needed (it's fine to wait until the main PR is accepted).
 
 Coding Standards
 ----------------
-- We would ask that contributions conform to [our coding standards](https://tls.mbed.org/kb/development/mbedtls-coding-standards), and that contributions are fully tested before submission, as mentioned in the [Tests](#tests) and [Continuous Integration](#continuous-integration-tests) sections.
-- The code should be written in a clean and readable style.
+- Contributions should include tests, as mentioned in the [Tests](#tests) and [Continuous Integration](#continuous-integration-tests) sections. Please check that your contribution passes basic tests before submission, and check the CI results after making a pull request.
+- The code should be written in a clean and readable style, and must follow [our coding standards](https://mbed-tls.readthedocs.io/en/latest/kb/development/mbedtls-coding-standards/).
 - The code should be written in a portable generic way, that will benefit the whole community, and not only your own needs.
 - The code should be secure, and will be reviewed from a security point of view as well.
 
 Making a Contribution
 ---------------------
-1. [Check for open issues](https://github.com/ARMmbed/mbedtls/issues) or [start a discussion](https://lists.trustedfirmware.org/mailman/listinfo/mbed-tls) around a feature idea or a bug.
-1. Fork the [Mbed TLS repository on GitHub](https://github.com/ARMmbed/mbedtls) to start making your changes. As a general rule, you should use the ["development" branch](https://github.com/ARMmbed/mbedtls/tree/development) as a basis.
+1. [Check for open issues](https://github.com/Mbed-TLS/mbedtls/issues) or [start a discussion](https://lists.trustedfirmware.org/mailman3/lists/mbed-tls.lists.trustedfirmware.org) around a feature idea or a bug.
+1. Fork the [Mbed TLS repository on GitHub](https://github.com/Mbed-TLS/mbedtls) to start making your changes. As a general rule, you should use the ["development" branch](https://github.com/Mbed-TLS/mbedtls/tree/development) as a basis.
 1. Write a test which shows that the bug was fixed or that the feature works as expected.
 1. Send a pull request (PR) and work with us until it gets merged and published. Contributions may need some modifications, so a few rounds of review and fixing may be necessary. We will include your name in the ChangeLog :)
 1. For quick merging, the contribution should be short, and concentrated on a single feature or topic. The larger the contribution is, the longer it would take to review it and merge it.
-1. All new files should include the [Apache-2.0](https://spdx.org/licenses/Apache-2.0.html) standard license header where possible.
-1. Ensure that each commit has at least one `Signed-off-by:` line from the committer. If anyone else contributes to the commit, they should also add their own `Signed-off-by:` line. By adding this line, contributor(s) certify that the contribution is made under the terms of the [Developer Certificate of Origin](dco.txt). The contribution licensing is described in the [License section of the README](README.md#License).
 
 Backwards Compatibility
 -----------------------
 
 The project aims to minimise the impact on users upgrading to newer versions of the library and it should not be necessary for a user to make any changes to their own code to work with a newer version of the library. Unless the user has made an active decision to use newer features, a newer generation of the library or a change has been necessary due to a security issue or other significant software defect, no modifications to their own code should be necessary. To achieve this, API compatibility is maintained between different versions of Mbed TLS on the main development branch and in LTS (Long Term Support) branches, as described in [BRANCHES.md](BRANCHES.md).
 
-To minimise such disruption to users, where a change to the interface is required, all changes to the ABI or API, even on the main development branch where new features are added, need to be justifiable by either being a significant enhancement, new feature or bug fix which is best resolved by an interface change.
+To minimise such disruption to users, where a change to the interface is required, all changes to the ABI or API, even on the main development branch where new features are added, need to be justifiable by either being a significant enhancement, new feature or bug fix which is best resolved by an interface change. If there is an API change, the contribution, if accepted, will be merged only when there is a major release.
 
-Where changes to an existing interface are necessary, functions in the public interface which need to be changed, are marked as 'deprecated'. This is done with the preprocessor symbols `MBEDTLS_DEPRECATED_WARNING` and `MBEDTLS_DEPRECATED_REMOVED`. Then, a new function with a new name but similar if not identical behaviour to the original function containing the necessary changes should be created alongside the existing deprecated function.
-
-When a build is made with the deprecation preprocessor symbols defined, a compiler warning will be generated to warn a user that the function will be removed at some point in the future, notifying users that they should change from the older deprecated function to the newer function at their own convenience.
-
-Therefore, no changes are permitted to the definition of functions in the public interface which will change the API. Instead the interface can only be changed by its extension. As described above, if a function needs to be changed, a new function needs to be created alongside it, with a new name, and whatever change is necessary, such as a new parameter or the addition of a return value.
+No changes are permitted to the definition of functions in the public interface which will change the API. Instead the interface can only be changed by its extension. Where changes to an existing interface are necessary, functions in the public interface which need to be changed are marked as 'deprecated'. If there is a strong reason to replace an existing function with one that has a slightly different interface (different prototype, or different documented behavior), create a new function with a new name with the desired interface. Keep the old function, but mark it as deprecated.
 
 Periodically, the library will remove deprecated functions from the library which will be a breaking change in the API, but such changes will be made only in a planned, structured way that gives sufficient notice to users of the library.
 
@@ -47,23 +48,18 @@
 1. All bug fixes that correct a defect that is also present in an LTS branch must be backported to that LTS branch. If a bug fix introduces a change to the API such as a new function, the fix should be reworked to avoid the API change. API changes without very strong justification are unlikely to be accepted.
 1. If a contribution is a new feature or enhancement, no backporting is required. Exceptions to this may be additional test cases or quality improvements such as changes to build or test scripts.
 
-It would be highly appreciated if contributions are backported to LTS branches in addition to the [development branch](https://github.com/ARMmbed/mbedtls/tree/development) by contributors.
+It would be highly appreciated if contributions are backported to LTS branches in addition to the [development branch](https://github.com/Mbed-TLS/mbedtls/tree/development) by contributors.
 
 The list of maintained branches can be found in the [Current Branches section
 of BRANCHES.md](BRANCHES.md#current-branches).
 
-Currently maintained LTS branches are:
-1. [mbedtls-2.7](https://github.com/ARMmbed/mbedtls/tree/mbedtls-2.7)
-1. [mbedtls-2.16](https://github.com/ARMmbed/mbedtls/tree/mbedtls-2.16)
-
-
 Tests
 -----
 As mentioned, tests that show the correctness of the feature or bug fix should be added to the pull request, if no such tests exist.
 
-Mbed TLS includes a comprehensive set of test suites in the `tests/` directory that are dynamically generated to produce the actual test source files (e.g. `test_suite_mpi.c`). These files are generated from a `function file` (e.g. `suites/test_suite_mpi.function`) and a `data file` (e.g. `suites/test_suite_mpi.data`). The function file contains the test functions. The data file contains the test cases, specified as parameters that will be passed to the test function.
+Mbed TLS includes a comprehensive set of test suites in the `tests/` directory that are dynamically generated to produce the actual test source files (e.g. `test_suite_rsa.c`). These files are generated from a `function file` (e.g. `suites/test_suite_rsa.function`) and a `data file` (e.g. `suites/test_suite_rsa.data`). The function file contains the test functions. The data file contains the test cases, specified as parameters that will be passed to the test function.
 
-[A Knowledge Base article describing how to add additional tests is available on the Mbed TLS website](https://tls.mbed.org/kb/development/test_suites).
+[A Knowledge Base article describing how to add additional tests is available on the Mbed TLS website](https://mbed-tls.readthedocs.io/en/latest/kb/development/test_suites/).
 
 A test script `tests/scripts/basic-build-test.sh` is available to show test coverage of the library. New code contributions should provide a similar level of code coverage to that which already exists for the library.
 
@@ -73,7 +69,7 @@
 ----------------------------
 Once a PR has been made, the Continuous Integration (CI) tests are triggered and run. You should follow the result of the CI tests, and fix failures.
 
-It is advised to enable the [githooks scripts](https://github.com/ARMmbed/mbedtls/tree/development/tests/git-scripts) prior to pushing your changes, for catching some of the issues as early as possible.
+It is advised to enable the [githooks scripts](https://github.com/Mbed-TLS/mbedtls/tree/development/tests/git-scripts) prior to pushing your changes, for catching some of the issues as early as possible.
 
 Documentation
 -------------
@@ -82,5 +78,20 @@
 1. All interfaces should be documented through Doxygen. New APIs should introduce Doxygen documentation.
 1. Complex parts in the code should include comments.
 1. If needed, a Readme file is advised.
-1. If a [Knowledge Base (KB)](https://tls.mbed.org/kb) article should be added, write this as a comment in the PR description.
-1. A [ChangeLog](https://github.com/ARMmbed/mbedtls/blob/development/ChangeLog.d/00README.md) entry should be added for this contribution.
+1. If a [Knowledge Base (KB)](https://mbed-tls.readthedocs.io/en/latest/kb/) article should be added, write this as a comment in the PR description.
+1. A [ChangeLog](https://github.com/Mbed-TLS/mbedtls/blob/development/ChangeLog.d/00README.md) entry should be added for this contribution.
+
+License and Copyright
+---------------------
+
+Unless specifically indicated otherwise in a file, Mbed TLS files are provided under a dual [Apache-2.0](https://spdx.org/licenses/Apache-2.0.html) OR [GPL-2.0-or-later](https://spdx.org/licenses/GPL-2.0-or-later.html) license. See the [LICENSE](LICENSE) file for the full text of these licenses. This means that users may choose which of these licenses they take the code under.
+
+Contributors must accept that their contributions are made under both the Apache-2.0 AND [GPL-2.0-or-later](https://spdx.org/licenses/GPL-2.0-or-later.html) licenses. This enables LTS (Long Term Support) branches of the software to be provided under either the Apache-2.0 or GPL-2.0-or-later licenses.
+
+All new files should include the standard SPDX license identifier where possible, i.e. "SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later".
+
+The copyright on contributions is retained by the original authors of the code. Where possible for new files, this should be noted in a comment at the top of the file in the form: "Copyright The Mbed TLS Contributors".
+
+When contributing code to us, the committer and all authors are required to make the submission under the terms of the [Developer Certificate of Origin](dco.txt), confirming that the code submitted can (legally) become part of the project, and is submitted under both the Apache-2.0 AND GPL-2.0-or-later licenses.
+
+This is done by including the standard Git `Signed-off-by:` line in every commit message. If more than one person contributed to the commit, they should also add their own `Signed-off-by:` line.
diff --git a/third_party/mbedtls/repo/ChangeLog b/third_party/mbedtls/repo/ChangeLog
index 021012a..5434e55 100644
--- a/third_party/mbedtls/repo/ChangeLog
+++ b/third_party/mbedtls/repo/ChangeLog
@@ -1,4 +1,346 @@
-mbed TLS ChangeLog (Sorted per branch, date)
+Mbed TLS ChangeLog (Sorted per branch, date)
+
+= Mbed TLS 2.28.7 branch released 2024-01-26
+
+Security
+   * Fix a timing side channel in private key RSA operations. This side channel
+     could be sufficient for an attacker to recover the plaintext. A local
+     attacker or a remote attacker who is close to the victim on the network
+     might have precise enough timing measurements to exploit this. It requires
+     the attacker to send a large number of messages for decryption. For
+     details, see "Everlasting ROBOT: the Marvin Attack", Hubert Kario. Reported
+     by Hubert Kario, Red Hat.
+   * Fix a failure to validate input when writing x509 extensions lengths which
+     could result in an integer overflow, causing a zero-length buffer to be
+     allocated to hold the extension. The extension would then be copied into
+     the buffer, causing a heap buffer overflow.
+
+= Mbed TLS 2.28.6 branch released 2023-11-06
+
+Changes
+   *  Mbed TLS is now released under a dual Apache-2.0 OR GPL-2.0-or-later
+      license. Users may choose which license they take the code under.
+
+= Mbed TLS 2.28.5 branch released 2023-10-05
+
+Features
+   * The documentation of mbedtls_ecp_group now describes the optimized
+     representation of A for some curves. Fixes #8045.
+
+Security
+   * Developers using mbedtls_pkcs5_pbes2() or mbedtls_pkcs12_pbe() should
+     review the size of the output buffer passed to this function, and note
+     that the output after decryption may include CBC padding. Consider moving
+     to the new functions mbedtls_pkcs5_pbes2_ext() or mbedtls_pkcs12_pbe_ext()
+     which checks for overflow of the output buffer and reports the actual
+     length of the output.
+   * Improve padding calculations in CBC decryption, NIST key unwrapping and
+     RSA OAEP decryption. With the previous implementation, some compilers
+     (notably recent versions of Clang and IAR) could produce non-constant
+     time code, which could allow a padding oracle attack if the attacker
+     has access to precise timing measurements.
+   * Fix a buffer overread when parsing short TLS application data records in
+     ARC4 or null-cipher cipher suites. Credit to OSS-Fuzz.
+
+Bugfix
+   * Fix x509 certificate generation to conform to RFC 5480 / RFC 5758 when
+     using ECC key. The certificate was rejected by some crypto frameworks.
+     Fixes #2924.
+   * Fix some cases where mbedtls_mpi_mod_exp, RSA key construction or ECDSA
+     signature can silently return an incorrect result in low memory conditions.
+   * Fix IAR compiler warnings. Fixes #7873, #4300.
+   * Fix an issue when parsing an otherName subject alternative name into a
+     mbedtls_x509_san_other_name struct. The type-id of the otherName was not
+     copied to the struct. This meant that the struct had incomplete
+     information about the otherName SAN and contained uninitialized memory.
+   * Fix the detection of HardwareModuleName otherName SANs. These were being
+     detected by comparing the wrong field and the check was erroneously
+     inverted.
+   * Fix an error when MBEDTLS_ECDSA_SIGN_ALT is defined but not
+     MBEDTLS_ECDSA_VERIFY_ALT, causing ecdsa verify to fail. Fixes #7498.
+   * Functions in the ssl_cache module now return a negative MBEDTLS_ERR_xxx
+     error code on failure. Before, they returned 1 to indicate failure in
+     some cases involving a missing entry or a full cache.
+
+Changes
+   * In configurations with ARIA or Camellia but not AES, the value of
+     MBEDTLS_CIPHER_BLKSIZE_MAX was 8, rather than 16 as the name might
+     suggest. This did not affect any library code, because this macro was
+     only used in relation with CMAC which does not support these ciphers.
+     Its value is now 16 if ARIA or Camellia are present. This may affect
+     application code that uses this macro.
+
+= Mbed TLS 2.28.4 branch released 2023-08-04
+
+Features
+   * Allow MBEDTLS_CONFIG_FILE and MBEDTLS_USER_CONFIG_FILE to be set by
+     setting the CMake variable of the same name at configuration time.
+
+Bugfix
+   * Fix crypt_and_hash decryption fail when used with a stream cipher
+     mode of operation, due to the input not being a multiple of the block
+     size. Resolves #7417.
+   * Fix a bug where mbedtls_x509_string_to_names() would return success
+     when given a invalid name string, if it did not contain '=' or ','.
+   * Fix missing PSA initialization in sample programs when
+     MBEDTLS_USE_PSA_CRYPTO is enabled.
+   * Fix clang and armclang compilation error when targeting certain Arm
+     M-class CPUs (Cortex-M0, Cortex-M0+, Cortex-M1, Cortex-M23,
+     SecurCore SC000). Fixes #1077.
+   * Fixed an issue that caused compile errors when using CMake and the IAR
+     toolchain.
+   * Fix the build with MBEDTLS_PSA_INJECT_ENTROPY. Fixes #7516.
+   * Fix builds on Windows with clang.
+   * Fix compilation warnings in aes.c for certain combinations
+     of configuration options.
+   * Fix a compilation error on some platforms when including mbedtls/ssl.h
+     with all TLS support disabled. Fixes #6628.
+
+Changes
+   * Update test data to avoid failures of unit tests after 2023-08-07, and
+     update expiring certififcates in the certs module.
+
+= Mbed TLS 2.28.3 branch released 2023-03-28
+
+Features
+   * Use HOSTCC (if it is set) when compiling C code during generation of the
+     configuration-independent files. This allows them to be generated when
+     CC is set for cross compilation.
+   * AES-NI is now supported with Visual Studio.
+   * AES-NI is now supported in 32-bit builds, or when MBEDTLS_HAVE_ASM
+     is disabled, when compiling with GCC or Clang or a compatible compiler
+     for a target CPU that supports the requisite instructions (for example
+     gcc -m32 -msse2 -maes -mpclmul). (Generic x86 builds with GCC-like
+     compilers still require MBEDTLS_HAVE_ASM and a 64-bit target.)
+
+Security
+   * MBEDTLS_AESNI_C, which is enabled by default, was silently ignored on
+     builds that couldn't compile the GCC-style assembly implementation
+     (most notably builds with Visual Studio), leaving them vulnerable to
+     timing side-channel attacks. There is now an intrinsics-based AES-NI
+     implementation as a fallback for when the assembly one cannot be used.
+
+Bugfix
+   * Fix a build issue on Windows where the source and build directory could
+     not be on different drives (#5751).
+   * Fix possible integer overflow in mbedtls_timing_hardclock(), which
+     could cause a crash for certain platforms & compiler options.
+   * Fix IAR compiler warnings. Fixes #6924.
+   * Fix a bug in the build where directory names containing spaces were
+     causing generate_errors.pl to error out resulting in a build failure.
+     Fixes issue #6879.
+   * Fix compile error where MBEDTLS_RSA_C and MBEDTLS_X509_CRT_WRITE_C are
+     defined, but MBEDTLS_PK_RSA_ALT_SUPPORT is not defined. Fixes #3174.
+   * Fix a build issue when defining MBEDTLS_TIMING_ALT and MBEDTLS_SELF_TEST.
+     The library would not link if the user didn't provide an external self-test
+     function. The self-test is now provided regardless of the choice of
+     internal/alternative timing implementation. Fixes #6923.
+   * mbedtls_x509write_crt_set_serial() now explicitly rejects serial numbers
+     whose binary representation is longer than 20 bytes. This was already
+     forbidden by the standard (RFC5280 - section 4.1.2.2) and now it's being
+     enforced also at code level.
+   * Fix potential undefined behavior in mbedtls_mpi_sub_abs().  Reported by
+     Pascal Cuoq using TrustInSoft Analyzer in #6701; observed independently by
+     Aaron Ucko under Valgrind.
+   * Fix behavior of certain sample programs which could, when run with no
+     arguments, access uninitialized memory in some cases. Fixes #6700 (which
+     was found by TrustInSoft Analyzer during REDOCS'22) and #1120.
+   * Fix build errors in test programs when MBEDTLS_CERTS_C is disabled.
+     Fixes #6243.
+   * Fix parsing of X.509 SubjectAlternativeName extension. Previously,
+     malformed alternative name components were not caught during initial
+     certificate parsing, but only on subsequent calls to
+     mbedtls_x509_parse_subject_alt_name(). Fixes #2838.
+   * Fix bug in conversion from OID to string in
+     mbedtls_oid_get_numeric_string(). OIDs such as 2.40.0.25 are now printed
+     correctly.
+   * Reject OIDs with overlong-encoded subidentifiers when converting
+     them to a string.
+   * Reject OIDs with subidentifier values exceeding UINT_MAX.  Such
+     subidentifiers can be valid, but Mbed TLS cannot currently handle them.
+   * Reject OIDs that have unterminated subidentifiers, or (equivalently)
+     have the most-significant bit set in their last byte.
+   * Silence a warning about an unused local variable in bignum.c on
+     some architectures. Fixes #7166.
+   * Silence warnings from clang -Wdocumentation about empty \retval
+     descriptions, which started appearing with Clang 15. Fixes #6960.
+   * Fix undefined behavior in mbedtls_ssl_read() and mbedtls_ssl_write() if
+     len argument is 0 and buffer is NULL.
+
+Changes
+   * The C code follows a new coding style. This is transparent for users but
+     affects contributors and maintainers of local patches. For more
+     information, see
+     https://mbed-tls.readthedocs.io/en/latest/kb/how-to/rewrite-branch-for-coding-style/
+   * Changed the default MBEDTLS_ECP_WINDOW_SIZE from 6 to 2.
+     As tested in issue 6790, the correlation between this define and
+     RSA decryption performance has changed lately due to security fixes.
+     To fix the performance degradation when using default values the
+     window was reduced from 6 to 2, a value that gives the best or close
+     to best results when tested on Cortex-M4 and Intel i7.
+
+= Mbed TLS 2.28.2 branch released 2022-12-14
+
+Security
+   * Fix potential heap buffer overread and overwrite in DTLS if
+     MBEDTLS_SSL_DTLS_CONNECTION_ID is enabled and
+     MBEDTLS_SSL_CID_IN_LEN_MAX > 2 * MBEDTLS_SSL_CID_OUT_LEN_MAX.
+   * Fix an issue where an adversary with access to precise enough information
+     about memory accesses (typically, an untrusted operating system attacking
+     a secure enclave) could recover an RSA private key after observing the
+     victim performing a single private-key operation if the window size used
+     for the exponentiation was 3 or smaller. Found and reported by Zili KOU,
+     Wenjian HE, Sharad Sinha, and Wei ZHANG. See "Cache Side-channel Attacks
+     and Defenses of the Sliding Window Algorithm in TEEs" - Design, Automation
+     and Test in Europe 2023.
+
+Bugfix
+   * Fix a long-standing build failure when building x86 PIC code with old
+     gcc (4.x). The code will be slower, but will compile. We do however
+     recommend upgrading to a more recent compiler instead. Fixes #1910.
+   * Fix support for little-endian Microblaze when MBEDTLS_HAVE_ASM is defined.
+     Contributed by Kazuyuki Kimura to fix #2020.
+   * Use double quotes to include private header file psa_crypto_cipher.h.
+     Fixes 'file not found with <angled> include' error
+     when building with Xcode.
+   * Fix handling of broken symlinks when loading certificates using
+     mbedtls_x509_crt_parse_path(). Instead of returning an error as soon as a
+     broken link is encountered, skip the broken link and continue parsing
+     other certificate files. Contributed by Eduardo Silva in #2602.
+   * Fix a compilation error when using CMake with an IAR toolchain.
+     Fixes #5964.
+   * Fix bugs and missing dependencies when building and testing
+     configurations with only one encryption type enabled in TLS 1.2.
+   * Provide the missing definition of mbedtls_setbuf() in some configurations
+     with MBEDTLS_PLATFORM_C disabled. Fixes #6118, #6196.
+   * Fix compilation errors when trying to build with
+     PSA drivers for AEAD (GCM, CCM, Chacha20-Poly1305).
+   * Fix memory leak in ssl_parse_certificate_request() caused by
+     mbedtls_x509_get_name() not freeing allocated objects in case of error.
+     Change mbedtls_x509_get_name() to clean up allocated objects on error.
+   * Fix checks on PK in check_config.h for builds with PSA and RSA. This does
+     not change which builds actually work, only moving a link-time error to
+     an early check.
+   * Fix ECDSA verification, where it was not always validating the
+     public key. This bug meant that it was possible to verify a
+     signature with an invalid public key, in some cases. Reported by
+     Guido Vranken using Cryptofuzz in #4420.
+   * Fix a possible null pointer dereference if a memory allocation fails
+     in TLS PRF code. Reported by Michael Madsen in #6516.
+   * Fix a bug in which mbedtls_x509_crt_info() would produce non-printable
+     bytes when parsing certificates containing a binary RFC 4108
+     HardwareModuleName as a Subject Alternative Name extension. Hardware
+     serial numbers are now rendered in hex format. Fixes #6262.
+   * Fix bug in error reporting in dh_genprime.c where upon failure,
+     the error code returned by mbedtls_mpi_write_file() is overwritten
+     and therefore not printed.
+   * In the bignum module, operations of the form (-A) - (+A) or (-A) - (-A)
+     with A > 0 created an unintended representation of the value 0 which was
+     not processed correctly by some bignum operations. Fix this. This had no
+     consequence on cryptography code, but might affect applications that call
+     bignum directly and use negative numbers.
+   * Fix undefined behavior (typically harmless in practice) of
+     mbedtls_mpi_add_mpi(), mbedtls_mpi_add_abs() and mbedtls_mpi_add_int()
+     when both operands are 0 and the left operand is represented with 0 limbs.
+   * Fix undefined behavior (typically harmless in practice) when some bignum
+     functions receive the most negative value of mbedtls_mpi_sint. Credit
+     to OSS-Fuzz. Fixes #6597.
+   * Fix undefined behavior (typically harmless in practice) in PSA ECB
+     encryption and decryption.
+
+= Mbed TLS 2.28.1 branch released 2022-07-11
+
+Default behavior changes
+   * mbedtls_cipher_set_iv will now fail with ChaCha20 and ChaCha20+Poly1305
+     for IV lengths other than 12. The library was silently overwriting this
+     length with 12, but did not inform the caller about it. Fixes #4301.
+
+Features
+   * When MBEDTLS_PSA_CRYPTO_CONFIG is enabled, you may list the PSA crypto
+     feature requirements in the file named by the new macro
+     MBEDTLS_PSA_CRYPTO_CONFIG_FILE instead of the default psa/crypto_config.h.
+     Furthermore you may name an additional file to include after the main
+     file with the macro MBEDTLS_PSA_CRYPTO_USER_CONFIG_FILE.
+
+Security
+   * Zeroize dynamically-allocated buffers used by the PSA Crypto key storage
+     module before freeing them. These buffers contain secret key material, and
+     could thus potentially leak the key through freed heap.
+   * Fix a potential heap buffer overread in TLS 1.2 server-side when
+     MBEDTLS_USE_PSA_CRYPTO is enabled, an opaque key (created with
+     mbedtls_pk_setup_opaque()) is provisioned, and a static ECDH ciphersuite
+     is selected. This may result in an application crash or potentially an
+     information leak.
+   * Fix a buffer overread in DTLS ClientHello parsing in servers with
+     MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE enabled. An unauthenticated client
+     or a man-in-the-middle could cause a DTLS server to read up to 255 bytes
+     after the end of the SSL input buffer. The buffer overread only happens
+     when MBEDTLS_SSL_IN_CONTENT_LEN is less than a threshold that depends on
+     the exact configuration: 258 bytes if using mbedtls_ssl_cookie_check(),
+     and possibly up to 571 bytes with a custom cookie check function.
+     Reported by the Cybeats PSI Team.
+
+Bugfix
+   * Fix a memory leak if mbedtls_ssl_config_defaults() is called twice.
+   * Fix several bugs (warnings, compiler and linker errors, test failures)
+     in reduced configurations when MBEDTLS_USE_PSA_CRYPTO is enabled.
+   * Fix a bug in (D)TLS curve negotiation: when MBEDTLS_USE_PSA_CRYPTO was
+     enabled and an ECDHE-ECDSA or ECDHE-RSA key exchange was used, the
+     client would fail to check that the curve selected by the server for
+     ECDHE was indeed one that was offered. As a result, the client would
+     accept any curve that it supported, even if that curve was not allowed
+     according to its configuration. Fixes #5291.
+   * Fix unit tests that used 0 as the file UID. This failed on some
+     implementations of PSA ITS. Fixes #3838.
+   * Fix API violation in mbedtls_md_process() test by adding a call to
+     mbedtls_md_starts(). Fixes #2227.
+   * Fix compile errors when MBEDTLS_HAVE_TIME is not defined. Add tests
+     to catch bad uses of time.h.
+   * Fix the library search path when building a shared library with CMake
+     on Windows.
+   * Fix bug in the alert sending function mbedtls_ssl_send_alert_message()
+     potentially leading to corrupted alert messages being sent in case
+     the function needs to be re-called after initially returning
+     MBEDTLS_SSL_WANT_WRITE. Fixes #1916.
+   * In configurations with MBEDTLS_SSL_DTLS_CONNECTION_ID enabled but none of
+     MBEDTLS_SSL_HW_RECORD_ACCEL, MBEDTLS_SSL_EXPORT_KEYS or MBEDTLS_DEBUG_C,
+     DTLS handshakes using CID would crash due to a null pointer dereference.
+     Fix this. Fixes #3998.
+   * Fix incorrect documentation of mbedtls_x509_crt_profile. The previous
+     documentation stated that the `allowed_pks` field applies to signatures
+     only, but in fact it does apply to the public key type of the end entity
+     certificate, too. Fixes #1992.
+   * Fix PSA cipher multipart operations using ARC4. Previously, an IV was
+     required but discarded. Now, an IV is rejected, as it should be.
+   * Fix undefined behavior in mbedtls_asn1_find_named_data(), where val is
+     not NULL and val_len is zero.
+   * psa_raw_key_agreement() now returns PSA_ERROR_BUFFER_TOO_SMALL when
+     applicable. Fixes #5735.
+   * Fix a bug in the x25519 example program where the removal of
+     MBEDTLS_ECDH_LEGACY_CONTEXT caused the program not to run. Fixes #4901 and
+     #3191.
+   * Encode X.509 dates before 1/1/2000 as UTCTime rather than
+     GeneralizedTime. Fixes #5465.
+   * Fix order value of curve x448.
+   * Fix string representation of DNs when outputting values containing commas
+     and other special characters, conforming to RFC 1779. Fixes #769.
+   * Silence a warning from GCC 12 in the selftest program. Fixes #5974.
+   * Fix mbedtls_asn1_write_mpi() writing an incorrect encoding of 0.
+   * Fix resource leaks in mbedtls_pk_parse_public_key() in low
+     memory conditions.
+   * Fix server connection identifier setting for outgoing encrypted records
+     on DTLS 1.2 session resumption. After DTLS 1.2 session resumption with
+     connection identifier, the Mbed TLS client now properly sends the server
+     connection identifier in encrypted record headers. Fix #5872.
+   * Fix a null pointer dereference when performing some operations on zero
+     represented with 0 limbs (specifically mbedtls_mpi_mod_int() dividing
+     by 2, and mbedtls_mpi_write_string() in base 2).
+   * Fix record sizes larger than 16384 being sometimes accepted despite being
+     non-compliant. This could not lead to a buffer overflow. In particular,
+     application data size was already checked correctly.
+
+Changes
+   * Assume source files are in UTF-8 when using MSVC with CMake.
 
 = mbed TLS 2.28.0 branch released 2021-12-17
 
@@ -174,16 +516,17 @@
    * It was possible to configure MBEDTLS_ECP_MAX_BITS to a value that is
      too small, leading to buffer overflows in ECC operations. Fail the build
      in such a case.
-   * An adversary with access to precise enough information about memory
-     accesses (typically, an untrusted operating system attacking a secure
-     enclave) could recover an RSA private key after observing the victim
-     performing a single private-key operation. Found and reported by
+   * Fix an issue where an adversary with access to precise enough information
+     about memory accesses (typically, an untrusted operating system attacking
+     a secure enclave) could recover an RSA private key after observing the
+     victim performing a single private-key operation. Found and reported by
      Zili KOU, Wenjian HE, Sharad Sinha, and Wei ZHANG.
-   * An adversary with access to precise enough timing information (typically, a
-     co-located process) could recover a Curve25519 or Curve448 static ECDH key
-     after inputting a chosen public key and observing the victim performing the
-     corresponding private-key operation. Found and reported by Leila Batina,
-     Lukas Chmielewski, Björn Haase, Niels Samwel and Peter Schwabe.
+   * Fix an issue where an adversary with access to precise enough timing
+     information (typically, a co-located process) could recover a Curve25519
+     or Curve448 static ECDH key after inputting a chosen public key and
+     observing the victim performing the corresponding private-key operation.
+     Found and reported by Leila Batina, Lukas Chmielewski, Björn Haase, Niels
+     Samwel and Peter Schwabe.
 
 Bugfix
    * Add printf function attributes to mbedtls_debug_print_msg to ensure we
diff --git a/third_party/mbedtls/repo/ChangeLog.d/00README.md b/third_party/mbedtls/repo/ChangeLog.d/00README.md
index d2ea73d..2fbc989 100644
--- a/third_party/mbedtls/repo/ChangeLog.d/00README.md
+++ b/third_party/mbedtls/repo/ChangeLog.d/00README.md
@@ -21,6 +21,9 @@
 * Performance improvements, unless they are particularly significant.
 * Changes to parts of the code base that users don't interact with directly,
   such as test code and test data.
+* Fixes for compiler warnings. Releases typically contain a number of fixes
+  of this kind, so we will only mention them in the Changelog if they are
+  particularly significant.
 
 Until Mbed TLS 2.24.0, we required changelog entries in more cases.
 Looking at older changelog entries is good practice for how to write a
diff --git a/third_party/mbedtls/repo/DartConfiguration.tcl b/third_party/mbedtls/repo/DartConfiguration.tcl
index dfa0f07..af0578a 100644
--- a/third_party/mbedtls/repo/DartConfiguration.tcl
+++ b/third_party/mbedtls/repo/DartConfiguration.tcl
@@ -1,4 +1,4 @@
 Site: localhost
-BuildName: mbed TLS-test
+BuildName: Mbed TLS-test
 CoverageCommand: /usr/bin/gcov
 MemoryCheckCommand: /usr/bin/valgrind
diff --git a/third_party/mbedtls/repo/LICENSE b/third_party/mbedtls/repo/LICENSE
index d645695..776ac77 100644
--- a/third_party/mbedtls/repo/LICENSE
+++ b/third_party/mbedtls/repo/LICENSE
@@ -1,3 +1,10 @@
+Mbed TLS files are provided under a dual [Apache-2.0](https://spdx.org/licenses/Apache-2.0.html)
+OR [GPL-2.0-or-later](https://spdx.org/licenses/GPL-2.0-or-later.html) license.
+This means that users may choose which of these licenses they take the code
+under.
+
+The full text of each of these licenses is given below.
+
 
                                  Apache License
                            Version 2.0, January 2004
@@ -200,3 +207,347 @@
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
+
+
+===============================================================================
+
+
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/third_party/mbedtls/repo/Makefile b/third_party/mbedtls/repo/Makefile
index 6a8b230..1efe89d 100644
--- a/third_party/mbedtls/repo/Makefile
+++ b/third_party/mbedtls/repo/Makefile
@@ -3,7 +3,7 @@
 
 .SILENT:
 
-.PHONY: all no_test programs lib tests install uninstall clean test check covtest lcov apidoc apidoc_clean
+.PHONY: all no_test programs lib tests install uninstall clean test check lcov apidoc apidoc_clean
 
 all: programs tests
 	$(MAKE) post_build
@@ -100,23 +100,15 @@
 test: check
 
 ifndef WINDOWS
-# note: for coverage testing, build with:
-# make CFLAGS='--coverage -g3 -O0'
-covtest:
-	$(MAKE) check
-	programs/test/selftest
-	tests/compat.sh
-	tests/ssl-opt.sh
-
+# For coverage testing:
+# 1. Build with:
+#         make CFLAGS='--coverage -g3 -O0' LDFLAGS='--coverage'
+# 2. Run the relevant tests for the part of the code you're interested in.
+#    For the reference coverage measurement, see
+#    tests/scripts/basic-build-test.sh
+# 3. Run scripts/lcov.sh to generate an HTML report.
 lcov:
-	rm -rf Coverage
-	lcov --capture --initial --directory library -o files.info
-	lcov --rc lcov_branch_coverage=1 --capture --directory library -o tests.info
-	lcov --rc lcov_branch_coverage=1 --add-tracefile files.info --add-tracefile tests.info -o all.info
-	lcov --rc lcov_branch_coverage=1 --remove all.info -o final.info '*.h'
-	gendesc tests/Descriptions.txt -o descriptions
-	genhtml --title "mbed TLS" --description-file descriptions --keep-descriptions --legend --branch-coverage -o Coverage final.info
-	rm -f files.info tests.info all.info final.info descriptions
+	scripts/lcov.sh
 
 apidoc:
 	mkdir -p apidoc
diff --git a/third_party/mbedtls/repo/README.md b/third_party/mbedtls/repo/README.md
index eb3829c..bbd1fb2 100644
--- a/third_party/mbedtls/repo/README.md
+++ b/third_party/mbedtls/repo/README.md
@@ -17,7 +17,9 @@
 Documentation
 -------------
 
-Documentation for the Mbed TLS interfaces in the default library configuration is available as part of the [Mbed TLS documentation](https://tls.mbed.org/api/).
+The main Mbed TLS documentation is available via [ReadTheDocs](https://mbed-tls.readthedocs.io/).
+
+Documentation for the PSA Cryptography API is available [on GitHub](https://arm-software.github.io/psa-api/crypto/).
 
 To generate a local copy of the library documentation in HTML format, tailored to your compile-time configuration:
 
@@ -46,7 +48,7 @@
 
 * GNU Make or a build tool that CMake supports.
 * A C99 toolchain (compiler, linker, archiver). We actively test with GCC 5.4, Clang 3.8, IAR8 and Visual Studio 2013. More recent versions should work. Slightly older versions may work.
-* Python 3 to generate the test code.
+* Python 3.6 or later to generate the test code.
 * Perl to run the tests.
 
 ### Make
@@ -77,9 +79,9 @@
 
 Please note that setting `CFLAGS` overrides its default value of `-O2` and setting `WARNING_CFLAGS` overrides its default value (starting with `-Wall -Wextra`), so if you just want to add some warning options to the default ones, you can do so by setting `CFLAGS=-O2 -Werror` for example. Setting `WARNING_CFLAGS` is useful when you want to get rid of its default content (for example because your compiler doesn't accept `-Wall` as an option). Directory-specific options cannot be overridden from the command line.
 
-Depending on your platform, you might run into some issues. Please check the Makefiles in `library/`, `programs/` and `tests/` for options to manually add or remove for specific platforms. You can also check [the Mbed TLS Knowledge Base](https://tls.mbed.org/kb) for articles on your platform or issue.
+Depending on your platform, you might run into some issues. Please check the Makefiles in `library/`, `programs/` and `tests/` for options to manually add or remove for specific platforms. You can also check [the Mbed TLS Knowledge Base](https://mbed-tls.readthedocs.io/en/latest/kb/) for articles on your platform or issue.
 
-In case you find that you need to do something else as well, please let us know what, so we can add it to the [Mbed TLS Knowledge Base](https://tls.mbed.org/kb).
+In case you find that you need to do something else as well, please let us know what, so we can add it to the [Mbed TLS Knowledge Base](https://mbed-tls.readthedocs.io/en/latest/kb/).
 
 ### CMake
 
@@ -185,17 +187,19 @@
 -   `tests/ssl-opt.sh` runs integration tests for various TLS options (renegotiation, resumption, etc.) and tests interoperability of these options with other implementations.
 -   `tests/compat.sh` tests interoperability of every ciphersuite with other implementations.
 -   `tests/scripts/test-ref-configs.pl` test builds in various reduced configurations.
--   `tests/scripts/key-exchanges.pl` test builds in configurations with a single key exchange enabled
+-   `tests/scripts/depends.py` test builds in configurations with a single curve, key exchange, hash, cipher, or pkalg on.
 -   `tests/scripts/all.sh` runs a combination of the above tests, plus some more, with various build options (such as ASan, full `config.h`, etc).
 
+Instead of manually installing the required versions of all tools required for testing, it is possible to use the Docker images from our CI systems, as explained in [our testing infrastructure repository](https://github.com/Mbed-TLS/mbedtls-test/blob/master/README.md#quick-start).
+
 Porting Mbed TLS
 ----------------
 
 Mbed TLS can be ported to many different architectures, OS's and platforms. Before starting a port, you may find the following Knowledge Base articles useful:
 
--   [Porting Mbed TLS to a new environment or OS](https://tls.mbed.org/kb/how-to/how-do-i-port-mbed-tls-to-a-new-environment-OS)
--   [What external dependencies does Mbed TLS rely on?](https://tls.mbed.org/kb/development/what-external-dependencies-does-mbedtls-rely-on)
--   [How do I configure Mbed TLS](https://tls.mbed.org/kb/compiling-and-building/how-do-i-configure-mbedtls)
+-   [Porting Mbed TLS to a new environment or OS](https://mbed-tls.readthedocs.io/en/latest/kb/how-to/how-do-i-port-mbed-tls-to-a-new-environment-OS/)
+-   [What external dependencies does Mbed TLS rely on?](https://mbed-tls.readthedocs.io/en/latest/kb/development/what-external-dependencies-does-mbedtls-rely-on/)
+-   [How do I configure Mbed TLS](https://mbed-tls.readthedocs.io/en/latest/kb/compiling-and-building/how-do-i-configure-mbedtls/)
 
 Mbed TLS is mostly written in portable C99; however, it has a few platform requirements that go beyond the standard, but are met by most modern architectures:
 
@@ -208,11 +212,11 @@
 PSA cryptography API
 --------------------
 
-### PSA API design
+### PSA API
 
 Arm's [Platform Security Architecture (PSA)](https://developer.arm.com/architectures/security-architectures/platform-security-architecture) is a holistic set of threat models, security analyses, hardware and firmware architecture specifications, and an open source firmware reference implementation. PSA provides a recipe, based on industry best practice, that allows security to be consistently designed in, at both a hardware and firmware level.
 
-The [PSA cryptography API](https://armmbed.github.io/mbed-crypto/psa/#application-programming-interface) provides access to a set of cryptographic primitives. It has a dual purpose. First, it can be used in a PSA-compliant platform to build services, such as secure boot, secure storage and secure communication. Second, it can also be used independently of other PSA components on any platform.
+The [PSA cryptography API](https://arm-software.github.io/psa-api/crypto/) provides access to a set of cryptographic primitives. It has a dual purpose. First, it can be used in a PSA-compliant platform to build services, such as secure boot, secure storage and secure communication. Second, it can also be used independently of other PSA components on any platform.
 
 The design goals of the PSA cryptography API include:
 
@@ -224,10 +228,6 @@
 
 Arm welcomes feedback on the design of the API. If you think something could be improved, please open an issue on our Github repository. Alternatively, if you prefer to provide your feedback privately, please email us at [`mbed-crypto@arm.com`](mailto:mbed-crypto@arm.com). All feedback received by email is treated confidentially.
 
-### PSA API documentation
-
-A browsable copy of the PSA Cryptography API documents is available on the [PSA cryptography interfaces documentation portal](https://armmbed.github.io/mbed-crypto/psa/#application-programming-interface) in [PDF](https://armmbed.github.io/mbed-crypto/PSA_Cryptography_API_Specification.pdf) and [HTML](https://armmbed.github.io/mbed-crypto/html/index.html) formats.
-
 ### PSA implementation in Mbed TLS
 
 Mbed TLS includes a reference implementation of the PSA Cryptography API.
@@ -235,7 +235,7 @@
 
 The X.509 and TLS code can use PSA cryptography for a limited subset of operations. To enable this support, activate the compilation option `MBEDTLS_USE_PSA_CRYPTO` in `config.h`.
 
-There are currently a few deviations where the library does not yet implement the latest version of the specification. Please refer to the [compliance issues on Github](https://github.com/ARMmbed/mbed-crypto/labels/compliance) for an up-to-date list.
+There are currently a few deviations where the library does not yet implement the latest version of the specification. Please refer to the [compliance issues on Github](https://github.com/Mbed-TLS/mbed-crypto/labels/compliance) for an up-to-date list.
 
 ### Upcoming features
 
@@ -249,7 +249,7 @@
 License
 -------
 
-Unless specifically indicated otherwise in a file, Mbed TLS files are provided under the [Apache-2.0](https://spdx.org/licenses/Apache-2.0.html) license. See the [LICENSE](LICENSE) file for the full text of this license. Contributors must accept that their contributions are made under both the Apache-2.0 AND [GPL-2.0-or-later](https://spdx.org/licenses/GPL-2.0-or-later.html) licenses. This enables LTS (Long Term Support) branches of the software to be provided under either the Apache-2.0 OR GPL-2.0-or-later licenses.
+Unless specifically indicated otherwise in a file, Mbed TLS files are provided under a dual [Apache-2.0](https://spdx.org/licenses/Apache-2.0.html) OR [GPL-2.0-or-later](https://spdx.org/licenses/GPL-2.0-or-later.html) license. See the [LICENSE](LICENSE) file for the full text of these licenses, and [the 'License and Copyright' section in the contributing guidelines](CONTRIBUTING.md#License-and-Copyright) for more information.
 
 Contributing
 ------------
@@ -260,5 +260,5 @@
 -------
 
 * To report a security vulnerability in Mbed TLS, please email <mbed-tls-security@lists.trustedfirmware.org>. For more information, see [`SECURITY.md`](SECURITY.md).
-* To report a bug or request a feature in Mbed TLS, please [file an issue on GitHub](https://github.com/ARMmbed/mbedtls/issues/new/choose).
+* To report a bug or request a feature in Mbed TLS, please [file an issue on GitHub](https://github.com/Mbed-TLS/mbedtls/issues/new/choose).
 * Please see [`SUPPORT.md`](SUPPORT.md) for other channels for discussion and support about Mbed TLS.
diff --git a/third_party/mbedtls/repo/SECURITY.md b/third_party/mbedtls/repo/SECURITY.md
index bd18f6c..732335b 100644
--- a/third_party/mbedtls/repo/SECURITY.md
+++ b/third_party/mbedtls/repo/SECURITY.md
@@ -1,4 +1,4 @@
-## Reporting Vulneratibilities
+## Reporting Vulnerabilities
 
 If you think you have found an Mbed TLS security vulnerability, then please
 send an email to the security team at
@@ -6,7 +6,7 @@
 
 ## Security Incident Handling Process
 
-Our security process is detailled in our
+Our security process is detailed in our
 [security
 center](https://developer.trustedfirmware.org/w/mbed-tls/security-center/).
 
@@ -18,3 +18,120 @@
 Only the maintained branches, as listed in [`BRANCHES.md`](BRANCHES.md),
 get security fixes.
 Users are urged to always use the latest version of a maintained branch.
+
+## Threat model
+
+We classify attacks based on the capabilities of the attacker.
+
+### Remote attacks
+
+In this section, we consider an attacker who can observe and modify data sent
+over the network. This includes observing the content and timing of individual
+packets, as well as suppressing or delaying legitimate messages, and injecting
+messages.
+
+Mbed TLS aims to fully protect against remote attacks and to enable the user
+application in providing full protection against remote attacks. Said
+protection is limited to providing security guarantees offered by the protocol
+being implemented. (For example Mbed TLS alone won't guarantee that the
+messages will arrive without delay, as the TLS protocol doesn't guarantee that
+either.)
+
+**Warning!** Block ciphers do not yet achieve full protection against attackers
+who can measure the timing of packets with sufficient precision. For details
+and workarounds see the [Block Ciphers](#block-ciphers) section.
+
+### Local attacks
+
+In this section, we consider an attacker who can run software on the same
+machine. The attacker has insufficient privileges to directly access Mbed TLS
+assets such as memory and files.
+
+#### Timing attacks
+
+The attacker is able to observe the timing of instructions executed by Mbed TLS
+by leveraging shared hardware that both Mbed TLS and the attacker have access
+to. Typical attack vectors include cache timings, memory bus contention and
+branch prediction.
+
+Mbed TLS provides limited protection against timing attacks. The cost of
+protecting against timing attacks widely varies depending on the granularity of
+the measurements and the noise present. Therefore the protection in Mbed TLS is
+limited. We are only aiming to provide protection against **publicly
+documented attack techniques**.
+
+As attacks keep improving, so does Mbed TLS's protection. Mbed TLS is moving
+towards a model of fully timing-invariant code, but has not reached this point
+yet.
+
+**Remark:** Timing information can be observed over the network or through
+physical side channels as well. Remote and physical timing attacks are covered
+in the [Remote attacks](remote-attacks) and [Physical
+attacks](physical-attacks) sections respectively.
+
+**Warning!** Block ciphers do not yet achieve full protection. For
+details and workarounds see the [Block Ciphers](#block-ciphers) section.
+
+#### Local non-timing side channels
+
+The attacker code running on the platform has access to some sensor capable of
+picking up information on the physical state of the hardware while Mbed TLS is
+running. This could for example be an analogue-to-digital converter on the
+platform that is located unfortunately enough to pick up the CPU noise.
+
+Mbed TLS doesn't make any security guarantees against local non-timing-based
+side channel attacks. If local non-timing attacks are present in a use case or
+a user application's threat model, they need to be mitigated by the platform.
+
+#### Local fault injection attacks
+
+Software running on the same hardware can affect the physical state of the
+device and introduce faults.
+
+Mbed TLS doesn't make any security guarantees against local fault injection
+attacks. If local fault injection attacks are present in a use case or a user
+application's threat model, they need to be mitigated by the platform.
+
+### Physical attacks
+
+In this section, we consider an attacker who has access to physical information
+about the hardware Mbed TLS is running on and/or can alter the physical state
+of the hardware (e.g. power analysis, radio emissions or fault injection).
+
+Mbed TLS doesn't make any security guarantees against physical attacks. If
+physical attacks are present in a use case or a user application's threat
+model, they need to be mitigated by physical countermeasures.
+
+### Caveats
+
+#### Out-of-scope countermeasures
+
+Mbed TLS has evolved organically and a well defined threat model hasn't always
+been present. Therefore, Mbed TLS might have countermeasures against attacks
+outside the above defined threat model.
+
+The presence of such countermeasures don't mean that Mbed TLS provides
+protection against a class of attacks outside of the above described threat
+model. Neither does it mean that the failure of such a countermeasure is
+considered a vulnerability.
+
+#### Block ciphers
+
+Currently there are four block ciphers in Mbed TLS: AES, CAMELLIA, ARIA and
+DES. The pure software implementation in Mbed TLS implementation uses lookup
+tables, which are vulnerable to timing attacks.
+
+These timing attacks can be physical, local or depending on network latency
+even a remote. The attacks can result in key recovery.
+
+**Workarounds:**
+
+- Turn on hardware acceleration for AES. This is supported only on selected
+  architectures and currently only available for AES. See configuration options
+  `MBEDTLS_AESNI_C` and `MBEDTLS_PADLOCK_C` for details.
+- Add a secure alternative implementation (typically hardware acceleration) for
+  the vulnerable cipher. See the [Alternative Implementations
+Guide](docs/architecture/alternative-implementations.md) for more information.
+- Use cryptographic mechanisms that are not based on block ciphers. In
+  particular, for authenticated encryption, use ChaCha20/Poly1305 instead of
+  block cipher modes. For random generation, use HMAC\_DRBG instead of CTR\_DRBG.
diff --git a/third_party/mbedtls/repo/SUPPORT.md b/third_party/mbedtls/repo/SUPPORT.md
index 1bc0695..b550e08 100644
--- a/third_party/mbedtls/repo/SUPPORT.md
+++ b/third_party/mbedtls/repo/SUPPORT.md
@@ -2,14 +2,15 @@
 
 Here are some useful sources of information about using Mbed TLS:
 
+- [ReadTheDocs](https://mbed-tls.readthedocs.io/);
 - API documentation, see the [Documentation section of the
-  README](README.md#License);
+  README](README.md#documentation);
 - the `docs` directory in the source tree;
-- the [Mbed TLS knowledge Base](https://tls.mbed.org/kb);
+- the [Mbed TLS Knowledge Base](https://mbed-tls.readthedocs.io/en/latest/kb/);
 - the [Mbed TLS mailing-list
-  archives](https://lists.trustedfirmware.org/pipermail/mbed-tls/).
+  archives](https://lists.trustedfirmware.org/archives/list/mbed-tls@lists.trustedfirmware.org/).
 
 ## Asking Questions
 
 If you can't find your answer in the above sources, please use the [Mbed TLS
-mailing list](https://lists.trustedfirmware.org/mailman/listinfo/mbed-tls).
+mailing list](https://lists.trustedfirmware.org/mailman3/lists/mbed-tls.lists.trustedfirmware.org).
diff --git a/third_party/mbedtls/repo/configs/README.txt b/third_party/mbedtls/repo/configs/README.txt
index 933fa7f..d2f9bcb 100644
--- a/third_party/mbedtls/repo/configs/README.txt
+++ b/third_party/mbedtls/repo/configs/README.txt
@@ -23,4 +23,4 @@
     make
 
 Note that the second method also works if you want to keep your custom
-configuration file outside the mbed TLS tree.
+configuration file outside the Mbed TLS tree.
diff --git a/third_party/mbedtls/repo/configs/config-ccm-psk-dtls1_2.h b/third_party/mbedtls/repo/configs/config-ccm-psk-dtls1_2.h
new file mode 100644
index 0000000..3ae9149
--- /dev/null
+++ b/third_party/mbedtls/repo/configs/config-ccm-psk-dtls1_2.h
@@ -0,0 +1,98 @@
+/**
+ * \file config-ccm-psk-dtls1_2.h
+ *
+ * \brief Small configuration for DTLS 1.2 with PSK and AES-CCM ciphersuites
+ */
+/*
+ *  Copyright The Mbed TLS Contributors
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+/*
+ * Minimal configuration for DTLS 1.2 with PSK and AES-CCM ciphersuites
+ *
+ * Distinguishing features:
+ * - Optimized for small code size, low bandwidth (on an unreliable transport),
+ *   and low RAM usage.
+ * - No asymmetric cryptography (no certificates, no Diffie-Hellman key
+ *   exchange).
+ * - Fully modern and secure (provided the pre-shared keys are generated and
+ *   stored securely).
+ * - Very low record overhead with CCM-8.
+ * - Includes several optional DTLS features typically used in IoT.
+ *
+ * See README.txt for usage instructions.
+ */
+#ifndef MBEDTLS_CONFIG_H
+#define MBEDTLS_CONFIG_H
+
+/* System support */
+//#define MBEDTLS_HAVE_TIME /* Optionally used in Hello messages */
+/* Other MBEDTLS_HAVE_XXX flags irrelevant for this configuration */
+
+/* Mbed TLS modules */
+#define MBEDTLS_AES_C
+#define MBEDTLS_CCM_C
+#define MBEDTLS_CIPHER_C
+#define MBEDTLS_CTR_DRBG_C
+#define MBEDTLS_ENTROPY_C
+#define MBEDTLS_MD_C
+#define MBEDTLS_NET_C
+#define MBEDTLS_SHA256_C
+#define MBEDTLS_SSL_CLI_C
+#define MBEDTLS_SSL_COOKIE_C
+#define MBEDTLS_SSL_SRV_C
+#define MBEDTLS_SSL_TLS_C
+#define MBEDTLS_TIMING_C
+
+/* TLS protocol feature support */
+#define MBEDTLS_KEY_EXCHANGE_PSK_ENABLED
+#define MBEDTLS_SSL_PROTO_TLS1_2
+#define MBEDTLS_SSL_PROTO_DTLS
+#define MBEDTLS_SSL_DTLS_ANTI_REPLAY
+#define MBEDTLS_SSL_DTLS_BADMAC_LIMIT
+#define MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE
+#define MBEDTLS_SSL_DTLS_CONNECTION_ID
+#define MBEDTLS_SSL_DTLS_HELLO_VERIFY
+#define MBEDTLS_SSL_MAX_FRAGMENT_LENGTH
+
+/*
+ * Use only CCM_8 ciphersuites, and
+ * save ROM and a few bytes of RAM by specifying our own ciphersuite list
+ */
+#define MBEDTLS_SSL_CIPHERSUITES                        \
+    MBEDTLS_TLS_PSK_WITH_AES_256_CCM_8,             \
+    MBEDTLS_TLS_PSK_WITH_AES_128_CCM_8
+
+/*
+ * Save RAM at the expense of interoperability: do this only if you control
+ * both ends of the connection!  (See comments in "mbedtls/ssl.h".)
+ * The optimal size here depends on the typical size of records.
+ */
+#define MBEDTLS_SSL_MAX_CONTENT_LEN             256
+
+/* Save RAM at the expense of ROM */
+#define MBEDTLS_AES_ROM_TABLES
+
+/* Save some RAM by adjusting to your exact needs */
+#define MBEDTLS_PSK_MAX_LEN    16 /* 128-bits keys are generally enough */
+
+/*
+ * You should adjust this to the exact number of sources you're using: default
+ * is the "platform_entropy_poll" source plus a weak clock source, but you may
+ * want to add other ones. Minimum is 3 for the entropy test suite.
+ */
+#define MBEDTLS_ENTROPY_MAX_SOURCES 3
+
+/* These defines are present so that the config modifying scripts can enable
+ * them during tests/scripts/test-ref-configs.pl */
+//#define MBEDTLS_USE_PSA_CRYPTO
+//#define MBEDTLS_PSA_CRYPTO_C
+
+/* Error messages and TLS debugging traces
+ * (huge code size increase, needed for tests/ssl-opt.sh) */
+//#define MBEDTLS_DEBUG_C
+//#define MBEDTLS_ERROR_C
+
+#include "mbedtls/check_config.h"
+
+#endif /* MBEDTLS_CONFIG_H */
diff --git a/third_party/mbedtls/repo/configs/config-ccm-psk-tls1_2.h b/third_party/mbedtls/repo/configs/config-ccm-psk-tls1_2.h
index c58d150..d609835 100644
--- a/third_party/mbedtls/repo/configs/config-ccm-psk-tls1_2.h
+++ b/third_party/mbedtls/repo/configs/config-ccm-psk-tls1_2.h
@@ -5,27 +5,19 @@
  */
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 /*
  * Minimal configuration for TLS 1.2 with PSK and AES-CCM ciphersuites
+ *
  * Distinguishing features:
- * - no bignum, no PK, no X509
- * - fully modern and secure (provided the pre-shared keys have high entropy)
- * - very low record overhead with CCM-8
- * - optimized for low RAM usage
+ * - Optimized for small code size, low bandwidth (on a reliable transport),
+ *   and low RAM usage.
+ * - No asymmetric cryptography (no certificates, no Diffie-Hellman key
+ *   exchange).
+ * - Fully modern and secure (provided the pre-shared keys are generated and
+ *   stored securely).
+ * - Very low record overhead with CCM-8.
  *
  * See README.txt for usage instructions.
  */
@@ -36,11 +28,7 @@
 //#define MBEDTLS_HAVE_TIME /* Optionally used in Hello messages */
 /* Other MBEDTLS_HAVE_XXX flags irrelevant for this configuration */
 
-/* mbed TLS feature support */
-#define MBEDTLS_KEY_EXCHANGE_PSK_ENABLED
-#define MBEDTLS_SSL_PROTO_TLS1_2
-
-/* mbed TLS modules */
+/* Mbed TLS modules */
 #define MBEDTLS_AES_C
 #define MBEDTLS_CCM_C
 #define MBEDTLS_CIPHER_C
@@ -53,6 +41,25 @@
 #define MBEDTLS_SSL_SRV_C
 #define MBEDTLS_SSL_TLS_C
 
+/* TLS protocol feature support */
+#define MBEDTLS_KEY_EXCHANGE_PSK_ENABLED
+#define MBEDTLS_SSL_PROTO_TLS1_2
+
+/*
+ * Use only CCM_8 ciphersuites, and
+ * save ROM and a few bytes of RAM by specifying our own ciphersuite list
+ */
+#define MBEDTLS_SSL_CIPHERSUITES                        \
+    MBEDTLS_TLS_PSK_WITH_AES_256_CCM_8,             \
+    MBEDTLS_TLS_PSK_WITH_AES_128_CCM_8
+
+/*
+ * Save RAM at the expense of interoperability: do this only if you control
+ * both ends of the connection!  (See comments in "mbedtls/ssl.h".)
+ * The optimal size here depends on the typical size of records.
+ */
+#define MBEDTLS_SSL_MAX_CONTENT_LEN             1024
+
 /* Save RAM at the expense of ROM */
 #define MBEDTLS_AES_ROM_TABLES
 
@@ -66,20 +73,15 @@
  */
 #define MBEDTLS_ENTROPY_MAX_SOURCES 2
 
-/*
- * Use only CCM_8 ciphersuites, and
- * save ROM and a few bytes of RAM by specifying our own ciphersuite list
- */
-#define MBEDTLS_SSL_CIPHERSUITES                        \
-        MBEDTLS_TLS_PSK_WITH_AES_256_CCM_8,             \
-        MBEDTLS_TLS_PSK_WITH_AES_128_CCM_8
+/* These defines are present so that the config modifying scripts can enable
+ * them during tests/scripts/test-ref-configs.pl */
+//#define MBEDTLS_USE_PSA_CRYPTO
+//#define MBEDTLS_PSA_CRYPTO_C
 
-/*
- * Save RAM at the expense of interoperability: do this only if you control
- * both ends of the connection!  (See comments in "mbedtls/ssl.h".)
- * The optimal size here depends on the typical size of records.
- */
-#define MBEDTLS_SSL_MAX_CONTENT_LEN             1024
+/* Error messages and TLS debugging traces
+ * (huge code size increase, needed for tests/ssl-opt.sh) */
+//#define MBEDTLS_DEBUG_C
+//#define MBEDTLS_ERROR_C
 
 #include "mbedtls/check_config.h"
 
diff --git a/third_party/mbedtls/repo/configs/config-mini-tls1_1.h b/third_party/mbedtls/repo/configs/config-mini-tls1_1.h
index 638c1e2..20a137a 100644
--- a/third_party/mbedtls/repo/configs/config-mini-tls1_1.h
+++ b/third_party/mbedtls/repo/configs/config-mini-tls1_1.h
@@ -5,19 +5,7 @@
  */
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 /*
  * Minimal configuration for TLS 1.1 (RFC 4346), implementing only the
@@ -33,13 +21,13 @@
 #define MBEDTLS_HAVE_ASM
 #define MBEDTLS_HAVE_TIME
 
-/* mbed TLS feature support */
+/* Mbed TLS feature support */
 #define MBEDTLS_CIPHER_MODE_CBC
 #define MBEDTLS_PKCS1_V15
 #define MBEDTLS_KEY_EXCHANGE_RSA_ENABLED
 #define MBEDTLS_SSL_PROTO_TLS1_1
 
-/* mbed TLS modules */
+/* Mbed TLS modules */
 #define MBEDTLS_AES_C
 #define MBEDTLS_ASN1_PARSE_C
 #define MBEDTLS_ASN1_WRITE_C
@@ -71,6 +59,20 @@
 /* For testing with compat.sh */
 #define MBEDTLS_FS_IO
 
+/* These defines are present so that the config modifying scripts can enable
+ * them during tests/scripts/test-ref-configs.pl */
+//#define MBEDTLS_USE_PSA_CRYPTO
+//#define MBEDTLS_PSA_CRYPTO_C
+
+/* With MBEDTLS_PSA_CRYPTO_C, importing an RSA key requires MBEDTLS_PK_WRITE_C */
+#if defined(MBEDTLS_PSA_CRYPTO_C)
+#define MBEDTLS_PK_WRITE_C
+#endif
 #include "mbedtls/check_config.h"
 
+/* Error messages and TLS debugging traces
+ * (huge code size increase, needed for tests/ssl-opt.sh) */
+//#define MBEDTLS_DEBUG_C
+//#define MBEDTLS_ERROR_C
+
 #endif /* MBEDTLS_CONFIG_H */
diff --git a/third_party/mbedtls/repo/configs/config-no-entropy.h b/third_party/mbedtls/repo/configs/config-no-entropy.h
index 04c1213..d11251d 100644
--- a/third_party/mbedtls/repo/configs/config-no-entropy.h
+++ b/third_party/mbedtls/repo/configs/config-no-entropy.h
@@ -5,23 +5,11 @@
  */
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 /*
  * Minimal configuration of features that do not require an entropy source
- * Distinguishing reatures:
+ * Distinguishing features:
  * - no entropy module
  * - no TLS protocol implementation available due to absence of an entropy
  *   source
@@ -36,7 +24,7 @@
 #define MBEDTLS_HAVE_ASM
 #define MBEDTLS_HAVE_TIME
 
-/* mbed TLS feature support */
+/* Mbed TLS feature support */
 #define MBEDTLS_CIPHER_MODE_CBC
 #define MBEDTLS_CIPHER_PADDING_PKCS7
 #define MBEDTLS_REMOVE_ARC4_CIPHERSUITES
@@ -53,7 +41,7 @@
 #define MBEDTLS_X509_CHECK_KEY_USAGE
 #define MBEDTLS_X509_CHECK_EXTENDED_KEY_USAGE
 
-/* mbed TLS modules */
+/* Mbed TLS modules */
 #define MBEDTLS_AES_C
 #define MBEDTLS_ASN1_PARSE_C
 #define MBEDTLS_ASN1_WRITE_C
diff --git a/third_party/mbedtls/repo/configs/config-suite-b.h b/third_party/mbedtls/repo/configs/config-suite-b.h
index 9cad382..58fa691 100644
--- a/third_party/mbedtls/repo/configs/config-suite-b.h
+++ b/third_party/mbedtls/repo/configs/config-suite-b.h
@@ -5,19 +5,7 @@
  */
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 /*
  * Minimal configuration for TLS NSA Suite B Profile (RFC 6460)
@@ -40,13 +28,13 @@
 #define MBEDTLS_HAVE_ASM
 #define MBEDTLS_HAVE_TIME
 
-/* mbed TLS feature support */
+/* Mbed TLS feature support */
 #define MBEDTLS_ECP_DP_SECP256R1_ENABLED
 #define MBEDTLS_ECP_DP_SECP384R1_ENABLED
 #define MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
 #define MBEDTLS_SSL_PROTO_TLS1_2
 
-/* mbed TLS modules */
+/* Mbed TLS modules */
 #define MBEDTLS_AES_C
 #define MBEDTLS_ASN1_PARSE_C
 #define MBEDTLS_ASN1_WRITE_C
@@ -103,12 +91,27 @@
 
 /*
  * Save RAM at the expense of interoperability: do this only if you control
- * both ends of the connection!  (See coments in "mbedtls/ssl.h".)
+ * both ends of the connection!  (See comments in "mbedtls/ssl.h".)
  * The minimum size here depends on the certificate chain used as well as the
  * typical size of records.
  */
 #define MBEDTLS_SSL_MAX_CONTENT_LEN             1024
 
+/* These defines are present so that the config modifying scripts can enable
+ * them during tests/scripts/test-ref-configs.pl */
+//#define MBEDTLS_USE_PSA_CRYPTO
+//#define MBEDTLS_PSA_CRYPTO_C
+
+/* With USE_PSA_CRYPTO, some PK operations also need PK_WRITE */
+#if defined(MBEDTLS_USE_PSA_CRYPTO)
+#define MBEDTLS_PK_WRITE_C
+#endif
+
+/* Error messages and TLS debugging traces
+ * (huge code size increase, needed for tests/ssl-opt.sh) */
+//#define MBEDTLS_DEBUG_C
+//#define MBEDTLS_ERROR_C
+
 #include "mbedtls/check_config.h"
 
 #endif /* MBEDTLS_CONFIG_H */
diff --git a/third_party/mbedtls/repo/configs/config-symmetric-only.h b/third_party/mbedtls/repo/configs/config-symmetric-only.h
index f05a0d7..6a6dc48 100644
--- a/third_party/mbedtls/repo/configs/config-symmetric-only.h
+++ b/third_party/mbedtls/repo/configs/config-symmetric-only.h
@@ -5,19 +5,7 @@
  */
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 
 #ifndef MBEDTLS_CONFIG_H
@@ -28,7 +16,7 @@
 #define MBEDTLS_HAVE_TIME
 #define MBEDTLS_HAVE_TIME_DATE
 
-/* Mbed Crypto feature support */
+/* Mbed TLS feature support */
 #define MBEDTLS_CIPHER_MODE_CBC
 #define MBEDTLS_CIPHER_MODE_CFB
 #define MBEDTLS_CIPHER_MODE_CTR
@@ -45,7 +33,7 @@
 #define MBEDTLS_USE_PSA_CRYPTO
 #define MBEDTLS_VERSION_FEATURES
 
-/* Mbed Crypto modules */
+/* Mbed TLS modules */
 #define MBEDTLS_AES_C
 #define MBEDTLS_ARC4_C
 #define MBEDTLS_ASN1_PARSE_C
diff --git a/third_party/mbedtls/repo/configs/config-thread.h b/third_party/mbedtls/repo/configs/config-thread.h
index 8464fcb..f232d83 100644
--- a/third_party/mbedtls/repo/configs/config-thread.h
+++ b/third_party/mbedtls/repo/configs/config-thread.h
@@ -5,19 +5,7 @@
  */
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 
 /*
@@ -38,7 +26,7 @@
 /* System support */
 #define MBEDTLS_HAVE_ASM
 
-/* mbed TLS feature support */
+/* Mbed TLS feature support */
 #define MBEDTLS_AES_ROM_TABLES
 #define MBEDTLS_ECP_DP_SECP256R1_ENABLED
 #define MBEDTLS_ECP_NIST_OPTIM
@@ -50,7 +38,7 @@
 #define MBEDTLS_SSL_DTLS_HELLO_VERIFY
 #define MBEDTLS_SSL_EXPORT_KEYS
 
-/* mbed TLS modules */
+/* Mbed TLS modules */
 #define MBEDTLS_AES_C
 #define MBEDTLS_ASN1_PARSE_C
 #define MBEDTLS_ASN1_WRITE_C
@@ -86,6 +74,11 @@
 /* Save ROM and a few bytes of RAM by specifying our own ciphersuite list */
 #define MBEDTLS_SSL_CIPHERSUITES MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8
 
+/* These defines are present so that the config modifying scripts can enable
+ * them during tests/scripts/test-ref-configs.pl */
+//#define MBEDTLS_USE_PSA_CRYPTO
+//#define MBEDTLS_PSA_CRYPTO_C
+
 #include "mbedtls/check_config.h"
 
 #endif /* MBEDTLS_CONFIG_H */
diff --git a/third_party/mbedtls/repo/docs/.gitignore b/third_party/mbedtls/repo/docs/.gitignore
index 23f832b..11f197b 100644
--- a/third_party/mbedtls/repo/docs/.gitignore
+++ b/third_party/mbedtls/repo/docs/.gitignore
@@ -1,2 +1,4 @@
 *.html
 *.pdf
+_build/
+api/
diff --git a/third_party/mbedtls/repo/docs/Makefile b/third_party/mbedtls/repo/docs/Makefile
new file mode 100644
index 0000000..47510f9
--- /dev/null
+++ b/third_party/mbedtls/repo/docs/Makefile
@@ -0,0 +1,40 @@
+# Minimal makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line, and also
+# from the environment for the first two.
+SPHINXOPTS    ?=
+SPHINXBUILD   ?= sphinx-build
+SOURCEDIR     = .
+BUILDDIR      = _build
+
+# Put it first so that "make" without argument is like "make help".
+help:
+	@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+.PHONY: help clean apidoc breathe_apidoc Makefile
+
+# Intercept the 'clean' target so we can do the right thing for apidoc as well
+clean:
+	@# Clean the apidoc
+	$(MAKE) -C .. apidoc_clean
+	@# Clean the breathe-apidoc generated files
+	rm -rf ./api
+	@# Clean the sphinx docs
+	@$(SPHINXBUILD) -M clean "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+apidoc:
+	@# Generate doxygen from source using the main Makefile
+	$(MAKE) -C .. apidoc
+
+breathe_apidoc: apidoc
+	@# Remove existing files - breathe-apidoc skips them if they're present
+	rm -rf ./api
+	@# Generate RST file structure with breathe-apidoc
+	breathe-apidoc -o ./api ../apidoc/xml
+
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
+%: Makefile breathe_apidoc
+	@# Build the relevant target with sphinx
+	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
diff --git a/third_party/mbedtls/repo/docs/architecture/mbed-crypto-storage-specification.md b/third_party/mbedtls/repo/docs/architecture/mbed-crypto-storage-specification.md
index 914bca3..2b99f77 100644
--- a/third_party/mbedtls/repo/docs/architecture/mbed-crypto-storage-specification.md
+++ b/third_party/mbedtls/repo/docs/architecture/mbed-crypto-storage-specification.md
@@ -1,7 +1,9 @@
-Mbed Crypto storage specification
+Mbed TLS storage specification
 =================================
 
-This document specifies how Mbed Crypto uses storage.
+This document specifies how Mbed TLS uses storage.
+Key storage was originally introduced in a product called Mbed Crypto, which was re-distributed via Mbed TLS and has since been merged into Mbed TLS.
+This document contains historical information both from before and after this merge.
 
 Mbed Crypto may be upgraded on an existing device with the storage preserved. Therefore:
 
@@ -112,7 +114,7 @@
 The way in which the file name is constructed from the key identifier depends on the storage backend. The content of the file is described [below](#key-file-format-for-1.0.0).
 
 * Library integration: the key file name is just the key identifier as defined in the PSA crypto specification. This is a 32-bit value.
-* PSA service integration: the key file name is `(uint32_t)owner_uid << 32 | key_id` where `key_id` is the key identifier from the owner point of view and `owner_uid` (of type `int32_t`) is the calling partition identifier provided to the server by the partition manager. This is a 64-bit value.
+* PSA service integration: the key file name is `(uint64_t)owner_uid << 32 | key_id` where `key_id` is the key identifier from the owner point of view and `owner_uid` (of type `int32_t`) is the calling partition identifier provided to the server by the partition manager. This is a 64-bit value.
 
 ### Key file format for 1.0.0
 
@@ -120,7 +122,11 @@
 
 ### Nonvolatile random seed file format for 1.0.0
 
-[Identical to 0.1.0](#nonvolatile-random-seed-file-format-for-0.1.0).
+The nonvolatile random seed file contains a seed for the random generator. If present, it is rewritten at each boot as part of the random generator initialization.
+
+The file format is just the seed as a byte string with no metadata or encoding of any kind.
+
+This is unchanged since [the feature was introduced in Mbed Crypto 0.1.0](#nonvolatile-random-seed-file-format-for-0.1.0).
 
 ### File namespace on a PSA platform for 1.0.0
 
@@ -167,7 +173,21 @@
 Released in early June 2019. <br>
 Integrated in Mbed OS 5.13.
 
-Identical to [1.0.0](#mbed-crypto-1.0.0) except for some changes in the key file format.
+Changes since [1.0.0](#mbed-crypto-1.0.0):
+
+* The stdio backend for storage has been replaced by an implementation of [PSA ITS over stdio](#file-namespace-on-stdio-for-1.1.0).
+* [Some changes in the key file format](#key-file-format-for-1.1.0).
+
+### File namespace on stdio for 1.1.0
+
+Assumption: C stdio, allowing names containing lowercase letters, digits and underscores, of length up to 23.
+
+An undocumented build-time configuration value `PSA_ITS_STORAGE_PREFIX` allows storing the key files in a directory other than the current directory. This value is simply prepended to the file name (so it must end with a directory separator to put the keys in a different directory).
+
+* `PSA_ITS_STORAGE_PREFIX "tempfile.psa_its"`: used as a temporary file. Must be writable. May be overwritten or deleted if present.
+* `sprintf(PSA_ITS_STORAGE_PREFIX "%016llx.psa_its", key_id)`: a key or non-key file. The `key_id` in the name is the 64-bit file identifier, which is the [key identifier](#key-names-for-mbed-tls-2.25.0) for a key file or some reserved identifier for a non-key file (currently: only the [nonvolatile random seed](#nonvolatile-random-seed-file-format-for-1.0.0)). The contents of the file are:
+    * Magic header (8 bytes): `"PSA\0ITS\0"`
+    * File contents.
 
 ### Key file format for 1.1.0
 
@@ -314,3 +334,134 @@
     * For an opaque key (unified driver interface): driver-specific opaque key blob.
     * For an opaque key (key in a secure element): slot number (8 bytes), in platform endianness.
 * Any trailing data is rejected on load.
+
+Mbed TLS 2.25.0
+---------------
+
+Tags: `mbedtls-2.25.0`, `mbedtls-2.26.0`, `mbedtls-2.27.0`, `mbedtls-2.28.0` (continued in early 3.x releases)
+
+First released in December 2020.
+
+Note: this is the first version that is officially supported. The version number is still 0.
+
+Backward compatibility commitments: we promise backward compatibility for stored keys when Mbed TLS is upgraded from x to y if x >= 2.25 and y < 4. See [`BRANCHES.md`](../../BRANCHES.md) for more details.
+
+Supported integrations:
+
+* [PSA platform](#file-namespace-on-a-psa-platform-on-mbed-tls-2.25.0)
+* [library using PSA ITS](#file-namespace-on-its-as-a-library-on-mbed-tls-2.25.0)
+* [library using C stdio](#file-namespace-on-stdio-for-mbed-tls-2.25.0)
+
+Supported features:
+
+* [Persistent keys](#key-file-format-for-mbed-tls-2.25.0) designated by a [key identifier and owner](#key-names-for-mbed-tls-2.25.0). Keys can be:
+    * Transparent, stored in the export format.
+    * Opaque, using the unified driver interface with statically registered drivers (`MBEDTLS_PSA_CRYPTO_DRIVERS`). The driver determines the content of the opaque key blob.
+    * Opaque, using the deprecated secure element interface with dynamically registered drivers (`MBEDTLS_PSA_CRYPTO_SE_C`). The driver picks a slot number which is stored in the place of the key material.
+* [Nonvolatile random seed](#nonvolatile-random-seed-file-format-for-mbed-tls-2.25.0) on ITS only.
+
+### Changes introduced in Mbed TLS 2.25.0
+
+* The numerical encodings of `psa_key_type_t`, `psa_key_usage_t` and `psa_algorithm_t` have changed.
+
+### File namespace on a PSA platform on Mbed TLS 2.25.0
+
+Assumption: ITS provides a 64-bit file identifier namespace. The Crypto service can use arbitrary file identifiers and no other part of the system accesses the same file identifier namespace.
+
+Assumption: the owner identifier is a nonzero value of type `int32_t`.
+
+* Files 0 through 0xfffeffff: unused.
+* Files 0xffff0000 through 0xffffffff: reserved for internal use of the crypto library or crypto service. See [non-key files](#non-key-files-on-mbed-tls-2.25.0).
+* Files 0x100000000 through 0xffffffffffff: [content](#key-file-format-for-mbed-tls-2.25.0) of the [key whose identifier is the file identifier](#key-names-for-mbed-tls-2.25.0). The upper 32 bits determine the owner.
+
+### File namespace on ITS as a library on Mbed TLS 2.25.0
+
+Assumption: ITS provides a 64-bit file identifier namespace. The entity using the crypto library can use arbitrary file identifiers and no other part of the system accesses the same file identifier namespace.
+
+This is a library integration, so there is no owner. The key file identifier is identical to the key identifier.
+
+* File 0: unused.
+* Files 1 through 0xfffeffff: [content](#key-file-format-for-mbed-tls-2.25.0) of the [key whose identifier is the file identifier](#key-names-for-mbed-tls-2.25.0).
+* Files 0xffff0000 through 0xffffffff: reserved for internal use of the crypto library or crypto service. See [non-key files](#non-key-files-on-mbed-tls-2.25.0).
+* Files 0x100000000 through 0xffffffffffffffff: unused.
+
+### File namespace on stdio for Mbed TLS 2.25.0
+
+Assumption: C stdio, allowing names containing lowercase letters, digits and underscores, of length up to 23.
+
+An undocumented build-time configuration value `PSA_ITS_STORAGE_PREFIX` allows storing the key files in a directory other than the current directory. This value is simply prepended to the file name (so it must end with a directory separator to put the keys in a different directory).
+
+* `PSA_ITS_STORAGE_PREFIX "tempfile.psa_its"`: used as a temporary file. Must be writable. May be overwritten or deleted if present.
+* `sprintf(PSA_ITS_STORAGE_PREFIX "%016llx.psa_its", key_id)`: a key or non-key file. The `key_id` in the name is the 64-bit file identifier, which is the [key identifier](#key-names-for-mbed-tls-2.25.0) for a key file or some reserved identifier for a [non-key file](#non-key-files-on-mbed-tls-2.25.0). The contents of the file are:
+    * Magic header (8 bytes): `"PSA\0ITS\0"`
+    * File contents.
+
+### Key names for Mbed TLS 2.25.0
+
+Information about each key is stored in a dedicated file designated by the key identifier. In integrations where there is no concept of key owner (in particular, in library integrations), the key identifier is exactly the key identifier as defined in the PSA Cryptography API specification (`psa_key_id_t`). In integrations where there is a concept of key owner (integration into a service for example), the key identifier is made of an owner identifier (its semantics and type are integration specific) and of the key identifier (`psa_key_id_t`) from the key owner point of view.
+
+The way in which the file name is constructed from the key identifier depends on the storage backend. The content of the file is described [below](#key-file-format-for-mbed-tls-2.25.0).
+
+* Library integration: the key file name is just the key identifier as defined in the PSA crypto specification. This is a 32-bit value which must be in the range 0x00000001..0x3fffffff (`PSA_KEY_ID_USER_MIN`..`PSA_KEY_ID_USER_MAX`).
+* PSA service integration: the key file name is `(uint64_t)owner_uid << 32 | key_id` where `key_id` is the key identifier from the owner point of view and `owner_uid` (of type `int32_t`) is the calling partition identifier provided to the server by the partition manager. This is a 64-bit value.
+
+### Key file format for Mbed TLS 2.25.0
+
+All integers are encoded in little-endian order in 8-bit bytes except where otherwise indicated.
+
+The layout of a key file is:
+
+* magic (8 bytes): `"PSA\0KEY\0"`.
+* version (4 bytes): 0.
+* lifetime (4 bytes): `psa_key_lifetime_t` value.
+* type (2 bytes): `psa_key_type_t` value.
+* bits (2 bytes): `psa_key_bits_t` value.
+* policy usage flags (4 bytes): `psa_key_usage_t` value.
+* policy usage algorithm (4 bytes): `psa_algorithm_t` value.
+* policy enrollment algorithm (4 bytes): `psa_algorithm_t` value.
+* key material length (4 bytes).
+* key material:
+    * For a transparent key: output of `psa_export_key`.
+    * For an opaque key (unified driver interface): driver-specific opaque key blob.
+    * For an opaque key (key in a dynamic secure element): slot number (8 bytes), in platform endianness.
+* Any trailing data is rejected on load.
+
+### Non-key files on Mbed TLS 2.25.0
+
+File identifiers that are outside the range of persistent key identifiers are reserved for internal use by the library. The only identifiers currently in use have the owner id (top 32 bits) set to 0.
+
+* Files 0xfffffe02 through 0xfffffeff (`PSA_CRYPTO_SE_DRIVER_ITS_UID_BASE + lifetime`): dynamic secure element driver storage. The content of the file is the secure element driver's persistent data.
+* File 0xffffff52 (`PSA_CRYPTO_ITS_RANDOM_SEED_UID`): [nonvolatile random seed](#nonvolatile-random-seed-file-format-for-mbed-tls-2.25.0).
+* File 0xffffff54 (`PSA_CRYPTO_ITS_TRANSACTION_UID`): [transaction file](#transaction-file-format-for-mbed-tls-2.25.0).
+* Other files are unused and reserved for future use.
+
+### Nonvolatile random seed file format for Mbed TLS 2.25.0
+
+[Identical to Mbed Crypto 0.1.0](#nonvolatile-random-seed-file-format-for-0.1.0).
+
+### Transaction file format for Mbed TLS 2.25.0
+
+The transaction file contains data about an ongoing action that cannot be completed atomically. It exists only if there is an ongoing transaction.
+
+All integers are encoded in platform endianness.
+
+All currently existing transactions concern a key in a dynamic secure element.
+
+The layout of a transaction file is:
+
+* type (2 bytes): the [transaction type](#transaction-types-on-mbed-tls-2.25.0).
+* unused (2 bytes)
+* lifetime (4 bytes): `psa_key_lifetime_t` value that corresponds to a key in a secure element.
+* slot number (8 bytes): `psa_key_slot_number_t` value. This is the unique designation of the key for the secure element driver.
+* key identifier (4 bytes in a library integration, 8 bytes on a PSA platform): the internal representation of the key identifier. On a PSA platform, this encodes the key owner in the same way as [in file identifiers for key files](#file-namespace-on-a-psa-platform-on-mbed-tls-2.25.0)).
+
+#### Transaction types on Mbed TLS 2.25.0
+
+* 0x0001: key creation. The following locations may or may not contain data about the key that is being created:
+    * The slot in the secure element designated by the slot number.
+    * The file containing the key metadata designated by the key identifier.
+    * The driver persistent data.
+* 0x0002: key destruction. The following locations may or may not still contain data about the key that is being destroyed:
+    * The slot in the secure element designated by the slot number.
+    * The file containing the key metadata designated by the key identifier.
+    * The driver persistent data.
diff --git a/third_party/mbedtls/repo/docs/architecture/psa-crypto-implementation-structure.md b/third_party/mbedtls/repo/docs/architecture/psa-crypto-implementation-structure.md
index cd4d427..6a0a095 100644
--- a/third_party/mbedtls/repo/docs/architecture/psa-crypto-implementation-structure.md
+++ b/third_party/mbedtls/repo/docs/architecture/psa-crypto-implementation-structure.md
@@ -1,4 +1,4 @@
-PSA Cryptograpy API implementation and PSA driver interface
+PSA Cryptography API implementation and PSA driver interface
 ===========================================================
 
 ## Introduction
diff --git a/third_party/mbedtls/repo/docs/architecture/testing/driver-interface-test-strategy.md b/third_party/mbedtls/repo/docs/architecture/testing/driver-interface-test-strategy.md
index 086fc1a..380fd39 100644
--- a/third_party/mbedtls/repo/docs/architecture/testing/driver-interface-test-strategy.md
+++ b/third_party/mbedtls/repo/docs/architecture/testing/driver-interface-test-strategy.md
@@ -1,6 +1,6 @@
-# Mbed Crypto driver interface test strategy
+# Mbed TLS driver interface test strategy
 
-This document describes the test strategy for the driver interfaces in Mbed Crypto. Mbed Crypto has interfaces for secure element drivers, accelerator drivers and entropy drivers. This document is about testing Mbed Crypto itself; testing drivers is out of scope.
+This document describes the test strategy for the driver interfaces in Mbed TLS. Mbed TLS has interfaces for secure element drivers, accelerator drivers and entropy drivers. This document is about testing Mbed TLS itself; testing drivers is out of scope.
 
 The driver interfaces are standardized through PSA Cryptography functional specifications.
 
@@ -16,9 +16,9 @@
 
 #### Dynamic secure element driver interface
 
-The dynamic secure element driver interface (SE interface for short) is defined by [`psa/crypto_se_driver.h`](../../../include/psa/crypto_se_driver.h). This is an interface between Mbed Crypto and one or more third-party drivers.
+The dynamic secure element driver interface (SE interface for short) is defined by [`psa/crypto_se_driver.h`](../../../include/psa/crypto_se_driver.h). This is an interface between Mbed TLS and one or more third-party drivers.
 
-The SE interface consists of one function provided by Mbed Crypto (`psa_register_se_driver`) and many functions that drivers must implement. To make a driver usable by Mbed Crypto, the initialization code must call `psa_register_se_driver` with a structure that describes the driver. The structure mostly contains function pointers, pointing to the driver's methods. All calls to a driver function are triggered by a call to a PSA crypto API function.
+The SE interface consists of one function provided by Mbed TLS (`psa_register_se_driver`) and many functions that drivers must implement. To make a driver usable by Mbed TLS, the initialization code must call `psa_register_se_driver` with a structure that describes the driver. The structure mostly contains function pointers, pointing to the driver's methods. All calls to a driver function are triggered by a call to a PSA crypto API function.
 
 ### SE driver interface unit tests
 
@@ -57,7 +57,7 @@
 
 #### SE driver outputs
 
-For each API function that leads to a driver call, call it with parameters that cause a driver to be invoked and check how Mbed Crypto handles the outputs.
+For each API function that leads to a driver call, call it with parameters that cause a driver to be invoked and check how Mbed TLS handles the outputs.
 
 * Correct outputs.
 * Incorrect outputs such as an invalid output length.
@@ -87,7 +87,7 @@
 * This must be done for each possible flow, including error cases (e.g. a key creation that fails midway due to `OUT_OF_MEMORY`).
 * The recovery during `psa_crypto_init` can itself be interrupted. Test those interruptions too.
 * Two things need to be tested: the key that is being created or destroyed, and the driver's persistent storage.
-* Check both that the storage has the expected content (this can be done by e.g. using a key that is supposed to be present) and does not have any unexpected content (for keys, this can be done by checking that `psa_open_key` fails with `PSA_ERRROR_DOES_NOT_EXIST`).
+* Check both that the storage has the expected content (this can be done by e.g. using a key that is supposed to be present) and does not have any unexpected content (for keys, this can be done by checking that `psa_open_key` fails with `PSA_ERROR_DOES_NOT_EXIST`).
 
 This requires instrumenting the storage implementation, either to force it to fail at each point or to record successive storage states and replay each of them. Each `psa_its_xxx` function call is assumed to be atomic.
 
diff --git a/third_party/mbedtls/repo/docs/architecture/testing/psa-storage-format-testing.md b/third_party/mbedtls/repo/docs/architecture/testing/psa-storage-format-testing.md
index e293985..a5c1fd1 100644
--- a/third_party/mbedtls/repo/docs/architecture/testing/psa-storage-format-testing.md
+++ b/third_party/mbedtls/repo/docs/architecture/testing/psa-storage-format-testing.md
@@ -34,20 +34,20 @@
 
 Test cases should normally not be removed from the code base: if something has worked before, it should keep working in future versions, so we should keep testing it.
 
-This cannot be enforced solely by looking at a single version of Mbed TLS, since there would be no indication that more test cases used to exist. It can only be enforced through review of library changes. The review may be assisted by a tool that compares the old and the new version, in the same way that `abi-check.py` compares the library's API and ABI.
+This cannot be enforced solely by looking at a single version of Mbed TLS, since there would be no indication that more test cases used to exist. It can only be enforced through review of library changes. The review is be assisted by a tool that compares the old and the new version, which is implemented in `scripts/abi_check.py`. This tool fails the CI if load-and-check test case disappears (changed test cases are raised as false positives).
 
 If the way certain keys are stored changes, and we don't deliberately decide to stop supporting old keys (which should only be done by retiring a version of the storage format), then we should keep the corresponding test cases in load-only mode: create a file with the expected content, load it and check the data that it contains.
 
 ## Storage architecture overview
 
-The PSA subsystem provides storage on top of the PSA trusted storage interface. The state of the storage is a mapping from file identifer (a 64-bit number) to file content (a byte array). These files include:
+The PSA subsystem provides storage on top of the PSA trusted storage interface. The state of the storage is a mapping from file identifier (a 64-bit number) to file content (a byte array). These files include:
 
 * [Key files](#key-storage) (files containing one key's metadata and, except for some secure element keys, key material).
 * The [random generator injected seed or state file](#random-generator-state) (`PSA_CRYPTO_ITS_RANDOM_SEED_UID`).
 * [Storage transaction file](#storage-transaction-resumption).
 * [Driver state files](#driver-state-files).
 
-For a more detailed description, refer to the [Mbed Crypto storage specification](../mbed-crypto-storage-specification.md).
+For a more detailed description, refer to the [Mbed TLS storage specification](../mbed-crypto-storage-specification.md).
 
 In addition, Mbed TLS includes an implementation of the PSA trusted storage interface on top of C stdio. This document addresses the test strategy for [PSA ITS over file](#psa-its-over-file) in a separate section below.
 
diff --git a/third_party/mbedtls/repo/docs/architecture/testing/test-framework.md b/third_party/mbedtls/repo/docs/architecture/testing/test-framework.md
index c4178fa..8baa390 100644
--- a/third_party/mbedtls/repo/docs/architecture/testing/test-framework.md
+++ b/third_party/mbedtls/repo/docs/architecture/testing/test-framework.md
@@ -6,7 +6,7 @@
 
 ## Unit tests
 
-See <https://tls.mbed.org/kb/development/test_suites>
+See <https://mbed-tls.readthedocs.io/en/latest/kb/development/test_suites>
 
 ### Unit test descriptions
 
@@ -30,17 +30,23 @@
 
 #### SSL test case descriptions
 
-Each test case in `ssl-opt.sh` has a description which succinctly describes for a human audience what the test does. The test description is the first parameter to `run_tests`.
+Each test case in `ssl-opt.sh` has a description which succinctly describes for a human audience what the test does. The test description is the first parameter to `run_test`.
 
 The same rules and guidelines apply as for [unit test descriptions](#unit-test-descriptions). In addition, the description must be written on the same line as `run_test`, in double quotes, for the sake of `check_test_cases.py`.
 
+### SSL cipher suite tests
+
+Each test case in `compat.sh` has a description which succinctly describes for a human audience what the test does. The test description is `$TITLE` defined in `run_client`.
+
+The same rules and guidelines apply as for [unit test descriptions](#unit-test-descriptions). In addition, failure cause in `compat.sh` is not classified as `ssl-opt.sh`, so the information of failed log files are followed as prompt.
+
 ## Running tests
 
 ### Outcome file
 
 #### Generating an outcome file
 
-Unit tests and `ssl-opt.sh` record the outcome of each test case in a **test outcome file**. This feature is enabled if the environment variable `MBEDTLS_TEST_OUTCOME_FILE` is set. Set it to the path of the desired file.
+Unit tests, `ssl-opt.sh` and `compat.sh` record the outcome of each test case in a **test outcome file**. This feature is enabled if the environment variable `MBEDTLS_TEST_OUTCOME_FILE` is set. Set it to the path of the desired file.
 
 If you run `all.sh --outcome-file test-outcome.csv`, this collects the outcome of all the test cases in `test-outcome.csv`.
 
@@ -52,7 +58,7 @@
 
 * **Platform**: a description of the platform, e.g. `Linux-x86_64` or `Linux-x86_64-gcc7-msan`.
 * **Configuration**: a unique description of the configuration (`config.h`).
-* **Test suite**: `test_suite_xxx` or `ssl-opt`.
+* **Test suite**: `test_suite_xxx`, `ssl-opt` or `compat`.
 * **Test case**: the description of the test case.
 * **Result**: one of `PASS`, `SKIP` or `FAIL`.
 * **Cause**: more information explaining the result.
diff --git a/third_party/mbedtls/repo/docs/conf.py b/third_party/mbedtls/repo/docs/conf.py
new file mode 100644
index 0000000..41c50c7
--- /dev/null
+++ b/third_party/mbedtls/repo/docs/conf.py
@@ -0,0 +1,34 @@
+# Configuration file for the Sphinx documentation builder.
+#
+# For the full list of built-in configuration values, see the documentation:
+# https://www.sphinx-doc.org/en/master/usage/configuration.html
+
+# -- Project information -----------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
+import glob
+
+project = 'Mbed TLS Versioned'
+copyright = '2023, Mbed TLS Contributors'
+author = 'Mbed TLS Contributors'
+
+# -- General configuration ---------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
+
+extensions = ['breathe', 'sphinx.ext.graphviz']
+
+templates_path = ['_templates']
+exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
+
+breathe_projects = {
+    'mbedtls-versioned': '../apidoc/xml'
+}
+breathe_default_project = 'mbedtls-versioned'
+
+primary_domain = 'c'
+highlight_language = 'c'
+
+# -- Options for HTML output -------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
+
+html_theme = 'sphinx_rtd_theme'
+html_static_path = ['_static']
diff --git a/third_party/mbedtls/repo/docs/getting_started.md b/third_party/mbedtls/repo/docs/getting_started.md
deleted file mode 100644
index fdbf0e9..0000000
--- a/third_party/mbedtls/repo/docs/getting_started.md
+++ /dev/null
@@ -1,894 +0,0 @@
-## Getting started with Mbed Crypto
-
-### What is Mbed Crypto?
-
-Mbed Crypto is an open source cryptographic library that supports a wide range of cryptographic operations, including:
-* Key management
-* Hashing
-* Symmetric cryptography
-* Asymmetric cryptography
-* Message authentication (MAC)
-* Key generation and derivation
-* Authenticated encryption with associated data (AEAD)
-
-The Mbed Crypto library is a reference implementation of the cryptography interface of the Arm Platform Security Architecture (PSA). It is written in portable C.
-
-The Mbed Crypto library is distributed under the Apache License, version 2.0.
-
-#### Platform Security Architecture (PSA)
-
-Arm's Platform Security Architecture (PSA) is a holistic set of threat models,
-security analyses, hardware and firmware architecture specifications, and an open source firmware reference implementation. PSA provides a recipe, based on industry best practice, that enables you to design security into both hardware and firmware consistently. Part of the API provided by PSA is the cryptography interface, which provides access to a set of primitives.
-
-### Using Mbed Crypto
-
-* [Getting the Mbed Crypto library](#getting-the-mbed-crypto-library)
-* [Building the Mbed Crypto library](#building-the-mbed-crypto-library)
-* [Using the Mbed Crypto library](#using-the-mbed-crypto-library)
-* [Importing a key](#importing-a-key)
-* [Signing a message using RSA](#signing-a-message-using-RSA)
-* [Encrypting or decrypting using symmetric ciphers](#encrypting-or-decrypting-using-symmetric-ciphers)
-* [Hashing a message](#hashing-a-message)
-* [Deriving a new key from an existing key](#deriving-a-new-key-from-an-existing-key)
-* [Generating a random value](#generating-a-random-value)
-* [Authenticating and encrypting or decrypting a message](#authenticating-and-encrypting-or-decrypting-a-message)
-* [Generating and exporting keys](#generating-and-exporting-keys)
-* [More about the Mbed Crypto library](#more-about-the-mbed-crypto-library)
-
-### Getting the Mbed Crypto library
-
-Mbed Crypto releases are available in the [public GitHub repository](https://github.com/ARMmbed/mbed-crypto).
-
-### Building the Mbed Crypto library
-
-**Prerequisites to building the library with the provided makefiles:**
-* GNU Make.
-* A C toolchain (compiler, linker, archiver).
-* Python 2 or Python 3 (either works) to generate the test code.
-* Perl to run the tests.
-
-If you have a C compiler such as GCC or Clang, just run `make` in the top-level directory to build the library, a set of unit tests and some sample programs.
-
-To select a different compiler, set the `CC` variable to the name or path of the compiler and linker (default: `cc`) and set `AR` to a compatible archiver (default: `ar`); for example:
-```
-make CC=arm-linux-gnueabi-gcc AR=arm-linux-gnueabi-ar
-```
-The provided makefiles pass options to the compiler that assume a GCC-like command line syntax. To use a different compiler, you may need to pass different values for `CFLAGS`, `WARNINGS_CFLAGS` and `LDFLAGS`.
-
-To run the unit tests on the host machine, run `make test` from the top-level directory. If you are cross-compiling, copy the test executable from the `tests` directory to the target machine.
-
-### Using the Mbed Crypto library
-
-To use the Mbed Crypto APIs, call `psa_crypto_init()` before calling any other API. This initializes the library.
-
-### Importing a key
-
-To use a key for cryptography operations in Mbed Crypto, you need to first
-import it. The import operation returns the identifier of the key for use
-with other function calls.
-
-**Prerequisites to importing keys:**
-* Initialize the library with a successful call to `psa_crypto_init()`.
-
-This example shows how to import a key:
-```C
-void import_a_key(const uint8_t *key, size_t key_len)
-{
-    psa_status_t status;
-    psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
-    psa_key_id_t key_id;
-
-    printf("Import an AES key...\t");
-    fflush(stdout);
-
-    /* Initialize PSA Crypto */
-    status = psa_crypto_init();
-    if (status != PSA_SUCCESS) {
-        printf("Failed to initialize PSA Crypto\n");
-        return;
-    }
-
-    /* Set key attributes */
-    psa_set_key_usage_flags(&attributes, 0);
-    psa_set_key_algorithm(&attributes, 0);
-    psa_set_key_type(&attributes, PSA_KEY_TYPE_AES);
-    psa_set_key_bits(&attributes, 128);
-
-    /* Import the key */
-    status = psa_import_key(&attributes, key, key_len, &key_id);
-    if (status != PSA_SUCCESS) {
-        printf("Failed to import key\n");
-        return;
-    }
-    printf("Imported a key\n");
-
-    /* Free the attributes */
-    psa_reset_key_attributes(&attributes);
-
-    /* Destroy the key */
-    psa_destroy_key(key_id);
-
-    mbedtls_psa_crypto_free();
-}
-```
-
-### Signing a message using RSA
-
-Mbed Crypto supports encrypting, decrypting, signing and verifying messages using public key signature algorithms, such as RSA or ECDSA.
-
-**Prerequisites to performing asymmetric signature operations:**
-* Initialize the library with a successful call to `psa_crypto_init()`.
-* Have a valid key with appropriate attributes set:
-    * Usage flag `PSA_KEY_USAGE_SIGN_HASH` to allow signing.
-    * Usage flag `PSA_KEY_USAGE_VERIFY_HASH` to allow signature verification.
-    * Algorithm set to the desired signature algorithm.
-
-This example shows how to sign a hash that has already been calculated:
-```C
-void sign_a_message_using_rsa(const uint8_t *key, size_t key_len)
-{
-    psa_status_t status;
-    psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
-    uint8_t hash[32] = {0x50, 0xd8, 0x58, 0xe0, 0x98, 0x5e, 0xcc, 0x7f,
-                        0x60, 0x41, 0x8a, 0xaf, 0x0c, 0xc5, 0xab, 0x58,
-                        0x7f, 0x42, 0xc2, 0x57, 0x0a, 0x88, 0x40, 0x95,
-                        0xa9, 0xe8, 0xcc, 0xac, 0xd0, 0xf6, 0x54, 0x5c};
-    uint8_t signature[PSA_SIGNATURE_MAX_SIZE] = {0};
-    size_t signature_length;
-    psa_key_id_t key_id;
-
-    printf("Sign a message...\t");
-    fflush(stdout);
-
-    /* Initialize PSA Crypto */
-    status = psa_crypto_init();
-    if (status != PSA_SUCCESS) {
-        printf("Failed to initialize PSA Crypto\n");
-        return;
-    }
-
-    /* Set key attributes */
-    psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_SIGN_HASH);
-    psa_set_key_algorithm(&attributes, PSA_ALG_RSA_PKCS1V15_SIGN_RAW);
-    psa_set_key_type(&attributes, PSA_KEY_TYPE_RSA_KEY_PAIR);
-    psa_set_key_bits(&attributes, 1024);
-
-    /* Import the key */
-    status = psa_import_key(&attributes, key, key_len, &key_id);
-    if (status != PSA_SUCCESS) {
-        printf("Failed to import key\n");
-        return;
-    }
-
-    /* Sign message using the key */
-    status = psa_sign_hash(key_id, PSA_ALG_RSA_PKCS1V15_SIGN_RAW,
-                           hash, sizeof(hash),
-                           signature, sizeof(signature),
-                           &signature_length);
-    if (status != PSA_SUCCESS) {
-        printf("Failed to sign\n");
-        return;
-    }
-
-    printf("Signed a message\n");
-
-    /* Free the attributes */
-    psa_reset_key_attributes(&attributes);
-
-    /* Destroy the key */
-    psa_destroy_key(key_id);
-
-    mbedtls_psa_crypto_free();
-}
-```
-
-### Using symmetric ciphers
-
-Mbed Crypto supports encrypting and decrypting messages using various symmetric cipher algorithms (both block and stream ciphers).
-
-**Prerequisites to working with the symmetric cipher API:**
-* Initialize the library with a successful call to `psa_crypto_init()`.
-* Have a symmetric key. This key's usage flags must include `PSA_KEY_USAGE_ENCRYPT` to allow encryption or `PSA_KEY_USAGE_DECRYPT` to allow decryption.
-
-**To encrypt a message with a symmetric cipher:**
-1. Allocate an operation (`psa_cipher_operation_t`) structure to pass to the cipher functions.
-1. Initialize the operation structure to zero or to `PSA_CIPHER_OPERATION_INIT`.
-1. Call `psa_cipher_encrypt_setup()` to specify the algorithm and the key to be used.
-1. Call either `psa_cipher_generate_iv()` or `psa_cipher_set_iv()` to generate or set the initialization vector (IV). We recommend calling `psa_cipher_generate_iv()`, unless you require a specific IV value.
-1. Call `psa_cipher_update()` with the message to encrypt. You may call this function multiple times, passing successive fragments of the message on successive calls.
-1. Call `psa_cipher_finish()` to end the operation and output the encrypted message.
-
-This example shows how to encrypt data using an AES (Advanced Encryption Standard) key in CBC (Cipher Block Chaining) mode with no padding (assuming all prerequisites have been fulfilled):
-```c
-void encrypt_with_symmetric_ciphers(const uint8_t *key, size_t key_len)
-{
-    enum {
-        block_size = PSA_BLOCK_CIPHER_BLOCK_LENGTH(PSA_KEY_TYPE_AES),
-    };
-    psa_status_t status;
-    psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
-    psa_algorithm_t alg = PSA_ALG_CBC_NO_PADDING;
-    uint8_t plaintext[block_size] = SOME_PLAINTEXT;
-    uint8_t iv[block_size];
-    size_t iv_len;
-    uint8_t output[block_size];
-    size_t output_len;
-    psa_key_id_t key_id;
-    psa_cipher_operation_t operation = PSA_CIPHER_OPERATION_INIT;
-
-    printf("Encrypt with cipher...\t");
-    fflush(stdout);
-
-    /* Initialize PSA Crypto */
-    status = psa_crypto_init();
-    if (status != PSA_SUCCESS)
-    {
-        printf("Failed to initialize PSA Crypto\n");
-        return;
-    }
-
-    /* Import a key */
-    psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_ENCRYPT);
-    psa_set_key_algorithm(&attributes, alg);
-    psa_set_key_type(&attributes, PSA_KEY_TYPE_AES);
-    psa_set_key_bits(&attributes, 128);
-    status = psa_import_key(&attributes, key, key_len, &key_id);
-    if (status != PSA_SUCCESS) {
-        printf("Failed to import a key\n");
-        return;
-    }
-    psa_reset_key_attributes(&attributes);
-
-    /* Encrypt the plaintext */
-    status = psa_cipher_encrypt_setup(&operation, key_id, alg);
-    if (status != PSA_SUCCESS) {
-        printf("Failed to begin cipher operation\n");
-        return;
-    }
-    status = psa_cipher_generate_iv(&operation, iv, sizeof(iv), &iv_len);
-    if (status != PSA_SUCCESS) {
-        printf("Failed to generate IV\n");
-        return;
-    }
-    status = psa_cipher_update(&operation, plaintext, sizeof(plaintext),
-                               output, sizeof(output), &output_len);
-    if (status != PSA_SUCCESS) {
-        printf("Failed to update cipher operation\n");
-        return;
-    }
-    status = psa_cipher_finish(&operation, output + output_len,
-                               sizeof(output) - output_len, &output_len);
-    if (status != PSA_SUCCESS) {
-        printf("Failed to finish cipher operation\n");
-        return;
-    }
-    printf("Encrypted plaintext\n");
-
-    /* Clean up cipher operation context */
-    psa_cipher_abort(&operation);
-
-    /* Destroy the key */
-    psa_destroy_key(key_id);
-
-    mbedtls_psa_crypto_free();
-}
-```
-
-**To decrypt a message with a symmetric cipher:**
-1. Allocate an operation (`psa_cipher_operation_t`) structure to pass to the cipher functions.
-1. Initialize the operation structure to zero or to `PSA_CIPHER_OPERATION_INIT`.
-1. Call `psa_cipher_decrypt_setup()` to specify the algorithm and the key to be used.
-1. Call `psa_cipher_set_iv()` with the IV for the decryption.
-1. Call `psa_cipher_update()` with the message to encrypt. You may call this function multiple times, passing successive fragments of the message on successive calls.
-1. Call `psa_cipher_finish()` to end the operation and output the decrypted message.
-
-This example shows how to decrypt encrypted data using an AES key in CBC mode with no padding
-(assuming all prerequisites have been fulfilled):
-```c
-void decrypt_with_symmetric_ciphers(const uint8_t *key, size_t key_len)
-{
-    enum {
-        block_size = PSA_BLOCK_CIPHER_BLOCK_LENGTH(PSA_KEY_TYPE_AES),
-    };
-    psa_status_t status;
-    psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
-    psa_algorithm_t alg = PSA_ALG_CBC_NO_PADDING;
-    psa_cipher_operation_t operation = PSA_CIPHER_OPERATION_INIT;
-    uint8_t ciphertext[block_size] = SOME_CIPHERTEXT;
-    uint8_t iv[block_size] = ENCRYPTED_WITH_IV;
-    uint8_t output[block_size];
-    size_t output_len;
-    psa_key_id_t key_id;
-
-    printf("Decrypt with cipher...\t");
-    fflush(stdout);
-
-    /* Initialize PSA Crypto */
-    status = psa_crypto_init();
-    if (status != PSA_SUCCESS)
-    {
-        printf("Failed to initialize PSA Crypto\n");
-        return;
-    }
-
-    /* Import a key */
-    psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_DECRYPT);
-    psa_set_key_algorithm(&attributes, alg);
-    psa_set_key_type(&attributes, PSA_KEY_TYPE_AES);
-    psa_set_key_bits(&attributes, 128);
-    status = psa_import_key(&attributes, key, key_len, &key_id);
-    if (status != PSA_SUCCESS) {
-        printf("Failed to import a key\n");
-        return;
-    }
-    psa_reset_key_attributes(&attributes);
-
-    /* Decrypt the ciphertext */
-    status = psa_cipher_decrypt_setup(&operation, key_id, alg);
-    if (status != PSA_SUCCESS) {
-        printf("Failed to begin cipher operation\n");
-        return;
-    }
-    status = psa_cipher_set_iv(&operation, iv, sizeof(iv));
-    if (status != PSA_SUCCESS) {
-        printf("Failed to set IV\n");
-        return;
-    }
-    status = psa_cipher_update(&operation, ciphertext, sizeof(ciphertext),
-                               output, sizeof(output), &output_len);
-    if (status != PSA_SUCCESS) {
-        printf("Failed to update cipher operation\n");
-        return;
-    }
-    status = psa_cipher_finish(&operation, output + output_len,
-                               sizeof(output) - output_len, &output_len);
-    if (status != PSA_SUCCESS) {
-        printf("Failed to finish cipher operation\n");
-        return;
-    }
-    printf("Decrypted ciphertext\n");
-
-    /* Clean up cipher operation context */
-    psa_cipher_abort(&operation);
-
-    /* Destroy the key */
-    psa_destroy_key(key_id);
-
-    mbedtls_psa_crypto_free();
-}
-```
-
-#### Handling cipher operation contexts
-
-After you've initialized the operation structure with a successful call to `psa_cipher_encrypt_setup()` or `psa_cipher_decrypt_setup()`, you can terminate the operation at any time by calling `psa_cipher_abort()`.
-
-The call to `psa_cipher_abort()` frees any resources associated with the operation, except for the operation structure itself.
-
-Mbed Crypto implicitly calls `psa_cipher_abort()` when:
-* A call to `psa_cipher_generate_iv()`, `psa_cipher_set_iv()` or `psa_cipher_update()` fails (returning any status other than `PSA_SUCCESS`).
-* A call to `psa_cipher_finish()` succeeds or fails.
-
-After an implicit or explicit call to `psa_cipher_abort()`, the operation structure is invalidated; in other words, you cannot reuse the operation structure for the same operation. You can, however, reuse the operation structure for a different operation by calling either `psa_cipher_encrypt_setup()` or `psa_cipher_decrypt_setup()` again.
-
-You must call `psa_cipher_abort()` at some point for any operation that is initialized successfully (by a successful call to `psa_cipher_encrypt_setup()` or `psa_cipher_decrypt_setup()`).
-
-Making multiple sequential calls to `psa_cipher_abort()` on an operation that is terminated (either implicitly or explicitly) is safe and has no effect.
-
-### Hashing a message
-
-Mbed Crypto lets you compute and verify hashes using various hashing
-algorithms.
-
-**Prerequisites to working with the hash APIs:**
-* Initialize the library with a successful call to `psa_crypto_init()`.
-
-**To calculate a hash:**
-1. Allocate an operation structure (`psa_hash_operation_t`) to pass to the hash functions.
-1. Initialize the operation structure to zero or to `PSA_HASH_OPERATION_INIT`.
-1. Call `psa_hash_setup()` to specify the hash algorithm.
-1. Call `psa_hash_update()` with the message to encrypt. You may call this function multiple times, passing successive fragments of the message on successive calls.
-1. Call `psa_hash_finish()` to calculate the hash, or `psa_hash_verify()` to compare the computed hash with an expected hash value.
-
-This example shows how to calculate the SHA-256 hash of a message:
-```c
-    psa_status_t status;
-    psa_algorithm_t alg = PSA_ALG_SHA_256;
-    psa_hash_operation_t operation = PSA_HASH_OPERATION_INIT;
-    unsigned char input[] = { 'a', 'b', 'c' };
-    unsigned char actual_hash[PSA_HASH_MAX_SIZE];
-    size_t actual_hash_len;
-
-    printf("Hash a message...\t");
-    fflush(stdout);
-
-    /* Initialize PSA Crypto */
-    status = psa_crypto_init();
-    if (status != PSA_SUCCESS) {
-        printf("Failed to initialize PSA Crypto\n");
-        return;
-    }
-
-    /* Compute hash of message  */
-    status = psa_hash_setup(&operation, alg);
-    if (status != PSA_SUCCESS) {
-        printf("Failed to begin hash operation\n");
-        return;
-    }
-    status = psa_hash_update(&operation, input, sizeof(input));
-    if (status != PSA_SUCCESS) {
-        printf("Failed to update hash operation\n");
-        return;
-    }
-    status = psa_hash_finish(&operation, actual_hash, sizeof(actual_hash),
-                             &actual_hash_len);
-    if (status != PSA_SUCCESS) {
-        printf("Failed to finish hash operation\n");
-        return;
-    }
-
-    printf("Hashed a message\n");
-
-    /* Clean up hash operation context */
-    psa_hash_abort(&operation);
-
-    mbedtls_psa_crypto_free();
-```
-
-This example shows how to verify the SHA-256 hash of a message:
-```c
-    psa_status_t status;
-    psa_algorithm_t alg = PSA_ALG_SHA_256;
-    psa_hash_operation_t operation = PSA_HASH_OPERATION_INIT;
-    unsigned char input[] = { 'a', 'b', 'c' };
-    unsigned char expected_hash[] = {
-        0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde,
-        0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c,
-        0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad
-    };
-    size_t expected_hash_len = PSA_HASH_LENGTH(alg);
-
-    printf("Verify a hash...\t");
-    fflush(stdout);
-
-    /* Initialize PSA Crypto */
-    status = psa_crypto_init();
-    if (status != PSA_SUCCESS) {
-        printf("Failed to initialize PSA Crypto\n");
-        return;
-    }
-
-    /* Verify message hash */
-    status = psa_hash_setup(&operation, alg);
-    if (status != PSA_SUCCESS) {
-        printf("Failed to begin hash operation\n");
-        return;
-    }
-    status = psa_hash_update(&operation, input, sizeof(input));
-    if (status != PSA_SUCCESS) {
-        printf("Failed to update hash operation\n");
-        return;
-    }
-    status = psa_hash_verify(&operation, expected_hash, expected_hash_len);
-    if (status != PSA_SUCCESS) {
-        printf("Failed to verify hash\n");
-        return;
-    }
-
-    printf("Verified a hash\n");
-
-    /* Clean up hash operation context */
-    psa_hash_abort(&operation);
-
-    mbedtls_psa_crypto_free();
-```
-
-The API provides the macro `PSA_HASH_LENGTH`, which returns the expected hash length (in bytes) for the specified algorithm.
-
-#### Handling hash operation contexts
-
-After a successful call to `psa_hash_setup()`, you can terminate the operation at any time by calling `psa_hash_abort()`. The call to `psa_hash_abort()` frees any resources associated with the operation, except for the operation structure itself.
-
-Mbed Crypto implicitly calls `psa_hash_abort()` when:
-1. A call to `psa_hash_update()` fails (returning any status other than `PSA_SUCCESS`).
-1. A call to `psa_hash_finish()` succeeds or fails.
-1. A call to `psa_hash_verify()` succeeds or fails.
-
-After an implicit or explicit call to `psa_hash_abort()`, the operation structure is invalidated; in other words, you cannot reuse the operation structure for the same operation. You can, however, reuse the operation structure for a different operation by calling `psa_hash_setup()` again.
-
-You must call `psa_hash_abort()` at some point for any operation that is initialized successfully (by a successful call to `psa_hash_setup()`) .
-
-Making multiple sequential calls to `psa_hash_abort()` on an operation that has already been terminated (either implicitly or explicitly) is safe and has no effect.
-
-### Generating a random value
-
-Mbed Crypto can generate random data.
-
-**Prerequisites to generating random data:**
-* Initialize the library with a successful call to `psa_crypto_init()`.
-
-<span class="notes">**Note:** To generate a random key, use `psa_generate_key()` instead of `psa_generate_random()`.</span>
-
-This example shows how to generate ten bytes of random data by calling `psa_generate_random()`:
-```C
-    psa_status_t status;
-    uint8_t random[10] = { 0 };
-
-    printf("Generate random...\t");
-    fflush(stdout);
-
-    /* Initialize PSA Crypto */
-    status = psa_crypto_init();
-    if (status != PSA_SUCCESS) {
-        printf("Failed to initialize PSA Crypto\n");
-        return;
-    }
-
-    status = psa_generate_random(random, sizeof(random));
-    if (status != PSA_SUCCESS) {
-        printf("Failed to generate a random value\n");
-        return;
-    }
-
-    printf("Generated random data\n");
-
-    /* Clean up */
-    mbedtls_psa_crypto_free();
-```
-
-### Deriving a new key from an existing key
-
-Mbed Crypto provides a key derivation API that lets you derive new keys from
-existing ones. The key derivation API has functions to take inputs, including
-other keys and data, and functions to generate outputs, such as new keys or
-other data.
-
-You must first initialize and set up a key derivation context,
-provided with a key and, optionally, other data. Then, use the key derivation context to either read derived data to a buffer or send derived data directly to a key slot.
-
-See the documentation for the particular algorithm (such as HKDF or the TLS1.2 PRF) for
-information about which inputs to pass when, and when you can obtain which outputs.
-
-**Prerequisites to working with the key derivation APIs:**
-* Initialize the library with a successful call to `psa_crypto_init()`.
-* Use a key with the appropriate attributes set:
-    * Usage flags set for key derivation (`PSA_KEY_USAGE_DERIVE`)
-    * Key type set to `PSA_KEY_TYPE_DERIVE`.
-    * Algorithm set to a key derivation algorithm
-      (for example, `PSA_ALG_HKDF(PSA_ALG_SHA_256)`).
-
-**To derive a new AES-CTR 128-bit encryption key into a given key slot using HKDF
-with a given key, salt and info:**
-
-1. Set up the key derivation context using the `psa_key_derivation_setup()`
-function, specifying the derivation algorithm `PSA_ALG_HKDF(PSA_ALG_SHA_256)`.
-1. Provide an optional salt with `psa_key_derivation_input_bytes()`.
-1. Provide info with `psa_key_derivation_input_bytes()`.
-1. Provide a secret with `psa_key_derivation_input_key()`, referencing a key that
-   can be used for key derivation.
-1. Set the key attributes desired for the new derived key. We'll set
-   the `PSA_KEY_USAGE_ENCRYPT` usage flag and the `PSA_ALG_CTR` algorithm for this
-   example.
-1. Derive the key by calling `psa_key_derivation_output_key()`.
-1. Clean up the key derivation context.
-
-At this point, the derived key slot holds a new 128-bit AES-CTR encryption key
-derived from the key, salt and info provided:
-```C
-    psa_status_t status;
-    psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
-    static const unsigned char key[] = {
-        0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
-        0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
-        0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
-        0x0b };
-    static const unsigned char salt[] = {
-        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
-        0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c };
-    static const unsigned char info[] = {
-        0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6,
-        0xf7, 0xf8, 0xf9 };
-    psa_algorithm_t alg = PSA_ALG_HKDF(PSA_ALG_SHA_256);
-    psa_key_derivation_operation_t operation =
-        PSA_KEY_DERIVATION_OPERATION_INIT;
-    size_t derived_bits = 128;
-    size_t capacity = PSA_BITS_TO_BYTES(derived_bits);
-    psa_key_id_t base_key;
-    psa_key_id_t derived_key;
-
-    printf("Derive a key (HKDF)...\t");
-    fflush(stdout);
-
-    /* Initialize PSA Crypto */
-    status = psa_crypto_init();
-    if (status != PSA_SUCCESS) {
-        printf("Failed to initialize PSA Crypto\n");
-        return;
-    }
-
-    /* Import a key for use in key derivation. If such a key has already been
-     * generated or imported, you can skip this part. */
-    psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_DERIVE);
-    psa_set_key_algorithm(&attributes, alg);
-    psa_set_key_type(&attributes, PSA_KEY_TYPE_DERIVE);
-    status = psa_import_key(&attributes, key, sizeof(key), &base_key);
-    if (status != PSA_SUCCESS) {
-        printf("Failed to import a key\n");
-        return;
-    }
-    psa_reset_key_attributes(&attributes);
-
-    /* Derive a key */
-    status = psa_key_derivation_setup(&operation, alg);
-    if (status != PSA_SUCCESS) {
-        printf("Failed to begin key derivation\n");
-        return;
-    }
-    status = psa_key_derivation_set_capacity(&operation, capacity);
-    if (status != PSA_SUCCESS) {
-        printf("Failed to set capacity\n");
-        return;
-    }
-    status = psa_key_derivation_input_bytes(&operation,
-                                            PSA_KEY_DERIVATION_INPUT_SALT,
-                                            salt, sizeof(salt));
-    if (status != PSA_SUCCESS) {
-        printf("Failed to input salt (extract)\n");
-        return;
-    }
-    status = psa_key_derivation_input_key(&operation,
-                                          PSA_KEY_DERIVATION_INPUT_SECRET,
-                                          base_key);
-    if (status != PSA_SUCCESS) {
-        printf("Failed to input key (extract)\n");
-        return;
-    }
-    status = psa_key_derivation_input_bytes(&operation,
-                                            PSA_KEY_DERIVATION_INPUT_INFO,
-                                            info, sizeof(info));
-    if (status != PSA_SUCCESS) {
-        printf("Failed to input info (expand)\n");
-        return;
-    }
-    psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_ENCRYPT);
-    psa_set_key_algorithm(&attributes, PSA_ALG_CTR);
-    psa_set_key_type(&attributes, PSA_KEY_TYPE_AES);
-    psa_set_key_bits(&attributes, 128);
-    status = psa_key_derivation_output_key(&attributes, &operation,
-                                           &derived_key);
-    if (status != PSA_SUCCESS) {
-        printf("Failed to derive key\n");
-        return;
-    }
-    psa_reset_key_attributes(&attributes);
-
-    printf("Derived key\n");
-
-    /* Clean up key derivation operation */
-    psa_key_derivation_abort(&operation);
-
-    /* Destroy the keys */
-    psa_destroy_key(derived_key);
-    psa_destroy_key(base_key);
-
-    mbedtls_psa_crypto_free();
-```
-
-### Authenticating and encrypting or decrypting a message
-
-Mbed Crypto provides a simple way to authenticate and encrypt with associated data (AEAD), supporting the `PSA_ALG_CCM` algorithm.
-
-**Prerequisites to working with the AEAD cipher APIs:**
-* Initialize the library with a successful call to `psa_crypto_init()`.
-* The key attributes for the key used for derivation must have the `PSA_KEY_USAGE_ENCRYPT` or `PSA_KEY_USAGE_DECRYPT` usage flags.
-
-This example shows how to authenticate and encrypt a message:
-```C
-    psa_status_t status;
-    static const uint8_t key[] = {
-        0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
-        0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF };
-    static const uint8_t nonce[] = {
-        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
-        0x08, 0x09, 0x0A, 0x0B };
-    static const uint8_t additional_data[] = {
-        0xEC, 0x46, 0xBB, 0x63, 0xB0, 0x25,
-        0x20, 0xC3, 0x3C, 0x49, 0xFD, 0x70 };
-    static const uint8_t input_data[] = {
-        0xB9, 0x6B, 0x49, 0xE2, 0x1D, 0x62, 0x17, 0x41,
-        0x63, 0x28, 0x75, 0xDB, 0x7F, 0x6C, 0x92, 0x43,
-        0xD2, 0xD7, 0xC2 };
-    uint8_t *output_data = NULL;
-    size_t output_size = 0;
-    size_t output_length = 0;
-    size_t tag_length = 16;
-    psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
-    psa_key_id_t key_id;
-
-    printf("Authenticate encrypt...\t");
-    fflush(stdout);
-
-    /* Initialize PSA Crypto */
-    status = psa_crypto_init();
-    if (status != PSA_SUCCESS) {
-        printf("Failed to initialize PSA Crypto\n");
-        return;
-    }
-
-    output_size = sizeof(input_data) + tag_length;
-    output_data = (uint8_t *)malloc(output_size);
-    if (!output_data) {
-        printf("Out of memory\n");
-        return;
-    }
-
-    /* Import a key */
-    psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_ENCRYPT);
-    psa_set_key_algorithm(&attributes, PSA_ALG_CCM);
-    psa_set_key_type(&attributes, PSA_KEY_TYPE_AES);
-    psa_set_key_bits(&attributes, 128);
-    status = psa_import_key(&attributes, key, sizeof(key), &key_id);
-    psa_reset_key_attributes(&attributes);
-
-    /* Authenticate and encrypt */
-    status = psa_aead_encrypt(key_id, PSA_ALG_CCM,
-                              nonce, sizeof(nonce),
-                              additional_data, sizeof(additional_data),
-                              input_data, sizeof(input_data),
-                              output_data, output_size,
-                              &output_length);
-    if (status != PSA_SUCCESS) {
-        printf("Failed to authenticate and encrypt\n");
-        return;
-    }
-
-    printf("Authenticated and encrypted\n");
-
-    /* Clean up */
-    free(output_data);
-
-    /* Destroy the key */
-    psa_destroy_key(key_id);
-
-    mbedtls_psa_crypto_free();
-```
-
-This example shows how to authenticate and decrypt a message:
-
-```C
-    psa_status_t status;
-    static const uint8_t key_data[] = {
-        0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
-        0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF };
-    static const uint8_t nonce[] = {
-        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
-        0x08, 0x09, 0x0A, 0x0B };
-    static const uint8_t additional_data[] = {
-        0xEC, 0x46, 0xBB, 0x63, 0xB0, 0x25,
-        0x20, 0xC3, 0x3C, 0x49, 0xFD, 0x70 };
-    static const uint8_t input_data[] = {
-        0x20, 0x30, 0xE0, 0x36, 0xED, 0x09, 0xA0, 0x45, 0xAF, 0x3C, 0xBA, 0xEE,
-        0x0F, 0xC8, 0x48, 0xAF, 0xCD, 0x89, 0x54, 0xF4, 0xF6, 0x3F, 0x28, 0x9A,
-        0xA1, 0xDD, 0xB2, 0xB8, 0x09, 0xCD, 0x7C, 0xE1, 0x46, 0xE9, 0x98 };
-    uint8_t *output_data = NULL;
-    size_t output_size = 0;
-    size_t output_length = 0;
-    psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
-    psa_key_id_t key_id;
-
-    printf("Authenticate decrypt...\t");
-    fflush(stdout);
-
-    /* Initialize PSA Crypto */
-    status = psa_crypto_init();
-    if (status != PSA_SUCCESS) {
-        printf("Failed to initialize PSA Crypto\n");
-        return;
-    }
-
-    output_size = sizeof(input_data);
-    output_data = (uint8_t *)malloc(output_size);
-    if (!output_data) {
-        printf("Out of memory\n");
-        return;
-    }
-
-    /* Import a key */
-    psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_DECRYPT);
-    psa_set_key_algorithm(&attributes, PSA_ALG_CCM);
-    psa_set_key_type(&attributes, PSA_KEY_TYPE_AES);
-    psa_set_key_bits(&attributes, 128);
-    status = psa_import_key(&attributes, key_data, sizeof(key_data), &key_id);
-    if (status != PSA_SUCCESS) {
-        printf("Failed to import a key\n");
-        return;
-    }
-    psa_reset_key_attributes(&attributes);
-
-    /* Authenticate and decrypt */
-    status = psa_aead_decrypt(key_id, PSA_ALG_CCM,
-                              nonce, sizeof(nonce),
-                              additional_data, sizeof(additional_data),
-                              input_data, sizeof(input_data),
-                              output_data, output_size,
-                              &output_length);
-    if (status != PSA_SUCCESS) {
-        printf("Failed to authenticate and decrypt %ld\n", status);
-        return;
-    }
-
-    printf("Authenticated and decrypted\n");
-
-    /* Clean up */
-    free(output_data);
-
-    /* Destroy the key */
-    psa_destroy_key(key_id);
-
-    mbedtls_psa_crypto_free();
-```
-
-### Generating and exporting keys
-
-Mbed Crypto provides a simple way to generate a key or key pair.
-
-**Prerequisites to using key generation and export APIs:**
-* Initialize the library with a successful call to `psa_crypto_init()`.
-
-**To generate an ECDSA key:**
-1. Set the desired key attributes for key generation by calling
-   `psa_set_key_algorithm()` with the chosen ECDSA algorithm (such as
-   `PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_256)`). You only want to export the public key, not the key pair (or private key); therefore, do not set `PSA_KEY_USAGE_EXPORT`.
-1. Generate a key by calling `psa_generate_key()`.
-1. Export the generated public key by calling `psa_export_public_key()`:
-```C
-    enum {
-        key_bits = 256,
-    };
-    psa_status_t status;
-    size_t exported_length = 0;
-    static uint8_t exported[PSA_KEY_EXPORT_ECC_PUBLIC_KEY_MAX_SIZE(key_bits)];
-    psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
-    psa_key_id_t key_id;
-
-    printf("Generate a key pair...\t");
-    fflush(stdout);
-
-    /* Initialize PSA Crypto */
-    status = psa_crypto_init();
-    if (status != PSA_SUCCESS) {
-        printf("Failed to initialize PSA Crypto\n");
-        return;
-    }
-
-    /* Generate a key */
-    psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_SIGN_HASH);
-    psa_set_key_algorithm(&attributes,
-                          PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_256));
-    psa_set_key_type(&attributes,
-                     PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1));
-    psa_set_key_bits(&attributes, key_bits);
-    status = psa_generate_key(&attributes, &key_id);
-    if (status != PSA_SUCCESS) {
-        printf("Failed to generate key\n");
-        return;
-    }
-    psa_reset_key_attributes(&attributes);
-
-    status = psa_export_public_key(key_id, exported, sizeof(exported),
-                                   &exported_length);
-    if (status != PSA_SUCCESS) {
-        printf("Failed to export public key %ld\n", status);
-        return;
-    }
-
-    printf("Exported a public key\n");
-
-    /* Destroy the key */
-    psa_destroy_key(key_id);
-
-    mbedtls_psa_crypto_free();
-```
-
-### More about the PSA Crypto API
-
-For more information about the PSA Crypto API, please see the [PSA Cryptography API Specification](https://armmbed.github.io/mbed-crypto/html/index.html).
diff --git a/third_party/mbedtls/repo/docs/index.rst b/third_party/mbedtls/repo/docs/index.rst
new file mode 100644
index 0000000..33a9722
--- /dev/null
+++ b/third_party/mbedtls/repo/docs/index.rst
@@ -0,0 +1,20 @@
+.. Mbed TLS Versioned documentation master file, created by
+   sphinx-quickstart on Thu Feb 23 18:13:44 2023.
+   You can adapt this file completely to your liking, but it should at least
+   contain the root `toctree` directive.
+
+Mbed TLS API documentation
+==========================
+
+.. doxygenpage:: index
+   :project: mbedtls-versioned
+
+.. toctree::
+   :caption: Contents
+   :maxdepth: 1
+
+   Home <self>
+   api/grouplist.rst
+   api/filelist.rst
+   api/structlist.rst
+   api/unionlist.rst
diff --git a/third_party/mbedtls/repo/docs/proposed/psa-driver-interface.md b/third_party/mbedtls/repo/docs/proposed/psa-driver-interface.md
index 23274c7..65601a4 100644
--- a/third_party/mbedtls/repo/docs/proposed/psa-driver-interface.md
+++ b/third_party/mbedtls/repo/docs/proposed/psa-driver-interface.md
@@ -237,7 +237,7 @@
 1. The core calls the `xxx_setup` entry point for this operation family. If this fails, the core destroys the operation context object without calling any other driver entry point on it.
 1. The core calls other entry points that manipulate the operation context object, respecting the constraints.
 1. If any entry point fails, the core calls the driver's `xxx_abort` entry point for this operation family, then destroys the operation context object without calling any other driver entry point on it.
-1. If a “finish” entry point fails, the core destroys the operation context object without calling any other driver entry point on it. The finish entry points are: *prefix*`_mac_sign_finish`, *prefix*`_mac_verify_finish`, *prefix*`_cipher_fnish`, *prefix*`_aead_finish`, *prefix*`_aead_verify`.
+1. If a “finish” entry point fails, the core destroys the operation context object without calling any other driver entry point on it. The finish entry points are: *prefix*`_mac_sign_finish`, *prefix*`_mac_verify_finish`, *prefix*`_cipher_finish`, *prefix*`_aead_finish`, *prefix*`_aead_verify`.
 
 If a driver implements a multi-part operation but not the corresponding single-part operation, the core calls the driver's multipart operation entry points to perform the single-part operation.
 
diff --git a/third_party/mbedtls/repo/docs/requirements.in b/third_party/mbedtls/repo/docs/requirements.in
new file mode 100644
index 0000000..a523188
--- /dev/null
+++ b/third_party/mbedtls/repo/docs/requirements.in
@@ -0,0 +1,2 @@
+sphinx-rtd-theme
+breathe
diff --git a/third_party/mbedtls/repo/docs/requirements.txt b/third_party/mbedtls/repo/docs/requirements.txt
new file mode 100644
index 0000000..4b9f3a6
--- /dev/null
+++ b/third_party/mbedtls/repo/docs/requirements.txt
@@ -0,0 +1,66 @@
+#
+# This file is autogenerated by pip-compile with Python 3.9
+# by the following command:
+#
+#    pip-compile requirements.in
+#
+alabaster==0.7.13
+    # via sphinx
+babel==2.12.1
+    # via sphinx
+breathe==4.35.0
+    # via -r requirements.in
+certifi==2022.12.7
+    # via requests
+charset-normalizer==3.1.0
+    # via requests
+docutils==0.17.1
+    # via
+    #   breathe
+    #   sphinx
+    #   sphinx-rtd-theme
+idna==3.4
+    # via requests
+imagesize==1.4.1
+    # via sphinx
+importlib-metadata==6.0.0
+    # via sphinx
+jinja2==3.1.2
+    # via sphinx
+markupsafe==2.1.2
+    # via jinja2
+packaging==23.0
+    # via sphinx
+pygments==2.14.0
+    # via sphinx
+requests==2.28.2
+    # via sphinx
+snowballstemmer==2.2.0
+    # via sphinx
+sphinx==4.5.0
+    # via
+    #   breathe
+    #   sphinx-rtd-theme
+sphinx-rtd-theme==1.2.0
+    # via -r requirements.in
+sphinxcontrib-applehelp==1.0.4
+    # via sphinx
+sphinxcontrib-devhelp==1.0.2
+    # via sphinx
+sphinxcontrib-htmlhelp==2.0.1
+    # via sphinx
+sphinxcontrib-jquery==2.0.0
+    # via sphinx-rtd-theme
+sphinxcontrib-jsmath==1.0.1
+    # via sphinx
+sphinxcontrib-qthelp==1.0.3
+    # via sphinx
+sphinxcontrib-serializinghtml==1.1.5
+    # via sphinx
+urllib3==1.26.15
+    # via requests
+zipp==3.15.0
+    # via importlib-metadata
+
+# The following packages are considered to be unsafe in a requirements file:
+# setuptools
diff --git a/third_party/mbedtls/repo/doxygen/input/doc_encdec.h b/third_party/mbedtls/repo/doxygen/input/doc_encdec.h
index 46fb04f..e29f40f 100644
--- a/third_party/mbedtls/repo/doxygen/input/doc_encdec.h
+++ b/third_party/mbedtls/repo/doxygen/input/doc_encdec.h
@@ -6,19 +6,7 @@
 /*
  *
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 
 /**
@@ -38,7 +26,7 @@
  * All symmetric encryption algorithms are accessible via the generic cipher layer
  * (see \c mbedtls_cipher_setup()).
  *
- * The asymmetric encryptrion algorithms are accessible via the generic public
+ * The asymmetric encryption algorithms are accessible via the generic public
  * key layer (see \c mbedtls_pk_init()).
  *
  * The following algorithms are provided:
diff --git a/third_party/mbedtls/repo/doxygen/input/doc_hashing.h b/third_party/mbedtls/repo/doxygen/input/doc_hashing.h
index aaa0c78..aff7dfa 100644
--- a/third_party/mbedtls/repo/doxygen/input/doc_hashing.h
+++ b/third_party/mbedtls/repo/doxygen/input/doc_hashing.h
@@ -6,19 +6,7 @@
 /*
  *
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 
 /**
diff --git a/third_party/mbedtls/repo/doxygen/input/doc_mainpage.h b/third_party/mbedtls/repo/doxygen/input/doc_mainpage.h
index 5a0f3ec..f22ca10 100644
--- a/third_party/mbedtls/repo/doxygen/input/doc_mainpage.h
+++ b/third_party/mbedtls/repo/doxygen/input/doc_mainpage.h
@@ -6,89 +6,14 @@
 /*
  *
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 
 /**
- * @mainpage mbed TLS v2.28.0 source code documentation
+ * @mainpage Mbed TLS v2.28.7 API Documentation
  *
- * This documentation describes the internal structure of mbed TLS.  It was
+ * This documentation describes the internal structure of Mbed TLS.  It was
  * automatically generated from specially formatted comment blocks in
- * mbed TLS's source code using Doxygen.  (See
- * http://www.stack.nl/~dimitri/doxygen/ for more information on Doxygen)
- *
- * mbed TLS has a simple setup: it provides the ingredients for an SSL/TLS
- * implementation. These ingredients are listed as modules in the
- * \ref mainpage_modules "Modules section". This "Modules section" introduces
- * the high-level module concepts used throughout this documentation.\n
- * Some examples of mbed TLS usage can be found in the \ref mainpage_examples
- * "Examples section".
- *
- * @section mainpage_modules Modules
- *
- * mbed TLS supports SSLv3 up to TLSv1.2 communication by providing the
- * following:
- * - TCP/IP communication functions: listen, connect, accept, read/write.
- * - SSL/TLS communication functions: init, handshake, read/write.
- * - X.509 functions: CRT, CRL and key handling
- * - Random number generation
- * - Hashing
- * - Encryption/decryption
- *
- * Above functions are split up neatly into logical interfaces. These can be
- * used separately to provide any of the above functions or to mix-and-match
- * into an SSL server/client solution that utilises a X.509 PKI. Examples of
- * such implementations are amply provided with the source code.
- *
- * Note that mbed TLS does not provide a control channel or (multiple) session
- * handling without additional work from the developer.
- *
- * @section mainpage_examples Examples
- *
- * Example server setup:
- *
- * \b Prerequisites:
- * - X.509 certificate and private key
- * - session handling functions
- *
- * \b Setup:
- * - Load your certificate and your private RSA key (X.509 interface)
- * - Setup the listening TCP socket (TCP/IP interface)
- * - Accept incoming client connection (TCP/IP interface)
- * - Initialise as an SSL-server (SSL/TLS interface)
- *   - Set parameters, e.g. authentication, ciphers, CA-chain, key exchange
- *   - Set callback functions RNG, IO, session handling
- * - Perform an SSL-handshake (SSL/TLS interface)
- * - Read/write data (SSL/TLS interface)
- * - Close and cleanup (all interfaces)
- *
- * Example client setup:
- *
- * \b Prerequisites:
- * - X.509 certificate and private key
- * - X.509 trusted CA certificates
- *
- * \b Setup:
- * - Load the trusted CA certificates (X.509 interface)
- * - Load your certificate and your private RSA key (X.509 interface)
- * - Setup a TCP/IP connection (TCP/IP interface)
- * - Initialise as an SSL-client (SSL/TLS interface)
- *   - Set parameters, e.g. authentication mode, ciphers, CA-chain, session
- *   - Set callback functions RNG, IO
- * - Perform an SSL-handshake (SSL/TLS interface)
- * - Verify the server certificate (SSL/TLS interface)
- * - Write/read data (SSL/TLS interface)
- * - Close and cleanup (all interfaces)
+ * Mbed TLS's source code using Doxygen.  (See
+ * https://www.doxygen.nl for more information on Doxygen)
  */
diff --git a/third_party/mbedtls/repo/doxygen/input/doc_rng.h b/third_party/mbedtls/repo/doxygen/input/doc_rng.h
index b298d3b..5470b75 100644
--- a/third_party/mbedtls/repo/doxygen/input/doc_rng.h
+++ b/third_party/mbedtls/repo/doxygen/input/doc_rng.h
@@ -6,19 +6,7 @@
 /*
  *
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 
 /**
diff --git a/third_party/mbedtls/repo/doxygen/input/doc_ssltls.h b/third_party/mbedtls/repo/doxygen/input/doc_ssltls.h
index 6961124..5757574 100644
--- a/third_party/mbedtls/repo/doxygen/input/doc_ssltls.h
+++ b/third_party/mbedtls/repo/doxygen/input/doc_ssltls.h
@@ -6,19 +6,7 @@
 /*
  *
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 
 /**
diff --git a/third_party/mbedtls/repo/doxygen/input/doc_tcpip.h b/third_party/mbedtls/repo/doxygen/input/doc_tcpip.h
index a705de1..f8d8c69 100644
--- a/third_party/mbedtls/repo/doxygen/input/doc_tcpip.h
+++ b/third_party/mbedtls/repo/doxygen/input/doc_tcpip.h
@@ -6,19 +6,7 @@
 /*
  *
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 
 /**
diff --git a/third_party/mbedtls/repo/doxygen/input/doc_x509.h b/third_party/mbedtls/repo/doxygen/input/doc_x509.h
index 9049675..945830f 100644
--- a/third_party/mbedtls/repo/doxygen/input/doc_x509.h
+++ b/third_party/mbedtls/repo/doxygen/input/doc_x509.h
@@ -6,19 +6,7 @@
 /*
  *
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 
 /**
diff --git a/third_party/mbedtls/repo/doxygen/mbedtls.doxyfile b/third_party/mbedtls/repo/doxygen/mbedtls.doxyfile
index efd9ac6..2c71040 100644
--- a/third_party/mbedtls/repo/doxygen/mbedtls.doxyfile
+++ b/third_party/mbedtls/repo/doxygen/mbedtls.doxyfile
@@ -1,1911 +1,55 @@
-# Doxyfile 1.8.4
-
-# This file describes the settings to be used by the documentation system
-# doxygen (www.doxygen.org) for a project.
-#
-# All text after a double hash (##) is considered a comment and is placed
-# in front of the TAG it is preceding .
-# All text after a hash (#) is considered a comment and will be ignored.
-# The format is:
-#       TAG = value [value, ...]
-# For lists items can also be appended using:
-#       TAG += value [value, ...]
-# Values that contain spaces should be placed between quotes (" ").
-
-#---------------------------------------------------------------------------
-# Project related configuration options
-#---------------------------------------------------------------------------
-
-# This tag specifies the encoding used for all characters in the config file
-# that follow. The default is UTF-8 which is also the encoding used for all
-# text before the first occurrence of this tag. Doxygen uses libiconv (or the
-# iconv built into libc) for the transcoding. See
-# http://www.gnu.org/software/libiconv for the list of possible encodings.
-
-DOXYFILE_ENCODING      = UTF-8
-
-# The PROJECT_NAME tag is a single word (or sequence of words) that should
-# identify the project. Note that if you do not use Doxywizard you need
-# to put quotes around the project name if it contains spaces.
-
-PROJECT_NAME           = "mbed TLS v2.28.0"
-
-# The PROJECT_NUMBER tag can be used to enter a project or revision number.
-# This could be handy for archiving the generated documentation or
-# if some version control system is used.
-
-PROJECT_NUMBER         =
-
-# Using the PROJECT_BRIEF tag one can provide an optional one line description
-# for a project that appears at the top of each page and should give viewer
-# a quick idea about the purpose of the project. Keep the description short.
-
-PROJECT_BRIEF          =
-
-# With the PROJECT_LOGO tag one can specify an logo or icon that is
-# included in the documentation. The maximum height of the logo should not
-# exceed 55 pixels and the maximum width should not exceed 200 pixels.
-# Doxygen will copy the logo to the output directory.
-
-PROJECT_LOGO           =
-
-# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
-# base path where the generated documentation will be put.
-# If a relative path is entered, it will be relative to the location
-# where doxygen was started. If left blank the current directory will be used.
-
+PROJECT_NAME           = "Mbed TLS v2.28.7"
 OUTPUT_DIRECTORY       = ../apidoc/
-
-# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
-# 4096 sub-directories (in 2 levels) under the output directory of each output
-# format and will distribute the generated files over these directories.
-# Enabling this option can be useful when feeding doxygen a huge amount of
-# source files, where putting all generated files in the same directory would
-# otherwise cause performance problems for the file system.
-
-CREATE_SUBDIRS         = NO
-
-# The OUTPUT_LANGUAGE tag is used to specify the language in which all
-# documentation generated by doxygen is written. Doxygen will use this
-# information to generate all constant output in the proper language.
-# The default language is English, other supported languages are:
-# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
-# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
-# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
-# messages), Korean, Korean-en, Latvian, Lithuanian, Norwegian, Macedonian,
-# Persian, Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic,
-# Slovak, Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
-
-OUTPUT_LANGUAGE        = English
-
-# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
-# include brief member descriptions after the members that are listed in
-# the file and class documentation (similar to JavaDoc).
-# Set to NO to disable this.
-
-BRIEF_MEMBER_DESC      = YES
-
-# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
-# the brief description of a member or function before the detailed description.
-# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
-# brief descriptions will be completely suppressed.
-
-REPEAT_BRIEF           = YES
-
-# This tag implements a quasi-intelligent brief description abbreviator
-# that is used to form the text in various listings. Each string
-# in this list, if found as the leading text of the brief description, will be
-# stripped from the text and the result after processing the whole list, is
-# used as the annotated text. Otherwise, the brief description is used as-is.
-# If left blank, the following values are used ("$name" is automatically
-# replaced with the name of the entity): "The $name class" "The $name widget"
-# "The $name file" "is" "provides" "specifies" "contains"
-# "represents" "a" "an" "the"
-
-ABBREVIATE_BRIEF       = "The $name class" \
-                         "The $name widget" \
-                         "The $name file" \
-                         is \
-                         provides \
-                         specifies \
-                         contains \
-                         represents \
-                         a \
-                         an \
-                         the
-
-# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
-# Doxygen will generate a detailed section even if there is only a brief
-# description.
-
-ALWAYS_DETAILED_SEC    = NO
-
-# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
-# inherited members of a class in the documentation of that class as if those
-# members were ordinary class members. Constructors, destructors and assignment
-# operators of the base classes will not be shown.
-
-INLINE_INHERITED_MEMB  = NO
-
-# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
-# path before files name in the file list and in the header files. If set
-# to NO the shortest path that makes the file name unique will be used.
-
 FULL_PATH_NAMES        = NO
-
-# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
-# can be used to strip a user-defined part of the path. Stripping is
-# only done if one of the specified strings matches the left-hand part of
-# the path. The tag can be used to show relative paths in the file list.
-# If left blank the directory from which doxygen is run is used as the
-# path to strip. Note that you specify absolute paths here, but also
-# relative paths, which will be relative from the directory where doxygen is
-# started.
-
-STRIP_FROM_PATH        =
-
-# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
-# the path mentioned in the documentation of a class, which tells
-# the reader which header file to include in order to use a class.
-# If left blank only the name of the header file containing the class
-# definition is used. Otherwise one should specify the include paths that
-# are normally passed to the compiler using the -I flag.
-
-STRIP_FROM_INC_PATH    =
-
-# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
-# (but less readable) file names. This can be useful if your file system
-# doesn't support long names like on DOS, Mac, or CD-ROM.
-
-SHORT_NAMES            = NO
-
-# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
-# will interpret the first line (until the first dot) of a JavaDoc-style
-# comment as the brief description. If set to NO, the JavaDoc
-# comments will behave just like regular Qt-style comments
-# (thus requiring an explicit @brief command for a brief description.)
-
-JAVADOC_AUTOBRIEF      = NO
-
-# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
-# interpret the first line (until the first dot) of a Qt-style
-# comment as the brief description. If set to NO, the comments
-# will behave just like regular Qt-style comments (thus requiring
-# an explicit \brief command for a brief description.)
-
-QT_AUTOBRIEF           = NO
-
-# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
-# treat a multi-line C++ special comment block (i.e. a block of //! or ///
-# comments) as a brief description. This used to be the default behaviour.
-# The new default is to treat a multi-line C++ comment block as a detailed
-# description. Set this tag to YES if you prefer the old behaviour instead.
-
-MULTILINE_CPP_IS_BRIEF = NO
-
-# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
-# member inherits the documentation from any documented member that it
-# re-implements.
-
-INHERIT_DOCS           = YES
-
-# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
-# a new page for each member. If set to NO, the documentation of a member will
-# be part of the file/class/namespace that contains it.
-
-SEPARATE_MEMBER_PAGES  = NO
-
-# The TAB_SIZE tag can be used to set the number of spaces in a tab.
-# Doxygen uses this value to replace tabs by spaces in code fragments.
-
-TAB_SIZE               = 8
-
-# This tag can be used to specify a number of aliases that acts
-# as commands in the documentation. An alias has the form "name=value".
-# For example adding "sideeffect=\par Side Effects:\n" will allow you to
-# put the command \sideeffect (or @sideeffect) in the documentation, which
-# will result in a user-defined paragraph with heading "Side Effects:".
-# You can put \n's in the value part of an alias to insert newlines.
-
-ALIASES                =
-
-# This tag can be used to specify a number of word-keyword mappings (TCL only).
-# A mapping has the form "name=value". For example adding
-# "class=itcl::class" will allow you to use the command class in the
-# itcl::class meaning.
-
-TCL_SUBST              =
-
-# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
-# sources only. Doxygen will then generate output that is more tailored for C.
-# For instance, some of the names that are used will be different. The list
-# of all members will be omitted, etc.
-
 OPTIMIZE_OUTPUT_FOR_C  = YES
-
-# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
-# sources only. Doxygen will then generate output that is more tailored for
-# Java. For instance, namespaces will be presented as packages, qualified
-# scopes will look different, etc.
-
-OPTIMIZE_OUTPUT_JAVA   = NO
-
-# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
-# sources only. Doxygen will then generate output that is more tailored for
-# Fortran.
-
-OPTIMIZE_FOR_FORTRAN   = NO
-
-# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
-# sources. Doxygen will then generate output that is tailored for
-# VHDL.
-
-OPTIMIZE_OUTPUT_VHDL   = NO
-
-# Doxygen selects the parser to use depending on the extension of the files it
-# parses. With this tag you can assign which parser to use for a given
-# extension. Doxygen has a built-in mapping, but you can override or extend it
-# using this tag. The format is ext=language, where ext is a file extension,
-# and language is one of the parsers supported by doxygen: IDL, Java,
-# Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C,
-# C++. For instance to make doxygen treat .inc files as Fortran files (default
-# is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note
-# that for custom extensions you also need to set FILE_PATTERNS otherwise the
-# files are not read by doxygen.
-
-EXTENSION_MAPPING      =
-
-# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all
-# comments according to the Markdown format, which allows for more readable
-# documentation. See http://daringfireball.net/projects/markdown/ for details.
-# The output of markdown processing is further processed by doxygen, so you
-# can mix doxygen, HTML, and XML commands with Markdown formatting.
-# Disable only in case of backward compatibilities issues.
-
-MARKDOWN_SUPPORT       = YES
-
-# When enabled doxygen tries to link words that correspond to documented
-# classes, or namespaces to their corresponding documentation. Such a link can
-# be prevented in individual cases by by putting a % sign in front of the word
-# or globally by setting AUTOLINK_SUPPORT to NO.
-
-AUTOLINK_SUPPORT       = YES
-
-# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
-# to include (a tag file for) the STL sources as input, then you should
-# set this tag to YES in order to let doxygen match functions declarations and
-# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
-# func(std::string) {}). This also makes the inheritance and collaboration
-# diagrams that involve STL classes more complete and accurate.
-
-BUILTIN_STL_SUPPORT    = NO
-
-# If you use Microsoft's C++/CLI language, you should set this option to YES to
-# enable parsing support.
-
-CPP_CLI_SUPPORT        = NO
-
-# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
-# Doxygen will parse them like normal C++ but will assume all classes use public
-# instead of private inheritance when no explicit protection keyword is present.
-
-SIP_SUPPORT            = NO
-
-# For Microsoft's IDL there are propget and propput attributes to indicate
-# getter and setter methods for a property. Setting this option to YES (the
-# default) will make doxygen replace the get and set methods by a property in
-# the documentation. This will only work if the methods are indeed getting or
-# setting a simple type. If this is not the case, or you want to show the
-# methods anyway, you should set this option to NO.
-
-IDL_PROPERTY_SUPPORT   = YES
-
-# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
-# tag is set to YES, then doxygen will reuse the documentation of the first
-# member in the group (if any) for the other members of the group. By default
-# all members of a group must be documented explicitly.
-
-DISTRIBUTE_GROUP_DOC   = NO
-
-# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
-# the same type (for instance a group of public functions) to be put as a
-# subgroup of that type (e.g. under the Public Functions section). Set it to
-# NO to prevent subgrouping. Alternatively, this can be done per class using
-# the \nosubgrouping command.
-
-SUBGROUPING            = YES
-
-# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and
-# unions are shown inside the group in which they are included (e.g. using
-# @ingroup) instead of on a separate page (for HTML and Man pages) or
-# section (for LaTeX and RTF).
-
-INLINE_GROUPED_CLASSES = NO
-
-# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and
-# unions with only public data fields or simple typedef fields will be shown
-# inline in the documentation of the scope in which they are defined (i.e. file,
-# namespace, or group documentation), provided this scope is documented. If set
-# to NO (the default), structs, classes, and unions are shown on a separate
-# page (for HTML and Man pages) or section (for LaTeX and RTF).
-
-INLINE_SIMPLE_STRUCTS  = NO
-
-# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
-# is documented as struct, union, or enum with the name of the typedef. So
-# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
-# with name TypeT. When disabled the typedef will appear as a member of a file,
-# namespace, or class. And the struct will be named TypeS. This can typically
-# be useful for C code in case the coding convention dictates that all compound
-# types are typedef'ed and only the typedef is referenced, never the tag name.
-
-TYPEDEF_HIDES_STRUCT   = NO
-
-# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
-# cache is used to resolve symbols given their name and scope. Since this can
-# be an expensive process and often the same symbol appear multiple times in
-# the code, doxygen keeps a cache of pre-resolved symbols. If the cache is too
-# small doxygen will become slower. If the cache is too large, memory is wasted.
-# The cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid
-# range is 0..9, the default is 0, corresponding to a cache size of 2^16 = 65536
-# symbols.
-
-LOOKUP_CACHE_SIZE      = 0
-
-#---------------------------------------------------------------------------
-# Build related configuration options
-#---------------------------------------------------------------------------
-
-# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
-# documentation are documented, even if no documentation was available.
-# Private class members and static file members will be hidden unless
-# the EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES
-
 EXTRACT_ALL            = YES
-
-# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
-# will be included in the documentation.
-
 EXTRACT_PRIVATE        = YES
-
-# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
-# scope will be included in the documentation.
-
-EXTRACT_PACKAGE        = NO
-
-# If the EXTRACT_STATIC tag is set to YES all static members of a file
-# will be included in the documentation.
-
 EXTRACT_STATIC         = YES
-
-# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
-# defined locally in source files will be included in the documentation.
-# If set to NO only classes defined in header files are included.
-
-EXTRACT_LOCAL_CLASSES  = YES
-
-# This flag is only useful for Objective-C code. When set to YES local
-# methods, which are defined in the implementation section but not in
-# the interface are included in the documentation.
-# If set to NO (the default) only methods in the interface are included.
-
-EXTRACT_LOCAL_METHODS  = YES
-
-# If this flag is set to YES, the members of anonymous namespaces will be
-# extracted and appear in the documentation as a namespace called
-# 'anonymous_namespace{file}', where file will be replaced with the base
-# name of the file that contains the anonymous namespace. By default
-# anonymous namespaces are hidden.
-
-EXTRACT_ANON_NSPACES   = YES
-
-# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
-# undocumented members of documented classes, files or namespaces.
-# If set to NO (the default) these members will be included in the
-# various overviews, but no documentation section is generated.
-# This option has no effect if EXTRACT_ALL is enabled.
-
-HIDE_UNDOC_MEMBERS     = NO
-
-# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
-# undocumented classes that are normally visible in the class hierarchy.
-# If set to NO (the default) these classes will be included in the various
-# overviews. This option has no effect if EXTRACT_ALL is enabled.
-
-HIDE_UNDOC_CLASSES     = NO
-
-# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
-# friend (class|struct|union) declarations.
-# If set to NO (the default) these declarations will be included in the
-# documentation.
-
-HIDE_FRIEND_COMPOUNDS  = NO
-
-# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
-# documentation blocks found inside the body of a function.
-# If set to NO (the default) these blocks will be appended to the
-# function's detailed documentation block.
-
-HIDE_IN_BODY_DOCS      = NO
-
-# The INTERNAL_DOCS tag determines if documentation
-# that is typed after a \internal command is included. If the tag is set
-# to NO (the default) then the documentation will be excluded.
-# Set it to YES to include the internal documentation.
-
-INTERNAL_DOCS          = NO
-
-# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
-# file names in lower-case letters. If set to YES upper-case letters are also
-# allowed. This is useful if you have classes or files whose names only differ
-# in case and if your file system supports case sensitive file names. Windows
-# and Mac users are advised to set this option to NO.
-
 CASE_SENSE_NAMES       = NO
-
-# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
-# will show members with their full class and namespace scopes in the
-# documentation. If set to YES the scope will be hidden.
-
-HIDE_SCOPE_NAMES       = NO
-
-# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
-# will put a list of the files that are included by a file in the documentation
-# of that file.
-
-SHOW_INCLUDE_FILES     = YES
-
-# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
-# will list include files with double quotes in the documentation
-# rather than with sharp brackets.
-
-FORCE_LOCAL_INCLUDES   = NO
-
-# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
-# is inserted in the documentation for inline members.
-
-INLINE_INFO            = YES
-
-# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
-# will sort the (detailed) documentation of file and class members
-# alphabetically by member name. If set to NO the members will appear in
-# declaration order.
-
-SORT_MEMBER_DOCS       = YES
-
-# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
-# brief documentation of file, namespace and class members alphabetically
-# by member name. If set to NO (the default) the members will appear in
-# declaration order.
-
-SORT_BRIEF_DOCS        = NO
-
-# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
-# will sort the (brief and detailed) documentation of class members so that
-# constructors and destructors are listed first. If set to NO (the default)
-# the constructors will appear in the respective orders defined by
-# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
-# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
-# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
-
-SORT_MEMBERS_CTORS_1ST = NO
-
-# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
-# hierarchy of group names into alphabetical order. If set to NO (the default)
-# the group names will appear in their defined order.
-
-SORT_GROUP_NAMES       = NO
-
-# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
-# sorted by fully-qualified names, including namespaces. If set to
-# NO (the default), the class list will be sorted only by class name,
-# not including the namespace part.
-# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
-# Note: This option applies only to the class list, not to the
-# alphabetical list.
-
-SORT_BY_SCOPE_NAME     = NO
-
-# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to
-# do proper type resolution of all parameters of a function it will reject a
-# match between the prototype and the implementation of a member function even
-# if there is only one candidate or it is obvious which candidate to choose
-# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen
-# will still accept a match between prototype and implementation in such cases.
-
-STRICT_PROTO_MATCHING  = NO
-
-# The GENERATE_TODOLIST tag can be used to enable (YES) or
-# disable (NO) the todo list. This list is created by putting \todo
-# commands in the documentation.
-
-GENERATE_TODOLIST      = YES
-
-# The GENERATE_TESTLIST tag can be used to enable (YES) or
-# disable (NO) the test list. This list is created by putting \test
-# commands in the documentation.
-
-GENERATE_TESTLIST      = YES
-
-# The GENERATE_BUGLIST tag can be used to enable (YES) or
-# disable (NO) the bug list. This list is created by putting \bug
-# commands in the documentation.
-
-GENERATE_BUGLIST       = YES
-
-# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
-# disable (NO) the deprecated list. This list is created by putting
-# \deprecated commands in the documentation.
-
-GENERATE_DEPRECATEDLIST= YES
-
-# The ENABLED_SECTIONS tag can be used to enable conditional
-# documentation sections, marked by \if section-label ... \endif
-# and \cond section-label ... \endcond blocks.
-
-ENABLED_SECTIONS       =
-
-# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
-# the initial value of a variable or macro consists of for it to appear in
-# the documentation. If the initializer consists of more lines than specified
-# here it will be hidden. Use a value of 0 to hide initializers completely.
-# The appearance of the initializer of individual variables and macros in the
-# documentation can be controlled using \showinitializer or \hideinitializer
-# command in the documentation regardless of this setting.
-
-MAX_INITIALIZER_LINES  = 30
-
-# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
-# at the bottom of the documentation of classes and structs. If set to YES the
-# list will mention the files that were used to generate the documentation.
-
-SHOW_USED_FILES        = YES
-
-# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
-# This will remove the Files entry from the Quick Index and from the
-# Folder Tree View (if specified). The default is YES.
-
-SHOW_FILES             = YES
-
-# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
-# Namespaces page.
-# This will remove the Namespaces entry from the Quick Index
-# and from the Folder Tree View (if specified). The default is YES.
-
-SHOW_NAMESPACES        = YES
-
-# The FILE_VERSION_FILTER tag can be used to specify a program or script that
-# doxygen should invoke to get the current version for each file (typically from
-# the version control system). Doxygen will invoke the program by executing (via
-# popen()) the command <command> <input-file>, where <command> is the value of
-# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
-# provided by doxygen. Whatever the program writes to standard output
-# is used as the file version. See the manual for examples.
-
-FILE_VERSION_FILTER    =
-
-# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
-# by doxygen. The layout file controls the global structure of the generated
-# output files in an output format independent way. To create the layout file
-# that represents doxygen's defaults, run doxygen with the -l option.
-# You can optionally specify a file name after the option, if omitted
-# DoxygenLayout.xml will be used as the name of the layout file.
-
-LAYOUT_FILE            =
-
-# The CITE_BIB_FILES tag can be used to specify one or more bib files
-# containing the references data. This must be a list of .bib files. The
-# .bib extension is automatically appended if omitted. Using this command
-# requires the bibtex tool to be installed. See also
-# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style
-# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this
-# feature you need bibtex and perl available in the search path. Do not use
-# file names with spaces, bibtex cannot handle them.
-
-CITE_BIB_FILES         =
-
-#---------------------------------------------------------------------------
-# configuration options related to warning and progress messages
-#---------------------------------------------------------------------------
-
-# The QUIET tag can be used to turn on/off the messages that are generated
-# by doxygen. Possible values are YES and NO. If left blank NO is used.
-
-QUIET                  = NO
-
-# The WARNINGS tag can be used to turn on/off the warning messages that are
-# generated by doxygen. Possible values are YES and NO. If left blank
-# NO is used.
-
-WARNINGS               = YES
-
-# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
-# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
-# automatically be disabled.
-
-WARN_IF_UNDOCUMENTED   = YES
-
-# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
-# potential errors in the documentation, such as not documenting some
-# parameters in a documented function, or documenting parameters that
-# don't exist or using markup commands wrongly.
-
-WARN_IF_DOC_ERROR      = YES
-
-# The WARN_NO_PARAMDOC option can be enabled to get warnings for
-# functions that are documented, but have no documentation for their parameters
-# or return value. If set to NO (the default) doxygen will only warn about
-# wrong or incomplete parameter documentation, but not about the absence of
-# documentation.
-
-WARN_NO_PARAMDOC       = NO
-
-# The WARN_FORMAT tag determines the format of the warning messages that
-# doxygen can produce. The string should contain the $file, $line, and $text
-# tags, which will be replaced by the file and line number from which the
-# warning originated and the warning text. Optionally the format may contain
-# $version, which will be replaced by the version of the file (if it could
-# be obtained via FILE_VERSION_FILTER)
-
-WARN_FORMAT            = "$file:$line: $text"
-
-# The WARN_LOGFILE tag can be used to specify a file to which warning
-# and error messages should be written. If left blank the output is written
-# to stderr.
-
-WARN_LOGFILE           =
-
-#---------------------------------------------------------------------------
-# configuration options related to the input files
-#---------------------------------------------------------------------------
-
-# The INPUT tag can be used to specify the files and/or directories that contain
-# documented source files. You may enter file names like "myfile.cpp" or
-# directories like "/usr/src/myproject". Separate the files or directories
-# with spaces.
-
 INPUT                  = ../include input
-
-# This tag can be used to specify the character encoding of the source files
-# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
-# also the default input encoding. Doxygen uses libiconv (or the iconv built
-# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
-# the list of possible encodings.
-
-INPUT_ENCODING         = UTF-8
-
-# If the value of the INPUT tag contains directories, you can use the
-# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
-# and *.h) to filter out the source-files in the directories. If left
-# blank the following patterns are tested:
-# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
-# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
-# *.f90 *.f *.for *.vhd *.vhdl
-
 FILE_PATTERNS          = *.h
-
-# The RECURSIVE tag can be used to turn specify whether or not subdirectories
-# should be searched for input files as well. Possible values are YES and NO.
-# If left blank NO is used.
-
 RECURSIVE              = YES
-
-# The EXCLUDE tag can be used to specify files and/or directories that should be
-# excluded from the INPUT source files. This way you can easily exclude a
-# subdirectory from a directory tree whose root is specified with the INPUT tag.
-# Note that relative paths are relative to the directory from which doxygen is
-# run.
-
-EXCLUDE                =
-
-# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
-# directories that are symbolic links (a Unix file system feature) are excluded
-# from the input.
-
 EXCLUDE_SYMLINKS       = YES
-
-# If the value of the INPUT tag contains directories, you can use the
-# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
-# certain files from those directories. Note that the wildcards are matched
-# against the file with absolute path, so to exclude all test directories
-# for example use the pattern */test/*
-
 EXCLUDE_PATTERNS       = *_internal.h *_wrap.h
-
-# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
-# (namespaces, classes, functions, etc.) that should be excluded from the
-# output. The symbol name can be a fully qualified name, a word, or if the
-# wildcard * is used, a substring. Examples: ANamespace, AClass,
-# AClass::ANamespace, ANamespace::*Test
-
-EXCLUDE_SYMBOLS        =
-
-# The EXAMPLE_PATH tag can be used to specify one or more files or
-# directories that contain example code fragments that are included (see
-# the \include command).
-
-EXAMPLE_PATH           =
-
-# If the value of the EXAMPLE_PATH tag contains directories, you can use the
-# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
-# and *.h) to filter out the source-files in the directories. If left
-# blank all files are included.
-
-EXAMPLE_PATTERNS       = *
-
-# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
-# searched for input files to be used with the \include or \dontinclude
-# commands irrespective of the value of the RECURSIVE tag.
-# Possible values are YES and NO. If left blank NO is used.
-
-EXAMPLE_RECURSIVE      = NO
-
-# The IMAGE_PATH tag can be used to specify one or more files or
-# directories that contain image that are included in the documentation (see
-# the \image command).
-
-IMAGE_PATH             =
-
-# The INPUT_FILTER tag can be used to specify a program that doxygen should
-# invoke to filter for each input file. Doxygen will invoke the filter program
-# by executing (via popen()) the command <filter> <input-file>, where <filter>
-# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
-# input file. Doxygen will then use the output that the filter program writes
-# to standard output.
-# If FILTER_PATTERNS is specified, this tag will be ignored.
-# Note that the filter must not add or remove lines; it is applied before the
-# code is scanned, but not when the output code is generated. If lines are added
-# or removed, the anchors will not be placed correctly.
-
-INPUT_FILTER           =
-
-# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
-# basis.
-# Doxygen will compare the file name with each pattern and apply the
-# filter if there is a match.
-# The filters are a list of the form:
-# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
-# info on how filters are used. If FILTER_PATTERNS is empty or if
-# non of the patterns match the file name, INPUT_FILTER is applied.
-
-FILTER_PATTERNS        =
-
-# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
-# INPUT_FILTER) will be used to filter the input files when producing source
-# files to browse (i.e. when SOURCE_BROWSER is set to YES).
-
-FILTER_SOURCE_FILES    = NO
-
-# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
-# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
-# and it is also possible to disable source filtering for a specific pattern
-# using *.ext= (so without naming a filter). This option only has effect when
-# FILTER_SOURCE_FILES is enabled.
-
-FILTER_SOURCE_PATTERNS =
-
-# If the USE_MD_FILE_AS_MAINPAGE tag refers to the name of a markdown file that
-# is part of the input, its contents will be placed on the main page
-# (index.html). This can be useful if you have a project on for instance GitHub
-# and want reuse the introduction page also for the doxygen output.
-
-USE_MDFILE_AS_MAINPAGE =
-
-#---------------------------------------------------------------------------
-# configuration options related to source browsing
-#---------------------------------------------------------------------------
-
-# If the SOURCE_BROWSER tag is set to YES then a list of source files will
-# be generated. Documented entities will be cross-referenced with these sources.
-# Note: To get rid of all source code in the generated output, make sure also
-# VERBATIM_HEADERS is set to NO.
-
 SOURCE_BROWSER         = YES
-
-# Setting the INLINE_SOURCES tag to YES will include the body
-# of functions and classes directly in the documentation.
-
-INLINE_SOURCES         = NO
-
-# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
-# doxygen to hide any special comment blocks from generated source code
-# fragments. Normal C, C++ and Fortran comments will always remain visible.
-
-STRIP_CODE_COMMENTS    = YES
-
-# If the REFERENCED_BY_RELATION tag is set to YES
-# then for each documented function all documented
-# functions referencing it will be listed.
-
 REFERENCED_BY_RELATION = YES
-
-# If the REFERENCES_RELATION tag is set to YES
-# then for each documented function all documented entities
-# called/used by that function will be listed.
-
 REFERENCES_RELATION    = YES
-
-# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
-# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
-# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
-# link to the source code.
-# Otherwise they will link to the documentation.
-
-REFERENCES_LINK_SOURCE = YES
-
-# If the USE_HTAGS tag is set to YES then the references to source code
-# will point to the HTML generated by the htags(1) tool instead of doxygen
-# built-in source browser. The htags tool is part of GNU's global source
-# tagging system (see http://www.gnu.org/software/global/global.html). You
-# will need version 4.8.6 or higher.
-
-USE_HTAGS              = NO
-
-# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
-# will generate a verbatim copy of the header file for each class for
-# which an include is specified. Set to NO to disable this.
-
-VERBATIM_HEADERS       = YES
-
-#---------------------------------------------------------------------------
-# configuration options related to the alphabetical class index
-#---------------------------------------------------------------------------
-
-# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
-# of all compounds will be generated. Enable this if the project
-# contains a lot of classes, structs, unions or interfaces.
-
 ALPHABETICAL_INDEX     = NO
-
-# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
-# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
-# in which this list will be split (can be a number in the range [1..20])
-
-COLS_IN_ALPHA_INDEX    = 5
-
-# In case all classes in a project start with a common prefix, all
-# classes will be put under the same header in the alphabetical index.
-# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
-# should be ignored while generating the index headers.
-
-IGNORE_PREFIX          =
-
-#---------------------------------------------------------------------------
-# configuration options related to the HTML output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
-# generate HTML output.
-
-GENERATE_HTML          = YES
-
-# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `html' will be used as the default path.
-
 HTML_OUTPUT            = .
-
-# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
-# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
-# doxygen will generate files with .html extension.
-
-HTML_FILE_EXTENSION    = .html
-
-# The HTML_HEADER tag can be used to specify a personal HTML header for
-# each generated HTML page. If it is left blank doxygen will generate a
-# standard header. Note that when using a custom header you are responsible
-#  for the proper inclusion of any scripts and style sheets that doxygen
-# needs, which is dependent on the configuration options used.
-# It is advised to generate a default header using "doxygen -w html
-# header.html footer.html stylesheet.css YourConfigFile" and then modify
-# that header. Note that the header is subject to change so you typically
-# have to redo this when upgrading to a newer version of doxygen or when
-# changing the value of configuration settings such as GENERATE_TREEVIEW!
-
-HTML_HEADER            =
-
-# The HTML_FOOTER tag can be used to specify a personal HTML footer for
-# each generated HTML page. If it is left blank doxygen will generate a
-# standard footer.
-
-HTML_FOOTER            =
-
-# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
-# style sheet that is used by each HTML page. It can be used to
-# fine-tune the look of the HTML output. If left blank doxygen will
-# generate a default style sheet. Note that it is recommended to use
-# HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this
-# tag will in the future become obsolete.
-
-HTML_STYLESHEET        =
-
-# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional
-# user-defined cascading style sheet that is included after the standard
-# style sheets created by doxygen. Using this option one can overrule
-# certain style aspects. This is preferred over using HTML_STYLESHEET
-# since it does not replace the standard style sheet and is therefor more
-# robust against future updates. Doxygen will copy the style sheet file to
-# the output directory.
-
-HTML_EXTRA_STYLESHEET  =
-
-# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
-# other source files which should be copied to the HTML output directory. Note
-# that these files will be copied to the base HTML output directory. Use the
-# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
-# files. In the HTML_STYLESHEET file, use the file name only. Also note that
-# the files will be copied as-is; there are no commands or markers available.
-
-HTML_EXTRA_FILES       =
-
-# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
-# Doxygen will adjust the colors in the style sheet and background images
-# according to this color. Hue is specified as an angle on a colorwheel,
-# see http://en.wikipedia.org/wiki/Hue for more information.
-# For instance the value 0 represents red, 60 is yellow, 120 is green,
-# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
-# The allowed range is 0 to 359.
-
-HTML_COLORSTYLE_HUE    = 220
-
-# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
-# the colors in the HTML output. For a value of 0 the output will use
-# grayscales only. A value of 255 will produce the most vivid colors.
-
-HTML_COLORSTYLE_SAT    = 100
-
-# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
-# the luminance component of the colors in the HTML output. Values below
-# 100 gradually make the output lighter, whereas values above 100 make
-# the output darker. The value divided by 100 is the actual gamma applied,
-# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
-# and 100 does not change the gamma.
-
-HTML_COLORSTYLE_GAMMA  = 80
-
-# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
-# page will contain the date and time when the page was generated. Setting
-# this to NO can help when comparing the output of multiple runs.
-
 HTML_TIMESTAMP         = YES
-
-# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
-# documentation will contain sections that can be hidden and shown after the
-# page has loaded.
-
-HTML_DYNAMIC_SECTIONS  = NO
-
-# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of
-# entries shown in the various tree structured indices initially; the user
-# can expand and collapse entries dynamically later on. Doxygen will expand
-# the tree to such a level that at most the specified number of entries are
-# visible (unless a fully collapsed tree already exceeds this amount).
-# So setting the number of entries 1 will produce a full collapsed tree by
-# default. 0 is a special value representing an infinite number of entries
-# and will result in a full expanded tree by default.
-
-HTML_INDEX_NUM_ENTRIES = 100
-
-# If the GENERATE_DOCSET tag is set to YES, additional index files
-# will be generated that can be used as input for Apple's Xcode 3
-# integrated development environment, introduced with OSX 10.5 (Leopard).
-# To create a documentation set, doxygen will generate a Makefile in the
-# HTML output directory. Running make will produce the docset in that
-# directory and running "make install" will install the docset in
-# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
-# it at startup.
-# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
-# for more information.
-
-GENERATE_DOCSET        = NO
-
-# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
-# feed. A documentation feed provides an umbrella under which multiple
-# documentation sets from a single provider (such as a company or product suite)
-# can be grouped.
-
-DOCSET_FEEDNAME        = "Doxygen generated docs"
-
-# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
-# should uniquely identify the documentation set bundle. This should be a
-# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
-# will append .docset to the name.
-
-DOCSET_BUNDLE_ID       = org.doxygen.Project
-
-# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely
-# identify the documentation publisher. This should be a reverse domain-name
-# style string, e.g. com.mycompany.MyDocSet.documentation.
-
-DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
-
-# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
-
-DOCSET_PUBLISHER_NAME  = Publisher
-
-# If the GENERATE_HTMLHELP tag is set to YES, additional index files
-# will be generated that can be used as input for tools like the
-# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
-# of the generated HTML documentation.
-
-GENERATE_HTMLHELP      = NO
-
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
-# be used to specify the file name of the resulting .chm file. You
-# can add a path in front of the file if the result should not be
-# written to the html output directory.
-
-CHM_FILE               =
-
-# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
-# be used to specify the location (absolute path including file name) of
-# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
-# the HTML help compiler on the generated index.hhp.
-
-HHC_LOCATION           =
-
-# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
-# controls if a separate .chi index file is generated (YES) or that
-# it should be included in the master .chm file (NO).
-
-GENERATE_CHI           = NO
-
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
-# is used to encode HtmlHelp index (hhk), content (hhc) and project file
-# content.
-
-CHM_INDEX_ENCODING     =
-
-# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
-# controls whether a binary table of contents is generated (YES) or a
-# normal table of contents (NO) in the .chm file.
-
-BINARY_TOC             = NO
-
-# The TOC_EXPAND flag can be set to YES to add extra items for group members
-# to the contents of the HTML help documentation and to the tree view.
-
-TOC_EXPAND             = NO
-
-# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
-# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
-# that can be used as input for Qt's qhelpgenerator to generate a
-# Qt Compressed Help (.qch) of the generated HTML documentation.
-
-GENERATE_QHP           = NO
-
-# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
-# be used to specify the file name of the resulting .qch file.
-# The path specified is relative to the HTML output folder.
-
-QCH_FILE               =
-
-# The QHP_NAMESPACE tag specifies the namespace to use when generating
-# Qt Help Project output. For more information please see
-# http://doc.trolltech.com/qthelpproject.html#namespace
-
-QHP_NAMESPACE          = org.doxygen.Project
-
-# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
-# Qt Help Project output. For more information please see
-# http://doc.trolltech.com/qthelpproject.html#virtual-folders
-
-QHP_VIRTUAL_FOLDER     = doc
-
-# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
-# add. For more information please see
-# http://doc.trolltech.com/qthelpproject.html#custom-filters
-
-QHP_CUST_FILTER_NAME   =
-
-# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
-# custom filter to add. For more information please see
-# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
-# Qt Help Project / Custom Filters</a>.
-
-QHP_CUST_FILTER_ATTRS  =
-
-# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
-# project's
-# filter section matches.
-# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
-# Qt Help Project / Filter Attributes</a>.
-
-QHP_SECT_FILTER_ATTRS  =
-
-# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
-# be used to specify the location of Qt's qhelpgenerator.
-# If non-empty doxygen will try to run qhelpgenerator on the generated
-# .qhp file.
-
-QHG_LOCATION           =
-
-# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
-#  will be generated, which together with the HTML files, form an Eclipse help
-# plugin. To install this plugin and make it available under the help contents
-# menu in Eclipse, the contents of the directory containing the HTML and XML
-# files needs to be copied into the plugins directory of eclipse. The name of
-# the directory within the plugins directory should be the same as
-# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
-# the help appears.
-
-GENERATE_ECLIPSEHELP   = NO
-
-# A unique identifier for the eclipse help plugin. When installing the plugin
-# the directory name containing the HTML and XML files should also have
-# this name.
-
-ECLIPSE_DOC_ID         = org.doxygen.Project
-
-# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs)
-# at top of each HTML page. The value NO (the default) enables the index and
-# the value YES disables it. Since the tabs have the same information as the
-# navigation tree you can set this option to NO if you already set
-# GENERATE_TREEVIEW to YES.
-
-DISABLE_INDEX          = NO
-
-# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
-# structure should be generated to display hierarchical information.
-# If the tag value is set to YES, a side panel will be generated
-# containing a tree-like index structure (just like the one that
-# is generated for HTML Help). For this to work a browser that supports
-# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
-# Windows users are probably better off using the HTML help feature.
-# Since the tree basically has the same information as the tab index you
-# could consider to set DISABLE_INDEX to NO when enabling this option.
-
-GENERATE_TREEVIEW      = NO
-
-# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values
-# (range [0,1..20]) that doxygen will group on one line in the generated HTML
-# documentation. Note that a value of 0 will completely suppress the enum
-# values from appearing in the overview section.
-
-ENUM_VALUES_PER_LINE   = 4
-
-# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
-# used to set the initial width (in pixels) of the frame in which the tree
-# is shown.
-
-TREEVIEW_WIDTH         = 250
-
-# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
-# links to external symbols imported via tag files in a separate window.
-
-EXT_LINKS_IN_WINDOW    = NO
-
-# Use this tag to change the font size of Latex formulas included
-# as images in the HTML documentation. The default is 10. Note that
-# when you change the font size after a successful doxygen run you need
-# to manually remove any form_*.png images from the HTML output directory
-# to force them to be regenerated.
-
-FORMULA_FONTSIZE       = 10
-
-# Use the FORMULA_TRANPARENT tag to determine whether or not the images
-# generated for formulas are transparent PNGs. Transparent PNGs are
-# not supported properly for IE 6.0, but are supported on all modern browsers.
-# Note that when changing this option you need to delete any form_*.png files
-# in the HTML output before the changes have effect.
-
-FORMULA_TRANSPARENT    = YES
-
-# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
-# (see http://www.mathjax.org) which uses client side Javascript for the
-# rendering instead of using prerendered bitmaps. Use this if you do not
-# have LaTeX installed or if you want to formulas look prettier in the HTML
-# output. When enabled you may also need to install MathJax separately and
-# configure the path to it using the MATHJAX_RELPATH option.
-
-USE_MATHJAX            = NO
-
-# When MathJax is enabled you can set the default output format to be used for
-# the MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and
-# SVG. The default value is HTML-CSS, which is slower, but has the best
-# compatibility.
-
-MATHJAX_FORMAT         = HTML-CSS
-
-# When MathJax is enabled you need to specify the location relative to the
-# HTML output directory using the MATHJAX_RELPATH option. The destination
-# directory should contain the MathJax.js script. For instance, if the mathjax
-# directory is located at the same level as the HTML output directory, then
-# MATHJAX_RELPATH should be ../mathjax. The default value points to
-# the MathJax Content Delivery Network so you can quickly see the result without
-# installing MathJax.
-# However, it is strongly recommended to install a local
-# copy of MathJax from http://www.mathjax.org before deployment.
-
-MATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest
-
-# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension
-# names that should be enabled during MathJax rendering.
-
-MATHJAX_EXTENSIONS     =
-
-# The MATHJAX_CODEFILE tag can be used to specify a file with javascript
-# pieces of code that will be used on startup of the MathJax code.
-
-MATHJAX_CODEFILE       =
-
-# When the SEARCHENGINE tag is enabled doxygen will generate a search box
-# for the HTML output. The underlying search engine uses javascript
-# and DHTML and should work on any modern browser. Note that when using
-# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
-# (GENERATE_DOCSET) there is already a search function so this one should
-# typically be disabled. For large projects the javascript based search engine
-# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
-
-SEARCHENGINE           = NO
-
-# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
-# implemented using a web server instead of a web client using Javascript.
-# There are two flavours of web server based search depending on the
-# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for
-# searching and an index file used by the script. When EXTERNAL_SEARCH is
-# enabled the indexing and searching needs to be provided by external tools.
-# See the manual for details.
-
-SERVER_BASED_SEARCH    = NO
-
-# When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP
-# script for searching. Instead the search results are written to an XML file
-# which needs to be processed by an external indexer. Doxygen will invoke an
-# external search engine pointed to by the SEARCHENGINE_URL option to obtain
-# the search results. Doxygen ships with an example indexer (doxyindexer) and
-# search engine (doxysearch.cgi) which are based on the open source search
-# engine library Xapian. See the manual for configuration details.
-
-EXTERNAL_SEARCH        = NO
-
-# The SEARCHENGINE_URL should point to a search engine hosted by a web server
-# which will returned the search results when EXTERNAL_SEARCH is enabled.
-# Doxygen ships with an example search engine (doxysearch) which is based on
-# the open source search engine library Xapian. See the manual for configuration
-# details.
-
-SEARCHENGINE_URL       =
-
-# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
-# search data is written to a file for indexing by an external tool. With the
-# SEARCHDATA_FILE tag the name of this file can be specified.
-
-SEARCHDATA_FILE        = searchdata.xml
-
-# When SERVER_BASED_SEARCH AND EXTERNAL_SEARCH are both enabled the
-# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
-# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
-# projects and redirect the results back to the right project.
-
-EXTERNAL_SEARCH_ID     =
-
-# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
-# projects other than the one defined by this configuration file, but that are
-# all added to the same external search index. Each project needs to have a
-# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id
-# of to a relative location where the documentation can be found.
-# The format is: EXTRA_SEARCH_MAPPINGS = id1=loc1 id2=loc2 ...
-
-EXTRA_SEARCH_MAPPINGS  =
-
-#---------------------------------------------------------------------------
-# configuration options related to the LaTeX output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
-# generate Latex output.
-
+SEARCHENGINE           = YES
 GENERATE_LATEX         = NO
-
-# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `latex' will be used as the default path.
-
-LATEX_OUTPUT           = latex
-
-# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
-# invoked. If left blank `latex' will be used as the default command name.
-# Note that when enabling USE_PDFLATEX this option is only used for
-# generating bitmaps for formulas in the HTML output, but not in the
-# Makefile that is written to the output directory.
-
-LATEX_CMD_NAME         = latex
-
-# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
-# generate index for LaTeX. If left blank `makeindex' will be used as the
-# default command name.
-
-MAKEINDEX_CMD_NAME     = makeindex
-
-# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
-# LaTeX documents. This may be useful for small projects and may help to
-# save some trees in general.
-
-COMPACT_LATEX          = YES
-
-# The PAPER_TYPE tag can be used to set the paper type that is used
-# by the printer. Possible values are: a4, letter, legal and
-# executive. If left blank a4 will be used.
-
-PAPER_TYPE             = a4wide
-
-# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
-# packages that should be included in the LaTeX output.
-
-EXTRA_PACKAGES         =
-
-# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
-# the generated latex document. The header should contain everything until
-# the first chapter. If it is left blank doxygen will generate a
-# standard header. Notice: only use this tag if you know what you are doing!
-
-LATEX_HEADER           =
-
-# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for
-# the generated latex document. The footer should contain everything after
-# the last chapter. If it is left blank doxygen will generate a
-# standard footer. Notice: only use this tag if you know what you are doing!
-
-LATEX_FOOTER           =
-
-# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images
-# or other source files which should be copied to the LaTeX output directory.
-# Note that the files will be copied as-is; there are no commands or markers
-# available.
-
-LATEX_EXTRA_FILES      =
-
-# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
-# is prepared for conversion to pdf (using ps2pdf). The pdf file will
-# contain links (just like the HTML output) instead of page references
-# This makes the output suitable for online browsing using a pdf viewer.
-
-PDF_HYPERLINKS         = YES
-
-# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
-# plain latex in the generated Makefile. Set this option to YES to get a
-# higher quality PDF documentation.
-
-USE_PDFLATEX           = YES
-
-# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
-# command to the generated LaTeX files. This will instruct LaTeX to keep
-# running if errors occur, instead of asking the user for help.
-# This option is also used when generating formulas in HTML.
-
-LATEX_BATCHMODE        = NO
-
-# If LATEX_HIDE_INDICES is set to YES then doxygen will not
-# include the index chapters (such as File Index, Compound Index, etc.)
-# in the output.
-
-LATEX_HIDE_INDICES     = NO
-
-# If LATEX_SOURCE_CODE is set to YES then doxygen will include
-# source code with syntax highlighting in the LaTeX output.
-# Note that which sources are shown also depends on other settings
-# such as SOURCE_BROWSER.
-
-LATEX_SOURCE_CODE      = NO
-
-# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
-# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See
-# http://en.wikipedia.org/wiki/BibTeX for more info.
-
-LATEX_BIB_STYLE        = plain
-
-#---------------------------------------------------------------------------
-# configuration options related to the RTF output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
-# The RTF output is optimized for Word 97 and may not look very pretty with
-# other RTF readers or editors.
-
-GENERATE_RTF           = NO
-
-# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `rtf' will be used as the default path.
-
-RTF_OUTPUT             = rtf
-
-# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
-# RTF documents. This may be useful for small projects and may help to
-# save some trees in general.
-
-COMPACT_RTF            = NO
-
-# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
-# will contain hyperlink fields. The RTF file will
-# contain links (just like the HTML output) instead of page references.
-# This makes the output suitable for online browsing using WORD or other
-# programs which support those fields.
-# Note: wordpad (write) and others do not support links.
-
-RTF_HYPERLINKS         = NO
-
-# Load style sheet definitions from file. Syntax is similar to doxygen's
-# config file, i.e. a series of assignments. You only have to provide
-# replacements, missing definitions are set to their default value.
-
-RTF_STYLESHEET_FILE    =
-
-# Set optional variables used in the generation of an rtf document.
-# Syntax is similar to doxygen's config file.
-
-RTF_EXTENSIONS_FILE    =
-
-#---------------------------------------------------------------------------
-# configuration options related to the man page output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
-# generate man pages
-
-GENERATE_MAN           = NO
-
-# The MAN_OUTPUT tag is used to specify where the man pages will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `man' will be used as the default path.
-
-MAN_OUTPUT             = man
-
-# The MAN_EXTENSION tag determines the extension that is added to
-# the generated man pages (default is the subroutine's section .3)
-
-MAN_EXTENSION          = .3
-
-# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
-# then it will generate one additional man file for each entity
-# documented in the real man page(s). These additional files
-# only source the real man page, but without them the man command
-# would be unable to find the correct page. The default is NO.
-
-MAN_LINKS              = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to the XML output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_XML tag is set to YES Doxygen will
-# generate an XML file that captures the structure of
-# the code including all documentation.
-
-GENERATE_XML           = NO
-
-# The XML_OUTPUT tag is used to specify where the XML pages will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `xml' will be used as the default path.
-
-XML_OUTPUT             = xml
-
-# The XML_SCHEMA tag can be used to specify an XML schema,
-# which can be used by a validating XML parser to check the
-# syntax of the XML files.
-
-#XML_SCHEMA             =
-
-# The XML_DTD tag can be used to specify an XML DTD,
-# which can be used by a validating XML parser to check the
-# syntax of the XML files.
-
-#XML_DTD                =
-
-# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
-# dump the program listings (including syntax highlighting
-# and cross-referencing information) to the XML output. Note that
-# enabling this will significantly increase the size of the XML output.
-
-XML_PROGRAMLISTING     = YES
-
-#---------------------------------------------------------------------------
-# configuration options related to the DOCBOOK output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_DOCBOOK tag is set to YES Doxygen will generate DOCBOOK files
-# that can be used to generate PDF.
-
-GENERATE_DOCBOOK       = NO
-
-# The DOCBOOK_OUTPUT tag is used to specify where the DOCBOOK pages will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
-# front of it. If left blank docbook will be used as the default path.
-
-DOCBOOK_OUTPUT         = docbook
-
-#---------------------------------------------------------------------------
-# configuration options for the AutoGen Definitions output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
-# generate an AutoGen Definitions (see autogen.sf.net) file
-# that captures the structure of the code including all
-# documentation. Note that this feature is still experimental
-# and incomplete at the moment.
-
-GENERATE_AUTOGEN_DEF   = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to the Perl module output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_PERLMOD tag is set to YES Doxygen will
-# generate a Perl module file that captures the structure of
-# the code including all documentation. Note that this
-# feature is still experimental and incomplete at the
-# moment.
-
-GENERATE_PERLMOD       = NO
-
-# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
-# the necessary Makefile rules, Perl scripts and LaTeX code to be able
-# to generate PDF and DVI output from the Perl module output.
-
-PERLMOD_LATEX          = NO
-
-# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
-# nicely formatted so it can be parsed by a human reader.
-# This is useful
-# if you want to understand what is going on.
-# On the other hand, if this
-# tag is set to NO the size of the Perl module output will be much smaller
-# and Perl will parse it just the same.
-
-PERLMOD_PRETTY         = YES
-
-# The names of the make variables in the generated doxyrules.make file
-# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
-# This is useful so different doxyrules.make files included by the same
-# Makefile don't overwrite each other's variables.
-
-PERLMOD_MAKEVAR_PREFIX =
-
-#---------------------------------------------------------------------------
-# Configuration options related to the preprocessor
-#---------------------------------------------------------------------------
-
-# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
-# evaluate all C-preprocessor directives found in the sources and include
-# files.
-
-ENABLE_PREPROCESSING   = YES
-
-# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
-# names in the source code. If set to NO (the default) only conditional
-# compilation will be performed. Macro expansion can be done in a controlled
-# way by setting EXPAND_ONLY_PREDEF to YES.
-
-MACRO_EXPANSION        = NO
-
-# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
-# then the macro expansion is limited to the macros specified with the
-# PREDEFINED and EXPAND_AS_DEFINED tags.
-
-EXPAND_ONLY_PREDEF     = NO
-
-# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
-# pointed to by INCLUDE_PATH will be searched when a #include is found.
-
-SEARCH_INCLUDES        = YES
-
-# The INCLUDE_PATH tag can be used to specify one or more directories that
-# contain include files that are not input files but should be processed by
-# the preprocessor.
-
+GENERATE_XML           = YES
+MACRO_EXPANSION        = YES
+EXPAND_ONLY_PREDEF     = YES
 INCLUDE_PATH           = ../include
-
-# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
-# patterns (like *.h and *.hpp) to filter out the header-files in the
-# directories. If left blank, the patterns specified with FILE_PATTERNS will
-# be used.
-
-INCLUDE_FILE_PATTERNS  =
-
-# The PREDEFINED tag can be used to specify one or more macro names that
-# are defined before the preprocessor is started (similar to the -D option of
-# gcc). The argument of the tag is a list of macros of the form: name
-# or name=definition (no spaces). If the definition and the = are
-# omitted =1 is assumed. To prevent a macro definition from being
-# undefined via #undef or recursively expanded use the := operator
-# instead of the = operator.
-
-PREDEFINED             = WIN32 \
-                         NTLM \
-                         USE_LZO \
-                         ENABLE_FRAGMENT \
-                         P2MP \
-                         P2MP_SERVER \
-                         USE_CRYPTO \
-                         USE_SSL \
-                         ENABLE_PLUGIN \
-                         ENABLE_MANAGEMENT \
-                         ENABLE_OCC \
-                         HAVE_GETTIMEOFDAY
-
-# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
-# this tag can be used to specify a list of macro names that should be expanded.
-# The macro definition that is found in the sources will be used.
-# Use the PREDEFINED tag if you want to use a different macro definition that
-# overrules the definition found in the source code.
-
-EXPAND_AS_DEFINED      =
-
-# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
-# doxygen's preprocessor will remove all references to function-like macros
-# that are alone on a line, have an all uppercase name, and do not end with a
-# semicolon, because these will confuse the parser if not removed.
-
-SKIP_FUNCTION_MACROS   = YES
-
-#---------------------------------------------------------------------------
-# Configuration::additions related to external references
-#---------------------------------------------------------------------------
-
-# The TAGFILES option can be used to specify one or more tagfiles. For each
-# tag file the location of the external documentation should be added. The
-# format of a tag file without this location is as follows:
-#
-# TAGFILES = file1 file2 ...
-# Adding location for the tag files is done as follows:
-#
-# TAGFILES = file1=loc1 "file2 = loc2" ...
-# where "loc1" and "loc2" can be relative or absolute paths
-# or URLs. Note that each tag file must have a unique name (where the name does
-# NOT include the path). If a tag file is not located in the directory in which
-# doxygen is run, you must also specify the path to the tagfile here.
-
-TAGFILES               =
-
-# When a file name is specified after GENERATE_TAGFILE, doxygen will create
-# a tag file that is based on the input files it reads.
-
-GENERATE_TAGFILE       =
-
-# If the ALLEXTERNALS tag is set to YES all external classes will be listed
-# in the class index. If set to NO only the inherited external classes
-# will be listed.
-
-ALLEXTERNALS           = NO
-
-# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
-# in the modules index. If set to NO, only the current project's groups will
-# be listed.
-
-EXTERNAL_GROUPS        = YES
-
-# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed
-# in the related pages index. If set to NO, only the current project's
-# pages will be listed.
-
-EXTERNAL_PAGES         = YES
-
-# The PERL_PATH should be the absolute path and name of the perl script
-# interpreter (i.e. the result of `which perl').
-
-PERL_PATH              = /usr/bin/perl
-
-#---------------------------------------------------------------------------
-# Configuration options related to the dot tool
-#---------------------------------------------------------------------------
-
-# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
-# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
-# or super classes. Setting the tag to NO turns the diagrams off. Note that
-# this option also works with HAVE_DOT disabled, but it is recommended to
-# install and use dot, since it yields more powerful graphs.
-
 CLASS_DIAGRAMS         = NO
-
-# You can define message sequence charts within doxygen comments using the \msc
-# command. Doxygen will then run the mscgen tool (see
-# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
-# documentation. The MSCGEN_PATH tag allows you to specify the directory where
-# the mscgen tool resides. If left empty the tool is assumed to be found in the
-# default search path.
-
-MSCGEN_PATH            =
-
-# If set to YES, the inheritance and collaboration graphs will hide
-# inheritance and usage relations if the target is undocumented
-# or is not a class.
-
-HIDE_UNDOC_RELATIONS   = YES
-
-# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
-# available from the path. This tool is part of Graphviz, a graph visualization
-# toolkit from AT&T and Lucent Bell Labs. The other options in this section
-# have no effect if this option is set to NO (the default)
-
 HAVE_DOT               = YES
-
-# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
-# allowed to run in parallel. When set to 0 (the default) doxygen will
-# base this on the number of processors available in the system. You can set it
-# explicitly to a value larger than 0 to get control over the balance
-# between CPU load and processing speed.
-
-DOT_NUM_THREADS        = 0
-
-# By default doxygen will use the Helvetica font for all dot files that
-# doxygen generates. When you want a differently looking font you can specify
-# the font name using DOT_FONTNAME. You need to make sure dot is able to find
-# the font, which can be done by putting it in a standard location or by setting
-# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
-# directory containing the font.
-
-DOT_FONTNAME           = Helvetica
-
-# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
-# The default size is 10pt.
-
-DOT_FONTSIZE           = 10
-
-# By default doxygen will tell dot to use the Helvetica font.
-# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to
-# set the path where dot can find it.
-
-DOT_FONTPATH           =
-
-# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for each documented class showing the direct and
-# indirect inheritance relations. Setting this tag to YES will force the
-# CLASS_DIAGRAMS tag to NO.
-
-CLASS_GRAPH            = YES
-
-# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for each documented class showing the direct and
-# indirect implementation dependencies (inheritance, containment, and
-# class references variables) of the class with other documented classes.
-
-COLLABORATION_GRAPH    = YES
-
-# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for groups, showing the direct groups dependencies
-
-GROUP_GRAPHS           = YES
-
-# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
-# collaboration diagrams in a style similar to the OMG's Unified Modeling
-# Language.
-
-UML_LOOK               = NO
-
-# If the UML_LOOK tag is enabled, the fields and methods are shown inside
-# the class node. If there are many fields or methods and many nodes the
-# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS
-# threshold limits the number of items for each type to make the size more
-# manageable. Set this to 0 for no limit. Note that the threshold may be
-# exceeded by 50% before the limit is enforced.
-
-UML_LIMIT_NUM_FIELDS   = 10
-
-# If set to YES, the inheritance and collaboration graphs will show the
-# relations between templates and their instances.
-
-TEMPLATE_RELATIONS     = NO
-
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
-# tags are set to YES then doxygen will generate a graph for each documented
-# file showing the direct and indirect include dependencies of the file with
-# other documented files.
-
-INCLUDE_GRAPH          = YES
-
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
-# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
-# documented header file showing the documented files that directly or
-# indirectly include this file.
-
-INCLUDED_BY_GRAPH      = YES
-
-# If the CALL_GRAPH and HAVE_DOT options are set to YES then
-# doxygen will generate a call dependency graph for every global function
-# or class method. Note that enabling this option will significantly increase
-# the time of a run. So in most cases it will be better to enable call graphs
-# for selected functions only using the \callgraph command.
-
-CALL_GRAPH             = NO
-
-# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
-# doxygen will generate a caller dependency graph for every global function
-# or class method. Note that enabling this option will significantly increase
-# the time of a run. So in most cases it will be better to enable caller
-# graphs for selected functions only using the \callergraph command.
-
-CALLER_GRAPH           = NO
-
-# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
-# will generate a graphical hierarchy of all classes instead of a textual one.
-
-GRAPHICAL_HIERARCHY    = YES
-
-# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES
-# then doxygen will show the dependencies a directory has on other directories
-# in a graphical way. The dependency relations are determined by the #include
-# relations between the files in the directories.
-
-DIRECTORY_GRAPH        = YES
-
-# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
-# generated by dot. Possible values are svg, png, jpg, or gif.
-# If left blank png will be used. If you choose svg you need to set
-# HTML_FILE_EXTENSION to xhtml in order to make the SVG files
-# visible in IE 9+ (other browsers do not have this requirement).
-
-DOT_IMAGE_FORMAT       = png
-
-# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
-# enable generation of interactive SVG images that allow zooming and panning.
-# Note that this requires a modern browser other than Internet Explorer.
-# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you
-# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files
-# visible. Older versions of IE do not have SVG support.
-
-INTERACTIVE_SVG        = NO
-
-# The tag DOT_PATH can be used to specify the path where the dot tool can be
-# found. If left blank, it is assumed the dot tool can be found in the path.
-
-DOT_PATH               =
-
-# The DOTFILE_DIRS tag can be used to specify one or more directories that
-# contain dot files that are included in the documentation (see the
-# \dotfile command).
-
-DOTFILE_DIRS           =
-
-# The MSCFILE_DIRS tag can be used to specify one or more directories that
-# contain msc files that are included in the documentation (see the
-# \mscfile command).
-
-MSCFILE_DIRS           =
-
-# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
-# nodes that will be shown in the graph. If the number of nodes in a graph
-# becomes larger than this value, doxygen will truncate the graph, which is
-# visualized by representing a node as a red box. Note that doxygen if the
-# number of direct children of the root node in a graph is already larger than
-# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
-# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
-
 DOT_GRAPH_MAX_NODES    = 200
-
-# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
-# graphs generated by dot. A depth value of 3 means that only nodes reachable
-# from the root by following a path via at most 3 edges will be shown. Nodes
-# that lay further from the root node will be omitted. Note that setting this
-# option to 1 or 2 may greatly reduce the computation time needed for large
-# code bases. Also note that the size of a graph can be further restricted by
-# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
-
 MAX_DOT_GRAPH_DEPTH    = 1000
-
-# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
-# background. This is disabled by default, because dot on Windows does not
-# seem to support this out of the box. Warning: Depending on the platform used,
-# enabling this option may lead to badly anti-aliased labels on the edges of
-# a graph (i.e. they become hard to read).
-
 DOT_TRANSPARENT        = YES
 
-# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
-# files in one run (i.e. multiple -o and -T options on the command line). This
-# makes dot run faster, but since only newer versions of dot (>1.8.10)
-# support this, this feature is disabled by default.
+# We mostly use \retval declarations to document which error codes a function
+# can return. The reader can follow the hyperlink to the definition of the
+# constant to get the generic documentation of that error code. If we don't
+# have anything to say about the specific error code for the specific
+# function, we can leave the description part of the \retval command blank.
+# This is perfectly valid as far as Doxygen is concerned. However, with
+# Clang >=15, the -Wdocumentation option emits a warning for empty
+# descriptions.
+#   https://github.com/Mbed-TLS/mbedtls/issues/6960
+#   https://github.com/llvm/llvm-project/issues/60315
+# As a workaround, you can write something like
+#     \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+# This avoids writing redundant text and keeps Clang happy.
+ALIASES += emptydescription=""
 
-DOT_MULTI_TARGETS      = NO
+# Define away Mbed TLS macros that make parsing definitions difficult.
+# MBEDTLS_DEPRECATED is not included in this list as it's important to
+# display deprecated status in the documentation.
+PREDEFINED             = "MBEDTLS_CHECK_RETURN_CRITICAL="   \
+                         "MBEDTLS_CHECK_RETURN_TYPICAL="    \
+                         "MBEDTLS_CHECK_RETURN_OPTIONAL="   \
+                         "MBEDTLS_PRINTF_ATTRIBUTE(a,b)="   \
+                         "__DOXYGEN__"                      \
 
-# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
-# generate a legend page explaining the meaning of the various boxes and
-# arrows in the dot generated graphs.
-
-GENERATE_LEGEND        = YES
-
-# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
-# remove the intermediate dot files that are used to generate
-# the various graphs.
-
-DOT_CLEANUP            = YES
diff --git a/third_party/mbedtls/repo/include/CMakeLists.txt b/third_party/mbedtls/repo/include/CMakeLists.txt
index 62c0f62..11b417b 100644
--- a/third_party/mbedtls/repo/include/CMakeLists.txt
+++ b/third_party/mbedtls/repo/include/CMakeLists.txt
@@ -1,4 +1,4 @@
-option(INSTALL_MBEDTLS_HEADERS "Install mbed TLS headers." ON)
+option(INSTALL_MBEDTLS_HEADERS "Install Mbed TLS headers." ON)
 
 if(INSTALL_MBEDTLS_HEADERS)
 
diff --git a/third_party/mbedtls/repo/include/mbedtls/aes.h b/third_party/mbedtls/repo/include/mbedtls/aes.h
index e280dbb..2623a42 100644
--- a/third_party/mbedtls/repo/include/mbedtls/aes.h
+++ b/third_party/mbedtls/repo/include/mbedtls/aes.h
@@ -22,19 +22,7 @@
 
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 
 #ifndef MBEDTLS_AES_H
@@ -72,7 +60,7 @@
 /** AES hardware accelerator failed. */
 #define MBEDTLS_ERR_AES_HW_ACCEL_FAILED                   -0x0025
 
-#if ( defined(__ARMCC_VERSION) || defined(_MSC_VER) ) && \
+#if (defined(__ARMCC_VERSION) || defined(_MSC_VER)) && \
     !defined(inline) && !defined(__cplusplus)
 #define inline __inline
 #endif
@@ -88,8 +76,7 @@
 /**
  * \brief The AES context-type definition.
  */
-typedef struct mbedtls_aes_context
-{
+typedef struct mbedtls_aes_context {
     int nr;                     /*!< The number of rounds. */
     uint32_t *rk;               /*!< AES round keys. */
     uint32_t buf[68];           /*!< Unaligned data buffer. This buffer can
@@ -107,8 +94,7 @@
 /**
  * \brief The AES XTS context-type definition.
  */
-typedef struct mbedtls_aes_xts_context
-{
+typedef struct mbedtls_aes_xts_context {
     mbedtls_aes_context crypt; /*!< The AES context to use for AES block
                                         encryption or decryption. */
     mbedtls_aes_context tweak; /*!< The AES context used for tweak
@@ -128,7 +114,7 @@
  *
  * \param ctx      The AES context to initialize. This must not be \c NULL.
  */
-void mbedtls_aes_init( mbedtls_aes_context *ctx );
+void mbedtls_aes_init(mbedtls_aes_context *ctx);
 
 /**
  * \brief          This function releases and clears the specified AES context.
@@ -137,7 +123,7 @@
  *                 If this is \c NULL, this function does nothing.
  *                 Otherwise, the context must have been at least initialized.
  */
-void mbedtls_aes_free( mbedtls_aes_context *ctx );
+void mbedtls_aes_free(mbedtls_aes_context *ctx);
 
 #if defined(MBEDTLS_CIPHER_MODE_XTS)
 /**
@@ -148,7 +134,7 @@
  *
  * \param ctx      The AES XTS context to initialize. This must not be \c NULL.
  */
-void mbedtls_aes_xts_init( mbedtls_aes_xts_context *ctx );
+void mbedtls_aes_xts_init(mbedtls_aes_xts_context *ctx);
 
 /**
  * \brief          This function releases and clears the specified AES XTS context.
@@ -157,7 +143,7 @@
  *                 If this is \c NULL, this function does nothing.
  *                 Otherwise, the context must have been at least initialized.
  */
-void mbedtls_aes_xts_free( mbedtls_aes_xts_context *ctx );
+void mbedtls_aes_xts_free(mbedtls_aes_xts_context *ctx);
 #endif /* MBEDTLS_CIPHER_MODE_XTS */
 
 /**
@@ -176,8 +162,8 @@
  * \return         #MBEDTLS_ERR_AES_INVALID_KEY_LENGTH on failure.
  */
 MBEDTLS_CHECK_RETURN_TYPICAL
-int mbedtls_aes_setkey_enc( mbedtls_aes_context *ctx, const unsigned char *key,
-                    unsigned int keybits );
+int mbedtls_aes_setkey_enc(mbedtls_aes_context *ctx, const unsigned char *key,
+                           unsigned int keybits);
 
 /**
  * \brief          This function sets the decryption key.
@@ -195,8 +181,8 @@
  * \return         #MBEDTLS_ERR_AES_INVALID_KEY_LENGTH on failure.
  */
 MBEDTLS_CHECK_RETURN_TYPICAL
-int mbedtls_aes_setkey_dec( mbedtls_aes_context *ctx, const unsigned char *key,
-                    unsigned int keybits );
+int mbedtls_aes_setkey_dec(mbedtls_aes_context *ctx, const unsigned char *key,
+                           unsigned int keybits);
 
 #if defined(MBEDTLS_CIPHER_MODE_XTS)
 /**
@@ -216,9 +202,9 @@
  * \return         #MBEDTLS_ERR_AES_INVALID_KEY_LENGTH on failure.
  */
 MBEDTLS_CHECK_RETURN_TYPICAL
-int mbedtls_aes_xts_setkey_enc( mbedtls_aes_xts_context *ctx,
-                                const unsigned char *key,
-                                unsigned int keybits );
+int mbedtls_aes_xts_setkey_enc(mbedtls_aes_xts_context *ctx,
+                               const unsigned char *key,
+                               unsigned int keybits);
 
 /**
  * \brief          This function prepares an XTS context for decryption and
@@ -237,9 +223,9 @@
  * \return         #MBEDTLS_ERR_AES_INVALID_KEY_LENGTH on failure.
  */
 MBEDTLS_CHECK_RETURN_TYPICAL
-int mbedtls_aes_xts_setkey_dec( mbedtls_aes_xts_context *ctx,
-                                const unsigned char *key,
-                                unsigned int keybits );
+int mbedtls_aes_xts_setkey_dec(mbedtls_aes_xts_context *ctx,
+                               const unsigned char *key,
+                               unsigned int keybits);
 #endif /* MBEDTLS_CIPHER_MODE_XTS */
 
 /**
@@ -266,10 +252,10 @@
  * \return         \c 0 on success.
  */
 MBEDTLS_CHECK_RETURN_TYPICAL
-int mbedtls_aes_crypt_ecb( mbedtls_aes_context *ctx,
-                    int mode,
-                    const unsigned char input[16],
-                    unsigned char output[16] );
+int mbedtls_aes_crypt_ecb(mbedtls_aes_context *ctx,
+                          int mode,
+                          const unsigned char input[16],
+                          unsigned char output[16]);
 
 #if defined(MBEDTLS_CIPHER_MODE_CBC)
 /**
@@ -314,12 +300,12 @@
  *                 on failure.
  */
 MBEDTLS_CHECK_RETURN_TYPICAL
-int mbedtls_aes_crypt_cbc( mbedtls_aes_context *ctx,
-                    int mode,
-                    size_t length,
-                    unsigned char iv[16],
-                    const unsigned char *input,
-                    unsigned char *output );
+int mbedtls_aes_crypt_cbc(mbedtls_aes_context *ctx,
+                          int mode,
+                          size_t length,
+                          unsigned char iv[16],
+                          const unsigned char *input,
+                          unsigned char *output);
 #endif /* MBEDTLS_CIPHER_MODE_CBC */
 
 #if defined(MBEDTLS_CIPHER_MODE_XTS)
@@ -359,12 +345,12 @@
  *                     length is larger than 2^20 blocks (16 MiB).
  */
 MBEDTLS_CHECK_RETURN_TYPICAL
-int mbedtls_aes_crypt_xts( mbedtls_aes_xts_context *ctx,
-                           int mode,
-                           size_t length,
-                           const unsigned char data_unit[16],
-                           const unsigned char *input,
-                           unsigned char *output );
+int mbedtls_aes_crypt_xts(mbedtls_aes_xts_context *ctx,
+                          int mode,
+                          size_t length,
+                          const unsigned char data_unit[16],
+                          const unsigned char *input,
+                          unsigned char *output);
 #endif /* MBEDTLS_CIPHER_MODE_XTS */
 
 #if defined(MBEDTLS_CIPHER_MODE_CFB)
@@ -408,13 +394,13 @@
  * \return         \c 0 on success.
  */
 MBEDTLS_CHECK_RETURN_TYPICAL
-int mbedtls_aes_crypt_cfb128( mbedtls_aes_context *ctx,
-                       int mode,
-                       size_t length,
-                       size_t *iv_off,
-                       unsigned char iv[16],
-                       const unsigned char *input,
-                       unsigned char *output );
+int mbedtls_aes_crypt_cfb128(mbedtls_aes_context *ctx,
+                             int mode,
+                             size_t length,
+                             size_t *iv_off,
+                             unsigned char iv[16],
+                             const unsigned char *input,
+                             unsigned char *output);
 
 /**
  * \brief This function performs an AES-CFB8 encryption or decryption
@@ -453,12 +439,12 @@
  * \return         \c 0 on success.
  */
 MBEDTLS_CHECK_RETURN_TYPICAL
-int mbedtls_aes_crypt_cfb8( mbedtls_aes_context *ctx,
-                    int mode,
-                    size_t length,
-                    unsigned char iv[16],
-                    const unsigned char *input,
-                    unsigned char *output );
+int mbedtls_aes_crypt_cfb8(mbedtls_aes_context *ctx,
+                           int mode,
+                           size_t length,
+                           unsigned char iv[16],
+                           const unsigned char *input,
+                           unsigned char *output);
 #endif /*MBEDTLS_CIPHER_MODE_CFB */
 
 #if defined(MBEDTLS_CIPHER_MODE_OFB)
@@ -508,12 +494,12 @@
  * \return         \c 0 on success.
  */
 MBEDTLS_CHECK_RETURN_TYPICAL
-int mbedtls_aes_crypt_ofb( mbedtls_aes_context *ctx,
-                       size_t length,
-                       size_t *iv_off,
-                       unsigned char iv[16],
-                       const unsigned char *input,
-                       unsigned char *output );
+int mbedtls_aes_crypt_ofb(mbedtls_aes_context *ctx,
+                          size_t length,
+                          size_t *iv_off,
+                          unsigned char iv[16],
+                          const unsigned char *input,
+                          unsigned char *output);
 
 #endif /* MBEDTLS_CIPHER_MODE_OFB */
 
@@ -564,7 +550,7 @@
  *             for example, with 96-bit random nonces, you should not encrypt
  *             more than 2**32 messages with the same key.
  *
- *             Note that for both stategies, sizes are measured in blocks and
+ *             Note that for both strategies, sizes are measured in blocks and
  *             that an AES block is 16 bytes.
  *
  * \warning    Upon return, \p stream_block contains sensitive data. Its
@@ -591,13 +577,13 @@
  * \return                 \c 0 on success.
  */
 MBEDTLS_CHECK_RETURN_TYPICAL
-int mbedtls_aes_crypt_ctr( mbedtls_aes_context *ctx,
-                       size_t length,
-                       size_t *nc_off,
-                       unsigned char nonce_counter[16],
-                       unsigned char stream_block[16],
-                       const unsigned char *input,
-                       unsigned char *output );
+int mbedtls_aes_crypt_ctr(mbedtls_aes_context *ctx,
+                          size_t length,
+                          size_t *nc_off,
+                          unsigned char nonce_counter[16],
+                          unsigned char stream_block[16],
+                          const unsigned char *input,
+                          unsigned char *output);
 #endif /* MBEDTLS_CIPHER_MODE_CTR */
 
 /**
@@ -612,9 +598,9 @@
  * \return          \c 0 on success.
  */
 MBEDTLS_CHECK_RETURN_TYPICAL
-int mbedtls_internal_aes_encrypt( mbedtls_aes_context *ctx,
-                                  const unsigned char input[16],
-                                  unsigned char output[16] );
+int mbedtls_internal_aes_encrypt(mbedtls_aes_context *ctx,
+                                 const unsigned char input[16],
+                                 unsigned char output[16]);
 
 /**
  * \brief           Internal AES block decryption function. This is only
@@ -628,9 +614,9 @@
  * \return          \c 0 on success.
  */
 MBEDTLS_CHECK_RETURN_TYPICAL
-int mbedtls_internal_aes_decrypt( mbedtls_aes_context *ctx,
-                                  const unsigned char input[16],
-                                  unsigned char output[16] );
+int mbedtls_internal_aes_decrypt(mbedtls_aes_context *ctx,
+                                 const unsigned char input[16],
+                                 unsigned char output[16]);
 
 #if !defined(MBEDTLS_DEPRECATED_REMOVED)
 #if defined(MBEDTLS_DEPRECATED_WARNING)
@@ -648,9 +634,9 @@
  * \param input     Plaintext block.
  * \param output    Output (ciphertext) block.
  */
-MBEDTLS_DEPRECATED void mbedtls_aes_encrypt( mbedtls_aes_context *ctx,
-                                             const unsigned char input[16],
-                                             unsigned char output[16] );
+MBEDTLS_DEPRECATED void mbedtls_aes_encrypt(mbedtls_aes_context *ctx,
+                                            const unsigned char input[16],
+                                            unsigned char output[16]);
 
 /**
  * \brief           Deprecated internal AES block decryption function
@@ -662,9 +648,9 @@
  * \param input     Ciphertext block.
  * \param output    Output (plaintext) block.
  */
-MBEDTLS_DEPRECATED void mbedtls_aes_decrypt( mbedtls_aes_context *ctx,
-                                             const unsigned char input[16],
-                                             unsigned char output[16] );
+MBEDTLS_DEPRECATED void mbedtls_aes_decrypt(mbedtls_aes_context *ctx,
+                                            const unsigned char input[16],
+                                            unsigned char output[16]);
 
 #undef MBEDTLS_DEPRECATED
 #endif /* !MBEDTLS_DEPRECATED_REMOVED */
@@ -678,7 +664,7 @@
  * \return         \c 1 on failure.
  */
 MBEDTLS_CHECK_RETURN_CRITICAL
-int mbedtls_aes_self_test( int verbose );
+int mbedtls_aes_self_test(int verbose);
 
 #endif /* MBEDTLS_SELF_TEST */
 
diff --git a/third_party/mbedtls/repo/include/mbedtls/aesni.h b/third_party/mbedtls/repo/include/mbedtls/aesni.h
index c1d22f5..b636c10 100644
--- a/third_party/mbedtls/repo/include/mbedtls/aesni.h
+++ b/third_party/mbedtls/repo/include/mbedtls/aesni.h
@@ -8,19 +8,7 @@
  */
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 #ifndef MBEDTLS_AESNI_H
 #define MBEDTLS_AESNI_H
@@ -36,13 +24,57 @@
 #define MBEDTLS_AESNI_AES      0x02000000u
 #define MBEDTLS_AESNI_CLMUL    0x00000002u
 
-#if defined(MBEDTLS_HAVE_ASM) && defined(__GNUC__) &&  \
-    ( defined(__amd64__) || defined(__x86_64__) )   &&  \
-    ! defined(MBEDTLS_HAVE_X86_64)
+#if !defined(MBEDTLS_HAVE_X86_64) && \
+    (defined(__amd64__) || defined(__x86_64__) || \
+    defined(_M_X64) || defined(_M_AMD64)) && \
+    !defined(_M_ARM64EC)
 #define MBEDTLS_HAVE_X86_64
 #endif
 
-#if defined(MBEDTLS_HAVE_X86_64)
+#if !defined(MBEDTLS_HAVE_X86) && \
+    (defined(__i386__) || defined(_M_IX86))
+#define MBEDTLS_HAVE_X86
+#endif
+
+#if defined(MBEDTLS_AESNI_C) && \
+    (defined(MBEDTLS_HAVE_X86_64) || defined(MBEDTLS_HAVE_X86))
+
+/* Can we do AESNI with intrinsics?
+ * (Only implemented with certain compilers, only for certain targets.)
+ *
+ * NOTE: MBEDTLS_AESNI_HAVE_INTRINSICS and MBEDTLS_AESNI_HAVE_CODE are internal
+ *       macros that may change in future releases.
+ */
+#undef MBEDTLS_AESNI_HAVE_INTRINSICS
+#if defined(_MSC_VER)
+/* Visual Studio supports AESNI intrinsics since VS 2008 SP1. We only support
+ * VS 2013 and up for other reasons anyway, so no need to check the version. */
+#define MBEDTLS_AESNI_HAVE_INTRINSICS
+#endif
+/* GCC-like compilers: currently, we only support intrinsics if the requisite
+ * target flag is enabled when building the library (e.g. `gcc -mpclmul -msse2`
+ * or `clang -maes -mpclmul`). */
+#if defined(__GNUC__) && defined(__AES__) && defined(__PCLMUL__)
+#define MBEDTLS_AESNI_HAVE_INTRINSICS
+#endif
+
+/* Choose the implementation of AESNI, if one is available. */
+#undef MBEDTLS_AESNI_HAVE_CODE
+/* To minimize disruption when releasing the intrinsics-based implementation,
+ * favor the assembly-based implementation if it's available. We intend to
+ * revise this in a later release of Mbed TLS 3.x. In the long run, we will
+ * likely remove the assembly implementation. */
+#if defined(MBEDTLS_HAVE_ASM) && \
+    defined(__GNUC__) && defined(MBEDTLS_HAVE_X86_64)
+/* Can we do AESNI with inline assembly?
+ * (Only implemented with gas syntax, only for 64-bit.)
+ */
+#define MBEDTLS_AESNI_HAVE_CODE 1 // via assembly
+#elif defined(MBEDTLS_AESNI_HAVE_INTRINSICS)
+#define MBEDTLS_AESNI_HAVE_CODE 2 // via intrinsics
+#endif
+
+#if defined(MBEDTLS_AESNI_HAVE_CODE)
 
 #ifdef __cplusplus
 extern "C" {
@@ -59,7 +91,7 @@
  *
  * \return         1 if CPU has support for the feature, 0 otherwise
  */
-int mbedtls_aesni_has_support( unsigned int what );
+int mbedtls_aesni_has_support(unsigned int what);
 
 /**
  * \brief          Internal AES-NI AES-ECB block encryption and decryption
@@ -74,10 +106,10 @@
  *
  * \return         0 on success (cannot fail)
  */
-int mbedtls_aesni_crypt_ecb( mbedtls_aes_context *ctx,
-                             int mode,
-                             const unsigned char input[16],
-                             unsigned char output[16] );
+int mbedtls_aesni_crypt_ecb(mbedtls_aes_context *ctx,
+                            int mode,
+                            const unsigned char input[16],
+                            unsigned char output[16]);
 
 /**
  * \brief          Internal GCM multiplication: c = a * b in GF(2^128)
@@ -92,9 +124,9 @@
  * \note           Both operands and result are bit strings interpreted as
  *                 elements of GF(2^128) as per the GCM spec.
  */
-void mbedtls_aesni_gcm_mult( unsigned char c[16],
-                             const unsigned char a[16],
-                             const unsigned char b[16] );
+void mbedtls_aesni_gcm_mult(unsigned char c[16],
+                            const unsigned char a[16],
+                            const unsigned char b[16]);
 
 /**
  * \brief           Internal round key inversion. This function computes
@@ -107,9 +139,9 @@
  * \param fwdkey    Original round keys (for encryption)
  * \param nr        Number of rounds (that is, number of round keys minus one)
  */
-void mbedtls_aesni_inverse_key( unsigned char *invkey,
-                                const unsigned char *fwdkey,
-                                int nr );
+void mbedtls_aesni_inverse_key(unsigned char *invkey,
+                               const unsigned char *fwdkey,
+                               int nr);
 
 /**
  * \brief           Internal key expansion for encryption
@@ -123,14 +155,15 @@
  *
  * \return          0 if successful, or MBEDTLS_ERR_AES_INVALID_KEY_LENGTH
  */
-int mbedtls_aesni_setkey_enc( unsigned char *rk,
-                              const unsigned char *key,
-                              size_t bits );
+int mbedtls_aesni_setkey_enc(unsigned char *rk,
+                             const unsigned char *key,
+                             size_t bits);
 
 #ifdef __cplusplus
 }
 #endif
 
-#endif /* MBEDTLS_HAVE_X86_64 */
+#endif /* MBEDTLS_AESNI_HAVE_CODE */
+#endif /* MBEDTLS_AESNI_C && (MBEDTLS_HAVE_X86_64 || MBEDTLS_HAVE_X86) */
 
 #endif /* MBEDTLS_AESNI_H */
diff --git a/third_party/mbedtls/repo/include/mbedtls/arc4.h b/third_party/mbedtls/repo/include/mbedtls/arc4.h
index f4b0f9f..1f813aa 100644
--- a/third_party/mbedtls/repo/include/mbedtls/arc4.h
+++ b/third_party/mbedtls/repo/include/mbedtls/arc4.h
@@ -8,19 +8,7 @@
  */
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  *
  */
 #ifndef MBEDTLS_ARC4_H
@@ -53,8 +41,7 @@
  *            security risk. We recommend considering stronger ciphers instead.
  *
  */
-typedef struct mbedtls_arc4_context
-{
+typedef struct mbedtls_arc4_context {
     int x;                      /*!< permutation index */
     int y;                      /*!< permutation index */
     unsigned char m[256];       /*!< permutation table */
@@ -75,7 +62,7 @@
  *                 instead.
  *
  */
-void mbedtls_arc4_init( mbedtls_arc4_context *ctx );
+void mbedtls_arc4_init(mbedtls_arc4_context *ctx);
 
 /**
  * \brief          Clear ARC4 context
@@ -87,7 +74,7 @@
  *                 instead.
  *
  */
-void mbedtls_arc4_free( mbedtls_arc4_context *ctx );
+void mbedtls_arc4_free(mbedtls_arc4_context *ctx);
 
 /**
  * \brief          ARC4 key schedule
@@ -101,8 +88,8 @@
  *                 instead.
  *
  */
-void mbedtls_arc4_setup( mbedtls_arc4_context *ctx, const unsigned char *key,
-                 unsigned int keylen );
+void mbedtls_arc4_setup(mbedtls_arc4_context *ctx, const unsigned char *key,
+                        unsigned int keylen);
 
 /**
  * \brief          ARC4 cipher function
@@ -119,8 +106,8 @@
  *                 instead.
  *
  */
-int mbedtls_arc4_crypt( mbedtls_arc4_context *ctx, size_t length, const unsigned char *input,
-                unsigned char *output );
+int mbedtls_arc4_crypt(mbedtls_arc4_context *ctx, size_t length, const unsigned char *input,
+                       unsigned char *output);
 
 #if defined(MBEDTLS_SELF_TEST)
 
@@ -134,7 +121,7 @@
  *                 instead.
  *
  */
-int mbedtls_arc4_self_test( int verbose );
+int mbedtls_arc4_self_test(int verbose);
 
 #endif /* MBEDTLS_SELF_TEST */
 
diff --git a/third_party/mbedtls/repo/include/mbedtls/aria.h b/third_party/mbedtls/repo/include/mbedtls/aria.h
index 226e2db..e360aa6 100644
--- a/third_party/mbedtls/repo/include/mbedtls/aria.h
+++ b/third_party/mbedtls/repo/include/mbedtls/aria.h
@@ -11,19 +11,7 @@
  */
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 
 #ifndef MBEDTLS_ARIA_H
@@ -44,11 +32,11 @@
 #define MBEDTLS_ARIA_DECRYPT     0 /**< ARIA decryption. */
 
 #define MBEDTLS_ARIA_BLOCKSIZE   16 /**< ARIA block size in bytes. */
-#define MBEDTLS_ARIA_MAX_ROUNDS  16 /**< Maxiumum number of rounds in ARIA. */
+#define MBEDTLS_ARIA_MAX_ROUNDS  16 /**< Maximum number of rounds in ARIA. */
 #define MBEDTLS_ARIA_MAX_KEYSIZE 32 /**< Maximum size of an ARIA key in bytes. */
 
 #if !defined(MBEDTLS_DEPRECATED_REMOVED)
-#define MBEDTLS_ERR_ARIA_INVALID_KEY_LENGTH   MBEDTLS_DEPRECATED_NUMERIC_CONSTANT( -0x005C )
+#define MBEDTLS_ERR_ARIA_INVALID_KEY_LENGTH   MBEDTLS_DEPRECATED_NUMERIC_CONSTANT(-0x005C)
 #endif /* !MBEDTLS_DEPRECATED_REMOVED */
 /** Bad input data. */
 #define MBEDTLS_ERR_ARIA_BAD_INPUT_DATA -0x005C
@@ -76,8 +64,7 @@
 /**
  * \brief The ARIA context-type definition.
  */
-typedef struct mbedtls_aria_context
-{
+typedef struct mbedtls_aria_context {
     unsigned char nr;           /*!< The number of rounds (12, 14 or 16) */
     /*! The ARIA round keys. */
     uint32_t rk[MBEDTLS_ARIA_MAX_ROUNDS + 1][MBEDTLS_ARIA_BLOCKSIZE / 4];
@@ -96,7 +83,7 @@
  *
  * \param ctx      The ARIA context to initialize. This must not be \c NULL.
  */
-void mbedtls_aria_init( mbedtls_aria_context *ctx );
+void mbedtls_aria_init(mbedtls_aria_context *ctx);
 
 /**
  * \brief          This function releases and clears the specified ARIA context.
@@ -105,7 +92,7 @@
  *                 case this function returns immediately. If it is not \c NULL,
  *                 it must point to an initialized ARIA context.
  */
-void mbedtls_aria_free( mbedtls_aria_context *ctx );
+void mbedtls_aria_free(mbedtls_aria_context *ctx);
 
 /**
  * \brief          This function sets the encryption key.
@@ -122,9 +109,9 @@
  * \return         \c 0 on success.
  * \return         A negative error code on failure.
  */
-int mbedtls_aria_setkey_enc( mbedtls_aria_context *ctx,
-                             const unsigned char *key,
-                             unsigned int keybits );
+int mbedtls_aria_setkey_enc(mbedtls_aria_context *ctx,
+                            const unsigned char *key,
+                            unsigned int keybits);
 
 /**
  * \brief          This function sets the decryption key.
@@ -141,9 +128,9 @@
  * \return         \c 0 on success.
  * \return         A negative error code on failure.
  */
-int mbedtls_aria_setkey_dec( mbedtls_aria_context *ctx,
-                             const unsigned char *key,
-                             unsigned int keybits );
+int mbedtls_aria_setkey_dec(mbedtls_aria_context *ctx,
+                            const unsigned char *key,
+                            unsigned int keybits);
 
 /**
  * \brief          This function performs an ARIA single-block encryption or
@@ -165,9 +152,9 @@
  * \return         \c 0 on success.
  * \return         A negative error code on failure.
  */
-int mbedtls_aria_crypt_ecb( mbedtls_aria_context *ctx,
-                            const unsigned char input[MBEDTLS_ARIA_BLOCKSIZE],
-                            unsigned char output[MBEDTLS_ARIA_BLOCKSIZE] );
+int mbedtls_aria_crypt_ecb(mbedtls_aria_context *ctx,
+                           const unsigned char input[MBEDTLS_ARIA_BLOCKSIZE],
+                           unsigned char output[MBEDTLS_ARIA_BLOCKSIZE]);
 
 #if defined(MBEDTLS_CIPHER_MODE_CBC)
 /**
@@ -211,12 +198,12 @@
  * \return         \c 0 on success.
  * \return         A negative error code on failure.
  */
-int mbedtls_aria_crypt_cbc( mbedtls_aria_context *ctx,
-                            int mode,
-                            size_t length,
-                            unsigned char iv[MBEDTLS_ARIA_BLOCKSIZE],
-                            const unsigned char *input,
-                            unsigned char *output );
+int mbedtls_aria_crypt_cbc(mbedtls_aria_context *ctx,
+                           int mode,
+                           size_t length,
+                           unsigned char iv[MBEDTLS_ARIA_BLOCKSIZE],
+                           const unsigned char *input,
+                           unsigned char *output);
 #endif /* MBEDTLS_CIPHER_MODE_CBC */
 
 #if defined(MBEDTLS_CIPHER_MODE_CFB)
@@ -261,13 +248,13 @@
  * \return         \c 0 on success.
  * \return         A negative error code on failure.
  */
-int mbedtls_aria_crypt_cfb128( mbedtls_aria_context *ctx,
-                               int mode,
-                               size_t length,
-                               size_t *iv_off,
-                               unsigned char iv[MBEDTLS_ARIA_BLOCKSIZE],
-                               const unsigned char *input,
-                               unsigned char *output );
+int mbedtls_aria_crypt_cfb128(mbedtls_aria_context *ctx,
+                              int mode,
+                              size_t length,
+                              size_t *iv_off,
+                              unsigned char iv[MBEDTLS_ARIA_BLOCKSIZE],
+                              const unsigned char *input,
+                              unsigned char *output);
 #endif /* MBEDTLS_CIPHER_MODE_CFB */
 
 #if defined(MBEDTLS_CIPHER_MODE_CTR)
@@ -275,10 +262,6 @@
  * \brief      This function performs an ARIA-CTR encryption or decryption
  *             operation.
  *
- *             This function performs the operation defined in the \p mode
- *             parameter (encrypt/decrypt), on the input data buffer
- *             defined in the \p input parameter.
- *
  *             Due to the nature of CTR, you must use the same key schedule
  *             for both encryption and decryption operations. Therefore, you
  *             must use the context initialized with mbedtls_aria_setkey_enc()
@@ -321,7 +304,7 @@
  *             for example, with 96-bit random nonces, you should not encrypt
  *             more than 2**32 messages with the same key.
  *
- *             Note that for both stategies, sizes are measured in blocks and
+ *             Note that for both strategies, sizes are measured in blocks and
  *             that an ARIA block is 16 bytes.
  *
  * \warning    Upon return, \p stream_block contains sensitive data. Its
@@ -348,13 +331,13 @@
  * \return                 \c 0 on success.
  * \return                 A negative error code on failure.
  */
-int mbedtls_aria_crypt_ctr( mbedtls_aria_context *ctx,
-                            size_t length,
-                            size_t *nc_off,
-                            unsigned char nonce_counter[MBEDTLS_ARIA_BLOCKSIZE],
-                            unsigned char stream_block[MBEDTLS_ARIA_BLOCKSIZE],
-                            const unsigned char *input,
-                            unsigned char *output );
+int mbedtls_aria_crypt_ctr(mbedtls_aria_context *ctx,
+                           size_t length,
+                           size_t *nc_off,
+                           unsigned char nonce_counter[MBEDTLS_ARIA_BLOCKSIZE],
+                           unsigned char stream_block[MBEDTLS_ARIA_BLOCKSIZE],
+                           const unsigned char *input,
+                           unsigned char *output);
 #endif /* MBEDTLS_CIPHER_MODE_CTR */
 
 #if defined(MBEDTLS_SELF_TEST)
@@ -363,7 +346,7 @@
  *
  * \return         \c 0 on success, or \c 1 on failure.
  */
-int mbedtls_aria_self_test( int verbose );
+int mbedtls_aria_self_test(int verbose);
 #endif /* MBEDTLS_SELF_TEST */
 
 #ifdef __cplusplus
diff --git a/third_party/mbedtls/repo/include/mbedtls/asn1.h b/third_party/mbedtls/repo/include/mbedtls/asn1.h
index 10f7905..c676fd3 100644
--- a/third_party/mbedtls/repo/include/mbedtls/asn1.h
+++ b/third_party/mbedtls/repo/include/mbedtls/asn1.h
@@ -5,19 +5,7 @@
  */
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 #ifndef MBEDTLS_ASN1_H
 #define MBEDTLS_ASN1_H
@@ -61,7 +49,7 @@
 /** Buffer too small when writing ASN.1 data structure. */
 #define MBEDTLS_ERR_ASN1_BUF_TOO_SMALL                    -0x006C
 
-/* \} name */
+/** \} name ASN1 Error codes */
 
 /**
  * \name DER constants
@@ -97,15 +85,15 @@
 
 /* Slightly smaller way to check if tag is a string tag
  * compared to canonical implementation. */
-#define MBEDTLS_ASN1_IS_STRING_TAG( tag )                                     \
-    ( ( tag ) < 32u && (                                                      \
-        ( ( 1u << ( tag ) ) & ( ( 1u << MBEDTLS_ASN1_BMP_STRING )       |     \
-                                ( 1u << MBEDTLS_ASN1_UTF8_STRING )      |     \
-                                ( 1u << MBEDTLS_ASN1_T61_STRING )       |     \
-                                ( 1u << MBEDTLS_ASN1_IA5_STRING )       |     \
-                                ( 1u << MBEDTLS_ASN1_UNIVERSAL_STRING ) |     \
-                                ( 1u << MBEDTLS_ASN1_PRINTABLE_STRING ) |     \
-                                ( 1u << MBEDTLS_ASN1_BIT_STRING ) ) ) != 0 ) )
+#define MBEDTLS_ASN1_IS_STRING_TAG(tag)                                     \
+    ((tag) < 32u && (                                                      \
+         ((1u << (tag)) & ((1u << MBEDTLS_ASN1_BMP_STRING)       |     \
+                           (1u << MBEDTLS_ASN1_UTF8_STRING)      |     \
+                           (1u << MBEDTLS_ASN1_T61_STRING)       |     \
+                           (1u << MBEDTLS_ASN1_IA5_STRING)       |     \
+                           (1u << MBEDTLS_ASN1_UNIVERSAL_STRING) |     \
+                           (1u << MBEDTLS_ASN1_PRINTABLE_STRING) |     \
+                           (1u << MBEDTLS_ASN1_BIT_STRING))) != 0))
 
 /*
  * Bit masks for each of the components of an ASN.1 tag as specified in
@@ -121,8 +109,7 @@
 #define MBEDTLS_ASN1_TAG_PC_MASK             0x20
 #define MBEDTLS_ASN1_TAG_VALUE_MASK          0x1F
 
-/* \} name */
-/* \} addtogroup asn1_module */
+/** \} name DER constants */
 
 /** Returns the size of the binary string, without the trailing \\0 */
 #define MBEDTLS_OID_SIZE(x) (sizeof(x) - 1)
@@ -134,12 +121,12 @@
  * 'unsigned char *oid' here!
  */
 #define MBEDTLS_OID_CMP(oid_str, oid_buf)                                   \
-        ( ( MBEDTLS_OID_SIZE(oid_str) != (oid_buf)->len ) ||                \
-          memcmp( (oid_str), (oid_buf)->p, (oid_buf)->len) != 0 )
+    ((MBEDTLS_OID_SIZE(oid_str) != (oid_buf)->len) ||                \
+     memcmp((oid_str), (oid_buf)->p, (oid_buf)->len) != 0)
 
 #define MBEDTLS_OID_CMP_RAW(oid_str, oid_buf, oid_buf_len)              \
-        ( ( MBEDTLS_OID_SIZE(oid_str) != (oid_buf_len) ) ||             \
-          memcmp( (oid_str), (oid_buf), (oid_buf_len) ) != 0 )
+    ((MBEDTLS_OID_SIZE(oid_str) != (oid_buf_len)) ||             \
+     memcmp((oid_str), (oid_buf), (oid_buf_len)) != 0)
 
 #ifdef __cplusplus
 extern "C" {
@@ -153,8 +140,7 @@
 /**
  * Type-length-value structure that allows for ASN1 using DER.
  */
-typedef struct mbedtls_asn1_buf
-{
+typedef struct mbedtls_asn1_buf {
     int tag;                /**< ASN1 type, e.g. MBEDTLS_ASN1_UTF8_STRING. */
     size_t len;             /**< ASN1 length, in octets. */
     unsigned char *p;       /**< ASN1 data, e.g. in ASCII. */
@@ -164,8 +150,7 @@
 /**
  * Container for ASN1 bit strings.
  */
-typedef struct mbedtls_asn1_bitstring
-{
+typedef struct mbedtls_asn1_bitstring {
     size_t len;                 /**< ASN1 length, in octets. */
     unsigned char unused_bits;  /**< Number of unused bits at the end of the string */
     unsigned char *p;           /**< Raw ASN1 data for the bit string */
@@ -175,8 +160,7 @@
 /**
  * Container for a sequence of ASN.1 items
  */
-typedef struct mbedtls_asn1_sequence
-{
+typedef struct mbedtls_asn1_sequence {
     mbedtls_asn1_buf buf;                   /**< Buffer containing the given ASN.1 item. */
     struct mbedtls_asn1_sequence *next;    /**< The next entry in the sequence. */
 }
@@ -185,8 +169,7 @@
 /**
  * Container for a sequence or list of 'named' ASN.1 data items
  */
-typedef struct mbedtls_asn1_named_data
-{
+typedef struct mbedtls_asn1_named_data {
     mbedtls_asn1_buf oid;                   /**< The object identifier. */
     mbedtls_asn1_buf val;                   /**< The named value. */
     struct mbedtls_asn1_named_data *next;  /**< The next entry in the sequence. */
@@ -210,11 +193,11 @@
  * \return      0 if successful.
  * \return      #MBEDTLS_ERR_ASN1_OUT_OF_DATA if the ASN.1 element
  *              would end beyond \p end.
- * \return      #MBEDTLS_ERR_ASN1_INVALID_LENGTH if the length is unparseable.
+ * \return      #MBEDTLS_ERR_ASN1_INVALID_LENGTH if the length is unparsable.
  */
-int mbedtls_asn1_get_len( unsigned char **p,
-                          const unsigned char *end,
-                          size_t *len );
+int mbedtls_asn1_get_len(unsigned char **p,
+                         const unsigned char *end,
+                         size_t *len);
 
 /**
  * \brief       Get the tag and length of the element.
@@ -235,11 +218,11 @@
  *              with the requested tag.
  * \return      #MBEDTLS_ERR_ASN1_OUT_OF_DATA if the ASN.1 element
  *              would end beyond \p end.
- * \return      #MBEDTLS_ERR_ASN1_INVALID_LENGTH if the length is unparseable.
+ * \return      #MBEDTLS_ERR_ASN1_INVALID_LENGTH if the length is unparsable.
  */
-int mbedtls_asn1_get_tag( unsigned char **p,
-                          const unsigned char *end,
-                          size_t *len, int tag );
+int mbedtls_asn1_get_tag(unsigned char **p,
+                         const unsigned char *end,
+                         size_t *len, int tag);
 
 /**
  * \brief       Retrieve a boolean ASN.1 tag and its value.
@@ -256,9 +239,9 @@
  * \return      An ASN.1 error code if the input does not start with
  *              a valid ASN.1 BOOLEAN.
  */
-int mbedtls_asn1_get_bool( unsigned char **p,
-                           const unsigned char *end,
-                           int *val );
+int mbedtls_asn1_get_bool(unsigned char **p,
+                          const unsigned char *end,
+                          int *val);
 
 /**
  * \brief       Retrieve an integer ASN.1 tag and its value.
@@ -277,9 +260,9 @@
  * \return      #MBEDTLS_ERR_ASN1_INVALID_LENGTH if the parsed value does
  *              not fit in an \c int.
  */
-int mbedtls_asn1_get_int( unsigned char **p,
-                          const unsigned char *end,
-                          int *val );
+int mbedtls_asn1_get_int(unsigned char **p,
+                         const unsigned char *end,
+                         int *val);
 
 /**
  * \brief       Retrieve an enumerated ASN.1 tag and its value.
@@ -298,9 +281,9 @@
  * \return      #MBEDTLS_ERR_ASN1_INVALID_LENGTH if the parsed value does
  *              not fit in an \c int.
  */
-int mbedtls_asn1_get_enum( unsigned char **p,
-                           const unsigned char *end,
-                           int *val );
+int mbedtls_asn1_get_enum(unsigned char **p,
+                          const unsigned char *end,
+                          int *val);
 
 /**
  * \brief       Retrieve a bitstring ASN.1 tag and its value.
@@ -319,8 +302,8 @@
  * \return      An ASN.1 error code if the input does not start with
  *              a valid ASN.1 BIT STRING.
  */
-int mbedtls_asn1_get_bitstring( unsigned char **p, const unsigned char *end,
-                                mbedtls_asn1_bitstring *bs );
+int mbedtls_asn1_get_bitstring(unsigned char **p, const unsigned char *end,
+                               mbedtls_asn1_bitstring *bs);
 
 /**
  * \brief       Retrieve a bitstring ASN.1 tag without unused bits and its
@@ -340,9 +323,9 @@
  * \return      An ASN.1 error code if the input does not start with
  *              a valid ASN.1 BIT STRING.
  */
-int mbedtls_asn1_get_bitstring_null( unsigned char **p,
-                                     const unsigned char *end,
-                                     size_t *len );
+int mbedtls_asn1_get_bitstring_null(unsigned char **p,
+                                    const unsigned char *end,
+                                    size_t *len);
 
 /**
  * \brief       Parses and splits an ASN.1 "SEQUENCE OF <tag>".
@@ -391,10 +374,10 @@
  * \return      An ASN.1 error code if the input does not start with
  *              a valid ASN.1 SEQUENCE.
  */
-int mbedtls_asn1_get_sequence_of( unsigned char **p,
-                                  const unsigned char *end,
-                                  mbedtls_asn1_sequence *cur,
-                                  int tag );
+int mbedtls_asn1_get_sequence_of(unsigned char **p,
+                                 const unsigned char *end,
+                                 mbedtls_asn1_sequence *cur,
+                                 int tag);
 /**
  * \brief          Free a heap-allocated linked list presentation of
  *                 an ASN.1 sequence, including the first element.
@@ -416,7 +399,7 @@
  *                 be \c NULL, in which case this functions returns
  *                 immediately.
  */
-void mbedtls_asn1_sequence_free( mbedtls_asn1_sequence *seq );
+void mbedtls_asn1_sequence_free(mbedtls_asn1_sequence *seq);
 
 /**
  * \brief                Traverse an ASN.1 SEQUENCE container and
@@ -458,7 +441,7 @@
  *                       on a successful invocation.
  * \param end            The end of the ASN.1 SEQUENCE container.
  * \param tag_must_mask  A mask to be applied to the ASN.1 tags found within
- *                       the SEQUENCE before comparing to \p tag_must_value.
+ *                       the SEQUENCE before comparing to \p tag_must_val.
  * \param tag_must_val   The required value of each ASN.1 tag found in the
  *                       SEQUENCE, after masking with \p tag_must_mask.
  *                       Mismatching tags lead to an error.
@@ -467,7 +450,7 @@
  *                       while a value of \c 0xFF for \p tag_must_mask means
  *                       that \p tag_must_val is the only allowed tag.
  * \param tag_may_mask   A mask to be applied to the ASN.1 tags found within
- *                       the SEQUENCE before comparing to \p tag_may_value.
+ *                       the SEQUENCE before comparing to \p tag_may_val.
  * \param tag_may_val    The desired value of each ASN.1 tag found in the
  *                       SEQUENCE, after masking with \p tag_may_mask.
  *                       Mismatching tags will be silently ignored.
@@ -508,9 +491,9 @@
     const unsigned char *end,
     unsigned char tag_must_mask, unsigned char tag_must_val,
     unsigned char tag_may_mask, unsigned char tag_may_val,
-    int (*cb)( void *ctx, int tag,
-               unsigned char* start, size_t len ),
-    void *ctx );
+    int (*cb)(void *ctx, int tag,
+              unsigned char *start, size_t len),
+    void *ctx);
 
 #if defined(MBEDTLS_BIGNUM_C)
 /**
@@ -531,9 +514,9 @@
  *              not fit in an \c int.
  * \return      An MPI error code if the parsed value is too large.
  */
-int mbedtls_asn1_get_mpi( unsigned char **p,
-                          const unsigned char *end,
-                          mbedtls_mpi *X );
+int mbedtls_asn1_get_mpi(unsigned char **p,
+                         const unsigned char *end,
+                         mbedtls_mpi *X);
 #endif /* MBEDTLS_BIGNUM_C */
 
 /**
@@ -552,9 +535,9 @@
  *
  * \return      0 if successful or a specific ASN.1 or MPI error code.
  */
-int mbedtls_asn1_get_alg( unsigned char **p,
-                  const unsigned char *end,
-                  mbedtls_asn1_buf *alg, mbedtls_asn1_buf *params );
+int mbedtls_asn1_get_alg(unsigned char **p,
+                         const unsigned char *end,
+                         mbedtls_asn1_buf *alg, mbedtls_asn1_buf *params);
 
 /**
  * \brief       Retrieve an AlgorithmIdentifier ASN.1 sequence with NULL or no
@@ -571,9 +554,9 @@
  *
  * \return      0 if successful or a specific ASN.1 or MPI error code.
  */
-int mbedtls_asn1_get_alg_null( unsigned char **p,
-                       const unsigned char *end,
-                       mbedtls_asn1_buf *alg );
+int mbedtls_asn1_get_alg_null(unsigned char **p,
+                              const unsigned char *end,
+                              mbedtls_asn1_buf *alg);
 
 /**
  * \brief       Find a specific named_data entry in a sequence or list based on
@@ -585,8 +568,8 @@
  *
  * \return      NULL if not found, or a pointer to the existing entry.
  */
-mbedtls_asn1_named_data *mbedtls_asn1_find_named_data( mbedtls_asn1_named_data *list,
-                                       const char *oid, size_t len );
+mbedtls_asn1_named_data *mbedtls_asn1_find_named_data(mbedtls_asn1_named_data *list,
+                                                      const char *oid, size_t len);
 
 /**
  * \brief       Free a mbedtls_asn1_named_data entry
@@ -595,7 +578,7 @@
  *              This function calls mbedtls_free() on
  *              `entry->oid.p` and `entry->val.p`.
  */
-void mbedtls_asn1_free_named_data( mbedtls_asn1_named_data *entry );
+void mbedtls_asn1_free_named_data(mbedtls_asn1_named_data *entry);
 
 /**
  * \brief       Free all entries in a mbedtls_asn1_named_data list.
@@ -605,7 +588,10 @@
  *              mbedtls_free() on each list element and
  *              sets \c *head to \c NULL.
  */
-void mbedtls_asn1_free_named_data_list( mbedtls_asn1_named_data **head );
+void mbedtls_asn1_free_named_data_list(mbedtls_asn1_named_data **head);
+
+/** \} name Functions to parse ASN.1 data structures */
+/** \} addtogroup asn1_module */
 
 #ifdef __cplusplus
 }
diff --git a/third_party/mbedtls/repo/include/mbedtls/asn1write.h b/third_party/mbedtls/repo/include/mbedtls/asn1write.h
index 44afae0..a12bf03 100644
--- a/third_party/mbedtls/repo/include/mbedtls/asn1write.h
+++ b/third_party/mbedtls/repo/include/mbedtls/asn1write.h
@@ -5,19 +5,7 @@
  */
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 #ifndef MBEDTLS_ASN1_WRITE_H
 #define MBEDTLS_ASN1_WRITE_H
@@ -33,11 +21,11 @@
 #define MBEDTLS_ASN1_CHK_ADD(g, f)                      \
     do                                                  \
     {                                                   \
-        if( ( ret = (f) ) < 0 )                         \
-            return( ret );                              \
+        if ((ret = (f)) < 0)                         \
+        return ret;                              \
         else                                            \
-            (g) += ret;                                 \
-    } while( 0 )
+        (g) += ret;                                 \
+    } while (0)
 
 #ifdef __cplusplus
 extern "C" {
@@ -55,8 +43,8 @@
  * \return          The number of bytes written to \p p on success.
  * \return          A negative \c MBEDTLS_ERR_ASN1_XXX error code on failure.
  */
-int mbedtls_asn1_write_len( unsigned char **p, unsigned char *start,
-                            size_t len );
+int mbedtls_asn1_write_len(unsigned char **p, unsigned char *start,
+                           size_t len);
 /**
  * \brief           Write an ASN.1 tag in ASN.1 format.
  *
@@ -69,8 +57,8 @@
  * \return          The number of bytes written to \p p on success.
  * \return          A negative \c MBEDTLS_ERR_ASN1_XXX error code on failure.
  */
-int mbedtls_asn1_write_tag( unsigned char **p, unsigned char *start,
-                            unsigned char tag );
+int mbedtls_asn1_write_tag(unsigned char **p, unsigned char *start,
+                           unsigned char tag);
 
 /**
  * \brief           Write raw buffer data.
@@ -85,12 +73,12 @@
  * \return          The number of bytes written to \p p on success.
  * \return          A negative \c MBEDTLS_ERR_ASN1_XXX error code on failure.
  */
-int mbedtls_asn1_write_raw_buffer( unsigned char **p, unsigned char *start,
-                                   const unsigned char *buf, size_t size );
+int mbedtls_asn1_write_raw_buffer(unsigned char **p, unsigned char *start,
+                                  const unsigned char *buf, size_t size);
 
 #if defined(MBEDTLS_BIGNUM_C)
 /**
- * \brief           Write a arbitrary-precision number (#MBEDTLS_ASN1_INTEGER)
+ * \brief           Write an arbitrary-precision number (#MBEDTLS_ASN1_INTEGER)
  *                  in ASN.1 format.
  *
  * \note            This function works backwards in data buffer.
@@ -103,8 +91,8 @@
  * \return          The number of bytes written to \p p on success.
  * \return          A negative \c MBEDTLS_ERR_ASN1_XXX error code on failure.
  */
-int mbedtls_asn1_write_mpi( unsigned char **p, unsigned char *start,
-                            const mbedtls_mpi *X );
+int mbedtls_asn1_write_mpi(unsigned char **p, unsigned char *start,
+                           const mbedtls_mpi *X);
 #endif /* MBEDTLS_BIGNUM_C */
 
 /**
@@ -119,7 +107,7 @@
  * \return          The number of bytes written to \p p on success.
  * \return          A negative \c MBEDTLS_ERR_ASN1_XXX error code on failure.
  */
-int mbedtls_asn1_write_null( unsigned char **p, unsigned char *start );
+int mbedtls_asn1_write_null(unsigned char **p, unsigned char *start);
 
 /**
  * \brief           Write an OID tag (#MBEDTLS_ASN1_OID) and data
@@ -135,8 +123,8 @@
  * \return          The number of bytes written to \p p on success.
  * \return          A negative \c MBEDTLS_ERR_ASN1_XXX error code on failure.
  */
-int mbedtls_asn1_write_oid( unsigned char **p, unsigned char *start,
-                            const char *oid, size_t oid_len );
+int mbedtls_asn1_write_oid(unsigned char **p, unsigned char *start,
+                           const char *oid, size_t oid_len);
 
 /**
  * \brief           Write an AlgorithmIdentifier sequence in ASN.1 format.
@@ -153,10 +141,31 @@
  * \return          The number of bytes written to \p p on success.
  * \return          A negative \c MBEDTLS_ERR_ASN1_XXX error code on failure.
  */
-int mbedtls_asn1_write_algorithm_identifier( unsigned char **p,
-                                             unsigned char *start,
-                                             const char *oid, size_t oid_len,
-                                             size_t par_len );
+int mbedtls_asn1_write_algorithm_identifier(unsigned char **p,
+                                            unsigned char *start,
+                                            const char *oid, size_t oid_len,
+                                            size_t par_len);
+
+/**
+ * \brief           Write an AlgorithmIdentifier sequence in ASN.1 format.
+ *
+ * \note            This function works backwards in data buffer.
+ *
+ * \param p         The reference to the current position pointer.
+ * \param start     The start of the buffer, for bounds-checking.
+ * \param oid       The OID of the algorithm to write.
+ * \param oid_len   The length of the algorithm's OID.
+ * \param par_len   The length of the parameters, which must be already written.
+ * \param has_par   If there are any parameters. If 0, par_len must be 0. If 1
+ *                  and \p par_len is 0, NULL parameters are added.
+ *
+ * \return          The number of bytes written to \p p on success.
+ * \return          A negative \c MBEDTLS_ERR_ASN1_XXX error code on failure.
+ */
+int mbedtls_asn1_write_algorithm_identifier_ext(unsigned char **p,
+                                                unsigned char *start,
+                                                const char *oid, size_t oid_len,
+                                                size_t par_len, int has_par);
 
 /**
  * \brief           Write a boolean tag (#MBEDTLS_ASN1_BOOLEAN) and value
@@ -171,8 +180,8 @@
  * \return          The number of bytes written to \p p on success.
  * \return          A negative \c MBEDTLS_ERR_ASN1_XXX error code on failure.
  */
-int mbedtls_asn1_write_bool( unsigned char **p, unsigned char *start,
-                             int boolean );
+int mbedtls_asn1_write_bool(unsigned char **p, unsigned char *start,
+                            int boolean);
 
 /**
  * \brief           Write an int tag (#MBEDTLS_ASN1_INTEGER) and value
@@ -188,7 +197,7 @@
  * \return          The number of bytes written to \p p on success.
  * \return          A negative \c MBEDTLS_ERR_ASN1_XXX error code on failure.
  */
-int mbedtls_asn1_write_int( unsigned char **p, unsigned char *start, int val );
+int mbedtls_asn1_write_int(unsigned char **p, unsigned char *start, int val);
 
 /**
  * \brief           Write an enum tag (#MBEDTLS_ASN1_ENUMERATED) and value
@@ -203,7 +212,7 @@
  * \return          The number of bytes written to \p p on success.
  * \return          A negative \c MBEDTLS_ERR_ASN1_XXX error code on failure.
  */
-int mbedtls_asn1_write_enum( unsigned char **p, unsigned char *start, int val );
+int mbedtls_asn1_write_enum(unsigned char **p, unsigned char *start, int val);
 
 /**
  * \brief           Write a string in ASN.1 format using a specific
@@ -222,9 +231,9 @@
  * \return          The number of bytes written to \p p on success.
  * \return          A negative error code on failure.
  */
-int mbedtls_asn1_write_tagged_string( unsigned char **p, unsigned char *start,
-                                      int tag, const char *text,
-                                      size_t text_len );
+int mbedtls_asn1_write_tagged_string(unsigned char **p, unsigned char *start,
+                                     int tag, const char *text,
+                                     size_t text_len);
 
 /**
  * \brief           Write a string in ASN.1 format using the PrintableString
@@ -241,9 +250,9 @@
  * \return          The number of bytes written to \p p on success.
  * \return          A negative error code on failure.
  */
-int mbedtls_asn1_write_printable_string( unsigned char **p,
-                                         unsigned char *start,
-                                         const char *text, size_t text_len );
+int mbedtls_asn1_write_printable_string(unsigned char **p,
+                                        unsigned char *start,
+                                        const char *text, size_t text_len);
 
 /**
  * \brief           Write a UTF8 string in ASN.1 format using the UTF8String
@@ -260,8 +269,8 @@
  * \return          The number of bytes written to \p p on success.
  * \return          A negative error code on failure.
  */
-int mbedtls_asn1_write_utf8_string( unsigned char **p, unsigned char *start,
-                                    const char *text, size_t text_len );
+int mbedtls_asn1_write_utf8_string(unsigned char **p, unsigned char *start,
+                                   const char *text, size_t text_len);
 
 /**
  * \brief           Write a string in ASN.1 format using the IA5String
@@ -278,8 +287,8 @@
  * \return          The number of bytes written to \p p on success.
  * \return          A negative error code on failure.
  */
-int mbedtls_asn1_write_ia5_string( unsigned char **p, unsigned char *start,
-                                   const char *text, size_t text_len );
+int mbedtls_asn1_write_ia5_string(unsigned char **p, unsigned char *start,
+                                  const char *text, size_t text_len);
 
 /**
  * \brief           Write a bitstring tag (#MBEDTLS_ASN1_BIT_STRING) and
@@ -295,8 +304,8 @@
  * \return          The number of bytes written to \p p on success.
  * \return          A negative error code on failure.
  */
-int mbedtls_asn1_write_bitstring( unsigned char **p, unsigned char *start,
-                                  const unsigned char *buf, size_t bits );
+int mbedtls_asn1_write_bitstring(unsigned char **p, unsigned char *start,
+                                 const unsigned char *buf, size_t bits);
 
 /**
  * \brief           This function writes a named bitstring tag
@@ -315,10 +324,10 @@
  * \return          The number of bytes written to \p p on success.
  * \return          A negative error code on failure.
  */
-int mbedtls_asn1_write_named_bitstring( unsigned char **p,
-                                        unsigned char *start,
-                                        const unsigned char *buf,
-                                        size_t bits );
+int mbedtls_asn1_write_named_bitstring(unsigned char **p,
+                                       unsigned char *start,
+                                       const unsigned char *buf,
+                                       size_t bits);
 
 /**
  * \brief           Write an octet string tag (#MBEDTLS_ASN1_OCTET_STRING)
@@ -334,8 +343,8 @@
  * \return          The number of bytes written to \p p on success.
  * \return          A negative error code on failure.
  */
-int mbedtls_asn1_write_octet_string( unsigned char **p, unsigned char *start,
-                                     const unsigned char *buf, size_t size );
+int mbedtls_asn1_write_octet_string(unsigned char **p, unsigned char *start,
+                                    const unsigned char *buf, size_t size);
 
 /**
  * \brief           Create or find a specific named_data entry for writing in a
@@ -358,10 +367,10 @@
  * \return          A pointer to the new / existing entry on success.
  * \return          \c NULL if if there was a memory allocation error.
  */
-mbedtls_asn1_named_data *mbedtls_asn1_store_named_data( mbedtls_asn1_named_data **list,
-                                        const char *oid, size_t oid_len,
-                                        const unsigned char *val,
-                                        size_t val_len );
+mbedtls_asn1_named_data *mbedtls_asn1_store_named_data(mbedtls_asn1_named_data **list,
+                                                       const char *oid, size_t oid_len,
+                                                       const unsigned char *val,
+                                                       size_t val_len);
 
 #ifdef __cplusplus
 }
diff --git a/third_party/mbedtls/repo/include/mbedtls/base64.h b/third_party/mbedtls/repo/include/mbedtls/base64.h
index cf4149e..cc46047 100644
--- a/third_party/mbedtls/repo/include/mbedtls/base64.h
+++ b/third_party/mbedtls/repo/include/mbedtls/base64.h
@@ -5,19 +5,7 @@
  */
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 #ifndef MBEDTLS_BASE64_H
 #define MBEDTLS_BASE64_H
@@ -58,8 +46,8 @@
  * \note           Call this function with dlen = 0 to obtain the
  *                 required buffer size in *olen
  */
-int mbedtls_base64_encode( unsigned char *dst, size_t dlen, size_t *olen,
-                   const unsigned char *src, size_t slen );
+int mbedtls_base64_encode(unsigned char *dst, size_t dlen, size_t *olen,
+                          const unsigned char *src, size_t slen);
 
 /**
  * \brief          Decode a base64-formatted buffer
@@ -78,8 +66,8 @@
  * \note           Call this function with *dst = NULL or dlen = 0 to obtain
  *                 the required buffer size in *olen
  */
-int mbedtls_base64_decode( unsigned char *dst, size_t dlen, size_t *olen,
-                   const unsigned char *src, size_t slen );
+int mbedtls_base64_decode(unsigned char *dst, size_t dlen, size_t *olen,
+                          const unsigned char *src, size_t slen);
 
 #if defined(MBEDTLS_SELF_TEST)
 /**
@@ -87,7 +75,7 @@
  *
  * \return         0 if successful, or 1 if the test failed
  */
-int mbedtls_base64_self_test( int verbose );
+int mbedtls_base64_self_test(int verbose);
 
 #endif /* MBEDTLS_SELF_TEST */
 
diff --git a/third_party/mbedtls/repo/include/mbedtls/bignum.h b/third_party/mbedtls/repo/include/mbedtls/bignum.h
index 9d2cff3..fb0ca15 100644
--- a/third_party/mbedtls/repo/include/mbedtls/bignum.h
+++ b/third_party/mbedtls/repo/include/mbedtls/bignum.h
@@ -5,19 +5,7 @@
  */
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 #ifndef MBEDTLS_BIGNUM_H
 #define MBEDTLS_BIGNUM_H
@@ -55,9 +43,9 @@
 #define MBEDTLS_MPI_CHK(f)       \
     do                           \
     {                            \
-        if( ( ret = (f) ) != 0 ) \
-            goto cleanup;        \
-    } while( 0 )
+        if ((ret = (f)) != 0) \
+        goto cleanup;        \
+    } while (0)
 
 /*
  * Maximum size MPIs are allowed to grow to in number of limbs.
@@ -66,7 +54,7 @@
 
 #if !defined(MBEDTLS_MPI_WINDOW_SIZE)
 /*
- * Maximum window size used for modular exponentiation. Default: 6
+ * Maximum window size used for modular exponentiation. Default: 2
  * Minimum value: 1. Maximum value: 6.
  *
  * Result is an array of ( 2 ** MBEDTLS_MPI_WINDOW_SIZE ) MPIs used
@@ -74,7 +62,7 @@
  *
  * Reduction in size, reduces speed.
  */
-#define MBEDTLS_MPI_WINDOW_SIZE                           6        /**< Maximum window size used. */
+#define MBEDTLS_MPI_WINDOW_SIZE                           2        /**< Maximum window size used. */
 #endif /* !MBEDTLS_MPI_WINDOW_SIZE */
 
 #if !defined(MBEDTLS_MPI_MAX_SIZE)
@@ -88,7 +76,7 @@
 #define MBEDTLS_MPI_MAX_SIZE                              1024     /**< Maximum number of bytes for usable MPIs. */
 #endif /* !MBEDTLS_MPI_MAX_SIZE */
 
-#define MBEDTLS_MPI_MAX_BITS                              ( 8 * MBEDTLS_MPI_MAX_SIZE )    /**< Maximum number of bits for usable MPIs. */
+#define MBEDTLS_MPI_MAX_BITS                              (8 * MBEDTLS_MPI_MAX_SIZE)      /**< Maximum number of bits for usable MPIs. */
 
 /*
  * When reading from files with mbedtls_mpi_read_file() and writing to files with
@@ -108,9 +96,11 @@
  *  MBEDTLS_MPI_RW_BUFFER_SIZE = ceil(MBEDTLS_MPI_MAX_BITS / ln(10) * ln(2)) +
  *                                LabelSize + 6
  */
-#define MBEDTLS_MPI_MAX_BITS_SCALE100          ( 100 * MBEDTLS_MPI_MAX_BITS )
+#define MBEDTLS_MPI_MAX_BITS_SCALE100          (100 * MBEDTLS_MPI_MAX_BITS)
 #define MBEDTLS_LN_2_DIV_LN_10_SCALE100                 332
-#define MBEDTLS_MPI_RW_BUFFER_SIZE             ( ((MBEDTLS_MPI_MAX_BITS_SCALE100 + MBEDTLS_LN_2_DIV_LN_10_SCALE100 - 1) / MBEDTLS_LN_2_DIV_LN_10_SCALE100) + 10 + 6 )
+#define MBEDTLS_MPI_RW_BUFFER_SIZE             (((MBEDTLS_MPI_MAX_BITS_SCALE100 + \
+                                                  MBEDTLS_LN_2_DIV_LN_10_SCALE100 - 1) / \
+                                                 MBEDTLS_LN_2_DIV_LN_10_SCALE100) + 10 + 6)
 
 /*
  * Define the base integer type, architecture-wise.
@@ -124,64 +114,78 @@
  */
 #if !defined(MBEDTLS_HAVE_INT32)
     #if defined(_MSC_VER) && defined(_M_AMD64)
-        /* Always choose 64-bit when using MSC */
+/* Always choose 64-bit when using MSC */
         #if !defined(MBEDTLS_HAVE_INT64)
             #define MBEDTLS_HAVE_INT64
         #endif /* !MBEDTLS_HAVE_INT64 */
-        typedef  int64_t mbedtls_mpi_sint;
-        typedef uint64_t mbedtls_mpi_uint;
+typedef  int64_t mbedtls_mpi_sint;
+typedef uint64_t mbedtls_mpi_uint;
     #elif defined(__GNUC__) && (                         \
-        defined(__amd64__) || defined(__x86_64__)     || \
-        defined(__ppc64__) || defined(__powerpc64__)  || \
-        defined(__ia64__)  || defined(__alpha__)      || \
-        ( defined(__sparc__) && defined(__arch64__) ) || \
-        defined(__s390x__) || defined(__mips64)       || \
-        defined(__aarch64__) )
+    defined(__amd64__) || defined(__x86_64__)     || \
+    defined(__ppc64__) || defined(__powerpc64__)  || \
+    defined(__ia64__)  || defined(__alpha__)      || \
+    (defined(__sparc__) && defined(__arch64__)) || \
+    defined(__s390x__) || defined(__mips64)       || \
+    defined(__aarch64__))
         #if !defined(MBEDTLS_HAVE_INT64)
             #define MBEDTLS_HAVE_INT64
         #endif /* MBEDTLS_HAVE_INT64 */
-        typedef  int64_t mbedtls_mpi_sint;
-        typedef uint64_t mbedtls_mpi_uint;
+typedef  int64_t mbedtls_mpi_sint;
+typedef uint64_t mbedtls_mpi_uint;
         #if !defined(MBEDTLS_NO_UDBL_DIVISION)
-            /* mbedtls_t_udbl defined as 128-bit unsigned int */
-            typedef unsigned int mbedtls_t_udbl __attribute__((mode(TI)));
+/* mbedtls_t_udbl defined as 128-bit unsigned int */
+typedef unsigned int mbedtls_t_udbl __attribute__((mode(TI)));
             #define MBEDTLS_HAVE_UDBL
         #endif /* !MBEDTLS_NO_UDBL_DIVISION */
     #elif defined(__ARMCC_VERSION) && defined(__aarch64__)
-        /*
-         * __ARMCC_VERSION is defined for both armcc and armclang and
-         * __aarch64__ is only defined by armclang when compiling 64-bit code
-         */
+/*
+ * __ARMCC_VERSION is defined for both armcc and armclang and
+ * __aarch64__ is only defined by armclang when compiling 64-bit code
+ */
         #if !defined(MBEDTLS_HAVE_INT64)
             #define MBEDTLS_HAVE_INT64
         #endif /* !MBEDTLS_HAVE_INT64 */
-        typedef  int64_t mbedtls_mpi_sint;
-        typedef uint64_t mbedtls_mpi_uint;
+typedef  int64_t mbedtls_mpi_sint;
+typedef uint64_t mbedtls_mpi_uint;
         #if !defined(MBEDTLS_NO_UDBL_DIVISION)
-            /* mbedtls_t_udbl defined as 128-bit unsigned int */
-            typedef __uint128_t mbedtls_t_udbl;
+/* mbedtls_t_udbl defined as 128-bit unsigned int */
+typedef __uint128_t mbedtls_t_udbl;
             #define MBEDTLS_HAVE_UDBL
         #endif /* !MBEDTLS_NO_UDBL_DIVISION */
     #elif defined(MBEDTLS_HAVE_INT64)
-        /* Force 64-bit integers with unknown compiler */
-        typedef  int64_t mbedtls_mpi_sint;
-        typedef uint64_t mbedtls_mpi_uint;
+/* Force 64-bit integers with unknown compiler */
+typedef  int64_t mbedtls_mpi_sint;
+typedef uint64_t mbedtls_mpi_uint;
     #endif
 #endif /* !MBEDTLS_HAVE_INT32 */
 
 #if !defined(MBEDTLS_HAVE_INT64)
-    /* Default to 32-bit compilation */
+/* Default to 32-bit compilation */
     #if !defined(MBEDTLS_HAVE_INT32)
         #define MBEDTLS_HAVE_INT32
     #endif /* !MBEDTLS_HAVE_INT32 */
-    typedef  int32_t mbedtls_mpi_sint;
-    typedef uint32_t mbedtls_mpi_uint;
+typedef  int32_t mbedtls_mpi_sint;
+typedef uint32_t mbedtls_mpi_uint;
     #if !defined(MBEDTLS_NO_UDBL_DIVISION)
-        typedef uint64_t mbedtls_t_udbl;
+typedef uint64_t mbedtls_t_udbl;
         #define MBEDTLS_HAVE_UDBL
     #endif /* !MBEDTLS_NO_UDBL_DIVISION */
 #endif /* !MBEDTLS_HAVE_INT64 */
 
+/** \typedef mbedtls_mpi_uint
+ * \brief The type of machine digits in a bignum, called _limbs_.
+ *
+ * This is always an unsigned integer type with no padding bits. The size
+ * is platform-dependent.
+ */
+
+/** \typedef mbedtls_mpi_sint
+ * \brief The signed type corresponding to #mbedtls_mpi_uint.
+ *
+ * This is always a signed integer type with no padding bits. The size
+ * is platform-dependent.
+ */
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -189,11 +193,28 @@
 /**
  * \brief          MPI structure
  */
-typedef struct mbedtls_mpi
-{
-    int s;              /*!<  Sign: -1 if the mpi is negative, 1 otherwise */
-    size_t n;           /*!<  total # of limbs  */
-    mbedtls_mpi_uint *p;          /*!<  pointer to limbs  */
+typedef struct mbedtls_mpi {
+    /** Sign: -1 if the mpi is negative, 1 otherwise.
+     *
+     * The number 0 must be represented with `s = +1`. Although many library
+     * functions treat all-limbs-zero as equivalent to a valid representation
+     * of 0 regardless of the sign bit, there are exceptions, so bignum
+     * functions and external callers must always set \c s to +1 for the
+     * number zero.
+     *
+     * Note that this implies that calloc() or `... = {0}` does not create
+     * a valid MPI representation. You must call mbedtls_mpi_init().
+     */
+    int s;
+
+    /** Total number of limbs in \c p.  */
+    size_t n;
+
+    /** Pointer to limbs.
+     *
+     * This may be \c NULL if \c n is 0.
+     */
+    mbedtls_mpi_uint *p;
 }
 mbedtls_mpi;
 
@@ -205,7 +226,7 @@
  *
  * \param X         The MPI context to initialize. This must not be \c NULL.
  */
-void mbedtls_mpi_init( mbedtls_mpi *X );
+void mbedtls_mpi_init(mbedtls_mpi *X);
 
 /**
  * \brief          This function frees the components of an MPI context.
@@ -214,7 +235,7 @@
  *                 in which case this function is a no-op. If it is
  *                 not \c NULL, it must point to an initialized MPI.
  */
-void mbedtls_mpi_free( mbedtls_mpi *X );
+void mbedtls_mpi_free(mbedtls_mpi *X);
 
 /**
  * \brief          Enlarge an MPI to the specified number of limbs.
@@ -229,7 +250,7 @@
  * \return         #MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed.
  * \return         Another negative error code on other kinds of failure.
  */
-int mbedtls_mpi_grow( mbedtls_mpi *X, size_t nblimbs );
+int mbedtls_mpi_grow(mbedtls_mpi *X, size_t nblimbs);
 
 /**
  * \brief          This function resizes an MPI downwards, keeping at least the
@@ -246,7 +267,7 @@
  *                 (this can only happen when resizing up).
  * \return         Another negative error code on other kinds of failure.
  */
-int mbedtls_mpi_shrink( mbedtls_mpi *X, size_t nblimbs );
+int mbedtls_mpi_shrink(mbedtls_mpi *X, size_t nblimbs);
 
 /**
  * \brief          Make a copy of an MPI.
@@ -261,7 +282,7 @@
  * \return         #MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed.
  * \return         Another negative error code on other kinds of failure.
  */
-int mbedtls_mpi_copy( mbedtls_mpi *X, const mbedtls_mpi *Y );
+int mbedtls_mpi_copy(mbedtls_mpi *X, const mbedtls_mpi *Y);
 
 /**
  * \brief          Swap the contents of two MPIs.
@@ -269,7 +290,7 @@
  * \param X        The first MPI. It must be initialized.
  * \param Y        The second MPI. It must be initialized.
  */
-void mbedtls_mpi_swap( mbedtls_mpi *X, mbedtls_mpi *Y );
+void mbedtls_mpi_swap(mbedtls_mpi *X, mbedtls_mpi *Y);
 
 /**
  * \brief          Perform a safe conditional copy of MPI which doesn't
@@ -280,7 +301,7 @@
  * \param Y        The MPI to be assigned from. This must point to an
  *                 initialized MPI.
  * \param assign   The condition deciding whether to perform the
- *                 assignment or not. Possible values:
+ *                 assignment or not. Must be either 0 or 1:
  *                 * \c 1: Perform the assignment `X = Y`.
  *                 * \c 0: Keep the original value of \p X.
  *
@@ -291,11 +312,15 @@
  *                 information through branch prediction and/or memory access
  *                 patterns analysis).
  *
+ * \warning        If \p assign is neither 0 nor 1, the result of this function
+ *                 is indeterminate, and the resulting value in \p X might be
+ *                 neither its original value nor the value in \p Y.
+ *
  * \return         \c 0 if successful.
  * \return         #MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed.
  * \return         Another negative error code on other kinds of failure.
  */
-int mbedtls_mpi_safe_cond_assign( mbedtls_mpi *X, const mbedtls_mpi *Y, unsigned char assign );
+int mbedtls_mpi_safe_cond_assign(mbedtls_mpi *X, const mbedtls_mpi *Y, unsigned char assign);
 
 /**
  * \brief          Perform a safe conditional swap which doesn't
@@ -303,24 +328,28 @@
  *
  * \param X        The first MPI. This must be initialized.
  * \param Y        The second MPI. This must be initialized.
- * \param assign   The condition deciding whether to perform
- *                 the swap or not. Possible values:
+ * \param swap     The condition deciding whether to perform
+ *                 the swap or not. Must be either 0 or 1:
  *                 * \c 1: Swap the values of \p X and \p Y.
  *                 * \c 0: Keep the original values of \p X and \p Y.
  *
  * \note           This function is equivalent to
- *                      if( assign ) mbedtls_mpi_swap( X, Y );
+ *                      if( swap ) mbedtls_mpi_swap( X, Y );
  *                 except that it avoids leaking any information about whether
- *                 the assignment was done or not (the above code may leak
+ *                 the swap was done or not (the above code may leak
  *                 information through branch prediction and/or memory access
  *                 patterns analysis).
  *
+ * \warning        If \p swap is neither 0 nor 1, the result of this function
+ *                 is indeterminate, and both \p X and \p Y might end up with
+ *                 values different to either of the original ones.
+ *
  * \return         \c 0 if successful.
  * \return         #MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed.
  * \return         Another negative error code on other kinds of failure.
  *
  */
-int mbedtls_mpi_safe_cond_swap( mbedtls_mpi *X, mbedtls_mpi *Y, unsigned char assign );
+int mbedtls_mpi_safe_cond_swap(mbedtls_mpi *X, mbedtls_mpi *Y, unsigned char swap);
 
 /**
  * \brief          Store integer value in MPI.
@@ -332,7 +361,7 @@
  * \return         #MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed.
  * \return         Another negative error code on other kinds of failure.
  */
-int mbedtls_mpi_lset( mbedtls_mpi *X, mbedtls_mpi_sint z );
+int mbedtls_mpi_lset(mbedtls_mpi *X, mbedtls_mpi_sint z);
 
 /**
  * \brief          Get a specific bit from an MPI.
@@ -344,7 +373,7 @@
  *                 of \c X is unset or set.
  * \return         A negative error code on failure.
  */
-int mbedtls_mpi_get_bit( const mbedtls_mpi *X, size_t pos );
+int mbedtls_mpi_get_bit(const mbedtls_mpi *X, size_t pos);
 
 /**
  * \brief          Modify a specific bit in an MPI.
@@ -361,7 +390,7 @@
  * \return         #MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed.
  * \return         Another negative error code on other kinds of failure.
  */
-int mbedtls_mpi_set_bit( mbedtls_mpi *X, size_t pos, unsigned char val );
+int mbedtls_mpi_set_bit(mbedtls_mpi *X, size_t pos, unsigned char val);
 
 /**
  * \brief          Return the number of bits of value \c 0 before the
@@ -375,7 +404,7 @@
  * \return         The number of bits of value \c 0 before the least significant
  *                 bit of value \c 1 in \p X.
  */
-size_t mbedtls_mpi_lsb( const mbedtls_mpi *X );
+size_t mbedtls_mpi_lsb(const mbedtls_mpi *X);
 
 /**
  * \brief          Return the number of bits up to and including the most
@@ -389,7 +418,7 @@
  * \return         The number of bits up to and including the most
  *                 significant bit of value \c 1.
  */
-size_t mbedtls_mpi_bitlen( const mbedtls_mpi *X );
+size_t mbedtls_mpi_bitlen(const mbedtls_mpi *X);
 
 /**
  * \brief          Return the total size of an MPI value in bytes.
@@ -404,7 +433,7 @@
  * \return         The least number of bytes capable of storing
  *                 the absolute value of \p X.
  */
-size_t mbedtls_mpi_size( const mbedtls_mpi *X );
+size_t mbedtls_mpi_size(const mbedtls_mpi *X);
 
 /**
  * \brief          Import an MPI from an ASCII string.
@@ -416,7 +445,7 @@
  * \return         \c 0 if successful.
  * \return         A negative error code on failure.
  */
-int mbedtls_mpi_read_string( mbedtls_mpi *X, int radix, const char *s );
+int mbedtls_mpi_read_string(mbedtls_mpi *X, int radix, const char *s);
 
 /**
  * \brief          Export an MPI to an ASCII string.
@@ -440,8 +469,8 @@
  *                 size of \p buf required for a successful call.
  * \return         Another negative error code on different kinds of failure.
  */
-int mbedtls_mpi_write_string( const mbedtls_mpi *X, int radix,
-                              char *buf, size_t buflen, size_t *olen );
+int mbedtls_mpi_write_string(const mbedtls_mpi *X, int radix,
+                             char *buf, size_t buflen, size_t *olen);
 
 #if defined(MBEDTLS_FS_IO)
 /**
@@ -465,7 +494,7 @@
  *                 is too small.
  * \return         Another negative error code on failure.
  */
-int mbedtls_mpi_read_file( mbedtls_mpi *X, int radix, FILE *fin );
+int mbedtls_mpi_read_file(mbedtls_mpi *X, int radix, FILE *fin);
 
 /**
  * \brief          Export an MPI into an opened file.
@@ -482,8 +511,8 @@
  * \return         \c 0 if successful.
  * \return         A negative error code on failure.
  */
-int mbedtls_mpi_write_file( const char *p, const mbedtls_mpi *X,
-                            int radix, FILE *fout );
+int mbedtls_mpi_write_file(const char *p, const mbedtls_mpi *X,
+                           int radix, FILE *fout);
 #endif /* MBEDTLS_FS_IO */
 
 /**
@@ -492,14 +521,14 @@
  * \param X        The destination MPI. This must point to an initialized MPI.
  * \param buf      The input buffer. This must be a readable buffer of length
  *                 \p buflen Bytes.
- * \param buflen   The length of the input buffer \p p in Bytes.
+ * \param buflen   The length of the input buffer \p buf in Bytes.
  *
  * \return         \c 0 if successful.
  * \return         #MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed.
  * \return         Another negative error code on different kinds of failure.
  */
-int mbedtls_mpi_read_binary( mbedtls_mpi *X, const unsigned char *buf,
-                             size_t buflen );
+int mbedtls_mpi_read_binary(mbedtls_mpi *X, const unsigned char *buf,
+                            size_t buflen);
 
 /**
  * \brief          Import X from unsigned binary data, little endian
@@ -507,14 +536,14 @@
  * \param X        The destination MPI. This must point to an initialized MPI.
  * \param buf      The input buffer. This must be a readable buffer of length
  *                 \p buflen Bytes.
- * \param buflen   The length of the input buffer \p p in Bytes.
+ * \param buflen   The length of the input buffer \p buf in Bytes.
  *
  * \return         \c 0 if successful.
  * \return         #MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed.
  * \return         Another negative error code on different kinds of failure.
  */
-int mbedtls_mpi_read_binary_le( mbedtls_mpi *X,
-                                const unsigned char *buf, size_t buflen );
+int mbedtls_mpi_read_binary_le(mbedtls_mpi *X,
+                               const unsigned char *buf, size_t buflen);
 
 /**
  * \brief          Export X into unsigned binary data, big endian.
@@ -531,8 +560,8 @@
  *                 large enough to hold the value of \p X.
  * \return         Another negative error code on different kinds of failure.
  */
-int mbedtls_mpi_write_binary( const mbedtls_mpi *X, unsigned char *buf,
-                              size_t buflen );
+int mbedtls_mpi_write_binary(const mbedtls_mpi *X, unsigned char *buf,
+                             size_t buflen);
 
 /**
  * \brief          Export X into unsigned binary data, little endian.
@@ -549,8 +578,8 @@
  *                 large enough to hold the value of \p X.
  * \return         Another negative error code on different kinds of failure.
  */
-int mbedtls_mpi_write_binary_le( const mbedtls_mpi *X,
-                                 unsigned char *buf, size_t buflen );
+int mbedtls_mpi_write_binary_le(const mbedtls_mpi *X,
+                                unsigned char *buf, size_t buflen);
 
 /**
  * \brief          Perform a left-shift on an MPI: X <<= count
@@ -562,7 +591,7 @@
  * \return         #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed.
  * \return         Another negative error code on different kinds of failure.
  */
-int mbedtls_mpi_shift_l( mbedtls_mpi *X, size_t count );
+int mbedtls_mpi_shift_l(mbedtls_mpi *X, size_t count);
 
 /**
  * \brief          Perform a right-shift on an MPI: X >>= count
@@ -574,7 +603,7 @@
  * \return         #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed.
  * \return         Another negative error code on different kinds of failure.
  */
-int mbedtls_mpi_shift_r( mbedtls_mpi *X, size_t count );
+int mbedtls_mpi_shift_r(mbedtls_mpi *X, size_t count);
 
 /**
  * \brief          Compare the absolute values of two MPIs.
@@ -586,7 +615,7 @@
  * \return         \c -1 if `|X|` is lesser than `|Y|`.
  * \return         \c 0 if `|X|` is equal to `|Y|`.
  */
-int mbedtls_mpi_cmp_abs( const mbedtls_mpi *X, const mbedtls_mpi *Y );
+int mbedtls_mpi_cmp_abs(const mbedtls_mpi *X, const mbedtls_mpi *Y);
 
 /**
  * \brief          Compare two MPIs.
@@ -598,7 +627,7 @@
  * \return         \c -1 if \p X is lesser than \p Y.
  * \return         \c 0 if \p X is equal to \p Y.
  */
-int mbedtls_mpi_cmp_mpi( const mbedtls_mpi *X, const mbedtls_mpi *Y );
+int mbedtls_mpi_cmp_mpi(const mbedtls_mpi *X, const mbedtls_mpi *Y);
 
 /**
  * \brief          Check if an MPI is less than the other in constant time.
@@ -615,8 +644,8 @@
  * \return         MBEDTLS_ERR_MPI_BAD_INPUT_DATA if the allocated length of
  *                 the two input MPIs is not the same.
  */
-int mbedtls_mpi_lt_mpi_ct( const mbedtls_mpi *X, const mbedtls_mpi *Y,
-        unsigned *ret );
+int mbedtls_mpi_lt_mpi_ct(const mbedtls_mpi *X, const mbedtls_mpi *Y,
+                          unsigned *ret);
 
 /**
  * \brief          Compare an MPI with an integer.
@@ -628,7 +657,7 @@
  * \return         \c -1 if \p X is lesser than \p z.
  * \return         \c 0 if \p X is equal to \p z.
  */
-int mbedtls_mpi_cmp_int( const mbedtls_mpi *X, mbedtls_mpi_sint z );
+int mbedtls_mpi_cmp_int(const mbedtls_mpi *X, mbedtls_mpi_sint z);
 
 /**
  * \brief          Perform an unsigned addition of MPIs: X = |A| + |B|
@@ -641,8 +670,8 @@
  * \return         #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed.
  * \return         Another negative error code on different kinds of failure.
  */
-int mbedtls_mpi_add_abs( mbedtls_mpi *X, const mbedtls_mpi *A,
-                         const mbedtls_mpi *B );
+int mbedtls_mpi_add_abs(mbedtls_mpi *X, const mbedtls_mpi *A,
+                        const mbedtls_mpi *B);
 
 /**
  * \brief          Perform an unsigned subtraction of MPIs: X = |A| - |B|
@@ -656,8 +685,8 @@
  * \return         Another negative error code on different kinds of failure.
  *
  */
-int mbedtls_mpi_sub_abs( mbedtls_mpi *X, const mbedtls_mpi *A,
-                         const mbedtls_mpi *B );
+int mbedtls_mpi_sub_abs(mbedtls_mpi *X, const mbedtls_mpi *A,
+                        const mbedtls_mpi *B);
 
 /**
  * \brief          Perform a signed addition of MPIs: X = A + B
@@ -670,8 +699,8 @@
  * \return         #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed.
  * \return         Another negative error code on different kinds of failure.
  */
-int mbedtls_mpi_add_mpi( mbedtls_mpi *X, const mbedtls_mpi *A,
-                         const mbedtls_mpi *B );
+int mbedtls_mpi_add_mpi(mbedtls_mpi *X, const mbedtls_mpi *A,
+                        const mbedtls_mpi *B);
 
 /**
  * \brief          Perform a signed subtraction of MPIs: X = A - B
@@ -684,8 +713,8 @@
  * \return         #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed.
  * \return         Another negative error code on different kinds of failure.
  */
-int mbedtls_mpi_sub_mpi( mbedtls_mpi *X, const mbedtls_mpi *A,
-                         const mbedtls_mpi *B );
+int mbedtls_mpi_sub_mpi(mbedtls_mpi *X, const mbedtls_mpi *A,
+                        const mbedtls_mpi *B);
 
 /**
  * \brief          Perform a signed addition of an MPI and an integer: X = A + b
@@ -698,8 +727,8 @@
  * \return         #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed.
  * \return         Another negative error code on different kinds of failure.
  */
-int mbedtls_mpi_add_int( mbedtls_mpi *X, const mbedtls_mpi *A,
-                         mbedtls_mpi_sint b );
+int mbedtls_mpi_add_int(mbedtls_mpi *X, const mbedtls_mpi *A,
+                        mbedtls_mpi_sint b);
 
 /**
  * \brief          Perform a signed subtraction of an MPI and an integer:
@@ -713,8 +742,8 @@
  * \return         #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed.
  * \return         Another negative error code on different kinds of failure.
  */
-int mbedtls_mpi_sub_int( mbedtls_mpi *X, const mbedtls_mpi *A,
-                         mbedtls_mpi_sint b );
+int mbedtls_mpi_sub_int(mbedtls_mpi *X, const mbedtls_mpi *A,
+                        mbedtls_mpi_sint b);
 
 /**
  * \brief          Perform a multiplication of two MPIs: X = A * B
@@ -728,8 +757,8 @@
  * \return         Another negative error code on different kinds of failure.
  *
  */
-int mbedtls_mpi_mul_mpi( mbedtls_mpi *X, const mbedtls_mpi *A,
-                         const mbedtls_mpi *B );
+int mbedtls_mpi_mul_mpi(mbedtls_mpi *X, const mbedtls_mpi *A,
+                        const mbedtls_mpi *B);
 
 /**
  * \brief          Perform a multiplication of an MPI with an unsigned integer:
@@ -744,8 +773,8 @@
  * \return         Another negative error code on different kinds of failure.
  *
  */
-int mbedtls_mpi_mul_int( mbedtls_mpi *X, const mbedtls_mpi *A,
-                         mbedtls_mpi_uint b );
+int mbedtls_mpi_mul_int(mbedtls_mpi *X, const mbedtls_mpi *A,
+                        mbedtls_mpi_uint b);
 
 /**
  * \brief          Perform a division with remainder of two MPIs:
@@ -753,11 +782,11 @@
  *
  * \param Q        The destination MPI for the quotient.
  *                 This may be \c NULL if the value of the
- *                 quotient is not needed.
+ *                 quotient is not needed. This must not alias A or B.
  * \param R        The destination MPI for the remainder value.
  *                 This may be \c NULL if the value of the
- *                 remainder is not needed.
- * \param A        The dividend. This must point to an initialized MPi.
+ *                 remainder is not needed. This must not alias A or B.
+ * \param A        The dividend. This must point to an initialized MPI.
  * \param B        The divisor. This must point to an initialized MPI.
  *
  * \return         \c 0 if successful.
@@ -765,8 +794,8 @@
  * \return         #MBEDTLS_ERR_MPI_DIVISION_BY_ZERO if \p B equals zero.
  * \return         Another negative error code on different kinds of failure.
  */
-int mbedtls_mpi_div_mpi( mbedtls_mpi *Q, mbedtls_mpi *R, const mbedtls_mpi *A,
-                         const mbedtls_mpi *B );
+int mbedtls_mpi_div_mpi(mbedtls_mpi *Q, mbedtls_mpi *R, const mbedtls_mpi *A,
+                        const mbedtls_mpi *B);
 
 /**
  * \brief          Perform a division with remainder of an MPI by an integer:
@@ -774,10 +803,10 @@
  *
  * \param Q        The destination MPI for the quotient.
  *                 This may be \c NULL if the value of the
- *                 quotient is not needed.
+ *                 quotient is not needed.  This must not alias A.
  * \param R        The destination MPI for the remainder value.
  *                 This may be \c NULL if the value of the
- *                 remainder is not needed.
+ *                 remainder is not needed.  This must not alias A.
  * \param A        The dividend. This must point to an initialized MPi.
  * \param b        The divisor.
  *
@@ -786,8 +815,8 @@
  * \return         #MBEDTLS_ERR_MPI_DIVISION_BY_ZERO if \p b equals zero.
  * \return         Another negative error code on different kinds of failure.
  */
-int mbedtls_mpi_div_int( mbedtls_mpi *Q, mbedtls_mpi *R, const mbedtls_mpi *A,
-                         mbedtls_mpi_sint b );
+int mbedtls_mpi_div_int(mbedtls_mpi *Q, mbedtls_mpi *R, const mbedtls_mpi *A,
+                        mbedtls_mpi_sint b);
 
 /**
  * \brief          Perform a modular reduction. R = A mod B
@@ -806,8 +835,8 @@
  * \return         Another negative error code on different kinds of failure.
  *
  */
-int mbedtls_mpi_mod_mpi( mbedtls_mpi *R, const mbedtls_mpi *A,
-                         const mbedtls_mpi *B );
+int mbedtls_mpi_mod_mpi(mbedtls_mpi *R, const mbedtls_mpi *A,
+                        const mbedtls_mpi *B);
 
 /**
  * \brief          Perform a modular reduction with respect to an integer.
@@ -825,13 +854,14 @@
  * \return         #MBEDTLS_ERR_MPI_NEGATIVE_VALUE if \p b is negative.
  * \return         Another negative error code on different kinds of failure.
  */
-int mbedtls_mpi_mod_int( mbedtls_mpi_uint *r, const mbedtls_mpi *A,
-                         mbedtls_mpi_sint b );
+int mbedtls_mpi_mod_int(mbedtls_mpi_uint *r, const mbedtls_mpi *A,
+                        mbedtls_mpi_sint b);
 
 /**
  * \brief          Perform a sliding-window exponentiation: X = A^E mod N
  *
  * \param X        The destination MPI. This must point to an initialized MPI.
+ *                 This must not alias E or N.
  * \param A        The base of the exponentiation.
  *                 This must point to an initialized MPI.
  * \param E        The exponent MPI. This must point to an initialized MPI.
@@ -854,9 +884,9 @@
  * \return         Another negative error code on different kinds of failures.
  *
  */
-int mbedtls_mpi_exp_mod( mbedtls_mpi *X, const mbedtls_mpi *A,
-                         const mbedtls_mpi *E, const mbedtls_mpi *N,
-                         mbedtls_mpi *prec_RR );
+int mbedtls_mpi_exp_mod(mbedtls_mpi *X, const mbedtls_mpi *A,
+                        const mbedtls_mpi *E, const mbedtls_mpi *N,
+                        mbedtls_mpi *prec_RR);
 
 /**
  * \brief          Fill an MPI with a number of random bytes.
@@ -875,9 +905,9 @@
  *                 as a big-endian representation of an MPI; this can
  *                 be relevant in applications like deterministic ECDSA.
  */
-int mbedtls_mpi_fill_random( mbedtls_mpi *X, size_t size,
-                     int (*f_rng)(void *, unsigned char *, size_t),
-                     void *p_rng );
+int mbedtls_mpi_fill_random(mbedtls_mpi *X, size_t size,
+                            int (*f_rng)(void *, unsigned char *, size_t),
+                            void *p_rng);
 
 /** Generate a random number uniformly in a range.
  *
@@ -911,11 +941,11 @@
  *                 for all usual cryptographic applications.
  * \return         Another negative error code on failure.
  */
-int mbedtls_mpi_random( mbedtls_mpi *X,
-                        mbedtls_mpi_sint min,
-                        const mbedtls_mpi *N,
-                        int (*f_rng)(void *, unsigned char *, size_t),
-                        void *p_rng );
+int mbedtls_mpi_random(mbedtls_mpi *X,
+                       mbedtls_mpi_sint min,
+                       const mbedtls_mpi *N,
+                       int (*f_rng)(void *, unsigned char *, size_t),
+                       void *p_rng);
 
 /**
  * \brief          Compute the greatest common divisor: G = gcd(A, B)
@@ -928,8 +958,8 @@
  * \return         #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed.
  * \return         Another negative error code on different kinds of failure.
  */
-int mbedtls_mpi_gcd( mbedtls_mpi *G, const mbedtls_mpi *A,
-                     const mbedtls_mpi *B );
+int mbedtls_mpi_gcd(mbedtls_mpi *G, const mbedtls_mpi *A,
+                    const mbedtls_mpi *B);
 
 /**
  * \brief          Compute the modular inverse: X = A^-1 mod N
@@ -944,11 +974,11 @@
  * \return         #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed.
  * \return         #MBEDTLS_ERR_MPI_BAD_INPUT_DATA if \p N is less than
  *                 or equal to one.
- * \return         #MBEDTLS_ERR_MPI_NOT_ACCEPTABLE if \p has no modular inverse
- *                 with respect to \p N.
+ * \return         #MBEDTLS_ERR_MPI_NOT_ACCEPTABLE if \p A has no modular
+ *                 inverse with respect to \p N.
  */
-int mbedtls_mpi_inv_mod( mbedtls_mpi *X, const mbedtls_mpi *A,
-                         const mbedtls_mpi *N );
+int mbedtls_mpi_inv_mod(mbedtls_mpi *X, const mbedtls_mpi *A,
+                        const mbedtls_mpi *N);
 
 #if !defined(MBEDTLS_DEPRECATED_REMOVED)
 #if defined(MBEDTLS_DEPRECATED_WARNING)
@@ -975,9 +1005,9 @@
  * \return         #MBEDTLS_ERR_MPI_NOT_ACCEPTABLE if \p X is not prime.
  * \return         Another negative error code on other kinds of failure.
  */
-MBEDTLS_DEPRECATED int mbedtls_mpi_is_prime( const mbedtls_mpi *X,
-                          int (*f_rng)(void *, unsigned char *, size_t),
-                          void *p_rng );
+MBEDTLS_DEPRECATED int mbedtls_mpi_is_prime(const mbedtls_mpi *X,
+                                            int (*f_rng)(void *, unsigned char *, size_t),
+                                            void *p_rng);
 #undef MBEDTLS_DEPRECATED
 #endif /* !MBEDTLS_DEPRECATED_REMOVED */
 
@@ -989,7 +1019,7 @@
  *                 generate yourself and that are supposed to be prime, then
  *                 \p rounds should be at least the half of the security
  *                 strength of the cryptographic algorithm. On the other hand,
- *                 if \p X is chosen uniformly or non-adversially (as is the
+ *                 if \p X is chosen uniformly or non-adversarially (as is the
  *                 case when mbedtls_mpi_gen_prime calls this function), then
  *                 \p rounds can be much lower.
  *
@@ -997,7 +1027,7 @@
  *                 This must point to an initialized MPI.
  * \param rounds   The number of bases to perform the Miller-Rabin primality
  *                 test for. The probability of returning 0 on a composite is
- *                 at most 2<sup>-2*\p rounds</sup>.
+ *                 at most 2<sup>-2*\p rounds </sup>.
  * \param f_rng    The RNG function to use. This must not be \c NULL.
  * \param p_rng    The RNG parameter to be passed to \p f_rng.
  *                 This may be \c NULL if \p f_rng doesn't use
@@ -1008,9 +1038,9 @@
  * \return         #MBEDTLS_ERR_MPI_NOT_ACCEPTABLE if \p X is not prime.
  * \return         Another negative error code on other kinds of failure.
  */
-int mbedtls_mpi_is_prime_ext( const mbedtls_mpi *X, int rounds,
-                              int (*f_rng)(void *, unsigned char *, size_t),
-                              void *p_rng );
+int mbedtls_mpi_is_prime_ext(const mbedtls_mpi *X, int rounds,
+                             int (*f_rng)(void *, unsigned char *, size_t),
+                             void *p_rng);
 /**
  * \brief Flags for mbedtls_mpi_gen_prime()
  *
@@ -1041,9 +1071,9 @@
  * \return         #MBEDTLS_ERR_MPI_BAD_INPUT_DATA if `nbits` is not between
  *                 \c 3 and #MBEDTLS_MPI_MAX_BITS.
  */
-int mbedtls_mpi_gen_prime( mbedtls_mpi *X, size_t nbits, int flags,
-                   int (*f_rng)(void *, unsigned char *, size_t),
-                   void *p_rng );
+int mbedtls_mpi_gen_prime(mbedtls_mpi *X, size_t nbits, int flags,
+                          int (*f_rng)(void *, unsigned char *, size_t),
+                          void *p_rng);
 
 #if defined(MBEDTLS_SELF_TEST)
 
@@ -1052,7 +1082,7 @@
  *
  * \return         0 if successful, or 1 if the test failed
  */
-int mbedtls_mpi_self_test( int verbose );
+int mbedtls_mpi_self_test(int verbose);
 
 #endif /* MBEDTLS_SELF_TEST */
 
diff --git a/third_party/mbedtls/repo/include/mbedtls/blowfish.h b/third_party/mbedtls/repo/include/mbedtls/blowfish.h
index 77dca70..7979670 100644
--- a/third_party/mbedtls/repo/include/mbedtls/blowfish.h
+++ b/third_party/mbedtls/repo/include/mbedtls/blowfish.h
@@ -5,19 +5,7 @@
  */
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 #ifndef MBEDTLS_BLOWFISH_H
 #define MBEDTLS_BLOWFISH_H
@@ -41,7 +29,7 @@
 #define MBEDTLS_BLOWFISH_BLOCKSIZE   8          /* Blowfish uses 64 bit blocks */
 
 #if !defined(MBEDTLS_DEPRECATED_REMOVED)
-#define MBEDTLS_ERR_BLOWFISH_INVALID_KEY_LENGTH   MBEDTLS_DEPRECATED_NUMERIC_CONSTANT( -0x0016 )
+#define MBEDTLS_ERR_BLOWFISH_INVALID_KEY_LENGTH   MBEDTLS_DEPRECATED_NUMERIC_CONSTANT(-0x0016)
 #endif /* !MBEDTLS_DEPRECATED_REMOVED */
 /** Bad input data. */
 #define MBEDTLS_ERR_BLOWFISH_BAD_INPUT_DATA -0x0016
@@ -65,8 +53,7 @@
 /**
  * \brief          Blowfish context structure
  */
-typedef struct mbedtls_blowfish_context
-{
+typedef struct mbedtls_blowfish_context {
     uint32_t P[MBEDTLS_BLOWFISH_ROUNDS + 2];    /*!<  Blowfish round keys    */
     uint32_t S[4][256];                 /*!<  key dependent S-boxes  */
 }
@@ -82,7 +69,7 @@
  * \param ctx      The Blowfish context to be initialized.
  *                 This must not be \c NULL.
  */
-void mbedtls_blowfish_init( mbedtls_blowfish_context *ctx );
+void mbedtls_blowfish_init(mbedtls_blowfish_context *ctx);
 
 /**
  * \brief          Clear a Blowfish context.
@@ -92,7 +79,7 @@
  *                 returns immediately. If it is not \c NULL, it must
  *                 point to an initialized Blowfish context.
  */
-void mbedtls_blowfish_free( mbedtls_blowfish_context *ctx );
+void mbedtls_blowfish_free(mbedtls_blowfish_context *ctx);
 
 /**
  * \brief          Perform a Blowfish key schedule operation.
@@ -106,8 +93,8 @@
  * \return         \c 0 if successful.
  * \return         A negative error code on failure.
  */
-int mbedtls_blowfish_setkey( mbedtls_blowfish_context *ctx, const unsigned char *key,
-                     unsigned int keybits );
+int mbedtls_blowfish_setkey(mbedtls_blowfish_context *ctx, const unsigned char *key,
+                            unsigned int keybits);
 
 /**
  * \brief          Perform a Blowfish-ECB block encryption/decryption operation.
@@ -125,10 +112,10 @@
  * \return         \c 0 if successful.
  * \return         A negative error code on failure.
  */
-int mbedtls_blowfish_crypt_ecb( mbedtls_blowfish_context *ctx,
-                        int mode,
-                        const unsigned char input[MBEDTLS_BLOWFISH_BLOCKSIZE],
-                        unsigned char output[MBEDTLS_BLOWFISH_BLOCKSIZE] );
+int mbedtls_blowfish_crypt_ecb(mbedtls_blowfish_context *ctx,
+                               int mode,
+                               const unsigned char input[MBEDTLS_BLOWFISH_BLOCKSIZE],
+                               unsigned char output[MBEDTLS_BLOWFISH_BLOCKSIZE]);
 
 #if defined(MBEDTLS_CIPHER_MODE_CBC)
 /**
@@ -159,12 +146,12 @@
  * \return         \c 0 if successful.
  * \return         A negative error code on failure.
  */
-int mbedtls_blowfish_crypt_cbc( mbedtls_blowfish_context *ctx,
-                        int mode,
-                        size_t length,
-                        unsigned char iv[MBEDTLS_BLOWFISH_BLOCKSIZE],
-                        const unsigned char *input,
-                        unsigned char *output );
+int mbedtls_blowfish_crypt_cbc(mbedtls_blowfish_context *ctx,
+                               int mode,
+                               size_t length,
+                               unsigned char iv[MBEDTLS_BLOWFISH_BLOCKSIZE],
+                               const unsigned char *input,
+                               unsigned char *output);
 #endif /* MBEDTLS_CIPHER_MODE_CBC */
 
 #if defined(MBEDTLS_CIPHER_MODE_CFB)
@@ -185,7 +172,7 @@
  *                 #MBEDTLS_BLOWFISH_ENCRYPT for encryption, or
  *                 #MBEDTLS_BLOWFISH_DECRYPT for decryption.
  * \param length   The length of the input data in Bytes.
- * \param iv_off   The offset in the initialiation vector.
+ * \param iv_off   The offset in the initialization vector.
  *                 The value pointed to must be smaller than \c 8 Bytes.
  *                 It is updated by this function to support the aforementioned
  *                 streaming usage.
@@ -199,13 +186,13 @@
  * \return         \c 0 if successful.
  * \return         A negative error code on failure.
  */
-int mbedtls_blowfish_crypt_cfb64( mbedtls_blowfish_context *ctx,
-                          int mode,
-                          size_t length,
-                          size_t *iv_off,
-                          unsigned char iv[MBEDTLS_BLOWFISH_BLOCKSIZE],
-                          const unsigned char *input,
-                          unsigned char *output );
+int mbedtls_blowfish_crypt_cfb64(mbedtls_blowfish_context *ctx,
+                                 int mode,
+                                 size_t length,
+                                 size_t *iv_off,
+                                 unsigned char iv[MBEDTLS_BLOWFISH_BLOCKSIZE],
+                                 const unsigned char *input,
+                                 unsigned char *output);
 #endif /*MBEDTLS_CIPHER_MODE_CFB */
 
 #if defined(MBEDTLS_CIPHER_MODE_CTR)
@@ -246,7 +233,7 @@
  *             The recommended way to ensure uniqueness is to use a message
  *             counter.
  *
- *             Note that for both stategies, sizes are measured in blocks and
+ *             Note that for both strategies, sizes are measured in blocks and
  *             that a Blowfish block is 8 bytes.
  *
  * \warning    Upon return, \p stream_block contains sensitive data. Its
@@ -272,13 +259,13 @@
  * \return              \c 0 if successful.
  * \return              A negative error code on failure.
  */
-int mbedtls_blowfish_crypt_ctr( mbedtls_blowfish_context *ctx,
-                        size_t length,
-                        size_t *nc_off,
-                        unsigned char nonce_counter[MBEDTLS_BLOWFISH_BLOCKSIZE],
-                        unsigned char stream_block[MBEDTLS_BLOWFISH_BLOCKSIZE],
-                        const unsigned char *input,
-                        unsigned char *output );
+int mbedtls_blowfish_crypt_ctr(mbedtls_blowfish_context *ctx,
+                               size_t length,
+                               size_t *nc_off,
+                               unsigned char nonce_counter[MBEDTLS_BLOWFISH_BLOCKSIZE],
+                               unsigned char stream_block[MBEDTLS_BLOWFISH_BLOCKSIZE],
+                               const unsigned char *input,
+                               unsigned char *output);
 #endif /* MBEDTLS_CIPHER_MODE_CTR */
 
 #ifdef __cplusplus
diff --git a/third_party/mbedtls/repo/include/mbedtls/bn_mul.h b/third_party/mbedtls/repo/include/mbedtls/bn_mul.h
index 31137cd..fc0c3cf 100644
--- a/third_party/mbedtls/repo/include/mbedtls/bn_mul.h
+++ b/third_party/mbedtls/repo/include/mbedtls/bn_mul.h
@@ -5,19 +5,7 @@
  */
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 /*
  *      Multiply source vector [s] with b, add result
@@ -51,39 +39,40 @@
  */
 #if defined(MBEDTLS_HAVE_INT32)
 
-#define MBEDTLS_BYTES_TO_T_UINT_4( a, b, c, d )               \
-    ( (mbedtls_mpi_uint) (a) <<  0 ) |                        \
-    ( (mbedtls_mpi_uint) (b) <<  8 ) |                        \
-    ( (mbedtls_mpi_uint) (c) << 16 ) |                        \
-    ( (mbedtls_mpi_uint) (d) << 24 )
+#define MBEDTLS_BYTES_TO_T_UINT_4(a, b, c, d)               \
+    ((mbedtls_mpi_uint) (a) <<  0) |                        \
+    ((mbedtls_mpi_uint) (b) <<  8) |                        \
+    ((mbedtls_mpi_uint) (c) << 16) |                        \
+    ((mbedtls_mpi_uint) (d) << 24)
 
-#define MBEDTLS_BYTES_TO_T_UINT_2( a, b )                   \
-    MBEDTLS_BYTES_TO_T_UINT_4( a, b, 0, 0 )
+#define MBEDTLS_BYTES_TO_T_UINT_2(a, b)                   \
+    MBEDTLS_BYTES_TO_T_UINT_4(a, b, 0, 0)
 
-#define MBEDTLS_BYTES_TO_T_UINT_8( a, b, c, d, e, f, g, h ) \
-    MBEDTLS_BYTES_TO_T_UINT_4( a, b, c, d ),                \
-    MBEDTLS_BYTES_TO_T_UINT_4( e, f, g, h )
+#define MBEDTLS_BYTES_TO_T_UINT_8(a, b, c, d, e, f, g, h) \
+    MBEDTLS_BYTES_TO_T_UINT_4(a, b, c, d),                \
+    MBEDTLS_BYTES_TO_T_UINT_4(e, f, g, h)
 
 #else /* 64-bits */
 
-#define MBEDTLS_BYTES_TO_T_UINT_8( a, b, c, d, e, f, g, h )   \
-    ( (mbedtls_mpi_uint) (a) <<  0 ) |                        \
-    ( (mbedtls_mpi_uint) (b) <<  8 ) |                        \
-    ( (mbedtls_mpi_uint) (c) << 16 ) |                        \
-    ( (mbedtls_mpi_uint) (d) << 24 ) |                        \
-    ( (mbedtls_mpi_uint) (e) << 32 ) |                        \
-    ( (mbedtls_mpi_uint) (f) << 40 ) |                        \
-    ( (mbedtls_mpi_uint) (g) << 48 ) |                        \
-    ( (mbedtls_mpi_uint) (h) << 56 )
+#define MBEDTLS_BYTES_TO_T_UINT_8(a, b, c, d, e, f, g, h)   \
+    ((mbedtls_mpi_uint) (a) <<  0) |                        \
+    ((mbedtls_mpi_uint) (b) <<  8) |                        \
+    ((mbedtls_mpi_uint) (c) << 16) |                        \
+    ((mbedtls_mpi_uint) (d) << 24) |                        \
+    ((mbedtls_mpi_uint) (e) << 32) |                        \
+    ((mbedtls_mpi_uint) (f) << 40) |                        \
+    ((mbedtls_mpi_uint) (g) << 48) |                        \
+    ((mbedtls_mpi_uint) (h) << 56)
 
-#define MBEDTLS_BYTES_TO_T_UINT_4( a, b, c, d )             \
-    MBEDTLS_BYTES_TO_T_UINT_8( a, b, c, d, 0, 0, 0, 0 )
+#define MBEDTLS_BYTES_TO_T_UINT_4(a, b, c, d)             \
+    MBEDTLS_BYTES_TO_T_UINT_8(a, b, c, d, 0, 0, 0, 0)
 
-#define MBEDTLS_BYTES_TO_T_UINT_2( a, b )                   \
-    MBEDTLS_BYTES_TO_T_UINT_8( a, b, 0, 0, 0, 0, 0, 0 )
+#define MBEDTLS_BYTES_TO_T_UINT_2(a, b)                   \
+    MBEDTLS_BYTES_TO_T_UINT_8(a, b, 0, 0, 0, 0, 0, 0)
 
 #endif /* bits in mbedtls_mpi_uint */
 
+/* *INDENT-OFF* */
 #if defined(MBEDTLS_HAVE_ASM)
 
 #ifndef asm
@@ -95,12 +84,28 @@
     ( !defined(__ARMCC_VERSION) || __ARMCC_VERSION >= 6000000 )
 
 /*
+ * GCC < 5.0 treated the x86 ebx (which is used for the GOT) as a
+ * fixed reserved register when building as PIC, leading to errors
+ * like: bn_mul.h:46:13: error: PIC register clobbered by 'ebx' in 'asm'
+ *
+ * This is fixed by an improved register allocator in GCC 5+. From the
+ * release notes:
+ * Register allocation improvements: Reuse of the PIC hard register,
+ * instead of using a fixed register, was implemented on x86/x86-64
+ * targets. This improves generated PIC code performance as more hard
+ * registers can be used.
+ */
+#if defined(__GNUC__) && __GNUC__ < 5 && defined(__PIC__)
+#define MULADDC_CANNOT_USE_EBX
+#endif
+
+/*
  * Disable use of the i386 assembly code below if option -O0, to disable all
  * compiler optimisations, is passed, detected with __OPTIMIZE__
  * This is done as the number of registers used in the assembly code doesn't
  * work with the -O0 option.
  */
-#if defined(__i386__) && defined(__OPTIMIZE__)
+#if defined(__i386__) && defined(__OPTIMIZE__) && !defined(MULADDC_CANNOT_USE_EBX)
 
 #define MULADDC_INIT                        \
     asm(                                    \
@@ -563,10 +568,20 @@
         "andi  r7,   r6, 0xffff \n\t"   \
         "bsrli r6,   r6, 16     \n\t"
 
-#define MULADDC_CORE                    \
+#if(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
+#define MULADDC_LHUI                    \
+        "lhui  r9,   r3,   0    \n\t"   \
+        "addi  r3,   r3,   2    \n\t"   \
+        "lhui  r8,   r3,   0    \n\t"
+#else
+#define MULADDC_LHUI                    \
         "lhui  r8,   r3,   0    \n\t"   \
         "addi  r3,   r3,   2    \n\t"   \
-        "lhui  r9,   r3,   0    \n\t"   \
+        "lhui  r9,   r3,   0    \n\t"
+#endif
+
+#define MULADDC_CORE                    \
+        MULADDC_LHUI                    \
         "addi  r3,   r3,   2    \n\t"   \
         "mul   r10,  r9,  r6    \n\t"   \
         "mul   r11,  r8,  r7    \n\t"   \
@@ -650,6 +665,15 @@
 #if defined(__arm__) && !defined(MULADDC_CANNOT_USE_R7)
 
 #if defined(__thumb__) && !defined(__thumb2__)
+#if !defined(__ARMCC_VERSION) && !defined(__clang__) \
+    && !defined(__llvm__) && !defined(__INTEL_COMPILER)
+/*
+ * Thumb 1 ISA. This code path has only been tested successfully on gcc;
+ * it does not compile on clang or armclang.
+ *
+ * Other compilers which define __GNUC__ may not work. The above macro
+ * attempts to exclude these untested compilers.
+ */
 
 #define MULADDC_INIT                                    \
     asm(                                                \
@@ -704,6 +728,8 @@
            "r6", "r7", "r8", "r9", "cc"         \
          );
 
+#endif /* Compiler is gcc */
+
 #elif (__ARM_ARCH >= 6) && \
     defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)
 
@@ -975,4 +1001,5 @@
 #endif /* C (generic)  */
 #endif /* C (longlong) */
 
+/* *INDENT-ON* */
 #endif /* bn_mul.h */
diff --git a/third_party/mbedtls/repo/include/mbedtls/camellia.h b/third_party/mbedtls/repo/include/mbedtls/camellia.h
index 925a623..be8c515 100644
--- a/third_party/mbedtls/repo/include/mbedtls/camellia.h
+++ b/third_party/mbedtls/repo/include/mbedtls/camellia.h
@@ -5,19 +5,7 @@
  */
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 #ifndef MBEDTLS_CAMELLIA_H
 #define MBEDTLS_CAMELLIA_H
@@ -37,7 +25,7 @@
 #define MBEDTLS_CAMELLIA_DECRYPT     0
 
 #if !defined(MBEDTLS_DEPRECATED_REMOVED)
-#define MBEDTLS_ERR_CAMELLIA_INVALID_KEY_LENGTH   MBEDTLS_DEPRECATED_NUMERIC_CONSTANT( -0x0024 )
+#define MBEDTLS_ERR_CAMELLIA_INVALID_KEY_LENGTH   MBEDTLS_DEPRECATED_NUMERIC_CONSTANT(-0x0024)
 #endif /* !MBEDTLS_DEPRECATED_REMOVED */
 /** Bad input data. */
 #define MBEDTLS_ERR_CAMELLIA_BAD_INPUT_DATA -0x0024
@@ -61,8 +49,7 @@
 /**
  * \brief          CAMELLIA context structure
  */
-typedef struct mbedtls_camellia_context
-{
+typedef struct mbedtls_camellia_context {
     int nr;                     /*!<  number of rounds  */
     uint32_t rk[68];            /*!<  CAMELLIA round keys    */
 }
@@ -78,7 +65,7 @@
  * \param ctx      The CAMELLIA context to be initialized.
  *                 This must not be \c NULL.
  */
-void mbedtls_camellia_init( mbedtls_camellia_context *ctx );
+void mbedtls_camellia_init(mbedtls_camellia_context *ctx);
 
 /**
  * \brief          Clear a CAMELLIA context.
@@ -87,7 +74,7 @@
  *                 in which case this function returns immediately. If it is not
  *                 \c NULL, it must be initialized.
  */
-void mbedtls_camellia_free( mbedtls_camellia_context *ctx );
+void mbedtls_camellia_free(mbedtls_camellia_context *ctx);
 
 /**
  * \brief          Perform a CAMELLIA key schedule operation for encryption.
@@ -101,9 +88,9 @@
  * \return         \c 0 if successful.
  * \return         A negative error code on failure.
  */
-int mbedtls_camellia_setkey_enc( mbedtls_camellia_context *ctx,
-                                 const unsigned char *key,
-                                 unsigned int keybits );
+int mbedtls_camellia_setkey_enc(mbedtls_camellia_context *ctx,
+                                const unsigned char *key,
+                                unsigned int keybits);
 
 /**
  * \brief          Perform a CAMELLIA key schedule operation for decryption.
@@ -117,9 +104,9 @@
  * \return         \c 0 if successful.
  * \return         A negative error code on failure.
  */
-int mbedtls_camellia_setkey_dec( mbedtls_camellia_context *ctx,
-                                 const unsigned char *key,
-                                 unsigned int keybits );
+int mbedtls_camellia_setkey_dec(mbedtls_camellia_context *ctx,
+                                const unsigned char *key,
+                                unsigned int keybits);
 
 /**
  * \brief          Perform a CAMELLIA-ECB block encryption/decryption operation.
@@ -136,10 +123,10 @@
  * \return         \c 0 if successful.
  * \return         A negative error code on failure.
  */
-int mbedtls_camellia_crypt_ecb( mbedtls_camellia_context *ctx,
-                    int mode,
-                    const unsigned char input[16],
-                    unsigned char output[16] );
+int mbedtls_camellia_crypt_ecb(mbedtls_camellia_context *ctx,
+                               int mode,
+                               const unsigned char input[16],
+                               unsigned char output[16]);
 
 #if defined(MBEDTLS_CIPHER_MODE_CBC)
 /**
@@ -170,12 +157,12 @@
  * \return         \c 0 if successful.
  * \return         A negative error code on failure.
  */
-int mbedtls_camellia_crypt_cbc( mbedtls_camellia_context *ctx,
-                    int mode,
-                    size_t length,
-                    unsigned char iv[16],
-                    const unsigned char *input,
-                    unsigned char *output );
+int mbedtls_camellia_crypt_cbc(mbedtls_camellia_context *ctx,
+                               int mode,
+                               size_t length,
+                               unsigned char iv[16],
+                               const unsigned char *input,
+                               unsigned char *output);
 #endif /* MBEDTLS_CIPHER_MODE_CBC */
 
 #if defined(MBEDTLS_CIPHER_MODE_CFB)
@@ -216,13 +203,13 @@
  * \return         \c 0 if successful.
  * \return         A negative error code on failure.
  */
-int mbedtls_camellia_crypt_cfb128( mbedtls_camellia_context *ctx,
-                       int mode,
-                       size_t length,
-                       size_t *iv_off,
-                       unsigned char iv[16],
-                       const unsigned char *input,
-                       unsigned char *output );
+int mbedtls_camellia_crypt_cfb128(mbedtls_camellia_context *ctx,
+                                  int mode,
+                                  size_t length,
+                                  size_t *iv_off,
+                                  unsigned char iv[16],
+                                  const unsigned char *input,
+                                  unsigned char *output);
 #endif /* MBEDTLS_CIPHER_MODE_CFB */
 
 #if defined(MBEDTLS_CIPHER_MODE_CTR)
@@ -232,7 +219,7 @@
  * *note       Due to the nature of CTR mode, you should use the same
  *             key for both encryption and decryption. In particular, calls
  *             to this function should be preceded by a key-schedule via
- *             mbedtls_camellia_setkey_enc() regardless of whether \p mode
+ *             mbedtls_camellia_setkey_enc() regardless of whether the mode
  *             is #MBEDTLS_CAMELLIA_ENCRYPT or #MBEDTLS_CAMELLIA_DECRYPT.
  *
  * \warning    You must never reuse a nonce value with the same key. Doing so
@@ -273,7 +260,7 @@
  *             encrypted: for example, with 96-bit random nonces, you should
  *             not encrypt more than 2**32 messages with the same key.
  *
- *             Note that for both stategies, sizes are measured in blocks and
+ *             Note that for both strategies, sizes are measured in blocks and
  *             that a CAMELLIA block is \c 16 Bytes.
  *
  * \warning    Upon return, \p stream_block contains sensitive data. Its
@@ -300,13 +287,13 @@
  * \return              \c 0 if successful.
  * \return              A negative error code on failure.
  */
-int mbedtls_camellia_crypt_ctr( mbedtls_camellia_context *ctx,
-                       size_t length,
-                       size_t *nc_off,
-                       unsigned char nonce_counter[16],
-                       unsigned char stream_block[16],
-                       const unsigned char *input,
-                       unsigned char *output );
+int mbedtls_camellia_crypt_ctr(mbedtls_camellia_context *ctx,
+                               size_t length,
+                               size_t *nc_off,
+                               unsigned char nonce_counter[16],
+                               unsigned char stream_block[16],
+                               const unsigned char *input,
+                               unsigned char *output);
 #endif /* MBEDTLS_CIPHER_MODE_CTR */
 
 #if defined(MBEDTLS_SELF_TEST)
@@ -316,7 +303,7 @@
  *
  * \return         0 if successful, or 1 if the test failed
  */
-int mbedtls_camellia_self_test( int verbose );
+int mbedtls_camellia_self_test(int verbose);
 
 #endif /* MBEDTLS_SELF_TEST */
 
diff --git a/third_party/mbedtls/repo/include/mbedtls/ccm.h b/third_party/mbedtls/repo/include/mbedtls/ccm.h
index ece5a90..adb14cc 100644
--- a/third_party/mbedtls/repo/include/mbedtls/ccm.h
+++ b/third_party/mbedtls/repo/include/mbedtls/ccm.h
@@ -29,19 +29,7 @@
  */
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 
 #ifndef MBEDTLS_CCM_H
@@ -76,8 +64,7 @@
  * \brief    The CCM context-type definition. The CCM context is passed
  *           to the APIs called.
  */
-typedef struct mbedtls_ccm_context
-{
+typedef struct mbedtls_ccm_context {
     mbedtls_cipher_context_t cipher_ctx;    /*!< The cipher context used. */
 }
 mbedtls_ccm_context;
@@ -93,7 +80,7 @@
  *
  * \param ctx       The CCM context to initialize. This must not be \c NULL.
  */
-void mbedtls_ccm_init( mbedtls_ccm_context *ctx );
+void mbedtls_ccm_init(mbedtls_ccm_context *ctx);
 
 /**
  * \brief           This function initializes the CCM context set in the
@@ -108,10 +95,10 @@
  * \return          \c 0 on success.
  * \return          A CCM or cipher-specific error code on failure.
  */
-int mbedtls_ccm_setkey( mbedtls_ccm_context *ctx,
-                        mbedtls_cipher_id_t cipher,
-                        const unsigned char *key,
-                        unsigned int keybits );
+int mbedtls_ccm_setkey(mbedtls_ccm_context *ctx,
+                       mbedtls_cipher_id_t cipher,
+                       const unsigned char *key,
+                       unsigned int keybits);
 
 /**
  * \brief   This function releases and clears the specified CCM context
@@ -120,7 +107,7 @@
  * \param ctx       The CCM context to clear. If this is \c NULL, the function
  *                  has no effect. Otherwise, this must be initialized.
  */
-void mbedtls_ccm_free( mbedtls_ccm_context *ctx );
+void mbedtls_ccm_free(mbedtls_ccm_context *ctx);
 
 /**
  * \brief           This function encrypts a buffer using CCM.
@@ -158,11 +145,11 @@
  * \return          \c 0 on success.
  * \return          A CCM or cipher-specific error code on failure.
  */
-int mbedtls_ccm_encrypt_and_tag( mbedtls_ccm_context *ctx, size_t length,
-                         const unsigned char *iv, size_t iv_len,
-                         const unsigned char *add, size_t add_len,
-                         const unsigned char *input, unsigned char *output,
-                         unsigned char *tag, size_t tag_len );
+int mbedtls_ccm_encrypt_and_tag(mbedtls_ccm_context *ctx, size_t length,
+                                const unsigned char *iv, size_t iv_len,
+                                const unsigned char *add, size_t add_len,
+                                const unsigned char *input, unsigned char *output,
+                                unsigned char *tag, size_t tag_len);
 
 /**
  * \brief           This function encrypts a buffer using CCM*.
@@ -206,11 +193,11 @@
  * \return          \c 0 on success.
  * \return          A CCM or cipher-specific error code on failure.
  */
-int mbedtls_ccm_star_encrypt_and_tag( mbedtls_ccm_context *ctx, size_t length,
-                         const unsigned char *iv, size_t iv_len,
-                         const unsigned char *add, size_t add_len,
-                         const unsigned char *input, unsigned char *output,
-                         unsigned char *tag, size_t tag_len );
+int mbedtls_ccm_star_encrypt_and_tag(mbedtls_ccm_context *ctx, size_t length,
+                                     const unsigned char *iv, size_t iv_len,
+                                     const unsigned char *add, size_t add_len,
+                                     const unsigned char *input, unsigned char *output,
+                                     unsigned char *tag, size_t tag_len);
 
 /**
  * \brief           This function performs a CCM authenticated decryption of a
@@ -243,11 +230,11 @@
  * \return          #MBEDTLS_ERR_CCM_AUTH_FAILED if the tag does not match.
  * \return          A cipher-specific error code on calculation failure.
  */
-int mbedtls_ccm_auth_decrypt( mbedtls_ccm_context *ctx, size_t length,
-                      const unsigned char *iv, size_t iv_len,
-                      const unsigned char *add, size_t add_len,
-                      const unsigned char *input, unsigned char *output,
-                      const unsigned char *tag, size_t tag_len );
+int mbedtls_ccm_auth_decrypt(mbedtls_ccm_context *ctx, size_t length,
+                             const unsigned char *iv, size_t iv_len,
+                             const unsigned char *add, size_t add_len,
+                             const unsigned char *input, unsigned char *output,
+                             const unsigned char *tag, size_t tag_len);
 
 /**
  * \brief           This function performs a CCM* authenticated decryption of a
@@ -288,11 +275,11 @@
  * \return          #MBEDTLS_ERR_CCM_AUTH_FAILED if the tag does not match.
  * \return          A cipher-specific error code on calculation failure.
  */
-int mbedtls_ccm_star_auth_decrypt( mbedtls_ccm_context *ctx, size_t length,
-                      const unsigned char *iv, size_t iv_len,
-                      const unsigned char *add, size_t add_len,
-                      const unsigned char *input, unsigned char *output,
-                      const unsigned char *tag, size_t tag_len );
+int mbedtls_ccm_star_auth_decrypt(mbedtls_ccm_context *ctx, size_t length,
+                                  const unsigned char *iv, size_t iv_len,
+                                  const unsigned char *add, size_t add_len,
+                                  const unsigned char *input, unsigned char *output,
+                                  const unsigned char *tag, size_t tag_len);
 
 #if defined(MBEDTLS_SELF_TEST) && defined(MBEDTLS_AES_C)
 /**
@@ -301,7 +288,7 @@
  * \return         \c 0 on success.
  * \return         \c 1 on failure.
  */
-int mbedtls_ccm_self_test( int verbose );
+int mbedtls_ccm_self_test(int verbose);
 #endif /* MBEDTLS_SELF_TEST && MBEDTLS_AES_C */
 
 #ifdef __cplusplus
diff --git a/third_party/mbedtls/repo/include/mbedtls/certs.h b/third_party/mbedtls/repo/include/mbedtls/certs.h
index c93c741..8a1f293 100644
--- a/third_party/mbedtls/repo/include/mbedtls/certs.h
+++ b/third_party/mbedtls/repo/include/mbedtls/certs.h
@@ -5,19 +5,7 @@
  */
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 #ifndef MBEDTLS_CERTS_H
 #define MBEDTLS_CERTS_H
@@ -37,11 +25,11 @@
 /* List of all PEM-encoded CA certificates, terminated by NULL;
  * PEM encoded if MBEDTLS_PEM_PARSE_C is enabled, DER encoded
  * otherwise. */
-extern const char * mbedtls_test_cas[];
+extern const char *mbedtls_test_cas[];
 extern const size_t mbedtls_test_cas_len[];
 
 /* List of all DER-encoded CA certificates, terminated by NULL */
-extern const unsigned char * mbedtls_test_cas_der[];
+extern const unsigned char *mbedtls_test_cas_der[];
 extern const size_t mbedtls_test_cas_der_len[];
 
 #if defined(MBEDTLS_PEM_PARSE_C)
@@ -112,9 +100,9 @@
 /* Config-dependent dispatch between EC and RSA
  * (RSA if enabled, otherwise EC) */
 
-extern const char * mbedtls_test_ca_crt;
-extern const char * mbedtls_test_ca_key;
-extern const char * mbedtls_test_ca_pwd;
+extern const char *mbedtls_test_ca_crt;
+extern const char *mbedtls_test_ca_key;
+extern const char *mbedtls_test_ca_pwd;
 extern const size_t mbedtls_test_ca_crt_len;
 extern const size_t mbedtls_test_ca_key_len;
 extern const size_t mbedtls_test_ca_pwd_len;
@@ -181,9 +169,9 @@
 /* Config-dependent dispatch between EC and RSA
  * (RSA if enabled, otherwise EC) */
 
-extern const char * mbedtls_test_srv_crt;
-extern const char * mbedtls_test_srv_key;
-extern const char * mbedtls_test_srv_pwd;
+extern const char *mbedtls_test_srv_crt;
+extern const char *mbedtls_test_srv_key;
+extern const char *mbedtls_test_srv_pwd;
 extern const size_t mbedtls_test_srv_crt_len;
 extern const size_t mbedtls_test_srv_key_len;
 extern const size_t mbedtls_test_srv_pwd_len;
@@ -236,9 +224,9 @@
 /* Config-dependent dispatch between EC and RSA
  * (RSA if enabled, otherwise EC) */
 
-extern const char * mbedtls_test_cli_crt;
-extern const char * mbedtls_test_cli_key;
-extern const char * mbedtls_test_cli_pwd;
+extern const char *mbedtls_test_cli_crt;
+extern const char *mbedtls_test_cli_key;
+extern const char *mbedtls_test_cli_pwd;
 extern const size_t mbedtls_test_cli_crt_len;
 extern const size_t mbedtls_test_cli_key_len;
 extern const size_t mbedtls_test_cli_pwd_len;
diff --git a/third_party/mbedtls/repo/include/mbedtls/chacha20.h b/third_party/mbedtls/repo/include/mbedtls/chacha20.h
index 03b4871..0c0d6a1 100644
--- a/third_party/mbedtls/repo/include/mbedtls/chacha20.h
+++ b/third_party/mbedtls/repo/include/mbedtls/chacha20.h
@@ -14,19 +14,7 @@
 
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 
 #ifndef MBEDTLS_CHACHA20_H
@@ -60,8 +48,7 @@
 
 #if !defined(MBEDTLS_CHACHA20_ALT)
 
-typedef struct mbedtls_chacha20_context
-{
+typedef struct mbedtls_chacha20_context {
     uint32_t state[16];          /*! The state (before round operations). */
     uint8_t  keystream8[64];     /*! Leftover keystream bytes. */
     size_t keystream_bytes_used; /*! Number of keystream bytes already used. */
@@ -87,7 +74,7 @@
  * \param ctx       The ChaCha20 context to initialize.
  *                  This must not be \c NULL.
  */
-void mbedtls_chacha20_init( mbedtls_chacha20_context *ctx );
+void mbedtls_chacha20_init(mbedtls_chacha20_context *ctx);
 
 /**
  * \brief           This function releases and clears the specified
@@ -98,7 +85,7 @@
  *                  \c NULL, it must point to an initialized context.
  *
  */
-void mbedtls_chacha20_free( mbedtls_chacha20_context *ctx );
+void mbedtls_chacha20_free(mbedtls_chacha20_context *ctx);
 
 /**
  * \brief           This function sets the encryption/decryption key.
@@ -116,8 +103,8 @@
  * \return          \c 0 on success.
  * \return          #MBEDTLS_ERR_CHACHA20_BAD_INPUT_DATA if ctx or key is NULL.
  */
-int mbedtls_chacha20_setkey( mbedtls_chacha20_context *ctx,
-                             const unsigned char key[32] );
+int mbedtls_chacha20_setkey(mbedtls_chacha20_context *ctx,
+                            const unsigned char key[32]);
 
 /**
  * \brief           This function sets the nonce and initial counter value.
@@ -138,9 +125,9 @@
  * \return          #MBEDTLS_ERR_CHACHA20_BAD_INPUT_DATA if ctx or nonce is
  *                  NULL.
  */
-int mbedtls_chacha20_starts( mbedtls_chacha20_context* ctx,
-                             const unsigned char nonce[12],
-                             uint32_t counter );
+int mbedtls_chacha20_starts(mbedtls_chacha20_context *ctx,
+                            const unsigned char nonce[12],
+                            uint32_t counter);
 
 /**
  * \brief           This function encrypts or decrypts data.
@@ -171,10 +158,10 @@
  * \return          \c 0 on success.
  * \return          A negative error code on failure.
  */
-int mbedtls_chacha20_update( mbedtls_chacha20_context *ctx,
-                             size_t size,
-                             const unsigned char *input,
-                             unsigned char *output );
+int mbedtls_chacha20_update(mbedtls_chacha20_context *ctx,
+                            size_t size,
+                            const unsigned char *input,
+                            unsigned char *output);
 
 /**
  * \brief           This function encrypts or decrypts data with ChaCha20 and
@@ -204,12 +191,12 @@
  * \return          \c 0 on success.
  * \return          A negative error code on failure.
  */
-int mbedtls_chacha20_crypt( const unsigned char key[32],
-                            const unsigned char nonce[12],
-                            uint32_t counter,
-                            size_t size,
-                            const unsigned char* input,
-                            unsigned char* output );
+int mbedtls_chacha20_crypt(const unsigned char key[32],
+                           const unsigned char nonce[12],
+                           uint32_t counter,
+                           size_t size,
+                           const unsigned char *input,
+                           unsigned char *output);
 
 #if defined(MBEDTLS_SELF_TEST)
 /**
@@ -218,7 +205,7 @@
  * \return          \c 0 on success.
  * \return          \c 1 on failure.
  */
-int mbedtls_chacha20_self_test( int verbose );
+int mbedtls_chacha20_self_test(int verbose);
 #endif /* MBEDTLS_SELF_TEST */
 
 #ifdef __cplusplus
diff --git a/third_party/mbedtls/repo/include/mbedtls/chachapoly.h b/third_party/mbedtls/repo/include/mbedtls/chachapoly.h
index c4ec7b5..1156d7d 100644
--- a/third_party/mbedtls/repo/include/mbedtls/chachapoly.h
+++ b/third_party/mbedtls/repo/include/mbedtls/chachapoly.h
@@ -14,19 +14,7 @@
 
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 
 #ifndef MBEDTLS_CHACHAPOLY_H
@@ -50,8 +38,7 @@
 extern "C" {
 #endif
 
-typedef enum
-{
+typedef enum {
     MBEDTLS_CHACHAPOLY_ENCRYPT,     /**< The mode value for performing encryption. */
     MBEDTLS_CHACHAPOLY_DECRYPT      /**< The mode value for performing decryption. */
 }
@@ -61,8 +48,7 @@
 
 #include "mbedtls/chacha20.h"
 
-typedef struct mbedtls_chachapoly_context
-{
+typedef struct mbedtls_chachapoly_context {
     mbedtls_chacha20_context chacha20_ctx;  /**< The ChaCha20 context. */
     mbedtls_poly1305_context poly1305_ctx;  /**< The Poly1305 context. */
     uint64_t aad_len;                       /**< The length (bytes) of the Additional Authenticated Data. */
@@ -118,7 +104,7 @@
  *
  * \param ctx       The ChachaPoly context to initialize. Must not be \c NULL.
  */
-void mbedtls_chachapoly_init( mbedtls_chachapoly_context *ctx );
+void mbedtls_chachapoly_init(mbedtls_chachapoly_context *ctx);
 
 /**
  * \brief           This function releases and clears the specified
@@ -127,7 +113,7 @@
  * \param ctx       The ChachaPoly context to clear. This may be \c NULL, in which
  *                  case this function is a no-op.
  */
-void mbedtls_chachapoly_free( mbedtls_chachapoly_context *ctx );
+void mbedtls_chachapoly_free(mbedtls_chachapoly_context *ctx);
 
 /**
  * \brief           This function sets the ChaCha20-Poly1305
@@ -140,8 +126,8 @@
  * \return          \c 0 on success.
  * \return          A negative error code on failure.
  */
-int mbedtls_chachapoly_setkey( mbedtls_chachapoly_context *ctx,
-                               const unsigned char key[32] );
+int mbedtls_chachapoly_setkey(mbedtls_chachapoly_context *ctx,
+                              const unsigned char key[32]);
 
 /**
  * \brief           This function starts a ChaCha20-Poly1305 encryption or
@@ -161,16 +147,16 @@
  * \param ctx       The ChaCha20-Poly1305 context. This must be initialized
  *                  and bound to a key.
  * \param nonce     The nonce/IV to use for the message.
- *                  This must be a redable buffer of length \c 12 Bytes.
+ *                  This must be a readable buffer of length \c 12 Bytes.
  * \param mode      The operation to perform: #MBEDTLS_CHACHAPOLY_ENCRYPT or
  *                  #MBEDTLS_CHACHAPOLY_DECRYPT (discouraged, see warning).
  *
  * \return          \c 0 on success.
  * \return          A negative error code on failure.
  */
-int mbedtls_chachapoly_starts( mbedtls_chachapoly_context *ctx,
-                               const unsigned char nonce[12],
-                               mbedtls_chachapoly_mode_t mode );
+int mbedtls_chachapoly_starts(mbedtls_chachapoly_context *ctx,
+                              const unsigned char nonce[12],
+                              mbedtls_chachapoly_mode_t mode);
 
 /**
  * \brief           This function feeds additional data to be authenticated
@@ -211,9 +197,9 @@
  *                  if the operations has not been started or has been
  *                  finished, or if the AAD has been finished.
  */
-int mbedtls_chachapoly_update_aad( mbedtls_chachapoly_context *ctx,
-                                   const unsigned char *aad,
-                                   size_t aad_len );
+int mbedtls_chachapoly_update_aad(mbedtls_chachapoly_context *ctx,
+                                  const unsigned char *aad,
+                                  size_t aad_len);
 
 /**
  * \brief           Thus function feeds data to be encrypted or decrypted
@@ -246,10 +232,10 @@
  *                  finished.
  * \return          Another negative error code on other kinds of failure.
  */
-int mbedtls_chachapoly_update( mbedtls_chachapoly_context *ctx,
-                               size_t len,
-                               const unsigned char *input,
-                               unsigned char *output );
+int mbedtls_chachapoly_update(mbedtls_chachapoly_context *ctx,
+                              size_t len,
+                              const unsigned char *input,
+                              unsigned char *output);
 
 /**
  * \brief           This function finished the ChaCha20-Poly1305 operation and
@@ -267,8 +253,8 @@
  *                  finished.
  * \return          Another negative error code on other kinds of failure.
  */
-int mbedtls_chachapoly_finish( mbedtls_chachapoly_context *ctx,
-                               unsigned char mac[16] );
+int mbedtls_chachapoly_finish(mbedtls_chachapoly_context *ctx,
+                              unsigned char mac[16]);
 
 /**
  * \brief           This function performs a complete ChaCha20-Poly1305
@@ -299,14 +285,14 @@
  * \return          \c 0 on success.
  * \return          A negative error code on failure.
  */
-int mbedtls_chachapoly_encrypt_and_tag( mbedtls_chachapoly_context *ctx,
-                                        size_t length,
-                                        const unsigned char nonce[12],
-                                        const unsigned char *aad,
-                                        size_t aad_len,
-                                        const unsigned char *input,
-                                        unsigned char *output,
-                                        unsigned char tag[16] );
+int mbedtls_chachapoly_encrypt_and_tag(mbedtls_chachapoly_context *ctx,
+                                       size_t length,
+                                       const unsigned char nonce[12],
+                                       const unsigned char *aad,
+                                       size_t aad_len,
+                                       const unsigned char *input,
+                                       unsigned char *output,
+                                       unsigned char tag[16]);
 
 /**
  * \brief           This function performs a complete ChaCha20-Poly1305
@@ -333,14 +319,14 @@
  *                  if the data was not authentic.
  * \return          Another negative error code on other kinds of failure.
  */
-int mbedtls_chachapoly_auth_decrypt( mbedtls_chachapoly_context *ctx,
-                                     size_t length,
-                                     const unsigned char nonce[12],
-                                     const unsigned char *aad,
-                                     size_t aad_len,
-                                     const unsigned char tag[16],
-                                     const unsigned char *input,
-                                     unsigned char *output );
+int mbedtls_chachapoly_auth_decrypt(mbedtls_chachapoly_context *ctx,
+                                    size_t length,
+                                    const unsigned char nonce[12],
+                                    const unsigned char *aad,
+                                    size_t aad_len,
+                                    const unsigned char tag[16],
+                                    const unsigned char *input,
+                                    unsigned char *output);
 
 #if defined(MBEDTLS_SELF_TEST)
 /**
@@ -349,7 +335,7 @@
  * \return          \c 0 on success.
  * \return          \c 1 on failure.
  */
-int mbedtls_chachapoly_self_test( int verbose );
+int mbedtls_chachapoly_self_test(int verbose);
 #endif /* MBEDTLS_SELF_TEST */
 
 #ifdef __cplusplus
diff --git a/third_party/mbedtls/repo/include/mbedtls/check_config.h b/third_party/mbedtls/repo/include/mbedtls/check_config.h
index 396fe7d..96081fe 100644
--- a/third_party/mbedtls/repo/include/mbedtls/check_config.h
+++ b/third_party/mbedtls/repo/include/mbedtls/check_config.h
@@ -5,19 +5,7 @@
  */
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 
 /*
@@ -28,13 +16,14 @@
 #ifndef MBEDTLS_CHECK_CONFIG_H
 #define MBEDTLS_CHECK_CONFIG_H
 
+/* *INDENT-OFF* */
 /*
  * We assume CHAR_BIT is 8 in many places. In practice, this is true on our
  * target platforms, so not an issue, but let's just be extra sure.
  */
 #include <limits.h>
 #if CHAR_BIT != 8
-#error "mbed TLS requires a platform with 8-bit chars"
+#error "Mbed TLS requires a platform with 8-bit chars"
 #endif
 
 #if defined(_WIN32)
@@ -68,10 +57,6 @@
 #error "MBEDTLS_HAVE_TIME_DATE without MBEDTLS_HAVE_TIME does not make sense"
 #endif
 
-#if defined(MBEDTLS_AESNI_C) && !defined(MBEDTLS_HAVE_ASM)
-#error "MBEDTLS_AESNI_C defined, but not all prerequisites"
-#endif
-
 #if defined(MBEDTLS_CTR_DRBG_C) && !defined(MBEDTLS_AES_C)
 #error "MBEDTLS_CTR_DRBG_C defined, but not all prerequisites"
 #endif
@@ -143,6 +128,11 @@
 #error "MBEDTLS_ECDH_VARIANT_EVEREST_ENABLED defined, but MBEDTLS_ECDH_LEGACY_CONTEXT not disabled"
 #endif
 
+#if defined(MBEDTLS_ECP_RESTARTABLE)           && \
+    !defined(MBEDTLS_ECP_C)
+#error "MBEDTLS_ECP_RESTARTABLE defined, but not all prerequisites"
+#endif
+
 #if defined(MBEDTLS_ECDSA_DETERMINISTIC) && !defined(MBEDTLS_HMAC_DRBG_C)
 #error "MBEDTLS_ECDSA_DETERMINISTIC defined, but not all prerequisites"
 #endif
@@ -173,7 +163,11 @@
 #endif
 
 #if defined(MBEDTLS_PK_PARSE_C) && !defined(MBEDTLS_ASN1_PARSE_C)
-#error "MBEDTLS_PK_PARSE_C defined, but not all prerequesites"
+#error "MBEDTLS_PK_PARSE_C defined, but not all prerequisites"
+#endif
+
+#if defined(MBEDTLS_PKCS5_C) && !defined(MBEDTLS_MD_C)
+#error "MBEDTLS_PKCS5_C defined, but not all prerequisites"
 #endif
 
 #if defined(MBEDTLS_ENTROPY_C) && (!defined(MBEDTLS_SHA512_C) &&      \
@@ -214,11 +208,32 @@
 #error "MBEDTLS_TEST_NULL_ENTROPY defined, but entropy sources too"
 #endif
 
+#if defined(MBEDTLS_CCM_C) && (                                        \
+    !defined(MBEDTLS_AES_C) && !defined(MBEDTLS_CAMELLIA_C) && !defined(MBEDTLS_ARIA_C) )
+#error "MBEDTLS_CCM_C defined, but not all prerequisites"
+#endif
+
+#if defined(MBEDTLS_CCM_C) && !defined(MBEDTLS_CIPHER_C)
+#error "MBEDTLS_CCM_C defined, but not all prerequisites"
+#endif
+
 #if defined(MBEDTLS_GCM_C) && (                                        \
-        !defined(MBEDTLS_AES_C) && !defined(MBEDTLS_CAMELLIA_C) && !defined(MBEDTLS_ARIA_C) )
+    !defined(MBEDTLS_AES_C) && !defined(MBEDTLS_CAMELLIA_C) && !defined(MBEDTLS_ARIA_C) )
 #error "MBEDTLS_GCM_C defined, but not all prerequisites"
 #endif
 
+#if defined(MBEDTLS_GCM_C) && !defined(MBEDTLS_CIPHER_C)
+#error "MBEDTLS_GCM_C defined, but not all prerequisites"
+#endif
+
+#if defined(MBEDTLS_CHACHAPOLY_C) && !defined(MBEDTLS_CHACHA20_C)
+#error "MBEDTLS_CHACHAPOLY_C defined, but not all prerequisites"
+#endif
+
+#if defined(MBEDTLS_CHACHAPOLY_C) && !defined(MBEDTLS_POLY1305_C)
+#error "MBEDTLS_CHACHAPOLY_C defined, but not all prerequisites"
+#endif
+
 #if defined(MBEDTLS_ECP_RANDOMIZE_JAC_ALT) && !defined(MBEDTLS_ECP_INTERNAL_ALT)
 #error "MBEDTLS_ECP_RANDOMIZE_JAC_ALT defined, but not all prerequisites"
 #endif
@@ -338,11 +353,11 @@
 #endif
 
 #if defined(MBEDTLS_MEMORY_BACKTRACE) && !defined(MBEDTLS_MEMORY_BUFFER_ALLOC_C)
-#error "MBEDTLS_MEMORY_BACKTRACE defined, but not all prerequesites"
+#error "MBEDTLS_MEMORY_BACKTRACE defined, but not all prerequisites"
 #endif
 
 #if defined(MBEDTLS_MEMORY_DEBUG) && !defined(MBEDTLS_MEMORY_BUFFER_ALLOC_C)
-#error "MBEDTLS_MEMORY_DEBUG defined, but not all prerequesites"
+#error "MBEDTLS_MEMORY_DEBUG defined, but not all prerequisites"
 #endif
 
 #if defined(MBEDTLS_PADLOCK_C) && !defined(MBEDTLS_HAVE_ASM)
@@ -500,6 +515,20 @@
 #error "MBEDTLS_PLATFORM_SNPRINTF_MACRO and MBEDTLS_PLATFORM_STD_SNPRINTF/MBEDTLS_PLATFORM_SNPRINTF_ALT cannot be defined simultaneously"
 #endif
 
+#if defined(MBEDTLS_PLATFORM_VSNPRINTF_ALT) && !defined(MBEDTLS_PLATFORM_C)
+#error "MBEDTLS_PLATFORM_VSNPRINTF_ALT defined, but not all prerequisites"
+#endif
+
+#if defined(MBEDTLS_PLATFORM_VSNPRINTF_MACRO) && !defined(MBEDTLS_PLATFORM_C)
+#error "MBEDTLS_PLATFORM_VSNPRINTF_MACRO defined, but not all prerequisites"
+#endif
+
+#if defined(MBEDTLS_PLATFORM_VSNPRINTF_MACRO) &&\
+    ( defined(MBEDTLS_PLATFORM_STD_VSNPRINTF) ||\
+        defined(MBEDTLS_PLATFORM_VSNPRINTF_ALT) )
+#error "MBEDTLS_PLATFORM_VSNPRINTF_MACRO and MBEDTLS_PLATFORM_STD_VSNPRINTF/MBEDTLS_PLATFORM_VSNPRINTF_ALT cannot be defined simultaneously"
+#endif
+
 #if defined(MBEDTLS_PLATFORM_STD_MEM_HDR) &&\
     !defined(MBEDTLS_PLATFORM_NO_STD_FUNCTIONS)
 #error "MBEDTLS_PLATFORM_STD_MEM_HDR defined, but not all prerequisites"
@@ -619,6 +648,17 @@
 #error "MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER defined, but it cannot coexist with MBEDTLS_USE_PSA_CRYPTO."
 #endif
 
+#if defined(MBEDTLS_PK_C) && defined(MBEDTLS_USE_PSA_CRYPTO) && \
+    !defined(MBEDTLS_PK_WRITE_C) && defined(MBEDTLS_ECDSA_C)
+#error "MBEDTLS_PK_C in configuration with MBEDTLS_USE_PSA_CRYPTO and \
+        MBEDTLS_ECDSA_C requires MBEDTLS_PK_WRITE_C to be defined."
+#endif
+
+#if defined(MBEDTLS_PSA_CRYPTO_C) && defined(MBEDTLS_RSA_C) && \
+    !( defined(MBEDTLS_PK_PARSE_C) && defined(MBEDTLS_PK_WRITE_C) )
+#error "MBEDTLS_PSA_CRYPTO_C with MBEDTLS_RSA_C requires MBEDTLS_PK_PARSE_C and MBEDTLS_PK_WRITE_C"
+#endif
+
 #if defined(MBEDTLS_RSA_C) && ( !defined(MBEDTLS_BIGNUM_C) ||         \
     !defined(MBEDTLS_OID_C) )
 #error "MBEDTLS_RSA_C defined, but not all prerequisites"
@@ -761,20 +801,25 @@
     !defined(MBEDTLS_SSL_PROTO_TLS1)   &&      \
     !defined(MBEDTLS_SSL_PROTO_TLS1_1) &&      \
     !defined(MBEDTLS_SSL_PROTO_TLS1_2)
-#error "MBEDTLS_SSL_ENCRYPT_THEN_MAC defined, but not all prerequsites"
+#error "MBEDTLS_SSL_ENCRYPT_THEN_MAC defined, but not all prerequisites"
 #endif
 
 #if defined(MBEDTLS_SSL_EXTENDED_MASTER_SECRET) && \
     !defined(MBEDTLS_SSL_PROTO_TLS1)   &&          \
     !defined(MBEDTLS_SSL_PROTO_TLS1_1) &&          \
     !defined(MBEDTLS_SSL_PROTO_TLS1_2)
-#error "MBEDTLS_SSL_EXTENDED_MASTER_SECRET defined, but not all prerequsites"
+#error "MBEDTLS_SSL_EXTENDED_MASTER_SECRET defined, but not all prerequisites"
 #endif
 
 #if defined(MBEDTLS_SSL_TICKET_C) && !defined(MBEDTLS_CIPHER_C)
 #error "MBEDTLS_SSL_TICKET_C defined, but not all prerequisites"
 #endif
 
+#if defined(MBEDTLS_SSL_TICKET_C) && \
+    !( defined(MBEDTLS_GCM_C) || defined(MBEDTLS_CCM_C) || defined(MBEDTLS_CHACHAPOLY_C) )
+#error "MBEDTLS_SSL_TICKET_C defined, but not all prerequisites"
+#endif
+
 #if defined(MBEDTLS_SSL_CBC_RECORD_SPLITTING) && \
     !defined(MBEDTLS_SSL_PROTO_SSL3) && !defined(MBEDTLS_SSL_PROTO_TLS1)
 #error "MBEDTLS_SSL_CBC_RECORD_SPLITTING defined, but not all prerequisites"
@@ -889,6 +934,10 @@
 #error "MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH defined, but not all prerequisites"
 #endif
 
+#if defined(MBEDTLS_SSL_CONTEXT_SERIALIZATION) && !( defined(MBEDTLS_GCM_C) || defined(MBEDTLS_CCM_C) || defined(MBEDTLS_CHACHAPOLY_C) )
+#error "MBEDTLS_SSL_CONTEXT_SERIALIZATION defined, but not all prerequisites"
+#endif
+
 /*
  * Avoid warning from -pedantic. This is a convenient place for this
  * workaround since this is included by every single file before the
@@ -896,4 +945,5 @@
  */
 typedef int mbedtls_iso_c_forbids_empty_translation_units;
 
+/* *INDENT-ON* */
 #endif /* MBEDTLS_CHECK_CONFIG_H */
diff --git a/third_party/mbedtls/repo/include/mbedtls/cipher.h b/third_party/mbedtls/repo/include/mbedtls/cipher.h
index 6d83da8..db73c1b 100644
--- a/third_party/mbedtls/repo/include/mbedtls/cipher.h
+++ b/third_party/mbedtls/repo/include/mbedtls/cipher.h
@@ -9,19 +9,7 @@
  */
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 
 #ifndef MBEDTLS_CIPHER_H
@@ -49,7 +37,7 @@
 #define MBEDTLS_CIPHER_MODE_STREAM
 #endif
 
-#if ( defined(__ARMCC_VERSION) || defined(_MSC_VER) ) && \
+#if (defined(__ARMCC_VERSION) || defined(_MSC_VER)) && \
     !defined(inline) && !defined(__cplusplus)
 #define inline __inline
 #endif
@@ -83,16 +71,16 @@
 /**
  * \brief     Supported cipher types.
  *
- * \warning   RC4 and DES are considered weak ciphers and their use
- *            constitutes a security risk. Arm recommends considering stronger
+ * \warning   RC4 and DES/3DES are considered weak ciphers and their use
+ *            constitutes a security risk. We recommend considering stronger
  *            ciphers instead.
  */
 typedef enum {
     MBEDTLS_CIPHER_ID_NONE = 0,  /**< Placeholder to mark the end of cipher ID lists. */
     MBEDTLS_CIPHER_ID_NULL,      /**< The identity cipher, treated as a stream cipher. */
     MBEDTLS_CIPHER_ID_AES,       /**< The AES cipher. */
-    MBEDTLS_CIPHER_ID_DES,       /**< The DES cipher. */
-    MBEDTLS_CIPHER_ID_3DES,      /**< The Triple DES cipher. */
+    MBEDTLS_CIPHER_ID_DES,       /**< The DES cipher. \warning DES is considered weak. */
+    MBEDTLS_CIPHER_ID_3DES,      /**< The Triple DES cipher. \warning 3DES is considered weak. */
     MBEDTLS_CIPHER_ID_CAMELLIA,  /**< The Camellia cipher. */
     MBEDTLS_CIPHER_ID_BLOWFISH,  /**< The Blowfish cipher. */
     MBEDTLS_CIPHER_ID_ARC4,      /**< The RC4 cipher. */
@@ -103,8 +91,8 @@
 /**
  * \brief     Supported {cipher type, cipher mode} pairs.
  *
- * \warning   RC4 and DES are considered weak ciphers and their use
- *            constitutes a security risk. Arm recommends considering stronger
+ * \warning   RC4 and DES/3DES are considered weak ciphers and their use
+ *            constitutes a security risk. We recommend considering stronger
  *            ciphers instead.
  */
 typedef enum {
@@ -140,12 +128,12 @@
     MBEDTLS_CIPHER_CAMELLIA_128_GCM,     /**< Camellia cipher with 128-bit GCM mode. */
     MBEDTLS_CIPHER_CAMELLIA_192_GCM,     /**< Camellia cipher with 192-bit GCM mode. */
     MBEDTLS_CIPHER_CAMELLIA_256_GCM,     /**< Camellia cipher with 256-bit GCM mode. */
-    MBEDTLS_CIPHER_DES_ECB,              /**< DES cipher with ECB mode. */
-    MBEDTLS_CIPHER_DES_CBC,              /**< DES cipher with CBC mode. */
-    MBEDTLS_CIPHER_DES_EDE_ECB,          /**< DES cipher with EDE ECB mode. */
-    MBEDTLS_CIPHER_DES_EDE_CBC,          /**< DES cipher with EDE CBC mode. */
-    MBEDTLS_CIPHER_DES_EDE3_ECB,         /**< DES cipher with EDE3 ECB mode. */
-    MBEDTLS_CIPHER_DES_EDE3_CBC,         /**< DES cipher with EDE3 CBC mode. */
+    MBEDTLS_CIPHER_DES_ECB,              /**< DES cipher with ECB mode. \warning DES is considered weak. */
+    MBEDTLS_CIPHER_DES_CBC,              /**< DES cipher with CBC mode. \warning DES is considered weak. */
+    MBEDTLS_CIPHER_DES_EDE_ECB,          /**< DES cipher with EDE ECB mode. \warning 3DES is considered weak. */
+    MBEDTLS_CIPHER_DES_EDE_CBC,          /**< DES cipher with EDE CBC mode. \warning 3DES is considered weak. */
+    MBEDTLS_CIPHER_DES_EDE3_ECB,         /**< DES cipher with EDE3 ECB mode. \warning 3DES is considered weak. */
+    MBEDTLS_CIPHER_DES_EDE3_CBC,         /**< DES cipher with EDE3 CBC mode. \warning 3DES is considered weak. */
     MBEDTLS_CIPHER_BLOWFISH_ECB,         /**< Blowfish cipher with ECB mode. */
     MBEDTLS_CIPHER_BLOWFISH_CBC,         /**< Blowfish cipher with CBC mode. */
     MBEDTLS_CIPHER_BLOWFISH_CFB64,       /**< Blowfish cipher with CFB64 mode. */
@@ -226,11 +214,11 @@
 enum {
     /** Undefined key length. */
     MBEDTLS_KEY_LENGTH_NONE = 0,
-    /** Key length, in bits (including parity), for DES keys. */
+    /** Key length, in bits (including parity), for DES keys. \warning DES is considered weak. */
     MBEDTLS_KEY_LENGTH_DES  = 64,
-    /** Key length in bits, including parity, for DES in two-key EDE. */
+    /** Key length in bits, including parity, for DES in two-key EDE. \warning 3DES is considered weak. */
     MBEDTLS_KEY_LENGTH_DES_EDE = 128,
-    /** Key length in bits, including parity, for DES in three-key EDE. */
+    /** Key length in bits, including parity, for DES in three-key EDE. \warning 3DES is considered weak. */
     MBEDTLS_KEY_LENGTH_DES_EDE3 = 192,
 };
 
@@ -273,8 +261,7 @@
  * Cipher information. Allows calling cipher functions
  * in a generic way.
  */
-typedef struct mbedtls_cipher_info_t
-{
+typedef struct mbedtls_cipher_info_t {
     /** Full cipher identifier. For example,
      * MBEDTLS_CIPHER_AES_256_CBC.
      */
@@ -290,7 +277,7 @@
     unsigned int key_bitlen;
 
     /** Name of the cipher. */
-    const char * name;
+    const char *name;
 
     /** IV or nonce size, in Bytes.
      * For ciphers that accept variable IV sizes,
@@ -315,8 +302,7 @@
 /**
  * Generic cipher context.
  */
-typedef struct mbedtls_cipher_context_t
-{
+typedef struct mbedtls_cipher_context_t {
     /** Information about the associated cipher. */
     const mbedtls_cipher_info_t *cipher_info;
 
@@ -332,8 +318,8 @@
     /** Padding functions to use, if relevant for
      * the specific cipher mode.
      */
-    void (*add_padding)( unsigned char *output, size_t olen, size_t data_len );
-    int (*get_padding)( unsigned char *input, size_t ilen, size_t *data_len );
+    void (*add_padding)(unsigned char *output, size_t olen, size_t data_len);
+    int (*get_padding)(unsigned char *input, size_t ilen, size_t *data_len);
 #endif
 
     /** Buffer for input that has not been processed yet. */
@@ -383,7 +369,7 @@
  * \return      A statically-allocated array of cipher identifiers
  *              of type cipher_type_t. The last entry is zero.
  */
-const int *mbedtls_cipher_list( void );
+const int *mbedtls_cipher_list(void);
 
 /**
  * \brief               This function retrieves the cipher-information
@@ -396,7 +382,7 @@
  *                      given \p cipher_name.
  * \return              \c NULL if the associated cipher information is not found.
  */
-const mbedtls_cipher_info_t *mbedtls_cipher_info_from_string( const char *cipher_name );
+const mbedtls_cipher_info_t *mbedtls_cipher_info_from_string(const char *cipher_name);
 
 /**
  * \brief               This function retrieves the cipher-information
@@ -408,7 +394,7 @@
  *                      given \p cipher_type.
  * \return              \c NULL if the associated cipher information is not found.
  */
-const mbedtls_cipher_info_t *mbedtls_cipher_info_from_type( const mbedtls_cipher_type_t cipher_type );
+const mbedtls_cipher_info_t *mbedtls_cipher_info_from_type(const mbedtls_cipher_type_t cipher_type);
 
 /**
  * \brief               This function retrieves the cipher-information
@@ -424,16 +410,16 @@
  *                      given \p cipher_id.
  * \return              \c NULL if the associated cipher information is not found.
  */
-const mbedtls_cipher_info_t *mbedtls_cipher_info_from_values( const mbedtls_cipher_id_t cipher_id,
-                                              int key_bitlen,
-                                              const mbedtls_cipher_mode_t mode );
+const mbedtls_cipher_info_t *mbedtls_cipher_info_from_values(const mbedtls_cipher_id_t cipher_id,
+                                                             int key_bitlen,
+                                                             const mbedtls_cipher_mode_t mode);
 
 /**
- * \brief               This function initializes a \p cipher_context as NONE.
+ * \brief               This function initializes a \p ctx as NONE.
  *
  * \param ctx           The context to be initialized. This must not be \c NULL.
  */
-void mbedtls_cipher_init( mbedtls_cipher_context_t *ctx );
+void mbedtls_cipher_init(mbedtls_cipher_context_t *ctx);
 
 /**
  * \brief               This function frees and clears the cipher-specific
@@ -444,14 +430,33 @@
  *                      function has no effect, otherwise this must point to an
  *                      initialized context.
  */
-void mbedtls_cipher_free( mbedtls_cipher_context_t *ctx );
+void mbedtls_cipher_free(mbedtls_cipher_context_t *ctx);
 
 
 /**
- * \brief               This function initializes a cipher context for
+ * \brief               This function prepares a cipher context for
  *                      use with the given cipher primitive.
  *
- * \param ctx           The context to initialize. This must be initialized.
+ * \warning             In CBC mode, if mbedtls_cipher_set_padding_mode() is not called:
+ *                      - If MBEDTLS_CIPHER_PADDING_PKCS7 is enabled, the
+ *                      context will use PKCS7 padding.
+ *                      - Otherwise the context uses no padding and the input
+ *                      must be a whole number of blocks.
+ *
+ * \note                After calling this function, you should call
+ *                      mbedtls_cipher_setkey() and, if the mode uses padding,
+ *                      mbedtls_cipher_set_padding_mode(), then for each
+ *                      message to encrypt or decrypt with this key, either:
+ *                      - mbedtls_cipher_crypt() for one-shot processing with
+ *                      non-AEAD modes;
+ *                      - mbedtls_cipher_auth_encrypt_ext() or
+ *                      mbedtls_cipher_auth_decrypt_ext() for one-shot
+ *                      processing with AEAD modes or NIST_KW;
+ *                      - for multi-part processing, see the documentation of
+ *                      mbedtls_cipher_reset().
+ *
+ * \param ctx           The context to prepare. This must be initialized by
+ *                      a call to mbedtls_cipher_init() first.
  * \param cipher_info   The cipher to use.
  *
  * \return              \c 0 on success.
@@ -464,8 +469,8 @@
  * In future versions, the caller will be required to call
  * mbedtls_cipher_init() on the structure first.
  */
-int mbedtls_cipher_setup( mbedtls_cipher_context_t *ctx,
-                          const mbedtls_cipher_info_t *cipher_info );
+int mbedtls_cipher_setup(mbedtls_cipher_context_t *ctx,
+                         const mbedtls_cipher_info_t *cipher_info);
 
 #if defined(MBEDTLS_USE_PSA_CRYPTO)
 /**
@@ -489,9 +494,9 @@
  * \return              #MBEDTLS_ERR_CIPHER_ALLOC_FAILED if allocation of the
  *                      cipher-specific context fails.
  */
-int mbedtls_cipher_setup_psa( mbedtls_cipher_context_t *ctx,
-                              const mbedtls_cipher_info_t *cipher_info,
-                              size_t taglen );
+int mbedtls_cipher_setup_psa(mbedtls_cipher_context_t *ctx,
+                             const mbedtls_cipher_info_t *cipher_info,
+                             size_t taglen);
 #endif /* MBEDTLS_USE_PSA_CRYPTO */
 
 /**
@@ -503,11 +508,12 @@
  * \return       \c 0 if \p ctx has not been initialized.
  */
 static inline unsigned int mbedtls_cipher_get_block_size(
-    const mbedtls_cipher_context_t *ctx )
+    const mbedtls_cipher_context_t *ctx)
 {
-    MBEDTLS_INTERNAL_VALIDATE_RET( ctx != NULL, 0 );
-    if( ctx->cipher_info == NULL )
+    MBEDTLS_INTERNAL_VALIDATE_RET(ctx != NULL, 0);
+    if (ctx->cipher_info == NULL) {
         return 0;
+    }
 
     return ctx->cipher_info->block_size;
 }
@@ -522,11 +528,12 @@
  * \return       #MBEDTLS_MODE_NONE if \p ctx has not been initialized.
  */
 static inline mbedtls_cipher_mode_t mbedtls_cipher_get_cipher_mode(
-    const mbedtls_cipher_context_t *ctx )
+    const mbedtls_cipher_context_t *ctx)
 {
-    MBEDTLS_INTERNAL_VALIDATE_RET( ctx != NULL, MBEDTLS_MODE_NONE );
-    if( ctx->cipher_info == NULL )
+    MBEDTLS_INTERNAL_VALIDATE_RET(ctx != NULL, MBEDTLS_MODE_NONE);
+    if (ctx->cipher_info == NULL) {
         return MBEDTLS_MODE_NONE;
+    }
 
     return ctx->cipher_info->mode;
 }
@@ -542,14 +549,16 @@
  * \return      The actual size if an IV has been set.
  */
 static inline int mbedtls_cipher_get_iv_size(
-    const mbedtls_cipher_context_t *ctx )
+    const mbedtls_cipher_context_t *ctx)
 {
-    MBEDTLS_INTERNAL_VALIDATE_RET( ctx != NULL, 0 );
-    if( ctx->cipher_info == NULL )
+    MBEDTLS_INTERNAL_VALIDATE_RET(ctx != NULL, 0);
+    if (ctx->cipher_info == NULL) {
         return 0;
+    }
 
-    if( ctx->iv_size != 0 )
+    if (ctx->iv_size != 0) {
         return (int) ctx->iv_size;
+    }
 
     return (int) ctx->cipher_info->iv_size;
 }
@@ -563,12 +572,13 @@
  * \return              #MBEDTLS_CIPHER_NONE if \p ctx has not been initialized.
  */
 static inline mbedtls_cipher_type_t mbedtls_cipher_get_type(
-    const mbedtls_cipher_context_t *ctx )
+    const mbedtls_cipher_context_t *ctx)
 {
     MBEDTLS_INTERNAL_VALIDATE_RET(
-        ctx != NULL, MBEDTLS_CIPHER_NONE );
-    if( ctx->cipher_info == NULL )
+        ctx != NULL, MBEDTLS_CIPHER_NONE);
+    if (ctx->cipher_info == NULL) {
         return MBEDTLS_CIPHER_NONE;
+    }
 
     return ctx->cipher_info->type;
 }
@@ -583,11 +593,12 @@
  * \return              NULL if \p ctx has not been not initialized.
  */
 static inline const char *mbedtls_cipher_get_name(
-    const mbedtls_cipher_context_t *ctx )
+    const mbedtls_cipher_context_t *ctx)
 {
-    MBEDTLS_INTERNAL_VALIDATE_RET( ctx != NULL, 0 );
-    if( ctx->cipher_info == NULL )
+    MBEDTLS_INTERNAL_VALIDATE_RET(ctx != NULL, 0);
+    if (ctx->cipher_info == NULL) {
         return 0;
+    }
 
     return ctx->cipher_info->name;
 }
@@ -598,16 +609,17 @@
  * \param ctx           The context of the cipher. This must be initialized.
  *
  * \return              The key length of the cipher in bits.
- * \return              #MBEDTLS_KEY_LENGTH_NONE if ctx \p has not been
+ * \return              #MBEDTLS_KEY_LENGTH_NONE if \p ctx has not been
  *                      initialized.
  */
 static inline int mbedtls_cipher_get_key_bitlen(
-    const mbedtls_cipher_context_t *ctx )
+    const mbedtls_cipher_context_t *ctx)
 {
     MBEDTLS_INTERNAL_VALIDATE_RET(
-        ctx != NULL, MBEDTLS_KEY_LENGTH_NONE );
-    if( ctx->cipher_info == NULL )
+        ctx != NULL, MBEDTLS_KEY_LENGTH_NONE);
+    if (ctx->cipher_info == NULL) {
         return MBEDTLS_KEY_LENGTH_NONE;
+    }
 
     return (int) ctx->cipher_info->key_bitlen;
 }
@@ -621,12 +633,13 @@
  * \return         #MBEDTLS_OPERATION_NONE if \p ctx has not been initialized.
  */
 static inline mbedtls_operation_t mbedtls_cipher_get_operation(
-    const mbedtls_cipher_context_t *ctx )
+    const mbedtls_cipher_context_t *ctx)
 {
     MBEDTLS_INTERNAL_VALIDATE_RET(
-        ctx != NULL, MBEDTLS_OPERATION_NONE );
-    if( ctx->cipher_info == NULL )
+        ctx != NULL, MBEDTLS_OPERATION_NONE);
+    if (ctx->cipher_info == NULL) {
         return MBEDTLS_OPERATION_NONE;
+    }
 
     return ctx->operation;
 }
@@ -647,18 +660,16 @@
  *                      parameter-verification failure.
  * \return              A cipher-specific error code on failure.
  */
-int mbedtls_cipher_setkey( mbedtls_cipher_context_t *ctx,
-                           const unsigned char *key,
-                           int key_bitlen,
-                           const mbedtls_operation_t operation );
+int mbedtls_cipher_setkey(mbedtls_cipher_context_t *ctx,
+                          const unsigned char *key,
+                          int key_bitlen,
+                          const mbedtls_operation_t operation);
 
 #if defined(MBEDTLS_CIPHER_MODE_WITH_PADDING)
 /**
  * \brief               This function sets the padding mode, for cipher modes
  *                      that use padding.
  *
- *                      The default passing mode is PKCS7 padding.
- *
  * \param ctx           The generic cipher context. This must be initialized and
  *                      bound to a cipher information structure.
  * \param mode          The padding mode.
@@ -669,8 +680,8 @@
  * \return              #MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA if the cipher mode
  *                      does not support padding.
  */
-int mbedtls_cipher_set_padding_mode( mbedtls_cipher_context_t *ctx,
-                                     mbedtls_cipher_padding_t mode );
+int mbedtls_cipher_set_padding_mode(mbedtls_cipher_context_t *ctx,
+                                    mbedtls_cipher_padding_t mode);
 #endif /* MBEDTLS_CIPHER_MODE_WITH_PADDING */
 
 /**
@@ -691,20 +702,42 @@
  * \return          #MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA on
  *                  parameter-verification failure.
  */
-int mbedtls_cipher_set_iv( mbedtls_cipher_context_t *ctx,
-                           const unsigned char *iv,
-                           size_t iv_len );
+int mbedtls_cipher_set_iv(mbedtls_cipher_context_t *ctx,
+                          const unsigned char *iv,
+                          size_t iv_len);
 
 /**
  * \brief         This function resets the cipher state.
  *
- * \param ctx     The generic cipher context. This must be initialized.
+ * \note          With non-AEAD ciphers, the order of calls for each message
+ *                is as follows:
+ *                1. mbedtls_cipher_set_iv() if the mode uses an IV/nonce.
+ *                2. mbedtls_cipher_reset()
+ *                3. mbedtls_cipher_update() one or more times
+ *                4. mbedtls_cipher_finish()
+ *                .
+ *                This sequence can be repeated to encrypt or decrypt multiple
+ *                messages with the same key.
+ *
+ * \note          With AEAD ciphers, the order of calls for each message
+ *                is as follows:
+ *                1. mbedtls_cipher_set_iv() if the mode uses an IV/nonce.
+ *                2. mbedtls_cipher_reset()
+ *                3. mbedtls_cipher_update_ad()
+ *                4. mbedtls_cipher_update() one or more times
+ *                5. mbedtls_cipher_check_tag() (for decryption) or
+ *                mbedtls_cipher_write_tag() (for encryption).
+ *                .
+ *                This sequence can be repeated to encrypt or decrypt multiple
+ *                messages with the same key.
+ *
+ * \param ctx     The generic cipher context. This must be bound to a key.
  *
  * \return        \c 0 on success.
  * \return        #MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA on
  *                parameter-verification failure.
  */
-int mbedtls_cipher_reset( mbedtls_cipher_context_t *ctx );
+int mbedtls_cipher_reset(mbedtls_cipher_context_t *ctx);
 
 #if defined(MBEDTLS_GCM_C) || defined(MBEDTLS_CHACHAPOLY_C)
 /**
@@ -721,8 +754,8 @@
  * \return              \c 0 on success.
  * \return              A specific error code on failure.
  */
-int mbedtls_cipher_update_ad( mbedtls_cipher_context_t *ctx,
-                      const unsigned char *ad, size_t ad_len );
+int mbedtls_cipher_update_ad(mbedtls_cipher_context_t *ctx,
+                             const unsigned char *ad, size_t ad_len);
 #endif /* MBEDTLS_GCM_C || MBEDTLS_CHACHAPOLY_C */
 
 /**
@@ -759,10 +792,10 @@
  *                      unsupported mode for a cipher.
  * \return              A cipher-specific error code on failure.
  */
-int mbedtls_cipher_update( mbedtls_cipher_context_t *ctx,
-                           const unsigned char *input,
-                           size_t ilen, unsigned char *output,
-                           size_t *olen );
+int mbedtls_cipher_update(mbedtls_cipher_context_t *ctx,
+                          const unsigned char *input,
+                          size_t ilen, unsigned char *output,
+                          size_t *olen);
 
 /**
  * \brief               The generic cipher finalization function. If data still
@@ -773,7 +806,7 @@
  * \param ctx           The generic cipher context. This must be initialized and
  *                      bound to a key.
  * \param output        The buffer to write data to. This needs to be a writable
- *                      buffer of at least \p block_size Bytes.
+ *                      buffer of at least block_size Bytes.
  * \param olen          The length of the data written to the \p output buffer.
  *                      This may not be \c NULL.
  *
@@ -786,8 +819,8 @@
  *                      while decrypting.
  * \return              A cipher-specific error code on failure.
  */
-int mbedtls_cipher_finish( mbedtls_cipher_context_t *ctx,
-                   unsigned char *output, size_t *olen );
+int mbedtls_cipher_finish(mbedtls_cipher_context_t *ctx,
+                          unsigned char *output, size_t *olen);
 
 #if defined(MBEDTLS_GCM_C) || defined(MBEDTLS_CHACHAPOLY_C)
 /**
@@ -806,8 +839,8 @@
  * \return              \c 0 on success.
  * \return              A specific error code on failure.
  */
-int mbedtls_cipher_write_tag( mbedtls_cipher_context_t *ctx,
-                      unsigned char *tag, size_t tag_len );
+int mbedtls_cipher_write_tag(mbedtls_cipher_context_t *ctx,
+                             unsigned char *tag, size_t tag_len);
 
 /**
  * \brief               This function checks the tag for AEAD ciphers.
@@ -822,8 +855,8 @@
  * \return              \c 0 on success.
  * \return              A specific error code on failure.
  */
-int mbedtls_cipher_check_tag( mbedtls_cipher_context_t *ctx,
-                      const unsigned char *tag, size_t tag_len );
+int mbedtls_cipher_check_tag(mbedtls_cipher_context_t *ctx,
+                             const unsigned char *tag, size_t tag_len);
 #endif /* MBEDTLS_GCM_C || MBEDTLS_CHACHAPOLY_C */
 
 /**
@@ -859,13 +892,13 @@
  *                      while decrypting.
  * \return              A cipher-specific error code on failure.
  */
-int mbedtls_cipher_crypt( mbedtls_cipher_context_t *ctx,
-                  const unsigned char *iv, size_t iv_len,
-                  const unsigned char *input, size_t ilen,
-                  unsigned char *output, size_t *olen );
+int mbedtls_cipher_crypt(mbedtls_cipher_context_t *ctx,
+                         const unsigned char *iv, size_t iv_len,
+                         const unsigned char *input, size_t ilen,
+                         unsigned char *output, size_t *olen);
 
 #if defined(MBEDTLS_CIPHER_MODE_AEAD)
-#if ! defined(MBEDTLS_DEPRECATED_REMOVED)
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
 #if defined(MBEDTLS_DEPRECATED_WARNING)
 #define MBEDTLS_DEPRECATED    __attribute__((deprecated))
 #else
@@ -917,13 +950,13 @@
  *                      parameter-verification failure.
  * \return              A cipher-specific error code on failure.
  */
-int mbedtls_cipher_auth_encrypt( mbedtls_cipher_context_t *ctx,
-                         const unsigned char *iv, size_t iv_len,
-                         const unsigned char *ad, size_t ad_len,
-                         const unsigned char *input, size_t ilen,
-                         unsigned char *output, size_t *olen,
-                         unsigned char *tag, size_t tag_len )
-                         MBEDTLS_DEPRECATED;
+int MBEDTLS_DEPRECATED mbedtls_cipher_auth_encrypt(
+    mbedtls_cipher_context_t *ctx,
+    const unsigned char *iv, size_t iv_len,
+    const unsigned char *ad, size_t ad_len,
+    const unsigned char *input, size_t ilen,
+    unsigned char *output, size_t *olen,
+    unsigned char *tag, size_t tag_len);
 
 /**
  * \brief               The generic authenticated decryption (AEAD) function.
@@ -976,13 +1009,13 @@
  * \return              #MBEDTLS_ERR_CIPHER_AUTH_FAILED if data is not authentic.
  * \return              A cipher-specific error code on failure.
  */
-int mbedtls_cipher_auth_decrypt( mbedtls_cipher_context_t *ctx,
-                         const unsigned char *iv, size_t iv_len,
-                         const unsigned char *ad, size_t ad_len,
-                         const unsigned char *input, size_t ilen,
-                         unsigned char *output, size_t *olen,
-                         const unsigned char *tag, size_t tag_len )
-                         MBEDTLS_DEPRECATED;
+int MBEDTLS_DEPRECATED mbedtls_cipher_auth_decrypt(
+    mbedtls_cipher_context_t *ctx,
+    const unsigned char *iv, size_t iv_len,
+    const unsigned char *ad, size_t ad_len,
+    const unsigned char *input, size_t ilen,
+    unsigned char *output, size_t *olen,
+    const unsigned char *tag, size_t tag_len);
 #undef MBEDTLS_DEPRECATED
 #endif /* MBEDTLS_DEPRECATED_REMOVED */
 #endif /* MBEDTLS_CIPHER_MODE_AEAD */
@@ -1032,12 +1065,12 @@
  *                      parameter-verification failure.
  * \return              A cipher-specific error code on failure.
  */
-int mbedtls_cipher_auth_encrypt_ext( mbedtls_cipher_context_t *ctx,
-                         const unsigned char *iv, size_t iv_len,
-                         const unsigned char *ad, size_t ad_len,
-                         const unsigned char *input, size_t ilen,
-                         unsigned char *output, size_t output_len,
-                         size_t *olen, size_t tag_len );
+int mbedtls_cipher_auth_encrypt_ext(mbedtls_cipher_context_t *ctx,
+                                    const unsigned char *iv, size_t iv_len,
+                                    const unsigned char *ad, size_t ad_len,
+                                    const unsigned char *input, size_t ilen,
+                                    unsigned char *output, size_t output_len,
+                                    size_t *olen, size_t tag_len);
 
 /**
  * \brief               The authenticated encryption (AEAD/NIST_KW) function.
@@ -1088,12 +1121,12 @@
  * \return              #MBEDTLS_ERR_CIPHER_AUTH_FAILED if data is not authentic.
  * \return              A cipher-specific error code on failure.
  */
-int mbedtls_cipher_auth_decrypt_ext( mbedtls_cipher_context_t *ctx,
-                         const unsigned char *iv, size_t iv_len,
-                         const unsigned char *ad, size_t ad_len,
-                         const unsigned char *input, size_t ilen,
-                         unsigned char *output, size_t output_len,
-                         size_t *olen, size_t tag_len );
+int mbedtls_cipher_auth_decrypt_ext(mbedtls_cipher_context_t *ctx,
+                                    const unsigned char *iv, size_t iv_len,
+                                    const unsigned char *ad, size_t ad_len,
+                                    const unsigned char *input, size_t ilen,
+                                    unsigned char *output, size_t output_len,
+                                    size_t *olen, size_t tag_len);
 #endif /* MBEDTLS_CIPHER_MODE_AEAD || MBEDTLS_NIST_KW_C */
 #ifdef __cplusplus
 }
diff --git a/third_party/mbedtls/repo/include/mbedtls/cipher_internal.h b/third_party/mbedtls/repo/include/mbedtls/cipher_internal.h
index 2484c01..c98abab 100644
--- a/third_party/mbedtls/repo/include/mbedtls/cipher_internal.h
+++ b/third_party/mbedtls/repo/include/mbedtls/cipher_internal.h
@@ -7,19 +7,7 @@
  */
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 #ifndef MBEDTLS_CIPHER_WRAP_H
 #define MBEDTLS_CIPHER_WRAP_H
@@ -43,82 +31,79 @@
 /**
  * Base cipher information. The non-mode specific functions and values.
  */
-struct mbedtls_cipher_base_t
-{
+struct mbedtls_cipher_base_t {
     /** Base Cipher type (e.g. MBEDTLS_CIPHER_ID_AES) */
     mbedtls_cipher_id_t cipher;
 
     /** Encrypt using ECB */
-    int (*ecb_func)( void *ctx, mbedtls_operation_t mode,
-                     const unsigned char *input, unsigned char *output );
+    int (*ecb_func)(void *ctx, mbedtls_operation_t mode,
+                    const unsigned char *input, unsigned char *output);
 
 #if defined(MBEDTLS_CIPHER_MODE_CBC)
     /** Encrypt using CBC */
-    int (*cbc_func)( void *ctx, mbedtls_operation_t mode, size_t length,
-                     unsigned char *iv, const unsigned char *input,
-                     unsigned char *output );
+    int (*cbc_func)(void *ctx, mbedtls_operation_t mode, size_t length,
+                    unsigned char *iv, const unsigned char *input,
+                    unsigned char *output);
 #endif
 
 #if defined(MBEDTLS_CIPHER_MODE_CFB)
     /** Encrypt using CFB (Full length) */
-    int (*cfb_func)( void *ctx, mbedtls_operation_t mode, size_t length, size_t *iv_off,
-                     unsigned char *iv, const unsigned char *input,
-                     unsigned char *output );
+    int (*cfb_func)(void *ctx, mbedtls_operation_t mode, size_t length, size_t *iv_off,
+                    unsigned char *iv, const unsigned char *input,
+                    unsigned char *output);
 #endif
 
 #if defined(MBEDTLS_CIPHER_MODE_OFB)
     /** Encrypt using OFB (Full length) */
-    int (*ofb_func)( void *ctx, size_t length, size_t *iv_off,
-                     unsigned char *iv,
-                     const unsigned char *input,
-                     unsigned char *output );
+    int (*ofb_func)(void *ctx, size_t length, size_t *iv_off,
+                    unsigned char *iv,
+                    const unsigned char *input,
+                    unsigned char *output);
 #endif
 
 #if defined(MBEDTLS_CIPHER_MODE_CTR)
     /** Encrypt using CTR */
-    int (*ctr_func)( void *ctx, size_t length, size_t *nc_off,
-                     unsigned char *nonce_counter, unsigned char *stream_block,
-                     const unsigned char *input, unsigned char *output );
+    int (*ctr_func)(void *ctx, size_t length, size_t *nc_off,
+                    unsigned char *nonce_counter, unsigned char *stream_block,
+                    const unsigned char *input, unsigned char *output);
 #endif
 
 #if defined(MBEDTLS_CIPHER_MODE_XTS)
     /** Encrypt or decrypt using XTS. */
-    int (*xts_func)( void *ctx, mbedtls_operation_t mode, size_t length,
-                     const unsigned char data_unit[16],
-                     const unsigned char *input, unsigned char *output );
+    int (*xts_func)(void *ctx, mbedtls_operation_t mode, size_t length,
+                    const unsigned char data_unit[16],
+                    const unsigned char *input, unsigned char *output);
 #endif
 
 #if defined(MBEDTLS_CIPHER_MODE_STREAM)
     /** Encrypt using STREAM */
-    int (*stream_func)( void *ctx, size_t length,
-                        const unsigned char *input, unsigned char *output );
+    int (*stream_func)(void *ctx, size_t length,
+                       const unsigned char *input, unsigned char *output);
 #endif
 
     /** Set key for encryption purposes */
-    int (*setkey_enc_func)( void *ctx, const unsigned char *key,
-                            unsigned int key_bitlen );
+    int (*setkey_enc_func)(void *ctx, const unsigned char *key,
+                           unsigned int key_bitlen);
 
     /** Set key for decryption purposes */
-    int (*setkey_dec_func)( void *ctx, const unsigned char *key,
-                            unsigned int key_bitlen);
+    int (*setkey_dec_func)(void *ctx, const unsigned char *key,
+                           unsigned int key_bitlen);
 
     /** Allocate a new context */
-    void * (*ctx_alloc_func)( void );
+    void * (*ctx_alloc_func)(void);
 
     /** Free the given context */
-    void (*ctx_free_func)( void *ctx );
+    void (*ctx_free_func)(void *ctx);
 
 };
 
-typedef struct
-{
+typedef struct {
     mbedtls_cipher_type_t type;
     const mbedtls_cipher_info_t *info;
 } mbedtls_cipher_definition_t;
 
 #if defined(MBEDTLS_USE_PSA_CRYPTO)
-typedef enum
-{
+typedef enum {
     MBEDTLS_CIPHER_PSA_KEY_UNSET = 0,
     MBEDTLS_CIPHER_PSA_KEY_OWNED, /* Used for PSA-based cipher contexts which */
                                   /* use raw key material internally imported */
@@ -131,8 +116,7 @@
                                       /* destroyed when the context is freed. */
 } mbedtls_cipher_psa_key_ownership;
 
-typedef struct
-{
+typedef struct {
     psa_algorithm_t alg;
     psa_key_id_t slot;
     mbedtls_cipher_psa_key_ownership slot_state;
diff --git a/third_party/mbedtls/repo/include/mbedtls/cmac.h b/third_party/mbedtls/repo/include/mbedtls/cmac.h
index 8934886..89634dc 100644
--- a/third_party/mbedtls/repo/include/mbedtls/cmac.h
+++ b/third_party/mbedtls/repo/include/mbedtls/cmac.h
@@ -8,19 +8,7 @@
  */
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 
 #ifndef MBEDTLS_CMAC_H
@@ -45,7 +33,11 @@
 #define MBEDTLS_AES_BLOCK_SIZE          16
 #define MBEDTLS_DES3_BLOCK_SIZE         8
 
-#if defined(MBEDTLS_AES_C)
+
+/* Although the CMAC module does not support ARIA or CAMELLIA, we adjust the value of
+ * MBEDTLS_CIPHER_BLKSIZE_MAX to reflect these ciphers.
+ * This is done to avoid confusion, given the general-purpose name of the macro. */
+#if defined(MBEDTLS_AES_C) || defined(MBEDTLS_ARIA_C) || defined(MBEDTLS_CAMELLIA_C)
 #define MBEDTLS_CIPHER_BLKSIZE_MAX      16  /**< The longest block used by CMAC is that of AES. */
 #else
 #define MBEDTLS_CIPHER_BLKSIZE_MAX      8   /**< The longest block used by CMAC is that of 3DES. */
@@ -56,8 +48,7 @@
 /**
  * The CMAC context structure.
  */
-struct mbedtls_cmac_context_t
-{
+struct mbedtls_cmac_context_t {
     /** The internal state of the CMAC algorithm.  */
     unsigned char       state[MBEDTLS_CIPHER_BLKSIZE_MAX];
 
@@ -103,8 +94,8 @@
  * \return              \c 0 on success.
  * \return              A cipher-specific error code on failure.
  */
-int mbedtls_cipher_cmac_starts( mbedtls_cipher_context_t *ctx,
-                                const unsigned char *key, size_t keybits );
+int mbedtls_cipher_cmac_starts(mbedtls_cipher_context_t *ctx,
+                               const unsigned char *key, size_t keybits);
 
 /**
  * \brief               This function feeds an input buffer into an ongoing CMAC
@@ -128,8 +119,8 @@
  * \return             #MBEDTLS_ERR_MD_BAD_INPUT_DATA
  *                     if parameter verification fails.
  */
-int mbedtls_cipher_cmac_update( mbedtls_cipher_context_t *ctx,
-                                const unsigned char *input, size_t ilen );
+int mbedtls_cipher_cmac_update(mbedtls_cipher_context_t *ctx,
+                               const unsigned char *input, size_t ilen);
 
 /**
  * \brief               This function finishes an ongoing CMAC operation, and
@@ -147,8 +138,8 @@
  * \return              #MBEDTLS_ERR_MD_BAD_INPUT_DATA
  *                      if parameter verification fails.
  */
-int mbedtls_cipher_cmac_finish( mbedtls_cipher_context_t *ctx,
-                                unsigned char *output );
+int mbedtls_cipher_cmac_finish(mbedtls_cipher_context_t *ctx,
+                               unsigned char *output);
 
 /**
  * \brief               This function starts a new CMAC operation with the same
@@ -166,7 +157,7 @@
  * \return              #MBEDTLS_ERR_MD_BAD_INPUT_DATA
  *                      if parameter verification fails.
  */
-int mbedtls_cipher_cmac_reset( mbedtls_cipher_context_t *ctx );
+int mbedtls_cipher_cmac_reset(mbedtls_cipher_context_t *ctx);
 
 /**
  * \brief               This function calculates the full generic CMAC
@@ -195,10 +186,10 @@
  * \return              #MBEDTLS_ERR_MD_BAD_INPUT_DATA
  *                      if parameter verification fails.
  */
-int mbedtls_cipher_cmac( const mbedtls_cipher_info_t *cipher_info,
-                         const unsigned char *key, size_t keylen,
-                         const unsigned char *input, size_t ilen,
-                         unsigned char *output );
+int mbedtls_cipher_cmac(const mbedtls_cipher_info_t *cipher_info,
+                        const unsigned char *key, size_t keylen,
+                        const unsigned char *input, size_t ilen,
+                        unsigned char *output);
 
 #if defined(MBEDTLS_AES_C)
 /**
@@ -218,12 +209,12 @@
  *
  * \return          \c 0 on success.
  */
-int mbedtls_aes_cmac_prf_128( const unsigned char *key, size_t key_len,
-                              const unsigned char *input, size_t in_len,
-                              unsigned char output[16] );
+int mbedtls_aes_cmac_prf_128(const unsigned char *key, size_t key_len,
+                             const unsigned char *input, size_t in_len,
+                             unsigned char output[16]);
 #endif /* MBEDTLS_AES_C */
 
-#if defined(MBEDTLS_SELF_TEST) && ( defined(MBEDTLS_AES_C) || defined(MBEDTLS_DES_C) )
+#if defined(MBEDTLS_SELF_TEST) && (defined(MBEDTLS_AES_C) || defined(MBEDTLS_DES_C))
 /**
  * \brief          The CMAC checkup routine.
  *
@@ -237,7 +228,7 @@
  * \return         \c 0 on success.
  * \return         \c 1 on failure.
  */
-int mbedtls_cmac_self_test( int verbose );
+int mbedtls_cmac_self_test(int verbose);
 #endif /* MBEDTLS_SELF_TEST && ( MBEDTLS_AES_C || MBEDTLS_DES_C ) */
 
 #ifdef __cplusplus
diff --git a/third_party/mbedtls/repo/include/mbedtls/compat-1.3.h b/third_party/mbedtls/repo/include/mbedtls/compat-1.3.h
index 4017751..de8f625 100644
--- a/third_party/mbedtls/repo/include/mbedtls/compat-1.3.h
+++ b/third_party/mbedtls/repo/include/mbedtls/compat-1.3.h
@@ -1,26 +1,14 @@
 /**
  * \file compat-1.3.h
  *
- * \brief Compatibility definitions for using mbed TLS with client code written
+ * \brief Compatibility definitions for using Mbed TLS with client code written
  *  for the PolarSSL naming conventions.
  *
  * \deprecated Use the new names directly instead
  */
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 
 #if !defined(MBEDTLS_CONFIG_FILE)
@@ -29,7 +17,7 @@
 #include MBEDTLS_CONFIG_FILE
 #endif
 
-#if ! defined(MBEDTLS_DEPRECATED_REMOVED)
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
 
 #if defined(MBEDTLS_DEPRECATED_WARNING)
 #warning "Including compat-1.3.h is deprecated"
@@ -597,7 +585,8 @@
 #define POLARSSL_X509_ALLOW_EXTENSIONS_NON_V3 MBEDTLS_X509_ALLOW_EXTENSIONS_NON_V3
 #endif
 #if defined MBEDTLS_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION
-#define POLARSSL_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION MBEDTLS_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION
+#define POLARSSL_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION \
+    MBEDTLS_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION
 #endif
 #if defined MBEDTLS_X509_CHECK_EXTENDED_KEY_USAGE
 #define POLARSSL_X509_CHECK_EXTENDED_KEY_USAGE MBEDTLS_X509_CHECK_EXTENDED_KEY_USAGE
@@ -1382,8 +1371,8 @@
 #define SSL_ANTI_REPLAY_ENABLED MBEDTLS_SSL_ANTI_REPLAY_ENABLED
 #define SSL_ARC4_DISABLED MBEDTLS_SSL_ARC4_DISABLED
 #define SSL_ARC4_ENABLED MBEDTLS_SSL_ARC4_ENABLED
-#define SSL_BUFFER_LEN ( ( ( MBEDTLS_SSL_IN_BUFFER_LEN ) < ( MBEDTLS_SSL_OUT_BUFFER_LEN ) ) \
-                         ? ( MBEDTLS_SSL_IN_BUFFER_LEN ) : ( MBEDTLS_SSL_OUT_BUFFER_LEN ) )
+#define SSL_BUFFER_LEN (((MBEDTLS_SSL_IN_BUFFER_LEN) < (MBEDTLS_SSL_OUT_BUFFER_LEN)) \
+                         ? (MBEDTLS_SSL_IN_BUFFER_LEN) : (MBEDTLS_SSL_OUT_BUFFER_LEN))
 #define SSL_CACHE_DEFAULT_MAX_ENTRIES MBEDTLS_SSL_CACHE_DEFAULT_MAX_ENTRIES
 #define SSL_CACHE_DEFAULT_TIMEOUT MBEDTLS_SSL_CACHE_DEFAULT_TIMEOUT
 #define SSL_CBC_RECORD_SPLITTING_DISABLED MBEDTLS_SSL_CBC_RECORD_SPLITTING_DISABLED
@@ -1554,10 +1543,14 @@
 #define TLS_ECDHE_ECDSA_WITH_AES_256_CCM MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM
 #define TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8 MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8
 #define TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
-#define TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256
-#define TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256
-#define TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384
-#define TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384
+#define TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 \
+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256
+#define TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 \
+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256
+#define TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 \
+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384
+#define TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 \
+    MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384
 #define TLS_ECDHE_ECDSA_WITH_NULL_SHA MBEDTLS_TLS_ECDHE_ECDSA_WITH_NULL_SHA
 #define TLS_ECDHE_ECDSA_WITH_RC4_128_SHA MBEDTLS_TLS_ECDHE_ECDSA_WITH_RC4_128_SHA
 #define TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA MBEDTLS_TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA
@@ -1565,8 +1558,10 @@
 #define TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256
 #define TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA
 #define TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384
-#define TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256
-#define TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384
+#define TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 \
+    MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256
+#define TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 \
+    MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384
 #define TLS_ECDHE_PSK_WITH_NULL_SHA MBEDTLS_TLS_ECDHE_PSK_WITH_NULL_SHA
 #define TLS_ECDHE_PSK_WITH_NULL_SHA256 MBEDTLS_TLS_ECDHE_PSK_WITH_NULL_SHA256
 #define TLS_ECDHE_PSK_WITH_NULL_SHA384 MBEDTLS_TLS_ECDHE_PSK_WITH_NULL_SHA384
@@ -1578,10 +1573,14 @@
 #define TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
 #define TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
 #define TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
-#define TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256
-#define TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256
-#define TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384
-#define TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384
+#define TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 \
+    MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256
+#define TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 \
+    MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256
+#define TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 \
+    MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384
+#define TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 \
+    MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384
 #define TLS_ECDHE_RSA_WITH_NULL_SHA MBEDTLS_TLS_ECDHE_RSA_WITH_NULL_SHA
 #define TLS_ECDHE_RSA_WITH_RC4_128_SHA MBEDTLS_TLS_ECDHE_RSA_WITH_RC4_128_SHA
 #define TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA MBEDTLS_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA
@@ -1591,10 +1590,14 @@
 #define TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA
 #define TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384
 #define TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384
-#define TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256
-#define TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256
-#define TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384
-#define TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384
+#define TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 \
+    MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256
+#define TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 \
+    MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256
+#define TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 \
+    MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384
+#define TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 \
+    MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384
 #define TLS_ECDH_ECDSA_WITH_NULL_SHA MBEDTLS_TLS_ECDH_ECDSA_WITH_NULL_SHA
 #define TLS_ECDH_ECDSA_WITH_RC4_128_SHA MBEDTLS_TLS_ECDH_ECDSA_WITH_RC4_128_SHA
 #define TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA MBEDTLS_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA
@@ -2492,7 +2495,8 @@
 #define x509write_crt_free mbedtls_x509write_crt_free
 #define x509write_crt_init mbedtls_x509write_crt_init
 #define x509write_crt_pem mbedtls_x509write_crt_pem
-#define x509write_crt_set_authority_key_identifier mbedtls_x509write_crt_set_authority_key_identifier
+#define x509write_crt_set_authority_key_identifier \
+    mbedtls_x509write_crt_set_authority_key_identifier
 #define x509write_crt_set_basic_constraints mbedtls_x509write_crt_set_basic_constraints
 #define x509write_crt_set_extension mbedtls_x509write_crt_set_extension
 #define x509write_crt_set_issuer_key mbedtls_x509write_crt_set_issuer_key
diff --git a/third_party/mbedtls/repo/include/mbedtls/config.h b/third_party/mbedtls/repo/include/mbedtls/config.h
index 87b4e91..ac2146e 100644
--- a/third_party/mbedtls/repo/include/mbedtls/config.h
+++ b/third_party/mbedtls/repo/include/mbedtls/config.h
@@ -9,19 +9,7 @@
  */
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 
 #ifndef MBEDTLS_CONFIG_H
@@ -51,7 +39,7 @@
  *      include/mbedtls/bn_mul.h
  *
  * Required by:
- *      MBEDTLS_AESNI_C
+ *      MBEDTLS_AESNI_C (on some platforms)
  *      MBEDTLS_PADLOCK_C
  *
  * Comment to disable the use of assembly code.
@@ -128,7 +116,12 @@
  * MBEDTLS_PLATFORM_TIME_MACRO, MBEDTLS_PLATFORM_TIME_TYPE_MACRO and
  * MBEDTLS_PLATFORM_STD_TIME.
  *
- * Comment if your system does not support time functions
+ * Comment if your system does not support time functions.
+ *
+ * \note If MBEDTLS_TIMING_C is set - to enable the semi-portable timing
+ *       interface - timing.c will include time.h on suitable platforms
+ *       regardless of the setting of MBEDTLS_HAVE_TIME, unless
+ *       MBEDTLS_TIMING_ALT is used. See timing.c for more information.
  */
 #define MBEDTLS_HAVE_TIME
 
@@ -158,19 +151,51 @@
  *
  * Enable the memory allocation layer.
  *
- * By default mbed TLS uses the system-provided calloc() and free().
+ * By default Mbed TLS uses the system-provided calloc() and free().
  * This allows different allocators (self-implemented or provided) to be
  * provided to the platform abstraction layer.
  *
- * Enabling MBEDTLS_PLATFORM_MEMORY without the
+ * Enabling #MBEDTLS_PLATFORM_MEMORY without the
  * MBEDTLS_PLATFORM_{FREE,CALLOC}_MACROs will provide
  * "mbedtls_platform_set_calloc_free()" allowing you to set an alternative calloc() and
  * free() function pointer at runtime.
  *
- * Enabling MBEDTLS_PLATFORM_MEMORY and specifying
+ * Enabling #MBEDTLS_PLATFORM_MEMORY and specifying
  * MBEDTLS_PLATFORM_{CALLOC,FREE}_MACROs will allow you to specify the
  * alternate function at compile time.
  *
+ * An overview of how the value of mbedtls_calloc is determined:
+ *
+ * - if !MBEDTLS_PLATFORM_MEMORY
+ *     - mbedtls_calloc = calloc
+ * - if MBEDTLS_PLATFORM_MEMORY
+ *     - if (MBEDTLS_PLATFORM_CALLOC_MACRO && MBEDTLS_PLATFORM_FREE_MACRO):
+ *         - mbedtls_calloc = MBEDTLS_PLATFORM_CALLOC_MACRO
+ *     - if !(MBEDTLS_PLATFORM_CALLOC_MACRO && MBEDTLS_PLATFORM_FREE_MACRO):
+ *         - Dynamic setup via mbedtls_platform_set_calloc_free is now possible with a default value MBEDTLS_PLATFORM_STD_CALLOC.
+ *         - How is MBEDTLS_PLATFORM_STD_CALLOC handled?
+ *         - if MBEDTLS_PLATFORM_NO_STD_FUNCTIONS:
+ *             - MBEDTLS_PLATFORM_STD_CALLOC is not set to anything;
+ *             - MBEDTLS_PLATFORM_STD_MEM_HDR can be included if present;
+ *         - if !MBEDTLS_PLATFORM_NO_STD_FUNCTIONS:
+ *             - if MBEDTLS_PLATFORM_STD_CALLOC is present:
+ *                 - User-defined MBEDTLS_PLATFORM_STD_CALLOC is respected;
+ *             - if !MBEDTLS_PLATFORM_STD_CALLOC:
+ *                 - MBEDTLS_PLATFORM_STD_CALLOC = calloc
+ *
+ *         - At this point the presence of MBEDTLS_PLATFORM_STD_CALLOC is checked.
+ *         - if !MBEDTLS_PLATFORM_STD_CALLOC
+ *             - MBEDTLS_PLATFORM_STD_CALLOC = uninitialized_calloc
+ *
+ *         - mbedtls_calloc = MBEDTLS_PLATFORM_STD_CALLOC.
+ *
+ * Defining MBEDTLS_PLATFORM_CALLOC_MACRO and #MBEDTLS_PLATFORM_STD_CALLOC at the same time is not possible.
+ * MBEDTLS_PLATFORM_CALLOC_MACRO and MBEDTLS_PLATFORM_FREE_MACRO must both be defined or undefined at the same time.
+ * #MBEDTLS_PLATFORM_STD_CALLOC and #MBEDTLS_PLATFORM_STD_FREE do not have to be defined at the same time, as, if they are used,
+ * dynamic setup of these functions is possible. See the tree above to see how are they handled in all cases.
+ * An uninitialized #MBEDTLS_PLATFORM_STD_CALLOC always fails, returning a null pointer.
+ * An uninitialized #MBEDTLS_PLATFORM_STD_FREE does not do anything.
+ *
  * Requires: MBEDTLS_PLATFORM_C
  *
  * Enable this layer to allow use of alternative memory allocators.
@@ -199,10 +224,10 @@
 /**
  * \def MBEDTLS_PLATFORM_EXIT_ALT
  *
- * MBEDTLS_PLATFORM_XXX_ALT: Uncomment a macro to let mbed TLS support the
+ * MBEDTLS_PLATFORM_XXX_ALT: Uncomment a macro to let Mbed TLS support the
  * function in the platform abstraction layer.
  *
- * Example: In case you uncomment MBEDTLS_PLATFORM_PRINTF_ALT, mbed TLS will
+ * Example: In case you uncomment MBEDTLS_PLATFORM_PRINTF_ALT, Mbed TLS will
  * provide a function "mbedtls_platform_set_printf()" that allows you to set an
  * alternative printf function pointer.
  *
@@ -229,6 +254,45 @@
 //#define MBEDTLS_PLATFORM_SETUP_TEARDOWN_ALT
 
 /**
+ * Uncomment the macro to let Mbed TLS use your alternate implementation of
+ * mbedtls_platform_gmtime_r(). This replaces the default implementation in
+ * platform_util.c.
+ *
+ * gmtime() is not a thread-safe function as defined in the C standard. The
+ * library will try to use safer implementations of this function, such as
+ * gmtime_r() when available. However, if Mbed TLS cannot identify the target
+ * system, the implementation of mbedtls_platform_gmtime_r() will default to
+ * using the standard gmtime(). In this case, calls from the library to
+ * gmtime() will be guarded by the global mutex mbedtls_threading_gmtime_mutex
+ * if MBEDTLS_THREADING_C is enabled. We recommend that calls from outside the
+ * library are also guarded with this mutex to avoid race conditions. However,
+ * if the macro MBEDTLS_PLATFORM_GMTIME_R_ALT is defined, Mbed TLS will
+ * unconditionally use the implementation for mbedtls_platform_gmtime_r()
+ * supplied at compile time.
+ */
+//#define MBEDTLS_PLATFORM_GMTIME_R_ALT
+
+/**
+ * Uncomment the macro to let Mbed TLS use your alternate implementation of
+ * mbedtls_platform_zeroize(). This replaces the default implementation in
+ * platform_util.c.
+ *
+ * mbedtls_platform_zeroize() is a widely used function across the library to
+ * zero a block of memory. The implementation is expected to be secure in the
+ * sense that it has been written to prevent the compiler from removing calls
+ * to mbedtls_platform_zeroize() as part of redundant code elimination
+ * optimizations. However, it is difficult to guarantee that calls to
+ * mbedtls_platform_zeroize() will not be optimized by the compiler as older
+ * versions of the C language standards do not provide a secure implementation
+ * of memset(). Therefore, MBEDTLS_PLATFORM_ZEROIZE_ALT enables users to
+ * configure their own implementation of mbedtls_platform_zeroize(), for
+ * example by using directives specific to their compiler, features from newer
+ * C standards (e.g using memset_s() in C11) or calling a secure memset() from
+ * their system (e.g explicit_bzero() in BSD).
+ */
+//#define MBEDTLS_PLATFORM_ZEROIZE_ALT
+
+/**
  * \def MBEDTLS_DEPRECATED_WARNING
  *
  * Mark deprecated functions and features so that they generate a warning if
@@ -321,10 +385,10 @@
  */
 //#define MBEDTLS_CHECK_PARAMS_ASSERT
 
-/* \} name SECTION: System support */
+/** \} name SECTION: System support */
 
 /**
- * \name SECTION: mbed TLS feature support
+ * \name SECTION: Mbed TLS feature support
  *
  * This section sets support for features that are or are not needed
  * within the modules that are enabled.
@@ -347,7 +411,7 @@
 /**
  * \def MBEDTLS_AES_ALT
  *
- * MBEDTLS__MODULE_NAME__ALT: Uncomment a macro to let mbed TLS use your
+ * MBEDTLS__MODULE_NAME__ALT: Uncomment a macro to let Mbed TLS use your
  * alternate core implementation of a symmetric crypto, an arithmetic or hash
  * module (e.g. platform specific assembly optimized implementations). Keep
  * in mind that the function prototypes should remain the same.
@@ -355,7 +419,7 @@
  * This replaces the whole module. If you only want to replace one of the
  * functions, use one of the MBEDTLS__FUNCTION_NAME__ALT flags.
  *
- * Example: In case you uncomment MBEDTLS_AES_ALT, mbed TLS will no longer
+ * Example: In case you uncomment MBEDTLS_AES_ALT, Mbed TLS will no longer
  * provide the "struct mbedtls_aes_context" definition and omit the base
  * function declarations and implementations. "aes_alt.h" will be included from
  * "aes.h" to include the new function definitions.
@@ -395,7 +459,7 @@
 //#define MBEDTLS_XTEA_ALT
 
 /*
- * When replacing the elliptic curve module, pleace consider, that it is
+ * When replacing the elliptic curve module, please consider, that it is
  * implemented with two .c files:
  *      - ecp.c
  *      - ecp_curves.c
@@ -408,14 +472,14 @@
 /**
  * \def MBEDTLS_MD2_PROCESS_ALT
  *
- * MBEDTLS__FUNCTION_NAME__ALT: Uncomment a macro to let mbed TLS use you
+ * MBEDTLS__FUNCTION_NAME__ALT: Uncomment a macro to let Mbed TLS use you
  * alternate core implementation of symmetric crypto or hash function. Keep in
  * mind that function prototypes should remain the same.
  *
- * This replaces only one function. The header file from mbed TLS is still
+ * This replaces only one function. The header file from Mbed TLS is still
  * used, in contrast to the MBEDTLS__MODULE_NAME__ALT flags.
  *
- * Example: In case you uncomment MBEDTLS_SHA256_PROCESS_ALT, mbed TLS will
+ * Example: In case you uncomment MBEDTLS_SHA256_PROCESS_ALT, Mbed TLS will
  * no longer provide the mbedtls_sha1_process() function, but it will still provide
  * the other function (using your mbedtls_sha1_process() function) and the definition
  * of mbedtls_sha1_context, so your implementation of mbedtls_sha1_process must be compatible
@@ -475,11 +539,11 @@
  *
  * Expose a part of the internal interface of the Elliptic Curve Point module.
  *
- * MBEDTLS_ECP__FUNCTION_NAME__ALT: Uncomment a macro to let mbed TLS use your
+ * MBEDTLS_ECP__FUNCTION_NAME__ALT: Uncomment a macro to let Mbed TLS use your
  * alternative core implementation of elliptic curve arithmetic. Keep in mind
  * that function prototypes should remain the same.
  *
- * This partially replaces one function. The header file from mbed TLS is still
+ * This partially replaces one function. The header file from Mbed TLS is still
  * used, in contrast to the MBEDTLS_ECP_ALT flag. The original implementation
  * is still present and it is used for group structures not supported by the
  * alternative.
@@ -503,11 +567,11 @@
  * implement optimized set up and tear down instructions.
  *
  * Example: In case you set MBEDTLS_ECP_INTERNAL_ALT and
- * MBEDTLS_ECP_DOUBLE_JAC_ALT, mbed TLS will still provide the ecp_double_jac()
+ * MBEDTLS_ECP_DOUBLE_JAC_ALT, Mbed TLS will still provide the ecp_double_jac()
  * function, but will use your mbedtls_internal_ecp_double_jac() if the group
  * for the operation is supported by your implementation (i.e. your
  * mbedtls_internal_ecp_grp_capable() function returns 1 for this group). If the
- * group is not supported by your implementation, then the original mbed TLS
+ * group is not supported by your implementation, then the original Mbed TLS
  * implementation of ecp_double_jac() is used instead, unless this fallback
  * behaviour is disabled by setting MBEDTLS_ECP_NO_FALLBACK (in which case
  * ecp_double_jac() will return MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE).
@@ -538,7 +602,7 @@
 /**
  * \def MBEDTLS_TEST_NULL_ENTROPY
  *
- * Enables testing and use of mbed TLS without any configured entropy sources.
+ * Enables testing and use of Mbed TLS without any configured entropy sources.
  * This permits use of the library on platforms before an entropy source has
  * been integrated (see for example the MBEDTLS_ENTROPY_HARDWARE_ALT or the
  * MBEDTLS_ENTROPY_NV_SEED switches).
@@ -555,7 +619,7 @@
 /**
  * \def MBEDTLS_ENTROPY_HARDWARE_ALT
  *
- * Uncomment this macro to let mbed TLS use your own implementation of a
+ * Uncomment this macro to let Mbed TLS use your own implementation of a
  * hardware entropy collector.
  *
  * Your function must be called \c mbedtls_hardware_poll(), have the same
@@ -782,6 +846,18 @@
 #define MBEDTLS_REMOVE_3DES_CIPHERSUITES
 
 /**
+ * Enable the verified implementations of ECDH primitives from Project Everest
+ * (currently only Curve25519). This feature changes the layout of ECDH
+ * contexts and therefore is a compatibility break for applications that access
+ * fields of a mbedtls_ecdh_context structure directly. See also
+ * MBEDTLS_ECDH_LEGACY_CONTEXT in include/mbedtls/ecdh.h.
+ *
+ * The Everest code is provided under the Apache 2.0 license only; therefore enabling this
+ * option is not compatible with taking the library under the GPL v2.0-or-later license.
+ */
+//#define MBEDTLS_ECDH_VARIANT_EVEREST_ENABLED
+
+/**
  * \def MBEDTLS_ECP_DP_SECP192R1_ENABLED
  *
  * MBEDTLS_ECP_XXXX_ENABLED: Enables specific curves within the Elliptic Curve
@@ -854,12 +930,37 @@
  * This is useful in non-threaded environments if you want to avoid blocking
  * for too long on ECC (and, hence, X.509 or SSL/TLS) operations.
  *
- * Uncomment this macro to enable restartable ECC computations.
+ * This option:
+ * - Adds xxx_restartable() variants of existing operations in the
+ *   following modules, with corresponding restart context types:
+ *   - ECP (for Short Weierstrass curves only): scalar multiplication (mul),
+ *     linear combination (muladd);
+ *   - ECDSA: signature generation & verification;
+ *   - PK: signature generation & verification;
+ *   - X509: certificate chain verification.
+ * - Adds mbedtls_ecdh_enable_restart() in the ECDH module.
+ * - Changes the behaviour of TLS 1.2 clients (not servers) when using the
+ *   ECDHE-ECDSA key exchange (not other key exchanges) to make all ECC
+ *   computations restartable:
+ *   - ECDH operations from the key exchange, only for Short Weierstrass
+ *     curves;
+ *   - verification of the server's key exchange signature;
+ *   - verification of the server's certificate chain;
+ *   - generation of the client's signature if client authentication is used,
+ *     with an ECC key/certificate.
+ *
+ * \note  In the cases above, the usual SSL/TLS functions, such as
+ *        mbedtls_ssl_handshake(), can now return
+ *        MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS.
  *
  * \note  This option only works with the default software implementation of
  *        elliptic curve functionality. It is incompatible with
- *        MBEDTLS_ECP_ALT, MBEDTLS_ECDH_XXX_ALT, MBEDTLS_ECDSA_XXX_ALT
- *        and MBEDTLS_ECDH_LEGACY_CONTEXT.
+ *        MBEDTLS_ECP_ALT, MBEDTLS_ECDH_XXX_ALT, MBEDTLS_ECDSA_XXX_ALT,
+ *        MBEDTLS_ECDH_LEGACY_CONTEXT, and MBEDTLS_USE_PSA_CRYPTO.
+ *
+ * Requires: MBEDTLS_ECP_C
+ *
+ * Uncomment this macro to enable restartable ECC computations.
  */
 //#define MBEDTLS_ECP_RESTARTABLE
 
@@ -1324,7 +1425,7 @@
  * Include backtrace information with each allocated block.
  *
  * Requires: MBEDTLS_MEMORY_BUFFER_ALLOC_C
- *           GLIBC-compatible backtrace() an backtrace_symbols() support
+ *           GLIBC-compatible backtrace() and backtrace_symbols() support
  *
  * Uncomment this macro to include backtrace information
  */
@@ -1428,8 +1529,8 @@
  * );
  * ```
  * The \c context value is initialized to 0 before the first call.
- * The function must fill the \c output buffer with \p output_size bytes
- * of random data and set \c *output_length to \p output_size.
+ * The function must fill the \c output buffer with \c output_size bytes
+ * of random data and set \c *output_length to \c output_size.
  *
  * Requires: MBEDTLS_PSA_CRYPTO_C
  *
@@ -1493,7 +1594,7 @@
  * Enable an implementation of SHA-256 that has lower ROM footprint but also
  * lower performance.
  *
- * The default implementation is meant to be a reasonnable compromise between
+ * The default implementation is meant to be a reasonable compromise between
  * performance and size. This version optimizes more aggressively for size at
  * the expense of performance. Eg on Cortex-M4 it reduces the size of
  * mbedtls_sha256_process() from ~2KB to ~0.5KB for a performance hit of about
@@ -1529,7 +1630,7 @@
  * \def MBEDTLS_SSL_ALL_ALERT_MESSAGES
  *
  * Enable sending of alert messages in case of encountered errors as per RFC.
- * If you choose not to send the alert messages, mbed TLS can still communicate
+ * If you choose not to send the alert messages, Mbed TLS can still communicate
  * with other servers, only debugging of failures is harder.
  *
  * The advantage of not sending alert messages, is that no information is given
@@ -1615,6 +1716,8 @@
  * saved after the handshake to allow for more efficient serialization, so if
  * you don't need this feature you'll save RAM by disabling it.
  *
+ * Requires: MBEDTLS_GCM_C or MBEDTLS_CCM_C or MBEDTLS_CHACHAPOLY_C
+ *
  * Comment to disable the context serialization APIs.
  */
 #define MBEDTLS_SSL_CONTEXT_SERIALIZATION
@@ -1658,7 +1761,7 @@
  * Enable support for RFC 7627: Session Hash and Extended Master Secret
  * Extension.
  *
- * This was introduced as "the proper fix" to the Triple Handshake familiy of
+ * This was introduced as "the proper fix" to the Triple Handshake family of
  * attacks, but it is recommended to always use it (even if you disable
  * renegotiation), since it actually fixes a more fundamental issue in the
  * original SSL/TLS design, and has implications beyond Triple Handshake.
@@ -1704,7 +1807,7 @@
  * \note This option has no influence on the protection against the
  *       triple handshake attack. Even if it is disabled, Mbed TLS will
  *       still ensure that certificates do not change during renegotiation,
- *       for exaple by keeping a hash of the peer's certificate.
+ *       for example by keeping a hash of the peer's certificate.
  *
  * Comment this macro to disable storing the peer's certificate
  * after the handshake.
@@ -1909,7 +2012,7 @@
  * unless you know for sure amplification cannot be a problem in the
  * environment in which your server operates.
  *
- * \warning Disabling this can ba a security risk! (see above)
+ * \warning Disabling this can be a security risk! (see above)
  *
  * Requires: MBEDTLS_SSL_PROTO_DTLS
  *
@@ -2054,6 +2157,23 @@
 //#define MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH
 
 /**
+ * Allow SHA-1 in the default TLS configuration for TLS 1.2 handshake
+ * signature and ciphersuite selection. Without this build-time option, SHA-1
+ * support must be activated explicitly through mbedtls_ssl_conf_sig_hashes.
+ * The use of SHA-1 in TLS <= 1.1 and in HMAC-SHA-1 is always allowed by
+ * default. At the time of writing, there is no practical attack on the use
+ * of SHA-1 in handshake signatures, hence this option is turned on by default
+ * to preserve compatibility with existing peers, but the general
+ * warning applies nonetheless:
+ *
+ * \warning   SHA-1 is considered a weak message digest and its use constitutes
+ *            a security risk. If possible, we recommend avoiding dependencies
+ *            on it, and considering stronger message digests instead.
+ *
+ */
+//#define MBEDTLS_TLS_DEFAULT_ALLOW_SHA1_IN_KEY_EXCHANGE
+
+/**
  * \def MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN
  *
  * Enable testing of the constant-flow nature of some sensitive functions with
@@ -2162,8 +2282,19 @@
  * This setting allows support for cryptographic mechanisms through the PSA
  * API to be configured separately from support through the mbedtls API.
  *
- * Uncomment this to enable use of PSA Crypto configuration settings which
- * can be found in include/psa/crypto_config.h.
+ * When this option is disabled, the PSA API exposes the cryptographic
+ * mechanisms that can be implemented on top of the `mbedtls_xxx` API
+ * configured with `MBEDTLS_XXX` symbols.
+ *
+ * When this option is enabled, the PSA API exposes the cryptographic
+ * mechanisms requested by the `PSA_WANT_XXX` symbols defined in
+ * include/psa/crypto_config.h. The corresponding `MBEDTLS_XXX` settings are
+ * automatically enabled if required (i.e. if no PSA driver provides the
+ * mechanism). You may still freely enable additional `MBEDTLS_XXX` symbols
+ * in config.h.
+ *
+ * If the symbol #MBEDTLS_PSA_CRYPTO_CONFIG_FILE is defined, it specifies
+ * an alternative header to include instead of include/psa/crypto_config.h.
  *
  * If you enable this option and write your own configuration file, you must
  * include mbedtls/config_psa.h in your configuration file. The default
@@ -2289,26 +2420,44 @@
  * Uncomment to enable use of ZLIB
  */
 //#define MBEDTLS_ZLIB_SUPPORT
-/* \} name SECTION: mbed TLS feature support */
+/** \} name SECTION: Mbed TLS feature support */
 
 /**
- * \name SECTION: mbed TLS modules
+ * \name SECTION: Mbed TLS modules
  *
- * This section enables or disables entire modules in mbed TLS
+ * This section enables or disables entire modules in Mbed TLS
  * \{
  */
 
 /**
  * \def MBEDTLS_AESNI_C
  *
- * Enable AES-NI support on x86-64.
+ * Enable AES-NI support on x86-64 or x86-32.
+ *
+ * \note AESNI is only supported with certain compilers and target options:
+ * - Visual Studio 2013: supported.
+ * - GCC, x86-64, target not explicitly supporting AESNI:
+ *   requires MBEDTLS_HAVE_ASM.
+ * - GCC, x86-32, target not explicitly supporting AESNI:
+ *   not supported.
+ * - GCC, x86-64 or x86-32, target supporting AESNI: supported.
+ *   For this assembly-less implementation, you must currently compile
+ *   `library/aesni.c` and `library/aes.c` with machine options to enable
+ *   SSE2 and AESNI instructions: `gcc -msse2 -maes -mpclmul` or
+ *   `clang -maes -mpclmul`.
+ * - Non-x86 targets: this option is silently ignored.
+ * - Other compilers: this option is silently ignored.
+ *
+ * \note
+ * Above, "GCC" includes compatible compilers such as Clang.
+ * The limitations on target support are likely to be relaxed in the future.
  *
  * Module:  library/aesni.c
  * Caller:  library/aes.c
  *
- * Requires: MBEDTLS_HAVE_ASM
+ * Requires: MBEDTLS_HAVE_ASM (on some platforms, see note)
  *
- * This modules adds support for the AES-NI instructions on x86-64
+ * This modules adds support for the AES-NI instructions on x86.
  */
 #define MBEDTLS_AESNI_C
 
@@ -2409,7 +2558,7 @@
  *      MBEDTLS_TLS_PSK_WITH_RC4_128_SHA
  *
  * \warning   ARC4 is considered a weak cipher and its use constitutes a
- *            security risk. If possible, we recommend avoidng dependencies on
+ *            security risk. If possible, we recommend avoiding dependencies on
  *            it, and considering stronger ciphers instead.
  *
  */
@@ -2722,7 +2871,7 @@
  *
  * PEM_PARSE uses DES/3DES for decrypting encrypted keys.
  *
- * \warning   DES is considered a weak cipher and its use constitutes a
+ * \warning   DES/3DES are considered weak ciphers and their use constitutes a
  *            security risk. We recommend considering stronger ciphers instead.
  */
 #define MBEDTLS_DES_C
@@ -2902,7 +3051,7 @@
  *
  * Requires: MBEDTLS_MD_C
  *
- * Uncomment to enable the HMAC_DRBG random number geerator.
+ * Uncomment to enable the HMAC_DRBG random number generator.
  */
 #define MBEDTLS_HMAC_DRBG_C
 
@@ -2997,7 +3146,7 @@
  * Module:  library/memory_buffer_alloc.c
  *
  * Requires: MBEDTLS_PLATFORM_C
- *           MBEDTLS_PLATFORM_MEMORY (to use it within mbed TLS)
+ *           MBEDTLS_PLATFORM_MEMORY (to use it within Mbed TLS)
  *
  * Enable this module to enable the buffer memory allocator.
  */
@@ -3014,7 +3163,7 @@
  *
  * \note See also our Knowledge Base article about porting to a new
  * environment:
- * https://tls.mbed.org/kb/how-to/how-do-i-port-mbed-tls-to-a-new-environment-OS
+ * https://mbed-tls.readthedocs.io/en/latest/kb/how-to/how-do-i-port-mbed-tls-to-a-new-environment-OS
  *
  * Module:  library/net_sockets.c
  *
@@ -3096,7 +3245,7 @@
 /**
  * \def MBEDTLS_PK_C
  *
- * Enable the generic public (asymetric) key layer.
+ * Enable the generic public (asymmetric) key layer.
  *
  * Module:  library/pk.c
  * Caller:  library/ssl_tls.c
@@ -3112,7 +3261,7 @@
 /**
  * \def MBEDTLS_PK_PARSE_C
  *
- * Enable the generic public (asymetric) key parser.
+ * Enable the generic public (asymmetric) key parser.
  *
  * Module:  library/pkparse.c
  * Caller:  library/x509_crt.c
@@ -3127,7 +3276,7 @@
 /**
  * \def MBEDTLS_PK_WRITE_C
  *
- * Enable the generic public (asymetric) key writer.
+ * Enable the generic public (asymmetric) key writer.
  *
  * Module:  library/pkwrite.c
  * Caller:  library/x509write.c
@@ -3384,7 +3533,8 @@
  * Module:  library/ssl_ticket.c
  * Caller:
  *
- * Requires: MBEDTLS_CIPHER_C
+ * Requires: MBEDTLS_CIPHER_C &&
+ *           ( MBEDTLS_GCM_C || MBEDTLS_CCM_C || MBEDTLS_CHACHAPOLY_C )
  */
 #define MBEDTLS_SSL_TICKET_C
 
@@ -3436,11 +3586,11 @@
  * \def MBEDTLS_THREADING_C
  *
  * Enable the threading abstraction layer.
- * By default mbed TLS assumes it is used in a non-threaded environment or that
+ * By default Mbed TLS assumes it is used in a non-threaded environment or that
  * contexts are not shared between threads. If you do intend to use contexts
  * between threads, you will need to enable this layer to prevent race
  * conditions. See also our Knowledge Base article about threading:
- * https://tls.mbed.org/kb/development/thread-safety-and-multi-threading
+ * https://mbed-tls.readthedocs.io/en/latest/kb/development/thread-safety-and-multi-threading
  *
  * Module:  library/threading.c
  *
@@ -3450,7 +3600,7 @@
  * You will have to enable either MBEDTLS_THREADING_ALT or
  * MBEDTLS_THREADING_PTHREAD.
  *
- * Enable this layer to allow use of mutexes within mbed TLS
+ * Enable this layer to allow use of mutexes within Mbed TLS
  */
 //#define MBEDTLS_THREADING_C
 
@@ -3466,9 +3616,13 @@
  * your own implementation of the whole module by setting
  * \c MBEDTLS_TIMING_ALT in the current file.
  *
+ * \note The timing module will include time.h on suitable platforms
+ *       regardless of the setting of MBEDTLS_HAVE_TIME, unless
+ *       MBEDTLS_TIMING_ALT is used. See timing.c for more information.
+ *
  * \note See also our Knowledge Base article about porting to a new
  * environment:
- * https://tls.mbed.org/kb/how-to/how-do-i-port-mbed-tls-to-a-new-environment-OS
+ * https://mbed-tls.readthedocs.io/en/latest/kb/how-to/how-do-i-port-mbed-tls-to-a-new-environment-OS
  *
  * Module:  library/timing.c
  * Caller:  library/havege.c
@@ -3598,7 +3752,88 @@
  */
 #define MBEDTLS_XTEA_C
 
-/* \} name SECTION: mbed TLS modules */
+/** \} name SECTION: Mbed TLS modules */
+
+/**
+ * \name SECTION: General configuration options
+ *
+ * This section contains Mbed TLS build settings that are not associated
+ * with a particular module.
+ *
+ * \{
+ */
+
+/**
+ * \def MBEDTLS_CONFIG_FILE
+ *
+ * If defined, this is a header which will be included instead of
+ * `"mbedtls/config.h"`.
+ * This header file specifies the compile-time configuration of Mbed TLS.
+ * Unlike other configuration options, this one must be defined on the
+ * compiler command line: a definition in `config.h` would have no effect.
+ *
+ * This macro is expanded after an <tt>\#include</tt> directive. This is a popular but
+ * non-standard feature of the C language, so this feature is only available
+ * with compilers that perform macro expansion on an <tt>\#include</tt> line.
+ *
+ * The value of this symbol is typically a path in double quotes, either
+ * absolute or relative to a directory on the include search path.
+ */
+//#define MBEDTLS_CONFIG_FILE "mbedtls/config.h"
+
+/**
+ * \def MBEDTLS_USER_CONFIG_FILE
+ *
+ * If defined, this is a header which will be included after
+ * `"mbedtls/config.h"` or #MBEDTLS_CONFIG_FILE.
+ * This allows you to modify the default configuration, including the ability
+ * to undefine options that are enabled by default.
+ *
+ * This macro is expanded after an <tt>\#include</tt> directive. This is a popular but
+ * non-standard feature of the C language, so this feature is only available
+ * with compilers that perform macro expansion on an <tt>\#include</tt> line.
+ *
+ * The value of this symbol is typically a path in double quotes, either
+ * absolute or relative to a directory on the include search path.
+ */
+//#define MBEDTLS_USER_CONFIG_FILE "/dev/null"
+
+/**
+ * \def MBEDTLS_PSA_CRYPTO_CONFIG_FILE
+ *
+ * If defined, this is a header which will be included instead of
+ * `"psa/crypto_config.h"`.
+ * This header file specifies which cryptographic mechanisms are available
+ * through the PSA API when #MBEDTLS_PSA_CRYPTO_CONFIG is enabled, and
+ * is not used when #MBEDTLS_PSA_CRYPTO_CONFIG is disabled.
+ *
+ * This macro is expanded after an <tt>\#include</tt> directive. This is a popular but
+ * non-standard feature of the C language, so this feature is only available
+ * with compilers that perform macro expansion on an <tt>\#include</tt> line.
+ *
+ * The value of this symbol is typically a path in double quotes, either
+ * absolute or relative to a directory on the include search path.
+ */
+//#define MBEDTLS_PSA_CRYPTO_CONFIG_FILE "psa/crypto_config.h"
+
+/**
+ * \def MBEDTLS_PSA_CRYPTO_USER_CONFIG_FILE
+ *
+ * If defined, this is a header which will be included after
+ * `"psa/crypto_config.h"` or #MBEDTLS_PSA_CRYPTO_CONFIG_FILE.
+ * This allows you to modify the default configuration, including the ability
+ * to undefine options that are enabled by default.
+ *
+ * This macro is expanded after an <tt>\#include</tt> directive. This is a popular but
+ * non-standard feature of the C language, so this feature is only available
+ * with compilers that perform macro expansion on an <tt>\#include</tt> line.
+ *
+ * The value of this symbol is typically a path in double quotes, either
+ * absolute or relative to a directory on the include search path.
+ */
+//#define MBEDTLS_PSA_CRYPTO_USER_CONFIG_FILE "/dev/null"
+
+/** \} name SECTION: General configuration options */
 
 /**
  * \name SECTION: Module configuration options
@@ -3609,14 +3844,18 @@
  *
  * Our advice is to enable options and change their values here
  * only if you have a good reason and know the consequences.
- *
- * Please check the respective header file for documentation on these
- * parameters (to prevent duplicate documentation).
  * \{
  */
+/* The Doxygen documentation here is used when a user comments out a
+ * setting and runs doxygen themselves. On the other hand, when we typeset
+ * the full documentation including disabled settings, the documentation
+ * in specific modules' header files is used if present. When editing this
+ * file, make sure that each option is documented in exactly one place,
+ * plus optionally a same-line Doxygen comment here if there is a Doxygen
+ * comment in the specific module. */
 
 /* MPI / BIGNUM options */
-//#define MBEDTLS_MPI_WINDOW_SIZE            6 /**< Maximum window size used. */
+//#define MBEDTLS_MPI_WINDOW_SIZE            2 /**< Maximum window size used. */
 //#define MBEDTLS_MPI_MAX_SIZE            1024 /**< Maximum number of bytes for usable MPIs. */
 
 /* CTR_DRBG options */
@@ -3647,8 +3886,29 @@
 
 /* Platform options */
 //#define MBEDTLS_PLATFORM_STD_MEM_HDR   <stdlib.h> /**< Header to include if MBEDTLS_PLATFORM_NO_STD_FUNCTIONS is defined. Don't define if no header is needed. */
-//#define MBEDTLS_PLATFORM_STD_CALLOC        calloc /**< Default allocator to use, can be undefined */
-//#define MBEDTLS_PLATFORM_STD_FREE            free /**< Default free to use, can be undefined */
+
+/** \def MBEDTLS_PLATFORM_STD_CALLOC
+ *
+ * Default allocator to use, can be undefined.
+ * It must initialize the allocated buffer memory to zeroes.
+ * The size of the buffer is the product of the two parameters.
+ * The calloc function returns either a null pointer or a pointer to the allocated space.
+ * If the product is 0, the function may either return NULL or a valid pointer to an array of size 0 which is a valid input to the deallocation function.
+ * An uninitialized #MBEDTLS_PLATFORM_STD_CALLOC always fails, returning a null pointer.
+ * See the description of #MBEDTLS_PLATFORM_MEMORY for more details.
+ * The corresponding deallocation function is #MBEDTLS_PLATFORM_STD_FREE.
+ */
+//#define MBEDTLS_PLATFORM_STD_CALLOC        calloc
+
+/** \def MBEDTLS_PLATFORM_STD_FREE
+ *
+ * Default free to use, can be undefined.
+ * NULL is a valid parameter, and the function must do nothing.
+ * A non-null parameter will always be a pointer previously returned by #MBEDTLS_PLATFORM_STD_CALLOC and not yet freed.
+ * An uninitialized #MBEDTLS_PLATFORM_STD_FREE does not do anything.
+ * See the description of #MBEDTLS_PLATFORM_MEMORY for more details (same principles as for MBEDTLS_PLATFORM_STD_CALLOC apply).
+ */
+//#define MBEDTLS_PLATFORM_STD_FREE            free
 //#define MBEDTLS_PLATFORM_STD_EXIT            exit /**< Default exit to use, can be undefined */
 //#define MBEDTLS_PLATFORM_STD_TIME            time /**< Default time to use, can be undefined. MBEDTLS_HAVE_TIME must be enabled */
 //#define MBEDTLS_PLATFORM_STD_FPRINTF      fprintf /**< Default fprintf to use, can be undefined */
@@ -3661,10 +3921,10 @@
 //#define MBEDTLS_PLATFORM_STD_NV_SEED_WRITE  mbedtls_platform_std_nv_seed_write /**< Default nv_seed_write function to use, can be undefined */
 //#define MBEDTLS_PLATFORM_STD_NV_SEED_FILE  "seedfile" /**< Seed file to read/write with default implementation */
 
-/* To Use Function Macros MBEDTLS_PLATFORM_C must be enabled */
+/* To use the following function macros, MBEDTLS_PLATFORM_C must be enabled. */
 /* MBEDTLS_PLATFORM_XXX_MACRO and MBEDTLS_PLATFORM_XXX_ALT cannot both be defined */
-//#define MBEDTLS_PLATFORM_CALLOC_MACRO        calloc /**< Default allocator macro to use, can be undefined */
-//#define MBEDTLS_PLATFORM_FREE_MACRO            free /**< Default free macro to use, can be undefined */
+//#define MBEDTLS_PLATFORM_CALLOC_MACRO        calloc /**< Default allocator macro to use, can be undefined. See MBEDTLS_PLATFORM_STD_CALLOC for requirements. */
+//#define MBEDTLS_PLATFORM_FREE_MACRO            free /**< Default free macro to use, can be undefined. See MBEDTLS_PLATFORM_STD_FREE for requirements. */
 //#define MBEDTLS_PLATFORM_EXIT_MACRO            exit /**< Default exit macro to use, can be undefined */
 //#define MBEDTLS_PLATFORM_TIME_MACRO            time /**< Default time macro to use, can be undefined. MBEDTLS_HAVE_TIME must be enabled */
 //#define MBEDTLS_PLATFORM_TIME_TYPE_MACRO       time_t /**< Default time macro to use, can be undefined. MBEDTLS_HAVE_TIME must be enabled */
@@ -3937,72 +4197,7 @@
 //#define MBEDTLS_X509_MAX_INTERMEDIATE_CA   8   /**< Maximum number of intermediate CAs in a verification chain. */
 //#define MBEDTLS_X509_MAX_FILE_PATH_LEN     512 /**< Maximum length of a path/filename string in bytes including the null terminator character ('\0'). */
 
-/**
- * Allow SHA-1 in the default TLS configuration for TLS 1.2 handshake
- * signature and ciphersuite selection. Without this build-time option, SHA-1
- * support must be activated explicitly through mbedtls_ssl_conf_sig_hashes.
- * The use of SHA-1 in TLS <= 1.1 and in HMAC-SHA-1 is always allowed by
- * default. At the time of writing, there is no practical attack on the use
- * of SHA-1 in handshake signatures, hence this option is turned on by default
- * to preserve compatibility with existing peers, but the general
- * warning applies nonetheless:
- *
- * \warning   SHA-1 is considered a weak message digest and its use constitutes
- *            a security risk. If possible, we recommend avoiding dependencies
- *            on it, and considering stronger message digests instead.
- *
- */
-//#define MBEDTLS_TLS_DEFAULT_ALLOW_SHA1_IN_KEY_EXCHANGE
-
-/**
- * Uncomment the macro to let mbed TLS use your alternate implementation of
- * mbedtls_platform_zeroize(). This replaces the default implementation in
- * platform_util.c.
- *
- * mbedtls_platform_zeroize() is a widely used function across the library to
- * zero a block of memory. The implementation is expected to be secure in the
- * sense that it has been written to prevent the compiler from removing calls
- * to mbedtls_platform_zeroize() as part of redundant code elimination
- * optimizations. However, it is difficult to guarantee that calls to
- * mbedtls_platform_zeroize() will not be optimized by the compiler as older
- * versions of the C language standards do not provide a secure implementation
- * of memset(). Therefore, MBEDTLS_PLATFORM_ZEROIZE_ALT enables users to
- * configure their own implementation of mbedtls_platform_zeroize(), for
- * example by using directives specific to their compiler, features from newer
- * C standards (e.g using memset_s() in C11) or calling a secure memset() from
- * their system (e.g explicit_bzero() in BSD).
- */
-//#define MBEDTLS_PLATFORM_ZEROIZE_ALT
-
-/**
- * Uncomment the macro to let Mbed TLS use your alternate implementation of
- * mbedtls_platform_gmtime_r(). This replaces the default implementation in
- * platform_util.c.
- *
- * gmtime() is not a thread-safe function as defined in the C standard. The
- * library will try to use safer implementations of this function, such as
- * gmtime_r() when available. However, if Mbed TLS cannot identify the target
- * system, the implementation of mbedtls_platform_gmtime_r() will default to
- * using the standard gmtime(). In this case, calls from the library to
- * gmtime() will be guarded by the global mutex mbedtls_threading_gmtime_mutex
- * if MBEDTLS_THREADING_C is enabled. We recommend that calls from outside the
- * library are also guarded with this mutex to avoid race conditions. However,
- * if the macro MBEDTLS_PLATFORM_GMTIME_R_ALT is defined, Mbed TLS will
- * unconditionally use the implementation for mbedtls_platform_gmtime_r()
- * supplied at compile time.
- */
-//#define MBEDTLS_PLATFORM_GMTIME_R_ALT
-
-/**
- * Enable the verified implementations of ECDH primitives from Project Everest
- * (currently only Curve25519). This feature changes the layout of ECDH
- * contexts and therefore is a compatibility break for applications that access
- * fields of a mbedtls_ecdh_context structure directly. See also
- * MBEDTLS_ECDH_LEGACY_CONTEXT in include/mbedtls/ecdh.h.
- */
-//#define MBEDTLS_ECDH_VARIANT_EVEREST_ENABLED
-
-/* \} name SECTION: Customisation configuration options */
+/** \} name SECTION: Module configuration options */
 
 /* Target and application specific configurations
  *
diff --git a/third_party/mbedtls/repo/include/mbedtls/config_psa.h b/third_party/mbedtls/repo/include/mbedtls/config_psa.h
index 189f6c2..205d303 100644
--- a/third_party/mbedtls/repo/include/mbedtls/config_psa.h
+++ b/third_party/mbedtls/repo/include/mbedtls/config_psa.h
@@ -7,33 +7,29 @@
  *  those definitions to define symbols used in the library code.
  *
  *  Users and integrators should not edit this file, please edit
- *  include/mbedtls/config.h for MBETLS_XXX settings or
+ *  include/mbedtls/config.h for MBEDTLS_XXX settings or
  *  include/psa/crypto_config.h for PSA_WANT_XXX settings.
  */
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 
 #ifndef MBEDTLS_CONFIG_PSA_H
 #define MBEDTLS_CONFIG_PSA_H
 
 #if defined(MBEDTLS_PSA_CRYPTO_CONFIG)
+#if defined(MBEDTLS_PSA_CRYPTO_CONFIG_FILE)
+#include MBEDTLS_PSA_CRYPTO_CONFIG_FILE
+#else
 #include "psa/crypto_config.h"
+#endif
 #endif /* defined(MBEDTLS_PSA_CRYPTO_CONFIG) */
 
+#if defined(MBEDTLS_PSA_CRYPTO_USER_CONFIG_FILE)
+#include MBEDTLS_PSA_CRYPTO_USER_CONFIG_FILE
+#endif
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -102,6 +98,10 @@
 
 #if defined(PSA_WANT_ALG_HKDF)
 #if !defined(MBEDTLS_PSA_ACCEL_ALG_HKDF)
+/*
+ * The PSA implementation has its own implementation of HKDF, separate from
+ * hkdf.c. No need to enable MBEDTLS_HKDF_C here.
+ */
 #define MBEDTLS_PSA_BUILTIN_ALG_HMAC 1
 #define MBEDTLS_PSA_BUILTIN_ALG_HKDF 1
 #endif /* !MBEDTLS_PSA_ACCEL_ALG_HKDF */
@@ -264,12 +264,11 @@
 #if (defined(PSA_WANT_ALG_CTR) && !defined(MBEDTLS_PSA_ACCEL_ALG_CTR)) || \
     (defined(PSA_WANT_ALG_CFB) && !defined(MBEDTLS_PSA_ACCEL_ALG_CFB)) || \
     (defined(PSA_WANT_ALG_OFB) && !defined(MBEDTLS_PSA_ACCEL_ALG_OFB)) || \
-    (defined(PSA_WANT_ALG_XTS) && !defined(MBEDTLS_PSA_ACCEL_ALG_XTS)) || \
     defined(PSA_WANT_ALG_ECB_NO_PADDING) || \
     (defined(PSA_WANT_ALG_CBC_NO_PADDING) && \
-     !defined(MBEDTLS_PSA_ACCEL_ALG_CBC_NO_PADDING)) || \
+    !defined(MBEDTLS_PSA_ACCEL_ALG_CBC_NO_PADDING)) || \
     (defined(PSA_WANT_ALG_CBC_PKCS7) && \
-     !defined(MBEDTLS_PSA_ACCEL_ALG_CBC_PKCS7)) || \
+    !defined(MBEDTLS_PSA_ACCEL_ALG_CBC_PKCS7)) || \
     (defined(PSA_WANT_ALG_CMAC) && !defined(MBEDTLS_PSA_ACCEL_ALG_CMAC))
 #define PSA_HAVE_SOFT_BLOCK_MODE 1
 #endif
@@ -393,15 +392,8 @@
 #endif
 #endif /* PSA_WANT_ALG_OFB */
 
-#if defined(PSA_WANT_ALG_XTS)
-#if !defined(MBEDTLS_PSA_ACCEL_ALG_XTS) || \
-    defined(PSA_HAVE_SOFT_BLOCK_CIPHER)
-#define MBEDTLS_PSA_BUILTIN_ALG_XTS 1
-#define MBEDTLS_CIPHER_MODE_XTS
-#endif
-#endif /* PSA_WANT_ALG_XTS */
-
-#if defined(PSA_WANT_ALG_ECB_NO_PADDING)
+#if defined(PSA_WANT_ALG_ECB_NO_PADDING) &&     \
+    !defined(MBEDTLS_PSA_ACCEL_ALG_ECB_NO_PADDING)
 #define MBEDTLS_PSA_BUILTIN_ALG_ECB_NO_PADDING 1
 #endif
 
@@ -446,6 +438,8 @@
 #if !defined(MBEDTLS_PSA_ACCEL_ALG_CHACHA20_POLY1305)
 #if defined(PSA_WANT_KEY_TYPE_CHACHA20)
 #define MBEDTLS_CHACHAPOLY_C
+#define MBEDTLS_CHACHA20_C
+#define MBEDTLS_POLY1305_C
 #define MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305 1
 #endif /* PSA_WANT_KEY_TYPE_CHACHA20 */
 #endif /* !MBEDTLS_PSA_ACCEL_ALG_CHACHA20_POLY1305 */
@@ -483,7 +477,7 @@
 #if !defined(MBEDTLS_PSA_ACCEL_ECC_MONTGOMERY_448)
 /*
  * Curve448 is not yet supported via the PSA API in Mbed TLS
- * (https://github.com/ARMmbed/mbedtls/issues/4249).
+ * (https://github.com/Mbed-TLS/mbedtls/issues/4249).
  */
 #error "Curve448 is not yet supported via the PSA API in Mbed TLS."
 #define MBEDTLS_ECP_DP_CURVE448_ENABLED
@@ -537,7 +531,7 @@
 #if !defined(MBEDTLS_PSA_ACCEL_ECC_SECP_K1_224)
 /*
  * SECP224K1 is buggy via the PSA API in Mbed TLS
- * (https://github.com/ARMmbed/mbedtls/issues/3541).
+ * (https://github.com/Mbed-TLS/mbedtls/issues/3541).
  */
 #error "SECP224K1 is buggy via the PSA API in Mbed TLS."
 #define MBEDTLS_ECP_DP_SECP224K1_ENABLED
@@ -615,7 +609,7 @@
 #if defined(MBEDTLS_MD_C)
 #define MBEDTLS_PSA_BUILTIN_ALG_HMAC 1
 #define PSA_WANT_ALG_HMAC 1
-#define PSA_WANT_KEY_TYPE_HMAC
+#define PSA_WANT_KEY_TYPE_HMAC 1
 #define MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF 1
 #define PSA_WANT_ALG_TLS12_PRF 1
 #define MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS 1
@@ -751,76 +745,71 @@
 #define PSA_WANT_ALG_OFB 1
 #endif
 
-#if defined(MBEDTLS_CIPHER_MODE_XTS)
-#define MBEDTLS_PSA_BUILTIN_ALG_XTS 1
-#define PSA_WANT_ALG_XTS 1
-#endif
-
 #if defined(MBEDTLS_ECP_DP_BP256R1_ENABLED)
 #define MBEDTLS_PSA_BUILTIN_ECC_BRAINPOOL_P_R1_256 1
-#define PSA_WANT_ECC_BRAINPOOL_P_R1_256
+#define PSA_WANT_ECC_BRAINPOOL_P_R1_256 1
 #endif
 
 #if defined(MBEDTLS_ECP_DP_BP384R1_ENABLED)
 #define MBEDTLS_PSA_BUILTIN_ECC_BRAINPOOL_P_R1_384 1
-#define PSA_WANT_ECC_BRAINPOOL_P_R1_384
+#define PSA_WANT_ECC_BRAINPOOL_P_R1_384 1
 #endif
 
 #if defined(MBEDTLS_ECP_DP_BP512R1_ENABLED)
 #define MBEDTLS_PSA_BUILTIN_ECC_BRAINPOOL_P_R1_512 1
-#define PSA_WANT_ECC_BRAINPOOL_P_R1_512
+#define PSA_WANT_ECC_BRAINPOOL_P_R1_512 1
 #endif
 
 #if defined(MBEDTLS_ECP_DP_CURVE25519_ENABLED)
 #define MBEDTLS_PSA_BUILTIN_ECC_MONTGOMERY_255 1
-#define PSA_WANT_ECC_MONTGOMERY_255
+#define PSA_WANT_ECC_MONTGOMERY_255 1
 #endif
 
-/* Curve448 is not yet supported via the PSA API (https://github.com/ARMmbed/mbedtls/issues/4249) */
+/* Curve448 is not yet supported via the PSA API (https://github.com/Mbed-TLS/mbedtls/issues/4249) */
 #if 0 && defined(MBEDTLS_ECP_DP_CURVE448_ENABLED)
 #define MBEDTLS_PSA_BUILTIN_ECC_MONTGOMERY_448 1
-#define PSA_WANT_ECC_MONTGOMERY_448
+#define PSA_WANT_ECC_MONTGOMERY_448 1
 #endif
 
 #if defined(MBEDTLS_ECP_DP_SECP192R1_ENABLED)
 #define MBEDTLS_PSA_BUILTIN_ECC_SECP_R1_192 1
-#define PSA_WANT_ECC_SECP_R1_192
+#define PSA_WANT_ECC_SECP_R1_192 1
 #endif
 
 #if defined(MBEDTLS_ECP_DP_SECP224R1_ENABLED)
 #define MBEDTLS_PSA_BUILTIN_ECC_SECP_R1_224 1
-#define PSA_WANT_ECC_SECP_R1_224
+#define PSA_WANT_ECC_SECP_R1_224 1
 #endif
 
 #if defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED)
 #define MBEDTLS_PSA_BUILTIN_ECC_SECP_R1_256 1
-#define PSA_WANT_ECC_SECP_R1_256
+#define PSA_WANT_ECC_SECP_R1_256 1
 #endif
 
 #if defined(MBEDTLS_ECP_DP_SECP384R1_ENABLED)
 #define MBEDTLS_PSA_BUILTIN_ECC_SECP_R1_384 1
-#define PSA_WANT_ECC_SECP_R1_384
+#define PSA_WANT_ECC_SECP_R1_384 1
 #endif
 
 #if defined(MBEDTLS_ECP_DP_SECP521R1_ENABLED)
 #define MBEDTLS_PSA_BUILTIN_ECC_SECP_R1_521 1
-#define PSA_WANT_ECC_SECP_R1_521
+#define PSA_WANT_ECC_SECP_R1_521 1
 #endif
 
 #if defined(MBEDTLS_ECP_DP_SECP192K1_ENABLED)
 #define MBEDTLS_PSA_BUILTIN_ECC_SECP_K1_192 1
-#define PSA_WANT_ECC_SECP_K1_192
+#define PSA_WANT_ECC_SECP_K1_192 1
 #endif
 
-/* SECP224K1 is buggy via the PSA API (https://github.com/ARMmbed/mbedtls/issues/3541) */
+/* SECP224K1 is buggy via the PSA API (https://github.com/Mbed-TLS/mbedtls/issues/3541) */
 #if 0 && defined(MBEDTLS_ECP_DP_SECP224K1_ENABLED)
 #define MBEDTLS_PSA_BUILTIN_ECC_SECP_K1_224 1
-#define PSA_WANT_ECC_SECP_K1_224
+#define PSA_WANT_ECC_SECP_K1_224 1
 #endif
 
 #if defined(MBEDTLS_ECP_DP_SECP256K1_ENABLED)
 #define MBEDTLS_PSA_BUILTIN_ECC_SECP_K1_256 1
-#define PSA_WANT_ECC_SECP_K1_256
+#define PSA_WANT_ECC_SECP_K1_256 1
 #endif
 
 #endif /* MBEDTLS_PSA_CRYPTO_CONFIG */
diff --git a/third_party/mbedtls/repo/include/mbedtls/constant_time.h b/third_party/mbedtls/repo/include/mbedtls/constant_time.h
index c5de57a..7226ae1 100644
--- a/third_party/mbedtls/repo/include/mbedtls/constant_time.h
+++ b/third_party/mbedtls/repo/include/mbedtls/constant_time.h
@@ -2,19 +2,7 @@
  *  Constant-time functions
  *
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 
 #ifndef MBEDTLS_CONSTANT_TIME_H
@@ -38,8 +26,8 @@
  * \return      Zero if the content of the two buffer is the same,
  *              otherwise non-zero.
  */
-int mbedtls_ct_memcmp( const void *a,
-                       const void *b,
-                       size_t n );
+int mbedtls_ct_memcmp(const void *a,
+                      const void *b,
+                      size_t n);
 
 #endif /* MBEDTLS_CONSTANT_TIME_H */
diff --git a/third_party/mbedtls/repo/include/mbedtls/ctr_drbg.h b/third_party/mbedtls/repo/include/mbedtls/ctr_drbg.h
index dc4adc8..eb72f9e 100644
--- a/third_party/mbedtls/repo/include/mbedtls/ctr_drbg.h
+++ b/third_party/mbedtls/repo/include/mbedtls/ctr_drbg.h
@@ -23,19 +23,7 @@
  */
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 
 #ifndef MBEDTLS_CTR_DRBG_H
@@ -80,8 +68,8 @@
  */
 #endif
 
-#define MBEDTLS_CTR_DRBG_KEYBITS            ( MBEDTLS_CTR_DRBG_KEYSIZE * 8 ) /**< The key size for the DRBG operation, in bits. */
-#define MBEDTLS_CTR_DRBG_SEEDLEN            ( MBEDTLS_CTR_DRBG_KEYSIZE + MBEDTLS_CTR_DRBG_BLOCKSIZE ) /**< The seed length, calculated as (counter + AES key). */
+#define MBEDTLS_CTR_DRBG_KEYBITS            (MBEDTLS_CTR_DRBG_KEYSIZE * 8)   /**< The key size for the DRBG operation, in bits. */
+#define MBEDTLS_CTR_DRBG_SEEDLEN            (MBEDTLS_CTR_DRBG_KEYSIZE + MBEDTLS_CTR_DRBG_BLOCKSIZE)   /**< The seed length, calculated as (counter + AES key). */
 
 /**
  * \name SECTION: Module settings
@@ -138,7 +126,7 @@
 /**< The maximum size of seed or reseed buffer in bytes. */
 #endif
 
-/* \} name SECTION: Module settings */
+/** \} name SECTION: Module settings */
 
 #define MBEDTLS_CTR_DRBG_PR_OFF             0
 /**< Prediction resistance is disabled. */
@@ -164,14 +152,13 @@
  * the entropy source does not provide enough material to form a nonce.
  * See the documentation of mbedtls_ctr_drbg_seed() for more information.
  */
-#define MBEDTLS_CTR_DRBG_ENTROPY_NONCE_LEN ( MBEDTLS_CTR_DRBG_ENTROPY_LEN + 1 ) / 2
+#define MBEDTLS_CTR_DRBG_ENTROPY_NONCE_LEN (MBEDTLS_CTR_DRBG_ENTROPY_LEN + 1) / 2
 #endif
 
 /**
  * \brief          The CTR_DRBG context structure.
  */
-typedef struct mbedtls_ctr_drbg_context
-{
+typedef struct mbedtls_ctr_drbg_context {
     unsigned char counter[16];  /*!< The counter (V). */
     int reseed_counter;         /*!< The reseed counter.
                                  * This is the number of requests that have
@@ -199,7 +186,7 @@
      * Callbacks (Entropy)
      */
     int (*f_entropy)(void *, unsigned char *, size_t);
-                                /*!< The entropy callback function. */
+    /*!< The entropy callback function. */
 
     void *p_entropy;            /*!< The context for the entropy function. */
 
@@ -228,7 +215,7 @@
  *
  * \param ctx           The CTR_DRBG context to initialize.
  */
-void mbedtls_ctr_drbg_init( mbedtls_ctr_drbg_context *ctx );
+void mbedtls_ctr_drbg_init(mbedtls_ctr_drbg_context *ctx);
 
 /**
  * \brief               This function seeds and sets up the CTR_DRBG
@@ -329,11 +316,11 @@
  * \return              \c 0 on success.
  * \return              #MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED on failure.
  */
-int mbedtls_ctr_drbg_seed( mbedtls_ctr_drbg_context *ctx,
-                   int (*f_entropy)(void *, unsigned char *, size_t),
-                   void *p_entropy,
-                   const unsigned char *custom,
-                   size_t len );
+int mbedtls_ctr_drbg_seed(mbedtls_ctr_drbg_context *ctx,
+                          int (*f_entropy)(void *, unsigned char *, size_t),
+                          void *p_entropy,
+                          const unsigned char *custom,
+                          size_t len);
 
 /**
  * \brief               This function resets CTR_DRBG context to the state immediately
@@ -341,7 +328,7 @@
  *
  * \param ctx           The CTR_DRBG context to clear.
  */
-void mbedtls_ctr_drbg_free( mbedtls_ctr_drbg_context *ctx );
+void mbedtls_ctr_drbg_free(mbedtls_ctr_drbg_context *ctx);
 
 /**
  * \brief               This function turns prediction resistance on or off.
@@ -356,8 +343,8 @@
  * \param ctx           The CTR_DRBG context.
  * \param resistance    #MBEDTLS_CTR_DRBG_PR_ON or #MBEDTLS_CTR_DRBG_PR_OFF.
  */
-void mbedtls_ctr_drbg_set_prediction_resistance( mbedtls_ctr_drbg_context *ctx,
-                                         int resistance );
+void mbedtls_ctr_drbg_set_prediction_resistance(mbedtls_ctr_drbg_context *ctx,
+                                                int resistance);
 
 /**
  * \brief               This function sets the amount of entropy grabbed on each
@@ -383,8 +370,8 @@
  *                      and at most the maximum length accepted by the
  *                      entropy function that is set in the context.
  */
-void mbedtls_ctr_drbg_set_entropy_len( mbedtls_ctr_drbg_context *ctx,
-                               size_t len );
+void mbedtls_ctr_drbg_set_entropy_len(mbedtls_ctr_drbg_context *ctx,
+                                      size_t len);
 
 /**
  * \brief               This function sets the amount of entropy grabbed
@@ -405,8 +392,8 @@
  * \return              #MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED
  *                      if the initial seeding has already taken place.
  */
-int mbedtls_ctr_drbg_set_nonce_len( mbedtls_ctr_drbg_context *ctx,
-                                    size_t len );
+int mbedtls_ctr_drbg_set_nonce_len(mbedtls_ctr_drbg_context *ctx,
+                                   size_t len);
 
 /**
  * \brief               This function sets the reseed interval.
@@ -420,8 +407,8 @@
  * \param ctx           The CTR_DRBG context.
  * \param interval      The reseed interval.
  */
-void mbedtls_ctr_drbg_set_reseed_interval( mbedtls_ctr_drbg_context *ctx,
-                                   int interval );
+void mbedtls_ctr_drbg_set_reseed_interval(mbedtls_ctr_drbg_context *ctx,
+                                          int interval);
 
 /**
  * \brief               This function reseeds the CTR_DRBG context, that is
@@ -443,8 +430,8 @@
  * \return              \c 0 on success.
  * \return              #MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED on failure.
  */
-int mbedtls_ctr_drbg_reseed( mbedtls_ctr_drbg_context *ctx,
-                     const unsigned char *additional, size_t len );
+int mbedtls_ctr_drbg_reseed(mbedtls_ctr_drbg_context *ctx,
+                            const unsigned char *additional, size_t len);
 
 /**
  * \brief              This function updates the state of the CTR_DRBG context.
@@ -466,9 +453,9 @@
  *                     #MBEDTLS_CTR_DRBG_MAX_SEED_INPUT.
  * \return             An error from the underlying AES cipher on failure.
  */
-int mbedtls_ctr_drbg_update_ret( mbedtls_ctr_drbg_context *ctx,
-                                 const unsigned char *additional,
-                                 size_t add_len );
+int mbedtls_ctr_drbg_update_ret(mbedtls_ctr_drbg_context *ctx,
+                                const unsigned char *additional,
+                                size_t add_len);
 
 /**
  * \brief   This function updates a CTR_DRBG instance with additional
@@ -501,9 +488,9 @@
  * \return    #MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED or
  *            #MBEDTLS_ERR_CTR_DRBG_REQUEST_TOO_BIG on failure.
  */
-int mbedtls_ctr_drbg_random_with_add( void *p_rng,
-                              unsigned char *output, size_t output_len,
-                              const unsigned char *additional, size_t add_len );
+int mbedtls_ctr_drbg_random_with_add(void *p_rng,
+                                     unsigned char *output, size_t output_len,
+                                     const unsigned char *additional, size_t add_len);
 
 /**
  * \brief   This function uses CTR_DRBG to generate random data.
@@ -529,11 +516,11 @@
  * \return              #MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED or
  *                      #MBEDTLS_ERR_CTR_DRBG_REQUEST_TOO_BIG on failure.
  */
-int mbedtls_ctr_drbg_random( void *p_rng,
-                     unsigned char *output, size_t output_len );
+int mbedtls_ctr_drbg_random(void *p_rng,
+                            unsigned char *output, size_t output_len);
 
 
-#if ! defined(MBEDTLS_DEPRECATED_REMOVED)
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
 #if defined(MBEDTLS_DEPRECATED_WARNING)
 #define MBEDTLS_DEPRECATED    __attribute__((deprecated))
 #else
@@ -557,7 +544,7 @@
 MBEDTLS_DEPRECATED void mbedtls_ctr_drbg_update(
     mbedtls_ctr_drbg_context *ctx,
     const unsigned char *additional,
-    size_t add_len );
+    size_t add_len);
 #undef MBEDTLS_DEPRECATED
 #endif /* !MBEDTLS_DEPRECATED_REMOVED */
 
@@ -573,7 +560,7 @@
  * \return              #MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED on reseed
  *                      failure.
  */
-int mbedtls_ctr_drbg_write_seed_file( mbedtls_ctr_drbg_context *ctx, const char *path );
+int mbedtls_ctr_drbg_write_seed_file(mbedtls_ctr_drbg_context *ctx, const char *path);
 
 /**
  * \brief               This function reads and updates a seed file. The seed
@@ -589,7 +576,7 @@
  * \return              #MBEDTLS_ERR_CTR_DRBG_INPUT_TOO_BIG if the existing
  *                      seed file is too large.
  */
-int mbedtls_ctr_drbg_update_seed_file( mbedtls_ctr_drbg_context *ctx, const char *path );
+int mbedtls_ctr_drbg_update_seed_file(mbedtls_ctr_drbg_context *ctx, const char *path);
 #endif /* MBEDTLS_FS_IO */
 
 #if defined(MBEDTLS_SELF_TEST)
@@ -600,7 +587,7 @@
  * \return              \c 0 on success.
  * \return              \c 1 on failure.
  */
-int mbedtls_ctr_drbg_self_test( int verbose );
+int mbedtls_ctr_drbg_self_test(int verbose);
 
 #endif /* MBEDTLS_SELF_TEST */
 
diff --git a/third_party/mbedtls/repo/include/mbedtls/debug.h b/third_party/mbedtls/repo/include/mbedtls/debug.h
index 3c08244..c29c40e 100644
--- a/third_party/mbedtls/repo/include/mbedtls/debug.h
+++ b/third_party/mbedtls/repo/include/mbedtls/debug.h
@@ -5,19 +5,7 @@
  */
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 #ifndef MBEDTLS_DEBUG_H
 #define MBEDTLS_DEBUG_H
@@ -36,47 +24,47 @@
 
 #if defined(MBEDTLS_DEBUG_C)
 
-#define MBEDTLS_DEBUG_STRIP_PARENS( ... )   __VA_ARGS__
+#define MBEDTLS_DEBUG_STRIP_PARENS(...)   __VA_ARGS__
 
-#define MBEDTLS_SSL_DEBUG_MSG( level, args )                    \
-    mbedtls_debug_print_msg( ssl, level, __FILE__, __LINE__,    \
-                             MBEDTLS_DEBUG_STRIP_PARENS args )
+#define MBEDTLS_SSL_DEBUG_MSG(level, args)                    \
+    mbedtls_debug_print_msg(ssl, level, __FILE__, __LINE__,    \
+                            MBEDTLS_DEBUG_STRIP_PARENS args)
 
-#define MBEDTLS_SSL_DEBUG_RET( level, text, ret )                \
-    mbedtls_debug_print_ret( ssl, level, __FILE__, __LINE__, text, ret )
+#define MBEDTLS_SSL_DEBUG_RET(level, text, ret)                \
+    mbedtls_debug_print_ret(ssl, level, __FILE__, __LINE__, text, ret)
 
-#define MBEDTLS_SSL_DEBUG_BUF( level, text, buf, len )           \
-    mbedtls_debug_print_buf( ssl, level, __FILE__, __LINE__, text, buf, len )
+#define MBEDTLS_SSL_DEBUG_BUF(level, text, buf, len)           \
+    mbedtls_debug_print_buf(ssl, level, __FILE__, __LINE__, text, buf, len)
 
 #if defined(MBEDTLS_BIGNUM_C)
-#define MBEDTLS_SSL_DEBUG_MPI( level, text, X )                  \
-    mbedtls_debug_print_mpi( ssl, level, __FILE__, __LINE__, text, X )
+#define MBEDTLS_SSL_DEBUG_MPI(level, text, X)                  \
+    mbedtls_debug_print_mpi(ssl, level, __FILE__, __LINE__, text, X)
 #endif
 
 #if defined(MBEDTLS_ECP_C)
-#define MBEDTLS_SSL_DEBUG_ECP( level, text, X )                  \
-    mbedtls_debug_print_ecp( ssl, level, __FILE__, __LINE__, text, X )
+#define MBEDTLS_SSL_DEBUG_ECP(level, text, X)                  \
+    mbedtls_debug_print_ecp(ssl, level, __FILE__, __LINE__, text, X)
 #endif
 
 #if defined(MBEDTLS_X509_CRT_PARSE_C)
-#define MBEDTLS_SSL_DEBUG_CRT( level, text, crt )                \
-    mbedtls_debug_print_crt( ssl, level, __FILE__, __LINE__, text, crt )
+#define MBEDTLS_SSL_DEBUG_CRT(level, text, crt)                \
+    mbedtls_debug_print_crt(ssl, level, __FILE__, __LINE__, text, crt)
 #endif
 
 #if defined(MBEDTLS_ECDH_C)
-#define MBEDTLS_SSL_DEBUG_ECDH( level, ecdh, attr )               \
-    mbedtls_debug_printf_ecdh( ssl, level, __FILE__, __LINE__, ecdh, attr )
+#define MBEDTLS_SSL_DEBUG_ECDH(level, ecdh, attr)               \
+    mbedtls_debug_printf_ecdh(ssl, level, __FILE__, __LINE__, ecdh, attr)
 #endif
 
 #else /* MBEDTLS_DEBUG_C */
 
-#define MBEDTLS_SSL_DEBUG_MSG( level, args )            do { } while( 0 )
-#define MBEDTLS_SSL_DEBUG_RET( level, text, ret )       do { } while( 0 )
-#define MBEDTLS_SSL_DEBUG_BUF( level, text, buf, len )  do { } while( 0 )
-#define MBEDTLS_SSL_DEBUG_MPI( level, text, X )         do { } while( 0 )
-#define MBEDTLS_SSL_DEBUG_ECP( level, text, X )         do { } while( 0 )
-#define MBEDTLS_SSL_DEBUG_CRT( level, text, crt )       do { } while( 0 )
-#define MBEDTLS_SSL_DEBUG_ECDH( level, ecdh, attr )     do { } while( 0 )
+#define MBEDTLS_SSL_DEBUG_MSG(level, args)            do { } while (0)
+#define MBEDTLS_SSL_DEBUG_RET(level, text, ret)       do { } while (0)
+#define MBEDTLS_SSL_DEBUG_BUF(level, text, buf, len)  do { } while (0)
+#define MBEDTLS_SSL_DEBUG_MPI(level, text, X)         do { } while (0)
+#define MBEDTLS_SSL_DEBUG_ECP(level, text, X)         do { } while (0)
+#define MBEDTLS_SSL_DEBUG_CRT(level, text, crt)       do { } while (0)
+#define MBEDTLS_SSL_DEBUG_ECDH(level, ecdh, attr)     do { } while (0)
 
 #endif /* MBEDTLS_DEBUG_C */
 
@@ -96,7 +84,7 @@
 #if __has_attribute(format)
 #if defined(__MINGW32__) && __USE_MINGW_ANSI_STDIO == 1
 #define MBEDTLS_PRINTF_ATTRIBUTE(string_index, first_to_check)    \
-    __attribute__((__format__ (gnu_printf, string_index, first_to_check)))
+    __attribute__((__format__(gnu_printf, string_index, first_to_check)))
 #else /* defined(__MINGW32__) && __USE_MINGW_ANSI_STDIO == 1 */
 #define MBEDTLS_PRINTF_ATTRIBUTE(string_index, first_to_check)    \
     __attribute__((format(printf, string_index, first_to_check)))
@@ -124,10 +112,12 @@
    #include <inttypes.h>
    #define MBEDTLS_PRINTF_SIZET     PRIuPTR
    #define MBEDTLS_PRINTF_LONGLONG  "I64d"
-#else /* (defined(__MINGW32__)  && __USE_MINGW_ANSI_STDIO == 0) || (defined(_MSC_VER) && _MSC_VER < 1800) */
+#else \
+    /* (defined(__MINGW32__)  && __USE_MINGW_ANSI_STDIO == 0) || (defined(_MSC_VER) && _MSC_VER < 1800) */
    #define MBEDTLS_PRINTF_SIZET     "zu"
    #define MBEDTLS_PRINTF_LONGLONG  "lld"
-#endif /* (defined(__MINGW32__)  && __USE_MINGW_ANSI_STDIO == 0) || (defined(_MSC_VER) && _MSC_VER < 1800) */
+#endif \
+    /* (defined(__MINGW32__)  && __USE_MINGW_ANSI_STDIO == 0) || (defined(_MSC_VER) && _MSC_VER < 1800) */
 
 #ifdef __cplusplus
 extern "C" {
@@ -139,7 +129,7 @@
  *          discarded.
  *          (Default value: 0 = No debug )
  *
- * \param threshold     theshold level of messages to filter on. Messages at a
+ * \param threshold     threshold level of messages to filter on. Messages at a
  *                      higher level will be discarded.
  *                          - Debug levels
  *                              - 0 No debug
@@ -148,7 +138,7 @@
  *                              - 3 Informational
  *                              - 4 Verbose
  */
-void mbedtls_debug_set_threshold( int threshold );
+void mbedtls_debug_set_threshold(int threshold);
 
 /**
  * \brief    Print a message to the debug output. This function is always used
@@ -165,9 +155,9 @@
  * \attention       This function is intended for INTERNAL usage within the
  *                  library only.
  */
-void mbedtls_debug_print_msg( const mbedtls_ssl_context *ssl, int level,
-                              const char *file, int line,
-                              const char *format, ... ) MBEDTLS_PRINTF_ATTRIBUTE(5, 6);
+void mbedtls_debug_print_msg(const mbedtls_ssl_context *ssl, int level,
+                             const char *file, int line,
+                             const char *format, ...) MBEDTLS_PRINTF_ATTRIBUTE(5, 6);
 
 /**
  * \brief   Print the return value of a function to the debug output. This
@@ -184,9 +174,9 @@
  * \attention       This function is intended for INTERNAL usage within the
  *                  library only.
  */
-void mbedtls_debug_print_ret( const mbedtls_ssl_context *ssl, int level,
-                      const char *file, int line,
-                      const char *text, int ret );
+void mbedtls_debug_print_ret(const mbedtls_ssl_context *ssl, int level,
+                             const char *file, int line,
+                             const char *text, int ret);
 
 /**
  * \brief   Output a buffer of size len bytes to the debug output. This function
@@ -205,9 +195,9 @@
  * \attention       This function is intended for INTERNAL usage within the
  *                  library only.
  */
-void mbedtls_debug_print_buf( const mbedtls_ssl_context *ssl, int level,
-                      const char *file, int line, const char *text,
-                      const unsigned char *buf, size_t len );
+void mbedtls_debug_print_buf(const mbedtls_ssl_context *ssl, int level,
+                             const char *file, int line, const char *text,
+                             const unsigned char *buf, size_t len);
 
 #if defined(MBEDTLS_BIGNUM_C)
 /**
@@ -226,9 +216,9 @@
  * \attention       This function is intended for INTERNAL usage within the
  *                  library only.
  */
-void mbedtls_debug_print_mpi( const mbedtls_ssl_context *ssl, int level,
-                      const char *file, int line,
-                      const char *text, const mbedtls_mpi *X );
+void mbedtls_debug_print_mpi(const mbedtls_ssl_context *ssl, int level,
+                             const char *file, int line,
+                             const char *text, const mbedtls_mpi *X);
 #endif
 
 #if defined(MBEDTLS_ECP_C)
@@ -248,9 +238,9 @@
  * \attention       This function is intended for INTERNAL usage within the
  *                  library only.
  */
-void mbedtls_debug_print_ecp( const mbedtls_ssl_context *ssl, int level,
-                      const char *file, int line,
-                      const char *text, const mbedtls_ecp_point *X );
+void mbedtls_debug_print_ecp(const mbedtls_ssl_context *ssl, int level,
+                             const char *file, int line,
+                             const char *text, const mbedtls_ecp_point *X);
 #endif
 
 #if defined(MBEDTLS_X509_CRT_PARSE_C)
@@ -269,14 +259,13 @@
  * \attention       This function is intended for INTERNAL usage within the
  *                  library only.
  */
-void mbedtls_debug_print_crt( const mbedtls_ssl_context *ssl, int level,
-                      const char *file, int line,
-                      const char *text, const mbedtls_x509_crt *crt );
+void mbedtls_debug_print_crt(const mbedtls_ssl_context *ssl, int level,
+                             const char *file, int line,
+                             const char *text, const mbedtls_x509_crt *crt);
 #endif
 
 #if defined(MBEDTLS_ECDH_C)
-typedef enum
-{
+typedef enum {
     MBEDTLS_DEBUG_ECDH_Q,
     MBEDTLS_DEBUG_ECDH_QP,
     MBEDTLS_DEBUG_ECDH_Z,
@@ -298,10 +287,10 @@
  * \attention       This function is intended for INTERNAL usage within the
  *                  library only.
  */
-void mbedtls_debug_printf_ecdh( const mbedtls_ssl_context *ssl, int level,
-                                const char *file, int line,
-                                const mbedtls_ecdh_context *ecdh,
-                                mbedtls_debug_ecdh_attr attr );
+void mbedtls_debug_printf_ecdh(const mbedtls_ssl_context *ssl, int level,
+                               const char *file, int line,
+                               const mbedtls_ecdh_context *ecdh,
+                               mbedtls_debug_ecdh_attr attr);
 #endif
 
 #ifdef __cplusplus
diff --git a/third_party/mbedtls/repo/include/mbedtls/des.h b/third_party/mbedtls/repo/include/mbedtls/des.h
index 325aab5..031b9cf 100644
--- a/third_party/mbedtls/repo/include/mbedtls/des.h
+++ b/third_party/mbedtls/repo/include/mbedtls/des.h
@@ -3,25 +3,13 @@
  *
  * \brief DES block cipher
  *
- * \warning   DES is considered a weak cipher and its use constitutes a
+ * \warning   DES/3DES are considered weak ciphers and their use constitutes a
  *            security risk. We recommend considering stronger ciphers
  *            instead.
  */
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  *
  */
 #ifndef MBEDTLS_DES_H
@@ -60,21 +48,23 @@
 /**
  * \brief          DES context structure
  *
- * \warning        DES is considered a weak cipher and its use constitutes a
+ * \warning        DES/3DES are considered weak ciphers and their use constitutes a
  *                 security risk. We recommend considering stronger ciphers
  *                 instead.
  */
-typedef struct mbedtls_des_context
-{
+typedef struct mbedtls_des_context {
     uint32_t sk[32];            /*!<  DES subkeys       */
 }
 mbedtls_des_context;
 
 /**
  * \brief          Triple-DES context structure
+ *
+ * \warning        DES/3DES are considered weak ciphers and their use constitutes a
+ *                 security risk. We recommend considering stronger ciphers
+ *                 instead.
  */
-typedef struct mbedtls_des3_context
-{
+typedef struct mbedtls_des3_context {
     uint32_t sk[96];            /*!<  3DES subkeys      */
 }
 mbedtls_des3_context;
@@ -88,36 +78,44 @@
  *
  * \param ctx      DES context to be initialized
  *
- * \warning        DES is considered a weak cipher and its use constitutes a
+ * \warning        DES/3DES are considered weak ciphers and their use constitutes a
  *                 security risk. We recommend considering stronger ciphers
  *                 instead.
  */
-void mbedtls_des_init( mbedtls_des_context *ctx );
+void mbedtls_des_init(mbedtls_des_context *ctx);
 
 /**
  * \brief          Clear DES context
  *
  * \param ctx      DES context to be cleared
  *
- * \warning        DES is considered a weak cipher and its use constitutes a
+ * \warning        DES/3DES are considered weak ciphers and their use constitutes a
  *                 security risk. We recommend considering stronger ciphers
  *                 instead.
  */
-void mbedtls_des_free( mbedtls_des_context *ctx );
+void mbedtls_des_free(mbedtls_des_context *ctx);
 
 /**
  * \brief          Initialize Triple-DES context
  *
  * \param ctx      DES3 context to be initialized
+ *
+ * \warning        DES/3DES are considered weak ciphers and their use constitutes a
+ *                 security risk. We recommend considering stronger ciphers
+ *                 instead.
  */
-void mbedtls_des3_init( mbedtls_des3_context *ctx );
+void mbedtls_des3_init(mbedtls_des3_context *ctx);
 
 /**
  * \brief          Clear Triple-DES context
  *
  * \param ctx      DES3 context to be cleared
+ *
+ * \warning        DES/3DES are considered weak ciphers and their use constitutes a
+ *                 security risk. We recommend considering stronger ciphers
+ *                 instead.
  */
-void mbedtls_des3_free( mbedtls_des3_context *ctx );
+void mbedtls_des3_free(mbedtls_des3_context *ctx);
 
 /**
  * \brief          Set key parity on the given key to odd.
@@ -127,11 +125,11 @@
  *
  * \param key      8-byte secret key
  *
- * \warning        DES is considered a weak cipher and its use constitutes a
+ * \warning        DES/3DES are considered weak ciphers and their use constitutes a
  *                 security risk. We recommend considering stronger ciphers
  *                 instead.
  */
-void mbedtls_des_key_set_parity( unsigned char key[MBEDTLS_DES_KEY_SIZE] );
+void mbedtls_des_key_set_parity(unsigned char key[MBEDTLS_DES_KEY_SIZE]);
 
 /**
  * \brief          Check that key parity on the given key is odd.
@@ -143,12 +141,12 @@
  *
  * \return         0 is parity was ok, 1 if parity was not correct.
  *
- * \warning        DES is considered a weak cipher and its use constitutes a
+ * \warning        DES/3DES are considered weak ciphers and their use constitutes a
  *                 security risk. We recommend considering stronger ciphers
  *                 instead.
  */
 MBEDTLS_CHECK_RETURN_TYPICAL
-int mbedtls_des_key_check_key_parity( const unsigned char key[MBEDTLS_DES_KEY_SIZE] );
+int mbedtls_des_key_check_key_parity(const unsigned char key[MBEDTLS_DES_KEY_SIZE]);
 
 /**
  * \brief          Check that key is not a weak or semi-weak DES key
@@ -157,12 +155,12 @@
  *
  * \return         0 if no weak key was found, 1 if a weak key was identified.
  *
- * \warning        DES is considered a weak cipher and its use constitutes a
+ * \warning        DES/3DES are considered weak ciphers and their use constitutes a
  *                 security risk. We recommend considering stronger ciphers
  *                 instead.
  */
 MBEDTLS_CHECK_RETURN_TYPICAL
-int mbedtls_des_key_check_weak( const unsigned char key[MBEDTLS_DES_KEY_SIZE] );
+int mbedtls_des_key_check_weak(const unsigned char key[MBEDTLS_DES_KEY_SIZE]);
 
 /**
  * \brief          DES key schedule (56-bit, encryption)
@@ -172,12 +170,12 @@
  *
  * \return         0
  *
- * \warning        DES is considered a weak cipher and its use constitutes a
+ * \warning        DES/3DES are considered weak ciphers and their use constitutes a
  *                 security risk. We recommend considering stronger ciphers
  *                 instead.
  */
 MBEDTLS_CHECK_RETURN_TYPICAL
-int mbedtls_des_setkey_enc( mbedtls_des_context *ctx, const unsigned char key[MBEDTLS_DES_KEY_SIZE] );
+int mbedtls_des_setkey_enc(mbedtls_des_context *ctx, const unsigned char key[MBEDTLS_DES_KEY_SIZE]);
 
 /**
  * \brief          DES key schedule (56-bit, decryption)
@@ -187,12 +185,12 @@
  *
  * \return         0
  *
- * \warning        DES is considered a weak cipher and its use constitutes a
+ * \warning        DES/3DES are considered weak ciphers and their use constitutes a
  *                 security risk. We recommend considering stronger ciphers
  *                 instead.
  */
 MBEDTLS_CHECK_RETURN_TYPICAL
-int mbedtls_des_setkey_dec( mbedtls_des_context *ctx, const unsigned char key[MBEDTLS_DES_KEY_SIZE] );
+int mbedtls_des_setkey_dec(mbedtls_des_context *ctx, const unsigned char key[MBEDTLS_DES_KEY_SIZE]);
 
 /**
  * \brief          Triple-DES key schedule (112-bit, encryption)
@@ -201,10 +199,14 @@
  * \param key      16-byte secret key
  *
  * \return         0
+ *
+ * \warning        DES/3DES are considered weak ciphers and their use constitutes a
+ *                 security risk. We recommend considering stronger ciphers
+ *                 instead.
  */
 MBEDTLS_CHECK_RETURN_TYPICAL
-int mbedtls_des3_set2key_enc( mbedtls_des3_context *ctx,
-                      const unsigned char key[MBEDTLS_DES_KEY_SIZE * 2] );
+int mbedtls_des3_set2key_enc(mbedtls_des3_context *ctx,
+                             const unsigned char key[MBEDTLS_DES_KEY_SIZE * 2]);
 
 /**
  * \brief          Triple-DES key schedule (112-bit, decryption)
@@ -213,10 +215,14 @@
  * \param key      16-byte secret key
  *
  * \return         0
+ *
+ * \warning        DES/3DES are considered weak ciphers and their use constitutes a
+ *                 security risk. We recommend considering stronger ciphers
+ *                 instead.
  */
 MBEDTLS_CHECK_RETURN_TYPICAL
-int mbedtls_des3_set2key_dec( mbedtls_des3_context *ctx,
-                      const unsigned char key[MBEDTLS_DES_KEY_SIZE * 2] );
+int mbedtls_des3_set2key_dec(mbedtls_des3_context *ctx,
+                             const unsigned char key[MBEDTLS_DES_KEY_SIZE * 2]);
 
 /**
  * \brief          Triple-DES key schedule (168-bit, encryption)
@@ -225,10 +231,14 @@
  * \param key      24-byte secret key
  *
  * \return         0
+ *
+ * \warning        DES/3DES are considered weak ciphers and their use constitutes a
+ *                 security risk. We recommend considering stronger ciphers
+ *                 instead.
  */
 MBEDTLS_CHECK_RETURN_TYPICAL
-int mbedtls_des3_set3key_enc( mbedtls_des3_context *ctx,
-                      const unsigned char key[MBEDTLS_DES_KEY_SIZE * 3] );
+int mbedtls_des3_set3key_enc(mbedtls_des3_context *ctx,
+                             const unsigned char key[MBEDTLS_DES_KEY_SIZE * 3]);
 
 /**
  * \brief          Triple-DES key schedule (168-bit, decryption)
@@ -237,10 +247,14 @@
  * \param key      24-byte secret key
  *
  * \return         0
+ *
+ * \warning        DES/3DES are considered weak ciphers and their use constitutes a
+ *                 security risk. We recommend considering stronger ciphers
+ *                 instead.
  */
 MBEDTLS_CHECK_RETURN_TYPICAL
-int mbedtls_des3_set3key_dec( mbedtls_des3_context *ctx,
-                      const unsigned char key[MBEDTLS_DES_KEY_SIZE * 3] );
+int mbedtls_des3_set3key_dec(mbedtls_des3_context *ctx,
+                             const unsigned char key[MBEDTLS_DES_KEY_SIZE * 3]);
 
 /**
  * \brief          DES-ECB block encryption/decryption
@@ -251,14 +265,14 @@
  *
  * \return         0 if successful
  *
- * \warning        DES is considered a weak cipher and its use constitutes a
+ * \warning        DES/3DES are considered weak ciphers and their use constitutes a
  *                 security risk. We recommend considering stronger ciphers
  *                 instead.
  */
 MBEDTLS_CHECK_RETURN_TYPICAL
-int mbedtls_des_crypt_ecb( mbedtls_des_context *ctx,
-                    const unsigned char input[8],
-                    unsigned char output[8] );
+int mbedtls_des_crypt_ecb(mbedtls_des_context *ctx,
+                          const unsigned char input[8],
+                          unsigned char output[8]);
 
 #if defined(MBEDTLS_CIPHER_MODE_CBC)
 /**
@@ -279,17 +293,17 @@
  * \param input    buffer holding the input data
  * \param output   buffer holding the output data
  *
- * \warning        DES is considered a weak cipher and its use constitutes a
+ * \warning        DES/3DES are considered weak ciphers and their use constitutes a
  *                 security risk. We recommend considering stronger ciphers
  *                 instead.
  */
 MBEDTLS_CHECK_RETURN_TYPICAL
-int mbedtls_des_crypt_cbc( mbedtls_des_context *ctx,
-                    int mode,
-                    size_t length,
-                    unsigned char iv[8],
-                    const unsigned char *input,
-                    unsigned char *output );
+int mbedtls_des_crypt_cbc(mbedtls_des_context *ctx,
+                          int mode,
+                          size_t length,
+                          unsigned char iv[8],
+                          const unsigned char *input,
+                          unsigned char *output);
 #endif /* MBEDTLS_CIPHER_MODE_CBC */
 
 /**
@@ -300,11 +314,15 @@
  * \param output   64-bit output block
  *
  * \return         0 if successful
+ *
+ * \warning        DES/3DES are considered weak ciphers and their use constitutes a
+ *                 security risk. We recommend considering stronger ciphers
+ *                 instead.
  */
 MBEDTLS_CHECK_RETURN_TYPICAL
-int mbedtls_des3_crypt_ecb( mbedtls_des3_context *ctx,
-                     const unsigned char input[8],
-                     unsigned char output[8] );
+int mbedtls_des3_crypt_ecb(mbedtls_des3_context *ctx,
+                           const unsigned char input[8],
+                           unsigned char output[8]);
 
 #if defined(MBEDTLS_CIPHER_MODE_CBC)
 /**
@@ -326,14 +344,18 @@
  * \param output   buffer holding the output data
  *
  * \return         0 if successful, or MBEDTLS_ERR_DES_INVALID_INPUT_LENGTH
+ *
+ * \warning        DES/3DES are considered weak ciphers and their use constitutes a
+ *                 security risk. We recommend considering stronger ciphers
+ *                 instead.
  */
 MBEDTLS_CHECK_RETURN_TYPICAL
-int mbedtls_des3_crypt_cbc( mbedtls_des3_context *ctx,
-                     int mode,
-                     size_t length,
-                     unsigned char iv[8],
-                     const unsigned char *input,
-                     unsigned char *output );
+int mbedtls_des3_crypt_cbc(mbedtls_des3_context *ctx,
+                           int mode,
+                           size_t length,
+                           unsigned char iv[8],
+                           const unsigned char *input,
+                           unsigned char *output);
 #endif /* MBEDTLS_CIPHER_MODE_CBC */
 
 /**
@@ -344,12 +366,12 @@
  * \param SK       Round keys
  * \param key      Base key
  *
- * \warning        DES is considered a weak cipher and its use constitutes a
+ * \warning        DES/3DES are considered weak ciphers and their use constitutes a
  *                 security risk. We recommend considering stronger ciphers
  *                 instead.
  */
-void mbedtls_des_setkey( uint32_t SK[32],
-                         const unsigned char key[MBEDTLS_DES_KEY_SIZE] );
+void mbedtls_des_setkey(uint32_t SK[32],
+                        const unsigned char key[MBEDTLS_DES_KEY_SIZE]);
 
 #if defined(MBEDTLS_SELF_TEST)
 
@@ -359,7 +381,7 @@
  * \return         0 if successful, or 1 if the test failed
  */
 MBEDTLS_CHECK_RETURN_CRITICAL
-int mbedtls_des_self_test( int verbose );
+int mbedtls_des_self_test(int verbose);
 
 #endif /* MBEDTLS_SELF_TEST */
 
diff --git a/third_party/mbedtls/repo/include/mbedtls/dhm.h b/third_party/mbedtls/repo/include/mbedtls/dhm.h
index c4b15a2..b61e4d4 100644
--- a/third_party/mbedtls/repo/include/mbedtls/dhm.h
+++ b/third_party/mbedtls/repo/include/mbedtls/dhm.h
@@ -45,19 +45,7 @@
  */
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 
 #ifndef MBEDTLS_DHM_H
@@ -108,8 +96,7 @@
 /**
  * \brief          The DHM context structure.
  */
-typedef struct mbedtls_dhm_context
-{
+typedef struct mbedtls_dhm_context {
     size_t len;         /*!<  The size of \p P in Bytes. */
     mbedtls_mpi P;      /*!<  The prime modulus. */
     mbedtls_mpi G;      /*!<  The generator. */
@@ -133,7 +120,7 @@
  *
  * \param ctx      The DHM context to initialize.
  */
-void mbedtls_dhm_init( mbedtls_dhm_context *ctx );
+void mbedtls_dhm_init(mbedtls_dhm_context *ctx);
 
 /**
  * \brief          This function parses the DHM parameters in a
@@ -157,9 +144,9 @@
  * \return         \c 0 on success.
  * \return         An \c MBEDTLS_ERR_DHM_XXX error code on failure.
  */
-int mbedtls_dhm_read_params( mbedtls_dhm_context *ctx,
-                             unsigned char **p,
-                             const unsigned char *end );
+int mbedtls_dhm_read_params(mbedtls_dhm_context *ctx,
+                            unsigned char **p,
+                            const unsigned char *end);
 
 /**
  * \brief          This function generates a DHM key pair and exports its
@@ -193,10 +180,10 @@
  * \return         \c 0 on success.
  * \return         An \c MBEDTLS_ERR_DHM_XXX error code on failure.
  */
-int mbedtls_dhm_make_params( mbedtls_dhm_context *ctx, int x_size,
-                     unsigned char *output, size_t *olen,
-                     int (*f_rng)(void *, unsigned char *, size_t),
-                     void *p_rng );
+int mbedtls_dhm_make_params(mbedtls_dhm_context *ctx, int x_size,
+                            unsigned char *output, size_t *olen,
+                            int (*f_rng)(void *, unsigned char *, size_t),
+                            void *p_rng);
 
 /**
  * \brief          This function sets the prime modulus and generator.
@@ -213,9 +200,9 @@
  * \return         \c 0 if successful.
  * \return         An \c MBEDTLS_ERR_DHM_XXX error code on failure.
  */
-int mbedtls_dhm_set_group( mbedtls_dhm_context *ctx,
-                           const mbedtls_mpi *P,
-                           const mbedtls_mpi *G );
+int mbedtls_dhm_set_group(mbedtls_dhm_context *ctx,
+                          const mbedtls_mpi *P,
+                          const mbedtls_mpi *G);
 
 /**
  * \brief          This function imports the raw public value of the peer.
@@ -233,8 +220,8 @@
  * \return         \c 0 on success.
  * \return         An \c MBEDTLS_ERR_DHM_XXX error code on failure.
  */
-int mbedtls_dhm_read_public( mbedtls_dhm_context *ctx,
-                     const unsigned char *input, size_t ilen );
+int mbedtls_dhm_read_public(mbedtls_dhm_context *ctx,
+                            const unsigned char *input, size_t ilen);
 
 /**
  * \brief          This function creates a DHM key pair and exports
@@ -260,10 +247,10 @@
  * \return         \c 0 on success.
  * \return         An \c MBEDTLS_ERR_DHM_XXX error code on failure.
  */
-int mbedtls_dhm_make_public( mbedtls_dhm_context *ctx, int x_size,
-                     unsigned char *output, size_t olen,
-                     int (*f_rng)(void *, unsigned char *, size_t),
-                     void *p_rng );
+int mbedtls_dhm_make_public(mbedtls_dhm_context *ctx, int x_size,
+                            unsigned char *output, size_t olen,
+                            int (*f_rng)(void *, unsigned char *, size_t),
+                            void *p_rng);
 
 /**
  * \brief          This function derives and exports the shared secret
@@ -291,10 +278,10 @@
  * \return              \c 0 on success.
  * \return              An \c MBEDTLS_ERR_DHM_XXX error code on failure.
  */
-int mbedtls_dhm_calc_secret( mbedtls_dhm_context *ctx,
-                     unsigned char *output, size_t output_size, size_t *olen,
-                     int (*f_rng)(void *, unsigned char *, size_t),
-                     void *p_rng );
+int mbedtls_dhm_calc_secret(mbedtls_dhm_context *ctx,
+                            unsigned char *output, size_t output_size, size_t *olen,
+                            int (*f_rng)(void *, unsigned char *, size_t),
+                            void *p_rng);
 
 /**
  * \brief          This function frees and clears the components
@@ -304,7 +291,7 @@
  *                 in which case this function is a no-op. If it is not \c NULL,
  *                 it must point to an initialized DHM context.
  */
-void mbedtls_dhm_free( mbedtls_dhm_context *ctx );
+void mbedtls_dhm_free(mbedtls_dhm_context *ctx);
 
 #if defined(MBEDTLS_ASN1_PARSE_C)
 /**
@@ -321,8 +308,8 @@
  * \return            An \c MBEDTLS_ERR_DHM_XXX or \c MBEDTLS_ERR_PEM_XXX error
  *                    code on failure.
  */
-int mbedtls_dhm_parse_dhm( mbedtls_dhm_context *dhm, const unsigned char *dhmin,
-                           size_t dhminlen );
+int mbedtls_dhm_parse_dhm(mbedtls_dhm_context *dhm, const unsigned char *dhmin,
+                          size_t dhminlen);
 
 #if defined(MBEDTLS_FS_IO)
 /**
@@ -337,7 +324,7 @@
  * \return         An \c MBEDTLS_ERR_DHM_XXX or \c MBEDTLS_ERR_PEM_XXX
  *                 error code on failure.
  */
-int mbedtls_dhm_parse_dhmfile( mbedtls_dhm_context *dhm, const char *path );
+int mbedtls_dhm_parse_dhmfile(mbedtls_dhm_context *dhm, const char *path);
 #endif /* MBEDTLS_FS_IO */
 #endif /* MBEDTLS_ASN1_PARSE_C */
 
@@ -349,7 +336,7 @@
  * \return         \c 0 on success.
  * \return         \c 1 on failure.
  */
-int mbedtls_dhm_self_test( int verbose );
+int mbedtls_dhm_self_test(int verbose);
 
 #endif /* MBEDTLS_SELF_TEST */
 #ifdef __cplusplus
@@ -426,7 +413,7 @@
         "CDF93ACC44328387315D75E198C641A480CD86A1B9E587E8"      \
         "BE60E69CC928B2B9C52172E413042E9B23F10B0E16E79763"      \
         "C9B53DCF4BA80A29E3FB73C16B8E75B97EF363E2FFA31F71"      \
-        "CF9DE5384E71B81C0AC4DFFE0C10E64F" )
+        "CF9DE5384E71B81C0AC4DFFE0C10E64F")
 
 /**
  * The hexadecimal presentation of the chosen generator of the 2048-bit MODP
@@ -445,7 +432,7 @@
         "10E183EDD19963DDD9E263E4770589EF6AA21E7F5F2FF381"      \
         "B539CCE3409D13CD566AFBB48D6C019181E1BCFE94B30269"      \
         "EDFE72FE9B6AA4BD7B5A0F1C71CFFF4C19C418E1F6EC0179"      \
-        "81BC087F2A7065B384B890D3191F2BFA" )
+        "81BC087F2A7065B384B890D3191F2BFA")
 
 /**
  * The hexadecimal presentation of the prime underlying the 2048-bit MODP
@@ -470,7 +457,7 @@
         "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B"      \
         "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9"      \
         "DE2BCBF6955817183995497CEA956AE515D2261898FA0510"      \
-        "15728E5A8AACAA68FFFFFFFFFFFFFFFF" )
+        "15728E5A8AACAA68FFFFFFFFFFFFFFFF")
 
 /**
  * The hexadecimal presentation of the chosen generator of the 2048-bit MODP
@@ -478,7 +465,7 @@
  * Diffie-Hellman groups for Internet Key Exchange (IKE)</em>.
  */
 #define MBEDTLS_DHM_RFC3526_MODP_2048_G                         \
-    MBEDTLS_DEPRECATED_STRING_CONSTANT( "02" )
+    MBEDTLS_DEPRECATED_STRING_CONSTANT("02")
 
 /**
  * The hexadecimal presentation of the prime underlying the 3072-bit MODP
@@ -502,7 +489,7 @@
         "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B"      \
         "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C"      \
         "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31"      \
-        "43DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF" )
+        "43DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF")
 
 /**
  * The hexadecimal presentation of the chosen generator of the 3072-bit MODP
@@ -510,7 +497,7 @@
  * Diffie-Hellman groups for Internet Key Exchange (IKE)</em>.
  */
 #define MBEDTLS_DHM_RFC3526_MODP_3072_G                      \
-    MBEDTLS_DEPRECATED_STRING_CONSTANT( "02" )
+    MBEDTLS_DEPRECATED_STRING_CONSTANT("02")
 
 /**
  * The hexadecimal presentation of the prime underlying the 4096-bit MODP
@@ -540,7 +527,7 @@
         "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED"   \
         "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9"   \
         "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199"   \
-        "FFFFFFFFFFFFFFFF" )
+        "FFFFFFFFFFFFFFFF")
 
 /**
  * The hexadecimal presentation of the chosen generator of the 4096-bit MODP
@@ -548,7 +535,7 @@
  * Diffie-Hellman groups for Internet Key Exchange (IKE)</em>.
  */
 #define MBEDTLS_DHM_RFC3526_MODP_4096_G                      \
-    MBEDTLS_DEPRECATED_STRING_CONSTANT( "02" )
+    MBEDTLS_DEPRECATED_STRING_CONSTANT("02")
 
 #endif /* MBEDTLS_DEPRECATED_REMOVED */
 
@@ -557,546 +544,546 @@
  */
 
 #define MBEDTLS_DHM_RFC3526_MODP_2048_P_BIN {        \
-     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \
-     0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, \
-     0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, \
-     0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, \
-     0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, \
-     0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, \
-     0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, \
-     0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, \
-     0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, \
-     0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, \
-     0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, \
-     0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, \
-     0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, \
-     0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, \
-     0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, \
-     0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, \
-     0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, \
-     0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, \
-     0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, \
-     0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, \
-     0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, \
-     0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, \
-     0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, \
-     0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, \
-     0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, \
-     0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, \
-     0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, \
-     0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, \
-     0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, \
-     0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, \
-     0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, \
-     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \
+        0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, \
+        0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, \
+        0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, \
+        0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, \
+        0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, \
+        0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, \
+        0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, \
+        0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, \
+        0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, \
+        0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, \
+        0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, \
+        0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, \
+        0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, \
+        0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, \
+        0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, \
+        0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, \
+        0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, \
+        0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, \
+        0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, \
+        0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, \
+        0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, \
+        0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, \
+        0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, \
+        0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, \
+        0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, \
+        0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, \
+        0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, \
+        0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, \
+        0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, \
+        0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, \
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }
 
 #define MBEDTLS_DHM_RFC3526_MODP_2048_G_BIN { 0x02 }
 
 #define MBEDTLS_DHM_RFC3526_MODP_3072_P_BIN {       \
-    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \
-    0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, \
-    0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, \
-    0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, \
-    0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, \
-    0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, \
-    0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, \
-    0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, \
-    0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, \
-    0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, \
-    0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, \
-    0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, \
-    0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, \
-    0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, \
-    0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, \
-    0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, \
-    0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, \
-    0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, \
-    0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, \
-    0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, \
-    0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, \
-    0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, \
-    0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, \
-    0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, \
-    0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, \
-    0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, \
-    0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, \
-    0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, \
-    0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, \
-    0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, \
-    0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D, \
-    0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33, \
-    0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64, \
-    0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A, \
-    0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D, \
-    0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7, \
-    0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7, \
-    0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D, \
-    0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B, \
-    0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64, \
-    0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64, \
-    0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C, \
-    0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C, \
-    0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2, \
-    0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31, \
-    0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E, \
-    0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x3A, 0xD2, 0xCA, \
-    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \
+        0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, \
+        0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, \
+        0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, \
+        0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, \
+        0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, \
+        0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, \
+        0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, \
+        0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, \
+        0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, \
+        0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, \
+        0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, \
+        0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, \
+        0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, \
+        0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, \
+        0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, \
+        0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, \
+        0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, \
+        0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, \
+        0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, \
+        0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, \
+        0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, \
+        0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, \
+        0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, \
+        0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, \
+        0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, \
+        0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, \
+        0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, \
+        0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, \
+        0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, \
+        0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D, \
+        0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33, \
+        0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64, \
+        0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A, \
+        0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D, \
+        0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7, \
+        0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7, \
+        0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D, \
+        0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B, \
+        0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64, \
+        0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64, \
+        0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C, \
+        0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C, \
+        0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2, \
+        0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31, \
+        0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E, \
+        0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x3A, 0xD2, 0xCA, \
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }
 
 #define MBEDTLS_DHM_RFC3526_MODP_3072_G_BIN { 0x02 }
 
 #define MBEDTLS_DHM_RFC3526_MODP_4096_P_BIN  {       \
-    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,  \
-    0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,  \
-    0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,  \
-    0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,  \
-    0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,  \
-    0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,  \
-    0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,  \
-    0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,  \
-    0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,  \
-    0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,  \
-    0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,  \
-    0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,  \
-    0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,  \
-    0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,  \
-    0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,  \
-    0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,  \
-    0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,  \
-    0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,  \
-    0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,  \
-    0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,  \
-    0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,  \
-    0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,  \
-    0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,  \
-    0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,  \
-    0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,  \
-    0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,  \
-    0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,  \
-    0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,  \
-    0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,  \
-    0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,  \
-    0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D,  \
-    0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33,  \
-    0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64,  \
-    0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A,  \
-    0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D,  \
-    0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7,  \
-    0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7,  \
-    0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D,  \
-    0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B,  \
-    0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64,  \
-    0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64,  \
-    0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C,  \
-    0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C,  \
-    0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2,  \
-    0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31,  \
-    0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E,  \
-    0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x21, 0x08, 0x01,  \
-    0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7,  \
-    0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26,  \
-    0x99, 0xC3, 0x27, 0x18, 0x6A, 0xF4, 0xE2, 0x3C,  \
-    0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA,  \
-    0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8,  \
-    0xDB, 0xBB, 0xC2, 0xDB, 0x04, 0xDE, 0x8E, 0xF9,  \
-    0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6,  \
-    0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D,  \
-    0x99, 0xB2, 0x96, 0x4F, 0xA0, 0x90, 0xC3, 0xA2,  \
-    0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED,  \
-    0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF,  \
-    0xB8, 0x1B, 0xDD, 0x76, 0x21, 0x70, 0x48, 0x1C,  \
-    0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9,  \
-    0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1,  \
-    0x86, 0xFF, 0xB7, 0xDC, 0x90, 0xA6, 0xC0, 0x8F,  \
-    0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x06, 0x31, 0x99,  \
-    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,  \
+        0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,  \
+        0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,  \
+        0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,  \
+        0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,  \
+        0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,  \
+        0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,  \
+        0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,  \
+        0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,  \
+        0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,  \
+        0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,  \
+        0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,  \
+        0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,  \
+        0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,  \
+        0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,  \
+        0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,  \
+        0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,  \
+        0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,  \
+        0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,  \
+        0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,  \
+        0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,  \
+        0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,  \
+        0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,  \
+        0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,  \
+        0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,  \
+        0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,  \
+        0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,  \
+        0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,  \
+        0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,  \
+        0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,  \
+        0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D,  \
+        0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33,  \
+        0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64,  \
+        0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A,  \
+        0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D,  \
+        0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7,  \
+        0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7,  \
+        0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D,  \
+        0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B,  \
+        0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64,  \
+        0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64,  \
+        0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C,  \
+        0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C,  \
+        0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2,  \
+        0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31,  \
+        0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E,  \
+        0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x21, 0x08, 0x01,  \
+        0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7,  \
+        0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26,  \
+        0x99, 0xC3, 0x27, 0x18, 0x6A, 0xF4, 0xE2, 0x3C,  \
+        0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA,  \
+        0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8,  \
+        0xDB, 0xBB, 0xC2, 0xDB, 0x04, 0xDE, 0x8E, 0xF9,  \
+        0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6,  \
+        0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D,  \
+        0x99, 0xB2, 0x96, 0x4F, 0xA0, 0x90, 0xC3, 0xA2,  \
+        0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED,  \
+        0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF,  \
+        0xB8, 0x1B, 0xDD, 0x76, 0x21, 0x70, 0x48, 0x1C,  \
+        0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9,  \
+        0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1,  \
+        0x86, 0xFF, 0xB7, 0xDC, 0x90, 0xA6, 0xC0, 0x8F,  \
+        0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x06, 0x31, 0x99,  \
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }
 
 #define MBEDTLS_DHM_RFC3526_MODP_4096_G_BIN { 0x02 }
 
 #define MBEDTLS_DHM_RFC7919_FFDHE2048_P_BIN {        \
-     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \
-     0xAD, 0xF8, 0x54, 0x58, 0xA2, 0xBB, 0x4A, 0x9A, \
-     0xAF, 0xDC, 0x56, 0x20, 0x27, 0x3D, 0x3C, 0xF1, \
-     0xD8, 0xB9, 0xC5, 0x83, 0xCE, 0x2D, 0x36, 0x95, \
-     0xA9, 0xE1, 0x36, 0x41, 0x14, 0x64, 0x33, 0xFB, \
-     0xCC, 0x93, 0x9D, 0xCE, 0x24, 0x9B, 0x3E, 0xF9, \
-     0x7D, 0x2F, 0xE3, 0x63, 0x63, 0x0C, 0x75, 0xD8, \
-     0xF6, 0x81, 0xB2, 0x02, 0xAE, 0xC4, 0x61, 0x7A, \
-     0xD3, 0xDF, 0x1E, 0xD5, 0xD5, 0xFD, 0x65, 0x61, \
-     0x24, 0x33, 0xF5, 0x1F, 0x5F, 0x06, 0x6E, 0xD0, \
-     0x85, 0x63, 0x65, 0x55, 0x3D, 0xED, 0x1A, 0xF3, \
-     0xB5, 0x57, 0x13, 0x5E, 0x7F, 0x57, 0xC9, 0x35, \
-     0x98, 0x4F, 0x0C, 0x70, 0xE0, 0xE6, 0x8B, 0x77, \
-     0xE2, 0xA6, 0x89, 0xDA, 0xF3, 0xEF, 0xE8, 0x72, \
-     0x1D, 0xF1, 0x58, 0xA1, 0x36, 0xAD, 0xE7, 0x35, \
-     0x30, 0xAC, 0xCA, 0x4F, 0x48, 0x3A, 0x79, 0x7A, \
-     0xBC, 0x0A, 0xB1, 0x82, 0xB3, 0x24, 0xFB, 0x61, \
-     0xD1, 0x08, 0xA9, 0x4B, 0xB2, 0xC8, 0xE3, 0xFB, \
-     0xB9, 0x6A, 0xDA, 0xB7, 0x60, 0xD7, 0xF4, 0x68, \
-     0x1D, 0x4F, 0x42, 0xA3, 0xDE, 0x39, 0x4D, 0xF4, \
-     0xAE, 0x56, 0xED, 0xE7, 0x63, 0x72, 0xBB, 0x19, \
-     0x0B, 0x07, 0xA7, 0xC8, 0xEE, 0x0A, 0x6D, 0x70, \
-     0x9E, 0x02, 0xFC, 0xE1, 0xCD, 0xF7, 0xE2, 0xEC, \
-     0xC0, 0x34, 0x04, 0xCD, 0x28, 0x34, 0x2F, 0x61, \
-     0x91, 0x72, 0xFE, 0x9C, 0xE9, 0x85, 0x83, 0xFF, \
-     0x8E, 0x4F, 0x12, 0x32, 0xEE, 0xF2, 0x81, 0x83, \
-     0xC3, 0xFE, 0x3B, 0x1B, 0x4C, 0x6F, 0xAD, 0x73, \
-     0x3B, 0xB5, 0xFC, 0xBC, 0x2E, 0xC2, 0x20, 0x05, \
-     0xC5, 0x8E, 0xF1, 0x83, 0x7D, 0x16, 0x83, 0xB2, \
-     0xC6, 0xF3, 0x4A, 0x26, 0xC1, 0xB2, 0xEF, 0xFA, \
-     0x88, 0x6B, 0x42, 0x38, 0x61, 0x28, 0x5C, 0x97, \
-     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \
+        0xAD, 0xF8, 0x54, 0x58, 0xA2, 0xBB, 0x4A, 0x9A, \
+        0xAF, 0xDC, 0x56, 0x20, 0x27, 0x3D, 0x3C, 0xF1, \
+        0xD8, 0xB9, 0xC5, 0x83, 0xCE, 0x2D, 0x36, 0x95, \
+        0xA9, 0xE1, 0x36, 0x41, 0x14, 0x64, 0x33, 0xFB, \
+        0xCC, 0x93, 0x9D, 0xCE, 0x24, 0x9B, 0x3E, 0xF9, \
+        0x7D, 0x2F, 0xE3, 0x63, 0x63, 0x0C, 0x75, 0xD8, \
+        0xF6, 0x81, 0xB2, 0x02, 0xAE, 0xC4, 0x61, 0x7A, \
+        0xD3, 0xDF, 0x1E, 0xD5, 0xD5, 0xFD, 0x65, 0x61, \
+        0x24, 0x33, 0xF5, 0x1F, 0x5F, 0x06, 0x6E, 0xD0, \
+        0x85, 0x63, 0x65, 0x55, 0x3D, 0xED, 0x1A, 0xF3, \
+        0xB5, 0x57, 0x13, 0x5E, 0x7F, 0x57, 0xC9, 0x35, \
+        0x98, 0x4F, 0x0C, 0x70, 0xE0, 0xE6, 0x8B, 0x77, \
+        0xE2, 0xA6, 0x89, 0xDA, 0xF3, 0xEF, 0xE8, 0x72, \
+        0x1D, 0xF1, 0x58, 0xA1, 0x36, 0xAD, 0xE7, 0x35, \
+        0x30, 0xAC, 0xCA, 0x4F, 0x48, 0x3A, 0x79, 0x7A, \
+        0xBC, 0x0A, 0xB1, 0x82, 0xB3, 0x24, 0xFB, 0x61, \
+        0xD1, 0x08, 0xA9, 0x4B, 0xB2, 0xC8, 0xE3, 0xFB, \
+        0xB9, 0x6A, 0xDA, 0xB7, 0x60, 0xD7, 0xF4, 0x68, \
+        0x1D, 0x4F, 0x42, 0xA3, 0xDE, 0x39, 0x4D, 0xF4, \
+        0xAE, 0x56, 0xED, 0xE7, 0x63, 0x72, 0xBB, 0x19, \
+        0x0B, 0x07, 0xA7, 0xC8, 0xEE, 0x0A, 0x6D, 0x70, \
+        0x9E, 0x02, 0xFC, 0xE1, 0xCD, 0xF7, 0xE2, 0xEC, \
+        0xC0, 0x34, 0x04, 0xCD, 0x28, 0x34, 0x2F, 0x61, \
+        0x91, 0x72, 0xFE, 0x9C, 0xE9, 0x85, 0x83, 0xFF, \
+        0x8E, 0x4F, 0x12, 0x32, 0xEE, 0xF2, 0x81, 0x83, \
+        0xC3, 0xFE, 0x3B, 0x1B, 0x4C, 0x6F, 0xAD, 0x73, \
+        0x3B, 0xB5, 0xFC, 0xBC, 0x2E, 0xC2, 0x20, 0x05, \
+        0xC5, 0x8E, 0xF1, 0x83, 0x7D, 0x16, 0x83, 0xB2, \
+        0xC6, 0xF3, 0x4A, 0x26, 0xC1, 0xB2, 0xEF, 0xFA, \
+        0x88, 0x6B, 0x42, 0x38, 0x61, 0x28, 0x5C, 0x97, \
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }
 
 #define MBEDTLS_DHM_RFC7919_FFDHE2048_G_BIN { 0x02 }
 
 #define MBEDTLS_DHM_RFC7919_FFDHE3072_P_BIN { \
-     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \
-     0xAD, 0xF8, 0x54, 0x58, 0xA2, 0xBB, 0x4A, 0x9A, \
-     0xAF, 0xDC, 0x56, 0x20, 0x27, 0x3D, 0x3C, 0xF1, \
-     0xD8, 0xB9, 0xC5, 0x83, 0xCE, 0x2D, 0x36, 0x95, \
-     0xA9, 0xE1, 0x36, 0x41, 0x14, 0x64, 0x33, 0xFB, \
-     0xCC, 0x93, 0x9D, 0xCE, 0x24, 0x9B, 0x3E, 0xF9, \
-     0x7D, 0x2F, 0xE3, 0x63, 0x63, 0x0C, 0x75, 0xD8, \
-     0xF6, 0x81, 0xB2, 0x02, 0xAE, 0xC4, 0x61, 0x7A, \
-     0xD3, 0xDF, 0x1E, 0xD5, 0xD5, 0xFD, 0x65, 0x61, \
-     0x24, 0x33, 0xF5, 0x1F, 0x5F, 0x06, 0x6E, 0xD0, \
-     0x85, 0x63, 0x65, 0x55, 0x3D, 0xED, 0x1A, 0xF3, \
-     0xB5, 0x57, 0x13, 0x5E, 0x7F, 0x57, 0xC9, 0x35, \
-     0x98, 0x4F, 0x0C, 0x70, 0xE0, 0xE6, 0x8B, 0x77, \
-     0xE2, 0xA6, 0x89, 0xDA, 0xF3, 0xEF, 0xE8, 0x72, \
-     0x1D, 0xF1, 0x58, 0xA1, 0x36, 0xAD, 0xE7, 0x35, \
-     0x30, 0xAC, 0xCA, 0x4F, 0x48, 0x3A, 0x79, 0x7A, \
-     0xBC, 0x0A, 0xB1, 0x82, 0xB3, 0x24, 0xFB, 0x61, \
-     0xD1, 0x08, 0xA9, 0x4B, 0xB2, 0xC8, 0xE3, 0xFB, \
-     0xB9, 0x6A, 0xDA, 0xB7, 0x60, 0xD7, 0xF4, 0x68, \
-     0x1D, 0x4F, 0x42, 0xA3, 0xDE, 0x39, 0x4D, 0xF4, \
-     0xAE, 0x56, 0xED, 0xE7, 0x63, 0x72, 0xBB, 0x19, \
-     0x0B, 0x07, 0xA7, 0xC8, 0xEE, 0x0A, 0x6D, 0x70, \
-     0x9E, 0x02, 0xFC, 0xE1, 0xCD, 0xF7, 0xE2, 0xEC, \
-     0xC0, 0x34, 0x04, 0xCD, 0x28, 0x34, 0x2F, 0x61, \
-     0x91, 0x72, 0xFE, 0x9C, 0xE9, 0x85, 0x83, 0xFF, \
-     0x8E, 0x4F, 0x12, 0x32, 0xEE, 0xF2, 0x81, 0x83, \
-     0xC3, 0xFE, 0x3B, 0x1B, 0x4C, 0x6F, 0xAD, 0x73, \
-     0x3B, 0xB5, 0xFC, 0xBC, 0x2E, 0xC2, 0x20, 0x05, \
-     0xC5, 0x8E, 0xF1, 0x83, 0x7D, 0x16, 0x83, 0xB2, \
-     0xC6, 0xF3, 0x4A, 0x26, 0xC1, 0xB2, 0xEF, 0xFA, \
-     0x88, 0x6B, 0x42, 0x38, 0x61, 0x1F, 0xCF, 0xDC, \
-     0xDE, 0x35, 0x5B, 0x3B, 0x65, 0x19, 0x03, 0x5B, \
-     0xBC, 0x34, 0xF4, 0xDE, 0xF9, 0x9C, 0x02, 0x38, \
-     0x61, 0xB4, 0x6F, 0xC9, 0xD6, 0xE6, 0xC9, 0x07, \
-     0x7A, 0xD9, 0x1D, 0x26, 0x91, 0xF7, 0xF7, 0xEE, \
-     0x59, 0x8C, 0xB0, 0xFA, 0xC1, 0x86, 0xD9, 0x1C, \
-     0xAE, 0xFE, 0x13, 0x09, 0x85, 0x13, 0x92, 0x70, \
-     0xB4, 0x13, 0x0C, 0x93, 0xBC, 0x43, 0x79, 0x44, \
-     0xF4, 0xFD, 0x44, 0x52, 0xE2, 0xD7, 0x4D, 0xD3, \
-     0x64, 0xF2, 0xE2, 0x1E, 0x71, 0xF5, 0x4B, 0xFF, \
-     0x5C, 0xAE, 0x82, 0xAB, 0x9C, 0x9D, 0xF6, 0x9E, \
-     0xE8, 0x6D, 0x2B, 0xC5, 0x22, 0x36, 0x3A, 0x0D, \
-     0xAB, 0xC5, 0x21, 0x97, 0x9B, 0x0D, 0xEA, 0xDA, \
-     0x1D, 0xBF, 0x9A, 0x42, 0xD5, 0xC4, 0x48, 0x4E, \
-     0x0A, 0xBC, 0xD0, 0x6B, 0xFA, 0x53, 0xDD, 0xEF, \
-     0x3C, 0x1B, 0x20, 0xEE, 0x3F, 0xD5, 0x9D, 0x7C, \
-     0x25, 0xE4, 0x1D, 0x2B, 0x66, 0xC6, 0x2E, 0x37, \
-     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \
+        0xAD, 0xF8, 0x54, 0x58, 0xA2, 0xBB, 0x4A, 0x9A, \
+        0xAF, 0xDC, 0x56, 0x20, 0x27, 0x3D, 0x3C, 0xF1, \
+        0xD8, 0xB9, 0xC5, 0x83, 0xCE, 0x2D, 0x36, 0x95, \
+        0xA9, 0xE1, 0x36, 0x41, 0x14, 0x64, 0x33, 0xFB, \
+        0xCC, 0x93, 0x9D, 0xCE, 0x24, 0x9B, 0x3E, 0xF9, \
+        0x7D, 0x2F, 0xE3, 0x63, 0x63, 0x0C, 0x75, 0xD8, \
+        0xF6, 0x81, 0xB2, 0x02, 0xAE, 0xC4, 0x61, 0x7A, \
+        0xD3, 0xDF, 0x1E, 0xD5, 0xD5, 0xFD, 0x65, 0x61, \
+        0x24, 0x33, 0xF5, 0x1F, 0x5F, 0x06, 0x6E, 0xD0, \
+        0x85, 0x63, 0x65, 0x55, 0x3D, 0xED, 0x1A, 0xF3, \
+        0xB5, 0x57, 0x13, 0x5E, 0x7F, 0x57, 0xC9, 0x35, \
+        0x98, 0x4F, 0x0C, 0x70, 0xE0, 0xE6, 0x8B, 0x77, \
+        0xE2, 0xA6, 0x89, 0xDA, 0xF3, 0xEF, 0xE8, 0x72, \
+        0x1D, 0xF1, 0x58, 0xA1, 0x36, 0xAD, 0xE7, 0x35, \
+        0x30, 0xAC, 0xCA, 0x4F, 0x48, 0x3A, 0x79, 0x7A, \
+        0xBC, 0x0A, 0xB1, 0x82, 0xB3, 0x24, 0xFB, 0x61, \
+        0xD1, 0x08, 0xA9, 0x4B, 0xB2, 0xC8, 0xE3, 0xFB, \
+        0xB9, 0x6A, 0xDA, 0xB7, 0x60, 0xD7, 0xF4, 0x68, \
+        0x1D, 0x4F, 0x42, 0xA3, 0xDE, 0x39, 0x4D, 0xF4, \
+        0xAE, 0x56, 0xED, 0xE7, 0x63, 0x72, 0xBB, 0x19, \
+        0x0B, 0x07, 0xA7, 0xC8, 0xEE, 0x0A, 0x6D, 0x70, \
+        0x9E, 0x02, 0xFC, 0xE1, 0xCD, 0xF7, 0xE2, 0xEC, \
+        0xC0, 0x34, 0x04, 0xCD, 0x28, 0x34, 0x2F, 0x61, \
+        0x91, 0x72, 0xFE, 0x9C, 0xE9, 0x85, 0x83, 0xFF, \
+        0x8E, 0x4F, 0x12, 0x32, 0xEE, 0xF2, 0x81, 0x83, \
+        0xC3, 0xFE, 0x3B, 0x1B, 0x4C, 0x6F, 0xAD, 0x73, \
+        0x3B, 0xB5, 0xFC, 0xBC, 0x2E, 0xC2, 0x20, 0x05, \
+        0xC5, 0x8E, 0xF1, 0x83, 0x7D, 0x16, 0x83, 0xB2, \
+        0xC6, 0xF3, 0x4A, 0x26, 0xC1, 0xB2, 0xEF, 0xFA, \
+        0x88, 0x6B, 0x42, 0x38, 0x61, 0x1F, 0xCF, 0xDC, \
+        0xDE, 0x35, 0x5B, 0x3B, 0x65, 0x19, 0x03, 0x5B, \
+        0xBC, 0x34, 0xF4, 0xDE, 0xF9, 0x9C, 0x02, 0x38, \
+        0x61, 0xB4, 0x6F, 0xC9, 0xD6, 0xE6, 0xC9, 0x07, \
+        0x7A, 0xD9, 0x1D, 0x26, 0x91, 0xF7, 0xF7, 0xEE, \
+        0x59, 0x8C, 0xB0, 0xFA, 0xC1, 0x86, 0xD9, 0x1C, \
+        0xAE, 0xFE, 0x13, 0x09, 0x85, 0x13, 0x92, 0x70, \
+        0xB4, 0x13, 0x0C, 0x93, 0xBC, 0x43, 0x79, 0x44, \
+        0xF4, 0xFD, 0x44, 0x52, 0xE2, 0xD7, 0x4D, 0xD3, \
+        0x64, 0xF2, 0xE2, 0x1E, 0x71, 0xF5, 0x4B, 0xFF, \
+        0x5C, 0xAE, 0x82, 0xAB, 0x9C, 0x9D, 0xF6, 0x9E, \
+        0xE8, 0x6D, 0x2B, 0xC5, 0x22, 0x36, 0x3A, 0x0D, \
+        0xAB, 0xC5, 0x21, 0x97, 0x9B, 0x0D, 0xEA, 0xDA, \
+        0x1D, 0xBF, 0x9A, 0x42, 0xD5, 0xC4, 0x48, 0x4E, \
+        0x0A, 0xBC, 0xD0, 0x6B, 0xFA, 0x53, 0xDD, 0xEF, \
+        0x3C, 0x1B, 0x20, 0xEE, 0x3F, 0xD5, 0x9D, 0x7C, \
+        0x25, 0xE4, 0x1D, 0x2B, 0x66, 0xC6, 0x2E, 0x37, \
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }
 
 #define MBEDTLS_DHM_RFC7919_FFDHE3072_G_BIN { 0x02 }
 
 #define MBEDTLS_DHM_RFC7919_FFDHE4096_P_BIN {        \
-     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \
-     0xAD, 0xF8, 0x54, 0x58, 0xA2, 0xBB, 0x4A, 0x9A, \
-     0xAF, 0xDC, 0x56, 0x20, 0x27, 0x3D, 0x3C, 0xF1, \
-     0xD8, 0xB9, 0xC5, 0x83, 0xCE, 0x2D, 0x36, 0x95, \
-     0xA9, 0xE1, 0x36, 0x41, 0x14, 0x64, 0x33, 0xFB, \
-     0xCC, 0x93, 0x9D, 0xCE, 0x24, 0x9B, 0x3E, 0xF9, \
-     0x7D, 0x2F, 0xE3, 0x63, 0x63, 0x0C, 0x75, 0xD8, \
-     0xF6, 0x81, 0xB2, 0x02, 0xAE, 0xC4, 0x61, 0x7A, \
-     0xD3, 0xDF, 0x1E, 0xD5, 0xD5, 0xFD, 0x65, 0x61, \
-     0x24, 0x33, 0xF5, 0x1F, 0x5F, 0x06, 0x6E, 0xD0, \
-     0x85, 0x63, 0x65, 0x55, 0x3D, 0xED, 0x1A, 0xF3, \
-     0xB5, 0x57, 0x13, 0x5E, 0x7F, 0x57, 0xC9, 0x35, \
-     0x98, 0x4F, 0x0C, 0x70, 0xE0, 0xE6, 0x8B, 0x77, \
-     0xE2, 0xA6, 0x89, 0xDA, 0xF3, 0xEF, 0xE8, 0x72, \
-     0x1D, 0xF1, 0x58, 0xA1, 0x36, 0xAD, 0xE7, 0x35, \
-     0x30, 0xAC, 0xCA, 0x4F, 0x48, 0x3A, 0x79, 0x7A, \
-     0xBC, 0x0A, 0xB1, 0x82, 0xB3, 0x24, 0xFB, 0x61, \
-     0xD1, 0x08, 0xA9, 0x4B, 0xB2, 0xC8, 0xE3, 0xFB, \
-     0xB9, 0x6A, 0xDA, 0xB7, 0x60, 0xD7, 0xF4, 0x68, \
-     0x1D, 0x4F, 0x42, 0xA3, 0xDE, 0x39, 0x4D, 0xF4, \
-     0xAE, 0x56, 0xED, 0xE7, 0x63, 0x72, 0xBB, 0x19, \
-     0x0B, 0x07, 0xA7, 0xC8, 0xEE, 0x0A, 0x6D, 0x70, \
-     0x9E, 0x02, 0xFC, 0xE1, 0xCD, 0xF7, 0xE2, 0xEC, \
-     0xC0, 0x34, 0x04, 0xCD, 0x28, 0x34, 0x2F, 0x61, \
-     0x91, 0x72, 0xFE, 0x9C, 0xE9, 0x85, 0x83, 0xFF, \
-     0x8E, 0x4F, 0x12, 0x32, 0xEE, 0xF2, 0x81, 0x83, \
-     0xC3, 0xFE, 0x3B, 0x1B, 0x4C, 0x6F, 0xAD, 0x73, \
-     0x3B, 0xB5, 0xFC, 0xBC, 0x2E, 0xC2, 0x20, 0x05, \
-     0xC5, 0x8E, 0xF1, 0x83, 0x7D, 0x16, 0x83, 0xB2, \
-     0xC6, 0xF3, 0x4A, 0x26, 0xC1, 0xB2, 0xEF, 0xFA, \
-     0x88, 0x6B, 0x42, 0x38, 0x61, 0x1F, 0xCF, 0xDC, \
-     0xDE, 0x35, 0x5B, 0x3B, 0x65, 0x19, 0x03, 0x5B, \
-     0xBC, 0x34, 0xF4, 0xDE, 0xF9, 0x9C, 0x02, 0x38, \
-     0x61, 0xB4, 0x6F, 0xC9, 0xD6, 0xE6, 0xC9, 0x07, \
-     0x7A, 0xD9, 0x1D, 0x26, 0x91, 0xF7, 0xF7, 0xEE, \
-     0x59, 0x8C, 0xB0, 0xFA, 0xC1, 0x86, 0xD9, 0x1C, \
-     0xAE, 0xFE, 0x13, 0x09, 0x85, 0x13, 0x92, 0x70, \
-     0xB4, 0x13, 0x0C, 0x93, 0xBC, 0x43, 0x79, 0x44, \
-     0xF4, 0xFD, 0x44, 0x52, 0xE2, 0xD7, 0x4D, 0xD3, \
-     0x64, 0xF2, 0xE2, 0x1E, 0x71, 0xF5, 0x4B, 0xFF, \
-     0x5C, 0xAE, 0x82, 0xAB, 0x9C, 0x9D, 0xF6, 0x9E, \
-     0xE8, 0x6D, 0x2B, 0xC5, 0x22, 0x36, 0x3A, 0x0D, \
-     0xAB, 0xC5, 0x21, 0x97, 0x9B, 0x0D, 0xEA, 0xDA, \
-     0x1D, 0xBF, 0x9A, 0x42, 0xD5, 0xC4, 0x48, 0x4E, \
-     0x0A, 0xBC, 0xD0, 0x6B, 0xFA, 0x53, 0xDD, 0xEF, \
-     0x3C, 0x1B, 0x20, 0xEE, 0x3F, 0xD5, 0x9D, 0x7C, \
-     0x25, 0xE4, 0x1D, 0x2B, 0x66, 0x9E, 0x1E, 0xF1, \
-     0x6E, 0x6F, 0x52, 0xC3, 0x16, 0x4D, 0xF4, 0xFB, \
-     0x79, 0x30, 0xE9, 0xE4, 0xE5, 0x88, 0x57, 0xB6, \
-     0xAC, 0x7D, 0x5F, 0x42, 0xD6, 0x9F, 0x6D, 0x18, \
-     0x77, 0x63, 0xCF, 0x1D, 0x55, 0x03, 0x40, 0x04, \
-     0x87, 0xF5, 0x5B, 0xA5, 0x7E, 0x31, 0xCC, 0x7A, \
-     0x71, 0x35, 0xC8, 0x86, 0xEF, 0xB4, 0x31, 0x8A, \
-     0xED, 0x6A, 0x1E, 0x01, 0x2D, 0x9E, 0x68, 0x32, \
-     0xA9, 0x07, 0x60, 0x0A, 0x91, 0x81, 0x30, 0xC4, \
-     0x6D, 0xC7, 0x78, 0xF9, 0x71, 0xAD, 0x00, 0x38, \
-     0x09, 0x29, 0x99, 0xA3, 0x33, 0xCB, 0x8B, 0x7A, \
-     0x1A, 0x1D, 0xB9, 0x3D, 0x71, 0x40, 0x00, 0x3C, \
-     0x2A, 0x4E, 0xCE, 0xA9, 0xF9, 0x8D, 0x0A, 0xCC, \
-     0x0A, 0x82, 0x91, 0xCD, 0xCE, 0xC9, 0x7D, 0xCF, \
-     0x8E, 0xC9, 0xB5, 0x5A, 0x7F, 0x88, 0xA4, 0x6B, \
-     0x4D, 0xB5, 0xA8, 0x51, 0xF4, 0x41, 0x82, 0xE1, \
-     0xC6, 0x8A, 0x00, 0x7E, 0x5E, 0x65, 0x5F, 0x6A, \
-     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \
+        0xAD, 0xF8, 0x54, 0x58, 0xA2, 0xBB, 0x4A, 0x9A, \
+        0xAF, 0xDC, 0x56, 0x20, 0x27, 0x3D, 0x3C, 0xF1, \
+        0xD8, 0xB9, 0xC5, 0x83, 0xCE, 0x2D, 0x36, 0x95, \
+        0xA9, 0xE1, 0x36, 0x41, 0x14, 0x64, 0x33, 0xFB, \
+        0xCC, 0x93, 0x9D, 0xCE, 0x24, 0x9B, 0x3E, 0xF9, \
+        0x7D, 0x2F, 0xE3, 0x63, 0x63, 0x0C, 0x75, 0xD8, \
+        0xF6, 0x81, 0xB2, 0x02, 0xAE, 0xC4, 0x61, 0x7A, \
+        0xD3, 0xDF, 0x1E, 0xD5, 0xD5, 0xFD, 0x65, 0x61, \
+        0x24, 0x33, 0xF5, 0x1F, 0x5F, 0x06, 0x6E, 0xD0, \
+        0x85, 0x63, 0x65, 0x55, 0x3D, 0xED, 0x1A, 0xF3, \
+        0xB5, 0x57, 0x13, 0x5E, 0x7F, 0x57, 0xC9, 0x35, \
+        0x98, 0x4F, 0x0C, 0x70, 0xE0, 0xE6, 0x8B, 0x77, \
+        0xE2, 0xA6, 0x89, 0xDA, 0xF3, 0xEF, 0xE8, 0x72, \
+        0x1D, 0xF1, 0x58, 0xA1, 0x36, 0xAD, 0xE7, 0x35, \
+        0x30, 0xAC, 0xCA, 0x4F, 0x48, 0x3A, 0x79, 0x7A, \
+        0xBC, 0x0A, 0xB1, 0x82, 0xB3, 0x24, 0xFB, 0x61, \
+        0xD1, 0x08, 0xA9, 0x4B, 0xB2, 0xC8, 0xE3, 0xFB, \
+        0xB9, 0x6A, 0xDA, 0xB7, 0x60, 0xD7, 0xF4, 0x68, \
+        0x1D, 0x4F, 0x42, 0xA3, 0xDE, 0x39, 0x4D, 0xF4, \
+        0xAE, 0x56, 0xED, 0xE7, 0x63, 0x72, 0xBB, 0x19, \
+        0x0B, 0x07, 0xA7, 0xC8, 0xEE, 0x0A, 0x6D, 0x70, \
+        0x9E, 0x02, 0xFC, 0xE1, 0xCD, 0xF7, 0xE2, 0xEC, \
+        0xC0, 0x34, 0x04, 0xCD, 0x28, 0x34, 0x2F, 0x61, \
+        0x91, 0x72, 0xFE, 0x9C, 0xE9, 0x85, 0x83, 0xFF, \
+        0x8E, 0x4F, 0x12, 0x32, 0xEE, 0xF2, 0x81, 0x83, \
+        0xC3, 0xFE, 0x3B, 0x1B, 0x4C, 0x6F, 0xAD, 0x73, \
+        0x3B, 0xB5, 0xFC, 0xBC, 0x2E, 0xC2, 0x20, 0x05, \
+        0xC5, 0x8E, 0xF1, 0x83, 0x7D, 0x16, 0x83, 0xB2, \
+        0xC6, 0xF3, 0x4A, 0x26, 0xC1, 0xB2, 0xEF, 0xFA, \
+        0x88, 0x6B, 0x42, 0x38, 0x61, 0x1F, 0xCF, 0xDC, \
+        0xDE, 0x35, 0x5B, 0x3B, 0x65, 0x19, 0x03, 0x5B, \
+        0xBC, 0x34, 0xF4, 0xDE, 0xF9, 0x9C, 0x02, 0x38, \
+        0x61, 0xB4, 0x6F, 0xC9, 0xD6, 0xE6, 0xC9, 0x07, \
+        0x7A, 0xD9, 0x1D, 0x26, 0x91, 0xF7, 0xF7, 0xEE, \
+        0x59, 0x8C, 0xB0, 0xFA, 0xC1, 0x86, 0xD9, 0x1C, \
+        0xAE, 0xFE, 0x13, 0x09, 0x85, 0x13, 0x92, 0x70, \
+        0xB4, 0x13, 0x0C, 0x93, 0xBC, 0x43, 0x79, 0x44, \
+        0xF4, 0xFD, 0x44, 0x52, 0xE2, 0xD7, 0x4D, 0xD3, \
+        0x64, 0xF2, 0xE2, 0x1E, 0x71, 0xF5, 0x4B, 0xFF, \
+        0x5C, 0xAE, 0x82, 0xAB, 0x9C, 0x9D, 0xF6, 0x9E, \
+        0xE8, 0x6D, 0x2B, 0xC5, 0x22, 0x36, 0x3A, 0x0D, \
+        0xAB, 0xC5, 0x21, 0x97, 0x9B, 0x0D, 0xEA, 0xDA, \
+        0x1D, 0xBF, 0x9A, 0x42, 0xD5, 0xC4, 0x48, 0x4E, \
+        0x0A, 0xBC, 0xD0, 0x6B, 0xFA, 0x53, 0xDD, 0xEF, \
+        0x3C, 0x1B, 0x20, 0xEE, 0x3F, 0xD5, 0x9D, 0x7C, \
+        0x25, 0xE4, 0x1D, 0x2B, 0x66, 0x9E, 0x1E, 0xF1, \
+        0x6E, 0x6F, 0x52, 0xC3, 0x16, 0x4D, 0xF4, 0xFB, \
+        0x79, 0x30, 0xE9, 0xE4, 0xE5, 0x88, 0x57, 0xB6, \
+        0xAC, 0x7D, 0x5F, 0x42, 0xD6, 0x9F, 0x6D, 0x18, \
+        0x77, 0x63, 0xCF, 0x1D, 0x55, 0x03, 0x40, 0x04, \
+        0x87, 0xF5, 0x5B, 0xA5, 0x7E, 0x31, 0xCC, 0x7A, \
+        0x71, 0x35, 0xC8, 0x86, 0xEF, 0xB4, 0x31, 0x8A, \
+        0xED, 0x6A, 0x1E, 0x01, 0x2D, 0x9E, 0x68, 0x32, \
+        0xA9, 0x07, 0x60, 0x0A, 0x91, 0x81, 0x30, 0xC4, \
+        0x6D, 0xC7, 0x78, 0xF9, 0x71, 0xAD, 0x00, 0x38, \
+        0x09, 0x29, 0x99, 0xA3, 0x33, 0xCB, 0x8B, 0x7A, \
+        0x1A, 0x1D, 0xB9, 0x3D, 0x71, 0x40, 0x00, 0x3C, \
+        0x2A, 0x4E, 0xCE, 0xA9, 0xF9, 0x8D, 0x0A, 0xCC, \
+        0x0A, 0x82, 0x91, 0xCD, 0xCE, 0xC9, 0x7D, 0xCF, \
+        0x8E, 0xC9, 0xB5, 0x5A, 0x7F, 0x88, 0xA4, 0x6B, \
+        0x4D, 0xB5, 0xA8, 0x51, 0xF4, 0x41, 0x82, 0xE1, \
+        0xC6, 0x8A, 0x00, 0x7E, 0x5E, 0x65, 0x5F, 0x6A, \
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }
 
 #define MBEDTLS_DHM_RFC7919_FFDHE4096_G_BIN { 0x02 }
 
 #define MBEDTLS_DHM_RFC7919_FFDHE6144_P_BIN {        \
-     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \
-     0xAD, 0xF8, 0x54, 0x58, 0xA2, 0xBB, 0x4A, 0x9A, \
-     0xAF, 0xDC, 0x56, 0x20, 0x27, 0x3D, 0x3C, 0xF1, \
-     0xD8, 0xB9, 0xC5, 0x83, 0xCE, 0x2D, 0x36, 0x95, \
-     0xA9, 0xE1, 0x36, 0x41, 0x14, 0x64, 0x33, 0xFB, \
-     0xCC, 0x93, 0x9D, 0xCE, 0x24, 0x9B, 0x3E, 0xF9, \
-     0x7D, 0x2F, 0xE3, 0x63, 0x63, 0x0C, 0x75, 0xD8, \
-     0xF6, 0x81, 0xB2, 0x02, 0xAE, 0xC4, 0x61, 0x7A, \
-     0xD3, 0xDF, 0x1E, 0xD5, 0xD5, 0xFD, 0x65, 0x61, \
-     0x24, 0x33, 0xF5, 0x1F, 0x5F, 0x06, 0x6E, 0xD0, \
-     0x85, 0x63, 0x65, 0x55, 0x3D, 0xED, 0x1A, 0xF3, \
-     0xB5, 0x57, 0x13, 0x5E, 0x7F, 0x57, 0xC9, 0x35, \
-     0x98, 0x4F, 0x0C, 0x70, 0xE0, 0xE6, 0x8B, 0x77, \
-     0xE2, 0xA6, 0x89, 0xDA, 0xF3, 0xEF, 0xE8, 0x72, \
-     0x1D, 0xF1, 0x58, 0xA1, 0x36, 0xAD, 0xE7, 0x35, \
-     0x30, 0xAC, 0xCA, 0x4F, 0x48, 0x3A, 0x79, 0x7A, \
-     0xBC, 0x0A, 0xB1, 0x82, 0xB3, 0x24, 0xFB, 0x61, \
-     0xD1, 0x08, 0xA9, 0x4B, 0xB2, 0xC8, 0xE3, 0xFB, \
-     0xB9, 0x6A, 0xDA, 0xB7, 0x60, 0xD7, 0xF4, 0x68, \
-     0x1D, 0x4F, 0x42, 0xA3, 0xDE, 0x39, 0x4D, 0xF4, \
-     0xAE, 0x56, 0xED, 0xE7, 0x63, 0x72, 0xBB, 0x19, \
-     0x0B, 0x07, 0xA7, 0xC8, 0xEE, 0x0A, 0x6D, 0x70, \
-     0x9E, 0x02, 0xFC, 0xE1, 0xCD, 0xF7, 0xE2, 0xEC, \
-     0xC0, 0x34, 0x04, 0xCD, 0x28, 0x34, 0x2F, 0x61, \
-     0x91, 0x72, 0xFE, 0x9C, 0xE9, 0x85, 0x83, 0xFF, \
-     0x8E, 0x4F, 0x12, 0x32, 0xEE, 0xF2, 0x81, 0x83, \
-     0xC3, 0xFE, 0x3B, 0x1B, 0x4C, 0x6F, 0xAD, 0x73, \
-     0x3B, 0xB5, 0xFC, 0xBC, 0x2E, 0xC2, 0x20, 0x05, \
-     0xC5, 0x8E, 0xF1, 0x83, 0x7D, 0x16, 0x83, 0xB2, \
-     0xC6, 0xF3, 0x4A, 0x26, 0xC1, 0xB2, 0xEF, 0xFA, \
-     0x88, 0x6B, 0x42, 0x38, 0x61, 0x1F, 0xCF, 0xDC, \
-     0xDE, 0x35, 0x5B, 0x3B, 0x65, 0x19, 0x03, 0x5B, \
-     0xBC, 0x34, 0xF4, 0xDE, 0xF9, 0x9C, 0x02, 0x38, \
-     0x61, 0xB4, 0x6F, 0xC9, 0xD6, 0xE6, 0xC9, 0x07, \
-     0x7A, 0xD9, 0x1D, 0x26, 0x91, 0xF7, 0xF7, 0xEE, \
-     0x59, 0x8C, 0xB0, 0xFA, 0xC1, 0x86, 0xD9, 0x1C, \
-     0xAE, 0xFE, 0x13, 0x09, 0x85, 0x13, 0x92, 0x70, \
-     0xB4, 0x13, 0x0C, 0x93, 0xBC, 0x43, 0x79, 0x44, \
-     0xF4, 0xFD, 0x44, 0x52, 0xE2, 0xD7, 0x4D, 0xD3, \
-     0x64, 0xF2, 0xE2, 0x1E, 0x71, 0xF5, 0x4B, 0xFF, \
-     0x5C, 0xAE, 0x82, 0xAB, 0x9C, 0x9D, 0xF6, 0x9E, \
-     0xE8, 0x6D, 0x2B, 0xC5, 0x22, 0x36, 0x3A, 0x0D, \
-     0xAB, 0xC5, 0x21, 0x97, 0x9B, 0x0D, 0xEA, 0xDA, \
-     0x1D, 0xBF, 0x9A, 0x42, 0xD5, 0xC4, 0x48, 0x4E, \
-     0x0A, 0xBC, 0xD0, 0x6B, 0xFA, 0x53, 0xDD, 0xEF, \
-     0x3C, 0x1B, 0x20, 0xEE, 0x3F, 0xD5, 0x9D, 0x7C, \
-     0x25, 0xE4, 0x1D, 0x2B, 0x66, 0x9E, 0x1E, 0xF1, \
-     0x6E, 0x6F, 0x52, 0xC3, 0x16, 0x4D, 0xF4, 0xFB, \
-     0x79, 0x30, 0xE9, 0xE4, 0xE5, 0x88, 0x57, 0xB6, \
-     0xAC, 0x7D, 0x5F, 0x42, 0xD6, 0x9F, 0x6D, 0x18, \
-     0x77, 0x63, 0xCF, 0x1D, 0x55, 0x03, 0x40, 0x04, \
-     0x87, 0xF5, 0x5B, 0xA5, 0x7E, 0x31, 0xCC, 0x7A, \
-     0x71, 0x35, 0xC8, 0x86, 0xEF, 0xB4, 0x31, 0x8A, \
-     0xED, 0x6A, 0x1E, 0x01, 0x2D, 0x9E, 0x68, 0x32, \
-     0xA9, 0x07, 0x60, 0x0A, 0x91, 0x81, 0x30, 0xC4, \
-     0x6D, 0xC7, 0x78, 0xF9, 0x71, 0xAD, 0x00, 0x38, \
-     0x09, 0x29, 0x99, 0xA3, 0x33, 0xCB, 0x8B, 0x7A, \
-     0x1A, 0x1D, 0xB9, 0x3D, 0x71, 0x40, 0x00, 0x3C, \
-     0x2A, 0x4E, 0xCE, 0xA9, 0xF9, 0x8D, 0x0A, 0xCC, \
-     0x0A, 0x82, 0x91, 0xCD, 0xCE, 0xC9, 0x7D, 0xCF, \
-     0x8E, 0xC9, 0xB5, 0x5A, 0x7F, 0x88, 0xA4, 0x6B, \
-     0x4D, 0xB5, 0xA8, 0x51, 0xF4, 0x41, 0x82, 0xE1, \
-     0xC6, 0x8A, 0x00, 0x7E, 0x5E, 0x0D, 0xD9, 0x02, \
-     0x0B, 0xFD, 0x64, 0xB6, 0x45, 0x03, 0x6C, 0x7A, \
-     0x4E, 0x67, 0x7D, 0x2C, 0x38, 0x53, 0x2A, 0x3A, \
-     0x23, 0xBA, 0x44, 0x42, 0xCA, 0xF5, 0x3E, 0xA6, \
-     0x3B, 0xB4, 0x54, 0x32, 0x9B, 0x76, 0x24, 0xC8, \
-     0x91, 0x7B, 0xDD, 0x64, 0xB1, 0xC0, 0xFD, 0x4C, \
-     0xB3, 0x8E, 0x8C, 0x33, 0x4C, 0x70, 0x1C, 0x3A, \
-     0xCD, 0xAD, 0x06, 0x57, 0xFC, 0xCF, 0xEC, 0x71, \
-     0x9B, 0x1F, 0x5C, 0x3E, 0x4E, 0x46, 0x04, 0x1F, \
-     0x38, 0x81, 0x47, 0xFB, 0x4C, 0xFD, 0xB4, 0x77, \
-     0xA5, 0x24, 0x71, 0xF7, 0xA9, 0xA9, 0x69, 0x10, \
-     0xB8, 0x55, 0x32, 0x2E, 0xDB, 0x63, 0x40, 0xD8, \
-     0xA0, 0x0E, 0xF0, 0x92, 0x35, 0x05, 0x11, 0xE3, \
-     0x0A, 0xBE, 0xC1, 0xFF, 0xF9, 0xE3, 0xA2, 0x6E, \
-     0x7F, 0xB2, 0x9F, 0x8C, 0x18, 0x30, 0x23, 0xC3, \
-     0x58, 0x7E, 0x38, 0xDA, 0x00, 0x77, 0xD9, 0xB4, \
-     0x76, 0x3E, 0x4E, 0x4B, 0x94, 0xB2, 0xBB, 0xC1, \
-     0x94, 0xC6, 0x65, 0x1E, 0x77, 0xCA, 0xF9, 0x92, \
-     0xEE, 0xAA, 0xC0, 0x23, 0x2A, 0x28, 0x1B, 0xF6, \
-     0xB3, 0xA7, 0x39, 0xC1, 0x22, 0x61, 0x16, 0x82, \
-     0x0A, 0xE8, 0xDB, 0x58, 0x47, 0xA6, 0x7C, 0xBE, \
-     0xF9, 0xC9, 0x09, 0x1B, 0x46, 0x2D, 0x53, 0x8C, \
-     0xD7, 0x2B, 0x03, 0x74, 0x6A, 0xE7, 0x7F, 0x5E, \
-     0x62, 0x29, 0x2C, 0x31, 0x15, 0x62, 0xA8, 0x46, \
-     0x50, 0x5D, 0xC8, 0x2D, 0xB8, 0x54, 0x33, 0x8A, \
-     0xE4, 0x9F, 0x52, 0x35, 0xC9, 0x5B, 0x91, 0x17, \
-     0x8C, 0xCF, 0x2D, 0xD5, 0xCA, 0xCE, 0xF4, 0x03, \
-     0xEC, 0x9D, 0x18, 0x10, 0xC6, 0x27, 0x2B, 0x04, \
-     0x5B, 0x3B, 0x71, 0xF9, 0xDC, 0x6B, 0x80, 0xD6, \
-     0x3F, 0xDD, 0x4A, 0x8E, 0x9A, 0xDB, 0x1E, 0x69, \
-     0x62, 0xA6, 0x95, 0x26, 0xD4, 0x31, 0x61, 0xC1, \
-     0xA4, 0x1D, 0x57, 0x0D, 0x79, 0x38, 0xDA, 0xD4, \
-     0xA4, 0x0E, 0x32, 0x9C, 0xD0, 0xE4, 0x0E, 0x65, \
-     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \
+        0xAD, 0xF8, 0x54, 0x58, 0xA2, 0xBB, 0x4A, 0x9A, \
+        0xAF, 0xDC, 0x56, 0x20, 0x27, 0x3D, 0x3C, 0xF1, \
+        0xD8, 0xB9, 0xC5, 0x83, 0xCE, 0x2D, 0x36, 0x95, \
+        0xA9, 0xE1, 0x36, 0x41, 0x14, 0x64, 0x33, 0xFB, \
+        0xCC, 0x93, 0x9D, 0xCE, 0x24, 0x9B, 0x3E, 0xF9, \
+        0x7D, 0x2F, 0xE3, 0x63, 0x63, 0x0C, 0x75, 0xD8, \
+        0xF6, 0x81, 0xB2, 0x02, 0xAE, 0xC4, 0x61, 0x7A, \
+        0xD3, 0xDF, 0x1E, 0xD5, 0xD5, 0xFD, 0x65, 0x61, \
+        0x24, 0x33, 0xF5, 0x1F, 0x5F, 0x06, 0x6E, 0xD0, \
+        0x85, 0x63, 0x65, 0x55, 0x3D, 0xED, 0x1A, 0xF3, \
+        0xB5, 0x57, 0x13, 0x5E, 0x7F, 0x57, 0xC9, 0x35, \
+        0x98, 0x4F, 0x0C, 0x70, 0xE0, 0xE6, 0x8B, 0x77, \
+        0xE2, 0xA6, 0x89, 0xDA, 0xF3, 0xEF, 0xE8, 0x72, \
+        0x1D, 0xF1, 0x58, 0xA1, 0x36, 0xAD, 0xE7, 0x35, \
+        0x30, 0xAC, 0xCA, 0x4F, 0x48, 0x3A, 0x79, 0x7A, \
+        0xBC, 0x0A, 0xB1, 0x82, 0xB3, 0x24, 0xFB, 0x61, \
+        0xD1, 0x08, 0xA9, 0x4B, 0xB2, 0xC8, 0xE3, 0xFB, \
+        0xB9, 0x6A, 0xDA, 0xB7, 0x60, 0xD7, 0xF4, 0x68, \
+        0x1D, 0x4F, 0x42, 0xA3, 0xDE, 0x39, 0x4D, 0xF4, \
+        0xAE, 0x56, 0xED, 0xE7, 0x63, 0x72, 0xBB, 0x19, \
+        0x0B, 0x07, 0xA7, 0xC8, 0xEE, 0x0A, 0x6D, 0x70, \
+        0x9E, 0x02, 0xFC, 0xE1, 0xCD, 0xF7, 0xE2, 0xEC, \
+        0xC0, 0x34, 0x04, 0xCD, 0x28, 0x34, 0x2F, 0x61, \
+        0x91, 0x72, 0xFE, 0x9C, 0xE9, 0x85, 0x83, 0xFF, \
+        0x8E, 0x4F, 0x12, 0x32, 0xEE, 0xF2, 0x81, 0x83, \
+        0xC3, 0xFE, 0x3B, 0x1B, 0x4C, 0x6F, 0xAD, 0x73, \
+        0x3B, 0xB5, 0xFC, 0xBC, 0x2E, 0xC2, 0x20, 0x05, \
+        0xC5, 0x8E, 0xF1, 0x83, 0x7D, 0x16, 0x83, 0xB2, \
+        0xC6, 0xF3, 0x4A, 0x26, 0xC1, 0xB2, 0xEF, 0xFA, \
+        0x88, 0x6B, 0x42, 0x38, 0x61, 0x1F, 0xCF, 0xDC, \
+        0xDE, 0x35, 0x5B, 0x3B, 0x65, 0x19, 0x03, 0x5B, \
+        0xBC, 0x34, 0xF4, 0xDE, 0xF9, 0x9C, 0x02, 0x38, \
+        0x61, 0xB4, 0x6F, 0xC9, 0xD6, 0xE6, 0xC9, 0x07, \
+        0x7A, 0xD9, 0x1D, 0x26, 0x91, 0xF7, 0xF7, 0xEE, \
+        0x59, 0x8C, 0xB0, 0xFA, 0xC1, 0x86, 0xD9, 0x1C, \
+        0xAE, 0xFE, 0x13, 0x09, 0x85, 0x13, 0x92, 0x70, \
+        0xB4, 0x13, 0x0C, 0x93, 0xBC, 0x43, 0x79, 0x44, \
+        0xF4, 0xFD, 0x44, 0x52, 0xE2, 0xD7, 0x4D, 0xD3, \
+        0x64, 0xF2, 0xE2, 0x1E, 0x71, 0xF5, 0x4B, 0xFF, \
+        0x5C, 0xAE, 0x82, 0xAB, 0x9C, 0x9D, 0xF6, 0x9E, \
+        0xE8, 0x6D, 0x2B, 0xC5, 0x22, 0x36, 0x3A, 0x0D, \
+        0xAB, 0xC5, 0x21, 0x97, 0x9B, 0x0D, 0xEA, 0xDA, \
+        0x1D, 0xBF, 0x9A, 0x42, 0xD5, 0xC4, 0x48, 0x4E, \
+        0x0A, 0xBC, 0xD0, 0x6B, 0xFA, 0x53, 0xDD, 0xEF, \
+        0x3C, 0x1B, 0x20, 0xEE, 0x3F, 0xD5, 0x9D, 0x7C, \
+        0x25, 0xE4, 0x1D, 0x2B, 0x66, 0x9E, 0x1E, 0xF1, \
+        0x6E, 0x6F, 0x52, 0xC3, 0x16, 0x4D, 0xF4, 0xFB, \
+        0x79, 0x30, 0xE9, 0xE4, 0xE5, 0x88, 0x57, 0xB6, \
+        0xAC, 0x7D, 0x5F, 0x42, 0xD6, 0x9F, 0x6D, 0x18, \
+        0x77, 0x63, 0xCF, 0x1D, 0x55, 0x03, 0x40, 0x04, \
+        0x87, 0xF5, 0x5B, 0xA5, 0x7E, 0x31, 0xCC, 0x7A, \
+        0x71, 0x35, 0xC8, 0x86, 0xEF, 0xB4, 0x31, 0x8A, \
+        0xED, 0x6A, 0x1E, 0x01, 0x2D, 0x9E, 0x68, 0x32, \
+        0xA9, 0x07, 0x60, 0x0A, 0x91, 0x81, 0x30, 0xC4, \
+        0x6D, 0xC7, 0x78, 0xF9, 0x71, 0xAD, 0x00, 0x38, \
+        0x09, 0x29, 0x99, 0xA3, 0x33, 0xCB, 0x8B, 0x7A, \
+        0x1A, 0x1D, 0xB9, 0x3D, 0x71, 0x40, 0x00, 0x3C, \
+        0x2A, 0x4E, 0xCE, 0xA9, 0xF9, 0x8D, 0x0A, 0xCC, \
+        0x0A, 0x82, 0x91, 0xCD, 0xCE, 0xC9, 0x7D, 0xCF, \
+        0x8E, 0xC9, 0xB5, 0x5A, 0x7F, 0x88, 0xA4, 0x6B, \
+        0x4D, 0xB5, 0xA8, 0x51, 0xF4, 0x41, 0x82, 0xE1, \
+        0xC6, 0x8A, 0x00, 0x7E, 0x5E, 0x0D, 0xD9, 0x02, \
+        0x0B, 0xFD, 0x64, 0xB6, 0x45, 0x03, 0x6C, 0x7A, \
+        0x4E, 0x67, 0x7D, 0x2C, 0x38, 0x53, 0x2A, 0x3A, \
+        0x23, 0xBA, 0x44, 0x42, 0xCA, 0xF5, 0x3E, 0xA6, \
+        0x3B, 0xB4, 0x54, 0x32, 0x9B, 0x76, 0x24, 0xC8, \
+        0x91, 0x7B, 0xDD, 0x64, 0xB1, 0xC0, 0xFD, 0x4C, \
+        0xB3, 0x8E, 0x8C, 0x33, 0x4C, 0x70, 0x1C, 0x3A, \
+        0xCD, 0xAD, 0x06, 0x57, 0xFC, 0xCF, 0xEC, 0x71, \
+        0x9B, 0x1F, 0x5C, 0x3E, 0x4E, 0x46, 0x04, 0x1F, \
+        0x38, 0x81, 0x47, 0xFB, 0x4C, 0xFD, 0xB4, 0x77, \
+        0xA5, 0x24, 0x71, 0xF7, 0xA9, 0xA9, 0x69, 0x10, \
+        0xB8, 0x55, 0x32, 0x2E, 0xDB, 0x63, 0x40, 0xD8, \
+        0xA0, 0x0E, 0xF0, 0x92, 0x35, 0x05, 0x11, 0xE3, \
+        0x0A, 0xBE, 0xC1, 0xFF, 0xF9, 0xE3, 0xA2, 0x6E, \
+        0x7F, 0xB2, 0x9F, 0x8C, 0x18, 0x30, 0x23, 0xC3, \
+        0x58, 0x7E, 0x38, 0xDA, 0x00, 0x77, 0xD9, 0xB4, \
+        0x76, 0x3E, 0x4E, 0x4B, 0x94, 0xB2, 0xBB, 0xC1, \
+        0x94, 0xC6, 0x65, 0x1E, 0x77, 0xCA, 0xF9, 0x92, \
+        0xEE, 0xAA, 0xC0, 0x23, 0x2A, 0x28, 0x1B, 0xF6, \
+        0xB3, 0xA7, 0x39, 0xC1, 0x22, 0x61, 0x16, 0x82, \
+        0x0A, 0xE8, 0xDB, 0x58, 0x47, 0xA6, 0x7C, 0xBE, \
+        0xF9, 0xC9, 0x09, 0x1B, 0x46, 0x2D, 0x53, 0x8C, \
+        0xD7, 0x2B, 0x03, 0x74, 0x6A, 0xE7, 0x7F, 0x5E, \
+        0x62, 0x29, 0x2C, 0x31, 0x15, 0x62, 0xA8, 0x46, \
+        0x50, 0x5D, 0xC8, 0x2D, 0xB8, 0x54, 0x33, 0x8A, \
+        0xE4, 0x9F, 0x52, 0x35, 0xC9, 0x5B, 0x91, 0x17, \
+        0x8C, 0xCF, 0x2D, 0xD5, 0xCA, 0xCE, 0xF4, 0x03, \
+        0xEC, 0x9D, 0x18, 0x10, 0xC6, 0x27, 0x2B, 0x04, \
+        0x5B, 0x3B, 0x71, 0xF9, 0xDC, 0x6B, 0x80, 0xD6, \
+        0x3F, 0xDD, 0x4A, 0x8E, 0x9A, 0xDB, 0x1E, 0x69, \
+        0x62, 0xA6, 0x95, 0x26, 0xD4, 0x31, 0x61, 0xC1, \
+        0xA4, 0x1D, 0x57, 0x0D, 0x79, 0x38, 0xDA, 0xD4, \
+        0xA4, 0x0E, 0x32, 0x9C, 0xD0, 0xE4, 0x0E, 0x65, \
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }
 
 #define MBEDTLS_DHM_RFC7919_FFDHE6144_G_BIN { 0x02 }
 
 #define MBEDTLS_DHM_RFC7919_FFDHE8192_P_BIN {        \
-     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \
-     0xAD, 0xF8, 0x54, 0x58, 0xA2, 0xBB, 0x4A, 0x9A, \
-     0xAF, 0xDC, 0x56, 0x20, 0x27, 0x3D, 0x3C, 0xF1, \
-     0xD8, 0xB9, 0xC5, 0x83, 0xCE, 0x2D, 0x36, 0x95, \
-     0xA9, 0xE1, 0x36, 0x41, 0x14, 0x64, 0x33, 0xFB, \
-     0xCC, 0x93, 0x9D, 0xCE, 0x24, 0x9B, 0x3E, 0xF9, \
-     0x7D, 0x2F, 0xE3, 0x63, 0x63, 0x0C, 0x75, 0xD8, \
-     0xF6, 0x81, 0xB2, 0x02, 0xAE, 0xC4, 0x61, 0x7A, \
-     0xD3, 0xDF, 0x1E, 0xD5, 0xD5, 0xFD, 0x65, 0x61, \
-     0x24, 0x33, 0xF5, 0x1F, 0x5F, 0x06, 0x6E, 0xD0, \
-     0x85, 0x63, 0x65, 0x55, 0x3D, 0xED, 0x1A, 0xF3, \
-     0xB5, 0x57, 0x13, 0x5E, 0x7F, 0x57, 0xC9, 0x35, \
-     0x98, 0x4F, 0x0C, 0x70, 0xE0, 0xE6, 0x8B, 0x77, \
-     0xE2, 0xA6, 0x89, 0xDA, 0xF3, 0xEF, 0xE8, 0x72, \
-     0x1D, 0xF1, 0x58, 0xA1, 0x36, 0xAD, 0xE7, 0x35, \
-     0x30, 0xAC, 0xCA, 0x4F, 0x48, 0x3A, 0x79, 0x7A, \
-     0xBC, 0x0A, 0xB1, 0x82, 0xB3, 0x24, 0xFB, 0x61, \
-     0xD1, 0x08, 0xA9, 0x4B, 0xB2, 0xC8, 0xE3, 0xFB, \
-     0xB9, 0x6A, 0xDA, 0xB7, 0x60, 0xD7, 0xF4, 0x68, \
-     0x1D, 0x4F, 0x42, 0xA3, 0xDE, 0x39, 0x4D, 0xF4, \
-     0xAE, 0x56, 0xED, 0xE7, 0x63, 0x72, 0xBB, 0x19, \
-     0x0B, 0x07, 0xA7, 0xC8, 0xEE, 0x0A, 0x6D, 0x70, \
-     0x9E, 0x02, 0xFC, 0xE1, 0xCD, 0xF7, 0xE2, 0xEC, \
-     0xC0, 0x34, 0x04, 0xCD, 0x28, 0x34, 0x2F, 0x61, \
-     0x91, 0x72, 0xFE, 0x9C, 0xE9, 0x85, 0x83, 0xFF, \
-     0x8E, 0x4F, 0x12, 0x32, 0xEE, 0xF2, 0x81, 0x83, \
-     0xC3, 0xFE, 0x3B, 0x1B, 0x4C, 0x6F, 0xAD, 0x73, \
-     0x3B, 0xB5, 0xFC, 0xBC, 0x2E, 0xC2, 0x20, 0x05, \
-     0xC5, 0x8E, 0xF1, 0x83, 0x7D, 0x16, 0x83, 0xB2, \
-     0xC6, 0xF3, 0x4A, 0x26, 0xC1, 0xB2, 0xEF, 0xFA, \
-     0x88, 0x6B, 0x42, 0x38, 0x61, 0x1F, 0xCF, 0xDC, \
-     0xDE, 0x35, 0x5B, 0x3B, 0x65, 0x19, 0x03, 0x5B, \
-     0xBC, 0x34, 0xF4, 0xDE, 0xF9, 0x9C, 0x02, 0x38, \
-     0x61, 0xB4, 0x6F, 0xC9, 0xD6, 0xE6, 0xC9, 0x07, \
-     0x7A, 0xD9, 0x1D, 0x26, 0x91, 0xF7, 0xF7, 0xEE, \
-     0x59, 0x8C, 0xB0, 0xFA, 0xC1, 0x86, 0xD9, 0x1C, \
-     0xAE, 0xFE, 0x13, 0x09, 0x85, 0x13, 0x92, 0x70, \
-     0xB4, 0x13, 0x0C, 0x93, 0xBC, 0x43, 0x79, 0x44, \
-     0xF4, 0xFD, 0x44, 0x52, 0xE2, 0xD7, 0x4D, 0xD3, \
-     0x64, 0xF2, 0xE2, 0x1E, 0x71, 0xF5, 0x4B, 0xFF, \
-     0x5C, 0xAE, 0x82, 0xAB, 0x9C, 0x9D, 0xF6, 0x9E, \
-     0xE8, 0x6D, 0x2B, 0xC5, 0x22, 0x36, 0x3A, 0x0D, \
-     0xAB, 0xC5, 0x21, 0x97, 0x9B, 0x0D, 0xEA, 0xDA, \
-     0x1D, 0xBF, 0x9A, 0x42, 0xD5, 0xC4, 0x48, 0x4E, \
-     0x0A, 0xBC, 0xD0, 0x6B, 0xFA, 0x53, 0xDD, 0xEF, \
-     0x3C, 0x1B, 0x20, 0xEE, 0x3F, 0xD5, 0x9D, 0x7C, \
-     0x25, 0xE4, 0x1D, 0x2B, 0x66, 0x9E, 0x1E, 0xF1, \
-     0x6E, 0x6F, 0x52, 0xC3, 0x16, 0x4D, 0xF4, 0xFB, \
-     0x79, 0x30, 0xE9, 0xE4, 0xE5, 0x88, 0x57, 0xB6, \
-     0xAC, 0x7D, 0x5F, 0x42, 0xD6, 0x9F, 0x6D, 0x18, \
-     0x77, 0x63, 0xCF, 0x1D, 0x55, 0x03, 0x40, 0x04, \
-     0x87, 0xF5, 0x5B, 0xA5, 0x7E, 0x31, 0xCC, 0x7A, \
-     0x71, 0x35, 0xC8, 0x86, 0xEF, 0xB4, 0x31, 0x8A, \
-     0xED, 0x6A, 0x1E, 0x01, 0x2D, 0x9E, 0x68, 0x32, \
-     0xA9, 0x07, 0x60, 0x0A, 0x91, 0x81, 0x30, 0xC4, \
-     0x6D, 0xC7, 0x78, 0xF9, 0x71, 0xAD, 0x00, 0x38, \
-     0x09, 0x29, 0x99, 0xA3, 0x33, 0xCB, 0x8B, 0x7A, \
-     0x1A, 0x1D, 0xB9, 0x3D, 0x71, 0x40, 0x00, 0x3C, \
-     0x2A, 0x4E, 0xCE, 0xA9, 0xF9, 0x8D, 0x0A, 0xCC, \
-     0x0A, 0x82, 0x91, 0xCD, 0xCE, 0xC9, 0x7D, 0xCF, \
-     0x8E, 0xC9, 0xB5, 0x5A, 0x7F, 0x88, 0xA4, 0x6B, \
-     0x4D, 0xB5, 0xA8, 0x51, 0xF4, 0x41, 0x82, 0xE1, \
-     0xC6, 0x8A, 0x00, 0x7E, 0x5E, 0x0D, 0xD9, 0x02, \
-     0x0B, 0xFD, 0x64, 0xB6, 0x45, 0x03, 0x6C, 0x7A, \
-     0x4E, 0x67, 0x7D, 0x2C, 0x38, 0x53, 0x2A, 0x3A, \
-     0x23, 0xBA, 0x44, 0x42, 0xCA, 0xF5, 0x3E, 0xA6, \
-     0x3B, 0xB4, 0x54, 0x32, 0x9B, 0x76, 0x24, 0xC8, \
-     0x91, 0x7B, 0xDD, 0x64, 0xB1, 0xC0, 0xFD, 0x4C, \
-     0xB3, 0x8E, 0x8C, 0x33, 0x4C, 0x70, 0x1C, 0x3A, \
-     0xCD, 0xAD, 0x06, 0x57, 0xFC, 0xCF, 0xEC, 0x71, \
-     0x9B, 0x1F, 0x5C, 0x3E, 0x4E, 0x46, 0x04, 0x1F, \
-     0x38, 0x81, 0x47, 0xFB, 0x4C, 0xFD, 0xB4, 0x77, \
-     0xA5, 0x24, 0x71, 0xF7, 0xA9, 0xA9, 0x69, 0x10, \
-     0xB8, 0x55, 0x32, 0x2E, 0xDB, 0x63, 0x40, 0xD8, \
-     0xA0, 0x0E, 0xF0, 0x92, 0x35, 0x05, 0x11, 0xE3, \
-     0x0A, 0xBE, 0xC1, 0xFF, 0xF9, 0xE3, 0xA2, 0x6E, \
-     0x7F, 0xB2, 0x9F, 0x8C, 0x18, 0x30, 0x23, 0xC3, \
-     0x58, 0x7E, 0x38, 0xDA, 0x00, 0x77, 0xD9, 0xB4, \
-     0x76, 0x3E, 0x4E, 0x4B, 0x94, 0xB2, 0xBB, 0xC1, \
-     0x94, 0xC6, 0x65, 0x1E, 0x77, 0xCA, 0xF9, 0x92, \
-     0xEE, 0xAA, 0xC0, 0x23, 0x2A, 0x28, 0x1B, 0xF6, \
-     0xB3, 0xA7, 0x39, 0xC1, 0x22, 0x61, 0x16, 0x82, \
-     0x0A, 0xE8, 0xDB, 0x58, 0x47, 0xA6, 0x7C, 0xBE, \
-     0xF9, 0xC9, 0x09, 0x1B, 0x46, 0x2D, 0x53, 0x8C, \
-     0xD7, 0x2B, 0x03, 0x74, 0x6A, 0xE7, 0x7F, 0x5E, \
-     0x62, 0x29, 0x2C, 0x31, 0x15, 0x62, 0xA8, 0x46, \
-     0x50, 0x5D, 0xC8, 0x2D, 0xB8, 0x54, 0x33, 0x8A, \
-     0xE4, 0x9F, 0x52, 0x35, 0xC9, 0x5B, 0x91, 0x17, \
-     0x8C, 0xCF, 0x2D, 0xD5, 0xCA, 0xCE, 0xF4, 0x03, \
-     0xEC, 0x9D, 0x18, 0x10, 0xC6, 0x27, 0x2B, 0x04, \
-     0x5B, 0x3B, 0x71, 0xF9, 0xDC, 0x6B, 0x80, 0xD6, \
-     0x3F, 0xDD, 0x4A, 0x8E, 0x9A, 0xDB, 0x1E, 0x69, \
-     0x62, 0xA6, 0x95, 0x26, 0xD4, 0x31, 0x61, 0xC1, \
-     0xA4, 0x1D, 0x57, 0x0D, 0x79, 0x38, 0xDA, 0xD4, \
-     0xA4, 0x0E, 0x32, 0x9C, 0xCF, 0xF4, 0x6A, 0xAA, \
-     0x36, 0xAD, 0x00, 0x4C, 0xF6, 0x00, 0xC8, 0x38, \
-     0x1E, 0x42, 0x5A, 0x31, 0xD9, 0x51, 0xAE, 0x64, \
-     0xFD, 0xB2, 0x3F, 0xCE, 0xC9, 0x50, 0x9D, 0x43, \
-     0x68, 0x7F, 0xEB, 0x69, 0xED, 0xD1, 0xCC, 0x5E, \
-     0x0B, 0x8C, 0xC3, 0xBD, 0xF6, 0x4B, 0x10, 0xEF, \
-     0x86, 0xB6, 0x31, 0x42, 0xA3, 0xAB, 0x88, 0x29, \
-     0x55, 0x5B, 0x2F, 0x74, 0x7C, 0x93, 0x26, 0x65, \
-     0xCB, 0x2C, 0x0F, 0x1C, 0xC0, 0x1B, 0xD7, 0x02, \
-     0x29, 0x38, 0x88, 0x39, 0xD2, 0xAF, 0x05, 0xE4, \
-     0x54, 0x50, 0x4A, 0xC7, 0x8B, 0x75, 0x82, 0x82, \
-     0x28, 0x46, 0xC0, 0xBA, 0x35, 0xC3, 0x5F, 0x5C, \
-     0x59, 0x16, 0x0C, 0xC0, 0x46, 0xFD, 0x82, 0x51, \
-     0x54, 0x1F, 0xC6, 0x8C, 0x9C, 0x86, 0xB0, 0x22, \
-     0xBB, 0x70, 0x99, 0x87, 0x6A, 0x46, 0x0E, 0x74, \
-     0x51, 0xA8, 0xA9, 0x31, 0x09, 0x70, 0x3F, 0xEE, \
-     0x1C, 0x21, 0x7E, 0x6C, 0x38, 0x26, 0xE5, 0x2C, \
-     0x51, 0xAA, 0x69, 0x1E, 0x0E, 0x42, 0x3C, 0xFC, \
-     0x99, 0xE9, 0xE3, 0x16, 0x50, 0xC1, 0x21, 0x7B, \
-     0x62, 0x48, 0x16, 0xCD, 0xAD, 0x9A, 0x95, 0xF9, \
-     0xD5, 0xB8, 0x01, 0x94, 0x88, 0xD9, 0xC0, 0xA0, \
-     0xA1, 0xFE, 0x30, 0x75, 0xA5, 0x77, 0xE2, 0x31, \
-     0x83, 0xF8, 0x1D, 0x4A, 0x3F, 0x2F, 0xA4, 0x57, \
-     0x1E, 0xFC, 0x8C, 0xE0, 0xBA, 0x8A, 0x4F, 0xE8, \
-     0xB6, 0x85, 0x5D, 0xFE, 0x72, 0xB0, 0xA6, 0x6E, \
-     0xDE, 0xD2, 0xFB, 0xAB, 0xFB, 0xE5, 0x8A, 0x30, \
-     0xFA, 0xFA, 0xBE, 0x1C, 0x5D, 0x71, 0xA8, 0x7E, \
-     0x2F, 0x74, 0x1E, 0xF8, 0xC1, 0xFE, 0x86, 0xFE, \
-     0xA6, 0xBB, 0xFD, 0xE5, 0x30, 0x67, 0x7F, 0x0D, \
-     0x97, 0xD1, 0x1D, 0x49, 0xF7, 0xA8, 0x44, 0x3D, \
-     0x08, 0x22, 0xE5, 0x06, 0xA9, 0xF4, 0x61, 0x4E, \
-     0x01, 0x1E, 0x2A, 0x94, 0x83, 0x8F, 0xF8, 0x8C, \
-     0xD6, 0x8C, 0x8B, 0xB7, 0xC5, 0xC6, 0x42, 0x4C, \
-     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \
+        0xAD, 0xF8, 0x54, 0x58, 0xA2, 0xBB, 0x4A, 0x9A, \
+        0xAF, 0xDC, 0x56, 0x20, 0x27, 0x3D, 0x3C, 0xF1, \
+        0xD8, 0xB9, 0xC5, 0x83, 0xCE, 0x2D, 0x36, 0x95, \
+        0xA9, 0xE1, 0x36, 0x41, 0x14, 0x64, 0x33, 0xFB, \
+        0xCC, 0x93, 0x9D, 0xCE, 0x24, 0x9B, 0x3E, 0xF9, \
+        0x7D, 0x2F, 0xE3, 0x63, 0x63, 0x0C, 0x75, 0xD8, \
+        0xF6, 0x81, 0xB2, 0x02, 0xAE, 0xC4, 0x61, 0x7A, \
+        0xD3, 0xDF, 0x1E, 0xD5, 0xD5, 0xFD, 0x65, 0x61, \
+        0x24, 0x33, 0xF5, 0x1F, 0x5F, 0x06, 0x6E, 0xD0, \
+        0x85, 0x63, 0x65, 0x55, 0x3D, 0xED, 0x1A, 0xF3, \
+        0xB5, 0x57, 0x13, 0x5E, 0x7F, 0x57, 0xC9, 0x35, \
+        0x98, 0x4F, 0x0C, 0x70, 0xE0, 0xE6, 0x8B, 0x77, \
+        0xE2, 0xA6, 0x89, 0xDA, 0xF3, 0xEF, 0xE8, 0x72, \
+        0x1D, 0xF1, 0x58, 0xA1, 0x36, 0xAD, 0xE7, 0x35, \
+        0x30, 0xAC, 0xCA, 0x4F, 0x48, 0x3A, 0x79, 0x7A, \
+        0xBC, 0x0A, 0xB1, 0x82, 0xB3, 0x24, 0xFB, 0x61, \
+        0xD1, 0x08, 0xA9, 0x4B, 0xB2, 0xC8, 0xE3, 0xFB, \
+        0xB9, 0x6A, 0xDA, 0xB7, 0x60, 0xD7, 0xF4, 0x68, \
+        0x1D, 0x4F, 0x42, 0xA3, 0xDE, 0x39, 0x4D, 0xF4, \
+        0xAE, 0x56, 0xED, 0xE7, 0x63, 0x72, 0xBB, 0x19, \
+        0x0B, 0x07, 0xA7, 0xC8, 0xEE, 0x0A, 0x6D, 0x70, \
+        0x9E, 0x02, 0xFC, 0xE1, 0xCD, 0xF7, 0xE2, 0xEC, \
+        0xC0, 0x34, 0x04, 0xCD, 0x28, 0x34, 0x2F, 0x61, \
+        0x91, 0x72, 0xFE, 0x9C, 0xE9, 0x85, 0x83, 0xFF, \
+        0x8E, 0x4F, 0x12, 0x32, 0xEE, 0xF2, 0x81, 0x83, \
+        0xC3, 0xFE, 0x3B, 0x1B, 0x4C, 0x6F, 0xAD, 0x73, \
+        0x3B, 0xB5, 0xFC, 0xBC, 0x2E, 0xC2, 0x20, 0x05, \
+        0xC5, 0x8E, 0xF1, 0x83, 0x7D, 0x16, 0x83, 0xB2, \
+        0xC6, 0xF3, 0x4A, 0x26, 0xC1, 0xB2, 0xEF, 0xFA, \
+        0x88, 0x6B, 0x42, 0x38, 0x61, 0x1F, 0xCF, 0xDC, \
+        0xDE, 0x35, 0x5B, 0x3B, 0x65, 0x19, 0x03, 0x5B, \
+        0xBC, 0x34, 0xF4, 0xDE, 0xF9, 0x9C, 0x02, 0x38, \
+        0x61, 0xB4, 0x6F, 0xC9, 0xD6, 0xE6, 0xC9, 0x07, \
+        0x7A, 0xD9, 0x1D, 0x26, 0x91, 0xF7, 0xF7, 0xEE, \
+        0x59, 0x8C, 0xB0, 0xFA, 0xC1, 0x86, 0xD9, 0x1C, \
+        0xAE, 0xFE, 0x13, 0x09, 0x85, 0x13, 0x92, 0x70, \
+        0xB4, 0x13, 0x0C, 0x93, 0xBC, 0x43, 0x79, 0x44, \
+        0xF4, 0xFD, 0x44, 0x52, 0xE2, 0xD7, 0x4D, 0xD3, \
+        0x64, 0xF2, 0xE2, 0x1E, 0x71, 0xF5, 0x4B, 0xFF, \
+        0x5C, 0xAE, 0x82, 0xAB, 0x9C, 0x9D, 0xF6, 0x9E, \
+        0xE8, 0x6D, 0x2B, 0xC5, 0x22, 0x36, 0x3A, 0x0D, \
+        0xAB, 0xC5, 0x21, 0x97, 0x9B, 0x0D, 0xEA, 0xDA, \
+        0x1D, 0xBF, 0x9A, 0x42, 0xD5, 0xC4, 0x48, 0x4E, \
+        0x0A, 0xBC, 0xD0, 0x6B, 0xFA, 0x53, 0xDD, 0xEF, \
+        0x3C, 0x1B, 0x20, 0xEE, 0x3F, 0xD5, 0x9D, 0x7C, \
+        0x25, 0xE4, 0x1D, 0x2B, 0x66, 0x9E, 0x1E, 0xF1, \
+        0x6E, 0x6F, 0x52, 0xC3, 0x16, 0x4D, 0xF4, 0xFB, \
+        0x79, 0x30, 0xE9, 0xE4, 0xE5, 0x88, 0x57, 0xB6, \
+        0xAC, 0x7D, 0x5F, 0x42, 0xD6, 0x9F, 0x6D, 0x18, \
+        0x77, 0x63, 0xCF, 0x1D, 0x55, 0x03, 0x40, 0x04, \
+        0x87, 0xF5, 0x5B, 0xA5, 0x7E, 0x31, 0xCC, 0x7A, \
+        0x71, 0x35, 0xC8, 0x86, 0xEF, 0xB4, 0x31, 0x8A, \
+        0xED, 0x6A, 0x1E, 0x01, 0x2D, 0x9E, 0x68, 0x32, \
+        0xA9, 0x07, 0x60, 0x0A, 0x91, 0x81, 0x30, 0xC4, \
+        0x6D, 0xC7, 0x78, 0xF9, 0x71, 0xAD, 0x00, 0x38, \
+        0x09, 0x29, 0x99, 0xA3, 0x33, 0xCB, 0x8B, 0x7A, \
+        0x1A, 0x1D, 0xB9, 0x3D, 0x71, 0x40, 0x00, 0x3C, \
+        0x2A, 0x4E, 0xCE, 0xA9, 0xF9, 0x8D, 0x0A, 0xCC, \
+        0x0A, 0x82, 0x91, 0xCD, 0xCE, 0xC9, 0x7D, 0xCF, \
+        0x8E, 0xC9, 0xB5, 0x5A, 0x7F, 0x88, 0xA4, 0x6B, \
+        0x4D, 0xB5, 0xA8, 0x51, 0xF4, 0x41, 0x82, 0xE1, \
+        0xC6, 0x8A, 0x00, 0x7E, 0x5E, 0x0D, 0xD9, 0x02, \
+        0x0B, 0xFD, 0x64, 0xB6, 0x45, 0x03, 0x6C, 0x7A, \
+        0x4E, 0x67, 0x7D, 0x2C, 0x38, 0x53, 0x2A, 0x3A, \
+        0x23, 0xBA, 0x44, 0x42, 0xCA, 0xF5, 0x3E, 0xA6, \
+        0x3B, 0xB4, 0x54, 0x32, 0x9B, 0x76, 0x24, 0xC8, \
+        0x91, 0x7B, 0xDD, 0x64, 0xB1, 0xC0, 0xFD, 0x4C, \
+        0xB3, 0x8E, 0x8C, 0x33, 0x4C, 0x70, 0x1C, 0x3A, \
+        0xCD, 0xAD, 0x06, 0x57, 0xFC, 0xCF, 0xEC, 0x71, \
+        0x9B, 0x1F, 0x5C, 0x3E, 0x4E, 0x46, 0x04, 0x1F, \
+        0x38, 0x81, 0x47, 0xFB, 0x4C, 0xFD, 0xB4, 0x77, \
+        0xA5, 0x24, 0x71, 0xF7, 0xA9, 0xA9, 0x69, 0x10, \
+        0xB8, 0x55, 0x32, 0x2E, 0xDB, 0x63, 0x40, 0xD8, \
+        0xA0, 0x0E, 0xF0, 0x92, 0x35, 0x05, 0x11, 0xE3, \
+        0x0A, 0xBE, 0xC1, 0xFF, 0xF9, 0xE3, 0xA2, 0x6E, \
+        0x7F, 0xB2, 0x9F, 0x8C, 0x18, 0x30, 0x23, 0xC3, \
+        0x58, 0x7E, 0x38, 0xDA, 0x00, 0x77, 0xD9, 0xB4, \
+        0x76, 0x3E, 0x4E, 0x4B, 0x94, 0xB2, 0xBB, 0xC1, \
+        0x94, 0xC6, 0x65, 0x1E, 0x77, 0xCA, 0xF9, 0x92, \
+        0xEE, 0xAA, 0xC0, 0x23, 0x2A, 0x28, 0x1B, 0xF6, \
+        0xB3, 0xA7, 0x39, 0xC1, 0x22, 0x61, 0x16, 0x82, \
+        0x0A, 0xE8, 0xDB, 0x58, 0x47, 0xA6, 0x7C, 0xBE, \
+        0xF9, 0xC9, 0x09, 0x1B, 0x46, 0x2D, 0x53, 0x8C, \
+        0xD7, 0x2B, 0x03, 0x74, 0x6A, 0xE7, 0x7F, 0x5E, \
+        0x62, 0x29, 0x2C, 0x31, 0x15, 0x62, 0xA8, 0x46, \
+        0x50, 0x5D, 0xC8, 0x2D, 0xB8, 0x54, 0x33, 0x8A, \
+        0xE4, 0x9F, 0x52, 0x35, 0xC9, 0x5B, 0x91, 0x17, \
+        0x8C, 0xCF, 0x2D, 0xD5, 0xCA, 0xCE, 0xF4, 0x03, \
+        0xEC, 0x9D, 0x18, 0x10, 0xC6, 0x27, 0x2B, 0x04, \
+        0x5B, 0x3B, 0x71, 0xF9, 0xDC, 0x6B, 0x80, 0xD6, \
+        0x3F, 0xDD, 0x4A, 0x8E, 0x9A, 0xDB, 0x1E, 0x69, \
+        0x62, 0xA6, 0x95, 0x26, 0xD4, 0x31, 0x61, 0xC1, \
+        0xA4, 0x1D, 0x57, 0x0D, 0x79, 0x38, 0xDA, 0xD4, \
+        0xA4, 0x0E, 0x32, 0x9C, 0xCF, 0xF4, 0x6A, 0xAA, \
+        0x36, 0xAD, 0x00, 0x4C, 0xF6, 0x00, 0xC8, 0x38, \
+        0x1E, 0x42, 0x5A, 0x31, 0xD9, 0x51, 0xAE, 0x64, \
+        0xFD, 0xB2, 0x3F, 0xCE, 0xC9, 0x50, 0x9D, 0x43, \
+        0x68, 0x7F, 0xEB, 0x69, 0xED, 0xD1, 0xCC, 0x5E, \
+        0x0B, 0x8C, 0xC3, 0xBD, 0xF6, 0x4B, 0x10, 0xEF, \
+        0x86, 0xB6, 0x31, 0x42, 0xA3, 0xAB, 0x88, 0x29, \
+        0x55, 0x5B, 0x2F, 0x74, 0x7C, 0x93, 0x26, 0x65, \
+        0xCB, 0x2C, 0x0F, 0x1C, 0xC0, 0x1B, 0xD7, 0x02, \
+        0x29, 0x38, 0x88, 0x39, 0xD2, 0xAF, 0x05, 0xE4, \
+        0x54, 0x50, 0x4A, 0xC7, 0x8B, 0x75, 0x82, 0x82, \
+        0x28, 0x46, 0xC0, 0xBA, 0x35, 0xC3, 0x5F, 0x5C, \
+        0x59, 0x16, 0x0C, 0xC0, 0x46, 0xFD, 0x82, 0x51, \
+        0x54, 0x1F, 0xC6, 0x8C, 0x9C, 0x86, 0xB0, 0x22, \
+        0xBB, 0x70, 0x99, 0x87, 0x6A, 0x46, 0x0E, 0x74, \
+        0x51, 0xA8, 0xA9, 0x31, 0x09, 0x70, 0x3F, 0xEE, \
+        0x1C, 0x21, 0x7E, 0x6C, 0x38, 0x26, 0xE5, 0x2C, \
+        0x51, 0xAA, 0x69, 0x1E, 0x0E, 0x42, 0x3C, 0xFC, \
+        0x99, 0xE9, 0xE3, 0x16, 0x50, 0xC1, 0x21, 0x7B, \
+        0x62, 0x48, 0x16, 0xCD, 0xAD, 0x9A, 0x95, 0xF9, \
+        0xD5, 0xB8, 0x01, 0x94, 0x88, 0xD9, 0xC0, 0xA0, \
+        0xA1, 0xFE, 0x30, 0x75, 0xA5, 0x77, 0xE2, 0x31, \
+        0x83, 0xF8, 0x1D, 0x4A, 0x3F, 0x2F, 0xA4, 0x57, \
+        0x1E, 0xFC, 0x8C, 0xE0, 0xBA, 0x8A, 0x4F, 0xE8, \
+        0xB6, 0x85, 0x5D, 0xFE, 0x72, 0xB0, 0xA6, 0x6E, \
+        0xDE, 0xD2, 0xFB, 0xAB, 0xFB, 0xE5, 0x8A, 0x30, \
+        0xFA, 0xFA, 0xBE, 0x1C, 0x5D, 0x71, 0xA8, 0x7E, \
+        0x2F, 0x74, 0x1E, 0xF8, 0xC1, 0xFE, 0x86, 0xFE, \
+        0xA6, 0xBB, 0xFD, 0xE5, 0x30, 0x67, 0x7F, 0x0D, \
+        0x97, 0xD1, 0x1D, 0x49, 0xF7, 0xA8, 0x44, 0x3D, \
+        0x08, 0x22, 0xE5, 0x06, 0xA9, 0xF4, 0x61, 0x4E, \
+        0x01, 0x1E, 0x2A, 0x94, 0x83, 0x8F, 0xF8, 0x8C, \
+        0xD6, 0x8C, 0x8B, 0xB7, 0xC5, 0xC6, 0x42, 0x4C, \
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }
 
 #define MBEDTLS_DHM_RFC7919_FFDHE8192_G_BIN { 0x02 }
 
diff --git a/third_party/mbedtls/repo/include/mbedtls/ecdh.h b/third_party/mbedtls/repo/include/mbedtls/ecdh.h
index 05855cd..6cc6cb9 100644
--- a/third_party/mbedtls/repo/include/mbedtls/ecdh.h
+++ b/third_party/mbedtls/repo/include/mbedtls/ecdh.h
@@ -14,19 +14,7 @@
  */
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 
 #ifndef MBEDTLS_ECDH_H
@@ -52,8 +40,7 @@
 /**
  * Defines the source of the imported EC key.
  */
-typedef enum
-{
+typedef enum {
     MBEDTLS_ECDH_OURS,   /**< Our key. */
     MBEDTLS_ECDH_THEIRS, /**< The key of the peer. */
 } mbedtls_ecdh_side;
@@ -65,8 +52,7 @@
  * Later versions of the library may add new variants, therefore users should
  * not make any assumptions about them.
  */
-typedef enum
-{
+typedef enum {
     MBEDTLS_ECDH_VARIANT_NONE = 0,   /*!< Implementation not defined. */
     MBEDTLS_ECDH_VARIANT_MBEDTLS_2_0,/*!< The default Mbed TLS implementation */
 #if defined(MBEDTLS_ECDH_VARIANT_EVEREST_ENABLED)
@@ -81,8 +67,7 @@
  * should not make any assumptions about the structure of
  * mbedtls_ecdh_context_mbed.
  */
-typedef struct mbedtls_ecdh_context_mbed
-{
+typedef struct mbedtls_ecdh_context_mbed {
     mbedtls_ecp_group grp;   /*!< The elliptic curve used. */
     mbedtls_mpi d;           /*!< The private key. */
     mbedtls_ecp_point Q;     /*!< The public key. */
@@ -101,8 +86,7 @@
  *                  should not be shared between multiple threads.
  * \brief           The ECDH context structure.
  */
-typedef struct mbedtls_ecdh_context
-{
+typedef struct mbedtls_ecdh_context {
 #if defined(MBEDTLS_ECDH_LEGACY_CONTEXT)
     mbedtls_ecp_group grp;   /*!< The elliptic curve used. */
     mbedtls_mpi d;           /*!< The private key. */
@@ -119,24 +103,23 @@
 #endif /* MBEDTLS_ECP_RESTARTABLE */
 #else
     uint8_t point_format;       /*!< The format of point export in TLS messages
-                                  as defined in RFC 4492. */
+                                   as defined in RFC 4492. */
     mbedtls_ecp_group_id grp_id;/*!< The elliptic curve used. */
     mbedtls_ecdh_variant var;   /*!< The ECDH implementation/structure used. */
-    union
-    {
+    union {
         mbedtls_ecdh_context_mbed   mbed_ecdh;
 #if defined(MBEDTLS_ECDH_VARIANT_EVEREST_ENABLED)
         mbedtls_ecdh_context_everest everest_ecdh;
 #endif
     } ctx;                      /*!< Implementation-specific context. The
-                                  context in use is specified by the \c var
-                                  field. */
+                                   context in use is specified by the \c var
+                                   field. */
 #if defined(MBEDTLS_ECP_RESTARTABLE)
     uint8_t restart_enabled;    /*!< The flag for restartable mode. Functions of
-                                  an alternative implementation not supporting
-                                  restartable mode must return
-                                  MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED error
-                                  if this flag is set. */
+                                   an alternative implementation not supporting
+                                   restartable mode must return
+                                   MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED error
+                                   if this flag is set. */
 #endif /* MBEDTLS_ECP_RESTARTABLE */
 #endif /* MBEDTLS_ECDH_LEGACY_CONTEXT */
 }
@@ -149,7 +132,7 @@
  *
  * \return         \c 1 if the group can be used, \c 0 otherwise
  */
-int mbedtls_ecdh_can_do( mbedtls_ecp_group_id gid );
+int mbedtls_ecdh_can_do(mbedtls_ecp_group_id gid);
 
 /**
  * \brief           This function generates an ECDH keypair on an elliptic
@@ -176,9 +159,9 @@
  * \return          Another \c MBEDTLS_ERR_ECP_XXX or
  *                  \c MBEDTLS_MPI_XXX error code on failure.
  */
-int mbedtls_ecdh_gen_public( mbedtls_ecp_group *grp, mbedtls_mpi *d, mbedtls_ecp_point *Q,
-                     int (*f_rng)(void *, unsigned char *, size_t),
-                     void *p_rng );
+int mbedtls_ecdh_gen_public(mbedtls_ecp_group *grp, mbedtls_mpi *d, mbedtls_ecp_point *Q,
+                            int (*f_rng)(void *, unsigned char *, size_t),
+                            void *p_rng);
 
 /**
  * \brief           This function computes the shared secret.
@@ -214,17 +197,17 @@
  * \return          Another \c MBEDTLS_ERR_ECP_XXX or
  *                  \c MBEDTLS_MPI_XXX error code on failure.
  */
-int mbedtls_ecdh_compute_shared( mbedtls_ecp_group *grp, mbedtls_mpi *z,
-                         const mbedtls_ecp_point *Q, const mbedtls_mpi *d,
-                         int (*f_rng)(void *, unsigned char *, size_t),
-                         void *p_rng );
+int mbedtls_ecdh_compute_shared(mbedtls_ecp_group *grp, mbedtls_mpi *z,
+                                const mbedtls_ecp_point *Q, const mbedtls_mpi *d,
+                                int (*f_rng)(void *, unsigned char *, size_t),
+                                void *p_rng);
 
 /**
  * \brief           This function initializes an ECDH context.
  *
  * \param ctx       The ECDH context to initialize. This must not be \c NULL.
  */
-void mbedtls_ecdh_init( mbedtls_ecdh_context *ctx );
+void mbedtls_ecdh_init(mbedtls_ecdh_context *ctx);
 
 /**
  * \brief           This function sets up the ECDH context with the information
@@ -242,8 +225,8 @@
  *
  * \return          \c 0 on success.
  */
-int mbedtls_ecdh_setup( mbedtls_ecdh_context *ctx,
-                        mbedtls_ecp_group_id grp_id );
+int mbedtls_ecdh_setup(mbedtls_ecdh_context *ctx,
+                       mbedtls_ecp_group_id grp_id);
 
 /**
  * \brief           This function frees a context.
@@ -252,7 +235,7 @@
  *                  case this function does nothing. If it is not \c NULL,
  *                  it must point to an initialized ECDH context.
  */
-void mbedtls_ecdh_free( mbedtls_ecdh_context *ctx );
+void mbedtls_ecdh_free(mbedtls_ecdh_context *ctx);
 
 /**
  * \brief           This function generates an EC key pair and exports its
@@ -279,10 +262,10 @@
  *                  operations was reached: see \c mbedtls_ecp_set_max_ops().
  * \return          Another \c MBEDTLS_ERR_ECP_XXX error code on failure.
  */
-int mbedtls_ecdh_make_params( mbedtls_ecdh_context *ctx, size_t *olen,
-                      unsigned char *buf, size_t blen,
-                      int (*f_rng)(void *, unsigned char *, size_t),
-                      void *p_rng );
+int mbedtls_ecdh_make_params(mbedtls_ecdh_context *ctx, size_t *olen,
+                             unsigned char *buf, size_t blen,
+                             int (*f_rng)(void *, unsigned char *, size_t),
+                             void *p_rng);
 
 /**
  * \brief           This function parses the ECDHE parameters in a
@@ -308,9 +291,9 @@
  * \return          An \c MBEDTLS_ERR_ECP_XXX error code on failure.
  *
  */
-int mbedtls_ecdh_read_params( mbedtls_ecdh_context *ctx,
-                              const unsigned char **buf,
-                              const unsigned char *end );
+int mbedtls_ecdh_read_params(mbedtls_ecdh_context *ctx,
+                             const unsigned char **buf,
+                             const unsigned char *end);
 
 /**
  * \brief           This function sets up an ECDH context from an EC key.
@@ -331,9 +314,9 @@
  * \return          Another \c MBEDTLS_ERR_ECP_XXX error code on failure.
  *
  */
-int mbedtls_ecdh_get_params( mbedtls_ecdh_context *ctx,
-                             const mbedtls_ecp_keypair *key,
-                             mbedtls_ecdh_side side );
+int mbedtls_ecdh_get_params(mbedtls_ecdh_context *ctx,
+                            const mbedtls_ecp_keypair *key,
+                            mbedtls_ecdh_side side);
 
 /**
  * \brief           This function generates a public key and exports it
@@ -361,10 +344,10 @@
  *                  operations was reached: see \c mbedtls_ecp_set_max_ops().
  * \return          Another \c MBEDTLS_ERR_ECP_XXX error code on failure.
  */
-int mbedtls_ecdh_make_public( mbedtls_ecdh_context *ctx, size_t *olen,
-                      unsigned char *buf, size_t blen,
-                      int (*f_rng)(void *, unsigned char *, size_t),
-                      void *p_rng );
+int mbedtls_ecdh_make_public(mbedtls_ecdh_context *ctx, size_t *olen,
+                             unsigned char *buf, size_t blen,
+                             int (*f_rng)(void *, unsigned char *, size_t),
+                             void *p_rng);
 
 /**
  * \brief       This function parses and processes the ECDHE payload of a
@@ -385,8 +368,8 @@
  * \return      \c 0 on success.
  * \return      An \c MBEDTLS_ERR_ECP_XXX error code on failure.
  */
-int mbedtls_ecdh_read_public( mbedtls_ecdh_context *ctx,
-                              const unsigned char *buf, size_t blen );
+int mbedtls_ecdh_read_public(mbedtls_ecdh_context *ctx,
+                             const unsigned char *buf, size_t blen);
 
 /**
  * \brief           This function derives and exports the shared secret.
@@ -418,10 +401,10 @@
  *                  operations was reached: see \c mbedtls_ecp_set_max_ops().
  * \return          Another \c MBEDTLS_ERR_ECP_XXX error code on failure.
  */
-int mbedtls_ecdh_calc_secret( mbedtls_ecdh_context *ctx, size_t *olen,
-                      unsigned char *buf, size_t blen,
-                      int (*f_rng)(void *, unsigned char *, size_t),
-                      void *p_rng );
+int mbedtls_ecdh_calc_secret(mbedtls_ecdh_context *ctx, size_t *olen,
+                             unsigned char *buf, size_t blen,
+                             int (*f_rng)(void *, unsigned char *, size_t),
+                             void *p_rng);
 
 #if defined(MBEDTLS_ECP_RESTARTABLE)
 /**
@@ -436,7 +419,7 @@
  *
  * \param ctx       The ECDH context to use. This must be initialized.
  */
-void mbedtls_ecdh_enable_restart( mbedtls_ecdh_context *ctx );
+void mbedtls_ecdh_enable_restart(mbedtls_ecdh_context *ctx);
 #endif /* MBEDTLS_ECP_RESTARTABLE */
 
 #ifdef __cplusplus
diff --git a/third_party/mbedtls/repo/include/mbedtls/ecdsa.h b/third_party/mbedtls/repo/include/mbedtls/ecdsa.h
index 264a638..34a6b13 100644
--- a/third_party/mbedtls/repo/include/mbedtls/ecdsa.h
+++ b/third_party/mbedtls/repo/include/mbedtls/ecdsa.h
@@ -12,19 +12,7 @@
  */
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 
 #ifndef MBEDTLS_ECDSA_H
@@ -56,13 +44,13 @@
  *
  * For each of r and s, the value (V) may include an extra initial "0" bit.
  */
-#define MBEDTLS_ECDSA_MAX_SIG_LEN( bits )                               \
-    ( /*T,L of SEQUENCE*/ ( ( bits ) >= 61 * 8 ? 3 : 2 ) +              \
-      /*T,L of r,s*/        2 * ( ( ( bits ) >= 127 * 8 ? 3 : 2 ) +     \
-      /*V of r,s*/                ( ( bits ) + 8 ) / 8 ) )
+#define MBEDTLS_ECDSA_MAX_SIG_LEN(bits)                               \
+    (/*T,L of SEQUENCE*/ ((bits) >= 61 * 8 ? 3 : 2) +              \
+     /*T,L of r,s*/ 2 * (((bits) >= 127 * 8 ? 3 : 2) +     \
+                         /*V of r,s*/ ((bits) + 8) / 8))
 
 /** The maximal size of an ECDSA signature in Bytes. */
-#define MBEDTLS_ECDSA_MAX_LEN  MBEDTLS_ECDSA_MAX_SIG_LEN( MBEDTLS_ECP_MAX_BITS )
+#define MBEDTLS_ECDSA_MAX_LEN  MBEDTLS_ECDSA_MAX_SIG_LEN(MBEDTLS_ECP_MAX_BITS)
 
 #ifdef __cplusplus
 extern "C" {
@@ -105,8 +93,7 @@
 /**
  * \brief           General context for resuming ECDSA operations
  */
-typedef struct
-{
+typedef struct {
     mbedtls_ecp_restart_ctx ecp;        /*!<  base context for ECP restart and
                                               shared administrative info    */
     mbedtls_ecdsa_restart_ver_ctx *ver; /*!<  ecdsa_verify() sub-context    */
@@ -131,7 +118,7 @@
  *
  * \return         \c 1 if the group can be used, \c 0 otherwise
  */
-int mbedtls_ecdsa_can_do( mbedtls_ecp_group_id gid );
+int mbedtls_ecdsa_can_do(mbedtls_ecp_group_id gid);
 
 /**
  * \brief           This function computes the ECDSA signature of a
@@ -169,12 +156,12 @@
  * \return          An \c MBEDTLS_ERR_ECP_XXX
  *                  or \c MBEDTLS_MPI_XXX error code on failure.
  */
-int mbedtls_ecdsa_sign( mbedtls_ecp_group *grp, mbedtls_mpi *r, mbedtls_mpi *s,
-                const mbedtls_mpi *d, const unsigned char *buf, size_t blen,
-                int (*f_rng)(void *, unsigned char *, size_t), void *p_rng );
+int mbedtls_ecdsa_sign(mbedtls_ecp_group *grp, mbedtls_mpi *r, mbedtls_mpi *s,
+                       const mbedtls_mpi *d, const unsigned char *buf, size_t blen,
+                       int (*f_rng)(void *, unsigned char *, size_t), void *p_rng);
 
 #if defined(MBEDTLS_ECDSA_DETERMINISTIC)
-#if ! defined(MBEDTLS_DEPRECATED_REMOVED)
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
 #if defined(MBEDTLS_DEPRECATED_WARNING)
 #define MBEDTLS_DEPRECATED    __attribute__((deprecated))
 #else
@@ -228,10 +215,10 @@
  * \return          An \c MBEDTLS_ERR_ECP_XXX or \c MBEDTLS_MPI_XXX
  *                  error code on failure.
  */
-int mbedtls_ecdsa_sign_det( mbedtls_ecp_group *grp, mbedtls_mpi *r,
-                            mbedtls_mpi *s, const mbedtls_mpi *d,
-                            const unsigned char *buf, size_t blen,
-                            mbedtls_md_type_t md_alg ) MBEDTLS_DEPRECATED;
+int mbedtls_ecdsa_sign_det(mbedtls_ecp_group *grp, mbedtls_mpi *r,
+                           mbedtls_mpi *s, const mbedtls_mpi *d,
+                           const unsigned char *buf, size_t blen,
+                           mbedtls_md_type_t md_alg) MBEDTLS_DEPRECATED;
 #undef MBEDTLS_DEPRECATED
 #endif /* MBEDTLS_DEPRECATED_REMOVED */
 
@@ -267,19 +254,20 @@
  * \param md_alg        The hash algorithm used to hash the original data.
  * \param f_rng_blind   The RNG function used for blinding. This must not be
  *                      \c NULL.
- * \param p_rng_blind   The RNG context to be passed to \p f_rng. This may be
- *                      \c NULL if \p f_rng doesn't need a context parameter.
+ * \param p_rng_blind   The RNG context to be passed to \p f_rng_blind. This
+ *                      may be \c NULL if \p f_rng_blind doesn't need
+ *                      a context parameter.
  *
  * \return          \c 0 on success.
  * \return          An \c MBEDTLS_ERR_ECP_XXX or \c MBEDTLS_MPI_XXX
  *                  error code on failure.
  */
-int mbedtls_ecdsa_sign_det_ext( mbedtls_ecp_group *grp, mbedtls_mpi *r,
-                            mbedtls_mpi *s, const mbedtls_mpi *d,
-                            const unsigned char *buf, size_t blen,
-                            mbedtls_md_type_t md_alg,
-                            int (*f_rng_blind)(void *, unsigned char *, size_t),
-                            void *p_rng_blind );
+int mbedtls_ecdsa_sign_det_ext(mbedtls_ecp_group *grp, mbedtls_mpi *r,
+                               mbedtls_mpi *s, const mbedtls_mpi *d,
+                               const unsigned char *buf, size_t blen,
+                               mbedtls_md_type_t md_alg,
+                               int (*f_rng_blind)(void *, unsigned char *, size_t),
+                               void *p_rng_blind);
 #endif /* MBEDTLS_ECDSA_DETERMINISTIC */
 
 /**
@@ -309,15 +297,13 @@
  *                  This must be initialized.
  *
  * \return          \c 0 on success.
- * \return          #MBEDTLS_ERR_ECP_BAD_INPUT_DATA if the signature
- *                  is invalid.
  * \return          An \c MBEDTLS_ERR_ECP_XXX or \c MBEDTLS_MPI_XXX
- *                  error code on failure for any other reason.
+ *                  error code on failure.
  */
-int mbedtls_ecdsa_verify( mbedtls_ecp_group *grp,
-                          const unsigned char *buf, size_t blen,
-                          const mbedtls_ecp_point *Q, const mbedtls_mpi *r,
-                          const mbedtls_mpi *s);
+int mbedtls_ecdsa_verify(mbedtls_ecp_group *grp,
+                         const unsigned char *buf, size_t blen,
+                         const mbedtls_ecp_point *Q, const mbedtls_mpi *r,
+                         const mbedtls_mpi *s);
 
 /**
  * \brief           This function computes the ECDSA signature and writes it
@@ -347,7 +333,7 @@
  *                  via mbedtls_ecdsa_genkey() or mbedtls_ecdsa_from_keypair().
  * \param md_alg    The message digest that was used to hash the message.
  * \param hash      The message hash to be signed. This must be a readable
- *                  buffer of length \p blen Bytes.
+ *                  buffer of length \p hlen Bytes.
  * \param hlen      The length of the hash \p hash in Bytes.
  * \param sig       The buffer to which to write the signature. This must be a
  *                  writable buffer of length at least twice as large as the
@@ -367,12 +353,12 @@
  * \return          An \c MBEDTLS_ERR_ECP_XXX, \c MBEDTLS_ERR_MPI_XXX or
  *                  \c MBEDTLS_ERR_ASN1_XXX error code on failure.
  */
-int mbedtls_ecdsa_write_signature( mbedtls_ecdsa_context *ctx,
-                                   mbedtls_md_type_t md_alg,
-                           const unsigned char *hash, size_t hlen,
-                           unsigned char *sig, size_t *slen,
-                           int (*f_rng)(void *, unsigned char *, size_t),
-                           void *p_rng );
+int mbedtls_ecdsa_write_signature(mbedtls_ecdsa_context *ctx,
+                                  mbedtls_md_type_t md_alg,
+                                  const unsigned char *hash, size_t hlen,
+                                  unsigned char *sig, size_t *slen,
+                                  int (*f_rng)(void *, unsigned char *, size_t),
+                                  void *p_rng);
 
 /**
  * \brief           This function computes the ECDSA signature and writes it
@@ -389,7 +375,7 @@
  *                  via mbedtls_ecdsa_genkey() or mbedtls_ecdsa_from_keypair().
  * \param md_alg    The message digest that was used to hash the message.
  * \param hash      The message hash to be signed. This must be a readable
- *                  buffer of length \p blen Bytes.
+ *                  buffer of length \p hlen Bytes.
  * \param hlen      The length of the hash \p hash in Bytes.
  * \param sig       The buffer to which to write the signature. This must be a
  *                  writable buffer of length at least twice as large as the
@@ -413,16 +399,16 @@
  * \return          Another \c MBEDTLS_ERR_ECP_XXX, \c MBEDTLS_ERR_MPI_XXX or
  *                  \c MBEDTLS_ERR_ASN1_XXX error code on failure.
  */
-int mbedtls_ecdsa_write_signature_restartable( mbedtls_ecdsa_context *ctx,
-                           mbedtls_md_type_t md_alg,
-                           const unsigned char *hash, size_t hlen,
-                           unsigned char *sig, size_t *slen,
-                           int (*f_rng)(void *, unsigned char *, size_t),
-                           void *p_rng,
-                           mbedtls_ecdsa_restart_ctx *rs_ctx );
+int mbedtls_ecdsa_write_signature_restartable(mbedtls_ecdsa_context *ctx,
+                                              mbedtls_md_type_t md_alg,
+                                              const unsigned char *hash, size_t hlen,
+                                              unsigned char *sig, size_t *slen,
+                                              int (*f_rng)(void *, unsigned char *, size_t),
+                                              void *p_rng,
+                                              mbedtls_ecdsa_restart_ctx *rs_ctx);
 
 #if defined(MBEDTLS_ECDSA_DETERMINISTIC)
-#if ! defined(MBEDTLS_DEPRECATED_REMOVED)
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
 #if defined(MBEDTLS_DEPRECATED_WARNING)
 #define MBEDTLS_DEPRECATED    __attribute__((deprecated))
 #else
@@ -456,7 +442,7 @@
  *                  and have a group and private key bound to it, for example
  *                  via mbedtls_ecdsa_genkey() or mbedtls_ecdsa_from_keypair().
  * \param hash      The message hash to be signed. This must be a readable
- *                  buffer of length \p blen Bytes.
+ *                  buffer of length \p hlen Bytes.
  * \param hlen      The length of the hash \p hash in Bytes.
  * \param sig       The buffer to which to write the signature. This must be a
  *                  writable buffer of length at least twice as large as the
@@ -471,10 +457,10 @@
  * \return          An \c MBEDTLS_ERR_ECP_XXX, \c MBEDTLS_ERR_MPI_XXX or
  *                  \c MBEDTLS_ERR_ASN1_XXX error code on failure.
  */
-int mbedtls_ecdsa_write_signature_det( mbedtls_ecdsa_context *ctx,
-                               const unsigned char *hash, size_t hlen,
-                               unsigned char *sig, size_t *slen,
-                               mbedtls_md_type_t md_alg ) MBEDTLS_DEPRECATED;
+int mbedtls_ecdsa_write_signature_det(mbedtls_ecdsa_context *ctx,
+                                      const unsigned char *hash, size_t hlen,
+                                      unsigned char *sig, size_t *slen,
+                                      mbedtls_md_type_t md_alg) MBEDTLS_DEPRECATED;
 #undef MBEDTLS_DEPRECATED
 #endif /* MBEDTLS_DEPRECATED_REMOVED */
 #endif /* MBEDTLS_ECDSA_DETERMINISTIC */
@@ -493,7 +479,7 @@
  * \param ctx       The ECDSA context to use. This must be initialized
  *                  and have a group and public key bound to it.
  * \param hash      The message hash that was signed. This must be a readable
- *                  buffer of length \p size Bytes.
+ *                  buffer of length \p hlen Bytes.
  * \param hlen      The size of the hash \p hash.
  * \param sig       The signature to read and verify. This must be a readable
  *                  buffer of length \p slen Bytes.
@@ -506,9 +492,9 @@
  * \return          An \c MBEDTLS_ERR_ECP_XXX or \c MBEDTLS_ERR_MPI_XXX
  *                  error code on failure for any other reason.
  */
-int mbedtls_ecdsa_read_signature( mbedtls_ecdsa_context *ctx,
-                          const unsigned char *hash, size_t hlen,
-                          const unsigned char *sig, size_t slen );
+int mbedtls_ecdsa_read_signature(mbedtls_ecdsa_context *ctx,
+                                 const unsigned char *hash, size_t hlen,
+                                 const unsigned char *sig, size_t slen);
 
 /**
  * \brief           This function reads and verifies an ECDSA signature,
@@ -523,7 +509,7 @@
  * \param ctx       The ECDSA context to use. This must be initialized
  *                  and have a group and public key bound to it.
  * \param hash      The message hash that was signed. This must be a readable
- *                  buffer of length \p size Bytes.
+ *                  buffer of length \p hlen Bytes.
  * \param hlen      The size of the hash \p hash.
  * \param sig       The signature to read and verify. This must be a readable
  *                  buffer of length \p slen Bytes.
@@ -541,10 +527,10 @@
  * \return          Another \c MBEDTLS_ERR_ECP_XXX or \c MBEDTLS_ERR_MPI_XXX
  *                  error code on failure for any other reason.
  */
-int mbedtls_ecdsa_read_signature_restartable( mbedtls_ecdsa_context *ctx,
-                          const unsigned char *hash, size_t hlen,
-                          const unsigned char *sig, size_t slen,
-                          mbedtls_ecdsa_restart_ctx *rs_ctx );
+int mbedtls_ecdsa_read_signature_restartable(mbedtls_ecdsa_context *ctx,
+                                             const unsigned char *hash, size_t hlen,
+                                             const unsigned char *sig, size_t slen,
+                                             mbedtls_ecdsa_restart_ctx *rs_ctx);
 
 /**
  * \brief          This function generates an ECDSA keypair on the given curve.
@@ -562,8 +548,8 @@
  * \return         \c 0 on success.
  * \return         An \c MBEDTLS_ERR_ECP_XXX code on failure.
  */
-int mbedtls_ecdsa_genkey( mbedtls_ecdsa_context *ctx, mbedtls_ecp_group_id gid,
-                  int (*f_rng)(void *, unsigned char *, size_t), void *p_rng );
+int mbedtls_ecdsa_genkey(mbedtls_ecdsa_context *ctx, mbedtls_ecp_group_id gid,
+                         int (*f_rng)(void *, unsigned char *, size_t), void *p_rng);
 
 /**
  * \brief           This function sets up an ECDSA context from an EC key pair.
@@ -580,8 +566,8 @@
  * \return          \c 0 on success.
  * \return          An \c MBEDTLS_ERR_ECP_XXX code on failure.
  */
-int mbedtls_ecdsa_from_keypair( mbedtls_ecdsa_context *ctx,
-                                const mbedtls_ecp_keypair *key );
+int mbedtls_ecdsa_from_keypair(mbedtls_ecdsa_context *ctx,
+                               const mbedtls_ecp_keypair *key);
 
 /**
  * \brief           This function initializes an ECDSA context.
@@ -589,7 +575,7 @@
  * \param ctx       The ECDSA context to initialize.
  *                  This must not be \c NULL.
  */
-void mbedtls_ecdsa_init( mbedtls_ecdsa_context *ctx );
+void mbedtls_ecdsa_init(mbedtls_ecdsa_context *ctx);
 
 /**
  * \brief           This function frees an ECDSA context.
@@ -598,7 +584,7 @@
  *                  in which case this function does nothing. If it
  *                  is not \c NULL, it must be initialized.
  */
-void mbedtls_ecdsa_free( mbedtls_ecdsa_context *ctx );
+void mbedtls_ecdsa_free(mbedtls_ecdsa_context *ctx);
 
 #if defined(MBEDTLS_ECP_RESTARTABLE)
 /**
@@ -607,7 +593,7 @@
  * \param ctx       The restart context to initialize.
  *                  This must not be \c NULL.
  */
-void mbedtls_ecdsa_restart_init( mbedtls_ecdsa_restart_ctx *ctx );
+void mbedtls_ecdsa_restart_init(mbedtls_ecdsa_restart_ctx *ctx);
 
 /**
  * \brief           Free the components of a restart context.
@@ -616,7 +602,7 @@
  *                  in which case this function does nothing. If it
  *                  is not \c NULL, it must be initialized.
  */
-void mbedtls_ecdsa_restart_free( mbedtls_ecdsa_restart_ctx *ctx );
+void mbedtls_ecdsa_restart_free(mbedtls_ecdsa_restart_ctx *ctx);
 #endif /* MBEDTLS_ECP_RESTARTABLE */
 
 #ifdef __cplusplus
diff --git a/third_party/mbedtls/repo/include/mbedtls/ecjpake.h b/third_party/mbedtls/repo/include/mbedtls/ecjpake.h
index 891705d..1a98442 100644
--- a/third_party/mbedtls/repo/include/mbedtls/ecjpake.h
+++ b/third_party/mbedtls/repo/include/mbedtls/ecjpake.h
@@ -5,19 +5,7 @@
  */
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 #ifndef MBEDTLS_ECJPAKE_H
 #define MBEDTLS_ECJPAKE_H
@@ -68,11 +56,10 @@
  * (KeyExchange) as defined by the Thread spec.
  *
  * In order to benefit from this symmetry, we choose a different naming
- * convetion from the Thread v1.0 spec. Correspondance is indicated in the
+ * convention from the Thread v1.0 spec. Correspondence is indicated in the
  * description as a pair C: client name, S: server name
  */
-typedef struct mbedtls_ecjpake_context
-{
+typedef struct mbedtls_ecjpake_context {
     const mbedtls_md_info_t *md_info;   /**< Hash to use                    */
     mbedtls_ecp_group grp;              /**< Elliptic curve                 */
     mbedtls_ecjpake_role role;          /**< Are we client or server?       */
@@ -100,7 +87,7 @@
  * \param ctx       The ECJPAKE context to initialize.
  *                  This must not be \c NULL.
  */
-void mbedtls_ecjpake_init( mbedtls_ecjpake_context *ctx );
+void mbedtls_ecjpake_init(mbedtls_ecjpake_context *ctx);
 
 /**
  * \brief           Set up an ECJPAKE context for use.
@@ -123,12 +110,12 @@
  * \return          \c 0 if successful.
  * \return          A negative error code on failure.
  */
-int mbedtls_ecjpake_setup( mbedtls_ecjpake_context *ctx,
-                           mbedtls_ecjpake_role role,
-                           mbedtls_md_type_t hash,
-                           mbedtls_ecp_group_id curve,
-                           const unsigned char *secret,
-                           size_t len );
+int mbedtls_ecjpake_setup(mbedtls_ecjpake_context *ctx,
+                          mbedtls_ecjpake_role role,
+                          mbedtls_md_type_t hash,
+                          mbedtls_ecp_group_id curve,
+                          const unsigned char *secret,
+                          size_t len);
 
 /**
  * \brief           Check if an ECJPAKE context is ready for use.
@@ -139,7 +126,7 @@
  * \return          \c 0 if the context is ready for use.
  * \return          #MBEDTLS_ERR_ECP_BAD_INPUT_DATA otherwise.
  */
-int mbedtls_ecjpake_check( const mbedtls_ecjpake_context *ctx );
+int mbedtls_ecjpake_check(const mbedtls_ecjpake_context *ctx);
 
 /**
  * \brief           Generate and write the first round message
@@ -160,10 +147,10 @@
  * \return          \c 0 if successful.
  * \return          A negative error code on failure.
  */
-int mbedtls_ecjpake_write_round_one( mbedtls_ecjpake_context *ctx,
-                            unsigned char *buf, size_t len, size_t *olen,
-                            int (*f_rng)(void *, unsigned char *, size_t),
-                            void *p_rng );
+int mbedtls_ecjpake_write_round_one(mbedtls_ecjpake_context *ctx,
+                                    unsigned char *buf, size_t len, size_t *olen,
+                                    int (*f_rng)(void *, unsigned char *, size_t),
+                                    void *p_rng);
 
 /**
  * \brief           Read and process the first round message
@@ -179,9 +166,9 @@
  * \return          \c 0 if successful.
  * \return          A negative error code on failure.
  */
-int mbedtls_ecjpake_read_round_one( mbedtls_ecjpake_context *ctx,
-                                    const unsigned char *buf,
-                                    size_t len );
+int mbedtls_ecjpake_read_round_one(mbedtls_ecjpake_context *ctx,
+                                   const unsigned char *buf,
+                                   size_t len);
 
 /**
  * \brief           Generate and write the second round message
@@ -201,10 +188,10 @@
  * \return          \c 0 if successful.
  * \return          A negative error code on failure.
  */
-int mbedtls_ecjpake_write_round_two( mbedtls_ecjpake_context *ctx,
-                            unsigned char *buf, size_t len, size_t *olen,
-                            int (*f_rng)(void *, unsigned char *, size_t),
-                            void *p_rng );
+int mbedtls_ecjpake_write_round_two(mbedtls_ecjpake_context *ctx,
+                                    unsigned char *buf, size_t len, size_t *olen,
+                                    int (*f_rng)(void *, unsigned char *, size_t),
+                                    void *p_rng);
 
 /**
  * \brief           Read and process the second round message
@@ -219,9 +206,9 @@
  * \return          \c 0 if successful.
  * \return          A negative error code on failure.
  */
-int mbedtls_ecjpake_read_round_two( mbedtls_ecjpake_context *ctx,
-                                    const unsigned char *buf,
-                                    size_t len );
+int mbedtls_ecjpake_read_round_two(mbedtls_ecjpake_context *ctx,
+                                   const unsigned char *buf,
+                                   size_t len);
 
 /**
  * \brief           Derive the shared secret
@@ -241,10 +228,10 @@
  * \return          \c 0 if successful.
  * \return          A negative error code on failure.
  */
-int mbedtls_ecjpake_derive_secret( mbedtls_ecjpake_context *ctx,
-                            unsigned char *buf, size_t len, size_t *olen,
-                            int (*f_rng)(void *, unsigned char *, size_t),
-                            void *p_rng );
+int mbedtls_ecjpake_derive_secret(mbedtls_ecjpake_context *ctx,
+                                  unsigned char *buf, size_t len, size_t *olen,
+                                  int (*f_rng)(void *, unsigned char *, size_t),
+                                  void *p_rng);
 
 /**
  * \brief           This clears an ECJPAKE context and frees any
@@ -254,7 +241,7 @@
  *                  in which case this function does nothing. If it is not
  *                  \c NULL, it must point to an initialized ECJPAKE context.
  */
-void mbedtls_ecjpake_free( mbedtls_ecjpake_context *ctx );
+void mbedtls_ecjpake_free(mbedtls_ecjpake_context *ctx);
 
 #if defined(MBEDTLS_SELF_TEST)
 
@@ -263,7 +250,7 @@
  *
  * \return         0 if successful, or 1 if a test failed
  */
-int mbedtls_ecjpake_self_test( int verbose );
+int mbedtls_ecjpake_self_test(int verbose);
 
 #endif /* MBEDTLS_SELF_TEST */
 
diff --git a/third_party/mbedtls/repo/include/mbedtls/ecp.h b/third_party/mbedtls/repo/include/mbedtls/ecp.h
index 0924341..e4e40c0 100644
--- a/third_party/mbedtls/repo/include/mbedtls/ecp.h
+++ b/third_party/mbedtls/repo/include/mbedtls/ecp.h
@@ -16,19 +16,7 @@
 
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 
 #ifndef MBEDTLS_ECP_H
@@ -42,6 +30,11 @@
 
 #include "mbedtls/bignum.h"
 
+#if (defined(__ARMCC_VERSION) || defined(_MSC_VER)) && \
+    !defined(inline) && !defined(__cplusplus)
+#define inline __inline
+#endif
+
 /*
  * ECP error codes
  */
@@ -117,8 +110,7 @@
  * - Add the curve to the ecp_supported_curves array in ecp.c.
  * - Add the curve to applicable profiles in x509_crt.c if applicable.
  */
-typedef enum
-{
+typedef enum {
     MBEDTLS_ECP_DP_NONE = 0,       /*!< Curve not defined. */
     MBEDTLS_ECP_DP_SECP192R1,      /*!< Domain parameters for the 192-bit curve defined by FIPS 186-4 and SEC1. */
     MBEDTLS_ECP_DP_SECP224R1,      /*!< Domain parameters for the 224-bit curve defined by FIPS 186-4 and SEC1. */
@@ -145,8 +137,7 @@
 /*
  * Curve types
  */
-typedef enum
-{
+typedef enum {
     MBEDTLS_ECP_TYPE_NONE = 0,
     MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS,    /* y^2 = x^3 + a x + b      */
     MBEDTLS_ECP_TYPE_MONTGOMERY,           /* y^2 = x^3 + a x^2 + x    */
@@ -155,8 +146,7 @@
 /**
  * Curve information, for use by other modules.
  */
-typedef struct mbedtls_ecp_curve_info
-{
+typedef struct mbedtls_ecp_curve_info {
     mbedtls_ecp_group_id grp_id;    /*!< An internal identifier. */
     uint16_t tls_id;                /*!< The TLS NamedCurve identifier. */
     uint16_t bit_size;              /*!< The curve size in bits. */
@@ -174,8 +164,7 @@
  *                  Otherwise, \p X and \p Y are its standard (affine)
  *                  coordinates.
  */
-typedef struct mbedtls_ecp_point
-{
+typedef struct mbedtls_ecp_point {
     mbedtls_mpi X;          /*!< The X coordinate of the ECP point. */
     mbedtls_mpi Y;          /*!< The Y coordinate of the ECP point. */
     mbedtls_mpi Z;          /*!< The Z coordinate of the ECP point. */
@@ -218,7 +207,7 @@
 
 #if !defined(MBEDTLS_ECP_ALT)
 /*
- * default mbed TLS elliptic curve arithmetic implementation
+ * default Mbed TLS elliptic curve arithmetic implementation
  *
  * (in case MBEDTLS_ECP_ALT is defined then the developer has to provide an
  * alternative implementation for the whole module and it will replace this
@@ -240,6 +229,27 @@
  * odd prime as mbedtls_ecp_mul() requires an odd number, and
  * mbedtls_ecdsa_sign() requires that it is prime for blinding purposes.
  *
+ * The default implementation only initializes \p A without setting it to the
+ * authentic value for curves with <code>A = -3</code>(SECP256R1, etc), in which
+ * case you need to load \p A by yourself when using domain parameters directly,
+ * for example:
+ * \code
+ * mbedtls_mpi_init(&A);
+ * mbedtls_ecp_group_init(&grp);
+ * CHECK_RETURN(mbedtls_ecp_group_load(&grp, grp_id));
+ * if (mbedtls_ecp_group_a_is_minus_3(&grp)) {
+ *     CHECK_RETURN(mbedtls_mpi_sub_int(&A, &grp.P, 3));
+ * } else {
+ *     CHECK_RETURN(mbedtls_mpi_copy(&A, &grp.A));
+ * }
+ *
+ * do_something_with_a(&A);
+ *
+ * cleanup:
+ * mbedtls_mpi_free(&A);
+ * mbedtls_ecp_group_free(&grp);
+ * \endcode
+ *
  * For Montgomery curves, we do not store \p A, but <code>(A + 2) / 4</code>,
  * which is the quantity used in the formulas. Additionally, \p nbits is
  * not the size of \p N but the required size for private keys.
@@ -257,12 +267,14 @@
  *              identical.
  *
  */
-typedef struct mbedtls_ecp_group
-{
+typedef struct mbedtls_ecp_group {
     mbedtls_ecp_group_id id;    /*!< An internal group identifier. */
     mbedtls_mpi P;              /*!< The prime modulus of the base field. */
-    mbedtls_mpi A;              /*!< For Short Weierstrass: \p A in the equation. For
-                                     Montgomery curves: <code>(A + 2) / 4</code>. */
+    mbedtls_mpi A;              /*!< For Short Weierstrass: \p A in the equation. Note that
+                                     \p A is not set to the authentic value in some cases.
+                                     Refer to detailed description of ::mbedtls_ecp_group if
+                                     using domain parameters in the structure.
+                                     For Montgomery curves: <code>(A + 2) / 4</code>. */
     mbedtls_mpi B;              /*!< For Short Weierstrass: \p B in the equation.
                                      For Montgomery curves: unused. */
     mbedtls_ecp_point G;        /*!< The generator of the subgroup used. */
@@ -309,13 +321,13 @@
 #define MBEDTLS_ECP_MAX_BITS 1
 #endif
 
-#define MBEDTLS_ECP_MAX_BYTES    ( ( MBEDTLS_ECP_MAX_BITS + 7 ) / 8 )
-#define MBEDTLS_ECP_MAX_PT_LEN   ( 2 * MBEDTLS_ECP_MAX_BYTES + 1 )
+#define MBEDTLS_ECP_MAX_BYTES    ((MBEDTLS_ECP_MAX_BITS + 7) / 8)
+#define MBEDTLS_ECP_MAX_PT_LEN   (2 * MBEDTLS_ECP_MAX_BYTES + 1)
 
 #if !defined(MBEDTLS_ECP_WINDOW_SIZE)
 /*
  * Maximum "window" size used for point multiplication.
- * Default: a point where higher memory usage yields disminishing performance
+ * Default: a point where higher memory usage yields diminishing performance
  *          returns.
  * Minimum value: 2. Maximum value: 7.
  *
@@ -351,7 +363,7 @@
 #define MBEDTLS_ECP_FIXED_POINT_OPTIM  1   /**< Enable fixed-point speed-up. */
 #endif /* MBEDTLS_ECP_FIXED_POINT_OPTIM */
 
-/* \} name SECTION: Module settings */
+/** \} name SECTION: Module settings */
 
 #else  /* MBEDTLS_ECP_ALT */
 #include "ecp_alt.h"
@@ -376,8 +388,7 @@
 /**
  * \brief           General context for resuming ECC operations
  */
-typedef struct
-{
+typedef struct {
     unsigned ops_done;                  /*!<  current ops count             */
     unsigned depth;                     /*!<  call depth (0 = top-level)    */
     mbedtls_ecp_restart_mul_ctx *rsm;   /*!<  ecp_mul_comb() sub-context    */
@@ -403,18 +414,18 @@
  * \return          \c 0 if doing \p ops basic ops is still allowed,
  * \return          #MBEDTLS_ERR_ECP_IN_PROGRESS otherwise.
  */
-int mbedtls_ecp_check_budget( const mbedtls_ecp_group *grp,
-                              mbedtls_ecp_restart_ctx *rs_ctx,
-                              unsigned ops );
+int mbedtls_ecp_check_budget(const mbedtls_ecp_group *grp,
+                             mbedtls_ecp_restart_ctx *rs_ctx,
+                             unsigned ops);
 
 /* Utility macro for checking and updating ops budget */
-#define MBEDTLS_ECP_BUDGET( ops )   \
-    MBEDTLS_MPI_CHK( mbedtls_ecp_check_budget( grp, rs_ctx, \
-                                               (unsigned) (ops) ) );
+#define MBEDTLS_ECP_BUDGET(ops)   \
+    MBEDTLS_MPI_CHK(mbedtls_ecp_check_budget(grp, rs_ctx, \
+                                             (unsigned) (ops)));
 
 #else /* MBEDTLS_ECP_RESTARTABLE */
 
-#define MBEDTLS_ECP_BUDGET( ops )   /* no-op; for compatibility */
+#define MBEDTLS_ECP_BUDGET(ops)     /* no-op; for compatibility */
 
 /* We want to declare restartable versions of existing functions anyway */
 typedef void mbedtls_ecp_restart_ctx;
@@ -429,8 +440,7 @@
  * \note    Members are deliberately in the same order as in the
  *          ::mbedtls_ecdsa_context structure.
  */
-typedef struct mbedtls_ecp_keypair
-{
+typedef struct mbedtls_ecp_keypair {
     mbedtls_ecp_group grp;      /*!<  Elliptic curve and base point     */
     mbedtls_mpi d;              /*!<  our secret value                  */
     mbedtls_ecp_point Q;        /*!<  our public value                  */
@@ -506,7 +516,7 @@
  *
  * \note            This setting is currently ignored by Curve25519.
  */
-void mbedtls_ecp_set_max_ops( unsigned max_ops );
+void mbedtls_ecp_set_max_ops(unsigned max_ops);
 
 /**
  * \brief           Check if restart is enabled (max_ops != 0)
@@ -514,13 +524,13 @@
  * \return          \c 0 if \c max_ops == 0 (restart disabled)
  * \return          \c 1 otherwise (restart enabled)
  */
-int mbedtls_ecp_restart_is_enabled( void );
+int mbedtls_ecp_restart_is_enabled(void);
 #endif /* MBEDTLS_ECP_RESTARTABLE */
 
 /*
  * Get the type of a curve
  */
-mbedtls_ecp_curve_type mbedtls_ecp_get_type( const mbedtls_ecp_group *grp );
+mbedtls_ecp_curve_type mbedtls_ecp_get_type(const mbedtls_ecp_group *grp);
 
 /**
  * \brief           This function retrieves the information defined in
@@ -534,7 +544,7 @@
  *
  * \return          A statically allocated array. The last entry is 0.
  */
-const mbedtls_ecp_curve_info *mbedtls_ecp_curve_list( void );
+const mbedtls_ecp_curve_info *mbedtls_ecp_curve_list(void);
 
 /**
  * \brief           This function retrieves the list of internal group
@@ -550,7 +560,7 @@
  * \return          A statically allocated array,
  *                  terminated with MBEDTLS_ECP_DP_NONE.
  */
-const mbedtls_ecp_group_id *mbedtls_ecp_grp_id_list( void );
+const mbedtls_ecp_group_id *mbedtls_ecp_grp_id_list(void);
 
 /**
  * \brief           This function retrieves curve information from an internal
@@ -561,7 +571,7 @@
  * \return          The associated curve information on success.
  * \return          NULL on failure.
  */
-const mbedtls_ecp_curve_info *mbedtls_ecp_curve_info_from_grp_id( mbedtls_ecp_group_id grp_id );
+const mbedtls_ecp_curve_info *mbedtls_ecp_curve_info_from_grp_id(mbedtls_ecp_group_id grp_id);
 
 /**
  * \brief           This function retrieves curve information from a TLS
@@ -572,7 +582,7 @@
  * \return          The associated curve information on success.
  * \return          NULL on failure.
  */
-const mbedtls_ecp_curve_info *mbedtls_ecp_curve_info_from_tls_id( uint16_t tls_id );
+const mbedtls_ecp_curve_info *mbedtls_ecp_curve_info_from_tls_id(uint16_t tls_id);
 
 /**
  * \brief           This function retrieves curve information from a
@@ -583,14 +593,14 @@
  * \return          The associated curve information on success.
  * \return          NULL on failure.
  */
-const mbedtls_ecp_curve_info *mbedtls_ecp_curve_info_from_name( const char *name );
+const mbedtls_ecp_curve_info *mbedtls_ecp_curve_info_from_name(const char *name);
 
 /**
  * \brief           This function initializes a point as zero.
  *
  * \param pt        The point to initialize.
  */
-void mbedtls_ecp_point_init( mbedtls_ecp_point *pt );
+void mbedtls_ecp_point_init(mbedtls_ecp_point *pt);
 
 /**
  * \brief           This function initializes an ECP group context
@@ -601,21 +611,21 @@
  *                  mbedtls_ecp_group_load() or mbedtls_ecp_tls_read_group()
  *                  functions.
  */
-void mbedtls_ecp_group_init( mbedtls_ecp_group *grp );
+void mbedtls_ecp_group_init(mbedtls_ecp_group *grp);
 
 /**
  * \brief           This function initializes a key pair as an invalid one.
  *
  * \param key       The key pair to initialize.
  */
-void mbedtls_ecp_keypair_init( mbedtls_ecp_keypair *key );
+void mbedtls_ecp_keypair_init(mbedtls_ecp_keypair *key);
 
 /**
  * \brief           This function frees the components of a point.
  *
  * \param pt        The point to free.
  */
-void mbedtls_ecp_point_free( mbedtls_ecp_point *pt );
+void mbedtls_ecp_point_free(mbedtls_ecp_point *pt);
 
 /**
  * \brief           This function frees the components of an ECP group.
@@ -624,7 +634,7 @@
  *                  case this function returns immediately. If it is not
  *                  \c NULL, it must point to an initialized ECP group.
  */
-void mbedtls_ecp_group_free( mbedtls_ecp_group *grp );
+void mbedtls_ecp_group_free(mbedtls_ecp_group *grp);
 
 /**
  * \brief           This function frees the components of a key pair.
@@ -633,7 +643,7 @@
  *                  case this function returns immediately. If it is not
  *                  \c NULL, it must point to an initialized ECP key pair.
  */
-void mbedtls_ecp_keypair_free( mbedtls_ecp_keypair *key );
+void mbedtls_ecp_keypair_free(mbedtls_ecp_keypair *key);
 
 #if defined(MBEDTLS_ECP_RESTARTABLE)
 /**
@@ -642,7 +652,7 @@
  * \param ctx       The restart context to initialize. This must
  *                  not be \c NULL.
  */
-void mbedtls_ecp_restart_init( mbedtls_ecp_restart_ctx *ctx );
+void mbedtls_ecp_restart_init(mbedtls_ecp_restart_ctx *ctx);
 
 /**
  * \brief           Free the components of a restart context.
@@ -651,7 +661,7 @@
  *                  case this function returns immediately. If it is not
  *                  \c NULL, it must point to an initialized restart context.
  */
-void mbedtls_ecp_restart_free( mbedtls_ecp_restart_ctx *ctx );
+void mbedtls_ecp_restart_free(mbedtls_ecp_restart_ctx *ctx);
 #endif /* MBEDTLS_ECP_RESTARTABLE */
 
 /**
@@ -665,7 +675,7 @@
  * \return          #MBEDTLS_ERR_MPI_ALLOC_FAILED on memory-allocation failure.
  * \return          Another negative error code for other kinds of failure.
  */
-int mbedtls_ecp_copy( mbedtls_ecp_point *P, const mbedtls_ecp_point *Q );
+int mbedtls_ecp_copy(mbedtls_ecp_point *P, const mbedtls_ecp_point *Q);
 
 /**
  * \brief           This function copies the contents of group \p src into
@@ -678,8 +688,8 @@
  * \return          #MBEDTLS_ERR_MPI_ALLOC_FAILED on memory-allocation failure.
  * \return          Another negative error code on other kinds of failure.
  */
-int mbedtls_ecp_group_copy( mbedtls_ecp_group *dst,
-                            const mbedtls_ecp_group *src );
+int mbedtls_ecp_group_copy(mbedtls_ecp_group *dst,
+                           const mbedtls_ecp_group *src);
 
 /**
  * \brief           This function sets a point to the point at infinity.
@@ -690,7 +700,7 @@
  * \return          #MBEDTLS_ERR_MPI_ALLOC_FAILED on memory-allocation failure.
  * \return          Another negative error code on other kinds of failure.
  */
-int mbedtls_ecp_set_zero( mbedtls_ecp_point *pt );
+int mbedtls_ecp_set_zero(mbedtls_ecp_point *pt);
 
 /**
  * \brief           This function checks if a point is the point at infinity.
@@ -701,7 +711,7 @@
  * \return          \c 0 if the point is non-zero.
  * \return          A negative error code on failure.
  */
-int mbedtls_ecp_is_zero( mbedtls_ecp_point *pt );
+int mbedtls_ecp_is_zero(mbedtls_ecp_point *pt);
 
 /**
  * \brief           This function compares two points.
@@ -715,8 +725,8 @@
  * \return          \c 0 if the points are equal.
  * \return          #MBEDTLS_ERR_ECP_BAD_INPUT_DATA if the points are not equal.
  */
-int mbedtls_ecp_point_cmp( const mbedtls_ecp_point *P,
-                           const mbedtls_ecp_point *Q );
+int mbedtls_ecp_point_cmp(const mbedtls_ecp_point *P,
+                          const mbedtls_ecp_point *Q);
 
 /**
  * \brief           This function imports a non-zero point from two ASCII
@@ -730,8 +740,8 @@
  * \return          \c 0 on success.
  * \return          An \c MBEDTLS_ERR_MPI_XXX error code on failure.
  */
-int mbedtls_ecp_point_read_string( mbedtls_ecp_point *P, int radix,
-                           const char *x, const char *y );
+int mbedtls_ecp_point_read_string(mbedtls_ecp_point *P, int radix,
+                                  const char *x, const char *y);
 
 /**
  * \brief           This function exports a point into unsigned binary data.
@@ -758,10 +768,10 @@
  *                  or the export for the given group is not implemented.
  * \return          Another negative error code on other kinds of failure.
  */
-int mbedtls_ecp_point_write_binary( const mbedtls_ecp_group *grp,
-                                    const mbedtls_ecp_point *P,
-                                    int format, size_t *olen,
-                                    unsigned char *buf, size_t buflen );
+int mbedtls_ecp_point_write_binary(const mbedtls_ecp_group *grp,
+                                   const mbedtls_ecp_point *P,
+                                   int format, size_t *olen,
+                                   unsigned char *buf, size_t buflen);
 
 /**
  * \brief           This function imports a point from unsigned binary data.
@@ -785,9 +795,9 @@
  * \return          #MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE if the import for the
  *                  given group is not implemented.
  */
-int mbedtls_ecp_point_read_binary( const mbedtls_ecp_group *grp,
-                                   mbedtls_ecp_point *P,
-                                   const unsigned char *buf, size_t ilen );
+int mbedtls_ecp_point_read_binary(const mbedtls_ecp_group *grp,
+                                  mbedtls_ecp_point *P,
+                                  const unsigned char *buf, size_t ilen);
 
 /**
  * \brief           This function imports a point from a TLS ECPoint record.
@@ -807,9 +817,9 @@
  *                  failure.
  * \return          #MBEDTLS_ERR_ECP_BAD_INPUT_DATA if input is invalid.
  */
-int mbedtls_ecp_tls_read_point( const mbedtls_ecp_group *grp,
-                                mbedtls_ecp_point *pt,
-                                const unsigned char **buf, size_t len );
+int mbedtls_ecp_tls_read_point(const mbedtls_ecp_group *grp,
+                               mbedtls_ecp_point *pt,
+                               const unsigned char **buf, size_t len);
 
 /**
  * \brief           This function exports a point as a TLS ECPoint record
@@ -833,10 +843,10 @@
  *                  is too small to hold the exported point.
  * \return          Another negative error code on other kinds of failure.
  */
-int mbedtls_ecp_tls_write_point( const mbedtls_ecp_group *grp,
-                                 const mbedtls_ecp_point *pt,
-                                 int format, size_t *olen,
-                                 unsigned char *buf, size_t blen );
+int mbedtls_ecp_tls_write_point(const mbedtls_ecp_group *grp,
+                                const mbedtls_ecp_point *pt,
+                                int format, size_t *olen,
+                                unsigned char *buf, size_t blen);
 
 /**
  * \brief           This function sets up an ECP group context
@@ -855,7 +865,7 @@
  *                  correspond to a known group.
  * \return          Another negative error code on other kinds of failure.
  */
-int mbedtls_ecp_group_load( mbedtls_ecp_group *grp, mbedtls_ecp_group_id id );
+int mbedtls_ecp_group_load(mbedtls_ecp_group *grp, mbedtls_ecp_group_id id);
 
 /**
  * \brief           This function sets up an ECP group context from a TLS
@@ -874,8 +884,8 @@
  *                  recognized.
  * \return          Another negative error code on other kinds of failure.
  */
-int mbedtls_ecp_tls_read_group( mbedtls_ecp_group *grp,
-                                const unsigned char **buf, size_t len );
+int mbedtls_ecp_tls_read_group(mbedtls_ecp_group *grp,
+                               const unsigned char **buf, size_t len);
 
 /**
  * \brief           This function extracts an elliptic curve group ID from a
@@ -895,9 +905,9 @@
  *                  recognized.
  * \return          Another negative error code on other kinds of failure.
  */
-int mbedtls_ecp_tls_read_group_id( mbedtls_ecp_group_id *grp,
-                                   const unsigned char **buf,
-                                   size_t len );
+int mbedtls_ecp_tls_read_group_id(mbedtls_ecp_group_id *grp,
+                                  const unsigned char **buf,
+                                  size_t len);
 /**
  * \brief           This function exports an elliptic curve as a TLS
  *                  ECParameters record as defined in RFC 4492, Section 5.4.
@@ -916,9 +926,9 @@
  *                  buffer is too small to hold the exported group.
  * \return          Another negative error code on other kinds of failure.
  */
-int mbedtls_ecp_tls_write_group( const mbedtls_ecp_group *grp,
-                                 size_t *olen,
-                                 unsigned char *buf, size_t blen );
+int mbedtls_ecp_tls_write_group(const mbedtls_ecp_group *grp,
+                                size_t *olen,
+                                unsigned char *buf, size_t blen);
 
 /**
  * \brief           This function performs a scalar multiplication of a point
@@ -956,9 +966,9 @@
  * \return          #MBEDTLS_ERR_MPI_ALLOC_FAILED on memory-allocation failure.
  * \return          Another negative error code on other kinds of failure.
  */
-int mbedtls_ecp_mul( mbedtls_ecp_group *grp, mbedtls_ecp_point *R,
-             const mbedtls_mpi *m, const mbedtls_ecp_point *P,
-             int (*f_rng)(void *, unsigned char *, size_t), void *p_rng );
+int mbedtls_ecp_mul(mbedtls_ecp_group *grp, mbedtls_ecp_point *R,
+                    const mbedtls_mpi *m, const mbedtls_ecp_point *P,
+                    int (*f_rng)(void *, unsigned char *, size_t), void *p_rng);
 
 /**
  * \brief           This function performs multiplication of a point by
@@ -990,13 +1000,33 @@
  *                  operations was reached: see \c mbedtls_ecp_set_max_ops().
  * \return          Another negative error code on other kinds of failure.
  */
-int mbedtls_ecp_mul_restartable( mbedtls_ecp_group *grp, mbedtls_ecp_point *R,
-             const mbedtls_mpi *m, const mbedtls_ecp_point *P,
-             int (*f_rng)(void *, unsigned char *, size_t), void *p_rng,
-             mbedtls_ecp_restart_ctx *rs_ctx );
+int mbedtls_ecp_mul_restartable(mbedtls_ecp_group *grp, mbedtls_ecp_point *R,
+                                const mbedtls_mpi *m, const mbedtls_ecp_point *P,
+                                int (*f_rng)(void *, unsigned char *, size_t), void *p_rng,
+                                mbedtls_ecp_restart_ctx *rs_ctx);
 
 #if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED)
 /**
+ * \brief           This function checks if domain parameter A of the curve is
+ *                  \c -3.
+ *
+ * \note            This function is only defined for short Weierstrass curves.
+ *                  It may not be included in builds without any short
+ *                  Weierstrass curve.
+ *
+ * \param grp       The ECP group to use.
+ *                  This must be initialized and have group parameters
+ *                  set, for example through mbedtls_ecp_group_load().
+ *
+ * \return          \c 1 if <code>A = -3</code>.
+ * \return          \c 0 Otherwise.
+ */
+static inline int mbedtls_ecp_group_a_is_minus_3(const mbedtls_ecp_group *grp)
+{
+    return grp->A.p == NULL;
+}
+
+/**
  * \brief           This function performs multiplication and addition of two
  *                  points by integers: \p R = \p m * \p P + \p n * \p Q
  *
@@ -1031,9 +1061,9 @@
  *                  designate a short Weierstrass curve.
  * \return          Another negative error code on other kinds of failure.
  */
-int mbedtls_ecp_muladd( mbedtls_ecp_group *grp, mbedtls_ecp_point *R,
-             const mbedtls_mpi *m, const mbedtls_ecp_point *P,
-             const mbedtls_mpi *n, const mbedtls_ecp_point *Q );
+int mbedtls_ecp_muladd(mbedtls_ecp_group *grp, mbedtls_ecp_point *R,
+                       const mbedtls_mpi *m, const mbedtls_ecp_point *P,
+                       const mbedtls_mpi *n, const mbedtls_ecp_point *Q);
 
 /**
  * \brief           This function performs multiplication and addition of two
@@ -1076,10 +1106,10 @@
  * \return          Another negative error code on other kinds of failure.
  */
 int mbedtls_ecp_muladd_restartable(
-             mbedtls_ecp_group *grp, mbedtls_ecp_point *R,
-             const mbedtls_mpi *m, const mbedtls_ecp_point *P,
-             const mbedtls_mpi *n, const mbedtls_ecp_point *Q,
-             mbedtls_ecp_restart_ctx *rs_ctx );
+    mbedtls_ecp_group *grp, mbedtls_ecp_point *R,
+    const mbedtls_mpi *m, const mbedtls_ecp_point *P,
+    const mbedtls_mpi *n, const mbedtls_ecp_point *Q,
+    mbedtls_ecp_restart_ctx *rs_ctx);
 #endif /* MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED */
 
 /**
@@ -1088,7 +1118,7 @@
  *
  *                  It only checks that the point is non-zero, has
  *                  valid coordinates and lies on the curve. It does not verify
- *                  that it is indeed a multiple of \p G. This additional
+ *                  that it is indeed a multiple of \c G. This additional
  *                  check is computationally more expensive, is not required
  *                  by standards, and should not be necessary if the group
  *                  used has a small cofactor. In particular, it is useless for
@@ -1109,11 +1139,11 @@
  *                  a valid public key for the given curve.
  * \return          Another negative error code on other kinds of failure.
  */
-int mbedtls_ecp_check_pubkey( const mbedtls_ecp_group *grp,
-                              const mbedtls_ecp_point *pt );
+int mbedtls_ecp_check_pubkey(const mbedtls_ecp_group *grp,
+                             const mbedtls_ecp_point *pt);
 
 /**
- * \brief           This function checks that an \p mbedtls_mpi is a
+ * \brief           This function checks that an \c mbedtls_mpi is a
  *                  valid private key for this curve.
  *
  * \note            This function uses bare components rather than an
@@ -1131,8 +1161,8 @@
  *                  private key for the given curve.
  * \return          Another negative error code on other kinds of failure.
  */
-int mbedtls_ecp_check_privkey( const mbedtls_ecp_group *grp,
-                               const mbedtls_mpi *d );
+int mbedtls_ecp_check_privkey(const mbedtls_ecp_group *grp,
+                              const mbedtls_mpi *d);
 
 /**
  * \brief           This function generates a private key.
@@ -1149,10 +1179,10 @@
  * \return          An \c MBEDTLS_ERR_ECP_XXX or \c MBEDTLS_MPI_XXX error code
  *                  on failure.
  */
-int mbedtls_ecp_gen_privkey( const mbedtls_ecp_group *grp,
-                     mbedtls_mpi *d,
-                     int (*f_rng)(void *, unsigned char *, size_t),
-                     void *p_rng );
+int mbedtls_ecp_gen_privkey(const mbedtls_ecp_group *grp,
+                            mbedtls_mpi *d,
+                            int (*f_rng)(void *, unsigned char *, size_t),
+                            void *p_rng);
 
 /**
  * \brief           This function generates a keypair with a configurable base
@@ -1181,11 +1211,11 @@
  * \return          An \c MBEDTLS_ERR_ECP_XXX or \c MBEDTLS_MPI_XXX error code
  *                  on failure.
  */
-int mbedtls_ecp_gen_keypair_base( mbedtls_ecp_group *grp,
-                                  const mbedtls_ecp_point *G,
-                                  mbedtls_mpi *d, mbedtls_ecp_point *Q,
-                                  int (*f_rng)(void *, unsigned char *, size_t),
-                                  void *p_rng );
+int mbedtls_ecp_gen_keypair_base(mbedtls_ecp_group *grp,
+                                 const mbedtls_ecp_point *G,
+                                 mbedtls_mpi *d, mbedtls_ecp_point *Q,
+                                 int (*f_rng)(void *, unsigned char *, size_t),
+                                 void *p_rng);
 
 /**
  * \brief           This function generates an ECP keypair.
@@ -1210,10 +1240,10 @@
  * \return          An \c MBEDTLS_ERR_ECP_XXX or \c MBEDTLS_MPI_XXX error code
  *                  on failure.
  */
-int mbedtls_ecp_gen_keypair( mbedtls_ecp_group *grp, mbedtls_mpi *d,
-                             mbedtls_ecp_point *Q,
-                             int (*f_rng)(void *, unsigned char *, size_t),
-                             void *p_rng );
+int mbedtls_ecp_gen_keypair(mbedtls_ecp_group *grp, mbedtls_mpi *d,
+                            mbedtls_ecp_point *Q,
+                            int (*f_rng)(void *, unsigned char *, size_t),
+                            void *p_rng);
 
 /**
  * \brief           This function generates an ECP key.
@@ -1228,9 +1258,9 @@
  * \return          An \c MBEDTLS_ERR_ECP_XXX or \c MBEDTLS_MPI_XXX error code
  *                  on failure.
  */
-int mbedtls_ecp_gen_key( mbedtls_ecp_group_id grp_id, mbedtls_ecp_keypair *key,
-                         int (*f_rng)(void *, unsigned char *, size_t),
-                         void *p_rng );
+int mbedtls_ecp_gen_key(mbedtls_ecp_group_id grp_id, mbedtls_ecp_keypair *key,
+                        int (*f_rng)(void *, unsigned char *, size_t),
+                        void *p_rng);
 
 /**
  * \brief           This function reads an elliptic curve private key.
@@ -1250,8 +1280,8 @@
  *                  the group is not implemented.
  * \return          Another negative error code on different kinds of failure.
  */
-int mbedtls_ecp_read_key( mbedtls_ecp_group_id grp_id, mbedtls_ecp_keypair *key,
-                          const unsigned char *buf, size_t buflen );
+int mbedtls_ecp_read_key(mbedtls_ecp_group_id grp_id, mbedtls_ecp_keypair *key,
+                         const unsigned char *buf, size_t buflen);
 
 /**
  * \brief           This function exports an elliptic curve private key.
@@ -1269,8 +1299,8 @@
  *                  the group is not implemented.
  * \return          Another negative error code on different kinds of failure.
  */
-int mbedtls_ecp_write_key( mbedtls_ecp_keypair *key,
-                           unsigned char *buf, size_t buflen );
+int mbedtls_ecp_write_key(mbedtls_ecp_keypair *key,
+                          unsigned char *buf, size_t buflen);
 
 /**
  * \brief           This function checks that the keypair objects
@@ -1289,8 +1319,8 @@
  * \return          An \c MBEDTLS_ERR_ECP_XXX or an \c MBEDTLS_ERR_MPI_XXX
  *                  error code on calculation failure.
  */
-int mbedtls_ecp_check_pub_priv( const mbedtls_ecp_keypair *pub,
-                                const mbedtls_ecp_keypair *prv );
+int mbedtls_ecp_check_pub_priv(const mbedtls_ecp_keypair *pub,
+                               const mbedtls_ecp_keypair *prv);
 
 #if defined(MBEDTLS_SELF_TEST)
 
@@ -1300,7 +1330,7 @@
  * \return         \c 0 on success.
  * \return         \c 1 on failure.
  */
-int mbedtls_ecp_self_test( int verbose );
+int mbedtls_ecp_self_test(int verbose);
 
 #endif /* MBEDTLS_SELF_TEST */
 
diff --git a/third_party/mbedtls/repo/include/mbedtls/ecp_internal.h b/third_party/mbedtls/repo/include/mbedtls/ecp_internal.h
index 6a47a8f..f6af5cb 100644
--- a/third_party/mbedtls/repo/include/mbedtls/ecp_internal.h
+++ b/third_party/mbedtls/repo/include/mbedtls/ecp_internal.h
@@ -6,19 +6,7 @@
  */
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 
 /*
@@ -76,7 +64,7 @@
  *
  * \return          Non-zero if successful.
  */
-unsigned char mbedtls_internal_ecp_grp_capable( const mbedtls_ecp_group *grp );
+unsigned char mbedtls_internal_ecp_grp_capable(const mbedtls_ecp_group *grp);
 
 /**
  * \brief           Initialise the Elliptic Curve Point module extension.
@@ -93,7 +81,7 @@
  *
  * \return          0 if successful.
  */
-int mbedtls_internal_ecp_init( const mbedtls_ecp_group *grp );
+int mbedtls_internal_ecp_init(const mbedtls_ecp_group *grp);
 
 /**
  * \brief           Frees and deallocates the Elliptic Curve Point module
@@ -101,7 +89,7 @@
  *
  * \param grp       The pointer to the group the module was initialised for.
  */
-void mbedtls_internal_ecp_free( const mbedtls_ecp_group *grp );
+void mbedtls_internal_ecp_free(const mbedtls_ecp_group *grp);
 
 #if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED)
 
@@ -121,9 +109,11 @@
  *
  * \return          0 if successful.
  */
-int mbedtls_internal_ecp_randomize_jac( const mbedtls_ecp_group *grp,
-        mbedtls_ecp_point *pt, int (*f_rng)(void *, unsigned char *, size_t),
-        void *p_rng );
+int mbedtls_internal_ecp_randomize_jac(const mbedtls_ecp_group *grp,
+                                       mbedtls_ecp_point *pt, int (*f_rng)(void *,
+                                                                           unsigned char *,
+                                                                           size_t),
+                                       void *p_rng);
 #endif
 
 #if defined(MBEDTLS_ECP_ADD_MIXED_ALT)
@@ -166,9 +156,9 @@
  *
  * \return          0 if successful.
  */
-int mbedtls_internal_ecp_add_mixed( const mbedtls_ecp_group *grp,
-        mbedtls_ecp_point *R, const mbedtls_ecp_point *P,
-        const mbedtls_ecp_point *Q );
+int mbedtls_internal_ecp_add_mixed(const mbedtls_ecp_group *grp,
+                                   mbedtls_ecp_point *R, const mbedtls_ecp_point *P,
+                                   const mbedtls_ecp_point *Q);
 #endif
 
 /**
@@ -191,8 +181,8 @@
  * \return          0 if successful.
  */
 #if defined(MBEDTLS_ECP_DOUBLE_JAC_ALT)
-int mbedtls_internal_ecp_double_jac( const mbedtls_ecp_group *grp,
-        mbedtls_ecp_point *R, const mbedtls_ecp_point *P );
+int mbedtls_internal_ecp_double_jac(const mbedtls_ecp_group *grp,
+                                    mbedtls_ecp_point *R, const mbedtls_ecp_point *P);
 #endif
 
 /**
@@ -221,8 +211,8 @@
  *                      an error if one of the points is zero.
  */
 #if defined(MBEDTLS_ECP_NORMALIZE_JAC_MANY_ALT)
-int mbedtls_internal_ecp_normalize_jac_many( const mbedtls_ecp_group *grp,
-        mbedtls_ecp_point *T[], size_t t_len );
+int mbedtls_internal_ecp_normalize_jac_many(const mbedtls_ecp_group *grp,
+                                            mbedtls_ecp_point *T[], size_t t_len);
 #endif
 
 /**
@@ -239,8 +229,8 @@
  * \return          0 if successful.
  */
 #if defined(MBEDTLS_ECP_NORMALIZE_JAC_ALT)
-int mbedtls_internal_ecp_normalize_jac( const mbedtls_ecp_group *grp,
-        mbedtls_ecp_point *pt );
+int mbedtls_internal_ecp_normalize_jac(const mbedtls_ecp_group *grp,
+                                       mbedtls_ecp_point *pt);
 #endif
 
 #endif /* MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED */
@@ -248,9 +238,12 @@
 #if defined(MBEDTLS_ECP_MONTGOMERY_ENABLED)
 
 #if defined(MBEDTLS_ECP_DOUBLE_ADD_MXZ_ALT)
-int mbedtls_internal_ecp_double_add_mxz( const mbedtls_ecp_group *grp,
-        mbedtls_ecp_point *R, mbedtls_ecp_point *S, const mbedtls_ecp_point *P,
-        const mbedtls_ecp_point *Q, const mbedtls_mpi *d );
+int mbedtls_internal_ecp_double_add_mxz(const mbedtls_ecp_group *grp,
+                                        mbedtls_ecp_point *R,
+                                        mbedtls_ecp_point *S,
+                                        const mbedtls_ecp_point *P,
+                                        const mbedtls_ecp_point *Q,
+                                        const mbedtls_mpi *d);
 #endif
 
 /**
@@ -269,9 +262,11 @@
  * \return          0 if successful
  */
 #if defined(MBEDTLS_ECP_RANDOMIZE_MXZ_ALT)
-int mbedtls_internal_ecp_randomize_mxz( const mbedtls_ecp_group *grp,
-        mbedtls_ecp_point *P, int (*f_rng)(void *, unsigned char *, size_t),
-        void *p_rng );
+int mbedtls_internal_ecp_randomize_mxz(const mbedtls_ecp_group *grp,
+                                       mbedtls_ecp_point *P, int (*f_rng)(void *,
+                                                                          unsigned char *,
+                                                                          size_t),
+                                       void *p_rng);
 #endif
 
 /**
@@ -285,8 +280,8 @@
  * \return          0 if successful
  */
 #if defined(MBEDTLS_ECP_NORMALIZE_MXZ_ALT)
-int mbedtls_internal_ecp_normalize_mxz( const mbedtls_ecp_group *grp,
-        mbedtls_ecp_point *P );
+int mbedtls_internal_ecp_normalize_mxz(const mbedtls_ecp_group *grp,
+                                       mbedtls_ecp_point *P);
 #endif
 
 #endif /* MBEDTLS_ECP_MONTGOMERY_ENABLED */
@@ -294,4 +289,3 @@
 #endif /* MBEDTLS_ECP_INTERNAL_ALT */
 
 #endif /* ecp_internal.h */
-
diff --git a/third_party/mbedtls/repo/include/mbedtls/entropy.h b/third_party/mbedtls/repo/include/mbedtls/entropy.h
index deb3c50..096bff8 100644
--- a/third_party/mbedtls/repo/include/mbedtls/entropy.h
+++ b/third_party/mbedtls/repo/include/mbedtls/entropy.h
@@ -5,19 +5,7 @@
  */
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 #ifndef MBEDTLS_ENTROPY_H
 #define MBEDTLS_ENTROPY_H
@@ -75,7 +63,7 @@
 #define MBEDTLS_ENTROPY_MAX_GATHER      128     /**< Maximum amount requested from entropy sources */
 #endif
 
-/* \} name SECTION: Module settings */
+/** \} name SECTION: Module settings */
 
 #if defined(MBEDTLS_ENTROPY_SHA512_ACCUMULATOR)
 #define MBEDTLS_ENTROPY_BLOCK_SIZE      64      /**< Block size of entropy accumulator (SHA-512) */
@@ -105,15 +93,14 @@
  *                  MBEDTLS_ERR_ENTROPY_SOURCE_FAILED otherwise
  */
 typedef int (*mbedtls_entropy_f_source_ptr)(void *data, unsigned char *output, size_t len,
-                            size_t *olen);
+                                            size_t *olen);
 
 /**
  * \brief           Entropy source state
  */
-typedef struct mbedtls_entropy_source_state
-{
+typedef struct mbedtls_entropy_source_state {
     mbedtls_entropy_f_source_ptr    f_source;   /**< The entropy source callback */
-    void *          p_source;   /**< The callback data pointer */
+    void *p_source;             /**< The callback data pointer */
     size_t          size;       /**< Amount received in bytes */
     size_t          threshold;  /**< Minimum bytes required before release */
     int             strong;     /**< Is the source strong? */
@@ -123,8 +110,7 @@
 /**
  * \brief           Entropy context structure
  */
-typedef struct mbedtls_entropy_context
-{
+typedef struct mbedtls_entropy_context {
     int accumulator_started; /* 0 after init.
                               * 1 after the first update.
                               * -1 after free. */
@@ -152,14 +138,14 @@
  *
  * \param ctx       Entropy context to initialize
  */
-void mbedtls_entropy_init( mbedtls_entropy_context *ctx );
+void mbedtls_entropy_init(mbedtls_entropy_context *ctx);
 
 /**
  * \brief           Free the data in the context
  *
  * \param ctx       Entropy context to free
  */
-void mbedtls_entropy_free( mbedtls_entropy_context *ctx );
+void mbedtls_entropy_free(mbedtls_entropy_context *ctx);
 
 /**
  * \brief           Adds an entropy source to poll
@@ -178,9 +164,9 @@
  *
  * \return          0 if successful or MBEDTLS_ERR_ENTROPY_MAX_SOURCES
  */
-int mbedtls_entropy_add_source( mbedtls_entropy_context *ctx,
-                        mbedtls_entropy_f_source_ptr f_source, void *p_source,
-                        size_t threshold, int strong );
+int mbedtls_entropy_add_source(mbedtls_entropy_context *ctx,
+                               mbedtls_entropy_f_source_ptr f_source, void *p_source,
+                               size_t threshold, int strong);
 
 /**
  * \brief           Trigger an extra gather poll for the accumulator
@@ -190,7 +176,7 @@
  *
  * \return          0 if successful, or MBEDTLS_ERR_ENTROPY_SOURCE_FAILED
  */
-int mbedtls_entropy_gather( mbedtls_entropy_context *ctx );
+int mbedtls_entropy_gather(mbedtls_entropy_context *ctx);
 
 /**
  * \brief           Retrieve entropy from the accumulator
@@ -203,7 +189,7 @@
  *
  * \return          0 if successful, or MBEDTLS_ERR_ENTROPY_SOURCE_FAILED
  */
-int mbedtls_entropy_func( void *data, unsigned char *output, size_t len );
+int mbedtls_entropy_func(void *data, unsigned char *output, size_t len);
 
 /**
  * \brief           Add data to the accumulator manually
@@ -215,8 +201,8 @@
  *
  * \return          0 if successful
  */
-int mbedtls_entropy_update_manual( mbedtls_entropy_context *ctx,
-                           const unsigned char *data, size_t len );
+int mbedtls_entropy_update_manual(mbedtls_entropy_context *ctx,
+                                  const unsigned char *data, size_t len);
 
 #if defined(MBEDTLS_ENTROPY_NV_SEED)
 /**
@@ -227,7 +213,7 @@
  *
  * \return          0 if successful
  */
-int mbedtls_entropy_update_nv_seed( mbedtls_entropy_context *ctx );
+int mbedtls_entropy_update_nv_seed(mbedtls_entropy_context *ctx);
 #endif /* MBEDTLS_ENTROPY_NV_SEED */
 
 #if defined(MBEDTLS_FS_IO)
@@ -241,7 +227,7 @@
  *                      MBEDTLS_ERR_ENTROPY_FILE_IO_ERROR on file error, or
  *                      MBEDTLS_ERR_ENTROPY_SOURCE_FAILED
  */
-int mbedtls_entropy_write_seed_file( mbedtls_entropy_context *ctx, const char *path );
+int mbedtls_entropy_write_seed_file(mbedtls_entropy_context *ctx, const char *path);
 
 /**
  * \brief               Read and update a seed file. Seed is added to this
@@ -255,7 +241,7 @@
  *                      MBEDTLS_ERR_ENTROPY_FILE_IO_ERROR on file error,
  *                      MBEDTLS_ERR_ENTROPY_SOURCE_FAILED
  */
-int mbedtls_entropy_update_seed_file( mbedtls_entropy_context *ctx, const char *path );
+int mbedtls_entropy_update_seed_file(mbedtls_entropy_context *ctx, const char *path);
 #endif /* MBEDTLS_FS_IO */
 
 #if defined(MBEDTLS_SELF_TEST)
@@ -267,7 +253,7 @@
  *
  * \return         0 if successful, or 1 if a test failed
  */
-int mbedtls_entropy_self_test( int verbose );
+int mbedtls_entropy_self_test(int verbose);
 
 #if defined(MBEDTLS_ENTROPY_HARDWARE_ALT)
 /**
@@ -283,7 +269,7 @@
  *
  * \return         0 if successful, or 1 if a test failed
  */
-int mbedtls_entropy_source_self_test( int verbose );
+int mbedtls_entropy_source_self_test(int verbose);
 #endif /* MBEDTLS_ENTROPY_HARDWARE_ALT */
 #endif /* MBEDTLS_SELF_TEST */
 
diff --git a/third_party/mbedtls/repo/include/mbedtls/entropy_poll.h b/third_party/mbedtls/repo/include/mbedtls/entropy_poll.h
index e1d7491..d7147b9 100644
--- a/third_party/mbedtls/repo/include/mbedtls/entropy_poll.h
+++ b/third_party/mbedtls/repo/include/mbedtls/entropy_poll.h
@@ -5,19 +5,7 @@
  */
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 #ifndef MBEDTLS_ENTROPY_POLL_H
 #define MBEDTLS_ENTROPY_POLL_H
@@ -48,16 +36,16 @@
  * \brief           Entropy poll callback that provides 0 entropy.
  */
 #if defined(MBEDTLS_TEST_NULL_ENTROPY)
-    int mbedtls_null_entropy_poll( void *data,
-                                unsigned char *output, size_t len, size_t *olen );
+int mbedtls_null_entropy_poll(void *data,
+                              unsigned char *output, size_t len, size_t *olen);
 #endif
 
 #if !defined(MBEDTLS_NO_PLATFORM_ENTROPY)
 /**
  * \brief           Platform-specific entropy poll callback
  */
-int mbedtls_platform_entropy_poll( void *data,
-                           unsigned char *output, size_t len, size_t *olen );
+int mbedtls_platform_entropy_poll(void *data,
+                                  unsigned char *output, size_t len, size_t *olen);
 #endif
 
 #if defined(MBEDTLS_HAVEGE_C)
@@ -66,29 +54,29 @@
  *
  * Requires an HAVEGE state as its data pointer.
  */
-int mbedtls_havege_poll( void *data,
-                 unsigned char *output, size_t len, size_t *olen );
+int mbedtls_havege_poll(void *data,
+                        unsigned char *output, size_t len, size_t *olen);
 #endif
 
 #if defined(MBEDTLS_TIMING_C)
 /**
  * \brief           mbedtls_timing_hardclock-based entropy poll callback
  */
-int mbedtls_hardclock_poll( void *data,
-                    unsigned char *output, size_t len, size_t *olen );
+int mbedtls_hardclock_poll(void *data,
+                           unsigned char *output, size_t len, size_t *olen);
 #endif
 
 #if defined(MBEDTLS_ENTROPY_HARDWARE_ALT)
 /**
  * \brief           Entropy poll callback for a hardware source
  *
- * \warning         This is not provided by mbed TLS!
+ * \warning         This is not provided by Mbed TLS!
  *                  See \c MBEDTLS_ENTROPY_HARDWARE_ALT in config.h.
  *
  * \note            This must accept NULL as its first argument.
  */
-int mbedtls_hardware_poll( void *data,
-                           unsigned char *output, size_t len, size_t *olen );
+int mbedtls_hardware_poll(void *data,
+                          unsigned char *output, size_t len, size_t *olen);
 #endif
 
 #if defined(MBEDTLS_ENTROPY_NV_SEED)
@@ -97,8 +85,8 @@
  *
  * \note            This must accept NULL as its first argument.
  */
-int mbedtls_nv_seed_poll( void *data,
-                          unsigned char *output, size_t len, size_t *olen );
+int mbedtls_nv_seed_poll(void *data,
+                         unsigned char *output, size_t len, size_t *olen);
 #endif
 
 #ifdef __cplusplus
diff --git a/third_party/mbedtls/repo/include/mbedtls/error.h b/third_party/mbedtls/repo/include/mbedtls/error.h
index 50f2538..7a18373 100644
--- a/third_party/mbedtls/repo/include/mbedtls/error.h
+++ b/third_party/mbedtls/repo/include/mbedtls/error.h
@@ -5,19 +5,7 @@
  */
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 #ifndef MBEDTLS_ERROR_H
 #define MBEDTLS_ERROR_H
@@ -30,7 +18,7 @@
 
 #include <stddef.h>
 
-#if ( defined(__ARMCC_VERSION) || defined(_MSC_VER) ) && \
+#if (defined(__ARMCC_VERSION) || defined(_MSC_VER)) && \
     !defined(inline) && !defined(__cplusplus)
 #define inline __inline
 #endif
@@ -127,15 +115,15 @@
  *        Wrapper macro for mbedtls_error_add(). See that function for
  *        more details.
  */
-#define MBEDTLS_ERROR_ADD( high, low ) \
-        mbedtls_error_add( high, low, __FILE__, __LINE__ )
+#define MBEDTLS_ERROR_ADD(high, low) \
+    mbedtls_error_add(high, low, __FILE__, __LINE__)
 
 #if defined(MBEDTLS_TEST_HOOKS)
 /**
  * \brief Testing hook called before adding/combining two error codes together.
  *        Only used when invasive testing is enabled via MBEDTLS_TEST_HOOKS.
  */
-extern void (*mbedtls_test_hook_error_add)( int, int, const char *, int );
+extern void (*mbedtls_test_hook_error_add)(int, int, const char *, int);
 #endif
 
 /**
@@ -156,29 +144,30 @@
  * \param file      file where this error code addition occurred.
  * \param line      line where this error code addition occurred.
  */
-static inline int mbedtls_error_add( int high, int low,
-                                     const char *file, int line )
+static inline int mbedtls_error_add(int high, int low,
+                                    const char *file, int line)
 {
 #if defined(MBEDTLS_TEST_HOOKS)
-    if( *mbedtls_test_hook_error_add != NULL )
-        ( *mbedtls_test_hook_error_add )( high, low, file, line );
+    if (*mbedtls_test_hook_error_add != NULL) {
+        (*mbedtls_test_hook_error_add)(high, low, file, line);
+    }
 #endif
-    (void)file;
-    (void)line;
+    (void) file;
+    (void) line;
 
-    return( high + low );
+    return high + low;
 }
 
 /**
- * \brief Translate a mbed TLS error code into a string representation,
- *        Result is truncated if necessary and always includes a terminating
- *        null byte.
+ * \brief Translate an Mbed TLS error code into a string representation.
+ *        The result is truncated if necessary and always includes a
+ *        terminating null byte.
  *
  * \param errnum    error code
  * \param buffer    buffer to place representation in
  * \param buflen    length of the buffer
  */
-void mbedtls_strerror( int errnum, char *buffer, size_t buflen );
+void mbedtls_strerror(int errnum, char *buffer, size_t buflen);
 
 /**
  * \brief Translate the high-level part of an Mbed TLS error code into a string
@@ -193,7 +182,7 @@
  * \return The string representation of the error code, or \c NULL if the error
  *         code is unknown.
  */
-const char * mbedtls_high_level_strerr( int error_code );
+const char *mbedtls_high_level_strerr(int error_code);
 
 /**
  * \brief Translate the low-level part of an Mbed TLS error code into a string
@@ -208,7 +197,7 @@
  * \return The string representation of the error code, or \c NULL if the error
  *         code is unknown.
  */
-const char * mbedtls_low_level_strerr( int error_code );
+const char *mbedtls_low_level_strerr(int error_code);
 
 #ifdef __cplusplus
 }
diff --git a/third_party/mbedtls/repo/include/mbedtls/gcm.h b/third_party/mbedtls/repo/include/mbedtls/gcm.h
index 9723a17..1ad0e9e 100644
--- a/third_party/mbedtls/repo/include/mbedtls/gcm.h
+++ b/third_party/mbedtls/repo/include/mbedtls/gcm.h
@@ -13,19 +13,7 @@
  */
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 
 #ifndef MBEDTLS_GCM_H
@@ -63,8 +51,7 @@
 /**
  * \brief          The GCM context structure.
  */
-typedef struct mbedtls_gcm_context
-{
+typedef struct mbedtls_gcm_context {
     mbedtls_cipher_context_t cipher_ctx;  /*!< The cipher context used. */
     uint64_t HL[16];                      /*!< Precalculated HTable low. */
     uint64_t HH[16];                      /*!< Precalculated HTable high. */
@@ -74,8 +61,8 @@
     unsigned char y[16];                  /*!< The Y working value. */
     unsigned char buf[16];                /*!< The buf working value. */
     int mode;                             /*!< The operation to perform:
-                                               #MBEDTLS_GCM_ENCRYPT or
-                                               #MBEDTLS_GCM_DECRYPT. */
+                                           #MBEDTLS_GCM_ENCRYPT or
+                                           #MBEDTLS_GCM_DECRYPT. */
 }
 mbedtls_gcm_context;
 
@@ -94,7 +81,7 @@
  *
  * \param ctx       The GCM context to initialize. This must not be \c NULL.
  */
-void mbedtls_gcm_init( mbedtls_gcm_context *ctx );
+void mbedtls_gcm_init(mbedtls_gcm_context *ctx);
 
 /**
  * \brief           This function associates a GCM context with a
@@ -112,10 +99,10 @@
  * \return          \c 0 on success.
  * \return          A cipher-specific error code on failure.
  */
-int mbedtls_gcm_setkey( mbedtls_gcm_context *ctx,
-                        mbedtls_cipher_id_t cipher,
-                        const unsigned char *key,
-                        unsigned int keybits );
+int mbedtls_gcm_setkey(mbedtls_gcm_context *ctx,
+                       mbedtls_cipher_id_t cipher,
+                       const unsigned char *key,
+                       unsigned int keybits);
 
 /**
  * \brief           This function performs GCM encryption or decryption of a buffer.
@@ -168,17 +155,17 @@
  *                  not valid or a cipher-specific error code if the encryption
  *                  or decryption failed.
  */
-int mbedtls_gcm_crypt_and_tag( mbedtls_gcm_context *ctx,
-                       int mode,
-                       size_t length,
-                       const unsigned char *iv,
-                       size_t iv_len,
-                       const unsigned char *add,
-                       size_t add_len,
-                       const unsigned char *input,
-                       unsigned char *output,
-                       size_t tag_len,
-                       unsigned char *tag );
+int mbedtls_gcm_crypt_and_tag(mbedtls_gcm_context *ctx,
+                              int mode,
+                              size_t length,
+                              const unsigned char *iv,
+                              size_t iv_len,
+                              const unsigned char *add,
+                              size_t add_len,
+                              const unsigned char *input,
+                              unsigned char *output,
+                              size_t tag_len,
+                              unsigned char *tag);
 
 /**
  * \brief           This function performs a GCM authenticated decryption of a
@@ -213,16 +200,16 @@
  *                  not valid or a cipher-specific error code if the decryption
  *                  failed.
  */
-int mbedtls_gcm_auth_decrypt( mbedtls_gcm_context *ctx,
-                      size_t length,
-                      const unsigned char *iv,
-                      size_t iv_len,
-                      const unsigned char *add,
-                      size_t add_len,
-                      const unsigned char *tag,
-                      size_t tag_len,
-                      const unsigned char *input,
-                      unsigned char *output );
+int mbedtls_gcm_auth_decrypt(mbedtls_gcm_context *ctx,
+                             size_t length,
+                             const unsigned char *iv,
+                             size_t iv_len,
+                             const unsigned char *add,
+                             size_t add_len,
+                             const unsigned char *tag,
+                             size_t tag_len,
+                             const unsigned char *input,
+                             unsigned char *output);
 
 /**
  * \brief           This function starts a GCM encryption or decryption
@@ -241,12 +228,12 @@
  *
  * \return          \c 0 on success.
  */
-int mbedtls_gcm_starts( mbedtls_gcm_context *ctx,
-                int mode,
-                const unsigned char *iv,
-                size_t iv_len,
-                const unsigned char *add,
-                size_t add_len );
+int mbedtls_gcm_starts(mbedtls_gcm_context *ctx,
+                       int mode,
+                       const unsigned char *iv,
+                       size_t iv_len,
+                       const unsigned char *add,
+                       size_t add_len);
 
 /**
  * \brief           This function feeds an input buffer into an ongoing GCM
@@ -273,10 +260,10 @@
  * \return         \c 0 on success.
  * \return         #MBEDTLS_ERR_GCM_BAD_INPUT on failure.
  */
-int mbedtls_gcm_update( mbedtls_gcm_context *ctx,
-                size_t length,
-                const unsigned char *input,
-                unsigned char *output );
+int mbedtls_gcm_update(mbedtls_gcm_context *ctx,
+                       size_t length,
+                       const unsigned char *input,
+                       unsigned char *output);
 
 /**
  * \brief           This function finishes the GCM operation and generates
@@ -294,9 +281,9 @@
  * \return          \c 0 on success.
  * \return          #MBEDTLS_ERR_GCM_BAD_INPUT on failure.
  */
-int mbedtls_gcm_finish( mbedtls_gcm_context *ctx,
-                unsigned char *tag,
-                size_t tag_len );
+int mbedtls_gcm_finish(mbedtls_gcm_context *ctx,
+                       unsigned char *tag,
+                       size_t tag_len);
 
 /**
  * \brief           This function clears a GCM context and the underlying
@@ -305,7 +292,7 @@
  * \param ctx       The GCM context to clear. If this is \c NULL, the call has
  *                  no effect. Otherwise, this must be initialized.
  */
-void mbedtls_gcm_free( mbedtls_gcm_context *ctx );
+void mbedtls_gcm_free(mbedtls_gcm_context *ctx);
 
 #if defined(MBEDTLS_SELF_TEST)
 
@@ -315,7 +302,7 @@
  * \return         \c 0 on success.
  * \return         \c 1 on failure.
  */
-int mbedtls_gcm_self_test( int verbose );
+int mbedtls_gcm_self_test(int verbose);
 
 #endif /* MBEDTLS_SELF_TEST */
 
diff --git a/third_party/mbedtls/repo/include/mbedtls/havege.h b/third_party/mbedtls/repo/include/mbedtls/havege.h
index 7d27039..cdaf8a8 100644
--- a/third_party/mbedtls/repo/include/mbedtls/havege.h
+++ b/third_party/mbedtls/repo/include/mbedtls/havege.h
@@ -5,19 +5,7 @@
  */
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 #ifndef MBEDTLS_HAVEGE_H
 #define MBEDTLS_HAVEGE_H
@@ -40,8 +28,7 @@
 /**
  * \brief          HAVEGE state structure
  */
-typedef struct mbedtls_havege_state
-{
+typedef struct mbedtls_havege_state {
     uint32_t PT1, PT2, offset[2];
     uint32_t pool[MBEDTLS_HAVEGE_COLLECT_SIZE];
     uint32_t WALK[8192];
@@ -53,14 +40,14 @@
  *
  * \param hs       HAVEGE state to be initialized
  */
-void mbedtls_havege_init( mbedtls_havege_state *hs );
+void mbedtls_havege_init(mbedtls_havege_state *hs);
 
 /**
  * \brief          Clear HAVEGE state
  *
  * \param hs       HAVEGE state to be cleared
  */
-void mbedtls_havege_free( mbedtls_havege_state *hs );
+void mbedtls_havege_free(mbedtls_havege_state *hs);
 
 /**
  * \brief          HAVEGE rand function
@@ -71,7 +58,7 @@
  *
  * \return         0
  */
-int mbedtls_havege_random( void *p_rng, unsigned char *output, size_t len );
+int mbedtls_havege_random(void *p_rng, unsigned char *output, size_t len);
 
 #ifdef __cplusplus
 }
diff --git a/third_party/mbedtls/repo/include/mbedtls/hkdf.h b/third_party/mbedtls/repo/include/mbedtls/hkdf.h
index 223004b..103f329 100644
--- a/third_party/mbedtls/repo/include/mbedtls/hkdf.h
+++ b/third_party/mbedtls/repo/include/mbedtls/hkdf.h
@@ -8,19 +8,7 @@
  */
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 #ifndef MBEDTLS_HKDF_H
 #define MBEDTLS_HKDF_H
@@ -39,7 +27,7 @@
  */
 /** Bad input parameters to function. */
 #define MBEDTLS_ERR_HKDF_BAD_INPUT_DATA  -0x5F80
-/* \} name */
+/** \} name */
 
 #ifdef __cplusplus
 extern "C" {
@@ -69,10 +57,10 @@
  *  \return An MBEDTLS_ERR_MD_* error for errors returned from the underlying
  *          MD layer.
  */
-int mbedtls_hkdf( const mbedtls_md_info_t *md, const unsigned char *salt,
-                  size_t salt_len, const unsigned char *ikm, size_t ikm_len,
-                  const unsigned char *info, size_t info_len,
-                  unsigned char *okm, size_t okm_len );
+int mbedtls_hkdf(const mbedtls_md_info_t *md, const unsigned char *salt,
+                 size_t salt_len, const unsigned char *ikm, size_t ikm_len,
+                 const unsigned char *info, size_t info_len,
+                 unsigned char *okm, size_t okm_len);
 
 /**
  *  \brief  Take the input keying material \p ikm and extract from it a
@@ -98,10 +86,10 @@
  *  \return An MBEDTLS_ERR_MD_* error for errors returned from the underlying
  *          MD layer.
  */
-int mbedtls_hkdf_extract( const mbedtls_md_info_t *md,
-                          const unsigned char *salt, size_t salt_len,
-                          const unsigned char *ikm, size_t ikm_len,
-                          unsigned char *prk );
+int mbedtls_hkdf_extract(const mbedtls_md_info_t *md,
+                         const unsigned char *salt, size_t salt_len,
+                         const unsigned char *ikm, size_t ikm_len,
+                         unsigned char *prk);
 
 /**
  *  \brief  Expand the supplied \p prk into several additional pseudorandom
@@ -129,9 +117,9 @@
  *  \return An MBEDTLS_ERR_MD_* error for errors returned from the underlying
  *          MD layer.
  */
-int mbedtls_hkdf_expand( const mbedtls_md_info_t *md, const unsigned char *prk,
-                         size_t prk_len, const unsigned char *info,
-                         size_t info_len, unsigned char *okm, size_t okm_len );
+int mbedtls_hkdf_expand(const mbedtls_md_info_t *md, const unsigned char *prk,
+                        size_t prk_len, const unsigned char *info,
+                        size_t info_len, unsigned char *okm, size_t okm_len);
 
 #ifdef __cplusplus
 }
diff --git a/third_party/mbedtls/repo/include/mbedtls/hmac_drbg.h b/third_party/mbedtls/repo/include/mbedtls/hmac_drbg.h
index 79132d4..d531382 100644
--- a/third_party/mbedtls/repo/include/mbedtls/hmac_drbg.h
+++ b/third_party/mbedtls/repo/include/mbedtls/hmac_drbg.h
@@ -9,19 +9,7 @@
  */
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 #ifndef MBEDTLS_HMAC_DRBG_H
 #define MBEDTLS_HMAC_DRBG_H
@@ -74,7 +62,7 @@
 #define MBEDTLS_HMAC_DRBG_MAX_SEED_INPUT    384     /**< Maximum size of (re)seed buffer */
 #endif
 
-/* \} name SECTION: Module settings */
+/** \} name SECTION: Module settings */
 
 #define MBEDTLS_HMAC_DRBG_PR_OFF   0   /**< No prediction resistance       */
 #define MBEDTLS_HMAC_DRBG_PR_ON    1   /**< Prediction resistance enabled  */
@@ -86,8 +74,7 @@
 /**
  * HMAC_DRBG context.
  */
-typedef struct mbedtls_hmac_drbg_context
-{
+typedef struct mbedtls_hmac_drbg_context {
     /* Working state: the key K is not stored explicitly,
      * but is implied by the HMAC context */
     mbedtls_md_context_t md_ctx;                    /*!< HMAC context (inc. K)  */
@@ -129,7 +116,7 @@
  *
  * \param ctx           HMAC_DRBG context to be initialized.
  */
-void mbedtls_hmac_drbg_init( mbedtls_hmac_drbg_context *ctx );
+void mbedtls_hmac_drbg_init(mbedtls_hmac_drbg_context *ctx);
 
 /**
  * \brief               HMAC_DRBG initial seeding.
@@ -187,8 +174,8 @@
  * \param len           The length of the personalization string.
  *                      This must be at most #MBEDTLS_HMAC_DRBG_MAX_INPUT
  *                      and also at most
- *                      #MBEDTLS_HMAC_DRBG_MAX_SEED_INPUT - \p entropy_len * 3 / 2
- *                      where \p entropy_len is the entropy length
+ *                      #MBEDTLS_HMAC_DRBG_MAX_SEED_INPUT - \c entropy_len * 3 / 2
+ *                      where \c entropy_len is the entropy length
  *                      described above.
  *
  * \return              \c 0 if successful.
@@ -199,15 +186,15 @@
  * \return              #MBEDTLS_ERR_HMAC_DRBG_ENTROPY_SOURCE_FAILED
  *                      if the call to \p f_entropy failed.
  */
-int mbedtls_hmac_drbg_seed( mbedtls_hmac_drbg_context *ctx,
-                    const mbedtls_md_info_t * md_info,
-                    int (*f_entropy)(void *, unsigned char *, size_t),
-                    void *p_entropy,
-                    const unsigned char *custom,
-                    size_t len );
+int mbedtls_hmac_drbg_seed(mbedtls_hmac_drbg_context *ctx,
+                           const mbedtls_md_info_t *md_info,
+                           int (*f_entropy)(void *, unsigned char *, size_t),
+                           void *p_entropy,
+                           const unsigned char *custom,
+                           size_t len);
 
 /**
- * \brief               Initilisation of simpified HMAC_DRBG (never reseeds).
+ * \brief               Initialisation of simplified HMAC_DRBG (never reseeds).
  *
  * This function is meant for use in algorithms that need a pseudorandom
  * input such as deterministic ECDSA.
@@ -234,9 +221,9 @@
  * \return              #MBEDTLS_ERR_MD_ALLOC_FAILED if there was not enough
  *                      memory to allocate context data.
  */
-int mbedtls_hmac_drbg_seed_buf( mbedtls_hmac_drbg_context *ctx,
-                        const mbedtls_md_info_t * md_info,
-                        const unsigned char *data, size_t data_len );
+int mbedtls_hmac_drbg_seed_buf(mbedtls_hmac_drbg_context *ctx,
+                               const mbedtls_md_info_t *md_info,
+                               const unsigned char *data, size_t data_len);
 
 /**
  * \brief               This function turns prediction resistance on or off.
@@ -251,8 +238,8 @@
  * \param ctx           The HMAC_DRBG context.
  * \param resistance    #MBEDTLS_HMAC_DRBG_PR_ON or #MBEDTLS_HMAC_DRBG_PR_OFF.
  */
-void mbedtls_hmac_drbg_set_prediction_resistance( mbedtls_hmac_drbg_context *ctx,
-                                          int resistance );
+void mbedtls_hmac_drbg_set_prediction_resistance(mbedtls_hmac_drbg_context *ctx,
+                                                 int resistance);
 
 /**
  * \brief               This function sets the amount of entropy grabbed on each
@@ -263,8 +250,8 @@
  * \param ctx           The HMAC_DRBG context.
  * \param len           The amount of entropy to grab, in bytes.
  */
-void mbedtls_hmac_drbg_set_entropy_len( mbedtls_hmac_drbg_context *ctx,
-                                size_t len );
+void mbedtls_hmac_drbg_set_entropy_len(mbedtls_hmac_drbg_context *ctx,
+                                       size_t len);
 
 /**
  * \brief               Set the reseed interval.
@@ -278,8 +265,8 @@
  * \param ctx           The HMAC_DRBG context.
  * \param interval      The reseed interval.
  */
-void mbedtls_hmac_drbg_set_reseed_interval( mbedtls_hmac_drbg_context *ctx,
-                                    int interval );
+void mbedtls_hmac_drbg_set_reseed_interval(mbedtls_hmac_drbg_context *ctx,
+                                           int interval);
 
 /**
  * \brief               This function updates the state of the HMAC_DRBG context.
@@ -298,8 +285,8 @@
  * \return              \c 0 on success, or an error from the underlying
  *                      hash calculation.
  */
-int mbedtls_hmac_drbg_update_ret( mbedtls_hmac_drbg_context *ctx,
-                       const unsigned char *additional, size_t add_len );
+int mbedtls_hmac_drbg_update_ret(mbedtls_hmac_drbg_context *ctx,
+                                 const unsigned char *additional, size_t add_len);
 
 /**
  * \brief               This function reseeds the HMAC_DRBG context, that is
@@ -317,16 +304,16 @@
  * \param len           The length of the additional data.
  *                      This must be at most #MBEDTLS_HMAC_DRBG_MAX_INPUT
  *                      and also at most
- *                      #MBEDTLS_HMAC_DRBG_MAX_SEED_INPUT - \p entropy_len
- *                      where \p entropy_len is the entropy length
+ *                      #MBEDTLS_HMAC_DRBG_MAX_SEED_INPUT - \c entropy_len
+ *                      where \c entropy_len is the entropy length
  *                      (see mbedtls_hmac_drbg_set_entropy_len()).
  *
  * \return              \c 0 if successful.
  * \return              #MBEDTLS_ERR_HMAC_DRBG_ENTROPY_SOURCE_FAILED
  *                      if a call to the entropy function failed.
  */
-int mbedtls_hmac_drbg_reseed( mbedtls_hmac_drbg_context *ctx,
-                      const unsigned char *additional, size_t len );
+int mbedtls_hmac_drbg_reseed(mbedtls_hmac_drbg_context *ctx,
+                             const unsigned char *additional, size_t len);
 
 /**
  * \brief   This function updates an HMAC_DRBG instance with additional
@@ -359,10 +346,10 @@
  * \return              #MBEDTLS_ERR_HMAC_DRBG_INPUT_TOO_BIG if
  *                      \p add_len > #MBEDTLS_HMAC_DRBG_MAX_INPUT.
  */
-int mbedtls_hmac_drbg_random_with_add( void *p_rng,
-                               unsigned char *output, size_t output_len,
-                               const unsigned char *additional,
-                               size_t add_len );
+int mbedtls_hmac_drbg_random_with_add(void *p_rng,
+                                      unsigned char *output, size_t output_len,
+                                      const unsigned char *additional,
+                                      size_t add_len);
 
 /**
  * \brief   This function uses HMAC_DRBG to generate random data.
@@ -391,7 +378,7 @@
  * \return              #MBEDTLS_ERR_HMAC_DRBG_REQUEST_TOO_BIG if
  *                      \p out_len > #MBEDTLS_HMAC_DRBG_MAX_REQUEST.
  */
-int mbedtls_hmac_drbg_random( void *p_rng, unsigned char *output, size_t out_len );
+int mbedtls_hmac_drbg_random(void *p_rng, unsigned char *output, size_t out_len);
 
 /**
  * \brief               This function resets HMAC_DRBG context to the state immediately
@@ -399,9 +386,9 @@
  *
  * \param ctx           The HMAC_DRBG context to free.
  */
-void mbedtls_hmac_drbg_free( mbedtls_hmac_drbg_context *ctx );
+void mbedtls_hmac_drbg_free(mbedtls_hmac_drbg_context *ctx);
 
-#if ! defined(MBEDTLS_DEPRECATED_REMOVED)
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
 #if defined(MBEDTLS_DEPRECATED_WARNING)
 #define MBEDTLS_DEPRECATED    __attribute__((deprecated))
 #else
@@ -421,7 +408,7 @@
  */
 MBEDTLS_DEPRECATED void mbedtls_hmac_drbg_update(
     mbedtls_hmac_drbg_context *ctx,
-    const unsigned char *additional, size_t add_len );
+    const unsigned char *additional, size_t add_len);
 #undef MBEDTLS_DEPRECATED
 #endif /* !MBEDTLS_DEPRECATED_REMOVED */
 
@@ -437,7 +424,7 @@
  * \return              #MBEDTLS_ERR_HMAC_DRBG_ENTROPY_SOURCE_FAILED on reseed
  *                      failure.
  */
-int mbedtls_hmac_drbg_write_seed_file( mbedtls_hmac_drbg_context *ctx, const char *path );
+int mbedtls_hmac_drbg_write_seed_file(mbedtls_hmac_drbg_context *ctx, const char *path);
 
 /**
  * \brief               This function reads and updates a seed file. The seed
@@ -453,7 +440,7 @@
  * \return              #MBEDTLS_ERR_HMAC_DRBG_INPUT_TOO_BIG if the existing
  *                      seed file is too large.
  */
-int mbedtls_hmac_drbg_update_seed_file( mbedtls_hmac_drbg_context *ctx, const char *path );
+int mbedtls_hmac_drbg_update_seed_file(mbedtls_hmac_drbg_context *ctx, const char *path);
 #endif /* MBEDTLS_FS_IO */
 
 
@@ -464,7 +451,7 @@
  * \return              \c 0 if successful.
  * \return              \c 1 if the test failed.
  */
-int mbedtls_hmac_drbg_self_test( int verbose );
+int mbedtls_hmac_drbg_self_test(int verbose);
 #endif
 
 #ifdef __cplusplus
diff --git a/third_party/mbedtls/repo/include/mbedtls/md.h b/third_party/mbedtls/repo/include/mbedtls/md.h
index 84fafd2..7b43113 100644
--- a/third_party/mbedtls/repo/include/mbedtls/md.h
+++ b/third_party/mbedtls/repo/include/mbedtls/md.h
@@ -1,4 +1,4 @@
- /**
+/**
  * \file md.h
  *
  * \brief This file contains the generic message-digest wrapper.
@@ -7,19 +7,7 @@
  */
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  */
 
 #ifndef MBEDTLS_MD_H
@@ -92,8 +80,7 @@
 /**
  * The generic message-digest context.
  */
-typedef struct mbedtls_md_context_t
-{
+typedef struct mbedtls_md_context_t {
     /** Information about the associated message digest. */
     const mbedtls_md_info_t *md_info;
 
@@ -115,7 +102,7 @@
  *                  message-digest enumeration #mbedtls_md_type_t.
  *                  The last entry is 0.
  */
-const int *mbedtls_md_list( void );
+const int *mbedtls_md_list(void);
 
 /**
  * \brief           This function returns the message-digest information
@@ -126,7 +113,7 @@
  * \return          The message-digest information associated with \p md_name.
  * \return          NULL if the associated message-digest information is not found.
  */
-const mbedtls_md_info_t *mbedtls_md_info_from_string( const char *md_name );
+const mbedtls_md_info_t *mbedtls_md_info_from_string(const char *md_name);
 
 /**
  * \brief           This function returns the message-digest information
@@ -137,7 +124,7 @@
  * \return          The message-digest information associated with \p md_type.
  * \return          NULL if the associated message-digest information is not found.
  */
-const mbedtls_md_info_t *mbedtls_md_info_from_type( mbedtls_md_type_t md_type );
+const mbedtls_md_info_t *mbedtls_md_info_from_type(mbedtls_md_type_t md_type);
 
 /**
  * \brief           This function initializes a message-digest context without
@@ -147,7 +134,7 @@
  *                  context for mbedtls_md_setup() for binding it to a
  *                  message-digest algorithm.
  */
-void mbedtls_md_init( mbedtls_md_context_t *ctx );
+void mbedtls_md_init(mbedtls_md_context_t *ctx);
 
 /**
  * \brief           This function clears the internal structure of \p ctx and
@@ -162,9 +149,9 @@
  *                  You must not call this function if you have not called
  *                  mbedtls_md_init().
  */
-void mbedtls_md_free( mbedtls_md_context_t *ctx );
+void mbedtls_md_free(mbedtls_md_context_t *ctx);
 
-#if ! defined(MBEDTLS_DEPRECATED_REMOVED)
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
 #if defined(MBEDTLS_DEPRECATED_WARNING)
 #define MBEDTLS_DEPRECATED    __attribute__((deprecated))
 #else
@@ -188,7 +175,8 @@
  *                  failure.
  * \return          #MBEDTLS_ERR_MD_ALLOC_FAILED on memory-allocation failure.
  */
-int mbedtls_md_init_ctx( mbedtls_md_context_t *ctx, const mbedtls_md_info_t *md_info ) MBEDTLS_DEPRECATED;
+int mbedtls_md_init_ctx(mbedtls_md_context_t *ctx,
+                        const mbedtls_md_info_t *md_info) MBEDTLS_DEPRECATED;
 #undef MBEDTLS_DEPRECATED
 #endif /* MBEDTLS_DEPRECATED_REMOVED */
 
@@ -212,10 +200,10 @@
  * \return          #MBEDTLS_ERR_MD_ALLOC_FAILED on memory-allocation failure.
  */
 MBEDTLS_CHECK_RETURN_TYPICAL
-int mbedtls_md_setup( mbedtls_md_context_t *ctx, const mbedtls_md_info_t *md_info, int hmac );
+int mbedtls_md_setup(mbedtls_md_context_t *ctx, const mbedtls_md_info_t *md_info, int hmac);
 
 /**
- * \brief           This function clones the state of an message-digest
+ * \brief           This function clones the state of a message-digest
  *                  context.
  *
  * \note            You must call mbedtls_md_setup() on \c dst before calling
@@ -234,8 +222,8 @@
  * \return          #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification failure.
  */
 MBEDTLS_CHECK_RETURN_TYPICAL
-int mbedtls_md_clone( mbedtls_md_context_t *dst,
-                      const mbedtls_md_context_t *src );
+int mbedtls_md_clone(mbedtls_md_context_t *dst,
+                     const mbedtls_md_context_t *src);
 
 /**
  * \brief           This function extracts the message-digest size from the
@@ -246,7 +234,7 @@
  *
  * \return          The size of the message-digest output in Bytes.
  */
-unsigned char mbedtls_md_get_size( const mbedtls_md_info_t *md_info );
+unsigned char mbedtls_md_get_size(const mbedtls_md_info_t *md_info);
 
 /**
  * \brief           This function extracts the message-digest type from the
@@ -257,7 +245,7 @@
  *
  * \return          The type of the message digest.
  */
-mbedtls_md_type_t mbedtls_md_get_type( const mbedtls_md_info_t *md_info );
+mbedtls_md_type_t mbedtls_md_get_type(const mbedtls_md_info_t *md_info);
 
 /**
  * \brief           This function extracts the message-digest name from the
@@ -268,7 +256,7 @@
  *
  * \return          The name of the message digest.
  */
-const char *mbedtls_md_get_name( const mbedtls_md_info_t *md_info );
+const char *mbedtls_md_get_name(const mbedtls_md_info_t *md_info);
 
 /**
  * \brief           This function starts a message-digest computation.
@@ -284,7 +272,7 @@
  *                  failure.
  */
 MBEDTLS_CHECK_RETURN_TYPICAL
-int mbedtls_md_starts( mbedtls_md_context_t *ctx );
+int mbedtls_md_starts(mbedtls_md_context_t *ctx);
 
 /**
  * \brief           This function feeds an input buffer into an ongoing
@@ -303,7 +291,7 @@
  *                  failure.
  */
 MBEDTLS_CHECK_RETURN_TYPICAL
-int mbedtls_md_update( mbedtls_md_context_t *ctx, const unsigned char *input, size_t ilen );
+int mbedtls_md_update(mbedtls_md_context_t *ctx, const unsigned char *input, size_t ilen);
 
 /**
  * \brief           This function finishes the digest operation,
@@ -324,7 +312,7 @@
  *                  failure.
  */
 MBEDTLS_CHECK_RETURN_TYPICAL
-int mbedtls_md_finish( mbedtls_md_context_t *ctx, unsigned char *output );
+int mbedtls_md_finish(mbedtls_md_context_t *ctx, unsigned char *output);
 
 /**
  * \brief          This function calculates the message-digest of a buffer,
@@ -345,8 +333,8 @@
  *                 failure.
  */
 MBEDTLS_CHECK_RETURN_TYPICAL
-int mbedtls_md( const mbedtls_md_info_t *md_info, const unsigned char *input, size_t ilen,
-        unsigned char *output );
+int mbedtls_md(const mbedtls_md_info_t *md_info, const unsigned char *input, size_t ilen,
+               unsigned char *output);
 
 #if defined(MBEDTLS_FS_IO)
 /**
@@ -367,8 +355,8 @@
  * \return         #MBEDTLS_ERR_MD_BAD_INPUT_DATA if \p md_info was NULL.
  */
 MBEDTLS_CHECK_RETURN_TYPICAL
-int mbedtls_md_file( const mbedtls_md_info_t *md_info, const char *path,
-                     unsigned char *output );
+int mbedtls_md_file(const mbedtls_md_info_t *md_info, const char *path,
+                    unsigned char *output);
 #endif /* MBEDTLS_FS_IO */
 
 /**
@@ -390,8 +378,8 @@
  *                  failure.
  */
 MBEDTLS_CHECK_RETURN_TYPICAL
-int mbedtls_md_hmac_starts( mbedtls_md_context_t *ctx, const unsigned char *key,
-                    size_t keylen );
+int mbedtls_md_hmac_starts(mbedtls_md_context_t *ctx, const unsigned char *key,
+                           size_t keylen);
 
 /**
  * \brief           This function feeds an input buffer into an ongoing HMAC
@@ -413,8 +401,8 @@
  *                  failure.
  */
 MBEDTLS_CHECK_RETURN_TYPICAL
-int mbedtls_md_hmac_update( mbedtls_md_context_t *ctx, const unsigned char *input,
-                    size_t ilen );
+int mbedtls_md_hmac_update(mbedtls_md_context_t *ctx, const unsigned char *input,
+                           size_t ilen);
 
 /**
  * \brief           This function finishes the HMAC operation, and writes
@@ -435,7 +423,7 @@
  *                  failure.
  */
 MBEDTLS_CHECK_RETURN_TYPICAL
-int mbedtls_md_hmac_finish( mbedtls_md_context_t *ctx, unsigned char *output);
+int mbedtls_md_hmac_finish(mbedtls_md_context_t *ctx, unsigned char *output);
 
 /**
  * \brief           This function prepares to authenticate a new message with
@@ -453,7 +441,7 @@
  *                  failure.
  */
 MBEDTLS_CHECK_RETURN_TYPICAL
-int mbedtls_md_hmac_reset( mbedtls_md_context_t *ctx );
+int mbedtls_md_hmac_reset(mbedtls_md_context_t *ctx);
 
 /**
  * \brief          This function calculates the full generic HMAC
@@ -478,13 +466,13 @@
  *                 failure.
  */
 MBEDTLS_CHECK_RETURN_TYPICAL
-int mbedtls_md_hmac( const mbedtls_md_info_t *md_info, const unsigned char *key, size_t keylen,
-                const unsigned char *input, size_t ilen,
-                unsigned char *output );
+int mbedtls_md_hmac(const mbedtls_md_info_t *md_info, const unsigned char *key, size_t keylen,
+                    const unsigned char *input, size_t ilen,
+                    unsigned char *output);
 
 /* Internal use */
 MBEDTLS_CHECK_RETURN_TYPICAL
-int mbedtls_md_process( mbedtls_md_context_t *ctx, const unsigned char *data );
+int mbedtls_md_process(mbedtls_md_context_t *ctx, const unsigned char *data);
 
 #ifdef __cplusplus
 }
diff --git a/third_party/mbedtls/repo/include/mbedtls/md2.h b/third_party/mbedtls/repo/include/mbedtls/md2.h
index 7f3d5cf..afcf3a3 100644
--- a/third_party/mbedtls/repo/include/mbedtls/md2.h
+++ b/third_party/mbedtls/repo/include/mbedtls/md2.h
@@ -9,19 +9,7 @@
  */
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  *
  */
 #ifndef MBEDTLS_MD2_H
@@ -55,8 +43,7 @@
  *                 stronger message digests instead.
  *
  */
-typedef struct mbedtls_md2_context
-{
+typedef struct mbedtls_md2_context {
     unsigned char cksum[16];    /*!< checksum of the data block */
     unsigned char state[48];    /*!< intermediate digest state  */
     unsigned char buffer[16];   /*!< data block being processed */
@@ -78,7 +65,7 @@
  *                 stronger message digests instead.
  *
  */
-void mbedtls_md2_init( mbedtls_md2_context *ctx );
+void mbedtls_md2_init(mbedtls_md2_context *ctx);
 
 /**
  * \brief          Clear MD2 context
@@ -90,7 +77,7 @@
  *                 stronger message digests instead.
  *
  */
-void mbedtls_md2_free( mbedtls_md2_context *ctx );
+void mbedtls_md2_free(mbedtls_md2_context *ctx);
 
 /**
  * \brief          Clone (the state of) an MD2 context
@@ -103,8 +90,8 @@
  *                 stronger message digests instead.
  *
  */
-void mbedtls_md2_clone( mbedtls_md2_context *dst,
-                        const mbedtls_md2_context *src );
+void mbedtls_md2_clone(mbedtls_md2_context *dst,
+                       const mbedtls_md2_context *src);
 
 /**
  * \brief          MD2 context setup
@@ -118,7 +105,7 @@
  *                 stronger message digests instead.
  *
  */
-int mbedtls_md2_starts_ret( mbedtls_md2_context *ctx );
+int mbedtls_md2_starts_ret(mbedtls_md2_context *ctx);
 
 /**
  * \brief          MD2 process buffer
@@ -134,9 +121,9 @@
  *                 stronger message digests instead.
  *
  */
-int mbedtls_md2_update_ret( mbedtls_md2_context *ctx,
-                            const unsigned char *input,
-                            size_t ilen );
+int mbedtls_md2_update_ret(mbedtls_md2_context *ctx,
+                           const unsigned char *input,
+                           size_t ilen);
 
 /**
  * \brief          MD2 final digest
@@ -151,8 +138,8 @@
  *                 stronger message digests instead.
  *
  */
-int mbedtls_md2_finish_ret( mbedtls_md2_context *ctx,
-                            unsigned char output[16] );
+int mbedtls_md2_finish_ret(mbedtls_md2_context *ctx,
+                           unsigned char output[16]);
 
 /**
  * \brief          MD2 process data block (internal use only)
@@ -166,7 +153,7 @@
  *                 stronger message digests instead.
  *
  */
-int mbedtls_internal_md2_process( mbedtls_md2_context *ctx );
+int mbedtls_internal_md2_process(mbedtls_md2_context *ctx);
 
 #if !defined(MBEDTLS_DEPRECATED_REMOVED)
 #if defined(MBEDTLS_DEPRECATED_WARNING)
@@ -186,7 +173,7 @@
  *                 stronger message digests instead.
  *
  */
-MBEDTLS_DEPRECATED void mbedtls_md2_starts( mbedtls_md2_context *ctx );
+MBEDTLS_DEPRECATED void mbedtls_md2_starts(mbedtls_md2_context *ctx);
 
 /**
  * \brief          MD2 process buffer
@@ -202,9 +189,9 @@
  *                 stronger message digests instead.
  *
  */
-MBEDTLS_DEPRECATED void mbedtls_md2_update( mbedtls_md2_context *ctx,
-                                            const unsigned char *input,
-                                            size_t ilen );
+MBEDTLS_DEPRECATED void mbedtls_md2_update(mbedtls_md2_context *ctx,
+                                           const unsigned char *input,
+                                           size_t ilen);
 
 /**
  * \brief          MD2 final digest
@@ -219,8 +206,8 @@
  *                 stronger message digests instead.
  *
  */
-MBEDTLS_DEPRECATED void mbedtls_md2_finish( mbedtls_md2_context *ctx,
-                                            unsigned char output[16] );
+MBEDTLS_DEPRECATED void mbedtls_md2_finish(mbedtls_md2_context *ctx,
+                                           unsigned char output[16]);
 
 /**
  * \brief          MD2 process data block (internal use only)
@@ -234,7 +221,7 @@
  *                 stronger message digests instead.
  *
  */
-MBEDTLS_DEPRECATED void mbedtls_md2_process( mbedtls_md2_context *ctx );
+MBEDTLS_DEPRECATED void mbedtls_md2_process(mbedtls_md2_context *ctx);
 
 #undef MBEDTLS_DEPRECATED
 #endif /* !MBEDTLS_DEPRECATED_REMOVED */
@@ -251,9 +238,9 @@
  *                 stronger message digests instead.
  *
  */
-int mbedtls_md2_ret( const unsigned char *input,
-                     size_t ilen,
-                     unsigned char output[16] );
+int mbedtls_md2_ret(const unsigned char *input,
+                    size_t ilen,
+                    unsigned char output[16]);
 
 #if !defined(MBEDTLS_DEPRECATED_REMOVED)
 #if defined(MBEDTLS_DEPRECATED_WARNING)
@@ -275,9 +262,9 @@
  *                 stronger message digests instead.
  *
  */
-MBEDTLS_DEPRECATED void mbedtls_md2( const unsigned char *input,
-                                     size_t ilen,
-                                     unsigned char output[16] );
+MBEDTLS_DEPRECATED void mbedtls_md2(const unsigned char *input,
+                                    size_t ilen,
+                                    unsigned char output[16]);
 
 #undef MBEDTLS_DEPRECATED
 #endif /* !MBEDTLS_DEPRECATED_REMOVED */
@@ -294,7 +281,7 @@
  *                 stronger message digests instead.
  *
  */
-int mbedtls_md2_self_test( int verbose );
+int mbedtls_md2_self_test(int verbose);
 
 #endif /* MBEDTLS_SELF_TEST */
 
diff --git a/third_party/mbedtls/repo/include/mbedtls/md4.h b/third_party/mbedtls/repo/include/mbedtls/md4.h
index 0238c67..b827ffe 100644
--- a/third_party/mbedtls/repo/include/mbedtls/md4.h
+++ b/third_party/mbedtls/repo/include/mbedtls/md4.h
@@ -9,19 +9,7 @@
  */
 /*
  *  Copyright The Mbed TLS Contributors
- *  SPDX-License-Identifier: Apache-2.0
- *
- *  Licensed under the Apache License, Version 2.0 (the "License"); you may
- *  not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
+ *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  *
  */
 #ifndef MBEDTLS_MD4_H
@@ -56,8 +44,7 @@
  *                 stronger message digests instead.
  *
  */
-typedef struct mbedtls_md4_context
-{
+typedef struct mbedtls_md4_context {
     uint32_t total[2];          /*!< number of bytes processed  */
     uint32_t state[4];          /*!< intermediate digest state  */
     unsigned char buffer[64];   /*!< data block being processed */
@@ -78,7 +65,7 @@
  *                 stronger message digests instead.
  *
  */
-void mbedtls_md4_init( mbedtls_md4_context *ctx );
+void mbedtls_md4_init(mbedtls_md4_context *ctx);
 
 /**
  * \brief          Clear MD4 context
@@ -90,7 +77,7 @@
  *                 stronger message digests instead.
  *
  */
-void mbedtls_md4_free( mbedtls_md4_context *ctx );
+void mbedtls_md4_free(mbedtls_md4_context *ctx);
 
 /**
  * \brief          Clone (the state of) an MD4 context
@@ -103,8 +90,8 @@
  *                 stronger message digests instead.
  *
  */
-void mbedtls_md4_clone( mbedtls_md4_context *dst,
-                        const mbedtls_md4_context *src );
+void mbedtls_md4_clone(mbedtls_md4_context *dst,
+                       const mbedtls_md4_context *src);
 
 /**
  * \brief          MD4 context setup
@@ -117,7 +104,7 @@
  *                 constitutes a security risk. We recommend considering
  *                 stronger message digests instead.
  */
-int mbedtls_md4_starts_ret( mbedtls_md4_context *ctx );
+int mbedtls_md4_starts_ret(mbedtls_md4_context *ctx);
 
 /**
  * \brief          MD4 process buffer
@@ -133,9 +120,9 @@
  *                 stronger message digests instead.
  *
  */
-int mbedtls_md4_update_ret( mbedtls_md4_context *ctx,
-                            const unsigned char *input,
-                            size_t ilen );
+int mbedtls_md4_update_ret(mbedtls_md4_context *ctx,
+                           const unsigned char *input,
+                           size_t ilen);
 
 /**
  * \brief          MD4 final digest
@@ -150,8 +137,8 @@
  *                 stronger message digests instead.
  *
  */
-int mbedtls_md4_finish_ret( mbedtls_md4_context *ctx,
-                            unsigned char output[16] );
+int mbedtls_md4_finish_ret(mbedtls_md4_context *ctx,
+                           unsigned char output[16]);
 
 /**
  * \brief          MD4 process data block (internal use only)
@@ -166,8 +153,8 @@
  *                 stronger message digests instead.
  *
  */
-int mbedtls_internal_md4_process( mbedtls_md4_context *ctx,
-                                  const unsigned char data[64] );
+int mbedtls_internal_md4_process(mbedtls_md4_context *ctx,
+                                 const unsigned char data[64]);
 
 #if !defined(MBEDTLS_DEPRECATED_REMOVED)
 #if defined(MBEDTLS_DEPRECATED_WARNING)
@@ -187,7 +174,7 @@
  *                 stronger message digests instead.
  *
  */
-MBEDTLS_DEPRECATED void mbedtls_md4_starts( mbedtls_md4_context *ctx );
+MBEDTLS_DEPRECATED void mbedtls_md4_starts(mbedtls_md4_context *ctx);
 
 /**
  * \brief          MD4 process buffer
@@ -203,9 +190,9 @@
  *                 stronger message digests instead.
  *
  */
-MBEDTLS_DEPRECATED void mbedtls_md4_update( mbedtls_md4_context *ctx,
-                                            const unsigned char *input,
-                                            size_t ilen );
+MBEDTLS_DEPRECATED void mbedtls_md4_update(mbedtls_md4_context *ctx,
+                                           const unsigned char *input,
+                                           size_t ilen);
 
 /**
  * \brief          MD4 final digest
@@ -220,8 +207,8 @@
  *                 stronger message digests instead.
  *
  */
-MBEDTLS_DEPRECATED void mbedtls_md4_finish( mbedtls_md4_context *ctx,
-                                            unsigned char output[16] );
+MBEDTLS_DEPRECATED void mbedtls_md4_finish(mbedtls_md4_context *ctx,
+                                           unsigned char output[16]);
 
 /**
  * \brief          MD4 process data block (internal use only)
@@ -236,8 +223,8 @@
  *                 stronger message digests instead.
  *
  */
-MBEDTLS_DEPRECATED void mbedtls_md4_process( mbedtls_md4_context *ctx,
-                                             const unsigned char data[64] );
+MBEDTLS_DEPRECATED void mbedtls_md4_process(mbedtls_md4_context *ctx,
+                                            const unsigned char data[64]);
 
 #undef MBEDTLS_DEPRECATED
 #endif /* !MBEDTLS_DEPRECATED_REMOVED */
@@ -256,9 +243,9 @@
  *                 stronger message digests instead.
  *
  */
-int mbedtls_md4_ret( const unsigned char *input,
-                     size_t ilen,
-                     unsigned char output[16] );
+int mbedtls_md4_ret(const unsigned char *input,
+                    size_t ilen,
+                    unsigned char output[16]);
 
 #if !defined(MBEDTLS_DEPRECATED_REMOVED)
 #if defined(MBEDTLS_DEPRECATED_WARNING)
@@ -280,9 +267,9 @@
  *                 stronger message digests instead.
  *
  */
-MBEDTLS_DEPRECATED void mbedtls_md4( const unsigned char *input,
-                                     size_t ilen,
-                                     unsigned char output[16] );
+MBEDTLS_DEPRECATED void mbedtls_md4(const unsigned char *input,
+                                    size_t ilen,
+                                    unsigned char output[16]);
 
 #undef MBEDTLS_DEPRECATED
 #endif /* !MBEDTLS_DEPRECATED_REMOVED */
@@ -299,7 +286,7 @@
  *                 stronger message digests instead.
  *
  */
-int mbedtls_md4_self_test( int verbose );
+int mbedtls_md4_self_test(int verbose);
 
 #endif /* MBEDTLS_SELF_TEST */