Merge remote-tracking branch 'github/main' into HEAD

<fxrev.dev/678949> needs to land prior to this CL.

Upstream-Commit: 645fbcd400d2e6c2de641bccb0335e3eaaed1f36
Change-Id: I9e2c785bcae75f25d5ad57d30330bcf3132d0344
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
index 160bc34..479ca7f 100644
--- a/.github/ISSUE_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE.md
@@ -1,3 +1,3 @@
 GitHub Issues are for bugs and feature requests. To make bugs and feature requests more easy to find and organize, we close issues that are deemed out of scope for GitHub Issues.
 
-The openthread-users Google Group is the recommended place for users to discuss OpenThread and interact directly with the OpenThread community. https://groups.google.com/forum/#!forum/openthread-users
+OpenThread GitHub Discussions is the recommended place for users to discuss OpenThread and interact directly with the OpenThread community. https://github.com/openthread/openthread/discussions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index ac4e986..fe08b5f 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -84,6 +84,7 @@
     name: package-${{ matrix.compiler }}
     runs-on: ubuntu-20.04
     strategy:
+      fail-fast: false
       matrix:
         include:
           - compiler: gcc
@@ -134,7 +135,7 @@
     - uses: actions/checkout@v2
       with:
         repository: ARMmbed/mbedtls
-        ref: v3.0.0
+        ref: v3.1.0
         path: third_party/mbedtls/repo
     - name: Build
       run: |
@@ -144,6 +145,7 @@
     name: arm-gcc-${{ matrix.gcc_ver }}
     runs-on: ubuntu-18.04
     strategy:
+      fail-fast: false
       matrix:
         include:
           - gcc_ver: 4
@@ -184,8 +186,9 @@
     name: gcc-${{ matrix.gcc_ver }}
     runs-on: ubuntu-18.04
     strategy:
+      fail-fast: false
       matrix:
-        gcc_ver: [5, 6, 7, 8, 9, 10]
+        gcc_ver: [5, 6, 7, 8, 9, 10, 11]
     env:
       CC: gcc-${{ matrix.gcc_ver }}
       CXX: g++-${{ matrix.gcc_ver }}
@@ -196,6 +199,11 @@
     - name: Bootstrap
       run: |
         sudo rm /etc/apt/sources.list.d/* && sudo apt-get update
+        case ${{ matrix.gcc_ver }} in
+          11)
+            sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
+            ;;
+        esac
         sudo apt-get --no-install-recommends install -y gcc-${{ matrix.gcc_ver }} g++-${{ matrix.gcc_ver }} ninja-build libreadline-dev libncurses-dev
     - name: Build
       run: |
@@ -206,6 +214,7 @@
     name: clang-${{ matrix.clang_ver }}
     runs-on: ubuntu-20.04
     strategy:
+      fail-fast: false
       matrix:
         clang_ver: ["6.0", "7", "8", "9"]
     env:
@@ -228,6 +237,7 @@
     name: clang-m32-${{ matrix.clang_ver }}
     runs-on: ubuntu-20.04
     strategy:
+      fail-fast: false
       matrix:
         clang_ver: ["6.0", "7", "8", "9"]
     env:
@@ -273,6 +283,7 @@
   macos:
     name: macos-${{ matrix.CC }}
     strategy:
+      fail-fast: false
       matrix:
         include:
           - CC: clang
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 627f0a9..339bb1a 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -44,10 +44,10 @@
     name: buildx-${{ matrix.docker_name }}
     runs-on: ubuntu-20.04
     strategy:
+      fail-fast: false
       matrix:
         include:
           - docker_name: environment
-          - docker_name: codelab_otsim
     steps:
     - uses: actions/checkout@v2
       with:
diff --git a/.github/workflows/otbr.yml b/.github/workflows/otbr.yml
index 173035f..e1c87b8 100644
--- a/.github/workflows/otbr.yml
+++ b/.github/workflows/otbr.yml
@@ -60,53 +60,33 @@
     - uses: actions/checkout@v2
       with:
         submodules: true
-    - name: Get Border Router Test ID
-      id: unique_action_id
-      env:
-        GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
-      run: |
-        ./script/git-tool clone https://github.com/openthread/ot-br-posix.git --depth 1 "/tmp/otbr_unique_action_id"
-        echo ::set-output  name=id::${GITHUB_WORKFLOW}-${GITHUB_JOB}-${GITHUB_RUN_ID}-$(cd /tmp/otbr_unique_action_id/ && git rev-parse HEAD)
-    - name: Check cached result
-      id: check_cache_result
-      uses: actions/cache@v2
-      with:
-        path: |
-          _test_complete_
-        key: "_test_complete_${{ steps.unique_action_id.outputs.id }}"
     - name: Build OTBR Docker
-      if: ${{ success() && steps.check_cache_result.outputs.cache-hit != 'true' }}
       env:
         GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
       run: |
         ./script/test build_otbr_docker
     - name: Bootstrap
-      if: ${{ success() && steps.check_cache_result.outputs.cache-hit != 'true' }}
       run: |
         sudo rm /etc/apt/sources.list.d/* && sudo apt-get update
         sudo apt-get --no-install-recommends install -y python3-setuptools python3-wheel ninja-build socat lcov
         python3 -m pip install -r tests/scripts/thread-cert/requirements.txt
     - name: Build
-      if: ${{ success() && steps.check_cache_result.outputs.cache-hit != 'true' }}
       run: |
         ./script/test build
     - name: Get Thread-Wireshark
-      if: ${{ success() && steps.check_cache_result.outputs.cache-hit != 'true' }}
       run: |
         ./script/test get_thread_wireshark
     - name: Run
-      if: ${{ success() && steps.check_cache_result.outputs.cache-hit != 'true' }}
       run: |
         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@v2
-      if: ${{ success() && steps.check_cache_result.outputs.cache-hit != 'true' }}
       with:
         name: cov-thread-1-2-backbone-docker
         path: /tmp/coverage/
     - uses: actions/upload-artifact@v2
-      if: ${{ failure() && steps.check_cache_result.outputs.cache-hit != 'true' }}
+      if: ${{ failure() }}
       with:
         name: thread-1-2-backbone-results
         path: |
@@ -116,18 +96,12 @@
           coredump_*
           otbr-agent_*
     - name: Generate Coverage
-      if: ${{ success() && steps.check_cache_result.outputs.cache-hit != 'true' }}
       run: |
         ./script/test generate_coverage gcc
     - uses: actions/upload-artifact@v2
-      if: ${{ success() && steps.check_cache_result.outputs.cache-hit != 'true' }}
       with:
         name: cov-thread-1-2-backbone
         path: tmp/coverage.info
-    - name: Cache test result
-      if: ${{ success() && steps.check_cache_result.outputs.cache-hit != 'true' }}
-      run: |
-        mkdir _test_complete_
 
   thread-border-router:
     runs-on: ubuntu-20.04
@@ -176,54 +150,34 @@
       MAX_JOBS: 3
     steps:
     - uses: actions/checkout@v2
-    - name: Get Border Router Test ID
-      id: unique_action_id
-      env:
-        GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
-      run: |
-        ./script/git-tool clone https://github.com/openthread/ot-br-posix.git --depth 1 "/tmp/otbr_unique_action_id"
-        echo ::set-output  name=id::${GITHUB_WORKFLOW}-${GITHUB_JOB}-${GITHUB_RUN_ID}-$(cd /tmp/otbr_unique_action_id/ && git rev-parse HEAD)-${{matrix.otbr_mdns}}-trel${{matrix.otbr_trel}}
-    - name: Check cached result
-      id: check_cache_result
-      uses: actions/cache@v2
-      with:
-        path: |
-          _test_complete_
-        key: "_test_complete_${{ steps.unique_action_id.outputs.id }}"
     - name: Build OTBR Docker
-      if: ${{ success() && steps.check_cache_result.outputs.cache-hit != 'true' }}
       env:
         GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
         TREL: ${{ matrix.otbr_trel }}
       run: |
         ./script/test build_otbr_docker
     - name: Bootstrap
-      if: ${{ success() && steps.check_cache_result.outputs.cache-hit != 'true' }}
       run: |
         sudo rm /etc/apt/sources.list.d/* && sudo apt-get update
         sudo apt-get --no-install-recommends install -y python3-setuptools python3-wheel ninja-build socat lcov
         python3 -m pip install -r tests/scripts/thread-cert/requirements.txt
     - name: Build
-      if: ${{ success() && steps.check_cache_result.outputs.cache-hit != 'true' }}
       run: |
         ./script/test build
     - name: Get Thread-Wireshark
-      if: ${{ success() && steps.check_cache_result.outputs.cache-hit != 'true' }}
       run: |
         ./script/test get_thread_wireshark
     - name: Run
-      if: ${{ success() && steps.check_cache_result.outputs.cache-hit != 'true' }}
       run: |
         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@v2
-      if: ${{ success() && steps.check_cache_result.outputs.cache-hit != 'true' }}
       with:
         name: cov-thread-border-router-docker
         path: /tmp/coverage/
     - uses: actions/upload-artifact@v2
-      if: ${{ failure() && steps.check_cache_result.outputs.cache-hit != 'true' }}
+      if: ${{ failure() }}
       with:
         name: thread-border-router-results
         path: |
@@ -233,18 +187,12 @@
           coredump_*
           otbr-agent_*
     - name: Generate Coverage
-      if: ${{ success() && steps.check_cache_result.outputs.cache-hit != 'true' }}
       run: |
         ./script/test generate_coverage gcc
     - uses: actions/upload-artifact@v2
-      if: ${{ success() && steps.check_cache_result.outputs.cache-hit != 'true' }}
       with:
         name: cov-thread-border-router
         path: tmp/coverage.info
-    - name: Cache test result
-      if: ${{ success() && steps.check_cache_result.outputs.cache-hit != 'true' }}
-      run: |
-        mkdir _test_complete_
 
   upload-coverage:
     needs:
@@ -274,7 +222,6 @@
 
   delete-coverage-artifacts:
     needs: upload-coverage
-    if: always()
     runs-on: ubuntu-20.04
     steps:
     - uses: geekyeggo/delete-artifact@1-glob-support
diff --git a/.github/workflows/otci.yml b/.github/workflows/otci.yml
index 0d7acf6..5023d7c 100644
--- a/.github/workflows/otci.yml
+++ b/.github/workflows/otci.yml
@@ -44,6 +44,7 @@
     name: cli-sim VIRTUAL_TIME=${{ matrix.virtual_time }}
     runs-on: ubuntu-20.04
     strategy:
+      fail-fast: false
       matrix:
         virtual_time: [0, 1]
     env:
diff --git a/.github/workflows/otns.yml b/.github/workflows/otns.yml
index 2d37956..46dc33c 100644
--- a/.github/workflows/otns.yml
+++ b/.github/workflows/otns.yml
@@ -132,6 +132,7 @@
     name: Stress ${{ matrix.suite }}
     runs-on: ubuntu-20.04
     strategy:
+      fail-fast: false
       matrix:
         include:
           - suite: "network-forming"
diff --git a/.github/workflows/posix.yml b/.github/workflows/posix.yml
index fc57a19..abe6f0b 100644
--- a/.github/workflows/posix.yml
+++ b/.github/workflows/posix.yml
@@ -183,6 +183,7 @@
     name: pty-linux OT_DAEMON=${{ matrix.OT_DAEMON }}
     runs-on: ubuntu-20.04
     strategy:
+      fail-fast: false
       matrix:
         OT_DAEMON: ['off', 'on']
     env:
@@ -227,6 +228,7 @@
     name: pty-macos OT_DAEMON=${{ matrix.OT_DAEMON }}
     runs-on: macos-10.15
     strategy:
+      fail-fast: false
       matrix:
         OT_DAEMON: ['off', 'on']
     env:
@@ -298,7 +300,6 @@
 
   delete-coverage-artifacts:
     needs: upload-coverage
-    if: always()
     runs-on: ubuntu-20.04
     steps:
     - uses: geekyeggo/delete-artifact@1-glob-support
diff --git a/.github/workflows/simulation-1.1.yml b/.github/workflows/simulation-1.1.yml
index 489759a..a16bf12 100644
--- a/.github/workflows/simulation-1.1.yml
+++ b/.github/workflows/simulation-1.1.yml
@@ -150,6 +150,7 @@
     name: cli-mtd MESSAGE_USE_HEAP=${{ matrix.message_use_heap }}
     runs-on: ubuntu-20.04
     strategy:
+      fail-fast: false
       matrix:
         message_use_heap: [0, 1]
     env:
@@ -347,97 +348,6 @@
         name: cov-multiple-instance
         path: tmp/coverage.info
 
-  ncp-gcc-m32:
-    runs-on: ubuntu-20.04
-    env:
-      CFLAGS: -m32
-      CXXFLAGS: -m32
-      LDFLAGS: -m32
-      COVERAGE: 1
-      GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
-      NODE_TYPE: ncp-sim
-      PYTHONUNBUFFERED: 1
-      REFERENCE_DEVICE: 1
-      THREAD_VERSION: 1.1
-      VIRTUAL_TIME: 1
-    steps:
-    - uses: actions/checkout@v2
-      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 python3-setuptools python3-wheel lcov
-        python3 -m pip install -r tests/scripts/thread-cert/requirements.txt
-        pyspineldir=$(mktemp -d -t pyspinel_XXXXXX)
-        ./script/git-tool clone https://github.com/openthread/pyspinel.git --depth 1 "${pyspineldir}"
-        cd "${pyspineldir}"
-        pip3 install .
-    - name: Build
-      run: |
-        ./bootstrap
-        make -f examples/Makefile-simulation
-    - name: Run
-      run: |
-        VERBOSE=1 make -f examples/Makefile-simulation check
-    - uses: actions/upload-artifact@v2
-      if: ${{ failure() }}
-      with:
-        name: ncp-gcc-m32-thread-cert
-        path: build/simulation/tests/scripts/thread-cert
-    - name: Generate Coverage
-      run: |
-        ./script/test generate_coverage gcc
-    - uses: actions/upload-artifact@v2
-      with:
-        name: cov-ncp-gcc-m32
-        path: tmp/coverage.info
-
-  ncp-clang:
-    runs-on: ubuntu-20.04
-    env:
-      COVERAGE: 1
-      GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
-      NODE_TYPE: ncp-sim
-      PYTHONUNBUFFERED: 1
-      REFERENCE_DEVICE: 1
-      THREAD_VERSION: 1.1
-      VIRTUAL_TIME: 1
-      CC: clang-10
-      CXX: clang++-10
-    steps:
-    - uses: actions/checkout@v2
-      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 clang-10 clang++-10 python3-setuptools python3-wheel llvm lcov
-        python3 -m pip install -r tests/scripts/thread-cert/requirements.txt
-        pyspineldir=$(mktemp -d -t pyspinel_XXXXXX)
-        ./script/git-tool clone https://github.com/openthread/pyspinel.git --depth 1 "${pyspineldir}"
-        cd "${pyspineldir}"
-        pip3 install .
-    - name: Build
-      run: |
-        ./bootstrap
-        make -f examples/Makefile-simulation
-    - name: Run
-      run: |
-        VERBOSE=1 make -f examples/Makefile-simulation check
-    - uses: actions/upload-artifact@v2
-      if: ${{ failure() }}
-      with:
-        name: cli-clang-thread-cert
-        path: build/simulation/tests/scripts/thread-cert
-    - name: Generate Coverage
-      run: |
-        ./script/test generate_coverage llvm
-    - uses: actions/upload-artifact@v2
-      with:
-        name: cov-ncp-clang
-        path: tmp/coverage.info
-
   upload-coverage:
     needs:
     - packet-verification
@@ -447,8 +357,6 @@
     - expects
     - ot-commissioner
     - multiple-instance
-    - ncp-gcc-m32
-    - ncp-clang
     runs-on: ubuntu-20.04
     steps:
     - uses: actions/checkout@v2
@@ -471,7 +379,6 @@
 
   delete-coverage-artifacts:
     needs: upload-coverage
-    if: always()
     runs-on: ubuntu-20.04
     steps:
     - uses: geekyeggo/delete-artifact@1-glob-support
diff --git a/.github/workflows/simulation-1.2.yml b/.github/workflows/simulation-1.2.yml
index df19047..f8bc267 100644
--- a/.github/workflows/simulation-1.2.yml
+++ b/.github/workflows/simulation-1.2.yml
@@ -54,6 +54,7 @@
       CC: ${{ matrix.compiler.c }}
       CXX: ${{ matrix.compiler.cxx }}
     strategy:
+      fail-fast: false
       matrix:
         compiler: [{c: "gcc", cxx: "g++", gcov: "gcc"}, { c: "clang-10", cxx: "clang++-10", gcov: "llvm"}]
         arch: ["m32", "m64"]
@@ -329,7 +330,6 @@
 
   delete-coverage-artifacts:
     needs: upload-coverage
-    if: always()
     runs-on: ubuntu-20.04
     steps:
     - uses: geekyeggo/delete-artifact@1-glob-support
diff --git a/.github/workflows/toranj.yml b/.github/workflows/toranj.yml
index ff88daa..46f9520 100644
--- a/.github/workflows/toranj.yml
+++ b/.github/workflows/toranj.yml
@@ -40,10 +40,11 @@
         GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
       if: "github.ref != 'refs/heads/main'"
 
-  toranj:
-    name: toranj-${{ matrix.TORANJ_RADIO }}
+  toranj-ncp:
+    name: toranj-ncp-${{ matrix.TORANJ_RADIO }}
     runs-on: ubuntu-18.04
     strategy:
+      fail-fast: false
       matrix:
         TORANJ_RADIO: ['15.4', 'trel', 'multi']
     env:
@@ -82,12 +83,47 @@
     - uses: actions/upload-artifact@v2
       if: "matrix.TORANJ_RADIO != 'multi'"
       with:
-        name: cov-toranj-${{ matrix.TORANJ_RADIO }}
+        name: cov-toranj-ncp-${{ matrix.TORANJ_RADIO }}
+        path: tmp/coverage.info
+
+  toranj-cli:
+    name: toranj-cli-${{ matrix.TORANJ_RADIO }}
+    runs-on: ubuntu-18.04
+    strategy:
+      matrix:
+        TORANJ_RADIO: ['15.4']
+    env:
+      COVERAGE: 1
+      TORANJ_RADIO : ${{ matrix.TORANJ_RADIO }}
+      TORANJ_CLI: 1
+    steps:
+    - uses: actions/checkout@v2
+      with:
+        submodules: true
+    - name: Bootstrap
+      env:
+        GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
+      run: |
+        sudo rm /etc/apt/sources.list.d/* && sudo apt-get update
+        sudo apt-get --no-install-recommends install -y lcov
+        python3 -m pip install -r tests/scripts/thread-cert/requirements.txt
+    - name: Build & Run
+      run: |
+        top_builddir=$(pwd)/build/toranj ./tests/toranj/start.sh
+    - name: Generate Coverage
+      if: "matrix.TORANJ_RADIO != 'multi'"
+      run: |
+        ./script/test generate_coverage gcc
+    - uses: actions/upload-artifact@v2
+      if: "matrix.TORANJ_RADIO != 'multi'"
+      with:
+        name: cov-toranj-cli-${{ matrix.TORANJ_RADIO }}
         path: tmp/coverage.info
 
   upload-coverage:
     needs:
-    - toranj
+    - toranj-ncp
+    - toranj-cli
     runs-on: ubuntu-18.04
     steps:
     - uses: actions/checkout@v2
@@ -110,7 +146,6 @@
 
   delete-coverage-artifacts:
     needs: upload-coverage
-    if: always()
     runs-on: ubuntu-20.04
     steps:
     - uses: geekyeggo/delete-artifact@1-glob-support
diff --git a/Android.mk b/Android.mk
index dba1524..4d09fcc 100644
--- a/Android.mk
+++ b/Android.mk
@@ -35,17 +35,24 @@
 
 OPENTHREAD_PROJECT_CFLAGS                                                 ?= \
     -DOPENTHREAD_PROJECT_CORE_CONFIG_FILE=\"openthread-core-posix-config.h\" \
-    -DOPENTHREAD_CONFIG_FILE=\<openthread-config-android.h\>                 \
     $(NULL)
 
 OPENTHREAD_PUBLIC_CFLAGS                                         := \
-    -DOPENTHREAD_CONFIG_PING_SENDER_ENABLE=1                        \
-    -DOPENTHREAD_CONFIG_COMMISSIONER_ENABLE=1                       \
+    -DOPENTHREAD_CONFIG_BORDER_AGENT_ENABLE=1                       \
+    -DOPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE=1                      \
+    -DOPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE=1                  \
+    -DOPENTHREAD_CONFIG_DTLS_ENABLE=1                               \
     -DOPENTHREAD_CONFIG_IP6_SLAAC_ENABLE=1                          \
+    -DOPENTHREAD_CONFIG_JAM_DETECTION_ENABLE=1                      \
+    -DOPENTHREAD_CONFIG_JOINER_ENABLE=1                             \
     -DOPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE=1                  \
     -DOPENTHREAD_CONFIG_MAC_FILTER_ENABLE=1                         \
-    -DOPENTHREAD_POSIX_CONFIG_RCP_PTY_ENABLE=1                      \
+    -DOPENTHREAD_CONFIG_NCP_HDLC_ENABLE=1                           \
+    -DOPENTHREAD_CONFIG_PING_SENDER_ENABLE=1                        \
+    -DOPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE=1                \
     -DOPENTHREAD_FTD=1                                              \
+    -DOPENTHREAD_PLATFORM_POSIX=1                                   \
+    -DOPENTHREAD_POSIX_CONFIG_RCP_PTY_ENABLE=1                      \
     -DOPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE=1          \
     $(NULL)
 
@@ -228,10 +235,10 @@
     src/core/common/heap_data.cpp                                   \
     src/core/common/heap_string.cpp                                 \
     src/core/common/instance.cpp                                    \
-    src/core/common/logging.cpp                                     \
+    src/core/common/log.cpp                                         \
     src/core/common/message.cpp                                     \
     src/core/common/notifier.cpp                                    \
-    src/core/common/random_manager.cpp                              \
+    src/core/common/random.cpp                                      \
     src/core/common/settings.cpp                                    \
     src/core/common/string.cpp                                      \
     src/core/common/tasklet.cpp                                     \
@@ -244,6 +251,7 @@
     src/core/crypto/aes_ecb.cpp                                     \
     src/core/crypto/crypto_platform.cpp                             \
     src/core/crypto/ecdsa.cpp                                       \
+    src/core/crypto/ecdsa_tinycrypt.cpp                             \
     src/core/crypto/hkdf_sha256.cpp                                 \
     src/core/crypto/hmac_sha256.cpp                                 \
     src/core/crypto/mbedtls.cpp                                     \
@@ -272,11 +280,13 @@
     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                                     \
     src/core/meshcop/joiner_router.cpp                              \
     src/core/meshcop/meshcop.cpp                                    \
     src/core/meshcop/meshcop_leader.cpp                             \
     src/core/meshcop/meshcop_tlvs.cpp                               \
+    src/core/meshcop/network_name.cpp                               \
     src/core/meshcop/panid_query_client.cpp                         \
     src/core/meshcop/timestamp.cpp                                  \
     src/core/net/checksum.cpp                                       \
@@ -404,6 +414,7 @@
     third_party/mbedtls/repo/library/cipher.c                       \
     third_party/mbedtls/repo/library/cipher_wrap.c                  \
     third_party/mbedtls/repo/library/cmac.c                         \
+    third_party/mbedtls/repo/library/constant_time.c                \
     third_party/mbedtls/repo/library/ctr_drbg.c                     \
     third_party/mbedtls/repo/library/debug.c                        \
     third_party/mbedtls/repo/library/des.c                          \
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 540550e..f311fc9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -58,7 +58,6 @@
     target_compile_definitions(ot-config INTERFACE "BYTE_ORDER_BIG_ENDIAN=1")
 endif()
 
-include("${PROJECT_SOURCE_DIR}/etc/cmake/checks.cmake")
 include("${PROJECT_SOURCE_DIR}/etc/cmake/options.cmake")
 include("${PROJECT_SOURCE_DIR}/etc/cmake/functions.cmake")
 
diff --git a/NOTICE b/NOTICE
index 962490e..94a2d71 100644
--- a/NOTICE
+++ b/NOTICE
@@ -1,5 +1,5 @@
-OpenThread is an open source implementation of the Thread 1.1.1 Final Specification.
-The Thread 1.1.1 Final Specification is promulgated by the Thread Group. The Thread
+OpenThread is an open source implementation of the Thread 1.2.0 Final Specification.
+The Thread 1.2.0 Final Specification is promulgated by the Thread Group. The Thread
 Group is a non-profit organization formed for the purposes of defining one or
 more specifications, best practices, reference architectures, implementation
 guidelines and certification programs to promote the availability of compliant
@@ -7,10 +7,10 @@
 information about the benefits thereof, can be found at http://threadgroup.org.
 
 OpenThread is not affiliated with or endorsed by the Thread Group. Implementation
-of this OpenThread code does not assure compliance with the Thread 1.1.1 Final
+of this OpenThread code does not assure compliance with the Thread 1.2.0 Final
 Specification and does not convey the right to identify any final product as Thread
 certified. Members of the Thread Group may hold patents and other intellectual
-property rights relating to the Thread 1.1.1 Final Specification, ownership and
+property rights relating to the Thread 1.2.0 Final Specification, ownership and
 licenses of which are subject to the Thread Group’s IP Policies, and not this license.
 
 The included copyright to the OpenThread code is subject to the license in the
diff --git a/README.md b/README.md
index 5370128..6574588 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@
 
 **...OS and platform agnostic**, with a narrow platform abstraction layer and a small memory footprint, making it highly portable. It supports both system-on-chip (SoC) and network co-processor (NCP) designs.
 
-**...a Thread Certified Component**, implementing all features defined in the [Thread 1.1.1 specification](https://www.threadgroup.org/support#specifications), including all Thread networking layers (IPv6, 6LoWPAN, IEEE 802.15.4 with MAC security, Mesh Link Establishment, Mesh Routing) and device roles, as well as [Border Router](https://github.com/openthread/ot-br-posix) support.
+**...a Thread Certified Component**, implementing all features defined in the [Thread 1.2 specification](https://www.threadgroup.org/support#specifications), including all Thread networking layers (IPv6, 6LoWPAN, IEEE 802.15.4 with MAC security, Mesh Link Establishment, Mesh Routing) and device roles, as well as [Border Router](https://github.com/openthread/ot-br-posix) support.
 
 More information about Thread can be found at [threadgroup.org](http://threadgroup.org/). Thread is a registered trademark of the Thread Group, Inc.
 
@@ -29,7 +29,7 @@
 
 # Who supports OpenThread?
 
-<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.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="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.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.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="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
 
@@ -66,10 +66,7 @@
 
 # Need help?
 
-There are numerous avenues for OpenThread support:
+OpenThread support is available on GitHub:
 
 - Bugs and feature requests — [submit to the Issue Tracker](https://github.com/openthread/openthread/issues)
-- Stack Overflow — [post questions using the `openthread` tag](http://stackoverflow.com/questions/tagged/openthread)
-- Google Groups — [discussion and announcements at openthread-users](https://groups.google.com/forum/#!forum/openthread-users)
-
-The openthread-users Google Group is the recommended place for users to discuss OpenThread and interact directly with the OpenThread team.
+- Community Discussion - [ask questions, share ideas, and engage with other community members](https://github.com/openthread/openthread/discussions)
diff --git a/configure.ac b/configure.ac
index 04b18b2..8c0c7fc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -990,6 +990,7 @@
 LIBS="${LIBS} ${NL_COVERAGE_LIBS}"
 LDFLAGS="${LDFLAGS} ${NL_COVERAGE_LDFLAGS}"
 
+CPPFLAGS="${CPPFLAGS} -DOPENTHREAD_CONFIG_FILE='\"openthread-config-generic.h\"'"
 # At this point, we can restore the compiler flags to whatever the
 # user passed in, now that we're clear of an -Werror issues by
 # transforming -Wno-error back to -Werror.
diff --git a/doc/images/ot-contrib-infineon.png b/doc/images/ot-contrib-infineon.png
new file mode 100644
index 0000000..293a52c
--- /dev/null
+++ b/doc/images/ot-contrib-infineon.png
Binary files differ
diff --git a/etc/cmake/options.cmake b/etc/cmake/options.cmake
index e8c9212..6eec0d6 100644
--- a/etc/cmake/options.cmake
+++ b/etc/cmake/options.cmake
@@ -381,7 +381,6 @@
         target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_LOG_LEVEL=OT_LOG_LEVEL_DEBG")
     endif()
     target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_LOG_PREPEND_LEVEL=1")
-    target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_LOG_PREPEND_REGION=1")
 endif()
 
 option(OT_OTNS "enable OTNS support")
diff --git a/etc/docker/codelab_otsim/Dockerfile b/etc/docker/codelab_otsim/Dockerfile
deleted file mode 100644
index d7c400f..0000000
--- a/etc/docker/codelab_otsim/Dockerfile
+++ /dev/null
@@ -1,30 +0,0 @@
-# Ubuntu image with tools required to build OpenThread
-FROM ubuntu:18.04
-
-ENV DEBIAN_FRONTEND noninteractive
-ARG OT_GIT_REF=main
-
-# Install dependencies:
-RUN apt-get update -qq
-
-# Install packages needed for build and runtime:
-RUN apt-get --no-install-recommends install -y \
-    git \
-    sudo \
-    inetutils-ping \
-    software-properties-common \
-    ca-certificates \
-    && update-ca-certificates
-
-# Install OpenThread
-RUN mkdir -p ~/src/openthread && \
-    cd ~/src/openthread && \
-    git init . && \
-    git fetch https://github.com/openthread/openthread.git ${OT_GIT_REF} && \
-    git checkout FETCH_HEAD && \
-    ./script/bootstrap && \
-    ./bootstrap && \
-    make -f examples/Makefile-simulation
-
-# Install OpenThread Daemon and ot-ctl
-RUN cd ~/src/openthread && make -f src/posix/Makefile-posix DAEMON=1
diff --git a/etc/gn/openthread.gni b/etc/gn/openthread.gni
index 981d758..68e6ecb 100644
--- a/etc/gn/openthread.gni
+++ b/etc/gn/openthread.gni
@@ -234,6 +234,9 @@
     # Enable builtin mbedtls management
     openthread_config_enable_builtin_mbedtls_management =
         openthread_external_mbedtls == ""
+
+    # Enable radio coexistence
+    openthread_config_coexistence_enable = false
   }
 }
 
diff --git a/include/Makefile.am b/include/Makefile.am
index 6729d68..e91c87c 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -84,6 +84,7 @@
     openthread/srp_server.h               \
     openthread/tasklet.h                  \
     openthread/tcp.h                      \
+    openthread/tcp_ext.h                  \
     openthread/thread.h                   \
     openthread/thread_ftd.h               \
     openthread/trel.h                     \
diff --git a/include/openthread-config-android.h b/include/openthread-config-android.h
deleted file mode 100644
index 9b07c81..0000000
--- a/include/openthread-config-android.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- *  Copyright (c) 2018, 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.
- */
-
-#ifdef OPENTHREAD_CONFIG_ANDROID_VERSION_HEADER_ENABLE
-#include <openthread-config-android-version.h>
-#endif
-
-/* Define to 1 to enable the border agent feature. */
-#define OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE 1
-
-/* Define to 1 if you want to enable Border Router */
-#define OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE 1
-
-/* Define to 1 if you want to enable channel manager feature */
-#define OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE 0
-
-/* Define to 1 if you want to use channel monitor feature */
-#define OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE 0
-
-/* Define to 1 if you want to use child supervision feature */
-#define OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE 1
-
-/* Define to 1 to enable dtls support. */
-#define OPENTHREAD_CONFIG_DTLS_ENABLE 1
-
-/* Define to 1 if you want to use jam detection feature */
-#define OPENTHREAD_CONFIG_JAM_DETECTION_ENABLE 1
-
-/* Define to 1 to enable the joiner role. */
-#define OPENTHREAD_CONFIG_JOINER_ENABLE 1
-
-/* Define to 1 if you want to use legacy network support */
-#define OPENTHREAD_CONFIG_LEGACY_ENABLE 1
-
-/* Define to 1 to enable the NCP HDLC interface. */
-#define OPENTHREAD_CONFIG_NCP_HDLC_ENABLE 1
-
-/* Define to 1 to enable posix platform. */
-#define OPENTHREAD_PLATFORM_POSIX 1
-
-/* Define to 1 if you want to enable Service */
-#define OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE 1
-
-/* OpenThread examples */
-#define OPENTHREAD_EXAMPLES none
diff --git a/include/openthread-config-fuchsia-legacy-extensions.h b/include/openthread-config-fuchsia-legacy-extensions.h
index 444e188..75ca8c4 100644
--- a/include/openthread-config-fuchsia-legacy-extensions.h
+++ b/include/openthread-config-fuchsia-legacy-extensions.h
@@ -45,5 +45,23 @@
  */
 #define OPENTHREAD_ENABLE_VENDOR_EXTENSION 1
 
+/*
+ * Implementation note:
+ *   These are all "weak" so that a platform may if it chooses to override the instance.
+ */
+
+#if OPENTHREAD_CONFIG_OTNS_ENABLE
+
+#include <openthread/platform/toolchain.h>
+#include "common/log.hpp"
+
+using namespace ot;
+
+OT_TOOL_WEAK
+void otPlatOtnsStatus(const char *aStatus)
+{
+    LogAlways("[OTNS] %s", aStatus);
+}
+#endif // OPENTHREAD_CONFIG_OTNS_ENABLE
 
 #endif  // OPENTHREAD_INCLUDE_OPENTHREAD_CONFIG_FUCHSIA_LEGACY_EXTENSIONS_H_
diff --git a/include/openthread-config-fuchsia.h b/include/openthread-config-fuchsia.h
index bbfe43e..993ab0d 100644
--- a/include/openthread-config-fuchsia.h
+++ b/include/openthread-config-fuchsia.h
@@ -153,7 +153,7 @@
  *
  */
 #ifndef OPENTHREAD_CONFIG_IP6_MAX_EXT_MCAST_ADDRS
-#define OPENTHREAD_CONFIG_IP6_MAX_EXT_MCAST_ADDRS 8
+#define OPENTHREAD_CONFIG_IP6_MAX_EXT_MCAST_ADDRS 32
 #endif
 
 /**
diff --git a/include/openthread/BUILD.gn b/include/openthread/BUILD.gn
index d558fcb..df7035a 100644
--- a/include/openthread/BUILD.gn
+++ b/include/openthread/BUILD.gn
@@ -27,27 +27,10 @@
 
 import("../../etc/gn/openthread.gni")
 
-copy("copy_openthread_config_generic") {
-  sources = [ "../../etc/cmake/openthread-config-generic.h.in" ]
-  outputs = [ "${root_gen_dir}/include/{{source_name_part}}" ]
-}
-
-config("openthread_config_generic_config") {
-  include_dirs = [ "${root_gen_dir}/include" ]
-}
-
-source_set("openthread_config_generic") {
-  sources = [ "${root_gen_dir}/include/openthread-config-generic.h" ]
-  deps = [ ":copy_openthread_config_generic" ]
-  public_configs = [ ":openthread_config_generic_config" ]
-}
-
 source_set("openthread_config") {
   public = [ "config.h" ]
 
-  if (openthread_config_file == "") {
-    public_deps = [ ":openthread_config_generic" ]
-  } else {
+  if (openthread_config_file != "") {
     public_deps = openthread_config_deps
   }
 
@@ -140,6 +123,7 @@
     "srp_server.h",
     "tasklet.h",
     "tcp.h",
+    "tcp_ext.h",
     "thread.h",
     "thread_ftd.h",
     "trel.h",
diff --git a/include/openthread/backbone_router_ftd.h b/include/openthread/backbone_router_ftd.h
index 746d599..a619687 100644
--- a/include/openthread/backbone_router_ftd.h
+++ b/include/openthread/backbone_router_ftd.h
@@ -273,19 +273,6 @@
  */
 otError otBackboneRouterMulticastListenerAdd(otInstance *aInstance, const otIp6Address *aAddress, uint32_t aTimeout);
 
-/**
- * This method configures the ability to increase or not the BBR Dataset Sequence Number when a
- * BBR recovers its BBR Dataset from the Leader's Network Data.
- *
- * Note: available only when `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is enabled.
- *       Only used for certification.
- *
- * @param[in] aInstance  A pointer to an OpenThread instance.
- * @param[in] aSkip      Whether to skip the increase of Sequence Number or not.
- *
- */
-void otBackboneRouterConfigSkipSeqNumIncrease(otInstance *aInstance, bool aSkip);
-
 #define OT_BACKBONE_ROUTER_MULTICAST_LISTENER_ITERATOR_INIT \
     0 ///< Initializer for otBackboneRouterMulticastListenerIterator
 
@@ -304,12 +291,12 @@
 /**
  * This function gets the next Multicast Listener info (using an iterator).
  *
- * @param[in]     aInstance    A pointer to an OpenThread instance.
- * @param[inout]  aIterator    A pointer to the iterator. On success the iterator will be updated to point to next
- *                             Multicast Listener. To get the first entry the iterator should be set to
- *                             OT_BACKBONE_ROUTER_MULTICAST_LISTENER_ITERATOR_INIT.
- * @param[out]    aListenerInfo  A pointer to an `otBackboneRouterMulticastListenerInfo` where information of next
- *                               Multicast Listener is placed (on success).
+ * @param[in]      aInstance      A pointer to an OpenThread instance.
+ * @param[in,out]  aIterator      A pointer to the iterator. On success the iterator will be updated to point to next
+ *                                Multicast Listener. To get the first entry the iterator should be set to
+ *                                OT_BACKBONE_ROUTER_MULTICAST_LISTENER_ITERATOR_INIT.
+ * @param[out]     aListenerInfo  A pointer to an `otBackboneRouterMulticastListenerInfo` where information of next
+ *                                Multicast Listener is placed (on success).
  *
  * @retval OT_ERROR_NONE       Successfully found the next Multicast Listener info (@p aListenerInfo was successfully
  *                             updated).
diff --git a/include/openthread/border_router.h b/include/openthread/border_router.h
index 4161e19..b09ab20 100644
--- a/include/openthread/border_router.h
+++ b/include/openthread/border_router.h
@@ -137,11 +137,11 @@
 /**
  * This method provides a full or stable copy of the local Thread Network Data.
  *
- * @param[in]     aInstance    A pointer to an OpenThread instance.
- * @param[in]     aStable      TRUE when copying the stable version, FALSE when copying the full version.
- * @param[out]    aData        A pointer to the data buffer.
- * @param[inout]  aDataLength  On entry, size of the data buffer pointed to by @p aData.
- *                             On exit, number of copied bytes.
+ * @param[in]      aInstance    A pointer to an OpenThread instance.
+ * @param[in]      aStable      TRUE when copying the stable version, FALSE when copying the full version.
+ * @param[out]     aData        A pointer to the data buffer.
+ * @param[in,out]  aDataLength  On entry, size of the data buffer pointed to by @p aData.
+ *                              On exit, number of copied bytes.
  */
 otError otBorderRouterGetNetData(otInstance *aInstance, bool aStable, uint8_t *aData, uint8_t *aDataLength);
 
@@ -177,10 +177,10 @@
 /**
  * This function gets the next On Mesh Prefix in the local Network Data.
  *
- * @param[in]     aInstance  A pointer to an OpenThread instance.
- * @param[inout]  aIterator  A pointer to the Network Data iterator context. To get the first on-mesh entry
-                             it should be set to OT_NETWORK_DATA_ITERATOR_INIT.
- * @param[out]    aConfig    A pointer to the On Mesh Prefix information.
+ * @param[in]      aInstance  A pointer to an OpenThread instance.
+ * @param[in,out]  aIterator  A pointer to the Network Data iterator context. To get the first on-mesh entry
+                              it should be set to OT_NETWORK_DATA_ITERATOR_INIT.
+ * @param[out]     aConfig    A pointer to the On Mesh Prefix information.
  *
  * @retval OT_ERROR_NONE       Successfully found the next On Mesh prefix.
  * @retval OT_ERROR_NOT_FOUND  No subsequent On Mesh prefix exists in the Thread Network Data.
@@ -222,10 +222,10 @@
 /**
  * This function gets the next external route in the local Network Data.
  *
- * @param[in]     aInstance  A pointer to an OpenThread instance.
- * @param[inout]  aIterator  A pointer to the Network Data iterator context. To get the first external route entry
-                             it should be set to OT_NETWORK_DATA_ITERATOR_INIT.
- * @param[out]    aConfig    A pointer to the External Route information.
+ * @param[in]      aInstance  A pointer to an OpenThread instance.
+ * @param[in,out]  aIterator  A pointer to the Network Data iterator context. To get the first external route entry
+                              it should be set to OT_NETWORK_DATA_ITERATOR_INIT.
+ * @param[out]     aConfig    A pointer to the External Route information.
  *
  * @retval OT_ERROR_NONE       Successfully found the next External Route.
  * @retval OT_ERROR_NOT_FOUND  No subsequent external route entry exists in the Thread Network Data.
diff --git a/include/openthread/cli.h b/include/openthread/cli.h
index 56c9868..3fa5c56 100644
--- a/include/openthread/cli.h
+++ b/include/openthread/cli.h
@@ -70,9 +70,9 @@
 /**
  * This function pointer is called to notify about Console output.
  *
- * @param[in]  aBuf        A pointer to a buffer with an output.
- * @param[in]  aBufLength  A length of the output data stored in the buffer.
  * @param[out] aContext    A user context pointer.
+ * @param[in]  aFormat     The format string.
+ * @param[in]  aArguments  The format string arguments.
  *
  * @returns                Number of bytes written by the callback.
  *
@@ -147,16 +147,6 @@
 void otCliPlatLogv(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, va_list aArgs);
 
 /**
- * Function to write the OpenThread Log to the CLI console.
- *
- * @param[in]  aLogLevel   The log level.
- * @param[in]  aLogRegion  The log region.
- * @param[in]  aLogLine    A pointer to the log line string.
- *
- */
-void otCliPlatLogLine(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aLogLine);
-
-/**
  * @}
  *
  */
diff --git a/include/openthread/coap.h b/include/openthread/coap.h
index 396d535..c2e036b 100644
--- a/include/openthread/coap.h
+++ b/include/openthread/coap.h
@@ -389,9 +389,9 @@
  * is enabled.
  *
  * @param[in]       aContext     A pointer to application-specific context.
- * @param[inout]    aBlock       A pointer to where the block segment can be written to.
+ * @param[in,out]   aBlock       A pointer to where the block segment can be written to.
  * @param[in]       aPosition    The position in a sequence from which to obtain the block segment.
- * @param[inout]    aBlockLength On entry, the maximum block segment length in bytes.
+ * @param[in,out]   aBlockLength On entry, the maximum block segment length in bytes.
  * @param[out]      aMore        A pointer to the flag if more block segments will follow.
  *
  * @warning By changing the value of aBlockLength, the block size of the whole exchange is
@@ -485,9 +485,9 @@
 /**
  * This function initializes the CoAP header.
  *
- * @param[inout] aMessage   A pointer to the CoAP message to initialize.
- * @param[in]    aType      CoAP message type.
- * @param[in]    aCode      CoAP message code.
+ * @param[in,out] aMessage   A pointer to the CoAP message to initialize.
+ * @param[in]     aType      CoAP message type.
+ * @param[in]     aCode      CoAP message code.
  *
  */
 void otCoapMessageInit(otMessage *aMessage, otCoapType aType, otCoapCode aCode);
@@ -497,10 +497,10 @@
  *
  * @note Both message ID and token are set according to @p aRequest.
  *
- * @param[inout] aResponse  A pointer to the CoAP response message.
- * @param[in]    aRequest   A pointer to the CoAP request message.
- * @param[in]    aType      CoAP message type.
- * @param[in]    aCode      CoAP message code.
+ * @param[in,out]  aResponse  A pointer to the CoAP response message.
+ * @param[in]      aRequest   A pointer to the CoAP request message.
+ * @param[in]      aType      CoAP message type.
+ * @param[in]      aCode      CoAP message code.
  *
  * @retval OT_ERROR_NONE     Successfully initialized the response message.
  * @retval OT_ERROR_NO_BUFS  Insufficient message buffers available to initialize the response message.
@@ -511,9 +511,9 @@
 /**
  * This function sets the Token value and length in a header.
  *
- * @param[inout]  aMessage          A pointer to the CoAP message.
- * @param[in]     aToken            A pointer to the Token value.
- * @param[in]     aTokenLength      The Length of @p aToken.
+ * @param[in,out]  aMessage          A pointer to the CoAP message.
+ * @param[in]      aToken            A pointer to the Token value.
+ * @param[in]      aTokenLength      The Length of @p aToken.
  *
  * @retval OT_ERROR_NONE     Successfully set the Token value.
  * @retval OT_ERROR_NO_BUFS  Insufficient buffers to set the Token value.
@@ -524,8 +524,8 @@
 /**
  * This function sets the Token length and randomizes its value.
  *
- * @param[inout]  aMessage      A pointer to the CoAP message.
- * @param[in]     aTokenLength  The Length of a Token to set.
+ * @param[in,out]  aMessage      A pointer to the CoAP message.
+ * @param[in]      aTokenLength  The Length of a Token to set.
  *
  */
 void otCoapMessageGenerateToken(otMessage *aMessage, uint8_t aTokenLength);
@@ -540,8 +540,8 @@
  * and if the desired format type code isn't listed in otCoapOptionContentFormat,
  * this base function should be used instead.
  *
- * @param[inout]  aMessage          A pointer to the CoAP message.
- * @param[in]     aContentFormat    One of the content formats listed in
+ * @param[in,out]  aMessage          A pointer to the CoAP message.
+ * @param[in]      aContentFormat    One of the content formats listed in
  *                                  otCoapOptionContentFormat above.
  *
  * @retval OT_ERROR_NONE          Successfully appended the option.
@@ -554,10 +554,10 @@
 /**
  * This function appends a CoAP option in a header.
  *
- * @param[inout]  aMessage  A pointer to the CoAP message.
- * @param[in]     aNumber   The CoAP Option number.
- * @param[in]     aLength   The CoAP Option length.
- * @param[in]     aValue    A pointer to the CoAP value.
+ * @param[in,out]  aMessage  A pointer to the CoAP message.
+ * @param[in]      aNumber   The CoAP Option number.
+ * @param[in]      aLength   The CoAP Option length.
+ * @param[in]      aValue    A pointer to the CoAP value.
  *
  * @retval OT_ERROR_NONE          Successfully appended the option.
  * @retval OT_ERROR_INVALID_ARGS  The option type is not equal or greater than the last option type.
@@ -570,9 +570,9 @@
  * This function appends an unsigned integer CoAP option as specified in
  * https://tools.ietf.org/html/rfc7252#section-3.2
  *
- * @param[inout]  aMessage A pointer to the CoAP message.
- * @param[in]     aNumber  The CoAP Option number.
- * @param[in]     aValue   The CoAP Option unsigned integer value.
+ * @param[in,out]  aMessage A pointer to the CoAP message.
+ * @param[in]      aNumber  The CoAP Option number.
+ * @param[in]      aValue   The CoAP Option unsigned integer value.
  *
  * @retval OT_ERROR_NONE          Successfully appended the option.
  * @retval OT_ERROR_INVALID_ARGS  The option type is not equal or greater than the last option type.
@@ -585,8 +585,8 @@
 /**
  * This function appends an Observe option.
  *
- * @param[inout]  aMessage  A pointer to the CoAP message.
- * @param[in]     aObserve  Observe field value.
+ * @param[in,out]  aMessage  A pointer to the CoAP message.
+ * @param[in]      aObserve  Observe field value.
  *
  * @retval OT_ERROR_NONE          Successfully appended the option.
  * @retval OT_ERROR_INVALID_ARGS  The option type is not equal or greater than the last option type.
@@ -598,8 +598,8 @@
 /**
  * This function appends a Uri-Path option.
  *
- * @param[inout]  aMessage  A pointer to the CoAP message.
- * @param[in]     aUriPath  A pointer to a NULL-terminated string.
+ * @param[in,out]  aMessage  A pointer to the CoAP message.
+ * @param[in]      aUriPath  A pointer to a NULL-terminated string.
  *
  * @retval OT_ERROR_NONE          Successfully appended the option.
  * @retval OT_ERROR_INVALID_ARGS  The option type is not equal or greater than the last option type.
@@ -621,10 +621,10 @@
 /**
  * This function appends a Block2 option
  *
- * @param[inout]  aMessage  A pointer to the CoAP message.
- * @param[in]     aNum      Current block number.
- * @param[in]     aMore     Boolean to indicate more blocks are to be sent.
- * @param[in]     aSize     Block Size Exponent.
+ * @param[in,out]  aMessage  A pointer to the CoAP message.
+ * @param[in]      aNum      Current block number.
+ * @param[in]      aMore     Boolean to indicate more blocks are to be sent.
+ * @param[in]      aSize     Block Size Exponent.
  *
  * @retval OT_ERROR_NONE          Successfully appended the option.
  * @retval OT_ERROR_INVALID_ARGS  The option type is not equal or greater than the last option type.
@@ -636,10 +636,10 @@
 /**
  * This function appends a Block1 option
  *
- * @param[inout]  aMessage  A pointer to the CoAP message.
- * @param[in]     aNum      Current block number.
- * @param[in]     aMore     Boolean to indicate more blocks are to be sent.
- * @param[in]     aSize     Block Size Exponent.
+ * @param[in,out]  aMessage  A pointer to the CoAP message.
+ * @param[in]      aNum      Current block number.
+ * @param[in]      aMore     Boolean to indicate more blocks are to be sent.
+ * @param[in]      aSize     Block Size Exponent.
  *
  * @retval OT_ERROR_NONE          Successfully appended the option.
  * @retval OT_ERROR_INVALID_ARGS  The option type is not equal or greater than the last option type.
@@ -651,8 +651,8 @@
 /**
  * This function appends a Proxy-Uri option.
  *
- * @param[inout]  aMessage  A pointer to the CoAP message.
- * @param[in]     aUriPath  A pointer to a NULL-terminated string.
+ * @param[in,out]  aMessage  A pointer to the CoAP message.
+ * @param[in]      aUriPath  A pointer to a NULL-terminated string.
  *
  * @retval OT_ERROR_NONE          Successfully appended the option.
  * @retval OT_ERROR_INVALID_ARGS  The option type is not equal or greater than the last option type.
@@ -664,8 +664,8 @@
 /**
  * This function appends a Max-Age option.
  *
- * @param[inout]  aMessage  A pointer to the CoAP message.
- * @param[in]     aMaxAge   The Max-Age value.
+ * @param[in,out]  aMessage  A pointer to the CoAP message.
+ * @param[in]      aMaxAge   The Max-Age value.
  *
  * @retval OT_ERROR_NONE          Successfully appended the option.
  * @retval OT_ERROR_INVALID_ARGS  The option type is not equal or greater than the last option type.
@@ -677,8 +677,8 @@
 /**
  * This function appends a single Uri-Query option.
  *
- * @param[inout]  aMessage  A pointer to the CoAP message.
- * @param[in]     aUriQuery A pointer to NULL-terminated string, which should contain a single key=value pair.
+ * @param[in,out]  aMessage  A pointer to the CoAP message.
+ * @param[in]      aUriQuery A pointer to NULL-terminated string, which should contain a single key=value pair.
  *
  * @retval OT_ERROR_NONE          Successfully appended the option.
  * @retval OT_ERROR_INVALID_ARGS  The option type is not equal or greater than the last option type.
@@ -689,7 +689,7 @@
 /**
  * This function adds Payload Marker indicating beginning of the payload to the CoAP header.
  *
- * @param[inout]  aMessage  A pointer to the CoAP message.
+ * @param[in,out]  aMessage  A pointer to the CoAP message.
  *
  * @retval OT_ERROR_NONE     Payload Marker successfully added.
  * @retval OT_ERROR_NO_BUFS  Header Payload Marker exceeds the buffer size.
@@ -720,8 +720,8 @@
 /**
  * This function sets the Code value.
  *
- * @param[inout]  aMessage  A pointer to the CoAP message to initialize.
- * @param[in]     aCode     CoAP message code.
+ * @param[in,out]  aMessage  A pointer to the CoAP message to initialize.
+ * @param[in]      aCode     CoAP message code.
  *
  */
 void otCoapMessageSetCode(otMessage *aMessage, otCoapCode aCode);
@@ -769,8 +769,8 @@
 /**
  * This function initialises an iterator for the options in the given message.
  *
- * @param[inout]  aIterator A pointer to the CoAP message option iterator.
- * @param[in]     aMessage  A pointer to the CoAP message.
+ * @param[in,out]  aIterator A pointer to the CoAP message option iterator.
+ * @param[in]      aMessage  A pointer to the CoAP message.
  *
  * @retval  OT_ERROR_NONE   Successfully initialised.
  * @retval  OT_ERROR_PARSE  Message state is inconsistent.
@@ -792,7 +792,7 @@
 /**
  * This function returns a pointer to the first option.
  *
- * @param[inout]  aIterator A pointer to the CoAP message option iterator.
+ * @param[in,out]  aIterator A pointer to the CoAP message option iterator.
  *
  * @returns A pointer to the first option. If no option is present NULL pointer is returned.
  *
@@ -813,7 +813,7 @@
 /**
  * This function returns a pointer to the next option.
  *
- * @param[inout]  aIterator A pointer to the CoAP message option iterator.
+ * @param[in,out]  aIterator A pointer to the CoAP message option iterator.
  *
  * @returns A pointer to the next option. If no more options are present NULL pointer is returned.
  *
@@ -824,7 +824,7 @@
  * This function fills current option value into @p aValue assuming the current value is an unsigned integer encoded
  * according to https://tools.ietf.org/html/rfc7252#section-3.2
  *
- * @param[inout]    aIterator   A pointer to the CoAP message option iterator.
+ * @param[in,out]   aIterator   A pointer to the CoAP message option iterator.
  * @param[out]      aValue      A pointer to an unsigned integer to receive the option value.
  *
  * @retval  OT_ERROR_NONE       Successfully filled value.
@@ -838,8 +838,8 @@
 /**
  * This function fills current option value into @p aValue.
  *
- * @param[inout]  aIterator A pointer to the CoAP message option iterator.
- * @param[out]    aValue    A pointer to a buffer to receive the option value.
+ * @param[in,out]  aIterator A pointer to the CoAP message option iterator.
+ * @param[out]     aValue    A pointer to a buffer to receive the option value.
  *
  * @retval  OT_ERROR_NONE       Successfully filled value.
  * @retval  OT_ERROR_NOT_FOUND  No current option.
diff --git a/include/openthread/commissioner.h b/include/openthread/commissioner.h
index f142edc..d632fd0 100644
--- a/include/openthread/commissioner.h
+++ b/include/openthread/commissioner.h
@@ -154,10 +154,8 @@
 /**
  * This function pointer is called whenever the commissioner state changes.
  *
- * @param[in]  aChannelMask       The channel mask value.
- * @param[in]  aEnergyList        A pointer to the energy measurement list.
- * @param[in]  aEnergyListLength  Number of entries in @p aEnergyListLength.
- * @param[in]  aContext           A pointer to application-specific context.
+ * @param[in]  aState    The Commissioner state.
+ * @param[in]  aContext  A pointer to application-specific context.
  *
  */
 typedef void (*otCommissionerStateCallback)(otCommissionerState aState, void *aContext);
@@ -206,6 +204,29 @@
 otError otCommissionerStop(otInstance *aInstance);
 
 /**
+ * This function returns the Commissioner Id.
+ *
+ * @param[in]  aInstance         A pointer to an OpenThread instance.
+ *
+ * @returns The Commissioner Id.
+ *
+ */
+const char *otCommissionerGetId(otInstance *aInstance);
+
+/**
+ * This function sets the Commissioner Id.
+ *
+ * @param[in]  aInstance     A pointer to an OpenThread instance.
+ * @param[in]  aId           A pointer to a string character array. Must be null terminated.
+ *
+ * @retval OT_ERROR_NONE            Successfully set the Commissioner Id.
+ * @retval OT_ERROR_INVALID_ARGS    Given name is too long.
+ * @retval OT_ERROR_INVALID_STATE   The commissioner is active and id cannot be changed.
+ *
+ */
+otError otCommissionerSetId(otInstance *aInstance, const char *aId);
+
+/**
  * This function adds a Joiner entry.
  *
  * @param[in]  aInstance          A pointer to an OpenThread instance.
@@ -251,7 +272,7 @@
  * This method get joiner info at aIterator position.
  *
  * @param[in]      aInstance   A pointer to instance.
- * @param[inout]   aIterator   A pointer to the Joiner Info iterator context.
+ * @param[in,out]  aIterator   A pointer to the Joiner Info iterator context.
  * @param[out]     aJoiner     A reference to Joiner info.
  *
  * @retval OT_ERROR_NONE       Successfully get the Joiner info.
@@ -280,7 +301,7 @@
  * This function removes a Joiner entry.
  *
  * @param[in]  aInstance          A pointer to an OpenThread instance.
- * @param[in]  aEui64             A pointer to the Joiner Discerner.
+ * @param[in]  aDiscerner         A pointer to the Joiner Discerner.
  *
  * @retval OT_ERROR_NONE          Successfully removed the Joiner.
  * @retval OT_ERROR_NOT_FOUND     The Joiner specified by @p aEui64 was not found.
diff --git a/include/openthread/config.h b/include/openthread/config.h
index 2a5fd26..090532c 100644
--- a/include/openthread/config.h
+++ b/include/openthread/config.h
@@ -41,10 +41,8 @@
  * The OpenThread feature configuration file.
  *
  */
-#if !defined(OPENTHREAD_CONFIG_FILE)
-#define OPENTHREAD_CONFIG_FILE <openthread-config-generic.h>
-#endif
-
+#if defined(OPENTHREAD_CONFIG_FILE)
 #include OPENTHREAD_CONFIG_FILE
+#endif
 
 #endif // OPENTHREAD_CONFIG_H_
diff --git a/include/openthread/crypto.h b/include/openthread/crypto.h
index a1a3d36..3be657b 100644
--- a/include/openthread/crypto.h
+++ b/include/openthread/crypto.h
@@ -89,20 +89,20 @@
 /**
  * This method performs AES CCM computation.
  *
- * @param[in]     aKey           A pointer to the key.
- * @param[in]     aTagLength     Length of tag in bytes.
- * @param[in]     aNonce         A pointer to the nonce.
- * @param[in]     aNonceLength   Length of nonce in bytes.
+ * @param[in]      aKey           A pointer to the key.
+ * @param[in]      aTagLength     Length of tag in bytes.
+ * @param[in]      aNonce         A pointer to the nonce.
+ * @param[in]      aNonceLength   Length of nonce in bytes.
  *
- * @param[in]     aHeader        A pointer to the header.
- * @param[in]     aHeaderLength  Length of header in bytes.
+ * @param[in]      aHeader        A pointer to the header.
+ * @param[in]      aHeaderLength  Length of header in bytes.
  *
- * @param[inout]  aPlainText     A pointer to the plaintext.
- * @param[inout]  aCipherText    A pointer to the ciphertext.
- * @param[in]     aLength        Plaintext length in bytes.
- * @param[in]     aEncrypt       `true` on encrypt and `false` on decrypt.
+ * @param[in,out]  aPlainText     A pointer to the plaintext.
+ * @param[in,out]  aCipherText    A pointer to the ciphertext.
+ * @param[in]      aLength        Plaintext length in bytes.
+ * @param[in]      aEncrypt       `true` on encrypt and `false` on decrypt.
  *
- * @param[out]    aTag           A pointer to the tag.
+ * @param[out]     aTag           A pointer to the tag.
  *
  */
 void otCryptoAesCcm(const otCryptoKey *aKey,
@@ -120,12 +120,12 @@
 /**
  * This method creates ECDSA sign.
  *
- * @param[out]    aOutput            An output buffer where ECDSA sign should be stored.
- * @param[inout]  aOutputLength      The length of the @p aOutput buffer.
- * @param[in]     aInputHash         An input hash.
- * @param[in]     aInputHashLength   The length of the @p aInputHash buffer.
- * @param[in]     aPrivateKey        A private key in PEM format.
- * @param[in]     aPrivateKeyLength  The length of the @p aPrivateKey buffer.
+ * @param[out]     aOutput            An output buffer where ECDSA sign should be stored.
+ * @param[in,out]  aOutputLength      The length of the @p aOutput buffer.
+ * @param[in]      aInputHash         An input hash.
+ * @param[in]      aInputHashLength   The length of the @p aInputHash buffer.
+ * @param[in]      aPrivateKey        A private key in PEM format.
+ * @param[in]      aPrivateKeyLength  The length of the @p aPrivateKey buffer.
  *
  * @retval  OT_ERROR_NONE         ECDSA sign has been created successfully.
  * @retval  OT_ERROR_NO_BUFS      Output buffer is too small.
diff --git a/include/openthread/dataset.h b/include/openthread/dataset.h
index 6e899b1..a92f3a7 100644
--- a/include/openthread/dataset.h
+++ b/include/openthread/dataset.h
@@ -152,7 +152,6 @@
     bool    mNativeCommissioningEnabled : 1;     ///< Native Commissioning using PSKc is allowed
     bool    mRoutersEnabled : 1;                 ///< Thread 1.0/1.1.x Routers are enabled
     bool    mExternalCommissioningEnabled : 1;   ///< External Commissioner authentication is allowed
-    bool    mBeaconsEnabled : 1;                 ///< Thread 1.0/1.1.x Beacons are enabled
     bool    mCommercialCommissioningEnabled : 1; ///< Commercial Commissioning is enabled
     bool    mAutonomousEnrollmentEnabled : 1;    ///< Autonomous Enrollment is enabled
     bool    mNetworkKeyProvisioningEnabled : 1;  ///< Network Key Provisioning is enabled
diff --git a/include/openthread/dns_client.h b/include/openthread/dns_client.h
index e212062..17817c1 100644
--- a/include/openthread/dns_client.h
+++ b/include/openthread/dns_client.h
@@ -199,6 +199,8 @@
  *
  * @retval OT_ERROR_NONE          Query sent successfully. @p aCallback will be invoked to report the status.
  * @retval OT_ERROR_NO_BUFS       Insufficient buffer to prepare and send query.
+ * @retval OT_ERROR_INVALID_ARGS  The host name is not valid format.
+ * @retval OT_ERROR_INVALID_STATE Cannot send query since Thread interface is not up.
  *
  */
 otError otDnsClientResolveAddress(otInstance *            aInstance,
@@ -208,6 +210,36 @@
                                   const otDnsQueryConfig *aConfig);
 
 /**
+ * This function sends an address resolution DNS query for A (IPv4) record(s) for a given host name.
+ *
+ * This function requires and is available when `OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE` is enabled.
+ *
+ * When a successful response is received, the addresses are returned from @p aCallback as NAT64 IPv6 translated
+ * versions of the IPv4 addresses from the query response.
+ *
+ * The @p aConfig can be NULL. In this case the default config (from `otDnsClientGetDefaultConfig()`) will be used as
+ * the config for this query. In a non-NULL @p aConfig, some of the fields can be left unspecified (value zero). The
+ * unspecified fields are then replaced by the values from the default config.
+ *
+ * @param[in]  aInstance        A pointer to an OpenThread instance.
+ * @param[in]  aHostName        The host name for which to query the address (MUST NOT be NULL).
+ * @param[in]  aCallback        A function pointer that shall be called on response reception or time-out.
+ * @param[in]  aContext         A pointer to arbitrary context information.
+ * @param[in]  aConfig          A pointer to the config to use for this query.
+ *
+ * @retval OT_ERROR_NONE          Query sent successfully. @p aCallback will be invoked to report the status.
+ * @retval OT_ERROR_NO_BUFS       Insufficient buffer to prepare and send query.
+ * @retval OT_ERROR_INVALID_ARGS  The host name is not valid format or NAT64 is not enabled in config.
+ * @retval OT_ERROR_INVALID_STATE Cannot send query since Thread interface is not up.
+ *
+ */
+otError otDnsClientResolveIp4Address(otInstance *            aInstance,
+                                     const char *            aHostName,
+                                     otDnsAddressCallback    aCallback,
+                                     void *                  aContext,
+                                     const otDnsQueryConfig *aConfig);
+
+/**
  * This function gets the full host name associated with an address resolution DNS response.
  *
  * This function MUST only be used from `otDnsAddressCallback`.
@@ -239,9 +271,10 @@
  * @param[out] aTtl          A pointer to an `uint32_t` to output TTL for the address. It can be NULL if caller does not
  *                           want to get the TTL.
  *
- * @retval OT_ERROR_NONE       The address was read successfully.
- * @retval OT_ERROR_NOT_FOUND  No address record in @p aResponse at @p aIndex.
- * @retval OT_ERROR_PARSE      Could not parse the records in the @p aResponse.
+ * @retval OT_ERROR_NONE           The address was read successfully.
+ * @retval OT_ERROR_NOT_FOUND      No address record in @p aResponse at @p aIndex.
+ * @retval OT_ERROR_PARSE          Could not parse the records in the @p aResponse.
+ * @retval OT_ERROR_INVALID_STATE  No NAT64 prefix (applicable only when NAT64 is allowed).
  *
  */
 otError otDnsAddressResponseGetAddress(const otDnsAddressResponse *aResponse,
diff --git a/include/openthread/history_tracker.h b/include/openthread/history_tracker.h
index dfb098f..a8dc7c3 100644
--- a/include/openthread/history_tracker.h
+++ b/include/openthread/history_tracker.h
@@ -241,12 +241,12 @@
 /**
  * This function iterates over the entries in the network info history list.
  *
- * @param[in]    aInstance   A pointer to the OpenThread instance.
- * @param[inout] aIterator   A pointer to an iterator. MUST be initialized or the behavior is undefined.
- * @param[out]   aEntryAge   A pointer to a variable to output the entry's age. MUST NOT be NULL.
- *                           Age is provided as the duration (in milliseconds) from when entry was recorded to
- *                           @p aIterator initialization time. It is set to `OT_HISTORY_TRACKER_MAX_AGE` for entries
- *                           older than max age.
+ * @param[in]     aInstance   A pointer to the OpenThread instance.
+ * @param[in,out] aIterator   A pointer to an iterator. MUST be initialized or the behavior is undefined.
+ * @param[out]    aEntryAge   A pointer to a variable to output the entry's age. MUST NOT be NULL.
+ *                            Age is provided as the duration (in milliseconds) from when entry was recorded to
+ *                            @p aIterator initialization time. It is set to `OT_HISTORY_TRACKER_MAX_AGE` for entries
+ *                            older than max age.
  *
  * @returns A pointer to `otHistoryTrackerNetworkInfo` entry or `NULL` if no more entries in the list.
  *
@@ -258,9 +258,9 @@
 /**
  * This function iterates over the entries in the unicast address history list.
  *
- * @param[in]    aInstance   A pointer to the OpenThread instance.
- * @param[inout] aIterator   A pointer to an iterator. MUST be initialized or the behavior is undefined.
- * @param[out]   aEntryAge   A pointer to a variable to output the entry's age. MUST NOT be NULL.
+ * @param[in]     aInstance  A pointer to the OpenThread instance.
+ * @param[in,out] aIterator  A pointer to an iterator. MUST be initialized or the behavior is undefined.
+ * @param[out]    aEntryAge  A pointer to a variable to output the entry's age. MUST NOT be NULL.
  *                           Age is provided as the duration (in milliseconds) from when entry was recorded to
  *                           @p aIterator initialization time. It is set to `OT_HISTORY_TRACKER_MAX_AGE` for entries
  *                           older than max age.
@@ -276,9 +276,9 @@
 /**
  * This function iterates over the entries in the multicast address history list.
  *
- * @param[in]    aInstance   A pointer to the OpenThread instance.
- * @param[inout] aIterator   A pointer to an iterator. MUST be initialized or the behavior is undefined.
- * @param[out]   aEntryAge   A pointer to a variable to output the entry's age. MUST NOT be NULL.
+ * @param[in]     aInstance  A pointer to the OpenThread instance.
+ * @param[in,out] aIterator  A pointer to an iterator. MUST be initialized or the behavior is undefined.
+ * @param[out]    aEntryAge  A pointer to a variable to output the entry's age. MUST NOT be NULL.
  *                           Age is provided as the duration (in milliseconds) from when entry was recorded to
  *                           @p aIterator initialization time. It is set to `OT_HISTORY_TRACKER_MAX_AGE` for entries
  *                           older than max age.
@@ -294,9 +294,9 @@
 /**
  * This function iterates over the entries in the RX message history list.
  *
- * @param[in]    aInstance   A pointer to the OpenThread instance.
- * @param[inout] aIterator   A pointer to an iterator. MUST be initialized or the behavior is undefined.
- * @param[out]   aEntryAge   A pointer to a variable to output the entry's age. MUST NOT be NULL.
+ * @param[in]     aInstance  A pointer to the OpenThread instance.
+ * @param[in,out] aIterator  A pointer to an iterator. MUST be initialized or the behavior is undefined.
+ * @param[out]    aEntryAge  A pointer to a variable to output the entry's age. MUST NOT be NULL.
  *                           Age is provided as the duration (in milliseconds) from when entry was recorded to
  *                           @p aIterator initialization time. It is set to `OT_HISTORY_TRACKER_MAX_AGE` for entries
  *                           older than max age.
@@ -311,9 +311,9 @@
 /**
  * This function iterates over the entries in the TX message history list.
  *
- * @param[in]    aInstance   A pointer to the OpenThread instance.
- * @param[inout] aIterator   A pointer to an iterator. MUST be initialized or the behavior is undefined.
- * @param[out]   aEntryAge   A pointer to a variable to output the entry's age. MUST NOT be NULL.
+ * @param[in]     aInstance  A pointer to the OpenThread instance.
+ * @param[in,out] aIterator  A pointer to an iterator. MUST be initialized or the behavior is undefined.
+ * @param[out]    aEntryAge  A pointer to a variable to output the entry's age. MUST NOT be NULL.
  *                           Age is provided as the duration (in milliseconds) from when entry was recorded to
  *                           @p aIterator initialization time. It is set to `OT_HISTORY_TRACKER_MAX_AGE` for entries
  *                           older than max age.
@@ -328,9 +328,9 @@
 /**
  * This function iterates over the entries in the neighbor history list.
  *
- * @param[in]    aInstance   A pointer to the OpenThread instance.
- * @param[inout] aIterator   A pointer to an iterator. MUST be initialized or the behavior is undefined.
- * @param[out]   aEntryAge   A pointer to a variable to output the entry's age. MUST NOT be NULL.
+ * @param[in]     aInstance  A pointer to the OpenThread instance.
+ * @param[in,out] aIterator  A pointer to an iterator. MUST be initialized or the behavior is undefined.
+ * @param[out]    aEntryAge  A pointer to a variable to output the entry's age. MUST NOT be NULL.
  *                           Age is provided as the duration (in milliseconds) from when entry was recorded to
  *                           @p aIterator initialization time. It is set to `OT_HISTORY_TRACKER_MAX_AGE` for entries
  *                           older than max age.
@@ -345,9 +345,9 @@
 /**
  * This function iterates over the entries in the Network Data on mesh prefix entry history list.
  *
- * @param[in]    aInstance   A pointer to the OpenThread instance.
- * @param[inout] aIterator   A pointer to an iterator. MUST be initialized or the behavior is undefined.
- * @param[out]   aEntryAge   A pointer to a variable to output the entry's age. MUST NOT be NULL.
+ * @param[in]     aInstance  A pointer to the OpenThread instance.
+ * @param[in,out] aIterator  A pointer to an iterator. MUST be initialized or the behavior is undefined.
+ * @param[out]    aEntryAge  A pointer to a variable to output the entry's age. MUST NOT be NULL.
  *                           Age is provided as the duration (in milliseconds) from when entry was recorded to
  *                           @p aIterator initialization time. It is set to `OT_HISTORY_TRACKER_MAX_AGE` for entries
  *                           older than max age.
@@ -362,9 +362,9 @@
 /**
  * This function iterates over the entries in the Network Data external route entry history list.
  *
- * @param[in]    aInstance   A pointer to the OpenThread instance.
- * @param[inout] aIterator   A pointer to an iterator. MUST be initialized or the behavior is undefined.
- * @param[out]   aEntryAge   A pointer to a variable to output the entry's age. MUST NOT be NULL.
+ * @param[in]     aInstance  A pointer to the OpenThread instance.
+ * @param[in,out] aIterator  A pointer to an iterator. MUST be initialized or the behavior is undefined.
+ * @param[out]    aEntryAge  A pointer to a variable to output the entry's age. MUST NOT be NULL.
  *                           Age is provided as the duration (in milliseconds) from when entry was recorded to
  *                           @p aIterator initialization time. It is set to `OT_HISTORY_TRACKER_MAX_AGE` for entries
  *                           older than max age.
diff --git a/include/openthread/instance.h b/include/openthread/instance.h
index 271378e..9fe27ff 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 (190)
+#define OPENTHREAD_API_VERSION (206)
 
 /**
  * @addtogroup api-instance
@@ -78,9 +78,9 @@
  *
  * This function is available and can only be used when support for multiple OpenThread instances is enabled.
  *
- * @param[in]    aInstanceBuffer      The buffer for OpenThread to use for allocating the otInstance structure.
- * @param[inout] aInstanceBufferSize  On input, the size of aInstanceBuffer. On output, if not enough space for
- *                                    otInstance, the number of bytes required for otInstance.
+ * @param[in]     aInstanceBuffer      The buffer for OpenThread to use for allocating the otInstance structure.
+ * @param[in,out] aInstanceBufferSize  On input, the size of aInstanceBuffer. On output, if not enough space for
+ *                                     otInstance, the number of bytes required for otInstance.
  *
  * @returns  A pointer to the new OpenThread instance.
  *
diff --git a/include/openthread/ip6.h b/include/openthread/ip6.h
index 62f18e9..00f20b0 100644
--- a/include/openthread/ip6.h
+++ b/include/openthread/ip6.h
@@ -344,8 +344,9 @@
  * @param[in]  aAddress  A pointer to an IP Address.
  *
  * @retval OT_ERROR_NONE          Successfully unsubscribed to the Network Interface Multicast Address.
- * @retval OT_ERROR_INVALID_ARGS  The IP Address indicated by @p aAddress is an internal address.
+ * @retval OT_ERROR_REJECTED      The IP Address indicated by @p aAddress is an internal address.
  * @retval OT_ERROR_NOT_FOUND     The IP Address indicated by @p aAddress was not found.
+ *
  */
 otError otIp6UnsubscribeMulticastAddress(otInstance *aInstance, const otIp6Address *aAddress);
 
@@ -692,8 +693,8 @@
 /**
  * This function perform OpenThread source address selection.
  *
- * @param[in]     aInstance     A pointer to an OpenThread instance.
- * @param[inout]  aMessageInfo  A pointer to the message information.
+ * @param[in]      aInstance     A pointer to an OpenThread instance.
+ * @param[in,out]  aMessageInfo  A pointer to the message information.
  *
  * @retval  OT_ERROR_NONE       Found a source address and is filled into mSockAddr of @p aMessageInfo.
  * @retval  OT_ERROR_NOT_FOUND  No source address was found and @p aMessageInfo is unchanged.
@@ -733,7 +734,7 @@
  * when it is about to add a SLAAC address based on a prefix. Its boolean return value determines whether the address
  * is filtered (not added) or not.
  *
- * @param[in] aInstacne   A pointer to an OpenThread instance.
+ * @param[in] aInstance   A pointer to an OpenThread instance.
  * @param[in] aPrefix     A pointer to prefix for which SLAAC address is about to be added.
  *
  * @retval TRUE    Indicates that the SLAAC address based on the prefix should be filtered and NOT added.
diff --git a/include/openthread/link.h b/include/openthread/link.h
index 1a6b0a8..4be9a79 100644
--- a/include/openthread/link.h
+++ b/include/openthread/link.h
@@ -60,11 +60,12 @@
  */
 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; ///< Indicates whether or not link security is enabled.
+    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.
@@ -378,18 +379,18 @@
  */
 typedef struct otActiveScanResult
 {
-    otExtAddress    mExtAddress;     ///< IEEE 802.15.4 Extended Address
-    otNetworkName   mNetworkName;    ///< Thread Network Name
-    otExtendedPanId mExtendedPanId;  ///< Thread Extended PAN ID
-    otSteeringData  mSteeringData;   ///< Steering Data
-    uint16_t        mPanId;          ///< IEEE 802.15.4 PAN ID
-    uint16_t        mJoinerUdpPort;  ///< Joiner UDP Port
-    uint8_t         mChannel;        ///< IEEE 802.15.4 Channel
-    int8_t          mRssi;           ///< RSSI (dBm)
-    uint8_t         mLqi;            ///< LQI
-    unsigned int    mVersion : 4;    ///< Version
-    bool            mIsNative : 1;   ///< Native Commissioner flag
-    bool            mIsJoinable : 1; ///< Joining Permitted flag
+    otExtAddress    mExtAddress;    ///< IEEE 802.15.4 Extended Address
+    otNetworkName   mNetworkName;   ///< Thread Network Name
+    otExtendedPanId mExtendedPanId; ///< Thread Extended PAN ID
+    otSteeringData  mSteeringData;  ///< Steering Data
+    uint16_t        mPanId;         ///< IEEE 802.15.4 PAN ID
+    uint16_t        mJoinerUdpPort; ///< Joiner UDP Port
+    uint8_t         mChannel;       ///< IEEE 802.15.4 Channel
+    int8_t          mRssi;          ///< RSSI (dBm)
+    uint8_t         mLqi;           ///< LQI
+    unsigned int    mVersion : 4;   ///< Version
+    bool            mIsNative : 1;  ///< Native Commissioner flag
+    bool            mDiscover : 1;  ///< Result from MLE Discovery
 } otActiveScanResult;
 
 /**
@@ -772,10 +773,10 @@
  *
  * This function is available when OPENTHREAD_CONFIG_MAC_FILTER_ENABLE configuration is enabled.
  *
- * @param[in]     aInstance  A pointer to an OpenThread instance.
- * @param[inout]  aIterator  A pointer to the MAC filter iterator context. To get the first in-use address filter entry,
- *                           it should be set to OT_MAC_FILTER_ITERATOR_INIT. MUST NOT be NULL.
- * @param[out]    aEntry     A pointer to where the information is placed. MUST NOT be NULL.
+ * @param[in]      aInstance  A pointer to an OpenThread instance.
+ * @param[in,out]  aIterator  A pointer to the MAC filter iterator context. To get the first in-use address filter
+ *                            entry, it should be set to OT_MAC_FILTER_ITERATOR_INIT. MUST NOT be NULL.
+ * @param[out]     aEntry     A pointer to where the information is placed. MUST NOT be NULL.
  *
  * @retval OT_ERROR_NONE          Successfully retrieved an in-use address filter entry.
  * @retval OT_ERROR_NOT_FOUND     No subsequent entry exists.
@@ -851,12 +852,12 @@
  *
  * This function is available when OPENTHREAD_CONFIG_MAC_FILTER_ENABLE configuration is enabled.
  *
- * @param[in]     aInstance  A pointer to an OpenThread instance.
- * @param[inout]  aIterator  A pointer to the MAC filter iterator context. MUST NOT be NULL.
- *                           To get the first entry, it should be set to OT_MAC_FILTER_ITERATOR_INIT.
- * @param[out]    aEntry     A pointer to where the information is placed. The last entry would have the extended
- *                           address as all 0xff to indicate the default received signal strength if it was set.
-                             @p aEntry MUST NOT be NULL.
+ * @param[in]      aInstance  A pointer to an OpenThread instance.
+ * @param[in,out]  aIterator  A pointer to the MAC filter iterator context. MUST NOT be NULL.
+ *                            To get the first entry, it should be set to OT_MAC_FILTER_ITERATOR_INIT.
+ * @param[out]     aEntry     A pointer to where the information is placed. The last entry would have the extended
+ *                            address as all 0xff to indicate the default received signal strength if it was set.
+                              @p aEntry MUST NOT be NULL.
  *
  * @retval OT_ERROR_NONE          Successfully retrieved the next entry.
  * @retval OT_ERROR_NOT_FOUND     No subsequent entry exists.
diff --git a/include/openthread/logging.h b/include/openthread/logging.h
index b406301..b07e643 100644
--- a/include/openthread/logging.h
+++ b/include/openthread/logging.h
@@ -77,6 +77,144 @@
 otError otLoggingSetLevel(otLogLevel aLogLevel);
 
 /**
+ * This function emits a log message at critical log level.
+ *
+ * This function is intended for use by platform. If `OPENTHREAD_CONFIG_LOG_PLATFORM` is not set or the current log
+ * level is below critical, this function does not emit any log message.
+ *
+ * @param[in]  aFormat  The format string.
+ * @param[in]  ...      Arguments for the format specification.
+ *
+ */
+void otLogCritPlat(const char *aFormat, ...);
+
+/**
+ * This function emits a log message at warning log level.
+ *
+ * This function is intended for use by platform. If `OPENTHREAD_CONFIG_LOG_PLATFORM` is not set or the current log
+ * level is below warning, this function does not emit any log message.
+ *
+ * @param[in]  aFormat  The format string.
+ * @param[in]  ...      Arguments for the format specification.
+ *
+ */
+void otLogWarnPlat(const char *aFormat, ...);
+
+/**
+ * This function emits a log message at note log level.
+ *
+ * This function is intended for use by platform. If `OPENTHREAD_CONFIG_LOG_PLATFORM` is not set or the current log
+ * level is below note, this function does not emit any log message.
+ *
+ * @param[in]  aFormat  The format string.
+ * @param[in]  ...      Arguments for the format specification.
+ *
+ */
+void otLogNotePlat(const char *aFormat, ...);
+
+/**
+ * This function emits a log message at info log level.
+ *
+ * This function is intended for use by platform. If `OPENTHREAD_CONFIG_LOG_PLATFORM` is not set or the current log
+ * level is below info, this function does not emit any log message.
+ *
+ * @param[in]  aFormat  The format string.
+ * @param[in]  ...      Arguments for the format specification.
+ *
+ */
+void otLogInfoPlat(const char *aFormat, ...);
+
+/**
+ * This function emits a log message at debug log level.
+ *
+ * This function is intended for use by platform. If `OPENTHREAD_CONFIG_LOG_PLATFORM` is not set or the current log
+ * level is below debug, this function does not emit any log message.
+ *
+ * @param[in]  aFormat  The format string.
+ * @param[in]  ...      Arguments for the format specification.
+ *
+ */
+void otLogDebgPlat(const char *aFormat, ...);
+
+/**
+ * This function generates a memory dump at critical log level.
+ *
+ * If `OPENTHREAD_CONFIG_LOG_PLATFORM` or `OPENTHREAD_CONFIG_LOG_PKT_DUMP` is not set or the current log level is below
+ * critical this function does not emit any log message.
+ *
+ * @param[in]  aText         A string that is printed before the bytes.
+ * @param[in]  aData         A pointer to the data buffer.
+ * @param[in]  aDataLength   Number of bytes in @p aData.
+ *
+ */
+void otDumpCritPlat(const char *aText, const void *aData, uint16_t aDataLength);
+
+/**
+ * This function generates a memory dump at warning log level.
+ *
+ * If `OPENTHREAD_CONFIG_LOG_PLATFORM` or `OPENTHREAD_CONFIG_LOG_PKT_DUMP` is not set or the current log level is below
+ * warning this function does not emit any log message.
+ *
+ * @param[in]  aText         A string that is printed before the bytes.
+ * @param[in]  aData         A pointer to the data buffer.
+ * @param[in]  aDataLength   Number of bytes in @p aData.
+ *
+ */
+void otDumpWarnPlat(const char *aText, const void *aData, uint16_t aDataLength);
+
+/**
+ * This function generates a memory dump at note log level.
+ *
+ * If `OPENTHREAD_CONFIG_LOG_PLATFORM` or `OPENTHREAD_CONFIG_LOG_PKT_DUMP` is not set or the current log level is below
+ * note this function does not emit any log message.
+ *
+ * @param[in]  aText         A string that is printed before the bytes.
+ * @param[in]  aData         A pointer to the data buffer.
+ * @param[in]  aDataLength   Number of bytes in @p aData.
+ *
+ */
+void otDumpNotePlat(const char *aText, const void *aData, uint16_t aDataLength);
+
+/**
+ * This function generates a memory dump at info log level.
+ *
+ * If `OPENTHREAD_CONFIG_LOG_PLATFORM` or `OPENTHREAD_CONFIG_LOG_PKT_DUMP` is not set or the current log level is below
+ * info this function does not emit any log message.
+ *
+ * @param[in]  aText         A string that is printed before the bytes.
+ * @param[in]  aData         A pointer to the data buffer.
+ * @param[in]  aDataLength   Number of bytes in @p aData.
+ *
+ */
+void otDumpInfoPlat(const char *aText, const void *aData, uint16_t aDataLength);
+
+/**
+ * This function generates a memory dump at debug log level.
+ *
+ * If `OPENTHREAD_CONFIG_LOG_PLATFORM` or `OPENTHREAD_CONFIG_LOG_PKT_DUMP` is not set or the current log level is below
+ * debug this function does not emit any log message.
+ *
+ * @param[in]  aText         A string that is printed before the bytes.
+ * @param[in]  aData         A pointer to the data buffer.
+ * @param[in]  aDataLength   Number of bytes in @p aData.
+ *
+ */
+void otDumpDebgPlat(const char *aText, const void *aData, uint16_t aDataLength);
+
+/**
+ * This function emits a log message at a given log level.
+ *
+ * This function is intended for use by CLI only. If `OPENTHREAD_CONFIG_LOG_CLI` is not set or the current log
+ * level is below the given log level, this function does not emit any log message.
+ *
+ * @param[in]  aLogLevel The log level.
+ * @param[in]  aFormat   The format string.
+ * @param[in]  ...       Arguments for the format specification.
+ *
+ */
+void otLogCli(otLogLevel aLogLevel, const char *aFormat, ...);
+
+/**
  * @}
  *
  */
diff --git a/include/openthread/message.h b/include/openthread/message.h
index 0b1430e..c39d99d 100644
--- a/include/openthread/message.h
+++ b/include/openthread/message.h
@@ -59,34 +59,6 @@
 typedef struct otMessage otMessage;
 
 /**
- * This structure represents the message buffer information.
- *
- */
-typedef struct otBufferInfo
-{
-    uint16_t mTotalBuffers;            ///< The number of buffers in the pool.
-    uint16_t mFreeBuffers;             ///< The number of free message buffers.
-    uint16_t m6loSendMessages;         ///< The number of messages in the 6lo send queue.
-    uint16_t m6loSendBuffers;          ///< The number of buffers in the 6lo send queue.
-    uint16_t m6loReassemblyMessages;   ///< The number of messages in the 6LoWPAN reassembly queue.
-    uint16_t m6loReassemblyBuffers;    ///< The number of buffers in the 6LoWPAN reassembly queue.
-    uint16_t mIp6Messages;             ///< The number of messages in the IPv6 send queue.
-    uint16_t mIp6Buffers;              ///< The number of buffers in the IPv6 send queue.
-    uint16_t mMplMessages;             ///< The number of messages in the MPL send queue.
-    uint16_t mMplBuffers;              ///< The number of buffers in the MPL send queue.
-    uint16_t mMleMessages;             ///< The number of messages in the MLE send queue.
-    uint16_t mMleBuffers;              ///< The number of buffers in the MLE send queue.
-    uint16_t mArpMessages;             ///< The number of messages in the ARP send queue.
-    uint16_t mArpBuffers;              ///< The number of buffers in the ARP send queue.
-    uint16_t mCoapMessages;            ///< The number of messages in the CoAP send queue.
-    uint16_t mCoapBuffers;             ///< The number of buffers in the CoAP send queue.
-    uint16_t mCoapSecureMessages;      ///< The number of messages in the CoAP secure send queue.
-    uint16_t mCoapSecureBuffers;       ///< The number of buffers in the CoAP secure send queue.
-    uint16_t mApplicationCoapMessages; ///< The number of messages in the application CoAP send queue.
-    uint16_t mApplicationCoapBuffers;  ///< The number of buffers in the application CoAP send queue.
-} otBufferInfo;
-
-/**
  * This enumeration defines the OpenThread message priority levels.
  *
  */
@@ -299,6 +271,35 @@
 } otMessageQueue;
 
 /**
+ * This structure represents information about a message queue.
+ *
+ */
+typedef struct otMessageQueueInfo
+{
+    uint16_t mNumMessages; ///< Number of messages in the queue.
+    uint16_t mNumBuffers;  ///< Number of data buffers used by messages in the queue.
+    uint32_t mTotalBytes;  ///< Total number of bytes used by all messages in the queue.
+} otMessageQueueInfo;
+
+/**
+ * This structure represents the message buffer information for different queues used by OpenThread stack.
+ *
+ */
+typedef struct otBufferInfo
+{
+    uint16_t           mTotalBuffers;         ///< The total number of buffers in the messages pool.
+    uint16_t           mFreeBuffers;          ///< The number of free buffers.
+    otMessageQueueInfo m6loSendQueue;         ///< Info about 6LoWPAN send queue.
+    otMessageQueueInfo m6loReassemblyQueue;   ///< Info about 6LoWPAN reassembly queue.
+    otMessageQueueInfo mIp6Queue;             ///< Info about IPv6 send queue.
+    otMessageQueueInfo mMplQueue;             ///< Info about MPL send queue.
+    otMessageQueueInfo mMleQueue;             ///< Info about MLE delayed message queue.
+    otMessageQueueInfo mCoapQueue;            ///< Info about CoAP/TMF send queue.
+    otMessageQueueInfo mCoapSecureQueue;      ///< Info about CoAP secure send queue.
+    otMessageQueueInfo mApplicationCoapQueue; ///< Info about application CoAP send queue.
+} otBufferInfo;
+
+/**
  * Initialize the message queue.
  *
  * This function MUST be called once and only once for a `otMessageQueue` instance before any other `otMessageQueue`
diff --git a/include/openthread/netdata.h b/include/openthread/netdata.h
index f67ea9b..74b92e8 100644
--- a/include/openthread/netdata.h
+++ b/include/openthread/netdata.h
@@ -127,11 +127,11 @@
 /**
  * This method provides a full or stable copy of the Partition's Thread Network Data.
  *
- * @param[in]     aInstance    A pointer to an OpenThread instance.
- * @param[in]     aStable      TRUE when copying the stable version, FALSE when copying the full version.
- * @param[out]    aData        A pointer to the data buffer.
- * @param[inout]  aDataLength  On entry, size of the data buffer pointed to by @p aData.
- *                             On exit, number of copied bytes.
+ * @param[in]      aInstance    A pointer to an OpenThread instance.
+ * @param[in]      aStable      TRUE when copying the stable version, FALSE when copying the full version.
+ * @param[out]     aData        A pointer to the data buffer.
+ * @param[in,out]  aDataLength  On entry, size of the data buffer pointed to by @p aData.
+ *                              On exit, number of copied bytes.
  *
  */
 otError otNetDataGet(otInstance *aInstance, bool aStable, uint8_t *aData, uint8_t *aDataLength);
@@ -139,10 +139,10 @@
 /**
  * This function gets the next On Mesh Prefix in the partition's Network Data.
  *
- * @param[in]     aInstance  A pointer to an OpenThread instance.
- * @param[inout]  aIterator  A pointer to the Network Data iterator context. To get the first on-mesh entry
-                             it should be set to OT_NETWORK_DATA_ITERATOR_INIT.
- * @param[out]    aConfig    A pointer to where the On Mesh Prefix information will be placed.
+ * @param[in]      aInstance  A pointer to an OpenThread instance.
+ * @param[in,out]  aIterator  A pointer to the Network Data iterator context. To get the first on-mesh entry
+                              it should be set to OT_NETWORK_DATA_ITERATOR_INIT.
+ * @param[out]     aConfig    A pointer to where the On Mesh Prefix information will be placed.
  *
  * @retval OT_ERROR_NONE       Successfully found the next On Mesh prefix.
  * @retval OT_ERROR_NOT_FOUND  No subsequent On Mesh prefix exists in the Thread Network Data.
@@ -155,10 +155,10 @@
 /**
  * This function gets the next external route in the partition's Network Data.
  *
- * @param[in]     aInstance  A pointer to an OpenThread instance.
- * @param[inout]  aIterator  A pointer to the Network Data iterator context. To get the first external route entry
-                             it should be set to OT_NETWORK_DATA_ITERATOR_INIT.
- * @param[out]    aConfig    A pointer to where the External Route information will be placed.
+ * @param[in]      aInstance  A pointer to an OpenThread instance.
+ * @param[in,out]  aIterator  A pointer to the Network Data iterator context. To get the first external route entry
+                              it should be set to OT_NETWORK_DATA_ITERATOR_INIT.
+ * @param[out]     aConfig    A pointer to where the External Route information will be placed.
  *
  * @retval OT_ERROR_NONE       Successfully found the next External Route.
  * @retval OT_ERROR_NOT_FOUND  No subsequent external route entry exists in the Thread Network Data.
@@ -169,10 +169,10 @@
 /**
  * This function gets the next service in the partition's Network Data.
  *
- * @param[in]     aInstance  A pointer to an OpenThread instance.
- * @param[inout]  aIterator  A pointer to the Network Data iterator context. To get the first service entry
-                             it should be set to OT_NETWORK_DATA_ITERATOR_INIT.
- * @param[out]    aConfig    A pointer to where the service information will be placed.
+ * @param[in]      aInstance  A pointer to an OpenThread instance.
+ * @param[in,out]  aIterator  A pointer to the Network Data iterator context. To get the first service entry
+                              it should be set to OT_NETWORK_DATA_ITERATOR_INIT.
+ * @param[out]     aConfig    A pointer to where the service information will be placed.
  *
  * @retval OT_ERROR_NONE       Successfully found the next service.
  * @retval OT_ERROR_NOT_FOUND  No subsequent service exists in the partition's Network Data.
@@ -231,6 +231,20 @@
                                                       const struct otJoinerDiscerner *aDiscerner);
 
 /**
+ * This function checks whether a given Prefix can act as a valid OMR prefix and also the Leader's Network Data contains
+ * this prefix.
+ *
+ * @param[in]  aInstance  A pointer to an OpenThread instance.
+ * @param[in]  aPrefix    A pointer to the IPv6 prefix.
+ *
+ * @returns  Whether @p aPrefix is a valid OMR prefix and Leader's Network Data contains the OMR prefix @p aPrefix.
+ *
+ * @note This API is only available when `OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE` is used.
+ *
+ */
+bool otNetDataContainsOmrPrefix(otInstance *aInstance, const otIp6Prefix *aPrefix);
+
+/**
  * @}
  *
  */
diff --git a/include/openthread/netdiag.h b/include/openthread/netdiag.h
index 2d291a0..d402383 100644
--- a/include/openthread/netdiag.h
+++ b/include/openthread/netdiag.h
@@ -264,10 +264,10 @@
 /**
  * This function gets the next Network Diagnostic TLV in the message.
  *
- * @param[in]     aMessage         A pointer to a message.
- * @param[inout]  aIterator        A pointer to the Network Diagnostic iterator context. To get the first
- *                                 Network Diagnostic TLV it should be set to OT_NETWORK_DIAGNOSTIC_ITERATOR_INIT.
- * @param[out]    aNetworkDiagTlv  A pointer to where the Network Diagnostic TLV information will be placed.
+ * @param[in]      aMessage         A pointer to a message.
+ * @param[in,out]  aIterator        A pointer to the Network Diagnostic iterator context. To get the first
+ *                                  Network Diagnostic TLV it should be set to OT_NETWORK_DIAGNOSTIC_ITERATOR_INIT.
+ * @param[out]     aNetworkDiagTlv  A pointer to where the Network Diagnostic TLV information will be placed.
  *
  * @retval OT_ERROR_NONE       Successfully found the next Network Diagnostic TLV.
  * @retval OT_ERROR_NOT_FOUND  No subsequent Network Diagnostic TLV exists in the message.
diff --git a/include/openthread/network_time.h b/include/openthread/network_time.h
index 23ab7e2..5e77231 100644
--- a/include/openthread/network_time.h
+++ b/include/openthread/network_time.h
@@ -78,8 +78,8 @@
 /**
  * Get the Thread network time.
  *
- * @param[in]    aInstance     The OpenThread instance structure.
- * @param[inout] aNetworkTime  The Thread network time in microseconds.
+ * @param[in]     aInstance     The OpenThread instance structure.
+ * @param[in,out] aNetworkTime  The Thread network time in microseconds.
  *
  * @returns The time synchronization status.
  *
diff --git a/include/openthread/platform/crypto.h b/include/openthread/platform/crypto.h
index 0d9bf5c..7cadc8e 100644
--- a/include/openthread/platform/crypto.h
+++ b/include/openthread/platform/crypto.h
@@ -139,13 +139,13 @@
 /**
  * Import a key into PSA ITS.
  *
- * @param[inout] aKeyRef           Pointer to the key ref to be used for crypto operations.
- * @param[in]    aKeyType          Key Type encoding for the key.
- * @param[in]    aKeyAlgorithm     Key algorithm encoding for the key.
- * @param[in]    aKeyUsage         Key Usage encoding for the key (combinations of `OT_CRYPTO_KEY_USAGE_*`).
- * @param[in]    aKeyPersistence   Key Persistence for this key
- * @param[in]    aKey              Actual key to be imported.
- * @param[in]    aKeyLen           Length of the key to be imported.
+ * @param[in,out] aKeyRef           Pointer to the key ref to be used for crypto operations.
+ * @param[in]     aKeyType          Key Type encoding for the key.
+ * @param[in]     aKeyAlgorithm     Key algorithm encoding for the key.
+ * @param[in]     aKeyUsage         Key Usage encoding for the key (combinations of `OT_CRYPTO_KEY_USAGE_*`).
+ * @param[in]     aKeyPersistence   Key Persistence for this key
+ * @param[in]     aKey              Actual key to be imported.
+ * @param[in]     aKeyLen           Length of the key to be imported.
  *
  * @retval OT_ERROR_NONE          Successfully imported the key.
  * @retval OT_ERROR_FAILED        Failed to import the key.
@@ -374,7 +374,7 @@
  *
  * @param[in]  aContext           Operation context for HKDF operation.
  * @param[in]  aSalt              Pointer to the Salt for HKDF.
- * @param[in]  aInfoLength        length of Salt.
+ * @param[in]  aSaltLength        Length of Salt.
  * @param[in]  aInputKey          Pointer to the input key.
  *
  * @retval OT_ERROR_NONE          HKDF Extract was successful.
@@ -455,7 +455,6 @@
  * Finish SHA-256 operation.
  *
  * @param[in]  aContext           Context for SHA-256 operation.
- * @param[in]  aContextSize       Context size SHA-256 operation.
  * @param[in]  aHash              A pointer to the output buffer, where hash needs to be stored.
  * @param[in]  aHashSize          The length of @p aHash in bytes.
  *
diff --git a/include/openthread/platform/logging.h b/include/openthread/platform/logging.h
index 4c62744..1efe361 100644
--- a/include/openthread/platform/logging.h
+++ b/include/openthread/platform/logging.h
@@ -115,6 +115,10 @@
 /**
  * This enumeration represents log regions.
  *
+ * The support for log region is removed and instead each core module can define its own name to appended to the logs.
+ * However, the `otLogRegion` enumeration is still defined as before to help with platforms which we may be using it
+ * in their `otPlatLog()` implementation. The OT core will always emit all logs with `OT_LOG_REGION_CORE`.
+ *
  */
 typedef enum otLogRegion
 {
@@ -146,6 +150,9 @@
 /**
  * This function outputs logs.
  *
+ * Note that the support for log region is removed. The OT core will always emit all logs with `OT_LOG_REGION_CORE`
+ * as @p aLogRegion.
+ *
  * @param[in]  aLogLevel   The log level.
  * @param[in]  aLogRegion  The log region.
  * @param[in]  aFormat     A pointer to the format string.
@@ -168,6 +175,19 @@
 void otPlatLogLine(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aLogLine);
 
 /**
+ * This function handles OpenThread log level changes.
+ *
+ * This platform function is called whenever the OpenThread log level changes.
+ * This platform function is optional since an empty weak implementation has been provided.
+ *
+ * @note Only applicable when `OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE=1`.
+ *
+ * @param[in]  aLogLevel  The new OpenThread log level.
+ *
+ */
+void otPlatLogHandleLevelChanged(otLogLevel aLogLevel);
+
+/**
  * @}
  *
  */
diff --git a/include/openthread/platform/radio.h b/include/openthread/platform/radio.h
index 2d7a654..6e85030 100644
--- a/include/openthread/platform/radio.h
+++ b/include/openthread/platform/radio.h
@@ -1126,8 +1126,8 @@
  * @param[in]  aInstance     The OpenThread instance structure.
  * @param[in]  aLinkMetrics  This parameter specifies what metrics to query. Per spec 4.11.3.4.4.6, at most 2 metrics
  *                           can be specified. The probing would be disabled if @p `aLinkMetrics` is bitwise 0.
- * @param[in]  aShortAddr    The short address of the Probing Initiator.
- * @param[in]  aExtAddr      The extended source address of the Probing Initiator. @p aExtAddr MUST NOT be `NULL`.
+ * @param[in]  aShortAddress The short address of the Probing Initiator.
+ * @param[in]  aExtAddress   The extended source address of the Probing Initiator. @p aExtAddr MUST NOT be `NULL`.
  *
  * @retval  OT_ERROR_NONE            Successfully configured the Enhanced-ACK Based Probing.
  * @retval  OT_ERROR_INVALID_ARGS    @p aExtAddress is `NULL`.
diff --git a/include/openthread/platform/settings.h b/include/openthread/platform/settings.h
index 39e0613..a5d6099 100644
--- a/include/openthread/platform/settings.h
+++ b/include/openthread/platform/settings.h
@@ -55,7 +55,7 @@
  * This enumeration defines the keys of settings.
  *
  * Note: When adding a new settings key, if the settings corresponding to the key contains security sensitive
- *       information, the developer MUST add the key to the array `kCriticalKeys`.
+ *       information, the developer MUST add the key to the array `kSensitiveKeys`.
  *
  */
 enum
@@ -68,21 +68,33 @@
     OT_SETTINGS_KEY_RESERVED             = 0x0006, ///< Reserved (previously auto-start).
     OT_SETTINGS_KEY_SLAAC_IID_SECRET_KEY = 0x0007, ///< SLAAC key to generate semantically opaque IID.
     OT_SETTINGS_KEY_DAD_INFO             = 0x0008, ///< Duplicate Address Detection (DAD) information.
-    OT_SETTINGS_KEY_OMR_PREFIX           = 0x0009, ///< Off-mesh routable (OMR) prefix.
+    OT_SETTINGS_KEY_LEGACY_OMR_PREFIX    = 0x0009, ///< Reserved. Legacy Off-mesh routable (OMR) prefix.
     OT_SETTINGS_KEY_ON_LINK_PREFIX       = 0x000a, ///< On-link prefix for infrastructure link.
     OT_SETTINGS_KEY_SRP_ECDSA_KEY        = 0x000b, ///< SRP client ECDSA public/private key pair.
     OT_SETTINGS_KEY_SRP_CLIENT_INFO      = 0x000c, ///< The SRP client info (selected SRP server address).
     OT_SETTINGS_KEY_SRP_SERVER_INFO      = 0x000d, ///< The SRP server info (UDP port).
-    OT_SETTINGS_KEY_NAT64_PREFIX         = 0x000e, ///< NAT64 prefix.
+    OT_SETTINGS_KEY_LEGACY_NAT64_PREFIX  = 0x000e, ///< Reserved. Legacy NAT64 prefix.
+    OT_SETTINGS_KEY_BR_ULA_PREFIX        = 0x000f, ///< BR ULA prefix.
+
+    // Keys in range 0x8000-0xffff are reserved for vendor-specific use.
+    OT_SETTINGS_KEY_VENDOR_RESERVED_MIN = 0x8000,
+    OT_SETTINGS_KEY_VENDOR_RESERVED_MAX = 0xffff,
 };
 
 /**
  * Performs any initialization for the settings subsystem, if necessary.
  *
- * @param[in]  aInstance The OpenThread instance structure.
+ * This function also sets the sensitive keys that should be stored in the secure area.
+ *
+ * Note that the memory pointed by @p aSensitiveKeys MUST not be released before @p aInstance is destroyed.
+ *
+ * @param[in]  aInstance             The OpenThread instance structure.
+ * @param[in]  aSensitiveKeys        A pointer to an array containing the list of sensitive keys. May be NULL only if
+ *                                   @p aSensitiveKeysLength is 0, which means that there is no sensitive keys.
+ * @param[in]  aSensitiveKeysLength  The number of entries in the @p aSensitiveKeys array.
  *
  */
-void otPlatSettingsInit(otInstance *aInstance);
+void otPlatSettingsInit(otInstance *aInstance, const uint16_t *aSensitiveKeys, uint16_t aSensitiveKeysLength);
 
 /**
  * Performs any de-initialization for the settings subsystem, if necessary.
@@ -92,18 +104,6 @@
  */
 void otPlatSettingsDeinit(otInstance *aInstance);
 
-/**
- * This function sets the critical keys that should be stored in the secure area.
- *
- * Note that the memory pointed by @p aKeys MUST not be released before @p aInstance is destroyed.
- *
- * @param[in]  aInstance    The OpenThread instance structure.
- * @param[in]  aKeys        A pointer to an array containing the list of critical keys.
- * @param[in]  aKeysLength  The number of entries in the @p aKeys array.
- *
- */
-void otPlatSettingsSetCriticalKeys(otInstance *aInstance, const uint16_t *aKeys, uint16_t aKeysLength);
-
 /// Fetches the value of a setting
 /** This function fetches the value of the setting identified
  *  by aKey and write it to the memory pointed to by aValue.
@@ -122,15 +122,15 @@
  *  values. The order of such values MAY change after ANY
  *  write operation to the store.
  *
- *  @param[in]     aInstance     The OpenThread instance structure.
- *  @param[in]     aKey          The key associated with the requested setting.
- *  @param[in]     aIndex        The index of the specific item to get.
- *  @param[out]    aValue        A pointer to where the value of the setting should be written. May be set to NULL if
- *                               just testing for the presence or length of a setting.
- *  @param[inout]  aValueLength  A pointer to the length of the value. When called, this pointer should point to an
- *                               integer containing the maximum value size that can be written to aValue. At return,
- *                               the actual length of the setting is written. This may be set to NULL if performing
- *                               a presence check.
+ *  @param[in]      aInstance     The OpenThread instance structure.
+ *  @param[in]      aKey          The key associated with the requested setting.
+ *  @param[in]      aIndex        The index of the specific item to get.
+ *  @param[out]     aValue        A pointer to where the value of the setting should be written. May be set to NULL if
+ *                                just testing for the presence or length of a setting.
+ *  @param[in,out]  aValueLength  A pointer to the length of the value. When called, this pointer should point to an
+ *                                integer containing the maximum value size that can be written to aValue. At return,
+ *                                the actual length of the setting is written. This may be set to NULL if performing
+ *                                a presence check.
  *
  *  @retval OT_ERROR_NONE             The given setting was found and fetched successfully.
  *  @retval OT_ERROR_NOT_FOUND        The given setting was not found in the setting store.
diff --git a/include/openthread/server.h b/include/openthread/server.h
index 05f3205..0d177c7 100644
--- a/include/openthread/server.h
+++ b/include/openthread/server.h
@@ -54,11 +54,11 @@
 /**
  * This method provides a full or stable copy of the local Thread Network Data.
  *
- * @param[in]     aInstance    A pointer to an OpenThread instance.
- * @param[in]     aStable      TRUE when copying the stable version, FALSE when copying the full version.
- * @param[out]    aData        A pointer to the data buffer.
- * @param[inout]  aDataLength  On entry, size of the data buffer pointed to by @p aData.
- *                             On exit, number of copied bytes.
+ * @param[in]      aInstance    A pointer to an OpenThread instance.
+ * @param[in]      aStable      TRUE when copying the stable version, FALSE when copying the full version.
+ * @param[out]     aData        A pointer to the data buffer.
+ * @param[in,out]  aDataLength  On entry, size of the data buffer pointed to by @p aData.
+ *                              On exit, number of copied bytes.
  *
  */
 otError otServerGetNetDataLocal(otInstance *aInstance, bool aStable, uint8_t *aData, uint8_t *aDataLength);
@@ -102,10 +102,10 @@
 /**
  * This function gets the next service in the local Network Data.
  *
- * @param[in]     aInstance  A pointer to an OpenThread instance.
- * @param[inout]  aIterator  A pointer to the Network Data iterator context. To get the first service entry
-                             it should be set to OT_NETWORK_DATA_ITERATOR_INIT.
- * @param[out]    aConfig    A pointer to where the service information will be placed.
+ * @param[in]      aInstance  A pointer to an OpenThread instance.
+ * @param[in,out]  aIterator  A pointer to the Network Data iterator context. To get the first service entry
+                              it should be set to OT_NETWORK_DATA_ITERATOR_INIT.
+ * @param[out]     aConfig    A pointer to where the service information will be placed.
  *
  * @retval OT_ERROR_NONE       Successfully found the next service.
  * @retval OT_ERROR_NOT_FOUND  No subsequent service exists in the Thread Network Data.
diff --git a/include/openthread/srp_client.h b/include/openthread/srp_client.h
index ba7801e..72c0361 100644
--- a/include/openthread/srp_client.h
+++ b/include/openthread/srp_client.h
@@ -160,7 +160,7 @@
  *
  * @param[in] aError            The error (see above).
  * @param[in] aHostInfo         A pointer to host info.
- * @param[in] aService          The head of linked-list containing all services (excluding the ones removed). NULL if
+ * @param[in] aServices         The head of linked-list containing all services (excluding the ones removed). NULL if
  *                              the list is empty.
  * @param[in] aRemovedServices  The head of linked-list containing all removed services. NULL if the list is empty.
  * @param[in] aContext          A pointer to an arbitrary context (provided when callback was registered).
@@ -180,9 +180,9 @@
  * This callback is invoked when auto-start mode is enabled and the SRP client is either automatically started or
  * stopped.
  *
- * @param[in] aServerSockAddress   A non-NULL pointer indicates SRP server was started and pointer will give the
- *                                 selected server socket address. A NULL pointer indicates SRP server was stopped.
- * @param[in] aContext             A pointer to an arbitrary context (provided when callback was registered).
+ * @param[in] aServerSockAddr   A non-NULL pointer indicates SRP server was started and pointer will give the
+ *                              selected server socket address. A NULL pointer indicates SRP server was stopped.
+ * @param[in] aContext          A pointer to an arbitrary context (provided when callback was registered).
  *
  */
 typedef void (*otSrpClientAutoStartCallback)(const otSockAddr *aServerSockAddr, void *aContext);
@@ -408,15 +408,15 @@
  * request from an earlier call to `otSrpClientRemoveHostAndServices()` and host info still being in  either
  * `STATE_TO_REMOVE` or `STATE_REMOVING` states).
  *
- * The host IPv6 address array pointed to by @p aAddresses MUST persist and remain unchanged after returning from this
- * function (with `OT_ERROR_NONE`). OpenThread will save the pointer to the array.
+ * The host IPv6 address array pointed to by @p aIp6Addresses MUST persist and remain unchanged after returning from
+ * this function (with `OT_ERROR_NONE`). OpenThread will save the pointer to the array.
  *
  * After a successful call to this function, `otSrpClientCallback` will be called to report the status of the address
  * registration with SRP server.
  *
  * @param[in] aInstance           A pointer to the OpenThread instance.
- * @param[in] aAddresses          A pointer to the an array containing the host IPv6 addresses.
- * @param[in] aNumAddresses       The number of addresses in the @p aAddresses array.
+ * @param[in] aIp6Addresses       A pointer to the an array containing the host IPv6 addresses.
+ * @param[in] aNumAddresses       The number of addresses in the @p aIp6Addresses array.
  *
  * @retval OT_ERROR_NONE            The host IPv6 address list change started successfully. The `otSrpClientCallback`
  *                                  will be called to report the status of registering addresses with server.
diff --git a/include/openthread/tcp.h b/include/openthread/tcp.h
index 46f12c4..6cc11f7 100644
--- a/include/openthread/tcp.h
+++ b/include/openthread/tcp.h
@@ -29,7 +29,7 @@
 /**
  * @file
  * @brief
- *  This file defines the OpenThread TCP API.
+ *   This file defines the OpenThread TCP API.
  *
  */
 
@@ -64,7 +64,7 @@
 {
     struct otLinkedBuffer *mNext;   ///< Pointer to the next linked buffer in the chain, or NULL if it is the end.
     const uint8_t *        mData;   ///< Pointer to data referenced by this linked buffer.
-    uint16_t               mLength; ///< Length of this linked buffer (number of bytes).
+    size_t                 mLength; ///< Length of this linked buffer (number of bytes).
 } otLinkedBuffer;
 
 struct otTcpEndpoint;
@@ -95,47 +95,74 @@
 typedef void (*otTcpSendDone)(otTcpEndpoint *aEndpoint, otLinkedBuffer *aData);
 
 /**
- * This callback informs the application that the first @p aNumBytes in the
- * send buffer have been acknowledged by the connection peer and that their
- * underlying memory can be reclaimed by the application.
+ * This callback informs the application if forward progress has been made in
+ * transferring data from the send buffer to the recipient. This callback is
+ * not necessary for correct TCP operation. Most applications can just rely on
+ * the otTcpSendDone() callback to reclaim linked buffers once the TCP stack is
+ * done using them. The purpose of this callback is to support advanced
+ * applications that benefit from finer-grained information about how the
+ * the connection is making forward progress in transferring data to the
+ * connection peer.
  *
- * This callback is not necessary for correct TCP operation. Most applications
- * can just rely on the otTcpSendDone() callback. If an application wants
- * fine-grained feedback as memory in the send buffer becomes available again
- * (instead of waiting for an entire linked buffer's worth of data becomes
- * available) or some indication as to whether the connection is making forward
- * progress, it can register this callback.
+ * This callback's operation is closely tied to TCP's send buffer. The send
+ * buffer can be understood as having two regions. First, there is the
+ * "in-flight" region at the head (front) of the send buffer. It corresponds
+ * to data which has been sent to the recipient, but is not yet acknowledged.
+ * Second, there is the "backlog" region, which consists of all data in the
+ * send buffer that is not in the "in-flight" region. The "backlog" region
+ * corresponds to data that is queued for sending, but has not yet been sent.
  *
- * @param[in]  aEndpoint  The TCP endpoint for the connection.
- * @param[in]  aNumBytes  The number of bytes newly acknowledged by the connection peer.
+ * The callback is invoked in response to two types of events. First, the
+ * "in-flight" region of the send buffer may shrink (e.g., when the recipient
+ * acknowledges data that we sent earlier). Second, the "backlog" region of the
+ * send buffer may shrink (e.g., new data was sent out). These two conditions
+ * often occur at the same time, in response to an ACK segment from the
+ * connection peer, which is why they are combined in a single callback.
+ *
+ * The TCP stack only uses the @p aInSendBuffer bytes at the tail of the send
+ * buffer; when @p aInSendBuffer decreases by an amount x, it means that x
+ * additional bytes that were formerly at the head of the send buffer are no
+ * longer part of the send buffer and can now be reclaimed (i.e., overwritten)
+ * by the application. Note that the otLinkedBuffer structure itself can only
+ * be reclaimed once all bytes that it references are no longer part of the
+ * send buffer.
+ *
+ * This callback subsumes otTcpSendDone(), in the following sense: applications
+ * can determine when linked buffers can be reclaimed by comparing
+ * @p aInSendBuffer with how many bytes are in each linked buffer. However, we
+ * expect otTcpSendDone(), which directly conveys which otLinkedBuffers can be
+ * reclaimed, to be much simpler to use. If both callbacks are registered and
+ * are triggered by the same event (e.g., the same ACK segment received), then
+ * the otTcpSendDone() callback will be triggered first, followed by this
+ * callback.
+ *
+ * Additionally, this callback provides @p aBacklog, which indicates how many
+ * bytes of data in the send buffer are not yet in flight. For applications
+ * that only want to add data to the send buffer when there is an assurance
+ * that it will be sent out soon, it may be desirable to only send out data
+ * when @p aBacklog is suitably small (0 or close to 0). For example, an
+ * application may use @p aBacklog so that it can react to queue buildup by
+ * dropping or aggregating data to avoid creating a backlog of data.
+ *
+ * After a call to otTcpSendByReference() or otTcpSendByExtension() with a
+ * positive number of bytes, the otTcpForwardProgress() callback is guaranteed
+ * to be called, to indicate when the bytes that were added to the send buffer
+ * are sent out. The call to otTcpForwardProgress() may be made immediately
+ * after the bytes are added to the send buffer (if some of those bytes are
+ * immediately sent out, reducing the backlog), or sometime in the future (once
+ * the connection sends out some or all of the data, reducing the backlog). By
+ * "immediately," we mean that the callback is immediately scheduled for
+ * execution in a tasklet; to avoid reentrancy-related complexity, the
+ * otTcpForwardProgress() callback is never directly called from the
+ * otTcpSendByReference() or otTcpSendByExtension() functions.
+ *
+ * @param[in]  aEndpoint      The TCP endpoint for the connection.
+ * @param[in]  aInSendBuffer  The number of bytes in the send buffer (sum of "in-flight" and "backlog" regions).
+ * @param[in]  aBacklog       The number of bytes that are queued for sending but have not yet been sent (the "backlog"
+ *                            region).
  *
  */
-typedef void (*otTcpBytesAcked)(otTcpEndpoint *aEndpoint, size_t aNumBytes);
-
-/**
- * This callback informs the application that if data is added to the send
- * buffer, some of it will be transmitted immediately without delay, as opposed
- * to being queued for transmission once the peer ACKs some data.
- *
- * After a call to otTcpSendByReference() or otTcpSendByExtension(), the
- * otTcpSendReady() callback is guaranteed to be called, either immediately (if
- * the connection is already ready) or sometime in the future (once the
- * connection becomes ready for more data).
- *
- * This callback is not necessary for correct TCP operation. If more data is
- * added to the send buffer than can be transmitted without delay, it will
- * simply be queued for transmission at a later time. This callback should be
- * used only in cases where some assurance is desired that data added to the
- * send buffer will be sent soon (e.g., TCP won't wait for the recipient to
- * ACK some other data first before sending this data out). For example, you
- * may use this callback if you'd rather have your data be dropped than develop
- * a backlog of data in your send buffer. But for most applications, where this
- * isn't a concern, it's expected that one would not use this callback at all.
- *
- * @param[in]  aEndpoint  The TCP endpoint for the connection.
- *
- */
-typedef void (*otTcpSendReady)(otTcpEndpoint *aEndpoint);
+typedef void (*otTcpForwardProgress)(otTcpEndpoint *aEndpoint, size_t aInSendBuffer, size_t aBacklog);
 
 /**
  * This callback indicates the number of bytes available for consumption from
@@ -226,7 +253,7 @@
 
     otTcpEstablished      mEstablishedCallback;      ///< "Established" callback function
     otTcpSendDone         mSendDoneCallback;         ///< "Send done" callback function
-    otTcpSendReady        mSendReadyCallback;        ///< "Send ready" callback function
+    otTcpForwardProgress  mForwardProgressCallback;  ///< "Forward progress" callback function
     otTcpReceiveAvailable mReceiveAvailableCallback; ///< "Receive available" callback function
     otTcpDisconnected     mDisconnectedCallback;     ///< "Disconnected" callback function
 
@@ -234,6 +261,8 @@
 
     otLinkedBuffer mReceiveLinks[2];
     otSockAddr     mSockAddr;
+
+    uint8_t mPendingCallbacks;
 };
 
 /**
@@ -246,8 +275,7 @@
 
     otTcpEstablished      mEstablishedCallback;      ///< "Established" callback function
     otTcpSendDone         mSendDoneCallback;         ///< "Send done" callback function
-    otTcpBytesAcked       mBytesAckedCallback;       ///< "Bytes acked" callback
-    otTcpSendReady        mSendReadyCallback;        ///< "Send ready" callback function
+    otTcpForwardProgress  mForwardProgressCallback;  ///< "Forward progress" callback function
     otTcpReceiveAvailable mReceiveAvailableCallback; ///< "Receive available" callback function
     otTcpDisconnected     mDisconnectedCallback;     ///< "Disconnected" callback function
 
@@ -297,7 +325,9 @@
  * @retval OT_ERROR_FAILED  Failed to open the TCP endpoint.
  *
  */
-otError otTcpEndpointInitialize(otInstance *aInstance, otTcpEndpoint *aEndpoint, otTcpEndpointInitializeArgs *aArgs);
+otError otTcpEndpointInitialize(otInstance *                       aInstance,
+                                otTcpEndpoint *                    aEndpoint,
+                                const otTcpEndpointInitializeArgs *aArgs);
 
 /**
  * Obtains the otInstance that was associated with @p aEndpoint upon
@@ -514,9 +544,10 @@
  *
  * This immediately makes the TCP endpoint free for use for another connection
  * and empties the send and receive buffers, transferring ownership of any data
- * provided by the application in otTcpSendByReference() calls back to
- * the application. The TCP endpoint's callbacks and memory for the receive
- * buffer remain associated with the TCP endpoint.
+ * provided by the application in otTcpSendByReference() and
+ * otTcpSendByExtension() calls back to the application. The TCP endpoint's
+ * callbacks and memory for the receive buffer remain associated with the
+ * TCP endpoint.
  *
  * @param[in]  aEndpoint  A pointer to the TCP endpoint structure representing the TCP endpoint to abort.
  *
@@ -678,7 +709,9 @@
  * @retval OT_ERROR_FAILED  Failed to open the TCP listener.
  *
  */
-otError otTcpListenerInitialize(otInstance *aInstance, otTcpListener *aListener, otTcpListenerInitializeArgs *aArgs);
+otError otTcpListenerInitialize(otInstance *                       aInstance,
+                                otTcpListener *                    aListener,
+                                const otTcpListenerInitializeArgs *aArgs);
 
 /**
  * Obtains the otInstance that was associated with @p aListener upon
diff --git a/include/openthread/tcp_ext.h b/include/openthread/tcp_ext.h
new file mode 100644
index 0000000..5c0ddc6
--- /dev/null
+++ b/include/openthread/tcp_ext.h
@@ -0,0 +1,216 @@
+/*
+ *  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.
+ */
+
+/**
+ * @file
+ * @brief
+ *   This file defines extensions to the OpenThread TCP API.
+ *
+ */
+
+#ifndef OPENTHREAD_TCP_EXT_H_
+#define OPENTHREAD_TCP_EXT_H_
+
+#include <openthread/tcp.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @addtogroup api-tcp-ext
+ *
+ * @brief
+ *   This module includes easy-to-use abstractions on top of the base TCP API.
+ *
+ * @{
+ *
+ */
+
+/**
+ * This structure represents a circular send buffer for use with a TCP endpoint.
+ *
+ * Using a circular send buffer is optional. Applications can use a TCP
+ * endpoint to send data by managing otLinkedBuffers directly. However, some
+ * applications may find it more convenient to have a circular send buffer;
+ * such applications can call otTcpCircularSendBufferWrite() to "attach" a
+ * circular send buffer to a TCP endpoint and send out data on that TCP
+ * endpoint, relying on the circular send buffer to manage the underlying
+ * otLinkedBuffers.
+ *
+ * otTcpCircularSendBuffer is implemented on top of the otLinkedBuffer-based
+ * API provided by an otTcpEndpoint. Once attached to an otTcpEndpoint, an
+ * otTcpCircularSendBuffer performs all the work of managing otLinkedBuffers
+ * for the connection. This means that, once an otTcpCircularSendBuffer is
+ * attached to an otTcpEndpoint, the application should not call
+ * otTcpSendByReference() or otTcpSendByExtension() on that otTcpEndpoint.
+ * Instead, the application should use otTcpCircularSendBufferWrite() to add
+ * data to the send buffer.
+ *
+ * The otTcpForwardProgress() callback is the intended way for users to learn
+ * when space becomes available in the circular send buffer. On an
+ * otTcpEndpoint to which an otTcpCircularSendBuffer is attached, the
+ * application MUST install an otTcpForwardProgress() callback and call
+ * otTcpCircularSendBufferHandleForwardProgress() on the attached
+ * otTcpCircularSendBuffer at the start of the callback function. It is
+ * recommended that the user NOT install an otTcpSendDone() callback, as all
+ * management of otLinkedBuffers is handled by the circular send buffer.
+ *
+ * The application should not inspect the fields of this structure directly; it
+ * should only interact with it via the TCP Circular Send Buffer API functions
+ * whose signature are provided in this file.
+ *
+ */
+typedef struct otTcpCircularSendBuffer
+{
+    const uint8_t *mDataBuffer;   ///< Pointer to data in the circular send buffer
+    size_t         mCapacity;     ///< Length of the circular send buffer
+    size_t         mStartIndex;   ///< Index of the first valid byte in the send buffer
+    size_t         mCapacityUsed; ///< Number of bytes stored in the send buffer
+
+    otLinkedBuffer mSendLinks[2];
+    uint8_t        mFirstSendLinkIndex;
+} otTcpCircularSendBuffer;
+
+/**
+ * Initializes a TCP circular send buffer.
+ *
+ * @param[in]  aSendBuffer      A pointer to the TCP circular send buffer to initialize.
+ * @param[in]  aDataBuffer      A pointer to memory to use to store data in the TCP circular send buffer.
+ * @param[in]  aCapacity        The capacity, in bytes, of the TCP circular send buffer, which must equal the size of
+ *                              the memory pointed to by @p aDataBuffer .
+ */
+void otTcpCircularSendBufferInitialize(otTcpCircularSendBuffer *aSendBuffer, void *aDataBuffer, size_t aCapacity);
+
+/**
+ * Sends out data on a TCP endpoint, using the provided TCP circular send
+ * buffer to manage buffering.
+ *
+ * Once this function is called, @p aSendBuffer and @p aEndpoint are considered
+ * "attached" to each other. While they are attached, ALL send operations for
+ * @p aEndpoint must be made using @p aSendBuffer and ALL operations on
+ * @p aSendBuffer must be associated with @p aEndpoint .
+ *
+ * The only way to "detach" a TCP circular send buffer and a TCP endpoint is to
+ * wait for the send buffer to become completely empty. This can happen in two
+ * ways: (1) all data in the send buffer is sent and acknowledged in the normal
+ * course of TCP protocol operation, or (2) the connection is terminated.
+ *
+ * The recommended usage pattern is to use a single TCP circular send buffer
+ * with a TCP endpoint, and to send data on that TCP endpoint only via its
+ * associated TCP circular buffer. This recommended usage pattern sidesteps the
+ * issues described above by always using a TCP endpoint and TCP circular send
+ * buffer together.
+ *
+ * If the circular send buffer reaches capacity, only a prefix of the provided
+ * data is copied into the circular send buffer.
+ *
+ * @param[in]   aEndpoint    The TCP endpoint on which to send out data.
+ * @param[in]   aSendBuffer  The TCP circular send buffer into which to copy data.
+ * @param[in]   aData        A pointer to data to copy into the TCP circular send buffer.
+ * @param[in]   aLength      The length of the data pointed to by @p aData to copy into the TCP circular send buffer.
+ * @param[out]  aWritten     Populated with the amount of data copied into the send buffer, which might be less than
+ *                           @p aLength if the send buffer reaches capacity.
+ *
+ * @returns OT_ERROR_NONE    Successfully copied data into the send buffer and sent it on the TCP endpoint.
+ * @returns OT_ERROR_FAILED  Failed to send out data on the TCP endpoint.
+ */
+otError otTcpCircularSendBufferWrite(otTcpEndpoint *          aEndpoint,
+                                     otTcpCircularSendBuffer *aSendBuffer,
+                                     void *                   aData,
+                                     size_t                   aLength,
+                                     size_t *                 aWritten);
+
+/**
+ * Performs circular-send-buffer-specific handling in the otTcpForwardProgress
+ * callback.
+ *
+ * The application is expected to install an otTcpForwardProgress() callback on
+ * the otTcpEndpoint, and call this function at the start of the callback
+ * function for circular-send-buffer-specific processing.
+ *
+ * In the callback function, the application can determine the amount of free
+ * space in the circular send buffer by calling
+ * otTcpCircularSendBufferFreeSpace(), or by comparing @p aInSendBuffer with
+ * the send buffer's capacity, chosen by the user when calling
+ * otTcpCircularSendBufferInitialize().
+ *
+ * @param[in]  aSendBuffer    A pointer to the TCP circular send buffer for the endpoint for which
+ *                            otTcpForwardProgress() was invoked.
+ * @param[in]  aInSendBuffer  Value of @p aInSendBuffer passed to the otTcpForwardProgress() callback.
+ */
+void otTcpCircularSendBufferHandleForwardProgress(otTcpCircularSendBuffer *aSendBuffer, size_t aInSendBuffer);
+
+/**
+ * Returns the amount of free space in the TCP circular send buffer.
+ *
+ * This operation will always succeed.
+ *
+ * @param[in]  aSendBuffer  A pointer to the TCP circular send buffer whose amount of free space to return.
+ *
+ * @return The amount of free space in the send buffer.
+ */
+size_t otTcpCircularSendBufferFreeSpace(otTcpCircularSendBuffer *aSendBuffer);
+
+/**
+ * Forcibly discards all data in the circular send buffer.
+ *
+ * The application is expected to call this function when a TCP connection is
+ * terminated unceremoniously (e.g., if the application calls
+ * otTcpEndpointAbort() or is informed of a reset connection via the
+ * otTcpConnectionLost() callback).
+ *
+ * Calling this function on a nonempty TCP circular send buffer attached to a
+ * TCP endpoint results in undefined behavior.
+ *
+ * @param[in]  aSendBuffer  The TCP circular send buffer whose data to discard.
+ */
+void otTcpCircularSendBufferForceDiscardAll(otTcpCircularSendBuffer *aSendBuffer);
+
+/**
+ * Deinitializes a TCP circular send buffer, detaching it if attached.
+ *
+ * If the TCP circular send buffer is not empty, then this operation will fail.
+ *
+ * @param[in]  aSendBuffer  The TCP circular send buffer to deinitialize.
+ *
+ * @retval OT_ERROR_NONE    Successfully deinitialize the TCP circular send buffer.
+ * @retval OT_ERROR_BUSY    Circular buffer contains data and cannot be deinitialized.
+ */
+otError otTcpCircularSendBufferDeinitialize(otTcpCircularSendBuffer *aSendBuffer);
+
+/**
+ * @}
+ *
+ */
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // OPENTHREAD_TCP_EXT_H_
diff --git a/include/openthread/thread.h b/include/openthread/thread.h
index b23bd66..fe82900 100644
--- a/include/openthread/thread.h
+++ b/include/openthread/thread.h
@@ -714,10 +714,10 @@
  * This function gets the next neighbor information. It is used to go through the entries of
  * the neighbor table.
  *
- * @param[in]     aInstance  A pointer to an OpenThread instance.
- * @param[inout]  aIterator  A pointer to the iterator context. To get the first neighbor entry
-                             it should be set to OT_NEIGHBOR_INFO_ITERATOR_INIT.
- * @param[out]    aInfo      A pointer to the neighbor information.
+ * @param[in]      aInstance  A pointer to an OpenThread instance.
+ * @param[in,out]  aIterator  A pointer to the iterator context. To get the first neighbor entry
+                              it should be set to OT_NEIGHBOR_INFO_ITERATOR_INIT.
+ * @param[out]     aInfo      A pointer to the neighbor information.
  *
  * @retval OT_ERROR_NONE         Successfully found the next neighbor entry in table.
  * @retval OT_ERROR_NOT_FOUND     No subsequent neighbor entry exists in the table.
@@ -872,7 +872,7 @@
 /**
  * This function pointer is called every time an MLE Parent Response message is received.
  *
- * @param[in]  aStats    pointer to a location on stack holding the stats data.
+ * @param[in]  aInfo     A pointer to a location on stack holding the stats data.
  * @param[in]  aContext  A pointer to callback client-specific context.
  *
  */
diff --git a/include/openthread/thread_ftd.h b/include/openthread/thread_ftd.h
index c54ea74..ea052de 100644
--- a/include/openthread/thread_ftd.h
+++ b/include/openthread/thread_ftd.h
@@ -484,12 +484,12 @@
 /**
  * This function gets the next IPv6 address (using an iterator) for a given child.
  *
- * @param[in]     aInstance    A pointer to an OpenThread instance.
- * @param[in]     aChildIndex  The child index.
- * @param[inout]  aIterator    A pointer to the iterator. On success the iterator will be updated to point to next
- *                             entry in the list. To get the first IPv6 address the iterator should be set to
- *                             OT_CHILD_IP6_ADDRESS_ITERATOR_INIT.
- * @param[out]    aAddress     A pointer to an IPv6 address where the child's next address is placed (on success).
+ * @param[in]      aInstance    A pointer to an OpenThread instance.
+ * @param[in]      aChildIndex  The child index.
+ * @param[in,out]  aIterator    A pointer to the iterator. On success the iterator will be updated to point to next
+ *                              entry in the list. To get the first IPv6 address the iterator should be set to
+ *                              OT_CHILD_IP6_ADDRESS_ITERATOR_INIT.
+ * @param[out]     aAddress     A pointer to an IPv6 address where the child's next address is placed (on success).
  *
  * @retval OT_ERROR_NONE          Successfully found the next IPv6 address (@p aAddress was successfully updated).
  * @retval OT_ERROR_NOT_FOUND     The child has no subsequent IPv6 address entry.
@@ -540,11 +540,11 @@
 /**
  * This function gets the next EID cache entry (using an iterator).
  *
- * @param[in]    aInstance   A pointer to an OpenThread instance.
- * @param[out]   aEntryInfo  A pointer to where the EID cache entry information is placed.
- * @param[inout] aIterator   A pointer to an iterator. It will be updated to point to next entry on success. To get the
- *                           first entry, initialize the iterator by setting all its fields to zero (e.g., `memset` the
- *                           the iterator structure to zero).
+ * @param[in]     aInstance   A pointer to an OpenThread instance.
+ * @param[out]    aEntryInfo  A pointer to where the EID cache entry information is placed.
+ * @param[in,out] aIterator   A pointer to an iterator. It will be updated to point to next entry on success. To get
+ *                            the first entry, initialize the iterator by setting all its fields to zero
+ *                            (e.g., `memset` the iterator structure to zero).
  *
  * @retval OT_ERROR_NONE          Successfully populated @p aEntryInfo for next EID cache entry.
  * @retval OT_ERROR_NOT_FOUND     No more entries in the address cache table.
@@ -605,7 +605,7 @@
  * non-volatile memory.
  *
  * @param[in]  aInstance   A pointer to an OpenThread instance.
- * @param[in]  aPskcRef    Key Reference to the new Thread PSKc.
+ * @param[in]  aKeyRef     Key Reference to the new Thread PSKc.
  *
  * @retval OT_ERROR_NONE           Successfully set the Thread PSKc.
  * @retval OT_ERROR_INVALID_STATE  Thread protocols are enabled.
diff --git a/openthread_upstream_version.gni b/openthread_upstream_version.gni
index 9df7b4c..e340913 100644
--- a/openthread_upstream_version.gni
+++ b/openthread_upstream_version.gni
@@ -1,3 +1,3 @@
 # This file is added to support soft-transition in Fuchsia.
 
-openthread_upstream_version = "74f5ab5c9f434c4ffe44c580be40b7ff14e7f0e9"
+openthread_upstream_version = "645fbcd400d2e6c2de641bccb0335e3eaaed1f36"
diff --git a/script/check-android-build b/script/check-android-build
index cf20557..6647b9f 100755
--- a/script/check-android-build
+++ b/script/check-android-build
@@ -51,12 +51,12 @@
 
     datetime="$(date)"
     cat >openthread-config-datetime.h <<EOF
-#include <openthread-config-android.h>
 #define OPENTHREAD_BUILD_DATETIME "$datetime"
 EOF
 
     OPENTHREAD_PROJECT_CFLAGS="-I$PWD -DOPENTHREAD_CONFIG_FILE=\\\"openthread-config-datetime.h\\\" \
-        -DOPENTHREAD_PROJECT_CORE_CONFIG_FILE=\\\"openthread-core-posix-config.h\\\"" \
+        -DOPENTHREAD_PROJECT_CORE_CONFIG_FILE=\\\"openthread-core-posix-config.h\\\" \
+        -std=c99" \
         make showcommands ot-cli
     grep "$datetime" -ao "out/target/product/generic/system/bin/ot-cli"
     make clean-ot-cli
diff --git a/script/check-simulation-build-autotools b/script/check-simulation-build-autotools
index ac55070..6b16957 100755
--- a/script/check-simulation-build-autotools
+++ b/script/check-simulation-build-autotools
@@ -99,7 +99,7 @@
     )
 
     # Build Thread 1.1 with full features and no log
-    export CPPFLAGS="${options[*]} -DOPENTHREAD_CONFIG_LOG_LEVEL=OT_LOG_LEVEL_NONE"
+    export CPPFLAGS="${options[*]} -DOPENTHREAD_CONFIG_LOG_OUTPUT=OT_LOG_OUTPUT_NONE"
     reset_source
     make -f examples/Makefile-simulation THREAD_VERSION=1.1
 
@@ -109,7 +109,7 @@
     make -f examples/Makefile-simulation THREAD_VERSION=1.1 FULL_LOGS=1
 
     # Build Thread 1.2 with full features and logs
-    export CPPFLAGS="${options[*]} ${options_1_2[*]} -DOPENTHREAD_CONFIG_LOG_LEVEL=OT_LOG_LEVEL_NONE"
+    export CPPFLAGS="${options[*]} ${options_1_2[*]} -DOPENTHREAD_CONFIG_LOG_OUTPUT=OT_LOG_OUTPUT_NONE"
     reset_source
     make -f examples/Makefile-simulation THREAD_VERSION=1.2
 
diff --git a/script/check-simulation-build-cmake b/script/check-simulation-build-cmake
index 0975ed2..e9a3b45 100755
--- a/script/check-simulation-build-cmake
+++ b/script/check-simulation-build-cmake
@@ -89,6 +89,10 @@
     # Build Thread 1.2 with full features and OT_ASSERT=OFF
     reset_source
     "$(dirname "$0")"/cmake-build simulation "${options[@]}" -DOT_DUA=ON -DOT_ASSERT=OFF
+
+    # Build with RAM settings
+    reset_source
+    "$(dirname "$0")"/cmake-build simulation -DOT_SETTINGS_RAM=ON
 }
 
 build_toranj()
diff --git a/src/cli/README.md b/src/cli/README.md
index 1085b2b..1dd92e4 100644
--- a/src/cli/README.md
+++ b/src/cli/README.md
@@ -107,6 +107,7 @@
 - [sntp](#sntp-query-sntp-server-ip-sntp-server-port)
 - [state](#state)
 - [srp](README_SRP.md)
+- [tcp](README_TCP.md)
 - [thread](#thread-start)
 - [trel](#trel)
 - [tvcheck](#tvcheck-enable)
@@ -324,17 +325,6 @@
 Done
 ```
 
-### bbr skipseqnuminc
-
-Skip increase of Sequence Number when updating the local BBR Dataset from the Network Data.
-
-Only for testing/reference device.
-
-```bash
-> bbr skipseqnuminc
-Done
-```
-
 ### ba
 
 Show current Border Agent information.
@@ -409,19 +399,25 @@
 
 Show the current message buffer information.
 
+- The `total` shows total number of message buffers in pool.
+- The `free` shows the number of free message buffers.
+- This is then followed by info about different queues used by OpenThread stack, each line representing info about a queue.
+  - The first number shows number messages in the queue.
+  - The second number shows number of buffers used by all messages in the queue.
+  - The third number shows total number of bytes of all messages in the queue.
+
 ```bash
 > bufferinfo
 total: 40
 free: 40
-6lo send: 0 0
-6lo reas: 0 0
-ip6: 0 0
-mpl: 0 0
-mle: 0 0
-arp: 0 0
-coap: 0 0
-coap secure: 0 0
-application coap: 0 0
+6lo send: 0 0 0
+6lo reas: 0 0 0
+ip6: 0 0 0
+mpl: 0 0 0
+mle: 0 0 0
+coap: 0 0 0
+coap secure: 0 0 0
+application coap: 0 0 0
 Done
 ```
 
@@ -2181,6 +2177,15 @@
 Done
 ```
 
+### prefix meshlocal <prefix>
+
+Set the mesh local prefix.
+
+```bash
+> prefix meshlocal fdde:ad00:beef:0::/64
+Done
+```
+
 ### prefix remove \<prefix\>
 
 Invalidate a prefix in the Network Data.
@@ -2491,9 +2496,9 @@
 
 ```bash
 > scan
-| J | Network Name     | Extended PAN     | PAN  | MAC Address      | Ch | dBm | LQI |
-+---+------------------+------------------+------+------------------+----+-----+-----+
-| 0 | OpenThread       | dead00beef00cafe | ffff | f1d92a82c8d8fe43 | 11 | -20 |   0 |
+| PAN  | MAC Address      | Ch | dBm | LQI |
++------+------------------+----+-----+-----+
+| ffff | f1d92a82c8d8fe43 | 11 | -20 |   0 |
 Done
 ```
 
diff --git a/src/cli/README_COMMISSIONER.md b/src/cli/README_COMMISSIONER.md
index 7cd7079..013b416 100644
--- a/src/cli/README_COMMISSIONER.md
+++ b/src/cli/README_COMMISSIONER.md
@@ -11,6 +11,7 @@
 - [energy](#energy)
 - [joiner add](#joiner-add)
 - [joiner remove](#joiner-remove)
+- [joiner table](#joiner-table)
 - [mgmtget](#mgmtget)
 - [mgmtset](#mgmtset)
 - [panid](#panid)
@@ -120,6 +121,22 @@
 Done
 ```
 
+### joiner table
+
+Usage: `commissioner joiner table`
+
+List all Joiner entries.
+
+```bash
+> commissioner joiner table
+| ID                    | PSKd                             | Expiration |
++-----------------------+----------------------------------+------------+
+|                     * |                           J01NME |      81015 |
+|      d45e64fa83f81cf7 |                           J01NME |     101204 |
+| 0x0000000000000abc/12 |                           J01NME |     114360 |
+Done
+```
+
 ### mgmtget
 
 Usage: `commissioner mgmtget [locator] [sessionid] [steeringdata] [joinerudpport] [-x <TLV Types>]`
@@ -183,6 +200,27 @@
 Done
 ```
 
+### id
+
+Usage: `commissioner id`
+
+Get the commissioner id.
+
+```bash
+> commissioner id
+OpenThread Commissioner
+Done
+```
+
+### id \<name\>
+
+Set the commissioner id.
+
+```bash
+> commissioner id "Custom Commissioner Id"
+Done
+```
+
 ### start
 
 Usage: `commissioner start`
diff --git a/src/cli/README_COMMISSIONING.md b/src/cli/README_COMMISSIONING.md
index 237b65d..8262a8c 100644
--- a/src/cli/README_COMMISSIONING.md
+++ b/src/cli/README_COMMISSIONING.md
@@ -21,7 +21,7 @@
    Network Name: OpenThread-8f28
    PAN ID: 0x8f28
    PSKc: c23a76e98f1a6483639b1ac1271e2e27
-   Security Policy: 0, onrcb
+   Security Policy: 0, onrc
    Done
    ```
 
@@ -107,7 +107,7 @@
    Network Name: OpenThread-8f28
    PAN ID: 0x8f28
    PSKc: c23a76e98f1a6483639b1ac1271e2e27
-   Security Policy: 0, onrcb
+   Security Policy: 0, onrc
    Done
    ```
 
diff --git a/src/cli/README_DATASET.md b/src/cli/README_DATASET.md
index a9f678f..1043bba 100644
--- a/src/cli/README_DATASET.md
+++ b/src/cli/README_DATASET.md
@@ -44,7 +44,7 @@
    Network Name: OpenThread-8f28
    PAN ID: 0x8f28
    PSKc: c23a76e98f1a6483639b1ac1271e2e27
-   Security Policy: 0, onrcb
+   Security Policy: 0, onrc
    Done
    ```
 
@@ -103,7 +103,7 @@
    Network Name: OpenThread-8f28
    PAN ID: 0x8f28
    PSKc: c23a76e98f1a6483639b1ac1271e2e27
-   Security Policy: 0, onrcb
+   Security Policy: 0, onrc
    Done
    ```
 
@@ -180,7 +180,7 @@
 Network Name: OpenThread-8f28
 PAN ID: 0x8f28
 PSKc: c23a76e98f1a6483639b1ac1271e2e27
-Security Policy: 0, onrcb
+Security Policy: 0, onrc
 Done
 ```
 
@@ -359,7 +359,7 @@
 Send MGMT_ACTIVE_SET or MGMT_PENDING_SET.
 
 ```bash
-> dataset mgmtsetcommand active activetimestamp 123 securitypolicy 1 onrcb
+> dataset mgmtsetcommand active activetimestamp 123 securitypolicy 1 onrc
 Done
 ```
 
@@ -441,7 +441,7 @@
 Network Name: OpenThread-8f28
 PAN ID: 0x8f28
 PSKc: c23a76e98f1a6483639b1ac1271e2e27
-Security Policy: 0, onrcb
+Security Policy: 0, onrc
 Done
 ```
 
@@ -497,13 +497,13 @@
 
 ### securitypolicy
 
-Usage: `dataset securitypolicy [<rotationtime> [onrcbCepR]]`
+Usage: `dataset securitypolicy [<rotationtime> [onrcCepR]]`
 
 Get security policy.
 
 ```bash
 > dataset securitypolicy
-672 onrcb
+672 onrc
 Done
 ```
 
@@ -513,14 +513,13 @@
 - n: Native Commissioning using PSKc is allowed.
 - r: Thread 1.x Routers are enabled.
 - c: External Commissioner authentication is allowed using PSKc.
-- b: Thread 1.x Beacons are enabled.
 - C: Thread 1.2 Commercial Commissioning is enabled.
 - e: Thread 1.2 Autonomous Enrollment is enabled.
 - p: Thread 1.2 Network Key Provisioning is enabled.
 - R: Non-CCM routers are allowed in Thread 1.2 CCM networks.
 
 ```bash
-> dataset securitypolicy 672 onrcb
+> dataset securitypolicy 672 onrc
 Done
 ```
 
diff --git a/src/cli/README_NETDATA.md b/src/cli/README_NETDATA.md
index b119de1..68c3b43 100644
--- a/src/cli/README_NETDATA.md
+++ b/src/cli/README_NETDATA.md
@@ -27,7 +27,7 @@
    Network Name: OpenThread-8f28
    PAN ID: 0x8f28
    PSKc: c23a76e98f1a6483639b1ac1271e2e27
-   Security Policy: 0, onrcb
+   Security Policy: 0, onrc
    Done
    ```
 
diff --git a/src/cli/README_SRP.md b/src/cli/README_SRP.md
index 645f44f..a0ab5ba 100644
--- a/src/cli/README_SRP.md
+++ b/src/cli/README_SRP.md
@@ -25,7 +25,7 @@
 Network Name: OpenThread-f7af
 PAN ID: 0xf7af
 PSKc: b658e40f174e3a11be149b302ef07a0f
-Security Policy: 672, onrcb
+Security Policy: 672, onrc
 Done
 > dataset commit active
 Done
diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp
index 74713cd..ef5376f 100644
--- a/src/cli/cli.cpp
+++ b/src/cli/cli.cpp
@@ -85,7 +85,6 @@
 #include <openthread/trel.h>
 #endif
 
-#include "common/logging.hpp"
 #include "common/new.hpp"
 #include "common/string.hpp"
 #include "mac/channel_mask.hpp"
@@ -93,8 +92,6 @@
 namespace ot {
 namespace Cli {
 
-constexpr Interpreter::Command Interpreter::sCommands[];
-
 Interpreter *Interpreter::sInterpreter = nullptr;
 static OT_DEFINE_ALIGNED_VAR(sInterpreterRaw, sizeof(Interpreter), uint64_t);
 
@@ -200,7 +197,7 @@
 }
 
 #if OPENTHREAD_CONFIG_DIAG_ENABLE
-otError Interpreter::ProcessDiag(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("diag")>(Arg aArgs[])
 {
     otError error;
     char *  args[kMaxArgs];
@@ -217,24 +214,7 @@
 }
 #endif
 
-otError Interpreter::ProcessHelp(Arg aArgs[])
-{
-    OT_UNUSED_VARIABLE(aArgs);
-
-    for (const Command &command : sCommands)
-    {
-        OutputLine(command.mName);
-    }
-
-    for (uint8_t i = 0; i < mUserCommandsLength; i++)
-    {
-        OutputLine("%s", mUserCommands[i].mName);
-    }
-
-    return OT_ERROR_NONE;
-}
-
-otError Interpreter::ProcessVersion(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("version")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -254,7 +234,7 @@
     return error;
 }
 
-otError Interpreter::ProcessReset(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("reset")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
 
@@ -265,9 +245,8 @@
 
 void Interpreter::ProcessLine(char *aBuf)
 {
-    Arg            args[kMaxArgs + 1];
-    const Command *command;
-    otError        error = OT_ERROR_NONE;
+    Arg     args[kMaxArgs + 1];
+    otError error = OT_ERROR_NONE;
 
     OT_ASSERT(aBuf != nullptr);
 
@@ -283,23 +262,14 @@
     LogInput(args);
 
 #if OPENTHREAD_CONFIG_DIAG_ENABLE
-    if (otDiagIsEnabled(GetInstancePtr()) && (args[0] != "diag"))
+    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
 
-    command = BinarySearch::Find(args[0].GetCString(), sCommands);
-
-    if (command != nullptr)
-    {
-        error = (this->*command->mHandler)(args + 1);
-    }
-    else
-    {
-        error = ProcessUserCommands(args);
-    }
+    error = ProcessCommand(args);
 
 exit:
     if ((error != OT_ERROR_NONE) || !args[0].IsEmpty())
@@ -432,14 +402,14 @@
 #endif // OPENTHREAD_CONFIG_PING_SENDER_ENABLE
 
 #if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
-otError Interpreter::ProcessHistory(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("history")>(Arg aArgs[])
 {
     return mHistory.Process(aArgs);
 }
 #endif
 
 #if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
-otError Interpreter::ProcessBorderAgent(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("ba")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -471,7 +441,7 @@
 #endif // OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
 
 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
-otError Interpreter::ProcessBorderRouting(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("br")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
     bool    enable;
@@ -514,7 +484,7 @@
 #endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
 
 #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
-otError Interpreter::ProcessBackboneRouter(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("bbr")>(Arg aArgs[])
 {
     otError                error = OT_ERROR_INVALID_COMMAND;
     otBackboneRouterConfig config;
@@ -573,13 +543,6 @@
             }
 #endif
         }
-#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
-        else if (aArgs[0] == "skipseqnuminc")
-        {
-            otBackboneRouterConfigSkipSeqNumIncrease(GetInstancePtr(), true);
-            ExitNow(error = OT_ERROR_NONE);
-        }
-#endif
         SuccessOrExit(error = ProcessBackboneRouterLocal(aArgs));
     }
 
@@ -730,7 +693,7 @@
 }
 #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
 
-otError Interpreter::ProcessDomainName(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("domainname")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -748,7 +711,7 @@
 }
 
 #if OPENTHREAD_CONFIG_DUA_ENABLE
-otError Interpreter::ProcessDua(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("dua")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -787,27 +750,25 @@
 
 #endif // (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
 
-otError Interpreter::ProcessBufferInfo(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("bufferinfo")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
 
     struct BufferInfoName
     {
-        const uint16_t otBufferInfo::*mNumMessagesPtr;
-        const uint16_t otBufferInfo::*mNumBuffersPtr;
-        const char *                  mName;
+        const otMessageQueueInfo otBufferInfo::*mQueuePtr;
+        const char *                            mName;
     };
 
     static const BufferInfoName kBufferInfoNames[] = {
-        {&otBufferInfo::m6loSendMessages, &otBufferInfo::m6loSendBuffers, "6lo send"},
-        {&otBufferInfo::m6loReassemblyMessages, &otBufferInfo::m6loReassemblyBuffers, "6lo reas"},
-        {&otBufferInfo::mIp6Messages, &otBufferInfo::mIp6Buffers, "ip6"},
-        {&otBufferInfo::mMplMessages, &otBufferInfo::mMplBuffers, "mpl"},
-        {&otBufferInfo::mMleMessages, &otBufferInfo::mMleBuffers, "mle"},
-        {&otBufferInfo::mArpMessages, &otBufferInfo::mArpBuffers, "arp"},
-        {&otBufferInfo::mCoapMessages, &otBufferInfo::mCoapBuffers, "coap"},
-        {&otBufferInfo::mCoapSecureMessages, &otBufferInfo::mCoapSecureBuffers, "coap secure"},
-        {&otBufferInfo::mApplicationCoapMessages, &otBufferInfo::mApplicationCoapBuffers, "application coap"},
+        {&otBufferInfo::m6loSendQueue, "6lo send"},
+        {&otBufferInfo::m6loReassemblyQueue, "6lo reas"},
+        {&otBufferInfo::mIp6Queue, "ip6"},
+        {&otBufferInfo::mMplQueue, "mpl"},
+        {&otBufferInfo::mMleQueue, "mle"},
+        {&otBufferInfo::mCoapQueue, "coap"},
+        {&otBufferInfo::mCoapSecureQueue, "coap secure"},
+        {&otBufferInfo::mApplicationCoapQueue, "application coap"},
     };
 
     otBufferInfo bufferInfo;
@@ -819,13 +780,14 @@
 
     for (const BufferInfoName &info : kBufferInfoNames)
     {
-        OutputLine("%s: %d %d", info.mName, bufferInfo.*info.mNumMessagesPtr, bufferInfo.*info.mNumBuffersPtr);
+        OutputLine("%s: %u %u %u", info.mName, (bufferInfo.*info.mQueuePtr).mNumMessages,
+                   (bufferInfo.*info.mQueuePtr).mNumBuffers, (bufferInfo.*info.mQueuePtr).mTotalBytes);
     }
 
     return OT_ERROR_NONE;
 }
 
-otError Interpreter::ProcessCcaThreshold(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("ccathreshold")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
     int8_t  cca;
@@ -846,7 +808,7 @@
 }
 
 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
-otError Interpreter::ProcessCcm(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("ccm")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
     bool    enable;
@@ -860,7 +822,7 @@
     return error;
 }
 
-otError Interpreter::ProcessThreadVersionCheck(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("tvcheck")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
     bool    enable;
@@ -876,7 +838,7 @@
 
 #endif
 
-otError Interpreter::ProcessChannel(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("channel")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -1013,7 +975,7 @@
 }
 
 #if OPENTHREAD_FTD
-otError Interpreter::ProcessChild(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("child")>(Arg aArgs[])
 {
     otError          error = OT_ERROR_NONE;
     otChildInfo      childInfo;
@@ -1101,7 +1063,7 @@
     return error;
 }
 
-otError Interpreter::ProcessChildIp(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("childip")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -1147,14 +1109,14 @@
     return error;
 }
 
-otError Interpreter::ProcessChildMax(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("childmax")>(Arg aArgs[])
 {
     return ProcessGetSet(aArgs, otThreadGetMaxAllowedChildren, otThreadSetMaxAllowedChildren);
 }
 #endif // OPENTHREAD_FTD
 
 #if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE
-otError Interpreter::ProcessChildSupervision(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("childsupervision")>(Arg aArgs[])
 {
     otError error = OT_ERROR_INVALID_ARGS;
 
@@ -1173,27 +1135,27 @@
 }
 #endif // OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE
 
-otError Interpreter::ProcessChildTimeout(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("childtimeout")>(Arg aArgs[])
 {
     return ProcessGetSet(aArgs, otThreadGetChildTimeout, otThreadSetChildTimeout);
 }
 
 #if OPENTHREAD_CONFIG_COAP_API_ENABLE
-otError Interpreter::ProcessCoap(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("coap")>(Arg aArgs[])
 {
     return mCoap.Process(aArgs);
 }
 #endif
 
 #if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
-otError Interpreter::ProcessCoapSecure(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("coaps")>(Arg aArgs[])
 {
     return mCoapSecure.Process(aArgs);
 }
 #endif
 
 #if OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
-otError Interpreter::ProcessCoexMetrics(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("coex")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
     bool    enable;
@@ -1268,13 +1230,13 @@
 #endif // OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
 
 #if OPENTHREAD_FTD
-otError Interpreter::ProcessContextIdReuseDelay(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("contextreusedelay")>(Arg aArgs[])
 {
     return ProcessGetSet(aArgs, otThreadGetContextIdReuseDelay, otThreadSetContextIdReuseDelay);
 }
 #endif
 
-otError Interpreter::ProcessCounters(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("counters")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -1434,7 +1396,7 @@
 }
 
 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
-otError Interpreter::ProcessCsl(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("csl")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -1467,7 +1429,7 @@
 #endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
 
 #if OPENTHREAD_FTD
-otError Interpreter::ProcessDelayTimerMin(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("delaytimermin")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -1491,7 +1453,7 @@
 }
 #endif
 
-otError Interpreter::ProcessDiscover(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("discover")>(Arg aArgs[])
 {
     otError  error        = OT_ERROR_NONE;
     uint32_t scanChannels = 0;
@@ -1508,7 +1470,15 @@
     SuccessOrExit(error = otThreadDiscover(GetInstancePtr(), scanChannels, OT_PANID_BROADCAST, false, false,
                                            &Interpreter::HandleActiveScanResult, this));
 
-    OutputScanTableHeader();
+    static const char *const kScanTableTitles[] = {
+        "Network Name", "Extended PAN", "PAN", "MAC Address", "Ch", "dBm", "LQI",
+    };
+
+    static const uint8_t kScanTableColumnWidths[] = {
+        18, 18, 6, 18, 4, 5, 5,
+    };
+
+    OutputTableHeader(kScanTableTitles, kScanTableColumnWidths);
 
     error = OT_ERROR_PENDING;
 
@@ -1516,7 +1486,7 @@
     return error;
 }
 
-otError Interpreter::ProcessDns(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("dns")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
 
@@ -1574,6 +1544,16 @@
                                                         &Interpreter::HandleDnsAddressResponse, this, config));
         error = OT_ERROR_PENDING;
     }
+#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE
+    else if (aArgs[0] == "resolve4")
+    {
+        VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
+        SuccessOrExit(error = GetDnsConfig(aArgs + 2, config));
+        SuccessOrExit(error = otDnsClientResolveIp4Address(GetInstancePtr(), aArgs[1].GetCString(),
+                                                           &Interpreter::HandleDnsAddressResponse, this, config));
+        error = OT_ERROR_PENDING;
+    }
+#endif
 #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
     else if (aArgs[0] == "browse")
     {
@@ -1801,7 +1781,7 @@
     OutputLine("");
 }
 
-otError Interpreter::ProcessEidCache(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("eidcache")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
 
@@ -1821,7 +1801,7 @@
 }
 #endif
 
-otError Interpreter::ProcessEui64(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("eui64")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
 
@@ -1837,7 +1817,7 @@
     return error;
 }
 
-otError Interpreter::ProcessExtAddress(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("extaddr")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -1857,7 +1837,7 @@
     return error;
 }
 
-otError Interpreter::ProcessLog(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("log")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -1896,7 +1876,7 @@
     return error;
 }
 
-otError Interpreter::ProcessExtPanId(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("extpanid")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -1916,7 +1896,7 @@
     return error;
 }
 
-otError Interpreter::ProcessFactoryReset(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("factoryreset")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
 
@@ -1926,7 +1906,7 @@
 }
 
 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
-otError Interpreter::ProcessFake(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("fake")>(Arg aArgs[])
 {
     otError error = OT_ERROR_INVALID_COMMAND;
 
@@ -1960,7 +1940,7 @@
 }
 #endif
 
-otError Interpreter::ProcessFem(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("fem")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -1997,7 +1977,7 @@
     return error;
 }
 
-otError Interpreter::ProcessIfconfig(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("ifconfig")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -2046,7 +2026,7 @@
     return Stringify(aOrigin, kOriginStrings);
 }
 
-otError Interpreter::ProcessIpAddr(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("ipaddr")>(Arg aArgs[])
 {
     otError error   = OT_ERROR_NONE;
     bool    verbose = false;
@@ -2113,7 +2093,7 @@
     return error;
 }
 
-otError Interpreter::ProcessIpMulticastAddr(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("ipmaddr")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -2127,12 +2107,12 @@
     }
     else if (aArgs[0] == "add")
     {
+        otIp6Address address;
+
         aArgs++;
 
         do
         {
-            otIp6Address address;
-
             SuccessOrExit(error = aArgs->ParseAsIp6Address(address));
             SuccessOrExit(error = otIp6SubscribeMulticastAddress(GetInstancePtr(), &address));
         }
@@ -2180,7 +2160,7 @@
     return error;
 }
 
-otError Interpreter::ProcessKeySequence(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("keysequence")>(Arg aArgs[])
 {
     otError error = OT_ERROR_INVALID_ARGS;
 
@@ -2196,7 +2176,7 @@
     return error;
 }
 
-otError Interpreter::ProcessLeaderData(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("leaderdata")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
 
@@ -2216,7 +2196,7 @@
 }
 
 #if OPENTHREAD_FTD
-otError Interpreter::ProcessPartitionId(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("partitionid")>(Arg aArgs[])
 {
     otError error = OT_ERROR_INVALID_COMMAND;
 
@@ -2235,7 +2215,7 @@
     return error;
 }
 
-otError Interpreter::ProcessLeaderWeight(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("leaderweight")>(Arg aArgs[])
 {
     return ProcessGetSet(aArgs, otThreadGetLocalLeaderWeight, otThreadSetLocalLeaderWeight);
 }
@@ -2358,13 +2338,36 @@
     return str;
 }
 
-otError Interpreter::ProcessLinkMetrics(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("linkmetrics")>(Arg aArgs[])
 {
     otError error = OT_ERROR_INVALID_COMMAND;
 
     if (aArgs[0] == "query")
     {
-        error = ProcessLinkMetricsQuery(aArgs + 1);
+        otIp6Address  address;
+        otLinkMetrics linkMetrics;
+
+        SuccessOrExit(error = aArgs[1].ParseAsIp6Address(address));
+
+        if (aArgs[2] == "single")
+        {
+            VerifyOrExit(!aArgs[3].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
+            SuccessOrExit(error = ParseLinkMetricsFlags(linkMetrics, aArgs[3]));
+            error = otLinkMetricsQuery(GetInstancePtr(), &address, /* aSeriesId */ 0, &linkMetrics,
+                                       &Interpreter::HandleLinkMetricsReport, this);
+        }
+        else if (aArgs[2] == "forward")
+        {
+            uint8_t seriesId;
+
+            SuccessOrExit(error = aArgs[3].ParseAsUint8(seriesId));
+            error = otLinkMetricsQuery(GetInstancePtr(), &address, seriesId, nullptr,
+                                       &Interpreter::HandleLinkMetricsReport, this);
+        }
+        else
+        {
+            error = OT_ERROR_INVALID_ARGS;
+        }
     }
     else if (aArgs[0] == "mgmt")
     {
@@ -2372,38 +2375,15 @@
     }
     else if (aArgs[0] == "probe")
     {
-        error = ProcessLinkMetricsProbe(aArgs + 1);
-    }
+        otIp6Address address;
+        uint8_t      seriesId;
+        uint8_t      length;
 
-    return error;
-}
-
-otError Interpreter::ProcessLinkMetricsQuery(Arg aArgs[])
-{
-    otError       error;
-    otIp6Address  address;
-    otLinkMetrics linkMetrics;
-
-    SuccessOrExit(error = aArgs[0].ParseAsIp6Address(address));
-
-    if (aArgs[1] == "single")
-    {
-        VerifyOrExit(!aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
-        SuccessOrExit(error = ParseLinkMetricsFlags(linkMetrics, aArgs[2]));
-        error = otLinkMetricsQuery(GetInstancePtr(), &address, /* aSeriesId */ 0, &linkMetrics,
-                                   &Interpreter::HandleLinkMetricsReport, this);
-    }
-    else if (aArgs[1] == "forward")
-    {
-        uint8_t seriesId;
-
+        SuccessOrExit(error = aArgs[1].ParseAsIp6Address(address));
         SuccessOrExit(error = aArgs[2].ParseAsUint8(seriesId));
-        error = otLinkMetricsQuery(GetInstancePtr(), &address, seriesId, nullptr, &Interpreter::HandleLinkMetricsReport,
-                                   this);
-    }
-    else
-    {
-        error = OT_ERROR_INVALID_ARGS;
+        SuccessOrExit(error = aArgs[3].ParseAsUint8(length));
+
+        error = otLinkMetricsSendLinkProbe(GetInstancePtr(), &address, seriesId, length);
     }
 
 exit:
@@ -2547,27 +2527,11 @@
     return error;
 }
 
-otError Interpreter::ProcessLinkMetricsProbe(Arg aArgs[])
-{
-    otError      error = OT_ERROR_INVALID_ARGS;
-    otIp6Address address;
-    uint8_t      seriesId = 0;
-    uint8_t      length   = 0;
-
-    SuccessOrExit(error = aArgs[0].ParseAsIp6Address(address));
-    SuccessOrExit(error = aArgs[1].ParseAsUint8(seriesId));
-    SuccessOrExit(error = aArgs[2].ParseAsUint8(length));
-
-    error = otLinkMetricsSendLinkProbe(GetInstancePtr(), &address, seriesId, length);
-
-exit:
-    return error;
-}
 #endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
 
 #if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE
 
-otError Interpreter::ProcessLocate(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("locate")>(Arg aArgs[])
 {
     otError      error = OT_ERROR_INVALID_ARGS;
     otIp6Address anycastAddress;
@@ -2618,7 +2582,7 @@
 #endif //  OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE
 
 #if OPENTHREAD_FTD
-otError Interpreter::ProcessPskc(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("pskc")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
     otPskc  pskc;
@@ -2654,7 +2618,7 @@
 }
 
 #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
-otError Interpreter::ProcessPskcRef(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("pskcref")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -2685,7 +2649,7 @@
 #endif // OPENTHREAD_FTD
 
 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
-otError Interpreter::ProcessMlIid(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("mliid")>(Arg aArgs[])
 {
     otError                  error = OT_ERROR_NONE;
     otIp6InterfaceIdentifier iid;
@@ -2702,7 +2666,7 @@
 
 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
 
-otError Interpreter::ProcessMlr(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("mlr")>(Arg aArgs[])
 {
     otError error = OT_ERROR_INVALID_COMMAND;
 
@@ -2774,7 +2738,7 @@
 
 #endif // (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE) && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
 
-otError Interpreter::ProcessMode(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("mode")>(Arg aArgs[])
 {
     otError          error = OT_ERROR_NONE;
     otLinkModeConfig linkMode;
@@ -2819,7 +2783,7 @@
     return error;
 }
 
-otError Interpreter::ProcessMultiRadio(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("multiradio")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -2907,7 +2871,7 @@
 #endif // OPENTHREAD_CONFIG_MULTI_RADIO
 
 #if OPENTHREAD_FTD
-otError Interpreter::ProcessNeighbor(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("neighbor")>(Arg aArgs[])
 {
     otError                error = OT_ERROR_NONE;
     otNeighborInfo         neighborInfo;
@@ -2964,7 +2928,7 @@
 }
 #endif // OPENTHREAD_FTD
 
-otError Interpreter::ProcessNetstat(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("netstat")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
 
@@ -2987,27 +2951,22 @@
 }
 
 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
-otError Interpreter::ProcessServiceList(void)
-{
-    otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
-    otServiceConfig       config;
-
-    while (otServerGetNextService(GetInstancePtr(), &iterator, &config) == OT_ERROR_NONE)
-    {
-        mNetworkData.OutputService(config);
-    }
-
-    return OT_ERROR_NONE;
-}
-
-otError Interpreter::ProcessService(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("service")>(Arg aArgs[])
 {
     otError         error = OT_ERROR_INVALID_COMMAND;
     otServiceConfig cfg;
 
     if (aArgs[0].IsEmpty())
     {
-        error = ProcessServiceList();
+        otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
+        otServiceConfig       config;
+
+        while (otServerGetNextService(GetInstancePtr(), &iterator, &config) == OT_ERROR_NONE)
+        {
+            mNetworkData.OutputService(config);
+        }
+
+        error = OT_ERROR_NONE;
     }
     else
     {
@@ -3043,19 +3002,19 @@
 }
 #endif // OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
 
-otError Interpreter::ProcessNetworkData(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("netdata")>(Arg aArgs[])
 {
     return mNetworkData.Process(aArgs);
 }
 
 #if OPENTHREAD_FTD
-otError Interpreter::ProcessNetworkIdTimeout(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("networkidtimeout")>(Arg aArgs[])
 {
     return ProcessGetSet(aArgs, otThreadGetNetworkIdTimeout, otThreadSetNetworkIdTimeout);
 }
 #endif
 
-otError Interpreter::ProcessNetworkKey(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("networkkey")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -3079,7 +3038,7 @@
 }
 
 #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
-otError Interpreter::ProcessNetworkKeyRef(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("networkkeyref")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -3100,7 +3059,7 @@
 }
 #endif
 
-otError Interpreter::ProcessNetworkName(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("networkname")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -3118,7 +3077,7 @@
 }
 
 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
-otError Interpreter::ProcessNetworkTime(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("networktime")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -3168,7 +3127,7 @@
 }
 #endif // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
 
-otError Interpreter::ProcessPanId(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("panid")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -3184,7 +3143,7 @@
     return error;
 }
 
-otError Interpreter::ProcessParent(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("parent")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
 
@@ -3204,14 +3163,14 @@
 }
 
 #if OPENTHREAD_FTD
-otError Interpreter::ProcessParentPriority(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("parentpriority")>(Arg aArgs[])
 {
     return ProcessGetSet(aArgs, otThreadGetParentPriority, otThreadSetParentPriority);
 }
 #endif
 
 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
-otError Interpreter::ProcessRouterIdRange(Arg *aArgs)
+template <> otError Interpreter::Process<Cmd("routeridrange")>(Arg *aArgs)
 {
     uint8_t minRouterId;
     uint8_t maxRouterId;
@@ -3281,7 +3240,7 @@
     }
 }
 
-otError Interpreter::ProcessPing(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("ping")>(Arg aArgs[])
 {
     otError            error = OT_ERROR_NONE;
     otPingSenderConfig config;
@@ -3378,12 +3337,12 @@
 
 #endif // OPENTHREAD_CONFIG_PING_SENDER_ENABLE
 
-otError Interpreter::ProcessPollPeriod(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("pollperiod")>(Arg aArgs[])
 {
     return ProcessGetSet(aArgs, otLinkGetPollPeriod, otLinkSetPollPeriod);
 }
 
-otError Interpreter::ProcessPromiscuous(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("promiscuous")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -3566,7 +3525,7 @@
     return error;
 }
 
-otError Interpreter::ProcessPrefix(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("prefix")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -3605,7 +3564,19 @@
     }
     else if (aArgs[0] == "meshlocal")
     {
-        OutputIp6PrefixLine(*otThreadGetMeshLocalPrefix(GetInstancePtr()));
+        if (aArgs[1].IsEmpty())
+        {
+            OutputIp6PrefixLine(*otThreadGetMeshLocalPrefix(GetInstancePtr()));
+        }
+        else
+        {
+            otIp6Prefix prefix;
+
+            SuccessOrExit(error = aArgs[1].ParseAsIp6Prefix(prefix));
+            VerifyOrExit(prefix.mLength == OT_IP6_PREFIX_BITSIZE, error = OT_ERROR_INVALID_ARGS);
+            error =
+                otThreadSetMeshLocalPrefix(GetInstancePtr(), reinterpret_cast<otMeshLocalPrefix *>(&prefix.mPrefix));
+        }
     }
     else
     {
@@ -3618,14 +3589,14 @@
 #endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
 
 #if OPENTHREAD_FTD
-otError Interpreter::ProcessPreferRouterId(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("preferrouterid")>(Arg aArgs[])
 {
     return ProcessSet(aArgs, otThreadSetPreferredRouterId);
 }
 #endif
 
 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE && OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
-otError Interpreter::ProcessRadioFilter(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("radiofilter")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -3646,7 +3617,7 @@
 }
 #endif
 
-otError Interpreter::ProcessRcp(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("rcp")>(Arg aArgs[])
 {
     otError     error   = OT_ERROR_NONE;
     const char *version = otPlatRadioGetVersionString(GetInstancePtr());
@@ -3666,7 +3637,7 @@
     return error;
 }
 
-otError Interpreter::ProcessRegion(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("region")>(Arg aArgs[])
 {
     otError  error = OT_ERROR_NONE;
     uint16_t regionCode;
@@ -3690,13 +3661,13 @@
 }
 
 #if OPENTHREAD_FTD
-otError Interpreter::ProcessReleaseRouterId(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("releaserouterid")>(Arg aArgs[])
 {
     return ProcessSet(aArgs, otThreadReleaseRouterId);
 }
 #endif
 
-otError Interpreter::ProcessRloc16(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("rloc16")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
 
@@ -3747,7 +3718,7 @@
     return error;
 }
 
-otError Interpreter::ProcessRoute(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("route")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -3786,7 +3757,7 @@
 #endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
 
 #if OPENTHREAD_FTD
-otError Interpreter::ProcessRouter(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("router")>(Arg aArgs[])
 {
     otError      error = OT_ERROR_NONE;
     otRouterInfo routerInfo;
@@ -3871,12 +3842,12 @@
     return error;
 }
 
-otError Interpreter::ProcessRouterDowngradeThreshold(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("routerdowngradethreshold")>(Arg aArgs[])
 {
     return ProcessGetSet(aArgs, otThreadGetRouterDowngradeThreshold, otThreadSetRouterDowngradeThreshold);
 }
 
-otError Interpreter::ProcessRouterEligible(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("routereligible")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -3896,18 +3867,18 @@
     return error;
 }
 
-otError Interpreter::ProcessRouterSelectionJitter(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("routerselectionjitter")>(Arg aArgs[])
 {
     return ProcessGetSet(aArgs, otThreadGetRouterSelectionJitter, otThreadSetRouterSelectionJitter);
 }
 
-otError Interpreter::ProcessRouterUpgradeThreshold(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("routerupgradethreshold")>(Arg aArgs[])
 {
     return ProcessGetSet(aArgs, otThreadGetRouterUpgradeThreshold, otThreadSetRouterUpgradeThreshold);
 }
 #endif // OPENTHREAD_FTD
 
-otError Interpreter::ProcessScan(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("scan")>(Arg aArgs[])
 {
     otError  error        = OT_ERROR_NONE;
     uint32_t scanChannels = 0;
@@ -3946,7 +3917,11 @@
     }
     else
     {
-        OutputScanTableHeader();
+        static const char *const kScanTableTitles[]       = {"PAN", "MAC Address", "Ch", "dBm", "LQI"};
+        static const uint8_t     kScanTableColumnWidths[] = {6, 18, 4, 5, 5};
+
+        OutputTableHeader(kScanTableTitles, kScanTableColumnWidths);
+
         SuccessOrExit(error = otLinkActiveScan(GetInstancePtr(), scanChannels, scanDuration,
                                                &Interpreter::HandleActiveScanResult, this));
     }
@@ -3957,19 +3932,6 @@
     return error;
 }
 
-void Interpreter::OutputScanTableHeader(void)
-{
-    static const char *const kScanTableTitles[] = {
-        "J", "Network Name", "Extended PAN", "PAN", "MAC Address", "Ch", "dBm", "LQI",
-    };
-
-    static const uint8_t kScanTableColumnWidths[] = {
-        3, 18, 18, 6, 18, 4, 5, 5,
-    };
-
-    OutputTableHeader(kScanTableTitles, kScanTableColumnWidths);
-}
-
 void Interpreter::HandleActiveScanResult(otActiveScanResult *aResult, void *aContext)
 {
     static_cast<Interpreter *>(aContext)->HandleActiveScanResult(aResult);
@@ -3983,13 +3945,14 @@
         ExitNow();
     }
 
-    OutputFormat("| %d ", aResult->mIsJoinable);
+    if (aResult->mDiscover)
+    {
+        OutputFormat("| %-16s ", aResult->mNetworkName.m8);
 
-    OutputFormat("| %-16s ", aResult->mNetworkName.m8);
-
-    OutputFormat("| ");
-    OutputBytes(aResult->mExtendedPanId.m8);
-    OutputFormat(" ");
+        OutputFormat("| ");
+        OutputBytes(aResult->mExtendedPanId.m8);
+        OutputFormat(" ");
+    }
 
     OutputFormat("| %04x | ", aResult->mPanId);
     OutputExtAddress(aResult->mExtAddress);
@@ -4020,7 +3983,7 @@
     return;
 }
 
-otError Interpreter::ProcessSingleton(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("singleton")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
 
@@ -4030,7 +3993,7 @@
 }
 
 #if OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE
-otError Interpreter::ProcessSntp(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("sntp")>(Arg aArgs[])
 {
     otError          error = OT_ERROR_NONE;
     uint16_t         port  = OT_SNTP_DEFAULT_SERVER_PORT;
@@ -4100,7 +4063,7 @@
 #endif // OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE
 
 #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE || OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
-otError Interpreter::ProcessSrp(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("srp")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -4135,7 +4098,7 @@
 }
 #endif // OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE || OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
 
-otError Interpreter::ProcessState(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("state")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -4169,7 +4132,7 @@
     return error;
 }
 
-otError Interpreter::ProcessThread(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("thread")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -4193,12 +4156,12 @@
     return error;
 }
 
-otError Interpreter::ProcessDataset(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("dataset")>(Arg aArgs[])
 {
     return mDataset.Process(aArgs);
 }
 
-otError Interpreter::ProcessTxPower(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("txpower")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
     int8_t  power;
@@ -4219,18 +4182,18 @@
 }
 
 #if OPENTHREAD_CONFIG_TCP_ENABLE && OPENTHREAD_CONFIG_CLI_TCP_ENABLE
-otError Interpreter::ProcessTcp(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("tcp")>(Arg aArgs[])
 {
     return mTcp.Process(aArgs);
 }
 #endif
 
-otError Interpreter::ProcessUdp(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("udp")>(Arg aArgs[])
 {
     return mUdp.Process(aArgs);
 }
 
-otError Interpreter::ProcessUnsecurePort(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("unsecureport")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -4275,7 +4238,7 @@
 }
 
 #if OPENTHREAD_CONFIG_UPTIME_ENABLE
-otError Interpreter::ProcessUptime(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("uptime")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -4300,28 +4263,28 @@
 #endif
 
 #if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD
-otError Interpreter::ProcessCommissioner(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("commissioner")>(Arg aArgs[])
 {
     return mCommissioner.Process(aArgs);
 }
 #endif
 
 #if OPENTHREAD_CONFIG_JOINER_ENABLE
-otError Interpreter::ProcessJoiner(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("joiner")>(Arg aArgs[])
 {
     return mJoiner.Process(aArgs);
 }
 #endif
 
 #if OPENTHREAD_FTD
-otError Interpreter::ProcessJoinerPort(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("joinerport")>(Arg aArgs[])
 {
     return ProcessGetSet(aArgs, otThreadGetJoinerUdpPort, otThreadSetJoinerUdpPort);
 }
 #endif
 
 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
-otError Interpreter::ProcessMacFilter(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("macfilter")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -4569,7 +4532,7 @@
 
 #endif // OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
 
-otError Interpreter::ProcessMac(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("mac")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -4620,7 +4583,7 @@
 }
 
 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
-otError Interpreter::ProcessTrel(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("trel")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
     bool    enable;
@@ -4715,7 +4678,7 @@
 #endif
 
 #if OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE
-otError Interpreter::ProcessNetworkDiagnostic(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("networkdiagnostic")>(Arg aArgs[])
 {
     otError      error = OT_ERROR_NONE;
     otIp6Address address;
@@ -4998,6 +4961,251 @@
     mTimer.Start(aTimeoutMilli);
 }
 
+otError Interpreter::ProcessCommand(Arg aArgs[])
+{
+#define CmdEntry(aCommandString)                                   \
+    {                                                              \
+        aCommandString, &Interpreter::Process<Cmd(aCommandString)> \
+    }
+
+    static constexpr Command kCommands[] = {
+#if OPENTHREAD_FTD || OPENTHREAD_MTD
+#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
+        CmdEntry("ba"),
+#endif
+#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
+        CmdEntry("bbr"),
+#endif
+#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
+        CmdEntry("br"),
+#endif
+        CmdEntry("bufferinfo"),
+        CmdEntry("ccathreshold"),
+#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
+        CmdEntry("ccm"),
+#endif
+        CmdEntry("channel"),
+#if OPENTHREAD_FTD
+        CmdEntry("child"),
+        CmdEntry("childip"),
+        CmdEntry("childmax"),
+#endif
+#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE
+        CmdEntry("childsupervision"),
+#endif
+        CmdEntry("childtimeout"),
+#if OPENTHREAD_CONFIG_COAP_API_ENABLE
+        CmdEntry("coap"),
+#endif
+#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
+        CmdEntry("coaps"),
+#endif
+#if OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
+        CmdEntry("coex"),
+#endif
+#if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD
+        CmdEntry("commissioner"),
+#endif
+#if OPENTHREAD_FTD
+        CmdEntry("contextreusedelay"),
+#endif
+        CmdEntry("counters"),
+#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
+        CmdEntry("csl"),
+#endif
+        CmdEntry("dataset"),
+#if OPENTHREAD_FTD
+        CmdEntry("delaytimermin"),
+#endif
+#endif // OPENTHREAD_FTD || OPENTHREAD_MTD
+#if OPENTHREAD_CONFIG_DIAG_ENABLE
+        CmdEntry("diag"),
+#endif
+#if OPENTHREAD_FTD || OPENTHREAD_MTD
+        CmdEntry("discover"),
+        CmdEntry("dns"),
+#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
+        CmdEntry("domainname"),
+#endif
+#if OPENTHREAD_CONFIG_DUA_ENABLE
+        CmdEntry("dua"),
+#endif
+#if OPENTHREAD_FTD
+        CmdEntry("eidcache"),
+#endif
+        CmdEntry("eui64"),
+        CmdEntry("extaddr"),
+        CmdEntry("extpanid"),
+        CmdEntry("factoryreset"),
+#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
+        CmdEntry("fake"),
+#endif
+        CmdEntry("fem"),
+#endif // OPENTHREAD_FTD || OPENTHREAD_MTD
+#if OPENTHREAD_FTD || OPENTHREAD_MTD
+#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
+        CmdEntry("history"),
+#endif
+        CmdEntry("ifconfig"),
+        CmdEntry("ipaddr"),
+        CmdEntry("ipmaddr"),
+#if OPENTHREAD_CONFIG_JOINER_ENABLE
+        CmdEntry("joiner"),
+#endif
+#if OPENTHREAD_FTD
+        CmdEntry("joinerport"),
+#endif
+        CmdEntry("keysequence"),
+        CmdEntry("leaderdata"),
+#if OPENTHREAD_FTD
+        CmdEntry("leaderweight"),
+#endif
+#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
+        CmdEntry("linkmetrics"),
+#endif
+#if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE
+        CmdEntry("locate"),
+#endif
+        CmdEntry("log"),
+        CmdEntry("mac"),
+#if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
+        CmdEntry("macfilter"),
+#endif
+#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
+        CmdEntry("mliid"),
+#endif
+#if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE) && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
+        CmdEntry("mlr"),
+#endif
+        CmdEntry("mode"),
+        CmdEntry("multiradio"),
+#if OPENTHREAD_FTD
+        CmdEntry("neighbor"),
+#endif
+        CmdEntry("netdata"),
+        CmdEntry("netstat"),
+#if OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE
+        CmdEntry("networkdiagnostic"),
+#endif
+#if OPENTHREAD_FTD
+        CmdEntry("networkidtimeout"),
+#endif
+        CmdEntry("networkkey"),
+#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
+        CmdEntry("networkkeyref"),
+#endif
+        CmdEntry("networkname"),
+#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
+        CmdEntry("networktime"),
+#endif
+        CmdEntry("panid"),
+        CmdEntry("parent"),
+#if OPENTHREAD_FTD
+        CmdEntry("parentpriority"),
+        CmdEntry("partitionid"),
+#endif
+#if OPENTHREAD_CONFIG_PING_SENDER_ENABLE
+        CmdEntry("ping"),
+#endif
+        CmdEntry("pollperiod"),
+#if OPENTHREAD_FTD
+        CmdEntry("preferrouterid"),
+#endif
+#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
+        CmdEntry("prefix"),
+#endif
+        CmdEntry("promiscuous"),
+#if OPENTHREAD_FTD
+        CmdEntry("pskc"),
+#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
+        CmdEntry("pskcref"),
+#endif
+#endif
+#if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE && OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
+        CmdEntry("radiofilter"),
+#endif
+        CmdEntry("rcp"),
+        CmdEntry("region"),
+#if OPENTHREAD_FTD
+        CmdEntry("releaserouterid"),
+#endif
+#endif // OPENTHREAD_FTD || OPENTHREAD_MTD
+        CmdEntry("reset"),
+#if OPENTHREAD_FTD || OPENTHREAD_MTD
+        CmdEntry("rloc16"),
+#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
+        CmdEntry("route"),
+#endif
+#if OPENTHREAD_FTD
+        CmdEntry("router"),
+        CmdEntry("routerdowngradethreshold"),
+        CmdEntry("routereligible"),
+#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
+        CmdEntry("routeridrange"),
+#endif
+        CmdEntry("routerselectionjitter"),
+        CmdEntry("routerupgradethreshold"),
+#endif
+        CmdEntry("scan"),
+#if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
+        CmdEntry("service"),
+#endif
+        CmdEntry("singleton"),
+#if OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE
+        CmdEntry("sntp"),
+#endif
+#if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE || OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
+        CmdEntry("srp"),
+#endif
+        CmdEntry("state"),
+#if OPENTHREAD_CONFIG_TCP_ENABLE && OPENTHREAD_CONFIG_CLI_TCP_ENABLE
+        CmdEntry("tcp"),
+#endif
+        CmdEntry("thread"),
+#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
+        CmdEntry("trel"),
+#endif
+#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
+        CmdEntry("tvcheck"),
+#endif
+        CmdEntry("txpower"),
+        CmdEntry("udp"),
+        CmdEntry("unsecureport"),
+#if OPENTHREAD_CONFIG_UPTIME_ENABLE
+        CmdEntry("uptime"),
+#endif
+#endif // OPENTHREAD_FTD || OPENTHREAD_MTD
+        CmdEntry("version"),
+    };
+
+#undef CmdEntry
+
+    static_assert(BinarySearch::IsSorted(kCommands), "Command Table is not sorted");
+
+    otError        error   = OT_ERROR_NONE;
+    const Command *command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
+
+    if (command != nullptr)
+    {
+        error = (this->*command->mHandler)(aArgs + 1);
+    }
+    else if (aArgs[0] == "help")
+    {
+        OutputCommandTable(kCommands);
+
+        for (uint8_t i = 0; i < mUserCommandsLength; i++)
+        {
+            OutputLine("%s", mUserCommands[i].mName);
+        }
+    }
+    else
+    {
+        error = ProcessUserCommands(aArgs);
+    }
+
+    return error;
+}
+
 extern "C" void otCliInit(otInstance *aInstance, otCliOutputCallback aCallback, void *aContext)
 {
     Interpreter::Initialize(aInstance, aCallback, aContext);
@@ -5049,21 +5257,6 @@
     return;
 }
 
-extern "C" void otCliPlatLogLine(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aLogLine)
-{
-    OT_UNUSED_VARIABLE(aLogLevel);
-    OT_UNUSED_VARIABLE(aLogRegion);
-
-    VerifyOrExit(Interpreter::IsInitialized());
-
-    Interpreter::GetInterpreter().SetEmittingCommandOutput(false);
-    Interpreter::GetInterpreter().OutputLine(aLogLine);
-    Interpreter::GetInterpreter().SetEmittingCommandOutput(true);
-
-exit:
-    return;
-}
-
 } // namespace Cli
 } // namespace ot
 
diff --git a/src/cli/cli.hpp b/src/cli/cli.hpp
index 0f57847..90144d1 100644
--- a/src/cli/cli.hpp
+++ b/src/cli/cli.hpp
@@ -46,6 +46,7 @@
 #include <openthread/instance.h>
 #include <openthread/ip6.h>
 #include <openthread/link.h>
+#include <openthread/logging.h>
 #include <openthread/ping_sender.h>
 #include <openthread/sntp.h>
 #if OPENTHREAD_CONFIG_TCP_ENABLE && OPENTHREAD_CONFIG_CLI_TCP_ENABLE
@@ -89,7 +90,6 @@
 namespace Cli {
 
 extern "C" void otCliPlatLogv(otLogLevel, otLogRegion, const char *, va_list);
-extern "C" void otCliPlatLogLine(otLogLevel, otLogRegion, const char *);
 extern "C" void otCliAppendResult(otError aError);
 extern "C" void otCliOutputBytes(const uint8_t *aBytes, uint8_t aLength);
 extern "C" void otCliOutputFormat(const char *aFmt, ...);
@@ -107,7 +107,6 @@
     friend class SrpClient;
 #endif
     friend void otCliPlatLogv(otLogLevel, otLogRegion, const char *, va_list);
-    friend void otCliPlatLogLine(otLogLevel, otLogRegion, const char *);
     friend void otCliAppendResult(otError aError);
     friend void otCliOutputBytes(const uint8_t *aBytes, uint8_t aLength);
     friend void otCliOutputFormat(const char *aFmt, ...);
@@ -166,7 +165,7 @@
     /**
      * This static method checks a given argument string against "enable" or "disable" commands.
      *
-     * @param[in]  aArgs    The argument string to parse.
+     * @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.
      *
@@ -179,9 +178,9 @@
     /**
      * This method sets the user command table.
      *
-     * @param[in]  aUserCommands  A pointer to an array with user commands.
-     * @param[in]  aLength        @p aUserCommands length.
-     * @param[in]  aContext       @p aUserCommands length.
+     * @param[in]  aCommands  A pointer to an array with user commands.
+     * @param[in]  aLength    @p aUserCommands length.
+     * @param[in]  aContext   @p aUserCommands length.
      *
      */
     void SetUserCommands(const otCliCommand *aCommands, uint8_t aLength, void *aContext);
@@ -317,34 +316,15 @@
     static otError ParseRoute(Arg aArgs[], otExternalRouteConfig &aConfig);
 #endif
 
-    // Process methods on FTD/MTD/RCP
-#if OPENTHREAD_CONFIG_DIAG_ENABLE
-    otError ProcessDiag(Arg aArgs[]);
-#endif
-    otError ProcessHelp(Arg aArgs[]);
-    otError ProcessHistory(Arg aArgs[]);
-    otError ProcessReset(Arg aArgs[]);
+    otError ProcessCommand(Arg aArgs[]);
+
+    template <CommandId kCommandId> otError Process(Arg aArgs[]);
+
     otError ProcessUserCommands(Arg aArgs[]);
-    otError ProcessVersion(Arg aArgs[]);
 
-    // Process methods only on FTD/MTD
 #if OPENTHREAD_FTD || OPENTHREAD_MTD
-    otError ProcessCcaThreshold(Arg aArgs[]);
-#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
-    otError ProcessCcm(Arg aArgs[]);
-    otError ProcessThreadVersionCheck(Arg aArgs[]);
-#endif
-    otError ProcessBufferInfo(Arg aArgs[]);
-    otError ProcessChannel(Arg aArgs[]);
-#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
-    otError ProcessBorderAgent(Arg aArgs[]);
-#endif
-#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
-    otError ProcessBorderRouting(Arg aArgs[]);
-#endif
-#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
-    otError ProcessBackboneRouter(Arg aArgs[]);
 
+#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
     otError ProcessBackboneRouterLocal(Arg aArgs[]);
 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE
@@ -352,86 +332,18 @@
     void    PrintMulticastListenersTable(void);
 #endif
 #endif
-
-    otError ProcessDomainName(Arg aArgs[]);
-
-#if OPENTHREAD_CONFIG_DUA_ENABLE
-    otError ProcessDua(Arg aArgs[]);
 #endif
 
-#endif // (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
-
 #if OPENTHREAD_FTD
-    otError ProcessChild(Arg aArgs[]);
-    otError ProcessChildIp(Arg aArgs[]);
-    otError ProcessChildMax(Arg aArgs[]);
-#endif
-#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE
-    otError ProcessChildSupervision(Arg aArgs[]);
-#endif
-    otError ProcessChildTimeout(Arg aArgs[]);
-#if OPENTHREAD_CONFIG_COAP_API_ENABLE
-    otError ProcessCoap(Arg aArgs[]);
-#endif
-#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
-    otError ProcessCoapSecure(Arg aArgs[]);
-#endif
-#if OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
-    otError ProcessCoexMetrics(Arg aArgs[]);
-#endif
-#if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD
-    otError ProcessCommissioner(Arg aArgs[]);
-#endif
-#if OPENTHREAD_FTD
-    otError ProcessContextIdReuseDelay(Arg aArgs[]);
-#endif
-    otError ProcessCounters(Arg aArgs[]);
-    otError ProcessCsl(Arg aArgs[]);
-#if OPENTHREAD_FTD
-    otError ProcessDelayTimerMin(Arg aArgs[]);
-#endif
-    otError ProcessDiscover(Arg aArgs[]);
-    otError ProcessDns(Arg aArgs[]);
-#if OPENTHREAD_FTD
-    void    OutputEidCacheEntry(const otCacheEntryInfo &aEntry);
-    otError ProcessEidCache(Arg aArgs[]);
-#endif
-    otError ProcessEui64(Arg aArgs[]);
-    otError ProcessLog(Arg aArgs[]);
-    otError ProcessExtAddress(Arg aArgs[]);
-    otError ProcessExtPanId(Arg aArgs[]);
-    otError ProcessFactoryReset(Arg aArgs[]);
-#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
-    otError ProcessFake(Arg aArgs[]);
-#endif
-    otError ProcessFem(Arg aArgs[]);
-    otError ProcessIfconfig(Arg aArgs[]);
-    otError ProcessIpAddr(Arg aArgs[]);
-    otError ProcessIpMulticastAddr(Arg aArgs[]);
-#if OPENTHREAD_CONFIG_JOINER_ENABLE
-    otError ProcessJoiner(Arg aArgs[]);
-#endif
-#if OPENTHREAD_FTD
-    otError ProcessJoinerPort(Arg aArgs[]);
-#endif
-    otError ProcessKeySequence(Arg aArgs[]);
-    otError ProcessLeaderData(Arg aArgs[]);
-#if OPENTHREAD_FTD
-    otError ProcessPartitionId(Arg aArgs[]);
-    otError ProcessLeaderWeight(Arg aArgs[]);
-#endif
-#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
-    otError ProcessMlIid(Arg aArgs[]);
+    void OutputEidCacheEntry(const otCacheEntryInfo &aEntry);
 #endif
 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
-    otError ProcessLinkMetrics(Arg aArgs[]);
     otError ProcessLinkMetricsQuery(Arg aArgs[]);
     otError ProcessLinkMetricsMgmt(Arg aArgs[]);
     otError ProcessLinkMetricsProbe(Arg aArgs[]);
     otError ParseLinkMetricsFlags(otLinkMetrics &aLinkMetrics, const Arg &aFlags);
 #endif
 #if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE
-    otError     ProcessLocate(Arg aArgs[]);
     static void HandleLocateResult(void *              aContext,
                                    otError             aError,
                                    const otIp6Address *aMeshLocalAddress,
@@ -439,8 +351,6 @@
     void        HandleLocateResult(otError aError, const otIp6Address *aMeshLocalAddress, uint16_t aRloc16);
 #endif
 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
-    otError ProcessMlr(Arg aArgs[]);
-
     static void HandleMlrRegResult(void *              aContext,
                                    otError             aError,
                                    uint8_t             aMlrStatus,
@@ -451,114 +361,22 @@
                                    const otIp6Address *aFailedAddresses,
                                    uint8_t             aFailedAddressNum);
 #endif
-    otError ProcessMode(Arg aArgs[]);
-    otError ProcessMultiRadio(Arg aArgs[]);
 #if OPENTHREAD_CONFIG_MULTI_RADIO
     void OutputMultiRadioInfo(const otMultiRadioNeighborInfo &aMultiRadioInfo);
 #endif
-#if OPENTHREAD_FTD
-    otError ProcessNeighbor(Arg aArgs[]);
-#endif
-    otError ProcessNetworkData(Arg aArgs[]);
-    otError ProcessNetstat(Arg aArgs[]);
-#if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
-    otError ProcessService(Arg aArgs[]);
-    otError ProcessServiceList(void);
-#endif
-#if OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE
-    otError ProcessNetworkDiagnostic(Arg aArgs[]);
-#endif
-#if OPENTHREAD_FTD
-    otError ProcessNetworkIdTimeout(Arg aArgs[]);
-#endif
-    otError ProcessNetworkKey(Arg aArgs[]);
-#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
-    otError ProcessNetworkKeyRef(Arg aArgs[]);
-#endif
-    otError ProcessNetworkName(Arg aArgs[]);
-#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
-    otError ProcessNetworkTime(Arg aArgs[]);
-#endif
-    otError ProcessPanId(Arg aArgs[]);
-    otError ProcessParent(Arg aArgs[]);
-#if OPENTHREAD_FTD
-    otError ProcessParentPriority(Arg aArgs[]);
-#endif
-#if OPENTHREAD_CONFIG_PING_SENDER_ENABLE
-    otError ProcessPing(Arg aArgs[]);
-#endif
-    otError ProcessPollPeriod(Arg aArgs[]);
-#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
-    otError ProcessPrefix(Arg aArgs[]);
-#endif
-    otError ProcessPromiscuous(Arg aArgs[]);
-#if OPENTHREAD_FTD
-    otError ProcessPreferRouterId(Arg aArgs[]);
-    otError ProcessPskc(Arg aArgs[]);
-#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
-    otError ProcessPskcRef(Arg aArgs[]);
-#endif
-#endif
-#if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE && OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
-    otError ProcessRadioFilter(Arg aArgs[]);
-#endif
-    otError ProcessRcp(Arg aArgs[]);
-    otError ProcessRegion(Arg aArgs[]);
-#if OPENTHREAD_FTD
-    otError ProcessReleaseRouterId(Arg aArgs[]);
-#endif
-#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
-    otError ProcessRoute(Arg aArgs[]);
-#endif
-#if OPENTHREAD_FTD
-    otError ProcessRouter(Arg aArgs[]);
-    otError ProcessRouterDowngradeThreshold(Arg aArgs[]);
-    otError ProcessRouterEligible(Arg aArgs[]);
-    otError ProcessRouterSelectionJitter(Arg aArgs[]);
-    otError ProcessRouterUpgradeThreshold(Arg aArgs[]);
-#endif
-    otError ProcessRloc16(Arg aArgs[]);
-    otError ProcessScan(Arg aArgs[]);
-    otError ProcessSingleton(Arg aArgs[]);
-#if OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE
-    otError ProcessSntp(Arg aArgs[]);
-#endif
-#if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE || OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
-    otError ProcessSrp(Arg aArgs[]);
-#endif
-    otError ProcessState(Arg aArgs[]);
-    otError ProcessThread(Arg aArgs[]);
-    otError ProcessDataset(Arg aArgs[]);
-    otError ProcessTxPower(Arg aArgs[]);
-#if OPENTHREAD_CONFIG_TCP_ENABLE && OPENTHREAD_CONFIG_CLI_TCP_ENABLE
-    otError ProcessTcp(Arg aArgs[]);
-#endif
-    otError ProcessUdp(Arg aArgs[]);
-    otError ProcessUnsecurePort(Arg aArgs[]);
-#if OPENTHREAD_CONFIG_UPTIME_ENABLE
-    otError ProcessUptime(Arg aArgs[]);
-#endif
+
 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
-    otError            ProcessMacFilter(Arg aArgs[]);
     void               PrintMacFilter(void);
     otError            ProcessMacFilterAddress(Arg aArgs[]);
     otError            ProcessMacFilterRss(Arg aArgs[]);
     void               OutputMacFilterEntry(const otMacFilterEntry &aEntry);
     static const char *MacFilterAddressModeToString(otMacFilterAddressMode aMode);
 #endif
-    otError ProcessMac(Arg aArgs[]);
-#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
-    otError ProcessTrel(Arg aArgs[]);
-#endif
-#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
-    otError ProcessRouterIdRange(Arg *aArgs);
-#endif
 
 #if OPENTHREAD_CONFIG_PING_SENDER_ENABLE
     static void HandlePingReply(const otPingSenderReply *aReply, void *aContext);
     static void HandlePingStatistics(const otPingSenderStatistics *aStatistics, void *aContext);
 #endif
-    void        OutputScanTableHeader(void);
     static void HandleActiveScanResult(otActiveScanResult *aResult, void *aContext);
     static void HandleEnergyScanResult(otEnergyScanResult *aResult, void *aContext);
     static void HandleLinkPcapReceive(const otRadioFrame *aFrame, bool aIsTx, void *aContext);
@@ -647,221 +465,6 @@
     static void HandleTimer(Timer &aTimer);
     void        HandleTimer(void);
 
-    // Commands supported by radio:
-    // [diag, help, reset, version]
-    static constexpr Command sCommands[] = {
-#if OPENTHREAD_FTD || OPENTHREAD_MTD
-#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
-        {"ba", &Interpreter::ProcessBorderAgent},
-#endif
-#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
-        {"bbr", &Interpreter::ProcessBackboneRouter},
-#endif
-#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
-        {"br", &Interpreter::ProcessBorderRouting},
-#endif
-        {"bufferinfo", &Interpreter::ProcessBufferInfo},
-        {"ccathreshold", &Interpreter::ProcessCcaThreshold},
-#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
-        {"ccm", &Interpreter::ProcessCcm},
-#endif
-        {"channel", &Interpreter::ProcessChannel},
-#if OPENTHREAD_FTD
-        {"child", &Interpreter::ProcessChild},
-        {"childip", &Interpreter::ProcessChildIp},
-        {"childmax", &Interpreter::ProcessChildMax},
-#endif
-#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE
-        {"childsupervision", &Interpreter::ProcessChildSupervision},
-#endif
-        {"childtimeout", &Interpreter::ProcessChildTimeout},
-#if OPENTHREAD_CONFIG_COAP_API_ENABLE
-        {"coap", &Interpreter::ProcessCoap},
-#endif
-#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
-        {"coaps", &Interpreter::ProcessCoapSecure},
-#endif
-#if OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
-        {"coex", &Interpreter::ProcessCoexMetrics},
-#endif
-#if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD
-        {"commissioner", &Interpreter::ProcessCommissioner},
-#endif
-#if OPENTHREAD_FTD
-        {"contextreusedelay", &Interpreter::ProcessContextIdReuseDelay},
-#endif
-        {"counters", &Interpreter::ProcessCounters},
-#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
-        {"csl", &Interpreter::ProcessCsl},
-#endif
-        {"dataset", &Interpreter::ProcessDataset},
-#if OPENTHREAD_FTD
-        {"delaytimermin", &Interpreter::ProcessDelayTimerMin},
-#endif
-#endif // OPENTHREAD_FTD || OPENTHREAD_MTD
-#if OPENTHREAD_CONFIG_DIAG_ENABLE
-        {"diag", &Interpreter::ProcessDiag},
-#endif
-#if OPENTHREAD_FTD || OPENTHREAD_MTD
-        {"discover", &Interpreter::ProcessDiscover},
-        {"dns", &Interpreter::ProcessDns},
-#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
-        {"domainname", &Interpreter::ProcessDomainName},
-#endif
-#if OPENTHREAD_CONFIG_DUA_ENABLE
-        {"dua", &Interpreter::ProcessDua},
-#endif
-#if OPENTHREAD_FTD
-        {"eidcache", &Interpreter::ProcessEidCache},
-#endif
-        {"eui64", &Interpreter::ProcessEui64},
-        {"extaddr", &Interpreter::ProcessExtAddress},
-        {"extpanid", &Interpreter::ProcessExtPanId},
-        {"factoryreset", &Interpreter::ProcessFactoryReset},
-#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
-        {"fake", &Interpreter::ProcessFake},
-#endif
-        {"fem", &Interpreter::ProcessFem},
-#endif // OPENTHREAD_FTD || OPENTHREAD_MTD
-        {"help", &Interpreter::ProcessHelp},
-#if OPENTHREAD_FTD || OPENTHREAD_MTD
-#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
-        {"history", &Interpreter::ProcessHistory},
-#endif
-        {"ifconfig", &Interpreter::ProcessIfconfig},
-        {"ipaddr", &Interpreter::ProcessIpAddr},
-        {"ipmaddr", &Interpreter::ProcessIpMulticastAddr},
-#if OPENTHREAD_CONFIG_JOINER_ENABLE
-        {"joiner", &Interpreter::ProcessJoiner},
-#endif
-#if OPENTHREAD_FTD
-        {"joinerport", &Interpreter::ProcessJoinerPort},
-#endif
-        {"keysequence", &Interpreter::ProcessKeySequence},
-        {"leaderdata", &Interpreter::ProcessLeaderData},
-#if OPENTHREAD_FTD
-        {"leaderweight", &Interpreter::ProcessLeaderWeight},
-#endif
-#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
-        {"linkmetrics", &Interpreter::ProcessLinkMetrics},
-#endif
-#if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE
-        {"locate", &Interpreter::ProcessLocate},
-#endif
-        {"log", &Interpreter::ProcessLog},
-        {"mac", &Interpreter::ProcessMac},
-#if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
-        {"macfilter", &Interpreter::ProcessMacFilter},
-#endif
-#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
-        {"mliid", &Interpreter::ProcessMlIid},
-#endif
-#if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE) && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
-        {"mlr", &Interpreter::ProcessMlr},
-#endif
-        {"mode", &Interpreter::ProcessMode},
-        {"multiradio", &Interpreter::ProcessMultiRadio},
-#if OPENTHREAD_FTD
-        {"neighbor", &Interpreter::ProcessNeighbor},
-#endif
-        {"netdata", &Interpreter::ProcessNetworkData},
-        {"netstat", &Interpreter::ProcessNetstat},
-#if OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE
-        {"networkdiagnostic", &Interpreter::ProcessNetworkDiagnostic},
-#endif
-#if OPENTHREAD_FTD
-        {"networkidtimeout", &Interpreter::ProcessNetworkIdTimeout},
-#endif
-        {"networkkey", &Interpreter::ProcessNetworkKey},
-#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
-        {"networkkeyref", &Interpreter::ProcessNetworkKeyRef},
-#endif
-        {"networkname", &Interpreter::ProcessNetworkName},
-#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
-        {"networktime", &Interpreter::ProcessNetworkTime},
-#endif
-        {"panid", &Interpreter::ProcessPanId},
-        {"parent", &Interpreter::ProcessParent},
-#if OPENTHREAD_FTD
-        {"parentpriority", &Interpreter::ProcessParentPriority},
-        {"partitionid", &Interpreter::ProcessPartitionId},
-#endif
-#if OPENTHREAD_CONFIG_PING_SENDER_ENABLE
-        {"ping", &Interpreter::ProcessPing},
-#endif
-        {"pollperiod", &Interpreter::ProcessPollPeriod},
-#if OPENTHREAD_FTD
-        {"preferrouterid", &Interpreter::ProcessPreferRouterId},
-#endif
-#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
-        {"prefix", &Interpreter::ProcessPrefix},
-#endif
-        {"promiscuous", &Interpreter::ProcessPromiscuous},
-#if OPENTHREAD_FTD
-        {"pskc", &Interpreter::ProcessPskc},
-#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
-        {"pskcref", &Interpreter::ProcessPskcRef},
-#endif
-#endif
-#if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE && OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
-        {"radiofilter", &Interpreter::ProcessRadioFilter},
-#endif
-        {"rcp", &Interpreter::ProcessRcp},
-        {"region", &Interpreter::ProcessRegion},
-#if OPENTHREAD_FTD
-        {"releaserouterid", &Interpreter::ProcessReleaseRouterId},
-#endif
-#endif // OPENTHREAD_FTD || OPENTHREAD_MTD
-        {"reset", &Interpreter::ProcessReset},
-#if OPENTHREAD_FTD || OPENTHREAD_MTD
-        {"rloc16", &Interpreter::ProcessRloc16},
-#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
-        {"route", &Interpreter::ProcessRoute},
-#endif
-#if OPENTHREAD_FTD
-        {"router", &Interpreter::ProcessRouter},
-        {"routerdowngradethreshold", &Interpreter::ProcessRouterDowngradeThreshold},
-        {"routereligible", &Interpreter::ProcessRouterEligible},
-#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
-        {"routeridrange", &Interpreter::ProcessRouterIdRange},
-#endif
-        {"routerselectionjitter", &Interpreter::ProcessRouterSelectionJitter},
-        {"routerupgradethreshold", &Interpreter::ProcessRouterUpgradeThreshold},
-#endif
-        {"scan", &Interpreter::ProcessScan},
-#if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
-        {"service", &Interpreter::ProcessService},
-#endif
-        {"singleton", &Interpreter::ProcessSingleton},
-#if OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE
-        {"sntp", &Interpreter::ProcessSntp},
-#endif
-#if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE || OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
-        {"srp", &Interpreter::ProcessSrp},
-#endif
-        {"state", &Interpreter::ProcessState},
-#if OPENTHREAD_CONFIG_TCP_ENABLE && OPENTHREAD_CONFIG_CLI_TCP_ENABLE
-        {"tcp", &Interpreter::ProcessTcp},
-#endif
-        {"thread", &Interpreter::ProcessThread},
-#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
-        {"trel", &Interpreter::ProcessTrel},
-#endif
-#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
-        {"tvcheck", &Interpreter::ProcessThreadVersionCheck},
-#endif
-        {"txpower", &Interpreter::ProcessTxPower},
-        {"udp", &Interpreter::ProcessUdp},
-        {"unsecureport", &Interpreter::ProcessUnsecurePort},
-#if OPENTHREAD_CONFIG_UPTIME_ENABLE
-        {"uptime", &Interpreter::ProcessUptime},
-#endif
-#endif // OPENTHREAD_FTD || OPENTHREAD_MTD
-        {"version", &Interpreter::ProcessVersion},
-    };
-
-    static_assert(BinarySearch::IsSorted(sCommands), "Command Table is not sorted");
-
     const otCliCommand *mUserCommands;
     uint8_t             mUserCommandsLength;
     void *              mUserCommandsContext;
diff --git a/src/cli/cli_coap.cpp b/src/cli/cli_coap.cpp
index 20e88cf..5d7c260 100644
--- a/src/cli/cli_coap.cpp
+++ b/src/cli/cli_coap.cpp
@@ -44,8 +44,6 @@
 namespace ot {
 namespace Cli {
 
-constexpr Coap::Command Coap::sCommands[];
-
 Coap::Coap(Output &aOutput)
     : OutputWrapper(aOutput)
     , mUseDefaultRequestTxParameters(true)
@@ -144,7 +142,7 @@
 }
 
 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
-otError Coap::ProcessCancel(Arg aArgs[])
+template <> otError Coap::Process<Cmd("cancel")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
 
@@ -152,19 +150,7 @@
 }
 #endif
 
-otError Coap::ProcessHelp(Arg aArgs[])
-{
-    OT_UNUSED_VARIABLE(aArgs);
-
-    for (const Command &command : sCommands)
-    {
-        OutputLine(command.mName);
-    }
-
-    return OT_ERROR_NONE;
-}
-
-otError Coap::ProcessResource(Arg aArgs[])
+template <> otError Coap::Process<Cmd("resource")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -203,7 +189,7 @@
     return error;
 }
 
-otError Coap::ProcessSet(Arg aArgs[])
+template <> otError Coap::Process<Cmd("set")>(Arg aArgs[])
 {
 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
     otMessage *   notificationMessage = nullptr;
@@ -264,14 +250,14 @@
     return error;
 }
 
-otError Coap::ProcessStart(Arg aArgs[])
+template <> otError Coap::Process<Cmd("start")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
 
     return otCoapStart(GetInstancePtr(), OT_DEFAULT_COAP_PORT);
 }
 
-otError Coap::ProcessStop(Arg aArgs[])
+template <> otError Coap::Process<Cmd("stop")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
 
@@ -284,7 +270,7 @@
     return otCoapStop(GetInstancePtr());
 }
 
-otError Coap::ProcessParameters(Arg aArgs[])
+template <> otError Coap::Process<Cmd("parameters")>(Arg aArgs[])
 {
     otError             error = OT_ERROR_NONE;
     bool *              defaultTxParameters;
@@ -342,28 +328,28 @@
     return error;
 }
 
-otError Coap::ProcessGet(Arg aArgs[])
+template <> otError Coap::Process<Cmd("get")>(Arg aArgs[])
 {
     return ProcessRequest(aArgs, OT_COAP_CODE_GET);
 }
 
-otError Coap::ProcessPost(Arg aArgs[])
+template <> otError Coap::Process<Cmd("post")>(Arg aArgs[])
 {
     return ProcessRequest(aArgs, OT_COAP_CODE_POST);
 }
 
-otError Coap::ProcessPut(Arg aArgs[])
+template <> otError Coap::Process<Cmd("put")>(Arg aArgs[])
 {
     return ProcessRequest(aArgs, OT_COAP_CODE_PUT);
 }
 
-otError Coap::ProcessDelete(Arg aArgs[])
+template <> otError Coap::Process<Cmd("delete")>(Arg aArgs[])
 {
     return ProcessRequest(aArgs, OT_COAP_CODE_DELETE);
 }
 
 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
-otError Coap::ProcessObserve(Arg aArgs[])
+template <> otError Coap::Process<Cmd("observe")>(Arg aArgs[])
 {
     return ProcessRequest(aArgs, OT_COAP_CODE_GET, /* aCoapObserve */ true);
 }
@@ -577,17 +563,44 @@
 
 otError Coap::Process(Arg aArgs[])
 {
-    otError        error = OT_ERROR_INVALID_ARGS;
-    const Command *command;
-
-    if (aArgs[0].IsEmpty())
-    {
-        IgnoreError(ProcessHelp(aArgs));
-        ExitNow();
+#define CmdEntry(aCommandString)                            \
+    {                                                       \
+        aCommandString, &Coap::Process<Cmd(aCommandString)> \
     }
 
-    command = BinarySearch::Find(aArgs[0].GetCString(), sCommands);
-    VerifyOrExit(command != nullptr, error = OT_ERROR_INVALID_COMMAND);
+    static constexpr Command kCommands[] = {
+#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
+        CmdEntry("cancel"),
+#endif
+        CmdEntry("delete"),
+        CmdEntry("get"),
+#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
+        CmdEntry("observe"),
+#endif
+        CmdEntry("parameters"),
+        CmdEntry("post"),
+        CmdEntry("put"),
+        CmdEntry("resource"),
+        CmdEntry("set"),
+        CmdEntry("start"),
+        CmdEntry("stop"),
+    };
+
+#undef CmdEntry
+
+    static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
+
+    otError        error = OT_ERROR_INVALID_COMMAND;
+    const Command *command;
+
+    if (aArgs[0].IsEmpty() || (aArgs[0] == "help"))
+    {
+        OutputCommandTable(kCommands);
+        ExitNow(error = aArgs[0].IsEmpty() ? OT_ERROR_INVALID_ARGS : OT_ERROR_NONE);
+    }
+
+    command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
+    VerifyOrExit(command != nullptr);
 
     error = (this->*command->mHandler)(aArgs + 1);
 
diff --git a/src/cli/cli_coap.hpp b/src/cli/cli_coap.hpp
index 022bff0..afa56bf 100644
--- a/src/cli/cli_coap.hpp
+++ b/src/cli/cli_coap.hpp
@@ -86,6 +86,8 @@
     };
 #endif
 
+    template <CommandId kCommandId> otError Process(Arg aArgs[]);
+
 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
     otError CancelResourceSubscription(void);
     void    CancelSubscriber(void);
@@ -93,23 +95,6 @@
 
     void PrintPayload(otMessage *aMessage);
 
-    otError ProcessHelp(Arg aArgs[]);
-#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
-    otError ProcessCancel(Arg aArgs[]);
-#endif
-    otError ProcessDelete(Arg aArgs[]);
-    otError ProcessGet(Arg aArgs[]);
-#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
-    otError ProcessObserve(Arg aArgs[]);
-#endif
-    otError ProcessParameters(Arg aArgs[]);
-    otError ProcessPost(Arg aArgs[]);
-    otError ProcessPut(Arg aArgs[]);
-    otError ProcessResource(Arg aArgs[]);
-    otError ProcessSet(Arg aArgs[]);
-    otError ProcessStart(Arg aArgs[]);
-    otError ProcessStop(Arg aArgs[]);
-
 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
     otError ProcessRequest(Arg aArgs[], otCoapCode aCoapCode, bool aCoapObserve = false);
 #else
@@ -161,27 +146,6 @@
         return mUseDefaultResponseTxParameters ? nullptr : &mResponseTxParameters;
     }
 
-    static constexpr Command sCommands[] = {
-#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
-        {"cancel", &Coap::ProcessCancel},
-#endif
-        {"delete", &Coap::ProcessDelete},
-        {"get", &Coap::ProcessGet},
-        {"help", &Coap::ProcessHelp},
-#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
-        {"observe", &Coap::ProcessObserve},
-#endif
-        {"parameters", &Coap::ProcessParameters},
-        {"post", &Coap::ProcessPost},
-        {"put", &Coap::ProcessPut},
-        {"resource", &Coap::ProcessResource},
-        {"set", &Coap::ProcessSet},
-        {"start", &Coap::ProcessStart},
-        {"stop", &Coap::ProcessStop},
-    };
-
-    static_assert(BinarySearch::IsSorted(sCommands), "Command Table is not sorted");
-
     bool mUseDefaultRequestTxParameters;
     bool mUseDefaultResponseTxParameters;
 
diff --git a/src/cli/cli_commissioner.cpp b/src/cli/cli_commissioner.cpp
index e8596fc..bbbcfce 100644
--- a/src/cli/cli_commissioner.cpp
+++ b/src/cli/cli_commissioner.cpp
@@ -40,21 +40,7 @@
 namespace ot {
 namespace Cli {
 
-constexpr Commissioner::Command Commissioner::sCommands[];
-
-otError Commissioner::ProcessHelp(Arg aArgs[])
-{
-    OT_UNUSED_VARIABLE(aArgs);
-
-    for (const Command &command : sCommands)
-    {
-        OutputLine(command.mName);
-    }
-
-    return OT_ERROR_NONE;
-}
-
-otError Commissioner::ProcessAnnounce(Arg aArgs[])
+template <> otError Commissioner::Process<Cmd("announce")>(Arg aArgs[])
 {
     otError      error;
     uint32_t     mask;
@@ -73,7 +59,7 @@
     return error;
 }
 
-otError Commissioner::ProcessEnergy(Arg aArgs[])
+template <> otError Commissioner::Process<Cmd("energy")>(Arg aArgs[])
 {
     otError      error;
     uint32_t     mask;
@@ -95,13 +81,54 @@
     return error;
 }
 
-otError Commissioner::ProcessJoiner(Arg aArgs[])
+template <> otError Commissioner::Process<Cmd("joiner")>(Arg aArgs[])
 {
     otError             error = OT_ERROR_NONE;
     otExtAddress        addr;
     const otExtAddress *addrPtr = nullptr;
     otJoinerDiscerner   discerner;
 
+    if (aArgs[0] == "table")
+    {
+        uint16_t     iter = 0;
+        otJoinerInfo joinerInfo;
+
+        static const char *const kJoinerTableTitles[] = {"ID", "PSKd", "Expiration"};
+
+        static const uint8_t kJoinerTableColumnWidths[] = {
+            23,
+            34,
+            12,
+        };
+
+        OutputTableHeader(kJoinerTableTitles, kJoinerTableColumnWidths);
+
+        while (otCommissionerGetNextJoinerInfo(GetInstancePtr(), &iter, &joinerInfo) == OT_ERROR_NONE)
+        {
+            switch (joinerInfo.mType)
+            {
+            case OT_JOINER_INFO_TYPE_ANY:
+                OutputFormat("| %21s", "*");
+                break;
+
+            case OT_JOINER_INFO_TYPE_EUI64:
+                OutputFormat("|      ");
+                OutputExtAddress(joinerInfo.mSharedId.mEui64);
+                break;
+
+            case OT_JOINER_INFO_TYPE_DISCERNER:
+                OutputFormat("| 0x%016llx/%2u", static_cast<unsigned long long>(joinerInfo.mSharedId.mDiscerner.mValue),
+                             joinerInfo.mSharedId.mDiscerner.mLength);
+                break;
+            }
+
+            OutputFormat(" | %32s | %10d |", joinerInfo.mPskd.m8, joinerInfo.mExpirationTime);
+            OutputLine("");
+        }
+
+        ExitNow(error = OT_ERROR_NONE);
+    }
+
     VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
 
     memset(&discerner, 0, sizeof(discerner));
@@ -163,7 +190,7 @@
     return error;
 }
 
-otError Commissioner::ProcessMgmtGet(Arg aArgs[])
+template <> otError Commissioner::Process<Cmd("mgmtget")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
     uint8_t tlvs[32];
@@ -210,7 +237,7 @@
     return error;
 }
 
-otError Commissioner::ProcessMgmtSet(Arg aArgs[])
+template <> otError Commissioner::Process<Cmd("mgmtset")>(Arg aArgs[])
 {
     otError                error;
     otCommissioningDataset dataset;
@@ -272,7 +299,7 @@
     return error;
 }
 
-otError Commissioner::ProcessPanId(Arg aArgs[])
+template <> otError Commissioner::Process<Cmd("panid")>(Arg aArgs[])
 {
     otError      error;
     uint16_t     panId;
@@ -289,14 +316,14 @@
     return error;
 }
 
-otError Commissioner::ProcessProvisioningUrl(Arg aArgs[])
+template <> otError Commissioner::Process<Cmd("provisioningurl")>(Arg aArgs[])
 {
     // If aArgs[0] is empty, `GetCString() will return `nullptr`
     /// which will correctly clear the provisioning URL.
     return otCommissionerSetProvisioningUrl(GetInstancePtr(), aArgs[0].GetCString());
 }
 
-otError Commissioner::ProcessSessionId(Arg aArgs[])
+template <> otError Commissioner::Process<Cmd("sessionid")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
 
@@ -305,7 +332,24 @@
     return OT_ERROR_NONE;
 }
 
-otError Commissioner::ProcessStart(Arg aArgs[])
+template <> otError Commissioner::Process<Cmd("id")>(Arg aArgs[])
+{
+    otError error;
+
+    if (aArgs[0].IsEmpty())
+    {
+        OutputLine("%s", otCommissionerGetId(GetInstancePtr()));
+        error = OT_ERROR_NONE;
+    }
+    else
+    {
+        error = otCommissionerSetId(GetInstancePtr(), aArgs[0].GetCString());
+    }
+
+    return error;
+}
+
+template <> otError Commissioner::Process<Cmd("start")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
 
@@ -376,14 +420,14 @@
     OutputLine("");
 }
 
-otError Commissioner::ProcessStop(Arg aArgs[])
+template <> otError Commissioner::Process<Cmd("stop")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
 
     return otCommissionerStop(GetInstancePtr());
 }
 
-otError Commissioner::ProcessState(Arg aArgs[])
+template <> otError Commissioner::Process<Cmd("state")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
 
@@ -394,16 +438,31 @@
 
 otError Commissioner::Process(Arg aArgs[])
 {
+#define CmdEntry(aCommandString)                                    \
+    {                                                               \
+        aCommandString, &Commissioner::Process<Cmd(aCommandString)> \
+    }
+
+    static constexpr Command kCommands[] = {
+        CmdEntry("announce"),  CmdEntry("energy"),  CmdEntry("id"),    CmdEntry("joiner"),
+        CmdEntry("mgmtget"),   CmdEntry("mgmtset"), CmdEntry("panid"), CmdEntry("provisioningurl"),
+        CmdEntry("sessionid"), CmdEntry("start"),   CmdEntry("state"), CmdEntry("stop"),
+    };
+
+#undef CmdEntry
+
+    static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
+
     otError        error = OT_ERROR_INVALID_COMMAND;
     const Command *command;
 
-    if (aArgs[0].IsEmpty())
+    if (aArgs[0].IsEmpty() || (aArgs[0] == "help"))
     {
-        IgnoreError(ProcessHelp(aArgs));
-        ExitNow();
+        OutputCommandTable(kCommands);
+        ExitNow(error = aArgs[0].IsEmpty() ? error : OT_ERROR_NONE);
     }
 
-    command = BinarySearch::Find(aArgs[0].GetCString(), sCommands);
+    command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
     VerifyOrExit(command != nullptr);
 
     error = (this->*command->mHandler)(aArgs + 1);
diff --git a/src/cli/cli_commissioner.hpp b/src/cli/cli_commissioner.hpp
index 7360818..d63089d 100644
--- a/src/cli/cli_commissioner.hpp
+++ b/src/cli/cli_commissioner.hpp
@@ -81,18 +81,7 @@
 
     using Command = CommandEntry<Commissioner>;
 
-    otError ProcessHelp(Arg aArgs[]);
-    otError ProcessAnnounce(Arg aArgs[]);
-    otError ProcessEnergy(Arg aArgs[]);
-    otError ProcessJoiner(Arg aArgs[]);
-    otError ProcessMgmtGet(Arg aArgs[]);
-    otError ProcessMgmtSet(Arg aArgs[]);
-    otError ProcessPanId(Arg aArgs[]);
-    otError ProcessProvisioningUrl(Arg aArgs[]);
-    otError ProcessSessionId(Arg aArgs[]);
-    otError ProcessStart(Arg aArgs[]);
-    otError ProcessState(Arg aArgs[]);
-    otError ProcessStop(Arg aArgs[]);
+    template <CommandId kCommandId> otError Process(Arg aArgs[]);
 
     static void HandleStateChanged(otCommissionerState aState, void *aContext);
     void        HandleStateChanged(otCommissionerState aState);
@@ -115,17 +104,6 @@
     void        HandlePanIdConflict(uint16_t aPanId, uint32_t aChannelMask);
 
     static const char *StateToString(otCommissionerState aState);
-
-    static constexpr Command sCommands[] = {
-        {"announce", &Commissioner::ProcessAnnounce},   {"energy", &Commissioner::ProcessEnergy},
-        {"help", &Commissioner::ProcessHelp},           {"joiner", &Commissioner::ProcessJoiner},
-        {"mgmtget", &Commissioner::ProcessMgmtGet},     {"mgmtset", &Commissioner::ProcessMgmtSet},
-        {"panid", &Commissioner::ProcessPanId},         {"provisioningurl", &Commissioner::ProcessProvisioningUrl},
-        {"sessionid", &Commissioner::ProcessSessionId}, {"start", &Commissioner::ProcessStart},
-        {"state", &Commissioner::ProcessState},         {"stop", &Commissioner::ProcessStop},
-    };
-
-    static_assert(BinarySearch::IsSorted(sCommands), "Command Table is not sorted");
 };
 
 } // namespace Cli
diff --git a/src/cli/cli_dataset.cpp b/src/cli/cli_dataset.cpp
index 61641a2..efade05 100644
--- a/src/cli/cli_dataset.cpp
+++ b/src/cli/cli_dataset.cpp
@@ -45,8 +45,7 @@
 namespace ot {
 namespace Cli {
 
-constexpr Dataset::Command Dataset::sCommands[];
-otOperationalDataset       Dataset::sDataset;
+otOperationalDataset Dataset::sDataset;
 
 otError Dataset::Print(otOperationalDataset &aDataset)
 {
@@ -119,38 +118,7 @@
     return OT_ERROR_NONE;
 }
 
-otError Dataset::Process(Arg aArgs[])
-{
-    otError        error = OT_ERROR_INVALID_COMMAND;
-    const Command *command;
-
-    if (aArgs[0].IsEmpty())
-    {
-        ExitNow(error = Print(sDataset));
-    }
-
-    command = BinarySearch::Find(aArgs[0].GetCString(), sCommands);
-    VerifyOrExit(command != nullptr);
-
-    error = (this->*command->mHandler)(aArgs + 1);
-
-exit:
-    return error;
-}
-
-otError Dataset::ProcessHelp(Arg aArgs[])
-{
-    OT_UNUSED_VARIABLE(aArgs);
-
-    for (const Command &command : sCommands)
-    {
-        OutputLine(command.mName);
-    }
-
-    return OT_ERROR_NONE;
-}
-
-otError Dataset::ProcessInit(Arg aArgs[])
+template <> otError Dataset::Process<Cmd("init")>(Arg aArgs[])
 {
     otError error = OT_ERROR_INVALID_ARGS;
 
@@ -183,7 +151,7 @@
     return error;
 }
 
-otError Dataset::ProcessActive(Arg aArgs[])
+template <> otError Dataset::Process<Cmd("active")>(Arg aArgs[])
 {
     otError error = OT_ERROR_INVALID_ARGS;
 
@@ -206,7 +174,7 @@
     return error;
 }
 
-otError Dataset::ProcessPending(Arg aArgs[])
+template <> otError Dataset::Process<Cmd("pending")>(Arg aArgs[])
 {
     otError error = OT_ERROR_INVALID_ARGS;
 
@@ -229,7 +197,7 @@
     return error;
 }
 
-otError Dataset::ProcessActiveTimestamp(Arg aArgs[])
+template <> otError Dataset::Process<Cmd("activetimestamp")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -250,7 +218,7 @@
     return error;
 }
 
-otError Dataset::ProcessChannel(Arg aArgs[])
+template <> otError Dataset::Process<Cmd("channel")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -271,7 +239,7 @@
     return error;
 }
 
-otError Dataset::ProcessChannelMask(Arg aArgs[])
+template <> otError Dataset::Process<Cmd("channelmask")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -292,7 +260,7 @@
     return error;
 }
 
-otError Dataset::ProcessClear(Arg aArgs[])
+template <> otError Dataset::Process<Cmd("clear")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
 
@@ -300,7 +268,7 @@
     return OT_ERROR_NONE;
 }
 
-otError Dataset::ProcessCommit(Arg aArgs[])
+template <> otError Dataset::Process<Cmd("commit")>(Arg aArgs[])
 {
     otError error = OT_ERROR_INVALID_ARGS;
 
@@ -316,7 +284,7 @@
     return error;
 }
 
-otError Dataset::ProcessDelay(Arg aArgs[])
+template <> otError Dataset::Process<Cmd("delay")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -337,7 +305,7 @@
     return error;
 }
 
-otError Dataset::ProcessExtPanId(Arg aArgs[])
+template <> otError Dataset::Process<Cmd("extpanid")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -358,7 +326,7 @@
     return error;
 }
 
-otError Dataset::ProcessMeshLocalPrefix(Arg aArgs[])
+template <> otError Dataset::Process<Cmd("meshlocalprefix")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -384,7 +352,7 @@
     return error;
 }
 
-otError Dataset::ProcessNetworkKey(Arg aArgs[])
+template <> otError Dataset::Process<Cmd("networkkey")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -405,7 +373,7 @@
     return error;
 }
 
-otError Dataset::ProcessNetworkName(Arg aArgs[])
+template <> otError Dataset::Process<Cmd("networkname")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -426,7 +394,7 @@
     return error;
 }
 
-otError Dataset::ProcessPanId(Arg aArgs[])
+template <> otError Dataset::Process<Cmd("panid")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -447,7 +415,7 @@
     return error;
 }
 
-otError Dataset::ProcessPendingTimestamp(Arg aArgs[])
+template <> otError Dataset::Process<Cmd("pendingtimestamp")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -468,7 +436,7 @@
     return error;
 }
 
-otError Dataset::ProcessMgmtSetCommand(Arg aArgs[])
+template <> otError Dataset::Process<Cmd("mgmtsetcommand")>(Arg aArgs[])
 {
     otError              error = OT_ERROR_NONE;
     otOperationalDataset dataset;
@@ -583,7 +551,7 @@
     return error;
 }
 
-otError Dataset::ProcessMgmtGetCommand(Arg aArgs[])
+template <> otError Dataset::Process<Cmd("mgmtgetcommand")>(Arg aArgs[])
 {
     otError                        error = OT_ERROR_NONE;
     otOperationalDatasetComponents datasetComponents;
@@ -676,7 +644,7 @@
     return error;
 }
 
-otError Dataset::ProcessPskc(Arg aArgs[])
+template <> otError Dataset::Process<Cmd("pskc")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -743,11 +711,6 @@
         OutputFormat("c");
     }
 
-    if (aSecurityPolicy.mBeaconsEnabled)
-    {
-        OutputFormat("b");
-    }
-
     if (aSecurityPolicy.mCommercialCommissioningEnabled)
     {
         OutputFormat("C");
@@ -803,10 +766,6 @@
             policy.mExternalCommissioningEnabled = true;
             break;
 
-        case 'b':
-            policy.mBeaconsEnabled = true;
-            break;
-
         case 'C':
             policy.mCommercialCommissioningEnabled = true;
             break;
@@ -839,7 +798,7 @@
     return error;
 }
 
-otError Dataset::ProcessSecurityPolicy(Arg aArgs[])
+template <> otError Dataset::Process<Cmd("securitypolicy")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -862,7 +821,7 @@
     return error;
 }
 
-otError Dataset::ProcessSet(Arg aArgs[])
+template <> otError Dataset::Process<Cmd("set")>(Arg aArgs[])
 {
     otError                error = OT_ERROR_NONE;
     MeshCoP::Dataset::Type datasetType;
@@ -907,7 +866,7 @@
 
 #if OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE && OPENTHREAD_FTD
 
-otError Dataset::ProcessUpdater(Arg aArgs[])
+template <> otError Dataset::Process<Cmd("updater")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -943,5 +902,65 @@
 
 #endif // OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE && OPENTHREAD_FTD
 
+otError Dataset::Process(Arg aArgs[])
+{
+#define CmdEntry(aCommandString)                               \
+    {                                                          \
+        aCommandString, &Dataset::Process<Cmd(aCommandString)> \
+    }
+
+    static constexpr Command kCommands[] = {
+        CmdEntry("active"),
+        CmdEntry("activetimestamp"),
+        CmdEntry("channel"),
+        CmdEntry("channelmask"),
+        CmdEntry("clear"),
+        CmdEntry("commit"),
+        CmdEntry("delay"),
+        CmdEntry("extpanid"),
+        CmdEntry("init"),
+        CmdEntry("meshlocalprefix"),
+        CmdEntry("mgmtgetcommand"),
+        CmdEntry("mgmtsetcommand"),
+        CmdEntry("networkkey"),
+        CmdEntry("networkname"),
+        CmdEntry("panid"),
+        CmdEntry("pending"),
+        CmdEntry("pendingtimestamp"),
+        CmdEntry("pskc"),
+        CmdEntry("securitypolicy"),
+        CmdEntry("set"),
+#if OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE && OPENTHREAD_FTD
+        CmdEntry("updater"),
+#endif
+    };
+
+#undef CmdEntry
+
+    static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
+
+    otError        error = OT_ERROR_INVALID_COMMAND;
+    const Command *command;
+
+    if (aArgs[0].IsEmpty())
+    {
+        ExitNow(error = Print(sDataset));
+    }
+
+    if (aArgs[0] == "help")
+    {
+        OutputCommandTable(kCommands);
+        ExitNow(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
diff --git a/src/cli/cli_dataset.hpp b/src/cli/cli_dataset.hpp
index d32afdd..980bb5d 100644
--- a/src/cli/cli_dataset.hpp
+++ b/src/cli/cli_dataset.hpp
@@ -70,29 +70,9 @@
 private:
     using Command = CommandEntry<Dataset>;
 
-    otError Print(otOperationalDataset &aDataset);
+    template <CommandId kCommandId> otError Process(Arg aArgs[]);
 
-    otError ProcessHelp(Arg aArgs[]);
-    otError ProcessActive(Arg aArgs[]);
-    otError ProcessActiveTimestamp(Arg aArgs[]);
-    otError ProcessChannel(Arg aArgs[]);
-    otError ProcessChannelMask(Arg aArgs[]);
-    otError ProcessClear(Arg aArgs[]);
-    otError ProcessCommit(Arg aArgs[]);
-    otError ProcessDelay(Arg aArgs[]);
-    otError ProcessExtPanId(Arg aArgs[]);
-    otError ProcessInit(Arg aArgs[]);
-    otError ProcessMeshLocalPrefix(Arg aArgs[]);
-    otError ProcessNetworkName(Arg aArgs[]);
-    otError ProcessNetworkKey(Arg aArgs[]);
-    otError ProcessPanId(Arg aArgs[]);
-    otError ProcessPending(Arg aArgs[]);
-    otError ProcessPendingTimestamp(Arg aArgs[]);
-    otError ProcessMgmtSetCommand(Arg aArgs[]);
-    otError ProcessMgmtGetCommand(Arg aArgs[]);
-    otError ProcessPskc(Arg aArgs[]);
-    otError ProcessSecurityPolicy(Arg aArgs[]);
-    otError ProcessSet(Arg aArgs[]);
+    otError Print(otOperationalDataset &aDataset);
 
 #if OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE && OPENTHREAD_FTD
     otError     ProcessUpdater(Arg aArgs[]);
@@ -103,35 +83,6 @@
     void    OutputSecurityPolicy(const otSecurityPolicy &aSecurityPolicy);
     otError ParseSecurityPolicy(otSecurityPolicy &aSecurityPolicy, Arg *&aArgs);
 
-    static constexpr Command sCommands[] = {
-        {"active", &Dataset::ProcessActive},
-        {"activetimestamp", &Dataset::ProcessActiveTimestamp},
-        {"channel", &Dataset::ProcessChannel},
-        {"channelmask", &Dataset::ProcessChannelMask},
-        {"clear", &Dataset::ProcessClear},
-        {"commit", &Dataset::ProcessCommit},
-        {"delay", &Dataset::ProcessDelay},
-        {"extpanid", &Dataset::ProcessExtPanId},
-        {"help", &Dataset::ProcessHelp},
-        {"init", &Dataset::ProcessInit},
-        {"meshlocalprefix", &Dataset::ProcessMeshLocalPrefix},
-        {"mgmtgetcommand", &Dataset::ProcessMgmtGetCommand},
-        {"mgmtsetcommand", &Dataset::ProcessMgmtSetCommand},
-        {"networkkey", &Dataset::ProcessNetworkKey},
-        {"networkname", &Dataset::ProcessNetworkName},
-        {"panid", &Dataset::ProcessPanId},
-        {"pending", &Dataset::ProcessPending},
-        {"pendingtimestamp", &Dataset::ProcessPendingTimestamp},
-        {"pskc", &Dataset::ProcessPskc},
-        {"securitypolicy", &Dataset::ProcessSecurityPolicy},
-        {"set", &Dataset::ProcessSet},
-#if OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE && OPENTHREAD_FTD
-        {"updater", &Dataset::ProcessUpdater},
-#endif
-    };
-
-    static_assert(BinarySearch::IsSorted(sCommands), "Command Table is not sorted");
-
     static otOperationalDataset sDataset;
 };
 
diff --git a/src/cli/cli_history.cpp b/src/cli/cli_history.cpp
index 422ee61..5f6095c 100644
--- a/src/cli/cli_history.cpp
+++ b/src/cli/cli_history.cpp
@@ -42,25 +42,11 @@
 namespace ot {
 namespace Cli {
 
-constexpr History::Command History::sCommands[];
-
 static const char *const kSimpleEventStrings[] = {
     "Added",  // (0) OT_HISTORY_TRACKER_{NET_DATA_ENTRY/ADDRESSS_EVENT}_ADDED
     "Removed" // (1) OT_HISTORY_TRACKER_{NET_DATA_ENTRY/ADDRESS_EVENT}_REMOVED
 };
 
-otError History::ProcessHelp(Arg aArgs[])
-{
-    OT_UNUSED_VARIABLE(aArgs);
-
-    for (const Command &command : sCommands)
-    {
-        OutputLine(command.mName);
-    }
-
-    return OT_ERROR_NONE;
-}
-
 otError History::ParseArgs(Arg aArgs[], bool &aIsList, uint16_t &aNumEntries) const
 {
     if (*aArgs == "list")
@@ -85,7 +71,7 @@
     return aArgs[0].IsEmpty() ? OT_ERROR_NONE : OT_ERROR_INVALID_ARGS;
 }
 
-otError History::ProcessIpAddr(Arg aArgs[])
+template <> otError History::Process<Cmd("ipaddr")>(Arg aArgs[])
 {
     otError                                   error;
     bool                                      isList;
@@ -146,7 +132,7 @@
     return error;
 }
 
-otError History::ProcessIpMulticastAddr(Arg aArgs[])
+template <> otError History::Process<Cmd("ipmaddr")>(Arg aArgs[])
 {
     static const char *const kEventStrings[] = {
         "Subscribed",  // (0) OT_HISTORY_TRACKER_ADDRESS_EVENT_ADDED
@@ -203,7 +189,7 @@
     return error;
 }
 
-otError History::ProcessNeighbor(Arg aArgs[])
+template <> otError History::Process<Cmd("neighbor")>(Arg aArgs[])
 {
     static const char *const kEventString[] = {
         /* (0) OT_HISTORY_TRACKER_NEIGHBOR_EVENT_ADDED     -> */ "Added",
@@ -268,7 +254,7 @@
     return error;
 }
 
-otError History::ProcessNetInfo(Arg aArgs[])
+template <> otError History::Process<Cmd("netinfo")>(Arg aArgs[])
 {
     otError                            error;
     bool                               isList;
@@ -311,17 +297,17 @@
     return error;
 }
 
-otError History::ProcessRx(Arg aArgs[])
+template <> otError History::Process<Cmd("rx")>(Arg aArgs[])
 {
     return ProcessRxTxHistory(kRx, aArgs);
 }
 
-otError History::ProcessRxTx(Arg aArgs[])
+template <> otError History::Process<Cmd("rxtx")>(Arg aArgs[])
 {
     return ProcessRxTxHistory(kRxTx, aArgs);
 }
 
-otError History::ProcessTx(Arg aArgs[])
+template <> otError History::Process<Cmd("tx")>(Arg aArgs[])
 {
     return ProcessRxTxHistory(kTx, aArgs);
 }
@@ -571,7 +557,7 @@
     OutputLine("| %20s | dst: %-70s |", "", addrString);
 }
 
-otError History::ProcessPrefix(Arg aArgs[])
+template <> otError History::Process<Cmd("prefix")>(Arg aArgs[])
 {
     otError                                 error;
     bool                                    isList;
@@ -621,7 +607,7 @@
     return error;
 }
 
-otError History::ProcessRoute(Arg aArgs[])
+template <> otError History::Process<Cmd("route")>(Arg aArgs[])
 {
     otError                                  error;
     bool                                     isList;
@@ -673,16 +659,30 @@
 
 otError History::Process(Arg aArgs[])
 {
+#define CmdEntry(aCommandString)                               \
+    {                                                          \
+        aCommandString, &History::Process<Cmd(aCommandString)> \
+    }
+
+    static constexpr Command kCommands[] = {
+        CmdEntry("ipaddr"), CmdEntry("ipmaddr"), CmdEntry("neighbor"), CmdEntry("netinfo"), CmdEntry("prefix"),
+        CmdEntry("route"),  CmdEntry("rx"),      CmdEntry("rxtx"),     CmdEntry("tx"),
+    };
+
+#undef CmdEntry
+
+    static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
+
     otError        error = OT_ERROR_INVALID_COMMAND;
     const Command *command;
 
-    if (aArgs[0].IsEmpty())
+    if (aArgs[0].IsEmpty() || (aArgs[0] == "help"))
     {
-        IgnoreError(ProcessHelp(aArgs));
-        ExitNow();
+        OutputCommandTable(kCommands);
+        ExitNow(error = aArgs[0].IsEmpty() ? error : OT_ERROR_NONE);
     }
 
-    command = BinarySearch::Find(aArgs[0].GetCString(), sCommands);
+    command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
     VerifyOrExit(command != nullptr);
 
     error = (this->*command->mHandler)(aArgs + 1);
diff --git a/src/cli/cli_history.hpp b/src/cli/cli_history.hpp
index b39a877..a17a667 100644
--- a/src/cli/cli_history.hpp
+++ b/src/cli/cli_history.hpp
@@ -88,16 +88,7 @@
         kRxTx,
     };
 
-    otError ProcessHelp(Arg aArgs[]);
-    otError ProcessIpAddr(Arg aArgs[]);
-    otError ProcessIpMulticastAddr(Arg aArgs[]);
-    otError ProcessNetInfo(Arg aArgs[]);
-    otError ProcessNeighbor(Arg aArgs[]);
-    otError ProcessPrefix(Arg aArgs[]);
-    otError ProcessRoute(Arg aArgs[]);
-    otError ProcessRx(Arg aArgs[]);
-    otError ProcessRxTx(Arg aArgs[]);
-    otError ProcessTx(Arg aArgs[]);
+    template <CommandId kCommandId> otError Process(Arg aArgs[]);
 
     otError ParseArgs(Arg aArgs[], bool &aIsList, uint16_t &aNumEntries) const;
     otError ProcessRxTxHistory(RxTx aRxTx, Arg aArgs[]);
@@ -107,21 +98,6 @@
     static const char *MessagePriorityToString(uint8_t aPriority);
     static const char *RadioTypeToString(const otHistoryTrackerMessageInfo &aInfo);
     static const char *MessageTypeToString(const otHistoryTrackerMessageInfo &aInfo);
-
-    static constexpr Command sCommands[] = {
-        {"help", &History::ProcessHelp},
-        {"ipaddr", &History::ProcessIpAddr},
-        {"ipmaddr", &History::ProcessIpMulticastAddr},
-        {"neighbor", &History::ProcessNeighbor},
-        {"netinfo", &History::ProcessNetInfo},
-        {"prefix", &History::ProcessPrefix},
-        {"route", &History::ProcessRoute},
-        {"rx", &History::ProcessRx},
-        {"rxtx", &History::ProcessRxTx},
-        {"tx", &History::ProcessTx},
-    };
-
-    static_assert(BinarySearch::IsSorted(sCommands), "Command Table is not sorted");
 };
 
 } // namespace Cli
diff --git a/src/cli/cli_joiner.cpp b/src/cli/cli_joiner.cpp
index a4c6e73..c4509fe 100644
--- a/src/cli/cli_joiner.cpp
+++ b/src/cli/cli_joiner.cpp
@@ -42,9 +42,7 @@
 namespace ot {
 namespace Cli {
 
-constexpr Joiner::Command Joiner::sCommands[];
-
-otError Joiner::ProcessDiscerner(Arg aArgs[])
+template <> otError Joiner::Process<Cmd("discerner")>(Arg aArgs[])
 {
     otError error = OT_ERROR_INVALID_ARGS;
 
@@ -79,19 +77,7 @@
     return error;
 }
 
-otError Joiner::ProcessHelp(Arg aArgs[])
-{
-    OT_UNUSED_VARIABLE(aArgs);
-
-    for (const Command &command : sCommands)
-    {
-        OutputLine(command.mName);
-    }
-
-    return OT_ERROR_NONE;
-}
-
-otError Joiner::ProcessId(Arg aArgs[])
+template <> otError Joiner::Process<Cmd("id")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
 
@@ -100,7 +86,7 @@
     return OT_ERROR_NONE;
 }
 
-otError Joiner::ProcessStart(Arg aArgs[])
+template <> otError Joiner::Process<Cmd("start")>(Arg aArgs[])
 {
     otError error;
 
@@ -119,7 +105,7 @@
     return error;
 }
 
-otError Joiner::ProcessStop(Arg aArgs[])
+template <> otError Joiner::Process<Cmd("stop")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
 
@@ -128,7 +114,7 @@
     return OT_ERROR_NONE;
 }
 
-otError Joiner::ProcessState(Arg aArgs[])
+template <> otError Joiner::Process<Cmd("state")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
 
@@ -139,16 +125,29 @@
 
 otError Joiner::Process(Arg aArgs[])
 {
+#define CmdEntry(aCommandString)                              \
+    {                                                         \
+        aCommandString, &Joiner::Process<Cmd(aCommandString)> \
+    }
+
+    static constexpr Command kCommands[] = {
+        CmdEntry("discerner"), CmdEntry("id"), CmdEntry("start"), CmdEntry("state"), CmdEntry("stop"),
+    };
+
+#undef CmdEntry
+
+    static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
+
     otError        error = OT_ERROR_INVALID_COMMAND;
     const Command *command;
 
-    if (aArgs[0].IsEmpty())
+    if (aArgs[0].IsEmpty() || (aArgs[0] == "help"))
     {
-        IgnoreError(ProcessHelp(aArgs));
-        ExitNow();
+        OutputCommandTable(kCommands);
+        ExitNow(error = aArgs[0].IsEmpty() ? error : OT_ERROR_NONE);
     }
 
-    command = BinarySearch::Find(aArgs[0].GetCString(), sCommands);
+    command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
     VerifyOrExit(command != nullptr);
 
     error = (this->*command->mHandler)(aArgs + 1);
diff --git a/src/cli/cli_joiner.hpp b/src/cli/cli_joiner.hpp
index f86a62d..d2bc640 100644
--- a/src/cli/cli_joiner.hpp
+++ b/src/cli/cli_joiner.hpp
@@ -76,22 +76,10 @@
 private:
     using Command = CommandEntry<Joiner>;
 
-    otError ProcessDiscerner(Arg aArgs[]);
-    otError ProcessHelp(Arg aArgs[]);
-    otError ProcessId(Arg aArgs[]);
-    otError ProcessStart(Arg aArgs[]);
-    otError ProcessStop(Arg aArgs[]);
-    otError ProcessState(Arg aArgs[]);
+    template <CommandId kCommandId> otError Process(Arg aArgs[]);
 
     static void HandleCallback(otError aError, void *aContext);
     void        HandleCallback(otError aError);
-
-    static constexpr Command sCommands[] = {
-        {"discerner", &Joiner::ProcessDiscerner}, {"help", &Joiner::ProcessHelp},   {"id", &Joiner::ProcessId},
-        {"start", &Joiner::ProcessStart},         {"state", &Joiner::ProcessState}, {"stop", &Joiner::ProcessStop},
-    };
-
-    static_assert(BinarySearch::IsSorted(sCommands), "Command Table is not sorted");
 };
 
 } // namespace Cli
diff --git a/src/cli/cli_network_data.cpp b/src/cli/cli_network_data.cpp
index 54a2239..ee02b1b 100644
--- a/src/cli/cli_network_data.cpp
+++ b/src/cli/cli_network_data.cpp
@@ -43,8 +43,6 @@
 namespace ot {
 namespace Cli {
 
-constexpr NetworkData::Command NetworkData::sCommands[];
-
 void NetworkData::PrefixFlagsToString(const otBorderRouterConfig &aConfig, FlagsString &aString)
 {
     char *flagsPtr = &aString[0];
@@ -186,20 +184,8 @@
     OutputLine(" %04x", aConfig.mServerConfig.mRloc16);
 }
 
-otError NetworkData::ProcessHelp(Arg aArgs[])
-{
-    OT_UNUSED_VARIABLE(aArgs);
-
-    for (const Command &command : sCommands)
-    {
-        OutputLine(command.mName);
-    }
-
-    return OT_ERROR_NONE;
-}
-
 #if OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE
-otError NetworkData::ProcessPublish(Arg aArgs[])
+template <> otError NetworkData::Process<Cmd("publish")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -261,7 +247,7 @@
     return error;
 }
 
-otError NetworkData::ProcessUnpublish(Arg aArgs[])
+template <> otError NetworkData::Process<Cmd("unpublish")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -293,7 +279,7 @@
 #endif // OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE
 
 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE || OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
-otError NetworkData::ProcessRegister(Arg aArgs[])
+template <> otError NetworkData::Process<Cmd("register")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
 
@@ -310,7 +296,7 @@
 }
 #endif
 
-otError NetworkData::ProcessSteeringData(Arg aArgs[])
+template <> otError NetworkData::Process<Cmd("steeringdata")>(Arg aArgs[])
 {
     otError           error;
     otExtAddress      addr;
@@ -466,7 +452,7 @@
     return error;
 }
 
-otError NetworkData::ProcessShow(Arg aArgs[])
+template <> otError NetworkData::Process<Cmd("show")>(Arg aArgs[])
 {
     otError error  = OT_ERROR_INVALID_ARGS;
     bool    local  = false;
@@ -506,16 +492,39 @@
 
 otError NetworkData::Process(Arg aArgs[])
 {
+#define CmdEntry(aCommandString)                                   \
+    {                                                              \
+        aCommandString, &NetworkData::Process<Cmd(aCommandString)> \
+    }
+
+    static constexpr Command kCommands[] = {
+#if OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE
+        CmdEntry("publish"),
+#endif
+#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE || OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
+        CmdEntry("register"),
+#endif
+        CmdEntry("show"),
+        CmdEntry("steeringdata"),
+#if OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE
+        CmdEntry("unpublish"),
+#endif
+    };
+
+#undef CmdEntry
+
+    static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
+
     otError        error = OT_ERROR_INVALID_COMMAND;
     const Command *command;
 
-    if (aArgs[0].IsEmpty())
+    if (aArgs[0].IsEmpty() || (aArgs[0] == "help"))
     {
-        IgnoreError(ProcessHelp(aArgs));
-        ExitNow();
+        OutputCommandTable(kCommands);
+        ExitNow(error = aArgs[0].IsEmpty() ? error : OT_ERROR_NONE);
     }
 
-    command = BinarySearch::Find(aArgs[0].GetCString(), sCommands);
+    command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
     VerifyOrExit(command != nullptr);
 
     error = (this->*command->mHandler)(aArgs + 1);
diff --git a/src/cli/cli_network_data.hpp b/src/cli/cli_network_data.hpp
index 2efc3a7..0fb2098 100644
--- a/src/cli/cli_network_data.hpp
+++ b/src/cli/cli_network_data.hpp
@@ -138,16 +138,7 @@
 private:
     using Command = CommandEntry<NetworkData>;
 
-    otError ProcessHelp(Arg aArgs[]);
-#if OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE
-    otError ProcessPublish(Arg aArgs[]);
-    otError ProcessUnpublish(Arg aArgs[]);
-#endif
-#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE || OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
-    otError ProcessRegister(Arg aArgs[]);
-#endif
-    otError ProcessShow(Arg aArgs[]);
-    otError ProcessSteeringData(Arg aArgs[]);
+    template <CommandId kCommandId> otError Process(Arg aArgs[]);
 
     otError GetNextPrefix(otNetworkDataIterator *aIterator, otBorderRouterConfig *aConfig, bool aLocal);
     otError GetNextRoute(otNetworkDataIterator *aIterator, otExternalRouteConfig *aConfig, bool aLocal);
@@ -157,23 +148,6 @@
     void    OutputPrefixes(bool aLocal);
     void    OutputRoutes(bool aLocal);
     void    OutputServices(bool aLocal);
-
-    static constexpr Command sCommands[] = {
-        {"help", &NetworkData::ProcessHelp},
-#if OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE
-        {"publish", &NetworkData::ProcessPublish},
-#endif
-#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE || OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
-        {"register", &NetworkData::ProcessRegister},
-#endif
-        {"show", &NetworkData::ProcessShow},
-        {"steeringdata", &NetworkData::ProcessSteeringData},
-#if OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE
-        {"unpublish", &NetworkData::ProcessUnpublish},
-#endif
-    };
-
-    static_assert(BinarySearch::IsSorted(sCommands), "Command Table is not sorted");
 };
 
 } // namespace Cli
diff --git a/src/cli/cli_output.cpp b/src/cli/cli_output.cpp
index af712af..7d2208e 100644
--- a/src/cli/cli_output.cpp
+++ b/src/cli/cli_output.cpp
@@ -40,8 +40,8 @@
 #if OPENTHREAD_FTD || OPENTHREAD_MTD
 #include <openthread/dns.h>
 #endif
+#include <openthread/logging.h>
 
-#include "common/logging.hpp"
 #include "common/string.hpp"
 
 namespace ot {
@@ -285,7 +285,7 @@
 
         if (lineEnd > mOutputString)
         {
-            otLogNoteCli("Output: %s", mOutputString);
+            otLogCli(OT_LOG_LEVEL_DEBG, "Output: %s", mOutputString);
         }
 
         lineEnd++;
@@ -326,7 +326,7 @@
 
     if (truncated)
     {
-        otLogNoteCli("Output: %s ...", mOutputString);
+        otLogCli(OT_LOG_LEVEL_DEBG, "Output: %s ...", mOutputString);
         mOutputLength = 0;
     }
 
@@ -345,7 +345,7 @@
         inputString.Append(isFirst ? "%s" : " %s", aArgs->GetCString());
     }
 
-    otLogNoteCli("Input: %s", inputString.AsCString());
+    otLogCli(OT_LOG_LEVEL_DEBG, "Input: %s", inputString.AsCString());
 }
 #endif
 
diff --git a/src/cli/cli_output.hpp b/src/cli/cli_output.hpp
index a7aa402..61e8997 100644
--- a/src/cli/cli_output.hpp
+++ b/src/cli/cli_output.hpp
@@ -50,12 +50,73 @@
 namespace Cli {
 
 /**
+ * This type represents a ID number value associated with a CLI command string.
+ *
+ */
+typedef uint64_t CommandId;
+
+/**
+ * This `constexpr` function converts a CLI command string to its associated `CommandId` value.
+ *
+ * @param[in] aString   The CLI command string.
+ *
+ * @returns The associated `CommandId` with @p aString.
+ *
+ */
+constexpr static CommandId Cmd(const char *aString)
+{
+    return (aString[0] == '\0') ? 0 : (static_cast<uint8_t>(aString[0]) + Cmd(aString + 1) * 255u);
+}
+
+/**
  * This class is the base class for `Output` and `OutputWrapper` providing common helper methods.
  *
  */
 class OutputBase
 {
 public:
+    typedef Utils::CmdLineParser::Arg Arg; ///< An argument
+
+    /**
+     * This structure represent a CLI command table entry, mapping a command with `aName` to a handler method.
+     *
+     * @tparam Cli    The CLI module type.
+     *
+     */
+    template <typename Cli> struct CommandEntry
+    {
+        typedef otError (Cli::*Handler)(Arg aArgs[]); ///< The handler method pointer type.
+
+        /**
+         * This method compares the entry's name with a given name.
+         *
+         * @param aName    The name string to compare with.
+         *
+         * @return zero means perfect match, positive (> 0) indicates @p aName is larger than entry's name, and
+         *         negative (< 0) indicates @p aName is smaller than entry's name.
+         *
+         */
+        int Compare(const char *aName) const { return strcmp(aName, mName); }
+
+        /**
+         * This `constexpr` method compares two entries to check if they are in order.
+         *
+         * @param[in] aFirst     The first entry.
+         * @param[in] aSecond    The second entry.
+         *
+         * @retval TRUE  if @p aFirst and @p aSecond are in order, i.e. `aFirst < aSecond`.
+         * @retval FALSE if @p aFirst and @p aSecond are not in order, i.e. `aFirst >= aSecond`.
+         *
+         */
+        constexpr static bool AreInOrder(const CommandEntry &aFirst, const CommandEntry &aSecond)
+        {
+            return AreStringsInOrder(aFirst.mName, aSecond.mName);
+        }
+
+        const char *mName;    ///< The command name.
+        Handler     mHandler; ///< The handler method pointer.
+    };
+
     static const char kUnknownString[]; // Constant string "unknown".
 
     /**
@@ -82,23 +143,6 @@
 
 protected:
     OutputBase(void) = default;
-
-    typedef Utils::CmdLineParser::Arg Arg;
-
-    template <typename Cli> struct CommandEntry
-    {
-        typedef otError (Cli::*Handler)(Arg aArgs[]);
-
-        int Compare(const char *aName) const { return strcmp(aName, mName); }
-
-        constexpr static bool AreInOrder(const CommandEntry &aFirst, const CommandEntry &aSecond)
-        {
-            return AreStringsInOrder(aFirst.mName, aSecond.mName);
-        }
-
-        const char *mName;
-        Handler     mHandler;
-    };
 };
 
 /**
@@ -335,7 +379,7 @@
      * @tparam kTableNumColumns   The number columns in the table.
      *
      * @param[in] aTitles   An array specifying the table column titles.
-     * @param[in] aWidth    An array specifying the table column widths (in number of chars).
+     * @param[in] aWidths   An array specifying the table column widths (in number of chars).
      *
      */
     template <uint8_t kTableNumColumns>
@@ -355,7 +399,7 @@
      *
      * @tparam kTableNumColumns   The number columns in the table.
      *
-     * @param[in] aWidth    An array specifying the table column widths (in number of chars).
+     * @param[in] aWidths   An array specifying the table column widths (in number of chars).
      *
      */
     template <uint8_t kTableNumColumns> void OutputTableSeparator(const uint8_t (&aWidths)[kTableNumColumns])
@@ -363,6 +407,23 @@
         OutputTableSeparator(kTableNumColumns, &aWidths[0]);
     }
 
+    /**
+     * This method outputs the list of commands from a given command table.
+     *
+     * @tparam Cli      The CLI module type.
+     * @tparam kLength  The length of command table array.
+     *
+     * @param[in] aCommandTable   The command table array.
+     *
+     */
+    template <typename Cli, uint16_t kLength> void OutputCommandTable(const CommandEntry<Cli> (&aCommandTable)[kLength])
+    {
+        for (const CommandEntry<Cli> &entry : aCommandTable)
+        {
+            OutputLine("%s", entry.mName);
+        }
+    }
+
 protected:
     void OutputFormatV(const char *aFormat, va_list aArguments);
 
@@ -463,6 +524,11 @@
         mOutput.OutputTableSeparator(aWidths);
     }
 
+    template <typename Cli, uint16_t kLength> void OutputCommandTable(const CommandEntry<Cli> (&aCommandTable)[kLength])
+    {
+        mOutput.OutputCommandTable(aCommandTable);
+    }
+
 private:
     Output &mOutput;
 };
diff --git a/src/cli/cli_srp_client.cpp b/src/cli/cli_srp_client.cpp
index 7ffa91c..fa1b740 100644
--- a/src/cli/cli_srp_client.cpp
+++ b/src/cli/cli_srp_client.cpp
@@ -42,8 +42,6 @@
 namespace ot {
 namespace Cli {
 
-constexpr SrpClient::Command SrpClient::sCommands[];
-
 static otError CopyString(char *aDest, uint16_t aDestSize, const char *aSource)
 {
     // Copies a string from `aSource` to `aDestination` (char array),
@@ -66,29 +64,9 @@
     otSrpClientSetCallback(GetInstancePtr(), SrpClient::HandleCallback, this);
 }
 
-otError SrpClient::Process(Arg aArgs[])
-{
-    otError        error = OT_ERROR_INVALID_COMMAND;
-    const Command *command;
-
-    if (aArgs[0].IsEmpty())
-    {
-        IgnoreError(ProcessHelp(aArgs));
-        ExitNow();
-    }
-
-    command = BinarySearch::Find(aArgs[0].GetCString(), sCommands);
-    VerifyOrExit(command != nullptr);
-
-    error = (this->*command->mHandler)(aArgs + 1);
-
-exit:
-    return error;
-}
-
 #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
 
-otError SrpClient::ProcessAutoStart(Arg aArgs[])
+template <> otError SrpClient::Process<Cmd("autostart")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
     bool    enable;
@@ -116,7 +94,7 @@
 
 #endif // OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
 
-otError SrpClient::ProcessCallback(Arg aArgs[])
+template <> otError SrpClient::Process<Cmd("callback")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -132,19 +110,7 @@
     return error;
 }
 
-otError SrpClient::ProcessHelp(Arg aArgs[])
-{
-    OT_UNUSED_VARIABLE(aArgs);
-
-    for (const Command &command : sCommands)
-    {
-        OutputLine(command.mName);
-    }
-
-    return OT_ERROR_NONE;
-}
-
-otError SrpClient::ProcessHost(Arg aArgs[])
+template <> otError SrpClient::Process<Cmd("host")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -265,18 +231,18 @@
     return error;
 }
 
-otError SrpClient::ProcessLeaseInterval(Arg aArgs[])
+template <> otError SrpClient::Process<Cmd("leaseinterval")>(Arg aArgs[])
 {
     return Interpreter::GetInterpreter().ProcessGetSet(aArgs, otSrpClientGetLeaseInterval, otSrpClientSetLeaseInterval);
 }
 
-otError SrpClient::ProcessKeyLeaseInterval(Arg aArgs[])
+template <> otError SrpClient::Process<Cmd("keyleaseinterval")>(Arg aArgs[])
 {
     return Interpreter::GetInterpreter().ProcessGetSet(aArgs, otSrpClientGetKeyLeaseInterval,
                                                        otSrpClientSetKeyLeaseInterval);
 }
 
-otError SrpClient::ProcessServer(Arg aArgs[])
+template <> otError SrpClient::Process<Cmd("server")>(Arg aArgs[])
 {
     otError           error          = OT_ERROR_NONE;
     const otSockAddr *serverSockAddr = otSrpClientGetServerAddress(GetInstancePtr());
@@ -306,7 +272,7 @@
     return error;
 }
 
-otError SrpClient::ProcessService(Arg aArgs[])
+template <> otError SrpClient::Process<Cmd("service")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
     bool    isRemove;
@@ -521,7 +487,7 @@
                aService.mPort, aService.mPriority, aService.mWeight);
 }
 
-otError SrpClient::ProcessStart(Arg aArgs[])
+template <> otError SrpClient::Process<Cmd("start")>(Arg aArgs[])
 {
     otError    error = OT_ERROR_NONE;
     otSockAddr serverSockAddr;
@@ -536,7 +502,7 @@
     return error;
 }
 
-otError SrpClient::ProcessState(Arg aArgs[])
+template <> otError SrpClient::Process<Cmd("state")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -548,7 +514,7 @@
     return error;
 }
 
-otError SrpClient::ProcessStop(Arg aArgs[])
+template <> otError SrpClient::Process<Cmd("stop")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -602,6 +568,39 @@
     }
 }
 
+otError SrpClient::Process(Arg aArgs[])
+{
+#define CmdEntry(aCommandString)                                 \
+    {                                                            \
+        aCommandString, &SrpClient::Process<Cmd(aCommandString)> \
+    }
+
+    static constexpr Command kCommands[] = {
+        CmdEntry("autostart"),     CmdEntry("callback"), CmdEntry("host"),    CmdEntry("keyleaseinterval"),
+        CmdEntry("leaseinterval"), CmdEntry("server"),   CmdEntry("service"), CmdEntry("start"),
+        CmdEntry("state"),         CmdEntry("stop"),
+    };
+
+    static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
+
+    otError        error = OT_ERROR_INVALID_COMMAND;
+    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
 
diff --git a/src/cli/cli_srp_client.hpp b/src/cli/cli_srp_client.hpp
index 5f73ae6..b868e6f 100644
--- a/src/cli/cli_srp_client.hpp
+++ b/src/cli/cli_srp_client.hpp
@@ -81,18 +81,9 @@
 
     using Command = CommandEntry<SrpClient>;
 
-    otError ProcessAutoStart(Arg aArgs[]);
-    otError ProcessCallback(Arg aArgs[]);
-    otError ProcessHelp(Arg aArgs[]);
-    otError ProcessHost(Arg aArgs[]);
-    otError ProcessLeaseInterval(Arg aArgs[]);
-    otError ProcessKeyLeaseInterval(Arg aArgs[]);
-    otError ProcessServer(Arg aArgs[]);
-    otError ProcessService(Arg aArgs[]);
+    template <CommandId kCommandId> otError Process(Arg aArgs[]);
+
     otError ProcessServiceAdd(Arg aArgs[]);
-    otError ProcessStart(Arg aArgs[]);
-    otError ProcessState(Arg aArgs[]);
-    otError ProcessStop(Arg aArgs[]);
 
     void OutputHostInfo(uint8_t aIndentSize, const otSrpClientHostInfo &aHostInfo);
     void OutputServiceList(uint8_t aIndentSize, const otSrpClientService *aServices);
@@ -108,22 +99,6 @@
                                const otSrpClientService * aServices,
                                const otSrpClientService * aRemovedServices);
 
-    static constexpr Command sCommands[] = {
-        {"autostart", &SrpClient::ProcessAutoStart},
-        {"callback", &SrpClient::ProcessCallback},
-        {"help", &SrpClient::ProcessHelp},
-        {"host", &SrpClient::ProcessHost},
-        {"keyleaseinterval", &SrpClient::ProcessKeyLeaseInterval},
-        {"leaseinterval", &SrpClient::ProcessLeaseInterval},
-        {"server", &SrpClient::ProcessServer},
-        {"service", &SrpClient::ProcessService},
-        {"start", &SrpClient::ProcessStart},
-        {"state", &SrpClient::ProcessState},
-        {"stop", &SrpClient::ProcessStop},
-    };
-
-    static_assert(BinarySearch::IsSorted(sCommands), "Command Table is not sorted");
-
     bool mCallbackEnabled;
 };
 
diff --git a/src/cli/cli_udp.hpp b/src/cli/cli_udp.hpp
index 11dfc57..bd8ba54 100644
--- a/src/cli/cli_udp.hpp
+++ b/src/cli/cli_udp.hpp
@@ -55,7 +55,7 @@
     /**
      * Constructor
      *
-     * @param[in]  aOutputContext The CLI console output context.
+     * @param[in]  aOutput The CLI console output context.
      *
      */
     explicit UdpExample(Output &aOutput);
diff --git a/src/core/BUILD.gn b/src/core/BUILD.gn
index 973c76b..5ba6b7c 100644
--- a/src/core/BUILD.gn
+++ b/src/core/BUILD.gn
@@ -261,12 +261,15 @@
     if (openthread_config_full_logs) {
       defines += [ "OPENTHREAD_CONFIG_LOG_LEVEL=OT_LOG_LEVEL_DEBG" ]
       defines += [ "OPENTHREAD_CONFIG_LOG_PREPEND_LEVEL=1" ]
-      defines += [ "OPENTHREAD_CONFIG_LOG_PREPEND_REGION=1" ]
     }
 
     if (openthread_config_otns_enable) {
       defines += [ "OPENTHREAD_CONFIG_OTNS_ENABLE=1" ]
     }
+
+    if (openthread_config_coexistence_enable) {
+      defines += [ "OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE=1" ]
+    }
   }
 }
 
@@ -398,6 +401,7 @@
   "common/heap.cpp",
   "common/heap.hpp",
   "common/heap_allocatable.hpp",
+  "common/heap_array.hpp",
   "common/heap_data.cpp",
   "common/heap_data.hpp",
   "common/heap_string.cpp",
@@ -408,7 +412,8 @@
   "common/linked_list.hpp",
   "common/locator.hpp",
   "common/locator_getters.hpp",
-  "common/logging.cpp",
+  "common/log.cpp",
+  "common/log.hpp",
   "common/logging.hpp",
   "common/message.cpp",
   "common/message.hpp",
@@ -421,9 +426,8 @@
   "common/owning_list.hpp",
   "common/pool.hpp",
   "common/ptr_wrapper.hpp",
+  "common/random.cpp",
   "common/random.hpp",
-  "common/random_manager.cpp",
-  "common/random_manager.hpp",
   "common/retain_ptr.hpp",
   "common/serial_number.hpp",
   "common/settings.cpp",
@@ -453,6 +457,7 @@
   "crypto/crypto_platform.cpp",
   "crypto/ecdsa.cpp",
   "crypto/ecdsa.hpp",
+  "crypto/ecdsa_tinycrypt.cpp",
   "crypto/hkdf_sha256.cpp",
   "crypto/hkdf_sha256.hpp",
   "crypto/hmac_sha256.cpp",
@@ -507,6 +512,8 @@
   "meshcop/dtls.hpp",
   "meshcop/energy_scan_client.cpp",
   "meshcop/energy_scan_client.hpp",
+  "meshcop/extended_panid.cpp",
+  "meshcop/extended_panid.hpp",
   "meshcop/joiner.cpp",
   "meshcop/joiner.hpp",
   "meshcop/joiner_router.cpp",
@@ -517,6 +524,8 @@
   "meshcop/meshcop_leader.hpp",
   "meshcop/meshcop_tlvs.cpp",
   "meshcop/meshcop_tlvs.hpp",
+  "meshcop/network_name.cpp",
+  "meshcop/network_name.hpp",
   "meshcop/panid_query_client.cpp",
   "meshcop/panid_query_client.hpp",
   "meshcop/timestamp.cpp",
@@ -550,6 +559,7 @@
   "net/ip6_headers.hpp",
   "net/ip6_mpl.cpp",
   "net/ip6_mpl.hpp",
+  "net/ip6_types.hpp",
   "net/nd_agent.cpp",
   "net/nd_agent.hpp",
   "net/netif.cpp",
@@ -702,8 +712,8 @@
   "common/binary_search.hpp",
   "common/error.hpp",
   "common/instance.cpp",
-  "common/logging.cpp",
-  "common/random_manager.cpp",
+  "common/log.cpp",
+  "common/random.cpp",
   "common/string.cpp",
   "common/tasklet.cpp",
   "common/timer.cpp",
@@ -759,10 +769,10 @@
     "config/link_raw.h",
     "config/logging.h",
     "config/mac.h",
+    "config/misc.h",
     "config/mle.h",
     "config/netdata_publisher.h",
     "config/openthread-core-config-check.h",
-    "config/openthread-core-default-config.h",
     "config/parent_search.h",
     "config/ping_sender.h",
     "config/platform.h",
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 95ac998..e09f7e5 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -101,10 +101,10 @@
     common/heap_data.cpp
     common/heap_string.cpp
     common/instance.cpp
-    common/logging.cpp
+    common/log.cpp
     common/message.cpp
     common/notifier.cpp
-    common/random_manager.cpp
+    common/random.cpp
     common/settings.cpp
     common/string.cpp
     common/tasklet.cpp
@@ -117,6 +117,7 @@
     crypto/aes_ecb.cpp
     crypto/crypto_platform.cpp
     crypto/ecdsa.cpp
+    crypto/ecdsa_tinycrypt.cpp
     crypto/hkdf_sha256.cpp
     crypto/hmac_sha256.cpp
     crypto/mbedtls.cpp
@@ -145,11 +146,13 @@
     meshcop/dataset_updater.cpp
     meshcop/dtls.cpp
     meshcop/energy_scan_client.cpp
+    meshcop/extended_panid.cpp
     meshcop/joiner.cpp
     meshcop/joiner_router.cpp
     meshcop/meshcop.cpp
     meshcop/meshcop_leader.cpp
     meshcop/meshcop_tlvs.cpp
+    meshcop/network_name.cpp
     meshcop/panid_query_client.cpp
     meshcop/timestamp.cpp
     net/checksum.cpp
@@ -246,8 +249,8 @@
     common/binary_search.cpp
     common/error.cpp
     common/instance.cpp
-    common/logging.cpp
-    common/random_manager.cpp
+    common/log.cpp
+    common/random.cpp
     common/string.cpp
     common/tasklet.cpp
     common/timer.cpp
diff --git a/src/core/Makefile.am b/src/core/Makefile.am
index c965f9e..850228d 100644
--- a/src/core/Makefile.am
+++ b/src/core/Makefile.am
@@ -191,10 +191,10 @@
     common/heap_data.cpp                          \
     common/heap_string.cpp                        \
     common/instance.cpp                           \
-    common/logging.cpp                            \
+    common/log.cpp                                \
     common/message.cpp                            \
     common/notifier.cpp                           \
-    common/random_manager.cpp                     \
+    common/random.cpp                             \
     common/settings.cpp                           \
     common/string.cpp                             \
     common/tasklet.cpp                            \
@@ -207,6 +207,7 @@
     crypto/aes_ecb.cpp                            \
     crypto/crypto_platform.cpp                    \
     crypto/ecdsa.cpp                              \
+    crypto/ecdsa_tinycrypt.cpp                    \
     crypto/hkdf_sha256.cpp                        \
     crypto/hmac_sha256.cpp                        \
     crypto/mbedtls.cpp                            \
@@ -235,11 +236,13 @@
     meshcop/dataset_updater.cpp                   \
     meshcop/dtls.cpp                              \
     meshcop/energy_scan_client.cpp                \
+    meshcop/extended_panid.cpp                    \
     meshcop/joiner.cpp                            \
     meshcop/joiner_router.cpp                     \
     meshcop/meshcop.cpp                           \
     meshcop/meshcop_leader.cpp                    \
     meshcop/meshcop_tlvs.cpp                      \
+    meshcop/network_name.cpp                      \
     meshcop/panid_query_client.cpp                \
     meshcop/timestamp.cpp                         \
     net/checksum.cpp                              \
@@ -336,8 +339,8 @@
     common/binary_search.cpp                 \
     common/error.cpp                         \
     common/instance.cpp                      \
-    common/logging.cpp                       \
-    common/random_manager.cpp                \
+    common/log.cpp                           \
+    common/random.cpp                        \
     common/string.cpp                        \
     common/tasklet.cpp                       \
     common/timer.cpp                         \
@@ -435,6 +438,7 @@
     common/extension.hpp                          \
     common/heap.hpp                               \
     common/heap_allocatable.hpp                   \
+    common/heap_array.hpp                         \
     common/heap_data.hpp                          \
     common/heap_string.hpp                        \
     common/instance.hpp                           \
@@ -442,6 +446,7 @@
     common/linked_list.hpp                        \
     common/locator.hpp                            \
     common/locator_getters.hpp                    \
+    common/log.hpp                                \
     common/logging.hpp                            \
     common/message.hpp                            \
     common/new.hpp                                \
@@ -453,7 +458,6 @@
     common/pool.hpp                               \
     common/ptr_wrapper.hpp                        \
     common/random.hpp                             \
-    common/random_manager.hpp                     \
     common/retain_ptr.hpp                         \
     common/serial_number.hpp                      \
     common/settings.hpp                           \
@@ -491,10 +495,10 @@
     config/link_raw.h                             \
     config/logging.h                              \
     config/mac.h                                  \
+    config/misc.h                                 \
     config/mle.h                                  \
     config/netdata_publisher.h                    \
     config/openthread-core-config-check.h         \
-    config/openthread-core-default-config.h       \
     config/parent_search.h                        \
     config/ping_sender.h                          \
     config/platform.h                             \
@@ -534,11 +538,13 @@
     meshcop/dataset_updater.hpp                   \
     meshcop/dtls.hpp                              \
     meshcop/energy_scan_client.hpp                \
+    meshcop/extended_panid.hpp                    \
     meshcop/joiner.hpp                            \
     meshcop/joiner_router.hpp                     \
     meshcop/meshcop.hpp                           \
     meshcop/meshcop_leader.hpp                    \
     meshcop/meshcop_tlvs.hpp                      \
+    meshcop/network_name.hpp                      \
     meshcop/panid_query_client.hpp                \
     meshcop/timestamp.hpp                         \
     net/checksum.hpp                              \
@@ -556,6 +562,7 @@
     net/ip6_filter.hpp                            \
     net/ip6_headers.hpp                           \
     net/ip6_mpl.hpp                               \
+    net/ip6_types.hpp                             \
     net/nd_agent.hpp                              \
     net/netif.hpp                                 \
     net/sntp_client.hpp                           \
diff --git a/src/core/api/backbone_router_ftd_api.cpp b/src/core/api/backbone_router_ftd_api.cpp
index b7e38e3..d8e5169 100644
--- a/src/core/api/backbone_router_ftd_api.cpp
+++ b/src/core/api/backbone_router_ftd_api.cpp
@@ -177,11 +177,6 @@
 }
 #endif // OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE
 
-void otBackboneRouterConfigSkipSeqNumIncrease(otInstance *aInstance, bool aSkip)
-{
-    AsCoreType(aInstance).Get<BackboneRouter::Local>().ConfigSkipSeqNumIncrease(aSkip);
-}
-
 #endif // OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
 
 #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
diff --git a/src/core/api/commissioner_api.cpp b/src/core/api/commissioner_api.cpp
index 4f5aa4b..9c5b575 100644
--- a/src/core/api/commissioner_api.cpp
+++ b/src/core/api/commissioner_api.cpp
@@ -50,6 +50,16 @@
     return AsCoreType(aInstance).Get<MeshCoP::Commissioner>().Start(aStateCallback, aJoinerCallback, aCallbackContext);
 }
 
+const char *otCommissionerGetId(otInstance *aInstance)
+{
+    return AsCoreType(aInstance).Get<MeshCoP::Commissioner>().GetId();
+}
+
+otError otCommissionerSetId(otInstance *aInstance, const char *aId)
+{
+    return AsCoreType(aInstance).Get<MeshCoP::Commissioner>().SetId(aId);
+}
+
 otError otCommissionerStop(otInstance *aInstance)
 {
     return AsCoreType(aInstance).Get<MeshCoP::Commissioner>().Stop();
diff --git a/src/core/api/dataset_api.cpp b/src/core/api/dataset_api.cpp
index a4153d2..457c783 100644
--- a/src/core/api/dataset_api.cpp
+++ b/src/core/api/dataset_api.cpp
@@ -44,63 +44,63 @@
 
 bool otDatasetIsCommissioned(otInstance *aInstance)
 {
-    return AsCoreType(aInstance).Get<MeshCoP::ActiveDataset>().IsCommissioned();
+    return AsCoreType(aInstance).Get<MeshCoP::ActiveDatasetManager>().IsCommissioned();
 }
 
 otError otDatasetGetActive(otInstance *aInstance, otOperationalDataset *aDataset)
 {
     OT_ASSERT(aDataset != nullptr);
 
-    return AsCoreType(aInstance).Get<MeshCoP::ActiveDataset>().Read(AsCoreType(aDataset));
+    return AsCoreType(aInstance).Get<MeshCoP::ActiveDatasetManager>().Read(AsCoreType(aDataset));
 }
 
 otError otDatasetGetActiveTlvs(otInstance *aInstance, otOperationalDatasetTlvs *aDataset)
 {
     OT_ASSERT(aDataset != nullptr);
 
-    return AsCoreType(aInstance).Get<MeshCoP::ActiveDataset>().Read(*aDataset);
+    return AsCoreType(aInstance).Get<MeshCoP::ActiveDatasetManager>().Read(*aDataset);
 }
 
 otError otDatasetSetActive(otInstance *aInstance, const otOperationalDataset *aDataset)
 {
     OT_ASSERT(aDataset != nullptr);
 
-    return AsCoreType(aInstance).Get<MeshCoP::ActiveDataset>().Save(AsCoreType(aDataset));
+    return AsCoreType(aInstance).Get<MeshCoP::ActiveDatasetManager>().Save(AsCoreType(aDataset));
 }
 
 otError otDatasetSetActiveTlvs(otInstance *aInstance, const otOperationalDatasetTlvs *aDataset)
 {
     OT_ASSERT(aDataset != nullptr);
 
-    return AsCoreType(aInstance).Get<MeshCoP::ActiveDataset>().Save(*aDataset);
+    return AsCoreType(aInstance).Get<MeshCoP::ActiveDatasetManager>().Save(*aDataset);
 }
 
 otError otDatasetGetPending(otInstance *aInstance, otOperationalDataset *aDataset)
 {
     OT_ASSERT(aDataset != nullptr);
 
-    return AsCoreType(aInstance).Get<MeshCoP::PendingDataset>().Read(AsCoreType(aDataset));
+    return AsCoreType(aInstance).Get<MeshCoP::PendingDatasetManager>().Read(AsCoreType(aDataset));
 }
 
 otError otDatasetGetPendingTlvs(otInstance *aInstance, otOperationalDatasetTlvs *aDataset)
 {
     OT_ASSERT(aDataset != nullptr);
 
-    return AsCoreType(aInstance).Get<MeshCoP::PendingDataset>().Read(*aDataset);
+    return AsCoreType(aInstance).Get<MeshCoP::PendingDatasetManager>().Read(*aDataset);
 }
 
 otError otDatasetSetPending(otInstance *aInstance, const otOperationalDataset *aDataset)
 {
     OT_ASSERT(aDataset != nullptr);
 
-    return AsCoreType(aInstance).Get<MeshCoP::PendingDataset>().Save(AsCoreType(aDataset));
+    return AsCoreType(aInstance).Get<MeshCoP::PendingDatasetManager>().Save(AsCoreType(aDataset));
 }
 
 otError otDatasetSetPendingTlvs(otInstance *aInstance, const otOperationalDatasetTlvs *aDataset)
 {
     OT_ASSERT(aDataset != nullptr);
 
-    return AsCoreType(aInstance).Get<MeshCoP::PendingDataset>().Save(*aDataset);
+    return AsCoreType(aInstance).Get<MeshCoP::PendingDatasetManager>().Save(*aDataset);
 }
 
 otError otDatasetSendMgmtActiveGet(otInstance *                          aInstance,
@@ -109,8 +109,8 @@
                                    uint8_t                               aLength,
                                    const otIp6Address *                  aAddress)
 {
-    return AsCoreType(aInstance).Get<MeshCoP::ActiveDataset>().SendGetRequest(AsCoreType(aDatasetComponents), aTlvTypes,
-                                                                              aLength, aAddress);
+    return AsCoreType(aInstance).Get<MeshCoP::ActiveDatasetManager>().SendGetRequest(AsCoreType(aDatasetComponents),
+                                                                                     aTlvTypes, aLength, aAddress);
 }
 
 otError otDatasetSendMgmtActiveSet(otInstance *                aInstance,
@@ -120,8 +120,8 @@
                                    otDatasetMgmtSetCallback    aCallback,
                                    void *                      aContext)
 {
-    return AsCoreType(aInstance).Get<MeshCoP::ActiveDataset>().SendSetRequest(AsCoreType(aDataset), aTlvs, aLength,
-                                                                              aCallback, aContext);
+    return AsCoreType(aInstance).Get<MeshCoP::ActiveDatasetManager>().SendSetRequest(AsCoreType(aDataset), aTlvs,
+                                                                                     aLength, aCallback, aContext);
 }
 
 otError otDatasetSendMgmtPendingGet(otInstance *                          aInstance,
@@ -130,8 +130,8 @@
                                     uint8_t                               aLength,
                                     const otIp6Address *                  aAddress)
 {
-    return AsCoreType(aInstance).Get<MeshCoP::PendingDataset>().SendGetRequest(AsCoreType(aDatasetComponents),
-                                                                               aTlvTypes, aLength, aAddress);
+    return AsCoreType(aInstance).Get<MeshCoP::PendingDatasetManager>().SendGetRequest(AsCoreType(aDatasetComponents),
+                                                                                      aTlvTypes, aLength, aAddress);
 }
 
 otError otDatasetSendMgmtPendingSet(otInstance *                aInstance,
@@ -141,8 +141,8 @@
                                     otDatasetMgmtSetCallback    aCallback,
                                     void *                      aContext)
 {
-    return AsCoreType(aInstance).Get<MeshCoP::PendingDataset>().SendSetRequest(AsCoreType(aDataset), aTlvs, aLength,
-                                                                               aCallback, aContext);
+    return AsCoreType(aInstance).Get<MeshCoP::PendingDatasetManager>().SendSetRequest(AsCoreType(aDataset), aTlvs,
+                                                                                      aLength, aCallback, aContext);
 }
 
 #if OPENTHREAD_FTD
diff --git a/src/core/api/dataset_ftd_api.cpp b/src/core/api/dataset_ftd_api.cpp
index 4c26088..ea1792d 100644
--- a/src/core/api/dataset_ftd_api.cpp
+++ b/src/core/api/dataset_ftd_api.cpp
@@ -44,7 +44,7 @@
 
 otError otDatasetCreateNewNetwork(otInstance *aInstance, otOperationalDataset *aDataset)
 {
-    return AsCoreType(aInstance).Get<MeshCoP::ActiveDataset>().CreateNewNetwork(AsCoreType(aDataset));
+    return AsCoreType(aInstance).Get<MeshCoP::ActiveDatasetManager>().CreateNewNetwork(AsCoreType(aDataset));
 }
 
 uint32_t otDatasetGetDelayTimerMinimal(otInstance *aInstance)
diff --git a/src/core/api/dns_api.cpp b/src/core/api/dns_api.cpp
index ed3bfcf..d9bdda7 100644
--- a/src/core/api/dns_api.cpp
+++ b/src/core/api/dns_api.cpp
@@ -91,6 +91,18 @@
                                                                    AsCoreTypePtr(aConfig));
 }
 
+#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE
+otError otDnsClientResolveIp4Address(otInstance *            aInstance,
+                                     const char *            aHostName,
+                                     otDnsAddressCallback    aCallback,
+                                     void *                  aContext,
+                                     const otDnsQueryConfig *aConfig)
+{
+    return AsCoreType(aInstance).Get<Dns::Client>().ResolveIp4Address(aHostName, aCallback, aContext,
+                                                                      AsCoreTypePtr(aConfig));
+}
+#endif
+
 otError otDnsAddressResponseGetHostName(const otDnsAddressResponse *aResponse,
                                         char *                      aNameBuffer,
                                         uint16_t                    aNameBufferSize)
diff --git a/src/core/api/instance_api.cpp b/src/core/api/instance_api.cpp
index f9cc00e..5557f68 100644
--- a/src/core/api/instance_api.cpp
+++ b/src/core/api/instance_api.cpp
@@ -38,7 +38,6 @@
 
 #include "common/as_core_type.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
 #include "common/new.hpp"
 #include "radio/radio.hpp"
 
@@ -51,7 +50,7 @@
 #endif
 #else // __ANDROID__
 #if defined(__DATE__)
-#define OPENTHREAD_BUILD_DATETIME "; " __DATE__ " " __TIME__
+#define OPENTHREAD_BUILD_DATETIME __DATE__ " " __TIME__
 #endif
 #endif // __ANDROID__
 #endif // !defined(OPENTHREAD_BUILD_DATETIME)
@@ -64,7 +63,6 @@
     Instance *instance;
 
     instance = Instance::Init(aInstanceBuffer, aInstanceBufferSize);
-    otLogInfoApi("otInstance Initialized");
 
     return instance;
 }
@@ -179,10 +177,10 @@
 #endif
     const char sVersion[] = PACKAGE_NAME "/" PACKAGE_VERSION "; " OPENTHREAD_CONFIG_PLATFORM_INFO
 #ifdef OPENTHREAD_BUILD_DATETIME
-        OPENTHREAD_BUILD_DATETIME
+                                         "; " OPENTHREAD_BUILD_DATETIME
 #endif
 #ifdef PLATFORM_VERSION_ATTR_SUFFIX
-            PLATFORM_VERSION_ATTR_SUFFIX
+                                             PLATFORM_VERSION_ATTR_SUFFIX
 #endif
         ; // Trailing semicolon to end statement.
 
diff --git a/src/core/api/ip6_api.cpp b/src/core/api/ip6_api.cpp
index a65dc55..b12b35e 100644
--- a/src/core/api/ip6_api.cpp
+++ b/src/core/api/ip6_api.cpp
@@ -37,7 +37,6 @@
 
 #include "common/as_core_type.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
 #include "utils/slaac_address.hpp"
 
 using namespace ot;
diff --git a/src/core/api/link_api.cpp b/src/core/api/link_api.cpp
index a1d38be..ff5922d 100644
--- a/src/core/api/link_api.cpp
+++ b/src/core/api/link_api.cpp
@@ -77,8 +77,8 @@
     VerifyOrExit(instance.Get<Mle::MleRouter>().IsDisabled(), error = kErrorInvalidState);
 
     SuccessOrExit(error = instance.Get<Mac::Mac>().SetPanChannel(aChannel));
-    instance.Get<MeshCoP::ActiveDataset>().Clear();
-    instance.Get<MeshCoP::PendingDataset>().Clear();
+    instance.Get<MeshCoP::ActiveDatasetManager>().Clear();
+    instance.Get<MeshCoP::PendingDatasetManager>().Clear();
 
 exit:
     return error;
@@ -141,8 +141,8 @@
     VerifyOrExit(instance.Get<Mle::MleRouter>().IsDisabled(), error = kErrorInvalidState);
 
     instance.Get<Mac::Mac>().SetPanId(aPanId);
-    instance.Get<MeshCoP::ActiveDataset>().Clear();
-    instance.Get<MeshCoP::PendingDataset>().Clear();
+    instance.Get<MeshCoP::ActiveDatasetManager>().Clear();
+    instance.Get<MeshCoP::PendingDatasetManager>().Clear();
 
 exit:
     return error;
@@ -288,7 +288,7 @@
 int8_t otLinkConvertLinkQualityToRss(otInstance *aInstance, uint8_t aLinkQuality)
 {
     return LinkQualityInfo::ConvertLinkQualityToRss(AsCoreType(aInstance).Get<Mac::Mac>().GetNoiseFloor(),
-                                                    aLinkQuality);
+                                                    static_cast<LinkQuality>(aLinkQuality));
 }
 
 #if OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_ENABLE
diff --git a/src/core/api/logging_api.cpp b/src/core/api/logging_api.cpp
index b00147c..3218837 100644
--- a/src/core/api/logging_api.cpp
+++ b/src/core/api/logging_api.cpp
@@ -33,19 +33,17 @@
 
 #include "openthread-core-config.h"
 
-#include <openthread/logging.h>
+#include "common/code_utils.hpp"
+#include "common/debug.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
+#include "common/log.hpp"
 
 using namespace ot;
 
 otLogLevel otLoggingGetLevel(void)
 {
-#if OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE
-    return Instance::GetLogLevel();
-#else
-    return static_cast<otLogLevel>(OPENTHREAD_CONFIG_LOG_LEVEL);
-#endif
+    return static_cast<otLogLevel>(Instance::GetLogLevel());
 }
 
 #if OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE
@@ -53,15 +51,154 @@
 {
     Error error = kErrorNone;
 
-    if (aLogLevel <= OT_LOG_LEVEL_DEBG && aLogLevel >= OT_LOG_LEVEL_NONE)
-    {
-        Instance::SetLogLevel(aLogLevel);
-    }
-    else
-    {
-        error = kErrorInvalidArgs;
-    }
+    VerifyOrExit(aLogLevel <= kLogLevelDebg && aLogLevel >= kLogLevelNone, error = kErrorInvalidArgs);
+    Instance::SetLogLevel(static_cast<LogLevel>(aLogLevel));
 
+exit:
     return error;
 }
 #endif
+
+static const char kPlatformModuleName[] = "Platform";
+
+void otLogCritPlat(const char *aFormat, ...)
+{
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_CRIT) && OPENTHREAD_CONFIG_LOG_PLATFORM
+    va_list args;
+
+    va_start(args, aFormat);
+    Logger::LogVarArgs(kPlatformModuleName, kLogLevelCrit, aFormat, args);
+    va_end(args);
+#else
+    OT_UNUSED_VARIABLE(aFormat);
+    OT_UNUSED_VARIABLE(kPlatformModuleName);
+#endif
+}
+
+void otLogWarnPlat(const char *aFormat, ...)
+{
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN) && OPENTHREAD_CONFIG_LOG_PLATFORM
+    va_list args;
+
+    va_start(args, aFormat);
+    Logger::LogVarArgs(kPlatformModuleName, kLogLevelWarn, aFormat, args);
+    va_end(args);
+#else
+    OT_UNUSED_VARIABLE(aFormat);
+#endif
+}
+
+void otLogNotePlat(const char *aFormat, ...)
+{
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_NOTE) && OPENTHREAD_CONFIG_LOG_PLATFORM
+    va_list args;
+
+    va_start(args, aFormat);
+    Logger::LogVarArgs(kPlatformModuleName, kLogLevelNote, aFormat, args);
+    va_end(args);
+#else
+    OT_UNUSED_VARIABLE(aFormat);
+#endif
+}
+
+void otLogInfoPlat(const char *aFormat, ...)
+{
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) && OPENTHREAD_CONFIG_LOG_PLATFORM
+    va_list args;
+
+    va_start(args, aFormat);
+    Logger::LogVarArgs(kPlatformModuleName, kLogLevelInfo, aFormat, args);
+    va_end(args);
+#else
+    OT_UNUSED_VARIABLE(aFormat);
+#endif
+}
+
+void otLogDebgPlat(const char *aFormat, ...)
+{
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_DEBG) && OPENTHREAD_CONFIG_LOG_PLATFORM
+    va_list args;
+
+    va_start(args, aFormat);
+    Logger::LogVarArgs(kPlatformModuleName, kLogLevelDebg, aFormat, args);
+    va_end(args);
+#else
+    OT_UNUSED_VARIABLE(aFormat);
+#endif
+}
+
+void otDumpCritPlat(const char *aText, const void *aData, uint16_t aDataLength)
+{
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_CRIT) && OPENTHREAD_CONFIG_LOG_PLATFORM && OPENTHREAD_CONFIG_LOG_PKT_DUMP
+    Logger::DumpInModule(kPlatformModuleName, kLogLevelCrit, aText, aData, aDataLength);
+#else
+    OT_UNUSED_VARIABLE(aText);
+    OT_UNUSED_VARIABLE(aData);
+    OT_UNUSED_VARIABLE(aDataLength);
+#endif
+}
+
+void otDumpWarnPlat(const char *aText, const void *aData, uint16_t aDataLength)
+{
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN) && OPENTHREAD_CONFIG_LOG_PLATFORM && OPENTHREAD_CONFIG_LOG_PKT_DUMP
+    Logger::DumpInModule(kPlatformModuleName, kLogLevelWarn, aText, aData, aDataLength);
+#else
+    OT_UNUSED_VARIABLE(aText);
+    OT_UNUSED_VARIABLE(aData);
+    OT_UNUSED_VARIABLE(aDataLength);
+#endif
+}
+
+void otDumpNotePlat(const char *aText, const void *aData, uint16_t aDataLength)
+{
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_NOTE) && OPENTHREAD_CONFIG_LOG_PLATFORM && OPENTHREAD_CONFIG_LOG_PKT_DUMP
+    Logger::DumpInModule(kPlatformModuleName, kLogLevelNote, aText, aData, aDataLength);
+#else
+    OT_UNUSED_VARIABLE(aText);
+    OT_UNUSED_VARIABLE(aData);
+    OT_UNUSED_VARIABLE(aDataLength);
+#endif
+}
+
+void otDumpInfoPlat(const char *aText, const void *aData, uint16_t aDataLength)
+{
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) && OPENTHREAD_CONFIG_LOG_PLATFORM && OPENTHREAD_CONFIG_LOG_PKT_DUMP
+    Logger::DumpInModule(kPlatformModuleName, kLogLevelInfo, aText, aData, aDataLength);
+#else
+    OT_UNUSED_VARIABLE(aText);
+    OT_UNUSED_VARIABLE(aData);
+    OT_UNUSED_VARIABLE(aDataLength);
+#endif
+}
+
+void otDumpDebgPlat(const char *aText, const void *aData, uint16_t aDataLength)
+{
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_DEBG) && OPENTHREAD_CONFIG_LOG_PLATFORM && OPENTHREAD_CONFIG_LOG_PKT_DUMP
+    Logger::DumpInModule(kPlatformModuleName, kLogLevelDebg, aText, aData, aDataLength);
+#else
+    OT_UNUSED_VARIABLE(aText);
+    OT_UNUSED_VARIABLE(aData);
+    OT_UNUSED_VARIABLE(aDataLength);
+#endif
+}
+
+void otLogCli(otLogLevel aLogLevel, const char *aFormat, ...)
+{
+#if OT_SHOULD_LOG && OPENTHREAD_CONFIG_LOG_CLI
+    static const char kCliModuleName[] = "Cli";
+
+    va_list args;
+
+    OT_ASSERT(aLogLevel >= kLogLevelNone && aLogLevel <= kLogLevelDebg);
+    VerifyOrExit(aLogLevel >= kLogLevelNone && aLogLevel <= kLogLevelDebg);
+
+    va_start(args, aFormat);
+    Logger::LogVarArgs(kCliModuleName, static_cast<LogLevel>(aLogLevel), aFormat, args);
+    va_end(args);
+exit:
+#else
+    OT_UNUSED_VARIABLE(aLogLevel);
+    OT_UNUSED_VARIABLE(aFormat);
+#endif
+    return;
+}
diff --git a/src/core/api/message_api.cpp b/src/core/api/message_api.cpp
index 81d9baf..ecc3bfc 100644
--- a/src/core/api/message_api.cpp
+++ b/src/core/api/message_api.cpp
@@ -145,61 +145,6 @@
 #if OPENTHREAD_MTD || OPENTHREAD_FTD
 void otMessageGetBufferInfo(otInstance *aInstance, otBufferInfo *aBufferInfo)
 {
-    uint16_t  messages, buffers;
-    Instance &instance = AsCoreType(aInstance);
-
-    aBufferInfo->mTotalBuffers = instance.Get<MessagePool>().GetTotalBufferCount();
-
-    aBufferInfo->mFreeBuffers = instance.Get<MessagePool>().GetFreeBufferCount();
-
-    instance.Get<MeshForwarder>().GetSendQueue().GetInfo(aBufferInfo->m6loSendMessages, aBufferInfo->m6loSendBuffers);
-
-    instance.Get<MeshForwarder>().GetReassemblyQueue().GetInfo(aBufferInfo->m6loReassemblyMessages,
-                                                               aBufferInfo->m6loReassemblyBuffers);
-
-#if OPENTHREAD_FTD
-    instance.Get<MeshForwarder>().GetResolvingQueue().GetInfo(aBufferInfo->mArpMessages, aBufferInfo->mArpBuffers);
-#else
-    aBufferInfo->mArpMessages             = 0;
-    aBufferInfo->mArpBuffers              = 0;
-#endif
-
-    instance.Get<Ip6::Ip6>().GetSendQueue().GetInfo(aBufferInfo->mIp6Messages, aBufferInfo->mIp6Buffers);
-
-#if OPENTHREAD_FTD
-    instance.Get<Ip6::Mpl>().GetBufferedMessageSet().GetInfo(aBufferInfo->mMplMessages, aBufferInfo->mMplBuffers);
-#else
-    aBufferInfo->mMplMessages             = 0;
-    aBufferInfo->mMplBuffers              = 0;
-#endif
-
-    instance.Get<Mle::MleRouter>().GetMessageQueue().GetInfo(aBufferInfo->mMleMessages, aBufferInfo->mMleBuffers);
-
-    instance.Get<Tmf::Agent>().GetRequestMessages().GetInfo(aBufferInfo->mCoapMessages, aBufferInfo->mCoapBuffers);
-    instance.Get<Tmf::Agent>().GetCachedResponses().GetInfo(messages, buffers);
-    aBufferInfo->mCoapMessages += messages;
-    aBufferInfo->mCoapBuffers += buffers;
-
-#if OPENTHREAD_CONFIG_DTLS_ENABLE
-    instance.Get<Coap::CoapSecure>().GetRequestMessages().GetInfo(aBufferInfo->mCoapSecureMessages,
-                                                                  aBufferInfo->mCoapSecureBuffers);
-    instance.Get<Coap::CoapSecure>().GetCachedResponses().GetInfo(messages, buffers);
-    aBufferInfo->mCoapSecureMessages += messages;
-    aBufferInfo->mCoapSecureBuffers += buffers;
-#else
-    aBufferInfo->mCoapSecureMessages      = 0;
-    aBufferInfo->mCoapSecureBuffers       = 0;
-#endif
-
-#if OPENTHREAD_CONFIG_COAP_API_ENABLE
-    instance.GetApplicationCoap().GetRequestMessages().GetInfo(aBufferInfo->mApplicationCoapMessages,
-                                                               aBufferInfo->mApplicationCoapBuffers);
-    instance.GetApplicationCoap().GetCachedResponses().GetInfo(messages, buffers);
-    aBufferInfo->mApplicationCoapMessages += messages;
-    aBufferInfo->mApplicationCoapBuffers += buffers;
-#else
-    aBufferInfo->mApplicationCoapMessages = 0;
-    aBufferInfo->mApplicationCoapBuffers  = 0;
-#endif
+    AsCoreType(aInstance).GetBufferInfo(AsCoreType(aBufferInfo));
 }
 #endif // OPENTHREAD_MTD || OPENTHREAD_FTD
diff --git a/src/core/api/netdata_api.cpp b/src/core/api/netdata_api.cpp
index e646645..c28b987 100644
--- a/src/core/api/netdata_api.cpp
+++ b/src/core/api/netdata_api.cpp
@@ -60,6 +60,13 @@
     return error;
 }
 
+#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
+bool otNetDataContainsOmrPrefix(otInstance *aInstance, const otIp6Prefix *aPrefix)
+{
+    return AsCoreType(aInstance).Get<NetworkData::Leader>().ContainsOmrPrefix(AsCoreType(aPrefix));
+}
+#endif
+
 otError otNetDataGetNextRoute(otInstance *aInstance, otNetworkDataIterator *aIterator, otExternalRouteConfig *aConfig)
 {
     Error error = kErrorNone;
diff --git a/src/core/api/tasklet_api.cpp b/src/core/api/tasklet_api.cpp
index 6f8a0de..e5213c8 100644
--- a/src/core/api/tasklet_api.cpp
+++ b/src/core/api/tasklet_api.cpp
@@ -38,7 +38,6 @@
 #include "common/as_core_type.hpp"
 #include "common/code_utils.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
 
 using namespace ot;
 
diff --git a/src/core/api/tcp_api.cpp b/src/core/api/tcp_api.cpp
index 8721d1e..896aac6 100644
--- a/src/core/api/tcp_api.cpp
+++ b/src/core/api/tcp_api.cpp
@@ -42,7 +42,9 @@
 
 using namespace ot;
 
-otError otTcpEndpointInitialize(otInstance *aInstance, otTcpEndpoint *aEndpoint, otTcpEndpointInitializeArgs *aArgs)
+otError otTcpEndpointInitialize(otInstance *                       aInstance,
+                                otTcpEndpoint *                    aEndpoint,
+                                const otTcpEndpointInitializeArgs *aArgs)
 {
     return AsCoreType(aEndpoint).Initialize(AsCoreType(aInstance), *aArgs);
 }
@@ -117,7 +119,9 @@
     return AsCoreType(aEndpoint).Deinitialize();
 }
 
-otError otTcpListenerInitialize(otInstance *aInstance, otTcpListener *aListener, otTcpListenerInitializeArgs *aArgs)
+otError otTcpListenerInitialize(otInstance *                       aInstance,
+                                otTcpListener *                    aListener,
+                                const otTcpListenerInitializeArgs *aArgs)
 {
     return AsCoreType(aListener).Initialize(AsCoreType(aInstance), *aArgs);
 }
diff --git a/src/core/api/thread_api.cpp b/src/core/api/thread_api.cpp
index 9ebafa9..b6ab068 100644
--- a/src/core/api/thread_api.cpp
+++ b/src/core/api/thread_api.cpp
@@ -55,25 +55,25 @@
 
 const otExtendedPanId *otThreadGetExtendedPanId(otInstance *aInstance)
 {
-    return &AsCoreType(aInstance).Get<Mac::Mac>().GetExtendedPanId();
+    return &AsCoreType(aInstance).Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId();
 }
 
 otError otThreadSetExtendedPanId(otInstance *aInstance, const otExtendedPanId *aExtendedPanId)
 {
-    Error                     error    = kErrorNone;
-    Instance &                instance = AsCoreType(aInstance);
-    const Mac::ExtendedPanId &extPanId = AsCoreType(aExtendedPanId);
-    Mle::MeshLocalPrefix      prefix;
+    Error                         error    = kErrorNone;
+    Instance &                    instance = AsCoreType(aInstance);
+    const MeshCoP::ExtendedPanId &extPanId = AsCoreType(aExtendedPanId);
+    Mle::MeshLocalPrefix          prefix;
 
     VerifyOrExit(instance.Get<Mle::MleRouter>().IsDisabled(), error = kErrorInvalidState);
 
-    instance.Get<Mac::Mac>().SetExtendedPanId(extPanId);
+    instance.Get<MeshCoP::ExtendedPanIdManager>().SetExtPanId(extPanId);
 
     prefix.SetFromExtendedPanId(extPanId);
     instance.Get<Mle::MleRouter>().SetMeshLocalPrefix(prefix);
 
-    instance.Get<MeshCoP::ActiveDataset>().Clear();
-    instance.Get<MeshCoP::PendingDataset>().Clear();
+    instance.Get<MeshCoP::ActiveDatasetManager>().Clear();
+    instance.Get<MeshCoP::PendingDatasetManager>().Clear();
 
 exit:
     return error;
@@ -123,8 +123,8 @@
 
     instance.Get<KeyManager>().SetNetworkKey(AsCoreType(aKey));
 
-    instance.Get<MeshCoP::ActiveDataset>().Clear();
-    instance.Get<MeshCoP::PendingDataset>().Clear();
+    instance.Get<MeshCoP::ActiveDatasetManager>().Clear();
+    instance.Get<MeshCoP::PendingDatasetManager>().Clear();
 
 exit:
     return error;
@@ -141,8 +141,8 @@
     VerifyOrExit(instance.Get<Mle::MleRouter>().IsDisabled(), error = kErrorInvalidState);
 
     instance.Get<KeyManager>().SetNetworkKeyRef((aKeyRef));
-    instance.Get<MeshCoP::ActiveDataset>().Clear();
-    instance.Get<MeshCoP::PendingDataset>().Clear();
+    instance.Get<MeshCoP::ActiveDatasetManager>().Clear();
+    instance.Get<MeshCoP::PendingDatasetManager>().Clear();
 
 exit:
     return error;
@@ -171,8 +171,8 @@
     VerifyOrExit(AsCoreType(aInstance).Get<Mle::MleRouter>().IsDisabled(), error = kErrorInvalidState);
 
     AsCoreType(aInstance).Get<Mle::MleRouter>().SetMeshLocalPrefix(AsCoreType(aMeshLocalPrefix));
-    AsCoreType(aInstance).Get<MeshCoP::ActiveDataset>().Clear();
-    AsCoreType(aInstance).Get<MeshCoP::PendingDataset>().Clear();
+    AsCoreType(aInstance).Get<MeshCoP::ActiveDatasetManager>().Clear();
+    AsCoreType(aInstance).Get<MeshCoP::PendingDatasetManager>().Clear();
 
 exit:
     return error;
@@ -200,7 +200,7 @@
 
 const char *otThreadGetNetworkName(otInstance *aInstance)
 {
-    return AsCoreType(aInstance).Get<Mac::Mac>().GetNetworkName().GetAsCString();
+    return AsCoreType(aInstance).Get<MeshCoP::NetworkNameManager>().GetNetworkName().GetAsCString();
 }
 
 otError otThreadSetNetworkName(otInstance *aInstance, const char *aNetworkName)
@@ -209,9 +209,9 @@
 
     VerifyOrExit(AsCoreType(aInstance).Get<Mle::MleRouter>().IsDisabled(), error = kErrorInvalidState);
 
-    error = AsCoreType(aInstance).Get<Mac::Mac>().SetNetworkName(aNetworkName);
-    AsCoreType(aInstance).Get<MeshCoP::ActiveDataset>().Clear();
-    AsCoreType(aInstance).Get<MeshCoP::PendingDataset>().Clear();
+    error = AsCoreType(aInstance).Get<MeshCoP::NetworkNameManager>().SetNetworkName(aNetworkName);
+    AsCoreType(aInstance).Get<MeshCoP::ActiveDatasetManager>().Clear();
+    AsCoreType(aInstance).Get<MeshCoP::PendingDatasetManager>().Clear();
 
 exit:
     return error;
@@ -220,7 +220,7 @@
 #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
 const char *otThreadGetDomainName(otInstance *aInstance)
 {
-    return AsCoreType(aInstance).Get<Mac::Mac>().GetDomainName().GetAsCString();
+    return AsCoreType(aInstance).Get<MeshCoP::NetworkNameManager>().GetDomainName().GetAsCString();
 }
 
 otError otThreadSetDomainName(otInstance *aInstance, const char *aDomainName)
@@ -229,7 +229,7 @@
 
     VerifyOrExit(AsCoreType(aInstance).Get<Mle::MleRouter>().IsDisabled(), error = kErrorInvalidState);
 
-    error = AsCoreType(aInstance).Get<Mac::Mac>().SetDomainName(aDomainName);
+    error = AsCoreType(aInstance).Get<MeshCoP::NetworkNameManager>().SetDomainName(aDomainName);
 
 exit:
     return error;
@@ -295,7 +295,7 @@
 
 otError otThreadBecomeChild(otInstance *aInstance)
 {
-    return AsCoreType(aInstance).Get<Mle::MleRouter>().BecomeChild(Mle::kAttachAny);
+    return AsCoreType(aInstance).Get<Mle::MleRouter>().BecomeChild();
 }
 
 otError otThreadGetNextNeighborInfo(otInstance *aInstance, otNeighborInfoIterator *aIterator, otNeighborInfo *aInfo)
diff --git a/src/core/api/thread_ftd_api.cpp b/src/core/api/thread_ftd_api.cpp
index 3333e85..e8d3a4b 100644
--- a/src/core/api/thread_ftd_api.cpp
+++ b/src/core/api/thread_ftd_api.cpp
@@ -296,8 +296,8 @@
     VerifyOrExit(AsCoreType(aInstance).Get<Mle::MleRouter>().IsDisabled(), error = kErrorInvalidState);
 
     AsCoreType(aInstance).Get<KeyManager>().SetPskc(AsCoreType(aPskc));
-    AsCoreType(aInstance).Get<MeshCoP::ActiveDataset>().Clear();
-    AsCoreType(aInstance).Get<MeshCoP::PendingDataset>().Clear();
+    AsCoreType(aInstance).Get<MeshCoP::ActiveDatasetManager>().Clear();
+    AsCoreType(aInstance).Get<MeshCoP::PendingDatasetManager>().Clear();
 
 exit:
     return error;
@@ -313,8 +313,8 @@
     VerifyOrExit(instance.Get<Mle::MleRouter>().IsDisabled(), error = kErrorInvalidState);
 
     instance.Get<KeyManager>().SetPskcRef(aKeyRef);
-    instance.Get<MeshCoP::ActiveDataset>().Clear();
-    instance.Get<MeshCoP::PendingDataset>().Clear();
+    instance.Get<MeshCoP::ActiveDatasetManager>().Clear();
+    instance.Get<MeshCoP::PendingDatasetManager>().Clear();
 
 exit:
     return error;
diff --git a/src/core/backbone_router/backbone_tmf.cpp b/src/core/backbone_router/backbone_tmf.cpp
index 676906f..293793f 100644
--- a/src/core/backbone_router/backbone_tmf.cpp
+++ b/src/core/backbone_router/backbone_tmf.cpp
@@ -36,11 +36,13 @@
 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
 
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 
 namespace ot {
 namespace BackboneRouter {
 
+RegisterLogModule("Bbr");
+
 Error BackboneTmfAgent::Start(void)
 {
     Error error = kErrorNone;
@@ -96,11 +98,11 @@
 
     if (aError == kErrorNone)
     {
-        otLogInfoBbr("%s %s: %s", aText, aAddress.ToString().AsCString(), ErrorToString(aError));
+        LogInfo("%s %s: %s", aText, aAddress.ToString().AsCString(), ErrorToString(aError));
     }
     else
     {
-        otLogWarnBbr("%s %s: %s", aText, aAddress.ToString().AsCString(), ErrorToString(aError));
+        LogWarn("%s %s: %s", aText, aAddress.ToString().AsCString(), ErrorToString(aError));
     }
 }
 
diff --git a/src/core/backbone_router/bbr_leader.cpp b/src/core/backbone_router/bbr_leader.cpp
index 41df0c1..4b64fff 100644
--- a/src/core/backbone_router/bbr_leader.cpp
+++ b/src/core/backbone_router/bbr_leader.cpp
@@ -37,11 +37,12 @@
 
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
 
 namespace ot {
 namespace BackboneRouter {
 
+RegisterLogModule("BbrLeader");
+
 Leader::Leader(Instance &aInstance)
     : InstanceLocator(aInstance)
 {
@@ -81,24 +82,24 @@
     return error;
 }
 
-#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_BBR == 1)
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
 
 void Leader::LogBackboneRouterPrimary(State aState, const BackboneRouterConfig &aConfig) const
 {
     OT_UNUSED_VARIABLE(aConfig);
 
-    otLogInfoBbr("PBBR state: %s", StateToString(aState));
+    LogInfo("PBBR state: %s", StateToString(aState));
 
     if (aState != kStateRemoved && aState != kStateNone)
     {
-        otLogInfoBbr("Rloc16: 0x%4X, seqno: %d, delay: %d, timeout %d", aConfig.mServer16, aConfig.mSequenceNumber,
-                     aConfig.mReregistrationDelay, aConfig.mMlrTimeout);
+        LogInfo("Rloc16: 0x%4X, seqno: %d, delay: %d, timeout %d", aConfig.mServer16, aConfig.mSequenceNumber,
+                aConfig.mReregistrationDelay, aConfig.mMlrTimeout);
     }
 }
 
 void Leader::LogDomainPrefix(DomainPrefixState aState, const Ip6::Prefix &aPrefix) const
 {
-    otLogInfoBbr("Domain Prefix: %s, state: %s", aPrefix.ToString().AsCString(), DomainPrefixStateToString(aState));
+    LogInfo("Domain Prefix: %s, state: %s", aPrefix.ToString().AsCString(), DomainPrefixStateToString(aState));
 }
 
 const char *Leader::StateToString(State aState)
@@ -141,7 +142,7 @@
     return kPrefixStateStrings[aState];
 }
 
-#endif // (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_BBR == 1)
+#endif // OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
 
 void Leader::Update(void)
 {
@@ -204,7 +205,7 @@
 
         if (config.mMlrTimeout != origMlrTimeout)
         {
-            otLogNoteBbr("Leader MLR Timeout is normalized from %u to %u", origMlrTimeout, config.mMlrTimeout);
+            LogNote("Leader MLR Timeout is normalized from %u to %u", origMlrTimeout, config.mMlrTimeout);
         }
     }
 
diff --git a/src/core/backbone_router/bbr_leader.hpp b/src/core/backbone_router/bbr_leader.hpp
index 823d619..6266d02 100644
--- a/src/core/backbone_router/bbr_leader.hpp
+++ b/src/core/backbone_router/bbr_leader.hpp
@@ -44,6 +44,7 @@
 #include "coap/coap.hpp"
 #include "coap/coap_message.hpp"
 #include "common/locator.hpp"
+#include "common/log.hpp"
 #include "common/non_copyable.hpp"
 #include "net/ip6_address.hpp"
 
@@ -175,7 +176,7 @@
 private:
     void UpdateBackboneRouterPrimary(void);
     void UpdateDomainPrefixConfig(void);
-#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_BBR == 1)
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
     void               LogBackboneRouterPrimary(State aState, const BackboneRouterConfig &aConfig) const;
     void               LogDomainPrefix(DomainPrefixState aState, const Ip6::Prefix &aPrefix) const;
     static const char *StateToString(State aState);
diff --git a/src/core/backbone_router/bbr_local.cpp b/src/core/backbone_router/bbr_local.cpp
index 862860f..4c7945a 100644
--- a/src/core/backbone_router/bbr_local.cpp
+++ b/src/core/backbone_router/bbr_local.cpp
@@ -38,7 +38,7 @@
 #include "common/code_utils.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "common/random.hpp"
 #include "thread/mle_types.hpp"
 #include "thread/thread_netif.hpp"
@@ -47,6 +47,8 @@
 
 namespace BackboneRouter {
 
+RegisterLogModule("BbrLocal");
+
 Local::Local(Instance &aInstance)
     : InstanceLocator(aInstance)
     , mState(OT_BACKBONE_ROUTER_STATE_DISABLED)
@@ -57,9 +59,6 @@
     , mIsServiceAdded(false)
     , mDomainPrefixCallback(nullptr)
     , mDomainPrefixCallbackContext(nullptr)
-#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
-    , mSkipSeqNumIncrease(false)
-#endif
 {
     mDomainPrefixConfig.GetPrefix().SetLength(0);
 
@@ -273,19 +272,7 @@
         mSequenceNumber      = aConfig.mSequenceNumber;
         mReregistrationDelay = aConfig.mReregistrationDelay;
         mMlrTimeout          = aConfig.mMlrTimeout;
-
-#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
-        if (mSkipSeqNumIncrease)
-        {
-            // BBR-TC-02 forces Sequence Number for the reference device with raw UDP API
-            // Do not increase Sequence Number in that case.
-        }
-        else
-#endif
-        {
-            SequenceNumberIncrease();
-        }
-
+        SequenceNumberIncrease();
         Get<Notifier>().Signal(kEventThreadBackboneRouterLocalChanged);
         if (AddService(true /* Force registration to refresh and restore Primary state */) == kErrorNone)
         {
@@ -458,17 +445,17 @@
     LogDomainPrefix("Add", error);
 }
 
-#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_BBR == 1)
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
 void Local::LogDomainPrefix(const char *aAction, Error aError)
 {
-    otLogInfoBbr("%s Domain Prefix: %s, %s", aAction, mDomainPrefixConfig.GetPrefix().ToString().AsCString(),
-                 ErrorToString(aError));
+    LogInfo("%s Domain Prefix: %s, %s", aAction, mDomainPrefixConfig.GetPrefix().ToString().AsCString(),
+            ErrorToString(aError));
 }
 
 void Local::LogBackboneRouterService(const char *aAction, Error aError)
 {
-    otLogInfoBbr("%s BBR Service: seqno (%d), delay (%ds), timeout (%ds), %s", aAction, mSequenceNumber,
-                 mReregistrationDelay, mMlrTimeout, ErrorToString(aError));
+    LogInfo("%s BBR Service: seqno (%d), delay (%ds), timeout (%ds), %s", aAction, mSequenceNumber,
+            mReregistrationDelay, mMlrTimeout, ErrorToString(aError));
 }
 #endif
 
@@ -478,13 +465,6 @@
     mDomainPrefixCallbackContext = aContext;
 }
 
-#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
-void Local::ConfigSkipSeqNumIncrease(bool aSkip)
-{
-    mSkipSeqNumIncrease = aSkip;
-}
-#endif
-
 } // namespace BackboneRouter
 
 } // namespace ot
diff --git a/src/core/backbone_router/bbr_local.hpp b/src/core/backbone_router/bbr_local.hpp
index 23c337a..b7a76f3 100644
--- a/src/core/backbone_router/bbr_local.hpp
+++ b/src/core/backbone_router/bbr_local.hpp
@@ -55,6 +55,7 @@
 
 #include "backbone_router/bbr_leader.hpp"
 #include "common/locator.hpp"
+#include "common/log.hpp"
 #include "common/non_copyable.hpp"
 #include "net/netif.hpp"
 #include "thread/network_data.hpp"
@@ -254,27 +255,13 @@
      */
     void SetDomainPrefixCallback(otBackboneRouterDomainPrefixCallback aCallback, void *aContext);
 
-#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
-    /**
-     * This method configures the ability to increase or not the BBR Dataset Sequence Number when a
-     * BBR recovers its BBR Dataset from the Leader's Network Data.
-     *
-     * Note: available only when `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is enabled.
-     *       Only used for certification.
-     *
-     * @param[in] aSkip  Whether to skip the increase of Sequence Number or not.
-     *
-     */
-    void ConfigSkipSeqNumIncrease(bool aSkip);
-#endif
-
 private:
     void SetState(BackboneRouterState aState);
     void RemoveService(void);
     void AddDomainPrefixToNetworkData(void);
     void RemoveDomainPrefixFromNetworkData(void);
     void SequenceNumberIncrease(void);
-#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_BBR == 1)
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
     void LogBackboneRouterService(const char *aAction, Error aError);
     void LogDomainPrefix(const char *aAction, Error aError);
 #else
@@ -300,10 +287,6 @@
     Ip6::Address                         mAllDomainBackboneRouters;
     otBackboneRouterDomainPrefixCallback mDomainPrefixCallback;
     void *                               mDomainPrefixCallbackContext;
-
-#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
-    bool mSkipSeqNumIncrease : 1;
-#endif
 };
 
 } // namespace BackboneRouter
diff --git a/src/core/backbone_router/bbr_manager.cpp b/src/core/backbone_router/bbr_manager.cpp
index 405d12e..850245d 100644
--- a/src/core/backbone_router/bbr_manager.cpp
+++ b/src/core/backbone_router/bbr_manager.cpp
@@ -39,7 +39,7 @@
 #include "common/code_utils.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "common/random.hpp"
 #include "thread/mle_types.hpp"
 #include "thread/thread_netif.hpp"
@@ -50,6 +50,8 @@
 
 namespace BackboneRouter {
 
+RegisterLogModule("BbrManager");
+
 Manager::Manager(Instance &aInstance)
     : InstanceLocator(aInstance)
 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE
@@ -108,11 +110,11 @@
 
             if (error != kErrorNone)
             {
-                otLogWarnBbr("Stop Backbone TMF agent: %s", ErrorToString(error));
+                LogWarn("Stop Backbone TMF agent: %s", ErrorToString(error));
             }
             else
             {
-                otLogInfoBbr("Stop Backbone TMF agent: %s", ErrorToString(error));
+                LogInfo("Stop Backbone TMF agent: %s", ErrorToString(error));
             }
         }
         else
@@ -226,7 +228,7 @@
 
             if (timeout != origTimeout)
             {
-                otLogNoteBbr("MLR.req: MLR timeout is normalized from %u to %u", origTimeout, timeout);
+                LogNote("MLR.req: MLR timeout is normalized from %u to %u", origTimeout, timeout);
             }
         }
     }
@@ -300,13 +302,11 @@
                                                         Ip6::Address *             aFailedAddresses,
                                                         uint8_t                    aFailedAddressNum)
 {
-    Error          error   = kErrorNone;
-    Coap::Message *message = nullptr;
+    Error          error = kErrorNone;
+    Coap::Message *message;
 
-    VerifyOrExit((message = Get<Tmf::Agent>().NewMessage()) != nullptr, error = kErrorNoBufs);
-
-    SuccessOrExit(message->SetDefaultResponseHeader(aMessage));
-    SuccessOrExit(message->SetPayloadMarker());
+    message = Get<Tmf::Agent>().NewResponseMessage(aMessage);
+    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
     SuccessOrExit(Tlv::Append<ThreadStatusTlv>(*message, aStatus));
 
@@ -328,7 +328,7 @@
 
 exit:
     FreeMessageOnError(message, error);
-    otLogInfoBbr("Sent MLR.rsp (status=%d): %s", aStatus, ErrorToString(error));
+    LogInfo("Sent MLR.rsp (status=%d): %s", aStatus, ErrorToString(error));
 }
 
 void Manager::SendBackboneMulticastListenerRegistration(const Ip6::Address *aAddresses,
@@ -343,10 +343,8 @@
 
     OT_ASSERT(aAddressNum >= kIp6AddressesNumMin && aAddressNum <= kIp6AddressesNumMax);
 
-    VerifyOrExit((message = backboneTmf.NewMessage()) != nullptr, error = kErrorNoBufs);
-
-    SuccessOrExit(error = message->InitAsNonConfirmablePost(UriPath::kBackboneMlr));
-    SuccessOrExit(error = message->SetPayloadMarker());
+    message = backboneTmf.NewNonConfirmablePostMessage(UriPath::kBackboneMlr);
+    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
     addressesTlv.Init();
     addressesTlv.SetLength(sizeof(Ip6::Address) * aAddressNum);
@@ -365,7 +363,7 @@
 
 exit:
     FreeMessageOnError(message, error);
-    otLogInfoBbr("Sent BMLR.ntf: %s", ErrorToString(error));
+    LogInfo("Sent BMLR.ntf: %s", ErrorToString(error));
 }
 #endif // OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE
 
@@ -430,7 +428,7 @@
     }
 
 exit:
-    otLogInfoBbr("Received DUA.req on %s: %s", (isPrimary ? "PBBR" : "SBBR"), ErrorToString(error));
+    LogInfo("Received DUA.req on %s: %s", (isPrimary ? "PBBR" : "SBBR"), ErrorToString(error));
 
     if (error == kErrorNone)
     {
@@ -452,13 +450,11 @@
                                           const Ip6::Address &       aTarget,
                                           ThreadStatusTlv::DuaStatus aStatus)
 {
-    Error          error   = kErrorNone;
-    Coap::Message *message = nullptr;
+    Error          error = kErrorNone;
+    Coap::Message *message;
 
-    VerifyOrExit((message = Get<Tmf::Agent>().NewMessage()) != nullptr, error = kErrorNoBufs);
-
-    SuccessOrExit(message->SetDefaultResponseHeader(aMessage));
-    SuccessOrExit(message->SetPayloadMarker());
+    message = Get<Tmf::Agent>().NewResponseMessage(aMessage);
+    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
     SuccessOrExit(Tlv::Append<ThreadStatusTlv>(*message, aStatus));
     SuccessOrExit(Tlv::Append<ThreadTargetTlv>(*message, aTarget));
@@ -467,8 +463,7 @@
 
 exit:
     FreeMessageOnError(message, error);
-    otLogInfoBbr("Sent DUA.rsp for DUA %s, status %d %s", aTarget.ToString().AsCString(), aStatus,
-                 ErrorToString(error));
+    LogInfo("Sent DUA.rsp for DUA %s, status %d %s", aTarget.ToString().AsCString(), aStatus, ErrorToString(error));
 }
 #endif // OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE
 
@@ -534,10 +529,8 @@
 
     VerifyOrExit(Get<Local>().IsPrimary(), error = kErrorInvalidState);
 
-    VerifyOrExit((message = mBackboneTmfAgent.NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
-
-    SuccessOrExit(error = message->InitAsNonConfirmablePost(UriPath::kBackboneQuery));
-    SuccessOrExit(error = message->SetPayloadMarker());
+    message = mBackboneTmfAgent.NewPriorityNonConfirmablePostMessage(UriPath::kBackboneQuery);
+    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
     SuccessOrExit(error = Tlv::Append<ThreadTargetTlv>(*message, aDua));
 
@@ -555,8 +548,7 @@
     error = mBackboneTmfAgent.SendMessage(*message, messageInfo);
 
 exit:
-    otLogInfoBbr("SendBackboneQuery for %s (rloc16=%04x): %s", aDua.ToString().AsCString(), aRloc16,
-                 ErrorToString(error));
+    LogInfo("SendBackboneQuery for %s (rloc16=%04x): %s", aDua.ToString().AsCString(), aRloc16, ErrorToString(error));
     FreeMessageOnError(message, error);
     return error;
 }
@@ -583,8 +575,8 @@
     error = Tlv::Find<ThreadRloc16Tlv>(aMessage, rloc16);
     VerifyOrExit(error == kErrorNone || error == kErrorNotFound);
 
-    otLogInfoBbr("Received BB.qry from %s for %s (rloc16=%04x)", aMessageInfo.GetPeerAddr().ToString().AsCString(),
-                 dua.ToString().AsCString(), rloc16);
+    LogInfo("Received BB.qry from %s for %s (rloc16=%04x)", aMessageInfo.GetPeerAddr().ToString().AsCString(),
+            dua.ToString().AsCString(), rloc16);
 
     ndProxy = mNdProxyTable.ResolveDua(dua);
     VerifyOrExit(ndProxy != nullptr && !ndProxy->GetDadFlag(), error = kErrorNotFound);
@@ -592,7 +584,7 @@
     error = SendBackboneAnswer(aMessageInfo, dua, rloc16, *ndProxy);
 
 exit:
-    otLogInfoBbr("HandleBackboneQuery: %s", ErrorToString(error));
+    LogInfo("HandleBackboneQuery: %s", ErrorToString(error));
 }
 
 void Manager::HandleBackboneAnswer(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
@@ -643,7 +635,7 @@
     SuccessOrExit(error = mBackboneTmfAgent.SendEmptyAck(aMessage, aMessageInfo));
 
 exit:
-    otLogInfoBbr("HandleBackboneAnswer: %s", ErrorToString(error));
+    LogInfo("HandleBackboneAnswer: %s", ErrorToString(error));
 }
 
 Error Manager::SendProactiveBackboneNotification(const Ip6::Address &            aDua,
@@ -687,7 +679,7 @@
     SuccessOrExit(error = Tlv::Append<ThreadLastTransactionTimeTlv>(*message, aTimeSinceLastTransaction));
 
     {
-        const Mac::NameData nameData = Get<Mac::Mac>().GetNetworkName().GetAsData();
+        const MeshCoP::NameData nameData = Get<MeshCoP::NetworkNameManager>().GetNetworkName().GetAsData();
 
         SuccessOrExit(error = Tlv::Append<ThreadNetworkNameTlv>(*message, nameData.GetBuffer(), nameData.GetLength()));
     }
@@ -706,8 +698,8 @@
     error = mBackboneTmfAgent.SendMessage(*message, messageInfo);
 
 exit:
-    otLogInfoBbr("Send %s for %s (rloc16=%04x): %s", proactive ? "PRO_BB.ntf" : "BB.ans", aDua.ToString().AsCString(),
-                 aSrcRloc16, ErrorToString(error));
+    LogInfo("Send %s for %s (rloc16=%04x): %s", proactive ? "PRO_BB.ntf" : "BB.ans", aDua.ToString().AsCString(),
+            aSrcRloc16, ErrorToString(error));
 
     FreeMessageOnError(message, error);
     return error;
@@ -736,8 +728,8 @@
     ot::BackboneRouter::NdProxyTable::NotifyDadComplete(*ndProxy, duplicate);
 
 exit:
-    otLogInfoBbr("HandleDadBackboneAnswer: %s, target=%s, mliid=%s, duplicate=%s", ErrorToString(error),
-                 aDua.ToString().AsCString(), aMeshLocalIid.ToString().AsCString(), duplicate ? "Y" : "N");
+    LogInfo("HandleDadBackboneAnswer: %s, target=%s, mliid=%s, duplicate=%s", ErrorToString(error),
+            aDua.ToString().AsCString(), aMeshLocalIid.ToString().AsCString(), duplicate ? "Y" : "N");
 }
 
 void Manager::HandleExtendedBackboneAnswer(const Ip6::Address &            aDua,
@@ -750,9 +742,8 @@
     dest.SetToRoutingLocator(Get<Mle::MleRouter>().GetMeshLocalPrefix(), aSrcRloc16);
     Get<AddressResolver>().SendAddressQueryResponse(aDua, aMeshLocalIid, &aTimeSinceLastTransaction, dest);
 
-    otLogInfoBbr("HandleExtendedBackboneAnswer: target=%s, mliid=%s, LTT=%lds, rloc16=%04x",
-                 aDua.ToString().AsCString(), aMeshLocalIid.ToString().AsCString(), aTimeSinceLastTransaction,
-                 aSrcRloc16);
+    LogInfo("HandleExtendedBackboneAnswer: target=%s, mliid=%s, LTT=%lds, rloc16=%04x", aDua.ToString().AsCString(),
+            aMeshLocalIid.ToString().AsCString(), aTimeSinceLastTransaction, aSrcRloc16);
 }
 
 void Manager::HandleProactiveBackboneNotification(const Ip6::Address &            aDua,
@@ -788,8 +779,8 @@
     }
 
 exit:
-    otLogInfoBbr("HandleProactiveBackboneNotification: %s, target=%s, mliid=%s, LTT=%lds", ErrorToString(error),
-                 aDua.ToString().AsCString(), aMeshLocalIid.ToString().AsCString(), aTimeSinceLastTransaction);
+    LogInfo("HandleProactiveBackboneNotification: %s, target=%s, mliid=%s, LTT=%lds", ErrorToString(error),
+            aDua.ToString().AsCString(), aMeshLocalIid.ToString().AsCString(), aTimeSinceLastTransaction);
 }
 #endif // OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE
 
@@ -799,11 +790,11 @@
 
     if (aError == kErrorNone)
     {
-        otLogInfoBbr("%s: %s", aText, ErrorToString(aError));
+        LogInfo("%s: %s", aText, ErrorToString(aError));
     }
     else
     {
-        otLogWarnBbr("%s: %s", aText, ErrorToString(aError));
+        LogWarn("%s: %s", aText, ErrorToString(aError));
     }
 }
 
diff --git a/src/core/backbone_router/multicast_listeners_table.cpp b/src/core/backbone_router/multicast_listeners_table.cpp
index bf9b51e..35a4972 100644
--- a/src/core/backbone_router/multicast_listeners_table.cpp
+++ b/src/core/backbone_router/multicast_listeners_table.cpp
@@ -35,10 +35,11 @@
 
 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE
 
+#include "common/array.hpp"
 #include "common/code_utils.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "common/random.hpp"
 #include "thread/mle_types.hpp"
 #include "thread/thread_netif.hpp"
@@ -48,6 +49,8 @@
 
 namespace BackboneRouter {
 
+RegisterLogModule("BbrMlt");
+
 Error MulticastListenersTable::Add(const Ip6::Address &aAddress, Time aExpireTime)
 {
     Error error = kErrorNone;
@@ -66,7 +69,7 @@
         }
     }
 
-    VerifyOrExit(mNumValidListeners < OT_ARRAY_LENGTH(mListeners), error = kErrorNoBufs);
+    VerifyOrExit(mNumValidListeners < GetArrayLength(mListeners), error = kErrorNoBufs);
 
     mListeners[mNumValidListeners].SetAddress(aAddress);
     mListeners[mNumValidListeners].SetExpireTime(aExpireTime);
@@ -154,8 +157,8 @@
     OT_UNUSED_VARIABLE(aExpireTime);
     OT_UNUSED_VARIABLE(aError);
 
-    otLogDebgBbr("MulticastListenersTable: %s %s expire %u: %s", aAction, aAddress.ToString().AsCString(),
-                 aExpireTime.GetValue(), ErrorToString(aError));
+    LogDebg("%s %s expire %u: %s", aAction, aAddress.ToString().AsCString(), aExpireTime.GetValue(),
+            ErrorToString(aError));
 }
 
 void MulticastListenersTable::FixHeap(uint16_t aIndex)
diff --git a/src/core/backbone_router/ndproxy_table.cpp b/src/core/backbone_router/ndproxy_table.cpp
index f680daa..9d65958 100644
--- a/src/core/backbone_router/ndproxy_table.cpp
+++ b/src/core/backbone_router/ndproxy_table.cpp
@@ -35,13 +35,16 @@
 
 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE
 
+#include "common/array.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 
 namespace ot {
 
 namespace BackboneRouter {
 
+RegisterLogModule("BbrNdProxy");
+
 void NdProxyTable::NdProxy::Init(const Ip6::InterfaceIdentifier &aAddressIid,
                                  const Ip6::InterfaceIdentifier &aMeshLocalIid,
                                  uint16_t                        aRloc16,
@@ -107,7 +110,7 @@
     : InstanceLocator(aInstance)
 {
     NdProxyTable &table = GetInstance().Get<BackboneRouter::NdProxyTable>();
-    mItem               = OT_ARRAY_END(table.mProxies);
+    mItem               = GetArrayEnd(table.mProxies);
 }
 
 void NdProxyTable::Iterator::Advance(void)
@@ -117,7 +120,7 @@
     do
     {
         mItem++;
-    } while (mItem < OT_ARRAY_END(table.mProxies) && !MatchesFilter(*mItem, mFilter));
+    } while (mItem < GetArrayEnd(table.mProxies) && !MatchesFilter(*mItem, mFilter));
 }
 
 void NdProxyTable::Erase(NdProxy &aNdProxy)
@@ -146,7 +149,7 @@
         mCallback(mCallbackContext, OT_BACKBONE_ROUTER_NDPROXY_CLEARED, nullptr);
     }
 
-    otLogNoteBbr("NdProxyTable::Clear!");
+    LogInfo("NdProxyTable::Clear!");
 }
 
 Error NdProxyTable::Register(const Ip6::InterfaceIdentifier &aAddressIid,
@@ -185,8 +188,8 @@
     mIsAnyDadInProcess = true;
 
 exit:
-    otLogInfoBbr("NdProxyTable::Register %s MLIID %s RLOC16 %04x LTT %u => %s", aAddressIid.ToString().AsCString(),
-                 aMeshLocalIid.ToString().AsCString(), aRloc16, timeSinceLastTransaction, ErrorToString(error));
+    LogInfo("NdProxyTable::Register %s MLIID %s RLOC16 %04x LTT %u => %s", aAddressIid.ToString().AsCString(),
+            aMeshLocalIid.ToString().AsCString(), aRloc16, timeSinceLastTransaction, ErrorToString(error));
     return error;
 }
 
@@ -203,8 +206,8 @@
     }
 
 exit:
-    otLogDebgBbr("NdProxyTable::FindByAddressIid(%s) => %s", aAddressIid.ToString().AsCString(),
-                 found ? found->mMeshLocalIid.ToString().AsCString() : "NOT_FOUND");
+    LogDebg("NdProxyTable::FindByAddressIid(%s) => %s", aAddressIid.ToString().AsCString(),
+            found ? found->mMeshLocalIid.ToString().AsCString() : "NOT_FOUND");
     return found;
 }
 
@@ -221,8 +224,8 @@
     }
 
 exit:
-    otLogDebgBbr("NdProxyTable::FindByMeshLocalIid(%s) => %s", aMeshLocalIid.ToString().AsCString(),
-                 found ? found->mAddressIid.ToString().AsCString() : "NOT_FOUND");
+    LogDebg("NdProxyTable::FindByMeshLocalIid(%s) => %s", aMeshLocalIid.ToString().AsCString(),
+            found ? found->mAddressIid.ToString().AsCString() : "NOT_FOUND");
     return found;
 }
 
@@ -236,7 +239,7 @@
     }
 
 exit:
-    otLogDebgBbr("NdProxyTable::FindInvalid() => %s", found ? "OK" : "NOT_FOUND");
+    LogDebg("NdProxyTable::FindInvalid() => %s", found ? "OK" : "NOT_FOUND");
     return found;
 }
 
diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp
index f861681..44ce6a0 100644
--- a/src/core/border_router/routing_manager.cpp
+++ b/src/core/border_router/routing_manager.cpp
@@ -44,7 +44,7 @@
 #include "common/debug.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "common/random.hpp"
 #include "common/settings.hpp"
 #include "net/ip6.hpp"
@@ -56,6 +56,8 @@
 
 namespace BorderRouter {
 
+RegisterLogModule("BorderRouter");
+
 RoutingManager::RoutingManager(Instance &aInstance)
     : InstanceLocator(aInstance)
     , mIsRunning(false)
@@ -70,6 +72,7 @@
     , mDiscoveredPrefixInvalidTimer(aInstance, HandleDiscoveredPrefixInvalidTimer)
     , mDiscoveredPrefixStaleTimer(aInstance, HandleDiscoveredPrefixStaleTimer)
     , mRouterAdvertisementCount(0)
+    , mLastRouterAdvertisementSendTime(TimerMilli::GetNow() - kMinDelayBetweenRtrAdvs)
 #if OPENTHREAD_CONFIG_BORDER_ROUTING_VICARIOUS_RS_ENABLE
     , mVicariousRouterSolicitTimer(aInstance, HandleVicariousRouterSolicitTimer)
 #endif
@@ -77,6 +80,8 @@
     , mRouterSolicitCount(0)
     , mRoutingPolicyTimer(aInstance, HandleRoutingPolicyTimer)
 {
+    mBrUlaPrefix.Clear();
+
     mLocalOmrPrefix.Clear();
 
     mLocalOnLinkPrefix.Clear();
@@ -91,11 +96,12 @@
     VerifyOrExit(!IsInitialized(), error = kErrorInvalidState);
     VerifyOrExit(aInfraIfIndex > 0, error = kErrorInvalidArgs);
 
-    SuccessOrExit(error = LoadOrGenerateRandomOmrPrefix());
-    SuccessOrExit(error = LoadOrGenerateRandomOnLinkPrefix());
+    SuccessOrExit(error = LoadOrGenerateRandomBrUlaPrefix());
+    GenerateOmrPrefix();
 #if OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE
-    SuccessOrExit(error = LoadOrGenerateRandomNat64Prefix());
+    GenerateNat64Prefix();
 #endif
+    SuccessOrExit(error = LoadOrGenerateRandomOnLinkPrefix());
 
     mInfraIfIndex = aInfraIfIndex;
 
@@ -160,94 +166,95 @@
 }
 #endif
 
-Error RoutingManager::LoadOrGenerateRandomOmrPrefix(void)
+Error RoutingManager::LoadOrGenerateRandomBrUlaPrefix(void)
 {
-    Error error = kErrorNone;
+    Error error     = kErrorNone;
+    bool  generated = false;
 
-    if (Get<Settings>().Read<Settings::OmrPrefix>(mLocalOmrPrefix) != kErrorNone || !IsValidOmrPrefix(mLocalOmrPrefix))
+    if (Get<Settings>().Read<Settings::BrUlaPrefix>(mBrUlaPrefix) != kErrorNone || !IsValidBrUlaPrefix(mBrUlaPrefix))
     {
-        Ip6::NetworkPrefix randomOmrPrefix;
+        Ip6::NetworkPrefix randomUlaPrefix;
 
-        otLogNoteBr("No valid OMR prefix found in settings, generating new one");
+        LogNote("No valid /48 BR ULA prefix found in settings, generating new one");
 
-        // TODO: generate OMR prefix from the /48 BR ULA prefix
-        error = randomOmrPrefix.GenerateRandomUla();
-        if (error != kErrorNone)
-        {
-            otLogCritBr("Failed to generate random OMR prefix");
-            ExitNow();
-        }
+        SuccessOrExit(error = randomUlaPrefix.GenerateRandomUla());
 
-        mLocalOmrPrefix.Set(randomOmrPrefix);
-        IgnoreError(Get<Settings>().Save<Settings::OmrPrefix>(mLocalOmrPrefix));
+        mBrUlaPrefix.Set(randomUlaPrefix);
+        mBrUlaPrefix.SetSubnetId(0);
+        mBrUlaPrefix.SetLength(kBrUlaPrefixLength);
+
+        IgnoreError(Get<Settings>().Save<Settings::BrUlaPrefix>(mBrUlaPrefix));
+        generated = true;
     }
 
+    OT_UNUSED_VARIABLE(generated);
+
+    LogNote("BR ULA prefix: %s (%s)", mBrUlaPrefix.ToString().AsCString(), generated ? "generated" : "loaded");
+
 exit:
+    if (error != kErrorNone)
+    {
+        LogCrit("Failed to generate random /48 BR ULA prefix");
+    }
     return error;
 }
 
+void RoutingManager::GenerateOmrPrefix(void)
+{
+    IgnoreError(Get<Settings>().Delete<Settings::LegacyOmrPrefix>());
+
+    mLocalOmrPrefix = mBrUlaPrefix;
+    mLocalOmrPrefix.SetSubnetId(kOmrPrefixSubnetId);
+    mLocalOmrPrefix.SetLength(kOmrPrefixLength);
+
+    LogInfo("Generated OMR prefix: %s", mLocalOmrPrefix.ToString().AsCString());
+}
+
+#if OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE
+void RoutingManager::GenerateNat64Prefix(void)
+{
+    mLocalNat64Prefix = mBrUlaPrefix;
+    mLocalNat64Prefix.SetSubnetId(kNat64PrefixSubnetId);
+    mLocalNat64Prefix.mPrefix.mFields.m32[2] = 0;
+    mLocalNat64Prefix.SetLength(kNat64PrefixLength);
+
+    LogInfo("Generated NAT64 prefix: %s", mLocalNat64Prefix.ToString().AsCString());
+}
+#endif
+
 Error RoutingManager::LoadOrGenerateRandomOnLinkPrefix(void)
 {
-    Error error = kErrorNone;
+    Error error     = kErrorNone;
+    bool  generated = false;
 
     if (Get<Settings>().Read<Settings::OnLinkPrefix>(mLocalOnLinkPrefix) != kErrorNone ||
         !mLocalOnLinkPrefix.IsUniqueLocal())
     {
         Ip6::NetworkPrefix randomOnLinkPrefix;
 
-        otLogNoteBr("No valid on-link prefix found in settings, generating new one");
-
         error = randomOnLinkPrefix.GenerateRandomUla();
         if (error != kErrorNone)
         {
-            otLogCritBr("Failed to generate random on-link prefix");
+            LogCrit("Failed to generate random on-link prefix");
             ExitNow();
         }
 
-        randomOnLinkPrefix.m8[6] = 0;
-        randomOnLinkPrefix.m8[7] = 0;
         mLocalOnLinkPrefix.Set(randomOnLinkPrefix);
+        mLocalOnLinkPrefix.SetSubnetId(0);
 
         IgnoreError(Get<Settings>().Save<Settings::OnLinkPrefix>(mLocalOnLinkPrefix));
+        generated = true;
     }
 
+    OT_UNUSED_VARIABLE(generated);
+
+    LogNote("Local on-link prefix: %s (%s)", mLocalOnLinkPrefix.ToString().AsCString(),
+            generated ? "generated" : "loaded");
+
 exit:
     return error;
 }
 
-#if OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE
-Error RoutingManager::LoadOrGenerateRandomNat64Prefix(void)
-{
-    Error error = kErrorNone;
-
-    if (Get<Settings>().Read<Settings::Nat64Prefix>(mLocalNat64Prefix) != kErrorNone ||
-        !mLocalNat64Prefix.IsValidNat64())
-    {
-        Ip6::NetworkPrefix randomNat64Prefix;
-        constexpr uint8_t  nat64PrefixLength = 96;
-
-        otLogNoteBr("No valid NAT64 prefix found in settings, generating new one");
-
-        // TODO: generate NAT64 prefix from the /48 BR ULA prefix
-        error = randomNat64Prefix.GenerateRandomUla();
-        if (error != kErrorNone)
-        {
-            otLogCritBr("Failed to generate random NAT64 prefix");
-            ExitNow();
-        }
-
-        mLocalNat64Prefix.Clear();
-        mLocalNat64Prefix.Set(randomNat64Prefix);
-        mLocalNat64Prefix.SetLength(nat64PrefixLength);
-
-        IgnoreError(Get<Settings>().Save<Settings::Nat64Prefix>(mLocalNat64Prefix));
-    }
-
-exit:
-    return error;
-}
-#endif
-
 void RoutingManager::EvaluateState(void)
 {
     if (mIsEnabled && Get<Mle::MleRouter>().IsAttached() && mInfraIfIsRunning)
@@ -264,7 +271,7 @@
 {
     if (!mIsRunning)
     {
-        otLogInfoBr("Border Routing manager started");
+        LogInfo("Border Routing manager started");
 
         mIsRunning = true;
         StartRouterSolicitationDelay();
@@ -315,7 +322,7 @@
 
     mRoutingPolicyTimer.Stop();
 
-    otLogInfoBr("Border Routing manager stopped");
+    LogInfo("Border Routing manager stopped");
 
     mIsRunning = false;
 
@@ -352,7 +359,7 @@
 exit:
     if (error != kErrorNone)
     {
-        otLogDebgBr("Dropped ICMPv6 message: %s", ErrorToString(error));
+        LogDebg("Dropped ICMPv6 message: %s", ErrorToString(error));
     }
 }
 
@@ -364,8 +371,8 @@
     VerifyOrExit(aInfraIfIndex == mInfraIfIndex, error = kErrorInvalidArgs);
     VerifyOrExit(aIsRunning != mInfraIfIsRunning);
 
-    otLogInfoBr("Infra interface (%u) state changed: %sRUNNING -> %sRUNNING", aInfraIfIndex,
-                (mInfraIfIsRunning ? "" : "NOT "), (aIsRunning ? "" : "NOT "));
+    LogInfo("Infra interface (%u) state changed: %sRUNNING -> %sRUNNING", aInfraIfIndex,
+            (mInfraIfIsRunning ? "" : "NOT "), (aIsRunning ? "" : "NOT "));
 
     mInfraIfIsRunning = aIsRunning;
     EvaluateState();
@@ -420,7 +427,7 @@
 
         if (aNewOmrPrefixes.PushBack(prefix) != kErrorNone)
         {
-            otLogWarnBr("EvaluateOmrPrefix: Too many OMR prefixes, ignoring prefix %s", prefix.ToString().AsCString());
+            LogWarn("EvaluateOmrPrefix: Too many OMR prefixes, ignoring prefix %s", prefix.ToString().AsCString());
             continue;
         }
 
@@ -439,7 +446,7 @@
 
     if (aNewOmrPrefixes.IsEmpty())
     {
-        otLogInfoBr("EvaluateOmrPrefix: No valid OMR prefixes found in Thread network");
+        LogInfo("EvaluateOmrPrefix: No valid OMR prefixes found in Thread network");
 
         if (PublishLocalOmrPrefix() == kErrorNone)
         {
@@ -449,18 +456,29 @@
         // The `aNewOmrPrefixes` remains empty if we fail to publish
         // the local OMR prefix.
     }
-    else if (publishedLocalOmrPrefix != nullptr && smallestOmrPrefix != publishedLocalOmrPrefix)
+    else
     {
-        otLogInfoBr("EvaluateOmrPrefix: There is already a smaller OMR prefix %s in the Thread network",
+        OT_ASSERT(smallestOmrPrefix != nullptr);
+
+        if (*smallestOmrPrefix == mLocalOmrPrefix)
+        {
+            IgnoreError(PublishLocalOmrPrefix());
+        }
+        else if (IsOmrPrefixAddedToLocalNetworkData())
+        {
+            LogInfo("EvaluateOmrPrefix: There is already a smaller OMR prefix %s in the Thread network",
                     smallestOmrPrefix->ToString().AsCString());
 
-        UnpublishLocalOmrPrefix();
+            UnpublishLocalOmrPrefix();
 
-        // Remove the local OMR prefix from the list by overwriting it
-        // with the last element and then popping it from the list.
-
-        *publishedLocalOmrPrefix = *aNewOmrPrefixes.Back();
-        aNewOmrPrefixes.PopBack();
+            // Remove the local OMR prefix from the list by overwriting it
+            // with the last element and then popping it from the list.
+            if (publishedLocalOmrPrefix != nullptr)
+            {
+                *publishedLocalOmrPrefix = *aNewOmrPrefixes.Back();
+                aNewOmrPrefixes.PopBack();
+            }
+        }
     }
 }
 
@@ -471,6 +489,8 @@
 
     OT_ASSERT(mIsRunning);
 
+    VerifyOrExit(!IsOmrPrefixAddedToLocalNetworkData());
+
     omrPrefixConfig.Clear();
     omrPrefixConfig.mPrefix       = mLocalOmrPrefix;
     omrPrefixConfig.mStable       = true;
@@ -483,15 +503,16 @@
     error = Get<NetworkData::Local>().AddOnMeshPrefix(omrPrefixConfig);
     if (error != kErrorNone)
     {
-        otLogWarnBr("Failed to publish local OMR prefix %s in Thread network: %s",
-                    mLocalOmrPrefix.ToString().AsCString(), ErrorToString(error));
+        LogWarn("Failed to publish local OMR prefix %s in Thread network: %s", mLocalOmrPrefix.ToString().AsCString(),
+                ErrorToString(error));
     }
     else
     {
         Get<NetworkData::Notifier>().HandleServerDataUpdated();
-        otLogInfoBr("Publishing local OMR prefix %s in Thread network", mLocalOmrPrefix.ToString().AsCString());
+        LogInfo("Publishing local OMR prefix %s in Thread network", mLocalOmrPrefix.ToString().AsCString());
     }
 
+exit:
     return error;
 }
 
@@ -501,19 +522,26 @@
 
     VerifyOrExit(mIsRunning);
 
+    VerifyOrExit(IsOmrPrefixAddedToLocalNetworkData());
+
     SuccessOrExit(error = Get<NetworkData::Local>().RemoveOnMeshPrefix(mLocalOmrPrefix));
 
     Get<NetworkData::Notifier>().HandleServerDataUpdated();
-    otLogInfoBr("Unpublishing local OMR prefix %s from Thread network", mLocalOmrPrefix.ToString().AsCString());
+    LogInfo("Unpublishing local OMR prefix %s from Thread network", mLocalOmrPrefix.ToString().AsCString());
 
 exit:
-    if (error != kErrorNone)
+    if (error != kErrorNone && error != kErrorNotFound)
     {
-        otLogWarnBr("Failed to unpublish local OMR prefix %s from Thread network: %s",
-                    mLocalOmrPrefix.ToString().AsCString(), ErrorToString(error));
+        LogWarn("Failed to unpublish local OMR prefix %s from Thread network: %s",
+                mLocalOmrPrefix.ToString().AsCString(), ErrorToString(error));
     }
 }
 
+bool RoutingManager::IsOmrPrefixAddedToLocalNetworkData(void) const
+{
+    return Get<NetworkData::Local>().ContainsOnMeshPrefix(mLocalOmrPrefix);
+}
+
 Error RoutingManager::AddExternalRoute(const Ip6::Prefix &aPrefix, RoutePreference aRoutePreference, bool aNat64)
 {
     Error                            error;
@@ -530,12 +558,12 @@
     error = Get<NetworkData::Local>().AddHasRoutePrefix(routeConfig);
     if (error != kErrorNone)
     {
-        otLogWarnBr("Failed to add external route %s: %s", aPrefix.ToString().AsCString(), ErrorToString(error));
+        LogWarn("Failed to add external route %s: %s", aPrefix.ToString().AsCString(), ErrorToString(error));
     }
     else
     {
         Get<NetworkData::Notifier>().HandleServerDataUpdated();
-        otLogInfoBr("Adding external route %s", aPrefix.ToString().AsCString());
+        LogInfo("Adding external route %s", aPrefix.ToString().AsCString());
     }
 
     return error;
@@ -550,12 +578,12 @@
     SuccessOrExit(error = Get<NetworkData::Local>().RemoveHasRoutePrefix(aPrefix));
 
     Get<NetworkData::Notifier>().HandleServerDataUpdated();
-    otLogInfoBr("Removing external route %s", aPrefix.ToString().AsCString());
+    LogInfo("Removing external route %s", aPrefix.ToString().AsCString());
 
 exit:
     if (error != kErrorNone)
     {
-        otLogWarnBr("Failed to remove external route %s: %s", aPrefix.ToString().AsCString(), ErrorToString(error));
+        LogWarn("Failed to remove external route %s: %s", aPrefix.ToString().AsCString(), ErrorToString(error));
     }
 }
 
@@ -604,8 +632,8 @@
         }
         else
         {
-            otLogInfoBr("EvaluateOnLinkPrefix: There is already smaller on-link prefix %s on interface %u",
-                        smallestOnLinkPrefix->ToString().AsCString(), mInfraIfIndex);
+            LogInfo("EvaluateOnLinkPrefix: There is already smaller on-link prefix %s on interface %u",
+                    smallestOnLinkPrefix->ToString().AsCString(), mInfraIfIndex);
             DeprecateOnLinkPrefix();
         }
     }
@@ -621,7 +649,7 @@
 
 void RoutingManager::HandleOnLinkPrefixDeprecateTimer(void)
 {
-    otLogInfoBr("Local on-link prefix %s expired", mLocalOnLinkPrefix.ToString().AsCString());
+    LogInfo("Local on-link prefix %s expired", mLocalOnLinkPrefix.ToString().AsCString());
     RemoveExternalRoute(mLocalOnLinkPrefix);
 }
 
@@ -629,7 +657,7 @@
 {
     OT_ASSERT(mIsAdvertisingLocalOnLinkPrefix);
 
-    otLogInfoBr("Deprecate local on-link prefix %s", mLocalOnLinkPrefix.ToString().AsCString());
+    LogInfo("Deprecate local on-link prefix %s", mLocalOnLinkPrefix.ToString().AsCString());
     mOnLinkPrefixDeprecateTimer.StartAt(mTimeAdvertisedOnLinkPrefix,
                                         TimeMilli::SecToMsec(kDefaultOnLinkPrefixLifetime));
 }
@@ -643,7 +671,7 @@
     NetworkData::ExternalRouteConfig config;
     Ip6::Prefix                      smallestNat64Prefix;
 
-    otLogInfoBr("Evaluating NAT64 prefix");
+    LogInfo("Evaluating NAT64 prefix");
 
     smallestNat64Prefix.Clear();
     while (Get<NetworkData::Leader>().GetNextExternalRoute(iterator, config) == kErrorNone)
@@ -661,8 +689,8 @@
 
     if (smallestNat64Prefix.GetLength() == 0 || smallestNat64Prefix == mLocalNat64Prefix)
     {
-        otLogInfoBr("No NAT64 prefix in Network Data is smaller than the local NAT64 prefix %s",
-                    mLocalNat64Prefix.ToString().AsCString());
+        LogInfo("No NAT64 prefix in Network Data is smaller than the local NAT64 prefix %s",
+                mLocalNat64Prefix.ToString().AsCString());
 
         // Advertise local NAT64 prefix.
         if (!mIsAdvertisingLocalNat64Prefix &&
@@ -675,8 +703,8 @@
     {
         // Withdraw local NAT64 prefix if it's not the smallest one in Network Data.
         // TODO: remove the prefix with lower preference after discovering upstream NAT64 prefix is supported
-        otLogNoteBr("Withdrawing local NAT64 prefix since a smaller one %s exists.",
-                    smallestNat64Prefix.ToString().AsCString());
+        LogNote("Withdrawing local NAT64 prefix since a smaller one %s exists.",
+                smallestNat64Prefix.ToString().AsCString());
 
         RemoveExternalRoute(mLocalNat64Prefix);
         mIsAdvertisingLocalNat64Prefix = false;
@@ -687,7 +715,7 @@
 // This method evaluate the routing policy depends on prefix and route
 // information on Thread Network and infra link. As a result, this
 // method May send RA messages on infra link and publish/unpublish
-// OMR prefix in the Thread network.
+// OMR and NAT64 prefix in the Thread network.
 void RoutingManager::EvaluateRoutingPolicy(void)
 {
     OT_ASSERT(mIsRunning);
@@ -695,9 +723,9 @@
     const Ip6::Prefix *newOnLinkPrefix = nullptr;
     OmrPrefixArray     newOmrPrefixes;
 
-    otLogInfoBr("Evaluating routing policy");
+    LogInfo("Evaluating routing policy");
 
-    // 0. Evaluate on-link & OMR prefixes.
+    // 0. Evaluate on-link, OMR and NAT64 prefixes.
     newOnLinkPrefix = EvaluateOnLinkPrefix();
     EvaluateOmrPrefix(newOmrPrefixes);
 #if OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE
@@ -710,13 +738,13 @@
     if (newOmrPrefixes.IsEmpty())
     {
         // This is the very exceptional case and happens only when we failed to publish
-        // our local OMR prefix to the Thread network. We schedule the Router Advertisement
+        // our local OMR prefix to the Thread network. We schedule the routing policy
         // timer to re-evaluate our routing policy in the future.
 
-        otLogWarnBr("No OMR prefix advertised! Start Router Advertisement timer for future evaluation");
+        LogWarn("No OMR prefix advertised! Start Routing Policy timer for future evaluation");
     }
 
-    // 2. Schedule Router Advertisement timer with random interval.
+    // 2. Schedule routing policy timer with random interval for the next Router Advertisement.
     {
         uint32_t nextSendDelay;
 
@@ -727,7 +755,6 @@
             nextSendDelay = kMaxInitRtrAdvInterval;
         }
 
-        otLogInfoBr("Router advertisement scheduled in %u seconds", nextSendDelay);
         StartRoutingPolicyEvaluationDelay(Time::SecToMsec(nextSendDelay));
     }
 
@@ -745,8 +772,15 @@
 
 void RoutingManager::StartRoutingPolicyEvaluationDelay(uint32_t aDelayMilli)
 {
-    otLogInfoBr("Start evaluating routing policy, scheduled in %u milliseconds", aDelayMilli);
-    mRoutingPolicyTimer.FireAtIfEarlier(TimerMilli::GetNow() + aDelayMilli);
+    TimeMilli now          = TimerMilli::GetNow();
+    TimeMilli evaluateTime = now + aDelayMilli;
+    TimeMilli earlestTime  = mLastRouterAdvertisementSendTime + kMinDelayBetweenRtrAdvs;
+
+    evaluateTime = OT_MAX(evaluateTime, earlestTime);
+
+    LogInfo("Start evaluating routing policy, scheduled in %u milliseconds", evaluateTime - now);
+
+    mRoutingPolicyTimer.FireAtIfEarlier(evaluateTime);
 }
 
 // starts sending Router Solicitations in random delay
@@ -766,7 +800,7 @@
     static_assert(kMaxRtrSolicitationDelay > 0, "invalid maximum Router Solicitation delay");
     randomDelay = Random::NonCrypto::GetUint32InRange(0, Time::SecToMsec(kMaxRtrSolicitationDelay));
 
-    otLogInfoBr("Start Router Solicitation, scheduled in %u milliseconds", randomDelay);
+    LogInfo("Start Router Solicitation, scheduled in %u milliseconds", randomDelay);
     mTimeRouterSolicitStart = TimerMilli::GetNow();
     mRouterSolicitTimer.Start(randomDelay);
 
@@ -823,12 +857,12 @@
 
         if (!mIsAdvertisingLocalOnLinkPrefix)
         {
-            otLogInfoBr("Start advertising new on-link prefix %s on interface %u",
-                        aNewOnLinkPrefix->ToString().AsCString(), mInfraIfIndex);
+            LogInfo("Start advertising new on-link prefix %s on interface %u", aNewOnLinkPrefix->ToString().AsCString(),
+                    mInfraIfIndex);
         }
 
-        otLogInfoBr("Send on-link prefix %s in PIO (preferred lifetime = %u seconds, valid lifetime = %u seconds)",
-                    aNewOnLinkPrefix->ToString().AsCString(), pio.GetPreferredLifetime(), pio.GetValidLifetime());
+        LogInfo("Send on-link prefix %s in PIO (preferred lifetime = %u seconds, valid lifetime = %u seconds)",
+                aNewOnLinkPrefix->ToString().AsCString(), pio.GetPreferredLifetime(), pio.GetValidLifetime());
 
         mTimeAdvertisedOnLinkPrefix = TimerMilli::GetNow();
     }
@@ -848,8 +882,8 @@
         memcpy(buffer + bufferLength, &pio, pio.GetSize());
         bufferLength += pio.GetSize();
 
-        otLogInfoBr("Send on-link prefix %s in PIO (preferred lifetime = %u seconds, valid lifetime = %u seconds)",
-                    mLocalOnLinkPrefix.ToString().AsCString(), pio.GetPreferredLifetime(), pio.GetValidLifetime());
+        LogInfo("Send on-link prefix %s in PIO (preferred lifetime = %u seconds, valid lifetime = %u seconds)",
+                mLocalOnLinkPrefix.ToString().AsCString(), pio.GetPreferredLifetime(), pio.GetValidLifetime());
     }
 
     // Invalidate the advertised OMR prefixes if they are no longer in the new OMR prefix array.
@@ -868,8 +902,8 @@
             memcpy(buffer + bufferLength, &rio, rio.GetSize());
             bufferLength += rio.GetSize();
 
-            otLogInfoBr("Stop advertising OMR prefix %s on interface %u", advertisedOmrPrefix.ToString().AsCString(),
-                        mInfraIfIndex);
+            LogInfo("Stop advertising OMR prefix %s on interface %u", advertisedOmrPrefix.ToString().AsCString(),
+                    mInfraIfIndex);
         }
     }
 
@@ -884,8 +918,8 @@
         memcpy(buffer + bufferLength, &rio, rio.GetSize());
         bufferLength += rio.GetSize();
 
-        otLogInfoBr("Send OMR prefix %s in RIO (valid lifetime = %u seconds)", newOmrPrefix.ToString().AsCString(),
-                    kDefaultOmrPrefixLifetime);
+        LogInfo("Send OMR prefix %s in RIO (valid lifetime = %u seconds)", newOmrPrefix.ToString().AsCString(),
+                kDefaultOmrPrefixLifetime);
     }
 
     // Send the message only when there are options.
@@ -901,16 +935,22 @@
 
         if (error == kErrorNone)
         {
-            otLogInfoBr("Sent Router Advertisement on interface %u", mInfraIfIndex);
-            otDumpDebgBr("[BR-CERT] direction=send | type=RA |", buffer, bufferLength);
+            mLastRouterAdvertisementSendTime = TimerMilli::GetNow();
+            LogInfo("Sent Router Advertisement on interface %u", mInfraIfIndex);
+            DumpDebg("[BR-CERT] direction=send | type=RA |", buffer, bufferLength);
         }
         else
         {
-            otLogWarnBr("Failed to send Router Advertisement on interface %u: %s", mInfraIfIndex, ErrorToString(error));
+            LogWarn("Failed to send Router Advertisement on interface %u: %s", mInfraIfIndex, ErrorToString(error));
         }
     }
 }
 
+bool RoutingManager::IsValidBrUlaPrefix(const Ip6::Prefix &aBrUlaPrefix)
+{
+    return aBrUlaPrefix.mLength == kBrUlaPrefixLength && aBrUlaPrefix.mPrefix.mFields.m8[0] == 0xfd;
+}
+
 bool RoutingManager::IsValidOmrPrefix(const NetworkData::OnMeshPrefixConfig &aOnMeshPrefixConfig)
 {
     return IsValidOmrPrefix(aOnMeshPrefixConfig.GetPrefix()) && aOnMeshPrefixConfig.mSlaac && !aOnMeshPrefixConfig.mDp;
@@ -941,7 +981,7 @@
 
 void RoutingManager::HandleVicariousRouterSolicitTimer(void)
 {
-    otLogInfoBr("Vicarious router solicitation time out");
+    LogInfo("Vicarious router solicitation time out");
 
     for (const ExternalPrefix &prefix : mDiscoveredPrefixes)
     {
@@ -961,7 +1001,7 @@
 
 void RoutingManager::HandleRouterSolicitTimer(void)
 {
-    otLogInfoBr("Router solicitation times out");
+    LogInfo("Router solicitation times out");
 
     if (mRouterSolicitCount < kMaxRtrSolicitations)
     {
@@ -972,14 +1012,14 @@
 
         if (error == kErrorNone)
         {
-            otLogDebgBr("Successfully sent %uth Router Solicitation", mRouterSolicitCount);
+            LogDebg("Successfully sent %uth Router Solicitation", mRouterSolicitCount);
             ++mRouterSolicitCount;
             nextSolicitationDelay =
                 (mRouterSolicitCount == kMaxRtrSolicitations) ? kMaxRtrSolicitationDelay : kRtrSolicitationInterval;
         }
         else
         {
-            otLogCritBr("Failed to send %uth Router Solicitation: %s", mRouterSolicitCount, ErrorToString(error));
+            LogCrit("Failed to send %uth Router Solicitation: %s", mRouterSolicitCount, ErrorToString(error));
 
             // It's unexpected that RS will fail and we will retry sending RS messages in 60 seconds.
             // Notice that `mRouterSolicitCount` is not incremented for failed RS and thus we will
@@ -989,7 +1029,7 @@
             mRouterSolicitCount   = 0;
         }
 
-        otLogDebgBr("Router solicitation timer scheduled in %u seconds", nextSolicitationDelay);
+        LogDebg("Router solicitation timer scheduled in %u seconds", nextSolicitationDelay);
         mRouterSolicitTimer.Start(Time::SecToMsec(nextSolicitationDelay));
     }
     else
@@ -1033,7 +1073,7 @@
 
 void RoutingManager::HandleDiscoveredPrefixStaleTimer(void)
 {
-    otLogInfoBr("Stale On-Link or OMR Prefixes or RA messages are detected");
+    LogInfo("Stale On-Link or OMR Prefixes or RA messages are detected");
     StartRouterSolicitationDelay();
 }
 
@@ -1060,8 +1100,7 @@
     OT_UNUSED_VARIABLE(aBuffer);
     OT_UNUSED_VARIABLE(aBufferLength);
 
-    otLogInfoBr("Received Router Solicitation from %s on interface %u", aSrcAddress.ToString().AsCString(),
-                mInfraIfIndex);
+    LogInfo("Received Router Solicitation from %s on interface %u", aSrcAddress.ToString().AsCString(), mInfraIfIndex);
 
 #if OPENTHREAD_CONFIG_BORDER_ROUTING_VICARIOUS_RS_ENABLE
     if (!mVicariousRouterSolicitTimer.IsRunning())
@@ -1071,7 +1110,7 @@
     }
 #endif
 
-    // Schedule Router Advertisements with random delay.
+    // Schedule routing policy evaluation with random jitter to respond with Router Advertisement.
     StartRoutingPolicyEvaluationJitter(kRaReplyJitter);
 }
 
@@ -1111,9 +1150,8 @@
 
     VerifyOrExit(aBufferLength >= sizeof(RouterAdvMessage));
 
-    otLogInfoBr("Received Router Advertisement from %s on interface %u", aSrcAddress.ToString().AsCString(),
-                mInfraIfIndex);
-    otDumpDebgBr("[BR-CERT] direction=recv | type=RA |", aBuffer, aBufferLength);
+    LogInfo("Received Router Advertisement from %s on interface %u", aSrcAddress.ToString().AsCString(), mInfraIfIndex);
+    DumpDebg("[BR-CERT] direction=recv | type=RA |", aBuffer, aBufferLength);
 
     routerAdvMessage = reinterpret_cast<const RouterAdvMessage *>(aBuffer);
     optionsBegin     = aBuffer + sizeof(RouterAdvMessage);
@@ -1179,14 +1217,14 @@
 
     if (!IsValidOnLinkPrefix(aPio))
     {
-        otLogInfoBr("Ignore invalid on-link prefix in PIO: %s", prefix.ToString().AsCString());
+        LogInfo("Ignore invalid on-link prefix in PIO: %s", prefix.ToString().AsCString());
         ExitNow();
     }
 
     VerifyOrExit(!mIsAdvertisingLocalOnLinkPrefix || prefix != mLocalOnLinkPrefix);
 
-    otLogInfoBr("Discovered on-link prefix (%s, %u seconds) from interface %u", prefix.ToString().AsCString(),
-                aPio.GetValidLifetime(), mInfraIfIndex);
+    LogInfo("Discovered on-link prefix (%s, %u seconds) from interface %u", prefix.ToString().AsCString(),
+            aPio.GetValidLifetime(), mInfraIfIndex);
 
     onLinkPrefix.mIsOnLinkPrefix    = true;
     onLinkPrefix.mPrefix            = prefix;
@@ -1218,7 +1256,7 @@
         }
         else
         {
-            otLogWarnBr("Discovered too many prefixes, ignore new on-link prefix %s", prefix.ToString().AsCString());
+            LogWarn("Discovered too many prefixes, ignore new on-link prefix %s", prefix.ToString().AsCString());
             ExitNow();
         }
     }
@@ -1271,7 +1309,7 @@
 
     if (!IsValidOmrPrefix(prefix))
     {
-        otLogInfoBr("Ignore invalid OMR prefix in RIO: %s", prefix.ToString().AsCString());
+        LogInfo("Ignore invalid OMR prefix in RIO: %s", prefix.ToString().AsCString());
         ExitNow();
     }
 
@@ -1291,8 +1329,8 @@
     VerifyOrExit(!mAdvertisedOmrPrefixes.Contains(prefix));
     VerifyOrExit(!NetworkDataContainsOmrPrefix(prefix));
 
-    otLogInfoBr("Discovered OMR prefix (%s, %u seconds) from interface %u", prefix.ToString().AsCString(),
-                aRio.GetRouteLifetime(), mInfraIfIndex);
+    LogInfo("Discovered OMR prefix (%s, %u seconds) from interface %u", prefix.ToString().AsCString(),
+            aRio.GetRouteLifetime(), mInfraIfIndex);
 
     if (aRio.GetRouteLifetime() == 0)
     {
@@ -1328,7 +1366,7 @@
         }
         else
         {
-            otLogWarnBr("Discovered too many prefixes, ignore new prefix %s", prefix.ToString().AsCString());
+            LogWarn("Discovered too many prefixes, ignore new prefix %s", prefix.ToString().AsCString());
             ExitNow();
         }
     }
@@ -1493,14 +1531,14 @@
     {
         if (mDiscoveredPrefixStaleTimer.IsRunning())
         {
-            otLogDebgBr("Prefix stale timer stopped");
+            LogDebg("Prefix stale timer stopped");
         }
         mDiscoveredPrefixStaleTimer.Stop();
     }
     else
     {
         mDiscoveredPrefixStaleTimer.FireAt(nextStaleTime);
-        otLogDebgBr("Prefix stale timer scheduled in %lu ms", nextStaleTime - now);
+        LogDebg("Prefix stale timer scheduled in %lu ms", nextStaleTime - now);
     }
 }
 
diff --git a/src/core/border_router/routing_manager.hpp b/src/core/border_router/routing_manager.hpp
index 01325f8..0023e9e 100644
--- a/src/core/border_router/routing_manager.hpp
+++ b/src/core/border_router/routing_manager.hpp
@@ -185,6 +185,25 @@
      */
     Error HandleInfraIfStateChanged(uint32_t aInfraIfIndex, bool aIsRunning);
 
+    /**
+     * This method checks if the on-mesh prefix configuration is a valid OMR prefix.
+     *
+     * @param[in] aOnMeshPrefixConfig  The on-mesh prefix configuration to check.
+     *
+     * @returns  Whether the on-mesh prefix configuration is a valid OMR prefix.
+     *
+     */
+    static bool IsValidOmrPrefix(const NetworkData::OnMeshPrefixConfig &aOnMeshPrefixConfig);
+
+    /**
+     * This method checks if the OMR prefix is valid (i.e. GUA/ULA prefix with length being 64).
+     *
+     * @param[in]  aOmrPrefix  The OMR prefix to check.
+     * @returns    Whether the OMR prefix is valid.
+     *
+     */
+    static bool IsValidOmrPrefix(const Ip6::Prefix &aOmrPrefix);
+
 private:
     typedef NetworkData::RoutePreference RoutePreference;
 
@@ -194,10 +213,15 @@
     static constexpr uint8_t kMaxOmrPrefixNum = OPENTHREAD_CONFIG_IP6_SLAAC_NUM_ADDRESSES;
 
     // The maximum number of prefixes to discover on the infra link.
-    static constexpr uint8_t kMaxDiscoveredPrefixNum = 8;
+    static constexpr uint8_t kMaxDiscoveredPrefixNum = OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_DISCOVERED_PREFIXES;
 
     static constexpr uint8_t kOmrPrefixLength    = OT_IP6_PREFIX_BITSIZE; // The length of an OMR prefix. In bits.
     static constexpr uint8_t kOnLinkPrefixLength = OT_IP6_PREFIX_BITSIZE; // The length of an On-link prefix. In bits.
+    static constexpr uint8_t kBrUlaPrefixLength  = 48;                    // The length of a BR ULA prefix. In bits.
+    static constexpr uint8_t kNat64PrefixLength  = 96;                    // The length of a NAT64 prefix. In bits.
+
+    static constexpr uint16_t kOmrPrefixSubnetId   = 1; // The subnet ID of an OMR prefix within a BR ULA prefix.
+    static constexpr uint16_t kNat64PrefixSubnetId = 2; // The subnet ID of a NAT64 prefix within a BR ULA prefix.
 
     // The maximum number of initial Router Advertisements.
     static constexpr uint32_t kMaxInitRtrAdvertisements = 3;
@@ -214,7 +238,9 @@
     static constexpr uint32_t kRtrSolicitationInterval     = 4;      // Interval between RSs. In sec.
     static constexpr uint32_t kMaxRtrSolicitationDelay     = 1;      // Max delay for initial solicitation. In sec.
     static constexpr uint32_t kRoutingPolicyEvaluationJitter = 1000; // Jitter for routing policy evaluation. In msec.
-    static constexpr uint32_t kRtrSolicitationRetryDelay     = 60;   // The delay before retrying failed RS tx. In Sec.
+    static constexpr uint32_t kRtrSolicitationRetryDelay =
+        kRtrSolicitationInterval;                             // The delay before retrying failed RS tx. In Sec.
+    static constexpr uint32_t kMinDelayBetweenRtrAdvs = 3000; // Min delay (msec) between consecutive RAs.
 
     // The STALE_RA_TIME in seconds. The Routing Manager will consider the prefixes
     // and learned RA parameters STALE when they are not refreshed in STALE_RA_TIME
@@ -289,14 +315,15 @@
     void  HandleNotifierEvents(Events aEvents);
     bool  IsInitialized(void) const { return mInfraIfIndex != 0; }
     bool  IsEnabled(void) const { return mIsEnabled; }
-    Error LoadOrGenerateRandomOmrPrefix(void);
+    Error LoadOrGenerateRandomBrUlaPrefix(void);
+    void  GenerateOmrPrefix(void);
     Error LoadOrGenerateRandomOnLinkPrefix(void);
 
     const Ip6::Prefix *EvaluateOnLinkPrefix(void);
 
 #if OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE
-    Error LoadOrGenerateRandomNat64Prefix(void);
-    void  EvaluateNat64Prefix(void);
+    void GenerateNat64Prefix(void);
+    void EvaluateNat64Prefix(void);
 #endif
 
     void  EvaluateRoutingPolicy(void);
@@ -305,6 +332,7 @@
     void  EvaluateOmrPrefix(OmrPrefixArray &aNewOmrPrefixes);
     Error PublishLocalOmrPrefix(void);
     void  UnpublishLocalOmrPrefix(void);
+    bool  IsOmrPrefixAddedToLocalNetworkData(void) const;
     Error AddExternalRoute(const Ip6::Prefix &aPrefix, RoutePreference aRoutePreference, bool aNat64 = false);
     void  RemoveExternalRoute(const Ip6::Prefix &aPrefix);
     void  StartRouterSolicitationDelay(void);
@@ -337,8 +365,7 @@
     bool UpdateRouterAdvMessage(const RouterAdv::RouterAdvMessage *aRouterAdvMessage);
     void ResetDiscoveredPrefixStaleTimer(void);
 
-    static bool IsValidOmrPrefix(const NetworkData::OnMeshPrefixConfig &aOnMeshPrefixConfig);
-    static bool IsValidOmrPrefix(const Ip6::Prefix &aOmrPrefix);
+    static bool IsValidBrUlaPrefix(const Ip6::Prefix &aBrUlaPrefix);
     static bool IsValidOnLinkPrefix(const RouterAdv::PrefixInfoOption &aPio);
     static bool IsValidOnLinkPrefix(const Ip6::Prefix &aOnLinkPrefix);
 
@@ -357,8 +384,11 @@
     // messages will be sent.
     uint32_t mInfraIfIndex;
 
-    // The OMR prefix loaded from local persistent storage or randomly
-    // generated if non is found in persistent storage.
+    // The /48 BR ULA prefix loaded from local persistent storage or
+    // randomly generated if none is found in persistent storage.
+    Ip6::Prefix mBrUlaPrefix;
+
+    // The OMR prefix allocated from the /48 BR ULA prefix.
     Ip6::Prefix mLocalOmrPrefix;
 
     // The advertised OMR prefixes. For a stable Thread network without
@@ -380,8 +410,7 @@
     TimeMilli  mTimeAdvertisedOnLinkPrefix;
     TimerMilli mOnLinkPrefixDeprecateTimer;
 
-    // The NAT64 prefix loaded from local persistent storage or
-    // randomly generated if none is found in persistent storage.
+    // The NAT64 prefix allocated from the /48 BR ULA prefix.
     Ip6::Prefix mLocalNat64Prefix;
 
     // True if the local NAT64 prefix is advertised in Thread network.
@@ -403,7 +432,8 @@
     TimerMilli mDiscoveredPrefixInvalidTimer;
     TimerMilli mDiscoveredPrefixStaleTimer;
 
-    uint32_t mRouterAdvertisementCount;
+    uint32_t  mRouterAdvertisementCount;
+    TimeMilli mLastRouterAdvertisementSendTime;
 
 #if OPENTHREAD_CONFIG_BORDER_ROUTING_VICARIOUS_RS_ENABLE
     TimerMilli mVicariousRouterSolicitTimer;
diff --git a/src/core/coap/coap.cpp b/src/core/coap/coap.cpp
index 37ceff9..f0b966d 100644
--- a/src/core/coap/coap.cpp
+++ b/src/core/coap/coap.cpp
@@ -28,12 +28,13 @@
 
 #include "coap.hpp"
 
+#include "common/array.hpp"
 #include "common/as_core_type.hpp"
 #include "common/code_utils.hpp"
 #include "common/debug.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "common/random.hpp"
 #include "net/ip6.hpp"
 #include "net/udp6.hpp"
@@ -47,6 +48,8 @@
 namespace ot {
 namespace Coap {
 
+RegisterLogModule("Coap");
+
 CoapBase::CoapBase(Instance &aInstance, Sender aSender)
     : InstanceLocator(aInstance)
     , mMessageId(Random::NonCrypto::GetUint16())
@@ -76,18 +79,15 @@
 
 void CoapBase::ClearRequests(const Ip6::Address *aAddress)
 {
-    Message *nextMessage;
-
-    for (Message *message = mPendingRequests.GetHead(); message != nullptr; message = nextMessage)
+    for (Message &message : mPendingRequests)
     {
         Metadata metadata;
 
-        nextMessage = message->GetNextCoapMessage();
-        metadata.ReadFrom(*message);
+        metadata.ReadFrom(message);
 
         if ((aAddress == nullptr) || (metadata.mSourceAddress == *aAddress))
         {
-            FinalizeCoapTransaction(*message, metadata, nullptr, nullptr, kErrorAbort);
+            FinalizeCoapTransaction(message, metadata, nullptr, nullptr, kErrorAbort);
         }
     }
 }
@@ -139,6 +139,64 @@
     return message;
 }
 
+Message *CoapBase::NewPriorityConfirmablePostMessage(const char *aUriPath)
+{
+    return InitMessage(NewPriorityMessage(), kTypeConfirmable, aUriPath);
+}
+
+Message *CoapBase::NewConfirmablePostMessage(const char *aUriPath)
+{
+    return InitMessage(NewMessage(), kTypeConfirmable, aUriPath);
+}
+
+Message *CoapBase::NewPriorityNonConfirmablePostMessage(const char *aUriPath)
+{
+    return InitMessage(NewPriorityMessage(), kTypeNonConfirmable, aUriPath);
+}
+
+Message *CoapBase::NewNonConfirmablePostMessage(const char *aUriPath)
+{
+    return InitMessage(NewMessage(), kTypeNonConfirmable, aUriPath);
+}
+
+Message *CoapBase::NewPriorityResponseMessage(const Message &aRequest)
+{
+    return InitResponse(NewPriorityMessage(), aRequest);
+}
+
+Message *CoapBase::NewResponseMessage(const Message &aRequest)
+{
+    return InitResponse(NewMessage(), aRequest);
+}
+
+Message *CoapBase::InitMessage(Message *aMessage, Type aType, const char *aUriPath)
+{
+    Error error = kErrorNone;
+
+    VerifyOrExit(aMessage != nullptr);
+
+    SuccessOrExit(error = aMessage->Init(aType, kCodePost, aUriPath));
+    SuccessOrExit(error = aMessage->SetPayloadMarker());
+
+exit:
+    FreeAndNullMessageOnError(aMessage, error);
+    return aMessage;
+}
+
+Message *CoapBase::InitResponse(Message *aMessage, const Message &aResponse)
+{
+    Error error = kErrorNone;
+
+    VerifyOrExit(aMessage != nullptr);
+
+    SuccessOrExit(error = aMessage->SetDefaultResponseHeader(aResponse));
+    SuccessOrExit(error = aMessage->SetPayloadMarker());
+
+exit:
+    FreeAndNullMessageOnError(aMessage, error);
+    return aMessage;
+}
+
 Error CoapBase::Send(ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
 {
     Error error;
@@ -418,19 +476,16 @@
     TimeMilli        now      = TimerMilli::GetNow();
     TimeMilli        nextTime = now.GetDistantFuture();
     Metadata         metadata;
-    Message *        nextMessage;
     Ip6::MessageInfo messageInfo;
 
-    for (Message *message = mPendingRequests.GetHead(); message != nullptr; message = nextMessage)
+    for (Message &message : mPendingRequests)
     {
-        nextMessage = message->GetNextCoapMessage();
-
-        metadata.ReadFrom(*message);
+        metadata.ReadFrom(message);
 
         if (now >= metadata.mNextTimerShot)
         {
 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
-            if (message->IsRequest() && metadata.mObserve && metadata.mAcknowledged)
+            if (message.IsRequest() && metadata.mObserve && metadata.mAcknowledged)
             {
                 // This is a RFC7641 subscription.  Do not time out.
                 continue;
@@ -440,7 +495,7 @@
             if (!metadata.mConfirmable || (metadata.mRetransmissionsRemaining == 0))
             {
                 // No expected response or acknowledgment.
-                FinalizeCoapTransaction(*message, metadata, nullptr, nullptr, kErrorResponseTimeout);
+                FinalizeCoapTransaction(message, metadata, nullptr, nullptr, kErrorResponseTimeout);
                 continue;
             }
 
@@ -448,7 +503,7 @@
             metadata.mRetransmissionsRemaining--;
             metadata.mRetransmissionTimeout *= 2;
             metadata.mNextTimerShot = now + metadata.mRetransmissionTimeout;
-            metadata.UpdateIn(*message);
+            metadata.UpdateIn(message);
 
             // Retransmit
             if (!metadata.mAcknowledged)
@@ -462,7 +517,7 @@
 #endif
                 messageInfo.SetMulticastLoop(metadata.mMulticastLoop);
 
-                SendCopy(*message, messageInfo);
+                SendCopy(message, messageInfo);
             }
         }
 
@@ -495,17 +550,15 @@
 Error CoapBase::AbortTransaction(ResponseHandler aHandler, void *aContext)
 {
     Error    error = kErrorNotFound;
-    Message *nextMessage;
     Metadata metadata;
 
-    for (Message *message = mPendingRequests.GetHead(); message != nullptr; message = nextMessage)
+    for (Message &message : mPendingRequests)
     {
-        nextMessage = message->GetNextCoapMessage();
-        metadata.ReadFrom(*message);
+        metadata.ReadFrom(message);
 
         if (metadata.mResponseHandler == aHandler && metadata.mResponseContext == aContext)
         {
-            FinalizeCoapTransaction(*message, metadata, nullptr, nullptr, kErrorAbort);
+            FinalizeCoapTransaction(message, metadata, nullptr, nullptr, kErrorAbort);
             error = kErrorNone;
         }
     }
@@ -676,8 +729,8 @@
 
     DequeueMessage(aRequest);
 
-    otLogInfoCoap("Send Block1 Nr. %d, Size: %d bytes, More Blocks Flag: %d", request->GetBlockWiseBlockNumber(),
-                  otCoapBlockSizeFromExponent(request->GetBlockWiseBlockSize()), request->IsMoreBlocksFlagSet());
+    LogInfo("Send Block1 Nr. %d, Size: %d bytes, More Blocks Flag: %d", request->GetBlockWiseBlockNumber(),
+            otCoapBlockSizeFromExponent(request->GetBlockWiseBlockSize()), request->IsMoreBlocksFlagSet());
 
     SuccessOrExit(error = SendMessage(*request, aMessageInfo, TxParameters::GetDefault(),
                                       aCoapMetadata.mResponseHandler, aCoapMetadata.mResponseContext,
@@ -718,8 +771,8 @@
                                                     bufLen, aMessage.IsMoreBlocksFlagSet(), aTotalLength));
 
     // CoAP Block-Wise Transfer continues
-    otLogInfoCoap("Received Block2 Nr. %d , Size: %d bytes, More Blocks Flag: %d", aMessage.GetBlockWiseBlockNumber(),
-                  otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize()), aMessage.IsMoreBlocksFlagSet());
+    LogInfo("Received Block2 Nr. %d , Size: %d bytes, More Blocks Flag: %d", aMessage.GetBlockWiseBlockNumber(),
+            otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize()), aMessage.IsMoreBlocksFlagSet());
 
     // Conclude block-wise transfer if last block has been received
     if (!aMessage.IsMoreBlocksFlagSet())
@@ -738,8 +791,8 @@
         DequeueMessage(aRequest);
     }
 
-    otLogInfoCoap("Request Block2 Nr. %d, Size: %d bytes", request->GetBlockWiseBlockNumber(),
-                  otCoapBlockSizeFromExponent(request->GetBlockWiseBlockSize()));
+    LogInfo("Request Block2 Nr. %d, Size: %d bytes", request->GetBlockWiseBlockNumber(),
+            otCoapBlockSizeFromExponent(request->GetBlockWiseBlockSize()));
 
     SuccessOrExit(error =
                       SendMessage(*request, aMessageInfo, TxParameters::GetDefault(), aCoapMetadata.mResponseHandler,
@@ -789,8 +842,8 @@
 
         SuccessOrExit(error = CacheLastBlockResponse(response));
 
-        otLogInfoCoap("Acknowledge Block1 Nr. %d, Size: %d bytes", response->GetBlockWiseBlockNumber(),
-                      otCoapBlockSizeFromExponent(response->GetBlockWiseBlockSize()));
+        LogInfo("Acknowledge Block1 Nr. %d, Size: %d bytes", response->GetBlockWiseBlockNumber(),
+                otCoapBlockSizeFromExponent(response->GetBlockWiseBlockSize()));
 
         SuccessOrExit(error = SendMessage(*response, aMessageInfo));
 
@@ -826,8 +879,8 @@
 
     SuccessOrExit(error = aMessage.ReadBlockOptionValues(kOptionBlock2));
 
-    otLogInfoCoap("Request for Block2 Nr. %d, Size: %d bytes received", aMessage.GetBlockWiseBlockNumber(),
-                  otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize()));
+    LogInfo("Request for Block2 Nr. %d, Size: %d bytes received", aMessage.GetBlockWiseBlockNumber(),
+            otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize()));
 
     if (aMessage.GetBlockWiseBlockNumber() == 0)
     {
@@ -926,8 +979,8 @@
         FreeLastBlockResponse();
     }
 
-    otLogInfoCoap("Send Block2 Nr. %d, Size: %d bytes, More Blocks Flag %d", response->GetBlockWiseBlockNumber(),
-                  otCoapBlockSizeFromExponent(response->GetBlockWiseBlockSize()), response->IsMoreBlocksFlagSet());
+    LogInfo("Send Block2 Nr. %d, Size: %d bytes, More Blocks Flag %d", response->GetBlockWiseBlockNumber(),
+            otCoapBlockSizeFromExponent(response->GetBlockWiseBlockSize()), response->IsMoreBlocksFlagSet());
 
     SuccessOrExit(error = SendMessage(*response, aMessageInfo));
 
@@ -953,7 +1006,7 @@
 
     if (error != kErrorNone)
     {
-        otLogWarnCoap("Failed to send copy: %s", ErrorToString(error));
+        LogWarn("Failed to send copy: %s", ErrorToString(error));
         FreeMessage(messageCopy);
     }
 }
@@ -962,11 +1015,11 @@
                                       const Ip6::MessageInfo &aMessageInfo,
                                       Metadata &              aMetadata)
 {
-    Message *message;
+    Message *request = nullptr;
 
-    for (message = mPendingRequests.GetHead(); message != nullptr; message = message->GetNextCoapMessage())
+    for (Message &message : mPendingRequests)
     {
-        aMetadata.ReadFrom(*message);
+        aMetadata.ReadFrom(message);
 
         if (((aMetadata.mDestinationAddress == aMessageInfo.GetPeerAddr()) ||
              aMetadata.mDestinationAddress.IsMulticast() ||
@@ -977,8 +1030,9 @@
             {
             case kTypeReset:
             case kTypeAck:
-                if (aResponse.GetMessageId() == message->GetMessageId())
+                if (aResponse.GetMessageId() == message.GetMessageId())
                 {
+                    request = &message;
                     ExitNow();
                 }
 
@@ -986,8 +1040,9 @@
 
             case kTypeConfirmable:
             case kTypeNonConfirmable:
-                if (aResponse.IsTokenEqual(*message))
+                if (aResponse.IsTokenEqual(message))
                 {
+                    request = &message;
                     ExitNow();
                 }
 
@@ -997,7 +1052,7 @@
     }
 
 exit:
-    return message;
+    return request;
 }
 
 void CoapBase::Receive(ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
@@ -1006,7 +1061,7 @@
 
     if (message.ParseHeader() != kErrorNone)
     {
-        otLogDebgCoap("Failed to parse CoAP header");
+        LogDebg("Failed to parse CoAP header");
 
         if (!aMessageInfo.GetSockAddr().IsMulticast() && message.IsConfirmable())
         {
@@ -1289,7 +1344,7 @@
                 *curUriPath++ = '/';
             }
 
-            VerifyOrExit(curUriPath + iterator.GetOption()->GetLength() < OT_ARRAY_END(uriPath), error = kErrorParse);
+            VerifyOrExit(curUriPath + iterator.GetOption()->GetLength() < GetArrayEnd(uriPath), error = kErrorParse);
 
             IgnoreError(iterator.ReadOptionValue(curUriPath));
             curUriPath += iterator.GetOption()->GetLength();
@@ -1398,7 +1453,7 @@
 
     if (error != kErrorNone)
     {
-        otLogInfoCoap("Failed to process request: %s", ErrorToString(error));
+        LogInfo("Failed to process request: %s", ErrorToString(error));
 
         if (error == kErrorNotFound && !aMessageInfo.GetSockAddr().IsMulticast())
         {
@@ -1446,25 +1501,26 @@
 
 const Message *ResponsesQueue::FindMatchedResponse(const Message &aRequest, const Ip6::MessageInfo &aMessageInfo) const
 {
-    Message *message;
+    const Message *response = nullptr;
 
-    for (message = mQueue.GetHead(); message != nullptr; message = message->GetNextCoapMessage())
+    for (const Message &message : mQueue)
     {
-        if (message->GetMessageId() == aRequest.GetMessageId())
+        if (message.GetMessageId() == aRequest.GetMessageId())
         {
             ResponseMetadata metadata;
 
-            metadata.ReadFrom(*message);
+            metadata.ReadFrom(message);
 
             if ((metadata.mMessageInfo.GetPeerPort() == aMessageInfo.GetPeerPort()) &&
                 (metadata.mMessageInfo.GetPeerAddr() == aMessageInfo.GetPeerAddr()))
             {
+                response = &message;
                 break;
             }
         }
     }
 
-    return message;
+    return response;
 }
 
 void ResponsesQueue::EnqueueResponse(Message &               aMessage,
@@ -1503,15 +1559,15 @@
     // `kMaxCachedResponses` remove the one with earliest dequeue
     // time.
 
-    for (Message *message = mQueue.GetHead(); message != nullptr; message = message->GetNextCoapMessage())
+    for (Message &message : mQueue)
     {
         ResponseMetadata metadata;
 
-        metadata.ReadFrom(*message);
+        metadata.ReadFrom(message);
 
         if ((earliestMsg == nullptr) || (metadata.mDequeueTime < earliestDequeueTime))
         {
-            earliestMsg         = message;
+            earliestMsg         = &message;
             earliestDequeueTime = metadata.mDequeueTime;
         }
 
@@ -1543,19 +1599,16 @@
 {
     TimeMilli now             = TimerMilli::GetNow();
     TimeMilli nextDequeueTime = now.GetDistantFuture();
-    Message * nextMessage;
 
-    for (Message *message = mQueue.GetHead(); message != nullptr; message = nextMessage)
+    for (Message &message : mQueue)
     {
         ResponseMetadata metadata;
 
-        nextMessage = message->GetNextCoapMessage();
-
-        metadata.ReadFrom(*message);
+        metadata.ReadFrom(message);
 
         if (now >= metadata.mDequeueTime)
         {
-            DequeueResponse(*message);
+            DequeueResponse(message);
             continue;
         }
 
diff --git a/src/core/coap/coap.hpp b/src/core/coap/coap.hpp
index d2bd0a8..aea240e 100644
--- a/src/core/coap/coap.hpp
+++ b/src/core/coap/coap.hpp
@@ -435,7 +435,7 @@
     void SetDefaultHandler(RequestHandler aHandler, void *aContext);
 
     /**
-     * This method creates a new message with a CoAP header.
+     * This method allocates a new message with a CoAP header.
      *
      * @param[in]  aSettings  The message settings.
      *
@@ -445,7 +445,7 @@
     Message *NewMessage(const Message::Settings &aSettings = Message::Settings::GetDefault());
 
     /**
-     * This method creates a new message with a CoAP header that has Network Control priority level.
+     * This method allocates a new message with a CoAP header that has Network Control priority level.
      *
      * @returns A pointer to the message or `nullptr` if failed to allocate message.
      *
@@ -455,6 +455,95 @@
         return NewMessage(Message::Settings(Message::kWithLinkSecurity, Message::kPriorityNet));
     }
 
+    /**
+     * This method allocates and initializes a new CoAP Confirmable Post message with Network Control priority level.
+     *
+     * The CoAP header is initialized as `kTypeConfirmable` and `kCodePost` with a given URI path and a randomly
+     * generated token (of default length). This method also sets the payload marker (`SetPayloadMarker()` on message.
+     * Even if message has no payload, calling `SetPayloadMarker()` is harmless, since `SendMessage()` will check and
+     * remove the payload marker when there is no payload.
+     *
+     * @param[in] aUriPath   The URI path string.
+     *
+     * @returns A pointer to the message or `nullptr` if failed to allocate message.
+     *
+     */
+    Message *NewPriorityConfirmablePostMessage(const char *aUriPath);
+
+    /**
+     * This method allocates and initializes a new CoAP Confirmable Post message with normal priority level.
+     *
+     * The CoAP header is initialized as `kTypeConfirmable` and `kCodePost` with a given URI path and a randomly
+     * generated token (of default length). This method also sets the payload marker (calling `SetPayloadMarker()`).
+     * Even if message has no payload, calling `SetPayloadMarker()` is harmless, since `SendMessage()` will check and
+     * remove the payload marker when there is no payload.
+     *
+     * @param[in] aUriPath   The URI path string.
+     *
+     * @returns A pointer to the message or `nullptr` if failed to allocate message.
+     *
+     */
+    Message *NewConfirmablePostMessage(const char *aUriPath);
+
+    /**
+     * This method allocates and initializes a new CoAP Non-confirmable Post message with Network Control priority
+     * level.
+     *
+     * The CoAP header is initialized as `kTypeNonConfirmable` and `kCodePost` with a given URI path and a randomly
+     * generated token (of default length). This method also sets the payload marker (calling `SetPayloadMarker()`).
+     * Even if message has no payload, calling `SetPayloadMarker()` is harmless, since `SendMessage()` will check and
+     * remove the payload marker when there is no payload.
+     *
+     * @param[in] aUriPath   The URI path string.
+     *
+     * @returns A pointer to the message or `nullptr` if failed to allocate message.
+     *
+     */
+    Message *NewPriorityNonConfirmablePostMessage(const char *aUriPath);
+
+    /**
+     * This method allocates and initializes a new CoAP Non-confirmable Post message with normal priority level.
+     *
+     * The CoAP header is initialized as `kTypeNonConfirmable` and `kCodePost` with a given URI path and a randomly
+     * generated token (of default length). This method also sets the payload marker (calling `SetPayloadMarker()`).
+     * Even if message has no payload, calling `SetPayloadMarker()` is harmless, since `SendMessage()` will check and
+     * remove the payload marker when there is no payload.
+     *
+     * @param[in] aUriPath   The URI path string.
+     *
+     * @returns A pointer to the message or `nullptr` if failed to allocate message.
+     *
+     */
+    Message *NewNonConfirmablePostMessage(const char *aUriPath);
+
+    /**
+     * This method allocates and initializes a new CoAP response message with Network Control priority level for a
+     * given request message.
+     *
+     * The CoAP header is initialized as `kTypeAck` with `kCodeChanged`. The token and message ID is copied from
+     * @p aRequest. This method also sets the payload marker (calling `SetPayloadMarker()`). Even if message has
+     * no payload, calling `SetPayloadMarker()` is harmless, since `SendMessage()` will check and remove the payload
+     * marker when there is no payload.
+     *
+     * @returns A pointer to the message or `nullptr` if failed to allocate message.
+     *
+     */
+    Message *NewPriorityResponseMessage(const Message &aRequest);
+
+    /**
+     * This method allocates and initializes a new CoAP response message with regular priority level for a given
+     * request message.
+     *
+     * The CoAP header is initialized as `kTypeAck` with `kCodeChanged`. The token and message ID is copied from
+     * @p aRequest. This method also sets the payload marker (calling `SetPayloadMarker()`). Even if message has
+     * no payload, calling `SetPayloadMarker()` is harmless, since `SendMessage()` will check and remove the payload
+     * marker when there is no payload.
+     *
+     * @returns A pointer to the message or `nullptr` if failed to allocate message.
+     *
+     */
+    Message *NewResponseMessage(const Message &aRequest);
+
 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
     /**
      * This method sends a CoAP message block-wise with custom transmission parameters.
@@ -718,6 +807,9 @@
 #endif
     };
 
+    Message *InitMessage(Message *aMessage, Type aType, const char *aUriPath);
+    Message *InitResponse(Message *aMessage, const Message &aResponse);
+
     static void HandleRetransmissionTimer(Timer &aTimer);
     void        HandleRetransmissionTimer(void);
 
diff --git a/src/core/coap/coap_message.cpp b/src/core/coap/coap_message.cpp
index 5d02662..bd1c152 100644
--- a/src/core/coap/coap_message.cpp
+++ b/src/core/coap/coap_message.cpp
@@ -34,6 +34,7 @@
 #include "coap_message.hpp"
 
 #include "coap/coap.hpp"
+#include "common/array.hpp"
 #include "common/code_utils.hpp"
 #include "common/debug.hpp"
 #include "common/encoding.hpp"
@@ -78,26 +79,6 @@
     return error;
 }
 
-void Message::InitAsConfirmablePost(void)
-{
-    Init(kTypeConfirmable, kCodePost);
-}
-
-void Message::InitAsNonConfirmablePost(void)
-{
-    Init(kTypeNonConfirmable, kCodePost);
-}
-
-Error Message::InitAsConfirmablePost(const char *aUriPath)
-{
-    return Init(kTypeConfirmable, kCodePost, aUriPath);
-}
-
-Error Message::InitAsNonConfirmablePost(const char *aUriPath)
-{
-    return Init(kTypeNonConfirmable, kCodePost, aUriPath);
-}
-
 Error Message::InitAsPost(const Ip6::Address &aDestination, const char *aUriPath)
 {
     return Init(aDestination.IsMulticast() ? kTypeNonConfirmable : kTypeConfirmable, kCodePost, aUriPath);
@@ -259,7 +240,7 @@
             *curUriPath++ = '/';
         }
 
-        VerifyOrExit(curUriPath + optionLength < OT_ARRAY_END(aUriPath), error = kErrorParse);
+        VerifyOrExit(curUriPath + optionLength < GetArrayEnd(aUriPath), error = kErrorParse);
 
         IgnoreError(iterator.ReadOptionValue(curUriPath));
         curUriPath += optionLength;
@@ -475,6 +456,16 @@
 }
 #endif // OPENTHREAD_CONFIG_COAP_API_ENABLE
 
+Message::Iterator MessageQueue::begin(void)
+{
+    return Message::Iterator(GetHead());
+}
+
+Message::ConstIterator MessageQueue::begin(void) const
+{
+    return Message::ConstIterator(GetHead());
+}
+
 Error Option::Iterator::Init(const Message &aMessage)
 {
     Error    error  = kErrorParse;
diff --git a/src/core/coap/coap_message.hpp b/src/core/coap/coap_message.hpp
index cf56cb0..1f4a86b 100644
--- a/src/core/coap/coap_message.hpp
+++ b/src/core/coap/coap_message.hpp
@@ -166,6 +166,7 @@
 class Message : public ot::Message
 {
     friend class Option;
+    friend class MessageQueue;
 
 public:
     static constexpr uint8_t kDefaultTokenLength = OT_COAP_DEFAULT_TOKEN_LENGTH; ///< Default token length.
@@ -203,18 +204,6 @@
     void Init(Type aType, Code aCode);
 
     /**
-     * This method initializes the CoAP header as `kTypeConfirmable` and `kCodePost`.
-     *
-     */
-    void InitAsConfirmablePost(void);
-
-    /**
-     * This method initializes the CoAP header as `kTypeNonConfirmable` and `kCodePost`.
-     *
-     */
-    void InitAsNonConfirmablePost(void);
-
-    /**
      * This method initializes the CoAP header with specific Type and Code.
      *
      * @param[in]  aType              The Type value.
@@ -228,28 +217,6 @@
     Error Init(Type aType, Code aCode, const char *aUriPath);
 
     /**
-     * This method initializes the CoAP header as `kTypeConfirmable` and `kCodePost` with a given URI Path.
-     *
-     * @param[in]  aUriPath           A pointer to a null-terminated string.
-     *
-     * @retval kErrorNone         Successfully appended the option.
-     * @retval kErrorNoBufs       The option length exceeds the buffer size.
-     *
-     */
-    Error InitAsConfirmablePost(const char *aUriPath);
-
-    /**
-     * This method initializes the CoAP header as `kTypeNonConfirmable` and `kCodePost` with a given URI Path.
-     *
-     * @param[in]  aUriPath           A pointer to a null-terminated string.
-     *
-     * @retval kErrorNone         Successfully appended the option.
-     * @retval kErrorNoBufs       The option length exceeds the buffer size.
-     *
-     */
-    Error InitAsNonConfirmablePost(const char *aUriPath);
-
-    /**
      * This method initializes the CoAP header as `kCodePost` with a given URI Path with its type determined from a
      * given destination IPv6 address.
      *
@@ -956,6 +923,27 @@
 #endif
     };
 
+    class ConstIterator : public ot::Message::ConstIterator
+    {
+    public:
+        using ot::Message::ConstIterator::ConstIterator;
+
+        const Message &operator*(void) { return static_cast<const Message &>(ot::Message::ConstIterator::operator*()); }
+        const Message *operator->(void)
+        {
+            return static_cast<const Message *>(ot::Message::ConstIterator::operator->());
+        }
+    };
+
+    class Iterator : public ot::Message::Iterator
+    {
+    public:
+        using ot::Message::Iterator::Iterator;
+
+        Message &operator*(void) { return static_cast<Message &>(ot::Message::Iterator::operator*()); }
+        Message *operator->(void) { return static_cast<Message *>(ot::Message::Iterator::operator->()); }
+    };
+
     static_assert(sizeof(HelpData) <= sizeof(Ip6::Header) + sizeof(Ip6::HopByHopHeader) + sizeof(Ip6::OptionMpl) +
                                           sizeof(Ip6::Udp::Header),
                   "HelpData size exceeds the size of the reserved region in the message");
@@ -1000,7 +988,15 @@
      * @returns A pointer to the first message.
      *
      */
-    Message *GetHead(void) const { return static_cast<Message *>(ot::MessageQueue::GetHead()); }
+    Message *GetHead(void) { return static_cast<Message *>(ot::MessageQueue::GetHead()); }
+
+    /**
+     * This method returns a pointer to the first message.
+     *
+     * @returns A pointer to the first message.
+     *
+     */
+    const Message *GetHead(void) const { return static_cast<const Message *>(ot::MessageQueue::GetHead()); }
 
     /**
      * This method adds a message to the end of the queue.
@@ -1034,6 +1030,17 @@
      *
      */
     void DequeueAndFree(Message &aMessage) { ot::MessageQueue::DequeueAndFree(aMessage); }
+
+    // The following methods are intended to support range-based `for`
+    // loop iteration over the queue entries and should not be used
+    // directly. The range-based `for` works correctly even if the
+    // current entry is removed from the queue during iteration.
+
+    Message::Iterator begin(void);
+    Message::Iterator end(void) { return Message::Iterator(); }
+
+    Message::ConstIterator begin(void) const;
+    Message::ConstIterator end(void) const { return Message::ConstIterator(); }
 };
 
 /**
diff --git a/src/core/coap/coap_secure.cpp b/src/core/coap/coap_secure.cpp
index b4b5197..d158731 100644
--- a/src/core/coap/coap_secure.cpp
+++ b/src/core/coap/coap_secure.cpp
@@ -32,7 +32,7 @@
 
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "common/new.hpp"
 #include "meshcop/dtls.hpp"
 #include "thread/thread_netif.hpp"
@@ -45,6 +45,8 @@
 namespace ot {
 namespace Coap {
 
+RegisterLogModule("CoapSecure");
+
 CoapSecure::CoapSecure(Instance &aInstance, bool aLayerTwoSecurity)
     : CoapBase(aInstance, &CoapSecure::Send)
     , mDtls(aInstance, aLayerTwoSecurity)
@@ -221,12 +223,12 @@
 exit:
     if (error != kErrorNone)
     {
-        otLogNoteMeshCoP("CoapSecure Transmit: %s", ErrorToString(error));
+        LogNote("Transmit: %s", ErrorToString(error));
         message->Free();
     }
     else
     {
-        otLogDebgMeshCoP("CoapSecure Transmit: %s", ErrorToString(error));
+        LogDebg("Transmit: %s", ErrorToString(error));
     }
 }
 
diff --git a/src/core/coap/coap_secure.hpp b/src/core/coap/coap_secure.hpp
index cd01ada..be8caf6 100644
--- a/src/core/coap/coap_secure.hpp
+++ b/src/core/coap/coap_secure.hpp
@@ -209,8 +209,8 @@
      *
      * DTLS mode "ECDHE ECDSA with AES 128 CCM 8" for Application CoAPS.
      *
-     * @param[in]  aX509Certificate   A pointer to the PEM formatted X509 PEM certificate.
-     * @param[in]  aX509CertLength    The length of certificate.
+     * @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.
      *
diff --git a/src/core/common/array.hpp b/src/core/common/array.hpp
index 9da3e77..ded006f 100644
--- a/src/core/common/array.hpp
+++ b/src/core/common/array.hpp
@@ -45,6 +45,61 @@
 namespace ot {
 
 /**
+ * This function returns the length of a given array (number of elements in the array).
+ *
+ * This template function is `constexpr`. The template arguments are expected to be deduced by the compiler allowing
+ * callers to simply use `GetArrayLength(aArray)`.
+ *
+ * @tparam  Type          The array element type.
+ * @tparam  kArrayLength  The array length.
+ *
+ * @returns The array length (number of elements in the array).
+ *
+ */
+template <typename Type, uint16_t kArrayLength> constexpr inline uint16_t GetArrayLength(const Type (&)[kArrayLength])
+{
+    return kArrayLength;
+}
+
+/**
+ * This function returns a pointer to end of a given array (pointing to the past-the-end element).
+ *
+ * Note that the past-the-end element is a theoretical element that would follow the last element in the array. It does
+ * not point to an actual element in array, and thus should not be dereferenced.
+ *
+ * @tparam  Type          The array element type.
+ * @tparam  kArrayLength  The array length.
+ *
+ * @param[in] aArray   A reference to the array.
+ *
+ * @returns Pointer to the past-the-end element.
+ *
+ */
+template <typename Type, uint16_t kArrayLength> inline Type *GetArrayEnd(Type (&aArray)[kArrayLength])
+{
+    return &aArray[kArrayLength];
+}
+
+/**
+ * This function returns a pointer to end of a given array (pointing to the past-the-end element).
+ *
+ * Note that the past-the-end element is a theoretical element that would follow the last element in the array. It does
+ * not point to an actual element in array, and thus should not be dereferenced.
+ *
+ * @tparam  Type          The array element type.
+ * @tparam  kArrayLength  The array length.
+ *
+ * @param[in] aArray   A reference to the array.
+ *
+ * @returns Pointer to the past-the-end element.
+ *
+ */
+template <typename Type, uint16_t kArrayLength> inline const Type *GetArrayEnd(const Type (&aArray)[kArrayLength])
+{
+    return &aArray[kArrayLength];
+}
+
+/**
  * This template class represents an array of elements with a fixed max size.
  *
  * @tparam Type        The array element type.
diff --git a/src/core/common/error.cpp b/src/core/common/error.cpp
index 5d4a92c..ec15c4a 100644
--- a/src/core/common/error.cpp
+++ b/src/core/common/error.cpp
@@ -33,6 +33,7 @@
 
 #include "error.hpp"
 
+#include "common/array.hpp"
 #include "common/code_utils.hpp"
 
 namespace ot {
@@ -80,7 +81,7 @@
         "Rejected",                   // (37) kErrorRejected
     };
 
-    return aError < OT_ARRAY_LENGTH(kErrorStrings) ? kErrorStrings[aError] : "UnknownErrorType";
+    return aError < GetArrayLength(kErrorStrings) ? kErrorStrings[aError] : "UnknownErrorType";
 }
 
 } // namespace ot
diff --git a/src/core/common/heap_array.hpp b/src/core/common/heap_array.hpp
new file mode 100644
index 0000000..353462d
--- /dev/null
+++ b/src/core/common/heap_array.hpp
@@ -0,0 +1,549 @@
+/*
+ *  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.
+ */
+
+/**
+ * @file
+ *   This file includes definitions for `Heap::Array` (a heap allocated array of flexible length).
+ */
+
+#ifndef HEAP_ARRAY_HPP_
+#define HEAP_ARRAY_HPP_
+
+#include "openthread-core-config.h"
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include "common/array.hpp"
+#include "common/code_utils.hpp"
+#include "common/error.hpp"
+#include "common/heap.hpp"
+//#include "common/new.hpp"
+
+namespace ot {
+namespace Heap {
+
+/**
+ * This class represents a heap allocated array.
+ *
+ * The buffer to store the elements is allocated from heap and is managed by the `Heap::Array` class itself. The `Array`
+ * implementation will automatically grow the buffer when new entries are added. The `Heap::Array` destructor will
+ * always free the allocated buffer.
+ *
+ * The `Type` class MUST provide a move constructor `Type(Type &&aOther)` (or a copy constructor if no move constructor
+ * is provided). This constructor is used to move existing elements when array buffer is grown (new buffer is
+ * allocated) to make room for new elements.
+ *
+ * @tparam  Type                  The array element type.
+ * @tparam  kCapacityIncrements   Number of elements to allocate at a time when updating the array buffer.
+ *
+ */
+template <typename Type, uint16_t kCapacityIncrements = 2> class Array
+{
+public:
+    using IndexType = uint16_t;
+
+    /**
+     * This constructor initializes the `Array` as empty.
+     *
+     */
+    Array(void)
+        : mArray(nullptr)
+        , mLength(0)
+        , mCapacity(0)
+    {
+    }
+
+    /**
+     * This is the destructor for `Array` object.
+     *
+     */
+    ~Array(void) { Free(); }
+
+    /**
+     * This method frees any buffer allocated by the `Array`.
+     *
+     * The `Array` destructor will automatically call `Free()`. This method allows caller to free buffer explicitly.
+     *
+     */
+    void Free(void)
+    {
+        Clear();
+        Heap::Free(mArray);
+        mArray    = nullptr;
+        mCapacity = 0;
+    }
+
+    /**
+     * This method clears the array.
+     *
+     * Note that `Clear()` method (unlike `Free()`) does not free the allocated buffer and therefore does not change
+     * the current capacity of the array.
+     *
+     * This method invokes `Type` destructor on all cleared existing elements of array.
+     *
+     */
+    void Clear(void)
+    {
+        for (Type &entry : *this)
+        {
+            entry.~Type();
+        }
+
+        mLength = 0;
+    }
+
+    /**
+     * This method returns the current array length (number of elements in the array).
+     *
+     * @returns The array length.
+     *
+     */
+    IndexType GetLength(void) const { return mLength; }
+
+    /**
+     * This method returns a raw pointer to the array buffer.
+     *
+     * The returned raw pointer is valid only while the `Array` remains unchanged.
+     *
+     * @returns A pointer to the array buffer or `nullptr` if the array is empty.
+     *
+     */
+    const Type *AsCArray(void) const { return (mLength != 0) ? mArray : nullptr; }
+
+    /**
+     * This method returns the current capacity of array (number of elements that can fit in current allocated buffer).
+     *
+     * The allocated buffer and array capacity are automatically increased (by the `Array` itself) when new elements
+     * are added to array. Removing elements does not change the buffer and the capacity. A desired capacity can be
+     * reserved using `ReserveCapacity()` method.
+     *
+     * @returns The current capacity of the array.
+     *
+     */
+    IndexType GetCapacity(void) const { return mCapacity; }
+
+    /**
+     * This method allocates buffer to reserve a given capacity for array.
+     *
+     * If the requested @p aCapacity is smaller than the current length of the array, capacity remains unchanged.
+     *
+     * @param[in] aCapacity   The target capacity for the array.
+     *
+     * @retval kErrorNone     Array was successfully updated to support @p aCapacity.
+     * @retval kErrorNoBufs   Could not allocate buffer.
+     *
+     */
+    Error ReserveCapacity(IndexType aCapacity) { return Allocate(aCapacity); }
+
+    /**
+     * This method sets the array by taking the buffer from another given array (using move semantics).
+     *
+     * @param[in] aOther    The other `Heap::Array` to take from (rvalue reference).
+     *
+     */
+    void TakeFrom(Array &&aOther)
+    {
+        Free();
+        mArray           = aOther.mArray;
+        mLength          = aOther.mLength;
+        mCapacity        = aOther.mCapacity;
+        aOther.mArray    = nullptr;
+        aOther.mLength   = 0;
+        aOther.mCapacity = 0;
+    }
+
+    /**
+     * This method overloads the `[]` operator to get the element at a given index.
+     *
+     * This method does not perform index bounds checking. Behavior is undefined if @p aIndex is not valid.
+     *
+     * @param[in] aIndex  The index to get.
+     *
+     * @returns A reference to the element in array at @p aIndex.
+     *
+     */
+    Type &operator[](IndexType aIndex) { return mArray[aIndex]; }
+
+    /**
+     * This method overloads the `[]` operator to get the element at a given index.
+     *
+     * This method does not perform index bounds checking. Behavior is undefined if @p aIndex is not valid.
+     *
+     * @param[in] aIndex  The index to get.
+     *
+     * @returns A reference to the element in array at @p aIndex.
+     *
+     */
+    const Type &operator[](IndexType aIndex) const { return mArray[aIndex]; }
+
+    /**
+     * This method gets a pointer to the element at a given index.
+     *
+     * Unlike `operator[]`, this method checks @p aIndex to be valid and within the current length. The returned
+     * pointer is valid only while the `Array` remains unchanged.
+     *
+     * @param[in] aIndex  The index to get.
+     *
+     * @returns A pointer to element in array at @p aIndex or `nullptr` if @p aIndex is not valid.
+     *
+     */
+    Type *At(IndexType aIndex) { return (aIndex < mLength) ? &mArray[aIndex] : nullptr; }
+
+    /**
+     * This method gets a pointer to the element at a given index.
+     *
+     * Unlike `operator[]`, this method checks @p aIndex to be valid and within the current length. The returned
+     * pointer is valid only while the `Array` remains unchanged.
+     *
+     * @param[in] aIndex  The index to get.
+     *
+     * @returns A pointer to element in array at @p aIndex or `nullptr` if @p aIndex is not valid.
+     *
+     */
+    const Type *At(IndexType aIndex) const { return (aIndex < mLength) ? &mArray[aIndex] : nullptr; }
+
+    /**
+     * This method gets a pointer to the element at the front of the array (first element).
+     *
+     * The returned pointer is valid only while the `Array` remains unchanged.
+     *
+     * @returns A pointer to the front element or `nullptr` if array is empty.
+     *
+     */
+    Type *Front(void) { return At(0); }
+
+    /**
+     * This method gets a pointer to the element at the front of the array (first element).
+     *
+     * The returned pointer is valid only while the `Array` remains unchanged.
+     *
+     * @returns A pointer to the front element or `nullptr` if array is empty.
+     *
+     */
+    const Type *Front(void) const { return At(0); }
+
+    /**
+     * This method gets a pointer to the element at the back of the array (last element).
+     *
+     * The returned pointer is valid only while the `Array` remains unchanged.
+     *
+     * @returns A pointer to the back element or `nullptr` if array is empty.
+     *
+     */
+    Type *Back(void) { return (mLength > 0) ? &mArray[mLength - 1] : nullptr; }
+
+    /**
+     * This method gets a pointer to the element at the back of the array (last element).
+     *
+     * The returned pointer is valid only while the `Array` remains unchanged.
+     *
+     * @returns A pointer to the back element or `nullptr` if array is empty.
+     *
+     */
+    const Type *Back(void) const { return (mLength > 0) ? &mArray[mLength - 1] : nullptr; }
+
+    /**
+     * This method appends a new entry to the end of the array.
+     *
+     * This method requires the `Type` to provide a copy constructor of format `Type(const Type &aOther)` to init the
+     * new element in the array from @p aEntry.
+     *
+     * @param[in] aEntry     The new entry to push back.
+     *
+     * @retval kErrorNone    Successfully pushed back @p aEntry to the end of the array.
+     * @retval kErrorNoBufs  Could not allocate buffer to grow the array.
+     *
+     */
+    Error PushBack(const Type &aEntry)
+    {
+        Error error = kErrorNone;
+
+        if (mLength == mCapacity)
+        {
+            SuccessOrExit(error = Allocate(mCapacity + kCapacityIncrements));
+        }
+
+        new (&mArray[mLength++]) Type(aEntry);
+
+    exit:
+        return error;
+    }
+
+    /**
+     * This method appends a new entry to the end of the array.
+     *
+     * This method requires the `Type` to provide a copy constructor of format `Type(Type &&aOther)` to init the
+     * new element in the array from @p aEntry.
+     *
+     * @param[in] aEntry     The new entry to push back (an rvalue reference)
+     *
+     * @retval kErrorNone    Successfully pushed back @p aEntry to the end of the array.
+     * @retval kErrorNoBufs  Could not allocate buffer to grow the array.
+     *
+     */
+    Error PushBack(Type &&aEntry)
+    {
+        Error error = kErrorNone;
+
+        if (mLength == mCapacity)
+        {
+            SuccessOrExit(error = Allocate(mCapacity + kCapacityIncrements));
+        }
+
+        new (&mArray[mLength++]) Type(static_cast<Type &&>(aEntry));
+
+    exit:
+        return error;
+    }
+
+    /**
+     * This method appends a new entry to the end of the array.
+     *
+     * On success, this method returns a pointer to the newly appended element in the array for the caller to
+     * initialize and use. This method uses the `Type(void)` default constructor on the newly appended element (if not
+     * `nullptr`).
+     *
+     * The returned pointer is valid only while the `Array` remains unchanged.
+     *
+     * @return A pointer to the newly appended element or `nullptr` if could not allocate buffer to grow the array
+     *
+     */
+    Type *PushBack(void)
+    {
+        Type *newEntry = nullptr;
+
+        if (mLength == mCapacity)
+        {
+            SuccessOrExit(Allocate(mCapacity + kCapacityIncrements));
+        }
+
+        newEntry = new (&mArray[mLength++]) Type();
+
+    exit:
+        return newEntry;
+    }
+
+    /**
+     * This method removes the last element in the array.
+     *
+     * This method will invoke the `Type` destructor on the removed element.
+     *
+     */
+    void PopBack(void)
+    {
+        if (mLength > 0)
+        {
+            mArray[mLength - 1].~Type();
+            mLength--;
+        }
+    }
+
+    /**
+     * This method returns the index of an element in the array.
+     *
+     * The @p aElement MUST be from the array, otherwise the behavior of this method is undefined.
+     *
+     * @param[in] aElement  A reference to an element in the array.
+     *
+     * @returns The index of @p aElement in the array.
+     *
+     */
+    IndexType IndexOf(const Type &aElement) const { return static_cast<IndexType>(&aElement - mArray); }
+
+    /**
+     * This method finds the first match of a given entry in the array.
+     *
+     * This method uses `==` operator on `Type` to compare the array element with @p aEntry. The returned pointer is
+     * valid only while the `Array` remains unchanged.
+     *
+     * @param[in] aEntry   The entry to search for within the array.
+     *
+     * @returns A pointer to matched array element, or `nullptr` if a match could not be found.
+     *
+     */
+    Type *Find(const Type &aEntry) { return AsNonConst(AsConst(this)->Find(aEntry)); }
+
+    /**
+     * This method finds the first match of a given entry in the array.
+     *
+     * This method uses `==` operator to compare the array elements with @p aEntry. The returned pointer is valid only
+     * while the `Array` remains unchanged.
+     *
+     * @param[in] aEntry   The entry to search for within the array.
+     *
+     * @returns A pointer to matched array element, or `nullptr` if a match could not be found.
+     *
+     */
+    const Type *Find(const Type &aEntry) const
+    {
+        const Type *matched = nullptr;
+
+        for (const Type &element : *this)
+        {
+            if (element == aEntry)
+            {
+                matched = &element;
+                break;
+            }
+        }
+
+        return matched;
+    }
+
+    /**
+     * This method indicates whether or not a match to given entry exists in the array.
+     *
+     * This method uses `==` operator on `Type` to compare the array elements with @p aEntry.
+     *
+     * @param[in] aEntry   The entry to search for within the array.
+     *
+     * @retval TRUE   The array contains a matching element with @p aEntry.
+     * @retval FALSE  The array does not contain a matching element with @p aEntry.
+     *
+     */
+    bool Contains(const Type &aEntry) const { return Find(aEntry) != nullptr; }
+
+    /**
+     * This template method finds the first element in the array matching a given indicator.
+     *
+     * The template type `Indicator` specifies the type of @p aIndicator object which is used to match against elements
+     * in the array. To check that an element matches the given indicator, the `Matches()` method is invoked on each
+     * `Type` element in the array. The `Matches()` method should be provided by `Type` class accordingly:
+     *
+     *     bool Type::Matches(const Indicator &aIndicator) const
+     *
+     * The returned pointer is valid only while the `Array` remains unchanged.
+     *
+     * @param[in]  aIndicator  An indicator to match with elements in the array.
+     *
+     * @returns A pointer to the matched array element, or `nullptr` if a match could not be found.
+     *
+     */
+    template <typename Indicator> Type *FindMatching(const Indicator &aIndicator)
+    {
+        return AsNonConst(AsConst(this)->FindMatching(aIndicator));
+    }
+
+    /**
+     * This template method finds the first element in the array matching a given indicator.
+     *
+     * The template type `Indicator` specifies the type of @p aIndicator object which is used to match against elements
+     * in the array. To check that an element matches the given indicator, the `Matches()` method is invoked on each
+     * `Type` element in the array. The `Matches()` method should be provided by `Type` class accordingly:
+     *
+     *     bool Type::Matches(const Indicator &aIndicator) const
+     *
+     * The returned pointer is valid only while the `Array` remains unchanged.
+     *
+     * @param[in]  aIndicator  An indicator to match with elements in the array.
+     *
+     * @returns A pointer to the matched array element, or `nullptr` if a match could not be found.
+     *
+     */
+    template <typename Indicator> const Type *FindMatching(const Indicator &aIndicator) const
+    {
+        const Type *matched = nullptr;
+
+        for (const Type &element : *this)
+        {
+            if (element.Matches(aIndicator))
+            {
+                matched = &element;
+                break;
+            }
+        }
+
+        return matched;
+    }
+
+    /**
+     * This template method indicates whether or not the array contains an element matching a given indicator.
+     *
+     * The template type `Indicator` specifies the type of @p aIndicator object which is used to match against elements
+     * in the array. To check that an element matches the given indicator, the `Matches()` method is invoked on each
+     * `Type` element in the array. The `Matches()` method should be provided by `Type` class accordingly:
+     *
+     *     bool Type::Matches(const Indicator &aIndicator) const
+     *
+     * @param[in]  aIndicator  An indicator to match with elements in the array.
+     *
+     * @retval TRUE   The array contains a matching element with @p aIndicator.
+     * @retval FALSE  The array does not contain a matching element with @p aIndicator.
+     *
+     */
+    template <typename Indicator> bool ContainsMatching(const Indicator &aIndicator) const
+    {
+        return FindMatching(aIndicator) != nullptr;
+    }
+
+    // The following methods are intended to support range-based `for`
+    // loop iteration over the array elements and should not be used
+    // directly.
+
+    Type *      begin(void) { return (mLength > 0) ? mArray : nullptr; }
+    Type *      end(void) { return (mLength > 0) ? &mArray[mLength] : nullptr; }
+    const Type *begin(void) const { return (mLength > 0) ? mArray : nullptr; }
+    const Type *end(void) const { return (mLength > 0) ? &mArray[mLength] : nullptr; }
+
+    Array(const Array &) = delete;
+    Array &operator=(const Array &) = delete;
+
+private:
+    Error Allocate(IndexType aCapacity)
+    {
+        Error error = kErrorNone;
+        Type *newArray;
+
+        VerifyOrExit((aCapacity != mCapacity) && (aCapacity >= mLength));
+        newArray = static_cast<Type *>(Heap::CAlloc(aCapacity, sizeof(Type)));
+        VerifyOrExit(newArray != nullptr, error = kErrorNoBufs);
+
+        for (IndexType index = 0; index < mLength; index++)
+        {
+            new (&newArray[index]) Type(static_cast<Type &&>(mArray[index]));
+            mArray[index].~Type();
+        }
+
+        Heap::Free(mArray);
+        mArray    = newArray;
+        mCapacity = aCapacity;
+
+    exit:
+        return error;
+    }
+
+    Type *    mArray;
+    IndexType mLength;
+    IndexType mCapacity;
+};
+
+} // namespace Heap
+} // namespace ot
+
+#endif // HEAP_ARRAY_HPP_
diff --git a/src/core/common/instance.cpp b/src/core/common/instance.cpp
index fb543e4..af453ae 100644
--- a/src/core/common/instance.cpp
+++ b/src/core/common/instance.cpp
@@ -35,7 +35,6 @@
 
 #include <openthread/platform/misc.h>
 
-#include "common/logging.hpp"
 #include "common/new.hpp"
 #include "radio/trel_link.hpp"
 #include "utils/heap.hpp"
@@ -59,7 +58,7 @@
 #endif
 
 #if OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE
-otLogLevel Instance::sLogLevel = static_cast<otLogLevel>(OPENTHREAD_CONFIG_LOG_LEVEL_INIT);
+LogLevel Instance::sLogLevel = static_cast<LogLevel>(OPENTHREAD_CONFIG_LOG_LEVEL_INIT);
 #endif
 
 Instance::Instance(void)
@@ -190,7 +189,7 @@
     // Restore datasets and network information
 
     Get<Settings>().Init();
-    IgnoreError(Get<Mle::MleRouter>().Restore());
+    Get<Mle::MleRouter>().Restore();
 
 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
     Get<Trel::Link>().AfterInit();
@@ -234,6 +233,7 @@
 }
 
 #if OPENTHREAD_MTD || OPENTHREAD_FTD
+
 void Instance::FactoryReset(void)
 {
     Get<Settings>().Wipe();
@@ -251,6 +251,55 @@
     return error;
 }
 
+void Instance::GetBufferInfo(BufferInfo &aInfo)
+{
+    aInfo.Clear();
+
+    aInfo.mTotalBuffers = Get<MessagePool>().GetTotalBufferCount();
+    aInfo.mFreeBuffers  = Get<MessagePool>().GetFreeBufferCount();
+
+    Get<MeshForwarder>().GetSendQueue().GetInfo(aInfo.m6loSendQueue);
+    Get<MeshForwarder>().GetReassemblyQueue().GetInfo(aInfo.m6loReassemblyQueue);
+    Get<Ip6::Ip6>().GetSendQueue().GetInfo(aInfo.mIp6Queue);
+
+#if OPENTHREAD_FTD
+    Get<Ip6::Mpl>().GetBufferedMessageSet().GetInfo(aInfo.mMplQueue);
+#endif
+
+    Get<Mle::MleRouter>().GetMessageQueue().GetInfo(aInfo.mMleQueue);
+
+    Get<Tmf::Agent>().GetRequestMessages().GetInfo(aInfo.mCoapQueue);
+    Get<Tmf::Agent>().GetCachedResponses().GetInfo(aInfo.mCoapQueue);
+
+#if OPENTHREAD_CONFIG_DTLS_ENABLE
+    Get<Coap::CoapSecure>().GetRequestMessages().GetInfo(aInfo.mCoapSecureQueue);
+    Get<Coap::CoapSecure>().GetCachedResponses().GetInfo(aInfo.mCoapSecureQueue);
+#endif
+
+#if OPENTHREAD_CONFIG_COAP_API_ENABLE
+    GetApplicationCoap().GetRequestMessages().GetInfo(aInfo.mApplicationCoapQueue);
+    GetApplicationCoap().GetCachedResponses().GetInfo(aInfo.mApplicationCoapQueue);
+#endif
+}
+
 #endif // OPENTHREAD_MTD || OPENTHREAD_FTD
 
+#if OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE
+
+void Instance::SetLogLevel(LogLevel aLogLevel)
+{
+    if (aLogLevel != sLogLevel)
+    {
+        sLogLevel = aLogLevel;
+        otPlatLogHandleLevelChanged(sLogLevel);
+    }
+}
+
+extern "C" OT_TOOL_WEAK void otPlatLogHandleLevelChanged(otLogLevel aLogLevel)
+{
+    OT_UNUSED_VARIABLE(aLogLevel);
+}
+
+#endif
+
 } // namespace ot
diff --git a/src/core/common/instance.hpp b/src/core/common/instance.hpp
index fa1b26f..386755f 100644
--- a/src/core/common/instance.hpp
+++ b/src/core/common/instance.hpp
@@ -40,17 +40,18 @@
 #include <stdint.h>
 
 #include <openthread/heap.h>
-#include <openthread/platform/logging.h>
 #if OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE
 #include <openthread/platform/memory.h>
 #endif
 
+#include "common/array.hpp"
 #include "common/as_core_type.hpp"
 #include "common/error.hpp"
 #include "common/extension.hpp"
+#include "common/log.hpp"
 #include "common/message.hpp"
 #include "common/non_copyable.hpp"
-#include "common/random_manager.hpp"
+#include "common/random.hpp"
 #include "common/tasklet.hpp"
 #include "common/time_ticker.hpp"
 #include "common/timer.hpp"
@@ -70,6 +71,8 @@
 #include "crypto/mbedtls.hpp"
 #include "meshcop/border_agent.hpp"
 #include "meshcop/dataset_updater.hpp"
+#include "meshcop/extended_panid.hpp"
+#include "meshcop/network_name.hpp"
 #include "net/ip6.hpp"
 #include "thread/announce_sender.hpp"
 #include "thread/link_metrics.hpp"
@@ -112,15 +115,23 @@
 class Instance : public otInstance, private NonCopyable
 {
 public:
+    /**
+     * This type represents the message buffer information (number of messages/buffers in all OT stack message queues).
+     *
+     */
+    class BufferInfo : public otBufferInfo, public Clearable<BufferInfo>
+    {
+    };
+
 #if OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE
     /**
       * This static method initializes the OpenThread instance.
       *
       * This function must be called before any other calls on OpenThread instance.
       *
-      * @param[in]    aBuffer      The buffer for OpenThread to use for allocating the Instance.
-      * @param[inout] aBufferSize  On input, the size of `aBuffer`. On output, if not enough space for `Instance`, the
-                                   number of bytes required for `Instance`.
+      * @param[in]     aBuffer      The buffer for OpenThread to use for allocating the Instance.
+      * @param[in,out] aBufferSize  On input, the size of `aBuffer`. On output, if not enough space for `Instance`, the
+                                    number of bytes required for `Instance`.
       *
       * @returns  A pointer to the new OpenThread instance.
       *
@@ -180,14 +191,14 @@
      * @returns The log level.
      *
      */
-    static otLogLevel GetLogLevel(void)
+    static LogLevel GetLogLevel(void)
 #if OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE
     {
         return sLogLevel;
     }
 #else
     {
-        return static_cast<otLogLevel>(OPENTHREAD_CONFIG_LOG_LEVEL);
+        return static_cast<LogLevel>(OPENTHREAD_CONFIG_LOG_LEVEL);
     }
 #endif
 
@@ -198,11 +209,7 @@
      * @param[in] aLogLevel  A log level.
      *
      */
-    static void SetLogLevel(otLogLevel aLogLevel)
-    {
-        OT_ASSERT(aLogLevel <= OT_LOG_LEVEL_DEBG && aLogLevel >= OT_LOG_LEVEL_NONE);
-        sLogLevel = aLogLevel;
-    }
+    static void SetLogLevel(LogLevel aLogLevel);
 #endif
 
     /**
@@ -284,6 +291,14 @@
     static bool IsDnsNameCompressionEnabled(void) { return sDnsNameCompressionEnabled; }
 #endif
 
+    /**
+     * This method retrieves the the Message Buffer information.
+     *
+     * @param[out]  aInfo  A `BufferInfo` where information is written.
+     *
+     */
+    void GetBufferInfo(BufferInfo &aInfo);
+
 #endif // OPENTHREAD_MTD || OPENTHREAD_FTD
 
     /**
@@ -318,7 +333,7 @@
 #endif
 
 #if OPENTHREAD_MTD || OPENTHREAD_FTD
-    // RandomManager is initialized before other objects. Note that it
+    // Random::Manager is initialized before other objects. Note that it
     // requires MbedTls which itself may use Heap.
 #if !OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE
     static Utils::Heap sHeap;
@@ -326,7 +341,7 @@
     Crypto::MbedTls mMbedTls;
 #endif // OPENTHREAD_MTD || OPENTHREAD_FTD
 
-    RandomManager mRandomManager;
+    Random::Manager mRandomManager;
 
     // Radio is initialized before other member variables
     // (particularly, SubMac and Mac) to allow them to use its methods
@@ -395,7 +410,7 @@
 #endif // OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE
 
 #if OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE
-    static otLogLevel sLogLevel;
+    static LogLevel sLogLevel;
 #endif
 #if OPENTHREAD_ENABLE_VENDOR_EXTENSION
     Extension::ExtensionBase &mExtension;
@@ -411,6 +426,7 @@
 };
 
 DefineCoreType(otInstance, Instance);
+DefineCoreType(otBufferInfo, Instance::BufferInfo);
 
 // Specializations of the `Get<Type>()` method.
 
@@ -687,12 +703,22 @@
 }
 #endif
 
-template <> inline MeshCoP::ActiveDataset &Instance::Get(void)
+template <> inline MeshCoP::ExtendedPanIdManager &Instance::Get(void)
+{
+    return mThreadNetif.mExtendedPanIdManager;
+}
+
+template <> inline MeshCoP::NetworkNameManager &Instance::Get(void)
+{
+    return mThreadNetif.mNetworkNameManager;
+}
+
+template <> inline MeshCoP::ActiveDatasetManager &Instance::Get(void)
 {
     return mThreadNetif.mActiveDataset;
 }
 
-template <> inline MeshCoP::PendingDataset &Instance::Get(void)
+template <> inline MeshCoP::PendingDatasetManager &Instance::Get(void)
 {
     return mThreadNetif.mPendingDataset;
 }
diff --git a/src/core/common/log.cpp b/src/core/common/log.cpp
new file mode 100644
index 0000000..a0a126a
--- /dev/null
+++ b/src/core/common/log.cpp
@@ -0,0 +1,266 @@
+/*
+ *  Copyright (c) 2017-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.
+ */
+
+/**
+ * @file
+ *   This file implements the logging related functions.
+ */
+
+#include "log.hpp"
+
+#include <ctype.h>
+
+#include <openthread/platform/logging.h>
+
+#include "common/code_utils.hpp"
+#include "common/instance.hpp"
+#include "common/string.hpp"
+
+/*
+ * Verify debug UART dependency.
+ *
+ * It is reasonable to only enable the debug UART and not enable logs to the DEBUG UART.
+ */
+#if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_DEBUG_UART) && (!OPENTHREAD_CONFIG_ENABLE_DEBUG_UART)
+#error "OPENTHREAD_CONFIG_ENABLE_DEBUG_UART_LOG requires OPENTHREAD_CONFIG_ENABLE_DEBUG_UART"
+#endif
+
+#if OPENTHREAD_CONFIG_LOG_PREPEND_UPTIME && !OPENTHREAD_CONFIG_UPTIME_ENABLE
+#error "OPENTHREAD_CONFIG_LOG_PREPEND_UPTIME requires OPENTHREAD_CONFIG_UPTIME_ENABLE"
+#endif
+
+#if OPENTHREAD_CONFIG_LOG_PREPEND_UPTIME && OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE
+#error "OPENTHREAD_CONFIG_LOG_PREPEND_UPTIME is not supported under OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE"
+#endif
+
+namespace ot {
+
+#if OT_SHOULD_LOG
+
+template <LogLevel kLogLevel> void Logger::LogAtLevel(const char *aModuleName, const char *aFormat, ...)
+{
+    va_list args;
+
+    va_start(args, aFormat);
+    LogVarArgs(aModuleName, kLogLevel, aFormat, args);
+    va_end(args);
+}
+
+// Explicit instantiations
+template void Logger::LogAtLevel<kLogLevelNone>(const char *aModuleName, const char *aFormat, ...);
+template void Logger::LogAtLevel<kLogLevelCrit>(const char *aModuleName, const char *aFormat, ...);
+template void Logger::LogAtLevel<kLogLevelWarn>(const char *aModuleName, const char *aFormat, ...);
+template void Logger::LogAtLevel<kLogLevelNote>(const char *aModuleName, const char *aFormat, ...);
+template void Logger::LogAtLevel<kLogLevelInfo>(const char *aModuleName, const char *aFormat, ...);
+template void Logger::LogAtLevel<kLogLevelDebg>(const char *aModuleName, const char *aFormat, ...);
+
+void Logger::LogInModule(const char *aModuleName, LogLevel aLogLevel, const char *aFormat, ...)
+{
+    va_list args;
+
+    va_start(args, aFormat);
+    LogVarArgs(aModuleName, aLogLevel, aFormat, args);
+    va_end(args);
+}
+
+void Logger::LogVarArgs(const char *aModuleName, LogLevel aLogLevel, const char *aFormat, va_list aArgs)
+{
+    static const char kModuleNamePadding[] = "--------------";
+
+    ot::String<OPENTHREAD_CONFIG_LOG_MAX_SIZE> logString;
+
+    static_assert(sizeof(kModuleNamePadding) == kMaxLogModuleNameLength + 1, "Padding string is not correct");
+
+#if OPENTHREAD_CONFIG_LOG_PREPEND_UPTIME
+    ot::Uptime::UptimeToString(ot::Instance::Get().Get<ot::Uptime>().GetUptime(), logString);
+    logString.Append(" ");
+#endif
+
+#if OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE
+    VerifyOrExit(Instance::GetLogLevel() >= aLogLevel);
+#endif
+
+#if OPENTHREAD_CONFIG_LOG_PREPEND_LEVEL
+    {
+        static const char kLevelChars[] = {
+            '-', /* kLogLevelNone */
+            'C', /* kLogLevelCrit */
+            'W', /* kLogLevelWarn */
+            'N', /* kLogLevelNote */
+            'I', /* kLogLevelInfo */
+            'D', /* kLogLevelDebg */
+        };
+
+        logString.Append("[%c] ", kLevelChars[aLogLevel]);
+    }
+#endif
+
+    logString.Append("%.*s%s: ", kMaxLogModuleNameLength, aModuleName,
+                     &kModuleNamePadding[StringLength(aModuleName, kMaxLogModuleNameLength)]);
+
+    logString.AppendVarArgs(aFormat, aArgs);
+
+    logString.Append("%s", OPENTHREAD_CONFIG_LOG_SUFFIX);
+    otPlatLog(aLogLevel, OT_LOG_REGION_CORE, "%s", logString.AsCString());
+
+    ExitNow();
+
+exit:
+    return;
+}
+
+#if OPENTHREAD_CONFIG_LOG_PKT_DUMP
+
+template <LogLevel kLogLevel>
+void Logger::DumpAtLevel(const char *aModuleName, const char *aText, const void *aData, uint16_t aDataLength)
+{
+    DumpInModule(aModuleName, kLogLevel, aText, aData, aDataLength);
+}
+
+// Explicit instantiations
+template void Logger::DumpAtLevel<kLogLevelNone>(const char *aModuleName,
+                                                 const char *aText,
+                                                 const void *aData,
+                                                 uint16_t    aDataLength);
+template void Logger::DumpAtLevel<kLogLevelCrit>(const char *aModuleName,
+                                                 const char *aText,
+                                                 const void *aData,
+                                                 uint16_t    aDataLength);
+template void Logger::DumpAtLevel<kLogLevelWarn>(const char *aModuleName,
+                                                 const char *aText,
+                                                 const void *aData,
+                                                 uint16_t    aDataLength);
+template void Logger::DumpAtLevel<kLogLevelNote>(const char *aModuleName,
+                                                 const char *aText,
+                                                 const void *aData,
+                                                 uint16_t    aDataLength);
+template void Logger::DumpAtLevel<kLogLevelInfo>(const char *aModuleName,
+                                                 const char *aText,
+                                                 const void *aData,
+                                                 uint16_t    aDataLength);
+template void Logger::DumpAtLevel<kLogLevelDebg>(const char *aModuleName,
+                                                 const char *aText,
+                                                 const void *aData,
+                                                 uint16_t    aDataLength);
+
+void Logger::DumpLine(const char *aModuleName, LogLevel aLogLevel, const uint8_t *aData, const uint16_t aDataLength)
+{
+    ot::String<kStringLineLength> string;
+
+    string.Append("|");
+
+    for (uint8_t i = 0; i < kDumpBytesPerLine; i++)
+    {
+        if (i < aDataLength)
+        {
+            string.Append(" %02X", aData[i]);
+        }
+        else
+        {
+            string.Append(" ..");
+        }
+
+        if (!((i + 1) % 8))
+        {
+            string.Append(" |");
+        }
+    }
+
+    string.Append(" ");
+
+    for (uint8_t i = 0; i < kDumpBytesPerLine; i++)
+    {
+        char c = '.';
+
+        if (i < aDataLength)
+        {
+            char byteAsChar = static_cast<char>(0x7f & aData[i]);
+
+            if (isprint(byteAsChar))
+            {
+                c = byteAsChar;
+            }
+        }
+
+        string.Append("%c", c);
+    }
+
+    LogInModule(aModuleName, aLogLevel, "%s", string.AsCString());
+}
+
+void Logger::DumpInModule(const char *aModuleName,
+                          LogLevel    aLogLevel,
+                          const char *aText,
+                          const void *aData,
+                          uint16_t    aDataLength)
+{
+    constexpr uint16_t kWidth         = 72;
+    constexpr uint16_t kTextSuffixLen = sizeof("[ len=000]") - 1;
+
+    uint16_t                      txtLen = StringLength(aText, kWidth - kTextSuffixLen) + kTextSuffixLen;
+    ot::String<kStringLineLength> string;
+
+    VerifyOrExit(otLoggingGetLevel() >= aLogLevel);
+
+    for (uint16_t i = 0; i < static_cast<uint16_t>((kWidth - txtLen) / 2); i++)
+    {
+        string.Append("=");
+    }
+
+    string.Append("[%s len=%03u]", aText, aDataLength);
+
+    for (uint16_t i = 0; i < static_cast<uint16_t>(kWidth - txtLen - (kWidth - txtLen) / 2); i++)
+    {
+        string.Append("=");
+    }
+
+    LogInModule(aModuleName, aLogLevel, "%s", string.AsCString());
+
+    for (uint16_t i = 0; i < aDataLength; i += kDumpBytesPerLine)
+    {
+        DumpLine(aModuleName, aLogLevel, static_cast<const uint8_t *>(aData) + i,
+                 OT_MIN((aDataLength - i), kDumpBytesPerLine));
+    }
+
+    string.Clear();
+
+    for (uint16_t i = 0; i < kWidth; i++)
+    {
+        string.Append("-");
+    }
+
+    LogInModule(aModuleName, aLogLevel, "%s", string.AsCString());
+
+exit:
+    return;
+}
+#endif // OPENTHREAD_CONFIG_LOG_PKT_DUMP
+
+#endif // OT_SHOULD_LOG
+
+} // namespace ot
diff --git a/src/core/common/log.hpp b/src/core/common/log.hpp
new file mode 100644
index 0000000..0870e06
--- /dev/null
+++ b/src/core/common/log.hpp
@@ -0,0 +1,379 @@
+/*
+ *  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.
+ */
+
+/**
+ * @file
+ *   This file includes logging related  definitions.
+ */
+
+#ifndef LOG_HPP_
+#define LOG_HPP_
+
+#include "openthread-core-config.h"
+
+#include <openthread/logging.h>
+#include <openthread/platform/logging.h>
+#include <openthread/platform/toolchain.h>
+
+namespace ot {
+
+/**
+ * @def OT_SHOULD_LOG
+ *
+ * This definition indicates whether or not logging is enabled.
+ *
+ */
+#define OT_SHOULD_LOG (OPENTHREAD_CONFIG_LOG_OUTPUT != OPENTHREAD_CONFIG_LOG_OUTPUT_NONE)
+
+/**
+ * This macro indicates whether the OpenThread logging is enabled at a given log level.
+ *
+ * @param[in] aLevel   The log level to check.
+ *
+ * @returns TRUE if logging is enabled at @p aLevel, FALSE otherwise.
+ *
+ */
+#define OT_SHOULD_LOG_AT(aLevel) (OT_SHOULD_LOG && (OPENTHREAD_CONFIG_LOG_LEVEL >= (aLevel)))
+
+/**
+ * This enumeration represents the log level.
+ *
+ */
+enum LogLevel : uint8_t
+{
+    kLogLevelNone = OT_LOG_LEVEL_NONE, ///< None (disable logs)
+    kLogLevelCrit = OT_LOG_LEVEL_CRIT, ///< Critical log level
+    kLogLevelWarn = OT_LOG_LEVEL_WARN, ///< Warning log level
+    kLogLevelNote = OT_LOG_LEVEL_NOTE, ///< Note log level
+    kLogLevelInfo = OT_LOG_LEVEL_INFO, ///< Info log level
+    kLogLevelDebg = OT_LOG_LEVEL_DEBG, ///< Debug log level
+};
+
+constexpr uint8_t kMaxLogModuleNameLength = 14; ///< Maximum module name length
+
+#if OT_SHOULD_LOG && (OPENTHREAD_CONFIG_LOG_LEVEL != OT_LOG_LEVEL_NONE)
+/**
+ * This macro registers log module name.
+ *
+ * This macro is used in a `cpp` file to register the log module name for that file before using any other logging
+ * functions or macros (e.g., `LogInfo()` or `DumpInfo()`, ...) in the file.
+ *
+ * @param[in] aName  The log module name string (MUST be shorter than `kMaxLogModuleNameLength`).
+ *
+ */
+#define RegisterLogModule(aName)                                     \
+    constexpr char kLogModuleName[] = aName;                         \
+    namespace {                                                      \
+    /* Defining this type to silence "unused constant" warning/error \
+     * for `kLogModuleName` under any log level config.              \
+     */                                                              \
+    using DummyType = char[sizeof(kLogModuleName)];                  \
+    }                                                                \
+    static_assert(sizeof(kLogModuleName) <= kMaxLogModuleNameLength + 1, "Log module name is too long")
+
+#else
+#define RegisterLogModule(aName) static_assert(true, "Consume the required semi-colon at the end of macro")
+#endif
+
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_CRIT)
+/**
+ * This macro emits a log message at critical log level.
+ *
+ * @param[in]  ...   Arguments for the format specification.
+ *
+ */
+#define LogCrit(...) Logger::Log<kLogLevelCrit, kLogModuleName>(__VA_ARGS__)
+#else
+#define LogCrit(...)
+#endif
+
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN)
+/**
+ * This macro emits a log message at warning log level.
+ *
+ * @param[in]  ...   Arguments for the format specification.
+ *
+ */
+#define LogWarn(...) Logger::Log<kLogLevelWarn, kLogModuleName>(__VA_ARGS__)
+#else
+#define LogWarn(...)
+#endif
+
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_NOTE)
+/**
+ * This macro emits a log message at note log level.
+ *
+ * @param[in]  ...   Arguments for the format specification.
+ *
+ */
+#define LogNote(...) Logger::Log<kLogLevelNote, kLogModuleName>(__VA_ARGS__)
+#else
+#define LogNote(...)
+#endif
+
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
+/**
+ * This macro emits a log message at info log level.
+ *
+ * @param[in]  ...   Arguments for the format specification.
+ *
+ */
+#define LogInfo(...) Logger::Log<kLogLevelInfo, kLogModuleName>(__VA_ARGS__)
+#else
+#define LogInfo(...)
+#endif
+
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_DEBG)
+/**
+ * This macro emits a log message at debug log level.
+ *
+ * @param[in]  ...   Arguments for the format specification.
+ *
+ */
+#define LogDebg(...) Logger::Log<kLogLevelDebg, kLogModuleName>(__VA_ARGS__)
+#else
+#define LogDebg(...)
+#endif
+
+#if OT_SHOULD_LOG
+/**
+ * This macro emits a log message at a given log level.
+ *
+ * @param[in] aLogLevel  The log level to use.
+ * @param[in] ...        Argument for the format specification.
+ *
+ */
+#define LogAt(aLogLevel, ...) Logger::LogInModule(kLogModuleName, aLogLevel, __VA_ARGS__)
+#else
+#define LogAt(aLogLevel, ...)
+#endif
+
+#if OT_SHOULD_LOG
+/**
+ * This macro emits a log message independent of the configured log level.
+ *
+ * @param[in]  ...   Arguments for the format specification.
+ *
+ */
+#define LogAlways(...) Logger::LogInModule("", kLogLevelNone, __VA_ARGS__)
+#else
+#define LogAlways(...)
+#endif
+
+#if OT_SHOULD_LOG && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
+/**
+ * This macro emit a log message for the certification test.
+ *
+ * @param[in]  ...  Arguments for the format specification.
+ *
+ */
+#define LogCert(...) LogAlways(__VA_ARGS__)
+#else
+#define LogCert(...)
+#endif
+
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_CRIT) && OPENTHREAD_CONFIG_LOG_PKT_DUMP
+/**
+ * This macro generates a memory dump at log level critical.
+ *
+ * @param[in]  aText         A string that is printed before the bytes.
+ * @param[in]  aData         A pointer to the data buffer.
+ * @param[in]  aDataLength   Number of bytes in @p aData.
+ *
+ */
+#define DumpCrit(aText, aData, aDataLength) Logger::Dump<kLogLevelCrit, kLogModuleName>(aText, aData, aDataLength)
+#else
+#define DumpCrit(aText, aData, aDataLength)
+#endif
+
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN) && OPENTHREAD_CONFIG_LOG_PKT_DUMP
+/**
+ * This macro generates a memory dump at log level warning.
+ *
+ * @param[in]  aText         A string that is printed before the bytes.
+ * @param[in]  aData         A pointer to the data buffer.
+ * @param[in]  aDataLength   Number of bytes in @p aData.
+ *
+ */
+#define DumpWarn(aText, aData, aDataLength) Logger::Dump<kLogLevelWarn, kLogModuleName>(aText, aData, aDataLength)
+#else
+#define DumpWarn(aText, aData, aDataLength)
+#endif
+
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_NOTE) && OPENTHREAD_CONFIG_LOG_PKT_DUMP
+/**
+ * This macro generates a memory dump at log level note.
+ *
+ * @param[in]  aText         A string that is printed before the bytes.
+ * @param[in]  aData         A pointer to the data buffer.
+ * @param[in]  aDataLength   Number of bytes in @p aData.
+ *
+ */
+#define DumpNote(aText, aData, aDataLength) Logger::Dump<kLogLevelNote, kLogModuleName>(aText, aData, aDataLength)
+#else
+#define DumpNote(aText, aData, aDataLength)
+#endif
+
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) && OPENTHREAD_CONFIG_LOG_PKT_DUMP
+/**
+ * This macro generates a memory dump at log level info.
+ *
+ * @param[in]  aText         A string that is printed before the bytes.
+ * @param[in]  aData         A pointer to the data buffer.
+ * @param[in]  aDataLength   Number of bytes in @p aData.
+ *
+ */
+#define DumpInfo(aText, aData, aDataLength) Logger::Dump<kLogLevelInfo, kLogModuleName>(aText, aData, aDataLength)
+#else
+#define DumpInfo(aText, aData, aDataLength)
+#endif
+
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_DEBG) && OPENTHREAD_CONFIG_LOG_PKT_DUMP
+/**
+ * This macro generates a memory dump at log level debug.
+ *
+ * @param[in]  aText         A string that is printed before the bytes.
+ * @param[in]  aData         A pointer to the data buffer.
+ * @param[in]  aDataLength   Number of bytes in @p aData.
+ *
+ */
+#define DumpDebg(aText, aData, aDataLength) Logger::Dump<kLogLevelDebg, kLogModuleName>(aText, aData, aDataLength)
+#else
+#define DumpDebg(aText, aData, aDataLength)
+#endif
+
+#if OT_SHOULD_LOG && OPENTHREAD_CONFIG_LOG_PKT_DUMP
+/**
+ * This macro generates a memory dump independent of the configured log level.
+ *
+ * @param[in]  aText         A string that is printed before the bytes.
+ * @param[in]  aData         A pointer to the data buffer.
+ * @param[in]  aDataLength   Number of bytes in @p aData.
+ *
+ */
+#define DumpAlways(aText, aData, aDataLength) Logger::DumpInModule("", kLogLevelNone, aText, aData, aDataLength)
+#endif
+
+#if OT_SHOULD_LOG && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE && OPENTHREAD_CONFIG_LOG_PKT_DUMP
+/**
+ * This macro generates a memory dump for certification test.
+ *
+ * @param[in]  aText         A string that is printed before the bytes.
+ * @param[in]  aData         A pointer to the data buffer.
+ * @param[in]  aDataLength   Number of bytes in @p aData.
+ *
+ */
+#define DumpCert(aText, aData, aDataLength) DumpAlways(aText, aData, aDataLength)
+#else
+#define DumpCert(aText, aData, aDataLength)
+#endif
+
+//----------------------------------------------------------------------------------------------------------------------
+
+#if OT_SHOULD_LOG
+
+class Logger
+{
+    // The `Logger` class implements the logging methods.
+    //
+    // The `Logger` methods are not intended to be directly used
+    // and instead the logging macros should be used.
+
+public:
+    template <LogLevel kLogLevel, const char *kModuleName, typename... Args>
+    static void Log(const char *aFormat, Args... aArgs)
+    {
+        LogAtLevel<kLogLevel>(kModuleName, aFormat, aArgs...);
+    }
+
+    static void LogInModule(const char *aModuleName, LogLevel aLogLevel, const char *aFormat, ...);
+
+    template <LogLevel kLogLevel> static void LogAtLevel(const char *aModuleName, const char *aFormat, ...);
+    static void LogVarArgs(const char *aModuleName, LogLevel aLogLevel, const char *aFormat, va_list aArgs);
+
+#if OPENTHREAD_CONFIG_LOG_PKT_DUMP
+    static constexpr uint8_t kStringLineLength = 80;
+    static constexpr uint8_t kDumpBytesPerLine = 16;
+
+    template <LogLevel kLogLevel, const char *kModuleName>
+    static void Dump(const char *aText, const void *aData, uint16_t aDataLength)
+    {
+        DumpAtLevel<kLogLevel>(kModuleName, aText, aData, aDataLength);
+    }
+
+    static void DumpInModule(const char *aModuleName,
+                             LogLevel    aLogLevel,
+                             const char *aText,
+                             const void *aData,
+                             uint16_t    aDataLength);
+
+    template <LogLevel kLogLevel>
+    static void DumpAtLevel(const char *aModuleName, const char *aText, const void *aData, uint16_t aDataLength);
+
+    static void DumpLine(const char *aModuleName, LogLevel aLogLevel, const uint8_t *aData, uint16_t aDataLength);
+#endif
+};
+
+extern template void Logger::LogAtLevel<kLogLevelNone>(const char *aModuleName, const char *aFormat, ...);
+extern template void Logger::LogAtLevel<kLogLevelCrit>(const char *aModuleName, const char *aFormat, ...);
+extern template void Logger::LogAtLevel<kLogLevelWarn>(const char *aModuleName, const char *aFormat, ...);
+extern template void Logger::LogAtLevel<kLogLevelNote>(const char *aModuleName, const char *aFormat, ...);
+extern template void Logger::LogAtLevel<kLogLevelInfo>(const char *aModuleName, const char *aFormat, ...);
+extern template void Logger::LogAtLevel<kLogLevelDebg>(const char *aModuleName, const char *aFormat, ...);
+
+#if OPENTHREAD_CONFIG_LOG_PKT_DUMP
+extern template void Logger::DumpAtLevel<kLogLevelNone>(const char *aModuleName,
+                                                        const char *aText,
+                                                        const void *aData,
+                                                        uint16_t    aDataLength);
+extern template void Logger::DumpAtLevel<kLogLevelCrit>(const char *aModuleName,
+                                                        const char *aText,
+                                                        const void *aData,
+                                                        uint16_t    aDataLength);
+extern template void Logger::DumpAtLevel<kLogLevelWarn>(const char *aModuleName,
+                                                        const char *aText,
+                                                        const void *aData,
+                                                        uint16_t    aDataLength);
+extern template void Logger::DumpAtLevel<kLogLevelNote>(const char *aModuleName,
+                                                        const char *aText,
+                                                        const void *aData,
+                                                        uint16_t    aDataLength);
+extern template void Logger::DumpAtLevel<kLogLevelInfo>(const char *aModuleName,
+                                                        const char *aText,
+                                                        const void *aData,
+                                                        uint16_t    aDataLength);
+extern template void Logger::DumpAtLevel<kLogLevelDebg>(const char *aModuleName,
+                                                        const char *aText,
+                                                        const void *aData,
+                                                        uint16_t    aDataLength);
+#endif // OPENTHREAD_CONFIG_LOG_PKT_DUMP
+#endif // OT_SHOULD_LOG
+
+} // namespace ot
+
+#endif // LOG_HPP_
diff --git a/src/core/common/logging.cpp b/src/core/common/logging.cpp
deleted file mode 100644
index af45094..0000000
--- a/src/core/common/logging.cpp
+++ /dev/null
@@ -1,351 +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 logging related functions.
- */
-
-#include "logging.hpp"
-
-#include "common/code_utils.hpp"
-#include "common/instance.hpp"
-#include "common/string.hpp"
-
-/*
- * Verify debug uart dependency.
- *
- * It is reasonable to only enable the debug uart and not enable logs to the DEBUG uart.
- */
-#if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_DEBUG_UART) && (!OPENTHREAD_CONFIG_ENABLE_DEBUG_UART)
-#error "OPENTHREAD_CONFIG_ENABLE_DEBUG_UART_LOG requires OPENTHREAD_CONFIG_ENABLE_DEBUG_UART"
-#endif
-
-#if OPENTHREAD_CONFIG_LOG_PREPEND_UPTIME && !OPENTHREAD_CONFIG_UPTIME_ENABLE
-#error "OPENTHREAD_CONFIG_LOG_PREPEND_UPTIME requires OPENTHREAD_CONFIG_UPTIME_ENABLE"
-#endif
-
-#if OPENTHREAD_CONFIG_LOG_PREPEND_UPTIME && OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE
-#error "OPENTHREAD_CONFIG_LOG_PREPEND_UPTIME is not supported under OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE"
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-static void Log(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, va_list aArgs)
-{
-    ot::String<OPENTHREAD_CONFIG_LOG_MAX_SIZE> logString;
-
-#if OPENTHREAD_CONFIG_LOG_PREPEND_UPTIME
-    ot::Uptime::UptimeToString(ot::Instance::Get().Get<ot::Uptime>().GetUptime(), logString);
-    logString.Append(" ");
-#endif
-
-#if OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE
-    VerifyOrExit(otLoggingGetLevel() >= aLogLevel);
-#endif
-
-#if OPENTHREAD_CONFIG_LOG_PREPEND_LEVEL
-
-    {
-        static const char *const kLevelStrings[] = {
-            "NONE", "CRIT", "WARN", "NOTE", "INFO", "DEBG",
-        };
-
-        uint16_t index = ((aLogLevel >= 0) && (aLogLevel < static_cast<int>(OT_ARRAY_LENGTH(kLevelStrings))))
-                             ? static_cast<uint16_t>(aLogLevel)
-                             : 0;
-
-        logString.Append("[%s]", kLevelStrings[index]);
-    }
-#endif
-
-#if OPENTHREAD_CONFIG_LOG_PREPEND_REGION
-    {
-        static const char *const kRegionStrings[] = {
-            "-------", "API----", "MLE----", "ARP----", "N-DATA-", "ICMP---", "IP6----", "TCP----",
-            "MAC----", "MEM----", "NCP----", "MESH-CP", "DIAG---", "PLAT---", "COAP---", "CLI----",
-            "CORE---", "UTIL---", "BBR----", "MLR----", "DUA----", "BR-----", "SRP----", "DNS----",
-        };
-
-        uint16_t index = (aLogRegion < OT_ARRAY_LENGTH(kRegionStrings)) ? static_cast<uint16_t>(aLogRegion) : 0;
-
-        logString.Append("-%s-: ", kRegionStrings[index]);
-    }
-#elif OPENTHREAD_CONFIG_LOG_PREPEND_LEVEL
-    logString.Append(": ");
-#endif
-
-    logString.AppendVarArgs(aFormat, aArgs);
-    otPlatLog(aLogLevel, aLogRegion, "%s" OPENTHREAD_CONFIG_LOG_SUFFIX, logString.AsCString());
-
-#if OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE
-exit:
-    return;
-#endif
-}
-
-#if OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_CRIT
-void _otLogCrit(otLogRegion aRegion, const char *aFormat, ...)
-{
-    va_list args;
-
-    va_start(args, aFormat);
-    Log(OT_LOG_LEVEL_CRIT, aRegion, aFormat, args);
-    va_end(args);
-}
-#endif
-
-#if OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_WARN
-void _otLogWarn(otLogRegion aRegion, const char *aFormat, ...)
-{
-    va_list args;
-
-    va_start(args, aFormat);
-    Log(OT_LOG_LEVEL_WARN, aRegion, aFormat, args);
-    va_end(args);
-}
-#endif
-
-#if OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_NOTE
-void _otLogNote(otLogRegion aRegion, const char *aFormat, ...)
-{
-    va_list args;
-
-    va_start(args, aFormat);
-    Log(OT_LOG_LEVEL_NOTE, aRegion, aFormat, args);
-    va_end(args);
-}
-#endif
-
-#if OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO
-void _otLogInfo(otLogRegion aRegion, const char *aFormat, ...)
-{
-    va_list args;
-
-    va_start(args, aFormat);
-    Log(OT_LOG_LEVEL_INFO, aRegion, aFormat, args);
-    va_end(args);
-}
-#endif
-
-#if OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_DEBG
-void _otLogDebg(otLogRegion aRegion, const char *aFormat, ...)
-{
-    va_list args;
-
-    va_start(args, aFormat);
-    Log(OT_LOG_LEVEL_DEBG, aRegion, aFormat, args);
-    va_end(args);
-}
-#endif
-
-#if OPENTHREAD_CONFIG_LOG_MAC
-void otLogMac(otLogLevel aLogLevel, const char *aFormat, ...)
-{
-    va_list args;
-
-    VerifyOrExit(otLoggingGetLevel() >= aLogLevel);
-
-    va_start(args, aFormat);
-    Log(aLogLevel, OT_LOG_REGION_MAC, aFormat, args);
-    va_end(args);
-
-exit:
-    return;
-}
-
-void otDumpMacFrame(otLogLevel aLogLevel, const char *aId, const void *aBuf, const size_t aLength)
-{
-    constexpr uint8_t kFixedStringPart = 10; // strlen(" seqno=000")
-    constexpr uint8_t kSeqnoIdx        = 2;  // index of the sequence number within aBuf
-
-    size_t idLength = strlen(aId) + kFixedStringPart + 1; // allow for the '\0' character
-    char   newId[25];
-
-    VerifyOrExit(idLength <= sizeof(newId));
-    snprintf(newId, idLength, "%s seqno=%03u", aId, static_cast<const uint8_t *>(aBuf)[kSeqnoIdx]);
-    otDump(aLogLevel, OT_LOG_REGION_MAC, newId, aBuf, aLength);
-exit:
-    return;
-}
-#endif
-
-#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
-void otLogCertMeshCoP(const char *aFormat, ...)
-{
-    va_list args;
-
-    va_start(args, aFormat);
-    Log(OT_LOG_LEVEL_NONE, OT_LOG_REGION_MESH_COP, aFormat, args);
-    va_end(args);
-}
-#endif
-
-#if OPENTHREAD_CONFIG_OTNS_ENABLE
-void otLogOtns(const char *aFormat, ...)
-{
-    va_list args;
-
-    va_start(args, aFormat);
-    Log(OT_LOG_LEVEL_NONE, OT_LOG_REGION_CORE, aFormat, args);
-    va_end(args);
-}
-#endif
-
-#if OPENTHREAD_CONFIG_LOG_PKT_DUMP
-
-static void otLogDump(otLogLevel aLogLevel, otLogRegion aRegion, const char *aFormat, ...)
-{
-    va_list args;
-
-    VerifyOrExit(otLoggingGetLevel() >= aLogLevel);
-
-    va_start(args, aFormat);
-    Log(aLogLevel, aRegion, aFormat, args);
-    va_end(args);
-
-exit:
-    return;
-}
-
-static constexpr uint8_t kStringLineLength = 80;
-static constexpr uint8_t kDumpBytesPerLine = 16;
-
-static void DumpLine(otLogLevel aLogLevel, otLogRegion aLogRegion, const uint8_t *aBytes, const size_t aLength)
-{
-    ot::String<kStringLineLength> string;
-
-    string.Append("|");
-
-    for (uint8_t i = 0; i < kDumpBytesPerLine; i++)
-    {
-        if (i < aLength)
-        {
-            string.Append(" %02X", aBytes[i]);
-        }
-        else
-        {
-            string.Append(" ..");
-        }
-
-        if (!((i + 1) % 8))
-        {
-            string.Append(" |");
-        }
-    }
-
-    string.Append(" ");
-
-    for (uint8_t i = 0; i < kDumpBytesPerLine; i++)
-    {
-        char c = '.';
-
-        if (i < aLength)
-        {
-            char byteAsChar = static_cast<char>(0x7f & aBytes[i]);
-
-            if (isprint(byteAsChar))
-            {
-                c = byteAsChar;
-            }
-        }
-
-        string.Append("%c", c);
-    }
-
-    otLogDump(aLogLevel, aLogRegion, "%s", string.AsCString());
-}
-
-void otDump(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aId, const void *aBuf, const size_t aLength)
-{
-    constexpr uint8_t kWidth           = 72;
-    constexpr uint8_t kFixedStringPart = 10; // strlen("[ len=000]")
-
-    size_t                        idLen = strlen(aId) + kFixedStringPart;
-    ot::String<kStringLineLength> string;
-
-    VerifyOrExit(otLoggingGetLevel() >= aLogLevel);
-
-    for (size_t i = 0; i < (kWidth - idLen) / 2; i++)
-    {
-        string.Append("=");
-    }
-
-    string.Append("[%s len=%03u]", aId, static_cast<unsigned>(aLength));
-
-    for (size_t i = 0; i < kWidth - idLen - (kWidth - idLen) / 2; i++)
-    {
-        string.Append("=");
-    }
-
-    otLogDump(aLogLevel, aLogRegion, "%s", string.AsCString());
-
-    for (size_t i = 0; i < aLength; i += kDumpBytesPerLine)
-    {
-        DumpLine(aLogLevel, aLogRegion, static_cast<const uint8_t *>(aBuf) + i,
-                 OT_MIN((aLength - i), static_cast<size_t>(kDumpBytesPerLine)));
-    }
-
-    string.Clear();
-
-    for (size_t i = 0; i < kWidth; i++)
-    {
-        string.Append("-");
-    }
-
-    otLogDump(aLogLevel, aLogRegion, "%s", string.AsCString());
-
-exit:
-    return;
-}
-#else  // OPENTHREAD_CONFIG_LOG_PKT_DUMP
-void otDump(otLogLevel, otLogRegion, const char *, const void *, const size_t)
-{
-}
-#endif // OPENTHREAD_CONFIG_LOG_PKT_DUMP
-
-#if OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_NONE
-/* this provides a stub, in case something uses the function */
-void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, ...)
-{
-    OT_UNUSED_VARIABLE(aLogLevel);
-    OT_UNUSED_VARIABLE(aLogRegion);
-    OT_UNUSED_VARIABLE(aFormat);
-}
-#endif
-
-OT_TOOL_WEAK void otPlatLogLine(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aLogLine)
-{
-    otPlatLog(aLogLevel, aLogRegion, "%s", aLogLine);
-}
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/src/core/common/logging.hpp b/src/core/common/logging.hpp
index dc3167b..56016bc 100644
--- a/src/core/common/logging.hpp
+++ b/src/core/common/logging.hpp
@@ -36,2554 +36,7 @@
 
 #include "openthread-core-config.h"
 
-#include <ctype.h>
-#include <stdio.h>
-
 #include <openthread/logging.h>
 #include <openthread/platform/logging.h>
 
-#include "common/arg_macros.hpp"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * @def otLogCrit
- *
- * Logging at log level critical.
- *
- * @param[in]  aRegion   The log region.
- * @param[in]  ...       Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_LEVEL < OT_LOG_LEVEL_CRIT
-#define otLogCrit(aRegion, ...)
-#else
-#define otLogCrit(aRegion, ...) _otLogCrit(aRegion, __VA_ARGS__)
-void _otLogCrit(otLogRegion aRegion, const char *aFormat, ...);
-#endif
-
-/**
- * @def otLogWarn
- *
- * Logging at log level warning.
- *
- * @param[in]  aRegion   The log region.
- * @param[in]  ...       Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_LEVEL < OT_LOG_LEVEL_WARN
-#define otLogWarn(aRegion, ...)
-#else
-#define otLogWarn(aRegion, ...) _otLogWarn(aRegion, __VA_ARGS__)
-void _otLogWarn(otLogRegion aRegion, const char *aFormat, ...);
-#endif
-
-/**
- * @def otLogNote
- *
- * Logging at log level note
- *
- * @param[in]  aRegion   The log region.
- * @param[in]  ...       Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_LEVEL < OT_LOG_LEVEL_NOTE
-#define otLogNote(aRegion, ...)
-#else
-#define otLogNote(aRegion, ...) _otLogNote(aRegion, __VA_ARGS__)
-void _otLogNote(otLogRegion aRegion, const char *aFormat, ...);
-#endif
-
-/**
- * @def otLogInfo
- *
- * Logging at log level info.
- *
- * @param[in]  aRegion   The log region.
- * @param[in]  ...       Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_LEVEL < OT_LOG_LEVEL_INFO
-#define otLogInfo(aRegion, ...)
-#else
-#define otLogInfo(aRegion, ...) _otLogInfo(aRegion, __VA_ARGS__)
-void _otLogInfo(otLogRegion aRegion, const char *aFormat, ...);
-#endif
-
-/**
- * @def otLogDebg
- *
- * Logging at log level debug.
- *
- * @param[in]  aRegion   The log region.
- * @param[in]  ...       Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_LEVEL < OT_LOG_LEVEL_DEBG
-#define otLogDebg(aRegion, ...)
-#else
-#define otLogDebg(aRegion, ...) _otLogDebg(aRegion, __VA_ARGS__)
-void _otLogDebg(otLogRegion aRegion, const char *aFormat, ...);
-#endif
-
-/**
- * @def otLogCritApi
- *
- * This function generates a log with level critical for the API region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogWarnApi
- *
- * This function generates a log with level warning for the API region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogNoteApi
- *
- * This function generates a log with level note for the API region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogInfoApi
- *
- * This function generates a log with level info for the API region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogDebgApi
- *
- * This function generates a log with level debug for the API region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_API
-#define otLogCritApi(...) otLogCrit(OT_LOG_REGION_API, __VA_ARGS__)
-#define otLogWarnApi(...) otLogWarn(OT_LOG_REGION_API, __VA_ARGS__)
-#define otLogNoteApi(...) otLogNote(OT_LOG_REGION_API, __VA_ARGS__)
-#define otLogInfoApi(...) otLogInfo(OT_LOG_REGION_API, __VA_ARGS__)
-#define otLogDebgApi(...) otLogDebg(OT_LOG_REGION_API, __VA_ARGS__)
-#else
-#define otLogCritApi(...)
-#define otLogWarnApi(...)
-#define otLogNoteApi(...)
-#define otLogInfoApi(...)
-#define otLogDebgApi(...)
-#endif
-
-/**
- * @def otLogCritMeshCoP
- *
- * This function generates a log with level critical for the MeshCoP region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- *
- */
-
-/**
- * @def otLogWarnMeshCoP
- *
- * This function generates a log with level warning for the MeshCoP region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogNoteMeshCoP
- *
- * This function generates a log with level note for the MeshCoP region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogInfoMeshCoP
- *
- * This function generates a log with level info for the MeshCoP region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogDebgMeshCoP
- *
- * This function generates a log with level debug for the MeshCoP region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_MESHCOP
-#define otLogCritMeshCoP(...) otLogCrit(OT_LOG_REGION_MESH_COP, __VA_ARGS__)
-#define otLogWarnMeshCoP(...) otLogWarn(OT_LOG_REGION_MESH_COP, __VA_ARGS__)
-#define otLogNoteMeshCoP(...) otLogNote(OT_LOG_REGION_MESH_COP, __VA_ARGS__)
-#define otLogInfoMeshCoP(...) otLogInfo(OT_LOG_REGION_MESH_COP, __VA_ARGS__)
-#define otLogDebgMeshCoP(...) otLogDebg(OT_LOG_REGION_MESH_COP, __VA_ARGS__)
-#else
-#define otLogCritMeshCoP(...)
-#define otLogWarnMeshCoP(...)
-#define otLogNoteMeshCoP(...)
-#define otLogInfoMeshCoP(...)
-#define otLogDebgMeshCoP(...)
-#endif
-
-#define otLogCritMbedTls(...) otLogCritMeshCoP(__VA_ARGS__)
-#define otLogWarnMbedTls(...) otLogWarnMeshCoP(__VA_ARGS__)
-#define otLogNoteMbedTls(...) otLogNoteMeshCoP(__VA_ARGS__)
-#define otLogInfoMbedTls(...) otLogInfoMeshCoP(__VA_ARGS__)
-#define otLogDebgMbedTls(...) otLogDebgMeshCoP(__VA_ARGS__)
-
-/**
- * @def otLogCritBr
- *
- * This function generates a log with level critical for the BR region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogWarnBr
- *
- * This function generates a log with level warning for the BR region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogNoteBr
- *
- * This function generates a log with level note for the BR region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogInfoBr
- *
- * This function generates a log with level info for the BR region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogDebgBr
- *
- * This function generates a log with level debug for the BR region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_BR
-#define otLogCritBr(...) otLogCrit(OT_LOG_REGION_BR, __VA_ARGS__)
-#define otLogWarnBr(...) otLogWarn(OT_LOG_REGION_BR, __VA_ARGS__)
-#define otLogNoteBr(...) otLogNote(OT_LOG_REGION_BR, __VA_ARGS__)
-#define otLogInfoBr(...) otLogInfo(OT_LOG_REGION_BR, __VA_ARGS__)
-#define otLogDebgBr(...) otLogDebg(OT_LOG_REGION_BR, __VA_ARGS__)
-#else
-#define otLogCritBr(...)
-#define otLogWarnBr(...)
-#define otLogNoteBr(...)
-#define otLogInfoBr(...)
-#define otLogDebgBr(...)
-#endif
-
-/**
- * @def otLogCritMle
- *
- * This function generates a log with level critical for the MLE region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogWarnMle
- *
- * This function generates a log with level warning for the MLE region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogNoteMle
- *
- * This function generates a log with level note for the MLE region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogInfoMle
- *
- * This function generates a log with level info for the MLE region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogDebgMle
- *
- * This function generates a log with level debug for the MLE region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- *
- */
-#if OPENTHREAD_CONFIG_LOG_MLE
-#define otLogCritMle(...) otLogCrit(OT_LOG_REGION_MLE, __VA_ARGS__)
-#define otLogWarnMle(...) otLogWarn(OT_LOG_REGION_MLE, __VA_ARGS__)
-#define otLogNoteMle(...) otLogNote(OT_LOG_REGION_MLE, __VA_ARGS__)
-#define otLogInfoMle(...) otLogInfo(OT_LOG_REGION_MLE, __VA_ARGS__)
-#define otLogDebgMle(...) otLogDebg(OT_LOG_REGION_MLE, __VA_ARGS__)
-#else
-#define otLogCritMle(...)
-#define otLogWarnMle(...)
-#define otLogNoteMle(...)
-#define otLogInfoMle(...)
-#define otLogDebgMle(...)
-#endif
-
-/**
- * @def otLogCritArp
- *
- * This function generates a log with level critical for the EID-to-RLOC mapping region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogWarnArp
- *
- * This function generates a log with level warning for the EID-to-RLOC mapping region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogNoteArp
- *
- * This function generates a log with level note for the EID-to-RLOC mapping region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogInfoArp
- *
- * This function generates a log with level info for the EID-to-RLOC mapping region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogDebgArp
- *
- * This function generates a log with level debug for the EID-to-RLOC mapping region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_ARP
-#define otLogCritArp(...) otLogCrit(OT_LOG_REGION_ARP, __VA_ARGS__)
-#define otLogWarnArp(...) otLogWarn(OT_LOG_REGION_ARP, __VA_ARGS__)
-#define otLogNoteArp(...) otLogNote(OT_LOG_REGION_ARP, __VA_ARGS__)
-#define otLogInfoArp(...) otLogInfo(OT_LOG_REGION_ARP, __VA_ARGS__)
-#define otLogDebgArp(...) otLogDebg(OT_LOG_REGION_ARP, __VA_ARGS__)
-#else
-#define otLogCritArp(...)
-#define otLogWarnArp(...)
-#define otLogNoteArp(...)
-#define otLogInfoArp(...)
-#define otLogDebgArp(...)
-#endif
-
-/**
- * @def otLogCritBbr
- *
- * This function generates a log with level critical for the Backbone Router (BBR) region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogWarnBbr
- *
- * This function generates a log with level warning for the Backbone Router (BBR) region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogNoteBbr
- *
- * This function generates a log with level note for the Backbone Router (BBR) region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogInfoBbr
- *
- * This function generates a log with level info for the Backbone Router (BBR) region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogDebgBbr
- *
- * This function generates a log with level debug for the Backbone Router (BBR) region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_BBR
-#define otLogCritBbr(...) otLogCrit(OT_LOG_REGION_BBR, __VA_ARGS__)
-#define otLogWarnBbr(...) otLogWarn(OT_LOG_REGION_BBR, __VA_ARGS__)
-#define otLogNoteBbr(...) otLogNote(OT_LOG_REGION_BBR, __VA_ARGS__)
-#define otLogInfoBbr(...) otLogInfo(OT_LOG_REGION_BBR, __VA_ARGS__)
-#define otLogDebgBbr(...) otLogDebg(OT_LOG_REGION_BBR, __VA_ARGS__)
-#else
-#define otLogCritBbr(...)
-#define otLogWarnBbr(...)
-#define otLogNoteBbr(...)
-#define otLogInfoBbr(...)
-#define otLogDebgBbr(...)
-#endif
-
-/**
- * @def otLogCritMlr
- *
- * This function generates a log with level critical for the Multicast Listener Registration (MLR) region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogWarnMlr
- *
- * This function generates a log with level warning for the Multicast Listener Registration (MLR) region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogNoteMlr
- *
- * This function generates a log with level note for the Multicast Listener Registration (MLR) region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogInfoMlr
- *
- * This function generates a log with level info for the Multicast Listener Registration (MLR) region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogDebgMlr
- *
- * This function generates a log with level debug for the Multicast Listener Registration (MLR) region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_MLR
-#define otLogCritMlr(...) otLogCrit(OT_LOG_REGION_MLR, __VA_ARGS__)
-#define otLogWarnMlr(...) otLogWarn(OT_LOG_REGION_MLR, __VA_ARGS__)
-#define otLogNoteMlr(...) otLogNote(OT_LOG_REGION_MLR, __VA_ARGS__)
-#define otLogInfoMlr(...) otLogInfo(OT_LOG_REGION_MLR, __VA_ARGS__)
-#define otLogDebgMlr(...) otLogDebg(OT_LOG_REGION_MLR, __VA_ARGS__)
-#else
-#define otLogCritMlr(...)
-#define otLogWarnMlr(...)
-#define otLogNoteMlr(...)
-#define otLogInfoMlr(...)
-#define otLogDebgMlr(...)
-#endif
-
-/**
- * @def otLogCritNetData
- *
- * This function generates a log with level critical for the Network Data region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogWarnNetData
- *
- * This function generates a log with level warning for the Network Data region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogNoteNetData
- *
- * This function generates a log with level note for the Network Data region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogInfoNetData
- *
- * This function generates a log with level info for the Network Data region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogDebgNetData
- *
- * This function generates a log with level debug for the Network Data region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_NETDATA
-#define otLogCritNetData(...) otLogCrit(OT_LOG_REGION_NET_DATA, __VA_ARGS__)
-#define otLogWarnNetData(...) otLogWarn(OT_LOG_REGION_NET_DATA, __VA_ARGS__)
-#define otLogNoteNetData(...) otLogNote(OT_LOG_REGION_NET_DATA, __VA_ARGS__)
-#define otLogInfoNetData(...) otLogInfo(OT_LOG_REGION_NET_DATA, __VA_ARGS__)
-#define otLogDebgNetData(...) otLogDebg(OT_LOG_REGION_NET_DATA, __VA_ARGS__)
-#else
-#define otLogCritNetData(...)
-#define otLogWarnNetData(...)
-#define otLogNoteNetData(...)
-#define otLogInfoNetData(...)
-#define otLogDebgNetData(...)
-#endif
-
-/**
- * @def otLogCritIcmp
- *
- * This function generates a log with level critical for the ICMPv6 region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogWarnIcmp
- *
- * This function generates a log with level warning for the ICMPv6 region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogNoteIcmp
- *
- * This function generates a log with level note for the ICMPv6 region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogInfoIcmp
- *
- * This function generates a log with level info for the ICMPv6 region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogDebgIcmp
- *
- * This function generates a log with level debug for the ICMPv6 region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_ICMP
-#define otLogCritIcmp(...) otLogCrit(OT_LOG_REGION_ICMP, __VA_ARGS__)
-#define otLogWarnIcmp(...) otLogWarn(OT_LOG_REGION_ICMP, __VA_ARGS__)
-#define otLogNoteIcmp(...) otLogNote(OT_LOG_REGION_ICMP, __VA_ARGS__)
-#define otLogInfoIcmp(...) otLogInfo(OT_LOG_REGION_ICMP, __VA_ARGS__)
-#define otLogDebgIcmp(...) otLogDebg(OT_LOG_REGION_ICMP, __VA_ARGS__)
-#else
-#define otLogCritIcmp(...)
-#define otLogWarnIcmp(...)
-#define otLogNoteIcmp(...)
-#define otLogInfoIcmp(...)
-#define otLogDebgIcmp(...)
-#endif
-
-/**
- * @def otLogCritIp6
- *
- * This function generates a log with level critical for the IPv6 region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogWarnIp6
- *
- * This function generates a log with level warning for the IPv6 region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogNoteIp6
- *
- * This function generates a log with level note for the IPv6 region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogInfoIp6
- *
- * This function generates a log with level info for the IPv6 region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogDebgIp6
- *
- * This function generates a log with level debug for the IPv6 region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_IP6
-#define otLogCritIp6(...) otLogCrit(OT_LOG_REGION_IP6, __VA_ARGS__)
-#define otLogWarnIp6(...) otLogWarn(OT_LOG_REGION_IP6, __VA_ARGS__)
-#define otLogNoteIp6(...) otLogNote(OT_LOG_REGION_IP6, __VA_ARGS__)
-#define otLogInfoIp6(...) otLogInfo(OT_LOG_REGION_IP6, __VA_ARGS__)
-#define otLogDebgIp6(...) otLogDebg(OT_LOG_REGION_IP6, __VA_ARGS__)
-#else
-#define otLogCritIp6(...)
-#define otLogWarnIp6(...)
-#define otLogNoteIp6(...)
-#define otLogInfoIp6(...)
-#define otLogDebgIp6(...)
-#endif
-
-/**
- * @def otLogCritTcp
- *
- * This function generates a log with level critical for the TCP region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogWarnTcp
- *
- * This function generates a log with level warning for the TCP region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogNoteTcp
- *
- * This function generates a log with level note for the TCP region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogInfoTcp
- *
- * This function generates a log with level info for the TCP region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogDebgTcp
- *
- * This function generates a log with level debug for the TCP region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_TCP
-#define otLogCritTcp(...) otLogCrit(OT_LOG_REGION_TCP, __VA_ARGS__)
-#define otLogWarnTcp(...) otLogWarn(OT_LOG_REGION_TCP, __VA_ARGS__)
-#define otLogNoteTcp(...) otLogNote(OT_LOG_REGION_TCP, __VA_ARGS__)
-#define otLogInfoTcp(...) otLogInfo(OT_LOG_REGION_TCP, __VA_ARGS__)
-#define otLogDebgTcp(...) otLogDebg(OT_LOG_REGION_TCP, __VA_ARGS__)
-#else
-#define otLogCritTcp(...)
-#define otLogWarnTcp(...)
-#define otLogNoteTcp(...)
-#define otLogInfoTcp(...)
-#define otLogDebgTcp(...)
-#endif
-
-/**
- * @def otLogCritMac
- *
- * This function generates a log with level critical for the MAC region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogWarnMac
- *
- * This function generates a log with level warning for the MAC region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogNoteMac
- *
- * This function generates a log with level note for the MAC region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogInfoMac
- *
- * This function generates a log with level info for the MAC region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogDebgMac
- *
- * This function generates a log with level debug for the MAC region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogMac
- *
- * This function generates a log with a given log level for the MAC region.
- *
- * @param[in]  aLogLevel  A log level.
- * @param[in]  aFormat    A pointer to the format string.
- * @param[in]  ...        Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_MAC
-#define otLogCritMac(...) otLogCrit(OT_LOG_REGION_MAC, __VA_ARGS__)
-#define otLogWarnMac(...) otLogWarn(OT_LOG_REGION_MAC, __VA_ARGS__)
-#define otLogNoteMac(...) otLogNote(OT_LOG_REGION_MAC, __VA_ARGS__)
-#define otLogInfoMac(...) otLogInfo(OT_LOG_REGION_MAC, __VA_ARGS__)
-#define otLogDebgMac(...) otLogDebg(OT_LOG_REGION_MAC, __VA_ARGS__)
-void otLogMac(otLogLevel aLogLevel, const char *aFormat, ...);
-#else
-#define otLogCritMac(...)
-#define otLogWarnMac(...)
-#define otLogNoteMac(...)
-#define otLogInfoMac(...)
-#define otLogDebgMac(...)
-#define otLogMac(aLogLevel, ...)
-#endif
-
-/**
- * @def otLogCritCore
- *
- * This function generates a log with level critical for the Core region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogWarnCore
- *
- * This function generates a log with level warning for the Core region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogNoteCore
- *
- * This function generates a log with level note for the Core region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogInfoCore
- *
- * This function generates a log with level info for the Core region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogDebgCore
- *
- * This function generates a log with level debug for the Core region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_CORE
-#define otLogCritCore(...) otLogCrit(OT_LOG_REGION_CORE, __VA_ARGS__)
-#define otLogWarnCore(...) otLogWarn(OT_LOG_REGION_CORE, __VA_ARGS__)
-#define otLogNoteCore(...) otLogNote(OT_LOG_REGION_CORE, __VA_ARGS__)
-#define otLogInfoCore(...) otLogInfo(OT_LOG_REGION_CORE, __VA_ARGS__)
-#define otLogDebgCore(...) otLogDebg(OT_LOG_REGION_CORE, __VA_ARGS__)
-#else
-#define otLogCritCore(...)
-#define otLogWarnCore(...)
-#define otLogInfoCore(...)
-#define otLogDebgCore(...)
-#endif
-
-/**
- * @def otLogCritMem
- *
- * This function generates a log with level critical for the memory region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogWarnMem
- *
- * This function generates a log with level warning for the memory region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogNoteMem
- *
- * This function generates a log with level note for the memory region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogInfoMem
- *
- * This function generates a log with level info for the memory region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogDebgMem
- *
- * This function generates a log with level debug for the memory region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_MEM
-#define otLogCritMem(...) otLogCrit(OT_LOG_REGION_MEM, __VA_ARGS__)
-#define otLogWarnMem(...) otLogWarn(OT_LOG_REGION_MEM, __VA_ARGS__)
-#define otLogNoteMem(...) otLogNote(OT_LOG_REGION_MEM, __VA_ARGS__)
-#define otLogInfoMem(...) otLogInfo(OT_LOG_REGION_MEM, __VA_ARGS__)
-#define otLogDebgMem(...) otLogDebg(OT_LOG_REGION_MEM, __VA_ARGS__)
-#else
-#define otLogCritMem(...)
-#define otLogWarnMem(...)
-#define otLogNoteMem(...)
-#define otLogInfoMem(...)
-#define otLogDebgMem(...)
-#endif
-
-/**
- * @def otLogCritUtil
- *
- * This function generates a log with level critical for the Util region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogWarnUtil
- *
- * This function generates a log with level warning for the Util region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogNoteUtil
- *
- * This function generates a log with level note for the Util region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogInfoUtil
- *
- * This function generates a log with level info for the Util region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogDebgUtil
- *
- * This function generates a log with level debug for the Util region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_UTIL
-#define otLogCritUtil(...) otLogCrit(OT_LOG_REGION_UTIL, __VA_ARGS__)
-#define otLogWarnUtil(...) otLogWarn(OT_LOG_REGION_UTIL, __VA_ARGS__)
-#define otLogNoteUtil(...) otLogNote(OT_LOG_REGION_UTIL, __VA_ARGS__)
-#define otLogInfoUtil(...) otLogInfo(OT_LOG_REGION_UTIL, __VA_ARGS__)
-#define otLogDebgUtil(...) otLogDebg(OT_LOG_REGION_UTIL, __VA_ARGS__)
-#else
-#define otLogCritUtil(...)
-#define otLogWarnUtil(...)
-#define otLogNoteUtil(...)
-#define otLogInfoUtil(...)
-#define otLogDebgUtil(...)
-#endif
-
-/**
- * @def otLogCritNetDiag
- *
- * This function generates a log with level critical for the NETDIAG region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogWarnNetDiag
- *
- * This function generates a log with level warning for the NETDIAG region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogNoteNetDiag
- *
- * This function generates a log with level note for the NETDIAG region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogInfoNetDiag
- *
- * This function generates a log with level info for the NETDIAG region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogDebgNetDiag
- *
- * This function generates a log with level debug for the NETDIAG region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_NETDIAG
-#define otLogCritNetDiag(...) otLogCrit(OT_LOG_REGION_NET_DIAG, __VA_ARGS__)
-#define otLogWarnNetDiag(...) otLogWarn(OT_LOG_REGION_NET_DIAG, __VA_ARGS__)
-#define otLogNoteNetDiag(...) otLogNote(OT_LOG_REGION_NET_DIAG, __VA_ARGS__)
-#define otLogInfoNetDiag(...) otLogInfo(OT_LOG_REGION_NET_DIAG, __VA_ARGS__)
-#define otLogDebgNetDiag(...) otLogDebg(OT_LOG_REGION_NET_DIAG, __VA_ARGS__)
-#else
-#define otLogCritNetDiag(...)
-#define otLogWarnNetDiag(...)
-#define otLogNoteNetDiag(...)
-#define otLogInfoNetDiag(...)
-#define otLogDebgNetDiag(...)
-#endif
-
-/**
- * @def otLogCert
- *
- * This function generates a log with level none for the certification test.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- *
- */
-#if !OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
-#define otLogCertMeshCoP(...)
-#else
-void otLogCertMeshCoP(const char *aFormat, ...);
-#endif
-
-/**
- * @def otLogCritCli
- *
- * This function generates a log with level critical for the CLI region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogWarnCli
- *
- * This function generates a log with level warning for the CLI region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogNoteCli
- *
- * This function generates a log with level note for the CLI region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogInfoCli
- *
- * This function generates a log with level info for the CLI region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogDebgCli
- *
- * This function generates a log with level debug for the CLI region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_CLI
-
-#define otLogCritCli(...) otLogCrit(OT_LOG_REGION_CLI, __VA_ARGS__)
-#define otLogWarnCli(...) otLogWarn(OT_LOG_REGION_CLI, __VA_ARGS__)
-#define otLogNoteCli(...) otLogNote(OT_LOG_REGION_CLI, __VA_ARGS__)
-#define otLogInfoCli(...) otLogInfo(OT_LOG_REGION_CLI, __VA_ARGS__)
-#define otLogDebgCli(...) otLogDebg(OT_LOG_REGION_CLI, __VA_ARGS__)
-#else
-#define otLogCritCli(...)
-#define otLogWarnCli(...)
-#define otLogNoteCli(...)
-#define otLogInfoCli(...)
-#define otLogDebgCli(...)
-#endif
-
-/**
- * @def otLogCritCoap
- *
- * This function generates a log with level critical for the CoAP region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogWarnCoap
- *
- * This function generates a log with level warning for the CoAP region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogNoteCoap
- *
- * This function generates a log with level note for the CoAP region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogInfoCoap
- *
- * This function generates a log with level info for the CoAP region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogDebgCoap
- *
- * This function generates a log with level debug for the CoAP region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_COAP
-#define otLogCritCoap(...) otLogCrit(OT_LOG_REGION_COAP, __VA_ARGS__)
-#define otLogWarnCoap(...) otLogWarn(OT_LOG_REGION_COAP, __VA_ARGS__)
-#define otLogNoteCoap(...) otLogNote(OT_LOG_REGION_COAP, __VA_ARGS__)
-#define otLogInfoCoap(...) otLogInfo(OT_LOG_REGION_COAP, __VA_ARGS__)
-#define otLogDebgCoap(...) otLogDebg(OT_LOG_REGION_COAP, __VA_ARGS__)
-#else
-#define otLogCritCoap(...)
-#define otLogWarnCoap(...)
-#define otLogNoteCoap(...)
-#define otLogInfoCoap(...)
-#define otLogDebgCoap(...)
-#endif
-
-/**
- * @def otLogCritDua
- *
- * This function generates a log with level critical for the Domain Unicast Address region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogWarnDua
- *
- * This function generates a log with level warning for the Domain Unicast Address region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogNoteDua
- *
- * This function generates a log with level note for the Domain Unicast Address region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogInfoDua
- *
- * This function generates a log with level info for the Domain Unicast Address region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogDebgDua
- *
- * This function generates a log with level debug for the Domain Unicast Address region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_DUA
-#define otLogCritDua(...) otLogCrit(OT_LOG_REGION_DUA, __VA_ARGS__)
-#define otLogWarnDua(...) otLogWarn(OT_LOG_REGION_DUA, __VA_ARGS__)
-#define otLogNoteDua(...) otLogNote(OT_LOG_REGION_DUA, __VA_ARGS__)
-#define otLogInfoDua(...) otLogInfo(OT_LOG_REGION_DUA, __VA_ARGS__)
-#define otLogDebgDua(...) otLogDebg(OT_LOG_REGION_DUA, __VA_ARGS__)
-#else
-#define otLogCritDua(...)
-#define otLogWarnDua(...)
-#define otLogNoteDua(...)
-#define otLogInfoDua(...)
-#define otLogDebgDua(...)
-#endif
-
-/**
- * @def otLogCritSrp
- *
- * This function generates a log with level critical for the Service Registration Protocol (SRP) region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogWarnSrp
- *
- * This function generates a log with level warning for the Service Registration Protocol (SRP) region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogNoteSrp
- *
- * This function generates a log with level note for the Service Registration Protocol (SRP) region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogInfoSrp
- *
- * This function generates a log with level info for the Service Registration Protocol (SRP) region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogDebgSrp
- *
- * This function generates a log with level debug for the Service Registration Protocol (SRP) region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_SRP
-#define otLogCritSrp(...) otLogCrit(OT_LOG_REGION_SRP, __VA_ARGS__)
-#define otLogWarnSrp(...) otLogWarn(OT_LOG_REGION_SRP, __VA_ARGS__)
-#define otLogNoteSrp(...) otLogNote(OT_LOG_REGION_SRP, __VA_ARGS__)
-#define otLogInfoSrp(...) otLogInfo(OT_LOG_REGION_SRP, __VA_ARGS__)
-#define otLogDebgSrp(...) otLogDebg(OT_LOG_REGION_SRP, __VA_ARGS__)
-#else
-#define otLogCritSrp(...)
-#define otLogWarnSrp(...)
-#define otLogNoteSrp(...)
-#define otLogInfoSrp(...)
-#define otLogDebgSrp(...)
-#endif
-
-/**
- * @def otLogCritDns
- *
- * This function generates a log with level critical for the DNS region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogWarnDns
- *
- * This function generates a log with level warning for the DNS region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogNoteDns
- *
- * This function generates a log with level note for the DNS region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogInfoDns
- *
- * This function generates a log with level info for the DNS region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogDebgDns
- *
- * This function generates a log with level debug for the DNS region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_DNS
-#define otLogCritDns(...) otLogCrit(OT_LOG_REGION_DNS, __VA_ARGS__)
-#define otLogWarnDns(...) otLogWarn(OT_LOG_REGION_DNS, __VA_ARGS__)
-#define otLogNoteDns(...) otLogNote(OT_LOG_REGION_DNS, __VA_ARGS__)
-#define otLogInfoDns(...) otLogInfo(OT_LOG_REGION_DNS, __VA_ARGS__)
-#define otLogDebgDns(...) otLogDebg(OT_LOG_REGION_DNS, __VA_ARGS__)
-#else
-#define otLogCritDns(...)
-#define otLogWarnDns(...)
-#define otLogNoteDns(...)
-#define otLogInfoDns(...)
-#define otLogDebgDns(...)
-#endif
-
-/**
- * @def otLogCritPlat
- *
- * This function generates a log with level critical for the Platform region.
- *
- * @param[in]  ...       Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogWarnPlat
- *
- * This function generates a log with level warning for the Platform region.
- *
- * @param[in]  ...       Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogNotePlat
- *
- * This function generates a log with level note for the Platform region.
- *
- * @param[in]  ...       Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogInfoPlat
- *
- * This function generates a log with level info for the Platform region.
- *
- * @param[in]  ...       Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogDebgPlat
- *
- * This function generates a log with level debug for the Platform region.
- *
- * @param[in]  ...       Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_PLATFORM
-#define otLogCritPlat(...) otLogCrit(OT_LOG_REGION_PLATFORM, __VA_ARGS__)
-#define otLogWarnPlat(...) otLogWarn(OT_LOG_REGION_PLATFORM, __VA_ARGS__)
-#define otLogNotePlat(...) otLogNote(OT_LOG_REGION_PLATFORM, __VA_ARGS__)
-#define otLogInfoPlat(...) otLogInfo(OT_LOG_REGION_PLATFORM, __VA_ARGS__)
-#define otLogDebgPlat(...) otLogDebg(OT_LOG_REGION_PLATFORM, __VA_ARGS__)
-#else
-#define otLogCritPlat(...)
-#define otLogWarnPlat(...)
-#define otLogNotePlat(...)
-#define otLogInfoPlat(...)
-#define otLogDebgPlat(...)
-#endif
-
-/**
- * @def otLogOtns
- *
- * This function generates a log with level none for the Core region,
- * and is specifically for OTNS visualization use.
- *
- * @param[in]  ...       Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_OTNS_ENABLE
-void otLogOtns(const char *aFormat, ...);
-#endif
-
-/**
- * @def otDumpCrit
- *
- * This function generates a memory dump with log level critical.
- *
- * @param[in]  aRegion      The log region.
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_CRIT
-#define otDumpCrit(aRegion, aId, aBuf, aLength) otDump(OT_LOG_LEVEL_CRIT, aRegion, aId, aBuf, aLength)
-#else
-#define otDumpCrit(aRegion, aId, aBuf, aLength)
-#endif
-
-/**
- * @def otDumpWarn
- *
- * This function generates a memory dump with log level warning.
- *
- * @param[in]  aRegion      The log region.
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_WARN
-#define otDumpWarn(aRegion, aId, aBuf, aLength) otDump(OT_LOG_LEVEL_WARN, aRegion, aId, aBuf, aLength)
-#else
-#define otDumpWarn(aRegion, aId, aBuf, aLength)
-#endif
-
-/**
- * @def otDumpNote
- *
- * This function generates a memory dump with log level note.
- *
- * @param[in]  aRegion      The log region.
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_NOTE
-#define otDumpNote(aRegion, aId, aBuf, aLength) otDump(OT_LOG_LEVEL_NOTE, aRegion, aId, aBuf, aLength)
-#else
-#define otDumpNote(aRegion, aId, aBuf, aLength)
-#endif
-
-/**
- * @def otDumpInfo
- *
- * This function generates a memory dump with log level info.
- *
- * @param[in]  aRegion      The log region.
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO
-#define otDumpInfo(aRegion, aId, aBuf, aLength) otDump(OT_LOG_LEVEL_INFO, aRegion, aId, aBuf, aLength)
-#else
-#define otDumpInfo(aRegion, aId, aBuf, aLength)
-#endif
-
-/**
- * @def otDumpDebg
- *
- * This function generates a memory dump with log level debug.
- *
- * @param[in]  aRegion      The log region.
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_DEBG
-#define otDumpDebg(aRegion, aId, aBuf, aLength) otDump(OT_LOG_LEVEL_DEBG, aRegion, aId, aBuf, aLength)
-#else
-#define otDumpDebg(aRegion, aId, aBuf, aLength)
-#endif
-
-/**
- * @def otDumpCritNetData
- *
- * This function generates a memory dump with log level debug and region Network Data.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpWarnNetData
- *
- * This function generates a memory dump with log level warning and region Network Data.
- *
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpNoteNetData
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpInfoNetData
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpDebgNetData
- *
- * This function generates a memory dump with log level debug and region Network Data.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_NETDATA
-#define otDumpCritNetData(aId, aBuf, aLength) otDumpCrit(OT_LOG_REGION_NET_DATA, aId, aBuf, aLength)
-#define otDumpWarnNetData(aId, aBuf, aLength) otDumpWarn(OT_LOG_REGION_NET_DATA, aId, aBuf, aLength)
-#define otDumpNoteNetData(aId, aBuf, aLength) otDumpNote(OT_LOG_REGION_NET_DATA, aId, aBuf, aLength)
-#define otDumpInfoNetData(aId, aBuf, aLength) otDumpInfo(OT_LOG_REGION_NET_DATA, aId, aBuf, aLength)
-#define otDumpDebgNetData(aId, aBuf, aLength) otDumpDebg(OT_LOG_REGION_NET_DATA, aId, aBuf, aLength)
-#else
-#define otDumpCritNetData(aId, aBuf, aLength)
-#define otDumpWarnNetData(aId, aBuf, aLength)
-#define otDumpNoteNetData(aId, aBuf, aLength)
-#define otDumpInfoNetData(aId, aBuf, aLength)
-#define otDumpDebgNetData(aId, aBuf, aLength)
-#endif
-
-/**
- * @def otDumpCritMle
- *
- * This function generates a memory dump with log level debug and region MLE.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpWarnMle
- *
- * This function generates a memory dump with log level warning and region MLE.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpNoteMle
- *
- * This function generates a memory dump with log level note and region MLE.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpInfoMle
- *
- * This function generates a memory dump with log level info and region MLE.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpDebgMle
- *
- * This function generates a memory dump with log level debug and region MLE.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_MLE
-#define otDumpCritMle(aId, aBuf, aLength) otDumpCrit(OT_LOG_REGION_MLE, aId, aBuf, aLength)
-#define otDumpWarnMle(aId, aBuf, aLength) otDumpWarn(OT_LOG_REGION_MLE, aId, aBuf, aLength)
-#define otDumpNoteMle(aId, aBuf, aLength) otDumpNote(OT_LOG_REGION_MLE, aId, aBuf, aLength)
-#define otDumpInfoMle(aId, aBuf, aLength) otDumpInfo(OT_LOG_REGION_MLE, aId, aBuf, aLength)
-#define otDumpDebgMle(aId, aBuf, aLength) otDumpDebg(OT_LOG_REGION_MLE, aId, aBuf, aLength)
-#else
-#define otDumpCritMle(aId, aBuf, aLength)
-#define otDumpWarnMle(aId, aBuf, aLength)
-#define otDumpNoteMle(aId, aBuf, aLength)
-#define otDumpInfoMle(aId, aBuf, aLength)
-#define otDumpDebgMle(aId, aBuf, aLength)
-#endif
-
-/**
- * @def otDumpCritArp
- *
- * This function generates a memory dump with log level debug and region EID-to-RLOC mapping.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpWarnArp
- *
- * This function generates a memory dump with log level warning and region EID-to-RLOC mapping.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpNoteArp
- *
- * This function generates a memory dump with log level note and region EID-to-RLOC mapping.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpInfoArp
- *
- * This function generates a memory dump with log level info and region EID-to-RLOC mapping.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpDebgArp
- *
- * This function generates a memory dump with log level debug and region EID-to-RLOC mapping.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_ARP
-#define otDumpCritArp(aId, aBuf, aLength) otDumpCrit(OT_LOG_REGION_ARP, aId, aBuf, aLength)
-#define otDumpWarnArp(aId, aBuf, aLength) otDumpWarn(OT_LOG_REGION_ARP, aId, aBuf, aLength)
-#define otDumpNoteArp(aId, aBuf, aLength) otDumpNote(OT_LOG_REGION_ARP, aId, aBuf, aLength)
-#define otDumpInfoArp(aId, aBuf, aLength) otDumpInfo(OT_LOG_REGION_ARP, aId, aBuf, aLength)
-#define otDumpDebgArp(aId, aBuf, aLength) otDumpDebg(OT_LOG_REGION_ARP, aId, aBuf, aLength)
-#else
-#define otDumpCritArp(aId, aBuf, aLength)
-#define otDumpWarnArp(aId, aBuf, aLength)
-#define otDumpNoteArp(aId, aBuf, aLength)
-#define otDumpInfoArp(aId, aBuf, aLength)
-#define otDumpDebgArp(aId, aBuf, aLength)
-#endif
-
-/**
- * @def otDumpCritBbr
- *
- * This function generates a memory dump with log level critical and region Backbone Router (BBR).
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpWarnBbr
- *
- * This function generates a memory dump with log level warning and region Backbone Router (BBR).
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpNoteBbr
- *
- * This function generates a memory dump with log level note and region Backbone Router (BBR).
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpInfoBbr
- *
- * This function generates a memory dump with log level info and region Backbone Router (BBR).
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpDebgBbr
- *
- * This function generates a memory dump with log level debug and region Backbone Router (BBR).
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_BBR
-#define otDumpCritBbr(aId, aBuf, aLength) otDumpCrit(OT_LOG_REGION_BBR, aId, aBuf, aLength)
-#define otDumpWarnBbr(aId, aBuf, aLength) otDumpWarn(OT_LOG_REGION_BBR, aId, aBuf, aLength)
-#define otDumpNoteBbr(aId, aBuf, aLength) otDumpNote(OT_LOG_REGION_BBR, aId, aBuf, aLength)
-#define otDumpInfoBbr(aId, aBuf, aLength) otDumpInfo(OT_LOG_REGION_BBR, aId, aBuf, aLength)
-#define otDumpDebgBbr(aId, aBuf, aLength) otDumpDebg(OT_LOG_REGION_BBR, aId, aBuf, aLength)
-#else
-#define otDumpCritBbr(aId, aBuf, aLength)
-#define otDumpWarnBbr(aId, aBuf, aLength)
-#define otDumpNoteBbr(aId, aBuf, aLength)
-#define otDumpInfoBbr(aId, aBuf, aLength)
-#define otDumpDebgBbr(aId, aBuf, aLength)
-#endif
-
-/**
- * @def otDumpCritMlr
- *
- * This function generates a memory dump with log level critical and region Multicast Listener Registration (MLR).
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpWarnMlr
- *
- * This function generates a memory dump with log level warning and region Multicast Listener Registration (MLR).
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpNoteMlr
- *
- * This function generates a memory dump with log level note and region Multicast Listener Registration (MLR).
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpInfoMlr
- *
- * This function generates a memory dump with log level info and region Multicast Listener Registration (MLR).
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpDebgMlr
- *
- * This function generates a memory dump with log level debug and region Multicast Listener Registration (MLR).
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_MLR
-#define otDumpCritMlr(aId, aBuf, aLength) otDumpCrit(OT_LOG_REGION_MLR, aId, aBuf, aLength)
-#define otDumpWarnMlr(aId, aBuf, aLength) otDumpWarn(OT_LOG_REGION_MLR, aId, aBuf, aLength)
-#define otDumpNoteMlr(aId, aBuf, aLength) otDumpNote(OT_LOG_REGION_MLR, aId, aBuf, aLength)
-#define otDumpInfoMlr(aId, aBuf, aLength) otDumpInfo(OT_LOG_REGION_MLR, aId, aBuf, aLength)
-#define otDumpDebgMlr(aId, aBuf, aLength) otDumpDebg(OT_LOG_REGION_MLR, aId, aBuf, aLength)
-#else
-#define otDumpCritMlr(aId, aBuf, aLength)
-#define otDumpWarnMlr(aId, aBuf, aLength)
-#define otDumpNoteMlr(aId, aBuf, aLength)
-#define otDumpInfoMlr(aId, aBuf, aLength)
-#define otDumpDebgMlr(aId, aBuf, aLength)
-#endif
-
-/**
- * @def otDumpCritIcmp
- *
- * This function generates a memory dump with log level debug and region ICMPv6.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpWarnIcmp
- *
- * This function generates a memory dump with log level warning and region ICMPv6.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpNoteIcmp
- *
- * This function generates a memory dump with log level note and region ICMPv6.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpInfoIcmp
- *
- * This function generates a memory dump with log level info and region ICMPv6.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpDebgIcmp
- *
- * This function generates a memory dump with log level debug and region ICMPv6.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_ICMP
-#define otDumpCritIcmp(aId, aBuf, aLength) otDumpCrit(OT_LOG_REGION_ICMP, aId, aBuf, aLength)
-#define otDumpWarnIcmp(aId, aBuf, aLength) otDumpWarn(OT_LOG_REGION_ICMP, aId, aBuf, aLength)
-#define otDumpNoteIcmp(aId, aBuf, aLength) otDumpNote(OT_LOG_REGION_ICMP, aId, aBuf, aLength)
-#define otDumpInfoIcmp(aId, aBuf, aLength) otDumpInfo(OT_LOG_REGION_ICMP, aId, aBuf, aLength)
-#define otDumpDebgIcmp(aId, aBuf, aLength) otDumpDebg(OT_LOG_REGION_ICMP, aId, aBuf, aLength)
-#else
-#define otDumpCritIcmp(aId, aBuf, aLength)
-#define otDumpWarnIcmp(aId, aBuf, aLength)
-#define otDumpNoteIcmp(aId, aBuf, aLength)
-#define otDumpInfoIcmp(aId, aBuf, aLength)
-#define otDumpDebgIcmp(aId, aBuf, aLength)
-#endif
-
-/**
- * @def otDumpCritIp6
- *
- * This function generates a memory dump with log level debug and region IPv6.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpWarnIp6
- *
- * This function generates a memory dump with log level warning and region IPv6.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpNoteIp6
- *
- * This function generates a memory dump with log level note and region IPv6.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpInfoIp6
- *
- * This function generates a memory dump with log level info and region IPv6.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpDebgIp6
- *
- * This function generates a memory dump with log level debug and region IPv6.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_IP6
-#define otDumpCritIp6(aId, aBuf, aLength) otDumpCrit(OT_LOG_REGION_IP6, aId, aBuf, aLength)
-#define otDumpWarnIp6(aId, aBuf, aLength) otDumpWarn(OT_LOG_REGION_IP6, aId, aBuf, aLength)
-#define otDumpNoteIp6(aId, aBuf, aLength) otDumpNote(OT_LOG_REGION_IP6, aId, aBuf, aLength)
-#define otDumpInfoIp6(aId, aBuf, aLength) otDumpInfo(OT_LOG_REGION_IP6, aId, aBuf, aLength)
-#define otDumpDebgIp6(aId, aBuf, aLength) otDumpDebg(OT_LOG_REGION_IP6, aId, aBuf, aLength)
-#else
-#define otDumpCritIp6(aId, aBuf, aLength)
-#define otDumpWarnIp6(aId, aBuf, aLength)
-#define otDumpNoteIp6(aId, aBuf, aLength)
-#define otDumpInfoIp6(aId, aBuf, aLength)
-#define otDumpDebgIp6(aId, aBuf, aLength)
-#endif
-
-/**
- * @def otDumpCritTcp
- *
- * This function generates a memory dump with log level debug and region TCP.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpWartTcp
- *
- * This function generates a memory dump with log level warning and region TCP.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpNottTcp
- *
- * This function generates a memory dump with log level note and region TCP.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpInftTcp
- *
- * This function generates a memory dump with log level info and region TCP.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpDebtTcp
- *
- * This function generates a memory dump with log level debug and region TCP.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_TCP
-#define otDumpCritTcp(aId, aBuf, aLength) otDumpCrit(OT_LOG_REGION_TCP, aId, aBuf, aLength)
-#define otDumpWarnTcp(aId, aBuf, aLength) otDumpWarn(OT_LOG_REGION_TCP, aId, aBuf, aLength)
-#define otDumpNoteTcp(aId, aBuf, aLength) otDumpNote(OT_LOG_REGION_TCP, aId, aBuf, aLength)
-#define otDumpInfoTcp(aId, aBuf, aLength) otDumpInfo(OT_LOG_REGION_TCP, aId, aBuf, aLength)
-#define otDumpDebgTcp(aId, aBuf, aLength) otDumpDebg(OT_LOG_REGION_TCP, aId, aBuf, aLength)
-#else
-#define otDumpCritTcp(aId, aBuf, aLength)
-#define otDumpWarnTcp(aId, aBuf, aLength)
-#define otDumpNoteTcp(aId, aBuf, aLength)
-#define otDumpInfoTcp(aId, aBuf, aLength)
-#define otDumpDebgTcp(aId, aBuf, aLength)
-#endif
-
-/**
- * @def otDumpCritMac
- *
- * This function generates a memory dump with log level debug and region MAC.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpWarnMac
- *
- * This function generates a memory dump with log level warning and region MAC.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpNoteMac
- *
- * This function generates a memory dump with log level note and region MAC.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpInfoMac
- *
- * This function generates a memory dump with log level info and region MAC.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpDebgMac
- *
- * This function generates a memory dump with log level debug and region MAC.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_MAC
-void otDumpMacFrame(otLogLevel aLogLevel, const char *aId, const void *aBuf, size_t aLength);
-#define otDumpCritMac(aId, aBuf, aLength) otDumpMacFrame(OT_LOG_LEVEL_CRIT, aId, aBuf, aLength)
-#define otDumpWarnMac(aId, aBuf, aLength) otDumpMacFrame(OT_LOG_LEVEL_WARN, aId, aBuf, aLength)
-#define otDumpNoteMac(aId, aBuf, aLength) otDumpMacFrame(OT_LOG_LEVEL_NOTE, aId, aBuf, aLength)
-#define otDumpInfoMac(aId, aBuf, aLength) otDumpMacFrame(OT_LOG_LEVEL_INFO, aId, aBuf, aLength)
-#define otDumpDebgMac(aId, aBuf, aLength) otDumpMacFrame(OT_LOG_LEVEL_DEBG, aId, aBuf, aLength)
-#else
-#define otDumpCritMac(aId, aBuf, aLength)
-#define otDumpWarnMac(aId, aBuf, aLength)
-#define otDumpNoteMac(aId, aBuf, aLength)
-#define otDumpInfoMac(aId, aBuf, aLength)
-#define otDumpDebgMac(aId, aBuf, aLength)
-#endif
-
-/**
- * @def otDumpCritCore
- *
- * This function generates a memory dump with log level debug and region Core.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpWarnCore
- *
- * This function generates a memory dump with log level warning and region Core.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpNoteCore
- *
- * This function generates a memory dump with log level note and region Core.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpInfoCore
- *
- * This function generates a memory dump with log level info and region Core.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpDebgCore
- *
- * This function generates a memory dump with log level debug and region Core.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_CORE
-#define otDumpCritCore(aId, aBuf, aLength) otDumpCrit(OT_LOG_REGION_CORE, aId, aBuf, aLength)
-#define otDumpWarnCore(aId, aBuf, aLength) otDumpWarn(OT_LOG_REGION_CORE, aId, aBuf, aLength)
-#define otDumpNoteCore(aId, aBuf, aLength) otDumpNote(OT_LOG_REGION_CORE, aId, aBuf, aLength)
-#define otDumpInfoCore(aId, aBuf, aLength) otDumpInfo(OT_LOG_REGION_CORE, aId, aBuf, aLength)
-#define otDumpDebgCore(aId, aBuf, aLength) otDumpDebg(OT_LOG_REGION_CORE, aId, aBuf, aLength)
-#else
-#define otDumpCritCore(aId, aBuf, aLength)
-#define otDumpWarnCore(aId, aBuf, aLength)
-#define otDumpNoteCore(aId, aBuf, aLength)
-#define otDumpInfoCore(aId, aBuf, aLength)
-#define otDumpDebgCore(aId, aBuf, aLength)
-#endif
-
-/**
- * @def otDumpCritDua
- *
- * This function generates a memory dump with log level critical and region Domain Unicast Address.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpWarnDua
- *
- * This function generates a memory dump with log level warning and region Domain Unicast Address.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpNoteDua
- *
- * This function generates a memory dump with log level note and region Domain Unicast Address.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpInfoDua
- *
- * This function generates a memory dump with log level info and region Domain Unicast Address.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpDebgDua
- *
- * This function generates a memory dump with log level debug and region Domain Unicast Address.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_DUA
-#define otDumpCritDua(aId, aBuf, aLength) otDumpCrit(OT_LOG_REGION_DUA, aId, aBuf, aLength)
-#define otDumpWarnDua(aId, aBuf, aLength) otDumpWarn(OT_LOG_REGION_DUA, aId, aBuf, aLength)
-#define otDumpNoteDua(aId, aBuf, aLength) otDumpNote(OT_LOG_REGION_DUA, aId, aBuf, aLength)
-#define otDumpInfoDua(aId, aBuf, aLength) otDumpInfo(OT_LOG_REGION_DUA, aId, aBuf, aLength)
-#define otDumpDebgDua(aId, aBuf, aLength) otDumpDebg(OT_LOG_REGION_DUA, aId, aBuf, aLength)
-#else
-#define otDumpCritDua(aId, aBuf, aLength)
-#define otDumpWarnDua(aId, aBuf, aLength)
-#define otDumpNoteDua(aId, aBuf, aLength)
-#define otDumpInfoDua(aId, aBuf, aLength)
-#define otDumpDebgDua(aId, aBuf, aLength)
-#endif
-
-/**
- * @def otDumpCritSrp
- *
- * This function generates a memory dump with log level critical and region Service Registration Protocol (SRP).
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpWarnSrp
- *
- * This function generates a memory dump with log level warning and region Service Registration Protocol (SRP).
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpNoteSrp
- *
- * This function generates a memory dump with log level note and region Service Registration Protocol (SRP).
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpInfoSrp
- *
- * This function generates a memory dump with log level info and region Service Registration Protocol (SRP).
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpDebgSrp
- *
- * This function generates a memory dump with log level debug and region Service Registration Protocol (SRP).
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_SRP
-#define otDumpCritSrp(aId, aBuf, aLength) otDumpCrit(OT_LOG_REGION_SRP, aId, aBuf, aLength)
-#define otDumpWarnSrp(aId, aBuf, aLength) otDumpWarn(OT_LOG_REGION_SRP, aId, aBuf, aLength)
-#define otDumpNoteSrp(aId, aBuf, aLength) otDumpNote(OT_LOG_REGION_SRP, aId, aBuf, aLength)
-#define otDumpInfoSrp(aId, aBuf, aLength) otDumpInfo(OT_LOG_REGION_SRP, aId, aBuf, aLength)
-#define otDumpDebgSrp(aId, aBuf, aLength) otDumpDebg(OT_LOG_REGION_SRP, aId, aBuf, aLength)
-#else
-#define otDumpCritSrp(aId, aBuf, aLength)
-#define otDumpWarnSrp(aId, aBuf, aLength)
-#define otDumpNoteSrp(aId, aBuf, aLength)
-#define otDumpInfoSrp(aId, aBuf, aLength)
-#define otDumpDebgSrp(aId, aBuf, aLength)
-#endif
-
-/**
- * @def otDumpCritBr
- *
- * This function generates a memory dump with log level critical and region Border Routing.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpWarnBr
- *
- * This function generates a memory dump with log level warning and region Border Routing.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpNoteBr
- *
- * This function generates a memory dump with log level note and region Border Routing.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpInfoBr
- *
- * This function generates a memory dump with log level info and region Border Routing.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpDebgBr
- *
- * This function generates a memory dump with log level debug and region Border Routing.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_BR
-#define otDumpCritBr(aId, aBuf, aLength) otDumpCrit(OT_LOG_REGION_BR, aId, aBuf, aLength)
-#define otDumpWarnBr(aId, aBuf, aLength) otDumpWarn(OT_LOG_REGION_BR, aId, aBuf, aLength)
-#define otDumpNoteBr(aId, aBuf, aLength) otDumpNote(OT_LOG_REGION_BR, aId, aBuf, aLength)
-#define otDumpInfoBr(aId, aBuf, aLength) otDumpInfo(OT_LOG_REGION_BR, aId, aBuf, aLength)
-#define otDumpDebgBr(aId, aBuf, aLength) otDumpDebg(OT_LOG_REGION_BR, aId, aBuf, aLength)
-#else
-#define otDumpCritBr(aId, aBuf, aLength)
-#define otDumpWarnBr(aId, aBuf, aLength)
-#define otDumpNoteBr(aId, aBuf, aLength)
-#define otDumpInfoBr(aId, aBuf, aLength)
-#define otDumpDebgBr(aId, aBuf, aLength)
-#endif
-
-/**
- * @def otDumpCritDns
- *
- * This function generates a memory dump with log level critical and region DNS.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpWarnDns
- *
- * This function generates a memory dump with log level warning and region DNS.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpNoteDns
- *
- * This function generates a memory dump with log level note and region DNS.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpInfoDns
- *
- * This function generates a memory dump with log level info and region DNS.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpDebgDns
- *
- * This function generates a memory dump with log level debug and region DNS.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_SRP
-#define otDumpCritDns(aId, aBuf, aLength) otDumpCrit(OT_LOG_REGION_SRP, aId, aBuf, aLength)
-#define otDumpWarnDns(aId, aBuf, aLength) otDumpWarn(OT_LOG_REGION_SRP, aId, aBuf, aLength)
-#define otDumpNoteDns(aId, aBuf, aLength) otDumpNote(OT_LOG_REGION_SRP, aId, aBuf, aLength)
-#define otDumpInfoDns(aId, aBuf, aLength) otDumpInfo(OT_LOG_REGION_SRP, aId, aBuf, aLength)
-#define otDumpDebgDns(aId, aBuf, aLength) otDumpDebg(OT_LOG_REGION_SRP, aId, aBuf, aLength)
-#else
-#define otDumpCritDns(aId, aBuf, aLength)
-#define otDumpWarnDns(aId, aBuf, aLength)
-#define otDumpNoteDns(aId, aBuf, aLength)
-#define otDumpInfoDns(aId, aBuf, aLength)
-#define otDumpDebgDns(aId, aBuf, aLength)
-#endif
-
-/**
- * @def otDumpCritMem
- *
- * This function generates a memory dump with log level debug and region memory.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpWarnMem
- *
- * This function generates a memory dump with log level warning and region memory.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpNoteMem
- *
- * This function generates a memory dump with log level note and region memory.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpInfoMem
- *
- * This function generates a memory dump with log level info and region memory.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpDebgMem
- *
- * This function generates a memory dump with log level debug and region memory.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_MEM
-#define otDumpCritMem(aId, aBuf, aLength) otDumpCrit(OT_LOG_REGION_MEM, aId, aBuf, aLength)
-#define otDumpWarnMem(aId, aBuf, aLength) otDumpWarn(OT_LOG_REGION_MEM, aId, aBuf, aLength)
-#define otDumpNoteMem(aId, aBuf, aLength) otDumpNote(OT_LOG_REGION_MEM, aId, aBuf, aLength)
-#define otDumpInfoMem(aId, aBuf, aLength) otDumpInfo(OT_LOG_REGION_MEM, aId, aBuf, aLength)
-#define otDumpDebgMem(aId, aBuf, aLength) otDumpDebg(OT_LOG_REGION_MEM, aId, aBuf, aLength)
-#else
-#define otDumpCritMem(aId, aBuf, aLength)
-#define otDumpWarnMem(aId, aBuf, aLength)
-#define otDumpNoteMem(aId, aBuf, aLength)
-#define otDumpInfoMem(aId, aBuf, aLength)
-#define otDumpDebgMem(aId, aBuf, aLength)
-#endif
-
-/**
- * @def otDumpCertMeshCoP
- *
- * This function generates a memory dump with log level none for the certification test.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
-#define otDumpCertMeshCoP(aId, aBuf, aLength) otDump(OT_LOG_LEVEL_NONE, OT_LOG_REGION_MESH_COP, aId, aBuf, aLength)
-#else
-#define otDumpCertMeshCoP(aId, aBuf, aLength)
-#endif
-
-/**
- * This function dumps bytes to the log in a human-readable fashion.
- *
- * @param[in]  aLogLevel    The log level.
- * @param[in]  aLogRegion   The log region.
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-void otDump(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aId, const void *aBuf, size_t aLength);
-
-#ifdef __cplusplus
-}
-#endif
-
 #endif // LOGGING_HPP_
diff --git a/src/core/common/message.cpp b/src/core/common/message.cpp
index 3ddc9f1..126d454 100644
--- a/src/core/common/message.cpp
+++ b/src/core/common/message.cpp
@@ -39,7 +39,7 @@
 #include "common/heap.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "net/checksum.hpp"
 #include "net/ip6.hpp"
 
@@ -55,6 +55,8 @@
 
 namespace ot {
 
+RegisterLogModule("Message");
+
 //---------------------------------------------------------------------------------------------------------------------
 // MessagePool
 
@@ -128,7 +130,7 @@
 exit:
     if (buffer == nullptr)
     {
-        otLogInfoMem("No available message buffer");
+        LogInfo("No available message buffer");
     }
 
     return buffer;
@@ -197,6 +199,15 @@
 }
 
 //---------------------------------------------------------------------------------------------------------------------
+// Message::Iterator
+
+void Message::Iterator::Advance(void)
+{
+    mItem = mNext;
+    mNext = NextMessage(mNext);
+}
+
+//---------------------------------------------------------------------------------------------------------------------
 // Message
 
 Error Message::ResizeMessage(uint16_t aLength)
@@ -335,6 +346,8 @@
     uint8_t        priority = static_cast<uint8_t>(aPriority);
     PriorityQueue *priorityQueue;
 
+    static_assert(kNumPriorities <= 4, "`Metadata::mPriority` as a 2-bit field cannot fit all `Priority` values");
+
     VerifyOrExit(priority < kNumPriorities, error = kErrorInvalidArgs);
 
     VerifyOrExit(IsInAQueue(), GetMetadata().mPriority = priority);
@@ -738,16 +751,6 @@
 //---------------------------------------------------------------------------------------------------------------------
 // MessageQueue
 
-MessageQueue::MessageQueue(void)
-{
-    SetTail(nullptr);
-}
-
-Message *MessageQueue::GetHead(void) const
-{
-    return (GetTail() == nullptr) ? nullptr : GetTail()->Next();
-}
-
 void MessageQueue::Enqueue(Message &aMessage, QueuePosition aPosition)
 {
     OT_ASSERT(!aMessage.IsInAQueue());
@@ -819,37 +822,37 @@
     }
 }
 
-void MessageQueue::GetInfo(uint16_t &aMessageCount, uint16_t &aBufferCount) const
+Message::Iterator MessageQueue::begin(void)
 {
-    aMessageCount = 0;
-    aBufferCount  = 0;
+    return Message::Iterator(GetHead());
+}
 
-    for (const Message *message = GetHead(); message != nullptr; message = message->GetNext())
+Message::ConstIterator MessageQueue::begin(void) const
+{
+    return Message::ConstIterator(GetHead());
+}
+
+void MessageQueue::GetInfo(Info &aInfo) const
+{
+    for (const Message &message : *this)
     {
-        aMessageCount++;
-        aBufferCount += message->GetBufferCount();
+        aInfo.mNumMessages++;
+        aInfo.mNumBuffers += message.GetBufferCount();
+        aInfo.mTotalBytes += message.GetLength();
     }
 }
 
 //---------------------------------------------------------------------------------------------------------------------
 // PriorityQueue
 
-PriorityQueue::PriorityQueue(void)
-{
-    for (Message *&tail : mTails)
-    {
-        tail = nullptr;
-    }
-}
-
-Message *PriorityQueue::FindFirstNonNullTail(Message::Priority aStartPriorityLevel) const
+const Message *PriorityQueue::FindFirstNonNullTail(Message::Priority aStartPriorityLevel) const
 {
     // Find the first non-`nullptr` tail starting from the given priority
     // level and moving forward (wrapping from priority value
     // `kNumPriorities` -1 back to 0).
 
-    Message *tail = nullptr;
-    uint8_t  priority;
+    const Message *tail = nullptr;
+    uint8_t        priority;
 
     priority = static_cast<uint8_t>(aStartPriorityLevel);
 
@@ -867,19 +870,15 @@
     return tail;
 }
 
-Message *PriorityQueue::GetHead(void) const
+const Message *PriorityQueue::GetHead(void) const
 {
-    Message *tail;
-
-    tail = FindFirstNonNullTail(Message::kPriorityLow);
-
-    return (tail == nullptr) ? nullptr : tail->Next();
+    return Message::NextOf(FindFirstNonNullTail(Message::kPriorityLow));
 }
 
-Message *PriorityQueue::GetHeadForPriority(Message::Priority aPriority) const
+const Message *PriorityQueue::GetHeadForPriority(Message::Priority aPriority) const
 {
-    Message *head;
-    Message *previousTail;
+    const Message *head;
+    const Message *previousTail;
 
     if (mTails[aPriority] != nullptr)
     {
@@ -897,7 +896,7 @@
     return head;
 }
 
-Message *PriorityQueue::GetTail(void) const
+const Message *PriorityQueue::GetTail(void) const
 {
     return FindFirstNonNullTail(Message::kPriorityLow);
 }
@@ -981,15 +980,23 @@
     }
 }
 
-void PriorityQueue::GetInfo(uint16_t &aMessageCount, uint16_t &aBufferCount) const
+Message::Iterator PriorityQueue::begin(void)
 {
-    aMessageCount = 0;
-    aBufferCount  = 0;
+    return Message::Iterator(GetHead());
+}
 
-    for (const Message *message = GetHead(); message != nullptr; message = message->GetNext())
+Message::ConstIterator PriorityQueue::begin(void) const
+{
+    return Message::ConstIterator(GetHead());
+}
+
+void PriorityQueue::GetInfo(Info &aInfo) const
+{
+    for (const Message &message : *this)
     {
-        aMessageCount++;
-        aBufferCount += message->GetBufferCount();
+        aInfo.mNumMessages++;
+        aInfo.mNumBuffers += message.GetBufferCount();
+        aInfo.mTotalBytes += message.GetLength();
     }
 }
 
diff --git a/src/core/common/message.hpp b/src/core/common/message.hpp
index 7999d2b..2d359ce 100644
--- a/src/core/common/message.hpp
+++ b/src/core/common/message.hpp
@@ -42,14 +42,17 @@
 #include <openthread/platform/messagepool.h>
 
 #include "common/as_core_type.hpp"
+#include "common/clearable.hpp"
 #include "common/code_utils.hpp"
 #include "common/const_cast.hpp"
 #include "common/data.hpp"
 #include "common/encoding.hpp"
+#include "common/iterator_utils.hpp"
 #include "common/linked_list.hpp"
 #include "common/locator.hpp"
 #include "common/non_copyable.hpp"
 #include "common/pool.hpp"
+#include "common/timer.hpp"
 #include "common/type_traits.hpp"
 #include "mac/mac_types.hpp"
 #include "thread/child_mask.hpp"
@@ -67,6 +70,7 @@
 
 namespace Crypto {
 
+class AesCcm;
 class Sha256;
 class HmacSha256;
 
@@ -187,28 +191,29 @@
         MessagePool *mMessagePool; // Message pool for this message.
         void *       mQueue;       // The queue where message is queued (if any). Queue type from `mInPriorityQ`.
         uint32_t     mDatagramTag; // The datagram tag used for 6LoWPAN frags or IPv6fragmentation.
+        TimeMilli    mTimestamp;   // The message timestamp.
         uint16_t     mReserved;    // Number of reserved bytes (for header).
         uint16_t     mLength;      // Current message length (number of bytes).
         uint16_t     mOffset;      // A byte offset within the message.
         uint16_t     mMeshDest;    // Used for unicast non-link-local messages.
         uint16_t     mPanId;       // PAN ID (used for MLE Discover Request and Response).
         uint8_t      mChannel;     // The message channel (used for MLE Announce).
-        uint8_t      mTimeout;     // Seconds remaining before dropping the message.
         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
         ChildMask mChildMask; // ChildMask to indicate which sleepy children need to receive this.
 
-        uint8_t mType : 3;          // The message type.
-        uint8_t mSubType : 4;       // The message sub type.
-        bool    mDirectTx : 1;      // Whether a direct transmission is required.
-        bool    mLinkSecurity : 1;  // Whether link security is enabled.
-        uint8_t mPriority : 2;      // The message priority level (higher value is higher priority).
-        bool    mInPriorityQ : 1;   // Whether the message is queued in normal or priority queue.
-        bool    mTxSuccess : 1;     // Whether the direct tx of the message was successful.
-        bool    mDoNotEvict : 1;    // Whether this message may be evicted.
-        bool    mMulticastLoop : 1; // Whether this multicast message may be looped back.
+        uint8_t mType : 3;             // The message type.
+        uint8_t mSubType : 4;          // The message sub type.
+        bool    mDirectTx : 1;         // Whether a direct transmission is required.
+        bool    mLinkSecurity : 1;     // Whether link security is enabled.
+        uint8_t mPriority : 2;         // The message priority level (higher value is higher priority).
+        bool    mInPriorityQ : 1;      // Whether the message is queued in normal or priority queue.
+        bool    mTxSuccess : 1;        // Whether the direct tx of the message was successful.
+        bool    mDoNotEvict : 1;       // Whether this message may be evicted.
+        bool    mMulticastLoop : 1;    // Whether this multicast message may be looped back.
+        bool    mResolvingAddress : 1; // Whether the message is pending an address query resolution.
 #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.
@@ -258,6 +263,7 @@
     friend class Checksum;
     friend class Crypto::HmacSha256;
     friend class Crypto::Sha256;
+    friend class Crypto::AesCcm;
     friend class MessagePool;
     friend class MessageQueue;
     friend class PriorityQueue;
@@ -950,26 +956,26 @@
     void SetChannel(uint8_t aChannel) { GetMetadata().mChannel = aChannel; }
 
     /**
-     * This method returns the timeout used for 6LoWPAN reassembly.
+     * This method returns the message timestamp.
      *
-     * @returns The time remaining in seconds.
+     * @returns The message timestamp.
      *
      */
-    uint8_t GetTimeout(void) const { return GetMetadata().mTimeout; }
+    TimeMilli GetTimestamp(void) const { return GetMetadata().mTimestamp; }
 
     /**
-     * This method sets the timeout used for 6LoWPAN reassembly.
+     * This method sets the message timestamp to a given time.
      *
-     * @param[in]  aTimeout  The timeout value.
+     * @param[in] aTimestamp   The timestamp value.
      *
      */
-    void SetTimeout(uint8_t aTimeout) { GetMetadata().mTimeout = aTimeout; }
+    void SetTimestamp(TimeMilli aTimestamp) { GetMetadata().mTimestamp = aTimestamp; }
 
     /**
-     * This method decrements the timeout.
+     * This method sets the message timestamp to the current time.
      *
      */
-    void DecrementTimeout(void) { GetMetadata().mTimeout--; }
+    void SetTimestampToNow(void) { SetTimestamp(TimerMilli::GetNow()); }
 
     /**
      * This method returns whether or not message forwarding is scheduled for direct transmission.
@@ -978,7 +984,7 @@
      * @retval FALSE  If message forwarding is not scheduled for direct transmission.
      *
      */
-    bool GetDirectTransmission(void) const { return GetMetadata().mDirectTx; }
+    bool IsDirectTransmission(void) const { return GetMetadata().mDirectTx; }
 
     /**
      * This method unschedules forwarding using direct transmission.
@@ -1028,6 +1034,23 @@
     void SetDoNotEvict(bool aDoNotEvict) { GetMetadata().mDoNotEvict = aDoNotEvict; }
 
     /**
+     * This method indicates whether the message is waiting for an address query resolution.
+     *
+     * @retval TRUE   If the message is waiting for address query resolution.
+     * @retval FALSE  If the message is not waiting for address query resolution.
+     *
+     */
+    bool IsResolvingAddress(void) const { return GetMetadata().mResolvingAddress; }
+
+    /**
+     * This method sets whether the message is waiting for an address query resolution.
+     *
+     * @param[in] aResolvingAddress    TRUE if message is waiting for address resolution, FALSE otherwise.
+     *
+     */
+    void SetResolvingAddress(bool aResolvingAddress) { GetMetadata().mResolvingAddress = aResolvingAddress; }
+
+    /**
      * This method indicates whether or not link security is enabled for the message.
      *
      * @retval TRUE   If link security is enabled.
@@ -1077,7 +1100,7 @@
      * The given LQI value would be added to the average. Note that a message can be composed of multiple 802.15.4
      * frame fragments each received with a different signal strength.
      *
-     * @param[in] aLQI A new LQI value (has no unit) to be added to average.
+     * @param[in] aLqi A new LQI value (has no unit) to be added to average.
      *
      */
     void AddLqi(uint8_t aLqi) { GetMetadata().mLqiAverager.Add(aLqi); }
@@ -1225,6 +1248,45 @@
 #endif // #if OPENTHREAD_CONFIG_MULTI_RADIO
 
 protected:
+    class ConstIterator : public ItemPtrIterator<const Message, ConstIterator>
+    {
+        friend class ItemPtrIterator<const Message, ConstIterator>;
+
+    public:
+        ConstIterator(void) = default;
+
+        explicit ConstIterator(const Message *aMessage)
+            : ItemPtrIterator(aMessage)
+        {
+        }
+
+    private:
+        void Advance(void) { mItem = mItem->GetNext(); }
+    };
+
+    class Iterator : public ItemPtrIterator<Message, Iterator>
+    {
+        friend class ItemPtrIterator<Message, Iterator>;
+
+    public:
+        Iterator(void)
+            : mNext(nullptr)
+        {
+        }
+
+        explicit Iterator(Message *aMessage)
+            : ItemPtrIterator(aMessage)
+            , mNext(NextMessage(aMessage))
+        {
+        }
+
+    private:
+        void            Advance(void);
+        static Message *NextMessage(Message *aMessage) { return (aMessage != nullptr) ? aMessage->GetNext() : nullptr; }
+
+        Message *mNext;
+    };
+
     uint16_t GetReserved(void) const { return GetMetadata().mReserved; }
     void     SetReserved(uint16_t aReservedHeader) { GetMetadata().mReserved = aReservedHeader; }
 
@@ -1269,6 +1331,9 @@
     Message *const &Next(void) const { return GetMetadata().mNext; }
     Message *&      Prev(void) { return GetMetadata().mPrev; }
 
+    static Message *      NextOf(Message *aMessage) { return (aMessage != nullptr) ? aMessage->Next() : nullptr; }
+    static const Message *NextOf(const Message *aMessage) { return (aMessage != nullptr) ? aMessage->Next() : nullptr; }
+
     Error ResizeMessage(uint16_t aLength);
 };
 
@@ -1282,6 +1347,8 @@
     friend class PriorityQueue;
 
 public:
+    typedef otMessageQueueInfo Info; ///< This struct represents info (number of messages/buffers) about a queue.
+
     /**
      * This enumeration represents a position (head or tail) in the queue. This is used to specify where a new message
      * should be added in the queue.
@@ -1297,7 +1364,7 @@
      * This constructor initializes the message queue.
      *
      */
-    MessageQueue(void);
+    MessageQueue(void) { SetTail(nullptr); }
 
     /**
      * This method returns a pointer to the first message.
@@ -1305,7 +1372,15 @@
      * @returns A pointer to the first message.
      *
      */
-    Message *GetHead(void) const;
+    Message *GetHead(void) { return Message::NextOf(GetTail()); }
+
+    /**
+     * This method returns a pointer to the first message.
+     *
+     * @returns A pointer to the first message.
+     *
+     */
+    const Message *GetHead(void) const { return Message::NextOf(GetTail()); }
 
     /**
      * This method adds a message to the end of the list.
@@ -1347,35 +1422,53 @@
     void DequeueAndFreeAll(void);
 
     /**
-     * This method returns the number of messages and buffers enqueued.
+     * This method gets the information about number of messages and buffers in the queue.
      *
-     * @param[out]  aMessageCount  Returns the number of messages enqueued.
-     * @param[out]  aBufferCount   Returns the number of buffers enqueued.
+     * This method updates `aInfo` and adds number of message/buffers in the message queue to the corresponding member
+     * variable in `aInfo`. The caller needs to make sure `aInfo` is initialized before calling this method (e.g.,
+     * clearing `aInfo`). Same `aInfo` can be passed in multiple calls of `GetInfo(aInfo)` on different queues to add
+     * up the number of messages/buffers on different queues.
+     *
+     * @param[out] aInfo  A reference to `Info` structure to update.ni
      *
      */
-    void GetInfo(uint16_t &aMessageCount, uint16_t &aBufferCount) const;
+    void GetInfo(Info &aInfo) const;
+
+    // The following methods are intended to support range-based `for`
+    // loop iteration over the queue entries and should not be used
+    // directly. The range-based `for` works correctly even if the
+    // current entry is removed from the queue during iteration.
+
+    Message::Iterator begin(void);
+    Message::Iterator end(void) { return Message::Iterator(); }
+
+    Message::ConstIterator begin(void) const;
+    Message::ConstIterator end(void) const { return Message::ConstIterator(); }
 
 private:
-    Message *GetTail(void) const { return static_cast<Message *>(mData); }
-    void     SetTail(Message *aMessage) { mData = aMessage; }
+    Message *      GetTail(void) { return static_cast<Message *>(mData); }
+    const Message *GetTail(void) const { return static_cast<const Message *>(mData); }
+    void           SetTail(Message *aMessage) { mData = aMessage; }
 };
 
 /**
  * This class implements a priority queue.
  *
  */
-class PriorityQueue
+class PriorityQueue : private Clearable<PriorityQueue>
 {
     friend class Message;
     friend class MessageQueue;
     friend class MessagePool;
 
 public:
+    typedef otMessageQueueInfo Info; ///< This struct represents info (number of messages/buffers) about a queue.
+
     /**
      * This constructor initializes the priority queue.
      *
      */
-    PriorityQueue(void);
+    PriorityQueue(void) { Clear(); }
 
     /**
      * This method returns a pointer to the first message.
@@ -1383,7 +1476,15 @@
      * @returns A pointer to the first message.
      *
      */
-    Message *GetHead(void) const;
+    Message *GetHead(void) { return AsNonConst(AsConst(this)->GetHead()); }
+
+    /**
+     * This method returns a pointer to the first message.
+     *
+     * @returns A pointer to the first message.
+     *
+     */
+    const Message *GetHead(void) const;
 
     /**
      * This method returns a pointer to the first message for a given priority level.
@@ -1394,7 +1495,21 @@
      *          this priority level.
      *
      */
-    Message *GetHeadForPriority(Message::Priority aPriority) const;
+    Message *GetHeadForPriority(Message::Priority aPriority)
+    {
+        return AsNonConst(AsConst(this)->GetHeadForPriority(aPriority));
+    }
+
+    /**
+     * This method returns a pointer to the first message for a given priority level.
+     *
+     * @param[in] aPriority   Priority level.
+     *
+     * @returns A pointer to the first message with given priority level or `nullptr` if there is no messages with
+     *          this priority level.
+     *
+     */
+    const Message *GetHeadForPriority(Message::Priority aPriority) const;
 
     /**
      * This method adds a message to the queue.
@@ -1427,21 +1542,44 @@
     void DequeueAndFreeAll(void);
 
     /**
-     * This method returns the number of messages and buffers enqueued.
-     *
-     * @param[out]  aMessageCount  Returns the number of messages enqueued.
-     * @param[out]  aBufferCount   Returns the number of buffers enqueued.
-     *
-     */
-    void GetInfo(uint16_t &aMessageCount, uint16_t &aBufferCount) const;
-
-    /**
-     * This method returns the tail of the list (last message in the list)
+     * This method returns the tail of the list (last message in the list).
      *
      * @returns A pointer to the tail of the list.
      *
      */
-    Message *GetTail(void) const;
+    Message *GetTail(void) { return AsNonConst(AsConst(this)->GetTail()); }
+
+    /**
+     * This method returns the tail of the list (last message in the list).
+     *
+     * @returns A pointer to the tail of the list.
+     *
+     */
+    const Message *GetTail(void) const;
+
+    /**
+     * This method gets the information about number of messages and buffers in the priority queue.
+     *
+     * This method updates `aInfo` array and adds number of message/buffers in the message queue to the corresponding
+     * member variable in `aInfo`. The caller needs to make sure `aInfo` is initialized before calling this method
+     * (e.g., clearing `aInfo`). Same `aInfo` can be passed in multiple calls of `GetInfo(aInfo)` on different queues
+     * to add up the number of messages/buffers on different queues.
+     *
+     * @param[out] aInfo  A reference to an `Info` structure to update.
+     *
+     */
+    void GetInfo(Info &aInfo) const;
+
+    // The following methods are intended to support range-based `for`
+    // loop iteration over the queue entries and should not be used
+    // directly. The range-based `for` works correctly even if the
+    // current entry is removed from the queue during iteration.
+
+    Message::Iterator begin(void);
+    Message::Iterator end(void) { return Message::Iterator(); }
+
+    Message::ConstIterator begin(void) const;
+    Message::ConstIterator end(void) const { return Message::ConstIterator(); }
 
 private:
     uint8_t PrevPriority(uint8_t aPriority) const
@@ -1449,7 +1587,12 @@
         return (aPriority == Message::kNumPriorities - 1) ? 0 : (aPriority + 1);
     }
 
-    Message *FindFirstNonNullTail(Message::Priority aStartPriorityLevel) const;
+    const Message *FindFirstNonNullTail(Message::Priority aStartPriorityLevel) const;
+
+    Message *FindFirstNonNullTail(Message::Priority aStartPriorityLevel)
+    {
+        return AsNonConst(AsConst(this)->FindFirstNonNullTail(aStartPriorityLevel));
+    }
 
     Message *mTails[Message::kNumPriorities]; // Tail pointers associated with different priority levels.
 };
diff --git a/src/core/common/notifier.cpp b/src/core/common/notifier.cpp
index 4b764ec..7fd2831 100644
--- a/src/core/common/notifier.cpp
+++ b/src/core/common/notifier.cpp
@@ -34,13 +34,16 @@
 #include "notifier.hpp"
 
 #include "border_router/routing_manager.hpp"
+#include "common/array.hpp"
 #include "common/code_utils.hpp"
 #include "common/debug.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 
 namespace ot {
 
+RegisterLogModule("Notifier");
+
 Notifier::Notifier(Instance &aInstance)
     : InstanceLocator(aInstance)
     , mTask(aInstance, Notifier::EmitEvents)
@@ -213,7 +216,7 @@
 
 // LCOV_EXCL_START
 
-#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_CORE == 1)
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
 
 void Notifier::LogEvents(Events aEvents) const
 {
@@ -230,8 +233,8 @@
         {
             if (string.GetLength() >= kFlagsStringLineLimit)
             {
-                otLogInfoCore("Notifier: StateChanged (0x%08x) %s%s ...", aEvents.GetAsFlags(), didLog ? "... " : "[",
-                              string.AsCString());
+                LogInfo("StateChanged (0x%08x) %s%s ...", aEvents.GetAsFlags(), didLog ? "... " : "[",
+                        string.AsCString());
                 string.Clear();
                 didLog   = true;
                 addSpace = false;
@@ -245,8 +248,7 @@
     }
 
 exit:
-    otLogInfoCore("Notifier: StateChanged (0x%08x) %s%s]", aEvents.GetAsFlags(), didLog ? "... " : "[",
-                  string.AsCString());
+    LogInfo("StateChanged (0x%08x) %s%s]", aEvents.GetAsFlags(), didLog ? "... " : "[", string.AsCString());
 }
 
 const char *Notifier::EventToString(Event aEvent) const
@@ -289,7 +291,7 @@
         "PndDset",           // kEventPendingDatasetChanged            (1 << 29)
     };
 
-    for (uint8_t index = 0; index < OT_ARRAY_LENGTH(kEventStrings); index++)
+    for (uint8_t index = 0; index < GetArrayLength(kEventStrings); index++)
     {
         if (static_cast<uint32_t>(aEvent) == (1U << index))
         {
@@ -301,7 +303,7 @@
     return retval;
 }
 
-#else // #if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_CORE == 1)
+#else // #if OT_SHOULD_LOG_AT( OT_LOG_LEVEL_INFO)
 
 void Notifier::LogEvents(Events) const
 {
@@ -312,7 +314,7 @@
     return "";
 }
 
-#endif // #if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_CORE == 1)
+#endif // #if OT_SHOULD_LOG_AT( OT_LOG_LEVEL_INFO)
 
 // LCOV_EXCL_STOP
 
diff --git a/src/core/common/notifier.hpp b/src/core/common/notifier.hpp
index 96494ac..8887671 100644
--- a/src/core/common/notifier.hpp
+++ b/src/core/common/notifier.hpp
@@ -222,7 +222,7 @@
      * This method removes/unregisters a previously registered `otStateChangedCallback` handler.
      *
      * @param[in]  aCallback     A pointer to the callback function pointer.
-     * @param[in]  aContex       A pointer to arbitrary context information.
+     * @param[in]  aContext      A pointer to arbitrary context information.
      *
      */
     void RemoveCallback(otStateChangedCallback aCallback, void *aContext);
@@ -270,9 +270,9 @@
      *
      * The template `Type` should support comparison operator `==` and assignment operator `=`.
      *
-     * @param[inout] aVariable    A reference to the variable to update.
-     * @param[in]    aNewValue    The new value.
-     * @param[in]    aEvent       The event to signal.
+     * @param[in,out] aVariable    A reference to the variable to update.
+     * @param[in]     aNewValue    The new value.
+     * @param[in]     aEvent       The event to signal.
      *
      * @retval kErrorNone      The variable was update successfully and @p aEvent was signaled.
      * @retval kErrorAlready   The variable was already set to the same value.
diff --git a/src/core/common/pool.hpp b/src/core/common/pool.hpp
index ad22b18..279655f 100644
--- a/src/core/common/pool.hpp
+++ b/src/core/common/pool.hpp
@@ -36,6 +36,7 @@
 
 #include "openthread-core-config.h"
 
+#include "common/array.hpp"
 #include "common/linked_list.hpp"
 #include "common/non_copyable.hpp"
 
@@ -147,7 +148,7 @@
      * @retval FALSE if @p aObject is not from the pool.
      *
      */
-    bool IsPoolEntry(const Type &aObject) const { return (&mPool[0] <= &aObject) && (&aObject < OT_ARRAY_END(mPool)); }
+    bool IsPoolEntry(const Type &aObject) const { return (&mPool[0] <= &aObject) && (&aObject < GetArrayEnd(mPool)); }
 
     /**
      * This method returns the associated index of a given entry from the pool.
diff --git a/src/core/common/random_manager.cpp b/src/core/common/random.cpp
similarity index 72%
rename from src/core/common/random_manager.cpp
rename to src/core/common/random.cpp
index 3ff7ffe..482d68d 100644
--- a/src/core/common/random_manager.cpp
+++ b/src/core/common/random.cpp
@@ -31,22 +31,20 @@
  *   This file provides an implementation of OpenThread random number generation manager class.
  */
 
-#include "random_manager.hpp"
+#include "random.hpp"
 
 #include <openthread/platform/entropy.h>
 
 #include "common/code_utils.hpp"
 #include "common/debug.hpp"
-#include "common/logging.hpp"
-#include "common/random.hpp"
-#include "crypto/mbedtls.hpp"
 
 namespace ot {
+namespace Random {
 
-uint16_t                     RandomManager::sInitCount = 0;
-RandomManager::NonCryptoPrng RandomManager::sPrng;
+uint16_t               Manager::sInitCount = 0;
+Manager::NonCryptoPrng Manager::sPrng;
 
-RandomManager::RandomManager(void)
+Manager::Manager(void)
 {
     uint32_t seed;
 
@@ -67,7 +65,7 @@
     sInitCount++;
 }
 
-RandomManager::~RandomManager(void)
+Manager::~Manager(void)
 {
     OT_ASSERT(sInitCount > 0);
 
@@ -82,7 +80,7 @@
     return;
 }
 
-uint32_t RandomManager::NonCryptoGetUint32(void)
+uint32_t Manager::NonCryptoGetUint32(void)
 {
     OT_ASSERT(sInitCount > 0);
 
@@ -92,7 +90,7 @@
 //-------------------------------------------------------------------
 // NonCryptoPrng
 
-void RandomManager::NonCryptoPrng::Init(uint32_t aSeed)
+void Manager::NonCryptoPrng::Init(uint32_t aSeed)
 {
     // The PRNG has a cycle of length 1 for the below two initial
     // seeds. For all other seed values the cycle is ~2^31 long.
@@ -105,7 +103,7 @@
     mState = aSeed;
 }
 
-uint32_t RandomManager::NonCryptoPrng::GetNext(void)
+uint32_t Manager::NonCryptoPrng::GetNext(void)
 {
     uint32_t mlcg, p, q;
     uint64_t tmpstate;
@@ -127,4 +125,44 @@
     return mlcg;
 }
 
+//-------------------------------------------------------------------
+
+namespace NonCrypto {
+
+uint8_t GetUint8InRange(uint8_t aMin, uint8_t aMax)
+{
+    OT_ASSERT(aMax > aMin);
+
+    return (aMin + (GetUint8() % (aMax - aMin)));
+}
+
+uint16_t GetUint16InRange(uint16_t aMin, uint16_t aMax)
+{
+    OT_ASSERT(aMax > aMin);
+    return (aMin + (GetUint16() % (aMax - aMin)));
+}
+
+uint32_t GetUint32InRange(uint32_t aMin, uint32_t aMax)
+{
+    OT_ASSERT(aMax > aMin);
+    return (aMin + (GetUint32() % (aMax - aMin)));
+}
+
+void FillBuffer(uint8_t *aBuffer, uint16_t aSize)
+{
+    while (aSize-- != 0)
+    {
+        *aBuffer++ = GetUint8();
+    }
+}
+
+uint32_t AddJitter(uint32_t aValue, uint16_t aJitter)
+{
+    aJitter = (aJitter <= aValue) ? aJitter : static_cast<uint16_t>(aValue);
+
+    return aValue + GetUint32InRange(0, 2 * aJitter + 1) - aJitter;
+}
+
+} // namespace NonCrypto
+} // namespace Random
 } // namespace ot
diff --git a/src/core/common/random.hpp b/src/core/common/random.hpp
index a7e2694..d3b75d0 100644
--- a/src/core/common/random.hpp
+++ b/src/core/common/random.hpp
@@ -38,12 +38,70 @@
 
 #include <stdint.h>
 
+#include <openthread/platform/crypto.h>
+
 #include "common/debug.hpp"
 #include "common/error.hpp"
-#include "common/random_manager.hpp"
+#include "common/non_copyable.hpp"
 
 namespace ot {
 namespace Random {
+
+/**
+ * This class manages random number generator initialization/deinitialization.
+ *
+ */
+class Manager : private NonCopyable
+{
+public:
+    /**
+     * This constructor initializes the object.
+     *
+     */
+    Manager(void);
+
+    /**
+     * This destructor deinitializes the object.
+     *
+     */
+    ~Manager(void);
+
+    /**
+     * This static method generates and returns a random value using a non-crypto Pseudo Random Number Generator.
+     *
+     * @returns    A random `uint32_t` value.
+     *
+     */
+    static uint32_t NonCryptoGetUint32(void);
+
+#if !OPENTHREAD_RADIO
+    /**
+     * This static method fills a given buffer with cryptographically secure random bytes.
+     *
+     * @param[out] aBuffer  A pointer to a buffer to fill with the random bytes.
+     * @param[in]  aSize    Size of buffer (number of bytes to fill).
+     *
+     * @retval kErrorNone    Successfully filled buffer with random values.
+     *
+     */
+    static Error CryptoFillBuffer(uint8_t *aBuffer, uint16_t aSize) { return otPlatCryptoRandomGet(aBuffer, aSize); }
+#endif
+
+private:
+    class NonCryptoPrng // A non-crypto Pseudo Random Number Generator (PRNG)
+    {
+    public:
+        void     Init(uint32_t aSeed);
+        uint32_t GetNext(void);
+
+    private:
+        uint32_t mState;
+    };
+
+    static uint16_t      sInitCount;
+    static NonCryptoPrng sPrng;
+};
+
 namespace NonCrypto {
 
 /**
@@ -54,7 +112,7 @@
  */
 inline uint32_t GetUint32(void)
 {
-    return ot::RandomManager::NonCryptoGetUint32();
+    return Manager::NonCryptoGetUint32();
 }
 
 /**
@@ -86,12 +144,9 @@
  * @param[in]  aMax  A maximum value (this value is excluded from returned random result).
  *
  * @returns    A random `uint8_t` value in the given range (i.e., aMin <= random value < aMax).
+ *
  */
-inline uint8_t GetUint8InRange(uint8_t aMin, uint8_t aMax)
-{
-    OT_ASSERT(aMax > aMin);
-    return (aMin + (GetUint8() % (aMax - aMin)));
-}
+uint8_t GetUint8InRange(uint8_t aMin, uint8_t aMax);
 
 /**
  * This function generates and returns a random `uint16_t` value within a given range `[aMin, aMax)`.
@@ -102,12 +157,9 @@
  * @param[in]  aMax  A maximum value (this value is excluded from returned random result).
  *
  * @returns    A random `uint16_t` value in the given range (i.e., aMin <= random value < aMax).
+ *
  */
-inline uint16_t GetUint16InRange(uint16_t aMin, uint16_t aMax)
-{
-    OT_ASSERT(aMax > aMin);
-    return (aMin + (GetUint16() % (aMax - aMin)));
-}
+uint16_t GetUint16InRange(uint16_t aMin, uint16_t aMax);
 
 /**
  * This function generates and returns a random `uint32_t` value within a given range `[aMin, aMax)`.
@@ -120,11 +172,7 @@
  * @returns    A random `uint32_t` value in the given range (i.e., aMin <= random value < aMax).
  *
  */
-inline uint32_t GetUint32InRange(uint32_t aMin, uint32_t aMax)
-{
-    OT_ASSERT(aMax > aMin);
-    return (aMin + (GetUint32() % (aMax - aMin)));
-}
+uint32_t GetUint32InRange(uint32_t aMin, uint32_t aMax);
 
 /**
  * This function fills a given buffer with random bytes.
@@ -133,13 +181,7 @@
  * @param[in]  aSize    Size of buffer (number of bytes to fill).
  *
  */
-inline void FillBuffer(uint8_t *aBuffer, uint16_t aSize)
-{
-    while (aSize-- != 0)
-    {
-        *aBuffer++ = GetUint8();
-    }
-}
+void FillBuffer(uint8_t *aBuffer, uint16_t aSize);
 
 /**
  * This function adds a random jitter within a given range to a given value.
@@ -150,12 +192,7 @@
  * @returns    The given value with an added random jitter.
  *
  */
-inline uint32_t AddJitter(uint32_t aValue, uint16_t aJitter)
-{
-    aJitter = (aJitter <= aValue) ? aJitter : static_cast<uint16_t>(aValue);
-
-    return aValue + GetUint32InRange(0, 2 * aJitter + 1) - aJitter;
-}
+uint32_t AddJitter(uint32_t aValue, uint16_t aJitter);
 
 } // namespace NonCrypto
 
@@ -174,7 +211,7 @@
  */
 inline Error FillBuffer(uint8_t *aBuffer, uint16_t aSize)
 {
-    return RandomManager::CryptoFillBuffer(aBuffer, aSize);
+    return Manager::CryptoFillBuffer(aBuffer, aSize);
 }
 
 } // namespace Crypto
diff --git a/src/core/common/random_manager.hpp b/src/core/common/random_manager.hpp
deleted file mode 100644
index adc4d09..0000000
--- a/src/core/common/random_manager.hpp
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- *  Copyright (c) 2019, 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 OpenThread random number generation manager class.
- */
-
-#ifndef RANDOM_MANAGER_HPP_
-#define RANDOM_MANAGER_HPP_
-
-#include "openthread-core-config.h"
-
-#include <stdint.h>
-
-#include <openthread/platform/crypto.h>
-
-#include "common/error.hpp"
-#include "common/non_copyable.hpp"
-
-namespace ot {
-
-/**
- * This class manages random number generator initialization/deinitialization.
- *
- */
-class RandomManager : private NonCopyable
-{
-public:
-    /**
-     * This constructor initializes the object.
-     *
-     */
-    RandomManager(void);
-
-    /**
-     * This destructor deinitializes the object.
-     *
-     */
-    ~RandomManager(void);
-
-    /**
-     * This static method generates and returns a random value using a non-crypto Pseudo Random Number Generator.
-     *
-     * @returns    A random `uint32_t` value.
-     *
-     */
-    static uint32_t NonCryptoGetUint32(void);
-
-#if !OPENTHREAD_RADIO
-    /**
-     * This static method fills a given buffer with cryptographically secure random bytes.
-     *
-     * @param[out] aBuffer  A pointer to a buffer to fill with the random bytes.
-     * @param[in]  aSize    Size of buffer (number of bytes to fill).
-     *
-     * @retval kErrorNone    Successfully filled buffer with random values.
-     *
-     */
-    static Error CryptoFillBuffer(uint8_t *aBuffer, uint16_t aSize) { return otPlatCryptoRandomGet(aBuffer, aSize); }
-#endif
-
-private:
-    class NonCryptoPrng // A non-crypto Pseudo Random Number Generator (PRNG)
-    {
-    public:
-        void     Init(uint32_t aSeed);
-        uint32_t GetNext(void);
-
-    private:
-        uint32_t mState;
-    };
-
-    static uint16_t      sInitCount;
-    static NonCryptoPrng sPrng;
-};
-
-} // namespace ot
-
-#endif // RANDOM_MANAGER_HPP_
diff --git a/src/core/common/settings.cpp b/src/core/common/settings.cpp
index 424db0f..c69b322 100644
--- a/src/core/common/settings.cpp
+++ b/src/core/common/settings.cpp
@@ -33,81 +33,81 @@
 
 #include "settings.hpp"
 
+#include "common/array.hpp"
 #include "common/code_utils.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
 #include "meshcop/dataset.hpp"
 #include "thread/mle.hpp"
 
 namespace ot {
 
+RegisterLogModule("Settings");
+
 //---------------------------------------------------------------------------------------------------------------------
 // SettingsBase
 
 // LCOV_EXCL_START
 
-#if OPENTHREAD_CONFIG_LOG_UTIL && (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO)
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
 
 void SettingsBase::NetworkInfo::Log(Action aAction) const
 {
-    otLogInfoCore(
-        "[settings] %s NetworkInfo {rloc:0x%04x, extaddr:%s, role:%s, mode:0x%02x, version:%hu, keyseq:0x%x, ...",
-        ActionToString(aAction), GetRloc16(), GetExtAddress().ToString().AsCString(),
-        Mle::Mle::RoleToString(static_cast<Mle::DeviceRole>(GetRole())), GetDeviceMode(), GetVersion(),
-        GetKeySequence());
+    LogInfo("%s NetworkInfo {rloc:0x%04x, extaddr:%s, role:%s, mode:0x%02x, version:%hu, keyseq:0x%x, ...",
+            ActionToString(aAction), GetRloc16(), GetExtAddress().ToString().AsCString(),
+            Mle::Mle::RoleToString(static_cast<Mle::DeviceRole>(GetRole())), GetDeviceMode(), GetVersion(),
+            GetKeySequence());
 
-    otLogInfoCore("[settings] ... pid:0x%x, mlecntr:0x%x, maccntr:0x%x, mliid:%s}", GetPreviousPartitionId(),
-                  GetMleFrameCounter(), GetMacFrameCounter(), GetMeshLocalIid().ToString().AsCString());
+    LogInfo("... pid:0x%x, mlecntr:0x%x, maccntr:0x%x, mliid:%s}", GetPreviousPartitionId(), GetMleFrameCounter(),
+            GetMacFrameCounter(), GetMeshLocalIid().ToString().AsCString());
 }
 
 void SettingsBase::ParentInfo::Log(Action aAction) const
 {
-    otLogInfoCore("[settings] %s ParentInfo {extaddr:%s, version:%hu}", ActionToString(aAction),
-                  GetExtAddress().ToString().AsCString(), GetVersion());
+    LogInfo("%s ParentInfo {extaddr:%s, version:%hu}", ActionToString(aAction), GetExtAddress().ToString().AsCString(),
+            GetVersion());
 }
 
 #if OPENTHREAD_FTD
 void SettingsBase::ChildInfo::Log(Action aAction) const
 {
-    otLogInfoCore("[settings] %s ChildInfo {rloc:0x%04x, extaddr:%s, timeout:%u, mode:0x%02x, version:%hu}",
-                  ActionToString(aAction), GetRloc16(), GetExtAddress().ToString().AsCString(), GetTimeout(), GetMode(),
-                  GetVersion());
+    LogInfo("%s ChildInfo {rloc:0x%04x, extaddr:%s, timeout:%u, mode:0x%02x, version:%hu}", ActionToString(aAction),
+            GetRloc16(), GetExtAddress().ToString().AsCString(), GetTimeout(), GetMode(), GetVersion());
 }
 #endif
 
 #if OPENTHREAD_CONFIG_DUA_ENABLE
 void SettingsBase::DadInfo::Log(Action aAction) const
 {
-    otLogInfoCore("[settings] %s DadInfo {DadCounter:%2d}", ActionToString(aAction), GetDadCounter());
+    LogInfo("%s DadInfo {DadCounter:%2d}", ActionToString(aAction), GetDadCounter());
 }
 #endif
 
 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
 void SettingsBase::LogPrefix(Action aAction, Key aKey, const Ip6::Prefix &aPrefix)
 {
-    otLogInfoCore("[settings] %s %s %s", ActionToString(aAction), KeyToString(aKey), aPrefix.ToString().AsCString());
+    LogInfo("%s %s %s", ActionToString(aAction), KeyToString(aKey), aPrefix.ToString().AsCString());
 }
 #endif
 
 #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE && OPENTHREAD_CONFIG_SRP_CLIENT_SAVE_SELECTED_SERVER_ENABLE
 void SettingsBase::SrpClientInfo::Log(Action aAction) const
 {
-    otLogInfoCore("[settings] %s SrpClientInfo {Server:[%s]:%u}", ActionToString(aAction),
-                  GetServerAddress().ToString().AsCString(), GetServerPort());
+    LogInfo("%s SrpClientInfo {Server:[%s]:%u}", ActionToString(aAction), GetServerAddress().ToString().AsCString(),
+            GetServerPort());
 }
 #endif
 
 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE && OPENTHREAD_CONFIG_SRP_SERVER_PORT_SWITCH_ENABLE
 void SettingsBase::SrpServerInfo::Log(Action aAction) const
 {
-    otLogInfoCore("[settings] %s SrpServerInfo {port:%u}", ActionToString(aAction), GetPort());
+    LogInfo("%s SrpServerInfo {port:%u}", ActionToString(aAction), GetPort());
 }
 #endif
 
-#endif // OPENTHREAD_CONFIG_LOG_UTIL && (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO)
+#endif // OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
 
-#if OPENTHREAD_CONFIG_LOG_UTIL && (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO)
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
 const char *SettingsBase::ActionToString(Action aAction)
 {
     static const char *const kActionStrings[] = {
@@ -134,9 +134,9 @@
 
     return kActionStrings[aAction];
 }
-#endif // OPENTHREAD_CONFIG_LOG_UTIL && (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO)
+#endif // OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
 
-#if OPENTHREAD_CONFIG_LOG_UTIL && (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_WARN)
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN)
 const char *SettingsBase::KeyToString(Key aKey)
 {
     static const char *const kKeyStrings[] = {
@@ -149,12 +149,13 @@
         "",                  // (6)  kKeyReserved
         "SlaacIidSecretKey", // (7)  kKeySlaacIidSecretKey
         "DadInfo",           // (8)  kKeyDadInfo
-        "OmrPrefix",         // (9)  kKeyOmrPrefix
+        "LegacyOmrPrefix",   // (9)  kKeyLegacyOmrPrefix
         "OnLinkPrefix",      // (10) kKeyOnLinkPrefix
         "SrpEcdsaKey",       // (11) kKeySrpEcdsaKey
         "SrpClientInfo",     // (12) kKeySrpClientInfo
         "SrpServerInfo",     // (13) kKeySrpServerInfo
-        "Nat64Prefix",       // (14) kKeyNat64Prefix
+        "LegacyNat64Prefix", // (14) kKeyLegacyNat64Prefix
+        "BrUlaPrefix",       // (15) kKeyBrUlaPrefix
     };
 
     static_assert(1 == kKeyActiveDataset, "kKeyActiveDataset value is incorrect");
@@ -165,28 +166,29 @@
     static_assert(6 == kKeyReserved, "kKeyReserved value is incorrect");
     static_assert(7 == kKeySlaacIidSecretKey, "kKeySlaacIidSecretKey value is incorrect");
     static_assert(8 == kKeyDadInfo, "kKeyDadInfo value is incorrect");
-    static_assert(9 == kKeyOmrPrefix, "kKeyOmrPrefix value is incorrect");
+    static_assert(9 == kKeyLegacyOmrPrefix, "kKeyLegacyOmrPrefix value is incorrect");
     static_assert(10 == kKeyOnLinkPrefix, "kKeyOnLinkPrefix value is incorrect");
     static_assert(11 == kKeySrpEcdsaKey, "kKeySrpEcdsaKey value is incorrect");
     static_assert(12 == kKeySrpClientInfo, "kKeySrpClientInfo value is incorrect");
     static_assert(13 == kKeySrpServerInfo, "kKeySrpServerInfo value is incorrect");
-    static_assert(14 == kKeyNat64Prefix, "kKeyNat64Prefix value is incorrect");
+    static_assert(14 == kKeyLegacyNat64Prefix, "kKeyLegacyNat64Prefix value is incorrect");
+    static_assert(15 == kKeyBrUlaPrefix, "kKeyBrUlaPrefix value is incorrect");
 
-    static_assert(kLastKey == kKeyNat64Prefix, "kLastKey is not valid");
+    static_assert(kLastKey == kKeyBrUlaPrefix, "kLastKey is not valid");
 
     OT_ASSERT(aKey <= kLastKey);
 
     return kKeyStrings[aKey];
 }
-#endif // OPENTHREAD_CONFIG_LOG_UTIL && (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_WARN)
+#endif // OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN)
 
 // LCOV_EXCL_STOP
 
 //---------------------------------------------------------------------------------------------------------------------
 // Settings
 
-// This array contains critical keys that should be stored in the secure area.
-const uint16_t Settings::kCriticalKeys[] = {
+// This array contains sensitive keys that should be stored in the secure area.
+const uint16_t Settings::kSensitiveKeys[] = {
     SettingsBase::kKeyActiveDataset,
     SettingsBase::kKeyPendingDataset,
     SettingsBase::kKeySrpEcdsaKey,
@@ -194,8 +196,7 @@
 
 void Settings::Init(void)
 {
-    Get<SettingsDriver>().Init();
-    Get<SettingsDriver>().SetCriticalKeys(kCriticalKeys, OT_ARRAY_LENGTH(kCriticalKeys));
+    Get<SettingsDriver>().Init(kSensitiveKeys, GetArrayLength(kSensitiveKeys));
 }
 
 void Settings::Deinit(void)
@@ -206,12 +207,17 @@
 void Settings::Wipe(void)
 {
     Get<SettingsDriver>().Wipe();
-    otLogInfoCore("[settings] Wiped all info");
+    LogInfo("Wiped all info");
 }
 
-Error Settings::SaveOperationalDataset(bool aIsActive, const MeshCoP::Dataset &aDataset)
+Settings::Key Settings::KeyForDatasetType(MeshCoP::Dataset::Type aType)
 {
-    Key   key   = (aIsActive ? kKeyActiveDataset : kKeyPendingDataset);
+    return (aType == MeshCoP::Dataset::kActive) ? kKeyActiveDataset : kKeyPendingDataset;
+}
+
+Error Settings::SaveOperationalDataset(MeshCoP::Dataset::Type aType, const MeshCoP::Dataset &aDataset)
+{
+    Key   key   = KeyForDatasetType(aType);
     Error error = Get<SettingsDriver>().Set(key, aDataset.GetBytes(), aDataset.GetSize());
 
     Log(kActionSave, error, key);
@@ -219,13 +225,12 @@
     return error;
 }
 
-Error Settings::ReadOperationalDataset(bool aIsActive, MeshCoP::Dataset &aDataset) const
+Error Settings::ReadOperationalDataset(MeshCoP::Dataset::Type aType, MeshCoP::Dataset &aDataset) const
 {
     Error    error  = kErrorNone;
     uint16_t length = MeshCoP::Dataset::kMaxSize;
 
-    SuccessOrExit(error = Get<SettingsDriver>().Get(aIsActive ? kKeyActiveDataset : kKeyPendingDataset,
-                                                    aDataset.GetBytes(), &length));
+    SuccessOrExit(error = Get<SettingsDriver>().Get(KeyForDatasetType(aType), aDataset.GetBytes(), &length));
     VerifyOrExit(length <= MeshCoP::Dataset::kMaxSize, error = kErrorNotFound);
 
     aDataset.SetSize(length);
@@ -234,9 +239,9 @@
     return error;
 }
 
-Error Settings::DeleteOperationalDataset(bool aIsActive)
+Error Settings::DeleteOperationalDataset(MeshCoP::Dataset::Type aType)
 {
-    Key   key   = (aIsActive ? kKeyActiveDataset : kKeyPendingDataset);
+    Key   key   = KeyForDatasetType(aType);
     Error error = Get<SettingsDriver>().Delete(key);
 
     Log(kActionDelete, error, key);
@@ -355,13 +360,11 @@
     OT_UNUSED_VARIABLE(aError);
     OT_UNUSED_VARIABLE(aValue);
 
-#if OPENTHREAD_CONFIG_LOG_UTIL
-
     if (aError != kErrorNone)
     {
         // Log error if log level is at "warn" or higher.
 
-#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_WARN)
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN)
         const char *actionText = "";
 
         switch (aAction)
@@ -395,9 +398,9 @@
             ExitNow();
         }
 
-        otLogWarnCore("[settings] Error %s %s %s", ErrorToString(aError), actionText, KeyToString(aKey));
+        LogWarn("Error %s %s %s", ErrorToString(aError), actionText, KeyToString(aKey));
 
-#endif // #if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_WARN)
+#endif // #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN)
 
         ExitNow();
     }
@@ -405,7 +408,7 @@
     // We reach here when `aError` is `kErrorNone`.
     // Log success if log level is at "info" or higher.
 
-#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO)
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
     if (aValue != nullptr)
     {
         switch (aKey)
@@ -431,9 +434,10 @@
 #endif
 
 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
-        case kKeyOmrPrefix:
+        case kKeyBrUlaPrefix:
+        case kKeyLegacyOmrPrefix:
         case kKeyOnLinkPrefix:
-        case kKeyNat64Prefix:
+        case kKeyLegacyNat64Prefix:
             LogPrefix(aAction, aKey, *reinterpret_cast<const Ip6::Prefix *>(aValue));
             break;
 #endif
@@ -462,13 +466,11 @@
 
     if (aValue == nullptr)
     {
-        otLogInfoCore("[settings] %s %s", ActionToString(aAction), KeyToString(aKey));
+        LogInfo("%s %s", ActionToString(aAction), KeyToString(aKey));
     }
-#endif // (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO)
+#endif // OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
 
 exit:
-
-#endif // OPENTHREAD_CONFIG_LOG_UTIL
     return;
 }
 
diff --git a/src/core/common/settings.hpp b/src/core/common/settings.hpp
index c394bb0..1758feb 100644
--- a/src/core/common/settings.hpp
+++ b/src/core/common/settings.hpp
@@ -42,20 +42,18 @@
 #include "common/encoding.hpp"
 #include "common/equatable.hpp"
 #include "common/locator.hpp"
+#include "common/log.hpp"
 #include "common/non_copyable.hpp"
 #include "common/settings_driver.hpp"
 #include "crypto/ecdsa.hpp"
 #include "mac/mac_types.hpp"
+#include "meshcop/dataset.hpp"
 #include "net/ip6_address.hpp"
 #include "utils/flash.hpp"
 #include "utils/slaac_address.hpp"
 
 namespace ot {
 
-namespace MeshCoP {
-class Dataset;
-}
-
 class Settings;
 
 /**
@@ -117,15 +115,18 @@
         kKeyReserved          = OT_SETTINGS_KEY_RESERVED,
         kKeySlaacIidSecretKey = OT_SETTINGS_KEY_SLAAC_IID_SECRET_KEY,
         kKeyDadInfo           = OT_SETTINGS_KEY_DAD_INFO,
-        kKeyOmrPrefix         = OT_SETTINGS_KEY_OMR_PREFIX,
+        kKeyLegacyOmrPrefix   = OT_SETTINGS_KEY_LEGACY_OMR_PREFIX,
         kKeyOnLinkPrefix      = OT_SETTINGS_KEY_ON_LINK_PREFIX,
         kKeySrpEcdsaKey       = OT_SETTINGS_KEY_SRP_ECDSA_KEY,
         kKeySrpClientInfo     = OT_SETTINGS_KEY_SRP_CLIENT_INFO,
         kKeySrpServerInfo     = OT_SETTINGS_KEY_SRP_SERVER_INFO,
-        kKeyNat64Prefix       = OT_SETTINGS_KEY_NAT64_PREFIX,
+        kKeyLegacyNat64Prefix = OT_SETTINGS_KEY_LEGACY_NAT64_PREFIX,
+        kKeyBrUlaPrefix       = OT_SETTINGS_KEY_BR_ULA_PREFIX,
     };
 
-    static constexpr Key kLastKey = kKeyNat64Prefix; ///< The last (numerically) enumerator value in `Key`.
+    static constexpr Key kLastKey = kKeyBrUlaPrefix; ///< The last (numerically) enumerator value in `Key`.
+    static_assert(static_cast<uint16_t>(kLastKey) < static_cast<uint16_t>(OT_SETTINGS_KEY_VENDOR_RESERVED_MIN),
+                  "Core settings keys overlap with vendor reserved keys");
 
     /**
      * This structure represents the device's own network information for settings storage.
@@ -476,7 +477,7 @@
         /**
          * This method sets the Thread device mode.
          *
-         * @param[in] aRloc16  The Thread device mode.
+         * @param[in] aMode  The Thread device mode.
          *
          */
         void SetMode(uint8_t aMode) { mMode = aMode; }
@@ -569,18 +570,33 @@
 
 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
     /**
-     * This class defines constants and types for OMR prefix settings.
+     * This class defines constants and types for BR ULA prefix settings.
      *
      */
-    class OmrPrefix
+    class BrUlaPrefix
     {
     public:
-        static constexpr Key kKey = kKeyOmrPrefix; ///< The associated key.
+        static constexpr Key kKey = kKeyBrUlaPrefix; ///< The associated key.
 
         typedef Ip6::Prefix ValueType; ///< The associated value type.
 
     private:
-        OmrPrefix(void) = default;
+        BrUlaPrefix(void) = default;
+    };
+
+    /**
+     * This class defines constants and types for legacy OMR prefix settings.
+     *
+     */
+    class LegacyOmrPrefix
+    {
+    public:
+        static constexpr Key kKey = kKeyLegacyOmrPrefix; ///< The associated key.
+
+        typedef Ip6::Prefix ValueType; ///< The associated value type.
+
+    private:
+        LegacyOmrPrefix(void) = default;
     };
 
     /**
@@ -597,23 +613,6 @@
     private:
         OnLinkPrefix(void) = default;
     };
-
-#if OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE
-    /**
-     * This class defines constants and types for NAT64 prefix settings.
-     *
-     */
-    class Nat64Prefix
-    {
-    public:
-        static constexpr Key kKey = kKeyNat64Prefix; ///< The associated key.
-
-        typedef Ip6::Prefix ValueType; ///< The associated value type.
-
-    private:
-        Nat64Prefix(void) = default;
-    };
-#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE
 #endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
 
 #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE
@@ -744,10 +743,10 @@
     static void LogPrefix(Action aAction, Key aKey, const Ip6::Prefix &aPrefix);
 #endif
 
-#if OPENTHREAD_CONFIG_LOG_UTIL && (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_WARN)
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN)
     static const char *KeyToString(Key aKey);
 #endif
-#if OPENTHREAD_CONFIG_LOG_UTIL && (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO)
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
     static const char *ActionToString(Action aAction);
 #endif
 };
@@ -797,38 +796,38 @@
     /**
      * This method saves the Operational Dataset (active or pending).
      *
-     * @param[in]   aIsActive   Indicates whether Dataset is active or pending.
+     * @param[in]   aType       The Dataset type (active or pending) to save.
      * @param[in]   aDataset    A reference to a `Dataset` object to be saved.
      *
      * @retval kErrorNone             Successfully saved the Dataset.
      * @retval kErrorNotImplemented   The platform does not implement settings functionality.
      *
      */
-    Error SaveOperationalDataset(bool aIsActive, const MeshCoP::Dataset &aDataset);
+    Error SaveOperationalDataset(MeshCoP::Dataset::Type aType, const MeshCoP::Dataset &aDataset);
 
     /**
      * This method reads the Operational Dataset (active or pending).
      *
-     * @param[in]   aIsActive             Indicates whether Dataset is active or pending.
-     * @param[out]  aDataset              A reference to a `Dataset` object to output the read content.
+     * @param[in]   aType            The Dataset type (active or pending) to read.
+     * @param[out]  aDataset         A reference to a `Dataset` object to output the read content.
      *
      * @retval kErrorNone             Successfully read the Dataset.
      * @retval kErrorNotFound         No corresponding value in the setting store.
      * @retval kErrorNotImplemented   The platform does not implement settings functionality.
      *
      */
-    Error ReadOperationalDataset(bool aIsActive, MeshCoP::Dataset &aDataset) const;
+    Error ReadOperationalDataset(MeshCoP::Dataset::Type aType, MeshCoP::Dataset &aDataset) const;
 
     /**
      * This method deletes the Operational Dataset (active/pending) from settings.
      *
-     * @param[in]   aIsActive            Indicates whether Dataset is active or pending.
+     * @param[in]   aType            The Dataset type (active or pending) to delete.
      *
      * @retval kErrorNone            Successfully deleted the Dataset.
      * @retval kErrorNotImplemented  The platform does not implement settings functionality.
      *
      */
-    Error DeleteOperationalDataset(bool aIsActive);
+    Error DeleteOperationalDataset(MeshCoP::Dataset::Type aType);
 
     /**
      * This template method reads a specified settings entry.
@@ -920,7 +919,7 @@
      *
      * @tparam EntryType              The settings entry type.
      *
-     * @param[in] aEntry              The entry value to be saved.
+     * @param[in] aValue              The entry value to be saved.
      *
      * @retval kErrorNone             Successfully saved Network Info in settings.
      * @retval kErrorNotImplemented   The platform does not implement settings functionality.
@@ -1112,13 +1111,15 @@
     };
 #endif
 
+    static Key KeyForDatasetType(MeshCoP::Dataset::Type aType);
+
     Error ReadEntry(Key aKey, void *aValue, uint16_t aMaxLength) const;
     Error SaveEntry(Key aKey, const void *aValue, void *aPrev, uint16_t aLength);
     Error DeleteEntry(Key aKey);
 
     static void Log(Action aAction, Error aError, Key aKey, const void *aValue = nullptr);
 
-    static const uint16_t kCriticalKeys[];
+    static const uint16_t kSensitiveKeys[];
 };
 
 } // namespace ot
diff --git a/src/core/common/settings_driver.hpp b/src/core/common/settings_driver.hpp
index 6650dcc..f7851a2 100644
--- a/src/core/common/settings_driver.hpp
+++ b/src/core/common/settings_driver.hpp
@@ -66,13 +66,19 @@
     /**
      * This method initializes the settings storage driver.
      *
+     * @param[in]  aSensitiveKeys        A pointer to an array containing the list of sensitive keys.
+     * @param[in]  aSensitiveKeysLength  The number of entries in the @p aSensitiveKeys array.
+     *
      */
-    void Init(void)
+    void Init(const uint16_t *aSensitiveKeys, uint16_t aSensitiveKeysLength)
     {
 #if OPENTHREAD_CONFIG_PLATFORM_FLASH_API_ENABLE
+        OT_UNUSED_VARIABLE(aSensitiveKeys);
+        OT_UNUSED_VARIABLE(aSensitiveKeysLength);
+
         mFlash.Init();
 #else
-        otPlatSettingsInit(GetInstancePtr());
+        otPlatSettingsInit(GetInstancePtr(), aSensitiveKeys, aSensitiveKeysLength);
 #endif
     }
 
@@ -88,23 +94,6 @@
     }
 
     /**
-     * This method sets the critical keys that should be stored in a secure area.
-     *
-     * @param[in]  aKeys        A pointer to an array containing the list of critical keys.
-     * @param[in]  aKeysLength  The number of entries in the @p aKeys array.
-     *
-     */
-    void SetCriticalKeys(const uint16_t *aKeys, uint16_t aKeysLength)
-    {
-#if OPENTHREAD_CONFIG_PLATFORM_FLASH_API_ENABLE
-        OT_UNUSED_VARIABLE(aKeys);
-        OT_UNUSED_VARIABLE(aKeysLength);
-#else
-        otPlatSettingsSetCriticalKeys(GetInstancePtr(), aKeys, aKeysLength);
-#endif
-    }
-
-    /**
      * This method adds a value to @p aKey.
      *
      * @param[in]  aKey          The key associated with the value.
@@ -155,15 +144,15 @@
     /**
      * This method fetches the value identified by @p aKey at a given @p aIndex.
      *
-     * @param[in]     aKey          The key associated with the requested value.
-     * @param[in]     aIndex        The index of the specific item to get.
-     * @param[out]    aValue        A pointer to where the value of the setting should be written.
-     *                              May be `nullptr` if just testing for the presence or length of a key.
-     * @param[inout]  aValueLength  A pointer to the length of the value.
-     *                              When called, this should point to an integer containing the maximum bytes that
-     *                              can be written to @p aValue.
-     *                              At return, the actual length of the setting is written.
-     *                              May be `nullptr` if performing a presence check.
+     * @param[in]      aKey          The key associated with the requested value.
+     * @param[in]      aIndex        The index of the specific item to get.
+     * @param[out]     aValue        A pointer to where the value of the setting should be written.
+     *                               May be `nullptr` if just testing for the presence or length of a key.
+     * @param[in,out]  aValueLength  A pointer to the length of the value.
+     *                               When called, this should point to an integer containing the maximum bytes that
+     *                               can be written to @p aValue.
+     *                               At return, the actual length of the setting is written.
+     *                               May be `nullptr` if performing a presence check.
      *
      * @retval kErrorNone        The value was fetched successfully.
      * @retval kErrorNotFound    The key was not found.
@@ -185,14 +174,14 @@
     /**
      * This method fetches the value identified by @p aKey.
      *
-     * @param[in]     aKey          The key associated with the requested value.
-     * @param[out]    aValue        A pointer to where the value of the setting should be written.
-     *                              May be `nullptr` if just testing for the presence or length of a key.
-     * @param[inout]  aValueLength  A pointer to the length of the value.
-     *                              When called, this should point to an integer containing the maximum bytes that
-     *                              can be written to @p aValue.
-     *                              At return, the actual length of the setting is written.
-     *                              May be `nullptr` if performing a presence check.
+     * @param[in]      aKey          The key associated with the requested value.
+     * @param[out]     aValue        A pointer to where the value of the setting should be written.
+     *                               May be `nullptr` if just testing for the presence or length of a key.
+     * @param[in,out]  aValueLength  A pointer to the length of the value.
+     *                               When called, this should point to an integer containing the maximum bytes that
+     *                               can be written to @p aValue.
+     *                               At return, the actual length of the setting is written.
+     *                               May be `nullptr` if performing a presence check.
      *
      * @retval kErrorNone        The value was fetched successfully.
      * @retval kErrorNotFound    The key was not found.
diff --git a/src/core/common/string.hpp b/src/core/common/string.hpp
index 6a51600..a2ab1aa 100644
--- a/src/core/common/string.hpp
+++ b/src/core/common/string.hpp
@@ -158,7 +158,7 @@
 /**
  * This function converts all uppercase letter characters in a given string to lowercase.
  *
- * @param[inout] aString   A pointer to the string to convert.
+ * @param[in,out] aString   A pointer to the string to convert.
  *
  */
 void StringConvertToLowercase(char *aString);
@@ -166,7 +166,7 @@
 /**
  * This function converts all lowercase letter characters in a given string to uppercase.
  *
- * @param[inout] aString   A pointer to the string to convert.
+ * @param[in,out] aString   A pointer to the string to convert.
  *
  */
 void StringConvertToUppercase(char *aString);
diff --git a/src/core/common/timer.cpp b/src/core/common/timer.cpp
index 803f054..66de06d 100644
--- a/src/core/common/timer.cpp
+++ b/src/core/common/timer.cpp
@@ -38,7 +38,6 @@
 #include "common/debug.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
 
 namespace ot {
 
diff --git a/src/core/common/tlvs.hpp b/src/core/common/tlvs.hpp
index d624ff9..4dd55f4 100644
--- a/src/core/common/tlvs.hpp
+++ b/src/core/common/tlvs.hpp
@@ -331,7 +331,6 @@
      * @tparam       SimpleTlvType   The simple TLV type to find (must be a sub-class of `SimpleTlvInfo`)
      *
      * @param[in]    aMessage        A reference to the message.
-     * @param[in]    aType           The TLV type to search for.
      * @param[out]   aValue          A reference to the value object to output the read value.
      *
      * @retval kErrorNone         The TLV was found and read successfully. @p aValue is updated.
diff --git a/src/core/common/uptime.hpp b/src/core/common/uptime.hpp
index f1215bc..d6cab3c 100644
--- a/src/core/common/uptime.hpp
+++ b/src/core/common/uptime.hpp
@@ -92,8 +92,8 @@
      * The string follows the format "<hh>:<mm>:<ss>.<mmmm>" for hours, minutes, seconds and millisecond (if uptime is
      * shorter than one day) or "<dd>d.<hh>:<mm>:<ss>.<mmmm>" (if longer than a day).
      *
-     * @param[in]    aUptime  The uptime to convert.
-     * @param[inout] aWriter  A `StringWriter` to append the converted string to.
+     * @param[in]     aUptime  The uptime to convert.
+     * @param[in,out] aWriter  A `StringWriter` to append the converted string to.
      *
      */
     static void UptimeToString(uint64_t aUptime, StringWriter &aWriter);
diff --git a/src/core/config/border_router.h b/src/core/config/border_router.h
index 18b4d33..5e45b38 100644
--- a/src/core/config/border_router.h
+++ b/src/core/config/border_router.h
@@ -66,6 +66,16 @@
 #endif
 
 /**
+ * @def OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_DISCOVERED_PREFIXES
+ *
+ * Specifies maximum number of discovered prefixes (on-link prefixes on the infra link) maintained by routing manager.
+ *
+ */
+#ifndef OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_DISCOVERED_PREFIXES
+#define OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_DISCOVERED_PREFIXES 8
+#endif
+
+/**
  * @def OPENTHREAD_CONFIG_BORDER_ROUTING_VICARIOUS_RS_ENABLE
  *
  * Define to 1 to enable Border Routing Vicarious Router Solicitation.
diff --git a/src/core/config/logging.h b/src/core/config/logging.h
index d531e4a..aacec69 100644
--- a/src/core/config/logging.h
+++ b/src/core/config/logging.h
@@ -104,109 +104,9 @@
 #endif
 
 /**
- * @def OPENTHREAD_CONFIG_LOG_API
- *
- * Define to enable OpenThread API logging.
- *
- */
-#ifndef OPENTHREAD_CONFIG_LOG_API
-#define OPENTHREAD_CONFIG_LOG_API 1
-#endif
-
-/**
- * @def OPENTHREAD_CONFIG_LOG_MLE
- *
- * Define to enable MLE logging.
- *
- */
-#ifndef OPENTHREAD_CONFIG_LOG_MLE
-#define OPENTHREAD_CONFIG_LOG_MLE 1
-#endif
-
-/**
- * @def OPENTHREAD_CONFIG_LOG_MESHCOP
- *
- * Define to enable MeshCoP logging.
- *
- */
-#ifndef OPENTHREAD_CONFIG_LOG_MESHCOP
-#define OPENTHREAD_CONFIG_LOG_MESHCOP 1
-#endif
-
-/**
- * @def OPENTHREAD_CONFIG_LOG_ARP
- *
- * Define to enable EID-to-RLOC map logging.
- *
- */
-#ifndef OPENTHREAD_CONFIG_LOG_ARP
-#define OPENTHREAD_CONFIG_LOG_ARP 1
-#endif
-
-/**
- * @def OPENTHREAD_CONFIG_LOG_NETDATA
- *
- * Define to enable Network Data logging.
- *
- */
-#ifndef OPENTHREAD_CONFIG_LOG_NETDATA
-#define OPENTHREAD_CONFIG_LOG_NETDATA 1
-#endif
-
-/**
- * @def OPENTHREAD_CONFIG_LOG_ICMP
- *
- * Define to enable ICMPv6 logging.
- *
- */
-#ifndef OPENTHREAD_CONFIG_LOG_ICMP
-#define OPENTHREAD_CONFIG_LOG_ICMP 1
-#endif
-
-/**
- * @def OPENTHREAD_CONFIG_LOG_IP6
- *
- * Define to enable IPv6 logging.
- *
- */
-#ifndef OPENTHREAD_CONFIG_LOG_IP6
-#define OPENTHREAD_CONFIG_LOG_IP6 1
-#endif
-
-/**
- * @def OPENTHREAD_CONFIG_LOG_TCP
- *
- * Define to enable IPv6 logging.
- *
- */
-#ifndef OPENTHREAD_CONFIG_LOG_TCP
-#define OPENTHREAD_CONFIG_LOG_TCP 1
-#endif
-
-/**
- * @def OPENTHREAD_CONFIG_LOG_MAC
- *
- * Define to enable IEEE 802.15.4 MAC logging.
- *
- */
-#ifndef OPENTHREAD_CONFIG_LOG_MAC
-#define OPENTHREAD_CONFIG_LOG_MAC 1
-#endif
-
-/**
- * @def OPENTHREAD_CONFIG_LOG_MEM
- *
- * Define to enable memory logging.
- *
- */
-#ifndef OPENTHREAD_CONFIG_LOG_MEM
-#define OPENTHREAD_CONFIG_LOG_MEM 1
-#endif
-
-/**
  * @def OPENTHREAD_CONFIG_LOG_PKT_DUMP
  *
- * Define to enable log content of packets.
+ * Define to enable dump logs (of packets).
  *
  */
 #ifndef OPENTHREAD_CONFIG_LOG_PKT_DUMP
@@ -214,29 +114,9 @@
 #endif
 
 /**
- * @def OPENTHREAD_CONFIG_LOG_NETDIAG
- *
- * Define to enable network diagnostic logging.
- *
- */
-#ifndef OPENTHREAD_CONFIG_LOG_NETDIAG
-#define OPENTHREAD_CONFIG_LOG_NETDIAG 1
-#endif
-
-/**
- * @def OPENTHREAD_CONFIG_LOG_PLATFORM
- *
- * Define to enable platform region logging.
- *
- */
-#ifndef OPENTHREAD_CONFIG_LOG_PLATFORM
-#define OPENTHREAD_CONFIG_LOG_PLATFORM 1
-#endif
-
-/**
  * @def OPENTHREAD_CONFIG_LOG_CLI
  *
- * Define to enable CLI logging.
+ * Define to enable CLI logging and `otLogCli()` OT function.
  *
  */
 #ifndef OPENTHREAD_CONFIG_LOG_CLI
@@ -244,99 +124,13 @@
 #endif
 
 /**
- * @def OPENTHREAD_CONFIG_LOG_COAP
+ * @def OPENTHREAD_CONFIG_LOG_PLATFORM
  *
- * Define to enable COAP logging.
+ * Define to enable platform logging and `otLog{Level}Plat()` OT functions.
  *
  */
-#ifndef OPENTHREAD_CONFIG_LOG_COAP
-#define OPENTHREAD_CONFIG_LOG_COAP 1
-#endif
-
-/**
- * @def OPENTHREAD_CONFIG_LOG_CORE
- *
- * Define to enable OpenThread Core logging.
- *
- */
-#ifndef OPENTHREAD_CONFIG_LOG_CORE
-#define OPENTHREAD_CONFIG_LOG_CORE 1
-#endif
-
-/**
- * @def OPENTHREAD_CONFIG_LOG_UTIL
- *
- * Define to enable OpenThread Utility module logging.
- *
- */
-#ifndef OPENTHREAD_CONFIG_LOG_UTIL
-#define OPENTHREAD_CONFIG_LOG_UTIL 1
-#endif
-
-/**
- * @def OPENTHREAD_CONFIG_LOG_BBR
- *
- * Note: available since Thread 1.2.
- *
- * Define to enable Backbone Router (BBR) region logging.
- *
- */
-#ifndef OPENTHREAD_CONFIG_LOG_BBR
-#define OPENTHREAD_CONFIG_LOG_BBR 1
-#endif
-
-/**
- * @def OPENTHREAD_CONFIG_LOG_MLR
- *
- * Note: available since Thread 1.2.
- *
- * Define to enable Multicast Listener Registration (MLR) region logging.
- *
- */
-#ifndef OPENTHREAD_CONFIG_LOG_MLR
-#define OPENTHREAD_CONFIG_LOG_MLR 1
-#endif
-
-/**
- * @def OPENTHREAD_CONFIG_LOG_DUA
- *
- * Note: available since Thread 1.2.
- *
- * Define to enable Domain Unicast Address (DUA) region logging.
- *
- */
-#ifndef OPENTHREAD_CONFIG_LOG_DUA
-#define OPENTHREAD_CONFIG_LOG_DUA 1
-#endif
-
-/**
- * @def OPENTHREAD_CONFIG_LOG_BR
- *
- * Define to Border Router (BR) region logging.
- *
- */
-#ifndef OPENTHREAD_CONFIG_LOG_BR
-#define OPENTHREAD_CONFIG_LOG_BR 1
-#endif
-
-/**
- * @def OPENTHREAD_CONFIG_LOG_SRP
- *
- * Define to enable Service Registration Protocol (SRP) region logging.
- *
- */
-#ifndef OPENTHREAD_CONFIG_LOG_SRP
-#define OPENTHREAD_CONFIG_LOG_SRP 1
-#endif
-
-/**
- * @def OPENTHREAD_CONFIG_LOG_DNS
- *
- * Define to enable DNS region logging.
- *
- */
-#ifndef OPENTHREAD_CONFIG_LOG_DNS
-#define OPENTHREAD_CONFIG_LOG_DNS 1
+#ifndef OPENTHREAD_CONFIG_LOG_PLATFORM
+#define OPENTHREAD_CONFIG_LOG_PLATFORM 1
 #endif
 
 /**
@@ -360,16 +154,6 @@
 #endif
 
 /**
- * @def OPENTHREAD_CONFIG_LOG_PREPEND_REGION
- *
- * Define to prepend the log region to all log messages.
- *
- */
-#ifndef OPENTHREAD_CONFIG_LOG_PREPEND_REGION
-#define OPENTHREAD_CONFIG_LOG_PREPEND_REGION 1
-#endif
-
-/**
  * @def OPENTHREAD_CONFIG_LOG_SUFFIX
  *
  * Define suffix to append at the end of logs.
diff --git a/src/core/config/mac.h b/src/core/config/mac.h
index e244425..6572dd0 100644
--- a/src/core/config/mac.h
+++ b/src/core/config/mac.h
@@ -86,6 +86,66 @@
 #endif
 
 /**
+ * @def OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
+ *
+ * Define as 1 to add random backoff delay in between frame transmission retries when the previous attempt resulted in
+ * no-ack error.
+ *
+ */
+#ifndef OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
+#define OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY 1
+#endif
+
+/**
+ * @def OPENTHREAD_CONFIG_MAC_RETX_DELAY_MIN_BACKOFF_EXPONENT
+ *
+ * Specifies the minimum backoff exponent to start with when adding random delay in between frame transmission
+ * retries on no-ack error. It is applicable only when `OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY`
+ * is enabled.
+ *
+ */
+#ifndef OPENTHREAD_CONFIG_MAC_RETX_DELAY_MIN_BACKOFF_EXPONENT
+#define OPENTHREAD_CONFIG_MAC_RETX_DELAY_MIN_BACKOFF_EXPONENT 0
+#endif
+
+/**
+ * @def OPENTHREAD_CONFIG_MAC_RETX_DELAY_MAX_BACKOFF_EXPONENT
+ *
+ * Specifies the maximum backoff exponent when adding random delay in between frame transmission retries on no-ack
+ * error. It is applicable only when `OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY` is enabled.
+ *
+ */
+#ifndef OPENTHREAD_CONFIG_MAC_RETX_DELAY_MAX_BACKOFF_EXPONENT
+#define OPENTHREAD_CONFIG_MAC_RETX_DELAY_MAX_BACKOFF_EXPONENT 5
+#endif
+
+/**
+ * @def OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE
+ *
+ * Define as 1 to enable collision avoidance delay feature, which adds a delay wait time after a successful frame tx
+ * to a neighbor which is expected to forward the frame. This delay is applied before the next direct frame tx (towards
+ * any neighbor) on an FTD.
+ *
+ * The delay interval is specified by `OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_INTERVAL` (in milliseconds).
+ *
+ */
+#ifndef OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE
+#define OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE 1
+#endif
+
+/**
+ * @def OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_INTERVAL
+ *
+ * Specifies the collision avoidance delay interval in milliseconds. This is added after a successful frame tx to a
+ * neighbor that is expected to forward the frame (when `OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE` is
+ * enabled).
+ *
+ */
+#ifndef OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_INTERVAL
+#define OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_INTERVAL 8
+#endif
+
+/**
  * @def OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_ENABLE
  *
  * Define to 1 to enable MAC retry packets histogram analysis.
diff --git a/src/core/config/openthread-core-default-config.h b/src/core/config/misc.h
similarity index 98%
rename from src/core/config/openthread-core-default-config.h
rename to src/core/config/misc.h
index c73c7f6..27a11e8 100644
--- a/src/core/config/openthread-core-default-config.h
+++ b/src/core/config/misc.h
@@ -28,12 +28,11 @@
 
 /**
  * @file
- *   This file includes default compile-time configuration constants
- *   for OpenThread.
+ *   This file includes miscellaneous compile-time configuration constants for OpenThread.
  */
 
-#ifndef OPENTHREAD_CORE_DEFAULT_CONFIG_H_
-#define OPENTHREAD_CORE_DEFAULT_CONFIG_H_
+#ifndef CONFIG_MISC_H_
+#define CONFIG_MISC_H_
 
 #include "config/coap.h"
 #include "config/srp_server.h"
@@ -527,4 +526,4 @@
 #define OPENTHREAD_CONFIG_NEIGHBOR_DISCOVERY_AGENT_ENABLE 0
 #endif
 
-#endif // OPENTHREAD_CORE_DEFAULT_CONFIG_H_
+#endif // CONFIG_MISC_H_
diff --git a/src/core/config/mle.h b/src/core/config/mle.h
index 068ca76..872e1a7 100644
--- a/src/core/config/mle.h
+++ b/src/core/config/mle.h
@@ -287,7 +287,7 @@
  *
  */
 #ifndef OPENTHREAD_CONFIG_MLE_LINK_METRICS_MAX_SERIES_SUPPORTED
-#define OPENTHREAD_CONFIG_MLE_LINK_METRICS_MAX_SERIES_SUPPORTED 10
+#define OPENTHREAD_CONFIG_MLE_LINK_METRICS_MAX_SERIES_SUPPORTED OPENTHREAD_CONFIG_MLE_MAX_CHILDREN
 #endif
 
 #endif // CONFIG_MLE_H_
diff --git a/src/core/config/openthread-core-config-check.h b/src/core/config/openthread-core-config-check.h
index b90e041..d7975d8 100644
--- a/src/core/config/openthread-core-config-check.h
+++ b/src/core/config/openthread-core-config-check.h
@@ -530,4 +530,93 @@
 #error "OPENTHREAD_CONFIG_UNSECURE_TRAFFIC_MANAGED_BY_STACK_ENABLE was removed and no longer supported"
 #endif
 
+#ifdef OPENTHREAD_CONFIG_LOG_API
+#error "OPENTHREAD_CONFIG_LOG_API was removed and no longer supported"
+#endif
+
+#ifdef OPENTHREAD_CONFIG_LOG_MLE
+#error "OPENTHREAD_CONFIG_LOG_MLE was removed and no longer supported"
+#endif
+
+#ifdef OPENTHREAD_CONFIG_LOG_MESHCOP
+#error "OPENTHREAD_CONFIG_LOG_MESHCOP was removed and no longer supported"
+#endif
+
+#ifdef OPENTHREAD_CONFIG_LOG_ARP
+#error "OPENTHREAD_CONFIG_LOG_ARP was removed and no longer supported"
+#endif
+
+#ifdef OPENTHREAD_CONFIG_LOG_NETDATA
+#error "OPENTHREAD_CONFIG_LOG_NETDATA was removed and no longer supported"
+#endif
+
+#ifdef OPENTHREAD_CONFIG_LOG_ICMP
+#error "OPENTHREAD_CONFIG_LOG_ICMP was removed and no longer supported"
+#endif
+
+#ifdef OPENTHREAD_CONFIG_LOG_IP6
+#error "OPENTHREAD_CONFIG_LOG_IP6 was removed and no longer supported"
+#endif
+
+#ifdef OPENTHREAD_CONFIG_LOG_TCP
+#error "OPENTHREAD_CONFIG_LOG_TCP was removed and no longer supported"
+#endif
+
+#ifdef OPENTHREAD_CONFIG_LOG_MAC
+#error "OPENTHREAD_CONFIG_LOG_MAC was removed and no longer supported"
+#endif
+
+#ifdef OPENTHREAD_CONFIG_LOG_MEM
+#error "OPENTHREAD_CONFIG_LOG_MEM was removed and no longer supported"
+#endif
+
+#ifdef OPENTHREAD_CONFIG_LOG_NETDIAG
+#error "OPENTHREAD_CONFIG_LOG_NETDIAG was removed and no longer supported"
+#endif
+
+#ifdef OPENTHREAD_CONFIG_LOG_COAP
+#error "OPENTHREAD_CONFIG_LOG_COAP was removed and no longer supported"
+#endif
+
+#ifdef OPENTHREAD_CONFIG_LOG_CORE
+#error "OPENTHREAD_CONFIG_LOG_CORE was removed and no longer supported"
+#endif
+
+#ifdef OPENTHREAD_CONFIG_LOG_UTIL
+#error "OPENTHREAD_CONFIG_LOG_UTIL was removed and no longer supported"
+#endif
+
+#ifdef OPENTHREAD_CONFIG_LOG_BBR
+#error "OPENTHREAD_CONFIG_LOG_BBR was removed and no longer supported"
+#endif
+
+#ifdef OPENTHREAD_CONFIG_LOG_MLR
+#error "OPENTHREAD_CONFIG_LOG_MLR was removed and no longer supported"
+#endif
+
+#ifdef OPENTHREAD_CONFIG_LOG_DUA
+#error "OPENTHREAD_CONFIG_LOG_DUA was removed and no longer supported"
+#endif
+
+#ifdef OPENTHREAD_CONFIG_LOG_BR
+#error "OPENTHREAD_CONFIG_LOG_BR was removed and no longer supported"
+#endif
+
+#ifdef OPENTHREAD_CONFIG_LOG_SRP
+#error "OPENTHREAD_CONFIG_LOG_SRP was removed and no longer supported"
+#endif
+
+#ifdef OPENTHREAD_CONFIG_LOG_DNS
+#error "OPENTHREAD_CONFIG_LOG_DNS was removed and no longer supported"
+#endif
+
+#ifdef OPENTHREAD_CONFIG_LOG_PREPEND_REGION
+#error "OPENTHREAD_CONFIG_LOG_PREPEND_REGION was removed and not longer supported"
+#endif
+
+#ifdef OPENTHREAD_CONFIG_SRP_SERVER_MAX_ADDRESSES_NUM
+#error "OPENTHREAD_CONFIG_SRP_SERVER_MAX_ADDRESSES_NUM was removed. "\
+       "SRP host uses dynamic heap array to store addresses so no need for config on max number of addresses".
+#endif
+
 #endif // OPENTHREAD_CORE_CONFIG_CHECK_H_
diff --git a/src/core/config/platform.h b/src/core/config/platform.h
index 83581fe..ff47f11 100644
--- a/src/core/config/platform.h
+++ b/src/core/config/platform.h
@@ -148,6 +148,16 @@
 #define OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE 0
 #endif
 
+/**
+ * @def OPENTHREAD_CONFIG_PLATFORM_MAC_KEYS_EXPORTABLE_ENABLE
+ *
+ * Define to 1 if you want to make MAC keys exportable.
+ *
+ */
+#ifndef OPENTHREAD_CONFIG_PLATFORM_MAC_KEYS_EXPORTABLE_ENABLE
+#define OPENTHREAD_CONFIG_PLATFORM_MAC_KEYS_EXPORTABLE_ENABLE 0
+#endif
+
 #if OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_SUPPORT
 #if (!defined(OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_PAGE) || \
      !defined(OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_MIN) ||  \
diff --git a/src/core/config/radio_link.h b/src/core/config/radio_link.h
index 1179261..e82ea96 100644
--- a/src/core/config/radio_link.h
+++ b/src/core/config/radio_link.h
@@ -72,4 +72,14 @@
 #define OPENTHREAD_CONFIG_MULTI_RADIO 0
 #endif
 
+/**
+ * @def OPENTHREAD_CONFIG_MULTI_RADIO_FRAG_TAG_TIMEOUT
+ *
+ * Specifies the fragment tag timeout interval in milliseconds.
+ *
+ */
+#ifndef OPENTHREAD_CONFIG_MULTI_RADIO_FRAG_TAG_TIMEOUT
+#define OPENTHREAD_CONFIG_MULTI_RADIO_FRAG_TAG_TIMEOUT (20 * 1000)
+#endif
+
 #endif // CONFIG_RADIO_LINK_H_
diff --git a/src/core/config/srp_server.h b/src/core/config/srp_server.h
index 7bc5aef..aeffbdf 100644
--- a/src/core/config/srp_server.h
+++ b/src/core/config/srp_server.h
@@ -109,14 +109,4 @@
 #define OPENTHREAD_CONFIG_SRP_SERVER_SERVICE_UPDATE_TIMEOUT ((4 * 250u) + 250u)
 #endif
 
-/**
- * @def OPENTHREAD_CONFIG_SRP_SERVER_MAX_ADDRESSES_NUM
- *
- * Specifies the maximum number of addresses the SRP server can handle for a host.
- *
- */
-#ifndef OPENTHREAD_CONFIG_SRP_SERVER_MAX_ADDRESSES_NUM
-#define OPENTHREAD_CONFIG_SRP_SERVER_MAX_ADDRESSES_NUM 2
-#endif
-
 #endif // CONFIG_SRP_SERVER_H_
diff --git a/src/core/config/tmf.h b/src/core/config/tmf.h
index cf3c062..538ccaf 100644
--- a/src/core/config/tmf.h
+++ b/src/core/config/tmf.h
@@ -213,6 +213,7 @@
 #endif
 
 /**
+ * @def OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
  *
  * This setting configures the Multicast Listener Registration parent proxing in Thread 1.2.
  *
diff --git a/src/core/crypto/aes_ccm.cpp b/src/core/crypto/aes_ccm.cpp
index a3ed5e5..49c42f2 100644
--- a/src/core/crypto/aes_ccm.cpp
+++ b/src/core/crypto/aes_ccm.cpp
@@ -248,6 +248,21 @@
     }
 }
 
+#if !OPENTHREAD_RADIO
+void AesCcm::Payload(Message &aMessage, uint16_t aOffset, uint16_t aLength, Mode aMode)
+{
+    Message::MutableChunk chunk;
+
+    aMessage.GetFirstChunk(aOffset, aLength, chunk);
+
+    while (chunk.GetLength() > 0)
+    {
+        Payload(chunk.GetBytes(), chunk.GetBytes(), chunk.GetLength(), aMode);
+        aMessage.GetNextChunk(aLength, chunk);
+    }
+}
+#endif
+
 void AesCcm::Finalize(void *aTag)
 {
     uint8_t *tagBytes = reinterpret_cast<uint8_t *>(aTag);
diff --git a/src/core/crypto/aes_ccm.hpp b/src/core/crypto/aes_ccm.hpp
index 34e50f7..0234243 100644
--- a/src/core/crypto/aes_ccm.hpp
+++ b/src/core/crypto/aes_ccm.hpp
@@ -40,6 +40,8 @@
 
 #include <openthread/platform/crypto.h>
 #include "common/error.hpp"
+#include "common/message.hpp"
+#include "common/type_traits.hpp"
 #include "crypto/aes_ecb.hpp"
 #include "crypto/storage.hpp"
 #include "mac/mac_types.hpp"
@@ -126,16 +128,46 @@
     void Header(const void *aHeader, uint32_t aHeaderLength);
 
     /**
+     * This method processes the header.
+     *
+     * @tparam    ObjectType   The object type.
+     *
+     * @param[in] aObject      A reference to the object to add to header.
+     *
+     */
+    template <typename ObjectType> void Header(const ObjectType &aObject)
+    {
+        static_assert(!TypeTraits::IsPointer<ObjectType>::kValue, "ObjectType must not be a pointer");
+
+        Header(&aObject, sizeof(ObjectType));
+    }
+
+    /**
      * This method processes the payload.
      *
-     * @param[inout]  aPlainText   A pointer to the plaintext.
-     * @param[inout]  aCipherText  A pointer to the ciphertext.
-     * @param[in]     aLength      Payload length in bytes.
-     * @param[in]     aMode        Mode to indicate whether to encrypt (`kEncrypt`) or decrypt (`kDecrypt`).
+     * @param[in,out]  aPlainText   A pointer to the plaintext.
+     * @param[in,out]  aCipherText  A pointer to the ciphertext.
+     * @param[in]      aLength      Payload length in bytes.
+     * @param[in]      aMode        Mode to indicate whether to encrypt (`kEncrypt`) or decrypt (`kDecrypt`).
      *
      */
     void Payload(void *aPlainText, void *aCipherText, uint32_t aLength, Mode aMode);
 
+#if !OPENTHREAD_RADIO
+    /**
+     * This method processes the payload within a given message.
+     *
+     * This method encrypts/decrypts the payload content in place within the @p aMessage.
+     *
+     * @param[in,out]  aMessage     The message to read from and update.
+     * @param[in]      aOffset      The offset in @p aMessage to start of payload.
+     * @param[in]      aLength      Payload length in bytes.
+     * @param[in]      aMode        Mode to indicate whether to encrypt (`kEncrypt`) or decrypt (`kDecrypt`).
+     *
+     */
+    void Payload(Message &aMessage, uint16_t aOffset, uint16_t aLength, Mode aMode);
+#endif
+
     /**
      * This method returns the tag length in bytes.
      *
diff --git a/src/core/crypto/ecdsa.cpp b/src/core/crypto/ecdsa.cpp
index f3ea081..b91e4e1 100644
--- a/src/core/crypto/ecdsa.cpp
+++ b/src/core/crypto/ecdsa.cpp
@@ -35,6 +35,8 @@
 
 #if OPENTHREAD_CONFIG_ECDSA_ENABLE
 
+#ifndef MBEDTLS_USE_TINYCRYPT
+
 #include <string.h>
 
 #include <mbedtls/ctr_drbg.h>
@@ -268,4 +270,5 @@
 } // namespace Crypto
 } // namespace ot
 
+#endif // MBEDTLS_USE_TINYCRYPT
 #endif // OPENTHREAD_CONFIG_ECDSA_ENABLE
diff --git a/src/core/crypto/ecdsa.hpp b/src/core/crypto/ecdsa.hpp
index 6f9d9b3..98b439b 100644
--- a/src/core/crypto/ecdsa.hpp
+++ b/src/core/crypto/ecdsa.hpp
@@ -265,12 +265,12 @@
 /**
  * This function creates an ECDSA signature.
  *
- * @param[out]    aOutput            An output buffer where ECDSA sign should be stored.
- * @param[inout]  aOutputLength      The length of the @p aOutput buffer.
- * @param[in]     aInputHash         An input hash.
- * @param[in]     aInputHashLength   The length of the @p aInputHash buffer.
- * @param[in]     aPrivateKey        A private key in PEM format.
- * @param[in]     aPrivateKeyLength  The length of the @p aPrivateKey buffer.
+ * @param[out]     aOutput            An output buffer where ECDSA sign should be stored.
+ * @param[in,out]  aOutputLength      The length of the @p aOutput buffer.
+ * @param[in]      aInputHash         An input hash.
+ * @param[in]      aInputHashLength   The length of the @p aInputHash buffer.
+ * @param[in]      aPrivateKey        A private key in PEM format.
+ * @param[in]      aPrivateKeyLength  The length of the @p aPrivateKey buffer.
  *
  * @retval  kErrorNone         ECDSA sign has been created successfully.
  * @retval  kErrorNoBufs       Output buffer is too small.
diff --git a/src/core/crypto/ecdsa_tinycrypt.cpp b/src/core/crypto/ecdsa_tinycrypt.cpp
new file mode 100644
index 0000000..ea75a2d
--- /dev/null
+++ b/src/core/crypto/ecdsa_tinycrypt.cpp
@@ -0,0 +1,211 @@
+/*
+ *  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.
+ */
+
+/**
+ * @file
+ *   This file implements ECDSA signing using TinyCrypt library.
+ */
+
+#include "ecdsa.hpp"
+
+#if OPENTHREAD_CONFIG_ECDSA_ENABLE
+
+#ifdef MBEDTLS_USE_TINYCRYPT
+
+#include <string.h>
+
+#include <mbedtls/pk.h>
+#include <mbedtls/version.h>
+
+#include <tinycrypt/ecc.h>
+#include <tinycrypt/ecc_dh.h>
+#include <tinycrypt/ecc_dsa.h>
+
+#include "common/code_utils.hpp"
+#include "common/debug.hpp"
+#include "common/random.hpp"
+#include "crypto/mbedtls.hpp"
+
+namespace ot {
+namespace Crypto {
+namespace Ecdsa {
+
+Error P256::KeyPair::Generate(void)
+{
+    mbedtls_pk_context    pk;
+    mbedtls_uecc_keypair *keypair;
+    int                   ret;
+
+    mbedtls_pk_init(&pk);
+
+    ret = mbedtls_pk_setup(&pk, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY));
+    VerifyOrExit(ret == 0);
+
+    keypair = mbedtls_pk_uecc(pk);
+
+    ret = uECC_make_key(keypair->public_key, keypair->private_key);
+    VerifyOrExit(ret == UECC_SUCCESS);
+
+    ret = mbedtls_pk_write_key_der(&pk, mDerBytes, sizeof(mDerBytes));
+    VerifyOrExit(ret > 0);
+
+    mDerLength = static_cast<uint8_t>(ret);
+
+    memmove(mDerBytes, mDerBytes + sizeof(mDerBytes) - mDerLength, mDerLength);
+
+exit:
+    mbedtls_pk_free(&pk);
+
+    return (ret >= 0) ? kErrorNone : MbedTls::MapError(ret);
+}
+
+Error P256::KeyPair::Parse(void *aContext) const
+{
+    Error               error = kErrorNone;
+    mbedtls_pk_context *pk    = reinterpret_cast<mbedtls_pk_context *>(aContext);
+
+    mbedtls_pk_init(pk);
+
+    VerifyOrExit(mbedtls_pk_setup(pk, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY)) == 0, error = kErrorFailed);
+#if (MBEDTLS_VERSION_NUMBER >= 0x03000000)
+    VerifyOrExit(mbedtls_pk_parse_key(pk, mDerBytes, mDerLength, nullptr, 0, MbedTls::CryptoSecurePrng, nullptr) == 0,
+                 error = kErrorParse);
+#else
+    VerifyOrExit(mbedtls_pk_parse_key(pk, mDerBytes, mDerLength, nullptr, 0) == 0, error = kErrorParse);
+#endif
+
+exit:
+    return error;
+}
+
+Error P256::KeyPair::GetPublicKey(PublicKey &aPublicKey) const
+{
+    Error                 error;
+    mbedtls_pk_context    pk;
+    mbedtls_uecc_keypair *keyPair;
+    int                   ret;
+
+    SuccessOrExit(error = Parse(&pk));
+
+    keyPair = mbedtls_pk_uecc(pk);
+
+    memcpy(aPublicKey.mData, keyPair->public_key, kMpiSize);
+    memcpy(aPublicKey.mData + kMpiSize, keyPair->public_key + kMpiSize, kMpiSize);
+
+exit:
+    mbedtls_pk_free(&pk);
+
+    return error;
+}
+
+Error P256::KeyPair::Sign(const Sha256::Hash &aHash, Signature &aSignature) const
+{
+    Error                 error;
+    mbedtls_pk_context    pk;
+    mbedtls_uecc_keypair *keypair;
+    int                   ret;
+    uint8_t               sig[2 * kMpiSize];
+
+    SuccessOrExit(error = Parse(&pk));
+
+    keypair = mbedtls_pk_uecc(pk);
+
+    ret = uECC_sign(keypair->private_key, aHash.GetBytes(), Sha256::Hash::kSize, sig);
+    VerifyOrExit(ret == UECC_SUCCESS, error = MbedTls::MapError(ret));
+
+    memcpy(aSignature.mShared.mMpis.mR, sig, kMpiSize);
+    memcpy(aSignature.mShared.mMpis.mS, sig + kMpiSize, kMpiSize);
+
+exit:
+    mbedtls_pk_free(&pk);
+
+    return error;
+}
+
+Error P256::PublicKey::Verify(const Sha256::Hash &aHash, const Signature &aSignature) const
+{
+    Error   error = kErrorNone;
+    int     ret;
+    uint8_t public_key[2 * kMpiSize];
+    uint8_t sig[2 * kMpiSize];
+
+    memcpy(public_key, GetBytes(), 2 * kMpiSize);
+
+    memcpy(sig, aSignature.mShared.mMpis.mR, kMpiSize);
+    memcpy(sig + kMpiSize, aSignature.mShared.mMpis.mS, kMpiSize);
+
+    ret = uECC_verify(public_key, aHash.GetBytes(), Sha256::Hash::kSize, sig);
+    VerifyOrExit(ret == UECC_SUCCESS, error = kErrorSecurity);
+
+exit:
+    return error;
+}
+
+Error Sign(uint8_t *      aOutput,
+           uint16_t &     aOutputLength,
+           const uint8_t *aInputHash,
+           uint16_t       aInputHashLength,
+           const uint8_t *aPrivateKey,
+           uint16_t       aPrivateKeyLength)
+{
+    Error                 error = kErrorNone;
+    mbedtls_pk_context    pkCtx;
+    mbedtls_uecc_keypair *keypair;
+    uint8_t               sig[2 * NUM_ECC_BYTES];
+
+    mbedtls_pk_init(&pkCtx);
+
+    // Parse a private key in PEM format.
+    VerifyOrExit(mbedtls_pk_parse_key(&pkCtx, aPrivateKey, aPrivateKeyLength, nullptr, 0) == 0,
+                 error = kErrorInvalidArgs);
+    VerifyOrExit(mbedtls_pk_get_type(&pkCtx) == MBEDTLS_PK_ECKEY, error = kErrorInvalidArgs);
+
+    keypair = mbedtls_pk_uecc(pkCtx);
+    OT_ASSERT(keypair != nullptr);
+
+    // Sign using ECDSA.
+    VerifyOrExit(uECC_sign(keypair->private_key, aInputHash, aInputHashLength, sig) == UECC_SUCCESS,
+                 error = kErrorFailed);
+    VerifyOrExit(2 * NUM_ECC_BYTES <= aOutputLength, error = kErrorNoBufs);
+
+    // Concatenate the two octet sequences in the order R and then S.
+    memcpy(aOutput, sig, 2 * NUM_ECC_BYTES);
+    aOutputLength = 2 * NUM_ECC_BYTES;
+
+exit:
+    mbedtls_pk_free(&pkCtx);
+
+    return error;
+}
+
+} // namespace Ecdsa
+} // namespace Crypto
+} // namespace ot
+
+#endif // MBEDTLS_USE_TINYCRYPT
+#endif // OPENTHREAD_CONFIG_ECDSA_ENABLE
diff --git a/src/core/crypto/storage.hpp b/src/core/crypto/storage.hpp
index fd2273a..725ab27 100644
--- a/src/core/crypto/storage.hpp
+++ b/src/core/crypto/storage.hpp
@@ -120,13 +120,13 @@
 /**
  * Import a key into PSA ITS.
  *
- * @param[inout] aKeyRef           Reference to the key ref to be used for crypto operations.
- * @param[in]    aKeyType          Key Type encoding for the key.
- * @param[in]    aKeyAlgorithm     Key algorithm encoding for the key.
- * @param[in]    aKeyUsage         Key Usage encoding for the key.
- * @param[in]    aStorageType      Key storage type.
- * @param[in]    aKey              Actual key to be imported.
- * @param[in]    aKeyLen           Length of the key to be imported.
+ * @param[in,out] aKeyRef          Reference to the key ref to be used for crypto operations.
+ * @param[in]     aKeyType         Key Type encoding for the key.
+ * @param[in]     aKeyAlgorithm    Key algorithm encoding for the key.
+ * @param[in]     aKeyUsage        Key Usage encoding for the key.
+ * @param[in]     aStorageType     Key storage type.
+ * @param[in]     aKey             Actual key to be imported.
+ * @param[in]     aKeyLen          Length of the key to be imported.
  *
  * @retval kErrorNone          Successfully imported the key.
  * @retval kErrorFailed        Failed to import the key.
@@ -280,9 +280,9 @@
      *
      * This method MUST be used when `IsKeyRef()` returns `true`.
      *
-     * @param[out]    aKeyBuffer  Pointer to a byte array buffer to place the extracted key.
-     * @param[inout]  aKeyLength  On input, the size of @p aKeyBuffer.
-     *                            On exit, returns the key length (number of bytes written in @p aKeyBuffer).
+     * @param[out]     aKeyBuffer  Pointer to a byte array buffer to place the extracted key.
+     * @param[in,out]  aKeyLength  On input, the size of @p aKeyBuffer.
+     *                             On exit, returns the key length (number of bytes written in @p aKeyBuffer).
      *
      * @retval kErrorNone    Successfully extracted the key, @p aKeyBuffer and @p aKeyLength are updated.
      * @retval kErrorNoBufs  Key does not fit in @p aKeyBuffer (extracted key length is larger than @p aKeyLength).
diff --git a/src/core/diags/README.md b/src/core/diags/README.md
index ba2a9ec..090aa03 100644
--- a/src/core/diags/README.md
+++ b/src/core/diags/README.md
@@ -171,3 +171,63 @@
 stop diagnostics mode
 status 0x00
 ```
+
+### diag rcp
+
+RCP-related diagnostics commands. These commands are used for debugging and testing only.
+
+#### diag rcp start
+
+Start RCP diagnostics mode.
+
+```bash
+> diag rcp start
+Done
+```
+
+#### diag rcp stop
+
+Stop RCP diagnostics mode.
+
+```bash
+> diag rcp stop
+Done
+```
+
+#### diag rcp channel \<channel\>
+
+Set the RCP IEEE 802.15.4 Channel value for diagnostics module.
+
+```bash
+> diag rcp channel 11
+Done
+```
+
+#### diag rcp power \<power\>
+
+Set the RCP tx power value(dBm) for diagnostics module.
+
+```bash
+> diag rcp power 0
+Done
+```
+
+#### diag rcp echo \<message\>
+
+RCP echoes the given message.
+
+```bash
+> diag rcp echo 0123456789
+0123456789
+Done
+```
+
+#### diag rcp echo -n \<number\>
+
+RCP echoes the message with the given number of bytes.
+
+```bash
+> diag rcp echo -n 20
+01234567890123456789
+Done
+```
diff --git a/src/core/diags/factory_diags.cpp b/src/core/diags/factory_diags.cpp
index 5eeec19..6410421 100644
--- a/src/core/diags/factory_diags.cpp
+++ b/src/core/diags/factory_diags.cpp
@@ -70,10 +70,8 @@
 #if OPENTHREAD_RADIO && !OPENTHREAD_RADIO_CLI
 
 const struct Diags::Command Diags::sCommands[] = {
-    {"channel", &Diags::ProcessChannel},
-    {"power", &Diags::ProcessPower},
-    {"start", &Diags::ProcessStart},
-    {"stop", &Diags::ProcessStop},
+    {"channel", &Diags::ProcessChannel}, {"echo", &Diags::ProcessEcho}, {"power", &Diags::ProcessPower},
+    {"start", &Diags::ProcessStart},     {"stop", &Diags::ProcessStop},
 };
 
 Diags::Diags(Instance &aInstance)
@@ -114,6 +112,43 @@
     return error;
 }
 
+Error Diags::ProcessEcho(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen)
+{
+    Error error = kErrorNone;
+
+    if (aArgsLength == 1)
+    {
+        snprintf(aOutput, aOutputMaxLen, "%s\r\n", aArgs[0]);
+    }
+    else if ((aArgsLength == 2) && (strcmp(aArgs[0], "-n") == 0))
+    {
+        const uint8_t kReservedLen = 3; // 1 byte '\r', 1 byte '\n' and 1 byte '\0'
+        uint32_t      outputMaxLen = static_cast<uint32_t>(aOutputMaxLen) - kReservedLen;
+        long          value;
+        uint32_t      i;
+        uint32_t      number;
+
+        SuccessOrExit(error = ParseLong(aArgs[1], value));
+        number = static_cast<uint32_t>(value);
+        number = (number < outputMaxLen) ? number : outputMaxLen;
+
+        for (i = 0; i < number; i++)
+        {
+            aOutput[i] = '0' + i % 10;
+        }
+
+        snprintf(&aOutput[i], aOutputMaxLen - i, "\r\n");
+    }
+    else
+    {
+        error = kErrorInvalidArgs;
+    }
+
+exit:
+    AppendErrorResult(error, aOutput, aOutputMaxLen);
+    return error;
+}
+
 Error Diags::ProcessStart(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen)
 {
     OT_UNUSED_VARIABLE(aArgsLength);
diff --git a/src/core/diags/factory_diags.hpp b/src/core/diags/factory_diags.hpp
index 69301f9..26ccd9e 100644
--- a/src/core/diags/factory_diags.hpp
+++ b/src/core/diags/factory_diags.hpp
@@ -151,6 +151,9 @@
     Error ProcessStart(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen);
     Error ProcessStats(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen);
     Error ProcessStop(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen);
+#if OPENTHREAD_RADIO && !OPENTHREAD_RADIO_CLI
+    Error ProcessEcho(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen);
+#endif
 
     void TransmitPacket(void);
 
diff --git a/src/core/mac/channel_mask.hpp b/src/core/mac/channel_mask.hpp
index e92eca1..200ab5b 100644
--- a/src/core/mac/channel_mask.hpp
+++ b/src/core/mac/channel_mask.hpp
@@ -200,7 +200,7 @@
      * This method can be used to iterate over all channels in the channel mask. To get the first channel (channel with
      * lowest number) in the mask the @p aChannel should be set to `kChannelIteratorFirst`.
      *
-     * @param[inout] aChannel        A reference to a `uint8_t`.
+     * @param[in,out] aChannel       A reference to a `uint8_t`.
      *                               On entry it should contain the previous channel or `kChannelIteratorFirst`.
      *                               On exit it contains the next channel.
      *
diff --git a/src/core/mac/data_poll_handler.cpp b/src/core/mac/data_poll_handler.cpp
index 8703ab0..c1d78e3 100644
--- a/src/core/mac/data_poll_handler.cpp
+++ b/src/core/mac/data_poll_handler.cpp
@@ -38,10 +38,12 @@
 #include "common/code_utils.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 
 namespace ot {
 
+RegisterLogModule("DataPollHandlr");
+
 DataPollHandler::Callbacks::Callbacks(Instance &aInstance)
     : InstanceLocator(aInstance)
 {
@@ -144,8 +146,8 @@
 
     indirectMsgCount = child->GetIndirectMessageCount();
 
-    otLogInfoMac("Rx data poll, src:0x%04x, qed_msgs:%d, rss:%d, ack-fp:%d", child->GetRloc16(), indirectMsgCount,
-                 aFrame.GetRssi(), aFrame.IsAckedWithFramePending());
+    LogInfo("Rx data poll, src:0x%04x, qed_msgs:%d, rss:%d, ack-fp:%d", child->GetRloc16(), indirectMsgCount,
+            aFrame.GetRssi(), aFrame.IsAckedWithFramePending());
 
     if (!aFrame.IsAckedWithFramePending())
     {
@@ -245,8 +247,8 @@
         OT_ASSERT(!aFrame.GetSecurityEnabled() || aFrame.IsHeaderUpdated());
 
         aChild.IncrementIndirectTxAttempts();
-        otLogInfoMac("Indirect tx to child %04x failed, attempt %d/%d", aChild.GetRloc16(),
-                     aChild.GetIndirectTxAttempts(), kMaxPollTriggeredTxAttempts);
+        LogInfo("Indirect tx to child %04x failed, attempt %d/%d", aChild.GetRloc16(), aChild.GetIndirectTxAttempts(),
+                kMaxPollTriggeredTxAttempts);
 
         OT_FALL_THROUGH;
 
diff --git a/src/core/mac/data_poll_sender.cpp b/src/core/mac/data_poll_sender.cpp
index 1756b1a..ab1ad0d 100644
--- a/src/core/mac/data_poll_sender.cpp
+++ b/src/core/mac/data_poll_sender.cpp
@@ -36,7 +36,7 @@
 #include "common/code_utils.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "common/message.hpp"
 #include "net/ip6.hpp"
 #include "net/netif.hpp"
@@ -46,6 +46,8 @@
 
 namespace ot {
 
+RegisterLogModule("DataPollSender");
+
 DataPollSender::DataPollSender(Instance &aInstance)
     : InstanceLocator(aInstance)
     , mTimerStartTime(0)
@@ -112,22 +114,22 @@
     switch (error)
     {
     case kErrorNone:
-        otLogDebgMac("Sending data poll");
+        LogDebg("Sending data poll");
         ScheduleNextPoll(kUsePreviousPollPeriod);
         break;
 
     case kErrorInvalidState:
-        otLogWarnMac("Data poll tx requested while data polling was not enabled!");
+        LogWarn("Data poll tx requested while data polling was not enabled!");
         StopPolling();
         break;
 
     case kErrorAlready:
-        otLogDebgMac("Data poll tx requested when a previous data request still in send queue.");
+        LogDebg("Data poll tx requested when a previous data request still in send queue.");
         ScheduleNextPoll(kUsePreviousPollPeriod);
         break;
 
     default:
-        otLogWarnMac("Unexpected error %s requesting data poll", ErrorToString(error));
+        LogWarn("Unexpected error %s requesting data poll", ErrorToString(error));
         ScheduleNextPoll(kRecalculatePollPeriod);
         break;
     }
@@ -260,12 +262,12 @@
         mPollTxFailureCounter++;
 
 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
-        otLogInfoMac("Failed to send data poll, error:%s, retx:%d/%d", ErrorToString(aError), mPollTxFailureCounter,
-                     (aFrame.GetHeaderIe(Mac::CslIe::kHeaderIeId) != nullptr) ? kMaxCslPollRetxAttempts
-                                                                              : kMaxPollRetxAttempts);
+        LogInfo("Failed to send data poll, error:%s, retx:%d/%d", ErrorToString(aError), mPollTxFailureCounter,
+                (aFrame.GetHeaderIe(Mac::CslIe::kHeaderIeId) != nullptr) ? kMaxCslPollRetxAttempts
+                                                                         : kMaxPollRetxAttempts);
 #else
-        otLogInfoMac("Failed to send data poll, error:%s, retx:%d/%d", ErrorToString(aError), mPollTxFailureCounter,
-                     kMaxPollRetxAttempts);
+        LogInfo("Failed to send data poll, error:%s, retx:%d/%d", ErrorToString(aError), mPollTxFailureCounter,
+                kMaxPollRetxAttempts);
 #endif
 
 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
@@ -310,7 +312,7 @@
 
     mPollTimeoutCounter++;
 
-    otLogInfoMac("Data poll timeout, retry:%d/%d", mPollTimeoutCounter, kQuickPollsAfterTimeout);
+    LogInfo("Data poll timeout, retry:%d/%d", mPollTimeoutCounter, kQuickPollsAfterTimeout);
 
     if (mPollTimeoutCounter < kQuickPollsAfterTimeout)
     {
@@ -548,7 +550,8 @@
 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_MAC_CSL_AUTO_SYNC_ENABLE
     if (Get<Mac::Mac>().IsCslEnabled())
     {
-        period = OT_MIN(period, Time::SecToMsec(Get<Mle::MleRouter>().GetCslTimeout()));
+        period    = OT_MIN(period, Time::SecToMsec(Get<Mle::MleRouter>().GetCslTimeout()));
+        pollAhead = static_cast<uint32_t>(kRetxPollPeriod);
     }
 #endif
 
diff --git a/src/core/mac/data_poll_sender.hpp b/src/core/mac/data_poll_sender.hpp
index d491c27..a30f563 100644
--- a/src/core/mac/data_poll_sender.hpp
+++ b/src/core/mac/data_poll_sender.hpp
@@ -271,7 +271,7 @@
     static constexpr uint32_t kRetxPollPeriod       = OPENTHREAD_CONFIG_MAC_RETX_POLL_PERIOD;
     static constexpr uint32_t kFastPollPeriod       = 188;
     static constexpr uint32_t kMinPollPeriod        = OPENTHREAD_CONFIG_MAC_MINIMUM_POLL_PERIOD;
-    static constexpr uint32_t kMaxExternalPeriod    = ((1 << 26) - 1); //< ~18.6 hours.
+    static constexpr uint32_t kMaxExternalPeriod    = ((1 << 26) - 1); // ~18.6 hours.
 
     void            ScheduleNextPoll(PollPeriodSelector aPollPeriodSelector);
     uint32_t        CalculatePollPeriod(void) const;
@@ -285,17 +285,17 @@
 
     TimeMilli mTimerStartTime;
     uint32_t  mPollPeriod;
-    uint32_t  mExternalPollPeriod : 26; //< In milliseconds.
-    uint8_t   mFastPollsUsers : 6;      //< Number of callers which request fast polls.
+    uint32_t  mExternalPollPeriod : 26; // In milliseconds.
+    uint8_t   mFastPollsUsers : 6;      // Number of callers which request fast polls.
 
     TimerMilli mTimer;
 
-    bool    mEnabled : 1;              //< Indicates whether data polling is enabled/started.
-    bool    mAttachMode : 1;           //< Indicates whether in attach mode (to use attach poll period).
-    bool    mRetxMode : 1;             //< Indicates whether last poll tx failed at mac/radio layer (poll retx mode).
-    uint8_t mPollTimeoutCounter : 4;   //< Poll timeouts counter (0 to `kQuickPollsAfterTimout`).
-    uint8_t mPollTxFailureCounter : 4; //< Poll tx failure counter (0 to `kMaxPollRetxAttempts`).
-    uint8_t mRemainingFastPolls : 4;   //< Number of remaining fast polls when in transient fast polling mode.
+    bool    mEnabled : 1;              // Indicates whether data polling is enabled/started.
+    bool    mAttachMode : 1;           // Indicates whether in attach mode (to use attach poll period).
+    bool    mRetxMode : 1;             // Indicates whether last poll tx failed at mac/radio layer (poll retx mode).
+    uint8_t mPollTimeoutCounter : 4;   // Poll timeouts counter (0 to `kQuickPollsAfterTimout`).
+    uint8_t mPollTxFailureCounter : 4; // Poll tx failure counter (0 to `kMaxPollRetxAttempts`).
+    uint8_t mRemainingFastPolls : 4;   // Number of remaining fast polls when in transient fast polling mode.
 };
 
 /**
diff --git a/src/core/mac/link_raw.cpp b/src/core/mac/link_raw.cpp
index 155708f..bbce9f2 100644
--- a/src/core/mac/link_raw.cpp
+++ b/src/core/mac/link_raw.cpp
@@ -42,13 +42,15 @@
 #include "common/debug.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "common/random.hpp"
 #include "mac/mac_frame.hpp"
 
 namespace ot {
 namespace Mac {
 
+RegisterLogModule("LinkRaw");
+
 LinkRaw::LinkRaw(Instance &aInstance)
     : InstanceLocator(aInstance)
     , mReceiveChannel(OPENTHREAD_CONFIG_DEFAULT_CHANNEL)
@@ -84,7 +86,7 @@
     Error error  = kErrorNone;
     bool  enable = aCallback != nullptr;
 
-    otLogDebgMac("LinkRaw::Enabled(%s)", (enable ? "true" : "false"));
+    LogDebg("Enabled(%s)", (enable ? "true" : "false"));
 
 #if OPENTHREAD_MTD || OPENTHREAD_FTD
     VerifyOrExit(!Get<ThreadNetif>().IsUp(), error = kErrorInvalidState);
@@ -179,8 +181,7 @@
 
 void LinkRaw::InvokeReceiveDone(RxFrame *aFrame, Error aError)
 {
-    otLogDebgMac("LinkRaw::ReceiveDone(%d bytes), error:%s", (aFrame != nullptr) ? aFrame->mLength : 0,
-                 ErrorToString(aError));
+    LogDebg("ReceiveDone(%d bytes), error:%s", (aFrame != nullptr) ? aFrame->mLength : 0, ErrorToString(aError));
 
     if (mReceiveDoneCallback && (aError == kErrorNone))
     {
@@ -203,7 +204,7 @@
 
 void LinkRaw::InvokeTransmitDone(TxFrame &aFrame, RxFrame *aAckFrame, Error aError)
 {
-    otLogDebgMac("LinkRaw::TransmitDone(%d bytes), error:%s", aFrame.mLength, ErrorToString(aError));
+    LogDebg("TransmitDone(%d bytes), error:%s", aFrame.mLength, ErrorToString(aError));
 
     if (mTransmitDoneCallback)
     {
@@ -270,7 +271,7 @@
 
 // LCOV_EXCL_START
 
-#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_MAC == 1)
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
 
 void LinkRaw::RecordFrameTransmitStatus(const TxFrame &aFrame,
                                         const RxFrame *aAckFrame,
@@ -283,8 +284,8 @@
 
     if (aError != kErrorNone)
     {
-        otLogInfoMac("Frame tx failed, error:%s, retries:%d/%d, %s", ErrorToString(aError), aRetryCount,
-                     aFrame.GetMaxFrameRetries(), aFrame.ToInfoString().AsCString());
+        LogInfo("Frame tx failed, error:%s, retries:%d/%d, %s", ErrorToString(aError), aRetryCount,
+                aFrame.GetMaxFrameRetries(), aFrame.ToInfoString().AsCString());
     }
 }
 
diff --git a/src/core/mac/link_raw.hpp b/src/core/mac/link_raw.hpp
index 7b8eb4c..16204b9 100644
--- a/src/core/mac/link_raw.hpp
+++ b/src/core/mac/link_raw.hpp
@@ -41,6 +41,7 @@
 #include <openthread/link_raw.h>
 
 #include "common/locator.hpp"
+#include "common/log.hpp"
 #include "common/non_copyable.hpp"
 #include "mac/mac_frame.hpp"
 #include "mac/sub_mac.hpp"
@@ -277,6 +278,7 @@
      */
     Error SetMacFrameCounter(uint32_t aMacFrameCounter);
 
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
     /**
      * This method records the status of a frame transmission attempt and is mainly used for logging failures.
      *
@@ -294,7 +296,6 @@
      *                        when there was an error in transmission (i.e., `aError` is not NONE).
      *
      */
-#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_MAC == 1)
     void RecordFrameTransmitStatus(const TxFrame &aFrame,
                                    const RxFrame *aAckFrame,
                                    Error          aError,
diff --git a/src/core/mac/mac.cpp b/src/core/mac/mac.cpp
index 50073eb..7e2f650 100644
--- a/src/core/mac/mac.cpp
+++ b/src/core/mac/mac.cpp
@@ -35,13 +35,13 @@
 
 #include <stdio.h>
 
+#include "common/array.hpp"
 #include "common/as_core_type.hpp"
 #include "common/code_utils.hpp"
 #include "common/debug.hpp"
 #include "common/encoding.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
 #include "common/random.hpp"
 #include "common/string.hpp"
 #include "crypto/aes_ccm.hpp"
@@ -57,20 +57,12 @@
 namespace ot {
 namespace Mac {
 
+RegisterLogModule("Mac");
+
 const otExtAddress Mac::sMode2ExtAddress = {
     {0x35, 0x06, 0xfe, 0xb8, 0x23, 0xd4, 0x87, 0x12},
 };
 
-const otExtendedPanId Mac::sExtendedPanidInit = {
-    {0xde, 0xad, 0x00, 0xbe, 0xef, 0x00, 0xca, 0xfe},
-};
-
-const char Mac::sNetworkNameInit[] = "OpenThread";
-
-#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
-const char Mac::sDomainNameInit[] = "DefaultDomain";
-#endif
-
 Mac::Mac(Instance &aInstance)
     : InstanceLocator(aInstance)
     , mEnabled(false)
@@ -121,17 +113,11 @@
 
     mCcaSuccessRateTracker.Clear();
     ResetCounters();
-    mExtendedPanId.Clear();
 
     SetEnabled(true);
     mLinks.Enable();
 
     Get<KeyManager>().UpdateKeyMaterial();
-    SetExtendedPanId(AsCoreType(&sExtendedPanidInit));
-    IgnoreError(SetNetworkName(sNetworkNameInit));
-#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
-    IgnoreError(SetDomainName(sDomainNameInit));
-#endif
     SetPanId(mPanId);
     SetExtAddress(randomExtAddress);
     SetShortAddress(GetShortAddress());
@@ -222,11 +208,8 @@
 
 Error Mac::ConvertBeaconToActiveScanResult(const RxFrame *aBeaconFrame, ActiveScanResult &aResult)
 {
-    Error                error = kErrorNone;
-    Address              address;
-    const Beacon *       beacon        = nullptr;
-    const BeaconPayload *beaconPayload = nullptr;
-    uint16_t             payloadLength;
+    Error   error = kErrorNone;
+    Address address;
 
     memset(&aResult, 0, sizeof(ActiveScanResult));
 
@@ -246,22 +229,7 @@
     aResult.mRssi    = aBeaconFrame->GetRssi();
     aResult.mLqi     = aBeaconFrame->GetLqi();
 
-    payloadLength = aBeaconFrame->GetPayloadLength();
-
-    beacon        = reinterpret_cast<const Beacon *>(aBeaconFrame->GetPayload());
-    beaconPayload = reinterpret_cast<const BeaconPayload *>(beacon->GetPayload());
-
-    if ((payloadLength >= (sizeof(*beacon) + sizeof(*beaconPayload))) && beacon->IsValid() && beaconPayload->IsValid())
-    {
-        aResult.mVersion    = beaconPayload->GetProtocolVersion();
-        aResult.mIsJoinable = beaconPayload->IsJoiningPermitted();
-        aResult.mIsNative   = beaconPayload->IsNative();
-        IgnoreError(AsCoreType(&aResult.mNetworkName).Set(beaconPayload->GetNetworkName()));
-        VerifyOrExit(IsValidUtf8String(aResult.mNetworkName.m8), error = kErrorParse);
-        aResult.mExtendedPanId = beaconPayload->GetExtendedPanId();
-    }
-
-    LogBeacon("Received", *beaconPayload);
+    LogBeacon("Received");
 
 exit:
     return error;
@@ -462,52 +430,6 @@
     IgnoreError(Get<Notifier>().Update(mSupportedChannelMask, newMask, kEventSupportedChannelMaskChanged));
 }
 
-Error Mac::SetNetworkName(const char *aNameString)
-{
-    return SignalNetworkNameChange(mNetworkName.Set(aNameString));
-}
-
-Error Mac::SetNetworkName(const NameData &aNameData)
-{
-    return SignalNetworkNameChange(mNetworkName.Set(aNameData));
-}
-
-Error Mac::SignalNetworkNameChange(Error aError)
-{
-    switch (aError)
-    {
-    case kErrorNone:
-        Get<Notifier>().Signal(kEventThreadNetworkNameChanged);
-        break;
-
-    case kErrorAlready:
-        Get<Notifier>().SignalIfFirst(kEventThreadNetworkNameChanged);
-        aError = kErrorNone;
-        break;
-
-    default:
-        break;
-    }
-
-    return aError;
-}
-
-#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
-Error Mac::SetDomainName(const char *aNameString)
-{
-    Error error = mDomainName.Set(aNameString);
-
-    return (error == kErrorAlready) ? kErrorNone : error;
-}
-
-Error Mac::SetDomainName(const NameData &aNameData)
-{
-    Error error = mDomainName.Set(aNameData);
-
-    return (error == kErrorAlready) ? kErrorNone : error;
-}
-#endif // (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
-
 void Mac::SetPanId(PanId aPanId)
 {
     SuccessOrExit(Get<Notifier>().Update(mPanId, aPanId, kEventThreadPanIdChanged));
@@ -517,11 +439,6 @@
     return;
 }
 
-void Mac::SetExtendedPanId(const ExtendedPanId &aExtendedPanId)
-{
-    IgnoreError(Get<Notifier>().Update(mExtendedPanId, aExtendedPanId, kEventThreadExtPanIdChanged));
-}
-
 void Mac::RequestDirectFrameTransmission(void)
 {
     VerifyOrExit(IsEnabled());
@@ -593,7 +510,7 @@
             mTimer.Start(kSleepDelay);
             mShouldDelaySleep = false;
             mDelayingSleep    = true;
-            otLogDebgMac("Idle mode: Sleep delayed");
+            LogDebg("Idle mode: Sleep delayed");
         }
 
         if (mDelayingSleep)
@@ -619,12 +536,12 @@
         }
 #endif
         mLinks.Sleep();
-        otLogDebgMac("Idle mode: Radio sleeping");
+        LogDebg("Idle mode: Radio sleeping");
     }
     else
     {
         mLinks.Receive(mRadioChannel);
-        otLogDebgMac("Idle mode: Radio receiving on channel %d", mRadioChannel);
+        LogDebg("Idle mode: Radio receiving on channel %d", mRadioChannel);
     }
 
 exit:
@@ -642,12 +559,12 @@
     {
         SetPending(aOperation);
 
-        otLogDebgMac("Request to start operation \"%s\"", OperationToString(aOperation));
+        LogDebg("Request to start operation \"%s\"", OperationToString(aOperation));
 
 #if OPENTHREAD_CONFIG_MAC_STAY_AWAKE_BETWEEN_FRAGMENTS
         if (mDelayingSleep)
         {
-            otLogDebgMac("Canceling sleep delay");
+            LogDebg("Canceling sleep delay");
             mTimer.Stop();
             mDelayingSleep    = false;
             mShouldDelaySleep = false;
@@ -731,7 +648,7 @@
     if (mOperation != kOperationIdle)
     {
         ClearPending(mOperation);
-        otLogDebgMac("Starting operation \"%s\"", OperationToString(mOperation));
+        LogDebg("Starting operation \"%s\"", OperationToString(mOperation));
         mTimer.Stop(); // Stop the timer before any non-idle operation, have the operation itself be responsible to
                        // start the timer (if it wants to).
     }
@@ -774,7 +691,7 @@
 
 void Mac::FinishOperation(void)
 {
-    otLogDebgMac("Finishing operation \"%s\"", OperationToString(mOperation));
+    LogDebg("Finishing operation \"%s\"", OperationToString(mOperation));
     mOperation = kOperationIdle;
 }
 
@@ -788,18 +705,16 @@
     frame.SetDstAddr(kShortAddrBroadcast);
     IgnoreError(frame.SetCommandId(Frame::kMacCmdBeaconRequest));
 
-    otLogInfoMac("Sending Beacon Request");
+    LogInfo("Sending Beacon Request");
 
     return &frame;
 }
 
 TxFrame *Mac::PrepareBeacon(void)
 {
-    TxFrame *      frame;
-    uint8_t        beaconLength;
-    uint16_t       fcf;
-    Beacon *       beacon        = nullptr;
-    BeaconPayload *beaconPayload = nullptr;
+    TxFrame *frame;
+    uint16_t fcf;
+    Beacon * beacon = nullptr;
 
 #if OPENTHREAD_CONFIG_MULTI_RADIO
     OT_ASSERT(!mTxBeaconRadioLinks.IsEmpty());
@@ -816,32 +731,8 @@
 
     beacon = reinterpret_cast<Beacon *>(frame->GetPayload());
     beacon->Init();
-    beaconLength = sizeof(*beacon);
 
-    beaconPayload = reinterpret_cast<BeaconPayload *>(beacon->GetPayload());
-
-    if (Get<KeyManager>().GetSecurityPolicy().mBeaconsEnabled)
-    {
-        beaconPayload->Init();
-
-        if (IsJoinable())
-        {
-            beaconPayload->SetJoiningPermitted();
-        }
-        else
-        {
-            beaconPayload->ClearJoiningPermitted();
-        }
-
-        beaconPayload->SetNetworkName(mNetworkName.GetAsData());
-        beaconPayload->SetExtendedPanId(mExtendedPanId);
-
-        beaconLength += sizeof(*beaconPayload);
-    }
-
-    frame->SetPayloadLength(beaconLength);
-
-    LogBeacon("Sending", *beaconPayload);
+    LogBeacon("Sending");
 
     return frame;
 }
@@ -1084,7 +975,7 @@
         // copy the frame into correct `TxFrame` for each radio type
         // (if it is not already prepared).
 
-        for (uint8_t index = 0; index < OT_ARRAY_LENGTH(RadioTypes::kAllRadioTypes); index++)
+        for (uint8_t index = 0; index < GetArrayLength(RadioTypes::kAllRadioTypes); index++)
         {
             RadioType radio = RadioTypes::kAllRadioTypes[index];
 
@@ -1103,7 +994,7 @@
         // process security for each radio type separately. This
         // allows radio links to handle security differently, e.g.,
         // with different keys or link frame counters.
-        for (uint8_t index = 0; index < OT_ARRAY_LENGTH(RadioTypes::kAllRadioTypes); index++)
+        for (uint8_t index = 0; index < GetArrayLength(RadioTypes::kAllRadioTypes); index++)
         {
             RadioType radio = RadioTypes::kAllRadioTypes[index];
 
@@ -1139,7 +1030,7 @@
     if (!mRxOnWhenIdle && !mPromiscuous)
     {
         mShouldDelaySleep = frame->GetFramePending();
-        otLogDebgMac("Delay sleep for pending tx");
+        LogDebg("Delay sleep for pending tx");
     }
 #endif
 
@@ -1232,7 +1123,7 @@
     if (aError != kErrorNone)
     {
         LogFrameTxFailure(aFrame, aError, aRetryCount, aWillRetx);
-        otDumpDebgMac("TX ERR", aFrame.GetHeader(), 16);
+        DumpDebg("TX ERR", aFrame.GetHeader(), 16);
 
         if (aWillRetx)
         {
@@ -1385,8 +1276,8 @@
 
             if (requriedRadios.Contains(radio) && (aError != kErrorNone))
             {
-                otLogDebgMac("Frame tx failed on required radio link %s with error %s", RadioTypeToString(radio),
-                             ErrorToString(aError));
+                LogDebg("Frame tx failed on required radio link %s with error %s", RadioTypeToString(radio),
+                        ErrorToString(aError));
 
                 mTxError = aError;
             }
@@ -1429,7 +1320,7 @@
                 StartOperation(kOperationWaitingForData);
             }
 
-            otLogInfoMac("Sent data poll, fp:%s", ToYesNo(framePending));
+            LogInfo("Sent data poll, fp:%s", ToYesNo(framePending));
         }
 
         mCounters.mTxDataPoll++;
@@ -1452,7 +1343,7 @@
         }
 #endif
 
-        otDumpDebgMac("TX", aFrame.GetHeader(), aFrame.GetLength());
+        DumpDebg("TX", aFrame.GetHeader(), aFrame.GetLength());
         FinishOperation();
         Get<MeshForwarder>().HandleSentFrame(aFrame, aError);
 #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
@@ -1466,7 +1357,7 @@
     case kOperationTransmitDataCsl:
         mCounters.mTxData++;
 
-        otDumpDebgMac("TX", aFrame.GetHeader(), aFrame.GetLength());
+        DumpDebg("TX", aFrame.GetHeader(), aFrame.GetLength());
         FinishOperation();
         Get<CslTxScheduler>().HandleSentFrame(aFrame, aError);
         PerformNextOperation();
@@ -1487,7 +1378,7 @@
         }
 #endif
 
-        otDumpDebgMac("TX", aFrame.GetHeader(), aFrame.GetLength());
+        DumpDebg("TX", aFrame.GetHeader(), aFrame.GetLength());
         FinishOperation();
         Get<DataPollHandler>().HandleSentFrame(aFrame, aError);
         PerformNextOperation();
@@ -1518,7 +1409,7 @@
         break;
 
     case kOperationWaitingForData:
-        otLogDebgMac("Data poll timeout");
+        LogDebg("Data poll timeout");
         FinishOperation();
         Get<DataPollSender>().HandlePollTimeout();
         PerformNextOperation();
@@ -1530,7 +1421,7 @@
 #if OPENTHREAD_CONFIG_MAC_STAY_AWAKE_BETWEEN_FRAGMENTS
             if (mDelayingSleep)
             {
-                otLogDebgMac("Sleep delay timeout expired");
+                LogDebg("Sleep delay timeout expired");
                 mDelayingSleep = false;
                 UpdateIdleMode();
             }
@@ -1567,7 +1458,7 @@
     VerifyOrExit(securityLevel == Frame::kSecEncMic32);
 
     IgnoreError(aFrame.GetFrameCounter(frameCounter));
-    otLogDebgMac("Rx security - frame counter %u", frameCounter);
+    LogDebg("Rx security - frame counter %u", frameCounter);
 
     IgnoreError(aFrame.GetKeyIdMode(keyIdMode));
 
@@ -1713,7 +1604,7 @@
     VerifyOrExit(txKeyId == ackKeyId);
 
     IgnoreError(aAckFrame.GetFrameCounter(frameCounter));
-    otLogDebgMac("Rx security - Ack frame counter %u", frameCounter);
+    LogDebg("Rx security - Ack frame counter %u", frameCounter);
 
     IgnoreError(aAckFrame.GetSrcAddr(srcAddr));
 
@@ -1774,7 +1665,7 @@
 exit:
     if (error != kErrorNone)
     {
-        otLogInfoMac("Frame tx attempt failed, error: Enh-ACK security check fail");
+        LogInfo("Frame tx attempt failed, error: Enh-ACK security check fail");
     }
 
     return error;
@@ -1841,7 +1732,7 @@
         break;
 
     case Address::kTypeShort:
-        otLogDebgMac("Received frame from short address 0x%04x", srcaddr.GetShort());
+        LogDebg("Received frame from short address 0x%04x", srcaddr.GetShort());
 
         VerifyOrExit(neighbor != nullptr, error = kErrorUnknownNeighbor);
 
@@ -2004,7 +1895,7 @@
             if (!mRxOnWhenIdle && !mPromiscuous && aFrame->GetFramePending())
             {
                 mShouldDelaySleep = true;
-                otLogDebgMac("Delay sleep for pending rx");
+                LogDebg("Delay sleep for pending rx");
             }
 #endif
             FinishOperation();
@@ -2042,7 +1933,7 @@
         ExitNow();
     }
 
-    otDumpDebgMac("RX", aFrame->GetHeader(), aFrame->GetLength());
+    DumpDebg("RX", aFrame->GetHeader(), aFrame->GetLength());
     Get<MeshForwarder>().HandleReceivedFrame(*aFrame);
 
     UpdateIdleMode();
@@ -2105,7 +1996,7 @@
     {
     case Frame::kMacCmdBeaconRequest:
         mCounters.mRxBeaconRequest++;
-        otLogInfoMac("Received Beacon Request");
+        LogInfo("Received Beacon Request");
 
         if (ShouldSendBeacon())
         {
@@ -2187,7 +2078,7 @@
 
 // LCOV_EXCL_START
 
-#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_MAC == 1)
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
 
 const char *Mac::OperationToString(Operation aOperation)
 {
@@ -2226,28 +2117,29 @@
 
 void Mac::LogFrameRxFailure(const RxFrame *aFrame, Error aError) const
 {
-    otLogLevel logLevel;
+    LogLevel logLevel;
 
     switch (aError)
     {
     case kErrorAbort:
     case kErrorNoFrameReceived:
+    case kErrorAddressFiltered:
     case kErrorDestinationAddressFiltered:
-        logLevel = OT_LOG_LEVEL_DEBG;
+        logLevel = kLogLevelDebg;
         break;
 
     default:
-        logLevel = OT_LOG_LEVEL_INFO;
+        logLevel = kLogLevelInfo;
         break;
     }
 
     if (aFrame == nullptr)
     {
-        otLogMac(logLevel, "Frame rx failed, error:%s", ErrorToString(aError));
+        LogAt(logLevel, "Frame rx failed, error:%s", ErrorToString(aError));
     }
     else
     {
-        otLogMac(logLevel, "Frame rx failed, error:%s, %s", ErrorToString(aError), aFrame->ToInfoString().AsCString());
+        LogAt(logLevel, "Frame rx failed, error:%s, %s", ErrorToString(aError), aFrame->ToInfoString().AsCString());
     }
 }
 
@@ -2264,27 +2156,27 @@
         uint8_t maxAttempts = aFrame.GetMaxFrameRetries() + 1;
         uint8_t curAttempt  = aWillRetx ? (aRetryCount + 1) : maxAttempts;
 
-        otLogInfoMac("Frame tx attempt %d/%d failed, error:%s, %s", curAttempt, maxAttempts, ErrorToString(aError),
-                     aFrame.ToInfoString().AsCString());
+        LogInfo("Frame tx attempt %d/%d failed, error:%s, %s", curAttempt, maxAttempts, ErrorToString(aError),
+                aFrame.ToInfoString().AsCString());
     }
     else
     {
-        otLogInfoMac("Frame tx failed, error:%s, %s", ErrorToString(aError), aFrame.ToInfoString().AsCString());
+        LogInfo("Frame tx failed, error:%s, %s", ErrorToString(aError), aFrame.ToInfoString().AsCString());
     }
 }
 
-void Mac::LogBeacon(const char *aActionText, const BeaconPayload &aBeaconPayload) const
+void Mac::LogBeacon(const char *aActionText) const
 {
-    otLogInfoMac("%s Beacon, %s", aActionText, aBeaconPayload.ToInfoString().AsCString());
+    LogInfo("%s Beacon", aActionText);
 }
 
-#else // #if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_MAC == 1)
+#else // #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
 
 void Mac::LogFrameRxFailure(const RxFrame *, Error) const
 {
 }
 
-void Mac::LogBeacon(const char *, const BeaconPayload &) const
+void Mac::LogBeacon(const char *) const
 {
 }
 
@@ -2292,7 +2184,7 @@
 {
 }
 
-#endif // #if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_MAC == 1)
+#endif // #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
 
 // LCOV_EXCL_STOP
 
@@ -2380,9 +2272,9 @@
     child->SetCslSynchronized(true);
     child->SetCslLastHeard(TimerMilli::GetNow());
     child->SetLastRxTimestamp(aFrame.GetTimestamp());
-    otLogDebgMac("Timestamp=%u Sequence=%u CslPeriod=%hu CslPhase=%hu TransmitPhase=%hu",
-                 static_cast<uint32_t>(aFrame.GetTimestamp()), aFrame.GetSequence(), csl->GetPeriod(), csl->GetPhase(),
-                 child->GetCslPhase());
+    LogDebg("Timestamp=%u Sequence=%u CslPeriod=%hu CslPhase=%hu TransmitPhase=%hu",
+            static_cast<uint32_t>(aFrame.GetTimestamp()), aFrame.GetSequence(), csl->GetPeriod(), csl->GetPhase(),
+            child->GetCslPhase());
 
     Get<CslTxScheduler>().Update();
 
diff --git a/src/core/mac/mac.hpp b/src/core/mac/mac.hpp
index 1276fb0..a5929a6 100644
--- a/src/core/mac/mac.hpp
+++ b/src/core/mac/mac.hpp
@@ -40,6 +40,7 @@
 #include <openthread/platform/time.h>
 
 #include "common/locator.hpp"
+#include "common/log.hpp"
 #include "common/non_copyable.hpp"
 #include "common/tasklet.hpp"
 #include "common/time.hpp"
@@ -323,68 +324,6 @@
     void SetSupportedChannelMask(const ChannelMask &aMask);
 
     /**
-     * This method returns the IEEE 802.15.4 Network Name.
-     *
-     * @returns The IEEE 802.15.4 Network Name.
-     *
-     */
-    const NetworkName &GetNetworkName(void) const { return mNetworkName; }
-
-    /**
-     * This method sets the IEEE 802.15.4 Network Name.
-     *
-     * @param[in]  aNameString   A pointer to a string character array. Must be null terminated.
-     *
-     * @retval kErrorNone          Successfully set the IEEE 802.15.4 Network Name.
-     * @retval kErrorInvalidArgs   Given name is too long.
-     *
-     */
-    Error SetNetworkName(const char *aNameString);
-
-    /**
-     * This method sets the IEEE 802.15.4 Network Name.
-     *
-     * @param[in]  aNameData     A name data (pointer to char buffer and length).
-     *
-     * @retval kErrorNone          Successfully set the IEEE 802.15.4 Network Name.
-     * @retval kErrorInvalidArgs   Given name is too long.
-     *
-     */
-    Error SetNetworkName(const NameData &aNameData);
-
-#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
-    /**
-     * This method returns the Thread Domain Name.
-     *
-     * @returns The Thread Domain Name.
-     *
-     */
-    const DomainName &GetDomainName(void) const { return mDomainName; }
-
-    /**
-     * This method sets the Thread Domain Name.
-     *
-     * @param[in]  aNameString   A pointer to a string character array. Must be null terminated.
-     *
-     * @retval kErrorNone          Successfully set the Thread Domain Name.
-     * @retval kErrorInvalidArgs   Given name is too long.
-     *
-     */
-    Error SetDomainName(const char *aNameString);
-
-    /**
-     * This method sets the Thread Domain Name.
-     *
-     * @param[in]  aNameData     A name data (pointer to char buffer and length).
-     *
-     * @retval kErrorNone          Successfully set the Thread Domain Name.
-     * @retval kErrorInvalidArgs   Given name is too long.
-     *
-     */
-    Error SetDomainName(const NameData &aNameData);
-#endif // (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
-
-    /**
      * This method returns the IEEE 802.15.4 PAN ID.
      *
      * @returns The IEEE 802.15.4 PAN ID.
@@ -401,22 +340,6 @@
     void SetPanId(PanId aPanId);
 
     /**
-     * This method returns the IEEE 802.15.4 Extended PAN Identifier.
-     *
-     * @returns The IEEE 802.15.4 Extended PAN Identifier.
-     *
-     */
-    const ExtendedPanId &GetExtendedPanId(void) const { return mExtendedPanId; }
-
-    /**
-     * This method sets the IEEE 802.15.4 Extended PAN Identifier.
-     *
-     * @param[in]  aExtendedPanId  The IEEE 802.15.4 Extended PAN Identifier.
-     *
-     */
-    void SetExtendedPanId(const ExtendedPanId &aExtendedPanId);
-
-    /**
      * This method returns the maximum number of frame retries during direct transmission.
      *
      * @returns The maximum number of retries during direct transmission.
@@ -851,11 +774,10 @@
     Error ConvertBeaconToActiveScanResult(const RxFrame *aBeaconFrame, ActiveScanResult &aResult);
     void  PerformEnergyScan(void);
     void  ReportEnergyScanResult(int8_t aRssi);
-    Error SignalNetworkNameChange(Error aError);
 
     void LogFrameRxFailure(const RxFrame *aFrame, Error aError) const;
     void LogFrameTxFailure(const TxFrame &aFrame, Error aError, uint8_t aRetryCount, bool aWillRetx) const;
-    void LogBeacon(const char *aActionText, const BeaconPayload &aBeaconPayload) const;
+    void LogBeacon(const char *aActionText) const;
 
 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
     uint8_t GetTimeIeOffset(const Frame &aFrame);
@@ -869,10 +791,7 @@
 #endif
     static const char *OperationToString(Operation aOperation);
 
-    static const otExtAddress    sMode2ExtAddress;
-    static const otExtendedPanId sExtendedPanidInit;
-    static const char            sNetworkNameInit[];
-    static const char            sDomainNameInit[];
+    static const otExtAddress sMode2ExtAddress;
 
     bool mEnabled : 1;
     bool mShouldTxPollBeforeData : 1;
@@ -884,20 +803,15 @@
     bool mShouldDelaySleep : 1;
     bool mDelayingSleep : 1;
 #endif
-    Operation     mOperation;
-    uint16_t      mPendingOperations;
-    uint8_t       mBeaconSequence;
-    uint8_t       mDataSequence;
-    uint8_t       mBroadcastTransmitCount;
-    PanId         mPanId;
-    uint8_t       mPanChannel;
-    uint8_t       mRadioChannel;
-    ChannelMask   mSupportedChannelMask;
-    ExtendedPanId mExtendedPanId;
-    NetworkName   mNetworkName;
-#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
-    DomainName mDomainName;
-#endif
+    Operation   mOperation;
+    uint16_t    mPendingOperations;
+    uint8_t     mBeaconSequence;
+    uint8_t     mDataSequence;
+    uint8_t     mBroadcastTransmitCount;
+    PanId       mPanId;
+    uint8_t     mPanChannel;
+    uint8_t     mRadioChannel;
+    ChannelMask mSupportedChannelMask;
     uint8_t     mScanChannel;
     uint16_t    mScanDuration;
     ChannelMask mScanChannelMask;
diff --git a/src/core/mac/mac_filter.cpp b/src/core/mac/mac_filter.cpp
index d05a221..c119c42 100644
--- a/src/core/mac/mac_filter.cpp
+++ b/src/core/mac/mac_filter.cpp
@@ -35,6 +35,7 @@
 
 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
 
+#include "common/array.hpp"
 #include "common/as_core_type.hpp"
 #include "common/code_utils.hpp"
 
@@ -123,7 +124,7 @@
 {
     Error error = kErrorNotFound;
 
-    for (; aIterator < OT_ARRAY_LENGTH(mFilterEntries); aIterator++)
+    for (; aIterator < GetArrayLength(mFilterEntries); aIterator++)
     {
         const FilterEntry &entry = mFilterEntries[aIterator];
 
@@ -185,7 +186,7 @@
 {
     Error error = kErrorNotFound;
 
-    for (; aIterator < OT_ARRAY_LENGTH(mFilterEntries); aIterator++)
+    for (; aIterator < GetArrayLength(mFilterEntries); aIterator++)
     {
         FilterEntry &entry = mFilterEntries[aIterator];
 
@@ -200,7 +201,7 @@
     }
 
     // Return the default RssIn at the end of list
-    if ((aIterator == OT_ARRAY_LENGTH(mFilterEntries)) && (mDefaultRssIn != kFixedRssDisabled))
+    if ((aIterator == GetArrayLength(mFilterEntries)) && (mDefaultRssIn != kFixedRssDisabled))
     {
         AsCoreType(&aEntry.mExtAddress).Fill(0xff);
         aEntry.mRssIn = mDefaultRssIn;
diff --git a/src/core/mac/mac_filter.hpp b/src/core/mac/mac_filter.hpp
index 30bbb4d..2eaef71 100644
--- a/src/core/mac/mac_filter.hpp
+++ b/src/core/mac/mac_filter.hpp
@@ -140,9 +140,9 @@
     /**
      * This method iterates through filter entries.
      *
-     * @param[inout]  aIterator  A reference to the MAC filter iterator context.
-     *                           To get the first in-use address filter, set it to OT_MAC_FILTER_ITERATOR_INIT.
-     * @param[out]     aEntry    A reference to where the information is placed.
+     * @param[in,out]  aIterator  A reference to the MAC filter iterator context.
+     *                            To get the first in-use address filter, set it to OT_MAC_FILTER_ITERATOR_INIT.
+     * @param[out]     aEntry     A reference to where the information is placed.
      *
      * @retval kErrorNone      Successfully retrieved the next address filter entry.
      * @retval kErrorNotFound  No subsequent entry exists.
@@ -198,11 +198,11 @@
     /**
      * This method iterates through RssIn filter entry.
      *
-     * @param[inout]  aIterator  A reference to the MAC filter iterator context. To get the first in-use RssIn
-     *                           filter entry, it should be set to OT_MAC_FILTER_ITERATOR_INIT.
-     * @param[out]    aEntry     A reference to where the information is placed. The last entry would have the
-     *                           Extended Address as all 0xff to indicate the default received signal strength
-     *                           if it was set.
+     * @param[in,out]  aIterator  A reference to the MAC filter iterator context. To get the first in-use RssIn
+     *                            filter entry, it should be set to OT_MAC_FILTER_ITERATOR_INIT.
+     * @param[out]     aEntry     A reference to where the information is placed. The last entry would have the
+     *                            Extended Address as all 0xff to indicate the default received signal strength
+     *                            if it was set.
      *
      * @retval kErrorNone      Successfully retrieved the next RssIn filter entry.
      * @retval kErrorNotFound  No subsequent entry exists.
diff --git a/src/core/mac/mac_frame.cpp b/src/core/mac/mac_frame.cpp
index 39334c8..2f0b1d1 100644
--- a/src/core/mac/mac_frame.cpp
+++ b/src/core/mac/mac_frame.cpp
@@ -37,6 +37,7 @@
 
 #include "common/code_utils.hpp"
 #include "common/debug.hpp"
+#include "common/log.hpp"
 #include "radio/trel_link.hpp"
 #if !OPENTHREAD_RADIO || OPENTHREAD_CONFIG_MAC_SOFTWARE_TX_SECURITY_ENABLE
 #include "crypto/aes_ccm.hpp"
@@ -1379,7 +1380,7 @@
 
 // LCOV_EXCL_START
 
-#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_NOTE) && (OPENTHREAD_CONFIG_LOG_MAC == 1)
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_NOTE)
 
 Frame::InfoString Frame::ToInfoString(void) const
 {
@@ -1446,20 +1447,7 @@
     return string;
 }
 
-BeaconPayload::InfoString BeaconPayload::ToInfoString(void) const
-{
-    NetworkName name;
-    InfoString  string;
-
-    IgnoreError(name.Set(GetNetworkName()));
-
-    string.Append("name:%s, xpanid:%s, id:%d, ver:%d, joinable:%s, native:%s", name.GetAsCString(),
-                  mExtendedPanId.ToString().AsCString(), GetProtocolId(), GetProtocolVersion(),
-                  ToYesNo(IsJoiningPermitted()), ToYesNo(IsNative()));
-    return string;
-}
-
-#endif // #if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_NOTE) && (OPENTHREAD_CONFIG_LOG_MAC == 1)
+#endif // #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_NOTE)
 
 // LCOV_EXCL_STOP
 
diff --git a/src/core/mac/mac_frame.hpp b/src/core/mac/mac_frame.hpp
index af26bfd..1c1a1a5 100644
--- a/src/core/mac/mac_frame.hpp
+++ b/src/core/mac/mac_frame.hpp
@@ -93,7 +93,7 @@
     /**
      * This method sets the IE Element Id.
      *
-     * @param[in]  aID  The IE Element Id.
+     * @param[in]  aId  The IE Element Id.
      *
      */
     void SetId(uint16_t aId)
@@ -155,7 +155,7 @@
     /**
      * This method returns the Vendor OUI.
      *
-     * @returns the Vendor OUI.
+     * @returns The Vendor OUI.
      *
      */
     uint32_t GetVendorOui(void) const { return ReadUint24(mOui); }
@@ -171,7 +171,7 @@
     /**
      * This method returns the Vendor IE sub-type.
      *
-     * @returns the Vendor IE sub-type.
+     * @returns The Vendor IE sub-type.
      *
      */
     uint8_t GetSubType(void) const { return mSubType; }
@@ -179,7 +179,7 @@
     /**
      * This method sets the Vendor IE sub-type.
      *
-     * @param[in] the Vendor IE sub-type.
+     * @param[in]  aSubType  The Vendor IE sub-type.
      *
      */
     void SetSubType(uint8_t aSubType) { mSubType = aSubType; }
@@ -364,8 +364,8 @@
     /**
      * This method initializes the MAC header.
      *
-     * @param[in]  aFcf          The Frame Control field.
-     * @param[in]  aSecurityCtl  The Security Control field.
+     * @param[in]  aFcf              The Frame Control field.
+     * @param[in]  aSecurityControl  The Security Control field.
      *
      */
     void InitMacHeader(uint16_t aFcf, uint8_t aSecurityControl);
@@ -663,7 +663,7 @@
     /**
      * This method gets the Key Identifier Mode.
      *
-     * @param[out]  aSecurityLevel  The Key Identifier Mode.
+     * @param[out]  aKeyIdMode  The Key Identifier Mode.
      *
      * @retval kErrorNone  Successfully retrieved the Key Identifier Mode.
      *
@@ -1483,161 +1483,6 @@
 } OT_TOOL_PACKED_END;
 
 /**
- * This class implements IEEE 802.15.4 Beacon Payload generation and parsing.
- *
- */
-OT_TOOL_PACKED_BEGIN
-class BeaconPayload
-{
-public:
-    static constexpr uint8_t kProtocolId      = 3;                     ///< Thread Protocol ID.
-    static constexpr uint8_t kProtocolVersion = 2;                     ///< Thread Protocol version.
-    static constexpr uint8_t kVersionOffset   = 4;                     ///< Version field bit offset.
-    static constexpr uint8_t kVersionMask     = 0xf << kVersionOffset; ///< Version field mask.
-    static constexpr uint8_t kNativeFlag      = 1 << 3;                ///< Native Commissioner flag.
-    static constexpr uint8_t kJoiningFlag     = 1 << 0;                ///< Joining Permitted flag.
-
-    static constexpr uint16_t kInfoStringSize = 92; ///< Max chars for the info string (@sa ToInfoString()).
-
-    /**
-     * This type defines the fixed-length `String` object returned from `ToInfoString()` method.
-     *
-     */
-    typedef String<kInfoStringSize> InfoString;
-
-    /**
-     * This method initializes the Beacon Payload.
-     *
-     */
-    void Init(void)
-    {
-        mProtocolId = kProtocolId;
-        mFlags      = kProtocolVersion << kVersionOffset;
-    }
-
-    /**
-     * This method indicates whether or not the beacon appears to be a valid Thread Beacon Payload.
-     *
-     * @retval TRUE   If the beacon appears to be a valid Thread Beacon Payload.
-     * @retval FALSE  If the beacon does not appear to be a valid Thread Beacon Payload.
-     *
-     */
-    bool IsValid(void) const { return (mProtocolId == kProtocolId); }
-
-    /**
-     * This method returns the Protocol ID value.
-     *
-     * @returns the Protocol ID value.
-     *
-     */
-    uint8_t GetProtocolId(void) const { return mProtocolId; }
-
-    /**
-     * This method returns the Protocol Version value.
-     *
-     * @returns The Protocol Version value.
-     *
-     */
-    uint8_t GetProtocolVersion(void) const { return mFlags >> kVersionOffset; }
-
-    /**
-     * This method indicates whether or not the Native Commissioner flag is set.
-     *
-     * @retval TRUE   If the Native Commissioner flag is set.
-     * @retval FALSE  If the Native Commissioner flag is not set.
-     *
-     */
-    bool IsNative(void) const { return (mFlags & kNativeFlag) != 0; }
-
-    /**
-     * This method clears the Native Commissioner flag.
-     *
-     */
-    void ClearNative(void) { mFlags &= ~kNativeFlag; }
-
-    /**
-     * This method sets the Native Commissioner flag.
-     *
-     */
-    void SetNative(void) { mFlags |= kNativeFlag; }
-
-    /**
-     * This method indicates whether or not the Joining Permitted flag is set.
-     *
-     * @retval TRUE   If the Joining Permitted flag is set.
-     * @retval FALSE  If the Joining Permitted flag is not set.
-     *
-     */
-    bool IsJoiningPermitted(void) const { return (mFlags & kJoiningFlag) != 0; }
-
-    /**
-     * This method clears the Joining Permitted flag.
-     *
-     */
-    void ClearJoiningPermitted(void) { mFlags &= ~kJoiningFlag; }
-
-    /**
-     * This method sets the Joining Permitted flag.
-     *
-     */
-    void SetJoiningPermitted(void)
-    {
-        mFlags |= kJoiningFlag;
-
-#if OPENTHREAD_CONFIG_MAC_JOIN_BEACON_VERSION != 2 // check against kProtocolVersion
-        mFlags &= ~kVersionMask;
-        mFlags |= OPENTHREAD_CONFIG_MAC_JOIN_BEACON_VERSION << kVersionOffset;
-#endif
-    }
-
-    /**
-     * This method gets the Network Name field.
-     *
-     * @returns The Network Name field as `NameData`.
-     *
-     */
-    NameData GetNetworkName(void) const { return NameData(mNetworkName, sizeof(mNetworkName)); }
-
-    /**
-     * This method sets the Network Name field.
-     *
-     * @param[in]  aNameData  The Network Name (as a `NameData`).
-     *
-     */
-    void SetNetworkName(const NameData &aNameData) { aNameData.CopyTo(mNetworkName, sizeof(mNetworkName)); }
-
-    /**
-     * This method returns the Extended PAN ID field.
-     *
-     * @returns The Extended PAN ID field.
-     *
-     */
-    const ExtendedPanId &GetExtendedPanId(void) const { return mExtendedPanId; }
-
-    /**
-     * This method sets the Extended PAN ID field.
-     *
-     * @param[in]  aExtPanId  An Extended PAN ID.
-     *
-     */
-    void SetExtendedPanId(const ExtendedPanId &aExtPanId) { mExtendedPanId = aExtPanId; }
-
-    /**
-     * This method returns information about the Beacon as a `InfoString`.
-     *
-     * @returns An `InfoString` representing the beacon payload.
-     *
-     */
-    InfoString ToInfoString(void) const;
-
-private:
-    uint8_t       mProtocolId;
-    uint8_t       mFlags;
-    char          mNetworkName[NetworkName::kMaxSize];
-    ExtendedPanId mExtendedPanId;
-} OT_TOOL_PACKED_END;
-
-/**
  * This class implements CSL IE data structure.
  *
  */
diff --git a/src/core/mac/mac_types.cpp b/src/core/mac/mac_types.cpp
index af1a680..83fab4b 100644
--- a/src/core/mac/mac_types.cpp
+++ b/src/core/mac/mac_types.cpp
@@ -28,7 +28,7 @@
 
 /**
  * @file
- *   This file implements MAC types such as Address, Extended PAN Identifier, Network Name, etc.
+ *   This file implements MAC types.
  */
 
 #include "mac_types.hpp"
@@ -110,80 +110,6 @@
     return string;
 }
 
-ExtendedPanId::InfoString ExtendedPanId::ToString(void) const
-{
-    InfoString string;
-
-    string.AppendHexBytes(m8, sizeof(ExtendedPanId));
-
-    return string;
-}
-
-uint8_t NameData::CopyTo(char *aBuffer, uint8_t aMaxSize) const
-{
-    MutableData<kWithUint8Length> destData;
-
-    destData.Init(aBuffer, aMaxSize);
-    destData.ClearBytes();
-    IgnoreError(destData.CopyBytesFrom(*this));
-
-    return destData.GetLength();
-}
-
-NameData NetworkName::GetAsData(void) const
-{
-    return NameData(m8, static_cast<uint8_t>(StringLength(m8, kMaxSize + 1)));
-}
-
-Error NetworkName::Set(const char *aNameString)
-{
-    // When setting `NetworkName` from a string, we treat it as `NameData`
-    // with `kMaxSize + 1` chars. `NetworkName::Set(data)` will look
-    // for null char in the data (within its given size) to calculate
-    // the name's length and ensure that the name fits in `kMaxSize`
-    // chars. The `+ 1` ensures that a `aNameString` with length
-    // longer than `kMaxSize` is correctly rejected (returning error
-    // `kErrorInvalidArgs`).
-
-    Error    error;
-    NameData data(aNameString, kMaxSize + 1);
-
-    VerifyOrExit(IsValidUtf8String(aNameString), error = kErrorInvalidArgs);
-
-    error = Set(data);
-
-exit:
-    return error;
-}
-
-Error NetworkName::Set(const NameData &aNameData)
-{
-    Error    error  = kErrorNone;
-    NameData data   = aNameData;
-    uint8_t  newLen = static_cast<uint8_t>(StringLength(data.GetBuffer(), data.GetLength()));
-
-    VerifyOrExit((0 < newLen) && (newLen <= kMaxSize), error = kErrorInvalidArgs);
-
-    data.SetLength(newLen);
-
-    // Ensure the new name does not match the current one.
-    if (data.MatchesBytesIn(m8) && m8[newLen] == '\0')
-    {
-        ExitNow(error = kErrorAlready);
-    }
-
-    data.CopyBytesTo(m8);
-    m8[newLen] = '\0';
-
-exit:
-    return error;
-}
-
-bool NetworkName::operator==(const NetworkName &aOther) const
-{
-    return GetAsData() == aOther.GetAsData();
-}
-
 #if OPENTHREAD_CONFIG_MULTI_RADIO
 
 const RadioType RadioTypes::kAllRadioTypes[kNumRadioTypes] = {
diff --git a/src/core/mac/mac_types.hpp b/src/core/mac/mac_types.hpp
index c28ff57..483a5b9 100644
--- a/src/core/mac/mac_types.hpp
+++ b/src/core/mac/mac_types.hpp
@@ -28,7 +28,7 @@
 
 /**
  * @file
- *   This file includes definitions for MAC types such as Address, Extended PAN Identifier, Network Name, etc.
+ *   This file includes definitions for MAC types.
  */
 
 #ifndef MAC_TYPES_HPP_
@@ -469,6 +469,8 @@
      *
      */
     KeyMaterial &operator=(const KeyMaterial &aOther);
+
+    KeyMaterial(const KeyMaterial &) = delete;
 #endif
 
     /**
@@ -523,7 +525,7 @@
     /**
      * This method converts `KeyMaterial` to a `Crypto::Key`.
      *
-     * @param[out]  A reference to a `Crypto::Key` to populate.
+     * @param[out]  aCryptoKey  A reference to a `Crypto::Key` to populate.
      *
      */
     void ConvertToCryptoKey(Crypto::Key &aCryptoKey) const;
@@ -539,8 +541,6 @@
      */
     bool operator==(const KeyMaterial &aOther) const;
 
-    KeyMaterial(const KeyMaterial &) = delete;
-
 private:
 #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
     static constexpr KeyRef kInvalidKeyRef = Crypto::Storage::kInvalidKeyRef;
@@ -552,165 +552,6 @@
     void SetKey(const Key &aKey) { mKeyMaterial.mKey = aKey; }
 };
 
-/**
- * This structure represents an IEEE 802.15.4 Extended PAN Identifier.
- *
- */
-OT_TOOL_PACKED_BEGIN
-class ExtendedPanId : public otExtendedPanId, public Equatable<ExtendedPanId>, public Clearable<ExtendedPanId>
-{
-public:
-    static constexpr uint16_t kInfoStringSize = 17; ///< Max chars for the info string (`ToString()`).
-
-    /**
-     * This type defines the fixed-length `String` object returned from `ToString()`.
-     *
-     */
-    typedef String<kInfoStringSize> InfoString;
-
-    /**
-     * This method converts an address to a string.
-     *
-     * @returns An `InfoString` containing the string representation of the Extended PAN Identifier.
-     *
-     */
-    InfoString ToString(void) const;
-
-} OT_TOOL_PACKED_END;
-
-/**
- * This class represents a name string as data (pointer to a char buffer along with a length).
- *
- * @note The char array does NOT need to be null terminated.
- *
- */
-class NameData : private Data<kWithUint8Length>
-{
-    friend class NetworkName;
-
-public:
-    /**
-     * This constructor initializes the NameData object.
-     *
-     * @param[in] aBuffer   A pointer to a `char` buffer (does not need to be null terminated).
-     * @param[in] aLength   The length (number of chars) in the buffer.
-     *
-     */
-    NameData(const char *aBuffer, uint8_t aLength) { Init(aBuffer, aLength); }
-
-    /**
-     * This method returns the pointer to char buffer (not necessarily null terminated).
-     *
-     * @returns The pointer to the char buffer.
-     *
-     */
-    const char *GetBuffer(void) const { return reinterpret_cast<const char *>(GetBytes()); }
-
-    /**
-     * This method returns the length (number of chars in buffer).
-     *
-     * @returns The name length.
-     *
-     */
-    uint8_t GetLength(void) const { return Data<kWithUint8Length>::GetLength(); }
-
-    /**
-     * This method copies the name data into a given char buffer with a given size.
-     *
-     * The given buffer is cleared (`memset` to zero) before copying the name into it. The copied string
-     * in @p aBuffer is NOT necessarily null terminated.
-     *
-     * @param[out] aBuffer   A pointer to a buffer where to copy the name into.
-     * @param[in]  aMaxSize  Size of @p aBuffer (maximum number of chars to write into @p aBuffer).
-     *
-     * @returns The actual number of chars copied into @p aBuffer.
-     *
-     */
-    uint8_t CopyTo(char *aBuffer, uint8_t aMaxSize) const;
-};
-
-/**
- * This structure represents an IEEE802.15.4 Network Name.
- *
- */
-class NetworkName : public otNetworkName, public Unequatable<NetworkName>
-{
-public:
-    /**
-     * This constant specified the maximum number of chars in Network Name (excludes null char).
-     *
-     */
-    static constexpr uint8_t kMaxSize = OT_NETWORK_NAME_MAX_SIZE;
-
-    /**
-     * This constructor initializes the IEEE802.15.4 Network Name as an empty string.
-     *
-     */
-    NetworkName(void) { m8[0] = '\0'; }
-
-    /**
-     * This method gets the IEEE802.15.4 Network Name as a null terminated C string.
-     *
-     * @returns The Network Name as a null terminated C string array.
-     *
-     */
-    const char *GetAsCString(void) const { return m8; }
-
-    /**
-     * This method gets the IEEE802.15.4 Network Name as NameData.
-     *
-     * @returns The Network Name as NameData.
-     *
-     */
-    NameData GetAsData(void) const;
-
-    /**
-     * This method sets the IEEE 802.15.4 Network Name from a given null terminated C string.
-     *
-     * This method also validates that the given @p aNameString follows UTF-8 encoding and can fit in `kMaxSize`
-     * chars.
-     *
-     * @param[in] aNameString      A name C string.
-     *
-     * @retval kErrorNone          Successfully set the IEEE 802.15.4 Network Name.
-     * @retval kErrorAlready       The name is already set to the same string.
-     * @retval kErrorInvalidArgs   Given name is invalid (too long or does not follow UTF-8 encoding).
-     *
-     */
-    Error Set(const char *aNameString);
-
-    /**
-     * This method sets the IEEE 802.15.4 Network Name.
-     *
-     * @param[in]  aNameData           A reference to name data.
-     *
-     * @retval kErrorNone          Successfully set the IEEE 802.15.4 Network Name.
-     * @retval kErrorAlready       The name is already set to the same string.
-     * @retval kErrorInvalidArgs   Given name is too long.
-     *
-     */
-    Error Set(const NameData &aNameData);
-
-    /**
-     * This method overloads operator `==` to evaluate whether or not two given `NetworkName` objects are equal.
-     *
-     * @param[in]  aOther  The other `NetworkName` to compare with.
-     *
-     * @retval TRUE   If the two are equal.
-     * @retval FALSE  If the two are not equal.
-     *
-     */
-    bool operator==(const NetworkName &aOther) const;
-};
-
-#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
-/**
- * This type represents a Thread Domain Name.
- *
- */
-typedef NetworkName DomainName;
-#endif
-
 #if OPENTHREAD_CONFIG_MULTI_RADIO
 
 /**
@@ -1024,8 +865,6 @@
 
 DefineCoreType(otExtAddress, Mac::ExtAddress);
 DefineCoreType(otMacKey, Mac::Key);
-DefineCoreType(otExtendedPanId, Mac::ExtendedPanId);
-DefineCoreType(otNetworkName, Mac::NetworkName);
 
 } // namespace ot
 
diff --git a/src/core/mac/sub_mac.cpp b/src/core/mac/sub_mac.cpp
index 0b8fbe7..eb7c064 100644
--- a/src/core/mac/sub_mac.cpp
+++ b/src/core/mac/sub_mac.cpp
@@ -41,7 +41,7 @@
 #include "common/debug.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "common/random.hpp"
 #include "common/time.hpp"
 #include "mac/mac_frame.hpp"
@@ -49,6 +49,8 @@
 namespace ot {
 namespace Mac {
 
+RegisterLogModule("SubMac");
+
 SubMac::SubMac(Instance &aInstance)
     : InstanceLocator(aInstance)
     , mRadioCaps(Get<Radio>().GetCaps())
@@ -76,6 +78,9 @@
     mRxOnWhenBackoff   = true;
     mEnergyScanMaxRssi = kInvalidRssiValue;
     mEnergyScanEndTime = Time{0};
+#if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
+    mRetxDelayBackOffExponent = kRetxDelayMinBackoffExponent;
+#endif
 
 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
     mRadioFilterEnabled = false;
@@ -147,14 +152,14 @@
 void SubMac::SetPanId(PanId aPanId)
 {
     Get<Radio>().SetPanId(aPanId);
-    otLogDebgMac("RadioPanId: 0x%04x", aPanId);
+    LogDebg("RadioPanId: 0x%04x", aPanId);
 }
 
 void SubMac::SetShortAddress(ShortAddress aShortAddress)
 {
     mShortAddress = aShortAddress;
     Get<Radio>().SetShortAddress(mShortAddress);
-    otLogDebgMac("RadioShortAddress: 0x%04x", mShortAddress);
+    LogDebg("RadioShortAddress: 0x%04x", mShortAddress);
 }
 
 void SubMac::SetExtAddress(const ExtAddress &aExtAddress)
@@ -167,7 +172,7 @@
     address.Set(aExtAddress.m8, ExtAddress::kReverseByteOrder);
     Get<Radio>().SetExtendedAddress(address);
 
-    otLogDebgMac("RadioExtAddress: %s", mExtAddress.ToString().AsCString());
+    LogDebg("RadioExtAddress: %s", mExtAddress.ToString().AsCString());
 }
 
 void SubMac::SetPcapCallback(otLinkPcapCallback aPcapCallback, void *aCallbackContext)
@@ -211,7 +216,7 @@
 
     if (error != kErrorNone)
     {
-        otLogWarnMac("RadioSleep() failed, error: %s", ErrorToString(error));
+        LogWarn("RadioSleep() failed, error: %s", ErrorToString(error));
         ExitNow();
     }
 
@@ -238,7 +243,7 @@
 
     if (error != kErrorNone)
     {
-        otLogWarnMac("RadioReceive() failed, error: %s", ErrorToString(error));
+        LogWarn("RadioReceive() failed, error: %s", ErrorToString(error));
         ExitNow();
     }
 
@@ -283,7 +288,7 @@
 exit:
     if (error != kErrorNone)
     {
-        otLogWarnMac("CslSample() failed, error: %s", ErrorToString(error));
+        LogWarn("CslSample() failed, error: %s", ErrorToString(error));
     }
     return error;
 }
@@ -312,10 +317,10 @@
 
 #if OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE
         // Split the log into two lines for RTT to output
-        otLogDebgMac("Received frame in state (SubMac %s, CSL %s), timestamp %u", StateToString(mState),
-                     CslStateToString(mCslState), static_cast<uint32_t>(aFrame->mInfo.mRxInfo.mTimestamp));
-        otLogDebgMac("Target sample start time %u, time drift %d", mCslSampleTime.GetValue(),
-                     static_cast<uint32_t>(aFrame->mInfo.mRxInfo.mTimestamp) - mCslSampleTime.GetValue());
+        LogDebg("Received frame in state (SubMac %s, CSL %s), timestamp %u", StateToString(mState),
+                CslStateToString(mCslState), static_cast<uint32_t>(aFrame->mInfo.mRxInfo.mTimestamp));
+        LogDebg("Target sample start time %u, time drift %d", mCslSampleTime.GetValue(),
+                static_cast<uint32_t>(aFrame->mInfo.mRxInfo.mTimestamp) - mCslSampleTime.GetValue());
 #endif
     }
 #endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
@@ -340,6 +345,9 @@
     case kStateCslTransmit:
 #endif
     case kStateTransmit:
+#if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
+    case kStateDelayBeforeRetx:
+#endif
     case kStateEnergyScan:
         ExitNow(error = kErrorInvalidState);
         OT_UNREACHABLE_CODE(break);
@@ -361,8 +369,14 @@
 #endif
 
     ProcessTransmitSecurity();
+
     mCsmaBackoffs    = 0;
     mTransmitRetries = 0;
+
+#if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
+    mRetxDelayBackOffExponent = kRetxDelayMinBackoffExponent;
+#endif
+
     StartCsmaBackoff();
 
 exit:
@@ -412,8 +426,7 @@
 
 void SubMac::StartCsmaBackoff(void)
 {
-    uint32_t backoff;
-    uint32_t backoffExponent = kCsmaMinBe + mTransmitRetries + mCsmaBackoffs;
+    uint8_t backoffExponent = kCsmaMinBe + mCsmaBackoffs;
 
 #if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
     if (mTransmitFrame.mInfo.mTxInfo.mTxDelay != 0)
@@ -442,6 +455,7 @@
         ExitNow();
     }
 #endif // !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
+
     SetState(kStateCsmaBackoff);
 
     VerifyOrExit(ShouldHandleCsmaBackOff(), BeginTransmit());
@@ -451,8 +465,18 @@
         backoffExponent = kCsmaMaxBe;
     }
 
-    backoff = Random::NonCrypto::GetUint32InRange(0, static_cast<uint32_t>(1UL << backoffExponent));
-    backoff *= (kUnitBackoffPeriod * OT_RADIO_SYMBOL_TIME);
+    StartTimerForBackoff(backoffExponent);
+
+exit:
+    return;
+}
+
+void SubMac::StartTimerForBackoff(uint8_t aBackoffExponent)
+{
+    uint32_t backoff;
+
+    backoff = Random::NonCrypto::GetUint32InRange(0, static_cast<uint32_t>(1UL << aBackoffExponent));
+    backoff *= (kUnitBackoffPeriod * Radio::kSymbolTime);
 
     if (mRxOnWhenBackoff)
     {
@@ -469,8 +493,12 @@
     mTimer.Start(backoff / 1000UL);
 #endif
 
-exit:
-    return;
+#if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
+    if (mState == kStateDelayBeforeRetx)
+    {
+        LogDebg("Delaying retx for %u usec (be=%d)", backoff, aBackoffExponent);
+    }
+#endif
 }
 
 void SubMac::BeginTransmit(void)
@@ -591,6 +619,17 @@
     {
         mTransmitRetries++;
         aFrame.SetIsARetransmission(true);
+
+#if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
+        if (aError == kErrorNoAck)
+        {
+            SetState(kStateDelayBeforeRetx);
+            StartTimerForBackoff(mRetxDelayBackOffExponent);
+            mRetxDelayBackOffExponent = OT_MIN(mRetxDelayBackOffExponent + 1, kRetxDelayMaxBackoffExponent);
+            ExitNow();
+        }
+#endif
+
         StartCsmaBackoff();
         ExitNow();
     }
@@ -671,6 +710,9 @@
 #if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
     case kStateCslTransmit:
 #endif
+#if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
+    case kStateDelayBeforeRetx:
+#endif
     case kStateEnergyScan:
         ExitNow(error = kErrorInvalidState);
 
@@ -762,11 +804,17 @@
         break;
 
     case kStateTransmit:
-        otLogDebgMac("Ack timer timed out");
+        LogDebg("Ack timer timed out");
         IgnoreError(Get<Radio>().Receive(mTransmitFrame.GetChannel()));
         HandleTransmitDone(mTransmitFrame, nullptr, kErrorNoAck);
         break;
 
+#if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
+    case kStateDelayBeforeRetx:
+        StartCsmaBackoff();
+        break;
+#endif
+
     case kStateEnergyScan:
         SampleRssi();
         break;
@@ -888,7 +936,7 @@
 {
     if (mState != aState)
     {
-        otLogDebgMac("RadioState: %s -> %s", StateToString(mState), StateToString(aState));
+        LogDebg("RadioState: %s -> %s", StateToString(mState), StateToString(aState));
         mState = aState;
     }
 }
@@ -966,11 +1014,14 @@
         "CsmaBackoff", // (3) kStateCsmaBackoff
         "Transmit",    // (4) kStateTransmit
         "EnergyScan",  // (5) kStateEnergyScan
+#if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
+        "DelayBeforeRetx", // (6) kStateDelayBeforeRetx
+#endif
 #if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
-        "CslTransmit", // (6) kStateCslTransmit
+        "CslTransmit", // (7) kStateCslTransmit
 #endif
 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
-        "CslSample", // (7) kStateCslSample
+        "CslSample", // (8) kStateCslSample
 #endif
     };
 
@@ -980,16 +1031,26 @@
     static_assert(kStateCsmaBackoff == 3, "kStateCsmaBackoff value is not correct");
     static_assert(kStateTransmit == 4, "kStateTransmit value is not correct");
     static_assert(kStateEnergyScan == 5, "kStateEnergyScan value is not correct");
+
+#if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
+    static_assert(kStateDelayBeforeRetx == 6, "kStateDelayBeforeRetx value is not correct");
 #if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
+    static_assert(kStateCslTransmit == 7, "kStateCslTransmit value is not correct");
+#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
+    static_assert(kStateCslSample == 8, "kStateCslSample value is not correct");
+#endif
+#elif OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
+    static_assert(kStateCslSample == 7, "kStateCslSample value is not correct");
+#endif
+
+#elif !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
     static_assert(kStateCslTransmit == 6, "kStateCslTransmit value is not correct");
 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
     static_assert(kStateCslSample == 7, "kStateCslSample value is not correct");
 #endif
-#else
-#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
+#elif OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
     static_assert(kStateCslSample == 6, "kStateCslSample value is not correct");
 #endif
-#endif
 
     return kStateStrings[aState];
 }
@@ -1047,7 +1108,7 @@
         }
     }
 
-    otLogDebgMac("CSL Period: %u", mCslPeriod);
+    LogDebg("CSL Period: %u", mCslPeriod);
 
 exit:
     return;
@@ -1085,7 +1146,7 @@
 #if !OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE
             IgnoreError(Get<Radio>().Sleep()); // Don't actually sleep for debugging
 #endif
-            otLogDebgMac("CSL sleep %u", mCslTimer.GetNow().GetValue());
+            LogDebg("CSL sleep %u", mCslTimer.GetNow().GetValue());
         }
         break;
 
@@ -1104,18 +1165,19 @@
         }
 
         Get<Radio>().UpdateCslSampleTime(mCslSampleTime.GetValue());
-        if (mState == kStateCslSample)
+
+        if (RadioSupportsReceiveTiming())
         {
-            if (RadioSupportsReceiveTiming())
+            if (mState != kStateDisabled && mCslChannel)
             {
                 IgnoreError(Get<Radio>().ReceiveAt(mCslChannel, mCslSampleTime.GetValue() - periodUs - timeAhead,
                                                    timeAhead + timeAfter));
             }
-            else
-            {
-                IgnoreError(Get<Radio>().Receive(mCslChannel));
-                otLogDebgMac("CSL sample %u, duration %u", mCslTimer.GetNow().GetValue(), timeAhead + timeAfter);
-            }
+        }
+        else if (mState == kStateCslSample)
+        {
+            IgnoreError(Get<Radio>().Receive(mCslChannel));
+            LogDebg("CSL sample %u, duration %u", mCslTimer.GetNow().GetValue(), timeAhead + timeAfter);
         }
         break;
 
@@ -1137,7 +1199,8 @@
 
     elapsed = curTime - mCslLastSync.GetValue();
 
-    semiWindow = elapsed * (Get<Radio>().GetCslAccuracy() + mCslParentAccuracy) / 1000000;
+    semiWindow = static_cast<uint32_t>(static_cast<uint64_t>(elapsed) *
+                                       (Get<Radio>().GetCslAccuracy() + mCslParentAccuracy) / 1000000);
     semiWindow += mCslParentUncert * kUsPerUncertUnit;
 
     aAhead = (semiWindow + kCslReceiveTimeAhead > semiPeriod) ? semiPeriod : semiWindow + kCslReceiveTimeAhead;
diff --git a/src/core/mac/sub_mac.hpp b/src/core/mac/sub_mac.hpp
index 7155fa1..4e243e9 100644
--- a/src/core/mac/sub_mac.hpp
+++ b/src/core/mac/sub_mac.hpp
@@ -584,6 +584,11 @@
     static constexpr uint32_t kAckTimeout        = 16;  // Timeout for waiting on an ACK (in msec).
     static constexpr uint32_t kCcaSampleInterval = 128; // CCA sample interval, 128 usec.
 
+#if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
+    static constexpr uint8_t kRetxDelayMinBackoffExponent = OPENTHREAD_CONFIG_MAC_RETX_DELAY_MIN_BACKOFF_EXPONENT;
+    static constexpr uint8_t kRetxDelayMaxBackoffExponent = OPENTHREAD_CONFIG_MAC_RETX_DELAY_MAX_BACKOFF_EXPONENT;
+#endif
+
 #if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
     static constexpr uint32_t kEnergyScanRssiSampleInterval = 128; // RSSI sample interval for energy scan, 128 usec
 #else
@@ -598,6 +603,9 @@
         kStateCsmaBackoff, // CSMA backoff before transmission.
         kStateTransmit,    // Radio is transmitting.
         kStateEnergyScan,  // Energy scan.
+#if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
+        kStateDelayBeforeRetx, // Delay before retx
+#endif
 #if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
         kStateCslTransmit, // CSL transmission.
 #endif
@@ -650,6 +658,7 @@
     void ProcessTransmitSecurity(void);
     void SignalFrameCounterUsed(uint32_t aFrameCounter);
     void StartCsmaBackoff(void);
+    void StartTimerForBackoff(uint8_t aBackoffExponent);
     void BeginTransmit(void);
     void SampleRssi(void);
 
@@ -689,6 +698,9 @@
     KeyMaterial        mNextKey;
     uint32_t           mFrameCounter;
     uint8_t            mKeyId;
+#if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
+    uint8_t mRetxDelayBackOffExponent;
+#endif
 #if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
     TimerMicro mTimer;
 #else
diff --git a/src/core/meshcop/announce_begin_client.cpp b/src/core/meshcop/announce_begin_client.cpp
index a312c38..b1bb699 100644
--- a/src/core/meshcop/announce_begin_client.cpp
+++ b/src/core/meshcop/announce_begin_client.cpp
@@ -40,7 +40,7 @@
 #include "common/debug.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "meshcop/meshcop.hpp"
 #include "meshcop/meshcop_tlvs.hpp"
 #include "thread/thread_netif.hpp"
@@ -48,6 +48,8 @@
 
 namespace ot {
 
+RegisterLogModule("MeshCoP");
+
 AnnounceBeginClient::AnnounceBeginClient(Instance &aInstance)
     : InstanceLocator(aInstance)
 {
@@ -60,7 +62,7 @@
 {
     Error                   error = kErrorNone;
     MeshCoP::ChannelMaskTlv channelMask;
-    Ip6::MessageInfo        messageInfo;
+    Tmf::MessageInfo        messageInfo(GetInstance());
     Coap::Message *         message = nullptr;
 
     VerifyOrExit(Get<MeshCoP::Commissioner>().IsActive(), error = kErrorInvalidState);
@@ -79,13 +81,11 @@
     SuccessOrExit(error = Tlv::Append<MeshCoP::CountTlv>(*message, aCount));
     SuccessOrExit(error = Tlv::Append<MeshCoP::PeriodTlv>(*message, aPeriod));
 
-    messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetMeshLocal16());
-    messageInfo.SetPeerAddr(aAddress);
-    messageInfo.SetPeerPort(Tmf::kUdpPort);
+    messageInfo.SetSockAddrToRlocPeerAddrTo(aAddress);
 
     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo));
 
-    otLogInfoMeshCoP("sent announce begin query");
+    LogInfo("sent announce begin query");
 
 exit:
     FreeMessageOnError(message, error);
diff --git a/src/core/meshcop/border_agent.cpp b/src/core/meshcop/border_agent.cpp
index c4a637b..1fb3984 100644
--- a/src/core/meshcop/border_agent.cpp
+++ b/src/core/meshcop/border_agent.cpp
@@ -40,7 +40,7 @@
 #include "common/heap.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "meshcop/meshcop.hpp"
 #include "meshcop/meshcop_tlvs.hpp"
 #include "thread/thread_netif.hpp"
@@ -50,6 +50,8 @@
 namespace ot {
 namespace MeshCoP {
 
+RegisterLogModule("BorderAgent");
+
 namespace {
 constexpr uint16_t kBorderAgentUdpPort = OPENTHREAD_CONFIG_BORDER_AGENT_UDP_PORT; ///< UDP port of border agent service.
 }
@@ -191,8 +193,8 @@
             Get<ThreadNetif>().AddUnicastAddress(mCommissionerAloc);
             IgnoreError(Get<Ip6::Udp>().AddReceiver(mUdpReceiver));
 
-            otLogInfoMeshCoP("commissioner accepted: session ID=%d, ALOC=%s", sessionId,
-                             mCommissionerAloc.GetAddress().ToString().AsCString());
+            LogInfo("commissioner accepted: session ID=%d, ALOC=%s", sessionId,
+                    mCommissionerAloc.GetAddress().ToString().AsCString());
         }
     }
 
@@ -211,7 +213,7 @@
     {
         FreeMessage(message);
 
-        otLogWarnMeshCoP("Commissioner request[%hu] failed: %s", aForwardContext.GetMessageId(), ErrorToString(error));
+        LogWarn("Commissioner request[%hu] failed: %s", aForwardContext.GetMessageId(), ErrorToString(error));
 
         SendErrorMessage(aForwardContext, error);
     }
@@ -301,7 +303,7 @@
     VerifyOrExit(aEvents.ContainsAny(kEventThreadRoleChanged | kEventCommissionerStateChanged));
 
 #if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD
-    VerifyOrExit(Get<MeshCoP::Commissioner>().IsDisabled());
+    VerifyOrExit(Get<Commissioner>().IsDisabled());
 #endif
 
     if (Get<Mle::MleRouter>().IsAttached())
@@ -343,7 +345,7 @@
     SuccessOrExit(error = Get<Ip6::Udp>().SendDatagram(*message, messageInfo, Ip6::kProtoUdp));
     mUdpProxyPort = tlv.GetSourcePort();
 
-    otLogInfoMeshCoP("Proxy transmit sent to %s", messageInfo.GetPeerAddr().ToString().AsCString());
+    LogInfo("Proxy transmit sent to %s", messageInfo.GetPeerAddr().ToString().AsCString());
 
 exit:
     FreeMessageOnError(message, error);
@@ -360,11 +362,8 @@
 
     VerifyOrExit(aMessage.GetLength() > 0, error = kErrorNone);
 
-    VerifyOrExit((message = Get<Coap::CoapSecure>().NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
-
-    message->InitAsNonConfirmablePost();
-    SuccessOrExit(error = message->AppendUriPathOptions(UriPath::kProxyRx));
-    SuccessOrExit(error = message->SetPayloadMarker());
+    message = Get<Coap::CoapSecure>().NewPriorityNonConfirmablePostMessage(UriPath::kProxyRx);
+    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
     {
         UdpEncapsulationTlv tlv;
@@ -386,7 +385,7 @@
 
     SuccessOrExit(error = Get<Coap::CoapSecure>().SendMessage(*message, Get<Coap::CoapSecure>().GetMessageInfo()));
 
-    otLogInfoMeshCoP("Sent to commissioner on %s", UriPath::kProxyRx);
+    LogInfo("Sent to commissioner on %s", UriPath::kProxyRx);
 
 exit:
     FreeMessageOnError(message, error);
@@ -401,18 +400,12 @@
     Error          error;
 
     VerifyOrExit(aMessage.IsNonConfirmablePostRequest(), error = kErrorDrop);
-    VerifyOrExit((message = Get<Coap::CoapSecure>().NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
 
-    message->InitAsNonConfirmablePost();
-    SuccessOrExit(error = message->AppendUriPathOptions(UriPath::kRelayRx));
-
-    if (aMessage.GetLength() > aMessage.GetOffset())
-    {
-        SuccessOrExit(error = message->SetPayloadMarker());
-    }
+    message = Get<Coap::CoapSecure>().NewPriorityNonConfirmablePostMessage(UriPath::kRelayRx);
+    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
     SuccessOrExit(error = ForwardToCommissioner(*message, aMessage));
-    otLogInfoMeshCoP("Sent to commissioner on %s", UriPath::kRelayRx);
+    LogInfo("Sent to commissioner on %s", UriPath::kRelayRx);
 
 exit:
     FreeMessageOnError(message, error);
@@ -430,7 +423,7 @@
     SuccessOrExit(error =
                       Get<Coap::CoapSecure>().SendMessage(aForwardMessage, Get<Coap::CoapSecure>().GetMessageInfo()));
 
-    otLogInfoMeshCoP("Sent to commissioner");
+    LogInfo("Sent to commissioner");
 
 exit:
     LogError("send to commissioner", error);
@@ -454,31 +447,26 @@
     Error            error = kErrorNone;
     uint16_t         joinerRouterRloc;
     Coap::Message *  message = nullptr;
-    Ip6::MessageInfo messageInfo;
+    Tmf::MessageInfo messageInfo(GetInstance());
     uint16_t         offset = 0;
 
     VerifyOrExit(aMessage.IsNonConfirmablePostRequest());
 
     SuccessOrExit(error = Tlv::Find<JoinerRouterLocatorTlv>(aMessage, joinerRouterRloc));
 
-    VerifyOrExit((message = Get<Tmf::Agent>().NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
-
-    SuccessOrExit(error = message->InitAsNonConfirmablePost(UriPath::kRelayTx));
-    SuccessOrExit(error = message->SetPayloadMarker());
+    message = Get<Tmf::Agent>().NewPriorityNonConfirmablePostMessage(UriPath::kRelayTx);
+    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
     offset = message->GetLength();
     SuccessOrExit(error = message->SetLength(offset + aMessage.GetLength() - aMessage.GetOffset()));
     aMessage.CopyTo(aMessage.GetOffset(), offset, aMessage.GetLength() - aMessage.GetOffset(), *message);
 
-    messageInfo.SetSockPort(Tmf::kUdpPort);
-    messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetMeshLocal16());
-    messageInfo.SetPeerPort(Tmf::kUdpPort);
-    messageInfo.SetPeerAddr(Get<Mle::MleRouter>().GetMeshLocal16());
-    messageInfo.GetPeerAddr().GetIid().SetLocator(joinerRouterRloc);
+    messageInfo.SetSockAddrToRlocPeerAddrTo(joinerRouterRloc);
+    messageInfo.SetSockPortToTmf();
 
     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo));
 
-    otLogInfoMeshCoP("Sent to joiner router request on %s", UriPath::kRelayTx);
+    LogInfo("Sent to joiner router request on %s", UriPath::kRelayTx);
 
 exit:
     FreeMessageOnError(message, error);
@@ -493,12 +481,10 @@
 {
     Error            error          = kErrorNone;
     ForwardContext * forwardContext = nullptr;
-    Ip6::MessageInfo messageInfo;
+    Tmf::MessageInfo messageInfo(GetInstance());
     Coap::Message *  message = nullptr;
     uint16_t         offset  = 0;
 
-    VerifyOrExit((message = Get<Tmf::Agent>().NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
-
     if (aSeparate)
     {
         SuccessOrExit(error = Get<Coap::CoapSecure>().SendAck(aMessage, aMessageInfo));
@@ -509,29 +495,22 @@
 
     forwardContext->Init(GetInstance(), aMessage, aPetition, aSeparate);
 
-    SuccessOrExit(error = message->InitAsConfirmablePost(aPath));
-
-    // Payload of c/cg may be empty
-    if (aMessage.GetLength() - aMessage.GetOffset() > 0)
-    {
-        SuccessOrExit(error = message->SetPayloadMarker());
-    }
+    message = Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(aPath);
+    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
     offset = message->GetLength();
     SuccessOrExit(error = message->SetLength(offset + aMessage.GetLength() - aMessage.GetOffset()));
     aMessage.CopyTo(aMessage.GetOffset(), offset, aMessage.GetLength() - aMessage.GetOffset(), *message);
 
-    SuccessOrExit(error = Get<Mle::MleRouter>().GetLeaderAloc(messageInfo.GetPeerAddr()));
-    messageInfo.SetPeerPort(Tmf::kUdpPort);
-    messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetMeshLocal16());
-    messageInfo.SetSockPort(Tmf::kUdpPort);
+    SuccessOrExit(error = messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc());
+    messageInfo.SetSockPortToTmf();
 
     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo, HandleCoapResponse, forwardContext));
 
     // HandleCoapResponse is responsible to free this forward context.
     forwardContext = nullptr;
 
-    otLogInfoMeshCoP("Forwarded request to leader on %s", aPath);
+    LogInfo("Forwarded request to leader on %s", aPath);
 
 exit:
     LogError("forward to leader", error);
@@ -559,13 +538,13 @@
 {
     if (aConnected)
     {
-        otLogInfoMeshCoP("Commissioner connected");
+        LogInfo("Commissioner connected");
         mState = kStateActive;
         mTimer.Start(kKeepAliveTimeout);
     }
     else
     {
-        otLogInfoMeshCoP("Commissioner disconnected");
+        LogInfo("Commissioner disconnected");
         IgnoreError(Get<Ip6::Udp>().RemoveReceiver(mUdpReceiver));
         Get<ThreadNetif>().RemoveUnicastAddress(mCommissionerAloc);
         mState        = kStateStarted;
@@ -584,7 +563,7 @@
     Coap::CoapSecure &coaps = Get<Coap::CoapSecure>();
     Pskc              pskc;
 
-    VerifyOrExit(mState == kStateStopped, error = kErrorAlready);
+    VerifyOrExit(mState == kStateStopped, error = kErrorNone);
 
     Get<KeyManager>().GetPskc(pskc);
     SuccessOrExit(error = coaps.Start(kBorderAgentUdpPort));
@@ -609,12 +588,12 @@
     mState        = kStateStarted;
     mUdpProxyPort = 0;
 
-    otLogInfoMeshCoP("Border Agent start listening on port %d", kBorderAgentUdpPort);
+    LogInfo("Border Agent start listening on port %d", kBorderAgentUdpPort);
 
 exit:
     if (error != kErrorNone)
     {
-        otLogWarnMeshCoP("failed to start Border Agent on port %d: %s", kBorderAgentUdpPort, ErrorToString(error));
+        LogWarn("failed to start Border Agent on port %d: %s", kBorderAgentUdpPort, ErrorToString(error));
     }
 }
 
@@ -628,7 +607,7 @@
     if (Get<Coap::CoapSecure>().IsConnected())
     {
         Get<Coap::CoapSecure>().Disconnect();
-        otLogWarnMeshCoP("Reset commissioner session");
+        LogWarn("Reset commissioner session");
     }
 }
 
@@ -658,7 +637,7 @@
     mState        = kStateStopped;
     mUdpProxyPort = 0;
 
-    otLogInfoMeshCoP("Border Agent stopped");
+    LogInfo("Border Agent stopped");
 
 exit:
     return;
diff --git a/src/core/meshcop/commissioner.cpp b/src/core/meshcop/commissioner.cpp
index 81b2d75..c1c73eb 100644
--- a/src/core/meshcop/commissioner.cpp
+++ b/src/core/meshcop/commissioner.cpp
@@ -38,11 +38,11 @@
 #include <stdio.h>
 
 #include "coap/coap_message.hpp"
+#include "common/array.hpp"
 #include "common/as_core_type.hpp"
 #include "common/encoding.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
 #include "common/string.hpp"
 #include "meshcop/joiner.hpp"
 #include "meshcop/joiner_router.hpp"
@@ -55,6 +55,8 @@
 namespace ot {
 namespace MeshCoP {
 
+RegisterLogModule("Commissioner");
+
 Commissioner::Commissioner(Instance &aInstance)
     : InstanceLocator(aInstance)
     , mActiveJoiner(nullptr)
@@ -84,6 +86,8 @@
     mCommissionerAloc.mScopeOverride      = Ip6::Address::kRealmLocalScope;
     mCommissionerAloc.mScopeOverrideValid = true;
 
+    IgnoreError(SetId("OpenThread Commissioner"));
+
     mProvisioningUrl[0] = '\0';
 }
 
@@ -95,7 +99,7 @@
 
     SuccessOrExit(Get<Notifier>().Update(mState, aState, kEventCommissionerStateChanged));
 
-    otLogInfoMeshCoP("CommissionerState: %s -> %s", StateToString(oldState), StateToString(aState));
+    LogInfo("State: %s -> %s", StateToString(oldState), StateToString(aState));
 
     if (mStateCallback)
     {
@@ -302,7 +306,7 @@
     VerifyOrExit(mState == kStateDisabled, error = kErrorAlready);
 
 #if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
-    Get<MeshCoP::BorderAgent>().Stop();
+    Get<BorderAgent>().Stop();
 #endif
 
     SuccessOrExit(error = Get<Coap::CoapSecure>().Start(SendRelayTransmit, this));
@@ -316,6 +320,8 @@
     SuccessOrExit(error = SendPetition());
     SetState(kStatePetition);
 
+    LogInfo("start commissioner %s", mCommissionerId);
+
 exit:
     if ((error != kErrorNone) && (error != kErrorAlready))
     {
@@ -357,7 +363,7 @@
     }
 
 #if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
-    Get<MeshCoP::BorderAgent>().Start();
+    Get<BorderAgent>().Start();
 #endif
 
 exit:
@@ -365,6 +371,28 @@
     return error;
 }
 
+Error Commissioner::SetId(const char *aId)
+{
+    Error   error = kErrorNone;
+    uint8_t len;
+
+    VerifyOrExit(IsDisabled(), error = kErrorInvalidState);
+    VerifyOrExit(aId != nullptr);
+    VerifyOrExit(IsValidUtf8String(aId), error = kErrorInvalidArgs);
+
+    len = static_cast<uint8_t>(StringLength(aId, sizeof(mCommissionerId)));
+
+    // CommissionerIdTlv::SetCommissionerId trims the string to the maximum array size.
+    // Prevent this from happening returning an error.
+    VerifyOrExit(len < CommissionerIdTlv::kMaxLength, error = kErrorInvalidArgs);
+
+    memcpy(mCommissionerId, aId, len);
+    mCommissionerId[len] = '\0';
+
+exit:
+    return error;
+}
+
 void Commissioner::ComputeBloomFilter(SteeringData &aSteeringData) const
 {
     Mac::ExtAddress joinerId;
@@ -516,7 +544,7 @@
 {
     Error error = kErrorNone;
 
-    while (aIterator < OT_ARRAY_LENGTH(mJoiners))
+    while (aIterator < GetArrayLength(mJoiners))
     {
         const Joiner &joiner = mJoiners[aIterator++];
 
@@ -640,7 +668,7 @@
 
         if (joiner.mExpirationTime <= now)
         {
-            otLogDebgMeshCoP("removing joiner due to timeout or successfully joined");
+            LogDebg("removing joiner due to timeout or successfully joined");
             RemoveJoinerEntry(joiner);
         }
     }
@@ -684,33 +712,25 @@
 {
     Error            error = kErrorNone;
     Coap::Message *  message;
-    Ip6::MessageInfo messageInfo;
-    MeshCoP::Tlv     tlv;
+    Tmf::MessageInfo messageInfo(GetInstance());
+    Tlv              tlv;
 
-    VerifyOrExit((message = Get<Tmf::Agent>().NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
-
-    SuccessOrExit(error = message->InitAsConfirmablePost(UriPath::kCommissionerGet));
+    message = Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(UriPath::kCommissionerGet);
+    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
     if (aLength > 0)
     {
-        SuccessOrExit(error = message->SetPayloadMarker());
-    }
-
-    if (aLength > 0)
-    {
-        tlv.SetType(MeshCoP::Tlv::kGet);
+        tlv.SetType(Tlv::kGet);
         tlv.SetLength(aLength);
         SuccessOrExit(error = message->Append(tlv));
         SuccessOrExit(error = message->AppendBytes(aTlvs, aLength));
     }
 
-    messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetMeshLocal16());
-    SuccessOrExit(error = Get<Mle::MleRouter>().GetLeaderAloc(messageInfo.GetPeerAddr()));
-    messageInfo.SetPeerPort(Tmf::kUdpPort);
+    SuccessOrExit(error = messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc());
     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo,
                                                         Commissioner::HandleMgmtCommissionerGetResponse, this));
 
-    otLogInfoMeshCoP("sent MGMT_COMMISSIONER_GET.req to leader");
+    LogInfo("sent MGMT_COMMISSIONER_GET.req to leader");
 
 exit:
     FreeMessageOnError(message, error);
@@ -733,7 +753,7 @@
     OT_UNUSED_VARIABLE(aMessageInfo);
 
     VerifyOrExit(aResult == kErrorNone && aMessage->GetCode() == Coap::kCodeChanged);
-    otLogInfoMeshCoP("received MGMT_COMMISSIONER_GET response");
+    LogInfo("received MGMT_COMMISSIONER_GET response");
 
 exit:
     return;
@@ -743,21 +763,19 @@
 {
     Error            error = kErrorNone;
     Coap::Message *  message;
-    Ip6::MessageInfo messageInfo;
+    Tmf::MessageInfo messageInfo(GetInstance());
 
-    VerifyOrExit((message = Get<Tmf::Agent>().NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
-
-    SuccessOrExit(error = message->InitAsConfirmablePost(UriPath::kCommissionerSet));
-    SuccessOrExit(error = message->SetPayloadMarker());
+    message = Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(UriPath::kCommissionerSet);
+    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
     if (aDataset.IsLocatorSet())
     {
-        SuccessOrExit(error = Tlv::Append<MeshCoP::BorderAgentLocatorTlv>(*message, aDataset.GetLocator()));
+        SuccessOrExit(error = Tlv::Append<BorderAgentLocatorTlv>(*message, aDataset.GetLocator()));
     }
 
     if (aDataset.IsSessionIdSet())
     {
-        SuccessOrExit(error = Tlv::Append<MeshCoP::CommissionerSessionIdTlv>(*message, aDataset.GetSessionId()));
+        SuccessOrExit(error = Tlv::Append<CommissionerSessionIdTlv>(*message, aDataset.GetSessionId()));
     }
 
     if (aDataset.IsSteeringDataSet())
@@ -777,13 +795,11 @@
         SuccessOrExit(error = message->AppendBytes(aTlvs, aLength));
     }
 
-    messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetMeshLocal16());
-    SuccessOrExit(error = Get<Mle::MleRouter>().GetLeaderAloc(messageInfo.GetPeerAddr()));
-    messageInfo.SetPeerPort(Tmf::kUdpPort);
+    SuccessOrExit(error = messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc());
     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo,
                                                         Commissioner::HandleMgmtCommissionerSetResponse, this));
 
-    otLogInfoMeshCoP("sent MGMT_COMMISSIONER_SET.req to leader");
+    LogInfo("sent MGMT_COMMISSIONER_SET.req to leader");
 
 exit:
     FreeMessageOnError(message, error);
@@ -806,7 +822,7 @@
     OT_UNUSED_VARIABLE(aMessageInfo);
 
     VerifyOrExit(aResult == kErrorNone && aMessage->GetCode() == Coap::kCodeChanged);
-    otLogInfoMeshCoP("received MGMT_COMMISSIONER_SET response");
+    LogInfo("received MGMT_COMMISSIONER_SET response");
 
 exit:
     return;
@@ -816,28 +832,23 @@
 {
     Error             error   = kErrorNone;
     Coap::Message *   message = nullptr;
-    Ip6::MessageInfo  messageInfo;
-    CommissionerIdTlv commissionerId;
+    Tmf::MessageInfo  messageInfo(GetInstance());
+    CommissionerIdTlv commissionerIdTlv;
 
     mTransmitAttempts++;
 
-    VerifyOrExit((message = Get<Tmf::Agent>().NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
+    message = Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(UriPath::kLeaderPetition);
+    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
-    SuccessOrExit(error = message->InitAsConfirmablePost(UriPath::kLeaderPetition));
-    SuccessOrExit(error = message->SetPayloadMarker());
+    commissionerIdTlv.Init();
+    commissionerIdTlv.SetCommissionerId(mCommissionerId);
 
-    commissionerId.Init();
-    commissionerId.SetCommissionerId("OpenThread Commissioner");
-
-    SuccessOrExit(error = commissionerId.AppendTo(*message));
-
-    SuccessOrExit(error = Get<Mle::MleRouter>().GetLeaderAloc(messageInfo.GetPeerAddr()));
-    messageInfo.SetPeerPort(Tmf::kUdpPort);
-    messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetMeshLocal16());
+    SuccessOrExit(error = commissionerIdTlv.AppendTo(*message));
+    SuccessOrExit(error = messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc());
     SuccessOrExit(
         error = Get<Tmf::Agent>().SendMessage(*message, messageInfo, Commissioner::HandleLeaderPetitionResponse, this));
 
-    otLogInfoMeshCoP("sent petition");
+    LogInfo("sent petition");
 
 exit:
     FreeMessageOnError(message, error);
@@ -866,7 +877,7 @@
     VerifyOrExit(aResult == kErrorNone && aMessage->GetCode() == Coap::kCodeChanged,
                  retransmit = (mState == kStatePetition));
 
-    otLogInfoMeshCoP("received Leader Petition response");
+    LogInfo("received Leader Petition response");
 
     SuccessOrExit(Tlv::Find<StateTlv>(*aMessage, state));
     VerifyOrExit(state == StateTlv::kAccept, IgnoreError(Stop(kDoNotSendKeepAlive)));
@@ -914,25 +925,21 @@
 {
     Error            error   = kErrorNone;
     Coap::Message *  message = nullptr;
-    Ip6::MessageInfo messageInfo;
+    Tmf::MessageInfo messageInfo(GetInstance());
 
-    VerifyOrExit((message = Get<Tmf::Agent>().NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
-
-    SuccessOrExit(error = message->InitAsConfirmablePost(UriPath::kLeaderKeepAlive));
-    SuccessOrExit(error = message->SetPayloadMarker());
+    message = Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(UriPath::kLeaderKeepAlive);
+    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
     SuccessOrExit(
         error = Tlv::Append<StateTlv>(*message, (mState == kStateActive) ? StateTlv::kAccept : StateTlv::kReject));
 
     SuccessOrExit(error = Tlv::Append<CommissionerSessionIdTlv>(*message, aSessionId));
 
-    messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetMeshLocal16());
-    SuccessOrExit(error = Get<Mle::MleRouter>().GetLeaderAloc(messageInfo.GetPeerAddr()));
-    messageInfo.SetPeerPort(Tmf::kUdpPort);
+    SuccessOrExit(error = messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc());
     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo,
                                                         Commissioner::HandleLeaderKeepAliveResponse, this));
 
-    otLogInfoMeshCoP("sent keep alive");
+    LogInfo("sent keep alive");
 
 exit:
     FreeMessageOnError(message, error);
@@ -960,7 +967,7 @@
     VerifyOrExit(aResult == kErrorNone && aMessage->GetCode() == Coap::kCodeChanged,
                  IgnoreError(Stop(kDoNotSendKeepAlive)));
 
-    otLogInfoMeshCoP("received Leader keep-alive response");
+    LogInfo("received Leader keep-alive response");
 
     SuccessOrExit(Tlv::Find<StateTlv>(*aMessage, state));
     VerifyOrExit(state == StateTlv::kAccept, IgnoreError(Stop(kDoNotSendKeepAlive)));
@@ -1024,7 +1031,7 @@
     mJoinerPort = joinerPort;
     mJoinerRloc = joinerRloc;
 
-    otLogInfoMeshCoP("Received Relay Receive (%s, 0x%04x)", mJoinerIid.ToString().AsCString(), mJoinerRloc);
+    LogInfo("Received Relay Receive (%s, 0x%04x)", mJoinerIid.ToString().AsCString(), mJoinerRloc);
 
     aMessage.SetOffset(offset);
     SuccessOrExit(error = aMessage.SetLength(offset + length));
@@ -1048,11 +1055,11 @@
 {
     VerifyOrExit(aMessage.IsConfirmablePostRequest());
 
-    otLogInfoMeshCoP("received dataset changed");
+    LogInfo("received dataset changed");
 
     SuccessOrExit(Get<Tmf::Agent>().SendEmptyAck(aMessage, aMessageInfo));
 
-    otLogInfoMeshCoP("sent dataset changed acknowledgment");
+    LogInfo("sent dataset changed acknowledgment");
 
 exit:
     return;
@@ -1070,7 +1077,7 @@
     StateTlv::State    state = StateTlv::kAccept;
     ProvisioningUrlTlv provisioningUrl;
 
-    otLogInfoMeshCoP("received joiner finalize");
+    LogInfo("received joiner finalize");
 
     if (Tlv::FindTlv(aMessage, provisioningUrl) == kErrorNone)
     {
@@ -1089,8 +1096,7 @@
         uint8_t buf[OPENTHREAD_CONFIG_MESSAGE_BUFFER_SIZE];
 
         aMessage.ReadBytes(aMessage.GetOffset(), buf, aMessage.GetLength() - aMessage.GetOffset());
-        otDumpCertMeshCoP("[THCI] direction=recv | type=JOIN_FIN.req |", buf,
-                          aMessage.GetLength() - aMessage.GetOffset());
+        DumpCert("[THCI] direction=recv | type=JOIN_FIN.req |", buf, aMessage.GetLength() - aMessage.GetOffset());
     }
 #endif
 
@@ -1103,10 +1109,9 @@
     Ip6::MessageInfo joinerMessageInfo;
     Coap::Message *  message;
 
-    VerifyOrExit((message = Get<Coap::CoapSecure>().NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
+    message = Get<Coap::CoapSecure>().NewPriorityResponseMessage(aRequest);
+    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
-    SuccessOrExit(error = message->SetDefaultResponseHeader(aRequest));
-    SuccessOrExit(error = message->SetPayloadMarker());
     message->SetOffset(message->GetLength());
     message->SetSubType(Message::kSubTypeJoinerFinalizeResponse);
 
@@ -1121,7 +1126,7 @@
 
     VerifyOrExit(message->GetLength() <= sizeof(buf));
     message->ReadBytes(message->GetOffset(), buf, message->GetLength() - message->GetOffset());
-    otDumpCertMeshCoP("[THCI] direction=send | type=JOIN_FIN.rsp |", buf, message->GetLength() - message->GetOffset());
+    DumpCert("[THCI] direction=send | type=JOIN_FIN.rsp |", buf, message->GetLength() - message->GetOffset());
 #endif
 
     SuccessOrExit(error = Get<Coap::CoapSecure>().SendMessage(*message, joinerMessageInfo));
@@ -1134,7 +1139,7 @@
         RemoveJoiner(*mActiveJoiner, kRemoveJoinerDelay);
     }
 
-    otLogInfoMeshCoP("sent joiner finalize response");
+    LogInfo("sent joiner finalize response");
 
 exit:
     FreeMessageOnError(message, error);
@@ -1153,16 +1158,13 @@
     ExtendedTlv      tlv;
     Coap::Message *  message;
     uint16_t         offset;
-    Ip6::MessageInfo messageInfo;
+    Tmf::MessageInfo messageInfo(GetInstance());
     Kek              kek;
 
-    VerifyOrExit((message = Get<Tmf::Agent>().NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
-
     Get<KeyManager>().ExtractKek(kek);
 
-    message->InitAsNonConfirmablePost();
-    SuccessOrExit(error = message->AppendUriPathOptions(UriPath::kRelayTx));
-    SuccessOrExit(error = message->SetPayloadMarker());
+    message = Get<Tmf::Agent>().NewPriorityNonConfirmablePostMessage(UriPath::kRelayTx);
+    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
     SuccessOrExit(error = Tlv::Append<JoinerUdpPortTlv>(*message, mJoinerPort));
     SuccessOrExit(error = Tlv::Append<JoinerIidTlv>(*message, mJoinerIid));
@@ -1180,10 +1182,7 @@
     SuccessOrExit(error = message->SetLength(offset + aMessage.GetLength()));
     aMessage.CopyTo(0, offset, aMessage.GetLength(), *message);
 
-    messageInfo.SetPeerAddr(Get<Mle::MleRouter>().GetMeshLocal16());
-    messageInfo.GetPeerAddr().GetIid().SetLocator(mJoinerRloc);
-    messageInfo.SetPeerPort(Tmf::kUdpPort);
-    messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetMeshLocal16());
+    messageInfo.SetSockAddrToRlocPeerAddrTo(mJoinerRloc);
 
     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo));
 
@@ -1208,7 +1207,7 @@
 
 // LCOV_EXCL_START
 
-#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_MESHCOP == 1)
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
 
 const char *Commissioner::StateToString(State aState)
 {
@@ -1233,17 +1232,17 @@
         break;
 
     case Joiner::kTypeAny:
-        otLogInfoMeshCoP("%s Joiner (any, %s)", aAction, aJoiner.mPskd.GetAsCString());
+        LogInfo("%s Joiner (any, %s)", aAction, aJoiner.mPskd.GetAsCString());
         break;
 
     case Joiner::kTypeEui64:
-        otLogInfoMeshCoP("%s Joiner (eui64:%s, %s)", aAction, aJoiner.mSharedId.mEui64.ToString().AsCString(),
-                         aJoiner.mPskd.GetAsCString());
+        LogInfo("%s Joiner (eui64:%s, %s)", aAction, aJoiner.mSharedId.mEui64.ToString().AsCString(),
+                aJoiner.mPskd.GetAsCString());
         break;
 
     case Joiner::kTypeDiscerner:
-        otLogInfoMeshCoP("%s Joiner (disc:%s, %s)", aAction, aJoiner.mSharedId.mDiscerner.ToString().AsCString(),
-                         aJoiner.mPskd.GetAsCString());
+        LogInfo("%s Joiner (disc:%s, %s)", aAction, aJoiner.mSharedId.mDiscerner.ToString().AsCString(),
+                aJoiner.mPskd.GetAsCString());
         break;
     }
 }
@@ -1254,7 +1253,7 @@
 {
 }
 
-#endif // (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_MESHCOP == 1)
+#endif // OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
 
 // LCOV_EXCL_STOP
 
diff --git a/src/core/meshcop/commissioner.hpp b/src/core/meshcop/commissioner.hpp
index ea91060..4c1d190 100644
--- a/src/core/meshcop/commissioner.hpp
+++ b/src/core/meshcop/commissioner.hpp
@@ -45,6 +45,7 @@
 #include "common/as_core_type.hpp"
 #include "common/clearable.hpp"
 #include "common/locator.hpp"
+#include "common/log.hpp"
 #include "common/non_copyable.hpp"
 #include "common/timer.hpp"
 #include "mac/mac_types.hpp"
@@ -251,6 +252,26 @@
     Error Stop(void) { return Stop(kSendKeepAliveToResign); }
 
     /**
+     * This method returns the Commissioner Id.
+     *
+     * @returns The Commissioner Id.
+     *
+     */
+    const char *GetId(void) const { return mCommissionerId; }
+
+    /**
+     * This method sets the Commissioner Id.
+     *
+     * @param[in]  aId   A pointer to a string character array. Must be null terminated.
+     *
+     * @retval kErrorNone           Successfully set the Commissioner Id.
+     * @retval kErrorInvalidArgs    Given name is too long.
+     * @retval kErrorInvalidState   The commissioner is active and id cannot be changed.
+     *
+     */
+    Error SetId(const char *aId);
+
+    /**
      * This method clears all Joiner entries.
      *
      */
@@ -306,7 +327,7 @@
     /**
      * This method get joiner info at aIterator position.
      *
-     * @param[inout]    aIterator   A iterator to the index of the joiner.
+     * @param[in,out]   aIterator   A iterator to the index of the joiner.
      * @param[out]      aJoiner     A reference to Joiner info.
      *
      * @retval kErrorNone       Successfully get the Joiner info.
@@ -605,6 +626,7 @@
     Ip6::Netif::UnicastAddress mCommissionerAloc;
 
     char mProvisioningUrl[OT_PROVISIONING_URL_MAX_SIZE + 1]; // + 1 is for null char at end of string.
+    char mCommissionerId[CommissionerIdTlv::kMaxLength + 1];
 
     State mState;
 
diff --git a/src/core/meshcop/dataset.cpp b/src/core/meshcop/dataset.cpp
index cbff801..74d9a09 100644
--- a/src/core/meshcop/dataset.cpp
+++ b/src/core/meshcop/dataset.cpp
@@ -40,7 +40,7 @@
 #include "common/encoding.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "mac/mac_types.hpp"
 #include "meshcop/meshcop_tlvs.hpp"
 #include "thread/mle_tlvs.hpp"
@@ -48,6 +48,8 @@
 namespace ot {
 namespace MeshCoP {
 
+RegisterLogModule("Dataset");
+
 Error Dataset::Info::GenerateRandom(Instance &aInstance)
 {
     Error            error;
@@ -71,12 +73,12 @@
     mChannel         = preferredChannels.ChooseRandomChannel();
     mChannelMask     = supportedChannels.GetMask();
     mPanId           = Mac::GenerateRandomPanId();
-    static_cast<SecurityPolicy &>(mSecurityPolicy).SetToDefault();
+    AsCoreType(&mSecurityPolicy).SetToDefault();
 
-    SuccessOrExit(error = static_cast<NetworkKey &>(mNetworkKey).GenerateRandom());
-    SuccessOrExit(error = static_cast<Pskc &>(mPskc).GenerateRandom());
+    SuccessOrExit(error = AsCoreType(&mNetworkKey).GenerateRandom());
+    SuccessOrExit(error = AsCoreType(&mPskc).GenerateRandom());
     SuccessOrExit(error = Random::Crypto::FillBuffer(mExtendedPanId.m8, sizeof(mExtendedPanId.m8)));
-    SuccessOrExit(error = static_cast<Ip6::NetworkPrefix &>(mMeshLocalPrefix).GenerateRandomUla());
+    SuccessOrExit(error = AsCoreType(&mMeshLocalPrefix).GenerateRandomUla());
 
     snprintf(mNetworkName.m8, sizeof(mNetworkName), "OpenThread-%04x", mPanId);
 
@@ -338,7 +340,7 @@
 
     if (aDatasetInfo.IsNetworkNamePresent())
     {
-        Mac::NameData nameData = aDatasetInfo.GetNetworkName().GetAsData();
+        NameData nameData = aDatasetInfo.GetNetworkName().GetAsData();
 
         IgnoreError(SetTlv(Tlv::kNetworkName, nameData.GetBuffer(), nameData.GetLength()));
     }
@@ -433,13 +435,15 @@
     return SetTlv(aTlv.GetType(), aTlv.GetValue(), aTlv.GetLength());
 }
 
-Error Dataset::Set(const Message &aMessage, uint16_t aOffset, uint8_t aLength)
+Error Dataset::ReadFromMessage(const Message &aMessage, uint16_t aOffset, uint8_t aLength)
 {
-    Error error = kErrorInvalidArgs;
+    Error error = kErrorParse;
 
     SuccessOrExit(aMessage.Read(aOffset, mTlvs, aLength));
     mLength = aLength;
 
+    VerifyOrExit(IsValid(), error = kErrorParse);
+
     mUpdateTime = TimerMilli::GetNow();
     error       = kErrorNone;
 
@@ -539,8 +543,7 @@
 
             if (error != kErrorNone)
             {
-                otLogWarnMeshCoP("DatasetManager::ApplyConfiguration() Failed to set channel to %d (%s)", channel,
-                                 ErrorToString(error));
+                LogWarn("ApplyConfiguration() Failed to set channel to %d (%s)", channel, ErrorToString(error));
                 ExitNow();
             }
 
@@ -552,11 +555,11 @@
             break;
 
         case Tlv::kExtendedPanId:
-            mac.SetExtendedPanId(As<ExtendedPanIdTlv>(cur)->GetExtendedPanId());
+            aInstance.Get<ExtendedPanIdManager>().SetExtPanId(As<ExtendedPanIdTlv>(cur)->GetExtendedPanId());
             break;
 
         case Tlv::kNetworkName:
-            IgnoreError(mac.SetNetworkName(As<NetworkNameTlv>(cur)->GetNetworkName()));
+            IgnoreError(aInstance.Get<NetworkNameManager>().SetNetworkName(As<NetworkNameTlv>(cur)->GetNetworkName()));
             break;
 
         case Tlv::kNetworkKey:
diff --git a/src/core/meshcop/dataset.hpp b/src/core/meshcop/dataset.hpp
index 2c8b27c..5990a48 100644
--- a/src/core/meshcop/dataset.hpp
+++ b/src/core/meshcop/dataset.hpp
@@ -308,7 +308,7 @@
          * @returns The Network Name in the Dataset.
          *
          */
-        const Mac::NetworkName &GetNetworkName(void) const { return AsCoreType(&mNetworkName); }
+        const NetworkName &GetNetworkName(void) const { return AsCoreType(&mNetworkName); }
 
         /**
          * This method sets the Network Name in the Dataset.
@@ -316,7 +316,7 @@
          * @param[in] aNetworkNameData   A Network Name Data.
          *
          */
-        void SetNetworkName(const Mac::NameData &aNetworkNameData)
+        void SetNetworkName(const NameData &aNetworkNameData)
         {
             IgnoreError(AsCoreType(&mNetworkName).Set(aNetworkNameData));
             mComponents.mIsNetworkNamePresent = true;
@@ -339,7 +339,7 @@
          * @returns The Extended PAN ID in the Dataset.
          *
          */
-        const Mac::ExtendedPanId &GetExtendedPanId(void) const { return AsCoreType(&mExtendedPanId); }
+        const ExtendedPanId &GetExtendedPanId(void) const { return AsCoreType(&mExtendedPanId); }
 
         /**
          * This method sets the Extended PAN ID in the Dataset.
@@ -347,7 +347,7 @@
          * @param[in] aExtendedPanId   An Extended PAN ID.
          *
          */
-        void SetExtendedPanId(const Mac::ExtendedPanId &aExtendedPanId)
+        void SetExtendedPanId(const ExtendedPanId &aExtendedPanId)
         {
             mExtendedPanId                      = aExtendedPanId;
             mComponents.mIsExtendedPanIdPresent = true;
@@ -406,7 +406,7 @@
         /**
          * This method sets the Delay Timer in the Dataset.
          *
-         * @param[in] aDely   A Delay value.
+         * @param[in] aDelay  A Delay value.
          *
          */
         void SetDelay(uint32_t aDelay)
@@ -782,17 +782,17 @@
     }
 
     /**
-     * This method sets the Dataset using TLVs stored in a message buffer.
+     * This method reads the Dataset from a given message and checks that it is well-formed and valid.
      *
-     * @param[in]  aMessage  The message buffer.
-     * @param[in]  aOffset   The message buffer offset where the dataset starts.
-     * @param[in]  aLength   The TLVs length in the message buffer in bytes.
+     * @param[in]  aMessage  The message to read from.
+     * @param[in]  aOffset   The offset in @p aMessage to start reading the Dataset TLVs.
+     * @param[in]  aLength   The dataset length in bytes.
      *
-     * @retval kErrorNone         Successfully set the Dataset.
-     * @retval kErrorInvalidArgs  The values of @p aOffset and @p aLength are not valid for @p aMessage.
+     * @retval kErrorNone    Successfully read and validated the Dataset.
+     * @retval kErrorParse   Could not read or parse the dataset from @p aMessage.
      *
      */
-    Error Set(const Message &aMessage, uint16_t aOffset, uint8_t aLength);
+    Error ReadFromMessage(const Message &aMessage, uint16_t aOffset, uint8_t aLength);
 
     /**
      * This method sets the Dataset using an existing Dataset.
diff --git a/src/core/meshcop/dataset_local.cpp b/src/core/meshcop/dataset_local.cpp
index 78e6f61..dfcb077 100644
--- a/src/core/meshcop/dataset_local.cpp
+++ b/src/core/meshcop/dataset_local.cpp
@@ -39,7 +39,7 @@
 #include "common/code_utils.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "common/settings.hpp"
 #include "crypto/storage.hpp"
 #include "meshcop/dataset.hpp"
@@ -49,6 +49,8 @@
 namespace ot {
 namespace MeshCoP {
 
+RegisterLogModule("DatasetLocal");
+
 DatasetLocal::DatasetLocal(Instance &aInstance, Dataset::Type aType)
     : InstanceLocator(aInstance)
     , mUpdateTime(0)
@@ -64,7 +66,7 @@
 #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
     DestroySecurelyStoredKeys();
 #endif
-    IgnoreError(Get<Settings>().DeleteOperationalDataset(IsActive()));
+    IgnoreError(Get<Settings>().DeleteOperationalDataset(mType));
     mTimestamp.Clear();
     mTimestampPresent = false;
     mSaved            = false;
@@ -92,7 +94,7 @@
     uint32_t       elapsed;
     Error          error;
 
-    error = Get<Settings>().ReadOperationalDataset(IsActive(), aDataset);
+    error = Get<Settings>().ReadOperationalDataset(mType, aDataset);
     VerifyOrExit(error == kErrorNone, aDataset.mLength = 0);
 
 #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
@@ -187,9 +189,9 @@
     if (aDataset.GetSize() == 0)
     {
         // do not propagate error back
-        IgnoreError(Get<Settings>().DeleteOperationalDataset(IsActive()));
+        IgnoreError(Get<Settings>().DeleteOperationalDataset(mType));
         mSaved = false;
-        otLogInfoMeshCoP("%s dataset deleted", Dataset::TypeToString(mType));
+        LogInfo("%s dataset deleted", Dataset::TypeToString(mType));
     }
     else
     {
@@ -199,13 +201,13 @@
 
         dataset.Set(GetType(), aDataset);
         MoveKeysToSecureStorage(dataset);
-        SuccessOrExit(error = Get<Settings>().SaveOperationalDataset(IsActive(), dataset));
+        SuccessOrExit(error = Get<Settings>().SaveOperationalDataset(mType, dataset));
 #else
-        SuccessOrExit(error = Get<Settings>().SaveOperationalDataset(IsActive(), aDataset));
+        SuccessOrExit(error = Get<Settings>().SaveOperationalDataset(mType, aDataset));
 #endif
 
         mSaved = true;
-        otLogInfoMeshCoP("%s dataset set", Dataset::TypeToString(mType));
+        LogInfo("%s dataset set", Dataset::TypeToString(mType));
     }
 
     mTimestampPresent = (aDataset.GetTimestamp(mType, mTimestamp) == kErrorNone);
@@ -268,16 +270,27 @@
     KeyRef         pskcRef       = IsActive() ? kActiveDatasetPskcRef : kPendingDatasetPskcRef;
     NetworkKeyTlv *networkKeyTlv = aDataset.GetTlv<NetworkKeyTlv>();
     PskcTlv *      pskcTlv       = aDataset.GetTlv<PskcTlv>();
+    bool           moveKeys      = false;
     size_t         keyLen;
+    Error          error;
 
     if (networkKeyTlv != nullptr)
     {
         // If the dataset contains a network key, its real value must have been moved to
         // the secure storage upon saving the dataset, so restore it back now.
         NetworkKey networkKey;
-        SuccessOrAssert(ExportKey(networkKeyRef, networkKey.m8, NetworkKey::kSize, keyLen));
-        OT_ASSERT(keyLen == NetworkKey::kSize);
-        networkKeyTlv->SetNetworkKey(networkKey);
+        error = ExportKey(networkKeyRef, networkKey.m8, NetworkKey::kSize, keyLen);
+
+        if (error != kErrorNone)
+        {
+            // If ExportKey fails, key is not in secure storage and is stored in settings
+            moveKeys = true;
+        }
+        else
+        {
+            OT_ASSERT(keyLen == NetworkKey::kSize);
+            networkKeyTlv->SetNetworkKey(networkKey);
+        }
     }
 
     if (pskcTlv != nullptr)
@@ -285,9 +298,29 @@
         // If the dataset contains a PSKC, its real value must have been moved to
         // the secure storage upon saving the dataset, so restore it back now.
         Pskc pskc;
-        SuccessOrAssert(ExportKey(pskcRef, pskc.m8, Pskc::kSize, keyLen));
-        OT_ASSERT(keyLen == Pskc::kSize);
-        pskcTlv->SetPskc(pskc);
+        error = ExportKey(pskcRef, pskc.m8, Pskc::kSize, keyLen);
+
+        if (error != kErrorNone)
+        {
+            // If ExportKey fails, key is not in secure storage and is stored in settings
+            moveKeys = true;
+        }
+        else
+        {
+            OT_ASSERT(keyLen == Pskc::kSize);
+            pskcTlv->SetPskc(pskc);
+        }
+    }
+
+    if (moveKeys)
+    {
+        // Clear the networkkey and Pskc stored in the settings and move them to secure storage.
+        // Store the network key and PSKC in the secure storage instead of settings.
+        Dataset dataset;
+
+        dataset.Set(GetType(), aDataset);
+        MoveKeysToSecureStorage(dataset);
+        SuccessOrAssert(error = Get<Settings>().SaveOperationalDataset(mType, dataset));
     }
 }
 #endif
diff --git a/src/core/meshcop/dataset_manager.cpp b/src/core/meshcop/dataset_manager.cpp
index d241b1f..355c4b0 100644
--- a/src/core/meshcop/dataset_manager.cpp
+++ b/src/core/meshcop/dataset_manager.cpp
@@ -39,7 +39,7 @@
 #include "common/as_core_type.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "common/notifier.hpp"
 #include "meshcop/meshcop.hpp"
 #include "meshcop/meshcop_tlvs.hpp"
@@ -51,6 +51,8 @@
 namespace ot {
 namespace MeshCoP {
 
+RegisterLogModule("DatasetManager");
+
 DatasetManager::DatasetManager(Instance &aInstance, Dataset::Type aType, Timer::Handler aTimerHandler)
     : InstanceLocator(aInstance)
     , mLocal(aInstance, aType)
@@ -138,7 +140,7 @@
 
     if (isNetworkkeyUpdated || compare > 0)
     {
-        IgnoreError(mLocal.Save(aDataset));
+        SuccessOrExit(error = mLocal.Save(aDataset));
 
 #if OPENTHREAD_FTD
         Get<NetworkData::Leader>().IncrementVersionAndStableVersion();
@@ -225,10 +227,10 @@
 
 Error DatasetManager::GetChannelMask(Mac::ChannelMask &aChannelMask) const
 {
-    Error                          error;
-    const MeshCoP::ChannelMaskTlv *channelMaskTlv;
-    uint32_t                       mask;
-    Dataset                        dataset;
+    Error                 error;
+    const ChannelMaskTlv *channelMaskTlv;
+    uint32_t              mask;
+    Dataset               dataset;
 
     SuccessOrExit(error = Read(dataset));
 
@@ -253,20 +255,20 @@
 {
     Error            error;
     Coap::Message *  message = nullptr;
-    Ip6::MessageInfo messageInfo;
+    Tmf::MessageInfo messageInfo(GetInstance());
     Dataset          dataset;
 
     VerifyOrExit(!mMgmtPending, error = kErrorBusy);
     VerifyOrExit(Get<Mle::MleRouter>().IsChild() || Get<Mle::MleRouter>().IsRouter(), error = kErrorInvalidState);
 
-    VerifyOrExit(Timestamp::Compare(GetTimestamp(), mLocal.GetTimestamp()) < 0, error = kErrorInvalidState);
+    VerifyOrExit(Timestamp::Compare(GetTimestamp(), mLocal.GetTimestamp()) < 0, error = kErrorAlready);
 
     if (IsActiveDataset())
     {
         Dataset   pendingDataset;
         Timestamp timestamp;
 
-        IgnoreError(Get<PendingDataset>().Read(pendingDataset));
+        IgnoreError(Get<PendingDatasetManager>().Read(pendingDataset));
 
         if ((pendingDataset.GetTimestamp(Dataset::kActive, timestamp) == kErrorNone) &&
             (Timestamp::Compare(&timestamp, mLocal.GetTimestamp()) == 0))
@@ -276,22 +278,18 @@
         }
     }
 
-    VerifyOrExit((message = Get<Tmf::Agent>().NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
-
-    SuccessOrExit(error =
-                      message->InitAsConfirmablePost(IsActiveDataset() ? UriPath::kActiveSet : UriPath::kPendingSet));
-    SuccessOrExit(error = message->SetPayloadMarker());
+    message = Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(IsActiveDataset() ? UriPath::kActiveSet
+                                                                                    : UriPath::kPendingSet);
+    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
     IgnoreError(Read(dataset));
     SuccessOrExit(error = message->AppendBytes(dataset.GetBytes(), dataset.GetSize()));
 
-    messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetMeshLocal16());
-    IgnoreError(Get<Mle::MleRouter>().GetLeaderAloc(messageInfo.GetPeerAddr()));
-    messageInfo.SetPeerPort(Tmf::kUdpPort);
+    IgnoreError(messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc());
     SuccessOrExit(
         error = Get<Tmf::Agent>().SendMessage(*message, messageInfo, &DatasetManager::HandleMgmtSetResponse, this));
 
-    otLogInfoMeshCoP("Sent %s set to leader", Dataset::TypeToString(GetType()));
+    LogInfo("Sent %s set to leader", Dataset::TypeToString(GetType()));
 
 exit:
 
@@ -345,7 +343,7 @@
     }
 
 exit:
-    otLogInfoMeshCoP("MGMT_SET finished: %s", ErrorToString(error));
+    LogInfo("MGMT_SET finished: %s", ErrorToString(error));
 
     mMgmtPending = false;
 
@@ -372,7 +370,7 @@
 
     while (offset < aMessage.GetLength())
     {
-        IgnoreError(aMessage.Read(offset, tlv));
+        SuccessOrExit(aMessage.Read(offset, tlv));
 
         if (tlv.GetType() == Tlv::kGet)
         {
@@ -419,10 +417,8 @@
 
     IgnoreError(Read(dataset));
 
-    VerifyOrExit((message = Get<Tmf::Agent>().NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
-
-    SuccessOrExit(error = message->SetDefaultResponseHeader(aRequest));
-    SuccessOrExit(error = message->SetPayloadMarker());
+    message = Get<Tmf::Agent>().NewPriorityResponseMessage(aRequest);
+    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
     if (aLength == 0)
     {
@@ -454,8 +450,8 @@
 
     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, aMessageInfo));
 
-    otLogInfoMeshCoP("sent %s dataset get response to %s", (GetType() == Dataset::kActive ? "active" : "pending"),
-                     aMessageInfo.GetPeerAddr().ToString().AsCString());
+    LogInfo("sent %s dataset get response to %s", (GetType() == Dataset::kActive ? "active" : "pending"),
+            aMessageInfo.GetPeerAddr().ToString().AsCString());
 
 exit:
     FreeMessageOnError(message, error);
@@ -481,15 +477,13 @@
 {
     Error            error   = kErrorNone;
     Coap::Message *  message = nullptr;
-    Ip6::MessageInfo messageInfo;
+    Tmf::MessageInfo messageInfo(GetInstance());
 
     VerifyOrExit(!mMgmtPending, error = kErrorBusy);
 
-    VerifyOrExit((message = Get<Tmf::Agent>().NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
-
-    SuccessOrExit(error =
-                      message->InitAsConfirmablePost(IsActiveDataset() ? UriPath::kActiveSet : UriPath::kPendingSet));
-    SuccessOrExit(error = message->SetPayloadMarker());
+    message = Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(IsActiveDataset() ? UriPath::kActiveSet
+                                                                                    : UriPath::kPendingSet);
+    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
 #if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD
 
@@ -524,16 +518,14 @@
         SuccessOrExit(error = message->AppendBytes(aTlvs, aLength));
     }
 
-    messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetMeshLocal16());
-    IgnoreError(Get<Mle::MleRouter>().GetLeaderAloc(messageInfo.GetPeerAddr()));
-    messageInfo.SetPeerPort(Tmf::kUdpPort);
+    IgnoreError(messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc());
 
     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo, HandleMgmtSetResponse, this));
     mMgmtSetCallback        = aCallback;
     mMgmtSetCallbackContext = aContext;
     mMgmtPending            = true;
 
-    otLogInfoMeshCoP("sent dataset set request to leader");
+    LogInfo("sent dataset set request to leader");
 
 exit:
     FreeMessageOnError(message, error);
@@ -547,7 +539,7 @@
 {
     Error            error = kErrorNone;
     Coap::Message *  message;
-    Ip6::MessageInfo messageInfo;
+    Tmf::MessageInfo messageInfo(GetInstance());
     Tlv              tlv;
     uint8_t          datasetTlvs[kMaxDatasetTlvs];
     uint8_t          length;
@@ -614,15 +606,9 @@
         datasetTlvs[length++] = Tlv::kChannelMask;
     }
 
-    VerifyOrExit((message = Get<Tmf::Agent>().NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
-
-    SuccessOrExit(error =
-                      message->InitAsConfirmablePost(IsActiveDataset() ? UriPath::kActiveGet : UriPath::kPendingGet));
-
-    if (aLength + length > 0)
-    {
-        SuccessOrExit(error = message->SetPayloadMarker());
-    }
+    message = Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(IsActiveDataset() ? UriPath::kActiveGet
+                                                                                    : UriPath::kPendingGet);
+    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
     if (aLength + length > 0)
     {
@@ -641,42 +627,39 @@
         }
     }
 
+    IgnoreError(messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc());
+
     if (aAddress != nullptr)
     {
+        // Use leader ALOC if `aAddress` is `nullptr`.
         messageInfo.SetPeerAddr(AsCoreType(aAddress));
     }
-    else
-    {
-        IgnoreError(Get<Mle::MleRouter>().GetLeaderAloc(messageInfo.GetPeerAddr()));
-    }
 
-    messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetMeshLocal16());
-    messageInfo.SetPeerPort(Tmf::kUdpPort);
     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo));
 
-    otLogInfoMeshCoP("sent dataset get request");
+    LogInfo("sent dataset get request");
 
 exit:
     FreeMessageOnError(message, error);
     return error;
 }
 
-ActiveDataset::ActiveDataset(Instance &aInstance)
-    : DatasetManager(aInstance, Dataset::kActive, ActiveDataset::HandleTimer)
-    , mResourceGet(UriPath::kActiveGet, &ActiveDataset::HandleGet, this)
+ActiveDatasetManager::ActiveDatasetManager(Instance &aInstance)
+    : DatasetManager(aInstance, Dataset::kActive, ActiveDatasetManager::HandleTimer)
+    , mResourceGet(UriPath::kActiveGet, &ActiveDatasetManager::HandleGet, this)
 #if OPENTHREAD_FTD
-    , mResourceSet(UriPath::kActiveSet, &ActiveDataset::HandleSet, this)
+    , mResourceSet(UriPath::kActiveSet, &ActiveDatasetManager::HandleSet, this)
 #endif
 {
     Get<Tmf::Agent>().AddResource(mResourceGet);
 }
 
-bool ActiveDataset::IsPartiallyComplete(void) const
+bool ActiveDatasetManager::IsPartiallyComplete(void) const
 {
     return mLocal.IsSaved() && !mTimestampValid;
 }
 
-bool ActiveDataset::IsCommissioned(void) const
+bool ActiveDatasetManager::IsCommissioned(void) const
 {
     Dataset::Info datasetInfo;
     bool          isValid = false;
@@ -690,52 +673,55 @@
     return isValid;
 }
 
-Error ActiveDataset::Save(const Timestamp &aTimestamp, const Message &aMessage, uint16_t aOffset, uint8_t aLength)
+Error ActiveDatasetManager::Save(const Timestamp &aTimestamp,
+                                 const Message &  aMessage,
+                                 uint16_t         aOffset,
+                                 uint8_t          aLength)
 {
     Error   error = kErrorNone;
     Dataset dataset;
 
-    SuccessOrExit(error = dataset.Set(aMessage, aOffset, aLength));
+    SuccessOrExit(error = dataset.ReadFromMessage(aMessage, aOffset, aLength));
     dataset.SetTimestamp(Dataset::kActive, aTimestamp);
-    IgnoreError(DatasetManager::Save(dataset));
+    error = DatasetManager::Save(dataset);
 
 exit:
     return error;
 }
 
-void ActiveDataset::HandleGet(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
+void ActiveDatasetManager::HandleGet(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
 {
-    static_cast<ActiveDataset *>(aContext)->HandleGet(AsCoapMessage(aMessage), AsCoreType(aMessageInfo));
+    static_cast<ActiveDatasetManager *>(aContext)->HandleGet(AsCoapMessage(aMessage), AsCoreType(aMessageInfo));
 }
 
-void ActiveDataset::HandleGet(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) const
+void ActiveDatasetManager::HandleGet(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) const
 {
     DatasetManager::HandleGet(aMessage, aMessageInfo);
 }
 
-void ActiveDataset::HandleTimer(Timer &aTimer)
+void ActiveDatasetManager::HandleTimer(Timer &aTimer)
 {
-    aTimer.Get<ActiveDataset>().HandleTimer();
+    aTimer.Get<ActiveDatasetManager>().HandleTimer();
 }
 
-PendingDataset::PendingDataset(Instance &aInstance)
-    : DatasetManager(aInstance, Dataset::kPending, PendingDataset::HandleTimer)
-    , mDelayTimer(aInstance, PendingDataset::HandleDelayTimer)
-    , mResourceGet(UriPath::kPendingGet, &PendingDataset::HandleGet, this)
+PendingDatasetManager::PendingDatasetManager(Instance &aInstance)
+    : DatasetManager(aInstance, Dataset::kPending, PendingDatasetManager::HandleTimer)
+    , mDelayTimer(aInstance, PendingDatasetManager::HandleDelayTimer)
+    , mResourceGet(UriPath::kPendingGet, &PendingDatasetManager::HandleGet, this)
 #if OPENTHREAD_FTD
-    , mResourceSet(UriPath::kPendingSet, &PendingDataset::HandleSet, this)
+    , mResourceSet(UriPath::kPendingSet, &PendingDatasetManager::HandleSet, this)
 #endif
 {
     Get<Tmf::Agent>().AddResource(mResourceGet);
 }
 
-void PendingDataset::Clear(void)
+void PendingDatasetManager::Clear(void)
 {
     DatasetManager::Clear();
     mDelayTimer.Stop();
 }
 
-void PendingDataset::ClearNetwork(void)
+void PendingDatasetManager::ClearNetwork(void)
 {
     Dataset dataset;
 
@@ -744,7 +730,7 @@
     IgnoreError(DatasetManager::Save(dataset));
 }
 
-Error PendingDataset::Save(const Dataset::Info &aDatasetInfo)
+Error PendingDatasetManager::Save(const Dataset::Info &aDatasetInfo)
 {
     Error error;
 
@@ -755,7 +741,7 @@
     return error;
 }
 
-Error PendingDataset::Save(const otOperationalDatasetTlvs &aDataset)
+Error PendingDatasetManager::Save(const otOperationalDatasetTlvs &aDataset)
 {
     Error error;
 
@@ -766,7 +752,7 @@
     return error;
 }
 
-Error PendingDataset::Save(const Dataset &aDataset)
+Error PendingDatasetManager::Save(const Dataset &aDataset)
 {
     Error error;
 
@@ -777,21 +763,24 @@
     return error;
 }
 
-Error PendingDataset::Save(const Timestamp &aTimestamp, const Message &aMessage, uint16_t aOffset, uint8_t aLength)
+Error PendingDatasetManager::Save(const Timestamp &aTimestamp,
+                                  const Message &  aMessage,
+                                  uint16_t         aOffset,
+                                  uint8_t          aLength)
 {
     Error   error = kErrorNone;
     Dataset dataset;
 
-    SuccessOrExit(error = dataset.Set(aMessage, aOffset, aLength));
+    SuccessOrExit(error = dataset.ReadFromMessage(aMessage, aOffset, aLength));
     dataset.SetTimestamp(Dataset::kPending, aTimestamp);
-    IgnoreError(DatasetManager::Save(dataset));
+    SuccessOrExit(error = DatasetManager::Save(dataset));
     StartDelayTimer();
 
 exit:
     return error;
 }
 
-void PendingDataset::StartDelayTimer(void)
+void PendingDatasetManager::StartDelayTimer(void)
 {
     DelayTimerTlv *delayTimer;
     Dataset        dataset;
@@ -811,16 +800,16 @@
         }
 
         mDelayTimer.StartAt(dataset.GetUpdateTime(), delay);
-        otLogInfoMeshCoP("delay timer started %d", delay);
+        LogInfo("delay timer started %d", delay);
     }
 }
 
-void PendingDataset::HandleDelayTimer(Timer &aTimer)
+void PendingDatasetManager::HandleDelayTimer(Timer &aTimer)
 {
-    aTimer.Get<PendingDataset>().HandleDelayTimer();
+    aTimer.Get<PendingDatasetManager>().HandleDelayTimer();
 }
 
-void PendingDataset::HandleDelayTimer(void)
+void PendingDatasetManager::HandleDelayTimer(void)
 {
     DelayTimerTlv *delayTimer;
     Dataset        dataset;
@@ -841,11 +830,11 @@
         }
     }
 
-    otLogInfoMeshCoP("pending delay timer expired");
+    LogInfo("pending delay timer expired");
 
     dataset.ConvertToActive();
 
-    Get<ActiveDataset>().Save(dataset);
+    Get<ActiveDatasetManager>().Save(dataset);
 
     Clear();
 
@@ -853,19 +842,19 @@
     return;
 }
 
-void PendingDataset::HandleGet(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
+void PendingDatasetManager::HandleGet(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
 {
-    static_cast<PendingDataset *>(aContext)->HandleGet(AsCoapMessage(aMessage), AsCoreType(aMessageInfo));
+    static_cast<PendingDatasetManager *>(aContext)->HandleGet(AsCoapMessage(aMessage), AsCoreType(aMessageInfo));
 }
 
-void PendingDataset::HandleGet(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) const
+void PendingDatasetManager::HandleGet(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) const
 {
     DatasetManager::HandleGet(aMessage, aMessageInfo);
 }
 
-void PendingDataset::HandleTimer(Timer &aTimer)
+void PendingDatasetManager::HandleTimer(Timer &aTimer)
 {
-    aTimer.Get<PendingDataset>().HandleTimer();
+    aTimer.Get<PendingDatasetManager>().HandleTimer();
 }
 
 } // namespace MeshCoP
diff --git a/src/core/meshcop/dataset_manager.hpp b/src/core/meshcop/dataset_manager.hpp
index 75ee9f7..9d54874 100644
--- a/src/core/meshcop/dataset_manager.hpp
+++ b/src/core/meshcop/dataset_manager.hpp
@@ -272,6 +272,9 @@
      * @param[in]  aOffset     The offset where the Operational Dataset begins.
      * @param[in]  aLength     The length of the Operational Dataset.
      *
+     * @retval kErrorNone     Successfully parsed the Dataset from the @p aMessage and saved it.
+     * @retval kErrorParse    Could not parse the Dataset from @p aMessage.
+     *
      */
     Error Save(const Timestamp &aTimestamp, const Message &aMessage, uint16_t aOffset, uint8_t aLength);
 
@@ -360,16 +363,16 @@
     void *                   mMgmtSetCallbackContext;
 };
 
-class ActiveDataset : public DatasetManager, private NonCopyable
+class ActiveDatasetManager : public DatasetManager, private NonCopyable
 {
 public:
     /**
-     * This constructor initializes the ActiveDataset object.
+     * This constructor initializes the ActiveDatasetManager object.
      *
      * @param[in]  aInstance  A reference to the OpenThread instance.
      *
      */
-    explicit ActiveDataset(Instance &aInstance);
+    explicit ActiveDatasetManager(Instance &aInstance);
 
     /**
      * This method indicates whether the Active Dataset is partially complete.
@@ -419,6 +422,9 @@
      * @param[in]  aOffset     The offset where the Operational Dataset begins.
      * @param[in]  aLength     The length of the Operational Dataset.
      *
+     * @retval kErrorNone     Successfully parsed the Dataset from the @p aMessage and saved it.
+     * @retval kErrorParse    Could not parse the Dataset from @p aMessage.
+     *
      */
     Error Save(const Timestamp &aTimestamp, const Message &aMessage, uint16_t aOffset, uint8_t aLength);
 
@@ -499,16 +505,16 @@
 #endif
 };
 
-class PendingDataset : public DatasetManager, private NonCopyable
+class PendingDatasetManager : public DatasetManager, private NonCopyable
 {
 public:
     /**
-     * This constructor initializes the PendingDataset object.
+     * This constructor initializes the PendingDatasetManager object.
      *
      * @param[in]  aInstance     A reference to the OpenThread instance.
      *
      */
-    explicit PendingDataset(Instance &aInstance);
+    explicit PendingDatasetManager(Instance &aInstance);
 
     /**
      * This method clears the Pending Operational Dataset.
diff --git a/src/core/meshcop/dataset_manager_ftd.cpp b/src/core/meshcop/dataset_manager_ftd.cpp
index 7447b17..a73e7d7 100644
--- a/src/core/meshcop/dataset_manager_ftd.cpp
+++ b/src/core/meshcop/dataset_manager_ftd.cpp
@@ -46,7 +46,7 @@
 #include "common/debug.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "common/random.hpp"
 #include "common/timer.hpp"
 #include "meshcop/dataset.hpp"
@@ -60,6 +60,8 @@
 namespace ot {
 namespace MeshCoP {
 
+RegisterLogModule("DatasetManager");
+
 Error DatasetManager::AppendMleDatasetTlv(Message &aMessage) const
 {
     Dataset dataset;
@@ -157,7 +159,7 @@
     if (GetType() == Dataset::kPending && (!hasNetworkKey || !doesAffectNetworkKey))
     {
         // no change to network key, active timestamp must be ahead
-        const Timestamp *localActiveTimestamp = Get<ActiveDataset>().GetTimestamp();
+        const Timestamp *localActiveTimestamp = Get<ActiveDatasetManager>().GetTimestamp();
 
         VerifyOrExit(Timestamp::Compare(&activeTimestamp, localActiveTimestamp) > 0);
     }
@@ -182,7 +184,7 @@
     {
         // Thread specification allows partial dataset changes for MGMT_ACTIVE_SET.req/MGMT_PENDING_SET.req
         // from Commissioner based on existing active dataset.
-        IgnoreError(Get<ActiveDataset>().Read(dataset));
+        IgnoreError(Get<ActiveDatasetManager>().Read(dataset));
     }
 
     if (GetType() == Dataset::kPending || !doesAffectConnectivity)
@@ -230,7 +232,7 @@
     }
     else
     {
-        Get<PendingDataset>().ApplyActiveDataset(activeTimestamp, aMessage);
+        Get<PendingDatasetManager>().ApplyActiveDataset(activeTimestamp, aMessage);
     }
 
     state = StateTlv::kAccept;
@@ -268,16 +270,14 @@
     Error          error = kErrorNone;
     Coap::Message *message;
 
-    VerifyOrExit((message = Get<Tmf::Agent>().NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
-
-    SuccessOrExit(error = message->SetDefaultResponseHeader(aRequest));
-    SuccessOrExit(error = message->SetPayloadMarker());
+    message = Get<Tmf::Agent>().NewPriorityResponseMessage(aRequest);
+    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
     SuccessOrExit(error = Tlv::Append<StateTlv>(*message, aState));
 
     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, aMessageInfo));
 
-    otLogInfoMeshCoP("sent dataset set response");
+    LogInfo("sent dataset set response");
 
 exit:
     FreeMessageOnError(message, error);
@@ -296,7 +296,7 @@
     return error;
 }
 
-Error ActiveDataset::GenerateLocal(void)
+Error ActiveDatasetManager::GenerateLocal(void)
 {
     Error   error = kErrorNone;
     Dataset dataset;
@@ -332,7 +332,7 @@
 
     if (dataset.GetTlv<ExtendedPanIdTlv>() == nullptr)
     {
-        IgnoreError(dataset.SetTlv(Tlv::kExtendedPanId, Get<Mac::Mac>().GetExtendedPanId()));
+        IgnoreError(dataset.SetTlv(Tlv::kExtendedPanId, Get<ExtendedPanIdManager>().GetExtPanId()));
     }
 
     if (dataset.GetTlv<MeshLocalPrefixTlv>() == nullptr)
@@ -350,7 +350,7 @@
 
     if (dataset.GetTlv<NetworkNameTlv>() == nullptr)
     {
-        Mac::NameData nameData = Get<Mac::Mac>().GetNetworkName().GetAsData();
+        NameData nameData = Get<NetworkNameManager>().GetNetworkName().GetAsData();
 
         IgnoreError(dataset.SetTlv(Tlv::kNetworkName, nameData.GetBuffer(), nameData.GetLength()));
     }
@@ -388,29 +388,29 @@
     SuccessOrExit(error = mLocal.Save(dataset));
     IgnoreError(Restore());
 
-    otLogInfoMeshCoP("Generated local dataset");
+    LogInfo("Generated local dataset");
 
 exit:
     return error;
 }
 
-void ActiveDataset::StartLeader(void)
+void ActiveDatasetManager::StartLeader(void)
 {
     IgnoreError(GenerateLocal());
     Get<Tmf::Agent>().AddResource(mResourceSet);
 }
 
-void ActiveDataset::StopLeader(void)
+void ActiveDatasetManager::StopLeader(void)
 {
     Get<Tmf::Agent>().RemoveResource(mResourceSet);
 }
 
-void ActiveDataset::HandleSet(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
+void ActiveDatasetManager::HandleSet(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
 {
-    static_cast<ActiveDataset *>(aContext)->HandleSet(AsCoapMessage(aMessage), AsCoreType(aMessageInfo));
+    static_cast<ActiveDatasetManager *>(aContext)->HandleSet(AsCoapMessage(aMessage), AsCoreType(aMessageInfo));
 }
 
-void ActiveDataset::HandleSet(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
+void ActiveDatasetManager::HandleSet(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
 {
     SuccessOrExit(DatasetManager::HandleSet(aMessage, aMessageInfo));
     IgnoreError(ApplyConfiguration());
@@ -419,23 +419,23 @@
     return;
 }
 
-void PendingDataset::StartLeader(void)
+void PendingDatasetManager::StartLeader(void)
 {
     StartDelayTimer();
     Get<Tmf::Agent>().AddResource(mResourceSet);
 }
 
-void PendingDataset::StopLeader(void)
+void PendingDatasetManager::StopLeader(void)
 {
     Get<Tmf::Agent>().RemoveResource(mResourceSet);
 }
 
-void PendingDataset::HandleSet(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
+void PendingDatasetManager::HandleSet(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
 {
-    static_cast<PendingDataset *>(aContext)->HandleSet(AsCoapMessage(aMessage), AsCoreType(aMessageInfo));
+    static_cast<PendingDatasetManager *>(aContext)->HandleSet(AsCoapMessage(aMessage), AsCoreType(aMessageInfo));
 }
 
-void PendingDataset::HandleSet(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
+void PendingDatasetManager::HandleSet(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
 {
     SuccessOrExit(DatasetManager::HandleSet(aMessage, aMessageInfo));
     StartDelayTimer();
@@ -444,7 +444,7 @@
     return;
 }
 
-void PendingDataset::ApplyActiveDataset(const Timestamp &aTimestamp, Coap::Message &aMessage)
+void PendingDatasetManager::ApplyActiveDataset(const Timestamp &aTimestamp, Coap::Message &aMessage)
 {
     uint16_t offset = aMessage.GetOffset();
     Dataset  dataset;
diff --git a/src/core/meshcop/dataset_updater.cpp b/src/core/meshcop/dataset_updater.cpp
index e0a6222..00782b2 100644
--- a/src/core/meshcop/dataset_updater.cpp
+++ b/src/core/meshcop/dataset_updater.cpp
@@ -39,7 +39,7 @@
 #include "common/code_utils.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "common/random.hpp"
 
 namespace ot {
@@ -54,7 +54,7 @@
 {
 }
 
-Error DatasetUpdater::RequestUpdate(const MeshCoP::Dataset::Info &aDataset, Callback aCallback, void *aContext)
+Error DatasetUpdater::RequestUpdate(const Dataset::Info &aDataset, Callback aCallback, void *aContext)
 {
     Error    error   = kErrorNone;
     Message *message = nullptr;
@@ -105,15 +105,15 @@
 
 void DatasetUpdater::PreparePendingDataset(void)
 {
-    Dataset                dataset;
-    MeshCoP::Dataset::Info requestedDataset;
-    Error                  error;
+    Dataset       dataset;
+    Dataset::Info requestedDataset;
+    Error         error;
 
     VerifyOrExit(!Get<Mle::Mle>().IsDisabled(), error = kErrorInvalidState);
 
     IgnoreError(mDataset->Read(0, requestedDataset));
 
-    error = Get<ActiveDataset>().Read(dataset);
+    error = Get<ActiveDatasetManager>().Read(dataset);
 
     if (error != kErrorNone)
     {
@@ -139,9 +139,9 @@
     {
         Timestamp timestamp;
 
-        if (Get<PendingDataset>().GetTimestamp() != nullptr)
+        if (Get<PendingDatasetManager>().GetTimestamp() != nullptr)
         {
-            timestamp = *Get<PendingDataset>().GetTimestamp();
+            timestamp = *Get<PendingDatasetManager>().GetTimestamp();
         }
 
         timestamp.AdvanceRandomTicks();
@@ -154,7 +154,7 @@
         tlv->GetTimestamp().AdvanceRandomTicks();
     }
 
-    SuccessOrExit(error = Get<PendingDataset>().Save(dataset));
+    SuccessOrExit(error = Get<PendingDatasetManager>().Save(dataset));
 
 exit:
     if (error != kErrorNone)
@@ -178,8 +178,8 @@
 
 void DatasetUpdater::HandleNotifierEvents(Events aEvents)
 {
-    MeshCoP::Dataset::Info requestedDataset;
-    MeshCoP::Dataset::Info dataset;
+    Dataset::Info requestedDataset;
+    Dataset::Info dataset;
 
     VerifyOrExit(mDataset != nullptr);
 
@@ -187,7 +187,7 @@
 
     IgnoreError(mDataset->Read(0, requestedDataset));
 
-    if (aEvents.Contains(kEventActiveDatasetChanged) && Get<MeshCoP::ActiveDataset>().Read(dataset) == kErrorNone)
+    if (aEvents.Contains(kEventActiveDatasetChanged) && Get<ActiveDatasetManager>().Read(dataset) == kErrorNone)
     {
         if (requestedDataset.IsSubsetOf(dataset))
         {
@@ -199,7 +199,7 @@
         }
     }
 
-    if (aEvents.Contains(kEventPendingDatasetChanged) && Get<MeshCoP::PendingDataset>().Read(dataset) == kErrorNone)
+    if (aEvents.Contains(kEventPendingDatasetChanged) && Get<PendingDatasetManager>().Read(dataset) == kErrorNone)
     {
         if (!requestedDataset.IsSubsetOf(dataset))
         {
diff --git a/src/core/meshcop/dataset_updater.hpp b/src/core/meshcop/dataset_updater.hpp
index c72c644..b3b94c0 100644
--- a/src/core/meshcop/dataset_updater.hpp
+++ b/src/core/meshcop/dataset_updater.hpp
@@ -74,13 +74,6 @@
      *
      * The function pointer has the syntax `void (*Callback)(Error aError, void *aContext)`.
      *
-     * @param[in] aError   The error status.
-     *                     kErrorNone           indicates Dataset update successfully finished.
-     *                     kErrorInvalidState   indicates failure due invalid state (MLE being disabled).
-     *                     kErrorAlready        indicates failure due to another device within network requesting a
-     *                                          conflicting Dataset update.
-     * @param[in] aContext A pointer to the arbitrary context provided by the user.
-     *
      */
     typedef otDatasetUpdaterCallback Callback;
 
@@ -101,7 +94,7 @@
      * @retval kErrorNoBufs         Could not allocated buffer to save Dataset.
      *
      */
-    Error RequestUpdate(const MeshCoP::Dataset::Info &aDataset, Callback aCallback, void *aContext);
+    Error RequestUpdate(const Dataset::Info &aDataset, Callback aCallback, void *aContext);
 
     /**
      * This method cancels an ongoing (if any) Operational Dataset update request.
diff --git a/src/core/meshcop/dtls.cpp b/src/core/meshcop/dtls.cpp
index 12a4303..7a558aa 100644
--- a/src/core/meshcop/dtls.cpp
+++ b/src/core/meshcop/dtls.cpp
@@ -46,7 +46,7 @@
 #include "common/encoding.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "common/timer.hpp"
 #include "crypto/mbedtls.hpp"
 #include "crypto/sha256.hpp"
@@ -57,7 +57,14 @@
 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)
 const int Dtls::sHashes[] = {MBEDTLS_MD_SHA256, MBEDTLS_MD_NONE};
 #endif
@@ -169,10 +176,10 @@
 {
     switch (mState)
     {
-    case MeshCoP::Dtls::kStateClosed:
+    case Dtls::kStateClosed:
         ExitNow();
 
-    case MeshCoP::Dtls::kStateOpen:
+    case Dtls::kStateOpen:
         IgnoreError(mSocket.Connect(Ip6::SockAddr(aMessageInfo.GetPeerAddr(), aMessageInfo.GetPeerPort())));
 
         mMessageInfo.SetPeerAddr(aMessageInfo.GetPeerAddr());
@@ -197,7 +204,7 @@
     }
 
 #ifdef MBEDTLS_SSL_SRV_C
-    if (mState == MeshCoP::Dtls::kStateConnecting)
+    if (mState == Dtls::kStateConnecting)
     {
         IgnoreError(SetClientId(mMessageInfo.GetPeerAddr().mFields.m8, sizeof(mMessageInfo.GetPeerAddr().mFields)));
     }
@@ -289,7 +296,11 @@
     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)
         mbedtls_ssl_conf_sig_hashes(&mConf, sHashes);
 #endif
@@ -338,12 +349,12 @@
 
     if (mCipherSuites[0] == MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8)
     {
-        otLogInfoMeshCoP("DTLS started");
+        LogInfo("DTLS started");
     }
 #if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
     else
     {
-        otLogInfoCoap("Application Coap Secure DTLS started");
+        LogInfo("Application Coap Secure DTLS started");
     }
 #endif
 
@@ -408,7 +419,7 @@
         break;
 
     default:
-        otLogCritCoap("Application Coap Secure DTLS: Not supported cipher.");
+        LogCrit("Application Coap Secure: Not supported cipher.");
         rval = MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
         ExitNow();
         break;
@@ -524,12 +535,19 @@
 
     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;
@@ -590,12 +608,12 @@
 
     if (mCipherSuites[0] == MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8)
     {
-        otLogDebgMeshCoP("Dtls::HandleMbedtlsTransmit");
+        LogDebg("HandleMbedtlsTransmit");
     }
 #if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
     else
     {
-        otLogDebgCoap("Dtls::ApplicationCoapSecure HandleMbedtlsTransmit");
+        LogDebg("ApplicationCoapSecure HandleMbedtlsTransmit");
     }
 #endif
 
@@ -615,7 +633,7 @@
         break;
 
     default:
-        otLogWarnMeshCoP("Dtls::HandleMbedtlsTransmit: %s error", ErrorToString(error));
+        LogWarn("HandleMbedtlsTransmit: %s error", ErrorToString(error));
         rval = MBEDTLS_ERR_NET_SEND_FAILED;
         break;
     }
@@ -634,12 +652,12 @@
 
     if (mCipherSuites[0] == MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8)
     {
-        otLogDebgMeshCoP("Dtls::HandleMbedtlsReceive");
+        LogDebg("HandleMbedtlsReceive");
     }
 #if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
     else
     {
-        otLogDebgCoap("Dtls:: ApplicationCoapSecure HandleMbedtlsReceive");
+        LogDebg("ApplicationCoapSecure HandleMbedtlsReceive");
     }
 #endif
 
@@ -669,12 +687,12 @@
 
     if (mCipherSuites[0] == MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8)
     {
-        otLogDebgMeshCoP("Dtls::HandleMbedtlsGetTimer");
+        LogDebg("HandleMbedtlsGetTimer");
     }
 #if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
     else
     {
-        otLogDebgCoap("Dtls:: ApplicationCoapSecure HandleMbedtlsGetTimer");
+        LogDebg("ApplicationCoapSecure HandleMbedtlsGetTimer");
     }
 #endif
 
@@ -707,12 +725,12 @@
 {
     if (mCipherSuites[0] == MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8)
     {
-        otLogDebgMeshCoP("Dtls::SetTimer");
+        LogDebg("SetTimer");
     }
 #if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
     else
     {
-        otLogDebgCoap("Dtls::ApplicationCoapSecure SetTimer");
+        LogDebg("ApplicationCoapSecure SetTimer");
     }
 #endif
 
@@ -769,7 +787,7 @@
     sha256.Update(keyBlock, kDtlsKeyBlockSize);
     sha256.Finish(kek);
 
-    otLogDebgMeshCoP("Generated KEK");
+    LogDebg("Generated KEK");
     Get<KeyManager>().SetKek(kek.GetBytes());
 
 exit:
@@ -778,12 +796,12 @@
 
 #else
 
-int Dtls::HandleMbedtlsExportKeys(void *aContext,
+int Dtls::HandleMbedtlsExportKeys(void *               aContext,
                                   const unsigned char *aMasterSecret,
                                   const unsigned char *aKeyBlock,
-                                  size_t aMacLength,
-                                  size_t aKeyLength,
-                                  size_t aIvLength)
+                                  size_t               aMacLength,
+                                  size_t               aKeyLength,
+                                  size_t               aIvLength)
 {
     return static_cast<Dtls *>(aContext)->HandleMbedtlsExportKeys(aMasterSecret, aKeyBlock, aMacLength, aKeyLength,
                                                                   aIvLength);
@@ -791,14 +809,14 @@
 
 int Dtls::HandleMbedtlsExportKeys(const unsigned char *aMasterSecret,
                                   const unsigned char *aKeyBlock,
-                                  size_t aMacLength,
-                                  size_t aKeyLength,
-                                  size_t aIvLength)
+                                  size_t               aMacLength,
+                                  size_t               aKeyLength,
+                                  size_t               aIvLength)
 {
     OT_UNUSED_VARIABLE(aMasterSecret);
 
     Crypto::Sha256::Hash kek;
-    Crypto::Sha256 sha256;
+    Crypto::Sha256       sha256;
 
     VerifyOrExit(mCipherSuites[0] == MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8);
 
@@ -806,7 +824,7 @@
     sha256.Update(aKeyBlock, 2 * static_cast<uint16_t>(aMacLength + aKeyLength + aIvLength));
     sha256.Finish(kek);
 
-    otLogDebgMeshCoP("Generated KEK");
+    LogDebg("Generated KEK");
     Get<KeyManager>().SetKek(kek.GetBytes());
 
 exit:
@@ -952,20 +970,20 @@
     switch (aLevel)
     {
     case 1:
-        otLogCritMbedTls("[%hu] %s", mSocket.GetSockName().mPort, aStr);
+        LogCrit("[%hu] %s", mSocket.GetSockName().mPort, aStr);
         break;
 
     case 2:
-        otLogWarnMbedTls("[%hu] %s", mSocket.GetSockName().mPort, aStr);
+        LogWarn("[%hu] %s", mSocket.GetSockName().mPort, aStr);
         break;
 
     case 3:
-        otLogInfoMbedTls("[%hu] %s", mSocket.GetSockName().mPort, aStr);
+        LogInfo("[%hu] %s", mSocket.GetSockName().mPort, aStr);
         break;
 
     case 4:
     default:
-        otLogDebgMbedTls("[%hu] %s", mSocket.GetSockName().mPort, aStr);
+        LogDebg("[%hu] %s", mSocket.GetSockName().mPort, aStr);
         break;
     }
 }
diff --git a/src/core/meshcop/dtls.hpp b/src/core/meshcop/dtls.hpp
index 40db551..0f809de 100644
--- a/src/core/meshcop/dtls.hpp
+++ b/src/core/meshcop/dtls.hpp
@@ -72,7 +72,7 @@
     /**
      * This constructor initializes the DTLS object.
      *
-     * @param[in]  aNetif               A reference to the Thread network interface.
+     * @param[in]  aInstance            A reference to the OpenThread instance.
      * @param[in]  aLayerTwoSecurity    Specifies whether to use layer two security or not.
      *
      */
@@ -357,7 +357,7 @@
 #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;
+    static constexpr uint16_t         kApplicationDataMaxLength = OPENTHREAD_CONFIG_DTLS_APPLICATION_DATA_MAX_LENGTH;
 #endif
 
     static constexpr size_t kDtlsKeyBlockSize     = 40;
@@ -442,7 +442,12 @@
     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)
     static const int sHashes[];
 #endif
diff --git a/src/core/meshcop/energy_scan_client.cpp b/src/core/meshcop/energy_scan_client.cpp
index e227365..09e8fbd 100644
--- a/src/core/meshcop/energy_scan_client.cpp
+++ b/src/core/meshcop/energy_scan_client.cpp
@@ -42,7 +42,7 @@
 #include "common/encoding.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "meshcop/meshcop.hpp"
 #include "meshcop/meshcop_tlvs.hpp"
 #include "thread/thread_netif.hpp"
@@ -50,6 +50,8 @@
 
 namespace ot {
 
+RegisterLogModule("EnergyScanClnt");
+
 EnergyScanClient::EnergyScanClient(Instance &aInstance)
     : InstanceLocator(aInstance)
     , mCallback(nullptr)
@@ -69,7 +71,7 @@
 {
     Error                   error = kErrorNone;
     MeshCoP::ChannelMaskTlv channelMask;
-    Ip6::MessageInfo        messageInfo;
+    Tmf::MessageInfo        messageInfo(GetInstance());
     Coap::Message *         message = nullptr;
 
     VerifyOrExit(Get<MeshCoP::Commissioner>().IsActive(), error = kErrorInvalidState);
@@ -89,12 +91,10 @@
     SuccessOrExit(error = Tlv::Append<MeshCoP::PeriodTlv>(*message, aPeriod));
     SuccessOrExit(error = Tlv::Append<MeshCoP::ScanDurationTlv>(*message, aScanDuration));
 
-    messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetMeshLocal16());
-    messageInfo.SetPeerAddr(aAddress);
-    messageInfo.SetPeerPort(Tmf::kUdpPort);
+    messageInfo.SetSockAddrToRlocPeerAddrTo(aAddress);
     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo));
 
-    otLogInfoMeshCoP("sent energy scan query");
+    LogInfo("sent query");
 
     mCallback = aCallback;
     mContext  = aContext;
@@ -122,7 +122,7 @@
 
     VerifyOrExit(aMessage.IsConfirmablePostRequest());
 
-    otLogInfoMeshCoP("received energy scan report");
+    LogInfo("received report");
 
     VerifyOrExit((mask = MeshCoP::ChannelMaskTlv::GetChannelMask(aMessage)) != 0);
 
@@ -136,7 +136,7 @@
 
     SuccessOrExit(Get<Tmf::Agent>().SendEmptyAck(aMessage, aMessageInfo));
 
-    otLogInfoMeshCoP("sent energy scan report response");
+    LogInfo("sent report response");
 
 exit:
     return;
diff --git a/etc/cmake/openthread-config-generic.h.in b/src/core/meshcop/extended_panid.cpp
similarity index 61%
rename from etc/cmake/openthread-config-generic.h.in
rename to src/core/meshcop/extended_panid.cpp
index 95c23b8..b90301a 100644
--- a/etc/cmake/openthread-config-generic.h.in
+++ b/src/core/meshcop/extended_panid.cpp
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2019, The OpenThread Authors.
+ *  Copyright (c) 2022, The OpenThread Authors.
  *  All rights reserved.
  *
  *  Redistribution and use in source and binary forms, with or without
@@ -25,3 +25,45 @@
  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  *  POSSIBILITY OF SUCH DAMAGE.
  */
+
+/**
+ * @file
+ *   This file implements Extended PAN ID management.
+ *
+ */
+
+#include "extended_panid.hpp"
+
+#include "common/locator_getters.hpp"
+#include "common/notifier.hpp"
+
+namespace ot {
+namespace MeshCoP {
+
+const otExtendedPanId ExtendedPanIdManager::sExtendedPanidInit = {
+    {0xde, 0xad, 0x00, 0xbe, 0xef, 0x00, 0xca, 0xfe},
+};
+
+ExtendedPanId::InfoString ExtendedPanId::ToString(void) const
+{
+    InfoString string;
+
+    string.AppendHexBytes(m8, sizeof(ExtendedPanId));
+
+    return string;
+}
+
+ExtendedPanIdManager::ExtendedPanIdManager(Instance &aInstance)
+    : InstanceLocator(aInstance)
+{
+    mExtendedPanId.Clear();
+    SetExtPanId(AsCoreType(&sExtendedPanidInit));
+}
+
+void ExtendedPanIdManager::SetExtPanId(const ExtendedPanId &aExtendedPanId)
+{
+    IgnoreError(Get<Notifier>().Update(mExtendedPanId, aExtendedPanId, kEventThreadExtPanIdChanged));
+}
+
+} // namespace MeshCoP
+} // namespace ot
diff --git a/src/core/meshcop/extended_panid.hpp b/src/core/meshcop/extended_panid.hpp
new file mode 100644
index 0000000..9bf5cf9
--- /dev/null
+++ b/src/core/meshcop/extended_panid.hpp
@@ -0,0 +1,117 @@
+/*
+ *  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.
+ */
+
+/**
+ * @file
+ *   This file includes definitions for managing the Extended PAN ID.
+ *
+ */
+
+#ifndef MESHCOP_EXTENDED_PANID_HPP_
+#define MESHCOP_EXTENDED_PANID_HPP_
+
+#include "openthread-core-config.h"
+
+#include <openthread/dataset.h>
+
+#include "common/as_core_type.hpp"
+#include "common/clearable.hpp"
+#include "common/equatable.hpp"
+#include "common/locator.hpp"
+#include "common/non_copyable.hpp"
+#include "common/string.hpp"
+
+namespace ot {
+namespace MeshCoP {
+
+/**
+ * This class represents an Extended PAN Identifier.
+ *
+ */
+OT_TOOL_PACKED_BEGIN
+class ExtendedPanId : public otExtendedPanId, public Equatable<ExtendedPanId>, public Clearable<ExtendedPanId>
+{
+public:
+    static constexpr uint16_t kInfoStringSize = 17; ///< Max chars for the info string (`ToString()`).
+
+    /**
+     * This type defines the fixed-length `String` object returned from `ToString()`.
+     *
+     */
+    typedef String<kInfoStringSize> InfoString;
+
+    /**
+     * This method converts an address to a string.
+     *
+     * @returns An `InfoString` containing the string representation of the Extended PAN Identifier.
+     *
+     */
+    InfoString ToString(void) const;
+
+} OT_TOOL_PACKED_END;
+
+class ExtendedPanIdManager : public InstanceLocator, private NonCopyable
+{
+public:
+    /**
+     * Constructor.
+     *
+     * @param[in]  aInstance  A reference to the OpenThread instance.
+     *
+     */
+    explicit ExtendedPanIdManager(Instance &aInstance);
+
+    /**
+     * This method returns the Extended PAN Identifier.
+     *
+     * @returns The Extended PAN Identifier.
+     *
+     */
+    const ExtendedPanId &GetExtPanId(void) const { return mExtendedPanId; }
+
+    /**
+     * This method sets the Extended PAN Identifier.
+     *
+     * @param[in]  aExtendedPanId  The Extended PAN Identifier.
+     *
+     */
+    void SetExtPanId(const ExtendedPanId &aExtendedPanId);
+
+private:
+    static const otExtendedPanId sExtendedPanidInit;
+
+    ExtendedPanId mExtendedPanId;
+};
+
+} // namespace MeshCoP
+
+DefineCoreType(otExtendedPanId, MeshCoP::ExtendedPanId);
+
+} // namespace ot
+
+#endif // MESHCOP_EXTENDED_PANID_HPP_
diff --git a/src/core/meshcop/joiner.cpp b/src/core/meshcop/joiner.cpp
index 52cac4c..4c48316 100644
--- a/src/core/meshcop/joiner.cpp
+++ b/src/core/meshcop/joiner.cpp
@@ -37,13 +37,14 @@
 
 #include <stdio.h>
 
+#include "common/array.hpp"
 #include "common/as_core_type.hpp"
 #include "common/code_utils.hpp"
 #include "common/debug.hpp"
 #include "common/encoding.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "common/string.hpp"
 #include "meshcop/meshcop.hpp"
 #include "radio/radio.hpp"
@@ -54,6 +55,8 @@
 namespace ot {
 namespace MeshCoP {
 
+RegisterLogModule("Joiner");
+
 Joiner::Joiner(Instance &aInstance)
     : InstanceLocator(aInstance)
     , mId()
@@ -120,7 +123,7 @@
 
     SuccessOrExit(Get<Notifier>().Update(mState, aState, kEventJoinerStateChanged));
 
-    otLogInfoMeshCoP("JoinerState: %s -> %s", StateToString(oldState), StateToString(aState));
+    LogInfo("JoinerState: %s -> %s", StateToString(oldState), StateToString(aState));
 exit:
     return;
 }
@@ -139,7 +142,7 @@
     Mac::ExtAddress              randomAddress;
     SteeringData::HashBitIndexes filterIndexes;
 
-    otLogInfoMeshCoP("Joiner starting");
+    LogInfo("Joiner starting");
 
     VerifyOrExit(aProvisioningUrl == nullptr || IsValidUtf8String(aProvisioningUrl), error = kErrorInvalidArgs);
     VerifyOrExit(aVendorName == nullptr || IsValidUtf8String(aVendorName), error = kErrorInvalidArgs);
@@ -196,7 +199,7 @@
 
 void Joiner::Stop(void)
 {
-    otLogInfoMeshCoP("Joiner stopped");
+    LogInfo("Joiner stopped");
 
     // Callback is set to `nullptr` to skip calling it from `Finish()`
     mCallback = nullptr;
@@ -302,14 +305,14 @@
 {
     uint8_t       priority;
     bool          doesAllowAny;
-    JoinerRouter *end = OT_ARRAY_END(mJoinerRouters);
+    JoinerRouter *end = GetArrayEnd(mJoinerRouters);
     JoinerRouter *entry;
 
     doesAllowAny = AsCoreType(&aResult.mSteeringData).PermitsAllJoiners();
 
-    otLogInfoMeshCoP("Joiner discover network: %s, pan:0x%04x, port:%d, chan:%d, rssi:%d, allow-any:%s",
-                     AsCoreType(&aResult.mExtAddress).ToString().AsCString(), aResult.mPanId, aResult.mJoinerUdpPort,
-                     aResult.mChannel, aResult.mRssi, ToYesNo(doesAllowAny));
+    LogInfo("Joiner discover network: %s, pan:0x%04x, port:%d, chan:%d, rssi:%d, allow-any:%s",
+            AsCoreType(&aResult.mExtAddress).ToString().AsCString(), aResult.mPanId, aResult.mJoinerUdpPort,
+            aResult.mChannel, aResult.mRssi, ToYesNo(doesAllowAny));
 
     priority = CalculatePriority(aResult.mRssi, doesAllowAny);
 
@@ -342,7 +345,7 @@
 
 void Joiner::TryNextJoinerRouter(Error aPrevError)
 {
-    for (; mJoinerRouterIndex < OT_ARRAY_LENGTH(mJoinerRouters); mJoinerRouterIndex++)
+    for (; mJoinerRouterIndex < GetArrayLength(mJoinerRouters); mJoinerRouterIndex++)
     {
         JoinerRouter &router = mJoinerRouters[mJoinerRouterIndex];
         Error         error;
@@ -384,8 +387,8 @@
     Error         error = kErrorNotFound;
     Ip6::SockAddr sockAddr(aRouter.mJoinerUdpPort);
 
-    otLogInfoMeshCoP("Joiner connecting to %s, pan:0x%04x, chan:%d", aRouter.mExtAddr.ToString().AsCString(),
-                     aRouter.mPanId, aRouter.mChannel);
+    LogInfo("Joiner connecting to %s, pan:0x%04x, chan:%d", aRouter.mExtAddr.ToString().AsCString(), aRouter.mPanId,
+            aRouter.mChannel);
 
     Get<Mac::Mac>().SetPanId(aRouter.mPanId);
     SuccessOrExit(error = Get<Mac::Mac>().SetPanChannel(aRouter.mChannel));
@@ -439,11 +442,9 @@
     VendorStackVersionTlv vendorStackVersionTlv;
     ProvisioningUrlTlv    provisioningUrlTlv;
 
-    VerifyOrExit((mFinalizeMessage = Get<Coap::CoapSecure>().NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
+    mFinalizeMessage = Get<Coap::CoapSecure>().NewPriorityConfirmablePostMessage(UriPath::kJoinerFinalize);
+    VerifyOrExit(mFinalizeMessage != nullptr, error = kErrorNoBufs);
 
-    mFinalizeMessage->InitAsConfirmablePost();
-    SuccessOrExit(error = mFinalizeMessage->AppendUriPathOptions(UriPath::kJoinerFinalize));
-    SuccessOrExit(error = mFinalizeMessage->SetPayloadMarker());
     mFinalizeMessage->SetOffset(mFinalizeMessage->GetLength());
 
     SuccessOrExit(error = Tlv::Append<StateTlv>(*mFinalizeMessage, StateTlv::kAccept));
@@ -514,7 +515,7 @@
     SuccessOrExit(Get<Coap::CoapSecure>().SendMessage(*mFinalizeMessage, Joiner::HandleJoinerFinalizeResponse, this));
     mFinalizeMessage = nullptr;
 
-    otLogInfoMeshCoP("Joiner sent finalize");
+    LogInfo("Joiner sent finalize");
 
 exit:
     return;
@@ -545,7 +546,7 @@
     SetState(kStateEntrust);
     mTimer.Start(kReponseTimeout);
 
-    otLogInfoMeshCoP("Joiner received finalize response %d", state);
+    LogInfo("Joiner received finalize response %d", state);
 
 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
     LogCertMessage("[THCI] direction=recv | type=JOIN_FIN.rsp |", *aMessage);
@@ -568,8 +569,8 @@
 
     VerifyOrExit(mState == kStateEntrust && aMessage.IsConfirmablePostRequest(), error = kErrorDrop);
 
-    otLogInfoMeshCoP("Joiner received entrust");
-    otLogCertMeshCoP("[THCI] direction=recv | type=JOIN_ENT.ntf");
+    LogInfo("Joiner received entrust");
+    LogCert("[THCI] direction=recv | type=JOIN_ENT.ntf");
 
     datasetInfo.Clear();
 
@@ -578,9 +579,9 @@
     datasetInfo.SetChannel(Get<Mac::Mac>().GetPanChannel());
     datasetInfo.SetPanId(Get<Mac::Mac>().GetPanId());
 
-    IgnoreError(Get<MeshCoP::ActiveDataset>().Save(datasetInfo));
+    IgnoreError(Get<ActiveDatasetManager>().Save(datasetInfo));
 
-    otLogInfoMeshCoP("Joiner successful!");
+    LogInfo("Joiner successful!");
 
     SendJoinerEntrustResponse(aMessage, aMessageInfo);
 
@@ -597,8 +598,9 @@
     Coap::Message *  message;
     Ip6::MessageInfo responseInfo(aRequestInfo);
 
-    VerifyOrExit((message = Get<Tmf::Agent>().NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
-    SuccessOrExit(error = message->SetDefaultResponseHeader(aRequest));
+    message = Get<Tmf::Agent>().NewPriorityResponseMessage(aRequest);
+    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
+
     message->SetSubType(Message::kSubTypeJoinerEntrust);
 
     responseInfo.GetSockAddr().Clear();
@@ -606,8 +608,8 @@
 
     SetState(kStateJoined);
 
-    otLogInfoMeshCoP("Joiner sent entrust response");
-    otLogCertMeshCoP("[THCI] direction=send | type=JOIN_ENT.rsp");
+    LogInfo("Joiner sent entrust response");
+    LogCert("[THCI] direction=send | type=JOIN_ENT.rsp");
 
 exit:
     FreeMessageOnError(message, error);
@@ -675,12 +677,14 @@
 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
 void Joiner::LogCertMessage(const char *aText, const Coap::Message &aMessage) const
 {
+    OT_UNUSED_VARIABLE(aText);
+
     uint8_t buf[OPENTHREAD_CONFIG_MESSAGE_BUFFER_SIZE];
 
     VerifyOrExit(aMessage.GetLength() <= sizeof(buf));
     aMessage.ReadBytes(aMessage.GetOffset(), buf, aMessage.GetLength() - aMessage.GetOffset());
 
-    otDumpCertMeshCoP(aText, buf, aMessage.GetLength() - aMessage.GetOffset());
+    DumpCert(aText, buf, aMessage.GetLength() - aMessage.GetOffset());
 
 exit:
     return;
diff --git a/src/core/meshcop/joiner.hpp b/src/core/meshcop/joiner.hpp
index 0a1ebd8..ac894e9 100644
--- a/src/core/meshcop/joiner.hpp
+++ b/src/core/meshcop/joiner.hpp
@@ -45,7 +45,7 @@
 #include "coap/coap_secure.hpp"
 #include "common/as_core_type.hpp"
 #include "common/locator.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "common/message.hpp"
 #include "common/non_copyable.hpp"
 #include "mac/mac_types.hpp"
diff --git a/src/core/meshcop/joiner_router.cpp b/src/core/meshcop/joiner_router.cpp
index 5247b66..679ae81 100644
--- a/src/core/meshcop/joiner_router.cpp
+++ b/src/core/meshcop/joiner_router.cpp
@@ -42,7 +42,7 @@
 #include "common/encoding.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "meshcop/meshcop.hpp"
 #include "meshcop/meshcop_tlvs.hpp"
 #include "thread/mle.hpp"
@@ -52,6 +52,8 @@
 namespace ot {
 namespace MeshCoP {
 
+RegisterLogModule("JoinerRouter");
+
 JoinerRouter::JoinerRouter(Instance &aInstance)
     : InstanceLocator(aInstance)
     , mSocket(aInstance)
@@ -84,7 +86,7 @@
         IgnoreError(mSocket.Open(&JoinerRouter::HandleUdpReceive, this));
         IgnoreError(mSocket.Bind(port));
         IgnoreError(Get<Ip6::Filter>().AddUnsecurePort(port));
-        otLogInfoMeshCoP("Joiner Router: start");
+        LogInfo("Joiner Router: start");
     }
     else
     {
@@ -131,19 +133,17 @@
 {
     Error            error;
     Coap::Message *  message = nullptr;
-    Ip6::MessageInfo messageInfo;
+    Tmf::MessageInfo messageInfo(GetInstance());
     ExtendedTlv      tlv;
     uint16_t         borderAgentRloc;
     uint16_t         offset;
 
-    otLogInfoMeshCoP("JoinerRouter::HandleUdpReceive");
+    LogInfo("JoinerRouter::HandleUdpReceive");
 
     SuccessOrExit(error = GetBorderAgentRloc(Get<ThreadNetif>(), borderAgentRloc));
 
-    VerifyOrExit((message = Get<Tmf::Agent>().NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
-
-    SuccessOrExit(error = message->InitAsNonConfirmablePost(UriPath::kRelayRx));
-    SuccessOrExit(error = message->SetPayloadMarker());
+    message = Get<Tmf::Agent>().NewPriorityNonConfirmablePostMessage(UriPath::kRelayRx);
+    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
     SuccessOrExit(error = Tlv::Append<JoinerUdpPortTlv>(*message, aMessageInfo.GetPeerPort()));
     SuccessOrExit(error = Tlv::Append<JoinerIidTlv>(*message, aMessageInfo.GetPeerAddr().GetIid()));
@@ -156,14 +156,11 @@
     SuccessOrExit(error = message->SetLength(offset + tlv.GetLength()));
     aMessage.CopyTo(aMessage.GetOffset(), offset, tlv.GetLength(), *message);
 
-    messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetMeshLocal16());
-    messageInfo.SetPeerAddr(Get<Mle::MleRouter>().GetMeshLocal16());
-    messageInfo.GetPeerAddr().GetIid().SetLocator(borderAgentRloc);
-    messageInfo.SetPeerPort(Tmf::kUdpPort);
+    messageInfo.SetSockAddrToRlocPeerAddrTo(borderAgentRloc);
 
     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo));
 
-    otLogInfoMeshCoP("Sent relay rx");
+    LogInfo("Sent relay rx");
 
 exit:
     FreeMessageOnError(message, error);
@@ -190,7 +187,7 @@
 
     VerifyOrExit(aMessage.IsNonConfirmablePostRequest(), error = kErrorDrop);
 
-    otLogInfoMeshCoP("Received relay transmit");
+    LogInfo("Received relay transmit");
 
     SuccessOrExit(error = Tlv::Find<JoinerUdpPortTlv>(aMessage, joinerPort));
     SuccessOrExit(error = Tlv::Find<JoinerIidTlv>(aMessage, joinerIid));
@@ -209,7 +206,7 @@
 
     if (Tlv::Find<JoinerRouterKekTlv>(aMessage, kek) == kErrorNone)
     {
-        otLogInfoMeshCoP("Received kek");
+        LogInfo("Received kek");
 
         DelaySendingJoinerEntrust(messageInfo, kek);
     }
@@ -295,12 +292,12 @@
 
     IgnoreError(Get<Tmf::Agent>().AbortTransaction(&JoinerRouter::HandleJoinerEntrustResponse, this));
 
-    otLogInfoMeshCoP("Sending JOIN_ENT.ntf");
+    LogInfo("Sending JOIN_ENT.ntf");
     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, aMessageInfo,
                                                         &JoinerRouter::HandleJoinerEntrustResponse, this));
 
-    otLogInfoMeshCoP("Sent joiner entrust length = %d", message->GetLength());
-    otLogCertMeshCoP("[THCI] direction=send | type=JOIN_ENT.ntf");
+    LogInfo("Sent joiner entrust length = %d", message->GetLength());
+    LogCert("[THCI] direction=send | type=JOIN_ENT.ntf");
 
 exit:
     FreeMessageOnError(message, error);
@@ -316,23 +313,21 @@
     const Tlv *    tlv;
     NetworkKey     networkKey;
 
-    VerifyOrExit((message = Get<Tmf::Agent>().NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
+    message = Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(UriPath::kJoinerEntrust);
+    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
-    message->InitAsConfirmablePost();
-    SuccessOrExit(error = message->AppendUriPathOptions(UriPath::kJoinerEntrust));
-    SuccessOrExit(error = message->SetPayloadMarker());
     message->SetSubType(Message::kSubTypeJoinerEntrust);
 
     Get<KeyManager>().GetNetworkKey(networkKey);
     SuccessOrExit(error = Tlv::Append<NetworkKeyTlv>(*message, networkKey));
     SuccessOrExit(error = Tlv::Append<MeshLocalPrefixTlv>(*message, Get<Mle::MleRouter>().GetMeshLocalPrefix()));
-    SuccessOrExit(error = Tlv::Append<ExtendedPanIdTlv>(*message, Get<Mac::Mac>().GetExtendedPanId()));
+    SuccessOrExit(error = Tlv::Append<ExtendedPanIdTlv>(*message, Get<ExtendedPanIdManager>().GetExtPanId()));
 
     networkName.Init();
-    networkName.SetNetworkName(Get<Mac::Mac>().GetNetworkName().GetAsData());
+    networkName.SetNetworkName(Get<NetworkNameManager>().GetNetworkName().GetAsData());
     SuccessOrExit(error = networkName.AppendTo(*message));
 
-    IgnoreError(Get<ActiveDataset>().Read(dataset));
+    IgnoreError(Get<ActiveDatasetManager>().Read(dataset));
 
     if ((tlv = dataset.GetTlv<ActiveTimestampTlv>()) != nullptr)
     {
@@ -406,8 +401,8 @@
 
     VerifyOrExit(aMessage->GetCode() == Coap::kCodeChanged);
 
-    otLogInfoMeshCoP("Receive joiner entrust response");
-    otLogCertMeshCoP("[THCI] direction=recv | type=JOIN_ENT.rsp");
+    LogInfo("Receive joiner entrust response");
+    LogCert("[THCI] direction=recv | type=JOIN_ENT.rsp");
 
 exit:
     return;
diff --git a/src/core/meshcop/joiner_router.hpp b/src/core/meshcop/joiner_router.hpp
index 206972b..e8c6116 100644
--- a/src/core/meshcop/joiner_router.hpp
+++ b/src/core/meshcop/joiner_router.hpp
@@ -78,7 +78,7 @@
     /**
      * This method sets the Joiner UDP Port.
      *
-     * @param[in]  The Joiner UDP Port number.
+     * @param[in]  aJoinerUdpPort  The Joiner UDP Port number.
      *
      */
     void SetJoinerUdpPort(uint16_t aJoinerUdpPort);
diff --git a/src/core/meshcop/meshcop.cpp b/src/core/meshcop/meshcop.cpp
index 121144a..a61b0ae 100644
--- a/src/core/meshcop/meshcop.cpp
+++ b/src/core/meshcop/meshcop.cpp
@@ -36,7 +36,6 @@
 #include "common/crc16.hpp"
 #include "common/debug.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
 #include "common/string.hpp"
 #include "crypto/pbkdf2_cmac.hpp"
 #include "crypto/sha256.hpp"
@@ -44,6 +43,9 @@
 #include "thread/thread_netif.hpp"
 
 namespace ot {
+
+RegisterLogModule("MeshCoP");
+
 namespace MeshCoP {
 
 Error JoinerPskd::SetFrom(const char *aPskdString)
@@ -314,10 +316,10 @@
 }
 
 #if OPENTHREAD_FTD
-Error GeneratePskc(const char *              aPassPhrase,
-                   const Mac::NetworkName &  aNetworkName,
-                   const Mac::ExtendedPanId &aExtPanId,
-                   Pskc &                    aPskc)
+Error GeneratePskc(const char *         aPassPhrase,
+                   const NetworkName &  aNetworkName,
+                   const ExtendedPanId &aExtPanId,
+                   Pskc &               aPskc)
 {
     Error      error        = kErrorNone;
     const char saltPrefix[] = "Thread";
@@ -354,12 +356,12 @@
 }
 #endif // OPENTHREAD_FTD
 
-#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_WARN) && (OPENTHREAD_CONFIG_LOG_MESHCOP == 1)
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN)
 void LogError(const char *aActionText, Error aError)
 {
-    if (aError != kErrorNone)
+    if (aError != kErrorNone && aError != kErrorAlready)
     {
-        otLogWarnMeshCoP("Failed to %s: %s", aActionText, ErrorToString(aError));
+        LogWarn("Failed to %s: %s", aActionText, ErrorToString(aError));
     }
 }
 #endif
diff --git a/src/core/meshcop/meshcop.hpp b/src/core/meshcop/meshcop.hpp
index 5598372..0de1047 100644
--- a/src/core/meshcop/meshcop.hpp
+++ b/src/core/meshcop/meshcop.hpp
@@ -47,6 +47,7 @@
 #include "common/as_core_type.hpp"
 #include "common/clearable.hpp"
 #include "common/equatable.hpp"
+#include "common/log.hpp"
 #include "common/message.hpp"
 #include "common/string.hpp"
 #include "mac/mac_types.hpp"
@@ -204,7 +205,7 @@
     /**
      * This method indicates whether a given Joiner ID matches the Discerner.
      *
-     * @param[in] aJoiner  A Joiner ID to match with the Discerner.
+     * @param[in] aJoinerId  A Joiner ID to match with the Discerner.
      *
      * @returns TRUE if the Joiner ID matches the Discerner, FALSE otherwise.
      *
@@ -419,10 +420,10 @@
  * @retval kErrorInvalidArgs   If the length of passphrase is out of range.
  *
  */
-Error GeneratePskc(const char *              aPassPhrase,
-                   const Mac::NetworkName &  aNetworkName,
-                   const Mac::ExtendedPanId &aExtPanId,
-                   Pskc &                    aPskc);
+Error GeneratePskc(const char *         aPassPhrase,
+                   const NetworkName &  aNetworkName,
+                   const ExtendedPanId &aExtPanId,
+                   Pskc &               aPskc);
 
 /**
  * This function computes the Joiner ID from a factory-assigned IEEE EUI-64.
@@ -436,7 +437,7 @@
 /**
  * This function gets the border agent RLOC.
  *
- * @param[in]   aNetif  A reference to the thread interface.
+ * @param[in]   aNetIf  A reference to the thread interface.
  * @param[out]  aRloc   Border agent RLOC.
  *
  * @retval kErrorNone       Successfully got the Border Agent Rloc.
@@ -445,7 +446,7 @@
  */
 Error GetBorderAgentRloc(ThreadNetif &aNetIf, uint16_t &aRloc);
 
-#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_WARN) && (OPENTHREAD_CONFIG_LOG_MESHCOP == 1)
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN)
 /**
  * This function emits a log message indicating an error during a MeshCoP action.
  *
diff --git a/src/core/meshcop/meshcop_leader.cpp b/src/core/meshcop/meshcop_leader.cpp
index 570a80c..a072026 100644
--- a/src/core/meshcop/meshcop_leader.cpp
+++ b/src/core/meshcop/meshcop_leader.cpp
@@ -42,7 +42,7 @@
 #include "common/code_utils.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "common/random.hpp"
 #include "meshcop/meshcop.hpp"
 #include "meshcop/meshcop_tlvs.hpp"
@@ -53,6 +53,8 @@
 namespace ot {
 namespace MeshCoP {
 
+RegisterLogModule("MeshCoPLeader");
+
 Leader::Leader(Instance &aInstance)
     : InstanceLocator(aInstance)
     , mPetition(UriPath::kLeaderPetition, Leader::HandlePetition, this)
@@ -78,7 +80,7 @@
     CommissionerIdTlv commissionerId;
     StateTlv::State   state = StateTlv::kReject;
 
-    otLogInfoMeshCoP("received petition");
+    LogInfo("received petition");
 
     VerifyOrExit(Get<Mle::MleRouter>().IsRoutingLocator(aMessageInfo.GetPeerAddr()));
     SuccessOrExit(Tlv::FindTlv(aMessage, commissionerId));
@@ -126,10 +128,8 @@
     Error          error = kErrorNone;
     Coap::Message *message;
 
-    VerifyOrExit((message = Get<Tmf::Agent>().NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
-
-    SuccessOrExit(error = message->SetDefaultResponseHeader(aRequest));
-    SuccessOrExit(error = message->SetPayloadMarker());
+    message = Get<Tmf::Agent>().NewPriorityResponseMessage(aRequest);
+    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
     SuccessOrExit(error = Tlv::Append<StateTlv>(*message, aState));
 
@@ -145,7 +145,7 @@
 
     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, aMessageInfo));
 
-    otLogInfoMeshCoP("sent petition response");
+    LogInfo("sent petition response");
 
 exit:
     FreeMessageOnError(message, error);
@@ -164,7 +164,7 @@
     BorderAgentLocatorTlv *borderAgentLocator;
     StateTlv::State        responseState;
 
-    otLogInfoMeshCoP("received keep alive");
+    LogInfo("received keep alive");
 
     SuccessOrExit(Tlv::Find<StateTlv>(aMessage, state));
 
@@ -209,16 +209,14 @@
     Error          error = kErrorNone;
     Coap::Message *message;
 
-    VerifyOrExit((message = Get<Tmf::Agent>().NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
-
-    SuccessOrExit(error = message->SetDefaultResponseHeader(aRequest));
-    SuccessOrExit(error = message->SetPayloadMarker());
+    message = Get<Tmf::Agent>().NewPriorityResponseMessage(aRequest);
+    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
     SuccessOrExit(error = Tlv::Append<StateTlv>(*message, aState));
 
     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, aMessageInfo));
 
-    otLogInfoMeshCoP("sent keep alive response");
+    LogInfo("sent keep alive response");
 
 exit:
     FreeMessageOnError(message, error);
@@ -228,19 +226,16 @@
 void Leader::SendDatasetChanged(const Ip6::Address &aAddress)
 {
     Error            error = kErrorNone;
-    Ip6::MessageInfo messageInfo;
+    Tmf::MessageInfo messageInfo(GetInstance());
     Coap::Message *  message;
 
-    VerifyOrExit((message = Get<Tmf::Agent>().NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
+    message = Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(UriPath::kDatasetChanged);
+    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
-    SuccessOrExit(error = message->InitAsConfirmablePost(UriPath::kDatasetChanged));
-
-    messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetMeshLocal16());
-    messageInfo.SetPeerAddr(aAddress);
-    messageInfo.SetPeerPort(Tmf::kUdpPort);
+    messageInfo.SetSockAddrToRlocPeerAddrTo(aAddress);
     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo));
 
-    otLogInfoMeshCoP("sent dataset changed");
+    LogInfo("sent dataset changed");
 
 exit:
     FreeMessageOnError(message, error);
@@ -294,7 +289,7 @@
     mTimer.Stop();
     SetEmptyCommissionerData();
 
-    otLogInfoMeshCoP("commissioner inactive");
+    LogInfo("commissioner inactive");
 }
 
 } // namespace MeshCoP
diff --git a/src/core/meshcop/meshcop_tlvs.cpp b/src/core/meshcop/meshcop_tlvs.cpp
index f497293..c047ed9 100644
--- a/src/core/meshcop/meshcop_tlvs.cpp
+++ b/src/core/meshcop/meshcop_tlvs.cpp
@@ -115,7 +115,7 @@
     return tlv;
 }
 
-Mac::NameData NetworkNameTlv::GetNetworkName(void) const
+NameData NetworkNameTlv::GetNetworkName(void) const
 {
     uint8_t len = GetLength();
 
@@ -124,10 +124,10 @@
         len = sizeof(mNetworkName);
     }
 
-    return Mac::NameData(mNetworkName, len);
+    return NameData(mNetworkName, len);
 }
 
-void NetworkNameTlv::SetNetworkName(const Mac::NameData &aNameData)
+void NetworkNameTlv::SetNetworkName(const NameData &aNameData)
 {
     uint8_t len;
 
@@ -280,9 +280,9 @@
         entry->SetChannelPage(OT_RADIO_CHANNEL_PAGE_2);
         entry->SetMask(aChannelMask & OT_RADIO_915MHZ_OQPSK_CHANNEL_MASK);
 
-        length += sizeof(MeshCoP::ChannelMaskEntry);
+        length += sizeof(ChannelMaskEntry);
 
-        entry = static_cast<MeshCoP::ChannelMaskEntry *>(entry->GetNext());
+        entry = static_cast<ChannelMaskEntry *>(entry->GetNext());
     }
 #endif
 
@@ -294,7 +294,7 @@
         entry->SetChannelPage(OT_RADIO_CHANNEL_PAGE_0);
         entry->SetMask(aChannelMask & OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MASK);
 
-        length += sizeof(MeshCoP::ChannelMaskEntry);
+        length += sizeof(ChannelMaskEntry);
     }
 #endif
 
@@ -306,7 +306,7 @@
         entry->SetChannelPage(OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_PAGE);
         entry->SetMask(aChannelMask & OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_MASK);
 
-        length += sizeof(MeshCoP::ChannelMaskEntry);
+        length += sizeof(ChannelMaskEntry);
     }
 #endif
 
diff --git a/src/core/meshcop/meshcop_tlvs.hpp b/src/core/meshcop/meshcop_tlvs.hpp
index 378319d..ffe14a4 100644
--- a/src/core/meshcop/meshcop_tlvs.hpp
+++ b/src/core/meshcop/meshcop_tlvs.hpp
@@ -47,6 +47,8 @@
 #include "common/string.hpp"
 #include "common/tlvs.hpp"
 #include "mac/mac_types.hpp"
+#include "meshcop/extended_panid.hpp"
+#include "meshcop/network_name.hpp"
 #include "meshcop/timestamp.hpp"
 #include "net/ip6_address.hpp"
 #include "radio/radio.hpp"
@@ -445,7 +447,7 @@
  *
  */
 OT_TOOL_PACKED_BEGIN
-class ExtendedPanIdTlv : public Tlv, public SimpleTlvInfo<Tlv::kExtendedPanId, Mac::ExtendedPanId>
+class ExtendedPanIdTlv : public Tlv, public SimpleTlvInfo<Tlv::kExtendedPanId, ExtendedPanId>
 {
 public:
     /**
@@ -473,7 +475,7 @@
      * @returns The Extended PAN ID value.
      *
      */
-    const Mac::ExtendedPanId &GetExtendedPanId(void) const { return mExtendedPanId; }
+    const ExtendedPanId &GetExtendedPanId(void) const { return mExtendedPanId; }
 
     /**
      * This method sets the Extended PAN ID value.
@@ -481,10 +483,10 @@
      * @param[in]  aExtendedPanId  An Extended PAN ID value.
      *
      */
-    void SetExtendedPanId(const Mac::ExtendedPanId &aExtendedPanId) { mExtendedPanId = aExtendedPanId; }
+    void SetExtendedPanId(const ExtendedPanId &aExtendedPanId) { mExtendedPanId = aExtendedPanId; }
 
 private:
-    Mac::ExtendedPanId mExtendedPanId;
+    ExtendedPanId mExtendedPanId;
 } OT_TOOL_PACKED_END;
 
 /**
@@ -520,7 +522,7 @@
      * @returns The Network Name value (as `NameData`).
      *
      */
-    Mac::NameData GetNetworkName(void) const;
+    NameData GetNetworkName(void) const;
 
     /**
      * This method sets the Network Name value.
@@ -528,10 +530,10 @@
      * @param[in] aNameData   A Network Name value (as `NameData`).
      *
      */
-    void SetNetworkName(const Mac::NameData &aNameData);
+    void SetNetworkName(const NameData &aNameData);
 
 private:
-    char mNetworkName[Mac::NetworkName::kMaxSize];
+    char mNetworkName[NetworkName::kMaxSize];
 } OT_TOOL_PACKED_END;
 
 /**
@@ -827,7 +829,7 @@
     /**
      * This method sets the Border Agent Locator value.
      *
-     * @param[in]  aBorderAgentLocator  The Border Agent Locator value.
+     * @param[in]  aLocator  The Border Agent Locator value.
      *
      */
     void SetBorderAgentLocator(uint16_t aLocator) { mLocator = HostSwap16(aLocator); }
@@ -930,7 +932,7 @@
     /**
      * This method sets the Commissioner Session ID value.
      *
-     * @param[in]  aCommissionerSessionId  The Commissioner Session ID value.
+     * @param[in]  aSessionId  The Commissioner Session ID value.
      *
      */
     void SetCommissionerSessionId(uint16_t aSessionId) { mSessionId = HostSwap16(aSessionId); }
@@ -1500,7 +1502,7 @@
     /**
      * This method sets the Channel Mask Entries.
      *
-     * @param[in]  aMask  The Channel Mask value.
+     * @param[in]  aChannelMask  The Channel Mask value.
      *
      */
     void SetChannelMask(uint32_t aChannelMask);
diff --git a/src/core/meshcop/network_name.cpp b/src/core/meshcop/network_name.cpp
new file mode 100644
index 0000000..e4ae562
--- /dev/null
+++ b/src/core/meshcop/network_name.cpp
@@ -0,0 +1,171 @@
+/*
+ *  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.
+ */
+
+/**
+ * @file
+ *   This file implements Network Name management.
+ *
+ */
+
+#include "network_name.hpp"
+
+#include "common/locator_getters.hpp"
+#include "common/notifier.hpp"
+
+namespace ot {
+namespace MeshCoP {
+
+const char NetworkNameManager::sNetworkNameInit[] = "OpenThread";
+
+#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
+const char NetworkNameManager::sDomainNameInit[] = "DefaultDomain";
+#endif
+
+uint8_t NameData::CopyTo(char *aBuffer, uint8_t aMaxSize) const
+{
+    MutableData<kWithUint8Length> destData;
+
+    destData.Init(aBuffer, aMaxSize);
+    destData.ClearBytes();
+    IgnoreError(destData.CopyBytesFrom(*this));
+
+    return destData.GetLength();
+}
+
+NameData NetworkName::GetAsData(void) const
+{
+    return NameData(m8, static_cast<uint8_t>(StringLength(m8, kMaxSize + 1)));
+}
+
+Error NetworkName::Set(const char *aNameString)
+{
+    // When setting `NetworkName` from a string, we treat it as `NameData`
+    // with `kMaxSize + 1` chars. `NetworkName::Set(data)` will look
+    // for null char in the data (within its given size) to calculate
+    // the name's length and ensure that the name fits in `kMaxSize`
+    // chars. The `+ 1` ensures that a `aNameString` with length
+    // longer than `kMaxSize` is correctly rejected (returning error
+    // `kErrorInvalidArgs`).
+
+    Error    error;
+    NameData data(aNameString, kMaxSize + 1);
+
+    VerifyOrExit(IsValidUtf8String(aNameString), error = kErrorInvalidArgs);
+
+    error = Set(data);
+
+exit:
+    return error;
+}
+
+Error NetworkName::Set(const NameData &aNameData)
+{
+    Error    error  = kErrorNone;
+    NameData data   = aNameData;
+    uint8_t  newLen = static_cast<uint8_t>(StringLength(data.GetBuffer(), data.GetLength()));
+
+    VerifyOrExit((0 < newLen) && (newLen <= kMaxSize), error = kErrorInvalidArgs);
+
+    data.SetLength(newLen);
+
+    // Ensure the new name does not match the current one.
+    if (data.MatchesBytesIn(m8) && m8[newLen] == '\0')
+    {
+        ExitNow(error = kErrorAlready);
+    }
+
+    data.CopyBytesTo(m8);
+    m8[newLen] = '\0';
+
+exit:
+    return error;
+}
+
+bool NetworkName::operator==(const NetworkName &aOther) const
+{
+    return GetAsData() == aOther.GetAsData();
+}
+
+NetworkNameManager::NetworkNameManager(Instance &aInstance)
+    : InstanceLocator(aInstance)
+{
+    IgnoreError(SetNetworkName(sNetworkNameInit));
+
+#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
+    IgnoreError(SetDomainName(sDomainNameInit));
+#endif
+}
+
+Error NetworkNameManager::SetNetworkName(const char *aNameString)
+{
+    return SignalNetworkNameChange(mNetworkName.Set(aNameString));
+}
+
+Error NetworkNameManager::SetNetworkName(const NameData &aNameData)
+{
+    return SignalNetworkNameChange(mNetworkName.Set(aNameData));
+}
+
+Error NetworkNameManager::SignalNetworkNameChange(Error aError)
+{
+    switch (aError)
+    {
+    case kErrorNone:
+        Get<Notifier>().Signal(kEventThreadNetworkNameChanged);
+        break;
+
+    case kErrorAlready:
+        Get<Notifier>().SignalIfFirst(kEventThreadNetworkNameChanged);
+        aError = kErrorNone;
+        break;
+
+    default:
+        break;
+    }
+
+    return aError;
+}
+
+#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
+Error NetworkNameManager::SetDomainName(const char *aNameString)
+{
+    Error error = mDomainName.Set(aNameString);
+
+    return (error == kErrorAlready) ? kErrorNone : error;
+}
+
+Error NetworkNameManager::SetDomainName(const NameData &aNameData)
+{
+    Error error = mDomainName.Set(aNameData);
+
+    return (error == kErrorAlready) ? kErrorNone : error;
+}
+#endif // (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
+
+} // namespace MeshCoP
+} // namespace ot
diff --git a/src/core/meshcop/network_name.hpp b/src/core/meshcop/network_name.hpp
new file mode 100644
index 0000000..1f6d847
--- /dev/null
+++ b/src/core/meshcop/network_name.hpp
@@ -0,0 +1,281 @@
+/*
+ *  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.
+ */
+
+/**
+ * @file
+ *   This file includes definitions for managing the Network Name.
+ *
+ */
+
+#ifndef MESHCOP_NETWORK_NAME_HPP_
+#define MESHCOP_NETWORK_NAME_HPP_
+
+#include "openthread-core-config.h"
+
+#include <openthread/dataset.h>
+
+#include "common/as_core_type.hpp"
+#include "common/data.hpp"
+#include "common/equatable.hpp"
+#include "common/locator.hpp"
+#include "common/non_copyable.hpp"
+#include "common/string.hpp"
+
+namespace ot {
+namespace MeshCoP {
+
+/**
+ * This class represents a name string as data (pointer to a char buffer along with a length).
+ *
+ * @note The char array does NOT need to be null terminated.
+ *
+ */
+class NameData : private Data<kWithUint8Length>
+{
+    friend class NetworkName;
+
+public:
+    /**
+     * This constructor initializes the NameData object.
+     *
+     * @param[in] aBuffer   A pointer to a `char` buffer (does not need to be null terminated).
+     * @param[in] aLength   The length (number of chars) in the buffer.
+     *
+     */
+    NameData(const char *aBuffer, uint8_t aLength) { Init(aBuffer, aLength); }
+
+    /**
+     * This method returns the pointer to char buffer (not necessarily null terminated).
+     *
+     * @returns The pointer to the char buffer.
+     *
+     */
+    const char *GetBuffer(void) const { return reinterpret_cast<const char *>(GetBytes()); }
+
+    /**
+     * This method returns the length (number of chars in buffer).
+     *
+     * @returns The name length.
+     *
+     */
+    uint8_t GetLength(void) const { return Data<kWithUint8Length>::GetLength(); }
+
+    /**
+     * This method copies the name data into a given char buffer with a given size.
+     *
+     * The given buffer is cleared (`memset` to zero) before copying the name into it. The copied string
+     * in @p aBuffer is NOT necessarily null terminated.
+     *
+     * @param[out] aBuffer   A pointer to a buffer where to copy the name into.
+     * @param[in]  aMaxSize  Size of @p aBuffer (maximum number of chars to write into @p aBuffer).
+     *
+     * @returns The actual number of chars copied into @p aBuffer.
+     *
+     */
+    uint8_t CopyTo(char *aBuffer, uint8_t aMaxSize) const;
+};
+
+/**
+ * This structure represents an Network Name.
+ *
+ */
+class NetworkName : public otNetworkName, public Unequatable<NetworkName>
+{
+public:
+    /**
+     * This constant specified the maximum number of chars in Network Name (excludes null char).
+     *
+     */
+    static constexpr uint8_t kMaxSize = OT_NETWORK_NAME_MAX_SIZE;
+
+    /**
+     * This constructor initializes the Network Name as an empty string.
+     *
+     */
+    NetworkName(void) { m8[0] = '\0'; }
+
+    /**
+     * This method gets the Network Name as a null terminated C string.
+     *
+     * @returns The Network Name as a null terminated C string array.
+     *
+     */
+    const char *GetAsCString(void) const { return m8; }
+
+    /**
+     * This method gets the Network Name as NameData.
+     *
+     * @returns The Network Name as NameData.
+     *
+     */
+    NameData GetAsData(void) const;
+
+    /**
+     * This method sets the Network Name from a given null terminated C string.
+     *
+     * This method also validates that the given @p aNameString follows UTF-8 encoding and can fit in `kMaxSize`
+     * chars.
+     *
+     * @param[in] aNameString      A name C string.
+     *
+     * @retval kErrorNone          Successfully set the Network Name.
+     * @retval kErrorAlready       The name is already set to the same string.
+     * @retval kErrorInvalidArgs   Given name is invalid (too long or does not follow UTF-8 encoding).
+     *
+     */
+    Error Set(const char *aNameString);
+
+    /**
+     * This method sets the Network Name.
+     *
+     * @param[in]  aNameData           A reference to name data.
+     *
+     * @retval kErrorNone          Successfully set the Network Name.
+     * @retval kErrorAlready       The name is already set to the same string.
+     * @retval kErrorInvalidArgs   Given name is too long.
+     *
+     */
+    Error Set(const NameData &aNameData);
+
+    /**
+     * This method overloads operator `==` to evaluate whether or not two given `NetworkName` objects are equal.
+     *
+     * @param[in]  aOther  The other `NetworkName` to compare with.
+     *
+     * @retval TRUE   If the two are equal.
+     * @retval FALSE  If the two are not equal.
+     *
+     */
+    bool operator==(const NetworkName &aOther) const;
+};
+
+#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
+/**
+ * This type represents a Thread Domain Name.
+ *
+ */
+typedef NetworkName DomainName;
+#endif
+
+/**
+ * This class manages the Network Name value.
+ *
+ */
+class NetworkNameManager : public InstanceLocator, private NonCopyable
+{
+public:
+    /**
+     * Constructor.
+     *
+     * @param[in]  aInstance  A reference to the OpenThread instance.
+     *
+     */
+    explicit NetworkNameManager(Instance &aInstance);
+
+    /**
+     * This method returns the Network Name.
+     *
+     * @returns The Network Name.
+     *
+     */
+    const NetworkName &GetNetworkName(void) const { return mNetworkName; }
+
+    /**
+     * This method sets the Network Name.
+     *
+     * @param[in]  aNameString   A pointer to a string character array. Must be null terminated.
+     *
+     * @retval kErrorNone          Successfully set the Network Name.
+     * @retval kErrorInvalidArgs   Given name is too long.
+     *
+     */
+    Error SetNetworkName(const char *aNameString);
+
+    /**
+     * This method sets the Network Name.
+     *
+     * @param[in]  aNameData     A name data (pointer to char buffer and length).
+     *
+     * @retval kErrorNone          Successfully set the Network Name.
+     * @retval kErrorInvalidArgs   Given name is too long.
+     *
+     */
+    Error SetNetworkName(const NameData &aNameData);
+
+#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
+    /**
+     * This method returns the Thread Domain Name.
+     *
+     * @returns The Thread Domain Name.
+     *
+     */
+    const DomainName &GetDomainName(void) const { return mDomainName; }
+
+    /**
+     * This method sets the Thread Domain Name.
+     *
+     * @param[in]  aNameString   A pointer to a string character array. Must be null terminated.
+     *
+     * @retval kErrorNone          Successfully set the Thread Domain Name.
+     * @retval kErrorInvalidArgs   Given name is too long.
+     *
+     */
+    Error SetDomainName(const char *aNameString);
+
+    /**
+     * This method sets the Thread Domain Name.
+     *
+     * @param[in]  aNameData     A name data (pointer to char buffer and length).
+     *
+     * @retval kErrorNone          Successfully set the Thread Domain Name.
+     * @retval kErrorInvalidArgs   Given name is too long.
+     *
+     */
+    Error SetDomainName(const NameData &aNameData);
+#endif // (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
+
+private:
+    Error SignalNetworkNameChange(Error aError);
+
+    static const char sNetworkNameInit[];
+    static const char sDomainNameInit[];
+
+    NetworkName mNetworkName;
+
+#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
+    DomainName mDomainName;
+#endif
+};
+
+} // namespace MeshCoP
+
+DefineCoreType(otNetworkName, MeshCoP::NetworkName);
+
+} // namespace ot
+
+#endif // MESHCOP_EXTENDED_PANID_HPP_
diff --git a/src/core/meshcop/panid_query_client.cpp b/src/core/meshcop/panid_query_client.cpp
index 094b1c5..7bacc80 100644
--- a/src/core/meshcop/panid_query_client.cpp
+++ b/src/core/meshcop/panid_query_client.cpp
@@ -41,7 +41,7 @@
 #include "common/debug.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "meshcop/meshcop.hpp"
 #include "meshcop/meshcop_tlvs.hpp"
 #include "thread/thread_netif.hpp"
@@ -49,6 +49,8 @@
 
 namespace ot {
 
+RegisterLogModule("PanIdQueryClnt");
+
 PanIdQueryClient::PanIdQueryClient(Instance &aInstance)
     : InstanceLocator(aInstance)
     , mCallback(nullptr)
@@ -66,7 +68,7 @@
 {
     Error                   error = kErrorNone;
     MeshCoP::ChannelMaskTlv channelMask;
-    Ip6::MessageInfo        messageInfo;
+    Tmf::MessageInfo        messageInfo(GetInstance());
     Coap::Message *         message = nullptr;
 
     VerifyOrExit(Get<MeshCoP::Commissioner>().IsActive(), error = kErrorInvalidState);
@@ -84,12 +86,10 @@
 
     SuccessOrExit(error = Tlv::Append<MeshCoP::PanIdTlv>(*message, aPanId));
 
-    messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetMeshLocal16());
-    messageInfo.SetPeerAddr(aAddress);
-    messageInfo.SetPeerPort(Tmf::kUdpPort);
+    messageInfo.SetSockAddrToRlocPeerAddrTo(aAddress);
     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo));
 
-    otLogInfoMeshCoP("sent panid query");
+    LogInfo("sent panid query");
 
     mCallback = aCallback;
     mContext  = aContext;
@@ -112,7 +112,7 @@
 
     VerifyOrExit(aMessage.IsConfirmablePostRequest());
 
-    otLogInfoMeshCoP("received panid conflict");
+    LogInfo("received panid conflict");
 
     SuccessOrExit(Tlv::Find<MeshCoP::PanIdTlv>(aMessage, panId));
 
@@ -125,7 +125,7 @@
 
     SuccessOrExit(Get<Tmf::Agent>().SendEmptyAck(aMessage, responseInfo));
 
-    otLogInfoMeshCoP("sent panid query conflict response");
+    LogInfo("sent panid query conflict response");
 
 exit:
     return;
diff --git a/src/core/net/checksum.hpp b/src/core/net/checksum.hpp
index e56dc2f..311ba6d 100644
--- a/src/core/net/checksum.hpp
+++ b/src/core/net/checksum.hpp
@@ -71,7 +71,7 @@
     /**
      * This static method calculates and then updates the checksum in a given message (if UDP/ICMP6).
      *
-     * @param[inout] aMessage   The message to update the checksum in. The `aMessage.GetOffset()` should point to start
+     * @param[in,out] aMessage  The message to update the checksum in. The `aMessage.GetOffset()` should point to start
      *                          of the UDP/ICMP6 header. On exit the checksum field in UDP/ICMP6 header in the message
      *                          is updated.
      * @param[in] aSource       The source address.
diff --git a/src/core/net/dhcp6.hpp b/src/core/net/dhcp6.hpp
index 2287e31..6a1f3da 100644
--- a/src/core/net/dhcp6.hpp
+++ b/src/core/net/dhcp6.hpp
@@ -311,7 +311,7 @@
     /**
      * This method sets the client LinkLayerAddress.
      *
-     * @param[in]  aLinkLayerAddress The client LinkLayerAddress.
+     * @param[in]  aDuidLinkLayerAddress  The client LinkLayerAddress.
      *
      */
     void SetDuidLinkLayerAddress(const Mac::ExtAddress &aDuidLinkLayerAddress)
@@ -382,7 +382,7 @@
     /**
      * This method sets the server LinkLayerAddress.
      *
-     * @param[in]  aLinkLayerAddress The server LinkLayerAddress.
+     * @param[in]  aDuidLinkLayerAddress  The server LinkLayerAddress.
      *
      */
     void SetDuidLinkLayerAddress(const Mac::ExtAddress &aDuidLinkLayerAddress)
@@ -428,7 +428,7 @@
     /**
      * This method sets the client IAID.
      *
-     * @param[in]  aIaId  The client IAID.
+     * @param[in]  aIaid  The client IAID.
      *
      */
     void SetIaid(uint32_t aIaid) { mIaid = HostSwap32(aIaid); }
diff --git a/src/core/net/dhcp6_client.cpp b/src/core/net/dhcp6_client.cpp
index eed2a27..9c3e772 100644
--- a/src/core/net/dhcp6_client.cpp
+++ b/src/core/net/dhcp6_client.cpp
@@ -40,7 +40,7 @@
 #include "common/encoding.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "mac/mac.hpp"
 #include "net/dhcp6.hpp"
 #include "thread/thread_netif.hpp"
@@ -48,6 +48,8 @@
 namespace ot {
 namespace Dhcp6 {
 
+RegisterLogModule("Dhcp6Client");
+
 Client::Client(Instance &aInstance)
     : InstanceLocator(aInstance)
     , mSocket(aInstance)
@@ -146,7 +148,7 @@
             }
             else
             {
-                otLogWarnIp6("Insufficient memory for new DHCP prefix");
+                LogWarn("Insufficient memory for new DHCP prefix");
                 continue;
             }
         }
@@ -283,13 +285,13 @@
     messageInfo.mPeerPort = kDhcpServerPort;
 
     SuccessOrExit(error = mSocket.SendTo(*message, messageInfo));
-    otLogInfoIp6("solicit");
+    LogInfo("solicit");
 
 exit:
     if (error != kErrorNone)
     {
         FreeMessage(message);
-        otLogWarnIp6("Failed to send DHCPv6 Solicit: %s", ErrorToString(error));
+        LogWarn("Failed to send DHCPv6 Solicit: %s", ErrorToString(error));
     }
 }
 
diff --git a/src/core/net/dhcp6_server.cpp b/src/core/net/dhcp6_server.cpp
index 805144d..eaab1b4 100644
--- a/src/core/net/dhcp6_server.cpp
+++ b/src/core/net/dhcp6_server.cpp
@@ -35,18 +35,21 @@
 
 #if OPENTHREAD_CONFIG_DHCP6_SERVER_ENABLE
 
+#include "common/array.hpp"
 #include "common/as_core_type.hpp"
 #include "common/code_utils.hpp"
 #include "common/encoding.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "thread/mle.hpp"
 #include "thread/thread_netif.hpp"
 
 namespace ot {
 namespace Dhcp6 {
 
+RegisterLogModule("Dhcp6Server");
+
 Server::Server(Instance &aInstance)
     : InstanceLocator(aInstance)
     , mSocket(aInstance)
@@ -175,7 +178,7 @@
 
     if (error != kErrorNone)
     {
-        otLogNoteIp6("Failed to add DHCPv6 prefix agent: %s", ErrorToString(error));
+        LogNote("Failed to add DHCPv6 prefix agent: %s", ErrorToString(error));
     }
 }
 
@@ -317,7 +320,7 @@
     VerifyOrExit(option.GetLength() == sizeof(option) - sizeof(Option), error = kErrorParse);
 
     // mask matching prefix
-    for (uint16_t i = 0; i < OT_ARRAY_LENGTH(mPrefixAgents); i++)
+    for (uint16_t i = 0; i < GetArrayLength(mPrefixAgents); i++)
     {
         if (mPrefixAgents[i].IsValid() && mPrefixAgents[i].IsPrefixMatch(option.GetAddress()))
         {
@@ -397,7 +400,7 @@
 
     if (mPrefixAgentsMask)
     {
-        for (uint16_t i = 0; i < OT_ARRAY_LENGTH(mPrefixAgents); i++)
+        for (uint16_t i = 0; i < GetArrayLength(mPrefixAgents); i++)
         {
             if (mPrefixAgentsMask & (1 << i))
             {
@@ -437,7 +440,7 @@
     if (mPrefixAgentsMask)
     {
         // if specified, only apply specified prefixes
-        for (uint16_t i = 0; i < OT_ARRAY_LENGTH(mPrefixAgents); i++)
+        for (uint16_t i = 0; i < GetArrayLength(mPrefixAgents); i++)
         {
             if (mPrefixAgentsMask & (1 << i))
             {
diff --git a/src/core/net/dns_client.cpp b/src/core/net/dns_client.cpp
index 01ada88..bb33d70 100644
--- a/src/core/net/dns_client.cpp
+++ b/src/core/net/dns_client.cpp
@@ -30,12 +30,13 @@
 
 #if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE
 
+#include "common/array.hpp"
 #include "common/as_core_type.hpp"
 #include "common/code_utils.hpp"
 #include "common/debug.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "net/udp6.hpp"
 #include "thread/network_data_types.hpp"
 #include "thread/thread_netif.hpp"
@@ -48,6 +49,8 @@
 namespace ot {
 namespace Dns {
 
+RegisterLogModule("DnsClient");
+
 //---------------------------------------------------------------------------------------------------------------------
 // Client::QueryConfig
 
@@ -325,16 +328,17 @@
 
     if ((info.mQueryType == kIp4AddressQuery) || mIp6QueryResponseRequiresNat64)
     {
-        Section     section;
-        ARecord     aRecord;
-        Ip6::Prefix nat64Prefix;
+        Section                          section;
+        ARecord                          aRecord;
+        NetworkData::ExternalRouteConfig nat64Prefix;
 
-        SuccessOrExit(error = GetNat64Prefix(nat64Prefix));
+        VerifyOrExit(mInstance->Get<NetworkData::Leader>().GetPreferredNat64Prefix(nat64Prefix) == kErrorNone,
+                     error = kErrorInvalidState);
 
         section = (info.mQueryType == kIp4AddressQuery) ? kAnswerSection : kAdditionalDataSection;
         SuccessOrExit(error = FindARecord(section, name, aIndex, aRecord));
 
-        aAddress.SynthesizeFromIp4Address(nat64Prefix, aRecord.GetAddress());
+        aAddress.SynthesizeFromIp4Address(nat64Prefix.GetPrefix(), aRecord.GetAddress());
         aTtl = aRecord.GetTtl();
 
         ExitNow();
@@ -348,37 +352,6 @@
     return error;
 }
 
-#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE
-
-Error Client::AddressResponse::GetNat64Prefix(Ip6::Prefix &aPrefix) const
-{
-    Error                            error      = kErrorNotFound;
-    NetworkData::Iterator            iterator   = NetworkData::kIteratorInit;
-    signed int                       preference = NetworkData::kRoutePreferenceLow;
-    NetworkData::ExternalRouteConfig config;
-
-    aPrefix.Clear();
-
-    while (mInstance->Get<NetworkData::Leader>().GetNextExternalRoute(iterator, config) == kErrorNone)
-    {
-        if (!config.mNat64 || !config.GetPrefix().IsValidNat64())
-        {
-            continue;
-        }
-
-        if ((aPrefix.GetLength() == 0) || (config.mPreference > preference))
-        {
-            aPrefix    = config.GetPrefix();
-            preference = config.mPreference;
-            error      = kErrorNone;
-        }
-    }
-
-    return error;
-}
-
-#endif // OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE
-
 #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
 
 //---------------------------------------------------------------------------------------------------------------------
@@ -533,13 +506,13 @@
 #endif
 
 const uint8_t Client::kQuestionCount[] = {
-    /* kIp6AddressQuery -> */ OT_ARRAY_LENGTH(kIp6AddressQueryRecordTypes), // AAAA records
+    /* kIp6AddressQuery -> */ GetArrayLength(kIp6AddressQueryRecordTypes), // AAAA records
 #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE
-    /* kIp4AddressQuery -> */ OT_ARRAY_LENGTH(kIp4AddressQueryRecordTypes), // A records
+    /* kIp4AddressQuery -> */ GetArrayLength(kIp4AddressQueryRecordTypes), // A records
 #endif
 #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
-    /* kBrowseQuery  -> */ OT_ARRAY_LENGTH(kBrowseQueryRecordTypes),  // PTR records
-    /* kServiceQuery -> */ OT_ARRAY_LENGTH(kServiceQueryRecordTypes), // SRV and TXT records
+    /* kBrowseQuery  -> */ GetArrayLength(kBrowseQueryRecordTypes),  // PTR records
+    /* kServiceQuery -> */ GetArrayLength(kServiceQueryRecordTypes), // SRV and TXT records
 #endif
 };
 
@@ -648,6 +621,22 @@
     return StartQuery(info, aConfig, nullptr, aHostName, aContext);
 }
 
+#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE
+Error Client::ResolveIp4Address(const char *       aHostName,
+                                AddressCallback    aCallback,
+                                void *             aContext,
+                                const QueryConfig *aConfig)
+{
+    QueryInfo info;
+
+    info.Clear();
+    info.mQueryType                 = kIp4AddressQuery;
+    info.mCallback.mAddressCallback = aCallback;
+
+    return StartQuery(info, aConfig, nullptr, aHostName, aContext);
+}
+#endif
+
 #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
 
 Error Client::Browse(const char *aServiceName, BrowseCallback aCallback, void *aContext, const QueryConfig *aConfig)
@@ -715,6 +704,17 @@
 
     aInfo.mCallbackContext = aContext;
 
+#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE
+    if (aInfo.mQueryType == kIp4AddressQuery)
+    {
+        NetworkData::ExternalRouteConfig nat64Prefix;
+
+        VerifyOrExit(aInfo.mConfig.GetNat64Mode() == QueryConfig::kNat64Allow, error = kErrorInvalidArgs);
+        VerifyOrExit(Get<NetworkData::Leader>().GetPreferredNat64Prefix(nat64Prefix) == kErrorNone,
+                     error = kErrorInvalidState);
+    }
+#endif
+
     SuccessOrExit(error = AllocateQuery(aInfo, aLabel, aName, query));
     mQueries.Enqueue(*query);
 
@@ -905,20 +905,21 @@
 
 Client::Query *Client::FindQueryById(uint16_t aMessageId)
 {
-    Query *   query;
+    Query *   matchedQuery = nullptr;
     QueryInfo info;
 
-    for (query = mQueries.GetHead(); query != nullptr; query = query->GetNext())
+    for (Query &query : mQueries)
     {
-        info.ReadFrom(*query);
+        info.ReadFrom(query);
 
         if (info.mMessageId == aMessageId)
         {
+            matchedQuery = &query;
             break;
         }
     }
 
-    return query;
+    return matchedQuery;
 }
 
 void Client::HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMsgInfo)
@@ -975,12 +976,18 @@
 
     // Check the Question Section
 
-    VerifyOrExit(header.GetQuestionCount() == kQuestionCount[aType], error = kErrorParse);
-
-    for (uint8_t num = 0; num < kQuestionCount[aType]; num++)
+    if (header.GetQuestionCount() == kQuestionCount[aType])
     {
-        SuccessOrExit(error = Name::CompareName(message, offset, queryName));
-        offset += sizeof(Question);
+        for (uint8_t num = 0; num < kQuestionCount[aType]; num++)
+        {
+            SuccessOrExit(error = Name::CompareName(message, offset, queryName));
+            offset += sizeof(Question);
+        }
+    }
+    else
+    {
+        VerifyOrExit((header.GetResponseCode() != Header::kResponseSuccess) && (header.GetQuestionCount() == 0),
+                     error = kErrorParse);
     }
 
     // Check the answer, authority and additional record sections
@@ -1049,7 +1056,7 @@
 exit:
     if (error != kErrorNone)
     {
-        otLogInfoDns("Failed to parse response %s", ErrorToString(error));
+        LogInfo("Failed to parse response %s", ErrorToString(error));
     }
 
     return error;
@@ -1064,24 +1071,21 @@
 {
     TimeMilli now      = TimerMilli::GetNow();
     TimeMilli nextTime = now.GetDistantFuture();
-    Query *   nextQuery;
     QueryInfo info;
 
-    for (Query *query = mQueries.GetHead(); query != nullptr; query = nextQuery)
+    for (Query &query : mQueries)
     {
-        nextQuery = query->GetNext();
-
-        info.ReadFrom(*query);
+        info.ReadFrom(query);
 
         if (now >= info.mRetransmissionTime)
         {
             if (info.mTransmissionCount >= info.mConfig.GetMaxTxAttempts())
             {
-                FinalizeQuery(*query, kErrorResponseTimeout);
+                FinalizeQuery(query, kErrorResponseTimeout);
                 continue;
             }
 
-            SendQuery(*query, info, /* aUpdateTimer */ false);
+            SendQuery(query, info, /* aUpdateTimer */ false);
         }
 
         if (nextTime > info.mRetransmissionTime)
diff --git a/src/core/net/dns_client.hpp b/src/core/net/dns_client.hpp
index f7d927f..de9afca 100644
--- a/src/core/net/dns_client.hpp
+++ b/src/core/net/dns_client.hpp
@@ -331,9 +331,10 @@
          * @param[out] aAddress      A reference to an IPv6 address to output the address.
          * @param[out] aTtl          A reference to a `uint32_t` to output TTL for the address.
          *
-         * @retval kErrorNone       The address was read successfully.
-         * @retval kErrorNotFound   No address record at @p aIndex.
-         * @retval kErrorParse      Could not parse the records.
+         * @retval kErrorNone          The address was read successfully.
+         * @retval kErrorNotFound      No address record at @p aIndex.
+         * @retval kErrorParse         Could not parse the records.
+         * @retval kErrorInvalidState  No NAT64 prefix (applicable only when NAT64 is allowed).
          *
          */
         Error GetAddress(uint16_t aIndex, Ip6::Address &aAddress, uint32_t &aTtl) const;
@@ -390,7 +391,6 @@
          * Note that this method gets the service instance label and not the full service instance name which is of the
          * form `<Instance>.<Service>.<Domain>`.
          *
-         * @param[in]  aResponse          A pointer to a response.
          * @param[in]  aIndex             The service instance record index to retrieve.
          * @param[out] aLabelBuffer       A char array to output the service instance label (MUST NOT be NULL).
          * @param[in]  aLabelBufferSize   The size of @p aLabelBuffer.
@@ -609,6 +609,34 @@
                          void *             aContext,
                          const QueryConfig *aConfig = nullptr);
 
+#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE
+    /**
+     * This method sends an address resolution DNS query for A (IPv4) record for a given host name.
+     *
+     * When a successful response is received, the addresses are returned from @p aCallback as NAT64 IPv6 translated
+     * versions of the IPv4 addresses from the query response.
+     *
+     * The @p aConfig can be nullptr. In this case the default config (from `GetDefaultConfig()`) will be used as
+     * the config for this query. In a non-nullptr @p aConfig, some of the fields can be left unspecified (value zero).
+     * The unspecified fields are then replaced by the values from the default config.
+     *
+     * @param[in]  aHostName        The host name for which to query the address (MUST NOT be `nullptr`).
+     * @param[in]  aCallback        A callback function pointer to report the result of query.
+     * @param[in]  aContext         A pointer to arbitrary context information passed to @p aCallback.
+     * @param[in]  aConfig          The config to use for this query.
+     *
+     * @retval kErrorNone           Successfully sent DNS query.
+     * @retval kErrorNoBufs         Failed to allocate retransmission data.
+     * @retval kErrorInvalidArgs    The host name is not valid format or NAT64 is not enabled in config.
+     * @retval kErrorInvalidState   Cannot send query since Thread interface is not up, or there is no NAT64 prefix.
+     *
+     */
+    Error ResolveIp4Address(const char *       aHostName,
+                            AddressCallback    aCallback,
+                            void *             aContext,
+                            const QueryConfig *aConfig = nullptr);
+#endif
+
 #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
 
     /**
@@ -639,7 +667,6 @@
      * the config for this query. In a non-`nullptr` @p aConfig, some of the fields can be left unspecified (value
      * zero). The unspecified fields are then replaced by the values from the default config.
      *
-     * @param[in]  aServerSockAddr    The server socket address.
      * @param[in]  aInstanceLabel     The service instance label.
      * @param[in]  aServiceName       The service name (together with @p aInstanceLabel form full instance name).
      * @param[in]  aCallback          A function pointer that shall be called on response reception or time-out.
diff --git a/src/core/net/dns_dso.cpp b/src/core/net/dns_dso.cpp
index 06712a5..27222ba 100644
--- a/src/core/net/dns_dso.cpp
+++ b/src/core/net/dns_dso.cpp
@@ -30,12 +30,13 @@
 
 #if OPENTHREAD_CONFIG_DNS_DSO_ENABLE
 
+#include "common/array.hpp"
 #include "common/as_core_type.hpp"
 #include "common/code_utils.hpp"
 #include "common/debug.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "common/random.hpp"
 
 /**
@@ -46,6 +47,8 @@
 namespace ot {
 namespace Dns {
 
+RegisterLogModule("DnsDso");
+
 //---------------------------------------------------------------------------------------------------------------------
 // otPlatDso transport callbacks
 
@@ -110,8 +113,8 @@
 {
     VerifyOrExit(mState != aState);
 
-    otLogInfoDns("[dso] State: %s -> %s on connection with %s", StateToString(mState), StateToString(aState),
-                 mPeerSockAddr.ToString().AsCString());
+    LogInfo("State: %s -> %s on connection with %s", StateToString(mState), StateToString(aState),
+            mPeerSockAddr.ToString().AsCString());
 
     mState          = aState;
     mStateDidChange = true;
@@ -253,7 +256,7 @@
     mPendingRequests.Clear();
     SetState(kStateDisconnected);
 
-    otLogInfoDns("[dso] Disconnect reason: %s", DisconnectReasonToString(mDisconnectReason));
+    LogInfo("Disconnect reason: %s", DisconnectReasonToString(mDisconnectReason));
 }
 
 void Dso::Connection::MarkSessionEstablished(void)
@@ -296,7 +299,7 @@
 
     mLongLivedOperation = aLongLivedOperation;
 
-    otLogInfoDns("[dso] Long-lived operation %s", mLongLivedOperation ? "started" : "stopped");
+    LogInfo("Long-lived operation %s", mLongLivedOperation ? "started" : "stopped");
 
     if (!mLongLivedOperation)
     {
@@ -578,8 +581,8 @@
         }
     }
 
-    otLogInfoDns("[dso] Sending %s message with id %u to %s", MessageTypeToString(aMessageType), aMessageId,
-                 mPeerSockAddr.ToString().AsCString());
+    LogInfo("Sending %s message with id %u to %s", MessageTypeToString(aMessageType), aMessageId,
+            mPeerSockAddr.ToString().AsCString());
 
     switch (mState)
     {
@@ -640,7 +643,7 @@
     // that its padded length is a multiple of the chosen block
     // length.
 
-    blockLength = kBlockLengths[Random::NonCrypto::GetUint8InRange(0, OT_ARRAY_LENGTH(kBlockLengths))];
+    blockLength = kBlockLengths[Random::NonCrypto::GetUint8InRange(0, GetArrayLength(kBlockLengths))];
 
     paddingTlv.Init((blockLength - ((aMessage.GetLength() + sizeof(Tlv)) % blockLength)) % blockLength);
 
@@ -816,7 +819,7 @@
     default:
         if (aHeader.GetMessageId() == 0)
         {
-            otLogInfoDns("[dso] Received unidirectional message from %s", mPeerSockAddr.ToString().AsCString());
+            LogInfo("Received unidirectional message from %s", mPeerSockAddr.ToString().AsCString());
 
             error = mCallbacks.mProcessUnidirectionalMessage(*this, aMessage, aPrimaryTlvType);
         }
@@ -824,8 +827,7 @@
         {
             MessageId messageId = aHeader.GetMessageId();
 
-            otLogInfoDns("[dso] Received request message with id %u from %s", messageId,
-                         mPeerSockAddr.ToString().AsCString());
+            LogInfo("Received request message with id %u from %s", messageId, mPeerSockAddr.ToString().AsCString());
 
             error = mCallbacks.mProcessRequestMessage(*this, messageId, aMessage, aPrimaryTlvType);
 
@@ -955,8 +957,7 @@
 
             VerifyOrExit(aHeader.GetMessageId() != 0);
 
-            otLogInfoDns("[dso] Received KeepAlive request message from client %s",
-                         mPeerSockAddr.ToString().AsCString());
+            LogInfo("Received KeepAlive request message from client %s", mPeerSockAddr.ToString().AsCString());
 
             IgnoreError(SendKeepAliveMessage(kResponseMessage, aHeader.GetMessageId()));
             error = kErrorNone;
@@ -970,8 +971,8 @@
         VerifyOrExit(aHeader.GetMessageId() == 0);
     }
 
-    otLogInfoDns("[dso] Received Keep Alive %s message from server %s",
-                 (aHeader.GetMessageId() == 0) ? "unidirectional" : "response", mPeerSockAddr.ToString().AsCString());
+    LogInfo("Received Keep Alive %s message from server %s",
+            (aHeader.GetMessageId() == 0) ? "unidirectional" : "response", mPeerSockAddr.ToString().AsCString());
 
     // Receiving a Keep Alive interval value from server less than the
     // minimum (ten seconds) is a fatal error and client MUST then
@@ -989,7 +990,7 @@
     AdjustInactivityTimeout(keepAliveTlv.GetInactivityTimeout());
     mKeepAlive.SetInterval(keepAliveTlv.GetKeepAliveInterval());
 
-    otLogInfoDns("[dso] Timeouts Inactivity:%u, KeepAlive:%u", mInactivity.GetInterval(), mKeepAlive.GetInterval());
+    LogInfo("Timeouts Inactivity:%u, KeepAlive:%u", mInactivity.GetInterval(), mKeepAlive.GetInterval());
 
     error = kErrorNone;
 
@@ -1017,8 +1018,8 @@
     mRetryDelayErrorCode = aHeader.GetResponseCode();
     mRetryDelay          = retryDelayTlv.GetRetryDelay();
 
-    otLogInfoDns("[dso] Received Retry Delay message from server %s", mPeerSockAddr.ToString().AsCString());
-    otLogInfoDns("[dso]    RetryDelay:%u ms, ResponseCode:%d", mRetryDelay, mRetryDelayErrorCode);
+    LogInfo("Received Retry Delay message from server %s", mPeerSockAddr.ToString().AsCString());
+    LogInfo("   RetryDelay:%u ms, ResponseCode:%d", mRetryDelay, mRetryDelayErrorCode);
 
     Disconnect(kGracefullyClose, kReasonServerRetryDelayRequest);
 
diff --git a/src/core/net/dns_dso.hpp b/src/core/net/dns_dso.hpp
index a8346c7..d9ff458 100644
--- a/src/core/net/dns_dso.hpp
+++ b/src/core/net/dns_dso.hpp
@@ -642,7 +642,7 @@
          * `kMinKeepAliveInterval`, otherwise `kErrorInvalidArgs` is returned.
          *
          * @param[in] aInactivityTimeout  The Inactivity timeout (in msec).
-         * @param[in] aKeepAliveTimeout   The Keep Alive timeout (in msec).
+         * @param[in] aKeepAliveInterval  The Keep Alive interval (in msec).
          *
          * @retval kErrorNone          Successfully set the timeouts and sent a Keep Alive message.
          * @retval kErrorInvalidArgs   The given timeouts are not valid.
diff --git a/src/core/net/dns_types.cpp b/src/core/net/dns_types.cpp
index 3862061..e65cd03 100644
--- a/src/core/net/dns_types.cpp
+++ b/src/core/net/dns_types.cpp
@@ -36,7 +36,6 @@
 #include "common/code_utils.hpp"
 #include "common/debug.hpp"
 #include "common/instance.hpp"
-#include "common/logging.hpp"
 #include "common/random.hpp"
 #include "common/string.hpp"
 
diff --git a/src/core/net/dns_types.hpp b/src/core/net/dns_types.hpp
index 33b3f9d..db447aa 100644
--- a/src/core/net/dns_types.hpp
+++ b/src/core/net/dns_types.hpp
@@ -805,9 +805,9 @@
     /**
      * This static method parses and skips over a full name in a message.
      *
-     * @param[in]    aMessage         The message to parse the name from. `aMessage.GetOffset()` MUST point to
+     * @param[in]     aMessage        The message to parse the name from. `aMessage.GetOffset()` MUST point to
      *                                the start of DNS header (this is used to handle compressed names).
-     * @param[inout] aOffset          On input the offset in @p aMessage pointing to the start of the name field.
+     * @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.
      *
@@ -829,13 +829,13 @@
      * Unlike `ReadName()` which requires and verifies that the read label to contain no dot '.' character, this method
      * allows the read label to include any character.
      *
-     * @param[in]    aMessage         The message to read the label from. `aMessage.GetOffset()` MUST point to
+     * @param[in]      aMessage       The message to read the label from. `aMessage.GetOffset()` MUST point to
      *                                the start of DNS header (this is used to handle compressed names).
-     * @param[inout] aOffset          On input, the offset in @p aMessage pointing to the start of the label to read.
+     * @param[in,out]  aOffset        On input, the offset in @p aMessage pointing to the start of the label to read.
      *                                On exit, when successfully read, @p aOffset is updated to point to the start of
      *                                the next label.
-     * @param[out]   aLabelBuffer     A pointer to a char array to output the read label as a null-terminated C string.
-     * @param[inout] aLabelLength     On input, the maximum number chars in @p aLabelBuffer array.
+     * @param[out]     aLabelBuffer   A pointer to a char array to output the read label as a null-terminated C string.
+     * @param[in,out]  aLabelLength   On input, the maximum number chars in @p aLabelBuffer array.
      *                                On output, when label is successfully read, @p aLabelLength is updated to return
      *                                the label's length (number of chars in the label string, excluding the null char).
      *
@@ -856,13 +856,13 @@
      * This method verifies that the read labels in message do not contain any dot character, otherwise it returns
      * `kErrorParse`).
      *
-     * @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[inout] 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      A pointer to a char array to output the read name as a null-terminated C string.
-     * @param[inout] aNameBufferSize  The maximum number of chars in @p aNameBuffer 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      A pointer to a char array to output the read name as a null-terminated C string.
+     * @param[in,out] aNameBufferSize  The maximum number of chars in @p aNameBuffer array.
      *
      * @retval kErrorNone         Successfully read the name, @p aNameBuffer and @p Offset are updated.
      * @retval kErrorParse        Name could not be parsed (invalid format).
@@ -880,12 +880,12 @@
      * Unlike `CompareName()` which requires the labels in the the name string to contain no dot '.' character, this
      * method allows @p aLabel to include any character.
      *
-     * @param[in]    aMessage         The message to read the label from to compare. `aMessage.GetOffset()` MUST point
+     * @param[in]     aMessage        The message to read the label from to compare. `aMessage.GetOffset()` MUST point
      *                                to the start of DNS header (this is used to handle compressed names).
-     * @param[inout] aOffset          On input, the offset in @p aMessage pointing to the start of the label to read.
+     * @param[in,out] aOffset         On input, the offset in @p aMessage pointing to the start of the label to read.
      *                                On exit and only when label is successfully read and does match @p aLabel,
      *                                @p aOffset is updated to point to the start of the next label.
-     * @param[in]    aLabel           A pointer to a null terminated string containing the label to compare with.
+     * @param[in]     aLabel          A pointer to a null terminated string containing the label to compare with.
      *
      * @retval kErrorNone          The label from @p aMessage matches @p aLabel. @p aOffset is updated.
      * @retval kErrorNotFound      The label from @p aMessage does not match @p aLabel (note that @p aOffset is not
@@ -906,14 +906,14 @@
      * The @p aName must follow  "<label1>.<label2>.<label3>", i.e., a sequence of labels separated by dot '.' char.
      * E.g., "example.com", "example.com." (same as previous one), "local.", "default.service.arpa", "." or "" (root).
      *
-     * @param[in]    aMessage         The message to read the name from and compare with @p aName.
+     * @param[in]     aMessage        The message to read the name from and compare with @p aName.
      *                                `aMessage.GetOffset()` MUST point to the start of DNS header (this is used to
      *                                handle compressed names).
-     * @param[inout] aOffset          On input, the offset in @p aMessage pointing to the start of the name field.
+     * @param[in,out] aOffset         On input, the offset in @p aMessage pointing to the start of the name field.
      *                                On exit (when parsed successfully independent of whether the read name matches
      *                                @p aName or not), @p aOffset is updated to point to the byte after the end of
      *                                the name field.
-     * @param[in]    aName            A pointer to a null terminated string containing the name to compare with.
+     * @param[in]     aName           A pointer to a null terminated string containing the name to compare with.
      *
      * @retval kErrorNone          The name from @p aMessage matches @p aName. @p aOffset is updated.
      * @retval kErrorNotFound      The name from @p aMessage does not match @p aName. @p aOffset is updated.
@@ -938,15 +938,15 @@
      * If the name in @p aMessage can be parsed fully (independent of whether the name matches or not with the name
      * from @p aMessage2), the @p aOffset is updated (note that @p aOffset2 for @p aMessage2 is not changed).
      *
-     * @param[in]    aMessage         The message to read the name from and compare. `aMessage.GetOffset()` MUST point
+     * @param[in]     aMessage        The message to read the name from and compare. `aMessage.GetOffset()` MUST point
      *                                to the start of DNS header (this is used to handle compressed names).
-     * @param[inout] aOffset          On input, the offset in @p aMessage pointing to the start of the name field.
+     * @param[in,out] aOffset         On input, the offset in @p aMessage pointing to the start of the name field.
      *                                On exit (when parsed successfully independent of whether the read name matches
      *                                or not), @p aOffset is updated to point to the byte after the end of the name
      *                                field.
-     * @param[in]    aMessage2        The second message to read the name from and compare with name from @p aMessage.
+     * @param[in]     aMessage2       The second message to read the name from and compare with name from @p aMessage.
      *                                `aMessage2.GetOffset()` MUST point to the start of DNS header.
-     * @param[in]    aOffset2         The offset in @p aMessage2 pointing to the start of the name field.
+     * @param[in]     aOffset2        The offset in @p aMessage2 pointing to the start of the name field.
      *
      * @retval kErrorNone       The name from @p aMessage matches the name from @p aMessage2. @p aOffset is updated.
      * @retval kErrorNotFound   The name from @p aMessage does not match the name from @p aMessage2. @p aOffset is
@@ -962,13 +962,13 @@
      *
      * If @p aName is empty (not specified), then any name in @p aMessage is considered a match to it.
      *
-     * @param[in]    aMessage         The message to read the name from and compare. `aMessage.GetOffset()` MUST point
+     * @param[in]     aMessage        The message to read the name from and compare. `aMessage.GetOffset()` MUST point
      *                                to the start of DNS header (this is used to handle compressed names).
-     * @param[inout] aOffset          On input, the offset in @p aMessage pointing to the start of the name field.
+     * @param[in,out] aOffset         On input, the offset in @p aMessage pointing to the start of the name field.
      *                                On exit (when parsed successfully independent of whether the read name matches
      *                                or not), @p aOffset is updated to point to the byte after the end of the name
      *                                field.
-     * @param[in]    aName            A reference to a name to compare with.
+     * @param[in]     aName           A reference to a name to compare with.
      *
      * @retval kErrorNone          The name from @p aMessage matches @p aName. @p aOffset is updated.
      * @retval kErrorNotFound      The name from @p aMessage does not match @p aName. @p aOffset is updated.
@@ -1137,7 +1137,17 @@
      * @param[in] aValueLength   Number of bytes in @p aValue buffer.
      *
      */
-    TxtEntry(const char *aKey, const uint8_t *aValue, uint8_t aValueLength)
+    TxtEntry(const char *aKey, const uint8_t *aValue, uint8_t aValueLength) { Init(aKey, aValue, aValueLength); }
+
+    /**
+     * This method initializes a `TxtEntry` object.
+     *
+     * @param[in] aKey           A pointer to the key string.
+     * @param[in] aValue         A pointer to a buffer containing the value.
+     * @param[in] aValueLength   Number of bytes in @p aValue buffer.
+     *
+     */
+    void Init(const char *aKey, const uint8_t *aValue, uint8_t aValueLength)
     {
         mKey         = aKey;
         mValue       = aValue;
@@ -1326,12 +1336,12 @@
     /**
      * This static method parses and skips over a given number of resource records in a message from a given offset.
      *
-     * @param[in]    aMessage     The message from which to parse/read the resource records. `aMessage.GetOffset()`
-     *                            MUST point to the start of DNS header.
-     * @param[inout] aOffset      On input the offset in @p aMessage pointing to the start of the first record.
-     *                            On exit (when parsed successfully), @p aOffset is updated to point to the byte after
-     *                            the last parsed record.
-     * @param[in]    aNumRecords  Number of resource records to parse.
+     * @param[in]     aMessage     The message from which to parse/read the resource records. `aMessage.GetOffset()`
+     *                             MUST point to the start of DNS header.
+     * @param[in,out] aOffset      On input the offset in @p aMessage pointing to the start of the first record.
+     *                             On exit (when parsed successfully), @p aOffset is updated to point to the byte after
+     *                             the last parsed record.
+     * @param[in]     aNumRecords  Number of resource records to parse.
      *
      * @retval kErrorNone      Parsed records successfully. @p aOffset is updated.
      * @retval kErrorParse     Could not parse the records from @p aMessage (e.g., ran out of bytes in @p aMessage).
@@ -1342,17 +1352,17 @@
     /**
      * This static method searches in a given message to find the first resource record matching a given record name.
      *
-     * @param[in]    aMessage        The message in which to search for a matching resource record.
+     * @param[in]     aMessage       The message in which to search for a matching resource record.
      *                               `aMessage.GetOffset()` MUST point to the start of DNS header.
-     * @param[inout] aOffset         On input, the offset in @p aMessage pointing to the start of the first record.
+     * @param[in,out] aOffset        On input, the offset in @p aMessage pointing to the start of the first record.
      *                               On exit, if a matching record is found, @p aOffset is updated to point to the byte
      *                               after the record name.
      *                               If a matching record could not be found, @p aOffset is updated to point to the byte
      *                               after the last record that was checked.
-     * @param[inout] aNumRecords     On input, the maximum number of records to check (starting from @p aOffset).
+     * @param[in,out] aNumRecords    On input, the maximum number of records to check (starting from @p aOffset).
      *                               On exit and if a matching record is found, @p aNumRecords is updated to give the
      *                               number of remaining records after @p aOffset (excluding the matching record).
-     * @param[in]    aName           The record name to match against.
+     * @param[in]     aName          The record name to match against.
      *
      * @retval kErrorNone         A matching record was found. @p aOffset, @p aNumRecords are updated.
      * @retval kErrorNotFound     A matching record could not be found. @p aOffset and @p aNumRecords are updated.
@@ -1372,19 +1382,19 @@
      * to after the last byte read from the message and copied into @p aRecord. This allows the caller to read any
      * remaining fields in the record data.
      *
-     * @tparam       RecordType      The resource record type (i.e., a sub-class of `ResourceRecord`).
+     * @tparam        RecordType     The resource record type (i.e., a sub-class of `ResourceRecord`).
      *
-     * @param[in]    aMessage        The message to search within for matching resource records.
+     * @param[in]     aMessage       The message to search within for matching resource records.
      *                               `aMessage.GetOffset()` MUST point to the start of DNS header.
-     * @param[inout] aOffset         On input, the offset in @p aMessage pointing to the start of the first record.
+     * @param[in,out] aOffset        On input, the offset in @p aMessage pointing to the start of the first record.
      *                               On exit and only if a matching record is found, @p aOffset is updated to point to
      *                               the last read byte in the record (allowing caller to read any remaining fields in
      *                               the record data from the message).
-     * @param[in]    aNumRecords     The maximum number of records to check (starting from @p aOffset).
-     * @param[in]    aIndex          The matching record index to find. @p aIndex value of zero returns the first
+     * @param[in]     aNumRecords    The maximum number of records to check (starting from @p aOffset).
+     * @param[in]     aIndex         The matching record index to find. @p aIndex value of zero returns the first
      *                               matching record.
-     * @param[in]    aName           The record name to match against.
-     * @param[in]    aRecord         A reference to a record object to read a matching record into.
+     * @param[in]     aName          The record name to match against.
+     * @param[in]     aRecord        A reference to a record object to read a matching record into.
      *                               If a matching record is found, `sizeof(RecordType)` bytes from @p aMessage are
      *                               read and copied into @p aRecord.
      *
@@ -1422,15 +1432,15 @@
      * example, when reading a SRV record using `SrvRecord` type, @p aOffset would point to after the last field in
      * `SrvRecord`  which is the start of "target host domain name" field.
      *
-     * @tparam       RecordType      The resource record type (i.e., a sub-class of `ResourceRecord`).
+     * @tparam        RecordType     The resource record type (i.e., a sub-class of `ResourceRecord`).
      *
-     * @param[in]    aMessage        The message from which to read the record.
-     * @param[inout] aOffset         On input, the offset in @p aMessage pointing to the byte after the record name.
+     * @param[in]     aMessage       The message from which to read the record.
+     * @param[in,out] aOffset        On input, the offset in @p aMessage pointing to the byte after the record name.
      *                               On exit, if a matching record is read, @p aOffset is updated to point to the last
      *                               read byte in the record.
      *                               If a matching record could not be read, @p aOffset is updated to point to the byte
      *                               after the entire record (skipping over the record).
-     * @param[out]   aRecord         A reference to a record to read a matching record into.
+     * @param[out]    aRecord        A reference to a record to read a matching record into.
      *                               If a matching record is found, `sizeof(RecordType)` bytes from @p aMessage are
      *                               read and copied into @p aRecord.
      *
@@ -1549,14 +1559,14 @@
      * This method also verifies that the CNAME record is well-formed (e.g., the record data length `GetLength()`
      * matches the CNAME encoded name).
      *
-     * @param[in]     aMessage          The message to read from. `aMessage.GetOffset()` MUST point to the start of
+     * @param[in]      aMessage         The message to read from. `aMessage.GetOffset()` MUST point to the start of
      *                                  DNS header.
-     * @param[inout]  aOffset           On input, the offset in @p aMessage to start of CNAME name field.
+     * @param[in,out]  aOffset          On input, the offset in @p aMessage to start of CNAME 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]    aNameBuffer       A pointer to a char array to output the read name as a null-terminated C string
+     * @param[out]     aNameBuffer      A pointer to a char array to output the read name as a null-terminated C string
      *                                  (MUST NOT be `nullptr`).
-     * @param[in]     aNameBufferSize   The size of @p aNameBuffer.
+     * @param[in]      aNameBufferSize  The size of @p aNameBuffer.
      *
      * @retval kErrorNone           The CNAME name was read successfully. @p aOffset and @p aNameBuffer are updated.
      * @retval kErrorParse          The CNAME record in @p aMessage could not be parsed (invalid format).
@@ -1600,14 +1610,14 @@
      * This method also verifies that the PTR record is well-formed (e.g., the record data length `GetLength()` matches
      * the PTR encoded name).
      *
-     * @param[in]     aMessage          The message to read from.  `aMessage.GetOffset()` MUST point to the start of
+     * @param[in]      aMessage         The message to read from.  `aMessage.GetOffset()` MUST point to the start of
      *                                  DNS header.
-     * @param[inout]  aOffset           On input, the offset in @p aMessage to start of PTR name field.
+     * @param[in,out]  aOffset          On input, the offset in @p aMessage to 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]    aNameBuffer       A pointer to a char array to output the read name as a null-terminated C string
+     * @param[out]     aNameBuffer      A pointer to a char array to output the read name as a null-terminated C string
      *                                  (MUST NOT be `nullptr`).
-     * @param[in]     aNameBufferSize   The size of @p aNameBuffer.
+     * @param[in]      aNameBufferSize  The size of @p aNameBuffer.
      *
      * @retval kErrorNone           The PTR name was read successfully. @p aOffset and @p aNameBuffer are updated.
      * @retval kErrorParse          The PTR record in @p aMessage could not be parsed (invalid format).
@@ -1633,17 +1643,17 @@
      * intended for "Service Instance Name" where first label (`<Instance>` portion) can be a user-friendly string and
      * can contain dot character.
      *
-     * @param[in]     aMessage          The message to read from. `aMessage.GetOffset()` MUST point to the start of
-     *                                  DNS header.
-     * @param[inout]  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 pointer to a char array to output the first label as a null-terminated C
-     *                                  string (MUST NOT be `nullptr`).
-     * @param[in]     aLabelBufferSize  The size of @p aLabelBuffer.
-     * @param[out]    aNameBuffer       A pointer to a char array to output the rest of name (after first label). Can
-     *                                  be `nullptr` if caller is only interested in the first label.
-     * @param[in]     aNameBufferSize   The size of @p aNameBuffer.
+     * @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 pointer to a char array to output the first label as a null-terminated C
+     *                                   string (MUST NOT be `nullptr`).
+     * @param[in]      aLabelBufferSize  The size of @p aLabelBuffer.
+     * @param[out]     aNameBuffer       A pointer to a char array to output the rest of name (after first label). Can
+     *                                   be `nullptr` if caller is only interested in the first label.
+     * @param[in]      aNameBufferSize   The size of @p aNameBuffer.
      *
      * @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).
@@ -1684,12 +1694,12 @@
      *
      * This method also checks if the TXT data is well-formed by calling `VerifyTxtData()`.
      *
-     * @param[in]     aMessage          The message to read from.
-     * @param[inout]  aOffset           On input, the offset in @p aMessage to start of TXT record data.
+     * @param[in]      aMessage         The message to read from.
+     * @param[in,out]  aOffset          On input, the offset in @p aMessage to start of TXT record data.
      *                                  On exit when successfully read, @p aOffset is updated to point to the byte
      *                                  after the entire TXT record (skipping over the record).
-     * @param[out]    aTxtBuffer        A pointer to a byte array to output the read TXT data.
-     * @param[inout]  aTxtBufferSize    On input, the size of @p aTxtBuffer (max bytes that can be read).
+     * @param[out]     aTxtBuffer       A pointer to a byte array to output the read TXT data.
+     * @param[in,out]  aTxtBufferSize   On input, the size of @p aTxtBuffer (max bytes that can be read).
      *                                  On exit, @p aTxtBufferSize gives number of bytes written to @p aTxtBuffer.
      *
      * @retval kErrorNone           The TXT data was read successfully. @p aOffset, @p aTxtBuffer and @p aTxtBufferSize
@@ -1838,14 +1848,14 @@
      * This method also verifies that the SRV record is well-formed (e.g., the record data length `GetLength()` matches
      * the SRV encoded name).
      *
-     * @param[in]     aMessage          The message to read from. `aMessage.GetOffset()` MUST point to the start of
+     * @param[in]      aMessage         The message to read from. `aMessage.GetOffset()` MUST point to the start of
      *                                  DNS header.
-     * @param[inout]  aOffset           On input, the offset in @p aMessage to start of target host name field.
+     * @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 pointer to a char array to output the read name as a null-terminated C string
+     * @param[out]     aNameBuffer      A pointer to a char array to output the read name as a null-terminated C string
      *                                  (MUST NOT be `nullptr`).
-     * @param[in]     aNameBufferSize   The size of @p aNameBuffer.
+     * @param[in]      aNameBufferSize  The size of @p aNameBuffer.
      *
      * @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).
@@ -2241,14 +2251,14 @@
     /**
      * This method parses and reads the SIG signer name from a message.
      *
-     * @param[in]     aMessage          The message to read from. `aMessage.GetOffset()` MUST point to the start of DNS
+     * @param[in]      aMessage         The message to read from. `aMessage.GetOffset()` MUST point to the start of DNS
      *                                  header.
-     * @param[inout]  aOffset           On input, the offset in @p aMessage to start of signer name field.
+     * @param[in,out]  aOffset          On input, the offset in @p aMessage to start of signer name field.
      *                                  On exit when successfully read, @p aOffset is updated to point to the byte
      *                                  after the name field (i.e., start of signature field).
-     * @param[out]    aNameBuffer       A pointer to a char array to output the read name as a null-terminated C string
+     * @param[out]     aNameBuffer      A pointer to a char array to output the read name as a null-terminated C string
      *                                  (MUST NOT be `nullptr`).
-     * @param[in]     aNameBufferSize   The size of @p aNameBuffer.
+     * @param[in]      aNameBufferSize  The size of @p aNameBuffer.
      *
      * @retval kErrorNone           The name was read successfully. @p aOffset and @p aNameBuffer are updated.
      * @retval kErrorParse          The SIG record in @p aMessage could not be parsed (invalid format).
diff --git a/src/core/net/dnssd_server.cpp b/src/core/net/dnssd_server.cpp
index 28240c5..40249ce 100644
--- a/src/core/net/dnssd_server.cpp
+++ b/src/core/net/dnssd_server.cpp
@@ -35,12 +35,13 @@
 
 #if OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE
 
+#include "common/array.hpp"
 #include "common/as_core_type.hpp"
 #include "common/code_utils.hpp"
 #include "common/debug.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "common/string.hpp"
 #include "net/srp_server.hpp"
 #include "net/udp6.hpp"
@@ -49,6 +50,8 @@
 namespace Dns {
 namespace ServiceDiscovery {
 
+RegisterLogModule("DnssdServer");
+
 const char Server::kDnssdProtocolUdp[]  = "_udp";
 const char Server::kDnssdProtocolTcp[]  = "_tcp";
 const char Server::kDnssdSubTypeLabel[] = "._sub.";
@@ -78,7 +81,7 @@
 #endif
 
 exit:
-    otLogInfoDns("[server] started: %s", ErrorToString(error));
+    LogInfo("started: %s", ErrorToString(error));
 
     if (error != kErrorNone)
     {
@@ -102,7 +105,7 @@
     mTimer.Stop();
 
     IgnoreError(mSocket.Close());
-    otLogInfoDns("[server] stopped");
+    LogInfo("stopped");
 
 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
     Get<Srp::Server>().HandleDnssdServerStateChange();
@@ -195,7 +198,7 @@
 
     if (aResponseCode == Header::kResponseServerFailure)
     {
-        otLogWarnDns("[server] failed to handle DNS query due to server failure");
+        LogWarn("failed to handle DNS query due to server failure");
         aHeader.SetQuestionCount(0);
         aHeader.SetAnswerCount(0);
         aHeader.SetAdditionalRecordCount(0);
@@ -211,11 +214,11 @@
 
     if (error != kErrorNone)
     {
-        otLogWarnDns("[server] failed to send DNS-SD reply: %s", ErrorToString(error));
+        LogWarn("failed to send DNS-SD reply: %s", ErrorToString(error));
     }
     else
     {
-        otLogInfoDns("[server] send DNS-SD reply: %s, RCODE=%d", ErrorToString(error), aResponseCode);
+        LogInfo("send DNS-SD reply: %s, RCODE=%d", ErrorToString(error), aResponseCode);
     }
 }
 
@@ -662,8 +665,8 @@
         response = ResolveQuestionBySrp(name, question, aResponseHeader, aResponseMessage, aCompressInfo,
                                         /* aAdditional */ false);
 
-        otLogInfoDns("[server] ANSWER: TRANSACTION=0x%04x, QUESTION=[%s %d %d], RCODE=%d",
-                     aResponseHeader.GetMessageId(), name, question.GetClass(), question.GetType(), response);
+        LogInfo("ANSWER: TRANSACTION=0x%04x, QUESTION=[%s %d %d], RCODE=%d", aResponseHeader.GetMessageId(), name,
+                question.GetClass(), question.GetType(), response);
     }
 
     // Answer the questions with additional RRs if required
@@ -681,8 +684,8 @@
                                                                                 /* aAdditional */ true),
                          response = Header::kResponseServerFailure);
 
-            otLogInfoDns("[server] ADDITIONAL: TRANSACTION=0x%04x, QUESTION=[%s %d %d], RCODE=%d",
-                         aResponseHeader.GetMessageId(), name, question.GetClass(), question.GetType(), response);
+            LogInfo("ADDITIONAL: TRANSACTION=0x%04x, QUESTION=[%s %d %d], RCODE=%d", aResponseHeader.GetMessageId(),
+                    name, question.GetClass(), question.GetType(), response);
         }
     }
 exit:
@@ -1022,7 +1025,7 @@
         cur = query + 1;
     }
 
-    for (; cur < OT_ARRAY_END(mQueryTransactions); cur++)
+    for (; cur < GetArrayEnd(mQueryTransactions); cur++)
     {
         if (cur->IsValid())
         {
diff --git a/src/core/net/dnssd_server.hpp b/src/core/net/dnssd_server.hpp
index b43e281..2bab3b0 100644
--- a/src/core/net/dnssd_server.hpp
+++ b/src/core/net/dnssd_server.hpp
@@ -147,8 +147,8 @@
     /**
      * This method acquires the DNS-SD query type and name for a specific query.
      *
-     * @param[in]   aQuery            The query pointer.
-     * @param[out]  aNameOutput       The name output buffer.
+     * @param[in]   aQuery      The query pointer.
+     * @param[out]  aName       The name output buffer.
      *
      * @returns The DNS-SD query type.
      *
diff --git a/src/core/net/icmp6.cpp b/src/core/net/icmp6.cpp
index f417374..fee412d 100644
--- a/src/core/net/icmp6.cpp
+++ b/src/core/net/icmp6.cpp
@@ -37,7 +37,7 @@
 #include "common/debug.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "common/message.hpp"
 #include "net/checksum.hpp"
 #include "net/ip6.hpp"
@@ -45,6 +45,8 @@
 namespace ot {
 namespace Ip6 {
 
+RegisterLogModule("Icmp6");
+
 Icmp::Icmp(Instance &aInstance)
     : InstanceLocator(aInstance)
     , mEchoSequence(1)
@@ -79,7 +81,7 @@
     aMessage.SetOffset(0);
     SuccessOrExit(error = Get<Ip6>().SendDatagram(aMessage, messageInfoLocal, kProtoIcmp6));
 
-    otLogInfoIcmp("Sent echo request: (seq = %d)", icmpHeader.GetSequence());
+    LogInfo("Sent echo request: (seq = %d)", icmpHeader.GetSequence());
 
 exit:
     return error;
@@ -116,7 +118,7 @@
 
     SuccessOrExit(error = Get<Ip6>().SendDatagram(*message, messageInfoLocal, kProtoIcmp6));
 
-    otLogInfoIcmp("Sent ICMPv6 Error");
+    LogInfo("Sent ICMPv6 Error");
 
 exit:
     FreeMessageOnError(message, error);
@@ -182,14 +184,14 @@
     // always handle Echo Request destined for RLOC or ALOC
     VerifyOrExit(ShouldHandleEchoRequest(aMessageInfo) || aMessageInfo.GetSockAddr().GetIid().IsLocator());
 
-    otLogInfoIcmp("Received Echo Request");
+    LogInfo("Received Echo Request");
 
     icmp6Header.Clear();
     icmp6Header.SetType(Header::kTypeEchoReply);
 
     if ((replyMessage = Get<Ip6>().NewMessage(0)) == nullptr)
     {
-        otLogDebgIcmp("Failed to allocate a new message");
+        LogDebg("Failed to allocate a new message");
         ExitNow();
     }
 
@@ -210,7 +212,7 @@
     SuccessOrExit(error = Get<Ip6>().SendDatagram(*replyMessage, replyMessageInfo, kProtoIcmp6));
 
     IgnoreError(replyMessage->Read(replyMessage->GetOffset(), icmp6Header));
-    otLogInfoIcmp("Sent Echo Reply (seq = %d)", icmp6Header.GetSequence());
+    LogInfo("Sent Echo Reply (seq = %d)", icmp6Header.GetSequence());
 
 exit:
     FreeMessageOnError(replyMessage, error);
diff --git a/src/core/net/icmp6.hpp b/src/core/net/icmp6.hpp
index 08d592d..ba73d60 100644
--- a/src/core/net/icmp6.hpp
+++ b/src/core/net/icmp6.hpp
@@ -307,9 +307,9 @@
     otIcmp6EchoMode GetEchoMode(void) const { return mEchoMode; }
 
     /**
-     * This method sets whether or not ICMPv6 Echo processing is enabled.
+     * Sets the ICMPv6 echo mode.
      *
-     * @param[in]  aEnabled  TRUE to enable ICMPv6 Echo processing, FALSE otherwise.
+     * @param[in]  aMode  The ICMPv6 echo mode.
      *
      */
     void SetEchoMode(otIcmp6EchoMode aMode) { mEchoMode = aMode; }
diff --git a/src/core/net/ip6.cpp b/src/core/net/ip6.cpp
index 1678830..3f5ec0d 100644
--- a/src/core/net/ip6.cpp
+++ b/src/core/net/ip6.cpp
@@ -40,7 +40,7 @@
 #include "common/debug.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "common/message.hpp"
 #include "common/random.hpp"
 #include "net/checksum.hpp"
@@ -62,6 +62,8 @@
 namespace ot {
 namespace Ip6 {
 
+RegisterLogModule("Ip6");
+
 Ip6::Ip6(Instance &aInstance)
     : InstanceLocator(aInstance)
     , mForwardingEnabled(false)
@@ -227,7 +229,7 @@
     // Use IP-in-IP encapsulation (RFC2473) and ALL_MPL_FORWARDERS address.
     messageInfo.GetPeerAddr().SetToRealmLocalAllMplForwarders();
 
-    tunnelHeader.Init();
+    tunnelHeader.InitVersionTrafficClassFlow();
     tunnelHeader.SetHopLimit(static_cast<uint8_t>(kDefaultHopLimit));
     tunnelHeader.SetPayloadLength(aHeader.GetPayloadLength() + sizeof(tunnelHeader));
     tunnelHeader.SetDestination(messageInfo.GetPeerAddr());
@@ -262,7 +264,7 @@
             OptionMpl      mplOption;
 
             // read existing hop-by-hop option header
-            IgnoreError(aMessage.Read(0, hbh));
+            SuccessOrExit(error = aMessage.Read(0, hbh));
             hbhLength = (hbh.GetLength() + 1) * 8;
 
             VerifyOrExit(hbhLength <= aHeader.GetPayloadLength(), error = kErrorParse);
@@ -307,12 +309,12 @@
 
             if ((messageCopy = aMessage.Clone()) != nullptr)
             {
-                IgnoreError(HandleDatagram(*messageCopy, nullptr, nullptr, /* aFromNcpHost */ true));
-                otLogInfoIp6("Message copy for indirect transmission to sleepy children");
+                IgnoreError(HandleDatagram(*messageCopy, nullptr, nullptr, /* aFromHost */ true));
+                LogInfo("Message copy for indirect transmission to sleepy children");
             }
             else
             {
-                otLogWarnIp6("No enough buffer for message copy for indirect transmission to sleepy children");
+                LogWarn("No enough buffer for message copy for indirect transmission to sleepy children");
             }
         }
 #endif
@@ -452,9 +454,9 @@
     Header   header;
     uint16_t payloadLength = aMessage.GetLength();
 
-    header.Init();
+    header.InitVersionTrafficClassFlow();
     header.SetDscp(PriorityToDscp(aMessage.GetPriority()));
-    header.SetEcn(aMessageInfo.mEcn);
+    header.SetEcn(aMessageInfo.GetEcn());
     header.SetPayloadLength(payloadLength);
     header.SetNextHeader(aIpProto);
 
@@ -499,12 +501,12 @@
 
             if (messageCopy != nullptr)
             {
-                otLogInfoIp6("Message copy for indirect transmission to sleepy children");
+                LogInfo("Message copy for indirect transmission to sleepy children");
                 EnqueueDatagram(*messageCopy);
             }
             else
             {
-                otLogWarnIp6("No enough buffer for message copy for indirect transmission to sleepy children");
+                LogWarn("No enough buffer for message copy for indirect transmission to sleepy children");
             }
         }
 #endif
@@ -540,7 +542,7 @@
     while ((message = mSendQueue.GetHead()) != nullptr)
     {
         mSendQueue.Dequeue(*message);
-        IgnoreError(HandleDatagram(*message, nullptr, nullptr, /* aFromNcpHost */ false));
+        IgnoreError(HandleDatagram(*message, nullptr, nullptr, /* aFromHost */ false));
     }
 }
 
@@ -637,7 +639,7 @@
             payloadFragment = payloadLeft;
             payloadLeft     = 0;
 
-            otLogDebgIp6("Last Fragment");
+            LogDebg("Last Fragment");
         }
         else
         {
@@ -649,6 +651,7 @@
         fragmentHeader.SetOffset(offset);
 
         VerifyOrExit((fragment = NewMessage(0)) != nullptr, error = kErrorNoBufs);
+        IgnoreError(fragment->SetPriority(aMessage.GetPriority()));
         SuccessOrExit(error = fragment->SetLength(aMessage.GetOffset() + sizeof(fragmentHeader) + payloadFragment));
 
         header.SetPayloadLength(payloadFragment + sizeof(fragmentHeader));
@@ -667,7 +670,7 @@
         fragmentCnt++;
         fragment = nullptr;
 
-        otLogInfoIp6("Fragment %d with %d bytes sent", fragmentCnt, payloadFragment);
+        LogInfo("Fragment %d with %d bytes sent", fragmentCnt, payloadFragment);
     }
 
     aMessage.Free();
@@ -676,14 +679,14 @@
 
     if (error == kErrorNoBufs)
     {
-        otLogWarnIp6("No buffer for Ip6 fragmentation");
+        LogWarn("No buffer for Ip6 fragmentation");
     }
 
     FreeMessageOnError(fragment, error);
     return error;
 }
 
-Error Ip6::HandleFragment(Message &aMessage, Netif *aNetif, MessageInfo &aMessageInfo, bool aFromNcpHost)
+Error Ip6::HandleFragment(Message &aMessage, Netif *aNetif, MessageInfo &aMessageInfo, bool aFromHost)
 {
     Error          error = kErrorNone;
     Header         header, headerBuffer;
@@ -706,13 +709,14 @@
         ExitNow();
     }
 
-    for (message = mReassemblyList.GetHead(); message; message = message->GetNext())
+    for (Message &msg : mReassemblyList)
     {
-        SuccessOrExit(error = message->Read(0, headerBuffer));
+        SuccessOrExit(error = msg.Read(0, headerBuffer));
 
-        if (message->GetDatagramTag() == fragmentHeader.GetIdentification() &&
+        if (msg.GetDatagramTag() == fragmentHeader.GetIdentification() &&
             headerBuffer.GetSource() == header.GetSource() && headerBuffer.GetDestination() == header.GetDestination())
         {
+            message = &msg;
             break;
         }
     }
@@ -720,23 +724,23 @@
     offset          = FragmentHeader::FragmentOffsetToBytes(fragmentHeader.GetOffset());
     payloadFragment = aMessage.GetLength() - aMessage.GetOffset() - sizeof(fragmentHeader);
 
-    otLogInfoIp6("Fragment with id %d received > %d bytes, offset %d", fragmentHeader.GetIdentification(),
-                 payloadFragment, offset);
+    LogInfo("Fragment with id %d received > %d bytes, offset %d", fragmentHeader.GetIdentification(), payloadFragment,
+            offset);
 
     if (offset + payloadFragment + aMessage.GetOffset() > kMaxAssembledDatagramLength)
     {
-        otLogWarnIp6("Packet too large for fragment buffer");
+        LogWarn("Packet too large for fragment buffer");
         ExitNow(error = kErrorNoBufs);
     }
 
     if (message == nullptr)
     {
-        otLogDebgIp6("start reassembly");
+        LogDebg("start reassembly");
         VerifyOrExit((message = NewMessage(0)) != nullptr, error = kErrorNoBufs);
         mReassemblyList.Enqueue(*message);
         SuccessOrExit(error = message->SetLength(aMessage.GetOffset()));
 
-        message->SetTimeout(kIp6ReassemblyTimeout);
+        message->SetTimestampToNow();
         message->SetOffset(0);
         message->SetDatagramTag(fragmentHeader.GetIdentification());
 
@@ -770,11 +774,11 @@
         header.SetNextHeader(fragmentHeader.GetNextHeader());
         message->Write(0, header);
 
-        otLogDebgIp6("Reassembly complete.");
+        LogDebg("Reassembly complete.");
 
         mReassemblyList.Dequeue(*message);
 
-        IgnoreError(HandleDatagram(*message, aNetif, aMessageInfo.mLinkInfo, aFromNcpHost));
+        IgnoreError(HandleDatagram(*message, aNetif, aMessageInfo.mLinkInfo, aFromHost));
     }
 
 exit:
@@ -785,7 +789,7 @@
             mReassemblyList.DequeueAndFree(*message);
         }
 
-        otLogWarnIp6("Reassembly failed: %s", ErrorToString(error));
+        LogWarn("Reassembly failed: %s", ErrorToString(error));
     }
 
     if (isFragmented)
@@ -814,22 +818,16 @@
 
 void Ip6::UpdateReassemblyList(void)
 {
-    Message *next;
+    TimeMilli now = TimerMilli::GetNow();
 
-    for (Message *message = mReassemblyList.GetHead(); message; message = next)
+    for (Message &message : mReassemblyList)
     {
-        next = message->GetNext();
-
-        if (message->GetTimeout() > 0)
+        if (now - message.GetTimestamp() >= TimeMilli::SecToMsec(kIp6ReassemblyTimeout))
         {
-            message->DecrementTimeout();
-        }
-        else
-        {
-            otLogNoteIp6("Reassembly timeout.");
-            SendIcmpError(*message, Icmp::Header::kTypeTimeExceeded, Icmp::Header::kCodeFragmReasTimeEx);
+            LogNote("Reassembly timeout.");
+            SendIcmpError(message, Icmp::Header::kTypeTimeExceeded, Icmp::Header::kCodeFragmReasTimeEx);
 
-            mReassemblyList.DequeueAndFree(*message);
+            mReassemblyList.DequeueAndFree(message);
         }
     }
 }
@@ -853,7 +851,7 @@
 
     if (error != kErrorNone)
     {
-        otLogWarnIp6("Failed to send ICMP error: %s", ErrorToString(error));
+        LogWarn("Failed to send ICMP error: %s", ErrorToString(error));
     }
 }
 
@@ -867,11 +865,11 @@
     return kErrorNone;
 }
 
-Error Ip6::HandleFragment(Message &aMessage, Netif *aNetif, MessageInfo &aMessageInfo, bool aFromNcpHost)
+Error Ip6::HandleFragment(Message &aMessage, Netif *aNetif, MessageInfo &aMessageInfo, bool aFromHost)
 {
     OT_UNUSED_VARIABLE(aNetif);
     OT_UNUSED_VARIABLE(aMessageInfo);
-    OT_UNUSED_VARIABLE(aFromNcpHost);
+    OT_UNUSED_VARIABLE(aFromHost);
 
     Error          error = kErrorNone;
     FragmentHeader fragmentHeader;
@@ -893,7 +891,7 @@
                                   Header &     aHeader,
                                   uint8_t &    aNextHeader,
                                   bool         aIsOutbound,
-                                  bool         aFromNcpHost,
+                                  bool         aFromHost,
                                   bool &       aReceive)
 {
     Error           error = kErrorNone;
@@ -910,11 +908,11 @@
             break;
 
         case kProtoFragment:
-            // Always forward IPv6 fragments to the Host.
-            IgnoreError(ProcessReceiveCallback(aMessage, aMessageInfo, aNextHeader, aFromNcpHost,
+#if !OPENTHREAD_CONFIG_IP6_FRAGMENTATION_ENABLE
+            IgnoreError(ProcessReceiveCallback(aMessage, aMessageInfo, aNextHeader, aFromHost,
                                                /* aAllowReceiveFilter */ false, Message::kCopyToUse));
-
-            SuccessOrExit(error = HandleFragment(aMessage, aNetif, aMessageInfo, aFromNcpHost));
+#endif
+            SuccessOrExit(error = HandleFragment(aMessage, aNetif, aMessageInfo, aFromHost));
             break;
 
         case kProtoDstOpts:
@@ -966,7 +964,7 @@
         error = mTcp.HandleMessage(aIp6Header, *message, aMessageInfo);
         if (error == kErrorDrop)
         {
-            otLogNoteIp6("Error TCP Checksum");
+            LogNote("Error TCP Checksum");
         }
         break;
 #endif
@@ -974,7 +972,7 @@
         error = mUdp.HandleMessage(*message, aMessageInfo);
         if (error == kErrorDrop)
         {
-            otLogNoteIp6("Error UDP Checksum");
+            LogNote("Error UDP Checksum");
         }
         break;
 
@@ -989,7 +987,7 @@
 exit:
     if (error != kErrorNone)
     {
-        otLogNoteIp6("Failed to handle payload: %s", ErrorToString(error));
+        LogNote("Failed to handle payload: %s", ErrorToString(error));
     }
 
     FreeMessage(message);
@@ -1000,14 +998,14 @@
 Error Ip6::ProcessReceiveCallback(Message &          aMessage,
                                   const MessageInfo &aMessageInfo,
                                   uint8_t            aIpProto,
-                                  bool               aFromNcpHost,
+                                  bool               aFromHost,
                                   bool               aAllowReceiveFilter,
                                   Message::Ownership aMessageOwnership)
 {
     Error    error   = kErrorNone;
     Message *message = &aMessage;
 
-    VerifyOrExit(!aFromNcpHost, error = kErrorNoRoute);
+    VerifyOrExit(!aFromHost, error = kErrorNoRoute);
     VerifyOrExit(mReceiveIp6DatagramCallback != nullptr, error = kErrorNoRoute);
 
     // Do not forward reassembled IPv6 packets.
@@ -1064,7 +1062,7 @@
 
         if (message == nullptr)
         {
-            otLogWarnIp6("No buff to clone msg (len: %d) to pass to host", aMessage.GetLength());
+            LogWarn("No buff to clone msg (len: %d) to pass to host", aMessage.GetLength());
             ExitNow(error = kErrorNoBufs);
         }
 
@@ -1084,14 +1082,14 @@
     return error;
 }
 
-Error Ip6::SendRaw(Message &aMessage, bool aFromNcpHost)
+Error Ip6::SendRaw(Message &aMessage, bool aFromHost)
 {
     Error       error = kErrorNone;
     Header      header;
     MessageInfo messageInfo;
     bool        freed = false;
 
-    SuccessOrExit(error = header.Init(aMessage));
+    SuccessOrExit(error = header.ParseFrom(aMessage));
     VerifyOrExit(!header.GetSource().IsMulticast(), error = kErrorInvalidSourceAddress);
 
     messageInfo.SetPeerAddr(header.GetSource());
@@ -1103,7 +1101,7 @@
         SuccessOrExit(error = InsertMplOption(aMessage, header, messageInfo));
     }
 
-    error = HandleDatagram(aMessage, nullptr, nullptr, aFromNcpHost);
+    error = HandleDatagram(aMessage, nullptr, nullptr, aFromHost);
     freed = true;
 
 exit:
@@ -1116,7 +1114,7 @@
     return error;
 }
 
-Error Ip6::HandleDatagram(Message &aMessage, Netif *aNetif, const void *aLinkMessageInfo, bool aFromNcpHost)
+Error Ip6::HandleDatagram(Message &aMessage, Netif *aNetif, const void *aLinkMessageInfo, bool aFromHost)
 {
     Error       error;
     MessageInfo messageInfo;
@@ -1133,12 +1131,13 @@
     forwardHost       = false;
     shouldFreeMessage = true;
 
-    SuccessOrExit(error = header.Init(aMessage));
+    SuccessOrExit(error = header.ParseFrom(aMessage));
 
     messageInfo.Clear();
     messageInfo.SetPeerAddr(header.GetSource());
     messageInfo.SetSockAddr(header.GetDestination());
     messageInfo.SetHopLimit(header.GetHopLimit());
+    messageInfo.SetEcn(header.GetEcn());
     messageInfo.SetLinkInfo(aLinkMessageInfo);
 
     // determine destination of packet
@@ -1192,7 +1191,7 @@
             forwardThread = true;
         }
 
-        if (forwardThread && !ShouldForwardToThread(messageInfo, aFromNcpHost))
+        if (forwardThread && !ShouldForwardToThread(messageInfo, aFromHost))
         {
             forwardThread = false;
             forwardHost   = true;
@@ -1204,7 +1203,7 @@
     // process IPv6 Extension Headers
     nextHeader = static_cast<uint8_t>(header.GetNextHeader());
     SuccessOrExit(error = HandleExtensionHeaders(aMessage, aNetif, messageInfo, header, nextHeader, aNetif == nullptr,
-                                                 aFromNcpHost, receive));
+                                                 aFromHost, receive));
 
     // process IPv6 Payload
     if (receive)
@@ -1213,11 +1212,11 @@
         {
             // Remove encapsulating header and start over.
             aMessage.RemoveHeader(aMessage.GetOffset());
-            Get<MeshForwarder>().LogMessage(MeshForwarder::kMessageReceive, aMessage, nullptr, kErrorNone);
+            Get<MeshForwarder>().LogMessage(MeshForwarder::kMessageReceive, aMessage);
             goto start;
         }
 
-        error = ProcessReceiveCallback(aMessage, messageInfo, nextHeader, aFromNcpHost,
+        error = ProcessReceiveCallback(aMessage, messageInfo, nextHeader, aFromHost,
                                        /* aAllowReceiveFilter */ !forwardHost, Message::kCopyToUse);
 
         if ((error == kErrorNone || error == kErrorNoRoute) && forwardHost)
@@ -1233,7 +1232,7 @@
     if (forwardHost)
     {
         // try passing to host
-        error = ProcessReceiveCallback(aMessage, messageInfo, nextHeader, aFromNcpHost, /* aAllowReceiveFilter */ false,
+        error = ProcessReceiveCallback(aMessage, messageInfo, nextHeader, aFromHost, /* aAllowReceiveFilter */ false,
                                        forwardThread ? Message::kCopyToUse : Message::kTakeCustody);
         shouldFreeMessage = forwardThread;
     }
@@ -1271,7 +1270,7 @@
         }
 
 #if !OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
-        if (aFromNcpHost && (nextHeader == kProtoUdp))
+        if (aFromHost && (nextHeader == kProtoUdp))
         {
             uint16_t destPort;
 
@@ -1308,9 +1307,9 @@
     return error;
 }
 
-bool Ip6::ShouldForwardToThread(const MessageInfo &aMessageInfo, bool aFromNcpHost) const
+bool Ip6::ShouldForwardToThread(const MessageInfo &aMessageInfo, bool aFromHost) const
 {
-    OT_UNUSED_VARIABLE(aFromNcpHost);
+    OT_UNUSED_VARIABLE(aFromHost);
 
     bool rval = false;
 
@@ -1328,7 +1327,7 @@
     {
         // on-link global address
 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE
-        ExitNow(rval = (aFromNcpHost ||
+        ExitNow(rval = (aFromHost ||
                         !Get<BackboneRouter::Manager>().ShouldForwardDuaToBackbone(aMessageInfo.GetSockAddr())));
 #else
         ExitNow(rval = true);
@@ -1491,6 +1490,23 @@
     return Stringify::Lookup(aIpProto, kIpProtoTable, "Unknown");
 }
 
+const char *Ip6::EcnToString(Ecn aEcn)
+{
+    static const char *const kEcnStrings[] = {
+        "no", // (0) kEcnNotCapable
+        "e1", // (1) kEcnCapable1  (ECT1)
+        "e0", // (2) kEcnCapable0  (ECT0)
+        "ce", // (3) kEcnMarked    (Congestion Encountered)
+    };
+
+    static_assert(0 == kEcnNotCapable, "kEcnNotCapable value is incorrect");
+    static_assert(1 == kEcnCapable1, "kEcnCapable1 value is incorrect");
+    static_assert(2 == kEcnCapable0, "kEcnCapable0 value is incorrect");
+    static_assert(3 == kEcnMarked, "kEcnMarked value is incorrect");
+
+    return kEcnStrings[aEcn];
+}
+
 // LCOV_EXCL_STOP
 
 } // namespace Ip6
diff --git a/src/core/net/ip6.hpp b/src/core/net/ip6.hpp
index 2a0ce39..0855e13 100644
--- a/src/core/net/ip6.hpp
+++ b/src/core/net/ip6.hpp
@@ -43,6 +43,7 @@
 
 #include "common/encoding.hpp"
 #include "common/locator.hpp"
+#include "common/log.hpp"
 #include "common/message.hpp"
 #include "common/non_copyable.hpp"
 #include "common/time_ticker.hpp"
@@ -51,6 +52,7 @@
 #include "net/ip6_address.hpp"
 #include "net/ip6_headers.hpp"
 #include "net/ip6_mpl.hpp"
+#include "net/ip6_types.hpp"
 #include "net/netif.hpp"
 #include "net/socket.hpp"
 #include "net/tcp6.hpp"
@@ -109,18 +111,6 @@
 
 public:
     /**
-     * The max datagram length (in bytes) of an IPv6 message.
-     *
-     */
-    static constexpr uint16_t kMaxDatagramLength = OPENTHREAD_CONFIG_IP6_MAX_DATAGRAM_LENGTH;
-
-    /**
-     * The max datagram length (in bytes) of an unfragmented IPv6 message.
-     *
-     */
-    static constexpr uint16_t kMaxAssembledDatagramLength = OPENTHREAD_CONFIG_IP6_MAX_ASSEMBLED_DATAGRAM;
-
-    /**
      * This constructor initializes the object.
      *
      * @param[in]  aInstance   A reference to the otInstance object.
@@ -196,7 +186,7 @@
      * processing is complete, including when a value other than `kErrorNone` is returned.
      *
      * @param[in]  aMessage          A reference to the message.
-     * @param[in]  aFromNcpHost      If the message was forwarded from the NCP Host.
+     * @param[in]  aFromHost         TRUE if the message is originated from the host, FALSE otherwise.
      *
      * @retval kErrorNone     Successfully processed the message.
      * @retval kErrorDrop     Message was well-formed but not fully processed due to packet processing rules.
@@ -205,7 +195,7 @@
      * @retval kErrorParse    Encountered a malformed header when processing the message.
      *
      */
-    Error SendRaw(Message &aMessage, bool aFromNcpHost);
+    Error SendRaw(Message &aMessage, bool aFromHost);
 
     /**
      * This method processes a received IPv6 datagram.
@@ -213,7 +203,7 @@
      * @param[in]  aMessage          A reference to the message.
      * @param[in]  aNetif            A pointer to the network interface that received the message.
      * @param[in]  aLinkMessageInfo  A pointer to link-specific message information.
-     * @param[in]  aFromNcpHost      TRUE if the message was submitted by the NCP host, FALSE otherwise.
+     * @param[in]  aFromHost         TRUE if the message is originated from the host, FALSE otherwise.
      *
      * @retval kErrorNone     Successfully processed the message.
      * @retval kErrorDrop     Message was well-formed but not fully processed due to packet processing rules.
@@ -222,7 +212,7 @@
      * @retval kErrorParse    Encountered a malformed header when processing the message.
      *
      */
-    Error HandleDatagram(Message &aMessage, Netif *aNetif, const void *aLinkMessageInfo, bool aFromNcpHost);
+    Error HandleDatagram(Message &aMessage, Netif *aNetif, const void *aLinkMessageInfo, bool aFromHost);
 
     /**
      * This method registers a callback to provide received raw IPv6 datagrams.
@@ -301,13 +291,23 @@
     /**
      * This static method converts an IP protocol number to a string.
      *
-     * @param[in] aIpPorto  An IP protocol number.
+     * @param[in] aIpProto  An IP protocol number.
      *
      * @returns The string representation of @p aIpProto.
      *
      */
     static const char *IpProtoToString(uint8_t aIpProto);
 
+    /**
+     * This static method converts an IP header ECN value to a string.
+     *
+     * @param[in] aEcn   The 2-bit ECN value.
+     *
+     * @returns The string representation of @p aEcn.
+     *
+     */
+    static const char *EcnToString(Ecn aEcn);
+
 private:
     static constexpr uint8_t kDefaultHopLimit      = OPENTHREAD_CONFIG_IP6_HOP_LIMIT_DEFAULT;
     static constexpr uint8_t kIp6ReassemblyTimeout = OPENTHREAD_CONFIG_IP6_REASSEMBLY_TIMEOUT;
@@ -324,7 +324,7 @@
     Error ProcessReceiveCallback(Message &          aMessage,
                                  const MessageInfo &aMessageInfo,
                                  uint8_t            aIpProto,
-                                 bool               aFromNcpHost,
+                                 bool               aFromHost,
                                  bool               aAllowReceiveFilter,
                                  Message::Ownership aMessageOwnership);
     Error HandleExtensionHeaders(Message &    aMessage,
@@ -333,10 +333,10 @@
                                  Header &     aHeader,
                                  uint8_t &    aNextHeader,
                                  bool         aIsOutbound,
-                                 bool         aFromNcpHost,
+                                 bool         aFromHost,
                                  bool &       aReceive);
     Error FragmentDatagram(Message &aMessage, uint8_t aIpProto);
-    Error HandleFragment(Message &aMessage, Netif *aNetif, MessageInfo &aMessageInfo, bool aFromNcpHost);
+    Error HandleFragment(Message &aMessage, Netif *aNetif, MessageInfo &aMessageInfo, bool aFromHost);
 #if OPENTHREAD_CONFIG_IP6_FRAGMENTATION_ENABLE
     void CleanupFragmentationBuffer(void);
     void HandleTimeTick(void);
@@ -353,7 +353,7 @@
                         MessageInfo &      aMessageInfo,
                         uint8_t            aIpProto,
                         Message::Ownership aMessageOwnership);
-    bool  ShouldForwardToThread(const MessageInfo &aMessageInfo, bool aFromNcpHost) const;
+    bool  ShouldForwardToThread(const MessageInfo &aMessageInfo, bool aFromHost) const;
     bool  IsOnLink(const Address &aAddress) const;
 
     bool                 mForwardingEnabled;
diff --git a/src/core/net/ip6_address.cpp b/src/core/net/ip6_address.cpp
index 27cc793..11dcf93 100644
--- a/src/core/net/ip6_address.cpp
+++ b/src/core/net/ip6_address.cpp
@@ -35,6 +35,7 @@
 
 #include <stdio.h>
 
+#include "common/array.hpp"
 #include "common/as_core_type.hpp"
 #include "common/code_utils.hpp"
 #include "common/encoding.hpp"
@@ -613,7 +614,7 @@
         Ip4::Address ip4Addr;
 
         SuccessOrExit(error = ip4Addr.FromString(aString));
-        memcpy(OT_ARRAY_END(mFields.m8) - Ip4::Address::kSize, ip4Addr.GetBytes(), Ip4::Address::kSize);
+        memcpy(GetArrayEnd(mFields.m8) - Ip4::Address::kSize, ip4Addr.GetBytes(), Ip4::Address::kSize);
     }
 
     error = kErrorNone;
@@ -639,7 +640,7 @@
 
 void Address::ToString(StringWriter &aWriter) const
 {
-    AppendHexWords(aWriter, OT_ARRAY_LENGTH(mFields.m16));
+    AppendHexWords(aWriter, static_cast<uint8_t>(GetArrayLength(mFields.m16)));
 }
 
 void Address::AppendHexWords(StringWriter &aWriter, uint8_t aLength) const
diff --git a/src/core/net/ip6_address.hpp b/src/core/net/ip6_address.hpp
index f733a74..681c270 100644
--- a/src/core/net/ip6_address.hpp
+++ b/src/core/net/ip6_address.hpp
@@ -111,6 +111,14 @@
     const uint8_t *GetBytes(void) const { return mPrefix.mFields.m8; }
 
     /**
+     * This method gets the subnet ID of the prefix.
+     *
+     * @returns The 16-bit subnet ID.
+     *
+     */
+    uint16_t GetSubnetId(void) const { return HostSwap16(mPrefix.mFields.m16[3]); }
+
+    /**
      * This method gets the prefix length (in bits).
      *
      * @returns The prefix length (in bits).
@@ -144,6 +152,14 @@
     void Set(const NetworkPrefix &aNetworkPrefix) { Set(aNetworkPrefix.m8, NetworkPrefix::kLength); }
 
     /**
+     * This method sets the subnet ID of the prefix.
+     *
+     * @param[in] aSubnetId  A 16-bit subnet ID.
+     *
+     */
+    void SetSubnetId(uint16_t aSubnetId) { mPrefix.mFields.m16[3] = HostSwap16(aSubnetId); }
+
+    /**
      * This method set the prefix length.
      *
      * @param[in] aLength  The new prefix length (in bits).
diff --git a/src/core/net/ip6_filter.cpp b/src/core/net/ip6_filter.cpp
index 5ea480a..86a3e54 100644
--- a/src/core/net/ip6_filter.cpp
+++ b/src/core/net/ip6_filter.cpp
@@ -38,7 +38,7 @@
 #include "common/code_utils.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "meshcop/meshcop.hpp"
 #include "net/ip6.hpp"
 #include "net/tcp6.hpp"
@@ -48,6 +48,8 @@
 namespace ot {
 namespace Ip6 {
 
+RegisterLogModule("Ip6Filter");
+
 Filter::Filter(Instance &aInstance)
     : InstanceLocator(aInstance)
 {
@@ -149,7 +151,7 @@
         if (unsecurePort == 0)
         {
             unsecurePort = aPort;
-            otLogInfoIp6("Added unsecure port %d", aPort);
+            LogInfo("Added unsecure port %d", aPort);
             ExitNow();
         }
     }
@@ -179,7 +181,7 @@
 
             // Clear the last port entry.
             mUnsecurePorts[i] = 0;
-            otLogInfoIp6("Removed unsecure port %d", aPort);
+            LogInfo("Removed unsecure port %d", aPort);
             ExitNow();
         }
     }
diff --git a/src/core/net/ip6_headers.cpp b/src/core/net/ip6_headers.cpp
index f545d2c..be72fb6 100644
--- a/src/core/net/ip6_headers.cpp
+++ b/src/core/net/ip6_headers.cpp
@@ -38,14 +38,15 @@
 namespace ot {
 namespace Ip6 {
 
-Error Header::Init(const Message &aMessage)
+Error Header::ParseFrom(const Message &aMessage)
 {
-    Error error = kErrorNone;
+    Error error = kErrorParse;
 
-    SuccessOrExit(error = aMessage.Read(0, *this));
+    SuccessOrExit(aMessage.Read(0, *this));
+    VerifyOrExit(IsValid());
+    VerifyOrExit(sizeof(Header) + GetPayloadLength() == aMessage.GetLength());
 
-    VerifyOrExit(IsValid(), error = kErrorParse);
-    VerifyOrExit((sizeof(*this) + GetPayloadLength()) == aMessage.GetLength(), error = kErrorParse);
+    error = kErrorNone;
 
 exit:
     return error;
@@ -53,20 +54,13 @@
 
 bool Header::IsValid(void) const
 {
-    bool ret = true;
-
-    // check Version
-    VerifyOrExit(IsVersion6(), ret = false);
-
-    // check Payload Length
 #if !OPENTHREAD_CONFIG_IP6_FRAGMENTATION_ENABLE
-    VerifyOrExit((sizeof(*this) + GetPayloadLength()) <= Ip6::kMaxDatagramLength, ret = false);
+    static constexpr uint32_t kMaxLength = kMaxDatagramLength;
 #else
-    VerifyOrExit((sizeof(*this) + GetPayloadLength()) <= Ip6::kMaxAssembledDatagramLength, ret = false);
+    static constexpr uint32_t kMaxLength = kMaxAssembledDatagramLength;
 #endif
 
-exit:
-    return ret;
+    return IsVersion6() && ((sizeof(Header) + GetPayloadLength()) <= kMaxLength);
 }
 
 } // namespace Ip6
diff --git a/src/core/net/ip6_headers.hpp b/src/core/net/ip6_headers.hpp
index 064c3e8..c6846a7 100644
--- a/src/core/net/ip6_headers.hpp
+++ b/src/core/net/ip6_headers.hpp
@@ -38,9 +38,11 @@
 
 #include <stddef.h>
 
+#include "common/clearable.hpp"
 #include "common/encoding.hpp"
 #include "common/message.hpp"
 #include "net/ip6_address.hpp"
+#include "net/ip6_types.hpp"
 #include "net/netif.hpp"
 #include "net/socket.hpp"
 
@@ -85,39 +87,12 @@
  *
  */
 
-// Internet Protocol Numbers
-static constexpr uint8_t kProtoHopOpts  = OT_IP6_PROTO_HOP_OPTS; ///< IPv6 Hop-by-Hop Option
-static constexpr uint8_t kProtoTcp      = OT_IP6_PROTO_TCP;      ///< Transmission Control Protocol
-static constexpr uint8_t kProtoUdp      = OT_IP6_PROTO_UDP;      ///< User Datagram
-static constexpr uint8_t kProtoIp6      = OT_IP6_PROTO_IP6;      ///< IPv6 encapsulation
-static constexpr uint8_t kProtoRouting  = OT_IP6_PROTO_ROUTING;  ///< Routing Header for IPv6
-static constexpr uint8_t kProtoFragment = OT_IP6_PROTO_FRAGMENT; ///< Fragment Header for IPv6
-static constexpr uint8_t kProtoIcmp6    = OT_IP6_PROTO_ICMP6;    ///< ICMP for IPv6
-static constexpr uint8_t kProtoNone     = OT_IP6_PROTO_NONE;     ///< No Next Header for IPv6
-static constexpr uint8_t kProtoDstOpts  = OT_IP6_PROTO_DST_OPTS; ///< Destination Options for IPv6
-
-/**
- * Class Selectors
- */
-enum IpDscpCs : uint8_t
-{
-    kDscpCs0    = 0,    ///< Class selector codepoint 0
-    kDscpCs1    = 8,    ///< Class selector codepoint 8
-    kDscpCs2    = 16,   ///< Class selector codepoint 16
-    kDscpCs3    = 24,   ///< Class selector codepoint 24
-    kDscpCs4    = 32,   ///< Class selector codepoint 32
-    kDscpCs5    = 40,   ///< Class selector codepoint 40
-    kDscpCs6    = 48,   ///< Class selector codepoint 48
-    kDscpCs7    = 56,   ///< Class selector codepoint 56
-    kDscpCsMask = 0x38, ///< Class selector mask
-};
-
 /**
  * This class implements IPv6 header generation and parsing.
  *
  */
 OT_TOOL_PACKED_BEGIN
-class Header
+class Header : public Clearable<Header>
 {
 public:
     static constexpr uint8_t kPayloadLengthFieldOffset = 4;  ///< Offset of Payload Length field in IPv6 header.
@@ -127,33 +102,18 @@
     static constexpr uint8_t kDestinationFieldOffset   = 24; ///< Offset of Destination Address field in IPv6 header.
 
     /**
-     * This method initializes the IPv6 header.
+     * This method initializes the Version to 6 and sets Traffic Class and Flow fields to zero.
+     *
+     * The other fields in the IPv6 header remain unchanged.
      *
      */
-    void Init(void) { mVersionClassFlow.m32 = HostSwap32(kVersionClassFlowInit); }
-
-    /**
-     * This method initializes the IPv6 header and sets Version, Traffic Control and Flow Label fields.
-     *
-     */
-    void Init(uint32_t aVersionClassFlow) { mVersionClassFlow.m32 = HostSwap32(aVersionClassFlow); }
-
-    /**
-     * This method reads the IPv6 header from @p aMessage.
-     *
-     * @param[in]  aMessage  The IPv6 datagram.
-     *
-     * @retval kErrorNone   Successfully read the IPv6 header.
-     * @retval kErrorParse  Malformed IPv6 header.
-     *
-     */
-    Error Init(const Message &aMessage);
+    void InitVersionTrafficClassFlow(void) { SetVerionTrafficClassFlow(kVersTcFlowInit); }
 
     /**
      * This method indicates whether or not the header appears to be well-formed.
      *
-     * @retval TRUE  if the header appears to be well-formed.
-     * @retval FALSE if the header does not appear to be well-formed.
+     * @retval TRUE    If the header appears to be well-formed.
+     * @retval FALSE   If the header does not appear to be well-formed.
      *
      */
     bool IsValid(void) const;
@@ -165,48 +125,103 @@
      * @retval FALSE  If the IPv6 Version is not set to 6.
      *
      */
-    bool IsVersion6(void) const { return (mVersionClassFlow.m8[0] & kVersionMask) == kVersion6; }
+    bool IsVersion6(void) const { return (mVerTcFlow.m8[0] & kVersionMask) == kVersion6; }
 
     /**
-     * This method returns the IPv6 DSCP value.
+     * This method gets the combination of Version, Traffic Class, and Flow fields as a 32-bit value.
      *
-     * @returns The IPv6 DSCP value.
+     * @returns The Version, Traffic Class, and Flow fields as a 32-bit value.
+     *
+     */
+    uint32_t GetVerionTrafficClassFlow(void) const { return HostSwap32(mVerTcFlow.m32); }
+
+    /**
+     * This method sets the combination of Version, Traffic Class, and Flow fields as a 32-bit value.
+     *
+     * @param[in] aVerTcFlow   The Version, Traffic Class, and Flow fields as a 32-bit value.
+     *
+     */
+    void SetVerionTrafficClassFlow(uint32_t aVerTcFlow) { mVerTcFlow.m32 = HostSwap32(aVerTcFlow); }
+
+    /**
+     * This method gets the Traffic Class field.
+     *
+     * @returns The Traffic Class field.
+     *
+     */
+    uint8_t GetTrafficClass(void) const
+    {
+        return static_cast<uint8_t>((HostSwap16(mVerTcFlow.m16[0]) & kTrafficClassMask) >> kTrafficClassOffset);
+    }
+
+    /**
+     * This method sets the Traffic Class filed.
+     *
+     * @param[in] aTc  The Traffic Class value.
+     *
+     */
+    void SetTrafficClass(uint8_t aTc)
+    {
+        mVerTcFlow.m16[0] = HostSwap16((HostSwap16(mVerTcFlow.m16[0]) & ~kTrafficClassMask) |
+                                       ((static_cast<uint16_t>(aTc) << kTrafficClassOffset) & kTrafficClassMask));
+    }
+
+    /**
+     * This method gets the 6-bit Differentiated Services Code Point (DSCP) from Traffic Class field.
+     *
+     * @returns The DSCP value.
      *
      */
     uint8_t GetDscp(void) const
     {
-        return static_cast<uint8_t>((HostSwap16(mVersionClassFlow.m16[0]) & kDscpMask) >> kDscpOffset);
+        return static_cast<uint8_t>((HostSwap16(mVerTcFlow.m16[0]) & kDscpMask) >> kDscpOffset);
     }
 
     /**
-     * This method sets the IPv6 DSCP value.
+     * This method sets 6-bit Differentiated Services Code Point (DSCP) in IPv6 header.
      *
-     * @param[in]  aDscp  The IPv6 DSCP value.
+     * @param[in]  aDscp  The DSCP value.
      *
      */
     void SetDscp(uint8_t aDscp)
     {
-        mVersionClassFlow.m16[0] = HostSwap16((HostSwap16(mVersionClassFlow.m16[0]) & ~kDscpMask) |
-                                              ((static_cast<uint16_t>(aDscp) << kDscpOffset) & kDscpMask));
+        mVerTcFlow.m16[0] = HostSwap16((HostSwap16(mVerTcFlow.m16[0]) & ~kDscpMask) |
+                                       ((static_cast<uint16_t>(aDscp) << kDscpOffset) & kDscpMask));
     }
 
     /**
-     * This method returns the IPv6 ECN value.
+     * This method gets the 2-bit Explicit Congestion Notification (ECN) from Traffic Class field.
      *
-     * @returns The IPv6 ECN value.
+     * @returns The ECN value.
      *
      */
-    uint8_t GetEcn(void) const { return (mVersionClassFlow.m8[1] & kEcnMask) >> kEcnOffset; }
+    Ecn GetEcn(void) const { return static_cast<Ecn>((mVerTcFlow.m8[1] & kEcnMask) >> kEcnOffset); }
 
     /**
-     * This method sets the Ipv6 ECN value.
+     * This method sets the 2-bit Explicit Congestion Notification (ECN) in IPv6 header..
      *
-     * @param[in]  aEcn  The Ipv6 ECN value.
+     * @param[in]  aEcn  The ECN value.
      *
      */
-    void SetEcn(uint8_t aEcn)
+    void SetEcn(Ecn aEcn) { mVerTcFlow.m8[1] = (mVerTcFlow.m8[1] & ~kEcnMask) | ((aEcn << kEcnOffset) & kEcnMask); }
+
+    /**
+     * This method gets the 20-bit Flow field.
+     *
+     * @returns  The Flow value.
+     *
+     */
+    uint32_t GetFlow(void) const { return HostSwap32(mVerTcFlow.m32) & kFlowMask; }
+
+    /**
+     * This method sets the 20-bit Flow field in IPv6 header.
+     *
+     * @param[in] aFlow  The Flow value.
+     *
+     */
+    void SetFlow(uint32_t aFlow)
     {
-        mVersionClassFlow.m8[1] = (mVersionClassFlow.m8[1] & ~kEcnMask) | ((aEcn << kEcnOffset) & kEcnMask);
+        mVerTcFlow.m32 = HostSwap32((HostSwap32(mVerTcFlow.m32) & ~kFlowMask) | (aFlow & kFlowMask));
     }
 
     /**
@@ -305,26 +320,48 @@
      */
     void SetDestination(const Address &aDestination) { mDestination = aDestination; }
 
-private:
-    static constexpr uint8_t  kVersion6             = 0x60;
-    static constexpr uint8_t  kVersionMask          = 0xf0;       // To use with `mVersionClassFlow.m8[0]`
-    static constexpr uint8_t  kDscpOffset           = 6;          // To use with `mVersionClassFlow.m16[0]`
-    static constexpr uint16_t kDscpMask             = 0x0fc0;     // To use with `mVersionClassFlow.m16[0]`
-    static constexpr uint8_t  kEcnOffset            = 4;          // To use with `mVersionClassFlow.m8[1]`
-    static constexpr uint8_t  kEcnMask              = 0x30;       // To use with `mVersionClassFlow.m8[1]`
-    static constexpr uint32_t kVersionClassFlowInit = 0x60000000; // Version 6, TC and flow zero.
+    /**
+     * This method parses and validates the IPv6 header from a given message.
+     *
+     * The header is read from @p aMessage at offset zero.
+     *
+     * @param[in]  aMessage  The IPv6 message.
+     *
+     * @retval kErrorNone   Successfully parsed the IPv6 header from @p aMessage.
+     * @retval kErrorParse  Malformed IPv6 header or message (e.g., message does not contained expected payload length).
+     *
+     */
+    Error ParseFrom(const Message &aMessage);
 
-    static constexpr uint8_t kEcnNotCapable = OT_ECN_NOT_CAPABLE; ///< Non-ECT
-    static constexpr uint8_t kEcnCapable0   = OT_ECN_CAPABLE_0;   ///< ECT(0)
-    static constexpr uint8_t kEcnCapable1   = OT_ECN_CAPABLE_1;   ///< ECT(1)
-    static constexpr uint8_t kEcnMarked     = OT_ECN_MARKED;      ///< Congestion encountered (CE)
+private:
+    // IPv6 header `mVerTcFlow` field:
+    //
+    // |             m16[0]            |            m16[1]             |
+    // |     m8[0]     |     m8[1]     |     m8[2]     |      m8[3]    |
+    // +---------------+---------------+---------------+---------------+
+    // |7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|
+    // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    // |Version|    DSCP   |ECN|             Flow Label                |
+    // |       | Traffic Class |                                       |
+    // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+    static constexpr uint8_t  kVersion6           = 0x60;       // Use with `mVerTcFlow.m8[0]`
+    static constexpr uint8_t  kVersionMask        = 0xf0;       // Use with `mVerTcFlow.m8[0]`
+    static constexpr uint8_t  kTrafficClassOffset = 4;          // Use with `mVerTcFlow.m16[0]`
+    static constexpr uint16_t kTrafficClassMask   = 0x0ff0;     // Use with `mVerTcFlow.m16[0]`
+    static constexpr uint8_t  kDscpOffset         = 6;          // Use with `mVerTcFlow.m16[0]`
+    static constexpr uint16_t kDscpMask           = 0x0fc0;     // Use with `mVerTcFlow.m16[0]`
+    static constexpr uint8_t  kEcnOffset          = 4;          // Use with `mVerTcFlow.m8[1]`
+    static constexpr uint8_t  kEcnMask            = 0x30;       // Use with `mVerTcFlow.m8[1]`
+    static constexpr uint32_t kFlowMask           = 0x000fffff; // Use with `mVerTcFlow.m32`
+    static constexpr uint32_t kVersTcFlowInit     = 0x60000000; // Version 6, TC and flow zero.
 
     union OT_TOOL_PACKED_FIELD
     {
         uint8_t  m8[sizeof(uint32_t) / sizeof(uint8_t)];
         uint16_t m16[sizeof(uint32_t) / sizeof(uint16_t)];
         uint32_t m32;
-    } mVersionClassFlow;
+    } mVerTcFlow;
     uint16_t mPayloadLength;
     uint8_t  mNextHeader;
     uint8_t  mHopLimit;
diff --git a/src/core/net/ip6_mpl.cpp b/src/core/net/ip6_mpl.cpp
index a41932a..3e0156a 100644
--- a/src/core/net/ip6_mpl.cpp
+++ b/src/core/net/ip6_mpl.cpp
@@ -344,14 +344,10 @@
     TimeMilli now      = TimerMilli::GetNow();
     TimeMilli nextTime = now.GetDistantFuture();
     Metadata  metadata;
-    Message * message;
-    Message * nextMessage;
 
-    for (message = mBufferedMessageSet.GetHead(); message != nullptr; message = nextMessage)
+    for (Message &message : mBufferedMessageSet)
     {
-        nextMessage = message->GetNext();
-
-        metadata.ReadFrom(*message);
+        metadata.ReadFrom(message);
 
         if (now < metadata.mTransmissionTime)
         {
@@ -367,7 +363,7 @@
 
             if (metadata.mTransmissionCount < GetTimerExpirations())
             {
-                Message *messageCopy = message->Clone(message->GetLength() - sizeof(Metadata));
+                Message *messageCopy = message.Clone(message.GetLength() - sizeof(Metadata));
 
                 if (messageCopy != nullptr)
                 {
@@ -380,7 +376,7 @@
                 }
 
                 metadata.GenerateNextTransmissionTime(now, kDataMessageInterval);
-                metadata.UpdateIn(*message);
+                metadata.UpdateIn(message);
 
                 if (nextTime > metadata.mTransmissionTime)
                 {
@@ -389,22 +385,22 @@
             }
             else
             {
-                mBufferedMessageSet.Dequeue(*message);
+                mBufferedMessageSet.Dequeue(message);
 
                 if (metadata.mTransmissionCount == GetTimerExpirations())
                 {
                     if (metadata.mTransmissionCount > 1)
                     {
-                        message->SetSubType(Message::kSubTypeMplRetransmission);
+                        message.SetSubType(Message::kSubTypeMplRetransmission);
                     }
 
-                    metadata.RemoveFrom(*message);
-                    Get<Ip6>().EnqueueDatagram(*message);
+                    metadata.RemoveFrom(message);
+                    Get<Ip6>().EnqueueDatagram(message);
                 }
                 else
                 {
                     // Stop retransmitting if the number of timer expirations is already exceeded.
-                    message->Free();
+                    message.Free();
                 }
             }
         }
diff --git a/src/core/net/ip6_types.hpp b/src/core/net/ip6_types.hpp
new file mode 100644
index 0000000..06af126
--- /dev/null
+++ b/src/core/net/ip6_types.hpp
@@ -0,0 +1,115 @@
+/*
+ *  Copyright (c) 2016-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.
+ */
+
+/**
+ * @file
+ *   This file includes types and constants for IPv6 processing.
+ */
+
+#ifndef IP6_TYPES_HPP_
+#define IP6_TYPES_HPP_
+
+#include "openthread-core-config.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+namespace ot {
+namespace Ip6 {
+
+/**
+ * @addtogroup core-ip6-ip6
+ *
+ * @brief
+ *   This module includes definitions for core IPv6 networking.
+ *
+ * @{
+ *
+ */
+
+// Internet Protocol Numbers
+static constexpr uint8_t kProtoHopOpts  = OT_IP6_PROTO_HOP_OPTS; ///< IPv6 Hop-by-Hop Option
+static constexpr uint8_t kProtoTcp      = OT_IP6_PROTO_TCP;      ///< Transmission Control Protocol
+static constexpr uint8_t kProtoUdp      = OT_IP6_PROTO_UDP;      ///< User Datagram
+static constexpr uint8_t kProtoIp6      = OT_IP6_PROTO_IP6;      ///< IPv6 encapsulation
+static constexpr uint8_t kProtoRouting  = OT_IP6_PROTO_ROUTING;  ///< Routing Header for IPv6
+static constexpr uint8_t kProtoFragment = OT_IP6_PROTO_FRAGMENT; ///< Fragment Header for IPv6
+static constexpr uint8_t kProtoIcmp6    = OT_IP6_PROTO_ICMP6;    ///< ICMP for IPv6
+static constexpr uint8_t kProtoNone     = OT_IP6_PROTO_NONE;     ///< No Next Header for IPv6
+static constexpr uint8_t kProtoDstOpts  = OT_IP6_PROTO_DST_OPTS; ///< Destination Options for IPv6
+
+/**
+ * The max datagram length (in bytes) of an IPv6 message.
+ *
+ */
+static constexpr uint16_t kMaxDatagramLength = OPENTHREAD_CONFIG_IP6_MAX_DATAGRAM_LENGTH;
+
+/**
+ * The max datagram length (in bytes) of an unfragmented IPv6 message.
+ *
+ */
+static constexpr uint16_t kMaxAssembledDatagramLength = OPENTHREAD_CONFIG_IP6_MAX_ASSEMBLED_DATAGRAM;
+
+/**
+ * 6-bit Differentiated Services Code Point (DSCP) values.
+ *
+ */
+enum IpDscpCs : uint8_t
+{
+    kDscpCs0    = 0,    ///< Class selector codepoint 0
+    kDscpCs1    = 8,    ///< Class selector codepoint 8
+    kDscpCs2    = 16,   ///< Class selector codepoint 16
+    kDscpCs3    = 24,   ///< Class selector codepoint 24
+    kDscpCs4    = 32,   ///< Class selector codepoint 32
+    kDscpCs5    = 40,   ///< Class selector codepoint 40
+    kDscpCs6    = 48,   ///< Class selector codepoint 48
+    kDscpCs7    = 56,   ///< Class selector codepoint 56
+    kDscpCsMask = 0x38, ///< Class selector mask
+};
+
+/**
+ * This enumeration represents the 2-bit Explicit Congestion Notification (ECN) values.
+ *
+ */
+enum Ecn : uint8_t
+{
+    kEcnNotCapable = OT_ECN_NOT_CAPABLE, ///< Non ECN-Capable Transport (ECT).
+    kEcnCapable0   = OT_ECN_CAPABLE_0,   ///< ECN Capable Transport, ECT(0).
+    kEcnCapable1   = OT_ECN_CAPABLE_1,   ///< ECN Capable Transport, ECT(1).
+    kEcnMarked     = OT_ECN_MARKED,      ///< Congestion encountered.
+};
+
+/**
+ * @}
+ *
+ */
+
+} // namespace Ip6
+} // namespace ot
+
+#endif // IP6_TYPES_HPP_
diff --git a/src/core/net/netif.cpp b/src/core/net/netif.cpp
index 543ccca..a55914f 100644
--- a/src/core/net/netif.cpp
+++ b/src/core/net/netif.cpp
@@ -430,7 +430,7 @@
     entry = mMulticastAddresses.FindMatching(aAddress, prev);
     VerifyOrExit(entry != nullptr, error = kErrorNotFound);
 
-    VerifyOrExit(IsMulticastAddressExternal(*entry), error = kErrorInvalidArgs);
+    VerifyOrExit(IsMulticastAddressExternal(*entry), error = kErrorRejected);
 
     mMulticastAddresses.PopAfter(prev);
 
@@ -556,7 +556,7 @@
     entry = mUnicastAddresses.FindMatching(aAddress, prev);
     VerifyOrExit(entry != nullptr, error = kErrorNotFound);
 
-    VerifyOrExit(IsUnicastAddressExternal(*entry), error = kErrorInvalidArgs);
+    VerifyOrExit(IsUnicastAddressExternal(*entry), error = kErrorRejected);
 
     mUnicastAddresses.PopAfter(prev);
 
diff --git a/src/core/net/netif.hpp b/src/core/net/netif.hpp
index 76cb923..6501355 100644
--- a/src/core/net/netif.hpp
+++ b/src/core/net/netif.hpp
@@ -561,7 +561,7 @@
      * @param[in]  aAddress  A reference to the multicast address.
      *
      * @retval kErrorNone         Successfully unsubscribed to the unicast address.
-     * @retval kErrorInvalidArgs  The address indicated by @p aAddress is an internal address.
+     * @retval kErrorRejected     The address indicated by @p aAddress is an internal address.
      * @retval kErrorNotFound     The multicast address was not found.
      *
      */
diff --git a/src/core/net/sntp_client.cpp b/src/core/net/sntp_client.cpp
index 108370b..b2e82f2 100644
--- a/src/core/net/sntp_client.cpp
+++ b/src/core/net/sntp_client.cpp
@@ -36,7 +36,7 @@
 #include "common/debug.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "net/udp6.hpp"
 #include "thread/thread_netif.hpp"
 
@@ -48,6 +48,8 @@
 namespace ot {
 namespace Sntp {
 
+RegisterLogModule("SntpClnt");
+
 Header::Header(void)
     : mFlags(kNtpVersion << kVersionOffset | kModeClient << kModeOffset)
     , mStratum(0)
@@ -111,18 +113,12 @@
 
 Error Client::Stop(void)
 {
-    Message *     message = mPendingQueries.GetHead();
-    Message *     messageToRemove;
-    QueryMetadata queryMetadata;
-
-    // Remove all pending queries.
-    while (message != nullptr)
+    for (Message &message : mPendingQueries)
     {
-        messageToRemove = message;
-        message         = message->GetNext();
+        QueryMetadata queryMetadata;
 
-        queryMetadata.ReadFrom(*messageToRemove);
-        FinalizeSntpTransaction(*messageToRemove, queryMetadata, 0, kErrorAbort);
+        queryMetadata.ReadFrom(message);
+        FinalizeSntpTransaction(message, queryMetadata, 0, kErrorAbort);
     }
 
     return mSocket.Close();
@@ -237,30 +233,27 @@
     if (error != kErrorNone)
     {
         FreeMessage(messageCopy);
-        otLogWarnIp6("Failed to send SNTP request: %s", ErrorToString(error));
+        LogWarn("Failed to send SNTP request: %s", ErrorToString(error));
     }
 }
 
 Message *Client::FindRelatedQuery(const Header &aResponseHeader, QueryMetadata &aQueryMetadata)
 {
-    Header   header;
-    Message *message = mPendingQueries.GetHead();
+    Message *matchedMessage = nullptr;
 
-    while (message != nullptr)
+    for (Message &message : mPendingQueries)
     {
         // Read originate timestamp.
-        aQueryMetadata.ReadFrom(*message);
+        aQueryMetadata.ReadFrom(message);
 
         if (aQueryMetadata.mTransmitTimestamp == aResponseHeader.GetOriginateTimestampSeconds())
         {
-            ExitNow();
+            matchedMessage = &message;
+            break;
         }
-
-        message = message->GetNext();
     }
 
-exit:
-    return message;
+    return matchedMessage;
 }
 
 void Client::FinalizeSntpTransaction(Message &            aQuery,
@@ -286,36 +279,32 @@
     TimeMilli        now      = TimerMilli::GetNow();
     TimeMilli        nextTime = now.GetDistantFuture();
     QueryMetadata    queryMetadata;
-    Message *        message;
-    Message *        nextMessage;
     Ip6::MessageInfo messageInfo;
 
-    for (message = mPendingQueries.GetHead(); message != nullptr; message = nextMessage)
+    for (Message &message : mPendingQueries)
     {
-        nextMessage = message->GetNext();
-
-        queryMetadata.ReadFrom(*message);
+        queryMetadata.ReadFrom(message);
 
         if (now >= queryMetadata.mTransmissionTime)
         {
             if (queryMetadata.mRetransmissionCount >= kMaxRetransmit)
             {
                 // No expected response.
-                FinalizeSntpTransaction(*message, queryMetadata, 0, kErrorResponseTimeout);
+                FinalizeSntpTransaction(message, queryMetadata, 0, kErrorResponseTimeout);
                 continue;
             }
 
             // Increment retransmission counter and timer.
             queryMetadata.mRetransmissionCount++;
             queryMetadata.mTransmissionTime = now + kResponseTimeout;
-            queryMetadata.UpdateIn(*message);
+            queryMetadata.UpdateIn(message);
 
             // Retransmit
             messageInfo.SetPeerAddr(queryMetadata.mDestinationAddress);
             messageInfo.SetPeerPort(queryMetadata.mDestinationPort);
             messageInfo.SetSockAddr(queryMetadata.mSourceAddress);
 
-            SendCopy(*message, messageInfo);
+            SendCopy(message, messageInfo);
         }
 
         if (nextTime > queryMetadata.mTransmissionTime)
@@ -360,7 +349,7 @@
         memcpy(kissCode, responseHeader.GetKissCode(), Header::kKissCodeLength);
         kissCode[Header::kKissCodeLength] = 0;
 
-        otLogInfoIp6("SNTP response contains the Kiss-o'-death packet with %s code", kissCode);
+        LogInfo("SNTP response contains the Kiss-o'-death packet with %s code", kissCode);
         ExitNow(error = kErrorBusy);
     }
 
diff --git a/src/core/net/socket.hpp b/src/core/net/socket.hpp
index 27e73ab..fed22d1 100644
--- a/src/core/net/socket.hpp
+++ b/src/core/net/socket.hpp
@@ -39,6 +39,7 @@
 #include "common/clearable.hpp"
 #include "common/equatable.hpp"
 #include "net/ip6_address.hpp"
+#include "net/ip6_types.hpp"
 
 namespace ot {
 
@@ -209,7 +210,7 @@
      * @returns The ECN status, as represented in the IP header.
      *
      */
-    uint8_t GetEcn(void) const { return mEcn; }
+    Ecn GetEcn(void) const { return static_cast<Ecn>(mEcn); }
 
     /**
      * This method sets the ECN status.
@@ -217,7 +218,7 @@
      * @param[in]  aEcn  The ECN status, as represented in the IP header.
      *
      */
-    void SetEcn(uint8_t aEcn) { mEcn = aEcn; }
+    void SetEcn(Ecn aEcn) { mEcn = aEcn; }
 
     /**
      * This method indicates whether peer is via the host interface.
diff --git a/src/core/net/srp_client.cpp b/src/core/net/srp_client.cpp
index a9022b4..0667d30 100644
--- a/src/core/net/srp_client.cpp
+++ b/src/core/net/srp_client.cpp
@@ -35,12 +35,9 @@
 #include "common/debug.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
-#include "common/numeric_limits.hpp"
 #include "common/random.hpp"
 #include "common/settings.hpp"
 #include "common/string.hpp"
-#include "thread/network_data_service.hpp"
 
 /**
  * @file
@@ -50,6 +47,8 @@
 namespace ot {
 namespace Srp {
 
+RegisterLogModule("SrpClient");
+
 //---------------------------------------------------------------------
 // Client::HostInfo
 
@@ -72,7 +71,7 @@
 {
     if (aState != GetState())
     {
-        otLogInfoSrp("[client] HostInfo %s -> %s", ItemStateToString(GetState()), ItemStateToString(aState));
+        LogInfo("HostInfo %s -> %s", ItemStateToString(GetState()), ItemStateToString(aState));
         mState = MapEnum(aState);
     }
 }
@@ -82,11 +81,11 @@
     mAddresses    = aAddresses;
     mNumAddresses = aNumAddresses;
 
-    otLogInfoSrp("[client] HostInfo set %d addrs", GetNumAddresses());
+    LogInfo("HostInfo set %d addrs", GetNumAddresses());
 
     for (uint8_t index = 0; index < GetNumAddresses(); index++)
     {
-        otLogInfoSrp("[client] %s", GetAddress(index).ToString().AsCString());
+        LogInfo("%s", GetAddress(index).ToString().AsCString());
     }
 }
 
@@ -112,8 +111,8 @@
 {
     VerifyOrExit(GetState() != aState);
 
-    otLogInfoSrp("[client] Service %s -> %s, \"%s\" \"%s\"", ItemStateToString(GetState()), ItemStateToString(aState),
-                 GetInstanceName(), GetName());
+    LogInfo("Service %s -> %s, \"%s\" \"%s\"", ItemStateToString(GetState()), ItemStateToString(aState),
+            GetInstanceName(), GetName());
 
     if (aState == kToAdd)
     {
@@ -133,8 +132,8 @@
             }
         }
 
-        otLogInfoSrp("[client] subtypes:[%s] port:%d weight:%d prio:%d txts:%d", string.AsCString(), GetPort(),
-                     GetWeight(), GetPriority(), GetNumTxtEntries());
+        LogInfo("subtypes:[%s] port:%d weight:%d prio:%d txts:%d", string.AsCString(), GetPort(), GetWeight(),
+                GetPriority(), GetNumTxtEntries());
     }
 
     mState = MapEnum(aState);
@@ -154,6 +153,83 @@
 }
 
 //---------------------------------------------------------------------
+// Client::AutoStart
+
+#if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
+
+Client::AutoStart::AutoStart(void)
+{
+    Clear();
+    mState = kDefaultMode ? kSelectedNone : kDisabled;
+}
+
+bool Client::AutoStart::HasSelectedServer(void) const
+{
+    bool hasSelected = false;
+
+    switch (mState)
+    {
+    case kDisabled:
+    case kSelectedNone:
+        break;
+
+    case kSelectedUnicastPreferred:
+    case kSelectedUnicast:
+    case kSelectedAnycast:
+        hasSelected = true;
+        break;
+    }
+
+    return hasSelected;
+}
+
+void Client::AutoStart::SetState(State aState)
+{
+    if (mState != aState)
+    {
+        LogInfo("AutoStartState %s -> %s", StateToString(mState), StateToString(aState));
+        mState = aState;
+    }
+}
+
+void Client::AutoStart::SetCallback(AutoStartCallback aCallback, void *aContext)
+{
+    mCallback = aCallback;
+    mContext  = aContext;
+}
+
+void Client::AutoStart::InvokeCallback(const Ip6::SockAddr *aServerSockAddr) const
+{
+    if (mCallback != nullptr)
+    {
+        mCallback(aServerSockAddr, mContext);
+    }
+}
+
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
+const char *Client::AutoStart::StateToString(State aState)
+{
+    static const char *const kStateStrings[] = {
+        "Disabled",    // (0) kDisabled
+        "Idle",        // (1) kSelectedNone
+        "Unicast-prf", // (2) kSelectedUnicastPreferred
+        "Anycast",     // (3) kSelectedAnycast
+        "Unicast",     // (4) kSelectedUnicast
+    };
+
+    static_assert(0 == kDisabled, "kDisabled value is incorrect");
+    static_assert(1 == kSelectedNone, "kSelectedNone value is incorrect");
+    static_assert(2 == kSelectedUnicastPreferred, "kSelectedUnicastPreferred value is incorrect");
+    static_assert(3 == kSelectedAnycast, "kSelectedAnycast value is incorrect");
+    static_assert(4 == kSelectedUnicast, "kSelectedUnicast value is incorrect");
+
+    return kStateStrings[aState];
+}
+#endif
+
+#endif // OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
+
+//---------------------------------------------------------------------
 // Client
 
 const char Client::kDefaultDomainName[] = "default.service.arpa";
@@ -163,11 +239,6 @@
     , mState(kStateStopped)
     , mTxFailureRetryCount(0)
     , mShouldRemoveKeyLease(false)
-#if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
-    , mAutoStartModeEnabled(kAutoStartDefaultMode)
-    , mAutoStartDidSelectServer(false)
-    , mAutoStartIsUsingAnycastAddress(false)
-#endif
 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
     , mServiceKeyRecordEnabled(false)
 #endif
@@ -179,14 +250,6 @@
     , mSocket(aInstance)
     , mCallback(nullptr)
     , mCallbackContext(nullptr)
-#if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
-    , mAutoStartCallback(nullptr)
-    , mAutoStartContext(nullptr)
-    , mServerSequenceNumber(0)
-#if OPENTHREAD_CONFIG_SRP_CLIENT_SWITCH_SERVER_ON_FAILURE
-    , mTimoutFailureCount(0)
-#endif
-#endif
     , mDomainName(kDefaultDomainName)
     , mTimer(aInstance, Client::HandleTimer)
 {
@@ -218,24 +281,18 @@
     SuccessOrExit(error = mSocket.Open(Client::HandleUdpReceive, this));
     SuccessOrExit(error = mSocket.Connect(aServerSockAddr));
 
-    otLogInfoSrp("[client] %starting, server %s", (aRequester == kRequesterUser) ? "S" : "Auto-s",
-                 aServerSockAddr.ToString().AsCString());
+    LogInfo("%starting, server %s", (aRequester == kRequesterUser) ? "S" : "Auto-s",
+            aServerSockAddr.ToString().AsCString());
 
     Resume();
 
 #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
-    mAutoStartDidSelectServer = (aRequester == kRequesterAuto);
-
-    if (mAutoStartDidSelectServer)
+    if (aRequester == kRequesterAuto)
     {
 #if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE && OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVER_ADDRESS_AUTO_SET_ENABLE
         Get<Dns::Client>().UpdateDefaultConfigAddress();
 #endif
-
-        if (mAutoStartCallback != nullptr)
-        {
-            mAutoStartCallback(&aServerSockAddr, mAutoStartContext);
-        }
+        mAutoStart.InvokeCallback(&aServerSockAddr);
     }
 #endif
 
@@ -288,14 +345,11 @@
 
 #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
 #if OPENTHREAD_CONFIG_SRP_CLIENT_SWITCH_SERVER_ON_FAILURE
-    mTimoutFailureCount = 0;
+    mAutoStart.ResetTimoutFailureCount();
 #endif
-
-    mAutoStartDidSelectServer = false;
-
-    if ((aRequester == kRequesterAuto) && (mAutoStartCallback != nullptr))
+    if (aRequester == kRequesterAuto)
     {
-        mAutoStartCallback(nullptr, mAutoStartContext);
+        mAutoStart.InvokeCallback(nullptr);
     }
 #endif
 
@@ -387,7 +441,7 @@
     VerifyOrExit((mHostInfo.GetState() == kToAdd) || (mHostInfo.GetState() == kRemoved), error = kErrorInvalidState);
 
     mDomainName = (aName != nullptr) ? aName : kDefaultDomainName;
-    otLogInfoSrp("[client] Domain name \"%s\"", mDomainName);
+    LogInfo("Domain name \"%s\"", mDomainName);
 
 exit:
     return error;
@@ -402,7 +456,7 @@
 
     VerifyOrExit((mHostInfo.GetState() == kToAdd) || (mHostInfo.GetState() == kRemoved), error = kErrorInvalidState);
 
-    otLogInfoSrp("[client] Host name \"%s\"", aName);
+    LogInfo("Host name \"%s\"", aName);
     mHostInfo.SetName(aName);
     mHostInfo.SetState(kToAdd);
     UpdateState();
@@ -506,7 +560,7 @@
 {
     Error error = kErrorNone;
 
-    otLogInfoSrp("[client] Remove host & services");
+    LogInfo("Remove host & services");
 
     VerifyOrExit(mHostInfo.GetState() != kRemoved, error = kErrorAlready);
 
@@ -542,7 +596,7 @@
 
 void Client::ClearHostAndServices(void)
 {
-    otLogInfoSrp("[client] Clear host & services");
+    LogInfo("Clear host & services");
 
     switch (GetState())
     {
@@ -569,7 +623,7 @@
 {
     VerifyOrExit(aState != mState);
 
-    otLogInfoSrp("[client] State %s -> %s", StateToString(mState), StateToString(aState));
+    LogInfo("State %s -> %s", StateToString(mState), StateToString(aState));
     mState = aState;
 
     switch (mState)
@@ -609,24 +663,29 @@
     }
 
 #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE && OPENTHREAD_CONFIG_SRP_CLIENT_SAVE_SELECTED_SERVER_ENABLE
-    if (mAutoStartModeEnabled && mAutoStartDidSelectServer && (oldHostState != kRegistered) &&
-        (mHostInfo.GetState() == kRegistered))
+    if ((oldHostState != kRegistered) && (mHostInfo.GetState() == kRegistered))
     {
-        if (mAutoStartIsUsingAnycastAddress)
-        {
-            IgnoreError(Get<Settings>().Delete<Settings::SrpClientInfo>());
-        }
-        else
-        {
-            Settings::SrpClientInfo info;
+        Settings::SrpClientInfo info;
 
+        switch (mAutoStart.GetState())
+        {
+        case AutoStart::kDisabled:
+        case AutoStart::kSelectedNone:
+            break;
+
+        case AutoStart::kSelectedUnicastPreferred:
+        case AutoStart::kSelectedUnicast:
             info.SetServerAddress(GetServerAddress().GetAddress());
             info.SetServerPort(GetServerAddress().GetPort());
-
             IgnoreError(Get<Settings>().Save(info));
+            break;
+
+        case AutoStart::kSelectedAnycast:
+            IgnoreError(Get<Settings>().Delete<Settings::SrpClientInfo>());
+            break;
         }
     }
-#endif
+#endif // OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE && OPENTHREAD_CONFIG_SRP_CLIENT_SAVE_SELECTED_SERVER_ENABLE
 }
 
 void Client::InvokeCallback(Error aError) const
@@ -663,7 +722,7 @@
     SuccessOrExit(error = PrepareUpdateMessage(*message));
     SuccessOrExit(error = mSocket.SendTo(*message, Ip6::MessageInfo()));
 
-    otLogInfoSrp("[client] Send update");
+    LogInfo("Send update");
 
     // State changes:
     //   kToAdd     -> kAdding
@@ -696,7 +755,7 @@
         // continue to retry using the `mRetryWaitInterval` (which keeps
         // growing on each failure).
 
-        otLogInfoSrp("[client] Failed to send update: %s", ErrorToString(error));
+        LogInfo("Failed to send update: %s", ErrorToString(error));
 
         FreeMessage(message);
 
@@ -710,7 +769,7 @@
             interval = Random::NonCrypto::AddJitter(kTxFailureRetryInterval, kTxFailureRetryJitter);
             mTimer.Start(interval);
 
-            otLogInfoSrp("[client] Quick retry %d in %u msec", mTxFailureRetryCount, interval);
+            LogInfo("Quick retry %d in %u msec", mTxFailureRetryCount, interval);
 
             // Do not report message preparation errors to user
             // until `kMaxTxFailureRetries` are exhausted.
@@ -1206,17 +1265,17 @@
 
     // Response is for the earlier request message.
 
-    otLogInfoSrp("[client] Received response");
+    LogInfo("Received response");
 
 #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE && OPENTHREAD_CONFIG_SRP_CLIENT_SWITCH_SERVER_ON_FAILURE
-    mTimoutFailureCount = 0;
+    mAutoStart.ResetTimoutFailureCount();
 #endif
 
     error = Dns::Header::ResponseCodeToError(header.GetResponseCode());
 
     if (error != kErrorNone)
     {
-        otLogInfoSrp("[client] Server rejected %s code:%d", ErrorToString(error), header.GetResponseCode());
+        LogInfo("Server rejected %s code:%d", ErrorToString(error), header.GetResponseCode());
 
         if (mHostInfo.GetState() == kAdding)
         {
@@ -1332,7 +1391,7 @@
 exit:
     if (error != kErrorNone)
     {
-        otLogInfoSrp("[client] Failed to process response %s", ErrorToString(error));
+        LogInfo("Failed to process response %s", ErrorToString(error));
     }
 }
 
@@ -1595,7 +1654,7 @@
 
     case kStateUpdating:
         LogRetryWaitInterval();
-        otLogInfoSrp("[client] Timed out, no response");
+        LogInfo("Timed out, no response");
         GrowRetryWaitInterval();
         SetState(kStateToUpdate);
         InvokeCallback(kErrorResponseTimeout);
@@ -1607,12 +1666,9 @@
         // callback. It works correctly due to the guard check at the
         // top of `SelectNextServer()`.
 
-        if (mTimoutFailureCount < NumericLimits<uint8_t>::kMax)
-        {
-            mTimoutFailureCount++;
-        }
+        mAutoStart.IncrementTimoutFailureCount();
 
-        if (mTimoutFailureCount >= kMaxTimeoutFailuresToSwitchServer)
+        if (mAutoStart.GetTimoutFailureCount() >= kMaxTimeoutFailuresToSwitchServer)
         {
             SelectNextServer(kDisallowSwitchOnRegisteredHost);
         }
@@ -1629,11 +1685,11 @@
 
 void Client::EnableAutoStartMode(AutoStartCallback aCallback, void *aContext)
 {
-    mAutoStartCallback = aCallback;
-    mAutoStartContext  = aContext;
+    mAutoStart.SetCallback(aCallback, aContext);
 
-    VerifyOrExit(!mAutoStartModeEnabled);
-    mAutoStartModeEnabled = true;
+    VerifyOrExit(mAutoStart.GetState() == AutoStart::kDisabled);
+
+    mAutoStart.SetState(AutoStart::kSelectedNone);
     ProcessAutoStart();
 
 exit:
@@ -1642,130 +1698,181 @@
 
 void Client::ProcessAutoStart(void)
 {
-    Ip6::SockAddr                             serverSockAddr;
-    bool                                      serverIsAnycast = false;
-    NetworkData::Service::DnsSrpAnycast::Info anycastInfo;
-#if OPENTHREAD_CONFIG_SRP_CLIENT_SAVE_SELECTED_SERVER_ENABLE
-    Settings::SrpClientInfo savedInfo;
-    bool                    hasSavedServerInfo = false;
-#endif
+    Ip6::SockAddr       serverSockAddr;
+    DnsSrpAnycast::Info anycastInfo;
+    DnsSrpUnicast::Info unicastInfo;
+    bool                shouldRestart = false;
 
-    VerifyOrExit(mAutoStartModeEnabled);
+    // If auto start mode is enabled, we check the Network Data entries
+    // to discover and select the preferred SRP server to register with.
+    // If we currently have a selected server, we ensure that it is
+    // still present in the Network Data and is still the preferred one.
+
+    VerifyOrExit(mAutoStart.GetState() != AutoStart::kDisabled);
+
+    // If SRP client is running, we check to make sure that auto-start
+    // did select the current server, and server was not specified by
+    // user directly.
+
+    if (IsRunning())
+    {
+        VerifyOrExit(mAutoStart.GetState() != AutoStart::kSelectedNone);
+    }
+
+    // There are three types of entries in Network Data:
+    //
+    // 1) Preferred unicast entries with address included in service data.
+    // 2) Anycast entries (each having a seq number).
+    // 3) Unicast entries with address info included in server data.
 
     serverSockAddr.Clear();
 
-    // If the SRP client is not running and auto start mode is
-    // enabled, we check if we can find any SRP server info in the
-    // Thread Network Data. If it is already running and the server
-    // was chosen by the auto-start feature, then we ensure that the
-    // selected server is still present in the Network Data.
-    //
-    // Two types of "DNS/SRP Service" entries can be present in
-    // Network Data, "DNS/SRP Service Anycast Address" model and
-    // "DNS/SRP Service Unicast" model. The Anycast entries are
-    // preferred over the Unicast entries.
+    if (SelectUnicastEntry(DnsSrpUnicast::kFromServiceData, unicastInfo) == kErrorNone)
+    {
+        mAutoStart.SetState(AutoStart::kSelectedUnicastPreferred);
+        serverSockAddr = unicastInfo.mSockAddr;
+    }
+    else if (Get<NetworkData::Service::Manager>().FindPreferredDnsSrpAnycastInfo(anycastInfo) == kErrorNone)
+    {
+        serverSockAddr.SetAddress(anycastInfo.mAnycastAddress);
+        serverSockAddr.SetPort(kAnycastServerPort);
 
-    VerifyOrExit(!IsRunning() || mAutoStartDidSelectServer);
+        // We check if we are selecting an anycast entry for first
+        // time, or if the seq number has changed. Even if the
+        // anycast address remains the same as before, on a seq
+        // number change, the client still needs to restart to
+        // re-register its info.
 
-    // Now `IsRunning()` implies `mAutoStartDidSelectServer`.
+        if ((mAutoStart.GetState() != AutoStart::kSelectedAnycast) ||
+            (mAutoStart.GetAnycastSeqNum() != anycastInfo.mSequenceNumber))
+        {
+            shouldRestart = true;
+            mAutoStart.SetAnycastSeqNum(anycastInfo.mSequenceNumber);
+        }
 
+        mAutoStart.SetState(AutoStart::kSelectedAnycast);
+    }
+    else if (SelectUnicastEntry(DnsSrpUnicast::kFromServerData, unicastInfo) == kErrorNone)
+    {
+        mAutoStart.SetState(AutoStart::kSelectedUnicast);
+        serverSockAddr = unicastInfo.mSockAddr;
+    }
+
+    if (IsRunning())
+    {
+        VerifyOrExit((GetServerAddress() != serverSockAddr) || shouldRestart);
+        Stop(kRequesterAuto, kResetRetryInterval);
+    }
+
+    if (!serverSockAddr.GetAddress().IsUnspecified())
+    {
+        IgnoreError(Start(serverSockAddr, kRequesterAuto));
+    }
+    else
+    {
+        mAutoStart.SetState(AutoStart::kSelectedNone);
+    }
+
+exit:
+    return;
+}
+
+Error Client::SelectUnicastEntry(DnsSrpUnicast::Origin aOrigin, DnsSrpUnicast::Info &aInfo) const
+{
+    Error                                   error = kErrorNotFound;
+    DnsSrpUnicast::Info                     unicastInfo;
+    NetworkData::Service::Manager::Iterator iterator;
+    uint16_t                                numServers = 0;
 #if OPENTHREAD_CONFIG_SRP_CLIENT_SAVE_SELECTED_SERVER_ENABLE
+    Settings::SrpClientInfo savedInfo;
+    bool                    hasSavedServerInfo = false;
+
     if (!IsRunning())
     {
         hasSavedServerInfo = (Get<Settings>().Read(savedInfo) == kErrorNone);
     }
 #endif
 
-    if (Get<NetworkData::Service::Manager>().FindPreferredDnsSrpAnycastInfo(anycastInfo) == kErrorNone)
+    while (Get<NetworkData::Service::Manager>().GetNextDnsSrpUnicastInfo(iterator, unicastInfo) == kErrorNone)
     {
-        if (IsRunning() && mAutoStartIsUsingAnycastAddress && (mServerSequenceNumber == anycastInfo.mSequenceNumber) &&
-            (GetServerAddress().GetAddress() == anycastInfo.mAnycastAddress))
+        if (unicastInfo.mOrigin != aOrigin)
         {
-            // Client is already using the same anycast address.
+            continue;
+        }
+
+        if (mAutoStart.HasSelectedServer() && (GetServerAddress() == unicastInfo.mSockAddr))
+        {
+            aInfo = unicastInfo;
+            error = kErrorNone;
             ExitNow();
         }
 
-        otLogInfoSrp("[client] Found anycast server %d", anycastInfo.mSequenceNumber);
-
-        serverSockAddr.SetAddress(anycastInfo.mAnycastAddress);
-        serverSockAddr.SetPort(kAnycastServerPort);
-        mServerSequenceNumber = anycastInfo.mSequenceNumber;
-        serverIsAnycast       = true;
-    }
-    else
-    {
-        uint16_t                                  numServers = 0;
-        NetworkData::Service::DnsSrpUnicast::Info unicastInfo;
-        NetworkData::Service::Manager::Iterator   iterator;
-
-        while (Get<NetworkData::Service::Manager>().GetNextDnsSrpUnicastInfo(iterator, unicastInfo) == kErrorNone)
-        {
-            if (IsRunning() && !mAutoStartIsUsingAnycastAddress && (GetServerAddress() == unicastInfo.mSockAddr))
-            {
-                ExitNow();
-            }
-
 #if OPENTHREAD_CONFIG_SRP_CLIENT_SAVE_SELECTED_SERVER_ENABLE
-            if (hasSavedServerInfo && (unicastInfo.mSockAddr.GetAddress() == savedInfo.GetServerAddress()) &&
-                (unicastInfo.mSockAddr.GetPort() == savedInfo.GetServerPort()))
-            {
-                // Stop the search if we see a match for the previously
-                // saved server info in the network data entries.
+        if (hasSavedServerInfo && (unicastInfo.mSockAddr.GetAddress() == savedInfo.GetServerAddress()) &&
+            (unicastInfo.mSockAddr.GetPort() == savedInfo.GetServerPort()))
+        {
+            // Stop the search if we see a match for the previously
+            // saved server info in the network data entries.
 
-                serverSockAddr  = unicastInfo.mSockAddr;
-                serverIsAnycast = false;
-                break;
-            }
+            aInfo = unicastInfo;
+            error = kErrorNone;
+            ExitNow();
+        }
 #endif
+        numServers++;
 
-            numServers++;
+        // Choose a server randomly (with uniform distribution) from
+        // the list of servers. As we iterate through server entries,
+        // with probability `1/numServers`, we choose to switch the
+        // current selected server with the new entry. This approach
+        // results in a uniform/same probability of selection among
+        // all server entries.
 
-            // Choose a server randomly (with uniform distribution) from
-            // the list of servers. As we iterate through server entries,
-            // with probability `1/numServers`, we choose to switch the
-            // current selected server with the new entry. This approach
-            // results in a uniform/same probability of selection among
-            // all server entries.
-
-            if ((numServers == 1) || (Random::NonCrypto::GetUint16InRange(0, numServers) == 0))
-            {
-                serverSockAddr  = unicastInfo.mSockAddr;
-                serverIsAnycast = false;
-            }
+        if ((numServers == 1) || (Random::NonCrypto::GetUint16InRange(0, numServers) == 0))
+        {
+            aInfo = unicastInfo;
+            error = kErrorNone;
         }
     }
 
-    if (IsRunning())
-    {
-        Stop(kRequesterAuto, kResetRetryInterval);
-    }
-
-    VerifyOrExit(!serverSockAddr.GetAddress().IsUnspecified());
-
-    mAutoStartIsUsingAnycastAddress = serverIsAnycast;
-    IgnoreError(Start(serverSockAddr, kRequesterAuto));
-
 exit:
-    return;
+    return error;
 }
 
 #if OPENTHREAD_CONFIG_SRP_CLIENT_SWITCH_SERVER_ON_FAILURE
 void Client::SelectNextServer(bool aDisallowSwitchOnRegisteredHost)
 {
-    // This method tries to find the next server info entry in the
+    // This method tries to find the next unicast server info entry in the
     // Network Data after the current one selected. If found, it
     // restarts the client with the new server (keeping the retry wait
     // interval as before).
 
-    Ip6::SockAddr serverSockAddr;
-    bool          selectNext = false;
+    Ip6::SockAddr         serverSockAddr;
+    bool                  selectNext = false;
+    DnsSrpUnicast::Origin origin     = DnsSrpUnicast::kFromServiceData;
 
     serverSockAddr.Clear();
 
     // Ensure that client is running, auto-start is enabled and
-    // auto-start selected the server.
+    // auto-start selected the server and it is a unicast entry.
 
-    VerifyOrExit(IsRunning() && mAutoStartModeEnabled && mAutoStartDidSelectServer);
+    VerifyOrExit(IsRunning());
+
+    switch (mAutoStart.GetState())
+    {
+    case AutoStart::kSelectedUnicastPreferred:
+        origin = DnsSrpUnicast::kFromServiceData;
+        break;
+
+    case AutoStart::kSelectedUnicast:
+        origin = DnsSrpUnicast::kFromServerData;
+        break;
+
+    case AutoStart::kSelectedAnycast:
+    case AutoStart::kDisabled:
+    case AutoStart::kSelectedNone:
+        ExitNow();
+    }
 
     if (aDisallowSwitchOnRegisteredHost)
     {
@@ -1780,36 +1887,19 @@
 
     do
     {
-        NetworkData::Service::DnsSrpAnycast::Info anycastInfo;
-        NetworkData::Service::DnsSrpUnicast::Info unicastInfo;
-        NetworkData::Service::Manager::Iterator   iterator;
-
-        while (Get<NetworkData::Service::Manager>().GetNextDnsSrpAnycastInfo(iterator, anycastInfo) == kErrorNone)
-        {
-            if (selectNext)
-            {
-                serverSockAddr.SetAddress(anycastInfo.mAnycastAddress);
-                serverSockAddr.SetPort(kAnycastServerPort);
-                mServerSequenceNumber           = anycastInfo.mSequenceNumber;
-                mAutoStartIsUsingAnycastAddress = true;
-                ExitNow();
-            }
-
-            if (mAutoStartIsUsingAnycastAddress && (GetServerAddress().GetAddress() == anycastInfo.mAnycastAddress) &&
-                (GetServerAddress().GetPort() == kAnycastServerPort))
-            {
-                selectNext = true;
-            }
-        }
-
-        iterator.Reset();
+        DnsSrpUnicast::Info                     unicastInfo;
+        NetworkData::Service::Manager::Iterator iterator;
 
         while (Get<NetworkData::Service::Manager>().GetNextDnsSrpUnicastInfo(iterator, unicastInfo) == kErrorNone)
         {
+            if (unicastInfo.mOrigin != origin)
+            {
+                continue;
+            }
+
             if (selectNext)
             {
-                serverSockAddr                  = unicastInfo.mSockAddr;
-                mAutoStartIsUsingAnycastAddress = false;
+                serverSockAddr = unicastInfo.mSockAddr;
                 ExitNow();
             }
 
@@ -1866,7 +1956,7 @@
     return kItemStateStrings[aState];
 }
 
-#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_SRP == 1)
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
 
 const char *Client::StateToString(State aState)
 {
@@ -1895,11 +1985,11 @@
 
     uint32_t interval = GetRetryWaitInterval();
 
-    otLogInfoSrp("[client] Retry interval %u %s", (interval < kLogInMsecLimit) ? interval : Time::MsecToSec(interval),
-                 (interval < kLogInMsecLimit) ? "ms" : "sec");
+    LogInfo("Retry interval %u %s", (interval < kLogInMsecLimit) ? interval : Time::MsecToSec(interval),
+            (interval < kLogInMsecLimit) ? "ms" : "sec");
 }
 
-#endif // #if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_SRP == 1)
+#endif // #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
 
 } // namespace Srp
 } // namespace ot
diff --git a/src/core/net/srp_client.hpp b/src/core/net/srp_client.hpp
index ec846f2..80b381f 100644
--- a/src/core/net/srp_client.hpp
+++ b/src/core/net/srp_client.hpp
@@ -39,14 +39,17 @@
 #include "common/clearable.hpp"
 #include "common/linked_list.hpp"
 #include "common/locator.hpp"
+#include "common/log.hpp"
 #include "common/message.hpp"
 #include "common/non_copyable.hpp"
 #include "common/notifier.hpp"
+#include "common/numeric_limits.hpp"
 #include "common/timer.hpp"
 #include "crypto/ecdsa.hpp"
 #include "net/dns_types.hpp"
 #include "net/ip6.hpp"
 #include "net/udp6.hpp"
+#include "thread/network_data_service.hpp"
 
 /**
  * @file
@@ -68,6 +71,9 @@
 {
     friend class ot::Notifier;
 
+    using DnsSrpUnicast = NetworkData::Service::DnsSrpUnicast;
+    using DnsSrpAnycast = NetworkData::Service::DnsSrpAnycast;
+
 public:
     /**
      * This enumeration types represents an SRP client item (service or host info) state.
@@ -354,7 +360,7 @@
      * Note that a call to `Stop()` will also disable the auto-start mode.
      *
      */
-    void DisableAutoStartMode(void) { mAutoStartModeEnabled = false; }
+    void DisableAutoStartMode(void) { mAutoStart.SetState(AutoStart::kDisabled); }
 
     /**
      * This method indicates the current state of auto-start mode (enabled or disabled).
@@ -362,7 +368,7 @@
      * @returns TRUE if the auto-start mode is enabled, FALSE otherwise.
      *
      */
-    bool IsAutoStartModeEnabled(void) const { return mAutoStartModeEnabled; }
+    bool IsAutoStartModeEnabled(void) const { return mAutoStart.GetState() != AutoStart::kDisabled; }
 
     /**
      * This method indicates whether or not the current SRP server's address is selected by auto-start.
@@ -370,7 +376,7 @@
      * @returns TRUE if the SRP server's address is selected by auto-start, FALSE otherwise.
      *
      */
-    bool IsServerSelectedByAutoStart(void) const { return mAutoStartDidSelectServer; }
+    bool IsServerSelectedByAutoStart(void) const { return mAutoStart.HasSelectedServer(); }
 #endif // OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
 
     /**
@@ -421,7 +427,7 @@
      * Changing the lease interval does not impact the accepted lease interval of already registered services/host-info.
      * It only changes any future SRP update messages (i.e adding new services and/or refreshes of existing services).
      *
-     * @param[in] The lease interval (in seconds). If zero, the default value `kDefaultLease` would be used.
+     * @param[in] aInterval  The lease interval (in seconds). If zero, the default value `kDefaultLease` would be used.
      *
      */
     void SetLeaseInterval(uint32_t aInterval) { mLeaseInterval = GetBoundedLeaseInterval(aInterval, kDefaultLease); }
@@ -440,7 +446,8 @@
      * Changing the lease interval does not impact the accepted lease interval of already registered services/host-info.
      * It only changes any future SRP update messages (i.e adding new services and/or refreshes of existing services).
      *
-     * @param[in] The key lease interval (in seconds). If zero, the default value `kDefaultKeyLease` would be used.
+     * @param[in] aInterval The key lease interval (in seconds). If zero, the default value `kDefaultKeyLease` would be
+     *                      used.
      *
      */
     void SetKeyLeaseInterval(uint32_t aInterval)
@@ -575,9 +582,9 @@
      * any previously registered services with the server. In this case, caller can `SetHostName()` and then request
      * `RemoveHostAndServices()` with `aSendUnregToServer` as `true`.
      *
-     * @param[in] aRemoveKeyLease     A boolean indicating whether or not the host key lease should also be removed.
-     * @param[in] aSendUnregToServer   A boolean indicating whether to send update to server when host info is not
-     *                                registered.
+     * @param[in] aShouldRemoveKeyLease  A boolean indicating whether or not the host key lease should also be removed.
+     * @param[in] aSendUnregToServer     A boolean indicating whether to send update to server when host info is not
+     *                                   registered.
      *
      * @retval kErrorNone      The removal of host and services started successfully. The `Callback` will be called
      *                         to report the status.
@@ -667,7 +674,7 @@
         OPENTHREAD_CONFIG_SRP_CLIENT_MAX_TIMEOUT_FAILURES_TO_SWITCH_SERVER;
 #endif
 
-    static constexpr uint16_t kUdpPayloadSize = Ip6::Ip6::kMaxDatagramLength - sizeof(Ip6::Udp::Header);
+    static constexpr uint16_t kUdpPayloadSize = Ip6::kMaxDatagramLength - sizeof(Ip6::Udp::Header);
 
     // -------------------------------
     // Lease related constants
@@ -788,6 +795,54 @@
         kKeepRetryInterval,
     };
 
+#if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
+    class AutoStart : Clearable<AutoStart>
+    {
+    public:
+        enum State : uint8_t{
+            kDisabled,                 // AutoStart is disabled.
+            kSelectedNone,             // AutoStart is enabled but not yet selected any servers.
+            kSelectedUnicastPreferred, // AutoStart selected a preferred unicast entry (address in service data).
+            kSelectedAnycast,          // AutoStart selected an anycast entry with `mAnycastSeqNum`.
+            kSelectedUnicast,          // AutoStart selected a unicast entry (address in server data).
+        };
+
+        AutoStart(void);
+        bool    HasSelectedServer(void) const;
+        State   GetState(void) const { return mState; }
+        void    SetState(State aState);
+        uint8_t GetAnycastSeqNum(void) const { return mAnycastSeqNum; }
+        void    SetAnycastSeqNum(uint8_t aAnycastSeqNum) { mAnycastSeqNum = aAnycastSeqNum; }
+        void    SetCallback(AutoStartCallback aCallback, void *aContext);
+        void    InvokeCallback(const Ip6::SockAddr *aServerSockAddr) const;
+
+#if OPENTHREAD_CONFIG_SRP_CLIENT_SWITCH_SERVER_ON_FAILURE
+        uint8_t GetTimoutFailureCount(void) const { return mTimoutFailureCount; }
+        void    ResetTimoutFailureCount(void) { mTimoutFailureCount = 0; }
+        void    IncrementTimoutFailureCount(void)
+        {
+            if (mTimoutFailureCount < NumericLimits<uint8_t>::kMax)
+            {
+                mTimoutFailureCount++;
+            }
+        }
+#endif
+
+    private:
+        static constexpr bool kDefaultMode = OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_DEFAULT_MODE;
+
+        static const char *StateToString(State aState);
+
+        AutoStartCallback mCallback;
+        void *            mContext;
+        State             mState;
+        uint8_t           mAnycastSeqNum;
+#if OPENTHREAD_CONFIG_SRP_CLIENT_SWITCH_SERVER_ON_FAILURE
+        uint8_t mTimoutFailureCount; // Number of no-response timeout failures with the currently selected server.
+#endif
+    };
+#endif // OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
+
     struct Info : public Clearable<Info>
     {
         static constexpr uint16_t kUnknownOffset = 0; // Unknown offset value (used when offset is not yet set).
@@ -837,13 +892,14 @@
     static void  HandleTimer(Timer &aTimer);
     void         HandleTimer(void);
 #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
-    void ProcessAutoStart(void);
+    void  ProcessAutoStart(void);
+    Error SelectUnicastEntry(DnsSrpUnicast::Origin aOrigin, DnsSrpUnicast::Info &aInfo) const;
 #if OPENTHREAD_CONFIG_SRP_CLIENT_SWITCH_SERVER_ON_FAILURE
     void SelectNextServer(bool aDisallowSwitchOnRegisteredHost);
 #endif
 #endif
 
-#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_SRP == 1)
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
     static const char *StateToString(State aState);
     void               LogRetryWaitInterval(void) const;
 #else
@@ -857,11 +913,6 @@
     State   mState;
     uint8_t mTxFailureRetryCount : 4;
     bool    mShouldRemoveKeyLease : 1;
-#if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
-    bool mAutoStartModeEnabled : 1;
-    bool mAutoStartDidSelectServer : 1;
-    bool mAutoStartIsUsingAnycastAddress : 1;
-#endif
 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
     bool mServiceKeyRecordEnabled : 1;
 #endif
@@ -876,22 +927,15 @@
 
     Ip6::Udp::Socket mSocket;
 
-    Callback mCallback;
-    void *   mCallbackContext;
-
-#if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
-    AutoStartCallback mAutoStartCallback;
-    void *            mAutoStartContext;
-    uint8_t           mServerSequenceNumber;
-#if OPENTHREAD_CONFIG_SRP_CLIENT_SWITCH_SERVER_ON_FAILURE
-    uint8_t mTimoutFailureCount;
-#endif
-#endif
-
+    Callback            mCallback;
+    void *              mCallbackContext;
     const char *        mDomainName;
     HostInfo            mHostInfo;
     LinkedList<Service> mServices;
     TimerMilli          mTimer;
+#if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
+    AutoStart mAutoStart;
+#endif
 };
 
 } // namespace Srp
diff --git a/src/core/net/srp_server.cpp b/src/core/net/srp_server.cpp
index cee8633..b3d59f5 100644
--- a/src/core/net/srp_server.cpp
+++ b/src/core/net/srp_server.cpp
@@ -39,7 +39,7 @@
 #include "common/const_cast.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "common/new.hpp"
 #include "common/random.hpp"
 #include "net/dns_types.hpp"
@@ -48,6 +48,8 @@
 namespace ot {
 namespace Srp {
 
+RegisterLogModule("SrpServer");
+
 static const char kDefaultDomain[]       = "default.service.arpa.";
 static const char kServiceSubTypeLabel[] = "._sub.";
 
@@ -109,7 +111,7 @@
 
     VerifyOrExit(mState == kStateDisabled, error = kErrorInvalidState);
     VerifyOrExit(mAddressMode != aMode);
-    otLogInfoSrp("[server] Address Mode: %s -> %s", AddressModeToString(mAddressMode), AddressModeToString(aMode));
+    LogInfo("Address Mode: %s -> %s", AddressModeToString(mAddressMode), AddressModeToString(aMode));
     mAddressMode = aMode;
 
 exit:
@@ -123,7 +125,7 @@
     VerifyOrExit(mState == kStateDisabled, error = kErrorInvalidState);
     mAnycastSequenceNumber = aSequenceNumber;
 
-    otLogInfoSrp("[server] Set Anycast Address Mode Seq Number to %d", aSequenceNumber);
+    LogInfo("Set Anycast Address Mode Seq Number to %d", aSequenceNumber);
 
 exit:
     return error;
@@ -259,6 +261,8 @@
 // The caller MUST make sure that there is no existing host with the same hostname.
 void Server::AddHost(Host &aHost)
 {
+    LogInfo("Add new host %s", aHost.GetFullName());
+
     OT_ASSERT(mHosts.FindMatching(aHost.GetFullName()) == nullptr);
     IgnoreError(mHosts.Add(aHost));
 }
@@ -272,20 +276,20 @@
 
     if (aRetainName)
     {
-        otLogInfoSrp("[server] remove host '%s' (but retain its name)", aHost->GetFullName());
+        LogInfo("Remove host %s (but retain its name)", aHost->GetFullName());
     }
     else
     {
         aHost->mKeyLease = 0;
         IgnoreError(mHosts.Remove(*aHost));
-        otLogInfoSrp("[server] fully remove host '%s'", aHost->GetFullName());
+        LogInfo("Fully remove host %s", aHost->GetFullName());
     }
 
     if (aNotifyServiceHandler && mServiceUpdateHandler != nullptr)
     {
         uint32_t updateId = AllocateId();
 
-        otLogInfoSrp("[server] SRP update handler is notified (updatedId = %u)", updateId);
+        LogInfo("SRP update handler is notified (updatedId = %u)", updateId);
         mServiceUpdateHandler(updateId, aHost, kDefaultEventsHandlerTimeout, mServiceUpdateHandlerContext);
         // We don't wait for the reply from the service update handler,
         // but always remove the host (and its services) regardless of
@@ -310,6 +314,7 @@
 
     if (existingHost != nullptr && aHost.GetKeyRecord()->GetKey() != existingHost->GetKeyRecord()->GetKey())
     {
+        LogWarn("Name conflict: host name %s has already been allocated", aHost.GetFullName());
         ExitNow(hasConflicts = true);
     }
 
@@ -321,9 +326,11 @@
 
         for (const Host &host : mHosts)
         {
-            if (host.HasServiceInstance(service.GetInstanceName()))
+            if (host.HasServiceInstance(service.GetInstanceName()) &&
+                aHost.GetKeyRecord()->GetKey() != host.GetKeyRecord()->GetKey())
             {
-                VerifyOrExit(aHost.GetKeyRecord()->GetKey() == host.GetKeyRecord()->GetKey(), hasConflicts = true);
+                LogWarn("Name conflict: service name %s has already been allocated", service.GetInstanceName());
+                ExitNow(hasConflicts = true);
             }
         }
     }
@@ -342,14 +349,13 @@
     }
     else
     {
-        otLogInfoSrp("[server] delayed SRP host update result, the SRP update has been committed (updateId = %u)", aId);
+        LogInfo("Delayed SRP host update result, the SRP update has been committed (updateId = %u)", aId);
     }
 }
 
 void Server::HandleServiceUpdateResult(UpdateMetadata *aUpdate, Error aError)
 {
-    otLogInfoSrp("[server] handler result of SRP update (id = %u) is received: %s", aUpdate->GetId(),
-                 otThreadErrorToString(aError));
+    LogInfo("Handler result of SRP update (id = %u) is received: %s", aUpdate->GetId(), ErrorToString(aError));
 
     IgnoreError(mOutstandingUpdates.Remove(*aUpdate));
     CommitSrpUpdate(aError, *aUpdate);
@@ -413,7 +419,7 @@
     {
         if (aHost.GetKeyLease() == 0)
         {
-            otLogInfoSrp("[server] remove key of host %s", aHost.GetFullName());
+            LogInfo("Remove key of host %s", aHost.GetFullName());
             RemoveHost(existingHost, kDeleteName, kDoNotNotifyServiceHandler);
         }
         else if (existingHost != nullptr)
@@ -433,7 +439,8 @@
     }
     else
     {
-        otLogInfoSrp("[server] add new host %s", aHost.GetFullName());
+        AddHost(aHost);
+        shouldFreeHost = false;
 
         for (Service &service : aHost.GetServices())
         {
@@ -441,9 +448,6 @@
             service.Log(Service::kAddNew);
         }
 
-        AddHost(aHost);
-        shouldFreeHost = false;
-
 #if OPENTHREAD_CONFIG_SRP_SERVER_PORT_SWITCH_ENABLE
         if (!mHasRegisteredAnyService && (mAddressMode == kAddressModeUnicast))
         {
@@ -497,7 +501,7 @@
     }
 #endif
 
-    otLogInfoSrp("[server] selected port %u", mPort);
+    LogInfo("Selected port %u", mPort);
 }
 
 void Server::Start(void)
@@ -506,7 +510,7 @@
 
     mState = kStateRunning;
     PrepareSocket();
-    otLogInfoSrp("[server] start listening on port %u", mPort);
+    LogInfo("Start listening on port %u", mPort);
 
 exit:
     return;
@@ -537,7 +541,7 @@
 exit:
     if (error != kErrorNone)
     {
-        otLogCritSrp("[server] failed to prepare socket: %s", ErrorToString(error));
+        LogCrit("Failed to prepare socket: %s", ErrorToString(error));
         Stop();
     }
 }
@@ -613,7 +617,7 @@
     mLeaseTimer.Stop();
     mOutstandingUpdatesTimer.Stop();
 
-    otLogInfoSrp("[server] stop listening on %u", mPort);
+    LogInfo("Stop listening on %u", mPort);
     IgnoreError(mSocket.Close());
     mHasRegisteredAnyService = false;
 
@@ -660,15 +664,15 @@
     Error error = kErrorNone;
     Host *host  = nullptr;
 
-    otLogInfoSrp("[server] Received DNS update from %s",
-                 aMetadata.IsDirectRxFromClient() ? aMetadata.mMessageInfo->GetPeerAddr().ToString().AsCString()
-                                                  : "an SRPL Partner");
+    LogInfo("Received DNS update from %s", aMetadata.IsDirectRxFromClient()
+                                               ? aMetadata.mMessageInfo->GetPeerAddr().ToString().AsCString()
+                                               : "an SRPL Partner");
 
     SuccessOrExit(error = ProcessZoneSection(aMessage, aMetadata));
 
     if (FindOutstandingUpdate(aMetadata) != nullptr)
     {
-        otLogInfoSrp("[server] Drop duplicated SRP update request: MessageId=%hu", aMetadata.mDnsHeader.GetMessageId());
+        LogInfo("Drop duplicated SRP update request: MessageId=%hu", aMetadata.mDnsHeader.GetMessageId());
 
         // Silently drop duplicate requests.
         // This could rarely happen, because the outstanding SRP update timer should
@@ -721,6 +725,11 @@
     aMetadata.mOffset = offset;
 
 exit:
+    if (error != kErrorNone)
+    {
+        LogWarn("Failed to process DNS Zone section: %s", ErrorToString(error));
+    }
+
     return error;
 }
 
@@ -746,6 +755,11 @@
     VerifyOrExit(!HasNameConflictsWith(aHost), error = kErrorDuplicated);
 
 exit:
+    if (error != kErrorNone)
+    {
+        LogWarn("Failed to process DNS Update section: %s", ErrorToString(error));
+    }
+
     return error;
 }
 
@@ -822,6 +836,11 @@
     // the host is being removed or registered.
 
 exit:
+    if (error != kErrorNone)
+    {
+        LogWarn("Failed to process Host Description instructions: %s", ErrorToString(error));
+    }
+
     return error;
 }
 
@@ -892,6 +911,11 @@
     }
 
 exit:
+    if (error != kErrorNone)
+    {
+        LogWarn("Failed to process Service Discovery instructions: %s", ErrorToString(error));
+    }
+
     return error;
 }
 
@@ -988,6 +1012,11 @@
     aMetadata.mOffset = offset;
 
 exit:
+    if (error != kErrorNone)
+    {
+        LogWarn("Failed to process Service Description instructions: %s", ErrorToString(error));
+    }
+
     return error;
 }
 
@@ -1066,6 +1095,11 @@
     aMetadata.mOffset = offset;
 
 exit:
+    if (error != kErrorNone)
+    {
+        LogWarn("Failed to process DNS Additional section: %s", ErrorToString(error));
+    }
+
     return error;
 }
 
@@ -1112,6 +1146,11 @@
     error = aKeyRecord.GetKey().Verify(hash, signature);
 
 exit:
+    if (error != kErrorNone)
+    {
+        LogWarn("Failed to verify message signature: %s", ErrorToString(error));
+    }
+
     FreeMessage(signerNameMessage);
     return error;
 }
@@ -1160,7 +1199,7 @@
         mOutstandingUpdates.Push(*update);
         mOutstandingUpdatesTimer.FireAtIfEarlier(update->GetExpireTime());
 
-        otLogInfoSrp("[server] SRP update handler is notified (updatedId = %u)", update->GetId());
+        LogInfo("SRP update handler is notified (updatedId = %u)", update->GetId());
         mServiceUpdateHandler(update->GetId(), &aHost, kDefaultEventsHandlerTimeout, mServiceUpdateHandlerContext);
     }
     else
@@ -1190,17 +1229,17 @@
 
     if (aResponseCode != Dns::UpdateHeader::kResponseSuccess)
     {
-        otLogInfoSrp("[server] send fail response: %d", aResponseCode);
+        LogWarn("Send fail response: %d", aResponseCode);
     }
     else
     {
-        otLogInfoSrp("[server] send success response");
+        LogInfo("Send success response");
     }
 
 exit:
     if (error != kErrorNone)
     {
-        otLogWarnSrp("[server] failed to send response: %s", ErrorToString(error));
+        LogWarn("Failed to send response: %s", ErrorToString(error));
         FreeMessage(response);
     }
 }
@@ -1242,12 +1281,12 @@
 
     SuccessOrExit(error = GetSocket().SendTo(*response, aMessageInfo));
 
-    otLogInfoSrp("[server] send response with granted lease: %u and key lease: %u", aLease, aKeyLease);
+    LogInfo("Send success response with granted lease: %u and key lease: %u", aLease, aKeyLease);
 
 exit:
     if (error != kErrorNone)
     {
-        otLogWarnSrp("[server] failed to send response: %s", ErrorToString(error));
+        LogWarn("Failed to send response: %s", ErrorToString(error));
         FreeMessage(response);
     }
 }
@@ -1263,7 +1302,7 @@
 
     if (error != kErrorNone)
     {
-        otLogInfoSrp("[server] failed to handle DNS message: %s", ErrorToString(error));
+        LogInfo("Failed to handle DNS message: %s", ErrorToString(error));
     }
 }
 
@@ -1314,7 +1353,7 @@
 
         if (host->GetKeyExpireTime() <= now)
         {
-            otLogInfoSrp("[server] KEY LEASE of host %s expired", host->GetFullName());
+            LogInfo("KEY LEASE of host %s expired", host->GetFullName());
 
             // Removes the whole host and all services if the KEY RR expired.
             RemoveHost(host, kDeleteName, kNotifyServiceHandler);
@@ -1347,7 +1386,7 @@
         }
         else if (host->GetExpireTime() <= now)
         {
-            otLogInfoSrp("[server] LEASE of host %s expired", host->GetFullName());
+            LogInfo("LEASE of host %s expired", host->GetFullName());
 
             // If the host expired, delete all resources of this host and its services.
             for (Service &service : host->mServices)
@@ -1405,13 +1444,13 @@
         OT_ASSERT(earliestExpireTime >= now);
         if (!mLeaseTimer.IsRunning() || earliestExpireTime <= mLeaseTimer.GetFireTime())
         {
-            otLogInfoSrp("[server] lease timer is scheduled for %u seconds", Time::MsecToSec(earliestExpireTime - now));
+            LogInfo("Lease timer is scheduled for %u seconds", Time::MsecToSec(earliestExpireTime - now));
             mLeaseTimer.StartAt(earliestExpireTime, 0);
         }
     }
     else
     {
-        otLogInfoSrp("[server] lease timer is stopped");
+        LogInfo("Lease timer is stopped");
         mLeaseTimer.Stop();
     }
 }
@@ -1425,8 +1464,7 @@
 {
     while (!mOutstandingUpdates.IsEmpty() && mOutstandingUpdates.GetTail()->GetExpireTime() <= TimerMilli::GetNow())
     {
-        otLogInfoSrp("[server] outstanding service update timeout (updateId = %u)",
-                     mOutstandingUpdates.GetTail()->GetId());
+        LogInfo("Outstanding service update timeout (updateId = %u)", mOutstandingUpdates.GetTail()->GetId());
         HandleServiceUpdateResult(mOutstandingUpdates.GetTail(), kErrorResponseTimeout);
     }
 }
@@ -1540,14 +1578,14 @@
     return matches;
 }
 
-#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && OPENTHREAD_CONFIG_LOG_SRP
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
 void Server::Service::Log(Action aAction) const
 {
     static const char *const kActionStrings[] = {
-        "add new",                   // (0) kAddNew
-        "update existing",           // (1) kUpdateExisting
-        "remove but retain name of", // (2) kRemoveButRetainName
-        "full remove",               // (3) kFullyRemove
+        "Add new",                   // (0) kAddNew
+        "Update existing",           // (1) kUpdateExisting
+        "Remove but retain name of", // (2) kRemoveButRetainName
+        "Fully remove",              // (3) kFullyRemove
         "LEASE expired for ",        // (4) kLeaseExpired
         "KEY LEASE expired for",     // (5) kKeyLeaseExpired
     };
@@ -1570,15 +1608,15 @@
     {
         IgnoreError(GetServiceSubTypeLabel(subLabel, sizeof(subLabel)));
 
-        otLogInfoSrp("[server] %s service '%s'%s%s", kActionStrings[aAction], GetInstanceName(),
-                     IsSubType() ? " subtype:" : "", subLabel);
+        LogInfo("%s service '%s'%s%s", kActionStrings[aAction], GetInstanceName(), IsSubType() ? " subtype:" : "",
+                subLabel);
     }
 }
 #else
 void Server::Service::Log(Action) const
 {
 }
-#endif // #if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && OPENTHREAD_CONFIG_LOG_SRP
+#endif // #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
 
 //---------------------------------------------------------------------------------------------------------------------
 // Server::Service::Description
@@ -1768,7 +1806,7 @@
     {
         uint32_t updateId = server.AllocateId();
 
-        otLogInfoSrp("[server] SRP update handler is notified (updatedId = %u)", updateId);
+        LogInfo("SRP update handler is notified (updatedId = %u)", updateId);
         server.mServiceUpdateHandler(updateId, this, kDefaultEventsHandlerTimeout, server.mServiceUpdateHandlerContext);
         // We don't wait for the reply from the service update handler,
         // but always remove the service regardless of service update result.
@@ -1797,7 +1835,7 @@
 
 void Server::Host::ClearResources(void)
 {
-    mAddresses.Clear();
+    mAddresses.Free();
 }
 
 Error Server::Host::MergeServicesAndResourcesFrom(Host &aHost)
@@ -1808,9 +1846,9 @@
 
     Error error = kErrorNone;
 
-    otLogInfoSrp("[server] update host %s", GetFullName());
+    LogInfo("Update host %s", GetFullName());
 
-    mAddresses  = aHost.mAddresses;
+    mAddresses.TakeFrom(static_cast<Heap::Array<Ip6::Address> &&>(aHost.mAddresses));
     mKeyRecord  = aHost.mKeyRecord;
     mLease      = aHost.mLease;
     mKeyLease   = aHost.mKeyLease;
@@ -1909,7 +1947,7 @@
 
     if (error == kErrorNoBufs)
     {
-        otLogWarnSrp("[server] too many addresses for host %s", GetFullName());
+        LogWarn("Too many addresses for host %s", GetFullName());
     }
 
 exit:
diff --git a/src/core/net/srp_server.hpp b/src/core/net/srp_server.hpp
index e3cd503..c74eb07 100644
--- a/src/core/net/srp_server.hpp
+++ b/src/core/net/srp_server.hpp
@@ -58,12 +58,14 @@
 #include "common/clearable.hpp"
 #include "common/heap.hpp"
 #include "common/heap_allocatable.hpp"
+#include "common/heap_array.hpp"
 #include "common/heap_data.hpp"
 #include "common/heap_string.hpp"
 #include "common/linked_list.hpp"
 #include "common/locator.hpp"
 #include "common/non_copyable.hpp"
 #include "common/notifier.hpp"
+#include "common/numeric_limits.hpp"
 #include "common/retain_ptr.hpp"
 #include "common/timer.hpp"
 #include "crypto/ecdsa.hpp"
@@ -270,8 +272,6 @@
         /**
          * This method returns the priority of the service instance.
          *
-         * @param[in]  aService  A pointer to the SRP service.
-         *
          * @returns  The priority of the service.
          *
          */
@@ -432,8 +432,8 @@
          */
         const Ip6::Address *GetAddresses(uint8_t &aAddressesNum) const
         {
-            aAddressesNum = mAddresses.GetLength();
-            return mAddresses.Front();
+            aAddressesNum = static_cast<uint8_t>(OT_MIN(mAddresses.GetLength(), NumericLimits<uint8_t>::kMax));
+            return mAddresses.AsCArray();
         }
 
         /**
@@ -512,8 +512,6 @@
         bool Matches(const char *aFullName) const;
 
     private:
-        static constexpr uint16_t kMaxAddresses = OPENTHREAD_CONFIG_SRP_SERVER_MAX_ADDRESSES_NUM;
-
         Host(Instance &aInstance, TimeMilli aUpdateTime);
         ~Host(void);
 
@@ -537,9 +535,9 @@
         Service *                             FindService(const char *aServiceName, const char *aInstanceName);
         const Service *                       FindService(const char *aServiceName, const char *aInstanceName) const;
 
-        Host *                             mNext;
-        Heap::String                       mFullName;
-        Array<Ip6::Address, kMaxAddresses> mAddresses;
+        Host *                    mNext;
+        Heap::String              mFullName;
+        Heap::Array<Ip6::Address> mAddresses;
 
         // TODO(wgtdkp): there is no necessary to save the entire resource
         // record, saving only the ECDSA-256 public key should be enough.
@@ -758,7 +756,7 @@
     void HandleServiceUpdateResult(ServiceUpdateId aId, Error aError);
 
 private:
-    static constexpr uint16_t kUdpPayloadSize = Ip6::Ip6::kMaxDatagramLength - sizeof(Ip6::Udp::Header);
+    static constexpr uint16_t kUdpPayloadSize = Ip6::kMaxDatagramLength - sizeof(Ip6::Udp::Header);
 
     static constexpr uint32_t kDefaultMinLease             = 60u * 30;        // 30 min (in seconds).
     static constexpr uint32_t kDefaultMaxLease             = 3600u * 2;       // 2 hours (in seconds).
diff --git a/src/core/net/tcp6.cpp b/src/core/net/tcp6.cpp
index d41bd45..8fdb407 100644
--- a/src/core/net/tcp6.cpp
+++ b/src/core/net/tcp6.cpp
@@ -41,7 +41,8 @@
 #include "common/code_utils.hpp"
 #include "common/error.hpp"
 #include "common/instance.hpp"
-#include "common/logging.hpp"
+#include "common/locator_getters.hpp"
+#include "common/log.hpp"
 #include "common/random.hpp"
 #include "net/checksum.hpp"
 #include "net/ip6.hpp"
@@ -55,31 +56,47 @@
 using ot::Encoding::BigEndian::HostSwap16;
 using ot::Encoding::BigEndian::HostSwap32;
 
+RegisterLogModule("Tcp");
+
+static_assert(sizeof(struct tcpcb) == sizeof(Tcp::Endpoint::mTcb), "mTcb field in otTcpEndpoint is sized incorrectly");
+static_assert(alignof(struct tcpcb) == alignof(decltype(Tcp::Endpoint::mTcb)),
+              "mTcb field in otTcpEndpoint is aligned incorrectly");
+static_assert(offsetof(Tcp::Endpoint, mTcb) == 0, "mTcb field in otTcpEndpoint has nonzero offset");
+
+static_assert(sizeof(struct tcpcb_listen) == sizeof(Tcp::Listener::mTcbListen),
+              "mTcbListen field in otTcpListener is sized incorrectly");
+static_assert(alignof(struct tcpcb_listen) == alignof(decltype(Tcp::Listener::mTcbListen)),
+              "mTcbListen field in otTcpListener is aligned incorrectly");
+static_assert(offsetof(Tcp::Listener, mTcbListen) == 0, "mTcbListen field in otTcpEndpoint has nonzero offset");
+
 Tcp::Tcp(Instance &aInstance)
     : InstanceLocator(aInstance)
     , mTimer(aInstance, Tcp::HandleTimer)
+    , mTasklet(aInstance, Tcp::HandleTasklet)
     , mEphemeralPort(kDynamicPortMin)
 {
     OT_UNUSED_VARIABLE(mEphemeralPort);
 }
 
-Error Tcp::Endpoint::Initialize(Instance &aInstance, otTcpEndpointInitializeArgs &aArgs)
+Error Tcp::Endpoint::Initialize(Instance &aInstance, const otTcpEndpointInitializeArgs &aArgs)
 {
     Error         error;
     struct tcpcb &tp = GetTcb();
 
+    memset(&tp, 0x00, sizeof(tp));
+
     SuccessOrExit(error = aInstance.Get<Tcp>().mEndpoints.Add(*this));
 
     mContext                  = aArgs.mContext;
     mEstablishedCallback      = aArgs.mEstablishedCallback;
     mSendDoneCallback         = aArgs.mSendDoneCallback;
-    mSendReadyCallback        = aArgs.mSendReadyCallback;
+    mForwardProgressCallback  = aArgs.mForwardProgressCallback;
     mReceiveAvailableCallback = aArgs.mReceiveAvailableCallback;
     mDisconnectedCallback     = aArgs.mDisconnectedCallback;
 
     memset(mTimers, 0x00, sizeof(mTimers));
     memset(&mSockAddr, 0x00, sizeof(mSockAddr));
-    memset(&tp, 0x00, sizeof(tp));
+    mPendingCallbacks = 0;
 
     /*
      * Initialize buffers --- formerly in initialize_tcb.
@@ -173,16 +190,26 @@
 
 Error Tcp::Endpoint::SendByReference(otLinkedBuffer &aBuffer, uint32_t aFlags)
 {
+    Error         error;
     struct tcpcb &tp = GetTcb();
 
-    return BsdErrorToOtError(tcp_usr_send(&tp, (aFlags & OT_TCP_SEND_MORE_TO_COME) != 0, &aBuffer, 0));
+    size_t backlogBefore = GetBacklogBytes();
+    size_t sent          = aBuffer.mLength;
+
+    SuccessOrExit(error = BsdErrorToOtError(tcp_usr_send(&tp, (aFlags & OT_TCP_SEND_MORE_TO_COME) != 0, &aBuffer, 0)));
+
+    PostCallbacksAfterSend(sent, backlogBefore);
+
+exit:
+    return error;
 }
 
 Error Tcp::Endpoint::SendByExtension(size_t aNumBytes, uint32_t aFlags)
 {
     Error         error;
-    bool          moreToCome = (aFlags & OT_TCP_SEND_MORE_TO_COME) != 0;
-    struct tcpcb &tp         = GetTcb();
+    bool          moreToCome    = (aFlags & OT_TCP_SEND_MORE_TO_COME) != 0;
+    struct tcpcb &tp            = GetTcb();
+    size_t        backlogBefore = GetBacklogBytes();
     int           bsdError;
 
     VerifyOrExit(lbuf_head(&tp.sendbuf) != nullptr, error = kErrorInvalidState);
@@ -190,6 +217,8 @@
     bsdError = tcp_usr_send(&tp, moreToCome ? 1 : 0, nullptr, aNumBytes);
     SuccessOrExit(error = BsdErrorToOtError(bsdError));
 
+    PostCallbacksAfterSend(aNumBytes, backlogBefore);
+
 exit:
     return error;
 }
@@ -257,6 +286,11 @@
     return error;
 }
 
+bool Tcp::Endpoint::IsClosed(void) const
+{
+    return GetTcb().t_state == TCP6S_CLOSED;
+}
+
 uint8_t Tcp::Endpoint::TimerFlagToIndex(uint8_t aTimerFlag)
 {
     uint8_t timerIndex = 0;
@@ -319,8 +353,8 @@
     uint8_t   timerIndex  = TimerFlagToIndex(aTimerFlag);
 
     mTimers[timerIndex] = newFireTime.GetValue();
-    otLogDebgTcp("Endpoint %p set timer %u to %u ms", static_cast<void *>(this), static_cast<unsigned int>(timerIndex),
-                 static_cast<unsigned int>(aDelay));
+    LogDebg("Endpoint %p set timer %u to %u ms", static_cast<void *>(this), static_cast<unsigned int>(timerIndex),
+            static_cast<unsigned int>(aDelay));
 
     GetInstance().Get<Tcp>().mTimer.FireAtIfEarlier(newFireTime);
 }
@@ -335,8 +369,8 @@
 
     OT_UNUSED_VARIABLE(aTimerFlag);
 
-    otLogDebgTcp("Endpoint %p cancelled timer %u", static_cast<void *>(this),
-                 static_cast<unsigned int>(TimerFlagToIndex(aTimerFlag)));
+    LogDebg("Endpoint %p cancelled timer %u", static_cast<void *>(this),
+            static_cast<unsigned int>(TimerFlagToIndex(aTimerFlag)));
 }
 
 bool Tcp::Endpoint::FirePendingTimers(TimeMilli aNow, bool &aHasFutureTimer, TimeMilli &aEarliestFutureExpiry)
@@ -410,6 +444,69 @@
     return calledUserCallback;
 }
 
+void Tcp::Endpoint::PostCallbacksAfterSend(size_t aSent, size_t aBacklogBefore)
+{
+    size_t backlogAfter = GetBacklogBytes();
+
+    if (backlogAfter < aBacklogBefore + aSent && mForwardProgressCallback != nullptr)
+    {
+        mPendingCallbacks |= kForwardProgressCallbackFlag;
+        GetInstance().Get<Tcp>().mTasklet.Post();
+    }
+}
+
+bool Tcp::Endpoint::FirePendingCallbacks(void)
+{
+    bool calledUserCallback = false;
+
+    if ((mPendingCallbacks & kForwardProgressCallbackFlag) != 0 && mForwardProgressCallback != nullptr)
+    {
+        mForwardProgressCallback(this, GetSendBufferBytes(), GetBacklogBytes());
+        calledUserCallback = true;
+    }
+
+    mPendingCallbacks = 0;
+
+    return calledUserCallback;
+}
+
+size_t Tcp::Endpoint::GetSendBufferBytes(void) const
+{
+    const struct tcpcb &tp = GetTcb();
+    return lbuf_used_space(&tp.sendbuf);
+}
+
+size_t Tcp::Endpoint::GetInFlightBytes(void) const
+{
+    const struct tcpcb &tp = GetTcb();
+    return tp.snd_max - tp.snd_una;
+}
+
+size_t Tcp::Endpoint::GetBacklogBytes(void) const
+{
+    return GetSendBufferBytes() - GetInFlightBytes();
+}
+
+Address &Tcp::Endpoint::GetLocalIp6Address(void)
+{
+    return *reinterpret_cast<Address *>(&GetTcb().laddr);
+}
+
+const Address &Tcp::Endpoint::GetLocalIp6Address(void) const
+{
+    return *reinterpret_cast<const Address *>(&GetTcb().laddr);
+}
+
+Address &Tcp::Endpoint::GetForeignIp6Address(void)
+{
+    return *reinterpret_cast<Address *>(&GetTcb().faddr);
+}
+
+const Address &Tcp::Endpoint::GetForeignIp6Address(void) const
+{
+    return *reinterpret_cast<const Address *>(&GetTcb().faddr);
+}
+
 bool Tcp::Endpoint::Matches(const MessageInfo &aMessageInfo) const
 {
     bool                matches = false;
@@ -427,7 +524,7 @@
     return matches;
 }
 
-Error Tcp::Listener::Initialize(Instance &aInstance, otTcpListenerInitializeArgs &aArgs)
+Error Tcp::Listener::Initialize(Instance &aInstance, const otTcpListenerInitializeArgs &aArgs)
 {
     Error                error;
     struct tcpcb_listen *tpl = &GetTcbListen();
@@ -492,6 +589,21 @@
     return error;
 }
 
+bool Tcp::Listener::IsClosed(void) const
+{
+    return GetTcbListen().t_state == TCP6S_CLOSED;
+}
+
+Address &Tcp::Listener::GetLocalIp6Address(void)
+{
+    return *reinterpret_cast<Address *>(&GetTcbListen().laddr);
+}
+
+const Address &Tcp::Listener::GetLocalIp6Address(void) const
+{
+    return *reinterpret_cast<const Address *>(&GetTcbListen().laddr);
+}
+
 bool Tcp::Listener::Matches(const MessageInfo &aMessageInfo) const
 {
     bool                       matches = false;
@@ -549,17 +661,18 @@
     endpoint = mEndpoints.FindMatching(aMessageInfo, endpointPrev);
     if (endpoint != nullptr)
     {
-        struct signals sig;
-        int            nextAction;
-        struct tcpcb * tp = &endpoint->GetTcb();
+        struct tcplp_signals sig;
+        int                  nextAction;
+        struct tcpcb *       tp = &endpoint->GetTcb();
 
-        otLinkedBuffer *priorHead = lbuf_head(&tp->sendbuf);
+        otLinkedBuffer *priorHead    = lbuf_head(&tp->sendbuf);
+        size_t          priorBacklog = endpoint->GetSendBufferBytes() - endpoint->GetInFlightBytes();
 
         memset(&sig, 0x00, sizeof(sig));
         nextAction = tcp_input(ip6Header, tcpHeader, &aMessage, tp, nullptr, &sig);
         if (nextAction != RELOOKUP_REQUIRED)
         {
-            ProcessSignals(*endpoint, priorHead, sig);
+            ProcessSignals(*endpoint, priorHead, priorBacklog, sig);
             ExitNow();
         }
         /* If the matching socket was in the TIME-WAIT state, then we try passive sockets. */
@@ -581,14 +694,23 @@
     return error;
 }
 
-void Tcp::ProcessSignals(Endpoint &aEndpoint, otLinkedBuffer *aPriorHead, struct signals &aSignals)
+void Tcp::ProcessSignals(Endpoint &            aEndpoint,
+                         otLinkedBuffer *      aPriorHead,
+                         size_t                aPriorBacklog,
+                         struct tcplp_signals &aSignals)
 {
     VerifyOrExit(IsInitialized(aEndpoint) && !aEndpoint.IsClosed());
+    if (aSignals.conn_established && aEndpoint.mEstablishedCallback != nullptr)
+    {
+        aEndpoint.mEstablishedCallback(&aEndpoint);
+    }
+
+    VerifyOrExit(IsInitialized(aEndpoint) && !aEndpoint.IsClosed());
     if (aEndpoint.mSendDoneCallback != nullptr)
     {
         otLinkedBuffer *curr = aPriorHead;
 
-        for (int i = 0; i != aSignals.links_popped; i++)
+        for (uint32_t i = 0; i != aSignals.links_popped; i++)
         {
             otLinkedBuffer *next = curr->mNext;
 
@@ -601,13 +723,19 @@
     }
 
     VerifyOrExit(IsInitialized(aEndpoint) && !aEndpoint.IsClosed());
-    if (aSignals.conn_established && aEndpoint.mEstablishedCallback != nullptr)
+    if (aEndpoint.mForwardProgressCallback != nullptr)
     {
-        aEndpoint.mEstablishedCallback(&aEndpoint);
+        size_t backlogBytes = aEndpoint.GetBacklogBytes();
+
+        if (aSignals.bytes_acked > 0 || backlogBytes < aPriorBacklog)
+        {
+            aEndpoint.mForwardProgressCallback(&aEndpoint, aEndpoint.GetSendBufferBytes(), backlogBytes);
+            aEndpoint.mPendingCallbacks &= ~kForwardProgressCallbackFlag;
+        }
     }
 
     VerifyOrExit(IsInitialized(aEndpoint) && !aEndpoint.IsClosed());
-    if ((aSignals.recvbuf_notempty || aSignals.rcvd_fin) && aEndpoint.mReceiveAvailableCallback != nullptr)
+    if ((aSignals.recvbuf_added || aSignals.rcvd_fin) && aEndpoint.mReceiveAvailableCallback != nullptr)
     {
         aEndpoint.mReceiveAvailableCallback(&aEndpoint, cbuf_used_space(&aEndpoint.GetTcb().recvbuf),
                                             aEndpoint.GetTcb().reass_fin_index != -1,
@@ -643,21 +771,21 @@
     uint16_t port    = HostSwap16(aSockName.mPort);
     bool     allowed = false;
 
-    for (Endpoint *endpoint = mEndpoints.GetHead(); endpoint != nullptr; endpoint = endpoint->GetNext())
+    for (Endpoint &endpoint : mEndpoints)
     {
-        struct tcpcb *tp = &endpoint->GetTcb();
+        struct tcpcb *tp = &endpoint.GetTcb();
 
         if (tp->lport == port)
         {
             VerifyOrExit(!aSockName.GetAddress().IsUnspecified());
             VerifyOrExit(!reinterpret_cast<Address *>(&tp->laddr)->IsUnspecified());
-            VerifyOrExit(memcmp(&endpoint->GetTcb().laddr, &aSockName.mAddress, sizeof(tp->laddr)) != 0);
+            VerifyOrExit(memcmp(&endpoint.GetTcb().laddr, &aSockName.mAddress, sizeof(tp->laddr)) != 0);
         }
     }
 
-    for (Listener *listener = mListeners.GetHead(); listener != nullptr; listener = listener->GetNext())
+    for (Listener &listener : mListeners)
     {
-        struct tcpcb_listen *tpl = &listener->GetTcbListen();
+        struct tcpcb_listen *tpl = &listener.GetTcbListen();
 
         if (tpl->lport == port)
         {
@@ -727,7 +855,7 @@
 void Tcp::HandleTimer(Timer &aTimer)
 {
     OT_ASSERT(&aTimer == &aTimer.GetInstance().Get<Tcp>().mTimer);
-    otLogDebgTcp("Main TCP timer expired");
+    LogDebg("Main TCP timer expired");
     aTimer.GetInstance().Get<Tcp>().ProcessTimers();
 }
 
@@ -785,11 +913,30 @@
          * timers.
          */
         mTimer.FireAtIfEarlier(earliestPendingTimerExpiry);
-        otLogDebgTcp("Reset main TCP timer to %u ms", static_cast<unsigned int>(earliestPendingTimerExpiry - now));
+        LogDebg("Reset main TCP timer to %u ms", static_cast<unsigned int>(earliestPendingTimerExpiry - now));
     }
     else
     {
-        otLogDebgTcp("Did not reset main TCP timer");
+        LogDebg("Did not reset main TCP timer");
+    }
+}
+
+void Tcp::HandleTasklet(Tasklet &aTasklet)
+{
+    OT_ASSERT(&aTasklet == &aTasklet.Get<Tcp>().mTasklet);
+    LogDebg("TCP tasklet invoked");
+    aTasklet.Get<Tcp>().ProcessCallbacks();
+}
+
+void Tcp::ProcessCallbacks(void)
+{
+    for (Endpoint &endpoint : mEndpoints)
+    {
+        if (endpoint.FirePendingCallbacks())
+        {
+            mTasklet.Post();
+            break;
+        }
     }
 }
 
@@ -837,7 +984,7 @@
     Message &    message  = AsCoreType(aMessage);
     MessageInfo &info     = AsCoreType(aMessageInfo);
 
-    otLogDebgTcp("Sending TCP segment: payload_size = %d", static_cast<int>(message.GetLength()));
+    LogDebg("Sending TCP segment: payload_size = %d", static_cast<int>(message.GetLength()));
 
     IgnoreError(instance.Get<ot::Ip6::Ip6>().SendDatagram(message, info, kProtoTcp));
 }
@@ -986,7 +1133,7 @@
     vsnprintf(buffer, sizeof(buffer), aFormat, args);
     va_end(args);
 
-    otLogDebgTcp(buffer);
+    LogDebg(buffer);
 }
 
 void tcplp_sys_panic(const char *aFormat, ...)
@@ -997,7 +1144,7 @@
     vsnprintf(buffer, sizeof(buffer), aFormat, args);
     va_end(args);
 
-    otLogCritTcp(buffer);
+    LogCrit("%s", buffer);
 
     OT_ASSERT(false);
 }
diff --git a/src/core/net/tcp6.hpp b/src/core/net/tcp6.hpp
index 9cd6662..ef827a3 100644
--- a/src/core/net/tcp6.hpp
+++ b/src/core/net/tcp6.hpp
@@ -39,6 +39,7 @@
 #include <openthread/tcp.h>
 
 #include "common/as_core_type.hpp"
+#include "common/clearable.hpp"
 #include "common/linked_list.hpp"
 #include "common/locator.hpp"
 #include "common/non_copyable.hpp"
@@ -46,7 +47,17 @@
 #include "net/ip6_headers.hpp"
 #include "net/socket.hpp"
 
-#include "../../third_party/tcplp/tcplp.h"
+/*
+ * These structures and functions are forward-declared here to avoid
+ * #includ'ing third_party/tcplp/tcplp.h in this header file.
+ */
+extern "C" {
+struct tcpcb;
+struct tcpcb_listen;
+struct tcplp_signals;
+void tcplp_sys_set_timer(struct tcpcb *aTcb, uint8_t aTimerFlag, uint32_t aDelay);
+void tcplp_sys_stop_timer(struct tcpcb *aTcb, uint8_t aTimerFlag);
+}
 
 namespace ot {
 namespace Ip6 {
@@ -96,7 +107,7 @@
          * @retval kErrorFailed  Failed to open the TCP endpoint.
          *
          */
-        Error Initialize(Instance &aInstance, otTcpEndpointInitializeArgs &aArgs);
+        Error Initialize(Instance &aInstance, const otTcpEndpointInitializeArgs &aArgs);
 
         /**
          * Obtains the Instance that was associated with this Endpoint upon
@@ -351,12 +362,11 @@
         /**
          * Checks if this Endpoint is in the closed state.
          */
-        bool IsClosed(void) const { return GetTcb().t_state == TCP6S_CLOSED; }
+        bool IsClosed(void) const;
 
     private:
         friend void ::tcplp_sys_set_timer(struct tcpcb *aTcb, uint8_t aTimerFlag, uint32_t aDelay);
         friend void ::tcplp_sys_stop_timer(struct tcpcb *aTcb, uint8_t aTimerFlag);
-        friend void ::tcplp_sys_connection_lost(struct tcpcb *aTcb, uint8_t aErrNum);
 
         enum : uint8_t
         {
@@ -374,18 +384,20 @@
         void CancelTimer(uint8_t aTimerFlag);
         bool FirePendingTimers(TimeMilli aNow, bool &aHasFutureTimer, TimeMilli &aEarliestFutureExpiry);
 
-        Address &      GetLocalIp6Address(void) { return *reinterpret_cast<Address *>(&GetTcb().laddr); }
-        const Address &GetLocalIp6Address(void) const { return *reinterpret_cast<const Address *>(&GetTcb().laddr); }
-        Address &      GetForeignIp6Address(void) { return *reinterpret_cast<Address *>(&GetTcb().faddr); }
-        const Address &GetForeignIp6Address(void) const { return *reinterpret_cast<const Address *>(&GetTcb().faddr); }
+        void PostCallbacksAfterSend(size_t aSent, size_t aBacklogBefore);
+        bool FirePendingCallbacks(void);
+
+        size_t GetSendBufferBytes(void) const;
+        size_t GetInFlightBytes(void) const;
+        size_t GetBacklogBytes(void) const;
+
+        Address &      GetLocalIp6Address(void);
+        const Address &GetLocalIp6Address(void) const;
+        Address &      GetForeignIp6Address(void);
+        const Address &GetForeignIp6Address(void) const;
         bool           Matches(const MessageInfo &aMessageInfo) const;
     };
 
-    static_assert(sizeof(struct tcpcb) == sizeof(Endpoint::mTcb), "mTcb field in otTcpEndpoint is sized incorrectly");
-    static_assert(alignof(struct tcpcb) == alignof(decltype(Endpoint::mTcb)),
-                  "mTcb field in otTcpEndpoint is aligned incorrectly");
-    static_assert(offsetof(Endpoint, mTcb) == 0, "mTcb field in otTcpEndpoint has nonzero offset");
-
     /**
      * This class represents a TCP/IPv6 listener.
      *
@@ -413,7 +425,7 @@
          * @retval kErrorFailed  Failed to open the TCP listener.
          *
          */
-        Error Initialize(Instance &aInstance, otTcpListenerInitializeArgs &aArgs);
+        Error Initialize(Instance &aInstance, const otTcpListenerInitializeArgs &aArgs);
 
         /**
          * Obtains the otInstance that was associated with this Listener upon
@@ -505,29 +517,20 @@
         /**
          * Checks if this Listener is in the closed state.
          */
-        bool IsClosed(void) const { return GetTcbListen().t_state == TCP6S_CLOSED; }
+        bool IsClosed(void) const;
 
     private:
-        Address &      GetLocalIp6Address(void) { return *reinterpret_cast<Address *>(&GetTcbListen().laddr); }
-        const Address &GetLocalIp6Address(void) const
-        {
-            return *reinterpret_cast<const Address *>(&GetTcbListen().laddr);
-        }
-        bool Matches(const MessageInfo &aMessageInfo) const;
+        Address &      GetLocalIp6Address(void);
+        const Address &GetLocalIp6Address(void) const;
+        bool           Matches(const MessageInfo &aMessageInfo) const;
     };
 
-    static_assert(sizeof(struct tcpcb_listen) == sizeof(Listener::mTcbListen),
-                  "mTcbListen field in otTcpListener is sized incorrectly");
-    static_assert(alignof(struct tcpcb_listen) == alignof(decltype(Listener::mTcbListen)),
-                  "mTcbListen field in otTcpListener is aligned incorrectly");
-    static_assert(offsetof(Listener, mTcbListen) == 0, "mTcbListen field in otTcpEndpoint has nonzero offset");
-
     /**
      * This class implements TCP header parsing.
      *
      */
     OT_TOOL_PACKED_BEGIN
-    class Header
+    class Header : public Clearable<Header>
     {
     public:
         static constexpr uint8_t kChecksumFieldOffset = 16; ///< Byte offset of the Checksum field in the TCP header.
@@ -660,7 +663,16 @@
         kDynamicPortMax = 65535, ///< Service Name and Transport Protocol Port Number Registry
     };
 
-    void ProcessSignals(Endpoint &aEndpoint, otLinkedBuffer *aPriorHead, struct signals &aSignals);
+    static constexpr uint8_t kEstablishedCallbackFlag      = (1 << 0);
+    static constexpr uint8_t kSendDoneCallbackFlag         = (1 << 1);
+    static constexpr uint8_t kForwardProgressCallbackFlag  = (1 << 2);
+    static constexpr uint8_t kReceiveAvailableCallbackFlag = (1 << 3);
+    static constexpr uint8_t kDisconnectedCallbackFlag     = (1 << 4);
+
+    void ProcessSignals(Endpoint &            aEndpoint,
+                        otLinkedBuffer *      aPriorHead,
+                        size_t                aPriorBacklog,
+                        struct tcplp_signals &aSignals);
 
     static Error BsdErrorToOtError(int aBsdError);
     bool         CanBind(const SockAddr &aSockName);
@@ -668,7 +680,11 @@
     static void HandleTimer(Timer &aTimer);
     void        ProcessTimers(void);
 
+    static void HandleTasklet(Tasklet &aTasklet);
+    void        ProcessCallbacks(void);
+
     TimerMilli mTimer;
+    Tasklet    mTasklet;
 
     LinkedList<Endpoint> mEndpoints;
     LinkedList<Listener> mListeners;
diff --git a/src/core/net/udp6.hpp b/src/core/net/udp6.hpp
index d7ea7dc..6471c4b 100644
--- a/src/core/net/udp6.hpp
+++ b/src/core/net/udp6.hpp
@@ -306,8 +306,8 @@
         /**
          * This constructor initializes the UDP receiver.
          *
-         * @param[in]   aUdpHandler     A pointer to the function to handle UDP message.
-         * @param[in]   aContext        A pointer to arbitrary context information.
+         * @param[in]   aHandler     A pointer to the function to handle UDP message.
+         * @param[in]   aContext     A pointer to arbitrary context information.
          *
          */
         Receiver(otUdpHandler aHandler, void *aContext)
@@ -329,7 +329,7 @@
      *
      */
     OT_TOOL_PACKED_BEGIN
-    class Header
+    class Header : public Clearable<Header>
     {
     public:
         static constexpr uint16_t kSourcePortFieldOffset = 0; ///< Byte offset of Source Port field in UDP header.
diff --git a/src/core/openthread-core-config.h b/src/core/openthread-core-config.h
index 09d5810..23d7015 100644
--- a/src/core/openthread-core-config.h
+++ b/src/core/openthread-core-config.h
@@ -54,8 +54,6 @@
 #define OPENTHREAD_CONFIG_THREAD_VERSION OT_THREAD_VERSION_1_2
 #endif
 
-#include "config/openthread-core-default-config.h"
-
 #include "config/announce_sender.h"
 #include "config/backbone_router.h"
 #include "config/border_router.h"
@@ -80,6 +78,7 @@
 #include "config/link_raw.h"
 #include "config/logging.h"
 #include "config/mac.h"
+#include "config/misc.h"
 #include "config/mle.h"
 #include "config/netdata_publisher.h"
 #include "config/parent_search.h"
diff --git a/src/core/radio/radio.hpp b/src/core/radio/radio.hpp
index 7947ae0..2e2ef88 100644
--- a/src/core/radio/radio.hpp
+++ b/src/core/radio/radio.hpp
@@ -81,6 +81,7 @@
     friend class Instance;
 
 public:
+    static constexpr uint32_t kSymbolTime = OT_RADIO_SYMBOL_TIME;
 #if (OPENTHREAD_CONFIG_RADIO_2P4GHZ_OQPSK_SUPPORT && OPENTHREAD_CONFIG_RADIO_915MHZ_OQPSK_SUPPORT)
     static constexpr uint16_t kNumChannelPages = 2;
     static constexpr uint32_t kSupportedChannels =
@@ -162,8 +163,7 @@
          * This method is used when radio provides OT_RADIO_CAPS_ENERGY_SCAN capability. It is called from
          * `otPlatRadioEnergyScanDone()`.
          *
-         * @param[in]  aInstance           The OpenThread instance structure.
-         * @param[in]  aEnergyScanMaxRssi  The maximum RSSI encountered on the scanned channel.
+         * @param[in]  aMaxRssi  The maximum RSSI encountered on the scanned channel.
          *
          */
         void HandleEnergyScanDone(int8_t aMaxRssi);
@@ -475,8 +475,6 @@
     /**
      * Get the current uncertainty, in units of 10 us, of the clock used for scheduling CSL operations.
      *
-     * @param[in]   aInstance    A pointer to an OpenThread instance.
-     *
      * @returns The current CSL Clock Uncertainty in units of 10 us.
      *
      */
@@ -629,12 +627,11 @@
      * starts/stops to collect Link Metrics data and include Vendor-Specific IE that containing the data
      * in Enhanced-ACK sent to that Probing Initiator.
      *
-     * @param[in]  aInstance    The OpenThread instance structure.
-     * @param[in]  aDataLength  Length of Link Metrics data in the Vendor-Specific IE. Per spec 4.11.3.4.4.6,
-     *                          @p aDataLength should only be 1 or 2. The probing would be disabled if `aDataLength` is
-     *                          `0`.
-     * @param[in]  aShortAddr   The short address of the the probing Initiator.
-     * @param[in]  aExtAddr     The extended source address of the probing Initiator.
+     * @param[in]  aLinkMetrics  This parameter specifies what metrics to query. Per spec 4.11.3.4.4.6, at most 2
+     *                           metrics can be specified. The probing would be disabled if @p `aLinkMetrics` is
+     *                           bitwise 0.
+     * @param[in]  aShortAddress The short address of the the probing Initiator.
+     * @param[in]  aExtAddress   The extended source address of the probing Initiator.
      *
      * @retval kErrorNone            Successfully enable/disable or update Enhanced-ACK Based Probing for a specific
      *                               Initiator.
diff --git a/src/core/radio/trel_interface.cpp b/src/core/radio/trel_interface.cpp
index a2cf1a5..68825e9 100644
--- a/src/core/radio/trel_interface.cpp
+++ b/src/core/radio/trel_interface.cpp
@@ -36,19 +36,21 @@
 
 #include <string.h>
 
+#include "common/array.hpp"
 #include "common/as_core_type.hpp"
 #include "common/code_utils.hpp"
 #include "common/debug.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "common/string.hpp"
 #include "net/dns_types.hpp"
-#include "utils/parse_cmdline.hpp"
 
 namespace ot {
 namespace Trel {
 
+RegisterLogModule("TrelInterface");
+
 const char Interface::kTxtRecordExtAddressKey[] = "xa";
 const char Interface::kTxtRecordExtPanIdKey[]   = "xp";
 
@@ -83,7 +85,7 @@
 
     otPlatTrelEnable(&GetInstance(), &mUdpPort);
 
-    otLogInfoMac("Trel: Enabled interface, local port:%u", mUdpPort);
+    LogInfo("Enabled interface, local port:%u", mUdpPort);
     mRegisterServiceTask.Post();
 
 exit:
@@ -99,7 +101,7 @@
 
     otPlatTrelDisable(&GetInstance());
     mPeerTable.Clear();
-    otLogDebgMac("Trel: Disabled interface");
+    LogDebg("Disabled interface");
 
 exit:
     return;
@@ -108,7 +110,7 @@
 void Interface::HandleExtAddressChange(void)
 {
     VerifyOrExit(mInitialized && mEnabled);
-    otLogDebgMac("Trel: Extended Address changed, re-registering DNS-SD service");
+    LogDebg("Extended Address changed, re-registering DNS-SD service");
     mRegisterServiceTask.Post();
 
 exit:
@@ -118,7 +120,7 @@
 void Interface::HandleExtPanIdChange(void)
 {
     VerifyOrExit(mInitialized && mEnabled);
-    otLogDebgMac("Trel: Extended PAN ID changed, re-registering DNS-SD service");
+    LogDebg("Extended PAN ID changed, re-registering DNS-SD service");
     mRegisterServiceTask.Post();
 
 exit:
@@ -132,41 +134,32 @@
 
 void Interface::RegisterService(void)
 {
-    // TXT data consists of two entries: the length fields , the
-    // "key" string, "=" char, and hex representation of the MAC or
-    // Extended PAN ID values.
+    // TXT data consists of two entries: the length fields, the
+    // "key" string, "=" char, and binary representation of the MAC
+    // or Extended PAN ID values.
     static constexpr uint8_t kTxtDataSize =
-        sizeof(uint8_t) + sizeof(kTxtRecordExtAddressKey) - 1 + sizeof(char) + sizeof(Mac::ExtAddress) * 2 +
-        sizeof(uint8_t) + sizeof(kTxtRecordExtPanIdKey) - 1 + sizeof(char) + sizeof(Mac::ExtendedPanId) * 2;
+        /* ExtAddr  */ sizeof(uint8_t) + sizeof(kTxtRecordExtAddressKey) - 1 + sizeof(char) + sizeof(Mac::ExtAddress) +
+        /* ExtPanId */ sizeof(uint8_t) + sizeof(kTxtRecordExtPanIdKey) - 1 + sizeof(char) +
+        sizeof(MeshCoP::ExtendedPanId);
 
-    uint8_t              txtData[kTxtDataSize];
-    uint8_t *            txtPtr = &txtData[0];
-    String<kTxtDataSize> string;
+    uint8_t                        txtDataBuffer[kTxtDataSize];
+    MutableData<kWithUint16Length> txtData;
+    Dns::TxtEntry                  txtEntries[2];
 
     VerifyOrExit(mInitialized && mEnabled);
 
-    string.Append("%s=", kTxtRecordExtAddressKey);
-    string.AppendHexBytes(Get<Mac::Mac>().GetExtAddress().m8, sizeof(Mac::ExtAddress));
+    txtEntries[0].Init(kTxtRecordExtAddressKey, Get<Mac::Mac>().GetExtAddress().m8, sizeof(Mac::ExtAddress));
+    txtEntries[1].Init(kTxtRecordExtPanIdKey, Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId().m8,
+                       sizeof(MeshCoP::ExtendedPanId));
 
-    *txtPtr++ = static_cast<uint8_t>(string.GetLength());
-    memcpy(txtPtr, string.AsCString(), string.GetLength());
-    txtPtr += string.GetLength();
+    txtData.Init(txtDataBuffer, sizeof(txtDataBuffer));
+    SuccessOrAssert(Dns::TxtEntry::AppendEntries(txtEntries, GetArrayLength(txtEntries), txtData));
 
-    string.Clear();
-    string.Append("%s=", kTxtRecordExtPanIdKey);
-    string.AppendHexBytes(Get<Mac::Mac>().GetExtendedPanId().m8, sizeof(Mac::ExtendedPanId));
+    LogInfo("Registering DNS-SD service: port:%u, txt:\"%s=%s, %s=%s\"", mUdpPort, kTxtRecordExtAddressKey,
+            Get<Mac::Mac>().GetExtAddress().ToString().AsCString(), kTxtRecordExtPanIdKey,
+            Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId().ToString().AsCString());
 
-    *txtPtr++ = static_cast<uint8_t>(string.GetLength());
-    memcpy(txtPtr, string.AsCString(), string.GetLength());
-    txtPtr += string.GetLength();
-
-    OT_ASSERT(txtPtr == OT_ARRAY_END(txtData));
-
-    otLogInfoMac("Trel: Registering DNS-SD service: port:%u, txt:\"%s=%s, %s=%s\"", mUdpPort, kTxtRecordExtAddressKey,
-                 Get<Mac::Mac>().GetExtAddress().ToString().AsCString(), kTxtRecordExtPanIdKey,
-                 Get<Mac::Mac>().GetExtendedPanId().ToString().AsCString());
-
-    otPlatTrelRegisterService(&GetInstance(), mUdpPort, txtData, sizeof(txtData));
+    otPlatTrelRegisterService(&GetInstance(), mUdpPort, txtData.GetBytes(), static_cast<uint8_t>(txtData.GetLength()));
 
 exit:
     return;
@@ -185,10 +178,10 @@
 
 void Interface::HandleDiscoveredPeerInfo(const Peer::Info &aInfo)
 {
-    Peer *             entry;
-    Mac::ExtAddress    extAddress;
-    Mac::ExtendedPanId extPanId;
-    bool               isNew = false;
+    Peer *                 entry;
+    Mac::ExtAddress        extAddress;
+    MeshCoP::ExtendedPanId extPanId;
+    bool                   isNew = false;
 
     VerifyOrExit(mInitialized && mEnabled);
 
@@ -246,30 +239,9 @@
     return;
 }
 
-template <uint16_t kBufferSize>
-static Error ParseValueAsHexString(const Dns::TxtEntry &aTxtEntry, uint8_t (&aBuffer)[kBufferSize])
-{
-    // Parse the value from `Dns::TxtEntry` as a hex string and
-    // populates the parsed bytes into `aBuffer`. It requires parsing of
-    // the hex string to result in exactly `kBufferSize` decoded bytes.
-
-    Error error = kErrorParse;
-    char  hexString[kBufferSize * 2 + 1];
-
-    VerifyOrExit(aTxtEntry.mValueLength < sizeof(hexString));
-
-    memcpy(hexString, aTxtEntry.mValue, aTxtEntry.mValueLength);
-    hexString[aTxtEntry.mValueLength] = '\0';
-
-    error = Utils::CmdLineParser::ParseAsHexString(hexString, aBuffer);
-
-exit:
-    return error;
-}
-
-Error Interface::ParsePeerInfoTxtData(const Peer::Info &  aInfo,
-                                      Mac::ExtAddress &   aExtAddress,
-                                      Mac::ExtendedPanId &aExtPanId) const
+Error Interface::ParsePeerInfoTxtData(const Peer::Info &      aInfo,
+                                      Mac::ExtAddress &       aExtAddress,
+                                      MeshCoP::ExtendedPanId &aExtPanId) const
 {
     Error                   error;
     Dns::TxtEntry           entry;
@@ -286,13 +258,15 @@
         if (strcmp(entry.mKey, kTxtRecordExtAddressKey) == 0)
         {
             VerifyOrExit(!parsedExtAddress, error = kErrorParse);
-            SuccessOrExit(error = ParseValueAsHexString(entry, aExtAddress.m8));
+            VerifyOrExit(entry.mValueLength == sizeof(Mac::ExtAddress), error = kErrorParse);
+            aExtAddress.Set(entry.mValue);
             parsedExtAddress = true;
         }
         else if (strcmp(entry.mKey, kTxtRecordExtPanIdKey) == 0)
         {
             VerifyOrExit(!parsedExtPanId, error = kErrorParse);
-            SuccessOrExit(error = ParseValueAsHexString(entry, aExtPanId.m8));
+            VerifyOrExit(entry.mValueLength == sizeof(MeshCoP::ExtendedPanId), error = kErrorParse);
+            memcpy(aExtPanId.m8, entry.mValue, sizeof(MeshCoP::ExtendedPanId));
             parsedExtPanId = true;
         }
 
@@ -317,7 +291,7 @@
 
     for (Peer &entry : mPeerTable)
     {
-        if (entry.GetExtPanId() != Get<Mac::Mac>().GetExtendedPanId())
+        if (entry.GetExtPanId() != Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId())
         {
             ExitNow(peerEntry = &entry);
         }
@@ -381,7 +355,7 @@
     case Header::kTypeBroadcast:
         for (Peer &entry : mPeerTable)
         {
-            if (!aIsDiscovery && (entry.GetExtPanId() != Get<Mac::Mac>().GetExtendedPanId()))
+            if (!aIsDiscovery && (entry.GetExtPanId() != Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId()))
             {
                 continue;
             }
@@ -415,7 +389,7 @@
 
 void Interface::HandleReceived(uint8_t *aBuffer, uint16_t aLength)
 {
-    otLogDebgMac("Trel: HandleReceived(aLength:%u)", aLength);
+    LogDebg("HandleReceived(aLength:%u)", aLength);
 
     VerifyOrExit(mInitialized && mEnabled && !mFiltered);
 
@@ -442,8 +416,8 @@
 {
     OT_UNUSED_VARIABLE(aAction);
 
-    otLogInfoMac("Trel: %s peer mac:%s, xpan:%s, %s", aAction, GetExtAddress().ToString().AsCString(),
-                 GetExtPanId().ToString().AsCString(), GetSockAddr().ToString().AsCString());
+    LogInfo("%s peer mac:%s, xpan:%s, %s", aAction, GetExtAddress().ToString().AsCString(),
+            GetExtPanId().ToString().AsCString(), GetSockAddr().ToString().AsCString());
 }
 
 } // namespace Trel
diff --git a/src/core/radio/trel_interface.hpp b/src/core/radio/trel_interface.hpp
index 93a1eab..b80bf1b 100644
--- a/src/core/radio/trel_interface.hpp
+++ b/src/core/radio/trel_interface.hpp
@@ -94,7 +94,10 @@
          * @returns The Extended PAN Identifier of the TREL peer.
          *
          */
-        const Mac::ExtendedPanId &GetExtPanId(void) const { return static_cast<const Mac::ExtendedPanId &>(mExtPanId); }
+        const MeshCoP::ExtendedPanId &GetExtPanId(void) const
+        {
+            return static_cast<const MeshCoP::ExtendedPanId &>(mExtPanId);
+        }
 
         /**
          * This method returns the IPv6 socket address of the discovered TREL peer.
@@ -137,7 +140,7 @@
         };
 
         void SetExtAddress(const Mac::ExtAddress &aExtAddress) { mExtAddress = aExtAddress; }
-        void SetExtPanId(const Mac::ExtendedPanId &aExtPanId) { mExtPanId = aExtPanId; }
+        void SetExtPanId(const MeshCoP::ExtendedPanId &aExtPanId) { mExtPanId = aExtPanId; }
         void SetSockAddr(const Ip6::SockAddr &aSockAddr) { mSockAddr = aSockAddr; }
         void Log(const char *aAction) const;
     };
@@ -241,9 +244,9 @@
 
     static void HandleRegisterServiceTask(Tasklet &aTasklet);
     void        RegisterService(void);
-    Error       ParsePeerInfoTxtData(const Peer::Info &  aInfo,
-                                     Mac::ExtAddress &   aExtAddress,
-                                     Mac::ExtendedPanId &aExtPanId) const;
+    Error       ParsePeerInfoTxtData(const Peer::Info &      aInfo,
+                                     Mac::ExtAddress &       aExtAddress,
+                                     MeshCoP::ExtendedPanId &aExtPanId) const;
     Peer *      GetNewPeerEntry(void);
     void        RemovePeerEntry(Peer &aEntry);
 
diff --git a/src/core/radio/trel_link.cpp b/src/core/radio/trel_link.cpp
index 263c98f..549633b 100644
--- a/src/core/radio/trel_link.cpp
+++ b/src/core/radio/trel_link.cpp
@@ -38,11 +38,13 @@
 #include "common/debug.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 
 namespace ot {
 namespace Trel {
 
+RegisterLogModule("TrelLink");
+
 Link::Link(Instance &aInstance)
     : InstanceLocator(aInstance)
     , mState(kStateDisabled)
@@ -219,8 +221,7 @@
         txPacket.GetHeader().SetDestination(destAddr.GetExtended());
     }
 
-    otLogDebgMac("Trel: BeginTransmit() [%s] plen:%d", txPacket.GetHeader().ToString().AsCString(),
-                 txPacket.GetPayloadLength());
+    LogDebg("BeginTransmit() [%s] plen:%d", txPacket.GetHeader().ToString().AsCString(), txPacket.GetPayloadLength());
 
     VerifyOrExit(mInterface.Send(txPacket, isDisovery) == kErrorNone, InvokeSendDone(kErrorAbort));
 
@@ -372,8 +373,7 @@
         }
     }
 
-    otLogDebgMac("Trel: ReceivedPacket() [%s] plen:%d", aPacket.GetHeader().ToString().AsCString(),
-                 aPacket.GetPayloadLength());
+    LogDebg("ReceivedPacket() [%s] plen:%d", aPacket.GetHeader().ToString().AsCString(), aPacket.GetPayloadLength());
 
     if (aPacket.GetHeader().GetAckMode() == Header::kAckRequested)
     {
@@ -404,7 +404,7 @@
     Neighbor *   neighbor;
     uint32_t     ackNumber;
 
-    otLogDebgMac("Trel: HandleAck() [%s]", aAckPacket.GetHeader().ToString().AsCString());
+    LogDebg("HandleAck() [%s]", aAckPacket.GetHeader().ToString().AsCString());
 
     srcAddress.SetExtended(aAckPacket.GetHeader().GetSource());
     neighbor = Get<NeighborTable>().FindNeighbor(srcAddress, Neighbor::kInStateAnyExceptInvalid);
@@ -450,15 +450,15 @@
     ackPacket.GetHeader().SetSource(Get<Mac::Mac>().GetExtAddress());
     ackPacket.GetHeader().SetDestination(aRxPacket.GetHeader().GetSource());
 
-    otLogDebgMac("Trel: SendAck [%s]", ackPacket.GetHeader().ToString().AsCString());
+    LogDebg("SendAck [%s]", ackPacket.GetHeader().ToString().AsCString());
 
     IgnoreError(mInterface.Send(ackPacket));
 }
 
 void Link::ReportDeferredAckStatus(Neighbor &aNeighbor, Error aError)
 {
-    otLogDebgMac("Trel: ReportDeferredAckStatus(): %s for %s", aNeighbor.GetExtAddress().ToString().AsCString(),
-                 ErrorToString(aError));
+    LogDebg("ReportDeferredAckStatus(): %s for %s", aNeighbor.GetExtAddress().ToString().AsCString(),
+            ErrorToString(aError));
 
     Get<MeshForwarder>().HandleDeferredAck(aNeighbor, aError);
 }
@@ -467,7 +467,7 @@
 {
     if (mState != aState)
     {
-        otLogDebgMac("Trel: State: %s -> %s", StateToString(mState), StateToString(aState));
+        LogDebg("State: %s -> %s", StateToString(mState), StateToString(aState));
         mState = aState;
     }
 }
diff --git a/src/core/radio/trel_packet.cpp b/src/core/radio/trel_packet.cpp
index aaad695..9b5cabd 100644
--- a/src/core/radio/trel_packet.cpp
+++ b/src/core/radio/trel_packet.cpp
@@ -38,7 +38,6 @@
 #include "common/debug.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
 
 namespace ot {
 namespace Trel {
diff --git a/src/core/thread/address_resolver.cpp b/src/core/thread/address_resolver.cpp
index a843e31..4041c01 100644
--- a/src/core/thread/address_resolver.cpp
+++ b/src/core/thread/address_resolver.cpp
@@ -42,7 +42,7 @@
 #include "common/encoding.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "common/time.hpp"
 #include "mac/mac_types.hpp"
 #include "thread/mesh_forwarder.hpp"
@@ -52,6 +52,8 @@
 
 namespace ot {
 
+RegisterLogModule("AddrResolver");
+
 AddressResolver::AddressResolver(Instance &aInstance)
     : InstanceLocator(aInstance)
     , mAddressError(UriPath::kAddressError, &AddressResolver::HandleAddressError, this)
@@ -556,24 +558,18 @@
 {
     Error            error;
     Coap::Message *  message;
-    Ip6::MessageInfo messageInfo;
+    Tmf::MessageInfo messageInfo(GetInstance());
 
-    VerifyOrExit((message = Get<Tmf::Agent>().NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
-
-    message->InitAsNonConfirmablePost();
-    SuccessOrExit(error = message->AppendUriPathOptions(UriPath::kAddressQuery));
-    SuccessOrExit(error = message->SetPayloadMarker());
+    message = Get<Tmf::Agent>().NewPriorityNonConfirmablePostMessage(UriPath::kAddressQuery);
+    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
     SuccessOrExit(error = Tlv::Append<ThreadTargetTlv>(*message, aEid));
 
-    messageInfo.GetPeerAddr().SetToRealmLocalAllRoutersMulticast();
-
-    messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetMeshLocal16());
-    messageInfo.SetPeerPort(Tmf::kUdpPort);
+    messageInfo.SetSockAddrToRlocPeerAddrToRealmLocalAllRoutersMulticast();
 
     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo));
 
-    otLogInfoArp("Sending address query for %s", aEid.ToString().AsCString());
+    LogInfo("Sending address query for %s", aEid.ToString().AsCString());
 
 exit:
 
@@ -585,8 +581,8 @@
     {
         uint16_t selfRloc16 = Get<Mle::MleRouter>().GetRloc16();
 
-        otLogInfoArp("Extending ADDR.qry to BB.qry for target=%s, rloc16=%04x(self)", aEid.ToString().AsCString(),
-                     selfRloc16);
+        LogInfo("Extending ADDR.qry to BB.qry for target=%s, rloc16=%04x(self)", aEid.ToString().AsCString(),
+                selfRloc16);
         IgnoreError(Get<BackboneRouter::Manager>().SendBackboneQuery(aEid, selfRloc16));
     }
 #endif
@@ -627,8 +623,8 @@
         ExitNow();
     }
 
-    otLogInfoArp("Received address notification from 0x%04x for %s to 0x%04x",
-                 aMessageInfo.GetPeerAddr().GetIid().GetLocator(), target.ToString().AsCString(), rloc16);
+    LogInfo("Received address notification from 0x%04x for %s to 0x%04x",
+            aMessageInfo.GetPeerAddr().GetIid().GetLocator(), target.ToString().AsCString(), rloc16);
 
     entry = FindCacheEntry(target, list, prev);
     VerifyOrExit(entry != nullptr);
@@ -659,7 +655,7 @@
 
     if (Get<Tmf::Agent>().SendEmptyAck(aMessage, aMessageInfo) == kErrorNone)
     {
-        otLogInfoArp("Sending address notification acknowledgment");
+        LogInfo("Sending address notification acknowledgment");
     }
 
     Get<MeshForwarder>().HandleResolved(target, kErrorNone);
@@ -674,7 +670,7 @@
 {
     Error            error;
     Coap::Message *  message;
-    Ip6::MessageInfo messageInfo;
+    Tmf::MessageInfo messageInfo(GetInstance());
 
     VerifyOrExit((message = Get<Tmf::Agent>().NewMessage()) != nullptr, error = kErrorNoBufs);
 
@@ -687,26 +683,23 @@
 
     if (aDestination == nullptr)
     {
-        messageInfo.GetPeerAddr().SetToRealmLocalAllRoutersMulticast();
+        messageInfo.SetSockAddrToRlocPeerAddrToRealmLocalAllRoutersMulticast();
     }
     else
     {
-        messageInfo.SetPeerAddr(*aDestination);
+        messageInfo.SetSockAddrToRlocPeerAddrTo(*aDestination);
     }
 
-    messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetMeshLocal16());
-    messageInfo.SetPeerPort(Tmf::kUdpPort);
-
     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo));
 
-    otLogInfoArp("Sending address error for target %s", aTarget.ToString().AsCString());
+    LogInfo("Sending address error for target %s", aTarget.ToString().AsCString());
 
 exit:
 
     if (error != kErrorNone)
     {
         FreeMessage(message);
-        otLogInfoArp("Failed to send address error: %s", ErrorToString(error));
+        LogInfo("Failed to send address error: %s", ErrorToString(error));
     }
 }
 
@@ -725,13 +718,13 @@
 
     VerifyOrExit(aMessage.IsPostRequest(), error = kErrorDrop);
 
-    otLogInfoArp("Received address error notification");
+    LogInfo("Received address error notification");
 
     if (aMessage.IsConfirmable() && !aMessageInfo.GetSockAddr().IsMulticast())
     {
         if (Get<Tmf::Agent>().SendEmptyAck(aMessage, aMessageInfo) == kErrorNone)
         {
-            otLogInfoArp("Sent address error notification acknowledgment");
+            LogInfo("Sent address error notification acknowledgment");
         }
     }
 
@@ -786,7 +779,7 @@
 
     if (error != kErrorNone)
     {
-        otLogWarnArp("Error while processing address error notification: %s", ErrorToString(error));
+        LogWarn("Error while processing address error notification: %s", ErrorToString(error));
     }
 }
 
@@ -804,8 +797,8 @@
 
     SuccessOrExit(Tlv::Find<ThreadTargetTlv>(aMessage, target));
 
-    otLogInfoArp("Received address query from 0x%04x for target %s", aMessageInfo.GetPeerAddr().GetIid().GetLocator(),
-                 target.ToString().AsCString());
+    LogInfo("Received address query from 0x%04x for target %s", aMessageInfo.GetPeerAddr().GetIid().GetLocator(),
+            target.ToString().AsCString());
 
     if (Get<ThreadNetif>().HasUnicastAddress(target))
     {
@@ -834,8 +827,7 @@
     {
         uint16_t srcRloc16 = aMessageInfo.GetPeerAddr().GetIid().GetLocator();
 
-        otLogInfoArp("Extending ADDR.qry to BB.qry for target=%s, rloc16=%04x", target.ToString().AsCString(),
-                     srcRloc16);
+        LogInfo("Extending ADDR.qry to BB.qry for target=%s, rloc16=%04x", target.ToString().AsCString(), srcRloc16);
         IgnoreError(Get<BackboneRouter::Manager>().SendBackboneQuery(target, srcRloc16));
     }
 #endif
@@ -851,13 +843,10 @@
 {
     Error            error;
     Coap::Message *  message;
-    Ip6::MessageInfo messageInfo;
+    Tmf::MessageInfo messageInfo(GetInstance());
 
-    VerifyOrExit((message = Get<Tmf::Agent>().NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
-
-    message->InitAsConfirmablePost();
-    SuccessOrExit(error = message->AppendUriPathOptions(UriPath::kAddressNotify));
-    SuccessOrExit(error = message->SetPayloadMarker());
+    message = Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(UriPath::kAddressNotify);
+    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
     SuccessOrExit(error = Tlv::Append<ThreadTargetTlv>(*message, aTarget));
     SuccessOrExit(error = Tlv::Append<ThreadMeshLocalEidTlv>(*message, aMeshLocalIid));
@@ -868,13 +857,11 @@
         SuccessOrExit(error = Tlv::Append<ThreadLastTransactionTimeTlv>(*message, *aLastTransactionTime));
     }
 
-    messageInfo.SetPeerAddr(aDestination);
-    messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetMeshLocal16());
-    messageInfo.SetPeerPort(Tmf::kUdpPort);
+    messageInfo.SetSockAddrToRlocPeerAddrTo(aDestination);
 
     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo));
 
-    otLogInfoArp("Sending address notification for target %s", aTarget.ToString().AsCString());
+    LogInfo("Sending address notification for target %s", aTarget.ToString().AsCString());
 
 exit:
     FreeMessageOnError(message, error);
@@ -942,8 +929,8 @@
                 mQueryList.PopAfter(prev);
                 mQueryRetryList.Push(*entry);
 
-                otLogInfoArp("Timed out waiting for address notification for %s, retry: %d",
-                             entry->GetTarget().ToString().AsCString(), entry->GetTimeout());
+                LogInfo("Timed out waiting for address notification for %s, retry: %d",
+                        entry->GetTarget().ToString().AsCString(), entry->GetTimeout());
 
                 Get<MeshForwarder>().HandleResolved(entry->GetTarget(), kErrorDrop);
 
@@ -994,7 +981,7 @@
 
 // LCOV_EXCL_START
 
-#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_NOTE) && (OPENTHREAD_CONFIG_LOG_ARP == 1)
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
 
 void AddressResolver::LogCacheEntryChange(EntryChange       aChange,
                                           Reason            aReason,
@@ -1031,9 +1018,8 @@
     static_assert(6 == kReasonEvictingForNewEntry, "kReasonEvictingForNewEntry value is incorrect");
     static_assert(7 == kReasonRemovingEid, "kReasonRemovingEid value is incorrect");
 
-    otLogNoteArp("Cache entry %s: %s, 0x%04x%s%s - %s", kChangeStrings[aChange],
-                 aEntry.GetTarget().ToString().AsCString(), aEntry.GetRloc16(),
-                 (aList == nullptr) ? "" : ", list:", ListToString(aList), kReasonStrings[aReason]);
+    LogInfo("Cache entry %s: %s, 0x%04x%s%s - %s", kChangeStrings[aChange], aEntry.GetTarget().ToString().AsCString(),
+            aEntry.GetRloc16(), (aList == nullptr) ? "" : ", list:", ListToString(aList), kReasonStrings[aReason]);
 }
 
 const char *AddressResolver::ListToString(const CacheEntryList *aList) const
@@ -1049,13 +1035,13 @@
     return str;
 }
 
-#else // #if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_NOTE) && (OPENTHREAD_CONFIG_LOG_ARP == 1)
+#else // #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
 
 void AddressResolver::LogCacheEntryChange(EntryChange, Reason, const CacheEntry &, CacheEntryList *)
 {
 }
 
-#endif // #if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_NOTE) && (OPENTHREAD_CONFIG_LOG_ARP == 1)
+#endif // #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_NOTE)
 
 // LCOV_EXCL_STOP
 
diff --git a/src/core/thread/address_resolver.hpp b/src/core/thread/address_resolver.hpp
index 5ce68e6..6a177b9 100644
--- a/src/core/thread/address_resolver.hpp
+++ b/src/core/thread/address_resolver.hpp
@@ -96,8 +96,8 @@
     /**
      * This method gets the information about the next EID cache entry (using an iterator).
      *
-     * @param[out]   aInfo       An `EntryInfo` where the EID cache entry information is placed.
-     * @param[inout] aIterator   An iterator. It will be updated to point to the next entry on success.
+     * @param[out]    aInfo      An `EntryInfo` where the EID cache entry information is placed.
+     * @param[in,out] aIterator  An iterator. It will be updated to point to the next entry on success.
      *                           To get the first entry, initialize the iterator by setting all its fields to zero.
      *                           e.g., `memset` the the iterator structure to zero.
      *
diff --git a/src/core/thread/announce_begin_server.cpp b/src/core/thread/announce_begin_server.cpp
index b23713e..ddac782 100644
--- a/src/core/thread/announce_begin_server.cpp
+++ b/src/core/thread/announce_begin_server.cpp
@@ -41,13 +41,15 @@
 #include "common/debug.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "meshcop/meshcop_tlvs.hpp"
 #include "thread/thread_netif.hpp"
 #include "thread/uri_paths.hpp"
 
 namespace ot {
 
+RegisterLogModule("MeshCoP");
+
 AnnounceBeginServer::AnnounceBeginServer(Instance &aInstance)
     : AnnounceSenderBase(aInstance, AnnounceBeginServer::HandleTimer)
     , mAnnounceBegin(UriPath::kAnnounceBegin, &AnnounceBeginServer::HandleRequest, this)
@@ -86,7 +88,7 @@
     if (aMessage.IsConfirmable() && !aMessageInfo.GetSockAddr().IsMulticast())
     {
         SuccessOrExit(Get<Tmf::Agent>().SendEmptyAck(aMessage, responseInfo));
-        otLogInfoMeshCoP("sent announce begin response");
+        LogInfo("Sent announce begin response");
     }
 
 exit:
diff --git a/src/core/thread/announce_sender.cpp b/src/core/thread/announce_sender.cpp
index d088896..c500576 100644
--- a/src/core/thread/announce_sender.cpp
+++ b/src/core/thread/announce_sender.cpp
@@ -38,7 +38,7 @@
 #include "common/code_utils.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "common/random.hpp"
 #include "meshcop/meshcop.hpp"
 #include "meshcop/meshcop_tlvs.hpp"
@@ -46,6 +46,8 @@
 
 namespace ot {
 
+RegisterLogModule("AnnounceSender");
+
 //---------------------------------------------------------------------------------------------------------------------
 // AnnounceSenderBase
 
@@ -168,7 +170,7 @@
 {
     AnnounceSenderBase::Stop();
     mTrickleTimer.Stop();
-    otLogInfoMle("[announce-sender] Stopped");
+    LogInfo("Stopped");
 }
 
 void AnnounceSender::HandleTimer(Timer &aTimer)
@@ -191,7 +193,7 @@
     // message transmissions.
 
     SendAnnounce(1);
-    otLogInfoMle("[announce-sender] Schedule tx for one cycle");
+    LogInfo("Schedule tx for one cycle");
 }
 
 void AnnounceSender::HandleNotifierEvents(Events aEvents)
@@ -240,7 +242,7 @@
     // desired Announce Tx cycle interval.
 
     mTrickleTimer.Start(TrickleTimer::kModeTrickle, kInterval, kInterval, kRedundancyConstant);
-    otLogInfoMle("[announce-sender] Started");
+    LogInfo("Started");
 
 exit:
     return;
@@ -250,14 +252,14 @@
 {
     Mac::ChannelMask channelMask;
 
-    SuccessOrExit(Get<MeshCoP::ActiveDataset>().GetChannelMask(channelMask));
+    SuccessOrExit(Get<MeshCoP::ActiveDatasetManager>().GetChannelMask(channelMask));
     VerifyOrExit(!channelMask.IsEmpty());
 
     VerifyOrExit(channelMask != GetChannelMask());
 
     SetChannelMask(channelMask);
     SetPeriod(kTxInterval / channelMask.GetNumberOfChannels());
-    otLogInfoMle("[announce-sender] ChannelMask:%s, period:%u", GetChannelMask().ToString().AsCString(), GetPeriod());
+    LogInfo("ChannelMask:%s, period:%u", GetChannelMask().ToString().AsCString(), GetPeriod());
 
     // When channel mask is changed, we also check and update the PAN
     // channel. This handles the case where `ThreadChannelChanged` event
@@ -273,7 +275,7 @@
 void AnnounceSender::HandleThreadChannelChanged(void)
 {
     SetStartingChannel(Get<Mac::Mac>().GetPanChannel());
-    otLogInfoMle("[announce-sender] StartingChannel:%d", GetStartingChannel());
+    LogInfo("StartingChannel:%d", GetStartingChannel());
 }
 
 #endif // OPENTHREAD_CONFIG_ANNOUNCE_SENDER_ENABLE
diff --git a/src/core/thread/anycast_locator.cpp b/src/core/thread/anycast_locator.cpp
index fc703d2..a8989cd 100644
--- a/src/core/thread/anycast_locator.cpp
+++ b/src/core/thread/anycast_locator.cpp
@@ -62,25 +62,20 @@
 {
     Error            error   = kErrorNone;
     Coap::Message *  message = nullptr;
-    Ip6::MessageInfo messageInfo;
+    Tmf::MessageInfo messageInfo(GetInstance());
 
     VerifyOrExit((aCallback != nullptr) && Get<Mle::Mle>().IsAnycastLocator(aAnycastAddress),
                  error = kErrorInvalidArgs);
 
-    message = Get<Tmf::Agent>().NewMessage();
+    message = Get<Tmf::Agent>().NewConfirmablePostMessage(UriPath::kAnycastLocate);
     VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
-    SuccessOrExit(error = message->InitAsConfirmablePost(UriPath::kAnycastLocate));
-    SuccessOrExit(error = message->SetPayloadMarker());
-
     if (mCallback != nullptr)
     {
         IgnoreError(Get<Tmf::Agent>().AbortTransaction(HandleResponse, this));
     }
 
-    messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetMeshLocal16());
-    messageInfo.SetPeerAddr(aAnycastAddress);
-    messageInfo.SetPeerPort(Tmf::kUdpPort);
+    messageInfo.SetSockAddrToRlocPeerAddrTo(aAnycastAddress);
 
     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo, HandleResponse, this));
 
@@ -145,12 +140,9 @@
 
     VerifyOrExit(aRequest.IsConfirmablePostRequest());
 
-    message = Get<Tmf::Agent>().NewMessage();
+    message = Get<Tmf::Agent>().NewResponseMessage(aRequest);
     VerifyOrExit(message != nullptr);
 
-    SuccessOrExit(message->SetDefaultResponseHeader(aRequest));
-    SuccessOrExit(message->SetPayloadMarker());
-
     SuccessOrExit(Tlv::Append<ThreadMeshLocalEidTlv>(*message, Get<Mle::Mle>().GetMeshLocal64().GetIid()));
     SuccessOrExit(Tlv::Append<ThreadRloc16Tlv>(*message, Get<Mle::Mle>().GetRloc16()));
 
diff --git a/src/core/thread/child_table.hpp b/src/core/thread/child_table.hpp
index 70a49ce..93cdfe0 100644
--- a/src/core/thread/child_table.hpp
+++ b/src/core/thread/child_table.hpp
@@ -276,7 +276,7 @@
     /**
      * This method removes a stored child information from non-volatile memory.
      *
-     * @param[in]  aChildRloc16     A reference to the child to remove from non-volatile memory.
+     * @param[in]  aChild     A reference to the child to remove from non-volatile memory.
      *
      */
     void RemoveStoredChild(const Child &aChild);
diff --git a/src/core/thread/csl_tx_scheduler.cpp b/src/core/thread/csl_tx_scheduler.cpp
index 5347fd7..15beb59 100644
--- a/src/core/thread/csl_tx_scheduler.cpp
+++ b/src/core/thread/csl_tx_scheduler.cpp
@@ -31,12 +31,14 @@
 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
 
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "common/time.hpp"
 #include "mac/mac.hpp"
 
 namespace ot {
 
+RegisterLogModule("CslTxScheduler");
+
 CslTxScheduler::Callbacks::Callbacks(Instance &aInstance)
     : InstanceLocator(aInstance)
 {
@@ -253,8 +255,8 @@
         OT_ASSERT(!aFrame.GetSecurityEnabled() || aFrame.IsHeaderUpdated());
 
         aChild.IncrementCslTxAttempts();
-        otLogInfoMac("CSL tx to child %04x failed, attempt %d/%d", aChild.GetRloc16(), aChild.GetCslTxAttempts(),
-                     kMaxCslTriggeredTxAttempts);
+        LogInfo("CSL tx to child %04x failed, attempt %d/%d", aChild.GetRloc16(), aChild.GetCslTxAttempts(),
+                kMaxCslTriggeredTxAttempts);
 
         if (aChild.GetCslTxAttempts() >= kMaxCslTriggeredTxAttempts)
         {
diff --git a/src/core/thread/discover_scanner.cpp b/src/core/thread/discover_scanner.cpp
index 3a89422..e652afe 100644
--- a/src/core/thread/discover_scanner.cpp
+++ b/src/core/thread/discover_scanner.cpp
@@ -37,7 +37,6 @@
 #include "common/code_utils.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
 #include "thread/mesh_forwarder.hpp"
 #include "thread/mle.hpp"
 #include "thread/mle_router.hpp"
@@ -106,10 +105,8 @@
         mScanChannels.Intersect(aScanChannels);
     }
 
-    VerifyOrExit((message = Get<Mle>().NewMleMessage()) != nullptr, error = kErrorNoBufs);
-    message->SetSubType(Message::kSubTypeMleDiscoverRequest);
+    VerifyOrExit((message = Get<Mle>().NewMleMessage(Mle::kCommandDiscoveryRequest)) != nullptr, error = kErrorNoBufs);
     message->SetPanId(aPanId);
-    SuccessOrExit(error = Get<Mle>().AppendHeader(*message, Mle::kCommandDiscoveryRequest));
 
     // Prepare sub-TLV MeshCoP Discovery Request.
     discoveryRequest.Init();
@@ -286,10 +283,10 @@
     return;
 }
 
-void DiscoverScanner::HandleDiscoveryResponse(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo) const
+void DiscoverScanner::HandleDiscoveryResponse(Mle::RxInfo &aRxInfo) const
 {
     Error                         error    = kErrorNone;
-    const ThreadLinkInfo *        linkInfo = aMessageInfo.GetThreadLinkInfo();
+    const ThreadLinkInfo *        linkInfo = aRxInfo.mMessageInfo.GetThreadLinkInfo();
     Tlv                           tlv;
     MeshCoP::Tlv                  meshcopTlv;
     MeshCoP::DiscoveryResponseTlv discoveryResponse;
@@ -299,46 +296,47 @@
     uint16_t                      end;
     bool                          didCheckSteeringData = false;
 
-    Mle::Log(Mle::kMessageReceive, Mle::kTypeDiscoveryResponse, aMessageInfo.GetPeerAddr());
+    Mle::Log(Mle::kMessageReceive, Mle::kTypeDiscoveryResponse, aRxInfo.mMessageInfo.GetPeerAddr());
 
     VerifyOrExit(mState == kStateScanning, error = kErrorDrop);
 
     // Find MLE Discovery TLV
-    VerifyOrExit(Tlv::FindTlvOffset(aMessage, Tlv::kDiscovery, offset) == kErrorNone, error = kErrorParse);
-    IgnoreError(aMessage.Read(offset, tlv));
+    VerifyOrExit(Tlv::FindTlvOffset(aRxInfo.mMessage, Tlv::kDiscovery, offset) == kErrorNone, error = kErrorParse);
+    IgnoreError(aRxInfo.mMessage.Read(offset, tlv));
 
     offset += sizeof(tlv);
     end = offset + tlv.GetLength();
 
     memset(&result, 0, sizeof(result));
-    result.mPanId   = linkInfo->mPanId;
-    result.mChannel = linkInfo->mChannel;
-    result.mRssi    = linkInfo->mRss;
-    result.mLqi     = linkInfo->mLqi;
+    result.mDiscover = true;
+    result.mPanId    = linkInfo->mPanId;
+    result.mChannel  = linkInfo->mChannel;
+    result.mRssi     = linkInfo->mRss;
+    result.mLqi      = linkInfo->mLqi;
 
-    aMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(AsCoreType(&result.mExtAddress));
+    aRxInfo.mMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(AsCoreType(&result.mExtAddress));
 
     // Process MeshCoP TLVs
     while (offset < end)
     {
-        IgnoreError(aMessage.Read(offset, meshcopTlv));
+        IgnoreError(aRxInfo.mMessage.Read(offset, meshcopTlv));
 
         switch (meshcopTlv.GetType())
         {
         case MeshCoP::Tlv::kDiscoveryResponse:
-            IgnoreError(aMessage.Read(offset, discoveryResponse));
+            IgnoreError(aRxInfo.mMessage.Read(offset, discoveryResponse));
             VerifyOrExit(discoveryResponse.IsValid(), error = kErrorParse);
             result.mVersion  = discoveryResponse.GetVersion();
             result.mIsNative = discoveryResponse.IsNativeCommissioner();
             break;
 
         case MeshCoP::Tlv::kExtendedPanId:
-            SuccessOrExit(
-                error = Tlv::Read<MeshCoP::ExtendedPanIdTlv>(aMessage, offset, AsCoreType(&result.mExtendedPanId)));
+            SuccessOrExit(error = Tlv::Read<MeshCoP::ExtendedPanIdTlv>(aRxInfo.mMessage, offset,
+                                                                       AsCoreType(&result.mExtendedPanId)));
             break;
 
         case MeshCoP::Tlv::kNetworkName:
-            IgnoreError(aMessage.Read(offset, networkName));
+            IgnoreError(aRxInfo.mMessage.Read(offset, networkName));
             if (networkName.IsValid())
             {
                 IgnoreError(AsCoreType(&result.mNetworkName).Set(networkName.GetNetworkName()));
@@ -358,7 +356,7 @@
 
                 steeringData.Init(dataLength);
 
-                SuccessOrExit(error = Tlv::ReadTlv(aMessage, offset, steeringData.GetData(), dataLength));
+                SuccessOrExit(error = Tlv::ReadTlv(aRxInfo.mMessage, offset, steeringData.GetData(), dataLength));
 
                 if (mEnableFiltering)
                 {
@@ -370,7 +368,8 @@
             break;
 
         case MeshCoP::Tlv::kJoinerUdpPort:
-            SuccessOrExit(error = Tlv::Read<MeshCoP::JoinerUdpPortTlv>(aMessage, offset, result.mJoinerUdpPort));
+            SuccessOrExit(error =
+                              Tlv::Read<MeshCoP::JoinerUdpPortTlv>(aRxInfo.mMessage, offset, result.mJoinerUdpPort));
             break;
 
         default:
diff --git a/src/core/thread/discover_scanner.hpp b/src/core/thread/discover_scanner.hpp
index bd1822c..061aa2f 100644
--- a/src/core/thread/discover_scanner.hpp
+++ b/src/core/thread/discover_scanner.hpp
@@ -113,7 +113,7 @@
      * @param[in]  aFilterIndexes     A pointer to `FilterIndexes` to use for filtering (when enabled).
      *                                If set to `nullptr`, filter indexes are derived from hash of factory-assigned
      *                                EUI64.
-     * @param[in]  aHandler           A pointer to a function that is called on receiving an MLE Discovery Response.
+     * @param[in]  aCallback          A pointer to a function that is called on receiving an MLE Discovery Response.
      * @param[in]  aContext           A pointer to arbitrary context information.
      *
      * @retval kErrorNone           Successfully started a Thread Discovery Scan.
@@ -167,7 +167,7 @@
     void          Stop(void) { HandleDiscoverComplete(); }
 
     // Methods used from `Mle`
-    void HandleDiscoveryResponse(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo) const;
+    void HandleDiscoveryResponse(Mle::RxInfo &aRxInfo) const;
 
     void        HandleDiscoverComplete(void);
     static void HandleTimer(Timer &aTimer);
diff --git a/src/core/thread/dua_manager.cpp b/src/core/thread/dua_manager.cpp
index 8119a69..108f07a 100644
--- a/src/core/thread/dua_manager.cpp
+++ b/src/core/thread/dua_manager.cpp
@@ -39,7 +39,7 @@
 #include "common/code_utils.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "common/settings.hpp"
 #include "net/ip6_address.hpp"
 #include "thread/mle_types.hpp"
@@ -50,6 +50,8 @@
 
 namespace ot {
 
+RegisterLogModule("DuaManager");
+
 DuaManager::DuaManager(Instance &aInstance)
     : InstanceLocator(aInstance)
     , mRegistrationTask(aInstance, DuaManager::HandleRegistrationTask)
@@ -156,11 +158,11 @@
             IgnoreError(Store());
         }
 
-        otLogInfoDua("Generated DUA: %s", mDomainUnicastAddress.GetAddress().ToString().AsCString());
+        LogInfo("Generated DUA: %s", mDomainUnicastAddress.GetAddress().ToString().AsCString());
     }
     else
     {
-        otLogWarnDua("Generate DUA: %s", ErrorToString(error));
+        LogWarn("Generate DUA: %s", ErrorToString(error));
     }
 
     return error;
@@ -174,7 +176,7 @@
     VerifyOrExit(mFixedDuaInterfaceIdentifier.IsUnspecified() || mFixedDuaInterfaceIdentifier != aIid);
 
     mFixedDuaInterfaceIdentifier = aIid;
-    otLogInfoDua("Set DUA IID: %s", mFixedDuaInterfaceIdentifier.ToString().AsCString());
+    LogInfo("Set DUA IID: %s", mFixedDuaInterfaceIdentifier.ToString().AsCString());
 
     if (Get<ThreadNetif>().HasUnicastAddress(GetDomainUnicastAddress()))
     {
@@ -203,7 +205,7 @@
         }
     }
 
-    otLogInfoDua("Cleared DUA IID: %s", mFixedDuaInterfaceIdentifier.ToString().AsCString());
+    LogInfo("Cleared DUA IID: %s", mFixedDuaInterfaceIdentifier.ToString().AsCString());
     mFixedDuaInterfaceIdentifier.Clear();
 
 exit:
@@ -254,7 +256,7 @@
     {
         mDelay.mFields.mRegistrationDelay = aDelay;
 
-        otLogDebgDua("update regdelay %d", mDelay.mFields.mRegistrationDelay);
+        LogDebg("update regdelay %d", mDelay.mFields.mRegistrationDelay);
         UpdateTimeTickerRegistration();
     }
 }
@@ -284,7 +286,7 @@
     {
         mDelay.mFields.mReregistrationDelay = delay;
         UpdateTimeTickerRegistration();
-        otLogDebgDua("update reregdelay %d", mDelay.mFields.mReregistrationDelay);
+        LogDebg("update reregdelay %d", mDelay.mFields.mReregistrationDelay);
     }
 
 exit:
@@ -297,7 +299,7 @@
     {
         mDelay.mFields.mCheckDelay = aDelay;
 
-        otLogDebgDua("update checkdelay %d", mDelay.mFields.mCheckDelay);
+        LogDebg("update checkdelay %d", mDelay.mFields.mCheckDelay);
         UpdateTimeTickerRegistration();
     }
 }
@@ -355,8 +357,8 @@
     bool attempt = false;
 
 #if OPENTHREAD_CONFIG_DUA_ENABLE
-    otLogDebgDua("regdelay %d, reregdelay %d, checkdelay %d", mDelay.mFields.mRegistrationDelay,
-                 mDelay.mFields.mReregistrationDelay, mDelay.mFields.mCheckDelay);
+    LogDebg("regdelay %d, reregdelay %d, checkdelay %d", mDelay.mFields.mRegistrationDelay,
+            mDelay.mFields.mReregistrationDelay, mDelay.mFields.mCheckDelay);
 
     if ((mDuaState != kNotExist) &&
         (TimerMilli::GetNow() > (mLastRegistrationTime + TimeMilli::SecToMsec(Mle::kDuaDadPeriod))))
@@ -369,7 +371,7 @@
         attempt = true;
     }
 #else
-    otLogDebgDua("reregdelay %d, checkdelay %d", mDelay.mFields.mReregistrationDelay, mDelay.mFields.mCheckDelay);
+    LogDebg("reregdelay %d, checkdelay %d", mDelay.mFields.mReregistrationDelay, mDelay.mFields.mCheckDelay);
 #endif
 
     if ((mDelay.mFields.mCheckDelay > 0) && (--mDelay.mFields.mCheckDelay == 0))
@@ -422,7 +424,7 @@
     Error            error   = kErrorNone;
     Mle::MleRouter & mle     = Get<Mle::MleRouter>();
     Coap::Message *  message = nullptr;
-    Ip6::MessageInfo messageInfo;
+    Tmf::MessageInfo messageInfo(GetInstance());
     Ip6::Address     dua;
 
     VerifyOrExit(mle.IsAttached(), error = kErrorInvalidState);
@@ -453,10 +455,8 @@
     }
 
     // Prepare DUA.req
-    VerifyOrExit((message = Get<Tmf::Agent>().NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
-
-    SuccessOrExit(error = message->InitAsConfirmablePost(UriPath::kDuaRegistrationRequest));
-    SuccessOrExit(error = message->SetPayloadMarker());
+    message = Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(UriPath::kDuaRegistrationRequest);
+    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
 #if OPENTHREAD_CONFIG_DUA_ENABLE
     if (mDuaState == kToRegister && mDelay.mFields.mRegistrationDelay == 0)
@@ -515,8 +515,7 @@
                                                       Get<BackboneRouter::Leader>().GetServer16());
     }
 
-    messageInfo.SetPeerPort(Tmf::kUdpPort);
-    messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetMeshLocal16());
+    messageInfo.SetSockAddrToRloc();
 
     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo, &DuaManager::HandleDuaResponse, this));
 
@@ -532,7 +531,7 @@
         Get<DataPollSender>().SendFastPolls();
     }
 
-    otLogInfoDua("Sent DUA.req for DUA %s", dua.ToString().AsCString());
+    LogInfo("Sent DUA.req for DUA %s", dua.ToString().AsCString());
 
 exit:
     if (error == kErrorNoBufs)
@@ -540,7 +539,7 @@
         UpdateCheckDelay(Mle::kNoBufDelay);
     }
 
-    otLogInfoDua("PerformNextRegistration: %s", ErrorToString(error));
+    LogInfo("PerformNextRegistration: %s", ErrorToString(error));
     FreeMessageOnError(message, error);
 }
 
@@ -583,7 +582,7 @@
         mRegistrationTask.Post();
     }
 
-    otLogInfoDua("Received DUA.rsp: %s", ErrorToString(error));
+    LogInfo("Received DUA.rsp: %s", ErrorToString(error));
 }
 
 void DuaManager::HandleDuaNotification(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
@@ -600,14 +599,14 @@
 
     if (aMessage.IsConfirmable() && Get<Tmf::Agent>().SendEmptyAck(aMessage, aMessageInfo) == kErrorNone)
     {
-        otLogInfoDua("Sent DUA.ntf acknowledgment");
+        LogInfo("Sent DUA.ntf acknowledgment");
     }
 
     error = ProcessDuaResponse(aMessage);
 
 exit:
     OT_UNUSED_VARIABLE(error);
-    otLogInfoDua("Received DUA.ntf: %d", ErrorToString(error));
+    LogInfo("Received DUA.ntf: %d", ErrorToString(error));
 }
 
 Error DuaManager::ProcessDuaResponse(Coap::Message &aMessage)
@@ -724,24 +723,20 @@
                                          const Child &              aChild)
 {
     Coap::Message *  message = nullptr;
-    Ip6::MessageInfo messageInfo;
+    Tmf::MessageInfo messageInfo(GetInstance());
     Error            error;
 
-    VerifyOrExit((message = Get<Tmf::Agent>().NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
-
-    SuccessOrExit(error = message->InitAsConfirmablePost(UriPath::kDuaRegistrationNotify));
-    SuccessOrExit(error = message->SetPayloadMarker());
+    message = Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(UriPath::kDuaRegistrationNotify);
+    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
     SuccessOrExit(error = Tlv::Append<ThreadStatusTlv>(*message, aStatus));
     SuccessOrExit(error = Tlv::Append<ThreadTargetTlv>(*message, aAddress));
 
-    messageInfo.GetPeerAddr().SetToRoutingLocator(Get<Mle::MleRouter>().GetMeshLocalPrefix(), aChild.GetRloc16());
-    messageInfo.SetPeerPort(Tmf::kUdpPort);
-    messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetMeshLocal16());
+    messageInfo.SetSockAddrToRlocPeerAddrTo(aChild.GetRloc16());
 
     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo));
 
-    otLogInfoDua("Sent ADDR_NTF for child %04x DUA %s", aChild.GetRloc16(), aAddress.ToString().AsCString());
+    LogInfo("Sent ADDR_NTF for child %04x DUA %s", aChild.GetRloc16(), aAddress.ToString().AsCString());
 
 exit:
 
@@ -750,8 +745,8 @@
         FreeMessage(message);
 
         // TODO: (DUA) (P4) may enhance to  guarantee the delivery of DUA.ntf
-        otLogWarnDua("Sent ADDR_NTF for child %04x DUA %s Error %s", aChild.GetRloc16(),
-                     aAddress.ToString().AsCString(), ErrorToString(error));
+        LogWarn("Sent ADDR_NTF for child %04x DUA %s Error %s", aChild.GetRloc16(), aAddress.ToString().AsCString(),
+                ErrorToString(error));
     }
 }
 
diff --git a/src/core/thread/energy_scan_server.cpp b/src/core/thread/energy_scan_server.cpp
index 0a2c819..9c6b913 100644
--- a/src/core/thread/energy_scan_server.cpp
+++ b/src/core/thread/energy_scan_server.cpp
@@ -39,7 +39,7 @@
 #include "common/debug.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "meshcop/meshcop.hpp"
 #include "meshcop/meshcop_tlvs.hpp"
 #include "thread/thread_netif.hpp"
@@ -47,6 +47,8 @@
 
 namespace ot {
 
+RegisterLogModule("EnergyScanSrv");
+
 EnergyScanServer::EnergyScanServer(Instance &aInstance)
     : InstanceLocator(aInstance)
     , mChannelMask(0)
@@ -97,7 +99,7 @@
     if (aMessage.IsConfirmable() && !aMessageInfo.GetSockAddr().IsMulticast())
     {
         SuccessOrExit(Get<Tmf::Agent>().SendEmptyAck(aMessage, responseInfo));
-        otLogInfoMeshCoP("sent energy scan query response");
+        LogInfo("sent energy scan query response");
     }
 
 exit:
@@ -172,13 +174,11 @@
     Error                   error = kErrorNone;
     MeshCoP::ChannelMaskTlv channelMask;
     MeshCoP::EnergyListTlv  energyList;
-    Ip6::MessageInfo        messageInfo;
+    Tmf::MessageInfo        messageInfo(GetInstance());
     Coap::Message *         message;
 
-    VerifyOrExit((message = Get<Tmf::Agent>().NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
-
-    SuccessOrExit(error = message->InitAsConfirmablePost(UriPath::kEnergyReport));
-    SuccessOrExit(error = message->SetPayloadMarker());
+    message = Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(UriPath::kEnergyReport);
+    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
     channelMask.Init();
     channelMask.SetChannelMask(mChannelMask);
@@ -189,12 +189,11 @@
     SuccessOrExit(error = message->Append(energyList));
     SuccessOrExit(error = message->AppendBytes(mScanResults, mScanResultsLength));
 
-    messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetMeshLocal16());
-    messageInfo.SetPeerAddr(mCommissioner);
-    messageInfo.SetPeerPort(Tmf::kUdpPort);
+    messageInfo.SetSockAddrToRlocPeerAddrTo(mCommissioner);
+
     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo));
 
-    otLogInfoMeshCoP("sent scan results");
+    LogInfo("sent scan results");
 
 exit:
     FreeMessageOnError(message, error);
diff --git a/src/core/thread/indirect_sender.cpp b/src/core/thread/indirect_sender.cpp
index 34fd271..6b4e68c 100644
--- a/src/core/thread/indirect_sender.cpp
+++ b/src/core/thread/indirect_sender.cpp
@@ -38,7 +38,6 @@
 #include "common/code_utils.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
 #include "common/message.hpp"
 #include "thread/mesh_forwarder.hpp"
 #include "thread/mle_tlvs.hpp"
@@ -137,18 +136,13 @@
 
 void IndirectSender::ClearAllMessagesForSleepyChild(Child &aChild)
 {
-    Message *message;
-    Message *nextMessage;
-
     VerifyOrExit(aChild.GetIndirectMessageCount() > 0);
 
-    for (message = Get<MeshForwarder>().mSendQueue.GetHead(); message; message = nextMessage)
+    for (Message &message : Get<MeshForwarder>().mSendQueue)
     {
-        nextMessage = message->GetNext();
+        message.ClearChildMask(Get<ChildTable>().GetChildIndex(aChild));
 
-        message->ClearChildMask(Get<ChildTable>().GetChildIndex(aChild));
-
-        Get<MeshForwarder>().RemoveMessageIfNoPendingTx(*message);
+        Get<MeshForwarder>().RemoveMessageIfNoPendingTx(message);
     }
 
     aChild.SetIndirectMessage(nullptr);
@@ -187,12 +181,12 @@
     {
         uint16_t childIndex = Get<ChildTable>().GetChildIndex(aChild);
 
-        for (Message *message = Get<MeshForwarder>().mSendQueue.GetHead(); message; message = message->GetNext())
+        for (Message &message : Get<MeshForwarder>().mSendQueue)
         {
-            if (message->GetChildMask(childIndex))
+            if (message.GetChildMask(childIndex))
             {
-                message->ClearChildMask(childIndex);
-                message->SetDirectTransmission();
+                message.ClearChildMask(childIndex);
+                message.SetDirectTransmission();
             }
         }
 
@@ -215,19 +209,20 @@
 
 Message *IndirectSender::FindIndirectMessage(Child &aChild, bool aSupervisionTypeOnly)
 {
-    Message *message;
+    Message *msg        = nullptr;
     uint16_t childIndex = Get<ChildTable>().GetChildIndex(aChild);
 
-    for (message = Get<MeshForwarder>().mSendQueue.GetHead(); message; message = message->GetNext())
+    for (Message &message : Get<MeshForwarder>().mSendQueue)
     {
-        if (message->GetChildMask(childIndex) &&
-            (!aSupervisionTypeOnly || (message->GetType() == Message::kTypeSupervision)))
+        if (message.GetChildMask(childIndex) &&
+            (!aSupervisionTypeOnly || (message.GetType() == Message::kTypeSupervision)))
         {
+            msg = &message;
             break;
         }
     }
 
-    return message;
+    return msg;
 }
 
 void IndirectSender::RequestMessageUpdate(Child &aChild)
@@ -325,7 +320,7 @@
         mDataPollHandler.HandleNewFrame(aChild);
 
         aChild.GetMacAddress(childAddress);
-        Get<MeshForwarder>().LogMessage(MeshForwarder::kMessagePrepareIndirect, *message, &childAddress, kErrorNone);
+        Get<MeshForwarder>().LogMessage(MeshForwarder::kMessagePrepareIndirect, *message, kErrorNone, &childAddress);
     }
 }
 
@@ -524,7 +519,7 @@
         if (!aFrame.IsEmpty())
         {
             IgnoreError(aFrame.GetDstAddr(macDest));
-            Get<MeshForwarder>().LogMessage(MeshForwarder::kMessageTransmit, *message, &macDest, txError);
+            Get<MeshForwarder>().LogMessage(MeshForwarder::kMessageTransmit, *message, txError, &macDest);
         }
 
         if (message->GetType() == Message::kTypeIp6)
diff --git a/src/core/thread/key_manager.cpp b/src/core/thread/key_manager.cpp
index 08c8b3e..7563972 100644
--- a/src/core/thread/key_manager.cpp
+++ b/src/core/thread/key_manager.cpp
@@ -37,7 +37,7 @@
 #include "common/encoding.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "common/timer.hpp"
 #include "crypto/hkdf_sha256.hpp"
 #include "crypto/storage.hpp"
@@ -46,6 +46,8 @@
 
 namespace ot {
 
+RegisterLogModule("KeyManager");
+
 const uint8_t KeyManager::kThreadString[] = {
     'T', 'h', 'r', 'e', 'a', 'd',
 };
@@ -70,7 +72,6 @@
     mNativeCommissioningEnabled     = true;
     mRoutersEnabled                 = true;
     mExternalCommissioningEnabled   = true;
-    mBeaconsEnabled                 = true;
     mCommercialCommissioningEnabled = false;
     mAutonomousEnrollmentEnabled    = false;
     mNetworkKeyProvisioningEnabled  = false;
@@ -89,7 +90,6 @@
     mNativeCommissioningEnabled     = aFlags[0] & kNativeCommissioningMask;
     mRoutersEnabled                 = aFlags[0] & kRoutersMask;
     mExternalCommissioningEnabled   = aFlags[0] & kExternalCommissioningMask;
-    mBeaconsEnabled                 = aFlags[0] & kBeaconsMask;
     mCommercialCommissioningEnabled = (aFlags[0] & kCommercialCommissioningMask) == 0;
     mAutonomousEnrollmentEnabled    = (aFlags[0] & kAutonomousEnrollmentMask) == 0;
     mNetworkKeyProvisioningEnabled  = (aFlags[0] & kNetworkKeyProvisioningMask) == 0;
@@ -129,11 +129,6 @@
         aFlags[0] |= kExternalCommissioningMask;
     }
 
-    if (mBeaconsEnabled)
-    {
-        aFlags[0] |= kBeaconsMask;
-    }
-
     if (!mCommercialCommissioningEnabled)
     {
         aFlags[0] |= kCommercialCommissioningMask;
@@ -348,13 +343,13 @@
         Mac::KeyMaterial prevKey;
         Mac::KeyMaterial nextKey;
 
-        curKey.SetFrom(hashKeys.GetMacKey());
+        curKey.SetFrom(hashKeys.GetMacKey(), kExportableMacKeys);
 
         ComputeKeys(mKeySequence - 1, hashKeys);
-        prevKey.SetFrom(hashKeys.GetMacKey());
+        prevKey.SetFrom(hashKeys.GetMacKey(), kExportableMacKeys);
 
         ComputeKeys(mKeySequence + 1, hashKeys);
-        nextKey.SetFrom(hashKeys.GetMacKey());
+        nextKey.SetFrom(hashKeys.GetMacKey(), kExportableMacKeys);
 
         Get<Mac::SubMac>().SetMacKey(Mac::Frame::kKeyIdMode1, (mKeySequence & 0x7f) + 1, prevKey, curKey, nextKey);
     }
@@ -486,7 +481,7 @@
 {
     if (aSecurityPolicy.mRotationTime < SecurityPolicy::kMinKeyRotationTime)
     {
-        otLogNoteMeshCoP("Key Rotation Time too small: %d", aSecurityPolicy.mRotationTime);
+        LogNote("Key Rotation Time too small: %d", aSecurityPolicy.mRotationTime);
         ExitNow();
     }
 
diff --git a/src/core/thread/key_manager.hpp b/src/core/thread/key_manager.hpp
index c383a97..a8e2e32 100644
--- a/src/core/thread/key_manager.hpp
+++ b/src/core/thread/key_manager.hpp
@@ -551,6 +551,7 @@
 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;
 
     OT_TOOL_PACKED_BEGIN
     struct Keys
diff --git a/src/core/thread/link_metrics.cpp b/src/core/thread/link_metrics.cpp
index 265a96c..c4265f6 100644
--- a/src/core/thread/link_metrics.cpp
+++ b/src/core/thread/link_metrics.cpp
@@ -39,7 +39,7 @@
 #include "common/encoding.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "mac/mac.hpp"
 #include "thread/link_metrics_tlvs.hpp"
 #include "thread/neighbor_table.hpp"
@@ -47,6 +47,8 @@
 namespace ot {
 namespace LinkMetrics {
 
+RegisterLogModule("LinkMetrics");
+
 using ot::Encoding::BigEndian::HostSwap32;
 
 void SeriesInfo::Init(uint8_t aSeriesId, const SeriesFlags &aSeriesFlags, const Metrics &aMetrics)
@@ -171,7 +173,7 @@
     error = Get<Mle::MleRouter>().SendLinkMetricsManagementRequest(aDestination, subTlvs, fwdProbingSubTlv->GetSize());
 
 exit:
-    otLogDebgMle("SendMgmtRequestForwardTrackingSeries, error:%s, Series ID:%u", ErrorToString(error), aSeriesId);
+    LogDebg("SendMgmtRequestForwardTrackingSeries, error:%s, Series ID:%u", ErrorToString(error), aSeriesId);
     return error;
 }
 
@@ -233,7 +235,7 @@
 
     error = Get<Mle::MleRouter>().SendLinkProbe(aDestination, aSeriesId, buf, aLength);
 exit:
-    otLogDebgMle("SendLinkProbe, error:%s, Series ID:%u", ErrorToString(error), aSeriesId);
+    LogDebg("SendLinkProbe, error:%s, Series ID:%u", ErrorToString(error), aSeriesId);
     return error;
 }
 #endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
@@ -332,7 +334,7 @@
     aMessage.Write(startOffset, tlv);
 
 exit:
-    otLogDebgMle("AppendReport, error:%s", ErrorToString(error));
+    LogDebg("AppendReport, error:%s", ErrorToString(error));
     return error;
 }
 
@@ -510,14 +512,14 @@
                 SuccessOrExit(aMessage.Read(pos, values.mPduCountValue));
                 values.mPduCountValue = HostSwap32(values.mPduCountValue);
                 pos += sizeof(uint32_t);
-                otLogDebgMle(" - PDU Counter: %d (Count/Summation)", values.mPduCountValue);
+                LogDebg(" - PDU Counter: %d (Count/Summation)", values.mPduCountValue);
                 break;
 
             case TypeIdFlags::kLqi:
                 values.GetMetrics().mLqi = true;
                 SuccessOrExit(aMessage.Read(pos, values.mLqiValue));
                 pos += sizeof(uint8_t);
-                otLogDebgMle(" - LQI: %d (Exponential Moving Average)", values.mLqiValue);
+                LogDebg(" - LQI: %d (Exponential Moving Average)", values.mLqiValue);
                 break;
 
             case TypeIdFlags::kLinkMargin:
@@ -526,7 +528,7 @@
                 // Reverse operation for linear scale, map from [0, 255] to [0, 130]
                 values.mLinkMarginValue = rawValue * 130 / 255;
                 pos += sizeof(uint8_t);
-                otLogDebgMle(" - Margin: %d (dB) (Exponential Moving Average)", values.mLinkMarginValue);
+                LogDebg(" - Margin: %d (dB) (Exponential Moving Average)", values.mLinkMarginValue);
                 break;
 
             case TypeIdFlags::kRssi:
@@ -535,7 +537,7 @@
                 // Reverse operation for linear scale, map from [0, 255] to [-130, 0]
                 values.mRssiValue = rawValue * 130 / 255 - 130;
                 pos += sizeof(uint8_t);
-                otLogDebgMle(" - RSSI: %d (dBm) (Exponential Moving Average)", values.mRssiValue);
+                LogDebg(" - RSSI: %d (dBm) (Exponential Moving Average)", values.mRssiValue);
                 break;
 
             default:
@@ -555,7 +557,7 @@
     }
 
 exit:
-    otLogDebgMle("HandleReport, error:%s", ErrorToString(error));
+    LogDebg("HandleReport, error:%s", ErrorToString(error));
     return;
 }
 
diff --git a/src/core/thread/link_metrics.hpp b/src/core/thread/link_metrics.hpp
index f1891af..318aea0 100644
--- a/src/core/thread/link_metrics.hpp
+++ b/src/core/thread/link_metrics.hpp
@@ -402,7 +402,7 @@
      * This method processes received Enh-ACK Probing IE data.
      *
      * @param[in] aData      A pointer to buffer containing the Enh-ACK Probing IE data.
-     * @param[in] aLen       The length of @p aData.
+     * @param[in] aLength    The length of @p aData.
      * @param[in] aNeighbor  The neighbor from which the Enh-ACK Probing IE was received.
      *
      */
diff --git a/src/core/thread/link_metrics_tlvs.hpp b/src/core/thread/link_metrics_tlvs.hpp
index 10f9f0b..118f730 100644
--- a/src/core/thread/link_metrics_tlvs.hpp
+++ b/src/core/thread/link_metrics_tlvs.hpp
@@ -329,7 +329,7 @@
     /**
      * This method sets the Link Metrics Type ID.
      *
-     * @param[in]  aMetricsTypeID  The Link Metrics Type ID to set.
+     * @param[in]  aMetricsTypeId  The Link Metrics Type ID to set.
      *
      */
     void SetMetricsTypeId(TypeIdFlags aMetricsTypeId)
diff --git a/src/core/thread/link_quality.cpp b/src/core/thread/link_quality.cpp
index c16d114..f3e1cb2 100644
--- a/src/core/thread/link_quality.cpp
+++ b/src/core/thread/link_quality.cpp
@@ -131,7 +131,7 @@
 void LinkQualityInfo::Clear(void)
 {
     mRssAverager.Clear();
-    SetLinkQuality(0);
+    SetLinkQuality(kLinkQuality0);
     mLastRss = OT_RADIO_RSSI_INVALID;
 
     mFrameErrorRate.Clear();
@@ -186,31 +186,31 @@
     return static_cast<uint8_t>(linkMargin);
 }
 
-uint8_t LinkQualityInfo::ConvertLinkMarginToLinkQuality(uint8_t aLinkMargin)
+LinkQuality LinkQualityInfo::ConvertLinkMarginToLinkQuality(uint8_t aLinkMargin)
 {
     return CalculateLinkQuality(aLinkMargin, kNoLinkQuality);
 }
 
-uint8_t LinkQualityInfo::ConvertRssToLinkQuality(int8_t aNoiseFloor, int8_t aRss)
+LinkQuality LinkQualityInfo::ConvertRssToLinkQuality(int8_t aNoiseFloor, int8_t aRss)
 {
     return ConvertLinkMarginToLinkQuality(ConvertRssToLinkMargin(aNoiseFloor, aRss));
 }
 
-int8_t LinkQualityInfo::ConvertLinkQualityToRss(int8_t aNoiseFloor, uint8_t aLinkQuality)
+int8_t LinkQualityInfo::ConvertLinkQualityToRss(int8_t aNoiseFloor, LinkQuality aLinkQuality)
 {
     int8_t linkmargin = 0;
 
     switch (aLinkQuality)
     {
-    case 3:
+    case kLinkQuality3:
         linkmargin = kLinkQuality3LinkMargin;
         break;
 
-    case 2:
+    case kLinkQuality2:
         linkmargin = kLinkQuality2LinkMargin;
         break;
 
-    case 1:
+    case kLinkQuality1:
         linkmargin = kLinkQuality1LinkMargin;
         break;
 
@@ -222,7 +222,7 @@
     return linkmargin + aNoiseFloor;
 }
 
-uint8_t LinkQualityInfo::CalculateLinkQuality(uint8_t aLinkMargin, uint8_t aLastLinkQuality)
+LinkQuality LinkQualityInfo::CalculateLinkQuality(uint8_t aLinkMargin, uint8_t aLastLinkQuality)
 {
     // Static private method to calculate the link quality from a given
     // link margin while taking into account the last link quality
@@ -230,8 +230,8 @@
     // there is no previous value for link quality, the constant
     // kNoLinkQuality should be passed as the second argument.
 
-    uint8_t threshold1, threshold2, threshold3;
-    uint8_t linkQuality = 0;
+    uint8_t     threshold1, threshold2, threshold3;
+    LinkQuality linkQuality = kLinkQuality0;
 
     threshold1 = kThreshold1;
     threshold2 = kThreshold2;
@@ -262,15 +262,15 @@
 
     if (aLinkMargin > threshold3)
     {
-        linkQuality = 3;
+        linkQuality = kLinkQuality3;
     }
     else if (aLinkMargin > threshold2)
     {
-        linkQuality = 2;
+        linkQuality = kLinkQuality2;
     }
     else if (aLinkMargin > threshold1)
     {
-        linkQuality = 1;
+        linkQuality = kLinkQuality1;
     }
 
     return linkQuality;
diff --git a/src/core/thread/link_quality.hpp b/src/core/thread/link_quality.hpp
index a7d34e9..bc10957 100644
--- a/src/core/thread/link_quality.hpp
+++ b/src/core/thread/link_quality.hpp
@@ -227,6 +227,21 @@
 };
 
 /**
+ * This enumeration represents the link quality constants.
+ *
+ * Link Quality is an integer in [0, 3]. A higher link quality indicates a more usable link, with 0 indicating that the
+ * link is non-existent or unusable.
+ *
+ */
+enum LinkQuality : uint8_t
+{
+    kLinkQuality0 = 0, ///< Link quality 0 (non-existent link)
+    kLinkQuality1 = 1, ///< Link quality 1
+    kLinkQuality2 = 2, ///< Link quality 2
+    kLinkQuality3 = 3, ///< Link quality 3
+};
+
+/**
  * This class encapsulates/stores all relevant information about quality of a link, including average received signal
  * strength (RSS), last RSS, link margin, and link quality.
  *
@@ -312,7 +327,7 @@
      * @returns The current link quality value (value 0-3 as per Thread specification).
      *
      */
-    uint8_t GetLinkQuality(void) const { return mLinkQuality; }
+    LinkQuality GetLinkQuality(void) const { return mLinkQuality; }
 
     /**
      * Returns the most recent RSS value.
@@ -391,7 +406,7 @@
      * @returns The link quality value (0-3).
      *
      */
-    static uint8_t ConvertLinkMarginToLinkQuality(uint8_t aLinkMargin);
+    static LinkQuality ConvertLinkMarginToLinkQuality(uint8_t aLinkMargin);
 
     /**
      * This method converts a received signal strength value to a link quality value.
@@ -402,7 +417,7 @@
      * @returns The link quality value (0-3).
      *
      */
-    static uint8_t ConvertRssToLinkQuality(int8_t aNoiseFloor, int8_t aRss);
+    static LinkQuality ConvertRssToLinkQuality(int8_t aNoiseFloor, int8_t aRss);
 
     /**
      * This method converts a link quality value to a typical received signal strength value.
@@ -415,7 +430,7 @@
      * @returns The typical platform RSSI.
      *
      */
-    static int8_t ConvertLinkQualityToRss(int8_t aNoiseFloor, uint8_t aLinkQuality);
+    static int8_t ConvertLinkQualityToRss(int8_t aNoiseFloor, LinkQuality aLinkQuality);
 
 private:
     // Constants for obtaining link quality from link margin:
@@ -432,12 +447,12 @@
 
     static constexpr uint8_t kNoLinkQuality = 0xff; // Indicate that there is no previous/last link quality.
 
-    void SetLinkQuality(uint8_t aLinkQuality) { mLinkQuality = aLinkQuality; }
+    void SetLinkQuality(LinkQuality aLinkQuality) { mLinkQuality = aLinkQuality; }
 
-    static uint8_t CalculateLinkQuality(uint8_t aLinkMargin, uint8_t aLastLinkQuality);
+    static LinkQuality CalculateLinkQuality(uint8_t aLinkMargin, uint8_t aLastLinkQuality);
 
     RssAverager mRssAverager;
-    uint8_t     mLinkQuality;
+    LinkQuality mLinkQuality;
     int8_t      mLastRss;
 
     SuccessRateTracker mFrameErrorRate;
diff --git a/src/core/thread/lowpan.cpp b/src/core/thread/lowpan.cpp
index f9e1133..b683c46 100644
--- a/src/core/thread/lowpan.cpp
+++ b/src/core/thread/lowpan.cpp
@@ -705,8 +705,8 @@
         IgnoreError(networkData.GetContext(0, dstContext));
     }
 
-    memset(&aIp6Header, 0, sizeof(aIp6Header));
-    aIp6Header.Init();
+    aIp6Header.Clear();
+    aIp6Header.InitVersionTrafficClassFlow();
 
     // Traffic Class and Flow Label
     if ((hcCtl & kHcTrafficFlowMask) != kHcTrafficFlow)
diff --git a/src/core/thread/lowpan.hpp b/src/core/thread/lowpan.hpp
index 7eab56f..ae42d1e 100644
--- a/src/core/thread/lowpan.hpp
+++ b/src/core/thread/lowpan.hpp
@@ -279,12 +279,12 @@
     /**
      * This method decompresses a LOWPAN_IPHC header.
      *
-     * @param[out]  aIp6Header              A reference where the IPv6 header will be placed.
-     * @param[out]  aCommpressedNextHeader  A boolean reference to output whether next header is compressed or not.
-     * @param[in]   aMacSource              The MAC source address.
-     * @param[in]   aMacDest                The MAC destination address.
-     * @param[in]   aBuf                    A pointer to the LOWPAN_IPHC header.
-     * @param[in]   aBufLength              The number of bytes in @p aBuf.
+     * @param[out]  aIp6Header             A reference where the IPv6 header will be placed.
+     * @param[out]  aCompressedNextHeader  A boolean reference to output whether next header is compressed or not.
+     * @param[in]   aMacSource             The MAC source address.
+     * @param[in]   aMacDest               The MAC destination address.
+     * @param[in]   aBuf                   A pointer to the LOWPAN_IPHC header.
+     * @param[in]   aBufLength             The number of bytes in @p aBuf.
      *
      * @returns The size of the compressed header in bytes or -1 if decompression fails.
      *
diff --git a/src/core/thread/mesh_forwarder.cpp b/src/core/thread/mesh_forwarder.cpp
index 161f596..7a1a18e 100644
--- a/src/core/thread/mesh_forwarder.cpp
+++ b/src/core/thread/mesh_forwarder.cpp
@@ -38,7 +38,6 @@
 #include "common/encoding.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
 #include "common/message.hpp"
 #include "common/random.hpp"
 #include "common/time_ticker.hpp"
@@ -54,6 +53,8 @@
 
 namespace ot {
 
+RegisterLogModule("MeshForwarder");
+
 void ThreadLinkInfo::SetFrom(const Mac::RxFrame &aFrame)
 {
     Clear();
@@ -63,6 +64,17 @@
         IgnoreError(aFrame.GetDstPanId(mPanId));
     }
 
+    {
+        Mac::PanId dstPanId;
+
+        if (kErrorNone != aFrame.GetDstPanId(dstPanId))
+        {
+            dstPanId = mPanId;
+        }
+
+        mIsDstPanIdBroadcast = (dstPanId == Mac::kPanIdBroadcast);
+    }
+
     mChannel      = aFrame.GetChannel();
     mRss          = aFrame.GetRssi();
     mLqi          = aFrame.GetLqi();
@@ -89,6 +101,10 @@
     , mEnabled(false)
     , mTxPaused(false)
     , mSendBusy(false)
+#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE
+    , mDelayNextTx(false)
+    , mTxDelayTimer(aInstance, HandleTxDelayTimer)
+#endif
     , mScheduleTransmissionTask(aInstance, MeshForwarder::ScheduleTransmissionTask)
 #if OPENTHREAD_FTD
     , mIndirectSender(aInstance)
@@ -133,6 +149,11 @@
     mFragmentPriorityList.Clear();
 #endif
 
+#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE
+    mTxDelayTimer.Stop();
+    mDelayNextTx = false;
+#endif
+
     mEnabled     = false;
     mSendMessage = nullptr;
     Get<Mac::Mac>().SetRxOnWhenIdle(false);
@@ -212,7 +233,7 @@
         }
     }
 
-    LogMessage(kMessageEvict, aMessage, nullptr, kErrorNoBufs);
+    LogMessage(kMessageEvict, aMessage, kErrorNoBufs);
     queue->DequeueAndFree(aMessage);
 }
 
@@ -225,6 +246,20 @@
     }
 }
 
+#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE
+void MeshForwarder::HandleTxDelayTimer(Timer &aTimer)
+{
+    aTimer.Get<MeshForwarder>().HandleTxDelayTimer();
+}
+
+void MeshForwarder::HandleTxDelayTimer(void)
+{
+    mDelayNextTx = false;
+    mScheduleTransmissionTask.Post();
+    LogDebg("Tx delay timer expired");
+}
+#endif
+
 void MeshForwarder::ScheduleTransmissionTask(Tasklet &aTasklet)
 {
     aTasklet.Get<MeshForwarder>().ScheduleTransmissionTask();
@@ -234,7 +269,11 @@
 {
     VerifyOrExit(!mSendBusy && !mTxPaused);
 
-    mSendMessage = GetDirectTransmission();
+#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE
+    VerifyOrExit(!mDelayNextTx);
+#endif
+
+    mSendMessage = PrepareNextDirectTransmission();
     VerifyOrExit(mSendMessage != nullptr);
 
     if (mSendMessage->GetOffset() == 0)
@@ -248,14 +287,14 @@
     return;
 }
 
-Message *MeshForwarder::GetDirectTransmission(void)
+Message *MeshForwarder::PrepareNextDirectTransmission(void)
 {
     Message *curMessage, *nextMessage;
     Error    error = kErrorNone;
 
     for (curMessage = mSendQueue.GetHead(); curMessage; curMessage = nextMessage)
     {
-        if (!curMessage->GetDirectTransmission())
+        if (!curMessage->IsDirectTransmission() || curMessage->IsResolvingAddress())
         {
             nextMessage = curMessage->GetNext();
             continue;
@@ -299,16 +338,13 @@
             ExitNow();
 
 #if OPENTHREAD_FTD
-
         case kErrorAddressQuery:
-            mSendQueue.Dequeue(*curMessage);
-            mResolvingQueue.Enqueue(*curMessage);
+            curMessage->SetResolvingAddress(true);
             continue;
-
 #endif
 
         default:
-            LogMessage(kMessageDrop, *curMessage, nullptr, error);
+            LogMessage(kMessageDrop, *curMessage, error);
             mSendQueue.DequeueAndFree(*curMessage);
             continue;
         }
@@ -511,12 +547,11 @@
 
         if ((mSendMessage->GetSubType() == Message::kSubTypeMleChildIdRequest) && mSendMessage->IsLinkSecurityEnabled())
         {
-            otLogNoteMac("Child ID Request requires fragmentation, aborting tx");
+            LogNote("Child ID Request requires fragmentation, aborting tx");
             mMessageNextOffset = mSendMessage->GetLength();
             ExitNow(frame = nullptr);
         }
 
-        OT_ASSERT(frame->GetLength() != 7);
         break;
 
 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
@@ -959,8 +994,8 @@
 
     if (aError == kErrorNoAck)
     {
-        otLogInfoMac("Deferred ack timeout on trel for neighbor %s rloc16:0x%04x",
-                     aNeighbor.GetExtAddress().ToString().AsCString(), aNeighbor.GetRloc16());
+        LogInfo("Deferred ack timeout on trel for neighbor %s rloc16:0x%04x",
+                aNeighbor.GetExtAddress().ToString().AsCString(), aNeighbor.GetRloc16());
     }
 
 #if OPENTHREAD_CONFIG_MULTI_RADIO
@@ -988,6 +1023,18 @@
 
     VerifyOrExit(mEnabled);
 
+#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE
+    if (mDelayNextTx && (aError == kErrorNone))
+    {
+        mTxDelayTimer.Start(kTxDelayInterval);
+        LogDebg("Start tx delay timer for %u msec", kTxDelayInterval);
+    }
+    else
+    {
+        mDelayNextTx = false;
+    }
+#endif
+
     if (!aFrame.IsEmpty())
     {
         IgnoreError(aFrame.GetDstAddr(macDest));
@@ -1006,7 +1053,7 @@
 
     VerifyOrExit(mSendMessage != nullptr);
 
-    OT_ASSERT(mSendMessage->GetDirectTransmission());
+    OT_ASSERT(mSendMessage->IsDirectTransmission());
 
     if (aFrameTxError != kErrorNone)
     {
@@ -1060,7 +1107,7 @@
     Get<Utils::HistoryTracker>().RecordTxMessage(*mSendMessage, aMacDest);
 #endif
 
-    LogMessage(kMessageTransmit, *mSendMessage, &aMacDest, txError);
+    LogMessage(kMessageTransmit, *mSendMessage, txError, &aMacDest);
 
     if (mSendMessage->GetType() == Message::kTypeIp6)
     {
@@ -1091,7 +1138,7 @@
             // shorter Child ID Request message (by only including mesh-local
             // address in the Address Registration TLV).
 
-            otLogInfoMac("Requesting shorter `Child ID Request`");
+            LogInfo("Requesting shorter `Child ID Request`");
             Get<Mle::Mle>().RequestShorterChildIdRequest();
         }
 
@@ -1109,7 +1156,7 @@
 
 void MeshForwarder::RemoveMessageIfNoPendingTx(Message &aMessage)
 {
-    VerifyOrExit(!aMessage.GetDirectTransmission() && !aMessage.IsChildPending());
+    VerifyOrExit(!aMessage.IsDirectTransmission() && !aMessage.IsChildPending());
 
     if (mSendMessage == &aMessage)
     {
@@ -1255,7 +1302,7 @@
         SuccessOrExit(error);
 
         message->SetDatagramTag(fragmentHeader.GetDatagramTag());
-        message->SetTimeout(kReassemblyTimeout);
+        message->SetTimestampToNow();
         message->SetLinkInfo(aLinkInfo);
 
         VerifyOrExit(Get<Ip6::Filter>().Accept(*message), error = kErrorDrop);
@@ -1279,15 +1326,16 @@
     }
     else // Received frame is a "next fragment".
     {
-        for (message = mReassemblyList.GetHead(); message; message = message->GetNext())
+        for (Message &msg : mReassemblyList)
         {
             // Security Check: only consider reassembly buffers that had the same Security Enabled setting.
-            if (message->GetLength() == fragmentHeader.GetDatagramSize() &&
-                message->GetDatagramTag() == fragmentHeader.GetDatagramTag() &&
-                message->GetOffset() == fragmentHeader.GetDatagramOffset() &&
-                message->GetOffset() + aFrameLength <= fragmentHeader.GetDatagramSize() &&
-                message->IsLinkSecurityEnabled() == aLinkInfo.IsLinkSecurityEnabled())
+            if (msg.GetLength() == fragmentHeader.GetDatagramSize() &&
+                msg.GetDatagramTag() == fragmentHeader.GetDatagramTag() &&
+                msg.GetOffset() == fragmentHeader.GetDatagramOffset() &&
+                msg.GetOffset() + aFrameLength <= fragmentHeader.GetDatagramSize() &&
+                msg.IsLinkSecurityEnabled() == aLinkInfo.IsLinkSecurityEnabled())
             {
+                message = &msg;
                 break;
             }
         }
@@ -1311,7 +1359,7 @@
 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
         message->AddLqi(aLinkInfo.GetLqi());
 #endif
-        message->SetTimeout(kReassemblyTimeout);
+        message->SetTimestampToNow();
     }
 
 exit:
@@ -1334,17 +1382,17 @@
 
 void MeshForwarder::ClearReassemblyList(void)
 {
-    for (const Message *message = mReassemblyList.GetHead(); message != nullptr; message = message->GetNext())
+    for (Message &message : mReassemblyList)
     {
-        LogMessage(kMessageReassemblyDrop, *message, nullptr, kErrorNoFrameReceived);
+        LogMessage(kMessageReassemblyDrop, message, kErrorNoFrameReceived);
 
-        if (message->GetType() == Message::kTypeIp6)
+        if (message.GetType() == Message::kTypeIp6)
         {
             mIpCounters.mRxFailure++;
         }
-    }
 
-    mReassemblyList.DequeueAndFreeAll();
+        mReassemblyList.DequeueAndFree(message);
+    }
 }
 
 void MeshForwarder::HandleTimeTick(void)
@@ -1365,26 +1413,20 @@
 
 bool MeshForwarder::UpdateReassemblyList(void)
 {
-    Message *next = nullptr;
+    TimeMilli now = TimerMilli::GetNow();
 
-    for (Message *message = mReassemblyList.GetHead(); message; message = next)
+    for (Message &message : mReassemblyList)
     {
-        next = message->GetNext();
-
-        if (message->GetTimeout() > 0)
+        if (now - message.GetTimestamp() >= TimeMilli::SecToMsec(kReassemblyTimeout))
         {
-            message->DecrementTimeout();
-        }
-        else
-        {
-            LogMessage(kMessageReassemblyDrop, *message, nullptr, kErrorReassemblyTimeout);
+            LogMessage(kMessageReassemblyDrop, message, kErrorReassemblyTimeout);
 
-            if (message->GetType() == Message::kTypeIp6)
+            if (message.GetType() == Message::kTypeIp6)
             {
                 mIpCounters.mRxFailure++;
             }
 
-            mReassemblyList.DequeueAndFree(*message);
+            mReassemblyList.DequeueAndFree(message);
         }
     }
 
@@ -1467,7 +1509,7 @@
     Get<Utils::HistoryTracker>().RecordRxMessage(aMessage, aMacSource);
 #endif
 
-    LogMessage(kMessageReceive, aMessage, &aMacSource, kErrorNone);
+    LogMessage(kMessageReceive, aMessage, kErrorNone, &aMacSource);
 
     if (aMessage.GetType() == Message::kTypeIp6)
     {
@@ -1559,7 +1601,7 @@
 
 exit:
     FreeMessageOnError(message, error);
-    otLogDebgMac("Send empty message, error:%s", ErrorToString(error));
+    LogDebg("Send empty message, error:%s", ErrorToString(error));
     return error;
 }
 #endif // OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
@@ -1692,7 +1734,7 @@
     return error;
 }
 
-#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_NOTE) && (OPENTHREAD_CONFIG_LOG_MAC == 1)
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_NOTE)
 
 const char *MeshForwarder::MessageActionToString(MessageAction aAction, Error aError)
 {
@@ -1705,6 +1747,8 @@
         "Evicting",                    // (5) kMessageEvict
     };
 
+    const char *string = kMessageActionStrings[aAction];
+
     static_assert(kMessageReceive == 0, "kMessageReceive value is incorrect");
     static_assert(kMessageTransmit == 1, "kMessageTransmit value is incorrect");
     static_assert(kMessagePrepareIndirect == 2, "kMessagePrepareIndirect value is incorrect");
@@ -1712,7 +1756,12 @@
     static_assert(kMessageReassemblyDrop == 4, "kMessageReassemblyDrop value is incorrect");
     static_assert(kMessageEvict == 5, "kMessageEvict value is incorrect");
 
-    return (aError == kErrorNone) ? kMessageActionStrings[aAction] : "Failed to send";
+    if ((aAction == kMessageTransmit) && (aError != kErrorNone))
+    {
+        string = "Failed to send";
+    }
+
+    return string;
 }
 
 const char *MeshForwarder::MessagePriorityToString(const Message &aMessage)
@@ -1724,28 +1773,28 @@
 void MeshForwarder::LogIp6SourceDestAddresses(Ip6::Header &aIp6Header,
                                               uint16_t     aSourcePort,
                                               uint16_t     aDestPort,
-                                              otLogLevel   aLogLevel)
+                                              LogLevel     aLogLevel)
 {
     if (aSourcePort != 0)
     {
-        otLogMac(aLogLevel, "    src:[%s]:%d", aIp6Header.GetSource().ToString().AsCString(), aSourcePort);
+        LogAt(aLogLevel, "    src:[%s]:%d", aIp6Header.GetSource().ToString().AsCString(), aSourcePort);
     }
     else
     {
-        otLogMac(aLogLevel, "    src:[%s]", aIp6Header.GetSource().ToString().AsCString());
+        LogAt(aLogLevel, "    src:[%s]", aIp6Header.GetSource().ToString().AsCString());
     }
 
     if (aDestPort != 0)
     {
-        otLogMac(aLogLevel, "    dst:[%s]:%d", aIp6Header.GetDestination().ToString().AsCString(), aDestPort);
+        LogAt(aLogLevel, "    dst:[%s]:%d", aIp6Header.GetDestination().ToString().AsCString(), aDestPort);
     }
     else
     {
-        otLogMac(aLogLevel, "    dst:[%s]", aIp6Header.GetDestination().ToString().AsCString());
+        LogAt(aLogLevel, "    dst:[%s]", aIp6Header.GetDestination().ToString().AsCString());
     }
 }
 #else
-void MeshForwarder::LogIp6SourceDestAddresses(Ip6::Header &, uint16_t, uint16_t, otLogLevel)
+void MeshForwarder::LogIp6SourceDestAddresses(Ip6::Header &, uint16_t, uint16_t, LogLevel)
 {
 }
 #endif
@@ -1754,7 +1803,7 @@
                                   const Message &     aMessage,
                                   const Mac::Address *aMacAddress,
                                   Error               aError,
-                                  otLogLevel          aLogLevel)
+                                  LogLevel            aLogLevel)
 {
     Ip6::Header ip6Header;
     uint16_t    checksum;
@@ -1773,16 +1822,16 @@
     radioString    = aMessage.IsRadioTypeSet() ? RadioTypeToString(aMessage.GetRadioType()) : "all";
 #endif
 
-    otLogMac(aLogLevel, "%s IPv6 %s msg, len:%d, chksum:%04x%s%s, sec:%s%s%s, prio:%s%s%s%s%s",
-             MessageActionToString(aAction, aError), Ip6::Ip6::IpProtoToString(ip6Header.GetNextHeader()),
-             aMessage.GetLength(), checksum,
-             (aMacAddress == nullptr) ? "" : ((aAction == kMessageReceive) ? ", from:" : ", to:"),
-             (aMacAddress == nullptr) ? "" : aMacAddress->ToString().AsCString(),
-             ToYesNo(aMessage.IsLinkSecurityEnabled()),
-             (aError == kErrorNone) ? "" : ", error:", (aError == kErrorNone) ? "" : ErrorToString(aError),
-             MessagePriorityToString(aMessage), shouldLogRss ? ", rss:" : "",
-             shouldLogRss ? aMessage.GetRssAverager().ToString().AsCString() : "", shouldLogRadio ? ", radio:" : "",
-             radioString);
+    LogAt(aLogLevel, "%s IPv6 %s msg, len:%d, chksum:%04x, ecn:%s%s%s, sec:%s%s%s, prio:%s%s%s%s%s",
+          MessageActionToString(aAction, aError), Ip6::Ip6::IpProtoToString(ip6Header.GetNextHeader()),
+          aMessage.GetLength(), checksum, Ip6::Ip6::EcnToString(ip6Header.GetEcn()),
+          (aMacAddress == nullptr) ? "" : ((aAction == kMessageReceive) ? ", from:" : ", to:"),
+          (aMacAddress == nullptr) ? "" : aMacAddress->ToString().AsCString(),
+          ToYesNo(aMessage.IsLinkSecurityEnabled()),
+          (aError == kErrorNone) ? "" : ", error:", (aError == kErrorNone) ? "" : ErrorToString(aError),
+          MessagePriorityToString(aMessage), shouldLogRss ? ", rss:" : "",
+          shouldLogRss ? aMessage.GetRssAverager().ToString().AsCString() : "", shouldLogRadio ? ", radio:" : "",
+          radioString);
 
     if (aAction != kMessagePrepareIndirect)
     {
@@ -1795,23 +1844,24 @@
 
 void MeshForwarder::LogMessage(MessageAction       aAction,
                                const Message &     aMessage,
-                               const Mac::Address *aMacAddress,
-                               Error               aError)
+                               Error               aError,
+                               const Mac::Address *aMacAddress)
+
 {
-    otLogLevel logLevel = OT_LOG_LEVEL_INFO;
+    LogLevel logLevel = kLogLevelInfo;
 
     switch (aAction)
     {
     case kMessageReceive:
     case kMessageTransmit:
     case kMessagePrepareIndirect:
-        logLevel = (aError == kErrorNone) ? OT_LOG_LEVEL_INFO : OT_LOG_LEVEL_NOTE;
+        logLevel = (aError == kErrorNone) ? kLogLevelInfo : kLogLevelNote;
         break;
 
     case kMessageDrop:
     case kMessageReassemblyDrop:
     case kMessageEvict:
-        logLevel = OT_LOG_LEVEL_NOTE;
+        logLevel = kLogLevelNote;
         break;
     }
 
@@ -1841,11 +1891,11 @@
 {
     if (aError != kErrorNone)
     {
-        otLogNoteMac("%s, aError:%s, %s", aActionText, ErrorToString(aError), aFrame.ToInfoString().AsCString());
+        LogNote("%s, aError:%s, %s", aActionText, ErrorToString(aError), aFrame.ToInfoString().AsCString());
     }
     else
     {
-        otLogInfoMac("%s, %s", aActionText, aFrame.ToInfoString().AsCString());
+        LogInfo("%s, %s", aActionText, aFrame.ToInfoString().AsCString());
     }
 }
 
@@ -1856,10 +1906,10 @@
                                          const Lowpan::FragmentHeader &aFragmentHeader,
                                          bool                          aIsSecure)
 {
-    otLogNoteMac("Dropping rx frag frame, error:%s, len:%d, src:%s, dst:%s, tag:%d, offset:%d, dglen:%d, sec:%s",
-                 ErrorToString(aError), aFrameLength, aMacSource.ToString().AsCString(),
-                 aMacDest.ToString().AsCString(), aFragmentHeader.GetDatagramTag(), aFragmentHeader.GetDatagramOffset(),
-                 aFragmentHeader.GetDatagramSize(), ToYesNo(aIsSecure));
+    LogNote("Dropping rx frag frame, error:%s, len:%d, src:%s, dst:%s, tag:%d, offset:%d, dglen:%d, sec:%s",
+            ErrorToString(aError), aFrameLength, aMacSource.ToString().AsCString(), aMacDest.ToString().AsCString(),
+            aFragmentHeader.GetDatagramTag(), aFragmentHeader.GetDatagramOffset(), aFragmentHeader.GetDatagramSize(),
+            ToYesNo(aIsSecure));
 }
 
 void MeshForwarder::LogLowpanHcFrameDrop(Error               aError,
@@ -1868,13 +1918,13 @@
                                          const Mac::Address &aMacDest,
                                          bool                aIsSecure)
 {
-    otLogNoteMac("Dropping rx lowpan HC frame, error:%s, len:%d, src:%s, dst:%s, sec:%s", ErrorToString(aError),
-                 aFrameLength, aMacSource.ToString().AsCString(), aMacDest.ToString().AsCString(), ToYesNo(aIsSecure));
+    LogNote("Dropping rx lowpan HC frame, error:%s, len:%d, src:%s, dst:%s, sec:%s", ErrorToString(aError),
+            aFrameLength, aMacSource.ToString().AsCString(), aMacDest.ToString().AsCString(), ToYesNo(aIsSecure));
 }
 
-#else // #if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_NOTE) && (OPENTHREAD_CONFIG_LOG_MAC == 1)
+#else // #if OT_SHOULD_LOG_AT( OT_LOG_LEVEL_NOTE)
 
-void MeshForwarder::LogMessage(MessageAction, const Message &, const Mac::Address *, Error)
+void MeshForwarder::LogMessage(MessageAction, const Message &, Error, const Mac::Address *)
 {
 }
 
@@ -1895,7 +1945,7 @@
 {
 }
 
-#endif // #if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_NOTE) && (OPENTHREAD_CONFIG_LOG_MAC == 1)
+#endif // #if OT_SHOULD_LOG_AT( OT_LOG_LEVEL_NOTE)
 
 // LCOV_EXCL_STOP
 
diff --git a/src/core/thread/mesh_forwarder.hpp b/src/core/thread/mesh_forwarder.hpp
index 1f05a80..603e567 100644
--- a/src/core/thread/mesh_forwarder.hpp
+++ b/src/core/thread/mesh_forwarder.hpp
@@ -39,6 +39,7 @@
 #include "common/as_core_type.hpp"
 #include "common/clearable.hpp"
 #include "common/locator.hpp"
+#include "common/log.hpp"
 #include "common/non_copyable.hpp"
 #include "common/tasklet.hpp"
 #include "common/time_ticker.hpp"
@@ -96,6 +97,15 @@
     uint8_t GetChannel(void) const { return mChannel; }
 
     /**
+     * This method returns whether the Destination PAN ID is broadcast.
+     *
+     * @retval TRUE   If Destination PAN ID is broadcast.
+     * @retval FALSE  If Destination PAN ID is not broadcast.
+     *
+     */
+    bool IsDstPanIdBroadcast(void) const { return mIsDstPanIdBroadcast; }
+
+    /**
      * This method indicates whether or not link security is enabled.
      *
      * @retval TRUE   If link security is enabled.
@@ -301,15 +311,6 @@
      */
     void ResetCounters(void) { memset(&mIpCounters, 0, sizeof(mIpCounters)); }
 
-#if OPENTHREAD_FTD
-    /**
-     * This method returns a reference to the resolving queue.
-     *
-     * @returns  A reference to the resolving queue.
-     *
-     */
-    const PriorityQueue &GetResolvingQueue(void) const { return mResolvingQueue; }
-#endif
 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
     /**
      * This method handles a deferred ack.
@@ -333,6 +334,8 @@
     static constexpr uint8_t kMeshHeaderFrameMtu     = OT_RADIO_FRAME_MAX_SIZE; // Max MTU with a Mesh Header frame.
     static constexpr uint8_t kMeshHeaderFrameFcsSize = sizeof(uint16_t);        // Frame FCS size for Mesh Header frame.
 
+    static constexpr uint32_t kTxDelayInterval = OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_INTERVAL; // In msec
+
     enum MessageAction : uint8_t
     {
         kMessageReceive,         // Indicates that the message was received.
@@ -419,7 +422,7 @@
                           Ip6::Header &       aIp6Header);
     void     GetMacDestinationAddress(const Ip6::Address &aIp6Addr, Mac::Address &aMacAddr);
     void     GetMacSourceAddress(const Ip6::Address &aIp6Addr, Mac::Address &aMacAddr);
-    Message *GetDirectTransmission(void);
+    Message *PrepareNextDirectTransmission(void);
     void     HandleMesh(uint8_t *             aFrame,
                         uint16_t              aFrameLength,
                         const Mac::Address &  aMacSource,
@@ -502,7 +505,15 @@
     void PauseMessageTransmissions(void) { mTxPaused = true; }
     void ResumeMessageTransmissions(void);
 
-    void LogMessage(MessageAction aAction, const Message &aMessage, const Mac::Address *aAddress, Error aError);
+#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE
+    static void HandleTxDelayTimer(Timer &aTimer);
+    void        HandleTxDelayTimer(void);
+#endif
+
+    void LogMessage(MessageAction       aAction,
+                    const Message &     aMessage,
+                    Error               aError   = kErrorNone,
+                    const Mac::Address *aAddress = nullptr);
     void LogFrame(const char *aActionText, const Mac::Frame &aFrame, Error aError);
     void LogFragmentFrameDrop(Error                         aError,
                               uint16_t                      aFrameLength,
@@ -522,11 +533,7 @@
                                       uint16_t &     aSourcePort,
                                       uint16_t &     aDestPort);
 
-#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
-    otError ForwardDuaToBackboneLink(Message &aMessage, const Ip6::Address &aDst);
-#endif
-
-#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_NOTE) && (OPENTHREAD_CONFIG_LOG_MAC == 1)
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_NOTE)
     const char *MessageActionToString(MessageAction aAction, Error aError);
     const char *MessagePriorityToString(const Message &aMessage);
 
@@ -546,28 +553,28 @@
                                 uint16_t &          aOffset,
                                 Mac::Address &      aMeshSource,
                                 Mac::Address &      aMeshDest,
-                                otLogLevel          aLogLevel);
+                                LogLevel            aLogLevel);
     void  LogMeshIpHeader(const Message &     aMessage,
                           uint16_t            aOffset,
                           const Mac::Address &aMeshSource,
                           const Mac::Address &aMeshDest,
-                          otLogLevel          aLogLevel);
+                          LogLevel            aLogLevel);
     void  LogMeshMessage(MessageAction       aAction,
                          const Message &     aMessage,
                          const Mac::Address *aAddress,
                          Error               aError,
-                         otLogLevel          aLogLevel);
+                         LogLevel            aLogLevel);
 #endif
     void LogIp6SourceDestAddresses(Ip6::Header &aIp6Header,
                                    uint16_t     aSourcePort,
                                    uint16_t     aDestPort,
-                                   otLogLevel   aLogLevel);
+                                   LogLevel     aLogLevel);
     void LogIp6Message(MessageAction       aAction,
                        const Message &     aMessage,
                        const Mac::Address *aAddress,
                        Error               aError,
-                       otLogLevel          aLogLevel);
-#endif // #if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_NOTE) && (OPENTHREAD_CONFIG_LOG_MAC == 1)
+                       LogLevel            aLogLevel);
+#endif // #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_NOTE)
 
     PriorityQueue mSendQueue;
     MessageQueue  mReassemblyList;
@@ -584,6 +591,10 @@
     bool         mEnabled : 1;
     bool         mTxPaused : 1;
     bool         mSendBusy : 1;
+#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE
+    bool       mDelayNextTx : 1;
+    TimerMilli mTxDelayTimer;
+#endif
 
     Tasklet mScheduleTransmissionTask;
 
@@ -591,7 +602,6 @@
 
 #if OPENTHREAD_FTD
     FragmentPriorityList mFragmentPriorityList;
-    PriorityQueue        mResolvingQueue;
     IndirectSender       mIndirectSender;
 #endif
 
diff --git a/src/core/thread/mesh_forwarder_ftd.cpp b/src/core/thread/mesh_forwarder_ftd.cpp
index f67edfc..ef1f60d 100644
--- a/src/core/thread/mesh_forwarder_ftd.cpp
+++ b/src/core/thread/mesh_forwarder_ftd.cpp
@@ -36,7 +36,6 @@
 #if OPENTHREAD_FTD
 
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
 #include "meshcop/meshcop.hpp"
 #include "net/ip6.hpp"
 #include "net/tcp6.hpp"
@@ -44,6 +43,8 @@
 
 namespace ot {
 
+RegisterLogModule("MeshForwarder");
+
 Error MeshForwarder::SendMessage(Message &aMessage)
 {
     Mle::MleRouter &mle   = Get<Mle::MleRouter>();
@@ -102,7 +103,7 @@
             }
         }
         else if ((neighbor = Get<NeighborTable>().FindNeighbor(ip6Header.GetDestination())) != nullptr &&
-                 !neighbor->IsRxOnWhenIdle() && !aMessage.GetDirectTransmission())
+                 !neighbor->IsRxOnWhenIdle() && !aMessage.IsDirectTransmission())
         {
             // destined for a sleepy child
             Child &child = *static_cast<Child *>(neighbor);
@@ -139,109 +140,85 @@
 
 void MeshForwarder::HandleResolved(const Ip6::Address &aEid, Error aError)
 {
-    Message *    cur, *next;
     Ip6::Address ip6Dst;
-    bool         enqueuedMessage = false;
+    bool         didUpdate = false;
 
-    for (cur = mResolvingQueue.GetHead(); cur; cur = next)
+    for (Message &message : mSendQueue)
     {
-        next = cur->GetNext();
-
-        if (cur->GetType() != Message::kTypeIp6)
+        if (!message.IsResolvingAddress())
         {
             continue;
         }
 
-        IgnoreError(cur->Read(Ip6::Header::kDestinationFieldOffset, ip6Dst));
+        IgnoreError(message.Read(Ip6::Header::kDestinationFieldOffset, ip6Dst));
 
-        if (ip6Dst == aEid)
+        if (ip6Dst != aEid)
         {
-            mResolvingQueue.Dequeue(*cur);
-
-            if (aError == kErrorNone)
-            {
-#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
-                // Pass back to IPv6 layer for DUA destination resolved by Backbone Query
-                if (ForwardDuaToBackboneLink(*cur, ip6Dst) != kErrorNone)
-#endif
-                {
-                    mSendQueue.Enqueue(*cur);
-                    enqueuedMessage = true;
-                }
-            }
-            else
-            {
-                LogMessage(kMessageDrop, *cur, nullptr, aError);
-                cur->Free();
-            }
+            continue;
         }
+
+        if (aError != kErrorNone)
+        {
+            LogMessage(kMessageDrop, message, kErrorAddressQuery);
+            mSendQueue.DequeueAndFree(message);
+            continue;
+        }
+
+#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
+        // Pass back to IPv6 layer for DUA destination resolved
+        // by Backbone Query
+        if (Get<BackboneRouter::Local>().IsPrimary() && Get<BackboneRouter::Leader>().IsDomainUnicast(ip6Dst) &&
+            Get<AddressResolver>().LookUp(ip6Dst) == Get<Mle::MleRouter>().GetRloc16())
+        {
+            uint8_t hopLimit;
+
+            mSendQueue.Dequeue(message);
+
+            // Avoid decreasing Hop Limit twice
+            IgnoreError(message.Read(Ip6::Header::kHopLimitFieldOffset, hopLimit));
+            hopLimit++;
+            message.Write(Ip6::Header::kHopLimitFieldOffset, hopLimit);
+
+            IgnoreError(Get<Ip6::Ip6>().HandleDatagram(message, nullptr, nullptr, /* aFromHost */ false));
+            continue;
+        }
+#endif
+
+        message.SetResolvingAddress(false);
+        didUpdate = true;
     }
 
-    if (enqueuedMessage)
+    if (didUpdate)
     {
         mScheduleTransmissionTask.Post();
     }
-
-    return;
 }
 
-#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
-Error MeshForwarder::ForwardDuaToBackboneLink(Message &aMessage, const Ip6::Address &aDst)
-{
-    Error   error = kErrorNone;
-    uint8_t ttl;
-
-    VerifyOrExit(Get<BackboneRouter::Local>().IsPrimary() && Get<BackboneRouter::Leader>().IsDomainUnicast(aDst),
-                 error = kErrorNoRoute);
-
-    VerifyOrExit(Get<AddressResolver>().LookUp(aDst) == Get<Mle::MleRouter>().GetRloc16(), error = kErrorNoRoute);
-
-    // Avoid decreasing TTL twice
-    IgnoreError(aMessage.Read(Ip6::Header::kHopLimitFieldOffset, ttl));
-    ttl++;
-    aMessage.Write(Ip6::Header::kHopLimitFieldOffset, ttl);
-
-    IgnoreError(Get<Ip6::Ip6>().HandleDatagram(aMessage, nullptr, nullptr, /* aFromNcpHost */ false));
-
-exit:
-    return error;
-}
-#endif
-
 Error MeshForwarder::EvictMessage(Message::Priority aPriority)
 {
-    Error          error    = kErrorNotFound;
-    PriorityQueue *queues[] = {&mResolvingQueue, &mSendQueue};
-    Message *      evict    = nullptr;
+    Error    error = kErrorNotFound;
+    Message *evict = nullptr;
 
-    // search for a lower priority message to evict (choose lowest priority message among all queues)
-    for (PriorityQueue *queue : queues)
+    // Search for a lower priority message to evict
+    for (uint8_t priority = 0; priority < aPriority; priority++)
     {
-        for (uint8_t priority = 0; priority < aPriority; priority++)
+        for (Message *message = mSendQueue.GetHeadForPriority(static_cast<Message::Priority>(priority)); message;
+             message          = message->GetNext())
         {
-            for (Message *message = queue->GetHeadForPriority(static_cast<Message::Priority>(priority)); message;
-                 message          = message->GetNext())
+            if (message->GetPriority() != priority)
             {
-                if (message->GetPriority() != priority)
-                {
-                    break;
-                }
-
-                if (message->GetDoNotEvict())
-                {
-                    continue;
-                }
-
-                evict     = message;
-                aPriority = static_cast<Message::Priority>(priority);
                 break;
             }
-        }
-    }
 
-    if (evict != nullptr)
-    {
-        ExitNow(error = kErrorNone);
+            if (message->GetDoNotEvict())
+            {
+                continue;
+            }
+
+            evict = message;
+            error = kErrorNone;
+            ExitNow();
+        }
     }
 
     for (uint8_t priority = aPriority; priority < Message::kNumPriorities; priority++)
@@ -279,30 +256,26 @@
 
 void MeshForwarder::RemoveMessages(Child &aChild, Message::SubType aSubType)
 {
-    Message *nextMessage;
-
-    for (Message *message = mSendQueue.GetHead(); message; message = nextMessage)
+    for (Message &message : mSendQueue)
     {
-        nextMessage = message->GetNext();
-
-        if ((aSubType != Message::kSubTypeNone) && (aSubType != message->GetSubType()))
+        if ((aSubType != Message::kSubTypeNone) && (aSubType != message.GetSubType()))
         {
             continue;
         }
 
-        if (mIndirectSender.RemoveMessageFromSleepyChild(*message, aChild) != kErrorNone)
+        if (mIndirectSender.RemoveMessageFromSleepyChild(message, aChild) != kErrorNone)
         {
-            switch (message->GetType())
+            switch (message.GetType())
             {
             case Message::kTypeIp6:
             {
                 Ip6::Header ip6header;
 
-                IgnoreError(message->Read(0, ip6header));
+                IgnoreError(message.Read(0, ip6header));
 
                 if (&aChild == static_cast<Child *>(Get<NeighborTable>().FindNeighbor(ip6header.GetDestination())))
                 {
-                    message->ClearDirectTransmission();
+                    message.ClearDirectTransmission();
                 }
 
                 break;
@@ -312,11 +285,11 @@
             {
                 Lowpan::MeshHeader meshHeader;
 
-                IgnoreError(meshHeader.ParseFrom(*message));
+                IgnoreError(meshHeader.ParseFrom(message));
 
                 if (&aChild == static_cast<Child *>(Get<NeighborTable>().FindNeighbor(meshHeader.GetDestination())))
                 {
-                    message->ClearDirectTransmission();
+                    message.ClearDirectTransmission();
                 }
 
                 break;
@@ -327,41 +300,38 @@
             }
         }
 
-        RemoveMessageIfNoPendingTx(*message);
+        RemoveMessageIfNoPendingTx(message);
     }
 }
 
 void MeshForwarder::RemoveDataResponseMessages(void)
 {
     Ip6::Header ip6Header;
-    Message *   next;
 
-    for (Message *message = mSendQueue.GetHead(); message != nullptr; message = next)
+    for (Message &message : mSendQueue)
     {
-        next = message->GetNext();
-
-        if (message->GetSubType() != Message::kSubTypeMleDataResponse)
+        if (message.GetSubType() != Message::kSubTypeMleDataResponse)
         {
             continue;
         }
 
-        IgnoreError(message->Read(0, ip6Header));
+        IgnoreError(message.Read(0, ip6Header));
 
         if (!(ip6Header.GetDestination().IsMulticast()))
         {
             for (Child &child : Get<ChildTable>().Iterate(Child::kInStateAnyExceptInvalid))
             {
-                IgnoreError(mIndirectSender.RemoveMessageFromSleepyChild(*message, child));
+                IgnoreError(mIndirectSender.RemoveMessageFromSleepyChild(message, child));
             }
         }
 
-        if (mSendMessage == message)
+        if (mSendMessage == &message)
         {
             mSendMessage = nullptr;
         }
 
-        LogMessage(kMessageDrop, *message, nullptr, kErrorNone);
-        mSendQueue.DequeueAndFree(*message);
+        LogMessage(kMessageDrop, message);
+        mSendQueue.DequeueAndFree(message);
     }
 }
 
@@ -433,6 +403,13 @@
     mMeshDest      = meshHeader.GetDestination();
     mMeshSource    = meshHeader.GetSource();
 
+#if OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE
+    if (mMacDest.GetShort() != mMeshDest)
+    {
+        mDelayNextTx = true;
+    }
+#endif
+
 exit:
     return error;
 }
@@ -646,6 +623,9 @@
         // destination is not neighbor
         mMacSource.SetShort(mMeshSource);
         mAddMeshHeader = true;
+#if OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE
+        mDelayNextTx = true;
+#endif
     }
 
 exit:
@@ -818,7 +798,7 @@
         message->SetRadioType(static_cast<Mac::RadioType>(aLinkInfo.mRadioType));
 #endif
 
-        LogMessage(kMessageReceive, *message, &aMacSource, kErrorNone);
+        LogMessage(kMessageReceive, *message, kErrorNone, &aMacSource);
 
 #if OPENTHREAD_CONFIG_MULTI_RADIO
         // Since the message will be forwarded, we clear the radio
@@ -835,8 +815,8 @@
 
     if (error != kErrorNone)
     {
-        otLogInfoMac("Dropping rx mesh frame, error:%s, len:%d, src:%s, sec:%s", ErrorToString(error), aFrameLength,
-                     aMacSource.ToString().AsCString(), ToYesNo(aLinkInfo.IsLinkSecurityEnabled()));
+        LogInfo("Dropping rx mesh frame, error:%s, len:%d, src:%s, sec:%s", ErrorToString(error), aFrameLength,
+                aMacSource.ToString().AsCString(), ToYesNo(aLinkInfo.IsLinkSecurityEnabled()));
         FreeMessage(message);
     }
 }
@@ -1010,8 +990,8 @@
 exit:
     if (error != kErrorNone)
     {
-        otLogNoteMac("Failed to get forwarded frame priority, error:%s, len:%d, src:%d, dst:%s", ErrorToString(error),
-                     aFrameLength, aMeshSource.ToString().AsCString(), aMeshDest.ToString().AsCString());
+        LogNote("Failed to get forwarded frame priority, error:%s, len:%d, src:%d, dst:%s", ErrorToString(error),
+                aFrameLength, aMeshSource.ToString().AsCString(), aMeshDest.ToString().AsCString());
     }
     else if (isFragment)
     {
@@ -1023,7 +1003,7 @@
 
 // LCOV_EXCL_START
 
-#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_NOTE) && (OPENTHREAD_CONFIG_LOG_MAC == 1)
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_NOTE)
 
 Error MeshForwarder::LogMeshFragmentHeader(MessageAction       aAction,
                                            const Message &     aMessage,
@@ -1032,7 +1012,7 @@
                                            uint16_t &          aOffset,
                                            Mac::Address &      aMeshSource,
                                            Mac::Address &      aMeshDest,
-                                           otLogLevel          aLogLevel)
+                                           LogLevel            aLogLevel)
 {
     Error                  error             = kErrorFailed;
     bool                   hasFragmentHeader = false;
@@ -1063,20 +1043,20 @@
     radioString    = aMessage.IsRadioTypeSet() ? RadioTypeToString(aMessage.GetRadioType()) : "all";
 #endif
 
-    otLogMac(aLogLevel, "%s mesh frame, len:%d%s%s, msrc:%s, mdst:%s, hops:%d, frag:%s, sec:%s%s%s%s%s%s%s",
-             MessageActionToString(aAction, aError), aMessage.GetLength(),
-             (aMacAddress == nullptr) ? "" : ((aAction == kMessageReceive) ? ", from:" : ", to:"),
-             (aMacAddress == nullptr) ? "" : aMacAddress->ToString().AsCString(), aMeshSource.ToString().AsCString(),
-             aMeshDest.ToString().AsCString(), meshHeader.GetHopsLeft() + ((aAction == kMessageReceive) ? 1 : 0),
-             ToYesNo(hasFragmentHeader), ToYesNo(aMessage.IsLinkSecurityEnabled()),
-             (aError == kErrorNone) ? "" : ", error:", (aError == kErrorNone) ? "" : ErrorToString(aError),
-             shouldLogRss ? ", rss:" : "", shouldLogRss ? aMessage.GetRssAverager().ToString().AsCString() : "",
-             shouldLogRadio ? ", radio:" : "", radioString);
+    LogAt(aLogLevel, "%s mesh frame, len:%d%s%s, msrc:%s, mdst:%s, hops:%d, frag:%s, sec:%s%s%s%s%s%s%s",
+          MessageActionToString(aAction, aError), aMessage.GetLength(),
+          (aMacAddress == nullptr) ? "" : ((aAction == kMessageReceive) ? ", from:" : ", to:"),
+          (aMacAddress == nullptr) ? "" : aMacAddress->ToString().AsCString(), aMeshSource.ToString().AsCString(),
+          aMeshDest.ToString().AsCString(), meshHeader.GetHopsLeft() + ((aAction == kMessageReceive) ? 1 : 0),
+          ToYesNo(hasFragmentHeader), ToYesNo(aMessage.IsLinkSecurityEnabled()),
+          (aError == kErrorNone) ? "" : ", error:", (aError == kErrorNone) ? "" : ErrorToString(aError),
+          shouldLogRss ? ", rss:" : "", shouldLogRss ? aMessage.GetRssAverager().ToString().AsCString() : "",
+          shouldLogRadio ? ", radio:" : "", radioString);
 
     if (hasFragmentHeader)
     {
-        otLogMac(aLogLevel, "    Frag tag:%04x, offset:%d, size:%d", fragmentHeader.GetDatagramTag(),
-                 fragmentHeader.GetDatagramOffset(), fragmentHeader.GetDatagramSize());
+        LogAt(aLogLevel, "    Frag tag:%04x, offset:%d, size:%d", fragmentHeader.GetDatagramTag(),
+              fragmentHeader.GetDatagramOffset(), fragmentHeader.GetDatagramSize());
 
         VerifyOrExit(fragmentHeader.GetDatagramOffset() == 0);
     }
@@ -1163,7 +1143,7 @@
                                     uint16_t            aOffset,
                                     const Mac::Address &aMeshSource,
                                     const Mac::Address &aMeshDest,
-                                    otLogLevel          aLogLevel)
+                                    LogLevel            aLogLevel)
 {
     uint16_t    checksum;
     uint16_t    sourcePort;
@@ -1173,8 +1153,9 @@
     SuccessOrExit(DecompressIp6UdpTcpHeader(aMessage, aOffset, aMeshSource, aMeshDest, ip6Header, checksum, sourcePort,
                                             destPort));
 
-    otLogMac(aLogLevel, "    IPv6 %s msg, chksum:%04x, prio:%s", Ip6::Ip6::IpProtoToString(ip6Header.GetNextHeader()),
-             checksum, MessagePriorityToString(aMessage));
+    LogAt(aLogLevel, "    IPv6 %s msg, chksum:%04x, ecn:%s, prio:%s",
+          Ip6::Ip6::IpProtoToString(ip6Header.GetNextHeader()), checksum, Ip6::Ip6::EcnToString(ip6Header.GetEcn()),
+          MessagePriorityToString(aMessage));
 
     LogIp6SourceDestAddresses(ip6Header, sourcePort, destPort, aLogLevel);
 
@@ -1186,7 +1167,7 @@
                                    const Message &     aMessage,
                                    const Mac::Address *aMacAddress,
                                    Error               aError,
-                                   otLogLevel          aLogLevel)
+                                   LogLevel            aLogLevel)
 {
     uint16_t     offset;
     Mac::Address meshSource;
@@ -1208,7 +1189,7 @@
     return;
 }
 
-#endif // #if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_NOTE) && (OPENTHREAD_CONFIG_LOG_MAC == 1)
+#endif // #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_NOTE)
 
 // LCOV_EXCL_STOP
 
diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp
index 89a662f..831ef87 100644
--- a/src/core/thread/mle.cpp
+++ b/src/core/thread/mle.cpp
@@ -36,17 +36,16 @@
 #include <openthread/platform/radio.h>
 #include <openthread/platform/time.h>
 
+#include "common/array.hpp"
 #include "common/as_core_type.hpp"
 #include "common/code_utils.hpp"
 #include "common/debug.hpp"
 #include "common/encoding.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
 #include "common/random.hpp"
 #include "common/serial_number.hpp"
 #include "common/settings.hpp"
-#include "crypto/aes_ccm.hpp"
 #include "meshcop/meshcop.hpp"
 #include "meshcop/meshcop_tlvs.hpp"
 #include "net/netif.hpp"
@@ -63,6 +62,8 @@
 namespace ot {
 namespace Mle {
 
+RegisterLogModule("Mle");
+
 Mle::Mle(Instance &aInstance)
     : InstanceLocator(aInstance)
     , mRetrieveNewNetworkData(false)
@@ -77,7 +78,7 @@
     , mDelayedResponseTimer(aInstance, Mle::HandleDelayedResponseTimer)
     , mMessageTransmissionTimer(aInstance, Mle::HandleMessageTransmissionTimer)
     , mParentLeaderCost(0)
-    , mParentRequestMode(kAttachAny)
+    , mAttachMode(kAnyPartition)
     , mParentPriority(0)
     , mParentLinkQuality3(0)
     , mParentLinkQuality2(0)
@@ -129,7 +130,7 @@
 
     mLeaderAloc.InitAsThreadOriginRealmLocalScope();
 
-    meshLocalPrefix.SetFromExtendedPanId(Get<Mac::Mac>().GetExtendedPanId());
+    meshLocalPrefix.SetFromExtendedPanId(Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId());
 
     mMeshLocal64.InitAsThreadOriginRealmLocalScope();
     mMeshLocal64.GetAddress().GetIid().GenerateRandom();
@@ -217,14 +218,14 @@
 
     if ((aMode == kAnnounceAttach) || (GetRloc16() == Mac::kShortAddrInvalid))
     {
-        IgnoreError(BecomeChild(kAttachAny));
+        Attach(kAnyPartition);
     }
 #if OPENTHREAD_FTD
     else if (IsActiveRouter(GetRloc16()))
     {
         if (Get<MleRouter>().BecomeRouter(ThreadStatusTlv::kTooFewRouters) != kErrorNone)
         {
-            IgnoreError(BecomeChild(kAttachAny));
+            Attach(kAnyPartition);
         }
     }
 #endif
@@ -242,8 +243,8 @@
 {
     if (aMode == kUpdateNetworkDatasets)
     {
-        Get<MeshCoP::ActiveDataset>().HandleDetach();
-        Get<MeshCoP::PendingDataset>().HandleDetach();
+        Get<MeshCoP::ActiveDatasetManager>().HandleDetach();
+        Get<MeshCoP::PendingDatasetManager>().HandleDetach();
     }
 
     VerifyOrExit(!IsDisabled());
@@ -267,7 +268,7 @@
 
     SuccessOrExit(Get<Notifier>().Update(mRole, aRole, kEventThreadRoleChanged));
 
-    otLogNoteMle("Role %s -> %s", RoleToString(oldRole), RoleToString(mRole));
+    LogNote("Role %s -> %s", RoleToString(oldRole), RoleToString(mRole));
 
     switch (mRole)
     {
@@ -295,38 +296,42 @@
     }
 
 exit:
-    OT_UNUSED_VARIABLE(oldRole);
+    return;
 }
 
 void Mle::SetAttachState(AttachState aState)
 {
     VerifyOrExit(aState != mAttachState);
-    otLogInfoMle("AttachState %s -> %s", AttachStateToString(mAttachState), AttachStateToString(aState));
+    LogInfo("AttachState %s -> %s", AttachStateToString(mAttachState), AttachStateToString(aState));
     mAttachState = aState;
 
 exit:
     return;
 }
 
-Error Mle::Restore(void)
+void Mle::Restore(void)
 {
-    Error                 error = kErrorNone;
     Settings::NetworkInfo networkInfo;
     Settings::ParentInfo  parentInfo;
 
-    IgnoreError(Get<MeshCoP::ActiveDataset>().Restore());
-    IgnoreError(Get<MeshCoP::PendingDataset>().Restore());
+    IgnoreError(Get<MeshCoP::ActiveDatasetManager>().Restore());
+    IgnoreError(Get<MeshCoP::PendingDatasetManager>().Restore());
 
 #if OPENTHREAD_CONFIG_DUA_ENABLE
     Get<DuaManager>().Restore();
 #endif
 
-    SuccessOrExit(error = Get<Settings>().Read(networkInfo));
+    SuccessOrExit(Get<Settings>().Read(networkInfo));
 
     Get<KeyManager>().SetCurrentKeySequence(networkInfo.GetKeySequence());
     Get<KeyManager>().SetMleFrameCounter(networkInfo.GetMleFrameCounter());
     Get<KeyManager>().SetAllMacFrameCounters(networkInfo.GetMacFrameCounter());
+
+#if OPENTHREAD_MTD
+    mDeviceMode.Set(networkInfo.GetDeviceMode() & ~DeviceMode::kModeFullThreadDevice);
+#else
     mDeviceMode.Set(networkInfo.GetDeviceMode());
+#endif
 
     // force re-attach when version mismatch.
     VerifyOrExit(networkInfo.GetVersion() == kThreadVersion);
@@ -342,7 +347,12 @@
         ExitNow();
     }
 
-    Get<Mac::Mac>().SetShortAddress(networkInfo.GetRloc16());
+#if OPENTHREAD_MTD
+    if (!IsActiveRouter(networkInfo.GetRloc16()))
+#endif
+    {
+        Get<Mac::Mac>().SetShortAddress(networkInfo.GetRloc16());
+    }
     Get<Mac::Mac>().SetExtAddress(networkInfo.GetExtAddress());
 
     mMeshLocal64.GetAddress().SetIid(networkInfo.GetMeshLocalIid());
@@ -354,9 +364,7 @@
 
     if (!IsActiveRouter(networkInfo.GetRloc16()))
     {
-        error = Get<Settings>().Read(parentInfo);
-
-        if (error != kErrorNone)
+        if (Get<Settings>().Read(parentInfo) != kErrorNone)
         {
             // If the restored RLOC16 corresponds to an end-device, it
             // is expected that the `ParentInfo` settings to be valid
@@ -364,8 +372,8 @@
             // setting by skipping the re-attach ("Child Update Request"
             // exchange) and going through the full attach process.
 
-            otLogWarnMle("Invalid settings - no saved parent info with valid end-device RLOC16 0x%04x",
-                         networkInfo.GetRloc16());
+            LogWarn("Invalid settings - no saved parent info with valid end-device RLOC16 0x%04x",
+                    networkInfo.GetRloc16());
             ExitNow();
         }
 
@@ -388,11 +396,11 @@
     }
 #endif
 
-    // Sucessfully restored the network information from non-volatile settings after boot.
+    // Successfully restored the network information from non-volatile settings after boot.
     mHasRestored = true;
 
 exit:
-    return error;
+    return;
 }
 
 Error Mle::Store(void)
@@ -451,7 +459,7 @@
     Get<KeyManager>().SetStoredMleFrameCounter(networkInfo.GetMleFrameCounter());
     Get<KeyManager>().SetStoredMacFrameCounter(networkInfo.GetMacFrameCounter());
 
-    otLogDebgMle("Store Network Information");
+    LogDebg("Store Network Information");
 
 exit:
     return error;
@@ -472,7 +480,7 @@
     // not in reattach stage after reset
     if (mReattachState == kReattachStop)
     {
-        Get<MeshCoP::PendingDataset>().HandleDetach();
+        Get<MeshCoP::PendingDatasetManager>().HandleDetach();
     }
 
 #if OPENTHREAD_CONFIG_PARENT_SEARCH_ENABLE
@@ -482,19 +490,29 @@
     SetStateDetached();
     mParent.SetState(Neighbor::kStateInvalid);
     SetRloc16(Mac::kShortAddrInvalid);
-    IgnoreError(BecomeChild(kAttachAny));
+    Attach(kAnyPartition);
 
 exit:
     return error;
 }
 
-Error Mle::BecomeChild(AttachMode aMode)
+Error Mle::BecomeChild(void)
 {
     Error error = kErrorNone;
 
     VerifyOrExit(!IsDisabled(), error = kErrorInvalidState);
     VerifyOrExit(!IsAttaching(), error = kErrorBusy);
 
+    Attach(kAnyPartition);
+
+exit:
+    return error;
+}
+
+void Mle::Attach(AttachMode aMode)
+{
+    VerifyOrExit(!IsDisabled() && !IsAttaching());
+
     if (!IsDetached())
     {
         mAttachCounter = 0;
@@ -502,7 +520,7 @@
 
     if (mReattachState == kReattachStart)
     {
-        if (Get<MeshCoP::ActiveDataset>().Restore() == kErrorNone)
+        if (Get<MeshCoP::ActiveDatasetManager>().Restore() == kErrorNone)
         {
             mReattachState = kReattachActive;
         }
@@ -514,9 +532,9 @@
 
     mParentCandidate.Clear();
     SetAttachState(kAttachStateStart);
-    mParentRequestMode = aMode;
+    mAttachMode = aMode;
 
-    if (aMode != kAttachBetter)
+    if (aMode != kBetterPartition)
     {
 #if OPENTHREAD_FTD
         if (IsFullThreadDevice())
@@ -550,7 +568,7 @@
     }
 
 exit:
-    return error;
+    return;
 }
 
 uint32_t Mle::GetAttachStartDelay(void) const
@@ -590,8 +608,8 @@
         delay += jitter;
     }
 
-    otLogNoteMle("Attach attempt %d unsuccessful, will try again in %u.%03u seconds", mAttachCounter, delay / 1000,
-                 delay % 1000);
+    LogNote("Attach attempt %d unsuccessful, will try again in %u.%03u seconds", mAttachCounter, delay / 1000,
+            delay % 1000);
 
 exit:
     return delay;
@@ -666,7 +684,7 @@
 #if OPENTHREAD_FTD
     if (IsFullThreadDevice())
     {
-        Get<MleRouter>().HandleChildStart(mParentRequestMode);
+        Get<MleRouter>().HandleChildStart(mAttachMode);
     }
 #endif
 
@@ -746,6 +764,10 @@
     Error      error   = kErrorNone;
     DeviceMode oldMode = mDeviceMode;
 
+#if OPENTHREAD_MTD
+    VerifyOrExit(!aDeviceMode.IsFullThreadDevice(), error = kErrorInvalidArgs);
+#endif
+
     VerifyOrExit(aDeviceMode.IsValid(), error = kErrorInvalidArgs);
     VerifyOrExit(mDeviceMode != aDeviceMode);
     mDeviceMode = aDeviceMode;
@@ -758,7 +780,7 @@
     Get<Utils::Otns>().EmitDeviceMode(mDeviceMode);
 #endif
 
-    otLogNoteMle("Mode 0x%02x -> 0x%02x [%s]", oldMode.Get(), mDeviceMode.Get(), mDeviceMode.ToString().AsCString());
+    LogNote("Mode 0x%02x -> 0x%02x [%s]", oldMode.Get(), mDeviceMode.Get(), mDeviceMode.ToString().AsCString());
 
     IgnoreError(Store());
 
@@ -770,7 +792,7 @@
     case kRoleDetached:
         mAttachCounter = 0;
         SetStateDetached();
-        IgnoreError(BecomeChild(kAttachAny));
+        Attach(kAnyPartition);
         break;
 
     case kRoleChild:
@@ -919,7 +941,7 @@
 
     if (aRloc16 != oldRloc16)
     {
-        otLogNoteMle("RLOC16 %04x -> %04x", oldRloc16, aRloc16);
+        LogNote("RLOC16 %04x -> %04x", oldRloc16, aRloc16);
     }
 
     if (Get<ThreadNetif>().HasUnicastAddress(mMeshLocal16) &&
@@ -1009,44 +1031,78 @@
     return mLeaderData;
 }
 
-Message *Mle::NewMleMessage(void)
+Message *Mle::NewMleMessage(Command aCommand)
 {
+    Error             error = kErrorNone;
     Message *         message;
     Message::Settings settings(Message::kNoLinkSecurity, Message::kPriorityNet);
+    Message::SubType  subType;
+    uint8_t           securitySuite;
 
     message = mSocket.NewMessage(0, settings);
-    VerifyOrExit(message != nullptr);
+    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
-    message->SetSubType(Message::kSubTypeMleGeneral);
+    securitySuite = k154Security;
+    subType       = Message::kSubTypeMleGeneral;
+
+    switch (aCommand)
+    {
+    case kCommandAnnounce:
+        subType = Message::kSubTypeMleAnnounce;
+        break;
+
+    case kCommandDiscoveryRequest:
+        subType       = Message::kSubTypeMleDiscoverRequest;
+        securitySuite = kNoSecurity;
+        break;
+
+    case kCommandDiscoveryResponse:
+        subType       = Message::kSubTypeMleDiscoverResponse;
+        securitySuite = kNoSecurity;
+        break;
+
+    case kCommandChildUpdateRequest:
+        subType = Message::kSubTypeMleChildUpdateRequest;
+        break;
+
+    case kCommandDataResponse:
+        subType = Message::kSubTypeMleDataResponse;
+        break;
+
+    case kCommandChildIdRequest:
+        subType = Message::kSubTypeMleChildIdRequest;
+        break;
+
+    case kCommandDataRequest:
+        subType = Message::kSubTypeMleDataRequest;
+        break;
+
+    default:
+        break;
+    }
+
+    message->SetSubType(subType);
+
+    SuccessOrExit(error = message->Append(securitySuite));
+
+    if (securitySuite == k154Security)
+    {
+        SecurityHeader securityHeader;
+
+        // The other fields in security header are updated in the
+        // message in `SendMessage()` before message is sent.
+
+        securityHeader.InitSecurityControl();
+        SuccessOrExit(error = message->Append(securityHeader));
+    }
+
+    error = message->Append<uint8_t>(aCommand);
 
 exit:
+    FreeAndNullMessageOnError(message, error);
     return message;
 }
 
-Error Mle::AppendHeader(Message &aMessage, Command aCommand)
-{
-    Error  error = kErrorNone;
-    Header header;
-
-    header.Init();
-
-    if (aCommand == kCommandDiscoveryRequest || aCommand == kCommandDiscoveryResponse)
-    {
-        header.SetSecuritySuite(Header::kNoSecurity);
-    }
-    else
-    {
-        header.SetKeyIdMode2();
-    }
-
-    header.SetCommand(aCommand);
-
-    SuccessOrExit(error = aMessage.AppendBytes(&header, header.GetLength()));
-
-exit:
-    return error;
-}
-
 Error Mle::AppendSourceAddress(Message &aMessage) const
 {
     return Tlv::Append<SourceAddressTlv>(aMessage, GetRloc16());
@@ -1424,7 +1480,7 @@
 Error Mle::AppendActiveTimestamp(Message &aMessage)
 {
     Error                     error     = kErrorNone;
-    const MeshCoP::Timestamp *timestamp = Get<MeshCoP::ActiveDataset>().GetTimestamp();
+    const MeshCoP::Timestamp *timestamp = Get<MeshCoP::ActiveDatasetManager>().GetTimestamp();
 
     VerifyOrExit(timestamp != nullptr);
     error = Tlv::Append<ActiveTimestampTlv>(aMessage, *timestamp);
@@ -1436,7 +1492,7 @@
 Error Mle::AppendPendingTimestamp(Message &aMessage)
 {
     Error                     error     = kErrorNone;
-    const MeshCoP::Timestamp *timestamp = Get<MeshCoP::PendingDataset>().GetTimestamp();
+    const MeshCoP::Timestamp *timestamp = Get<MeshCoP::PendingDatasetManager>().GetTimestamp();
 
     VerifyOrExit(timestamp != nullptr && timestamp->GetSeconds() != 0);
     error = Tlv::Append<PendingTimestampTlv>(aMessage, *timestamp);
@@ -1711,43 +1767,66 @@
     aTimer.Get<Mle>().HandleAttachTimer();
 }
 
-void Mle::HandleAttachTimer(void)
+bool Mle::HasAcceptableParentCandidate(void) const
 {
-    uint32_t delay          = 0;
-    bool     shouldAnnounce = true;
+    bool        hasAcceptableParent = false;
+    LinkQuality linkQuality;
 
-    if (mAttachState == kAttachStateParentRequestRouter || mAttachState == kAttachStateParentRequestReed ||
-        (mAttachState == kAttachStateAnnounce && !HasMoreChannelsToAnnouce()))
+    VerifyOrExit(mParentCandidate.IsStateParentResponse());
+
+    switch (mAttachState)
     {
-        uint8_t linkQuality;
+    case kAttachStateAnnounce:
+        VerifyOrExit(!HasMoreChannelsToAnnouce());
+        break;
 
-        linkQuality = mParentCandidate.GetLinkInfo().GetLinkQuality();
+    case kAttachStateParentRequestRouter:
+        // If we cannot find a parent with best link quality (3) when
+        // in `kAttachStateParentRequestRouter` state we will keep the
+        // candidate and forward to REED stage to potentially find a
+        // better parent.
+        linkQuality = OT_MIN(mParentCandidate.GetLinkInfo().GetLinkQuality(), mParentCandidate.GetLinkQualityOut());
+        VerifyOrExit(linkQuality == kLinkQuality3);
+        break;
 
-        if (linkQuality > mParentCandidate.GetLinkQualityOut())
-        {
-            linkQuality = mParentCandidate.GetLinkQualityOut();
-        }
+    case kAttachStateParentRequestReed:
+        break;
 
+    default:
+        ExitNow();
+    }
+
+    if (IsChild())
+    {
         // If already attached, accept the parent candidate if
         // we are trying to attach to a better partition or if a
         // Parent Response was also received from the current parent
         // to which the device is attached. This ensures that the
         // new parent candidate is compared with the current parent
         // and that it is indeed preferred over the current one.
-        // If we are in kAttachStateParentRequestRouter and cannot
-        // find a parent with best link quality(3), we will keep
-        // the candidate and forward to REED stage to find a better
-        // parent.
 
-        if ((linkQuality == 3 || mAttachState != kAttachStateParentRequestRouter) &&
-            mParentCandidate.IsStateParentResponse() &&
-            (!IsChild() || mReceivedResponseFromParent || mParentRequestMode == kAttachBetter) &&
-            SendChildIdRequest() == kErrorNone)
-        {
-            SetAttachState(kAttachStateChildIdRequest);
-            delay = kParentRequestReedTimeout;
-            ExitNow();
-        }
+        VerifyOrExit(mReceivedResponseFromParent || (mAttachMode == kBetterPartition));
+    }
+
+    hasAcceptableParent = true;
+
+exit:
+    return hasAcceptableParent;
+}
+
+void Mle::HandleAttachTimer(void)
+{
+    uint32_t delay          = 0;
+    bool     shouldAnnounce = true;
+
+    // First, check if we are waiting to receive parent responses and
+    // found an acceptable parent candidate.
+
+    if (HasAcceptableParentCandidate() && (SendChildIdRequest() == kErrorNone))
+    {
+        SetAttachState(kAttachStateChildIdRequest);
+        delay = kParentRequestReedTimeout;
+        ExitNow();
     }
 
     switch (mAttachState)
@@ -1763,13 +1842,13 @@
     case kAttachStateStart:
         if (mAttachCounter > 0)
         {
-            otLogNoteMle("Attempt to attach - attempt %d, %s %s", mAttachCounter,
-                         AttachModeToString(mParentRequestMode), ReattachStateToString(mReattachState));
+            LogNote("Attempt to attach - attempt %d, %s %s", mAttachCounter, AttachModeToString(mAttachMode),
+                    ReattachStateToString(mReattachState));
         }
         else
         {
-            otLogNoteMle("Attempt to attach - %s %s", AttachModeToString(mParentRequestMode),
-                         ReattachStateToString(mReattachState));
+            LogNote("Attempt to attach - %s %s", AttachModeToString(mAttachMode),
+                    ReattachStateToString(mReattachState));
         }
 
         SetAttachState(kAttachStateParentRequestRouter);
@@ -1779,16 +1858,16 @@
 
         // initial MLE Parent Request has both E and R flags set in Scan Mask TLV
         // during reattach when losing connectivity.
-        if (mParentRequestMode == kAttachSame1 || mParentRequestMode == kAttachSame2)
+        if (mAttachMode == kSamePartition || mAttachMode == kSamePartitionRetry)
         {
-            IgnoreError(SendParentRequest(kParentRequestTypeRoutersAndReeds));
+            SendParentRequest(kToRoutersAndReeds);
             delay = kParentRequestReedTimeout;
         }
         // initial MLE Parent Request has only R flag set in Scan Mask TLV for
         // during initial attach or downgrade process
         else
         {
-            IgnoreError(SendParentRequest(kParentRequestTypeRouters));
+            SendParentRequest(kToRouters);
             delay = kParentRequestRouterTimeout;
         }
 
@@ -1796,7 +1875,7 @@
 
     case kAttachStateParentRequestRouter:
         SetAttachState(kAttachStateParentRequestReed);
-        IgnoreError(SendParentRequest(kParentRequestTypeRoutersAndReeds));
+        SendParentRequest(kToRoutersAndReeds);
         delay = kParentRequestReedTimeout;
         break;
 
@@ -1814,7 +1893,7 @@
             // (with `mAnnounceDelay` wait between them).
 
             SetAttachState(kAttachStateAnnounce);
-            IgnoreError(SendParentRequest(kParentRequestTypeRoutersAndReeds));
+            SendParentRequest(kToRoutersAndReeds);
             mAnnounceChannel = Mac::ChannelMask::kChannelIteratorFirst;
             delay            = mAnnounceDelay;
             break;
@@ -1853,9 +1932,9 @@
     Mac::ChannelMask channelMask;
 
     VerifyOrExit(!IsChild() && (mReattachState == kReattachStop) &&
-                 (Get<MeshCoP::ActiveDataset>().IsPartiallyComplete() || !IsFullThreadDevice()));
+                 (Get<MeshCoP::ActiveDatasetManager>().IsPartiallyComplete() || !IsFullThreadDevice()));
 
-    if (Get<MeshCoP::ActiveDataset>().GetChannelMask(channelMask) != kErrorNone)
+    if (Get<MeshCoP::ActiveDatasetManager>().GetChannelMask(channelMask) != kErrorNone)
     {
         channelMask = Get<Mac::Mac>().GetSupportedChannelMask();
     }
@@ -1879,9 +1958,9 @@
 
     if (mReattachState == kReattachActive)
     {
-        if (Get<MeshCoP::PendingDataset>().Restore() == kErrorNone)
+        if (Get<MeshCoP::PendingDatasetManager>().Restore() == kErrorNone)
         {
-            IgnoreError(Get<MeshCoP::PendingDataset>().ApplyConfiguration());
+            IgnoreError(Get<MeshCoP::PendingDatasetManager>().ApplyConfiguration());
             mReattachState = kReattachPending;
             SetAttachState(kAttachStateStart);
             delay = 1 + Random::NonCrypto::GetUint32InRange(0, kAttachStartJitter);
@@ -1894,14 +1973,15 @@
     else if (mReattachState == kReattachPending)
     {
         mReattachState = kReattachStop;
-        IgnoreError(Get<MeshCoP::ActiveDataset>().Restore());
+        IgnoreError(Get<MeshCoP::ActiveDatasetManager>().Restore());
     }
 
     VerifyOrExit(mReattachState == kReattachStop);
 
-    switch (mParentRequestMode)
+    switch (mAttachMode)
     {
-    case kAttachAny:
+    case kAnyPartition:
+    case kBetterParent:
         if (!IsChild())
         {
             if (mAlternatePanId != Mac::kPanIdBroadcast)
@@ -1931,16 +2011,16 @@
 
         break;
 
-    case kAttachSame1:
-        IgnoreError(BecomeChild(kAttachSame2));
+    case kSamePartition:
+        Attach(kSamePartitionRetry);
         break;
 
-    case kAttachSame2:
-    case kAttachSameDowngrade:
-        IgnoreError(BecomeChild(kAttachAny));
+    case kSamePartitionRetry:
+    case kDowngradeToReed:
+        Attach(kAnyPartition);
         break;
 
-    case kAttachBetter:
+    case kBetterPartition:
         break;
     }
 
@@ -1955,17 +2035,14 @@
 
 void Mle::HandleDelayedResponseTimer(void)
 {
-    DelayedResponseMetadata metadata;
-    TimeMilli               now          = TimerMilli::GetNow();
-    TimeMilli               nextSendTime = now.GetDistantFuture();
-    Message *               message;
-    Message *               nextMessage;
+    TimeMilli now          = TimerMilli::GetNow();
+    TimeMilli nextSendTime = now.GetDistantFuture();
 
-    for (message = mDelayedResponses.GetHead(); message != nullptr; message = nextMessage)
+    for (Message &message : mDelayedResponses)
     {
-        nextMessage = message->GetNext();
+        DelayedResponseMetadata metadata;
 
-        metadata.ReadFrom(*message);
+        metadata.ReadFrom(message);
 
         if (now < metadata.mSendTime)
         {
@@ -1976,37 +2053,8 @@
         }
         else
         {
-            Error error = kErrorNone;
-
-            mDelayedResponses.Dequeue(*message);
-            metadata.RemoveFrom(*message);
-
-            if (message->GetSubType() == Message::kSubTypeMleDataRequest)
-            {
-                error = AppendActiveTimestamp(*message);
-                error = (error == kErrorNone) ? AppendPendingTimestamp(*message) : error;
-            }
-
-            error = (error == kErrorNone) ? SendMessage(*message, metadata.mDestination) : error;
-
-            if (error == kErrorNone)
-            {
-                Log(kMessageSend, kTypeGenericDelayed, metadata.mDestination);
-
-                // Here enters fast poll mode, as for Rx-Off-when-idle device, the enqueued msg should
-                // be Mle Data Request.
-                // Note: Finer-grade check (e.g. message subtype) might be required when deciding whether
-                // or not enters fast poll mode fast poll mode if there are other type of delayed message
-                // for Rx-Off-when-idle device.
-                if (!IsRxOnWhenIdle())
-                {
-                    Get<DataPollSender>().SendFastPolls(DataPollSender::kDefaultFastPolls);
-                }
-            }
-            else
-            {
-                message->Free();
-            }
+            mDelayedResponses.Dequeue(message);
+            SendDelayedResponse(message, metadata);
         }
     }
 
@@ -2016,48 +2064,63 @@
     }
 }
 
+void Mle::SendDelayedResponse(Message &aMessage, const DelayedResponseMetadata &aMetadata)
+{
+    Error error = kErrorNone;
+
+    aMetadata.RemoveFrom(aMessage);
+
+    if (aMessage.GetSubType() == Message::kSubTypeMleDataRequest)
+    {
+        SuccessOrExit(error = AppendActiveTimestamp(aMessage));
+        SuccessOrExit(error = AppendPendingTimestamp(aMessage));
+    }
+
+    SuccessOrExit(error = SendMessage(aMessage, aMetadata.mDestination));
+
+    Log(kMessageSend, kTypeGenericDelayed, aMetadata.mDestination);
+
+    if (!IsRxOnWhenIdle())
+    {
+        // Start fast poll mode, assuming enqueued msg is MLE Data Request.
+        // Note: Finer-grade check may be required when deciding whether or
+        // not to enter fast poll mode for other type of delayed message.
+
+        Get<DataPollSender>().SendFastPolls(DataPollSender::kDefaultFastPolls);
+    }
+
+exit:
+    FreeMessageOnError(&aMessage, error);
+}
+
 void Mle::RemoveDelayedDataResponseMessage(void)
 {
-    Message *               message = mDelayedResponses.GetHead();
-    DelayedResponseMetadata metadata;
-
-    while (message != nullptr)
-    {
-        metadata.ReadFrom(*message);
-
-        if (message->GetSubType() == Message::kSubTypeMleDataResponse)
-        {
-            mDelayedResponses.DequeueAndFree(*message);
-            Log(kMessageRemoveDelayed, kTypeDataResponse, metadata.mDestination);
-
-            // no more than one multicast MLE Data Response in Delayed Message Queue.
-            break;
-        }
-
-        message = message->GetNext();
-    }
+    RemoveDelayedMessage(Message::kSubTypeMleDataResponse, kTypeDataResponse, nullptr);
 }
 
 void Mle::RemoveDelayedDataRequestMessage(const Ip6::Address &aDestination)
 {
-    for (Message *message = mDelayedResponses.GetHead(); message != nullptr; message = message->GetNext())
+    RemoveDelayedMessage(Message::kSubTypeMleDataRequest, kTypeDataRequest, &aDestination);
+}
+
+void Mle::RemoveDelayedMessage(Message::SubType aSubType, MessageType aMessageType, const Ip6::Address *aDestination)
+{
+    for (Message &message : mDelayedResponses)
     {
         DelayedResponseMetadata metadata;
 
-        metadata.ReadFrom(*message);
+        metadata.ReadFrom(message);
 
-        if (message->GetSubType() == Message::kSubTypeMleDataRequest && metadata.mDestination == aDestination)
+        if ((message.GetSubType() == aSubType) &&
+            ((aDestination == nullptr) || (metadata.mDestination == *aDestination)))
         {
-            mDelayedResponses.DequeueAndFree(*message);
-            Log(kMessageRemoveDelayed, kTypeDataRequest, metadata.mDestination);
-
-            // no more than one MLE Data Request for the destination in Delayed Message Queue.
-            break;
+            mDelayedResponses.DequeueAndFree(message);
+            Log(kMessageRemoveDelayed, aMessageType, metadata.mDestination);
         }
     }
 }
 
-Error Mle::SendParentRequest(ParentRequestType aType)
+void Mle::SendParentRequest(ParentRequestType aType)
 {
     Error        error = kErrorNone;
     Message *    message;
@@ -2068,17 +2131,16 @@
 
     switch (aType)
     {
-    case kParentRequestTypeRouters:
+    case kToRouters:
         scanMask = ScanMaskTlv::kRouterFlag;
         break;
 
-    case kParentRequestTypeRoutersAndReeds:
+    case kToRoutersAndReeds:
         scanMask = ScanMaskTlv::kRouterFlag | ScanMaskTlv::kEndDeviceFlag;
         break;
     }
 
-    VerifyOrExit((message = NewMleMessage()) != nullptr, error = kErrorNoBufs);
-    SuccessOrExit(error = AppendHeader(*message, kCommandParentRequest));
+    VerifyOrExit((message = NewMleMessage(kCommandParentRequest)) != nullptr, error = kErrorNoBufs);
     SuccessOrExit(error = AppendMode(*message, mDeviceMode));
     SuccessOrExit(error = AppendChallenge(*message, mParentRequestChallenge));
     SuccessOrExit(error = AppendScanMask(*message, scanMask));
@@ -2092,18 +2154,17 @@
 
     switch (aType)
     {
-    case kParentRequestTypeRouters:
+    case kToRouters:
         Log(kMessageSend, kTypeParentRequestToRouters, destination);
         break;
 
-    case kParentRequestTypeRoutersAndReeds:
+    case kToRoutersAndReeds:
         Log(kMessageSend, kTypeParentRequestToRoutersReeds, destination);
         break;
     }
 
 exit:
     FreeMessageOnError(message, error);
-    return error;
 }
 
 void Mle::RequestShorterChildIdRequest(void)
@@ -2127,7 +2188,7 @@
     {
         if (IsChild())
         {
-            otLogInfoMle("Already attached to candidate parent");
+            LogInfo("Already attached to candidate parent");
             ExitNow(error = kErrorAlready);
         }
         else
@@ -2141,9 +2202,7 @@
         }
     }
 
-    VerifyOrExit((message = NewMleMessage()) != nullptr, error = kErrorNoBufs);
-    message->SetSubType(Message::kSubTypeMleChildIdRequest);
-    SuccessOrExit(error = AppendHeader(*message, kCommandChildIdRequest));
+    VerifyOrExit((message = NewMleMessage(kCommandChildIdRequest)) != nullptr, error = kErrorNoBufs);
     SuccessOrExit(error = AppendResponse(*message, mParentCandidateChallenge));
     SuccessOrExit(error = AppendLinkFrameCounter(*message));
     SuccessOrExit(error = AppendMleFrameCounter(*message));
@@ -2190,17 +2249,12 @@
                            const uint8_t *     aExtraTlvs,
                            uint8_t             aExtraTlvsLength)
 {
-    OT_UNUSED_VARIABLE(aExtraTlvs);
-    OT_UNUSED_VARIABLE(aExtraTlvsLength);
-
     Error    error = kErrorNone;
     Message *message;
 
     RemoveDelayedDataRequestMessage(aDestination);
 
-    VerifyOrExit((message = NewMleMessage()) != nullptr, error = kErrorNoBufs);
-    message->SetSubType(Message::kSubTypeMleDataRequest);
-    SuccessOrExit(error = AppendHeader(*message, kCommandDataRequest));
+    VerifyOrExit((message = NewMleMessage(kCommandDataRequest)) != nullptr, error = kErrorNoBufs);
     SuccessOrExit(error = AppendTlvRequest(*message, aTlvs, aTlvsLength));
 
     if (aExtraTlvs != nullptr && aExtraTlvsLength > 0)
@@ -2375,7 +2429,7 @@
 
     if (!mParent.IsStateValidOrRestoring())
     {
-        otLogWarnMle("No valid parent when sending Child Update Request");
+        LogWarn("No valid parent when sending Child Update Request");
         IgnoreError(BecomeDetached());
         ExitNow();
     }
@@ -2383,9 +2437,7 @@
     mChildUpdateRequestState = kChildUpdateRequestActive;
     ScheduleMessageTransmissionTimer();
 
-    VerifyOrExit((message = NewMleMessage()) != nullptr, error = kErrorNoBufs);
-    message->SetSubType(Message::kSubTypeMleChildUpdateRequest);
-    SuccessOrExit(error = AppendHeader(*message, kCommandChildUpdateRequest));
+    VerifyOrExit((message = NewMleMessage(kCommandChildUpdateRequest)) != nullptr, error = kErrorNoBufs);
     SuccessOrExit(error = AppendMode(*message, mDeviceMode));
 
     switch (mRole)
@@ -2452,8 +2504,7 @@
     Message *    message;
     bool         checkAddress = false;
 
-    VerifyOrExit((message = NewMleMessage()) != nullptr, error = kErrorNoBufs);
-    SuccessOrExit(error = AppendHeader(*message, kCommandChildUpdateResponse));
+    VerifyOrExit((message = NewMleMessage(kCommandChildUpdateResponse)) != nullptr, error = kErrorNoBufs);
     SuccessOrExit(error = AppendSourceAddress(*message));
     SuccessOrExit(error = AppendLeaderData(*message));
 
@@ -2538,11 +2589,9 @@
     Message *          message = nullptr;
 
     VerifyOrExit(Get<Mac::Mac>().GetSupportedChannelMask().ContainsChannel(aChannel), error = kErrorInvalidArgs);
-    VerifyOrExit((message = NewMleMessage()) != nullptr, error = kErrorNoBufs);
+    VerifyOrExit((message = NewMleMessage(kCommandAnnounce)) != nullptr, error = kErrorNoBufs);
     message->SetLinkSecurityEnabled(true);
-    message->SetSubType(Message::kSubTypeMleAnnounce);
     message->SetChannel(aChannel);
-    SuccessOrExit(error = AppendHeader(*message, kCommandAnnounce));
 
     channel.Init();
     channel.SetChannelPage(0);
@@ -2566,7 +2615,7 @@
 
     SuccessOrExit(error = SendMessage(*message, aDestination));
 
-    otLogInfoMle("Send Announce on channel %d", aChannel);
+    LogInfo("Send Announce on channel %d", aChannel);
 
 exit:
     FreeMessageOnError(message, error);
@@ -2580,7 +2629,7 @@
 
     Mac::ChannelMask channelMask;
 
-    if (Get<MeshCoP::ActiveDataset>().GetChannelMask(channelMask) != kErrorNone)
+    if (Get<MeshCoP::ActiveDatasetManager>().GetChannelMask(channelMask) != kErrorNone)
     {
         channelMask = Get<Mac::Mac>().GetSupportedChannelMask();
     }
@@ -2603,8 +2652,7 @@
     Tlv      tlv;
     ot::Tlv  statusSubTlv;
 
-    VerifyOrExit((message = NewMleMessage()) != nullptr, error = kErrorNoBufs);
-    SuccessOrExit(error = AppendHeader(*message, kCommandLinkMetricsManagementResponse));
+    VerifyOrExit((message = NewMleMessage(kCommandLinkMetricsManagementResponse)) != nullptr, error = kErrorNoBufs);
 
     tlv.SetType(Tlv::kLinkMetricsManagement);
     statusSubTlv.SetType(LinkMetrics::SubTlv::kStatus);
@@ -2630,8 +2678,7 @@
     Message *message;
     Tlv      tlv;
 
-    VerifyOrExit((message = NewMleMessage()) != nullptr, error = kErrorNoBufs);
-    SuccessOrExit(error = AppendHeader(*message, kCommandLinkProbe));
+    VerifyOrExit((message = NewMleMessage(kCommandLinkProbe)) != nullptr, error = kErrorNoBufs);
 
     tlv.SetType(Tlv::kLinkProbe);
     tlv.SetLength(sizeof(aSeriesId) + aLength);
@@ -2648,61 +2695,130 @@
 }
 #endif
 
+Error Mle::ProcessMessageSecurity(Crypto::AesCcm::Mode    aMode,
+                                  Message &               aMessage,
+                                  const Ip6::MessageInfo &aMessageInfo,
+                                  uint16_t                aCmdOffset,
+                                  const SecurityHeader &  aHeader)
+{
+    // This method performs MLE message security. Based on `aMode` it
+    // can be used to encrypt and append tag to `aMessage` or to
+    // decrypt and validate the tag in a received `aMessage` (which is
+    // then removed from `aMessage`).
+    //
+    // `aCmdOffset` in both cases specifies the offset in `aMessage`
+    // to the start of MLE payload (i.e., the command field).
+    //
+    // When decrypting, possible errors are:
+    // `kErrorNone` decrypted and verified tag, tag is also removed.
+    // `kErrorParse` message does not contain the tag
+    // `kErrorSecurity` message tag is invalid.
+    //
+    // When encrypting, possible errors are:
+    // `kErrorNone` message encrypted and tag appended to message.
+    // `kErrorNoBufs` could not grow the message to append the tag.
+
+    Error               error = kErrorNone;
+    Crypto::AesCcm      aesCcm;
+    uint8_t             nonce[Crypto::AesCcm::kNonceSize];
+    uint8_t             tag[kMleSecurityTagSize];
+    Mac::ExtAddress     extAddress;
+    uint32_t            keySequence;
+    uint16_t            payloadLength   = aMessage.GetLength() - aCmdOffset;
+    const Ip6::Address *senderAddress   = &aMessageInfo.GetSockAddr();
+    const Ip6::Address *receiverAddress = &aMessageInfo.GetPeerAddr();
+
+    switch (aMode)
+    {
+    case Crypto::AesCcm::kEncrypt:
+        // Use the initialized values for `senderAddress`,
+        // `receiverAddress` and `payloadLength`
+        break;
+
+    case Crypto::AesCcm::kDecrypt:
+        senderAddress   = &aMessageInfo.GetPeerAddr();
+        receiverAddress = &aMessageInfo.GetSockAddr();
+        // Ensure message contains command field (uint8_t) and
+        // tag. Then exclude the tag from payload to decrypt.
+        VerifyOrExit(aCmdOffset + sizeof(uint8_t) + kMleSecurityTagSize <= aMessage.GetLength(), error = kErrorParse);
+        payloadLength -= kMleSecurityTagSize;
+        break;
+    }
+
+    senderAddress->GetIid().ConvertToExtAddress(extAddress);
+    Crypto::AesCcm::GenerateNonce(extAddress, aHeader.GetFrameCounter(), Mac::Frame::kSecEncMic32, nonce);
+
+    keySequence = aHeader.GetKeyId();
+
+    aesCcm.SetKey(keySequence == Get<KeyManager>().GetCurrentKeySequence()
+                      ? Get<KeyManager>().GetCurrentMleKey()
+                      : Get<KeyManager>().GetTemporaryMleKey(keySequence));
+
+    aesCcm.Init(sizeof(Ip6::Address) + sizeof(Ip6::Address) + sizeof(SecurityHeader), payloadLength,
+                kMleSecurityTagSize, nonce, sizeof(nonce));
+
+    aesCcm.Header(*senderAddress);
+    aesCcm.Header(*receiverAddress);
+    aesCcm.Header(aHeader);
+
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+    if (aMode == Crypto::AesCcm::kDecrypt)
+    {
+        // Skip decrypting the message under fuzz build mode
+        IgnoreError(aMessage.SetLength(aMessage.GetLength() - kMleSecurityTagSize));
+        ExitNow();
+    }
+#endif
+
+    aesCcm.Payload(aMessage, aCmdOffset, payloadLength, aMode);
+    aesCcm.Finalize(tag);
+
+    if (aMode == Crypto::AesCcm::kEncrypt)
+    {
+        SuccessOrExit(error = aMessage.Append(tag));
+    }
+    else
+    {
+        VerifyOrExit(aMessage.Compare(aMessage.GetLength() - kMleSecurityTagSize, tag), error = kErrorSecurity);
+        IgnoreError(aMessage.SetLength(aMessage.GetLength() - kMleSecurityTagSize));
+    }
+
+exit:
+    return error;
+}
+
 Error Mle::SendMessage(Message &aMessage, const Ip6::Address &aDestination)
 {
-    Error            error = kErrorNone;
-    Header           header;
-    uint32_t         keySequence;
-    uint8_t          nonce[Crypto::AesCcm::kNonceSize];
-    uint8_t          tag[kMleSecurityTagSize];
-    Crypto::AesCcm   aesCcm;
-    uint8_t          buf[64];
-    uint16_t         length;
+    Error            error  = kErrorNone;
+    uint16_t         offset = 0;
+    uint8_t          securitySuite;
     Ip6::MessageInfo messageInfo;
 
-    IgnoreError(aMessage.Read(0, header));
-
-    if (header.GetSecuritySuite() == Header::k154Security)
-    {
-        header.SetFrameCounter(Get<KeyManager>().GetMleFrameCounter());
-
-        keySequence = Get<KeyManager>().GetCurrentKeySequence();
-        header.SetKeyId(keySequence);
-
-        aMessage.WriteBytes(0, &header, header.GetLength());
-
-        Crypto::AesCcm::GenerateNonce(Get<Mac::Mac>().GetExtAddress(), Get<KeyManager>().GetMleFrameCounter(),
-                                      Mac::Frame::kSecEncMic32, nonce);
-
-        aesCcm.SetKey(Get<KeyManager>().GetCurrentMleKey());
-        aesCcm.Init(16 + 16 + header.GetHeaderLength(), aMessage.GetLength() - (header.GetLength() - 1), sizeof(tag),
-                    nonce, sizeof(nonce));
-
-        aesCcm.Header(&mLinkLocal64.GetAddress(), sizeof(mLinkLocal64.GetAddress()));
-        aesCcm.Header(&aDestination, sizeof(aDestination));
-        aesCcm.Header(header.GetBytes() + 1, header.GetHeaderLength());
-
-        aMessage.SetOffset(header.GetLength() - 1);
-
-        while (aMessage.GetOffset() < aMessage.GetLength())
-        {
-            length = aMessage.ReadBytes(aMessage.GetOffset(), buf, sizeof(buf));
-            aesCcm.Payload(buf, buf, length, Crypto::AesCcm::kEncrypt);
-            aMessage.WriteBytes(aMessage.GetOffset(), buf, length);
-            aMessage.MoveOffset(length);
-        }
-
-        aesCcm.Finalize(tag);
-        SuccessOrExit(error = aMessage.AppendBytes(tag, sizeof(tag)));
-
-        Get<KeyManager>().IncrementMleFrameCounter();
-    }
-
     messageInfo.SetPeerAddr(aDestination);
     messageInfo.SetSockAddr(mLinkLocal64.GetAddress());
     messageInfo.SetPeerPort(kUdpPort);
     messageInfo.SetHopLimit(kMleHopLimit);
 
+    IgnoreError(aMessage.Read(offset, securitySuite));
+    offset += sizeof(securitySuite);
+
+    if (securitySuite == k154Security)
+    {
+        SecurityHeader header;
+
+        // Update the fields in the security header
+
+        IgnoreError(aMessage.Read(offset, header));
+        header.SetFrameCounter(Get<KeyManager>().GetMleFrameCounter());
+        header.SetKeyId(Get<KeyManager>().GetCurrentKeySequence());
+        aMessage.Write(offset, header);
+        offset += sizeof(SecurityHeader);
+
+        SuccessOrExit(error = ProcessMessageSecurity(Crypto::AesCcm::kEncrypt, aMessage, messageInfo, offset, header));
+
+        Get<KeyManager>().IncrementMleFrameCounter();
+    }
+
     SuccessOrExit(error = mSocket.SendTo(aMessage, messageInfo));
 
 exit:
@@ -2733,45 +2849,38 @@
 
 void Mle::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
 {
-    Error              error = kErrorNone;
-    Header             header;
-    uint32_t           keySequence;
-    const KeyMaterial *mleKey;
-    uint32_t           frameCounter;
-    uint8_t            messageTag[kMleSecurityTagSize];
-    uint8_t            nonce[Crypto::AesCcm::kNonceSize];
-    Mac::ExtAddress    extAddr;
-    Crypto::AesCcm     aesCcm;
-    uint16_t           mleOffset;
-    uint8_t            buf[64];
-    uint16_t           length;
-    uint8_t            tag[kMleSecurityTagSize];
-    uint8_t            command;
-    Neighbor *         neighbor;
-    bool               skipLoggingError = false;
+    Error           error = kErrorNone;
+    RxInfo          rxInfo(aMessage, aMessageInfo);
+    uint8_t         securitySuite;
+    SecurityHeader  header;
+    uint32_t        keySequence;
+    uint32_t        frameCounter;
+    Mac::ExtAddress extAddr;
+    uint8_t         command;
+    Neighbor *      neighbor;
 
-    otLogDebgMle("Receive UDP message");
+    LogDebg("Receive MLE message");
 
     VerifyOrExit(aMessageInfo.GetLinkInfo() != nullptr);
     VerifyOrExit(aMessageInfo.GetHopLimit() == kMleHopLimit, error = kErrorParse);
 
-    length = aMessage.ReadBytes(aMessage.GetOffset(), &header, sizeof(header));
-    VerifyOrExit(header.IsValid() && header.GetLength() <= length, error = kErrorParse);
+    SuccessOrExit(error = aMessage.Read(aMessage.GetOffset(), securitySuite));
+    aMessage.MoveOffset(sizeof(securitySuite));
 
-    if (header.GetSecuritySuite() == Header::kNoSecurity)
+    if (securitySuite == kNoSecurity)
     {
-        aMessage.MoveOffset(header.GetLength());
+        SuccessOrExit(error = aMessage.Read(aMessage.GetOffset(), command));
+        aMessage.MoveOffset(sizeof(command));
 
-        switch (header.GetCommand())
+        switch (command)
         {
 #if OPENTHREAD_FTD
         case kCommandDiscoveryRequest:
-            Get<MleRouter>().HandleDiscoveryRequest(aMessage, aMessageInfo);
+            Get<MleRouter>().HandleDiscoveryRequest(rxInfo);
             break;
 #endif
-
         case kCommandDiscoveryResponse:
-            Get<DiscoverScanner>().HandleDiscoveryResponse(aMessage, aMessageInfo);
+            Get<DiscoverScanner>().HandleDiscoveryResponse(rxInfo);
             break;
 
         default:
@@ -2782,74 +2891,28 @@
     }
 
     VerifyOrExit(!IsDisabled(), error = kErrorInvalidState);
-    VerifyOrExit(header.GetSecuritySuite() == Header::k154Security, error = kErrorParse);
+    VerifyOrExit(securitySuite == k154Security, error = kErrorParse);
 
-    keySequence = header.GetKeyId();
+    SuccessOrExit(error = aMessage.Read(aMessage.GetOffset(), header));
+    aMessage.MoveOffset(sizeof(header));
 
-    if (keySequence == Get<KeyManager>().GetCurrentKeySequence())
-    {
-        mleKey = &Get<KeyManager>().GetCurrentMleKey();
-    }
-    else
-    {
-        mleKey = &Get<KeyManager>().GetTemporaryMleKey(keySequence);
-    }
+    VerifyOrExit(header.IsSecurityControlValid(), error = kErrorParse);
 
-    VerifyOrExit(aMessage.GetOffset() + header.GetLength() + sizeof(messageTag) <= aMessage.GetLength(),
-                 error = kErrorParse);
-    aMessage.MoveOffset(header.GetLength() - 1);
-
-    IgnoreError(aMessage.Read(aMessage.GetLength() - sizeof(messageTag), messageTag));
-    SuccessOrExit(error = aMessage.SetLength(aMessage.GetLength() - sizeof(messageTag)));
-
-    aMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(extAddr);
-
+    keySequence  = header.GetKeyId();
     frameCounter = header.GetFrameCounter();
-    Crypto::AesCcm::GenerateNonce(extAddr, frameCounter, Mac::Frame::kSecEncMic32, nonce);
 
-    aesCcm.SetKey(*mleKey);
-    aesCcm.Init(sizeof(aMessageInfo.GetPeerAddr()) + sizeof(aMessageInfo.GetSockAddr()) + header.GetHeaderLength(),
-                aMessage.GetLength() - aMessage.GetOffset(), sizeof(messageTag), nonce, sizeof(nonce));
-
-    aesCcm.Header(&aMessageInfo.GetPeerAddr(), sizeof(aMessageInfo.GetPeerAddr()));
-    aesCcm.Header(&aMessageInfo.GetSockAddr(), sizeof(aMessageInfo.GetSockAddr()));
-    aesCcm.Header(header.GetBytes() + 1, header.GetHeaderLength());
-
-    mleOffset = aMessage.GetOffset();
-
-    while (aMessage.GetOffset() < aMessage.GetLength())
-    {
-        length = aMessage.ReadBytes(aMessage.GetOffset(), buf, sizeof(buf));
-        aesCcm.Payload(buf, buf, length, Crypto::AesCcm::kDecrypt);
-#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
-        aMessage.WriteBytes(aMessage.GetOffset(), buf, length);
-#endif
-        aMessage.MoveOffset(length);
-    }
-
-    aesCcm.Finalize(tag);
-#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
-    if (memcmp(messageTag, tag, sizeof(tag)) != 0)
-    {
-        // We skip logging security check failures for broadcast MLE
-        // messages since it can be common to receive such messages
-        // from adjacent Thread networks.
-        skipLoggingError = (aMessageInfo.GetSockAddr().IsMulticast() &&
-                            aMessageInfo.GetThreadLinkInfo()->GetPanId() == Mac::kPanIdBroadcast);
-        ExitNow(error = kErrorSecurity);
-    }
-#endif
+    SuccessOrExit(
+        error = ProcessMessageSecurity(Crypto::AesCcm::kDecrypt, aMessage, aMessageInfo, aMessage.GetOffset(), header));
 
     if (keySequence > Get<KeyManager>().GetCurrentKeySequence())
     {
         Get<KeyManager>().SetCurrentKeySequence(keySequence);
     }
 
-    aMessage.SetOffset(mleOffset);
-
     IgnoreError(aMessage.Read(aMessage.GetOffset(), command));
     aMessage.MoveOffset(sizeof(command));
 
+    aMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(extAddr);
     neighbor = (command == kCommandChildIdResponse) ? mNeighborTable.FindParent(extAddr)
                                                     : mNeighborTable.FindNeighbor(extAddr);
 
@@ -2900,38 +2963,42 @@
     }
 #endif
 
+    rxInfo.mKeySequence  = keySequence;
+    rxInfo.mFrameCounter = frameCounter;
+    rxInfo.mNeighbor     = neighbor;
+
     switch (command)
     {
     case kCommandAdvertisement:
-        HandleAdvertisement(aMessage, aMessageInfo, neighbor);
+        HandleAdvertisement(rxInfo);
         break;
 
     case kCommandDataResponse:
-        HandleDataResponse(aMessage, aMessageInfo, neighbor);
+        HandleDataResponse(rxInfo);
         break;
 
     case kCommandParentResponse:
-        HandleParentResponse(aMessage, aMessageInfo, keySequence);
+        HandleParentResponse(rxInfo);
         break;
 
     case kCommandChildIdResponse:
-        HandleChildIdResponse(aMessage, aMessageInfo, neighbor);
+        HandleChildIdResponse(rxInfo);
         break;
 
     case kCommandAnnounce:
-        HandleAnnounce(aMessage, aMessageInfo);
+        HandleAnnounce(rxInfo);
         break;
 
     case kCommandChildUpdateRequest:
 #if OPENTHREAD_FTD
         if (IsRouterOrLeader())
         {
-            Get<MleRouter>().HandleChildUpdateRequest(aMessage, aMessageInfo);
+            Get<MleRouter>().HandleChildUpdateRequest(rxInfo);
         }
         else
 #endif
         {
-            HandleChildUpdateRequest(aMessage, aMessageInfo, neighbor);
+            HandleChildUpdateRequest(rxInfo);
         }
 
         break;
@@ -2940,63 +3007,63 @@
 #if OPENTHREAD_FTD
         if (IsRouterOrLeader())
         {
-            Get<MleRouter>().HandleChildUpdateResponse(aMessage, aMessageInfo, keySequence, neighbor);
+            Get<MleRouter>().HandleChildUpdateResponse(rxInfo);
         }
         else
 #endif
         {
-            HandleChildUpdateResponse(aMessage, aMessageInfo, neighbor);
+            HandleChildUpdateResponse(rxInfo);
         }
 
         break;
 
 #if OPENTHREAD_FTD
     case kCommandLinkRequest:
-        Get<MleRouter>().HandleLinkRequest(aMessage, aMessageInfo, neighbor);
+        Get<MleRouter>().HandleLinkRequest(rxInfo);
         break;
 
     case kCommandLinkAccept:
-        Get<MleRouter>().HandleLinkAccept(aMessage, aMessageInfo, keySequence, neighbor);
+        Get<MleRouter>().HandleLinkAccept(rxInfo);
         break;
 
     case kCommandLinkAcceptAndRequest:
-        Get<MleRouter>().HandleLinkAcceptAndRequest(aMessage, aMessageInfo, keySequence, neighbor);
+        Get<MleRouter>().HandleLinkAcceptAndRequest(rxInfo);
         break;
 
     case kCommandDataRequest:
-        Get<MleRouter>().HandleDataRequest(aMessage, aMessageInfo, neighbor);
+        Get<MleRouter>().HandleDataRequest(rxInfo);
         break;
 
     case kCommandParentRequest:
-        Get<MleRouter>().HandleParentRequest(aMessage, aMessageInfo);
+        Get<MleRouter>().HandleParentRequest(rxInfo);
         break;
 
     case kCommandChildIdRequest:
-        Get<MleRouter>().HandleChildIdRequest(aMessage, aMessageInfo, keySequence);
+        Get<MleRouter>().HandleChildIdRequest(rxInfo);
         break;
 
 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
     case kCommandTimeSync:
-        Get<MleRouter>().HandleTimeSync(aMessage, aMessageInfo, neighbor);
+        Get<MleRouter>().HandleTimeSync(rxInfo);
         break;
 #endif
 #endif // OPENTHREAD_FTD
 
 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
     case kCommandLinkMetricsManagementRequest:
-        HandleLinkMetricsManagementRequest(aMessage, aMessageInfo, neighbor);
+        HandleLinkMetricsManagementRequest(rxInfo);
         break;
 #endif
 
 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
     case kCommandLinkMetricsManagementResponse:
-        HandleLinkMetricsManagementResponse(aMessage, aMessageInfo, neighbor);
+        HandleLinkMetricsManagementResponse(rxInfo);
         break;
 #endif
 
 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
     case kCommandLinkProbe:
-        HandleLinkProbe(aMessage, aMessageInfo, neighbor);
+        HandleLinkProbe(rxInfo);
         break;
 #endif
 
@@ -3026,13 +3093,16 @@
 #endif
 
 exit:
-    if (!skipLoggingError)
+    // 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())
     {
         LogProcessError(kTypeGenericUdp, error);
     }
 }
 
-void Mle::HandleAdvertisement(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, Neighbor *aNeighbor)
+void Mle::HandleAdvertisement(RxInfo &aRxInfo)
 {
     Error      error = kErrorNone;
     uint16_t   sourceAddress;
@@ -3041,24 +3111,24 @@
     uint16_t   delay;
 
     // Source Address
-    SuccessOrExit(error = Tlv::Find<SourceAddressTlv>(aMessage, sourceAddress));
+    SuccessOrExit(error = Tlv::Find<SourceAddressTlv>(aRxInfo.mMessage, sourceAddress));
 
-    Log(kMessageReceive, kTypeAdvertisement, aMessageInfo.GetPeerAddr(), sourceAddress);
+    Log(kMessageReceive, kTypeAdvertisement, aRxInfo.mMessageInfo.GetPeerAddr(), sourceAddress);
 
     // Leader Data
-    SuccessOrExit(error = ReadLeaderData(aMessage, leaderData));
+    SuccessOrExit(error = ReadLeaderData(aRxInfo.mMessage, leaderData));
 
     if (!IsDetached())
     {
 #if OPENTHREAD_FTD
         if (IsFullThreadDevice())
         {
-            SuccessOrExit(error = Get<MleRouter>().HandleAdvertisement(aMessage, aMessageInfo, aNeighbor));
+            SuccessOrExit(error = Get<MleRouter>().HandleAdvertisement(aRxInfo));
         }
         else
 #endif
         {
-            if ((aNeighbor == &mParent) && (mParent.GetRloc16() != sourceAddress))
+            if ((aRxInfo.mNeighbor == &mParent) && (mParent.GetRloc16() != sourceAddress))
             {
                 // Remove stale parent.
                 IgnoreError(BecomeDetached());
@@ -3073,7 +3143,7 @@
         ExitNow();
 
     case kRoleChild:
-        VerifyOrExit(aNeighbor == &mParent);
+        VerifyOrExit(aRxInfo.mNeighbor == &mParent);
 
         if ((mParent.GetRloc16() == sourceAddress) && (leaderData.GetPartitionId() != mLeaderData.GetPartitionId() ||
                                                        leaderData.GetLeaderRouterId() != GetLeaderId()))
@@ -3083,12 +3153,13 @@
 #if OPENTHREAD_FTD
             if (IsFullThreadDevice())
             {
-                RouteTlv route;
-
-                if ((Tlv::FindTlv(aMessage, route) == kErrorNone) && route.IsValid())
+                switch (Get<MleRouter>().ProcessRouteTlv(aRxInfo))
                 {
-                    // Overwrite Route Data
-                    IgnoreError(Get<MleRouter>().ProcessRouteTlv(route));
+                case kErrorNone:
+                case kErrorNotFound:
+                    break;
+                default:
+                    ExitNow(error = kErrorParse);
                 }
             }
 #endif
@@ -3101,21 +3172,21 @@
 
     case kRoleRouter:
     case kRoleLeader:
-        VerifyOrExit(aNeighbor && aNeighbor->IsStateValid());
+        VerifyOrExit(aRxInfo.mNeighbor && aRxInfo.mNeighbor->IsStateValid());
         break;
     }
 
     if (mRetrieveNewNetworkData || IsNetworkDataNewer(leaderData))
     {
         delay = Random::NonCrypto::GetUint16InRange(0, kMleMaxResponseDelay);
-        IgnoreError(SendDataRequest(aMessageInfo.GetPeerAddr(), tlvs, sizeof(tlvs), delay));
+        IgnoreError(SendDataRequest(aRxInfo.mMessageInfo.GetPeerAddr(), tlvs, sizeof(tlvs), delay));
     }
 
 exit:
     LogProcessError(kTypeAdvertisement, error);
 }
 
-void Mle::HandleDataResponse(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, const Neighbor *aNeighbor)
+void Mle::HandleDataResponse(RxInfo &aRxInfo)
 {
     Error error;
 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
@@ -3123,19 +3194,20 @@
     uint16_t length;
 #endif
 
-    Log(kMessageReceive, kTypeDataResponse, aMessageInfo.GetPeerAddr());
+    Log(kMessageReceive, kTypeDataResponse, aRxInfo.mMessageInfo.GetPeerAddr());
 
-    VerifyOrExit(aNeighbor && aNeighbor->IsStateValid(), error = kErrorSecurity);
+    VerifyOrExit(aRxInfo.mNeighbor && aRxInfo.mNeighbor->IsStateValid(), error = kErrorDrop);
 
 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
-    if (Tlv::FindTlvValueOffset(aMessage, Tlv::kLinkMetricsReport, metricsReportValueOffset, length) == kErrorNone)
+    if (Tlv::FindTlvValueOffset(aRxInfo.mMessage, Tlv::kLinkMetricsReport, metricsReportValueOffset, length) ==
+        kErrorNone)
     {
-        Get<LinkMetrics::LinkMetrics>().HandleReport(aMessage, metricsReportValueOffset, length,
-                                                     aMessageInfo.GetPeerAddr());
+        Get<LinkMetrics::LinkMetrics>().HandleReport(aRxInfo.mMessage, metricsReportValueOffset, length,
+                                                     aRxInfo.mMessageInfo.GetPeerAddr());
     }
 #endif
 
-    error = HandleLeaderData(aMessage, aMessageInfo);
+    error = HandleLeaderData(aRxInfo);
 
     if (mDataRequestState == kDataRequestNone && !IsRxOnWhenIdle())
     {
@@ -3157,7 +3229,7 @@
                                    Get<NetworkData::Leader>().GetVersion(GetNetworkDataType()));
 }
 
-Error Mle::HandleLeaderData(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
+Error Mle::HandleLeaderData(RxInfo &aRxInfo)
 {
     Error                     error = kErrorNone;
     LeaderData                leaderData;
@@ -3173,7 +3245,7 @@
     Tlv                       tlv;
 
     // Leader Data
-    SuccessOrExit(error = ReadLeaderData(aMessage, leaderData));
+    SuccessOrExit(error = ReadLeaderData(aRxInfo.mMessage, leaderData));
 
     if ((leaderData.GetPartitionId() != mLeaderData.GetPartitionId()) ||
         (leaderData.GetWeighting() != mLeaderData.GetWeighting()) || (leaderData.GetLeaderRouterId() != GetLeaderId()))
@@ -3194,17 +3266,17 @@
     }
 
     // Active Timestamp
-    switch (Tlv::Find<ActiveTimestampTlv>(aMessage, activeTimestamp))
+    switch (Tlv::Find<ActiveTimestampTlv>(aRxInfo.mMessage, activeTimestamp))
     {
     case kErrorNone:
         hasActiveTimestamp = true;
 
-        timestamp = Get<MeshCoP::ActiveDataset>().GetTimestamp();
+        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
         if (!IsLeader() && (MeshCoP::Timestamp::Compare(&activeTimestamp, timestamp) != 0) &&
-            (Tlv::FindTlvOffset(aMessage, Tlv::kActiveDataset, activeDatasetOffset) != kErrorNone))
+            (Tlv::FindTlvOffset(aRxInfo.mMessage, Tlv::kActiveDataset, activeDatasetOffset) != kErrorNone))
         {
             ExitNow(dataRequest = true);
         }
@@ -3219,17 +3291,17 @@
     }
 
     // Pending Timestamp
-    switch (Tlv::Find<PendingTimestampTlv>(aMessage, pendingTimestamp))
+    switch (Tlv::Find<PendingTimestampTlv>(aRxInfo.mMessage, pendingTimestamp))
     {
     case kErrorNone:
         hasPendingTimestamp = true;
 
-        timestamp = Get<MeshCoP::PendingDataset>().GetTimestamp();
+        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::FindTlvOffset(aMessage, Tlv::kPendingDataset, pendingDatasetOffset) != kErrorNone))
+            (Tlv::FindTlvOffset(aRxInfo.mMessage, Tlv::kPendingDataset, pendingDatasetOffset) != kErrorNone))
         {
             ExitNow(dataRequest = true);
         }
@@ -3243,11 +3315,11 @@
         ExitNow(error = kErrorParse);
     }
 
-    if (Tlv::FindTlvOffset(aMessage, Tlv::kNetworkData, networkDataOffset) == kErrorNone)
+    if (Tlv::FindTlvOffset(aRxInfo.mMessage, Tlv::kNetworkData, networkDataOffset) == kErrorNone)
     {
         error = Get<NetworkData::Leader>().SetNetworkData(leaderData.GetDataVersion(NetworkData::kFullSet),
                                                           leaderData.GetDataVersion(NetworkData::kStableSubset),
-                                                          GetNetworkDataType(), aMessage, networkDataOffset);
+                                                          GetNetworkDataType(), aRxInfo.mMessage, networkDataOffset);
         SuccessOrExit(error);
     }
     else
@@ -3268,9 +3340,9 @@
         {
             if (activeDatasetOffset > 0)
             {
-                IgnoreError(aMessage.Read(activeDatasetOffset, tlv));
-                IgnoreError(Get<MeshCoP::ActiveDataset>().Save(activeTimestamp, aMessage,
-                                                               activeDatasetOffset + sizeof(tlv), tlv.GetLength()));
+                IgnoreError(aRxInfo.mMessage.Read(activeDatasetOffset, tlv));
+                IgnoreError(Get<MeshCoP::ActiveDatasetManager>().Save(
+                    activeTimestamp, aRxInfo.mMessage, activeDatasetOffset + sizeof(tlv), tlv.GetLength()));
             }
         }
 
@@ -3279,9 +3351,9 @@
         {
             if (pendingDatasetOffset > 0)
             {
-                IgnoreError(aMessage.Read(pendingDatasetOffset, tlv));
-                IgnoreError(Get<MeshCoP::PendingDataset>().Save(pendingTimestamp, aMessage,
-                                                                pendingDatasetOffset + sizeof(tlv), tlv.GetLength()));
+                IgnoreError(aRxInfo.mMessage.Read(pendingDatasetOffset, tlv));
+                IgnoreError(Get<MeshCoP::PendingDatasetManager>().Save(
+                    pendingTimestamp, aRxInfo.mMessage, pendingDatasetOffset + sizeof(tlv), tlv.GetLength()));
             }
         }
     }
@@ -3295,7 +3367,7 @@
         static const uint8_t tlvs[] = {Tlv::kNetworkData};
         uint16_t             delay;
 
-        if (aMessageInfo.GetSockAddr().IsMulticast())
+        if (aRxInfo.mMessageInfo.GetSockAddr().IsMulticast())
         {
             delay = Random::NonCrypto::GetUint16InRange(0, kMleMaxResponseDelay);
         }
@@ -3307,7 +3379,7 @@
             delay = 10;
         }
 
-        IgnoreError(SendDataRequest(aMessageInfo.GetPeerAddr(), tlvs, sizeof(tlvs), delay));
+        IgnoreError(SendDataRequest(aRxInfo.mMessageInfo.GetPeerAddr(), tlvs, sizeof(tlvs), delay));
     }
     else if (error == kErrorNone)
     {
@@ -3326,7 +3398,7 @@
 }
 
 bool Mle::IsBetterParent(uint16_t               aRloc16,
-                         uint8_t                aLinkQuality,
+                         LinkQuality            aLinkQuality,
                          uint8_t                aLinkMargin,
                          const ConnectivityTlv &aConnectivityTlv,
                          uint8_t                aVersion,
@@ -3335,10 +3407,8 @@
 {
     bool rval = false;
 
-    uint8_t candidateLinkQualityIn     = mParentCandidate.GetLinkInfo().GetLinkQuality();
-    uint8_t candidateTwoWayLinkQuality = (candidateLinkQualityIn < mParentCandidate.GetLinkQualityOut())
-                                             ? candidateLinkQualityIn
-                                             : mParentCandidate.GetLinkQualityOut();
+    LinkQuality candidateLinkQualityIn     = mParentCandidate.GetLinkInfo().GetLinkQuality();
+    LinkQuality candidateTwoWayLinkQuality = OT_MIN(candidateLinkQualityIn, mParentCandidate.GetLinkQualityOut());
 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
     uint64_t candidateCslMetric = 0;
     uint64_t cslMetric          = 0;
@@ -3416,17 +3486,17 @@
     return rval;
 }
 
-void Mle::HandleParentResponse(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, uint32_t aKeySequence)
+void Mle::HandleParentResponse(RxInfo &aRxInfo)
 {
     Error                 error    = kErrorNone;
-    const ThreadLinkInfo *linkInfo = aMessageInfo.GetThreadLinkInfo();
+    const ThreadLinkInfo *linkInfo = aRxInfo.mMessageInfo.GetThreadLinkInfo();
     Challenge             response;
     uint16_t              version;
     uint16_t              sourceAddress;
     LeaderData            leaderData;
     uint8_t               linkMarginFromTlv;
     uint8_t               linkMargin;
-    uint8_t               linkQuality;
+    LinkQuality           linkQuality;
     ConnectivityTlv       connectivity;
     uint32_t              linkFrameCounter;
     uint32_t              mleFrameCounter;
@@ -3439,19 +3509,19 @@
 #endif
 
     // Source Address
-    SuccessOrExit(error = Tlv::Find<SourceAddressTlv>(aMessage, sourceAddress));
+    SuccessOrExit(error = Tlv::Find<SourceAddressTlv>(aRxInfo.mMessage, sourceAddress));
 
-    Log(kMessageReceive, kTypeParentResponse, aMessageInfo.GetPeerAddr(), sourceAddress);
+    Log(kMessageReceive, kTypeParentResponse, aRxInfo.mMessageInfo.GetPeerAddr(), sourceAddress);
 
     // Version
-    SuccessOrExit(error = Tlv::Find<VersionTlv>(aMessage, version));
+    SuccessOrExit(error = Tlv::Find<VersionTlv>(aRxInfo.mMessage, version));
     VerifyOrExit(version >= OT_THREAD_VERSION_1_1, error = kErrorParse);
 
     // Response
-    SuccessOrExit(error = ReadResponse(aMessage, response));
+    SuccessOrExit(error = ReadResponse(aRxInfo.mMessage, response));
     VerifyOrExit(response == mParentRequestChallenge, error = kErrorParse);
 
-    aMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(extAddress);
+    aRxInfo.mMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(extAddress);
 
     if (IsChild() && mParent.GetExtAddress() == extAddress)
     {
@@ -3459,10 +3529,10 @@
     }
 
     // Leader Data
-    SuccessOrExit(error = ReadLeaderData(aMessage, leaderData));
+    SuccessOrExit(error = ReadLeaderData(aRxInfo.mMessage, leaderData));
 
     // Link Margin
-    SuccessOrExit(error = Tlv::Find<LinkMarginTlv>(aMessage, linkMarginFromTlv));
+    SuccessOrExit(error = Tlv::Find<LinkMarginTlv>(aRxInfo.mMessage, linkMarginFromTlv));
 
     linkMargin = LinkQualityInfo::ConvertRssToLinkMargin(Get<Mac::Mac>().GetNoiseFloor(), linkInfo->GetRss());
 
@@ -3474,12 +3544,12 @@
     linkQuality = LinkQualityInfo::ConvertLinkMarginToLinkQuality(linkMargin);
 
     // Connectivity
-    SuccessOrExit(error = Tlv::FindTlv(aMessage, connectivity));
+    SuccessOrExit(error = Tlv::FindTlv(aRxInfo.mMessage, connectivity));
     VerifyOrExit(connectivity.IsValid(), error = kErrorParse);
 
 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
     // CSL Accuracy
-    if (Tlv::FindTlv(aMessage, clockAccuracy) != kErrorNone)
+    if (Tlv::FindTlv(aRxInfo.mMessage, clockAccuracy) != kErrorNone)
     {
         clockAccuracy.SetCslClockAccuracy(kCslWorstCrystalPpm);
         clockAccuracy.SetCslUncertainty(kCslWorstUncertainty);
@@ -3511,22 +3581,23 @@
         bool isIdSequenceGreater =
             SerialNumber::IsGreater(connectivity.GetIdSequence(), Get<RouterTable>().GetRouterIdSequence());
 
-        switch (mParentRequestMode)
+        switch (mAttachMode)
         {
-        case kAttachAny:
+        case kAnyPartition:
+        case kBetterParent:
             VerifyOrExit(!isPartitionIdSame || isIdSequenceGreater);
             break;
 
-        case kAttachSame1:
-        case kAttachSame2:
+        case kSamePartition:
+        case kSamePartitionRetry:
             VerifyOrExit(isPartitionIdSame && isIdSequenceGreater);
             break;
 
-        case kAttachSameDowngrade:
+        case kDowngradeToReed:
             VerifyOrExit(isPartitionIdSame && (isIdSequenceSame || isIdSequenceGreater));
             break;
 
-        case kAttachBetter:
+        case kBetterPartition:
             VerifyOrExit(!isPartitionIdSame);
 
             VerifyOrExit(MleRouter::ComparePartitions(connectivity.GetActiveRouters() <= 1, leaderData,
@@ -3568,12 +3639,12 @@
     }
 
     // Link/MLE Frame Counters
-    SuccessOrExit(error = ReadFrameCounters(aMessage, linkFrameCounter, mleFrameCounter));
+    SuccessOrExit(error = ReadFrameCounters(aRxInfo.mMessage, linkFrameCounter, mleFrameCounter));
 
 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
 
     // Time Parameter
-    if (Tlv::FindTlv(aMessage, timeParameter) == kErrorNone)
+    if (Tlv::FindTlv(aRxInfo.mMessage, timeParameter) == kErrorNone)
     {
         VerifyOrExit(timeParameter.IsValid());
 
@@ -3592,7 +3663,7 @@
 #endif // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
 
     // Challenge
-    SuccessOrExit(error = ReadChallenge(aMessage, mParentCandidateChallenge));
+    SuccessOrExit(error = ReadChallenge(aRxInfo.mMessage, mParentCandidateChallenge));
 
     mParentCandidate.SetExtAddress(extAddress);
     mParentCandidate.SetRloc16(sourceAddress);
@@ -3607,7 +3678,7 @@
     mParentCandidate.ResetLinkFailures();
     mParentCandidate.SetLinkQualityOut(LinkQualityInfo::ConvertLinkMarginToLinkQuality(linkMarginFromTlv));
     mParentCandidate.SetState(Neighbor::kStateParentResponse);
-    mParentCandidate.SetKeySequence(aKeySequence);
+    mParentCandidate.SetKeySequence(aRxInfo.mKeySequence);
 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
     mParentCandidate.SetCslClockAccuracy(clockAccuracy.GetCslClockAccuracy());
     mParentCandidate.SetCslClockUncertainty(clockAccuracy.GetCslUncertainty());
@@ -3628,12 +3699,8 @@
     LogProcessError(kTypeParentResponse, error);
 }
 
-void Mle::HandleChildIdResponse(const Message &         aMessage,
-                                const Ip6::MessageInfo &aMessageInfo,
-                                const Neighbor *        aNeighbor)
+void Mle::HandleChildIdResponse(RxInfo &aRxInfo)
 {
-    OT_UNUSED_VARIABLE(aMessageInfo);
-
     Error              error = kErrorNone;
     LeaderData         leaderData;
     uint16_t           sourceAddress;
@@ -3644,33 +3711,35 @@
     uint16_t           offset;
 
     // Source Address
-    SuccessOrExit(error = Tlv::Find<SourceAddressTlv>(aMessage, sourceAddress));
+    SuccessOrExit(error = Tlv::Find<SourceAddressTlv>(aRxInfo.mMessage, sourceAddress));
 
-    Log(kMessageReceive, kTypeChildIdResponse, aMessageInfo.GetPeerAddr(), sourceAddress);
+    Log(kMessageReceive, kTypeChildIdResponse, aRxInfo.mMessageInfo.GetPeerAddr(), sourceAddress);
 
-    VerifyOrExit(aNeighbor && aNeighbor->IsStateValid(), error = kErrorSecurity);
+    VerifyOrExit(aRxInfo.mNeighbor && aRxInfo.mNeighbor->IsStateValid(), error = kErrorSecurity);
 
     VerifyOrExit(mAttachState == kAttachStateChildIdRequest);
 
-    // Leader Data
-    SuccessOrExit(error = ReadLeaderData(aMessage, leaderData));
-
     // ShortAddress
-    SuccessOrExit(error = Tlv::Find<Address16Tlv>(aMessage, shortAddress));
+    SuccessOrExit(error = Tlv::Find<Address16Tlv>(aRxInfo.mMessage, shortAddress));
+    VerifyOrExit(RouterIdMatch(sourceAddress, shortAddress), error = kErrorRejected);
+
+    // Leader Data
+    SuccessOrExit(error = ReadLeaderData(aRxInfo.mMessage, leaderData));
 
     // Network Data
-    error = Tlv::FindTlvOffset(aMessage, Tlv::kNetworkData, networkDataOffset);
+    error = Tlv::FindTlvOffset(aRxInfo.mMessage, Tlv::kNetworkData, networkDataOffset);
     SuccessOrExit(error);
 
     // Active Timestamp
-    switch (Tlv::Find<ActiveTimestampTlv>(aMessage, timestamp))
+    switch (Tlv::Find<ActiveTimestampTlv>(aRxInfo.mMessage, timestamp))
     {
     case kErrorNone:
         // Active Dataset
-        if (Tlv::FindTlvOffset(aMessage, Tlv::kActiveDataset, offset) == kErrorNone)
+        if (Tlv::FindTlvOffset(aRxInfo.mMessage, Tlv::kActiveDataset, offset) == kErrorNone)
         {
-            IgnoreError(aMessage.Read(offset, tlv));
-            IgnoreError(Get<MeshCoP::ActiveDataset>().Save(timestamp, aMessage, offset + sizeof(tlv), tlv.GetLength()));
+            IgnoreError(aRxInfo.mMessage.Read(offset, tlv));
+            SuccessOrExit(error = Get<MeshCoP::ActiveDatasetManager>().Save(timestamp, aRxInfo.mMessage,
+                                                                            offset + sizeof(tlv), tlv.GetLength()));
         }
         break;
 
@@ -3684,24 +3753,24 @@
     // clear Pending Dataset if device succeed to reattach using stored Pending Dataset
     if (mReattachState == kReattachPending)
     {
-        Get<MeshCoP::PendingDataset>().Clear();
+        Get<MeshCoP::PendingDatasetManager>().Clear();
     }
 
     // Pending Timestamp
-    switch (Tlv::Find<PendingTimestampTlv>(aMessage, timestamp))
+    switch (Tlv::Find<PendingTimestampTlv>(aRxInfo.mMessage, timestamp))
     {
     case kErrorNone:
         // Pending Dataset
-        if (Tlv::FindTlvOffset(aMessage, Tlv::kPendingDataset, offset) == kErrorNone)
+        if (Tlv::FindTlvOffset(aRxInfo.mMessage, Tlv::kPendingDataset, offset) == kErrorNone)
         {
-            IgnoreError(aMessage.Read(offset, tlv));
-            IgnoreError(
-                Get<MeshCoP::PendingDataset>().Save(timestamp, aMessage, offset + sizeof(tlv), tlv.GetLength()));
+            IgnoreError(aRxInfo.mMessage.Read(offset, tlv));
+            IgnoreError(Get<MeshCoP::PendingDatasetManager>().Save(timestamp, aRxInfo.mMessage, offset + sizeof(tlv),
+                                                                   tlv.GetLength()));
         }
         break;
 
     case kErrorNotFound:
-        Get<MeshCoP::PendingDataset>().ClearNetwork();
+        Get<MeshCoP::PendingDatasetManager>().ClearNetwork();
         break;
 
     default:
@@ -3710,9 +3779,9 @@
 
 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
     // Sync to Thread network time
-    if (aMessage.GetTimeSyncSeq() != OT_TIME_SYNC_INVALID_SEQ)
+    if (aRxInfo.mMessage.GetTimeSyncSeq() != OT_TIME_SYNC_INVALID_SEQ)
     {
-        Get<TimeSync>().HandleTimeSyncMessage(aMessage);
+        Get<TimeSync>().HandleTimeSyncMessage(aRxInfo.mMessage);
     }
 #endif
 
@@ -3725,11 +3794,13 @@
 #if OPENTHREAD_FTD
     if (IsFullThreadDevice())
     {
-        RouteTlv route;
-
-        if (Tlv::FindTlv(aMessage, route) == kErrorNone)
+        switch (Get<MleRouter>().ProcessRouteTlv(aRxInfo))
         {
-            SuccessOrExit(error = Get<MleRouter>().ProcessRouteTlv(route));
+        case kErrorNone:
+        case kErrorNotFound:
+            break;
+        default:
+            ExitNow(error = kErrorParse);
         }
     }
 #endif
@@ -3746,7 +3817,7 @@
 
     IgnoreError(Get<NetworkData::Leader>().SetNetworkData(leaderData.GetDataVersion(NetworkData::kFullSet),
                                                           leaderData.GetDataVersion(NetworkData::kStableSubset),
-                                                          GetNetworkDataType(), aMessage, networkDataOffset));
+                                                          GetNetworkDataType(), aRxInfo.mMessage, networkDataOffset));
 
     SetStateChild(shortAddress);
 
@@ -3764,7 +3835,7 @@
     LogProcessError(kTypeChildIdResponse, error);
 }
 
-void Mle::HandleChildUpdateRequest(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, Neighbor *aNeighbor)
+void Mle::HandleChildUpdateRequest(RxInfo &aRxInfo)
 {
     static const uint8_t kMaxResponseTlvs = 6;
 
@@ -3776,12 +3847,12 @@
     uint8_t       numTlvs                = 0;
 
     // Source Address
-    SuccessOrExit(error = Tlv::Find<SourceAddressTlv>(aMessage, sourceAddress));
+    SuccessOrExit(error = Tlv::Find<SourceAddressTlv>(aRxInfo.mMessage, sourceAddress));
 
-    Log(kMessageReceive, kTypeChildUpdateRequestOfParent, aMessageInfo.GetPeerAddr(), sourceAddress);
+    Log(kMessageReceive, kTypeChildUpdateRequestOfParent, aRxInfo.mMessageInfo.GetPeerAddr(), sourceAddress);
 
     // Challenge
-    switch (ReadChallenge(aMessage, challenge))
+    switch (ReadChallenge(aRxInfo.mMessage, challenge))
     {
     case kErrorNone:
         tlvs[numTlvs++] = Tlv::kResponse;
@@ -3795,11 +3866,11 @@
         ExitNow(error = kErrorParse);
     }
 
-    if (aNeighbor == &mParent)
+    if (aRxInfo.mNeighbor == &mParent)
     {
         uint8_t status;
 
-        switch (Tlv::Find<StatusTlv>(aMessage, status))
+        switch (Tlv::Find<StatusTlv>(aRxInfo.mMessage, status))
         {
         case kErrorNone:
             VerifyOrExit(status != StatusTlv::kError, IgnoreError(BecomeDetached()));
@@ -3817,11 +3888,11 @@
         }
 
         // Leader Data, Network Data, Active Timestamp, Pending Timestamp
-        SuccessOrExit(error = HandleLeaderData(aMessage, aMessageInfo));
+        SuccessOrExit(error = HandleLeaderData(aRxInfo));
 
 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
         CslClockAccuracyTlv cslClockAccuracyTlv;
-        if (Tlv::FindTlv(aMessage, cslClockAccuracyTlv) == kErrorNone)
+        if (Tlv::FindTlv(aRxInfo.mMessage, cslClockAccuracyTlv) == kErrorNone)
         {
             // MUST include CSL timeout TLV when request includes CSL accuracy
             tlvs[numTlvs++] = Tlv::kCslTimeout;
@@ -3835,14 +3906,14 @@
     }
 
     // TLV Request
-    switch (FindTlvRequest(aMessage, requestedTlvs))
+    switch (FindTlvRequest(aRxInfo.mMessage, requestedTlvs))
     {
     case kErrorNone:
         for (uint8_t i = 0; i < requestedTlvs.mNumTlvs; i++)
         {
             if (numTlvs >= sizeof(tlvs))
             {
-                otLogWarnMle("Failed to respond with TLVs: %d of %d", i, requestedTlvs.mNumTlvs);
+                LogWarn("Failed to respond with TLVs: %d of %d", i, requestedTlvs.mNumTlvs);
                 break;
             }
 
@@ -3856,9 +3927,9 @@
     }
 
 #if OPENTHREAD_CONFIG_MULTI_RADIO
-    if ((aNeighbor != nullptr) && (challenge.mLength != 0))
+    if ((aRxInfo.mNeighbor != nullptr) && (challenge.mLength != 0))
     {
-        aNeighbor->ClearLastRxFragmentTag();
+        aRxInfo.mNeighbor->ClearLastRxFragmentTag();
     }
 #endif
 
@@ -3868,9 +3939,7 @@
     LogProcessError(kTypeChildUpdateRequestOfParent, error);
 }
 
-void Mle::HandleChildUpdateResponse(const Message &         aMessage,
-                                    const Ip6::MessageInfo &aMessageInfo,
-                                    const Neighbor *        aNeighbor)
+void Mle::HandleChildUpdateResponse(RxInfo &aRxInfo)
 {
     Error     error = kErrorNone;
     uint8_t   status;
@@ -3884,17 +3953,17 @@
     CslClockAccuracyTlv clockAccuracy;
 #endif
 
-    Log(kMessageReceive, kTypeChildUpdateResponseOfParent, aMessageInfo.GetPeerAddr());
+    Log(kMessageReceive, kTypeChildUpdateResponseOfParent, aRxInfo.mMessageInfo.GetPeerAddr());
 
     switch (mRole)
     {
     case kRoleDetached:
-        SuccessOrExit(error = ReadResponse(aMessage, response));
+        SuccessOrExit(error = ReadResponse(aRxInfo.mMessage, response));
         VerifyOrExit(response == mParentRequestChallenge, error = kErrorSecurity);
         break;
 
     case kRoleChild:
-        VerifyOrExit((aNeighbor == &mParent) && mParent.IsStateValid(), error = kErrorSecurity);
+        VerifyOrExit((aRxInfo.mNeighbor == &mParent) && mParent.IsStateValid(), error = kErrorSecurity);
         break;
 
     default:
@@ -3903,20 +3972,20 @@
     }
 
     // Status
-    if (Tlv::Find<StatusTlv>(aMessage, status) == kErrorNone)
+    if (Tlv::Find<StatusTlv>(aRxInfo.mMessage, status) == kErrorNone)
     {
         IgnoreError(BecomeDetached());
         ExitNow();
     }
 
     // Mode
-    SuccessOrExit(error = Tlv::Find<ModeTlv>(aMessage, mode));
+    SuccessOrExit(error = Tlv::Find<ModeTlv>(aRxInfo.mMessage, mode));
     VerifyOrExit(DeviceMode(mode) == mDeviceMode, error = kErrorDrop);
 
     switch (mRole)
     {
     case kRoleDetached:
-        SuccessOrExit(error = ReadFrameCounters(aMessage, linkFrameCounter, mleFrameCounter));
+        SuccessOrExit(error = ReadFrameCounters(aRxInfo.mMessage, linkFrameCounter, mleFrameCounter));
 
         mParent.GetLinkFrameCounters().SetAll(linkFrameCounter);
         mParent.SetLinkAckFrameCounter(linkFrameCounter);
@@ -3931,7 +4000,7 @@
 
     case kRoleChild:
         // Source Address
-        SuccessOrExit(error = Tlv::Find<SourceAddressTlv>(aMessage, sourceAddress));
+        SuccessOrExit(error = Tlv::Find<SourceAddressTlv>(aRxInfo.mMessage, sourceAddress));
 
         if (RouterIdFromRloc16(sourceAddress) != RouterIdFromRloc16(GetRloc16()))
         {
@@ -3940,10 +4009,10 @@
         }
 
         // Leader Data, Network Data, Active Timestamp, Pending Timestamp
-        SuccessOrExit(error = HandleLeaderData(aMessage, aMessageInfo));
+        SuccessOrExit(error = HandleLeaderData(aRxInfo));
 
         // Timeout optional
-        switch (Tlv::Find<TimeoutTlv>(aMessage, timeout))
+        switch (Tlv::Find<TimeoutTlv>(aRxInfo.mMessage, timeout))
         {
         case kErrorNone:
             mTimeout = timeout;
@@ -3956,7 +4025,7 @@
 
 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
         // CSL Accuracy
-        if (Tlv::FindTlv(aMessage, clockAccuracy) != kErrorNone)
+        if (Tlv::FindTlv(aRxInfo.mMessage, clockAccuracy) != kErrorNone)
         {
             Get<Mac::Mac>().SetCslParentClockAccuracy(clockAccuracy.GetCslClockAccuracy());
             Get<Mac::Mac>().SetCslParentUncertainty(clockAccuracy.GetCslUncertainty());
@@ -3995,10 +4064,8 @@
     LogProcessError(kTypeChildUpdateResponseOfParent, error);
 }
 
-void Mle::HandleAnnounce(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
+void Mle::HandleAnnounce(RxInfo &aRxInfo)
 {
-    OT_UNUSED_VARIABLE(aMessageInfo);
-
     Error                     error = kErrorNone;
     ChannelTlv                channelTlv;
     MeshCoP::Timestamp        timestamp;
@@ -4006,17 +4073,17 @@
     uint8_t                   channel;
     uint16_t                  panId;
 
-    Log(kMessageReceive, kTypeAnnounce, aMessageInfo.GetPeerAddr());
+    Log(kMessageReceive, kTypeAnnounce, aRxInfo.mMessageInfo.GetPeerAddr());
 
-    SuccessOrExit(error = Tlv::FindTlv(aMessage, channelTlv));
+    SuccessOrExit(error = Tlv::FindTlv(aRxInfo.mMessage, channelTlv));
     VerifyOrExit(channelTlv.IsValid(), error = kErrorParse);
 
     channel = static_cast<uint8_t>(channelTlv.GetChannel());
 
-    SuccessOrExit(error = Tlv::Find<ActiveTimestampTlv>(aMessage, timestamp));
-    SuccessOrExit(error = Tlv::Find<PanIdTlv>(aMessage, panId));
+    SuccessOrExit(error = Tlv::Find<ActiveTimestampTlv>(aRxInfo.mMessage, timestamp));
+    SuccessOrExit(error = Tlv::Find<PanIdTlv>(aRxInfo.mMessage, panId));
 
-    localTimestamp = Get<MeshCoP::ActiveDataset>().GetTimestamp();
+    localTimestamp = Get<MeshCoP::ActiveDatasetManager>().GetTimestamp();
 
     if (MeshCoP::Timestamp::Compare(&timestamp, localTimestamp) > 0)
     {
@@ -4039,14 +4106,14 @@
         mAttachTimer.Start(kAnnounceProcessTimeout);
         mAttachCounter = 0;
 
-        otLogNoteMle("Delay processing Announce - channel %d, panid 0x%02x", channel, panId);
+        LogNote("Delay processing Announce - channel %d, panid 0x%02x", channel, panId);
     }
     else if (MeshCoP::Timestamp::Compare(&timestamp, localTimestamp) < 0)
     {
         SendAnnounce(channel);
 
 #if OPENTHREAD_CONFIG_MLE_SEND_UNICAST_ANNOUNCE_RESPONSE
-        SendAnnounce(channel, aMessageInfo.GetPeerAddr());
+        SendAnnounce(channel, aRxInfo.mMessageInfo.GetPeerAddr());
 #endif
     }
     else
@@ -4066,19 +4133,18 @@
 }
 
 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
-void Mle::HandleLinkMetricsManagementRequest(const Message &         aMessage,
-                                             const Ip6::MessageInfo &aMessageInfo,
-                                             Neighbor *              aNeighbor)
+void Mle::HandleLinkMetricsManagementRequest(RxInfo &aRxInfo)
 {
     Error               error = kErrorNone;
     LinkMetrics::Status status;
 
-    Log(kMessageReceive, kTypeLinkMetricsManagementRequest, aMessageInfo.GetPeerAddr());
+    Log(kMessageReceive, kTypeLinkMetricsManagementRequest, aRxInfo.mMessageInfo.GetPeerAddr());
 
-    VerifyOrExit(aNeighbor != nullptr, error = kErrorInvalidState);
+    VerifyOrExit(aRxInfo.mNeighbor != nullptr, error = kErrorInvalidState);
 
-    SuccessOrExit(error = Get<LinkMetrics::LinkMetrics>().HandleManagementRequest(aMessage, *aNeighbor, status));
-    error = SendLinkMetricsManagementResponse(aMessageInfo.GetPeerAddr(), status);
+    SuccessOrExit(
+        error = Get<LinkMetrics::LinkMetrics>().HandleManagementRequest(aRxInfo.mMessage, *aRxInfo.mNeighbor, status));
+    error = SendLinkMetricsManagementResponse(aRxInfo.mMessageInfo.GetPeerAddr(), status);
 
 exit:
     LogProcessError(kTypeLinkMetricsManagementRequest, error);
@@ -4087,17 +4153,16 @@
 #endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
 
 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
-void Mle::HandleLinkMetricsManagementResponse(const Message &         aMessage,
-                                              const Ip6::MessageInfo &aMessageInfo,
-                                              Neighbor *              aNeighbor)
+void Mle::HandleLinkMetricsManagementResponse(RxInfo &aRxInfo)
 {
     Error error = kErrorNone;
 
-    Log(kMessageReceive, kTypeLinkMetricsManagementResponse, aMessageInfo.GetPeerAddr());
+    Log(kMessageReceive, kTypeLinkMetricsManagementResponse, aRxInfo.mMessageInfo.GetPeerAddr());
 
-    VerifyOrExit(aNeighbor != nullptr, error = kErrorInvalidState);
+    VerifyOrExit(aRxInfo.mNeighbor != nullptr, error = kErrorInvalidState);
 
-    error = Get<LinkMetrics::LinkMetrics>().HandleManagementResponse(aMessage, aMessageInfo.GetPeerAddr());
+    error =
+        Get<LinkMetrics::LinkMetrics>().HandleManagementResponse(aRxInfo.mMessage, aRxInfo.mMessageInfo.GetPeerAddr());
 
 exit:
     LogProcessError(kTypeLinkMetricsManagementResponse, error);
@@ -4105,16 +4170,16 @@
 #endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
 
 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
-void Mle::HandleLinkProbe(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, Neighbor *aNeighbor)
+void Mle::HandleLinkProbe(RxInfo &aRxInfo)
 {
     Error   error = kErrorNone;
     uint8_t seriesId;
 
-    Log(kMessageReceive, kTypeLinkProbe, aMessageInfo.GetPeerAddr());
+    Log(kMessageReceive, kTypeLinkProbe, aRxInfo.mMessageInfo.GetPeerAddr());
 
-    SuccessOrExit(error = Get<LinkMetrics::LinkMetrics>().HandleLinkProbe(aMessage, seriesId));
-    aNeighbor->AggregateLinkMetrics(seriesId, LinkMetrics::SeriesInfo::kSeriesTypeLinkProbe, aMessage.GetAverageLqi(),
-                                    aMessage.GetAverageRss());
+    SuccessOrExit(error = Get<LinkMetrics::LinkMetrics>().HandleLinkProbe(aRxInfo.mMessage, seriesId));
+    aRxInfo.mNeighbor->AggregateLinkMetrics(seriesId, LinkMetrics::SeriesInfo::kSeriesTypeLinkProbe,
+                                            aRxInfo.mMessage.GetAverageLqi(), aRxInfo.mMessage.GetAverageRss());
 
 exit:
     LogProcessError(kTypeLinkProbe, error);
@@ -4128,7 +4193,7 @@
 
     OT_ASSERT(mAttachState == kAttachStateProcessAnnounce);
 
-    otLogNoteMle("Processing Announce - channel %d, panid 0x%02x", newChannel, newPanId);
+    LogNote("Processing Announce - channel %d, panid 0x%02x", newChannel, newPanId);
 
     Stop(kKeepNetworkDatasets);
 
@@ -4196,13 +4261,13 @@
 
     SuccessOrExit(error = Get<Ip6::Ip6>().SendDatagram(*message, messageInfo, Ip6::kProtoNone));
 
-    otLogNoteMle("Sending message to inform previous parent 0x%04x", mPreviousParentRloc);
+    LogNote("Sending message to inform previous parent 0x%04x", mPreviousParentRloc);
 
 exit:
 
     if (error != kErrorNone)
     {
-        otLogWarnMle("Failed to inform previous parent: %s", ErrorToString(error));
+        LogWarn("Failed to inform previous parent: %s", ErrorToString(error));
 
         FreeMessage(message);
     }
@@ -4219,7 +4284,7 @@
 {
     int8_t parentRss;
 
-    otLogInfoMle("PeriodicParentSearch: %s interval passed", mParentSearchIsInBackoff ? "Backoff" : "Check");
+    LogInfo("PeriodicParentSearch: %s interval passed", mParentSearchIsInBackoff ? "Backoff" : "Check");
 
     if (mParentSearchBackoffWasCanceled)
     {
@@ -4230,7 +4295,7 @@
         if (TimerMilli::GetNow() - mParentSearchBackoffCancelTime >= kParentSearchBackoffInterval)
         {
             mParentSearchBackoffWasCanceled = false;
-            otLogInfoMle("PeriodicParentSearch: Backoff cancellation is allowed on parent switch");
+            LogInfo("PeriodicParentSearch: Backoff cancellation is allowed on parent switch");
         }
     }
 
@@ -4239,15 +4304,14 @@
     VerifyOrExit(IsChild());
 
     parentRss = GetParent().GetLinkInfo().GetAverageRss();
-    otLogInfoMle("PeriodicParentSearch: Parent RSS %d", parentRss);
+    LogInfo("PeriodicParentSearch: Parent RSS %d", parentRss);
     VerifyOrExit(parentRss != OT_RADIO_RSSI_INVALID);
 
     if (parentRss < kParentSearchRssThreadhold)
     {
-        otLogInfoMle("PeriodicParentSearch: Parent RSS less than %d, searching for new parents",
-                     kParentSearchRssThreadhold);
+        LogInfo("PeriodicParentSearch: Parent RSS less than %d, searching for new parents", kParentSearchRssThreadhold);
         mParentSearchIsInBackoff = true;
-        IgnoreError(BecomeChild(kAttachAny));
+        Attach(kBetterParent);
     }
 
 exit:
@@ -4271,8 +4335,7 @@
 
     mParentSearchTimer.Start(interval);
 
-    otLogInfoMle("PeriodicParentSearch: (Re)starting timer for %s interval",
-                 mParentSearchIsInBackoff ? "backoff" : "check");
+    LogInfo("PeriodicParentSearch: (Re)starting timer for %s interval", mParentSearchIsInBackoff ? "backoff" : "check");
 }
 
 void Mle::UpdateParentSearchState(void)
@@ -4301,7 +4364,7 @@
             mParentSearchIsInBackoff        = false;
             mParentSearchBackoffWasCanceled = true;
             mParentSearchBackoffCancelTime  = TimerMilli::GetNow();
-            otLogInfoMle("PeriodicParentSearch: Canceling backoff on switching to a new parent");
+            LogInfo("PeriodicParentSearch: Canceling backoff on switching to a new parent");
         }
     }
 
@@ -4316,7 +4379,7 @@
 }
 #endif // OPENTHREAD_CONFIG_PARENT_SEARCH_ENABLE
 
-#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_MLE == 1)
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
 void Mle::Log(MessageAction aAction, MessageType aType, const Ip6::Address &aAddress)
 {
     Log(aAction, aType, aAddress, Mac::kShortAddrInvalid);
@@ -4336,13 +4399,12 @@
         rlocString.Append(",0x%04x", aRloc);
     }
 
-    otLogInfoMle("%s %s%s (%s%s)", MessageActionToString(aAction), MessageTypeToString(aType),
-                 MessageTypeActionToSuffixString(aType, aAction), aAddress.ToString().AsCString(),
-                 rlocString.AsCString());
+    LogInfo("%s %s%s (%s%s)", MessageActionToString(aAction), MessageTypeToString(aType),
+            MessageTypeActionToSuffixString(aType, aAction), aAddress.ToString().AsCString(), rlocString.AsCString());
 }
 #endif
 
-#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_WARN) && (OPENTHREAD_CONFIG_LOG_MLE == 1)
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN)
 void Mle::LogProcessError(MessageType aType, Error aError)
 {
     LogError(kMessageReceive, aType, aError);
@@ -4357,8 +4419,16 @@
 {
     if (aError != kErrorNone)
     {
-        otLogWarnMle("Failed to %s %s%s: %s", aAction == kMessageSend ? "send" : "process", MessageTypeToString(aType),
-                     MessageTypeActionToSuffixString(aType, aAction), ErrorToString(aError));
+        if (aAction == kMessageReceive && (aError == kErrorDrop || aError == kErrorNoRoute))
+        {
+            LogInfo("Failed to %s %s%s: %s", "process", MessageTypeToString(aType),
+                    MessageTypeActionToSuffixString(aType, aAction), ErrorToString(aError));
+        }
+        else
+        {
+            LogWarn("Failed to %s %s%s: %s", aAction == kMessageSend ? "send" : "process", MessageTypeToString(aType),
+                    MessageTypeActionToSuffixString(aType, aAction), ErrorToString(aError));
+        }
     }
 }
 
@@ -4511,7 +4581,7 @@
     return str;
 }
 
-#endif // #if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_WARN) && (OPENTHREAD_CONFIG_LOG_MLE == 1)
+#endif // #if OT_SHOULD_LOG_AT( OT_LOG_LEVEL_WARN)
 
 const char *Mle::RoleToString(DeviceRole aRole)
 {
@@ -4529,28 +4599,30 @@
     static_assert(kRoleRouter == 3, "kRoleRouter value is incorrect");
     static_assert(kRoleLeader == 4, "kRoleLeader value is incorrect");
 
-    return (aRole < OT_ARRAY_LENGTH(kRoleStrings)) ? kRoleStrings[aRole] : "invalid";
+    return (aRole < GetArrayLength(kRoleStrings)) ? kRoleStrings[aRole] : "invalid";
 }
 
 // LCOV_EXCL_START
 
-#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_NOTE) && (OPENTHREAD_CONFIG_LOG_MLE == 1)
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_NOTE)
 
 const char *Mle::AttachModeToString(AttachMode aMode)
 {
     static const char *const kAttachModeStrings[] = {
-        "any-partition",            // (0) kAttachAny
-        "same-partition-try-1",     // (1) kAttachSame1
-        "same-partition-try-2",     // (2) kAttachSame2
-        "better-partition",         // (3) kAttachBetter
-        "same-partition-downgrade", // (4) kAttachSameDowngrade
+        "AnyPartition",       // (0) kAnyPartition
+        "SamePartition",      // (1) kSamePartition
+        "SamePartitionRetry", // (2) kSamePartitionRetry
+        "BetterPartition",    // (3) kBetterPartition
+        "DowngradeToReed",    // (4) kDowngradeToReed
+        "BetterParent",       // (5) kBetterParent
     };
 
-    static_assert(kAttachAny == 0, "kAttachAny value is incorrect");
-    static_assert(kAttachSame1 == 1, "kAttachSame1 value is incorrect");
-    static_assert(kAttachSame2 == 2, "kAttachSame2 value is incorrect");
-    static_assert(kAttachBetter == 3, "kAttachBetter value is incorrect");
-    static_assert(kAttachSameDowngrade == 4, "kAttachSameDowngrade value is incorrect");
+    static_assert(kAnyPartition == 0, "kAnyPartition value is incorrect");
+    static_assert(kSamePartition == 1, "kSamePartition value is incorrect");
+    static_assert(kSamePartitionRetry == 2, "kSamePartitionRetry value is incorrect");
+    static_assert(kBetterPartition == 3, "kBetterPartition value is incorrect");
+    static_assert(kDowngradeToReed == 4, "kDowngradeToReed value is incorrect");
+    static_assert(kBetterParent == 5, "kBetterParent value is incorrect");
 
     return kAttachModeStrings[aMode];
 }
@@ -4595,7 +4667,7 @@
     return kReattachStateStrings[aState];
 }
 
-#endif // (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_NOTE) && (OPENTHREAD_CONFIG_LOG_MLE == 1)
+#endif // OT_SHOULD_LOG_AT( OT_LOG_LEVEL_NOTE)
 
 // LCOV_EXCL_STOP
 
@@ -4606,8 +4678,7 @@
     Message *message;
     Tlv      tlv;
 
-    VerifyOrExit((message = NewMleMessage()) != nullptr, error = kErrorNoBufs);
-    SuccessOrExit(error = AppendHeader(*message, kCommandLinkMetricsManagementRequest));
+    VerifyOrExit((message = NewMleMessage(kCommandLinkMetricsManagementRequest)) != nullptr, error = kErrorNoBufs);
 
     // Link Metrics Management TLV
     tlv.SetType(Tlv::kLinkMetricsManagement);
diff --git a/src/core/thread/mle.hpp b/src/core/thread/mle.hpp
index c2f0193..b5a24d6 100644
--- a/src/core/thread/mle.hpp
+++ b/src/core/thread/mle.hpp
@@ -38,9 +38,11 @@
 
 #include "common/encoding.hpp"
 #include "common/locator.hpp"
+#include "common/log.hpp"
 #include "common/non_copyable.hpp"
 #include "common/notifier.hpp"
 #include "common/timer.hpp"
+#include "crypto/aes_ccm.hpp"
 #include "mac/mac.hpp"
 #include "meshcop/joiner_router.hpp"
 #include "meshcop/meshcop.hpp"
@@ -143,13 +145,10 @@
     void Stop(void) { Stop(kUpdateNetworkDatasets); }
 
     /**
-     * This method restores network information from non-volatile memory.
-     *
-     * @retval kErrorNone      Successfully restore the network information.
-     * @retval kErrorNotFound  There is no valid network information stored in non-volatile memory.
+     * This method restores network information from non-volatile memory (if any).
      *
      */
-    Error Restore(void);
+    void Restore(void);
 
     /**
      * This method stores network information into non-volatile memory.
@@ -180,14 +179,12 @@
     /**
      * This method causes the Thread interface to attempt an MLE attach.
      *
-     * @param[in]  aMode  Indicates what partitions to attach to.
-     *
      * @retval kErrorNone          Successfully began the attach process.
      * @retval kErrorInvalidState  MLE is Disabled.
      * @retval kErrorBusy          An attach process is in progress.
      *
      */
-    Error BecomeChild(AttachMode aMode);
+    Error BecomeChild(void);
 
     /**
      * This method indicates whether or not the Thread device is attached to a Thread network.
@@ -785,6 +782,20 @@
     };
 
     /**
+     * Attach mode.
+     *
+     */
+    enum AttachMode : uint8_t
+    {
+        kAnyPartition,       ///< Attach to any Thread partition.
+        kSamePartition,      ///< Attach to the same Thread partition (attempt 1 when losing connectivity).
+        kSamePartitionRetry, ///< Attach to the same Thread partition (attempt 2 when losing connectivity).
+        kBetterPartition,    ///< Attach to a better (i.e. higher weight/partition id) Thread partition.
+        kDowngradeToReed,    ///< Attach to the same Thread partition during downgrade process.
+        kBetterParent,       ///< Attach to a better parent.
+    };
+
+    /**
      * States during attach (when searching for a parent).
      *
      */
@@ -805,10 +816,10 @@
      */
     enum ReattachState : uint8_t
     {
-        kReattachStop    = 0, ///< Reattach process is disabled or finished
-        kReattachStart   = 1, ///< Start reattach process
-        kReattachActive  = 2, ///< Reattach using stored Active Dataset
-        kReattachPending = 3, ///< Reattach using stored Pending Dataset
+        kReattachStop,    ///< Reattach process is disabled or finished
+        kReattachStart,   ///< Start reattach process
+        kReattachActive,  ///< Reattach using stored Active Dataset
+        kReattachPending, ///< Reattach using stored Pending Dataset
     };
 
     static constexpr uint16_t kMleMaxResponseDelay = 1000u; ///< Max delay before responding to a multicast request.
@@ -934,12 +945,44 @@
     };
 
     /**
-     * This method allocates a new message buffer for preparing an MLE message.
+     * This structure represents a received MLE message containing additional information about the message (e.g.
+     * key sequence, neighbor from which it was received).
+     *
+     */
+    struct RxInfo
+    {
+        /**
+         * This constructor initializes the `RxInfo`
+         *
+         * @param[in] aMessage       The received MLE message.
+         * @param[in] aMessageInfo   The `Ip6::MessageInfo` associated with message.
+         *
+         */
+        RxInfo(Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
+            : mMessage(aMessage)
+            , mMessageInfo(aMessageInfo)
+            , mFrameCounter(0)
+            , mKeySequence(0)
+            , mNeighbor(nullptr)
+        {
+        }
+
+        Message &               mMessage;      ///< The MLE message.
+        const Ip6::MessageInfo &mMessageInfo;  ///< The `MessageInfo` associated with the message.
+        uint32_t                mFrameCounter; ///< The frame counter from aux security header.
+        uint32_t                mKeySequence;  ///< The key sequence from the aux security header.
+        Neighbor *              mNeighbor;     ///< Neighbor from which message was received (can be `nullptr`).
+    };
+
+    /**
+     * This method allocates and initializes new MLE message for a given command.
+     *
+     * @param[in] aCommand   The MLE command.
      *
      * @returns A pointer to the message or `nullptr` if insufficient message buffers are available.
      *
      */
-    Message *NewMleMessage(void);
+    Message *NewMleMessage(Command aCommand);
 
     /**
      * This method sets the device role.
@@ -950,6 +993,14 @@
     void SetRole(DeviceRole aRole);
 
     /**
+     * This method causes the Thread interface to attempt an MLE attach.
+     *
+     * @param[in]  aMode  Indicates what partitions to attach to.
+     *
+     */
+    void Attach(AttachMode aMode);
+
+    /**
      * This method sets the attach state
      *
      * @param[in] aState An attach state
@@ -958,18 +1009,6 @@
     void SetAttachState(AttachState aState);
 
     /**
-     * This method appends an MLE header to a message.
-     *
-     * @param[in]  aMessage  A reference to the message.
-     * @param[in]  aCommand  The MLE Command Type.
-     *
-     * @retval kErrorNone    Successfully appended the header.
-     * @retval kErrorNoBufs  Insufficient buffers available to append the header.
-     *
-     */
-    Error AppendHeader(Message &aMessage, Command aCommand);
-
-    /**
      * This method appends a Source Address TLV to a message.
      *
      * @param[in]  aMessage  A reference to the message.
@@ -1085,9 +1124,9 @@
      * Frame Counter TLV is present in the message, its value is read into @p aMleFrameCounter. If the MLE Frame
      * Counter TLV is not present in the message, then @p aMleFrameCounter is set to same value as @p aLinkFrameCounter.
      *
-     * @param[in]  aMesssage           A reference to the message to read from.
-     * @param[out] aLinkFrameCounter   A reference to an `uint32_t` to output the Link Frame Counter.
-     * @param[out] aMleFrameCounter    A reference to an `uint32_t` to output the MLE Frame Counter.
+     * @param[in]  aMessage           A reference to the message to read from.
+     * @param[out] aLinkFrameCounter  A reference to an `uint32_t` to output the Link Frame Counter.
+     * @param[out] aMleFrameCounter   A reference to an `uint32_t` to output the MLE Frame Counter.
      *
      * @retval kErrorNone       Successfully read the counters.
      * @retval kErrorNotFound   Link Frame Counter TLV was not found in the message.
@@ -1453,7 +1492,7 @@
      */
     Error AddDelayedResponse(Message &aMessage, const Ip6::Address &aDestination, uint16_t aDelay);
 
-#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_MLE == 1)
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
     /**
      * This static method emits a log message with an IPv6 address.
      *
@@ -1477,9 +1516,9 @@
 #else
     static void Log(MessageAction, MessageType, const Ip6::Address &) {}
     static void Log(MessageAction, MessageType, const Ip6::Address &, uint16_t) {}
-#endif // #if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_MLE == 1)
+#endif // #if OT_SHOULD_LOG_AT( OT_LOG_LEVEL_INFO)
 
-#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_WARN) && (OPENTHREAD_CONFIG_LOG_MLE == 1)
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN)
     /**
      * This static method emits a log message indicating an error in processing of a message.
      *
@@ -1506,7 +1545,7 @@
 #else
     static void LogProcessError(MessageType, Error) {}
     static void LogSendError(MessageType, Error) {}
-#endif // #if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_WARN) && (OPENTHREAD_CONFIG_LOG_MLE == 1)
+#endif // #if OT_SHOULD_LOG_AT( OT_LOG_LEVEL_WARN)
 
     /**
      * This method triggers MLE Announce on previous channel after the Thread device successfully
@@ -1526,7 +1565,7 @@
      */
     bool IsAnnounceAttach(void) const { return mAlternatePanId != Mac::kPanIdBroadcast; }
 
-#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_NOTE) && (OPENTHREAD_CONFIG_LOG_MLE == 1)
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_NOTE)
     /**
      * This method converts an `AttachMode` enumeration value into a human-readable string.
      *
@@ -1645,8 +1684,8 @@
 
     enum ParentRequestType : uint8_t
     {
-        kParentRequestTypeRouters,         // Parent Request to all routers.
-        kParentRequestTypeRoutersAndReeds, // Parent Request to all routers and REEDs.
+        kToRouters,         // Parent Request to routers only.
+        kToRoutersAndReeds, // Parent Request to all routers and REEDs.
     };
 
     enum ChildUpdateRequestState : uint8_t
@@ -1662,6 +1701,12 @@
         kDataRequestActive, // Data Request has been sent, Data Response is expected.
     };
 
+    enum SecuritySuite : uint8_t
+    {
+        k154Security = 0,   // Security suite value indicating that MLE message is not secured.
+        kNoSecurity  = 255, // Security suite value indicating that MLE message is secured.
+    };
+
     struct DelayedResponseMetadata
     {
         Error AppendTo(Message &aMessage) const { return aMessage.Append(*this); }
@@ -1673,95 +1718,29 @@
     };
 
     OT_TOOL_PACKED_BEGIN
-    class Header
+    class SecurityHeader
     {
     public:
-        enum SecuritySuite : uint8_t
-        {
-            k154Security = 0,
-            kNoSecurity  = 255,
-        };
+        void InitSecurityControl(void) { mSecurityControl = kKeyIdMode2Mic32; }
+        bool IsSecurityControlValid(void) const { return (mSecurityControl == kKeyIdMode2Mic32); }
 
-        void Init(void)
-        {
-            mSecuritySuite   = k154Security;
-            mSecurityControl = Mac::Frame::kSecEncMic32;
-        }
-
-        bool IsValid(void) const
-        {
-            return (mSecuritySuite == kNoSecurity) ||
-                   (mSecuritySuite == k154Security &&
-                    mSecurityControl == (Mac::Frame::kKeyIdMode2 | Mac::Frame::kSecEncMic32));
-        }
-
-        uint8_t GetLength(void) const
-        {
-            return sizeof(mSecuritySuite) + sizeof(mCommand) +
-                   ((mSecuritySuite == k154Security)
-                        ? sizeof(mSecurityControl) + sizeof(mFrameCounter) + sizeof(mKeySource) + sizeof(mKeyIndex)
-                        : 0);
-        }
-
-        SecuritySuite GetSecuritySuite(void) const { return static_cast<SecuritySuite>(mSecuritySuite); }
-        void SetSecuritySuite(SecuritySuite aSecuritySuite) { mSecuritySuite = static_cast<uint8_t>(aSecuritySuite); }
-
-        uint8_t GetHeaderLength(void) const
-        {
-            return sizeof(mSecurityControl) + sizeof(mFrameCounter) + sizeof(mKeySource) + sizeof(mKeyIndex);
-        }
-
-        const uint8_t *GetBytes(void) const { return reinterpret_cast<const uint8_t *>(&mSecuritySuite); }
-        uint8_t        GetSecurityControl(void) const { return mSecurityControl; }
-
-        bool IsKeyIdMode2(void) const
-        {
-            return (mSecurityControl & Mac::Frame::kKeyIdModeMask) == Mac::Frame::kKeyIdMode2;
-        }
-
-        void SetKeyIdMode2(void)
-        {
-            mSecurityControl = (mSecurityControl & ~Mac::Frame::kKeyIdModeMask) | Mac::Frame::kKeyIdMode2;
-        }
+        uint32_t GetFrameCounter(void) const { return Encoding::LittleEndian::HostSwap32(mFrameCounter); }
+        void     SetFrameCounter(uint32_t aCounter) { mFrameCounter = Encoding::LittleEndian::HostSwap32(aCounter); }
 
         uint32_t GetKeyId(void) const { return Encoding::BigEndian::HostSwap32(mKeySource); }
-
-        void SetKeyId(uint32_t aKeySequence)
+        void     SetKeyId(uint32_t aKeySequence)
         {
             mKeySource = Encoding::BigEndian::HostSwap32(aKeySequence);
             mKeyIndex  = (aKeySequence & 0x7f) + 1;
         }
 
-        uint32_t GetFrameCounter(void) const { return Encoding::LittleEndian::HostSwap32(mFrameCounter); }
-        void     SetFrameCounter(uint32_t aFrameCounter)
-        {
-            mFrameCounter = Encoding::LittleEndian::HostSwap32(aFrameCounter);
-        }
-
-        Command GetCommand(void) const
-        {
-            return static_cast<Command>((mSecuritySuite == kNoSecurity) ? mSecurityControl : mCommand);
-        }
-
-        void SetCommand(Command aCommand)
-        {
-            if (mSecuritySuite == kNoSecurity)
-            {
-                mSecurityControl = static_cast<uint8_t>(aCommand);
-            }
-            else
-            {
-                mCommand = static_cast<uint8_t>(aCommand);
-            }
-        }
-
     private:
-        uint8_t  mSecuritySuite;
+        static constexpr uint8_t kKeyIdMode2Mic32 = (Mac::Frame::kKeyIdMode2 | Mac::Frame::kSecEncMic32);
+
         uint8_t  mSecurityControl;
         uint32_t mFrameCounter;
         uint32_t mKeySource;
         uint8_t  mKeyIndex;
-        uint8_t  mCommand;
     } OT_TOOL_PACKED_END;
 
 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
@@ -1787,6 +1766,7 @@
     void        HandleAttachTimer(void);
     static void HandleDelayedResponseTimer(Timer &aTimer);
     void        HandleDelayedResponseTimer(void);
+    void        SendDelayedResponse(Message &aMessage, const DelayedResponseMetadata &aMetadata);
     static void HandleMessageTransmissionTimer(Timer &aTimer);
     void        HandleMessageTransmissionTimer(void);
     static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo);
@@ -1794,50 +1774,44 @@
     void        ScheduleMessageTransmissionTimer(void);
     Error       ReadChallengeOrResponse(const Message &aMessage, uint8_t aTlvType, Challenge &aBuffer);
 
-    void HandleAdvertisement(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, Neighbor *aNeighbor);
-    void HandleChildIdResponse(const Message &         aMessage,
-                               const Ip6::MessageInfo &aMessageInfo,
-                               const Neighbor *        aNeighbor);
-    void HandleChildUpdateRequest(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, Neighbor *aNeighbor);
-    void HandleChildUpdateResponse(const Message &         aMessage,
-                                   const Ip6::MessageInfo &aMessageInfo,
-                                   const Neighbor *        aNeighbor);
-    void HandleDataResponse(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, const Neighbor *aNeighbor);
-    void HandleParentResponse(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, uint32_t aKeySequence);
-    void HandleAnnounce(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
+    void HandleAdvertisement(RxInfo &aRxInfo);
+    void HandleChildIdResponse(RxInfo &aRxInfo);
+    void HandleChildUpdateRequest(RxInfo &aRxInfo);
+    void HandleChildUpdateResponse(RxInfo &aRxInfo);
+    void HandleDataResponse(RxInfo &aRxInfo);
+    void HandleParentResponse(RxInfo &aRxInfo);
+    void HandleAnnounce(RxInfo &aRxInfo);
 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
-    void HandleLinkMetricsManagementRequest(const Message &         aMessage,
-                                            const Ip6::MessageInfo &aMessageInfo,
-                                            Neighbor *              aNeighbor);
+    void HandleLinkMetricsManagementRequest(RxInfo &aRxInfo);
 #endif
 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
-    void HandleLinkMetricsManagementResponse(const Message &         aMessage,
-                                             const Ip6::MessageInfo &aMessageInfo,
-                                             Neighbor *              aNeighbor);
+    void HandleLinkMetricsManagementResponse(RxInfo &aRxInfo);
 #endif
 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
-    void HandleLinkProbe(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, Neighbor *aNeighbor);
+    void HandleLinkProbe(RxInfo &aRxInfo);
 #endif
-    Error HandleLeaderData(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
+    Error HandleLeaderData(RxInfo &aRxInfo);
     void  ProcessAnnounce(void);
     bool  HasUnregisteredAddress(void);
 
     uint32_t GetAttachStartDelay(void) const;
-    Error    SendParentRequest(ParentRequestType aType);
+    void     SendParentRequest(ParentRequestType aType);
     Error    SendChildIdRequest(void);
     Error    GetNextAnnouceChannel(uint8_t &aChannel) const;
     bool     HasMoreChannelsToAnnouce(void) const;
     bool     PrepareAnnounceState(void);
     void     SendAnnounce(uint8_t aChannel, AnnounceMode aMode);
     void     SendAnnounce(uint8_t aChannel, const Ip6::Address &aDestination, AnnounceMode aMode = kNormalAnnounce);
-    void     RemoveDelayedDataRequestMessage(const Ip6::Address &aDestination);
+    void RemoveDelayedMessage(Message::SubType aSubType, MessageType aMessageType, const Ip6::Address *aDestination);
+    void RemoveDelayedDataRequestMessage(const Ip6::Address &aDestination);
 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
     Error SendLinkMetricsManagementResponse(const Ip6::Address &aDestination, LinkMetrics::Status aStatus);
 #endif
     uint32_t Reattach(void);
+    bool     HasAcceptableParentCandidate(void) const;
 
     bool IsBetterParent(uint16_t               aRloc16,
-                        uint8_t                aLinkQuality,
+                        LinkQuality            aLinkQuality,
                         uint8_t                aLinkMargin,
                         const ConnectivityTlv &aConnectivityTlv,
                         uint8_t                aVersion,
@@ -1845,6 +1819,12 @@
                         uint8_t                aCslUncertainty);
     bool IsNetworkDataNewer(const LeaderData &aLeaderData);
 
+    Error ProcessMessageSecurity(Crypto::AesCcm::Mode    aMode,
+                                 Message &               aMessage,
+                                 const Ip6::MessageInfo &aMessageInfo,
+                                 uint16_t                aCmdOffset,
+                                 const SecurityHeader &  aHeader);
+
 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
     ServiceAloc *FindInServiceAlocs(uint16_t aAloc16);
     void         UpdateServiceAlocs(void);
@@ -1861,7 +1841,7 @@
     void        UpdateParentSearchState(void);
 #endif
 
-#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_WARN) && (OPENTHREAD_CONFIG_LOG_MLE == 1)
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN)
     static void        LogError(MessageAction aAction, MessageType aType, Error aError);
     static const char *MessageActionToString(MessageAction aAction);
     static const char *MessageTypeToString(MessageType aType);
@@ -1872,7 +1852,7 @@
 
     Challenge mParentRequestChallenge;
 
-    AttachMode mParentRequestMode;
+    AttachMode mAttachMode;
     int8_t     mParentPriority;
     uint8_t    mParentLinkQuality3;
     uint8_t    mParentLinkQuality2;
diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp
index 5c76f3a..bde4042 100644
--- a/src/core/thread/mle_router.cpp
+++ b/src/core/thread/mle_router.cpp
@@ -40,7 +40,6 @@
 #include "common/encoding.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
 #include "common/random.hpp"
 #include "common/serial_number.hpp"
 #include "common/settings.hpp"
@@ -57,6 +56,8 @@
 namespace ot {
 namespace Mle {
 
+RegisterLogModule("Mle");
+
 MleRouter::MleRouter(Instance &aInstance)
     : Mle(aInstance)
     , mAdvertiseTrickleTimer(aInstance, MleRouter::HandleAdvertiseTrickleTimer)
@@ -191,7 +192,7 @@
     VerifyOrExit(!IsRouter(), error = kErrorNone);
     VerifyOrExit(IsRouterEligible(), error = kErrorNotCapable);
 
-    otLogInfoMle("Attempt to become router");
+    LogInfo("Attempt to become router");
 
     Get<MeshForwarder>().SetRxOnWhenIdle(true);
     mRouterSelectionJitterTimeout = 0;
@@ -227,7 +228,7 @@
     uint8_t maxRouterId;
 #endif
 
-    VerifyOrExit(!Get<MeshCoP::ActiveDataset>().IsPartiallyComplete(), error = kErrorInvalidState);
+    VerifyOrExit(!Get<MeshCoP::ActiveDatasetManager>().IsPartiallyComplete(), error = kErrorInvalidState);
     VerifyOrExit(!IsDisabled(), error = kErrorInvalidState);
     VerifyOrExit(!IsLeader(), error = kErrorNone);
     VerifyOrExit(IsRouterEligible(), error = kErrorNotCapable);
@@ -276,8 +277,8 @@
 {
     Get<Tmf::Agent>().RemoveResource(mAddressSolicit);
     Get<Tmf::Agent>().RemoveResource(mAddressRelease);
-    Get<MeshCoP::ActiveDataset>().StopLeader();
-    Get<MeshCoP::PendingDataset>().StopLeader();
+    Get<MeshCoP::ActiveDatasetManager>().StopLeader();
+    Get<MeshCoP::PendingDatasetManager>().StopLeader();
     StopAdvertiseTrickleTimer();
     Get<NetworkData::Leader>().Stop();
     Get<ThreadNetif>().UnsubscribeAllRoutersMulticast();
@@ -311,7 +312,7 @@
 
     switch (aMode)
     {
-    case kAttachSameDowngrade:
+    case kDowngradeToReed:
         SendAddressRelease();
 
         // reset children info if any
@@ -324,8 +325,8 @@
         SetRouterId(kInvalidRouterId);
         break;
 
-    case kAttachSame1:
-    case kAttachSame2:
+    case kSamePartition:
+    case kSamePartitionRetry:
         if (HasChildren())
         {
             IgnoreError(BecomeRouter(ThreadStatusTlv::kHaveChildIdRequest));
@@ -333,7 +334,8 @@
 
         break;
 
-    case kAttachAny:
+    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,
@@ -350,7 +352,7 @@
 
         OT_FALL_THROUGH;
 
-    case kAttachBetter:
+    case kBetterPartition:
         if (HasChildren() && mPreviousPartitionIdRouter != mLeaderData.GetPartitionId())
         {
             IgnoreError(BecomeRouter(ThreadStatusTlv::kParentPartitionChange));
@@ -399,8 +401,8 @@
 
 void MleRouter::SetStateLeader(uint16_t aRloc16)
 {
-    IgnoreError(Get<MeshCoP::ActiveDataset>().Restore());
-    IgnoreError(Get<MeshCoP::PendingDataset>().Restore());
+    IgnoreError(Get<MeshCoP::ActiveDatasetManager>().Restore());
+    IgnoreError(Get<MeshCoP::PendingDatasetManager>().Restore());
     SetRloc16(aRloc16);
 
     SetRole(kRoleLeader);
@@ -418,8 +420,8 @@
     Get<TimeTicker>().RegisterReceiver(TimeTicker::kMleRouter);
 
     Get<NetworkData::Leader>().Start();
-    Get<MeshCoP::ActiveDataset>().StartLeader();
-    Get<MeshCoP::PendingDataset>().StartLeader();
+    Get<MeshCoP::ActiveDatasetManager>().StartLeader();
+    Get<MeshCoP::PendingDatasetManager>().StartLeader();
     Get<Tmf::Agent>().AddResource(mAddressSolicit);
     Get<Tmf::Agent>().AddResource(mAddressRelease);
     Get<Ip6::Ip6>().SetForwardingEnabled(true);
@@ -436,7 +438,7 @@
         }
     }
 
-    otLogNoteMle("Leader partition id 0x%x", mLeaderData.GetPartitionId());
+    LogNote("Leader partition id 0x%x", mLeaderData.GetPartitionId());
 }
 
 void MleRouter::HandleAdvertiseTrickleTimer(TrickleTimer &aTimer)
@@ -494,8 +496,7 @@
     // children to detach.
     VerifyOrExit(!mAddressSolicitPending);
 
-    VerifyOrExit((message = NewMleMessage()) != nullptr, error = kErrorNoBufs);
-    SuccessOrExit(error = AppendHeader(*message, kCommandAdvertisement));
+    VerifyOrExit((message = NewMleMessage(kCommandAdvertisement)) != nullptr, error = kErrorNoBufs);
     SuccessOrExit(error = AppendSourceAddress(*message));
     SuccessOrExit(error = AppendLeaderData(*message));
 
@@ -536,8 +537,7 @@
 
     destination.Clear();
 
-    VerifyOrExit((message = NewMleMessage()) != nullptr, error = kErrorNoBufs);
-    SuccessOrExit(error = AppendHeader(*message, kCommandLinkRequest));
+    VerifyOrExit((message = NewMleMessage(kCommandLinkRequest)) != nullptr, error = kErrorNoBufs);
     SuccessOrExit(error = AppendVersion(*message));
 
     switch (mRole)
@@ -610,7 +610,7 @@
     return error;
 }
 
-void MleRouter::HandleLinkRequest(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, Neighbor *aNeighbor)
+void MleRouter::HandleLinkRequest(RxInfo &aRxInfo)
 {
     Error         error    = kErrorNone;
     Neighbor *    neighbor = nullptr;
@@ -620,21 +620,21 @@
     uint16_t      sourceAddress;
     RequestedTlvs requestedTlvs;
 
-    Log(kMessageReceive, kTypeLinkRequest, aMessageInfo.GetPeerAddr());
+    Log(kMessageReceive, kTypeLinkRequest, aRxInfo.mMessageInfo.GetPeerAddr());
 
     VerifyOrExit(IsRouterOrLeader(), error = kErrorInvalidState);
 
     VerifyOrExit(!IsAttaching(), error = kErrorInvalidState);
 
     // Challenge
-    SuccessOrExit(error = ReadChallenge(aMessage, challenge));
+    SuccessOrExit(error = ReadChallenge(aRxInfo.mMessage, challenge));
 
     // Version
-    SuccessOrExit(error = Tlv::Find<VersionTlv>(aMessage, version));
+    SuccessOrExit(error = Tlv::Find<VersionTlv>(aRxInfo.mMessage, version));
     VerifyOrExit(version >= OT_THREAD_VERSION_1_1, error = kErrorParse);
 
     // Leader Data
-    switch (ReadLeaderData(aMessage, leaderData))
+    switch (ReadLeaderData(aRxInfo.mMessage, leaderData))
     {
     case kErrorNone:
         VerifyOrExit(leaderData.GetPartitionId() == mLeaderData.GetPartitionId(), error = kErrorInvalidState);
@@ -646,14 +646,14 @@
     }
 
     // Source Address
-    switch (Tlv::Find<SourceAddressTlv>(aMessage, sourceAddress))
+    switch (Tlv::Find<SourceAddressTlv>(aRxInfo.mMessage, sourceAddress))
     {
     case kErrorNone:
         if (IsActiveRouter(sourceAddress))
         {
             Mac::ExtAddress extAddr;
 
-            aMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(extAddr);
+            aRxInfo.mMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(extAddr);
 
             neighbor = mRouterTable.GetRouter(RouterIdFromRloc16(sourceAddress));
             VerifyOrExit(neighbor != nullptr, error = kErrorParse);
@@ -663,7 +663,7 @@
             {
                 neighbor->SetExtAddress(extAddr);
                 neighbor->GetLinkInfo().Clear();
-                neighbor->GetLinkInfo().AddRss(aMessageInfo.GetThreadLinkInfo()->GetRss());
+                neighbor->GetLinkInfo().AddRss(aRxInfo.mMessageInfo.GetThreadLinkInfo()->GetRss());
                 neighbor->ResetLinkFailures();
                 neighbor->SetLastHeard(TimerMilli::GetNow());
                 neighbor->SetState(Neighbor::kStateLinkRequest);
@@ -678,9 +678,10 @@
 
     case kErrorNotFound:
         // lack of source address indicates router coming out of reset
-        VerifyOrExit(aNeighbor && aNeighbor->IsStateValid() && IsActiveRouter(aNeighbor->GetRloc16()),
+        VerifyOrExit(aRxInfo.mNeighbor && aRxInfo.mNeighbor->IsStateValid() &&
+                         IsActiveRouter(aRxInfo.mNeighbor->GetRloc16()),
                      error = kErrorDrop);
-        neighbor = aNeighbor;
+        neighbor = aRxInfo.mNeighbor;
         break;
 
     default:
@@ -688,7 +689,7 @@
     }
 
     // TLV Request
-    switch (FindTlvRequest(aMessage, requestedTlvs))
+    switch (FindTlvRequest(aRxInfo.mMessage, requestedTlvs))
     {
     case kErrorNone:
         break;
@@ -702,7 +703,7 @@
 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
     if (neighbor != nullptr)
     {
-        neighbor->SetTimeSyncEnabled(Tlv::Find<TimeRequestTlv>(aMessage, nullptr, 0) == kErrorNone);
+        neighbor->SetTimeSyncEnabled(Tlv::Find<TimeRequestTlv>(aRxInfo.mMessage, nullptr, 0) == kErrorNone);
     }
 #endif
 
@@ -713,7 +714,7 @@
     }
 #endif
 
-    SuccessOrExit(error = SendLinkAccept(aMessageInfo, neighbor, requestedTlvs, challenge));
+    SuccessOrExit(error = SendLinkAccept(aRxInfo.mMessageInfo, neighbor, requestedTlvs, challenge));
 
 exit:
     LogProcessError(kTypeLinkRequest, error);
@@ -732,8 +733,7 @@
 
     command = (aNeighbor == nullptr || aNeighbor->IsStateValid()) ? kCommandLinkAccept : kCommandLinkAcceptAndRequest;
 
-    VerifyOrExit((message = NewMleMessage()) != nullptr, error = kErrorNoBufs);
-    SuccessOrExit(error = AppendHeader(*message, command));
+    VerifyOrExit((message = NewMleMessage(command)) != nullptr, error = kErrorNoBufs);
     SuccessOrExit(error = AppendVersion(*message));
     SuccessOrExit(error = AppendSourceAddress(*message));
     SuccessOrExit(error = AppendResponse(*message, aChallenge));
@@ -808,31 +808,21 @@
     return error;
 }
 
-void MleRouter::HandleLinkAccept(const Message &         aMessage,
-                                 const Ip6::MessageInfo &aMessageInfo,
-                                 uint32_t                aKeySequence,
-                                 Neighbor *              aNeighbor)
+void MleRouter::HandleLinkAccept(RxInfo &aRxInfo)
 {
-    Error error = HandleLinkAccept(aMessage, aMessageInfo, aKeySequence, aNeighbor, false);
+    Error error = HandleLinkAccept(aRxInfo, false);
 
     LogProcessError(kTypeLinkAccept, error);
 }
 
-void MleRouter::HandleLinkAcceptAndRequest(const Message &         aMessage,
-                                           const Ip6::MessageInfo &aMessageInfo,
-                                           uint32_t                aKeySequence,
-                                           Neighbor *              aNeighbor)
+void MleRouter::HandleLinkAcceptAndRequest(RxInfo &aRxInfo)
 {
-    Error error = HandleLinkAccept(aMessage, aMessageInfo, aKeySequence, aNeighbor, true);
+    Error error = HandleLinkAccept(aRxInfo, true);
 
     LogProcessError(kTypeLinkAcceptAndRequest, error);
 }
 
-Error MleRouter::HandleLinkAccept(const Message &         aMessage,
-                                  const Ip6::MessageInfo &aMessageInfo,
-                                  uint32_t                aKeySequence,
-                                  Neighbor *              aNeighbor,
-                                  bool                    aRequest)
+Error MleRouter::HandleLinkAccept(RxInfo &aRxInfo, bool aRequest)
 {
     static const uint8_t dataRequestTlvs[] = {Tlv::kNetworkData};
 
@@ -847,14 +837,14 @@
     uint32_t        mleFrameCounter;
     uint8_t         routerId;
     uint16_t        address16;
-    RouteTlv        route;
+    RouteTlv        routeTlv;
     LeaderData      leaderData;
     uint8_t         linkMargin;
 
     // Source Address
-    SuccessOrExit(error = Tlv::Find<SourceAddressTlv>(aMessage, sourceAddress));
+    SuccessOrExit(error = Tlv::Find<SourceAddressTlv>(aRxInfo.mMessage, sourceAddress));
 
-    Log(kMessageReceive, aRequest ? kTypeLinkAcceptAndRequest : kTypeLinkAccept, aMessageInfo.GetPeerAddr(),
+    Log(kMessageReceive, aRequest ? kTypeLinkAcceptAndRequest : kTypeLinkAccept, aRxInfo.mMessageInfo.GetPeerAddr(),
         sourceAddress);
 
     VerifyOrExit(IsActiveRouter(sourceAddress), error = kErrorParse);
@@ -864,7 +854,7 @@
     neighborState = (router != nullptr) ? router->GetState() : Neighbor::kStateInvalid;
 
     // Response
-    SuccessOrExit(error = ReadResponse(aMessage, response));
+    SuccessOrExit(error = ReadResponse(aRxInfo.mMessage, response));
 
     // verify response
     switch (neighborState)
@@ -886,20 +876,20 @@
     }
 
     // Remove stale neighbors
-    if (aNeighbor && aNeighbor->GetRloc16() != sourceAddress)
+    if (aRxInfo.mNeighbor && aRxInfo.mNeighbor->GetRloc16() != sourceAddress)
     {
-        RemoveNeighbor(*aNeighbor);
+        RemoveNeighbor(*aRxInfo.mNeighbor);
     }
 
     // Version
-    SuccessOrExit(error = Tlv::Find<VersionTlv>(aMessage, version));
+    SuccessOrExit(error = Tlv::Find<VersionTlv>(aRxInfo.mMessage, version));
     VerifyOrExit(version >= OT_THREAD_VERSION_1_1, error = kErrorParse);
 
     // Link and MLE Frame Counters
-    SuccessOrExit(error = ReadFrameCounters(aMessage, linkFrameCounter, mleFrameCounter));
+    SuccessOrExit(error = ReadFrameCounters(aRxInfo.mMessage, linkFrameCounter, mleFrameCounter));
 
     // Link Margin
-    switch (Tlv::Find<LinkMarginTlv>(aMessage, linkMargin))
+    switch (Tlv::Find<LinkMarginTlv>(aRxInfo.mMessage, linkMargin))
     {
     case kErrorNone:
         break;
@@ -921,18 +911,16 @@
 
     case kRoleDetached:
         // Address16
-        SuccessOrExit(error = Tlv::Find<Address16Tlv>(aMessage, address16));
+        SuccessOrExit(error = Tlv::Find<Address16Tlv>(aRxInfo.mMessage, address16));
         VerifyOrExit(GetRloc16() == address16, error = kErrorDrop);
 
         // Leader Data
-        SuccessOrExit(error = ReadLeaderData(aMessage, leaderData));
+        SuccessOrExit(error = ReadLeaderData(aRxInfo.mMessage, leaderData));
         SetLeaderData(leaderData.GetPartitionId(), leaderData.GetWeighting(), leaderData.GetLeaderRouterId());
 
         // Route
-        SuccessOrExit(error = Tlv::FindTlv(aMessage, Tlv::kRoute, sizeof(route), route));
-        VerifyOrExit(route.IsValid(), error = kErrorParse);
         mRouterTable.Clear();
-        SuccessOrExit(error = ProcessRouteTlv(route));
+        SuccessOrExit(error = ProcessRouteTlv(aRxInfo));
         router = mRouterTable.GetRouter(routerId);
         VerifyOrExit(router != nullptr);
 
@@ -946,10 +934,10 @@
         }
 
         mRetrieveNewNetworkData = true;
-        IgnoreError(SendDataRequest(aMessageInfo.GetPeerAddr(), dataRequestTlvs, sizeof(dataRequestTlvs), 0));
+        IgnoreError(SendDataRequest(aRxInfo.mMessageInfo.GetPeerAddr(), dataRequestTlvs, sizeof(dataRequestTlvs), 0));
 
 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
-        Get<TimeSync>().HandleTimeSyncMessage(aMessage);
+        Get<TimeSync>().HandleTimeSyncMessage(aRxInfo.mMessage);
 #endif
         break;
 
@@ -962,25 +950,33 @@
         VerifyOrExit(router != nullptr);
 
         // Leader Data
-        SuccessOrExit(error = ReadLeaderData(aMessage, leaderData));
+        SuccessOrExit(error = ReadLeaderData(aRxInfo.mMessage, leaderData));
         VerifyOrExit(leaderData.GetPartitionId() == mLeaderData.GetPartitionId());
 
         if (mRetrieveNewNetworkData ||
             SerialNumber::IsGreater(leaderData.GetDataVersion(NetworkData::kFullSet),
                                     Get<NetworkData::Leader>().GetVersion(NetworkData::kFullSet)))
         {
-            IgnoreError(SendDataRequest(aMessageInfo.GetPeerAddr(), dataRequestTlvs, sizeof(dataRequestTlvs), 0));
+            IgnoreError(
+                SendDataRequest(aRxInfo.mMessageInfo.GetPeerAddr(), dataRequestTlvs, sizeof(dataRequestTlvs), 0));
         }
 
         // Route (optional)
-        if (Tlv::FindTlv(aMessage, route) == kErrorNone)
+        switch (error = ProcessRouteTlv(aRxInfo, routeTlv))
         {
-            VerifyOrExit(route.IsValid(), error = kErrorParse);
-            SuccessOrExit(error = ProcessRouteTlv(route));
-            UpdateRoutes(route, routerId);
-            // need to update router after ProcessRouteTlv
+        case kErrorNone:
+            UpdateRoutes(routeTlv, routerId);
+            // Need to update router after ProcessRouteTlv
             router = mRouterTable.GetRouter(routerId);
             OT_ASSERT(router != nullptr);
+            break;
+
+        case kErrorNotFound:
+            error = kErrorNone;
+            break;
+
+        default:
+            ExitNow();
         }
 
         // update routing table
@@ -993,7 +989,7 @@
     }
 
     // finish link synchronization
-    aMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(extAddr);
+    aRxInfo.mMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(extAddr);
     router->SetExtAddress(extAddr);
     router->SetRloc16(sourceAddress);
     router->GetLinkFrameCounters().SetAll(linkFrameCounter);
@@ -1004,11 +1000,11 @@
     router->SetDeviceMode(DeviceMode(DeviceMode::kModeFullThreadDevice | DeviceMode::kModeRxOnWhenIdle |
                                      DeviceMode::kModeFullNetworkData));
     router->GetLinkInfo().Clear();
-    router->GetLinkInfo().AddRss(aMessageInfo.GetThreadLinkInfo()->GetRss());
+    router->GetLinkInfo().AddRss(aRxInfo.mMessageInfo.GetThreadLinkInfo()->GetRss());
     router->SetLinkQualityOut(LinkQualityInfo::ConvertLinkMarginToLinkQuality(linkMargin));
     router->ResetLinkFailures();
     router->SetState(Neighbor::kStateValid);
-    router->SetKeySequence(aKeySequence);
+    router->SetKeySequence(aRxInfo.mKeySequence);
 
     mNeighborTable.Signal(NeighborTable::kRouterAdded, *router);
 
@@ -1018,10 +1014,10 @@
         RequestedTlvs requestedTlvs;
 
         // Challenge
-        SuccessOrExit(error = ReadChallenge(aMessage, challenge));
+        SuccessOrExit(error = ReadChallenge(aRxInfo.mMessage, challenge));
 
         // TLV Request
-        switch (FindTlvRequest(aMessage, requestedTlvs))
+        switch (FindTlvRequest(aRxInfo.mMessage, requestedTlvs))
         {
         case kErrorNone:
             break;
@@ -1032,7 +1028,7 @@
             ExitNow(error = kErrorParse);
         }
 
-        SuccessOrExit(error = SendLinkAccept(aMessageInfo, router, requestedTlvs, challenge));
+        SuccessOrExit(error = SendLinkAccept(aRxInfo.mMessageInfo, router, requestedTlvs, challenge));
     }
 
 exit:
@@ -1093,18 +1089,54 @@
     return error;
 }
 
-Error MleRouter::ProcessRouteTlv(const RouteTlv &aRoute)
+Error MleRouter::ProcessRouteTlv(RxInfo &aRxInfo)
 {
-    Error error = kErrorNone;
+    RouteTlv routeTlv;
 
-    mRouterTable.UpdateRouterIdSet(aRoute.GetRouterIdSequence(), aRoute.GetRouterIdMask());
+    return ProcessRouteTlv(aRxInfo, routeTlv);
+}
 
-    if (IsRouter() && !mRouterTable.IsAllocated(mRouterId))
+Error MleRouter::ProcessRouteTlv(RxInfo &aRxInfo, RouteTlv &aRouteTlv)
+{
+    // This method processes Route TLV in a received MLE message
+    // (from `RxInfo`). In case of success, `aRouteTlv` is updated
+    // to return the read/processed route TLV from the message.
+    // If the message contains no Route TLV, `kErrorNotFound` is
+    // returned.
+    //
+    // During processing of Route TLV, the entries in the router table
+    // may shuffle. This method ensures that the `aRxInfo.mNeighbor`
+    // (which indicates the neighbor from which the MLE message was
+    // received) is correctly updated to point to the same neighbor
+    // (in case `mNeighbor` was pointing to a router entry from the
+    // `RouterTable`).
+
+    Error    error;
+    uint16_t neighborRloc16 = Mac::kShortAddrInvalid;
+
+    if ((aRxInfo.mNeighbor != nullptr) && Get<RouterTable>().Contains(*aRxInfo.mNeighbor))
+    {
+        neighborRloc16 = aRxInfo.mNeighbor->GetRloc16();
+    }
+
+    SuccessOrExit(error = Tlv::FindTlv(aRxInfo.mMessage, aRouteTlv));
+
+    VerifyOrExit(aRouteTlv.IsValid(), error = kErrorParse);
+
+    Get<RouterTable>().UpdateRouterIdSet(aRouteTlv.GetRouterIdSequence(), aRouteTlv.GetRouterIdMask());
+
+    if (IsRouter() && !Get<RouterTable>().IsAllocated(mRouterId))
     {
         IgnoreError(BecomeDetached());
         error = kErrorNoRoute;
     }
 
+    if (neighborRloc16 != Mac::kShortAddrInvalid)
+    {
+        aRxInfo.mNeighbor = Get<RouterTable>().GetNeighbor(neighborRloc16);
+    }
+
+exit:
     return error;
 }
 
@@ -1175,10 +1207,10 @@
     return rval;
 }
 
-Error MleRouter::HandleAdvertisement(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, Neighbor *aNeighbor)
+Error MleRouter::HandleAdvertisement(RxInfo &aRxInfo)
 {
     Error                 error    = kErrorNone;
-    const ThreadLinkInfo *linkInfo = aMessageInfo.GetThreadLinkInfo();
+    const ThreadLinkInfo *linkInfo = aRxInfo.mMessageInfo.GetThreadLinkInfo();
     uint8_t linkMargin = LinkQualityInfo::ConvertRssToLinkMargin(Get<Mac::Mac>().GetNoiseFloor(), linkInfo->GetRss());
     Mac::ExtAddress extAddr;
     uint16_t        sourceAddress = Mac::kShortAddrInvalid;
@@ -1189,16 +1221,16 @@
     uint8_t         routerId;
     uint8_t         routerCount;
 
-    aMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(extAddr);
+    aRxInfo.mMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(extAddr);
 
     // Source Address
-    SuccessOrExit(error = Tlv::Find<SourceAddressTlv>(aMessage, sourceAddress));
+    SuccessOrExit(error = Tlv::Find<SourceAddressTlv>(aRxInfo.mMessage, sourceAddress));
 
     // Leader Data
-    SuccessOrExit(error = ReadLeaderData(aMessage, leaderData));
+    SuccessOrExit(error = ReadLeaderData(aRxInfo.mMessage, leaderData));
 
     // Route Data (optional)
-    if (Tlv::FindTlv(aMessage, route) == kErrorNone)
+    if (Tlv::FindTlv(aRxInfo.mMessage, route) == kErrorNone)
     {
         VerifyOrExit(route.IsValid(), error = kErrorParse);
     }
@@ -1212,7 +1244,7 @@
 
     if (partitionId != mLeaderData.GetPartitionId())
     {
-        otLogNoteMle("Different partition (peer:%u, local:%u)", partitionId, mLeaderData.GetPartitionId());
+        LogNote("Different partition (peer:%u, local:%u)", partitionId, mLeaderData.GetPartitionId());
 
         VerifyOrExit(linkMargin >= OPENTHREAD_CONFIG_MLE_PARTITION_MERGE_MARGIN_MIN, error = kErrorLinkMarginLow);
 
@@ -1223,7 +1255,7 @@
                          error = kErrorDrop);
         }
 
-        if (IsChild() && (aNeighbor == &mParent || !IsFullThreadDevice()))
+        if (IsChild() && (aRxInfo.mNeighbor == &mParent || !IsFullThreadDevice()))
         {
             ExitNow();
         }
@@ -1231,22 +1263,22 @@
         if (ComparePartitions(IsSingleton(route), leaderData, 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.
-            && aMessage.GetTimeSyncSeq() != OT_TIME_SYNC_INVALID_SEQ
+            && aRxInfo.mMessage.GetTimeSyncSeq() != OT_TIME_SYNC_INVALID_SEQ
 #endif
         )
         {
-            IgnoreError(BecomeChild(kAttachBetter));
+            Attach(kBetterPartition);
         }
 
         ExitNow(error = kErrorDrop);
     }
     else if (leaderData.GetLeaderRouterId() != GetLeaderId())
     {
-        VerifyOrExit(aNeighbor && aNeighbor->IsStateValid());
+        VerifyOrExit(aRxInfo.mNeighbor && aRxInfo.mNeighbor->IsStateValid());
 
         if (!IsChild())
         {
-            otLogInfoMle("Leader ID mismatch");
+            LogInfo("Leader ID mismatch");
             IgnoreError(BecomeDetached());
             error = kErrorDrop;
         }
@@ -1258,10 +1290,10 @@
     routerId = RouterIdFromRloc16(sourceAddress);
 
 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
-    Get<TimeSync>().HandleTimeSyncMessage(aMessage);
+    Get<TimeSync>().HandleTimeSyncMessage(aRxInfo.mMessage);
 #endif
 
-    if (IsFullThreadDevice() && (aNeighbor && aNeighbor->IsStateValid()) &&
+    if (IsFullThreadDevice() && (aRxInfo.mNeighbor && aRxInfo.mNeighbor->IsStateValid()) &&
         ((mRouterTable.GetActiveRouterCount() == 0) ||
          SerialNumber::IsGreater(route.GetRouterIdSequence(), mRouterTable.GetRouterIdSequence())))
     {
@@ -1298,11 +1330,7 @@
 
         if (processRouteTlv)
         {
-            SuccessOrExit(error = ProcessRouteTlv(route));
-            if (Get<RouterTable>().Contains(*aNeighbor))
-            {
-                aNeighbor = nullptr; // aNeighbor is no longer valid after `ProcessRouteTlv`
-            }
+            SuccessOrExit(error = ProcessRouteTlv(aRxInfo));
         }
     }
 
@@ -1313,7 +1341,7 @@
         ExitNow();
 
     case kRoleChild:
-        if (aNeighbor == &mParent)
+        if (aRxInfo.mNeighbor == &mParent)
         {
             // MLE Advertisement from parent
             router = &mParent;
@@ -1321,7 +1349,7 @@
             if (mParent.GetRloc16() != sourceAddress)
             {
                 IgnoreError(BecomeDetached());
-                ExitNow(error = kErrorNoRoute);
+                ExitNow(error = kErrorDetached);
             }
 
             if (IsFullThreadDevice())
@@ -1441,10 +1469,10 @@
     UpdateRoutes(route, routerId);
 
 exit:
-    if (aNeighbor && aNeighbor->GetRloc16() != sourceAddress)
+    if (aRxInfo.mNeighbor && aRxInfo.mNeighbor->GetRloc16() != sourceAddress)
     {
         // Remove stale neighbors
-        RemoveNeighbor(*aNeighbor);
+        RemoveNeighbor(*aRxInfo.mNeighbor);
     }
 
     return error;
@@ -1550,18 +1578,18 @@
         ResetAdvertiseInterval();
     }
 
-#if (OPENTHREAD_CONFIG_LOG_MLE && (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO))
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
 
     VerifyOrExit(changed);
-    otLogInfoMle("Route table updated");
+    LogInfo("Route table updated");
 
     for (Router &router : Get<RouterTable>().Iterate())
     {
-        otLogInfoMle("    %04x -> %04x, cost:%d %d, lqin:%d, lqout:%d, link:%s", router.GetRloc16(),
-                     (router.GetNextHop() == kInvalidRouterId) ? 0xffff : Rloc16FromRouterId(router.GetNextHop()),
-                     router.GetCost(), mRouterTable.GetLinkCost(router), router.GetLinkInfo().GetLinkQuality(),
-                     router.GetLinkQualityOut(),
-                     router.GetRloc16() == GetRloc16() ? "device" : ToYesNo(router.IsStateValid()));
+        LogInfo("    %04x -> %04x, cost:%d %d, lqin:%d, lqout:%d, link:%s", router.GetRloc16(),
+                (router.GetNextHop() == kInvalidRouterId) ? 0xffff : Rloc16FromRouterId(router.GetNextHop()),
+                router.GetCost(), mRouterTable.GetLinkCost(router), router.GetLinkInfo().GetLinkQuality(),
+                router.GetLinkQualityOut(),
+                router.GetRloc16() == GetRloc16() ? "device" : ToYesNo(router.IsStateValid()));
     }
 
 #else
@@ -1574,12 +1602,12 @@
 
 bool MleRouter::UpdateLinkQualityOut(const RouteTlv &aRoute, Router &aNeighbor, bool &aResetAdvInterval)
 {
-    bool    changed = false;
-    uint8_t linkQuality;
-    uint8_t myRouterId;
-    uint8_t myRouteCount;
-    uint8_t oldLinkCost;
-    Router *nextHop;
+    bool        changed = false;
+    LinkQuality linkQuality;
+    uint8_t     myRouterId;
+    uint8_t     myRouteCount;
+    uint8_t     oldLinkCost;
+    Router *    nextHop;
 
     myRouterId = RouterIdFromRloc16(GetRloc16());
     VerifyOrExit(aRoute.IsRouterIdSet(myRouterId));
@@ -1609,7 +1637,7 @@
     return changed;
 }
 
-void MleRouter::HandleParentRequest(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
+void MleRouter::HandleParentRequest(RxInfo &aRxInfo)
 {
     Error           error = kErrorNone;
     Mac::ExtAddress extAddr;
@@ -1621,7 +1649,7 @@
     uint8_t         modeBitmask;
     DeviceMode      mode;
 
-    Log(kMessageReceive, kTypeParentRequest, aMessageInfo.GetPeerAddr());
+    Log(kMessageReceive, kTypeParentRequest, aRxInfo.mMessageInfo.GetPeerAddr());
 
     VerifyOrExit(IsRouterEligible(), error = kErrorInvalidState);
 
@@ -1652,14 +1680,14 @@
     // the network (because Leader would reject any further address solicit).
     // ==> Verified below when checking the scan mask.
 
-    aMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(extAddr);
+    aRxInfo.mMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(extAddr);
 
     // Version
-    SuccessOrExit(error = Tlv::Find<VersionTlv>(aMessage, version));
+    SuccessOrExit(error = Tlv::Find<VersionTlv>(aRxInfo.mMessage, version));
     VerifyOrExit(version >= OT_THREAD_VERSION_1_1, error = kErrorParse);
 
     // Scan Mask
-    SuccessOrExit(error = Tlv::Find<ScanMaskTlv>(aMessage, scanMask));
+    SuccessOrExit(error = Tlv::Find<ScanMaskTlv>(aRxInfo.mMessage, scanMask));
 
     switch (mRole)
     {
@@ -1679,7 +1707,7 @@
     }
 
     // Challenge
-    SuccessOrExit(error = ReadChallenge(aMessage, challenge));
+    SuccessOrExit(error = ReadChallenge(aRxInfo.mMessage, challenge));
 
     child = mChildTable.FindChild(extAddr, Child::kInStateAnyExceptInvalid);
 
@@ -1690,13 +1718,13 @@
         // MAC Address
         child->SetExtAddress(extAddr);
         child->GetLinkInfo().Clear();
-        child->GetLinkInfo().AddRss(aMessageInfo.GetThreadLinkInfo()->GetRss());
+        child->GetLinkInfo().AddRss(aRxInfo.mMessageInfo.GetThreadLinkInfo()->GetRss());
         child->ResetLinkFailures();
         child->SetState(Neighbor::kStateParentRequest);
 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
-        child->SetTimeSyncEnabled(Tlv::Find<TimeRequestTlv>(aMessage, nullptr, 0) == kErrorNone);
+        child->SetTimeSyncEnabled(Tlv::Find<TimeRequestTlv>(aRxInfo.mMessage, nullptr, 0) == kErrorNone);
 #endif
-        if (Tlv::Find<ModeTlv>(aMessage, modeBitmask) == kErrorNone)
+        if (Tlv::Find<ModeTlv>(aRxInfo.mMessage, modeBitmask) == kErrorNone)
         {
             mode.Set(modeBitmask);
             child->SetDeviceMode(mode);
@@ -1810,19 +1838,18 @@
 
     case kRoleRouter:
         // verify path to leader
-        otLogDebgMle("network id timeout = %d", mRouterTable.GetLeaderAge());
+        LogDebg("network id timeout = %d", mRouterTable.GetLeaderAge());
 
         if ((mRouterTable.GetActiveRouterCount() > 0) && (mRouterTable.GetLeaderAge() >= mNetworkIdTimeout))
         {
-            otLogInfoMle("Router ID Sequence timeout");
-            IgnoreError(BecomeChild(kAttachSame1));
+            LogInfo("Router ID Sequence timeout");
+            Attach(kSamePartition);
         }
 
         if (routerStateUpdate && mRouterTable.GetActiveRouterCount() > mRouterDowngradeThreshold)
         {
-            // downgrade to REED
-            otLogNoteMle("Downgrade to REED");
-            IgnoreError(BecomeChild(kAttachSameDowngrade));
+            LogNote("Downgrade to REED");
+            Attach(kDowngradeToReed);
         }
 
         break;
@@ -1859,7 +1886,7 @@
         if (child.IsCslSynchronized() &&
             TimerMilli::GetNow() - child.GetCslLastHeard() >= Time::SecToMsec(child.GetCslTimeout()))
         {
-            otLogInfoMle("Child CSL synchronization expired");
+            LogInfo("Child CSL synchronization expired");
             child.SetCslSynchronized(false);
             Get<CslTxScheduler>().Update();
         }
@@ -1867,7 +1894,7 @@
 
         if (TimerMilli::GetNow() - child.GetLastHeard() >= timeout)
         {
-            otLogInfoMle("Child timeout expired");
+            LogInfo("Child timeout expired");
             RemoveNeighbor(child);
         }
         else if (IsRouterOrLeader() && child.IsStateRestored())
@@ -1895,7 +1922,7 @@
 
             if (age >= Time::SecToMsec(kMaxNeighborAge))
             {
-                otLogInfoMle("Router timeout expired");
+                LogInfo("Router timeout expired");
                 RemoveNeighbor(router);
                 continue;
             }
@@ -1906,7 +1933,7 @@
             {
                 if (age < Time::SecToMsec(kMaxNeighborAge) + kMaxTransmissionCount * kUnicastRetransmissionDelay)
                 {
-                    otLogInfoMle("Router timeout expired");
+                    LogInfo("Router timeout expired");
                     IgnoreError(SendLinkRequest(&router));
                 }
                 else
@@ -1922,7 +1949,7 @@
         {
             if (age >= kMaxLinkRequestTimeout)
             {
-                otLogInfoMle("Link Request timeout expired");
+                LogInfo("Link Request timeout expired");
                 RemoveNeighbor(router);
                 continue;
             }
@@ -1933,7 +1960,7 @@
             if (mRouterTable.GetRouter(router.GetNextHop()) == nullptr &&
                 mRouterTable.GetLinkCost(router) >= kMaxRouteCost && age >= Time::SecToMsec(kMaxLeaderToRouterTimeout))
             {
-                otLogInfoMle("Router ID timeout expired (no route)");
+                LogInfo("Router ID timeout expired (no route)");
                 IgnoreError(mRouterTable.Release(router.GetRouterId()));
             }
         }
@@ -1961,10 +1988,9 @@
     Message *    message;
     uint16_t     delay;
 
-    VerifyOrExit((message = NewMleMessage()) != nullptr, error = kErrorNoBufs);
+    VerifyOrExit((message = NewMleMessage(kCommandParentResponse)) != nullptr, error = kErrorNoBufs);
     message->SetDirectTransmission();
 
-    SuccessOrExit(error = AppendHeader(*message, kCommandParentResponse));
     SuccessOrExit(error = AppendSourceAddress(*message));
     SuccessOrExit(error = AppendLeaderData(*message));
     SuccessOrExit(error = AppendLinkFrameCounter(*message));
@@ -2112,8 +2138,8 @@
         {
             if (Get<NetworkData::Leader>().GetContext(entry.GetContextId(), context) != kErrorNone)
             {
-                otLogWarnMle("Failed to get context %d for compressed address from child 0x%04x", entry.GetContextId(),
-                             aChild.GetRloc16());
+                LogWarn("Failed to get context %d for compressed address from child 0x%04x", entry.GetContextId(),
+                        aChild.GetRloc16());
                 continue;
             }
 
@@ -2162,8 +2188,8 @@
             }
 #endif
 
-            otLogInfoMle("Child 0x%04x IPv6 address[%d]=%s", aChild.GetRloc16(), storedCount,
-                         address.ToString().AsCString());
+            LogInfo("Child 0x%04x IPv6 address[%d]=%s", aChild.GetRloc16(), storedCount,
+                    address.ToString().AsCString());
         }
         else
         {
@@ -2175,8 +2201,8 @@
             }
 #endif
 
-            otLogWarnMle("Error %s adding IPv6 address %s to child 0x%04x", ErrorToString(error),
-                         address.ToString().AsCString(), aChild.GetRloc16());
+            LogWarn("Error %s adding IPv6 address %s to child 0x%04x", ErrorToString(error),
+                    address.ToString().AsCString(), aChild.GetRloc16());
         }
 
         if (address.IsMulticast())
@@ -2221,12 +2247,12 @@
 
     if (registeredCount == 0)
     {
-        otLogInfoMle("Child 0x%04x has no registered IPv6 address", aChild.GetRloc16());
+        LogInfo("Child 0x%04x has no registered IPv6 address", aChild.GetRloc16());
     }
     else
     {
-        otLogInfoMle("Child 0x%04x has %d registered IPv6 address%s, %d address%s stored", aChild.GetRloc16(),
-                     registeredCount, (registeredCount == 1) ? "" : "es", storedCount, (storedCount == 1) ? "" : "es");
+        LogInfo("Child 0x%04x has %d registered IPv6 address%s, %d address%s stored", aChild.GetRloc16(),
+                registeredCount, (registeredCount == 1) ? "" : "es", storedCount, (storedCount == 1) ? "" : "es");
     }
 
     error = kErrorNone;
@@ -2235,9 +2261,7 @@
     return error;
 }
 
-void MleRouter::HandleChildIdRequest(const Message &         aMessage,
-                                     const Ip6::MessageInfo &aMessageInfo,
-                                     uint32_t                aKeySequence)
+void MleRouter::HandleChildIdRequest(RxInfo &aRxInfo)
 {
     Error              error = kErrorNone;
     Mac::ExtAddress    extAddr;
@@ -2257,7 +2281,7 @@
     uint8_t            numTlvs;
     uint16_t           addressRegistrationOffset = 0;
 
-    Log(kMessageReceive, kTypeChildIdRequest, aMessageInfo.GetPeerAddr());
+    Log(kMessageReceive, kTypeChildIdRequest, aRxInfo.mMessageInfo.GetPeerAddr());
 
     VerifyOrExit(IsRouterEligible(), error = kErrorInvalidState);
 
@@ -2265,17 +2289,17 @@
     VerifyOrExit(IsAttached(), error = kErrorInvalidState);
 
     // Find Child
-    aMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(extAddr);
+    aRxInfo.mMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(extAddr);
 
     child = mChildTable.FindChild(extAddr, Child::kInStateAnyExceptInvalid);
     VerifyOrExit(child != nullptr, error = kErrorAlready);
 
     // Version
-    SuccessOrExit(error = Tlv::Find<VersionTlv>(aMessage, version));
+    SuccessOrExit(error = Tlv::Find<VersionTlv>(aRxInfo.mMessage, version));
     VerifyOrExit(version >= OT_THREAD_VERSION_1_1, error = kErrorParse);
 
     // Response
-    SuccessOrExit(error = ReadResponse(aMessage, response));
+    SuccessOrExit(error = ReadResponse(aRxInfo.mMessage, response));
     VerifyOrExit(response.Matches(child->GetChallenge(), child->GetChallengeSize()), error = kErrorSecurity);
 
     // Remove existing MLE messages
@@ -2285,26 +2309,26 @@
     Get<MeshForwarder>().RemoveMessages(*child, Message::kSubTypeMleDataResponse);
 
     // Link-Layer and MLE Frame Counters
-    SuccessOrExit(error = ReadFrameCounters(aMessage, linkFrameCounter, mleFrameCounter));
+    SuccessOrExit(error = ReadFrameCounters(aRxInfo.mMessage, linkFrameCounter, mleFrameCounter));
 
     // Mode
-    SuccessOrExit(error = Tlv::Find<ModeTlv>(aMessage, modeBitmask));
+    SuccessOrExit(error = Tlv::Find<ModeTlv>(aRxInfo.mMessage, modeBitmask));
     mode.Set(modeBitmask);
 
     // Timeout
-    SuccessOrExit(error = Tlv::Find<TimeoutTlv>(aMessage, timeout));
+    SuccessOrExit(error = Tlv::Find<TimeoutTlv>(aRxInfo.mMessage, timeout));
 
     // TLV Request
-    SuccessOrExit(error = FindTlvRequest(aMessage, requestedTlvs));
+    SuccessOrExit(error = FindTlvRequest(aRxInfo.mMessage, requestedTlvs));
     VerifyOrExit(requestedTlvs.mNumTlvs <= Child::kMaxRequestTlvs, error = kErrorParse);
 
     // Active Timestamp
     needsActiveDatasetTlv = true;
-    switch (Tlv::Find<ActiveTimestampTlv>(aMessage, timestamp))
+    switch (Tlv::Find<ActiveTimestampTlv>(aRxInfo.mMessage, timestamp))
     {
     case kErrorNone:
         needsActiveDatasetTlv =
-            (MeshCoP::Timestamp::Compare(&timestamp, Get<MeshCoP::ActiveDataset>().GetTimestamp()) != 0);
+            (MeshCoP::Timestamp::Compare(&timestamp, Get<MeshCoP::ActiveDatasetManager>().GetTimestamp()) != 0);
         break;
     case kErrorNotFound:
         break;
@@ -2314,11 +2338,11 @@
 
     // Pending Timestamp
     needsPendingDatasetTlv = true;
-    switch (Tlv::Find<PendingTimestampTlv>(aMessage, timestamp))
+    switch (Tlv::Find<PendingTimestampTlv>(aRxInfo.mMessage, timestamp))
     {
     case kErrorNone:
         needsPendingDatasetTlv =
-            (MeshCoP::Timestamp::Compare(&timestamp, Get<MeshCoP::PendingDataset>().GetTimestamp()) != 0);
+            (MeshCoP::Timestamp::Compare(&timestamp, Get<MeshCoP::PendingDatasetManager>().GetTimestamp()) != 0);
         break;
     case kErrorNotFound:
         break;
@@ -2328,8 +2352,9 @@
 
     if (!mode.IsFullThreadDevice())
     {
-        SuccessOrExit(error = Tlv::FindTlvOffset(aMessage, Tlv::kAddressRegistration, addressRegistrationOffset));
-        SuccessOrExit(error = UpdateChildAddresses(aMessage, addressRegistrationOffset, *child));
+        SuccessOrExit(error =
+                          Tlv::FindTlvOffset(aRxInfo.mMessage, Tlv::kAddressRegistration, addressRegistrationOffset));
+        SuccessOrExit(error = UpdateChildAddresses(aRxInfo.mMessage, addressRegistrationOffset, *child));
     }
 
     // Remove from router table
@@ -2353,10 +2378,10 @@
     child->GetLinkFrameCounters().SetAll(linkFrameCounter);
     child->SetLinkAckFrameCounter(linkFrameCounter);
     child->SetMleFrameCounter(mleFrameCounter);
-    child->SetKeySequence(aKeySequence);
+    child->SetKeySequence(aRxInfo.mKeySequence);
     child->SetDeviceMode(mode);
     child->SetVersion(static_cast<uint8_t>(version));
-    child->GetLinkInfo().AddRss(aMessageInfo.GetThreadLinkInfo()->GetRss());
+    child->GetLinkInfo().AddRss(aRxInfo.mMessageInfo.GetThreadLinkInfo()->GetRss());
     child->SetTimeout(timeout);
 #if OPENTHREAD_CONFIG_MULTI_RADIO
     child->ClearLastRxFragmentTag();
@@ -2402,7 +2427,7 @@
     LogProcessError(kTypeChildIdRequest, error);
 }
 
-void MleRouter::HandleChildUpdateRequest(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
+void MleRouter::HandleChildUpdateRequest(RxInfo &aRxInfo)
 {
     static const uint8_t kMaxResponseTlvs = 10;
 
@@ -2421,14 +2446,14 @@
     uint16_t        addressRegistrationOffset = 0;
     bool            childDidChange            = false;
 
-    Log(kMessageReceive, kTypeChildUpdateRequestOfChild, aMessageInfo.GetPeerAddr());
+    Log(kMessageReceive, kTypeChildUpdateRequestOfChild, aRxInfo.mMessageInfo.GetPeerAddr());
 
     // Mode
-    SuccessOrExit(error = Tlv::Find<ModeTlv>(aMessage, modeBitmask));
+    SuccessOrExit(error = Tlv::Find<ModeTlv>(aRxInfo.mMessage, modeBitmask));
     mode.Set(modeBitmask);
 
     // Challenge
-    switch (ReadChallenge(aMessage, challenge))
+    switch (ReadChallenge(aRxInfo.mMessage, challenge))
     {
     case kErrorNone:
         tlvs[tlvslength++] = Tlv::kResponse;
@@ -2442,7 +2467,7 @@
 
     tlvs[tlvslength++] = Tlv::kSourceAddress;
 
-    aMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(extAddr);
+    aRxInfo.mMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(extAddr);
     child = mChildTable.FindChild(extAddr, Child::kInStateAnyExceptInvalid);
 
     if (child == nullptr)
@@ -2452,7 +2477,7 @@
         if (mode.IsRxOnWhenIdle())
         {
             tlvs[tlvslength++] = Tlv::kStatus;
-            SendChildUpdateResponse(nullptr, aMessageInfo, tlvs, tlvslength, challenge);
+            SendChildUpdateResponse(nullptr, aRxInfo.mMessageInfo, tlvs, tlvslength, challenge);
         }
 
         ExitNow();
@@ -2481,14 +2506,14 @@
     }
 
     // IPv6 Address TLV
-    if (Tlv::FindTlvOffset(aMessage, Tlv::kAddressRegistration, addressRegistrationOffset) == kErrorNone)
+    if (Tlv::FindTlvOffset(aRxInfo.mMessage, Tlv::kAddressRegistration, addressRegistrationOffset) == kErrorNone)
     {
-        SuccessOrExit(error = UpdateChildAddresses(aMessage, addressRegistrationOffset, *child));
+        SuccessOrExit(error = UpdateChildAddresses(aRxInfo.mMessage, addressRegistrationOffset, *child));
         tlvs[tlvslength++] = Tlv::kAddressRegistration;
     }
 
     // Leader Data
-    switch (ReadLeaderData(aMessage, leaderData))
+    switch (ReadLeaderData(aRxInfo.mMessage, leaderData))
     {
     case kErrorNone:
         child->SetNetworkDataVersion(leaderData.GetDataVersion(child->GetNetworkDataType()));
@@ -2500,7 +2525,7 @@
     }
 
     // Timeout
-    switch (Tlv::Find<TimeoutTlv>(aMessage, timeout))
+    switch (Tlv::Find<TimeoutTlv>(aRxInfo.mMessage, timeout))
     {
     case kErrorNone:
         if (child->GetTimeout() != timeout)
@@ -2520,7 +2545,7 @@
     }
 
     // TLV Request
-    switch (FindTlvRequest(aMessage, requestedTlvs))
+    switch (FindTlvRequest(aRxInfo.mMessage, requestedTlvs))
     {
     case kErrorNone:
         VerifyOrExit(requestedTlvs.mNumTlvs <= (kMaxResponseTlvs - tlvslength), error = kErrorParse);
@@ -2545,14 +2570,14 @@
         CslChannelTlv cslChannel;
         uint32_t      cslTimeout;
 
-        if (Tlv::Find<CslTimeoutTlv>(aMessage, cslTimeout) == kErrorNone)
+        if (Tlv::Find<CslTimeoutTlv>(aRxInfo.mMessage, cslTimeout) == kErrorNone)
         {
             child->SetCslTimeout(cslTimeout);
             // MUST include CSL accuracy TLV when request includes CSL timeout
             tlvs[tlvslength++] = Tlv::kCslClockAccuracy;
         }
 
-        if (Tlv::FindTlv(aMessage, cslChannel) == kErrorNone)
+        if (Tlv::FindTlv(aRxInfo.mMessage, cslChannel) == kErrorNone)
         {
             child->SetCslChannel(static_cast<uint8_t>(cslChannel.GetChannel()));
         }
@@ -2568,8 +2593,8 @@
 
     if (oldMode != child->GetDeviceMode())
     {
-        otLogNoteMle("Child 0x%04x mode change 0x%02x -> 0x%02x [%s]", child->GetRloc16(), oldMode.Get(),
-                     child->GetDeviceMode().Get(), child->GetDeviceMode().ToString().AsCString());
+        LogNote("Child 0x%04x mode change 0x%02x -> 0x%02x [%s]", child->GetRloc16(), oldMode.Get(),
+                child->GetDeviceMode().Get(), child->GetDeviceMode().ToString().AsCString());
 
         childDidChange = true;
 
@@ -2605,16 +2630,13 @@
     }
 #endif
 
-    SendChildUpdateResponse(child, aMessageInfo, tlvs, tlvslength, challenge);
+    SendChildUpdateResponse(child, aRxInfo.mMessageInfo, tlvs, tlvslength, challenge);
 
 exit:
     LogProcessError(kTypeChildUpdateRequestOfChild, error);
 }
 
-void MleRouter::HandleChildUpdateResponse(const Message &         aMessage,
-                                          const Ip6::MessageInfo &aMessageInfo,
-                                          uint32_t                aKeySequence,
-                                          Neighbor *              aNeighbor)
+void MleRouter::HandleChildUpdateResponse(RxInfo &aRxInfo)
 {
     Error      error = kErrorNone;
     uint16_t   sourceAddress;
@@ -2627,16 +2649,16 @@
     Child *    child;
     uint16_t   addressRegistrationOffset = 0;
 
-    if ((aNeighbor == nullptr) || IsActiveRouter(aNeighbor->GetRloc16()))
+    if ((aRxInfo.mNeighbor == nullptr) || IsActiveRouter(aRxInfo.mNeighbor->GetRloc16()))
     {
-        Log(kMessageReceive, kTypeChildUpdateResponseOfUnknownChild, aMessageInfo.GetPeerAddr());
+        Log(kMessageReceive, kTypeChildUpdateResponseOfUnknownChild, aRxInfo.mMessageInfo.GetPeerAddr());
         ExitNow(error = kErrorNotFound);
     }
 
-    child = static_cast<Child *>(aNeighbor);
+    child = static_cast<Child *>(aRxInfo.mNeighbor);
 
     // Response
-    switch (ReadResponse(aMessage, response))
+    switch (ReadResponse(aRxInfo.mMessage, response))
     {
     case kErrorNone:
         VerifyOrExit(response.Matches(child->GetChallenge(), child->GetChallengeSize()), error = kErrorSecurity);
@@ -2648,10 +2670,10 @@
         ExitNow(error = kErrorNone);
     }
 
-    Log(kMessageReceive, kTypeChildUpdateResponseOfChild, aMessageInfo.GetPeerAddr(), child->GetRloc16());
+    Log(kMessageReceive, kTypeChildUpdateResponseOfChild, aRxInfo.mMessageInfo.GetPeerAddr(), child->GetRloc16());
 
     // Source Address
-    switch (Tlv::Find<SourceAddressTlv>(aMessage, sourceAddress))
+    switch (Tlv::Find<SourceAddressTlv>(aRxInfo.mMessage, sourceAddress))
     {
     case kErrorNone:
         if (child->GetRloc16() != sourceAddress)
@@ -2670,7 +2692,7 @@
     }
 
     // Status
-    switch (Tlv::Find<ThreadStatusTlv>(aMessage, status))
+    switch (Tlv::Find<ThreadStatusTlv>(aRxInfo.mMessage, status))
     {
     case kErrorNone:
         VerifyOrExit(status != StatusTlv::kError, RemoveNeighbor(*child));
@@ -2683,7 +2705,7 @@
 
     // Link-Layer Frame Counter
 
-    switch (Tlv::Find<LinkFrameCounterTlv>(aMessage, linkFrameCounter))
+    switch (Tlv::Find<LinkFrameCounterTlv>(aRxInfo.mMessage, linkFrameCounter))
     {
     case kErrorNone:
         child->GetLinkFrameCounters().SetAll(linkFrameCounter);
@@ -2696,7 +2718,7 @@
     }
 
     // MLE Frame Counter
-    switch (Tlv::Find<MleFrameCounterTlv>(aMessage, mleFrameCounter))
+    switch (Tlv::Find<MleFrameCounterTlv>(aRxInfo.mMessage, mleFrameCounter))
     {
     case kErrorNone:
         child->SetMleFrameCounter(mleFrameCounter);
@@ -2708,7 +2730,7 @@
     }
 
     // Timeout
-    switch (Tlv::Find<TimeoutTlv>(aMessage, timeout))
+    switch (Tlv::Find<TimeoutTlv>(aRxInfo.mMessage, timeout))
     {
     case kErrorNone:
         child->SetTimeout(timeout);
@@ -2720,13 +2742,13 @@
     }
 
     // IPv6 Address
-    if (Tlv::FindTlvOffset(aMessage, Tlv::kAddressRegistration, addressRegistrationOffset) == kErrorNone)
+    if (Tlv::FindTlvOffset(aRxInfo.mMessage, Tlv::kAddressRegistration, addressRegistrationOffset) == kErrorNone)
     {
-        SuccessOrExit(error = UpdateChildAddresses(aMessage, addressRegistrationOffset, *child));
+        SuccessOrExit(error = UpdateChildAddresses(aRxInfo.mMessage, addressRegistrationOffset, *child));
     }
 
     // Leader Data
-    switch (ReadLeaderData(aMessage, leaderData))
+    switch (ReadLeaderData(aRxInfo.mMessage, leaderData))
     {
     case kErrorNone:
         child->SetNetworkDataVersion(leaderData.GetDataVersion(child->GetNetworkDataType()));
@@ -2739,16 +2761,14 @@
 
     SetChildStateToValid(*child);
     child->SetLastHeard(TimerMilli::GetNow());
-    child->SetKeySequence(aKeySequence);
-    child->GetLinkInfo().AddRss(aMessageInfo.GetThreadLinkInfo()->GetRss());
+    child->SetKeySequence(aRxInfo.mKeySequence);
+    child->GetLinkInfo().AddRss(aRxInfo.mMessageInfo.GetThreadLinkInfo()->GetRss());
 
 exit:
     LogProcessError(kTypeChildUpdateResponseOfChild, error);
 }
 
-void MleRouter::HandleDataRequest(const Message &         aMessage,
-                                  const Ip6::MessageInfo &aMessageInfo,
-                                  const Neighbor *        aNeighbor)
+void MleRouter::HandleDataRequest(RxInfo &aRxInfo)
 {
     Error              error = kErrorNone;
     RequestedTlvs      requestedTlvs;
@@ -2756,12 +2776,12 @@
     uint8_t            tlvs[4];
     uint8_t            numTlvs;
 
-    Log(kMessageReceive, kTypeDataRequest, aMessageInfo.GetPeerAddr());
+    Log(kMessageReceive, kTypeDataRequest, aRxInfo.mMessageInfo.GetPeerAddr());
 
-    VerifyOrExit(aNeighbor && aNeighbor->IsStateValid(), error = kErrorSecurity);
+    VerifyOrExit(aRxInfo.mNeighbor && aRxInfo.mNeighbor->IsStateValid(), error = kErrorSecurity);
 
     // TLV Request
-    SuccessOrExit(error = FindTlvRequest(aMessage, requestedTlvs));
+    SuccessOrExit(error = FindTlvRequest(aRxInfo.mMessage, requestedTlvs));
     VerifyOrExit(requestedTlvs.mNumTlvs <= sizeof(tlvs), error = kErrorParse);
 
     memset(tlvs, Tlv::kInvalid, sizeof(tlvs));
@@ -2769,10 +2789,10 @@
     numTlvs = requestedTlvs.mNumTlvs;
 
     // Active Timestamp
-    switch (Tlv::Find<ActiveTimestampTlv>(aMessage, timestamp))
+    switch (Tlv::Find<ActiveTimestampTlv>(aRxInfo.mMessage, timestamp))
     {
     case kErrorNone:
-        if (MeshCoP::Timestamp::Compare(&timestamp, Get<MeshCoP::ActiveDataset>().GetTimestamp()) == 0)
+        if (MeshCoP::Timestamp::Compare(&timestamp, Get<MeshCoP::ActiveDatasetManager>().GetTimestamp()) == 0)
         {
             break;
         }
@@ -2788,10 +2808,10 @@
     }
 
     // Pending Timestamp
-    switch (Tlv::Find<PendingTimestampTlv>(aMessage, timestamp))
+    switch (Tlv::Find<PendingTimestampTlv>(aRxInfo.mMessage, timestamp))
     {
     case kErrorNone:
-        if (MeshCoP::Timestamp::Compare(&timestamp, Get<MeshCoP::PendingDataset>().GetTimestamp()) == 0)
+        if (MeshCoP::Timestamp::Compare(&timestamp, Get<MeshCoP::PendingDatasetManager>().GetTimestamp()) == 0)
         {
             break;
         }
@@ -2806,7 +2826,7 @@
         ExitNow(error = kErrorParse);
     }
 
-    SendDataResponse(aMessageInfo.GetPeerAddr(), tlvs, numTlvs, 0, &aMessage);
+    SendDataResponse(aRxInfo.mMessageInfo.GetPeerAddr(), tlvs, numTlvs, 0, &aRxInfo.mMessage);
 
 exit:
     LogProcessError(kTypeDataRequest, error);
@@ -2882,17 +2902,17 @@
 }
 #endif // OPENTHREAD_CONFIG_MLE_STEERING_DATA_SET_OOB_ENABLE
 
-void MleRouter::HandleDiscoveryRequest(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
+void MleRouter::HandleDiscoveryRequest(RxInfo &aRxInfo)
 {
     Error                        error = kErrorNone;
     Tlv                          tlv;
     MeshCoP::Tlv                 meshcopTlv;
     MeshCoP::DiscoveryRequestTlv discoveryRequest;
-    Mac::ExtendedPanId           extPanId;
+    MeshCoP::ExtendedPanId       extPanId;
     uint16_t                     offset;
     uint16_t                     end;
 
-    Log(kMessageReceive, kTypeDiscoveryRequest, aMessageInfo.GetPeerAddr());
+    Log(kMessageReceive, kTypeDiscoveryRequest, aRxInfo.mMessageInfo.GetPeerAddr());
 
     discoveryRequest.SetLength(0);
 
@@ -2900,27 +2920,27 @@
     VerifyOrExit(IsRouterEligible(), error = kErrorInvalidState);
 
     // find MLE Discovery TLV
-    VerifyOrExit(Tlv::FindTlvOffset(aMessage, Tlv::kDiscovery, offset) == kErrorNone, error = kErrorParse);
-    IgnoreError(aMessage.Read(offset, tlv));
+    VerifyOrExit(Tlv::FindTlvOffset(aRxInfo.mMessage, Tlv::kDiscovery, offset) == kErrorNone, error = kErrorParse);
+    IgnoreError(aRxInfo.mMessage.Read(offset, tlv));
 
     offset += sizeof(tlv);
     end = offset + sizeof(tlv) + tlv.GetLength();
 
     while (offset < end)
     {
-        IgnoreError(aMessage.Read(offset, meshcopTlv));
+        IgnoreError(aRxInfo.mMessage.Read(offset, meshcopTlv));
 
         switch (meshcopTlv.GetType())
         {
         case MeshCoP::Tlv::kDiscoveryRequest:
-            IgnoreError(aMessage.Read(offset, discoveryRequest));
+            IgnoreError(aRxInfo.mMessage.Read(offset, discoveryRequest));
             VerifyOrExit(discoveryRequest.IsValid(), error = kErrorParse);
 
             break;
 
         case MeshCoP::Tlv::kExtendedPanId:
-            SuccessOrExit(error = Tlv::Read<MeshCoP::ExtendedPanIdTlv>(aMessage, offset, extPanId));
-            VerifyOrExit(Get<Mac::Mac>().GetExtendedPanId() != extPanId, error = kErrorDrop);
+            SuccessOrExit(error = Tlv::Read<MeshCoP::ExtendedPanIdTlv>(aRxInfo.mMessage, offset, extPanId));
+            VerifyOrExit(Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId() != extPanId, error = kErrorDrop);
 
             break;
 
@@ -2937,7 +2957,7 @@
         {
             otThreadDiscoveryRequestInfo info;
 
-            aMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(AsCoreType(&info.mExtAddress));
+            aRxInfo.mMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(AsCoreType(&info.mExtAddress));
             info.mVersion  = discoveryRequest.GetVersion();
             info.mIsJoiner = discoveryRequest.IsJoiner();
 
@@ -2958,7 +2978,7 @@
         }
     }
 
-    error = SendDiscoveryResponse(aMessageInfo.GetPeerAddr(), aMessage);
+    error = SendDiscoveryResponse(aRxInfo.mMessageInfo.GetPeerAddr(), aRxInfo.mMessage);
 
 exit:
     LogProcessError(kTypeDiscoveryRequest, error);
@@ -2974,8 +2994,7 @@
     MeshCoP::NetworkNameTlv       networkName;
     uint16_t                      delay;
 
-    VerifyOrExit((message = NewMleMessage()) != nullptr, error = kErrorNoBufs);
-    message->SetSubType(Message::kSubTypeMleDiscoverResponse);
+    VerifyOrExit((message = NewMleMessage(kCommandDiscoveryResponse)) != nullptr, error = kErrorNoBufs);
     message->SetPanId(aDiscoverRequestMessage.GetPanId());
 #if OPENTHREAD_CONFIG_MULTI_RADIO
     // Send the MLE Discovery Response message on same radio link
@@ -2983,8 +3002,6 @@
     message->SetRadioType(aDiscoverRequestMessage.GetRadioType());
 #endif
 
-    SuccessOrExit(error = AppendHeader(*message, kCommandDiscoveryResponse));
-
     // Discovery TLV
     tlv.SetType(Tlv::kDiscovery);
     SuccessOrExit(error = message->Append(tlv));
@@ -3017,11 +3034,12 @@
     SuccessOrExit(error = discoveryResponse.AppendTo(*message));
 
     // Extended PAN ID TLV
-    SuccessOrExit(error = Tlv::Append<MeshCoP::ExtendedPanIdTlv>(*message, Get<Mac::Mac>().GetExtendedPanId()));
+    SuccessOrExit(
+        error = Tlv::Append<MeshCoP::ExtendedPanIdTlv>(*message, Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId()));
 
     // Network Name TLV
     networkName.Init();
-    networkName.SetNetworkName(Get<Mac::Mac>().GetNetworkName().GetAsData());
+    networkName.SetNetworkName(Get<MeshCoP::NetworkNameManager>().GetNetworkName().GetAsData());
     SuccessOrExit(error = networkName.AppendTo(*message));
 
 #if OPENTHREAD_CONFIG_MLE_STEERING_DATA_SET_OOB_ENABLE
@@ -3069,14 +3087,13 @@
     Ip6::Address destination;
     Message *    message;
 
-    VerifyOrExit((message = NewMleMessage()) != nullptr, error = kErrorNoBufs);
-    SuccessOrExit(error = AppendHeader(*message, kCommandChildIdResponse));
+    VerifyOrExit((message = NewMleMessage(kCommandChildIdResponse)) != nullptr, error = kErrorNoBufs);
     SuccessOrExit(error = AppendSourceAddress(*message));
     SuccessOrExit(error = AppendLeaderData(*message));
     SuccessOrExit(error = AppendActiveTimestamp(*message));
     SuccessOrExit(error = AppendPendingTimestamp(*message));
 
-    if (aChild.GetRloc16() == 0)
+    if ((aChild.GetRloc16() == 0) || !RouterIdMatch(aChild.GetRloc16(), GetRloc16()))
     {
         uint16_t rloc16;
 
@@ -3159,15 +3176,15 @@
     static const uint8_t tlvs[] = {Tlv::kTimeout, Tlv::kAddressRegistration};
     Error                error  = kErrorNone;
     Ip6::Address         destination;
-    Message *            message;
+    Message *            message = nullptr;
 
     if (!aChild.IsRxOnWhenIdle())
     {
         uint16_t childIndex = Get<ChildTable>().GetChildIndex(aChild);
 
-        for (message = Get<MeshForwarder>().GetSendQueue().GetHead(); message; message = message->GetNext())
+        for (const Message &msg : Get<MeshForwarder>().GetSendQueue())
         {
-            if (message->GetChildMask(childIndex) && message->GetSubType() == Message::kSubTypeMleChildUpdateRequest)
+            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.
@@ -3183,9 +3200,7 @@
         }
     }
 
-    VerifyOrExit((message = NewMleMessage()) != nullptr, error = kErrorNoBufs);
-    message->SetSubType(Message::kSubTypeMleChildUpdateRequest);
-    SuccessOrExit(error = AppendHeader(*message, kCommandChildUpdateRequest));
+    VerifyOrExit((message = NewMleMessage(kCommandChildUpdateRequest)) != nullptr, error = kErrorNoBufs);
     SuccessOrExit(error = AppendSourceAddress(*message));
     SuccessOrExit(error = AppendLeaderData(*message));
     SuccessOrExit(error = AppendNetworkData(*message, aChild.GetNetworkDataType()));
@@ -3224,8 +3239,7 @@
     Error    error = kErrorNone;
     Message *message;
 
-    VerifyOrExit((message = NewMleMessage()) != nullptr, error = kErrorNoBufs);
-    SuccessOrExit(error = AppendHeader(*message, kCommandChildUpdateResponse));
+    VerifyOrExit((message = NewMleMessage(kCommandChildUpdateResponse)) != nullptr, error = kErrorNoBufs);
 
     for (int i = 0; i < aTlvsLength; i++)
     {
@@ -3313,13 +3327,11 @@
 
     if (mRetrieveNewNetworkData)
     {
-        otLogInfoMle("Suppressing Data Response - waiting for new network data");
+        LogInfo("Suppressing Data Response - waiting for new network data");
         ExitNow();
     }
 
-    VerifyOrExit((message = NewMleMessage()) != nullptr, error = kErrorNoBufs);
-    message->SetSubType(Message::kSubTypeMleDataResponse);
-    SuccessOrExit(error = AppendHeader(*message, kCommandDataResponse));
+    VerifyOrExit((message = NewMleMessage(kCommandDataResponse)) != nullptr, error = kErrorNoBufs);
     SuccessOrExit(error = AppendSourceAddress(*message));
     SuccessOrExit(error = AppendLeaderData(*message));
     SuccessOrExit(error = AppendActiveTimestamp(*message));
@@ -3629,15 +3641,13 @@
 Error MleRouter::SendAddressSolicit(ThreadStatusTlv::Status aStatus)
 {
     Error            error = kErrorNone;
-    Ip6::MessageInfo messageInfo;
+    Tmf::MessageInfo messageInfo(GetInstance());
     Coap::Message *  message = nullptr;
 
     VerifyOrExit(!mAddressSolicitPending);
 
-    VerifyOrExit((message = Get<Tmf::Agent>().NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
-
-    SuccessOrExit(error = message->InitAsConfirmablePost(UriPath::kAddressSolicit));
-    SuccessOrExit(error = message->SetPayloadMarker());
+    message = Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(UriPath::kAddressSolicit);
+    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
     SuccessOrExit(error = Tlv::Append<ThreadExtMacAddressTlv>(*message, Get<Mac::Mac>().GetExtAddress()));
 
@@ -3652,12 +3662,9 @@
     SuccessOrExit(error = AppendXtalAccuracy(*message));
 #endif
 
-    SuccessOrExit(error = GetLeaderAddress(messageInfo.GetPeerAddr()));
-    messageInfo.SetSockAddr(GetMeshLocal16());
-    messageInfo.SetPeerPort(Tmf::kUdpPort);
+    SuccessOrExit(error = messageInfo.SetSockAddrToRlocPeerAddrToLeaderRloc());
 
-    SuccessOrExit(
-        error = Get<Tmf::Agent>().SendMessage(*message, messageInfo, &MleRouter::HandleAddressSolicitResponse, this));
+    SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo, &HandleAddressSolicitResponse, this));
     mAddressSolicitPending = true;
 
     Log(kMessageSend, kTypeAddressSolicit, messageInfo.GetPeerAddr());
@@ -3670,21 +3677,17 @@
 void MleRouter::SendAddressRelease(void)
 {
     Error            error = kErrorNone;
-    Ip6::MessageInfo messageInfo;
+    Tmf::MessageInfo messageInfo(GetInstance());
     Coap::Message *  message;
 
-    VerifyOrExit((message = Get<Tmf::Agent>().NewMessage()) != nullptr, error = kErrorNoBufs);
-
-    SuccessOrExit(error = message->InitAsConfirmablePost(UriPath::kAddressRelease));
-    SuccessOrExit(error = message->SetPayloadMarker());
+    message = Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(UriPath::kAddressRelease);
+    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
     SuccessOrExit(error = Tlv::Append<ThreadRloc16Tlv>(*message, Rloc16FromRouterId(mRouterId)));
-
     SuccessOrExit(error = Tlv::Append<ThreadExtMacAddressTlv>(*message, Get<Mac::Mac>().GetExtAddress()));
 
-    messageInfo.SetSockAddr(GetMeshLocal16());
-    SuccessOrExit(error = GetLeaderAddress(messageInfo.GetPeerAddr()));
-    messageInfo.SetPeerPort(Tmf::kUdpPort);
+    SuccessOrExit(error = messageInfo.SetSockAddrToRlocPeerAddrToLeaderRloc());
+
     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo));
 
     Log(kMessageSend, kTypeAddressRelease, messageInfo.GetPeerAddr());
@@ -3707,8 +3710,6 @@
                                              const Ip6::MessageInfo *aMessageInfo,
                                              Error                   aResult)
 {
-    OT_UNUSED_VARIABLE(aMessageInfo);
-
     uint8_t             status;
     uint16_t            rloc16;
     ThreadRouterMaskTlv routerMaskTlv;
@@ -3718,7 +3719,7 @@
 
     mAddressSolicitPending = false;
 
-    VerifyOrExit(aResult == kErrorNone && aMessage != nullptr && aMessage != nullptr);
+    VerifyOrExit(aResult == kErrorNone && aMessage != nullptr && aMessageInfo != nullptr);
 
     VerifyOrExit(aMessage->GetCode() == Coap::kCodeChanged);
 
@@ -3781,18 +3782,15 @@
         leader->SetNextHop(RouterIdFromRloc16(mParent.GetRloc16()));
     }
 
-    // send link request
     IgnoreError(SendLinkRequest(nullptr));
 
-    // send child id responses
     for (Child &child : Get<ChildTable>().Iterate(Child::kInStateChildIdRequest))
     {
         IgnoreError(SendChildIdResponse(child));
     }
 
 exit:
-
-    // send announce after received address solicit reply if needed
+    // Send announce after received address solicit reply if needed
     InformPreviousChannel();
 }
 
@@ -3812,14 +3810,12 @@
 
 void MleRouter::HandleAddressSolicit(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
 {
-    Error           error = kErrorNone;
-    Mac::ExtAddress extAddress;
-    uint16_t        rloc16;
-    uint8_t         status;
-    Router *        router = nullptr;
-#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
-    uint16_t xtalAccuracy;
-#endif
+    Error                   error          = kErrorNone;
+    ThreadStatusTlv::Status responseStatus = ThreadStatusTlv::kNoAddressAvailable;
+    Router *                router         = nullptr;
+    Mac::ExtAddress         extAddress;
+    uint16_t                rloc16;
+    uint8_t                 status;
 
     VerifyOrExit(aMessage.IsConfirmablePostRequest(), error = kErrorParse);
 
@@ -3828,21 +3824,35 @@
     SuccessOrExit(error = Tlv::Find<ThreadExtMacAddressTlv>(aMessage, extAddress));
     SuccessOrExit(error = Tlv::Find<ThreadStatusTlv>(aMessage, status));
 
+    switch (Tlv::Find<ThreadRloc16Tlv>(aMessage, rloc16))
+    {
+    case kErrorNone:
+        break;
+    case kErrorNotFound:
+        rloc16 = Mac::kShortAddrInvalid;
+        break;
+    default:
+        ExitNow(error = kErrorParse);
+    }
+
 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
-    // In a time sync enabled network, all routers' xtal accuracy must be less than the threshold.
-    SuccessOrExit(Tlv::Find<XtalAccuracyTlv>(aMessage, xtalAccuracy));
-    VerifyOrExit(xtalAccuracy <= Get<TimeSync>().GetXtalThreshold());
+    {
+        uint16_t xtalAccuracy;
+
+        SuccessOrExit(Tlv::Find<XtalAccuracyTlv>(aMessage, xtalAccuracy));
+        VerifyOrExit(xtalAccuracy <= Get<TimeSync>().GetXtalThreshold());
+    }
 #endif
 
-    // see if allocation already exists
+    // Check if allocation already exists
     router = mRouterTable.GetRouter(extAddress);
 
     if (router != nullptr)
     {
+        responseStatus = ThreadStatusTlv::kSuccess;
         ExitNow();
     }
 
-    // check the request reason
     switch (status)
     {
     case ThreadStatusTlv::kTooFewRouters:
@@ -3853,82 +3863,79 @@
     case ThreadStatusTlv::kParentPartitionChange:
         break;
 
+    case ThreadStatusTlv::kBorderRouterRequst:
+        if ((mRouterTable.GetActiveRouterCount() >= mRouterUpgradeThreshold) &&
+            (Get<NetworkData::Leader>().CountBorderRouters(NetworkData::kRouterRoleOnly) >=
+             kRouterUpgradeBorderRouterRequestThreshold))
+        {
+            LogInfo("Rejecting BR %s router role req - have %d BR routers", extAddress.ToString().AsCString(),
+                    kRouterUpgradeBorderRouterRequestThreshold);
+            ExitNow();
+        }
+        break;
+
     default:
-        ExitNow(error = kErrorParse);
-        OT_UNREACHABLE_CODE(break);
+        responseStatus = ThreadStatusTlv::kUnrecognizedStatus;
+        ExitNow();
     }
 
-    switch (Tlv::Find<ThreadRloc16Tlv>(aMessage, rloc16))
+    if (rloc16 != Mac::kShortAddrInvalid)
     {
-    case kErrorNone:
         router = mRouterTable.Allocate(RouterIdFromRloc16(rloc16));
-        break;
-    case kErrorNotFound:
-        break;
-    default:
-        ExitNow(error = kErrorParse);
+
+        if (router != nullptr)
+        {
+            LogInfo("Router id %d requested and provided!", RouterIdFromRloc16(rloc16));
+        }
     }
 
-    // allocate new router id
     if (router == nullptr)
     {
         router = mRouterTable.Allocate();
-    }
-    else
-    {
-        otLogInfoMle("router id requested and provided!");
+        VerifyOrExit(router != nullptr);
     }
 
-    if (router != nullptr)
-    {
-        router->SetExtAddress(extAddress);
-    }
-    else
-    {
-        otLogInfoMle("router address unavailable!");
-    }
+    router->SetExtAddress(extAddress);
+    responseStatus = ThreadStatusTlv::kSuccess;
 
 exit:
-
     if (error == kErrorNone)
     {
-        SendAddressSolicitResponse(aMessage, router, aMessageInfo);
+        SendAddressSolicitResponse(aMessage, responseStatus, router, aMessageInfo);
     }
 }
 
 void MleRouter::SendAddressSolicitResponse(const Coap::Message &   aRequest,
+                                           ThreadStatusTlv::Status aResponseStatus,
                                            const Router *          aRouter,
                                            const Ip6::MessageInfo &aMessageInfo)
 {
-    Error               error = kErrorNone;
-    ThreadRouterMaskTlv routerMaskTlv;
-    Coap::Message *     message;
+    Coap::Message *message = Get<Tmf::Agent>().NewPriorityResponseMessage(aRequest);
 
-    VerifyOrExit((message = Get<Tmf::Agent>().NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
+    VerifyOrExit(message != nullptr);
 
-    SuccessOrExit(error = message->SetDefaultResponseHeader(aRequest));
-    SuccessOrExit(error = message->SetPayloadMarker());
-
-    SuccessOrExit(error = Tlv::Append<ThreadStatusTlv>(
-                      *message, aRouter == nullptr ? ThreadStatusTlv::kNoAddressAvailable : ThreadStatusTlv::kSuccess));
+    SuccessOrExit(Tlv::Append<ThreadStatusTlv>(*message, aResponseStatus));
 
     if (aRouter != nullptr)
     {
-        SuccessOrExit(error = Tlv::Append<ThreadRloc16Tlv>(*message, aRouter->GetRloc16()));
+        ThreadRouterMaskTlv routerMaskTlv;
+
+        SuccessOrExit(Tlv::Append<ThreadRloc16Tlv>(*message, aRouter->GetRloc16()));
 
         routerMaskTlv.Init();
         routerMaskTlv.SetIdSequence(mRouterTable.GetRouterIdSequence());
         routerMaskTlv.SetAssignedRouterIdMask(mRouterTable.GetRouterIdSet());
 
-        SuccessOrExit(error = routerMaskTlv.AppendTo(*message));
+        SuccessOrExit(routerMaskTlv.AppendTo(*message));
     }
 
-    SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, aMessageInfo));
+    SuccessOrExit(Get<Tmf::Agent>().SendMessage(*message, aMessageInfo));
+    message = nullptr;
 
     Log(kMessageSend, kTypeAddressReply, aMessageInfo.GetPeerAddr());
 
 exit:
-    FreeMessageOnError(message, error);
+    FreeMessage(message);
 }
 
 void MleRouter::HandleAddressRelease(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
@@ -4010,15 +4017,18 @@
     case kRoleChild:
         switch (mParent.GetLinkInfo().GetLinkQuality())
         {
-        case 1:
+        case kLinkQuality0:
+            break;
+
+        case kLinkQuality1:
             aTlv.SetLinkQuality1(aTlv.GetLinkQuality1() + 1);
             break;
 
-        case 2:
+        case kLinkQuality2:
             aTlv.SetLinkQuality2(aTlv.GetLinkQuality2() + 1);
             break;
 
-        case 3:
+        case kLinkQuality3:
             aTlv.SetLinkQuality3(aTlv.GetLinkQuality3() + 1);
             break;
         }
@@ -4048,7 +4058,7 @@
 
     for (Router &router : Get<RouterTable>().Iterate())
     {
-        uint8_t linkQuality;
+        LinkQuality linkQuality;
 
         if (router.GetRloc16() == GetRloc16())
         {
@@ -4071,15 +4081,18 @@
 
         switch (linkQuality)
         {
-        case 1:
+        case kLinkQuality0:
+            break;
+
+        case kLinkQuality1:
             aTlv.SetLinkQuality1(aTlv.GetLinkQuality1() + 1);
             break;
 
-        case 2:
+        case kLinkQuality2:
             aTlv.SetLinkQuality2(aTlv.GetLinkQuality2() + 1);
             break;
 
-        case 3:
+        case kLinkQuality3:
             aTlv.SetLinkQuality3(aTlv.GetLinkQuality3() + 1);
             break;
         }
@@ -4201,8 +4214,8 @@
 
         if (router.GetRloc16() == GetRloc16())
         {
-            aTlv.SetLinkQualityIn(routerCount, 0);
-            aTlv.SetLinkQualityOut(routerCount, 0);
+            aTlv.SetLinkQualityIn(routerCount, kLinkQuality0);
+            aTlv.SetLinkQualityOut(routerCount, kLinkQuality0);
             aTlv.SetRouteCost(routerCount, 1);
         }
         else
@@ -4256,12 +4269,12 @@
 
 Error MleRouter::AppendActiveDataset(Message &aMessage)
 {
-    return Get<MeshCoP::ActiveDataset>().AppendMleDatasetTlv(aMessage);
+    return Get<MeshCoP::ActiveDatasetManager>().AppendMleDatasetTlv(aMessage);
 }
 
 Error MleRouter::AppendPendingDataset(Message &aMessage)
 {
-    return Get<MeshCoP::PendingDataset>().AppendMleDatasetTlv(aMessage);
+    return Get<MeshCoP::PendingDatasetManager>().AppendMleDatasetTlv(aMessage);
 }
 
 bool MleRouter::HasMinDowngradeNeighborRouters(void)
@@ -4294,66 +4307,56 @@
 
 bool MleRouter::HasOneNeighborWithComparableConnectivity(const RouteTlv &aRoute, uint8_t aRouterId)
 {
-    bool rval = true;
+    uint8_t routerCount = 0;
+    bool    rval        = true;
 
     // process local neighbor routers
     for (Router &router : Get<RouterTable>().Iterate())
     {
-        uint8_t localLinkQuality = 0;
-        uint8_t peerLinkQuality  = 0;
-        uint8_t routerCount      = 0;
+        uint8_t localLinkQuality;
+        uint8_t peerLinkQuality;
 
-        if (router.GetRouterId() == mRouterId)
+        if (!router.IsStateValid() || router.GetRouterId() == mRouterId || router.GetRouterId() == aRouterId)
         {
             routerCount++;
             continue;
         }
 
-        // check if neighbor is valid
-        if (router.IsStateValid())
+        localLinkQuality = router.GetLinkInfo().GetLinkQuality();
+
+        if (localLinkQuality > router.GetLinkQualityOut())
         {
-            // if neighbor is just peer
-            if (router.GetRouterId() == aRouterId)
-            {
-                routerCount++;
-                continue;
-            }
-
-            localLinkQuality = router.GetLinkInfo().GetLinkQuality();
-
-            if (localLinkQuality > router.GetLinkQualityOut())
-            {
-                localLinkQuality = router.GetLinkQualityOut();
-            }
-
-            if (localLinkQuality >= 2)
-            {
-                // check if this neighbor router is in peer Route64 TLV
-                if (!aRoute.IsRouterIdSet(router.GetRouterId()))
-                {
-                    ExitNow(rval = false);
-                }
-
-                // get the peer's two-way link quality to this router
-                peerLinkQuality = aRoute.GetLinkQualityIn(routerCount);
-
-                if (peerLinkQuality > aRoute.GetLinkQualityOut(routerCount))
-                {
-                    peerLinkQuality = aRoute.GetLinkQualityOut(routerCount);
-                }
-
-                // compare local link quality to this router with peer's
-                if (peerLinkQuality >= localLinkQuality)
-                {
-                    routerCount++;
-                    continue;
-                }
-
-                ExitNow(rval = false);
-            }
+            localLinkQuality = router.GetLinkQualityOut();
         }
 
-        routerCount++;
+        if (localLinkQuality < 2)
+        {
+            routerCount++;
+            continue;
+        }
+
+        // check if this neighbor router is in peer Route64 TLV
+        if (!aRoute.IsRouterIdSet(router.GetRouterId()))
+        {
+            ExitNow(rval = false);
+        }
+
+        // get the peer's two-way link quality to this router
+        peerLinkQuality = aRoute.GetLinkQualityIn(routerCount);
+
+        if (peerLinkQuality > aRoute.GetLinkQualityOut(routerCount))
+        {
+            peerLinkQuality = aRoute.GetLinkQualityOut(routerCount);
+        }
+
+        // compare local link quality to this router with peer's
+        if (peerLinkQuality >= localLinkQuality)
+        {
+            routerCount++;
+            continue;
+        }
+
+        ExitNow(rval = false);
     }
 
 exit:
@@ -4446,13 +4449,13 @@
 }
 
 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
-void MleRouter::HandleTimeSync(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, const Neighbor *aNeighbor)
+void MleRouter::HandleTimeSync(RxInfo &aRxInfo)
 {
-    Log(kMessageReceive, kTypeTimeSync, aMessageInfo.GetPeerAddr());
+    Log(kMessageReceive, kTypeTimeSync, aRxInfo.mMessageInfo.GetPeerAddr());
 
-    VerifyOrExit(aNeighbor && aNeighbor->IsStateValid());
+    VerifyOrExit(aRxInfo.mNeighbor && aRxInfo.mNeighbor->IsStateValid());
 
-    Get<TimeSync>().HandleTimeSyncMessage(aMessage);
+    Get<TimeSync>().HandleTimeSyncMessage(aRxInfo.mMessage);
 
 exit:
     return;
@@ -4464,8 +4467,7 @@
     Ip6::Address destination;
     Message *    message = nullptr;
 
-    VerifyOrExit((message = NewMleMessage()) != nullptr, error = kErrorNoBufs);
-    SuccessOrExit(error = AppendHeader(*message, kCommandTimeSync));
+    VerifyOrExit((message = NewMleMessage(kCommandTimeSync)) != nullptr, error = kErrorNoBufs);
 
     message->SetTimeSync(true);
 
diff --git a/src/core/thread/mle_router.hpp b/src/core/thread/mle_router.hpp
index f35a36c..6645498 100644
--- a/src/core/thread/mle_router.hpp
+++ b/src/core/thread/mle_router.hpp
@@ -303,7 +303,7 @@
     /**
      * This method sets the ROUTER_UPGRADE_THRESHOLD value.
      *
-     * @returns The ROUTER_UPGRADE_THRESHOLD value.
+     * @param[in]  aThreshold  The ROUTER_UPGRADE_THRESHOLD value.
      *
      */
     void SetRouterUpgradeThreshold(uint8_t aThreshold) { mRouterUpgradeThreshold = aThreshold; }
@@ -319,7 +319,7 @@
     /**
      * This method sets the ROUTER_DOWNGRADE_THRESHOLD value.
      *
-     * @returns The ROUTER_DOWNGRADE_THRESHOLD value.
+     * @param[in]  aThreshold  The ROUTER_DOWNGRADE_THRESHOLD value.
      *
      */
     void SetRouterDowngradeThreshold(uint8_t aThreshold) { mRouterDowngradeThreshold = aThreshold; }
@@ -575,6 +575,11 @@
     static constexpr uint32_t kStateUpdatePeriod             = 1000; // State update period (in msec).
     static constexpr uint16_t kUnsolicitedDataResponseJitter = 500;  // Max delay for unsol Data Response (in msec).
 
+    // Threshold to accept a router upgrade request with reason
+    // `kBorderRouterRequst` (number of BRs acting as router in
+    // Network Data).
+    static constexpr uint8_t kRouterUpgradeBorderRouterRequestThreshold = 2;
+
     Error AppendConnectivity(Message &aMessage);
     Error AppendChildAddresses(Message &aMessage, Child &aChild);
     Error AppendRoute(Message &aMessage, Neighbor *aNeighbor = nullptr);
@@ -582,40 +587,29 @@
     Error AppendPendingDataset(Message &aMessage);
     void  HandleDetachStart(void);
     void  HandleChildStart(AttachMode aMode);
-    void  HandleLinkRequest(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, Neighbor *aNeighbor);
-    void  HandleLinkAccept(const Message &         aMessage,
-                           const Ip6::MessageInfo &aMessageInfo,
-                           uint32_t                aKeySequence,
-                           Neighbor *              aNeighbor);
-    Error HandleLinkAccept(const Message &         aMessage,
-                           const Ip6::MessageInfo &aMessageInfo,
-                           uint32_t                aKeySequence,
-                           Neighbor *              aNeighbor,
-                           bool                    aRequest);
-    void  HandleLinkAcceptAndRequest(const Message &         aMessage,
-                                     const Ip6::MessageInfo &aMessageInfo,
-                                     uint32_t                aKeySequence,
-                                     Neighbor *              aNeighbor);
-    Error HandleAdvertisement(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, Neighbor *);
-    void  HandleParentRequest(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
-    void  HandleChildIdRequest(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, uint32_t aKeySequence);
-    void  HandleChildUpdateRequest(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
-    void  HandleChildUpdateResponse(const Message &         aMessage,
-                                    const Ip6::MessageInfo &aMessageInfo,
-                                    uint32_t                aKeySequence,
-                                    Neighbor *              aNeighbor);
-    void  HandleDataRequest(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, const Neighbor *aNeighbor);
+    void  HandleLinkRequest(RxInfo &aRxInfo);
+    void  HandleLinkAccept(RxInfo &aRxInfo);
+    Error HandleLinkAccept(RxInfo &aRxInfo, bool aRequest);
+    void  HandleLinkAcceptAndRequest(RxInfo &aRxInfo);
+    Error HandleAdvertisement(RxInfo &aRxInfo);
+    void  HandleParentRequest(RxInfo &aRxInfo);
+    void  HandleChildIdRequest(RxInfo &aRxInfo);
+    void  HandleChildUpdateRequest(RxInfo &aRxInfo);
+    void  HandleChildUpdateResponse(RxInfo &aRxInfo);
+    void  HandleDataRequest(RxInfo &aRxInfo);
     void  HandleNetworkDataUpdateRouter(void);
-    void  HandleDiscoveryRequest(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
+    void  HandleDiscoveryRequest(RxInfo &aRxInfo);
 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
-    void HandleTimeSync(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, const Neighbor *aNeighbor);
+    void HandleTimeSync(RxInfo &aRxInfo);
 #endif
 
-    Error ProcessRouteTlv(const RouteTlv &aRoute);
+    Error ProcessRouteTlv(RxInfo &aRxInfo);
+    Error ProcessRouteTlv(RxInfo &aRxInfo, RouteTlv &aRouteTlv);
     void  StopAdvertiseTrickleTimer(void);
     Error SendAddressSolicit(ThreadStatusTlv::Status aStatus);
     void  SendAddressRelease(void);
     void  SendAddressSolicitResponse(const Coap::Message &   aRequest,
+                                     ThreadStatusTlv::Status aResponseStatus,
                                      const Router *          aRouter,
                                      const Ip6::MessageInfo &aMessageInfo);
     void  SendAdvertisement(void);
diff --git a/src/core/thread/mle_tlvs.hpp b/src/core/thread/mle_tlvs.hpp
index 25934be..4b1dd67 100644
--- a/src/core/thread/mle_tlvs.hpp
+++ b/src/core/thread/mle_tlvs.hpp
@@ -360,9 +360,9 @@
      * @returns The Link Quality In value for a given Router index.
      *
      */
-    uint8_t GetLinkQualityIn(uint8_t aRouterIndex) const
+    LinkQuality GetLinkQualityIn(uint8_t aRouterIndex) const
     {
-        return (mRouteData[aRouterIndex] & kLinkQualityInMask) >> kLinkQualityInOffset;
+        return static_cast<LinkQuality>((mRouteData[aRouterIndex] & kLinkQualityInMask) >> kLinkQualityInOffset);
     }
 
     /**
@@ -372,7 +372,7 @@
      * @param[in]  aLinkQuality  The Link Quality In value for a given Router index.
      *
      */
-    void SetLinkQualityIn(uint8_t aRouterIndex, uint8_t aLinkQuality)
+    void SetLinkQualityIn(uint8_t aRouterIndex, LinkQuality aLinkQuality)
     {
         mRouteData[aRouterIndex] = (mRouteData[aRouterIndex] & ~kLinkQualityInMask) |
                                    ((aLinkQuality << kLinkQualityInOffset) & kLinkQualityInMask);
@@ -386,9 +386,9 @@
      * @returns The Link Quality Out value for a given Router index.
      *
      */
-    uint8_t GetLinkQualityOut(uint8_t aRouterIndex) const
+    LinkQuality GetLinkQualityOut(uint8_t aRouterIndex) const
     {
-        return (mRouteData[aRouterIndex] & kLinkQualityOutMask) >> kLinkQualityOutOffset;
+        return static_cast<LinkQuality>((mRouteData[aRouterIndex] & kLinkQualityOutMask) >> kLinkQualityOutOffset);
     }
 
     /**
@@ -398,7 +398,7 @@
      * @param[in]  aLinkQuality  The Link Quality Out value for a given Router index.
      *
      */
-    void SetLinkQualityOut(uint8_t aRouterIndex, uint8_t aLinkQuality)
+    void SetLinkQualityOut(uint8_t aRouterIndex, LinkQuality aLinkQuality)
     {
         mRouteData[aRouterIndex] = (mRouteData[aRouterIndex] & ~kLinkQualityOutMask) |
                                    ((aLinkQuality << kLinkQualityOutOffset) & kLinkQualityOutMask);
@@ -600,11 +600,12 @@
      * @returns The Link Quality Out value for a given Router index.
      *
      */
-    uint8_t GetLinkQualityOut(uint8_t aRouterIndex) const
+    LinkQuality GetLinkQualityOut(uint8_t aRouterIndex) const
     {
         int offset = ((aRouterIndex & 1) ? kOddEntryOffset : 0);
-        return (mRouteData[aRouterIndex + aRouterIndex / 2] & (kLinkQualityOutMask >> offset)) >>
-               (kLinkQualityOutOffset - offset);
+        return static_cast<LinkQuality>(
+            (mRouteData[aRouterIndex + aRouterIndex / 2] & (kLinkQualityOutMask >> offset)) >>
+            (kLinkQualityOutOffset - offset));
     }
 
     /**
@@ -614,7 +615,7 @@
      * @param[in]  aLinkQuality  The Link Quality Out value for a given Router index.
      *
      */
-    void SetLinkQualityOut(uint8_t aRouterIndex, uint8_t aLinkQuality)
+    void SetLinkQualityOut(uint8_t aRouterIndex, LinkQuality aLinkQuality)
     {
         int offset = ((aRouterIndex & 1) ? kOddEntryOffset : 0);
         mRouteData[aRouterIndex + aRouterIndex / 2] =
diff --git a/src/core/thread/mle_types.cpp b/src/core/thread/mle_types.cpp
index 68feab7..ccdf2a6 100644
--- a/src/core/thread/mle_types.cpp
+++ b/src/core/thread/mle_types.cpp
@@ -63,7 +63,7 @@
     return string;
 }
 
-void MeshLocalPrefix::SetFromExtendedPanId(const Mac::ExtendedPanId &aExtendedPanId)
+void MeshLocalPrefix::SetFromExtendedPanId(const MeshCoP::ExtendedPanId &aExtendedPanId)
 {
     m8[0] = 0xfd;
     memcpy(&m8[1], aExtendedPanId.m8, 5);
diff --git a/src/core/thread/mle_types.hpp b/src/core/thread/mle_types.hpp
index f939836..59efaec 100644
--- a/src/core/thread/mle_types.hpp
+++ b/src/core/thread/mle_types.hpp
@@ -49,6 +49,7 @@
 #include "common/equatable.hpp"
 #include "common/string.hpp"
 #include "mac/mac_types.hpp"
+#include "meshcop/extended_panid.hpp"
 #include "net/ip6_address.hpp"
 #include "thread/network_data_types.hpp"
 
@@ -194,19 +195,6 @@
     kRoleLeader   = OT_DEVICE_ROLE_LEADER,   ///< The Thread Leader role.
 };
 
-/**
- * MLE Attach modes
- *
- */
-enum AttachMode : uint8_t
-{
-    kAttachAny           = 0, ///< Attach to any Thread partition.
-    kAttachSame1         = 1, ///< Attach to the same Thread partition (attempt 1 when losing connectivity).
-    kAttachSame2         = 2, ///< Attach to the same Thread partition (attempt 2 when losing connectivity).
-    kAttachBetter        = 3, ///< Attach to a better (i.e. higher weight/partition id) Thread partition.
-    kAttachSameDowngrade = 4, ///< Attach to the same Thread partition during downgrade process.
-};
-
 constexpr uint16_t kAloc16Leader                      = 0xfc00;
 constexpr uint16_t kAloc16DhcpAgentStart              = 0xfc01;
 constexpr uint16_t kAloc16DhcpAgentEnd                = 0xfc0f;
@@ -441,7 +429,7 @@
      * @param[in] aExtendedPanId   An Extended PAN ID.
      *
      */
-    void SetFromExtendedPanId(const Mac::ExtendedPanId &aExtendedPanId);
+    void SetFromExtendedPanId(const MeshCoP::ExtendedPanId &aExtendedPanId);
 
 } OT_TOOL_PACKED_END;
 
diff --git a/src/core/thread/mlr_manager.cpp b/src/core/thread/mlr_manager.cpp
index 3623089..29aeaa4 100644
--- a/src/core/thread/mlr_manager.cpp
+++ b/src/core/thread/mlr_manager.cpp
@@ -39,7 +39,7 @@
 #include "common/code_utils.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "net/ip6_address.hpp"
 #include "thread/thread_netif.hpp"
 #include "thread/uri_paths.hpp"
@@ -47,6 +47,8 @@
 
 namespace ot {
 
+RegisterLogModule("MlrManager");
+
 MlrManager::MlrManager(Instance &aInstance)
     : InstanceLocator(aInstance)
 #if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE) && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
@@ -331,7 +333,7 @@
 #else
     if (!Get<MeshCoP::Commissioner>().IsActive())
     {
-        otLogWarnMlr("MLR.req without active commissioner session for test.");
+        LogWarn("MLR.req without active commissioner session for test.");
     }
 #endif
 
@@ -397,17 +399,13 @@
     Error            error   = kErrorNone;
     Mle::MleRouter & mle     = Get<Mle::MleRouter>();
     Coap::Message *  message = nullptr;
-    Ip6::MessageInfo messageInfo;
+    Tmf::MessageInfo messageInfo(GetInstance());
     Ip6AddressesTlv  addressesTlv;
 
     VerifyOrExit(Get<BackboneRouter::Leader>().HasPrimary(), error = kErrorInvalidState);
 
-    VerifyOrExit((message = Get<Tmf::Agent>().NewMessage()) != nullptr, error = kErrorNoBufs);
-
-    message->InitAsConfirmablePost();
-    SuccessOrExit(message->GenerateRandomToken(Coap::Message::kDefaultTokenLength));
-    SuccessOrExit(message->AppendUriPathOptions(UriPath::kMlr));
-    SuccessOrExit(message->SetPayloadMarker());
+    message = Get<Tmf::Agent>().NewConfirmablePostMessage(UriPath::kMlr);
+    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
     addressesTlv.Init();
     addressesTlv.SetLength(sizeof(Ip6::Address) * aAddressNum);
@@ -442,15 +440,14 @@
                                                       Get<BackboneRouter::Leader>().GetServer16());
     }
 
-    messageInfo.SetPeerPort(Tmf::kUdpPort);
-    messageInfo.SetSockAddr(mle.GetMeshLocal16());
+    messageInfo.SetSockAddrToRloc();
 
     error = Get<Tmf::Agent>().SendMessage(*message, messageInfo, aResponseHandler, aResponseContext);
 
-    otLogInfoMlr("Sent MLR.req: addressNum=%d", aAddressNum);
+    LogInfo("Sent MLR.req: addressNum=%d", aAddressNum);
 
 exit:
-    otLogInfoMlr("SendMulticastListenerRegistrationMessage(): %s", ErrorToString(error));
+    LogInfo("SendMulticastListenerRegistrationMessage(): %s", ErrorToString(error));
     FreeMessageOnError(message, error);
     return error;
 }
@@ -621,7 +618,7 @@
 
 void MlrManager::Reregister(void)
 {
-    otLogInfoMlr("MLR Reregister!");
+    LogInfo("MLR Reregister!");
 
     SetMulticastAddressMlrState(kMlrStateRegistered, kMlrStateToRegister);
     CheckInvariants();
@@ -674,19 +671,19 @@
 
     UpdateTimeTickerRegistration();
 
-    otLogDebgMlr("MlrManager::UpdateReregistrationDelay: rereg=%d, needSendMlr=%d, ReregDelay=%lu", aRereg, needSendMlr,
-                 mReregistrationDelay);
+    LogDebg("MlrManager::UpdateReregistrationDelay: rereg=%d, needSendMlr=%d, ReregDelay=%lu", aRereg, needSendMlr,
+            mReregistrationDelay);
 }
 
 void MlrManager::LogMulticastAddresses(void)
 {
-#if OPENTHREAD_CONFIG_LOG_MLR && OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_DEBG
-    otLogDebgMlr("-------- Multicast Addresses --------");
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_DEBG)
+    LogDebg("-------- Multicast Addresses --------");
 
 #if OPENTHREAD_CONFIG_MLR_ENABLE
     for (const Ip6::Netif::ExternalMulticastAddress &addr : Get<ThreadNetif>().IterateExternalMulticastAddresses())
     {
-        otLogDebgMlr("%-32s%c", addr.GetAddress().ToString().AsCString(), "-rR"[addr.GetMlrState()]);
+        LogDebg("%-32s%c", addr.GetAddress().ToString().AsCString(), "-rR"[addr.GetMlrState()]);
     }
 #endif
 
@@ -695,13 +692,13 @@
     {
         for (const Ip6::Address &address : child.IterateIp6Addresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal))
         {
-            otLogDebgMlr("%-32s%c %04x", address.ToString().AsCString(), "-rR"[child.GetAddressMlrState(address)],
-                         child.GetRloc16());
+            LogDebg("%-32s%c %04x", address.ToString().AsCString(), "-rR"[child.GetAddressMlrState(address)],
+                    child.GetRloc16());
         }
     }
 #endif
 
-#endif // OPENTHREAD_CONFIG_LOG_MLR && OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_DEBG
+#endif // OT_SHOULD_LOG_AT(OT_LOG_LEVEL_DEBG)
 }
 
 void MlrManager::AppendToUniqueAddressList(Ip6::Address (&aAddresses)[kIp6AddressesNumMax],
@@ -759,19 +756,19 @@
     OT_UNUSED_VARIABLE(aFailedAddresses);
     OT_UNUSED_VARIABLE(aFailedAddressNum);
 
-#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_WARN) && (OPENTHREAD_CONFIG_LOG_MLR == 1)
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN)
     if (aResult == kErrorNone && aError == kErrorNone && aStatus == ThreadStatusTlv::MlrStatus::kMlrSuccess)
     {
-        otLogInfoMlr("Receive MLR.rsp OK");
+        LogInfo("Receive MLR.rsp OK");
     }
     else
     {
-        otLogWarnMlr("Receive MLR.rsp: result=%s, error=%s, status=%d, failedAddressNum=%d", ErrorToString(aResult),
-                     ErrorToString(aError), aStatus, aFailedAddressNum);
+        LogWarn("Receive MLR.rsp: result=%s, error=%s, status=%d, failedAddressNum=%d", ErrorToString(aResult),
+                ErrorToString(aError), aStatus, aFailedAddressNum);
 
         for (uint8_t i = 0; i < aFailedAddressNum; i++)
         {
-            otLogWarnMlr("MA failed: %s", aFailedAddresses[i].ToString().AsCString());
+            LogWarn("MA failed: %s", aFailedAddresses[i].ToString().AsCString());
         }
     }
 #endif
diff --git a/src/core/thread/neighbor_table.hpp b/src/core/thread/neighbor_table.hpp
index 80433cc..abe2df6 100644
--- a/src/core/thread/neighbor_table.hpp
+++ b/src/core/thread/neighbor_table.hpp
@@ -194,9 +194,9 @@
      * This method gets the next neighbor information. It is used to iterate through the entries of
      * the neighbor table.
      *
-     * @param[inout]  aIterator  A reference to the iterator context. To get the first neighbor entry
-                                 it should be set to OT_NEIGHBOR_INFO_ITERATOR_INIT.
-     * @param[out]    aNeighInfo The neighbor information.
+     * @param[in,out]  aIterator  A reference to the iterator context. To get the first neighbor entry
+                                  it should be set to OT_NEIGHBOR_INFO_ITERATOR_INIT.
+     * @param[out]     aNeighInfo The neighbor information.
      *
      * @retval kErrorNone         Successfully found the next neighbor entry in table.
      * @retval kErrorNotFound     No subsequent neighbor entry exists in the table.
diff --git a/src/core/thread/network_data.cpp b/src/core/thread/network_data.cpp
index 94e61c9..eccacff 100644
--- a/src/core/thread/network_data.cpp
+++ b/src/core/thread/network_data.cpp
@@ -34,11 +34,12 @@
 #include "network_data.hpp"
 
 #include "coap/coap_message.hpp"
+#include "common/array.hpp"
 #include "common/code_utils.hpp"
 #include "common/debug.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "mac/mac_types.hpp"
 #include "thread/thread_netif.hpp"
 #include "thread/thread_tlvs.hpp"
@@ -47,6 +48,8 @@
 namespace ot {
 namespace NetworkData {
 
+RegisterLogModule("NetworkData");
+
 Error NetworkData::CopyNetworkData(Type aType, uint8_t *aData, uint8_t &aDataLength) const
 {
     Error              error;
@@ -326,7 +329,7 @@
     return contains;
 }
 
-bool NetworkData::ContainsEntriesFrom(const NetworkData &aComapre, uint16_t aRloc16) const
+bool NetworkData::ContainsEntriesFrom(const NetworkData &aCompare, uint16_t aRloc16) const
 {
     bool     contains = true;
     Iterator iterator = kIteratorInit;
@@ -342,7 +345,7 @@
         config.mExternalRoute = &route;
         config.mService       = &service;
 
-        SuccessOrExit(aComapre.Iterate(iterator, aRloc16, config));
+        SuccessOrExit(aCompare.Iterate(iterator, aRloc16, config));
 
         if (((config.mOnMeshPrefix != nullptr) && !ContainsOnMeshPrefix(*config.mOnMeshPrefix)) ||
             ((config.mExternalRoute != nullptr) && !ContainsExternalRoute(*config.mExternalRoute)) ||
@@ -637,14 +640,12 @@
                                               Coap::ResponseHandler aHandler,
                                               void *                aContext) const
 {
-    Error            error   = kErrorNone;
-    Coap::Message *  message = nullptr;
-    Ip6::MessageInfo messageInfo;
+    Error            error = kErrorNone;
+    Coap::Message *  message;
+    Tmf::MessageInfo messageInfo(GetInstance());
 
-    VerifyOrExit((message = Get<Tmf::Agent>().NewMessage()) != nullptr, error = kErrorNoBufs);
-
-    SuccessOrExit(error = message->InitAsConfirmablePost(UriPath::kServerData));
-    SuccessOrExit(error = message->SetPayloadMarker());
+    message = Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(UriPath::kServerData);
+    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
     if (aAppendNetDataTlv)
     {
@@ -660,12 +661,10 @@
         SuccessOrExit(error = Tlv::Append<ThreadRloc16Tlv>(*message, aRloc16));
     }
 
-    IgnoreError(Get<Mle::MleRouter>().GetLeaderAloc(messageInfo.GetPeerAddr()));
-    messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetMeshLocal16());
-    messageInfo.SetPeerPort(Tmf::kUdpPort);
+    IgnoreError(messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc());
     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo, aHandler, aContext));
 
-    otLogInfoNetData("Sent server data notification");
+    LogInfo("Sent server data notification");
 
 exit:
     FreeMessageOnError(message, error);
@@ -707,5 +706,113 @@
     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::Mle::IsActiveRouter(aRloc16));
+                break;
+
+            case kChildRoleOnly:
+                VerifyOrExit(!Mle::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;
+}
+
 } // namespace NetworkData
 } // namespace ot
diff --git a/src/core/thread/network_data.hpp b/src/core/thread/network_data.hpp
index 37e78be..9272b75 100644
--- a/src/core/thread/network_data.hpp
+++ b/src/core/thread/network_data.hpp
@@ -168,10 +168,10 @@
     /**
      * This method provides full or stable copy of the Thread Network Data.
      *
-     * @param[in]    aType        The Network Data type to copy, the full set or stable subset.
-     * @param[out]   aData        A pointer to the data buffer to copy the Network Data into.
-     * @param[inout] aDataLength  On entry, size of the data buffer pointed to by @p aData.
-     *                            On exit, number of copied bytes.
+     * @param[in]     aType        The Network Data type to copy, the full set or stable subset.
+     * @param[out]    aData        A pointer to the data buffer to copy the Network Data into.
+     * @param[in,out] aDataLength  On entry, size of the data buffer pointed to by @p aData.
+     *                             On exit, number of copied bytes.
      *
      * @retval kErrorNone       Successfully copied Thread Network Data.
      * @retval kErrorNoBufs     Not enough space in @p aData to fully copy Thread Network Data.
@@ -194,8 +194,8 @@
     /**
      * This method provides the next On Mesh prefix in the Thread Network Data.
      *
-     * @param[inout]  aIterator  A reference to the Network Data iterator.
-     * @param[out]    aConfig    A reference to a config variable where the On Mesh Prefix information will be placed.
+     * @param[in,out]  aIterator  A reference to the Network Data iterator.
+     * @param[out]     aConfig    A reference to a config variable where the On Mesh Prefix information will be placed.
      *
      * @retval kErrorNone       Successfully found the next On Mesh prefix.
      * @retval kErrorNotFound   No subsequent On Mesh prefix exists in the Thread Network Data.
@@ -206,9 +206,9 @@
     /**
      * This method provides the next On Mesh prefix in the Thread Network Data for a given RLOC16.
      *
-     * @param[inout]  aIterator  A reference to the Network Data iterator.
-     * @param[in]     aRloc16    The RLOC16 value.
-     * @param[out]    aConfig    A reference to a config variable where the On Mesh Prefix information will be placed.
+     * @param[in,out]  aIterator  A reference to the Network Data iterator.
+     * @param[in]      aRloc16    The RLOC16 value.
+     * @param[out]     aConfig    A reference to a config variable where the On Mesh Prefix information will be placed.
      *
      * @retval kErrorNone       Successfully found the next On Mesh prefix.
      * @retval kErrorNotFound   No subsequent On Mesh prefix exists in the Thread Network Data.
@@ -219,8 +219,8 @@
     /**
      * This method provides the next external route in the Thread Network Data.
      *
-     * @param[inout]  aIterator  A reference to the Network Data iterator.
-     * @param[out]    aConfig    A reference to a config variable where the external route information will be placed.
+     * @param[in,out]  aIterator  A reference to the Network Data iterator.
+     * @param[out]     aConfig    A reference to a config variable where the external route information will be placed.
      *
      * @retval kErrorNone       Successfully found the next external route.
      * @retval kErrorNotFound   No subsequent external route exists in the Thread Network Data.
@@ -231,9 +231,9 @@
     /**
      * This method provides the next external route in the Thread Network Data for a given RLOC16.
      *
-     * @param[inout]  aIterator  A reference to the Network Data iterator.
-     * @param[in]     aRloc16    The RLOC16 value.
-     * @param[out]    aConfig    A reference to a config variable where the external route information will be placed.
+     * @param[in,out]  aIterator  A reference to the Network Data iterator.
+     * @param[in]      aRloc16    The RLOC16 value.
+     * @param[out]     aConfig    A reference to a config variable where the external route information will be placed.
      *
      * @retval kErrorNone       Successfully found the next external route.
      * @retval kErrorNotFound   No subsequent external route exists in the Thread Network Data.
@@ -244,8 +244,8 @@
     /**
      * This method provides the next service in the Thread Network Data.
      *
-     * @param[inout]  aIterator  A reference to the Network Data iterator.
-     * @param[out]    aConfig    A reference to a config variable where the service information will be placed.
+     * @param[in,out]  aIterator  A reference to the Network Data iterator.
+     * @param[out]     aConfig    A reference to a config variable where the service information will be placed.
      *
      * @retval kErrorNone       Successfully found the next service.
      * @retval kErrorNotFound   No subsequent service exists in the Thread Network Data.
@@ -256,9 +256,9 @@
     /**
      * This method provides the next service in the Thread Network Data for a given RLOC16.
      *
-     * @param[inout]  aIterator  A reference to the Network Data iterator.
-     * @param[in]     aRloc16    The RLOC16 value.
-     * @param[out]    aConfig    A reference to a config variable where the service information will be placed.
+     * @param[in,out]  aIterator  A reference to the Network Data iterator.
+     * @param[in]      aRloc16    The RLOC16 value.
+     * @param[out]     aConfig    A reference to a config variable where the service information will be placed.
      *
      * @retval kErrorNone       Successfully found the next service.
      * @retval kErrorNotFound   No subsequent service exists in the Thread Network Data.
@@ -310,13 +310,13 @@
      * @retval FALSE if Network Data does not contains all the same entries as in @p aCompare for @p aRloc16.
      *
      */
-    bool ContainsEntriesFrom(const NetworkData &aComapre, uint16_t aRloc16) const;
+    bool ContainsEntriesFrom(const NetworkData &aCompare, uint16_t aRloc16) const;
 
     /**
      * This method provides the next server RLOC16 in the Thread Network Data.
      *
-     * @param[inout]  aIterator  A reference to the Network Data iterator.
-     * @param[out]    aRloc16    The RLOC16 value.
+     * @param[in,out]  aIterator  A reference to the Network Data iterator.
+     * @param[out]     aRloc16    The RLOC16 value.
      *
      * @retval kErrorNone       Successfully found the next server.
      * @retval kErrorNotFound   No subsequent server exists in the Thread Network Data.
@@ -324,6 +324,43 @@
      */
     Error GetNextServer(Iterator &aIterator, uint16_t &aRloc16) const;
 
+    /**
+     * This method finds and returns the list of RLOCs of border routers providing external IPv6 connectivity.
+     *
+     * A border router is considered to provide external IPv6 connectivity if it has added at least one external route
+     * entry, or an on-mesh prefix with default-route and on-mesh flags set.
+     *
+     * This method 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.
+     *
+     */
+    Error FindBorderRouters(RoleFilter aRoleFilter, uint16_t aRlocs[], uint8_t &aRlocsLength) const;
+
+    /**
+     * This method counts the number of border routers providing external IPv6 connectivity.
+     *
+     * A border router is considered to provide external IPv6 connectivity if it has added at least one external route
+     * entry, or an on-mesh prefix with default-route and on-mesh flags set.
+     *
+     * This method 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 RLOCs to include (any role, router only, or child only).
+     *
+     * @returns The number of border routers in Thread Network Data matching @p aRoleFilter.
+     *
+     */
+    uint8_t CountBorderRouters(RoleFilter aRoleFilter) const;
+
 protected:
     /**
      * This enumeration defines Service Data match mode.
diff --git a/src/core/thread/network_data_leader.cpp b/src/core/thread/network_data_leader.cpp
index 53c5e4b..8c28842 100644
--- a/src/core/thread/network_data_leader.cpp
+++ b/src/core/thread/network_data_leader.cpp
@@ -53,6 +53,8 @@
 namespace ot {
 namespace NetworkData {
 
+RegisterLogModule("NetworkData");
+
 void LeaderBase::Reset(void)
 {
     mVersion       = Random::NonCrypto::GetUint8();
@@ -87,6 +89,29 @@
     return error;
 }
 
+Error LeaderBase::GetPreferredNat64Prefix(ExternalRouteConfig &aConfig) const
+{
+    Error               error    = kErrorNotFound;
+    Iterator            iterator = kIteratorInit;
+    ExternalRouteConfig config;
+
+    while (GetNextExternalRoute(iterator, config) == kErrorNone)
+    {
+        if (!config.mNat64 || !config.GetPrefix().IsValidNat64())
+        {
+            continue;
+        }
+
+        if ((error == kErrorNotFound) || (config.mPreference > aConfig.mPreference))
+        {
+            aConfig = config;
+            error   = kErrorNone;
+        }
+    }
+
+    return error;
+}
+
 const PrefixTlv *LeaderBase::FindNextMatchingPrefix(const Ip6::Address &aAddress, const PrefixTlv *aPrevTlv) const
 {
     const PrefixTlv *prefixTlv;
@@ -376,7 +401,7 @@
     }
 #endif
 
-    otDumpDebgNetData("SetNetworkData", GetBytes(), GetLength());
+    DumpDebg("SetNetworkData", GetBytes(), GetLength());
 
     Get<ot::Notifier>().Signal(kEventThreadNetdataChanged);
 
diff --git a/src/core/thread/network_data_leader.hpp b/src/core/thread/network_data_leader.hpp
index b9a7124..54765ef 100644
--- a/src/core/thread/network_data_leader.hpp
+++ b/src/core/thread/network_data_leader.hpp
@@ -267,6 +267,20 @@
                        bool               aServerStable,
                        uint8_t &          aServiceId) const;
 
+    /**
+     * This methods gets the preferred NAT64 prefix from network data.
+     *
+     * The returned prefix is the highest preference external route entry in Network Data with NAT64 flag set. If there
+     * are multiple such entries the first one is returned.
+     *
+     * @param[out] aConfig      A reference to an `ExternalRouteConfig` to return the prefix.
+     *
+     * @retval kErrorNone       Found the NAT64 prefix and updated @p aConfig.
+     * @retval kErrorNotFound   Could not find any NAT64 entry.
+     *
+     */
+    Error GetPreferredNat64Prefix(ExternalRouteConfig &aConfig) const;
+
 protected:
     uint8_t mStableVersion;
     uint8_t mVersion;
diff --git a/src/core/thread/network_data_leader_ftd.cpp b/src/core/thread/network_data_leader_ftd.cpp
index f318d6e..770c241 100644
--- a/src/core/thread/network_data_leader_ftd.cpp
+++ b/src/core/thread/network_data_leader_ftd.cpp
@@ -42,7 +42,7 @@
 #include "common/encoding.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "common/message.hpp"
 #include "common/timer.hpp"
 #include "mac/mac_types.hpp"
@@ -56,6 +56,8 @@
 namespace ot {
 namespace NetworkData {
 
+RegisterLogModule("NetworkData");
+
 Leader::Leader(Instance &aInstance)
     : LeaderBase(aInstance)
     , mTimer(aInstance, Leader::HandleTimer)
@@ -142,7 +144,7 @@
     ThreadNetworkDataTlv networkDataTlv;
     uint16_t             rloc16;
 
-    otLogInfoNetData("Received network data registration");
+    LogInfo("Received network data registration");
 
     VerifyOrExit(aMessageInfo.GetPeerAddr().GetIid().IsRoutingLocator());
 
@@ -170,7 +172,7 @@
 
     SuccessOrExit(Get<Tmf::Agent>().SendEmptyAck(aMessage, aMessageInfo));
 
-    otLogInfoNetData("Sent network data registration acknowledgment");
+    LogInfo("Sent network data registration acknowledgment");
 
 exit:
     return;
@@ -305,10 +307,8 @@
     uint8_t *             data   = nullptr;
     uint8_t               length = 0;
 
-    VerifyOrExit((message = Get<Tmf::Agent>().NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
-
-    SuccessOrExit(error = message->SetDefaultResponseHeader(aRequest));
-    SuccessOrExit(error = message->SetPayloadMarker());
+    message = Get<Tmf::Agent>().NewPriorityResponseMessage(aRequest);
+    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
     commDataTlv = GetCommissioningData();
 
@@ -346,7 +346,7 @@
 
     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, aMessageInfo));
 
-    otLogInfoMeshCoP("sent commissioning dataset get response");
+    LogInfo("sent commissioning dataset get response");
 
 exit:
     FreeMessageOnError(message, error);
@@ -359,16 +359,14 @@
     Error          error = kErrorNone;
     Coap::Message *message;
 
-    VerifyOrExit((message = Get<Tmf::Agent>().NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
-
-    SuccessOrExit(error = message->SetDefaultResponseHeader(aRequest));
-    SuccessOrExit(error = message->SetPayloadMarker());
+    message = Get<Tmf::Agent>().NewPriorityResponseMessage(aRequest);
+    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
     SuccessOrExit(error = Tlv::Append<MeshCoP::StateTlv>(*message, aState));
 
     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, aMessageInfo));
 
-    otLogInfoMeshCoP("sent commissioning dataset set response");
+    LogInfo("sent commissioning dataset set response");
 
 exit:
     FreeMessageOnError(message, error);
@@ -729,13 +727,13 @@
 
     IncrementVersions(flags);
 
-    otDumpDebgNetData("Register", GetBytes(), GetLength());
+    DumpDebg("Register", GetBytes(), GetLength());
 
 exit:
 
     if (error != kErrorNone)
     {
-        otLogNoteNetData("Failed to register network data: %s", ErrorToString(error));
+        LogNote("Failed to register network data: %s", ErrorToString(error));
     }
 }
 
@@ -972,7 +970,7 @@
         {
             aServiceId = serviceId;
             error      = kErrorNone;
-            otLogInfoNetData("Allocated Service ID = %d", serviceId);
+            LogInfo("Allocated Service ID = %d", serviceId);
             break;
         }
     }
@@ -1007,7 +1005,7 @@
             mContextUsed |= (1 << contextId);
             aContextId = contextId;
             error      = kErrorNone;
-            otLogInfoNetData("Allocated Context ID = %d", contextId);
+            LogInfo("Allocated Context ID = %d", contextId);
             break;
         }
     }
@@ -1017,7 +1015,7 @@
 
 void Leader::FreeContextId(uint8_t aContextId)
 {
-    otLogInfoNetData("Free Context Id = %d", aContextId);
+    LogInfo("Free Context Id = %d", aContextId);
     RemoveContext(aContextId);
     mContextUsed &= ~(1 << aContextId);
     IncrementVersions(/* aIncludeStable */ true);
@@ -1380,6 +1378,45 @@
     return error;
 }
 
+#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
+bool Leader::ContainsOmrPrefix(const Ip6::Prefix &aPrefix)
+{
+    PrefixTlv *prefixTlv;
+    bool       contains = false;
+
+    VerifyOrExit(BorderRouter::RoutingManager::IsValidOmrPrefix(aPrefix));
+
+    prefixTlv = FindPrefix(aPrefix);
+    VerifyOrExit(prefixTlv != nullptr);
+
+    for (int i = 0; i < 2; i++)
+    {
+        const BorderRouterTlv *borderRouter = prefixTlv->FindSubTlv<BorderRouterTlv>(/* aStable */ (i == 0));
+
+        if (borderRouter == nullptr)
+        {
+            continue;
+        }
+
+        for (const BorderRouterEntry *entry = borderRouter->GetFirstEntry(); entry <= borderRouter->GetLastEntry();
+             entry                          = entry->GetNext())
+        {
+            OnMeshPrefixConfig config;
+
+            config.SetFrom(*prefixTlv, *borderRouter, *entry);
+
+            if (BorderRouter::RoutingManager::IsValidOmrPrefix(config))
+            {
+                ExitNow(contains = true);
+            }
+        }
+    }
+
+exit:
+    return contains;
+}
+#endif
+
 } // namespace NetworkData
 } // namespace ot
 
diff --git a/src/core/thread/network_data_leader_ftd.hpp b/src/core/thread/network_data_leader_ftd.hpp
index 2fecf38..c1f5764 100644
--- a/src/core/thread/network_data_leader_ftd.hpp
+++ b/src/core/thread/network_data_leader_ftd.hpp
@@ -173,6 +173,19 @@
      */
     Error RemoveStaleChildEntries(Coap::ResponseHandler aHandler, void *aContext);
 
+#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
+    /**
+     * This method indicates whether a given Prefix can act as a valid OMR prefix and exists in the network data.
+     *
+     * @param[in]  aPrefix   The OMR prefix to check.
+     *
+     * @retval TRUE  If @p aPrefix is a valid OMR prefix and Network Data contains @p aPrefix.
+     * @retval FALSE Otherwise.
+     *
+     */
+    bool ContainsOmrPrefix(const Ip6::Prefix &aPrefix);
+#endif
+
 private:
     class ChangedFlags
     {
diff --git a/src/core/thread/network_data_local.cpp b/src/core/thread/network_data_local.cpp
index 53366a1..7299bef 100644
--- a/src/core/thread/network_data_local.cpp
+++ b/src/core/thread/network_data_local.cpp
@@ -39,7 +39,7 @@
 #include "common/debug.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "mac/mac_types.hpp"
 #include "thread/mle_types.hpp"
 #include "thread/thread_netif.hpp"
@@ -47,6 +47,8 @@
 namespace ot {
 namespace NetworkData {
 
+RegisterLogModule("NetworkData");
+
 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
 
 Error Local::AddOnMeshPrefix(const OnMeshPrefixConfig &aConfig)
@@ -62,9 +64,18 @@
     return error;
 }
 
-Error Local::RemoveOnMeshPrefix(const Ip6::Prefix &aPrefix)
+bool Local::ContainsOnMeshPrefix(const Ip6::Prefix &aPrefix) const
 {
-    return RemovePrefix(aPrefix, NetworkDataTlv::kTypeBorderRouter);
+    const PrefixTlv *tlv;
+    bool             contains = false;
+
+    VerifyOrExit((tlv = FindPrefix(aPrefix)) != nullptr);
+    VerifyOrExit(tlv->FindSubTlv(NetworkDataTlv::kTypeBorderRouter) != nullptr);
+
+    contains = true;
+
+exit:
+    return contains;
 }
 
 Error Local::AddHasRoutePrefix(const ExternalRouteConfig &aConfig)
@@ -79,18 +90,13 @@
     return error;
 }
 
-Error Local::RemoveHasRoutePrefix(const Ip6::Prefix &aPrefix)
-{
-    return RemovePrefix(aPrefix, NetworkDataTlv::kTypeHasRoute);
-}
-
 Error Local::AddPrefix(const Ip6::Prefix &aPrefix, NetworkDataTlv::Type aSubTlvType, uint16_t aFlags, bool aStable)
 {
     Error      error = kErrorNone;
     uint8_t    subTlvLength;
     PrefixTlv *prefixTlv;
 
-    IgnoreError(RemovePrefix(aPrefix, aSubTlvType));
+    IgnoreError(RemovePrefix(aPrefix));
 
     subTlvLength = (aSubTlvType == NetworkDataTlv::kTypeBorderRouter)
                        ? sizeof(BorderRouterTlv) + sizeof(BorderRouterEntry)
@@ -125,23 +131,22 @@
         prefixTlv->GetSubTlvs()->SetStable();
     }
 
-    otDumpDebgNetData("AddPrefix", GetBytes(), GetLength());
+    DumpDebg("AddPrefix", GetBytes(), GetLength());
 
 exit:
     return error;
 }
 
-Error Local::RemovePrefix(const Ip6::Prefix &aPrefix, NetworkDataTlv::Type aSubTlvType)
+Error Local::RemovePrefix(const Ip6::Prefix &aPrefix)
 {
     Error      error = kErrorNone;
     PrefixTlv *tlv;
 
     VerifyOrExit((tlv = FindPrefix(aPrefix)) != nullptr, error = kErrorNotFound);
-    VerifyOrExit(tlv->FindSubTlv(aSubTlvType) != nullptr, error = kErrorNotFound);
     RemoveTlv(tlv);
 
 exit:
-    otDumpDebgNetData("RmvPrefix", GetBytes(), GetLength());
+    DumpDebg("RmvPrefix", GetBytes(), GetLength());
     return error;
 }
 
@@ -168,12 +173,6 @@
     }
 }
 
-bool Local::IsConsistent(void) const
-{
-    return Get<Leader>().ContainsEntriesFrom(*this, Get<Mle::MleRouter>().GetRloc16()) &&
-           ContainsEntriesFrom(Get<Leader>(), Get<Mle::MleRouter>().GetRloc16());
-}
-
 #endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
 
 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
@@ -210,7 +209,7 @@
         serverTlv->SetStable();
     }
 
-    otDumpDebgNetData("AddService", GetBytes(), GetLength());
+    DumpDebg("AddService", GetBytes(), GetLength());
 
 exit:
     return error;
@@ -226,7 +225,7 @@
     RemoveTlv(tlv);
 
 exit:
-    otDumpDebgNetData("RmvService", GetBytes(), GetLength());
+    DumpDebg("RmvService", GetBytes(), GetLength());
     return error;
 }
 
@@ -277,6 +276,12 @@
     }
 }
 
+bool Local::IsConsistent(void) const
+{
+    return Get<Leader>().ContainsEntriesFrom(*this, Get<Mle::MleRouter>().GetRloc16()) &&
+           ContainsEntriesFrom(Get<Leader>(), Get<Mle::MleRouter>().GetRloc16());
+}
+
 Error Local::UpdateInconsistentServerData(Coap::ResponseHandler aHandler, void *aContext)
 {
     Error    error = kErrorNone;
@@ -292,9 +297,7 @@
 
     UpdateRloc();
 
-#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
     VerifyOrExit(!IsConsistent(), error = kErrorNotFound);
-#endif
 
     if (mOldRloc == rloc)
     {
diff --git a/src/core/thread/network_data_local.hpp b/src/core/thread/network_data_local.hpp
index de28794..ec274d1 100644
--- a/src/core/thread/network_data_local.hpp
+++ b/src/core/thread/network_data_local.hpp
@@ -95,7 +95,18 @@
      * @retval kErrorNotFound   Could not find the Border Router entry.
      *
      */
-    Error RemoveOnMeshPrefix(const Ip6::Prefix &aPrefix);
+    Error RemoveOnMeshPrefix(const Ip6::Prefix &aPrefix) { return RemovePrefix(aPrefix); }
+
+    /**
+     * This method indicates whether or not the Thread Network Data contains a given on mesh prefix.
+     *
+     * @param[in]  aPrefix   The on mesh prefix to check.
+     *
+     * @retval TRUE  if Network Data contains mesh prefix @p aPrefix.
+     * @retval FALSE if Network Data does not contain mesh prefix @p aPrefix.
+     *
+     */
+    bool ContainsOnMeshPrefix(const Ip6::Prefix &aPrefix) const;
 
     /**
      * This method adds a Has Route entry to the Thread Network data.
@@ -118,7 +129,7 @@
      * @retval kErrorNotFound   Could not find the Border Router entry.
      *
      */
-    Error RemoveHasRoutePrefix(const Ip6::Prefix &aPrefix);
+    Error RemoveHasRoutePrefix(const Ip6::Prefix &aPrefix) { return RemovePrefix(aPrefix); }
 #endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
 
 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
@@ -168,11 +179,12 @@
 
 private:
     void UpdateRloc(void);
+    bool IsConsistent(void) const;
+
 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
     Error AddPrefix(const Ip6::Prefix &aPrefix, NetworkDataTlv::Type aSubTlvType, uint16_t aFlags, bool aStable);
-    Error RemovePrefix(const Ip6::Prefix &aPrefix, NetworkDataTlv::Type aSubTlvType);
+    Error RemovePrefix(const Ip6::Prefix &aPrefix);
     void  UpdateRloc(PrefixTlv &aPrefixTlv);
-    bool  IsConsistent(void) const;
 #endif
 
 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
diff --git a/src/core/thread/network_data_publisher.cpp b/src/core/thread/network_data_publisher.cpp
index f31ccdc..5f46008 100644
--- a/src/core/thread/network_data_publisher.cpp
+++ b/src/core/thread/network_data_publisher.cpp
@@ -36,11 +36,12 @@
 
 #if OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE
 
+#include "common/array.hpp"
 #include "common/code_utils.hpp"
 #include "common/const_cast.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "common/random.hpp"
 #include "thread/network_data_local.hpp"
 #include "thread/network_data_service.hpp"
@@ -48,6 +49,8 @@
 namespace ot {
 namespace NetworkData {
 
+RegisterLogModule("NetDataPublshr");
+
 //---------------------------------------------------------------------------------------------------------------------
 // Publisher
 
@@ -184,7 +187,7 @@
 
 bool Publisher::IsAPrefixEntry(const Entry &aEntry) const
 {
-    return (&mPrefixEntries[0] <= &aEntry) && (&aEntry < OT_ARRAY_END(mPrefixEntries));
+    return (&mPrefixEntries[0] <= &aEntry) && (&aEntry < GetArrayEnd(mPrefixEntries));
 }
 
 void Publisher::NotifyPrefixEntryChange(Event aEvent, const Ip6::Prefix &aPrefix) const
@@ -262,8 +265,8 @@
 {
     VerifyOrExit(mState != aState);
 
-    otLogInfoNetData("Publisher: %s - State: %s -> %s", ToString(/* aIncludeState */ false).AsCString(),
-                     StateToString(mState), StateToString(aState));
+    LogInfo("%s - State: %s -> %s", ToString(/* aIncludeState */ false).AsCString(), StateToString(mState),
+            StateToString(aState));
     mState = aState;
 
 exit:
@@ -290,8 +293,8 @@
     // entries we aim to have in the Network Data to decide whether or
     // not to take any action (add or remove our entry).
 
-    otLogInfoNetData("Publisher: %s in netdata - total:%d, preferred:%d, desired:%d", ToString().AsCString(),
-                     aNumEntries, aNumPreferredEntries, aDesiredNumEntries);
+    LogInfo("%s in netdata - total:%d, preferred:%d, desired:%d", ToString().AsCString(), aNumEntries,
+            aNumPreferredEntries, aDesiredNumEntries);
 
     switch (GetState())
     {
@@ -479,7 +482,7 @@
 
 void Publisher::Entry::LogUpdateTime(void) const
 {
-    otLogInfoNetData("Publisher: %s - update in %u msec", ToString().AsCString(), mUpdateTime - TimerMilli::GetNow());
+    LogInfo("%s - update in %u msec", ToString().AsCString(), mUpdateTime - TimerMilli::GetNow());
 }
 
 const char *Publisher::Entry::StateToString(State aState)
@@ -521,20 +524,19 @@
 
 void Publisher::DnsSrpServiceEntry::PublishAnycast(uint8_t aSequenceNumber)
 {
-    otLogInfoNetData("Publisher: Publishing DNS/SRP service anycast (seq-num:%d)", aSequenceNumber);
+    LogInfo("Publishing DNS/SRP service anycast (seq-num:%d)", aSequenceNumber);
     Publish(Info::InfoAnycast(aSequenceNumber));
 }
 
 void Publisher::DnsSrpServiceEntry::PublishUnicast(const Ip6::Address &aAddress, uint16_t aPort)
 {
-    otLogInfoNetData("Publisher: Publishing DNS/SRP service unicast (%s, port:%d)", aAddress.ToString().AsCString(),
-                     aPort);
+    LogInfo("Publishing DNS/SRP service unicast (%s, port:%d)", aAddress.ToString().AsCString(), aPort);
     Publish(Info::InfoUnicast(kTypeUnicast, aAddress, aPort));
 }
 
 void Publisher::DnsSrpServiceEntry::PublishUnicast(uint16_t aPort)
 {
-    otLogInfoNetData("Publisher: Publishing DNS/SRP service unicast (ml-eid, port:%d)", aPort);
+    LogInfo("Publishing DNS/SRP service unicast (ml-eid, port:%d)", aPort);
     Publish(Info::InfoUnicast(kTypeUnicastMeshLocalEid, Get<Mle::Mle>().GetMeshLocal64(), aPort));
 }
 
@@ -546,7 +548,7 @@
     {
         if (aInfo == mInfo)
         {
-            otLogInfoNetData("Publisher: %s is already being published", ToString().AsCString());
+            LogInfo("%s is already being published", ToString().AsCString());
             ExitNow();
         }
 
@@ -571,7 +573,7 @@
 {
     bool registerWithLeader;
 
-    otLogInfoNetData("Publisher: Unpublishing DNS/SRP service");
+    LogInfo("Unpublishing DNS/SRP service");
 
     registerWithLeader = Remove(/* aNextState */ kNoEntry);
 
@@ -843,7 +845,7 @@
 
 void Publisher::PrefixEntry::Publish(const OnMeshPrefixConfig &aConfig)
 {
-    otLogInfoNetData("Publisher: Publishing OnMeshPrefix %s", aConfig.GetPrefix().ToString().AsCString());
+    LogInfo("Publishing OnMeshPrefix %s", aConfig.GetPrefix().ToString().AsCString());
 
     mType   = kTypeOnMeshPrefix;
     mPrefix = aConfig.GetPrefix();
@@ -856,7 +858,7 @@
 
 void Publisher::PrefixEntry::Publish(const ExternalRouteConfig &aConfig)
 {
-    otLogInfoNetData("Publisher: Publishing ExternalRoute %s", aConfig.GetPrefix().ToString().AsCString());
+    LogInfo("Publishing ExternalRoute %s", aConfig.GetPrefix().ToString().AsCString());
 
     mType   = kTypeExternalRoute;
     mPrefix = aConfig.GetPrefix();
@@ -871,7 +873,7 @@
 {
     bool registerWithLeader = false;
 
-    otLogInfoNetData("Publisher: Unpublishing %s", mPrefix.ToString().AsCString());
+    LogInfo("Unpublishing %s", mPrefix.ToString().AsCString());
 
     registerWithLeader = Remove(/* aNextState */ kNoEntry);
 
diff --git a/src/core/thread/network_data_service.cpp b/src/core/thread/network_data_service.cpp
index 878c027..618812b 100644
--- a/src/core/thread/network_data_service.cpp
+++ b/src/core/thread/network_data_service.cpp
@@ -195,20 +195,68 @@
 
 Error Manager::FindPreferredDnsSrpAnycastInfo(DnsSrpAnycast::Info &aInfo) const
 {
+    Error               error = kErrorNotFound;
     Iterator            iterator;
     DnsSrpAnycast::Info info;
-    bool                found = false;
+    DnsSrpAnycast::Info maxNumericalSeqNumInfo;
+
+    // Determine the entry with largest seq number in two ways:
+    // `aInfo` will track the largest using serial number arithmetic
+    // comparison, while `maxNumericalSeqNumInfo` tracks the largest
+    // using normal numerical comparison.
 
     while (GetNextDnsSrpAnycastInfo(iterator, info) == kErrorNone)
     {
-        if (!found || info.IsSequenceNumberAheadOf(aInfo))
+        if (error == kErrorNotFound)
+        {
+            aInfo                  = info;
+            maxNumericalSeqNumInfo = info;
+            error                  = kErrorNone;
+            continue;
+        }
+
+        if (SerialNumber::IsGreater(info.mSequenceNumber, aInfo.mSequenceNumber))
         {
             aInfo = info;
-            found = true;
+        }
+
+        if (info.mSequenceNumber > maxNumericalSeqNumInfo.mSequenceNumber)
+        {
+            maxNumericalSeqNumInfo = info;
         }
     }
 
-    return found ? kErrorNone : kErrorNotFound;
+    SuccessOrExit(error);
+
+    // Check that the largest seq number using serial number arithmetic is
+    // well-defined (i.e., the seq number is larger than all other seq
+    // numbers values). If it is not, we prefer `maxNumericalSeqNumInfo`.
+
+    iterator.Reset();
+
+    while (GetNextDnsSrpAnycastInfo(iterator, info) == kErrorNone)
+    {
+        constexpr uint8_t kMidValue = (NumericLimits<uint8_t>::kMax / 2) + 1;
+        uint8_t           seqNumber = info.mSequenceNumber;
+        uint8_t           diff;
+
+        if (seqNumber == aInfo.mSequenceNumber)
+        {
+            continue;
+        }
+
+        diff = seqNumber - aInfo.mSequenceNumber;
+
+        if ((diff == kMidValue) || !SerialNumber::IsGreater(aInfo.mSequenceNumber, seqNumber))
+        {
+            aInfo = maxNumericalSeqNumInfo;
+
+            break;
+        }
+    }
+
+exit:
+    return error;
 }
 
 Error Manager::GetNextDnsSrpUnicastInfo(Iterator &aIterator, DnsSrpUnicast::Info &aInfo) const
@@ -239,6 +287,7 @@
 
                 aInfo.mSockAddr.SetAddress(serverData->GetAddress());
                 aInfo.mSockAddr.SetPort(serverData->GetPort());
+                aInfo.mOrigin = DnsSrpUnicast::kFromServerData;
                 ExitNow();
             }
 
@@ -250,6 +299,7 @@
                 aInfo.mSockAddr.GetAddress().SetToRoutingLocator(Get<Mle::Mle>().GetMeshLocalPrefix(),
                                                                  aIterator.mServerSubTlv->GetServer16());
                 aInfo.mSockAddr.SetPort(Encoding::BigEndian::ReadUint16(data.GetBytes()));
+                aInfo.mOrigin = DnsSrpUnicast::kFromServerData;
                 ExitNow();
             }
         }
@@ -271,6 +321,7 @@
             dnsServiceData = reinterpret_cast<const DnsSrpUnicast::ServiceData *>(serviceData.GetBytes());
             aInfo.mSockAddr.SetAddress(dnsServiceData->GetAddress());
             aInfo.mSockAddr.SetPort(dnsServiceData->GetPort());
+            aInfo.mOrigin = DnsSrpUnicast::kFromServiceData;
             ExitNow();
         }
 
diff --git a/src/core/thread/network_data_service.hpp b/src/core/thread/network_data_service.hpp
index 8c0c060..bd41dc7 100644
--- a/src/core/thread/network_data_service.hpp
+++ b/src/core/thread/network_data_service.hpp
@@ -174,25 +174,6 @@
      */
     struct Info
     {
-        /**
-         * This method indicates whether or not the sequence number from the current `Info` is ahead of (more recent
-         * than) the sequence number from another `Info`.
-         *
-         * The sequence numbers comparison follows the Serial Number Arithmetic logic from RFC-1982. It is semantically
-         * equivalent to `GetSequenceNumber() > aOther.GetSequenceNumber()` while handling roll-over of the `uint8_t`
-         * values.
-         *
-         * @param[in] aOther   The other `Info` to compare with.
-         *
-         * @retval TRUE  The `Info` is ahead of @p aOther.
-         * @retval FALSE The `Info` is not ahead of @p aOther.
-         *
-         */
-        bool IsSequenceNumberAheadOf(const Info &aOther) const
-        {
-            return SerialNumber::IsGreater(mSequenceNumber, aOther.mSequenceNumber);
-        }
-
         Ip6::Address mAnycastAddress; ///< The anycast address associated with the DNS/SRP servers.
         uint8_t      mSequenceNumber; ///< Sequence number used to notify SRP client if they need to re-register.
     };
@@ -260,12 +241,23 @@
     static const uint8_t kServiceData = kServiceNumber;
 
     /**
+     * This enumeration represents the origin a `DnsSrpUnicast` entry.
+     *
+     */
+    enum Origin : uint8_t
+    {
+        kFromServiceData, ///< Socket address is from service data.
+        kFromServerData,  ///< Socket address is from server data.
+    };
+
+    /**
      * This structure represents information about an DNS/SRP server parsed from related Network Data service entries.
      *
      */
     struct Info
     {
         Ip6::SockAddr mSockAddr; ///< The socket address (IPv6 address and port) of the DNS/SRP server.
+        Origin        mOrigin;   ///< The origin of the socket address (whether from service or server data).
     };
 
     /**
@@ -560,8 +552,8 @@
      * To get the first entry, @p aIterator should be cleared (e.g., a new instance of `Iterator` or calling `Clear()`
      * method).
      *
-     * @param[inout] aIterator     A reference to an iterator.
-     * @param[out]   aInfo         A reference to `DnsSrpAnycast::Info` to return the info.
+     * @param[in,out] aIterator    A reference to an iterator.
+     * @param[out]    aInfo        A reference to `DnsSrpAnycast::Info` to return the info.
      *
      * @retval kErrorNone       Successfully got the next info. @p aInfo and @p aIterator are updated.
      * @retval kErrorNotFound   No more matching entries in the Network Data.
@@ -590,8 +582,8 @@
      * To get the first entry @p aIterator should be cleared (e.g., a new instance of `Iterator` or calling `Clear()`
      * method).
      *
-     * @param[inout] aIterator     A reference to an iterator.
-     * @param[out]   aInfo         A reference to `DnsSrpUnicast::Info` to return the info.
+     * @param[in,out] aIterator    A reference to an iterator.
+     * @param[out]    aInfo        A reference to `DnsSrpUnicast::Info` to return the info.
      *
      * @retval kErrorNone       Successfully got the next info. @p aInfo and @p aIterator are updated.
      * @retval kErrorNotFound   No more matching entries in the Network Data.
diff --git a/src/core/thread/network_data_tlvs.hpp b/src/core/thread/network_data_tlvs.hpp
index 2fd372e..48568c7 100644
--- a/src/core/thread/network_data_tlvs.hpp
+++ b/src/core/thread/network_data_tlvs.hpp
@@ -1214,17 +1214,17 @@
     /**
      * This method initializes the Context TLV.
      *
-     * @param[in]  aConextId   The Context ID value.
-     * @param[in]  aLength     The Context Length value.
+     * @param[in]  aContextId      The Context ID value.
+     * @param[in]  aContextLength  The Context Length value.
      *
      */
-    void Init(uint8_t aContextId, uint8_t aConextLength)
+    void Init(uint8_t aContextId, uint8_t aContextLength)
     {
         NetworkDataTlv::Init();
         SetType(kTypeContext);
         SetLength(sizeof(ContextTlv) - sizeof(NetworkDataTlv));
         mFlags         = ((aContextId << kContextIdOffset) & kContextIdMask);
-        mContextLength = aConextLength;
+        mContextLength = aContextLength;
     }
 
     /**
diff --git a/src/core/thread/network_data_types.hpp b/src/core/thread/network_data_types.hpp
index 4ca8b56..06ff0a9 100644
--- a/src/core/thread/network_data_types.hpp
+++ b/src/core/thread/network_data_types.hpp
@@ -90,6 +90,18 @@
 };
 
 /**
+ * This enumeration represents the border router RLOC role filter used when searching for border routers in the Network
+ * Data.
+ *
+ */
+enum RoleFilter : uint8_t
+{
+    kAnyRole,        ///< Include devices in any role.
+    kRouterRoleOnly, ///< Include devices that act as Thread router.
+    kChildRoleOnly,  ///< Include devices that act as Thread child (end-device).
+};
+
+/**
  * This function indicates whether a given `int8_t` preference value is a valid route preference (i.e., one of the
  * values from `RoutePreference` enumeration).
  *
@@ -157,6 +169,7 @@
                            public Equatable<OnMeshPrefixConfig>
 {
     friend class NetworkData;
+    friend class Leader;
     friend class Local;
     friend class Publisher;
 
diff --git a/src/core/thread/network_diagnostic.cpp b/src/core/thread/network_diagnostic.cpp
index e4a6c6a..738e904 100644
--- a/src/core/thread/network_diagnostic.cpp
+++ b/src/core/thread/network_diagnostic.cpp
@@ -36,13 +36,14 @@
 #if OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE
 
 #include "coap/coap_message.hpp"
+#include "common/array.hpp"
 #include "common/as_core_type.hpp"
 #include "common/code_utils.hpp"
 #include "common/debug.hpp"
 #include "common/encoding.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "mac/mac.hpp"
 #include "net/netif.hpp"
 #include "thread/mesh_forwarder.hpp"
@@ -53,6 +54,8 @@
 
 namespace ot {
 
+RegisterLogModule("NetDiag");
+
 namespace NetworkDiagnostic {
 
 NetworkDiagnostic::NetworkDiagnostic(Instance &aInstance)
@@ -78,26 +81,21 @@
 {
     Error                 error;
     Coap::Message *       message = nullptr;
-    Ip6::MessageInfo      messageInfo;
+    Tmf::MessageInfo      messageInfo(GetInstance());
     otCoapResponseHandler handler = nullptr;
 
-    VerifyOrExit((message = Get<Tmf::Agent>().NewMessage()) != nullptr, error = kErrorNoBufs);
-
     if (aDestination.IsMulticast())
     {
-        SuccessOrExit(error = message->InitAsNonConfirmablePost(UriPath::kDiagnosticGetQuery));
+        message = Get<Tmf::Agent>().NewNonConfirmablePostMessage(UriPath::kDiagnosticGetQuery);
         messageInfo.SetMulticastLoop(true);
     }
     else
     {
         handler = &NetworkDiagnostic::HandleDiagnosticGetResponse;
-        SuccessOrExit(error = message->InitAsConfirmablePost(UriPath::kDiagnosticGetRequest));
+        message = Get<Tmf::Agent>().NewConfirmablePostMessage(UriPath::kDiagnosticGetRequest);
     }
 
-    if (aCount > 0)
-    {
-        SuccessOrExit(error = message->SetPayloadMarker());
-    }
+    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
     if (aCount > 0)
     {
@@ -110,18 +108,17 @@
     }
     else
     {
-        messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetMeshLocal16());
+        messageInfo.SetSockAddrToRloc();
     }
 
     messageInfo.SetPeerAddr(aDestination);
-    messageInfo.SetPeerPort(Tmf::kUdpPort);
 
     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo, handler, this));
 
     mReceiveDiagnosticGetCallback        = aCallback;
     mReceiveDiagnosticGetCallbackContext = aCallbackContext;
 
-    otLogInfoNetDiag("Sent diagnostic get");
+    LogInfo("Sent diagnostic get");
 
 exit:
     FreeMessageOnError(message, error);
@@ -151,7 +148,7 @@
     }
     else
     {
-        otLogDebgNetDiag("Received diagnostic get response, error = %s", ErrorToString(aResult));
+        LogDebg("Received diagnostic get response, error = %s", ErrorToString(aResult));
     }
     return;
 }
@@ -168,7 +165,7 @@
 {
     VerifyOrExit(aMessage.IsConfirmablePostRequest());
 
-    otLogInfoNetDiag("Diagnostic get answer received");
+    LogInfo("Diagnostic get answer received");
 
     if (mReceiveDiagnosticGetCallback)
     {
@@ -177,7 +174,7 @@
 
     SuccessOrExit(Get<Tmf::Agent>().SendEmptyAck(aMessage, aMessageInfo));
 
-    otLogInfoNetDiag("Sent diagnostic answer acknowledgment");
+    LogInfo("Sent diagnostic answer acknowledgment");
 
 exit:
     return;
@@ -294,7 +291,7 @@
     {
         SuccessOrExit(error = aRequest.Read(offset, type));
 
-        otLogInfoNetDiag("Type %d", type);
+        LogInfo("Type %d", type);
 
         switch (type)
         {
@@ -464,11 +461,11 @@
     Error                error   = kErrorNone;
     Coap::Message *      message = nullptr;
     NetworkDiagnosticTlv networkDiagnosticTlv;
-    Ip6::MessageInfo     messageInfo;
+    Tmf::MessageInfo     messageInfo(GetInstance());
 
     VerifyOrExit(aMessage.IsPostRequest(), error = kErrorDrop);
 
-    otLogInfoNetDiag("Received diagnostic get query");
+    LogInfo("Received diagnostic get query");
 
     SuccessOrExit(error = aMessage.Read(aMessage.GetOffset(), networkDiagnosticTlv));
 
@@ -479,36 +476,29 @@
     {
         if (Get<Tmf::Agent>().SendEmptyAck(aMessage, aMessageInfo) == kErrorNone)
         {
-            otLogInfoNetDiag("Sent diagnostic get query acknowledgment");
+            LogInfo("Sent diagnostic get query acknowledgment");
         }
     }
 
-    VerifyOrExit((message = Get<Tmf::Agent>().NewMessage()) != nullptr, error = kErrorNoBufs);
+    message = Get<Tmf::Agent>().NewConfirmablePostMessage(UriPath::kDiagnosticGetAnswer);
+    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
-    SuccessOrExit(error = message->InitAsConfirmablePost(UriPath::kDiagnosticGetAnswer));
-
-    if (networkDiagnosticTlv.GetLength() > 0)
-    {
-        SuccessOrExit(error = message->SetPayloadMarker());
-    }
-
-    if (aMessageInfo.GetSockAddr().IsLinkLocal() || aMessageInfo.GetSockAddr().IsLinkLocalMulticast())
+    if (aMessageInfo.GetPeerAddr().IsLinkLocal())
     {
         messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetLinkLocalAddress());
     }
     else
     {
-        messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetMeshLocal16());
+        messageInfo.SetSockAddrToRloc();
     }
 
     messageInfo.SetPeerAddr(aMessageInfo.GetPeerAddr());
-    messageInfo.SetPeerPort(Tmf::kUdpPort);
 
     SuccessOrExit(error = FillRequestedTlvs(aMessage, *message, networkDiagnosticTlv));
 
     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo, nullptr, this));
 
-    otLogInfoNetDiag("Sent diagnostic get answer");
+    LogInfo("Sent diagnostic get answer");
 
 exit:
     FreeMessageOnError(message, error);
@@ -531,22 +521,20 @@
 
     VerifyOrExit(aMessage.IsConfirmablePostRequest(), error = kErrorDrop);
 
-    otLogInfoNetDiag("Received diagnostic get request");
+    LogInfo("Received diagnostic get request");
 
     SuccessOrExit(error = aMessage.Read(aMessage.GetOffset(), networkDiagnosticTlv));
 
     VerifyOrExit(networkDiagnosticTlv.GetType() == NetworkDiagnosticTlv::kTypeList, error = kErrorParse);
 
-    VerifyOrExit((message = Get<Tmf::Agent>().NewMessage()) != nullptr, error = kErrorNoBufs);
-
-    SuccessOrExit(error = message->SetDefaultResponseHeader(aMessage));
-    SuccessOrExit(error = message->SetPayloadMarker());
+    message = Get<Tmf::Agent>().NewResponseMessage(aMessage);
+    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
     SuccessOrExit(error = FillRequestedTlvs(aMessage, *message, networkDiagnosticTlv));
 
     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo));
 
-    otLogInfoNetDiag("Sent diagnostic get response");
+    LogInfo("Sent diagnostic get response");
 
 exit:
     FreeMessageOnError(message, error);
@@ -558,16 +546,10 @@
 {
     Error            error;
     Coap::Message *  message = nullptr;
-    Ip6::MessageInfo messageInfo;
+    Tmf::MessageInfo messageInfo(GetInstance());
 
-    VerifyOrExit((message = Get<Tmf::Agent>().NewMessage()) != nullptr, error = kErrorNoBufs);
-
-    SuccessOrExit(error = message->InitAsConfirmablePost(UriPath::kDiagnosticReset));
-
-    if (aCount > 0)
-    {
-        SuccessOrExit(error = message->SetPayloadMarker());
-    }
+    message = Get<Tmf::Agent>().NewConfirmablePostMessage(UriPath::kDiagnosticReset);
+    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
     if (aCount > 0)
     {
@@ -580,15 +562,14 @@
     }
     else
     {
-        messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetMeshLocal16());
+        messageInfo.SetSockAddrToRloc();
     }
 
     messageInfo.SetPeerAddr(aDestination);
-    messageInfo.SetPeerPort(Tmf::kUdpPort);
 
     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo));
 
-    otLogInfoNetDiag("Sent network diagnostic reset");
+    LogInfo("Sent network diagnostic reset");
 
 exit:
     FreeMessageOnError(message, error);
@@ -607,7 +588,7 @@
     uint8_t              type;
     NetworkDiagnosticTlv tlv;
 
-    otLogInfoNetDiag("Received diagnostic reset request");
+    LogInfo("Received diagnostic reset request");
 
     VerifyOrExit(aMessage.IsConfirmablePostRequest());
 
@@ -625,18 +606,18 @@
         {
         case NetworkDiagnosticTlv::kMacCounters:
             Get<Mac::Mac>().ResetCounters();
-            otLogInfoNetDiag("Received diagnostic reset type kMacCounters(9)");
+            LogInfo("Received diagnostic reset type kMacCounters(9)");
             break;
 
         default:
-            otLogInfoNetDiag("Received diagnostic reset other type %d not resetable", type);
+            LogInfo("Received diagnostic reset other type %d not resetable", type);
             break;
         }
     }
 
     SuccessOrExit(Get<Tmf::Agent>().SendEmptyAck(aMessage, aMessageInfo));
 
-    otLogInfoNetDiag("Sent diagnostic reset acknowledgment");
+    LogInfo("Sent diagnostic reset acknowledgment");
 
 exit:
     return;
@@ -837,7 +818,7 @@
             ChildTableTlv &childTable = As<ChildTableTlv>(tlv);
 
             VerifyOrExit(childTable.IsValid(), error = kErrorParse);
-            VerifyOrExit(childTable.GetNumEntries() <= OT_ARRAY_LENGTH(aNetworkDiagTlv.mData.mChildTable.mTable),
+            VerifyOrExit(childTable.GetNumEntries() <= GetArrayLength(aNetworkDiagTlv.mData.mChildTable.mTable),
                          error = kErrorParse);
 
             for (uint8_t i = 0; i < childTable.GetNumEntries(); ++i)
diff --git a/src/core/thread/network_diagnostic.hpp b/src/core/thread/network_diagnostic.hpp
index a475a09..ade893f 100644
--- a/src/core/thread/network_diagnostic.hpp
+++ b/src/core/thread/network_diagnostic.hpp
@@ -111,9 +111,10 @@
     /**
      * This static method gets the next Network Diagnostic TLV in a given message.
      *
-     * @param[in]     aMessage         A message.
-     * @param[inout]  aIterator        The Network Diagnostic iterator. To get the first TLV set it to `kIteratorInit`.
-     * @param[out]    aNetworkDiagTlv  A reference to a Network Diagnostic TLV to output the next TLV.
+     * @param[in]      aMessage         A message.
+     * @param[in,out]  aIterator        The Network Diagnostic iterator. To get the first TLV set it to
+     *                                  `kIteratorInit`.
+     * @param[out]     aNetworkDiagTlv  A reference to a Network Diagnostic TLV to output the next TLV.
      *
      * @retval kErrorNone       Successfully found the next Network Diagnostic TLV.
      * @retval kErrorNotFound   No subsequent Network Diagnostic TLV exists in the message.
diff --git a/src/core/thread/network_diagnostic_tlvs.hpp b/src/core/thread/network_diagnostic_tlvs.hpp
index f3b565a..8352634 100644
--- a/src/core/thread/network_diagnostic_tlvs.hpp
+++ b/src/core/thread/network_diagnostic_tlvs.hpp
@@ -1033,7 +1033,7 @@
     static constexpr uint8_t  kReservedOffset = 9;
     static constexpr uint16_t kTimeoutMask    = 0xf800;
     static constexpr uint16_t kReservedMask   = 0x0600;
-    static constexpr uint16_t kChildIdMask    = 0x1f;
+    static constexpr uint16_t kChildIdMask    = 0x1ff;
 
     uint16_t mTimeoutRsvChildId;
     uint8_t  mMode;
diff --git a/src/core/thread/panid_query_server.cpp b/src/core/thread/panid_query_server.cpp
index 6a31b0a..9c57e89 100644
--- a/src/core/thread/panid_query_server.cpp
+++ b/src/core/thread/panid_query_server.cpp
@@ -39,7 +39,7 @@
 #include "common/debug.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "meshcop/meshcop.hpp"
 #include "meshcop/meshcop_tlvs.hpp"
 #include "thread/thread_netif.hpp"
@@ -47,6 +47,8 @@
 
 namespace ot {
 
+RegisterLogModule("MeshCoP");
+
 PanIdQueryServer::PanIdQueryServer(Instance &aInstance)
     : InstanceLocator(aInstance)
     , mChannelMask(0)
@@ -81,7 +83,7 @@
     if (aMessage.IsConfirmable() && !aMessageInfo.GetSockAddr().IsMulticast())
     {
         SuccessOrExit(Get<Tmf::Agent>().SendEmptyAck(aMessage, responseInfo));
-        otLogInfoMeshCoP("sent panid query response");
+        LogInfo("sent panid query response");
     }
 
 exit:
@@ -112,13 +114,11 @@
 {
     Error                   error = kErrorNone;
     MeshCoP::ChannelMaskTlv channelMask;
-    Ip6::MessageInfo        messageInfo;
+    Tmf::MessageInfo        messageInfo(GetInstance());
     Coap::Message *         message;
 
-    VerifyOrExit((message = Get<Tmf::Agent>().NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
-
-    SuccessOrExit(error = message->InitAsConfirmablePost(UriPath::kPanIdConflict));
-    SuccessOrExit(error = message->SetPayloadMarker());
+    message = Get<Tmf::Agent>().NewPriorityConfirmablePostMessage(UriPath::kPanIdConflict);
+    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
     channelMask.Init();
     channelMask.SetChannelMask(mChannelMask);
@@ -126,12 +126,11 @@
 
     SuccessOrExit(error = Tlv::Append<MeshCoP::PanIdTlv>(*message, mPanId));
 
-    messageInfo.SetSockAddr(Get<Mle::MleRouter>().GetMeshLocal16());
-    messageInfo.SetPeerAddr(mCommissioner);
-    messageInfo.SetPeerPort(Tmf::kUdpPort);
+    messageInfo.SetSockAddrToRlocPeerAddrTo(mCommissioner);
+
     SuccessOrExit(error = Get<Tmf::Agent>().SendMessage(*message, messageInfo));
 
-    otLogInfoMeshCoP("sent panid conflict");
+    LogInfo("sent panid conflict");
 
 exit:
     FreeMessageOnError(message, error);
diff --git a/src/core/thread/radio_selector.cpp b/src/core/thread/radio_selector.cpp
index 74ea6bc..cb619a3 100644
--- a/src/core/thread/radio_selector.cpp
+++ b/src/core/thread/radio_selector.cpp
@@ -38,11 +38,13 @@
 #include "common/code_utils.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "common/random.hpp"
 
 namespace ot {
 
+RegisterLogModule("RadioSelector");
+
 // This array defines the order in which different radio link types are
 // selected for message tx (direct message).
 const Mac::RadioType RadioSelector::sRadioSelectionOrder[Mac::kNumRadioTypes] = {
@@ -80,7 +82,7 @@
 #endif
 }
 
-otLogLevel RadioSelector::UpdatePreference(Neighbor &aNeighbor, Mac::RadioType aRadioType, int16_t aDifference)
+LogLevel RadioSelector::UpdatePreference(Neighbor &aNeighbor, Mac::RadioType aRadioType, int16_t aDifference)
 {
     uint8_t old        = aNeighbor.GetRadioPreference(aRadioType);
     int16_t preferecne = static_cast<int16_t>(old);
@@ -104,12 +106,12 @@
     // return a suggested log level. If there is cross, suggest info
     // log level, otherwise debug log level.
 
-    return ((old >= kHighPreference) != (preferecne >= kHighPreference)) ? OT_LOG_LEVEL_INFO : OT_LOG_LEVEL_DEBG;
+    return ((old >= kHighPreference) != (preferecne >= kHighPreference)) ? kLogLevelInfo : kLogLevelDebg;
 }
 
 void RadioSelector::UpdateOnReceive(Neighbor &aNeighbor, Mac::RadioType aRadioType, bool aIsDuplicate)
 {
-    otLogLevel logLevel = OT_LOG_LEVEL_INFO;
+    LogLevel logLevel = kLogLevelInfo;
 
     if (aNeighbor.GetSupportedRadioTypes().Contains(aRadioType))
     {
@@ -129,7 +131,7 @@
 
 void RadioSelector::UpdateOnSendDone(Mac::TxFrame &aFrame, Error aTxError)
 {
-    otLogLevel     logLevel  = OT_LOG_LEVEL_INFO;
+    LogLevel       logLevel  = kLogLevelInfo;
     Mac::RadioType radioType = aFrame.GetRadioType();
     Mac::Address   macDest;
     Neighbor *     neighbor;
@@ -173,7 +175,7 @@
 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
 void RadioSelector::UpdateOnDeferredAck(Neighbor &aNeighbor, Error aTxError, bool &aAllowNeighborRemove)
 {
-    otLogLevel logLevel = OT_LOG_LEVEL_INFO;
+    LogLevel logLevel = kLogLevelInfo;
 
     aAllowNeighborRemove = true;
 
@@ -292,7 +294,7 @@
     selectedRadio = Select(neighbor->GetSupportedRadioTypes(), *neighbor);
     selections.Add(selectedRadio);
 
-    Log(OT_LOG_LEVEL_DEBG, "SelectRadio", selectedRadio, *neighbor);
+    Log(kLogLevelDebg, "SelectRadio", selectedRadio, *neighbor);
 
     aMessage.SetRadioType(selectedRadio);
 
@@ -316,7 +318,7 @@
         aTxFrames.SetRequiredRadioTypes(selections);
         selections.Add(Mac::kRadioTypeTrel);
 
-        Log(OT_LOG_LEVEL_DEBG, "Probe", Mac::kRadioTypeTrel, *neighbor);
+        Log(kLogLevelDebg, "Probe", Mac::kRadioTypeTrel, *neighbor);
     }
 #endif
 
@@ -353,9 +355,9 @@
 
 // LCOV_EXCL_START
 
-#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_MAC == 1)
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
 
-void RadioSelector::Log(otLogLevel      aLogLevel,
+void RadioSelector::Log(LogLevel        aLogLevel,
                         const char *    aActionText,
                         Mac::RadioType  aRadioType,
                         const Neighbor &aNeighbor)
@@ -363,7 +365,7 @@
     String<kRadioPreferenceStringSize> preferenceString;
     bool                               isFirstEntry = true;
 
-    VerifyOrExit(otLoggingGetLevel() >= aLogLevel);
+    VerifyOrExit(Instance::GetLogLevel() >= aLogLevel);
 
     for (Mac::RadioType radio : sRadioSelectionOrder)
     {
@@ -375,21 +377,21 @@
         }
     }
 
-    otLogMac(aLogLevel, "RadioSelector: %s %s - neighbor:[%s rloc16:0x%04x radio-pref:{%s} state:%s]", aActionText,
-             RadioTypeToString(aRadioType), aNeighbor.GetExtAddress().ToString().AsCString(), aNeighbor.GetRloc16(),
-             preferenceString.AsCString(), Neighbor::StateToString(aNeighbor.GetState()));
+    LogAt(aLogLevel, "RadioSelector: %s %s - neighbor:[%s rloc16:0x%04x radio-pref:{%s} state:%s]", aActionText,
+          RadioTypeToString(aRadioType), aNeighbor.GetExtAddress().ToString().AsCString(), aNeighbor.GetRloc16(),
+          preferenceString.AsCString(), Neighbor::StateToString(aNeighbor.GetState()));
 
 exit:
     return;
 }
 
-#else // #if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_MAC == 1)
+#else // #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
 
-void RadioSelector::Log(otLogLevel, const char *, Mac::RadioType, const Neighbor &)
+void RadioSelector::Log(LogLevel, const char *, Mac::RadioType, const Neighbor &)
 {
 }
 
-#endif // #if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_MAC == 1)
+#endif // #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
 
 // LCOV_EXCL_STOP
 
diff --git a/src/core/thread/radio_selector.hpp b/src/core/thread/radio_selector.hpp
index c86f1c1..bf980cc 100644
--- a/src/core/thread/radio_selector.hpp
+++ b/src/core/thread/radio_selector.hpp
@@ -41,6 +41,7 @@
 #include <openthread/multi_radio.h>
 
 #include "common/locator.hpp"
+#include "common/log.hpp"
 #include "common/message.hpp"
 #include "mac/mac_frame.hpp"
 #include "mac/mac_links.hpp"
@@ -172,9 +173,9 @@
      * The `aMessage` will be updated to store the selected radio type (please see `Message::GetRadioType()`).
      * The `aTxFrames` will also be updated to indicate which radio links are to be used.
      *
-     * @param[inout] aMessage   The message to send.
-     * @param[in]    aMacDest   The MAC destination address.
-     * @param[inout] aTxFrames  The set of TxFrames for all radio links.
+     * @param[in,out] aMessage   The message to send.
+     * @param[in]     aMacDest   The MAC destination address.
+     * @param[in,out] aTxFrames  The set of TxFrames for all radio links.
      *
      * @returns  A reference to `mTxFrame` to use when preparing the frame for tx.
      *
@@ -198,9 +199,9 @@
 
     static constexpr uint16_t kRadioPreferenceStringSize = 75;
 
-    otLogLevel     UpdatePreference(Neighbor &aNeighbor, Mac::RadioType aRadioType, int16_t aDifference);
+    LogLevel       UpdatePreference(Neighbor &aNeighbor, Mac::RadioType aRadioType, int16_t aDifference);
     Mac::RadioType Select(Mac::RadioTypes aRadioOptions, const Neighbor &aNeighbor);
-    void           Log(otLogLevel aLogLevel, const char *aActionText, Mac::RadioType aType, const Neighbor &aNeighbor);
+    void           Log(LogLevel aLogLevel, const char *aActionText, Mac::RadioType aType, const Neighbor &aNeighbor);
 
     static const Mac::RadioType sRadioSelectionOrder[Mac::kNumRadioTypes];
 };
diff --git a/src/core/thread/router_table.cpp b/src/core/thread/router_table.cpp
index 7b81017..db90327 100644
--- a/src/core/thread/router_table.cpp
+++ b/src/core/thread/router_table.cpp
@@ -33,7 +33,7 @@
 #include "common/code_utils.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "common/timer.hpp"
 #include "thread/mle.hpp"
 #include "thread/mle_router.hpp"
@@ -42,6 +42,8 @@
 
 namespace ot {
 
+RegisterLogModule("RouterTable");
+
 RouterTable::Iterator::Iterator(Instance &aInstance)
     : InstanceLocator(aInstance)
     , ItemPtrIterator(Get<RouterTable>().GetFirstEntry())
@@ -268,7 +270,7 @@
     mRouterIdSequenceLastUpdated = TimerMilli::GetNow();
     Get<Mle::MleRouter>().ResetAdvertiseInterval();
 
-    otLogNoteMle("Allocate router id %d", aRouterId);
+    LogNote("Allocate router id %d", aRouterId);
 
 exit:
     return rval;
@@ -313,7 +315,7 @@
     Get<NetworkData::Leader>().RemoveBorderRouter(rloc16, NetworkData::Leader::kMatchModeRouterId);
     Get<Mle::MleRouter>().ResetAdvertiseInterval();
 
-    otLogNoteMle("Release router id %d", aRouterId);
+    LogNote("Release router id %d", aRouterId);
 
 exit:
     return error;
@@ -321,7 +323,7 @@
 
 void RouterTable::RemoveRouterLink(Router &aRouter)
 {
-    aRouter.SetLinkQualityOut(0);
+    aRouter.SetLinkQualityOut(kLinkQuality0);
     aRouter.SetLastHeard(TimerMilli::GetNow());
 
     for (Router *cur = GetFirstEntry(); cur != nullptr; cur = GetNextEntry(cur))
diff --git a/src/core/thread/src_match_controller.cpp b/src/core/thread/src_match_controller.cpp
index 810118b..371405f 100644
--- a/src/core/thread/src_match_controller.cpp
+++ b/src/core/thread/src_match_controller.cpp
@@ -39,7 +39,7 @@
 #include "common/debug.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "mac/mac_types.hpp"
 #include "radio/radio.hpp"
 #include "thread/mesh_forwarder.hpp"
@@ -48,6 +48,8 @@
 
 namespace ot {
 
+RegisterLogModule("SrcMatchCtrl");
+
 SourceMatchController::SourceMatchController(Instance &aInstance)
     : InstanceLocator(aInstance)
     , mEnabled(false)
@@ -69,7 +71,7 @@
 {
     if (aChild.GetIndirectMessageCount() == 0)
     {
-        otLogWarnMac("DecrementMessageCount(child 0x%04x) called when already at zero count.", aChild.GetRloc16());
+        LogWarn("DecrementMessageCount(child 0x%04x) called when already at zero count.", aChild.GetRloc16());
         ExitNow();
     }
 
@@ -113,14 +115,14 @@
 {
     Get<Radio>().ClearSrcMatchShortEntries();
     Get<Radio>().ClearSrcMatchExtEntries();
-    otLogDebgMac("SrcAddrMatch - Cleared all entries");
+    LogDebg("Cleared all entries");
 }
 
 void SourceMatchController::Enable(bool aEnable)
 {
     mEnabled = aEnable;
     Get<Radio>().EnableSrcMatch(mEnabled);
-    otLogDebgMac("SrcAddrMatch - %sabling", mEnabled ? "En" : "Dis");
+    LogDebg("%sabling", mEnabled ? "En" : "Dis");
 }
 
 void SourceMatchController::AddEntry(Child &aChild)
@@ -150,8 +152,7 @@
     {
         error = Get<Radio>().AddSrcMatchShortEntry(aChild.GetRloc16());
 
-        otLogDebgMac("SrcAddrMatch - Adding short addr: 0x%04x -- %s (%d)", aChild.GetRloc16(), ErrorToString(error),
-                     error);
+        LogDebg("Adding short addr: 0x%04x -- %s (%d)", aChild.GetRloc16(), ErrorToString(error), error);
     }
     else
     {
@@ -160,8 +161,8 @@
         address.Set(aChild.GetExtAddress().m8, Mac::ExtAddress::kReverseByteOrder);
         error = Get<Radio>().AddSrcMatchExtEntry(address);
 
-        otLogDebgMac("SrcAddrMatch - Adding addr: %s -- %s (%d)", aChild.GetExtAddress().ToString().AsCString(),
-                     ErrorToString(error), error);
+        LogDebg("Adding addr: %s -- %s (%d)", aChild.GetExtAddress().ToString().AsCString(), ErrorToString(error),
+                error);
     }
 
     return error;
@@ -173,7 +174,7 @@
 
     if (aChild.IsIndirectSourceMatchPending())
     {
-        otLogDebgMac("SrcAddrMatch - Clearing pending flag for 0x%04x", aChild.GetRloc16());
+        LogDebg("Clearing pending flag for 0x%04x", aChild.GetRloc16());
         aChild.SetIndirectSourceMatchPending(false);
         ExitNow();
     }
@@ -182,8 +183,7 @@
     {
         error = Get<Radio>().ClearSrcMatchShortEntry(aChild.GetRloc16());
 
-        otLogDebgMac("SrcAddrMatch - Clearing short addr: 0x%04x -- %s (%d)", aChild.GetRloc16(), ErrorToString(error),
-                     error);
+        LogDebg("Clearing short addr: 0x%04x -- %s (%d)", aChild.GetRloc16(), ErrorToString(error), error);
     }
     else
     {
@@ -192,8 +192,8 @@
         address.Set(aChild.GetExtAddress().m8, Mac::ExtAddress::kReverseByteOrder);
         error = Get<Radio>().ClearSrcMatchExtEntry(address);
 
-        otLogDebgMac("SrcAddrMatch - Clearing addr: %s -- %s (%d)", aChild.GetExtAddress().ToString().AsCString(),
-                     ErrorToString(error), error);
+        LogDebg("Clearing addr: %s -- %s (%d)", aChild.GetExtAddress().ToString().AsCString(), ErrorToString(error),
+                error);
     }
 
     SuccessOrExit(error);
diff --git a/src/core/thread/thread_netif.cpp b/src/core/thread/thread_netif.cpp
index f36469a..7cc880c 100644
--- a/src/core/thread/thread_netif.cpp
+++ b/src/core/thread/thread_netif.cpp
@@ -82,6 +82,8 @@
 #endif
     , mActiveDataset(aInstance)
     , mPendingDataset(aInstance)
+    , mExtendedPanIdManager(aInstance)
+    , mNetworkNameManager(aInstance)
     , mIp6Filter(aInstance)
     , mKeyManager(aInstance)
     , mLowpan(aInstance)
diff --git a/src/core/thread/thread_netif.hpp b/src/core/thread/thread_netif.hpp
index 77496b4..97efc33 100644
--- a/src/core/thread/thread_netif.hpp
+++ b/src/core/thread/thread_netif.hpp
@@ -45,9 +45,11 @@
 #include "meshcop/border_agent.hpp"
 #include "meshcop/commissioner.hpp"
 #include "meshcop/dataset_manager.hpp"
+#include "meshcop/extended_panid.hpp"
 #include "meshcop/joiner.hpp"
 #include "meshcop/joiner_router.hpp"
 #include "meshcop/meshcop_leader.hpp"
+#include "meshcop/network_name.hpp"
 #include "net/dhcp6.hpp"
 #include "net/dhcp6_client.hpp"
 #include "net/dhcp6_server.hpp"
@@ -197,15 +199,17 @@
 #if OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE
     Sntp::Client mSntpClient;
 #endif
-    MeshCoP::ActiveDataset  mActiveDataset;
-    MeshCoP::PendingDataset mPendingDataset;
-    Ip6::Filter             mIp6Filter;
-    KeyManager              mKeyManager;
-    Lowpan::Lowpan          mLowpan;
-    Mac::Mac                mMac;
-    MeshForwarder           mMeshForwarder;
-    Mle::MleRouter          mMleRouter;
-    Mle::DiscoverScanner    mDiscoverScanner;
+    MeshCoP::ActiveDatasetManager  mActiveDataset;
+    MeshCoP::PendingDatasetManager mPendingDataset;
+    MeshCoP::ExtendedPanIdManager  mExtendedPanIdManager;
+    MeshCoP::NetworkNameManager    mNetworkNameManager;
+    Ip6::Filter                    mIp6Filter;
+    KeyManager                     mKeyManager;
+    Lowpan::Lowpan                 mLowpan;
+    Mac::Mac                       mMac;
+    MeshForwarder                  mMeshForwarder;
+    Mle::MleRouter                 mMleRouter;
+    Mle::DiscoverScanner           mDiscoverScanner;
 #if OPENTHREAD_CONFIG_MULTI_RADIO
     RadioSelector mRadioSelector;
 #endif
diff --git a/src/core/thread/thread_tlvs.hpp b/src/core/thread/thread_tlvs.hpp
index 0e5b5da..24eb919 100644
--- a/src/core/thread/thread_tlvs.hpp
+++ b/src/core/thread/thread_tlvs.hpp
@@ -166,6 +166,8 @@
         kTooFewRouters         = 2, ///< Address Solicit due to too few routers.
         kHaveChildIdRequest    = 3, ///< Address Solicit due to child ID request.
         kParentPartitionChange = 4, ///< Address Solicit due to parent partition change
+        kBorderRouterRequst    = 5, ///< Address Solicit from Border Router request.
+        kUnrecognizedStatus    = 6, ///< The requested status is unrecognized or not meaningful in a request.
     };
 
     /**
diff --git a/src/core/thread/time_sync_service.cpp b/src/core/thread/time_sync_service.cpp
index 001997d..e938f38 100644
--- a/src/core/thread/time_sync_service.cpp
+++ b/src/core/thread/time_sync_service.cpp
@@ -43,12 +43,14 @@
 
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 
 #define ABS(value) (((value) >= 0) ? (value) : -(value))
 
 namespace ot {
 
+RegisterLogModule("TimeSync");
+
 TimeSync::TimeSync(Instance &aInstance)
     : InstanceLocator(aInstance)
     , mTimeSyncRequired(false)
@@ -90,8 +92,7 @@
         // synchronized with the current sequence, so forward it.
         mTimeSyncRequired = true;
 
-        otLogInfoCore("Older time sync seq received:%u. Forwarding current seq:%u", aMessage.GetTimeSyncSeq(),
-                      mTimeSyncSeq);
+        LogInfo("Older time sync seq received:%u. Forwarding current seq:%u", aMessage.GetTimeSyncSeq(), mTimeSyncSeq);
     }
     else if (Get<Mle::MleRouter>().IsLeader() && timeSyncSeqDelta > 0)
     {
@@ -101,8 +102,8 @@
         mTimeSyncSeq      = aMessage.GetTimeSyncSeq() + 1;
         mTimeSyncRequired = true;
 
-        otLogInfoCore("Newer time sync seq:%u received by leader. Setting current seq to:%u and forwarding",
-                      aMessage.GetTimeSyncSeq(), mTimeSyncSeq);
+        LogInfo("Newer time sync seq:%u received by leader. Setting current seq to:%u and forwarding",
+                aMessage.GetTimeSyncSeq(), mTimeSyncSeq);
     }
     else if (!Get<Mle::MleRouter>().IsLeader())
     {
@@ -118,7 +119,7 @@
             mNetworkTimeOffset    = aMessage.GetNetworkTimeOffset();
             mTimeSyncRequired     = true;
 
-            otLogInfoCore("Newer time sync seq:%u received. Forwarding", mTimeSyncSeq);
+            LogInfo("Newer time sync seq:%u received. Forwarding", mTimeSyncSeq);
 
             // Only notify listeners of an update for network time offset jumps of more than
             // OPENTHREAD_CONFIG_TIME_SYNC_JUMP_NOTIF_MIN_US but notify listeners regardless if the status changes.
@@ -156,7 +157,7 @@
         IncrementTimeSyncSeq();
         mTimeSyncRequired = true;
 
-        otLogInfoCore("Leader seeding new time sync seq:%u", mTimeSyncSeq);
+        LogInfo("Leader seeding new time sync seq:%u", mTimeSyncSeq);
     }
 
     if (mTimeSyncRequired)
@@ -194,7 +195,7 @@
 
         stateChanged = true;
 
-        otLogInfoCore("Resetting time sync seq, partition changed");
+        LogInfo("Resetting time sync seq, partition changed");
     }
 
     if (stateChanged)
@@ -226,7 +227,7 @@
     case Mle::kRoleDisabled:
     case Mle::kRoleDetached:
         networkTimeStatus = OT_NETWORK_TIME_UNSYNCHRONIZED;
-        otLogInfoCore("Time sync status UNSYNCHRONIZED as role:DISABLED/DETACHED");
+        LogInfo("Time sync status UNSYNCHRONIZED as role:DISABLED/DETACHED");
         break;
 
     case Mle::kRoleChild:
@@ -235,26 +236,26 @@
         {
             // Haven't yet received any time sync
             networkTimeStatus = OT_NETWORK_TIME_UNSYNCHRONIZED;
-            otLogInfoCore("Time sync status UNSYNCHRONIZED as mLastTimeSyncReceived:0");
+            LogInfo("Time sync status UNSYNCHRONIZED as mLastTimeSyncReceived:0");
         }
         else if (timeSyncLastSyncMs > resyncNeededThresholdMs)
         {
             // The device hasn’t received time sync for more than two periods time.
             networkTimeStatus = OT_NETWORK_TIME_RESYNC_NEEDED;
-            otLogInfoCore("Time sync status RESYNC_NEEDED as timeSyncLastSyncMs:%u > resyncNeededThresholdMs:%u",
-                          timeSyncLastSyncMs, resyncNeededThresholdMs);
+            LogInfo("Time sync status RESYNC_NEEDED as timeSyncLastSyncMs:%u > resyncNeededThresholdMs:%u",
+                    timeSyncLastSyncMs, resyncNeededThresholdMs);
         }
         else
         {
             // Schedule a check 1 millisecond after two periods of time
             OT_ASSERT(resyncNeededThresholdMs >= timeSyncLastSyncMs);
             mTimer.Start(resyncNeededThresholdMs - timeSyncLastSyncMs + 1);
-            otLogInfoCore("Time sync status SYNCHRONIZED");
+            LogInfo("Time sync status SYNCHRONIZED");
         }
         break;
 
     case Mle::kRoleLeader:
-        otLogInfoCore("Time sync status SYNCHRONIZED as role:LEADER");
+        LogInfo("Time sync status SYNCHRONIZED as role:LEADER");
         break;
     }
 
diff --git a/src/core/thread/time_sync_service.hpp b/src/core/thread/time_sync_service.hpp
index d1c5511..4f2e144 100644
--- a/src/core/thread/time_sync_service.hpp
+++ b/src/core/thread/time_sync_service.hpp
@@ -66,7 +66,7 @@
     /**
      * Get the Thread network time.
      *
-     * @param[inout] aNetworkTime  The Thread network time in microseconds.
+     * @param[in,out] aNetworkTime  The Thread network time in microseconds.
      *
      * @returns The time synchronization status.
      *
diff --git a/src/core/thread/tmf.cpp b/src/core/thread/tmf.cpp
index 7dc5ea4..d9547d6 100644
--- a/src/core/thread/tmf.cpp
+++ b/src/core/thread/tmf.cpp
@@ -38,12 +38,54 @@
 namespace ot {
 namespace Tmf {
 
+//----------------------------------------------------------------------------------------------------------------------
+// MessageInfo
+
+void MessageInfo::SetSockAddrToRloc(void)
+{
+    SetSockAddr(Get<Mle::MleRouter>().GetMeshLocal16());
+}
+
+Error MessageInfo::SetSockAddrToRlocPeerAddrToLeaderAloc(void)
+{
+    SetSockAddrToRloc();
+    return Get<Mle::MleRouter>().GetLeaderAloc(GetPeerAddr());
+}
+
+Error MessageInfo::SetSockAddrToRlocPeerAddrToLeaderRloc(void)
+{
+    SetSockAddrToRloc();
+    return Get<Mle::MleRouter>().GetLeaderAddress(GetPeerAddr());
+}
+
+void MessageInfo::SetSockAddrToRlocPeerAddrToRealmLocalAllRoutersMulticast(void)
+{
+    SetSockAddrToRloc();
+    GetPeerAddr().SetToRealmLocalAllRoutersMulticast();
+}
+
+void MessageInfo::SetSockAddrToRlocPeerAddrTo(uint16_t aRloc16)
+{
+    SetSockAddrToRloc();
+    SetPeerAddr(Get<Mle::MleRouter>().GetMeshLocal16());
+    GetPeerAddr().GetIid().SetLocator(aRloc16);
+}
+
+void MessageInfo::SetSockAddrToRlocPeerAddrTo(const Ip6::Address &aPeerAddress)
+{
+    SetSockAddrToRloc();
+    SetPeerAddr(aPeerAddress);
+}
+
+//----------------------------------------------------------------------------------------------------------------------
+// Agent
+
 Error Agent::Start(void)
 {
     return Coap::Start(kUdpPort, OT_NETIF_THREAD);
 }
 
-Error Agent::Filter(const ot::Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo, void *aContext)
+Error Agent::Filter(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, void *aContext)
 {
     OT_UNUSED_VARIABLE(aMessage);
 
diff --git a/src/core/thread/tmf.hpp b/src/core/thread/tmf.hpp
index 738f665..dc4e286 100644
--- a/src/core/thread/tmf.hpp
+++ b/src/core/thread/tmf.hpp
@@ -37,12 +37,93 @@
 #include "openthread-core-config.h"
 
 #include "coap/coap.hpp"
+#include "common/locator.hpp"
 
 namespace ot {
 namespace Tmf {
 
 constexpr uint16_t kUdpPort = 61631; ///< TMF UDP Port
 
+typedef Coap::Message Message; ///< A TMF message.
+
+/**
+ * This class represents message information for a TMF message.
+ *
+ * This is sub-class of `Ip6::MessageInfo` intended for use when sending TMF messages.
+ *
+ */
+class MessageInfo : public InstanceLocator, public Ip6::MessageInfo
+{
+public:
+    /**
+     * This constructor initializes the `MessageInfo`.
+     *
+     * The peer port is set to `Tmf::kUdpPort` and all other properties are cleared (set to zero).
+     *
+     * @param[in] aInstance    The OpenThread instance.
+     *
+     */
+    explicit MessageInfo(Instance &aInstance)
+        : InstanceLocator(aInstance)
+    {
+        SetPeerPort(kUdpPort);
+    }
+
+    /**
+     * This method sets the local socket port to TMF port.
+     *
+     */
+    void SetSockPortToTmf(void) { SetSockPort(kUdpPort); }
+
+    /**
+     * This method sets the local socket address to mesh-local RLOC address.
+     *
+     */
+    void SetSockAddrToRloc(void);
+
+    /**
+     * This method sets the local socket address to RLOC address and the peer socket address to leader ALOC.
+     *
+     * @retval kErrorNone      Successfully set the addresses.
+     * @retval kErrorDetached  Cannot set leader ALOC since device is currently detached.
+     *
+     */
+    Error SetSockAddrToRlocPeerAddrToLeaderAloc(void);
+
+    /**
+     * This method sets the local socket address to RLOC address and the peer socket address to leader RLOC.
+     *
+     * @retval kErrorNone      Successfully set the addresses.
+     * @retval kErrorDetached  Cannot set leader RLOC since device is currently detached.
+     *
+     */
+    Error SetSockAddrToRlocPeerAddrToLeaderRloc(void);
+
+    /**
+     * This method sets the local socket address to RLOC address and the peer socket address to realm-local all
+     * routers multicast address.
+     *
+     */
+    void SetSockAddrToRlocPeerAddrToRealmLocalAllRoutersMulticast(void);
+
+    /**
+     * This method sets the local socket address to RLOC address and the peer socket address to a router RLOC based on
+     * a given RLOC16.
+     *
+     * @param[in] aRloc16     The RLOC16 to use for peer address.
+     *
+     */
+    void SetSockAddrToRlocPeerAddrTo(uint16_t aRloc16);
+
+    /**
+     * This method sets the local socket address to RLOC address and the peer socket address to a given address.
+     *
+     * @param[in] aPeerAddress  The peer address.
+     *
+     */
+    void SetSockAddrToRlocPeerAddrTo(const Ip6::Address &aPeerAddress);
+};
+
 /**
  * This class implements functionality of the Thread TMF agent.
  *
@@ -81,7 +162,7 @@
     bool IsTmfMessage(const Ip6::MessageInfo &aMessageInfo) const;
 
 private:
-    static Error Filter(const ot::Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo, void *aContext);
+    static Error Filter(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, void *aContext);
 };
 
 } // namespace Tmf
diff --git a/src/core/thread/topology.cpp b/src/core/thread/topology.cpp
index b03abf3..0eef0a0 100644
--- a/src/core/thread/topology.cpp
+++ b/src/core/thread/topology.cpp
@@ -33,11 +33,11 @@
 
 #include "topology.hpp"
 
+#include "common/array.hpp"
 #include "common/code_utils.hpp"
 #include "common/debug.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
 
 namespace ot {
 
@@ -154,6 +154,19 @@
     return matches;
 }
 
+#if OPENTHREAD_CONFIG_MULTI_RADIO
+void Neighbor::SetLastRxFragmentTag(uint16_t aTag)
+{
+    mLastRxFragmentTag     = (aTag == 0) ? 0xffff : aTag;
+    mLastRxFragmentTagTime = TimerMilli::GetNow();
+}
+
+bool Neighbor::IsLastRxFragmentTagSet(void) const
+{
+    return (mLastRxFragmentTag != 0) && (TimerMilli::GetNow() <= mLastRxFragmentTagTime + kLastRxFragmentTagTimeout);
+}
+#endif
+
 void Neighbor::GenerateChallenge(void)
 {
     IgnoreError(
@@ -474,7 +487,7 @@
 {
     uint16_t addressIndex;
 
-    OT_ASSERT(&mIp6Address[0] <= &aAddress && &aAddress < OT_ARRAY_END(mIp6Address));
+    OT_ASSERT(&mIp6Address[0] <= &aAddress && &aAddress < GetArrayEnd(mIp6Address));
 
     addressIndex = static_cast<uint16_t>(&aAddress - mIp6Address);
 
@@ -487,7 +500,7 @@
 {
     uint16_t addressIndex;
 
-    OT_ASSERT(&mIp6Address[0] <= &aAddress && &aAddress < OT_ARRAY_END(mIp6Address));
+    OT_ASSERT(&mIp6Address[0] <= &aAddress && &aAddress < GetArrayEnd(mIp6Address));
 
     addressIndex = static_cast<uint16_t>(&aAddress - mIp6Address);
 
diff --git a/src/core/thread/topology.hpp b/src/core/thread/topology.hpp
index 218a24c..dc39d9d 100644
--- a/src/core/thread/topology.hpp
+++ b/src/core/thread/topology.hpp
@@ -501,7 +501,7 @@
     /**
      * This method clears the last received fragment tag.
      *
-     * The last received fragment tag is used for detect duplicate frames (receievd over different radios) when
+     * The last received fragment tag is used for detect duplicate frames (received over different radios) when
      * multi-radio feature is enabled.
      *
      */
@@ -523,15 +523,15 @@
      * @param[in] aTag   The new tag value.
      *
      */
-    void SetLastRxFragmentTag(uint16_t aTag) { mLastRxFragmentTag = (aTag == 0) ? 0xffff : aTag; }
+    void SetLastRxFragmentTag(uint16_t aTag);
 
     /**
-     * This method indicates whether the last received fragment tag is set or not.
+     * This method indicates whether or not the last received fragment tag is set and valid (i.e., not yet timed out).
      *
-     * @returns TRUE if the last received fragment tag is set, FALSE otherwise.
+     * @returns TRUE if the last received fragment tag is set and valid, FALSE otherwise.
      *
      */
-    bool IsLastRxFragmentTagSet(void) const { return (mLastRxFragmentTag != 0); }
+    bool IsLastRxFragmentTagSet(void) const;
 
     /**
      * This method indicates whether the last received fragment tag is strictly after a given tag value.
@@ -694,7 +694,7 @@
     /**
      * This method adds a new LinkMetrics::SeriesInfo to the neighbor's list.
      *
-     * @param[in]    A reference to the new SeriesInfo.
+     * @param[in]  aSeriesInfo  A reference to the new SeriesInfo.
      *
      */
     void AddForwardTrackingSeriesInfo(LinkMetrics::SeriesInfo &aSeriesInfo);
@@ -778,6 +778,11 @@
     void Init(Instance &aInstance);
 
 private:
+    enum : uint32_t
+    {
+        kLastRxFragmentTagTimeout = OPENTHREAD_CONFIG_MULTI_RADIO_FRAG_TAG_TIMEOUT, ///< Frag tag timeout in msec.
+    };
+
     Mac::ExtAddress mMacAddr;   ///< The IEEE 802.15.4 Extended Address
     TimeMilli       mLastHeard; ///< Time when last heard.
     union
@@ -797,7 +802,8 @@
     } mValidPending;
 
 #if OPENTHREAD_CONFIG_MULTI_RADIO
-    uint16_t mLastRxFragmentTag; ///< Last received fragment tag
+    uint16_t  mLastRxFragmentTag;     ///< Last received fragment tag
+    TimeMilli mLastRxFragmentTagTime; ///< The time last fragment tag was received and set.
 #endif
 
     uint32_t mKeySequence; ///< Current key sequence
@@ -1391,7 +1397,7 @@
      * @returns The link quality out value for this router.
      *
      */
-    uint8_t GetLinkQualityOut(void) const { return mLinkQualityOut; }
+    LinkQuality GetLinkQualityOut(void) const { return static_cast<LinkQuality>(mLinkQualityOut); }
 
     /**
      * This method sets the link quality out value for this router.
@@ -1399,7 +1405,7 @@
      * @param[in]  aLinkQuality  The link quality out value for this router.
      *
      */
-    void SetLinkQualityOut(uint8_t aLinkQuality) { mLinkQualityOut = aLinkQuality; }
+    void SetLinkQualityOut(LinkQuality aLinkQuality) { mLinkQualityOut = aLinkQuality; }
 
     /**
      * This method get the route cost to this router.
@@ -1429,7 +1435,7 @@
     /**
      * This method sets the CSL clock accuracy of this router.
      *
-     * @param[in]  aCost  The CSL clock accuracy of this router.
+     * @param[in]  aCslClockAccuracy  The CSL clock accuracy of this router.
      *
      */
     void SetCslClockAccuracy(uint8_t aCslClockAccuracy) { mCslClockAccuracy = aCslClockAccuracy; }
@@ -1445,7 +1451,7 @@
     /**
      * This method sets the CSL clock uncertainty of this router.
      *
-     * @param[in]  aCost  The CSL clock uncertainty of this router.
+     * @param[in]  aCslClockUncertainty  The CSL clock uncertainty of this router.
      *
      */
     void SetCslClockUncertainty(uint8_t aCslClockUncertainty) { mCslClockUncertainty = aCslClockUncertainty; }
diff --git a/src/core/utils/channel_manager.cpp b/src/core/utils/channel_manager.cpp
index ac2c6ba..93c24f5 100644
--- a/src/core/utils/channel_manager.cpp
+++ b/src/core/utils/channel_manager.cpp
@@ -39,7 +39,7 @@
 #include "common/code_utils.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "common/random.hpp"
 #include "common/string.hpp"
 #include "meshcop/dataset_updater.hpp"
@@ -48,6 +48,8 @@
 namespace ot {
 namespace Utils {
 
+RegisterLogModule("ChannelManager");
+
 ChannelManager::ChannelManager(Instance &aInstance)
     : InstanceLocator(aInstance)
     , mSupportedChannelMask(0)
@@ -64,11 +66,11 @@
 
 void ChannelManager::RequestChannelChange(uint8_t aChannel)
 {
-    otLogInfoUtil("ChannelManager: Request to change to channel %d with delay %d sec", aChannel, mDelay);
+    LogInfo("Request to change to channel %d with delay %d sec", aChannel, mDelay);
 
     if (aChannel == Get<Mac::Mac>().GetPanChannel())
     {
-        otLogInfoUtil("ChannelManager: Already operating on the requested channel %d", aChannel);
+        LogInfo("Already operating on the requested channel %d", aChannel);
         ExitNow();
     }
 
@@ -120,7 +122,7 @@
         break;
 
     case kErrorInvalidState:
-        otLogInfoUtil("ChannelManager: Request to change to channel %d failed. Device is disabled", mChannel);
+        LogInfo("Request to change to channel %d failed. Device is disabled", mChannel);
 
         OT_FALL_THROUGH;
 
@@ -140,12 +142,12 @@
 {
     if (aError == kErrorNone)
     {
-        otLogInfoUtil("ChannelManager: Channel changed to %d", mChannel);
+        LogInfo("Channel changed to %d", mChannel);
     }
     else
     {
-        otLogInfoUtil("ChannelManager: Canceling channel change to %d%s", mChannel,
-                      (aError == kErrorAlready) ? " since current ActiveDataset is more recent" : "");
+        LogInfo("Canceling channel change to %d%s", mChannel,
+                (aError == kErrorAlready) ? " since current ActiveDataset is more recent" : "");
     }
 
     mState = kStateIdle;
@@ -162,7 +164,7 @@
     switch (mState)
     {
     case kStateIdle:
-        otLogInfoUtil("ChannelManager: Auto-triggered channel select");
+        LogInfo("Auto-triggered channel select");
         IgnoreError(RequestChannelSelect(false));
         StartAutoSelectTimer();
         break;
@@ -189,8 +191,8 @@
 
     if (Get<ChannelMonitor>().GetSampleCount() <= kMinChannelMonitorSampleCount)
     {
-        otLogInfoUtil("ChannelManager: Too few samples (%d <= %d) to select channel",
-                      Get<ChannelMonitor>().GetSampleCount(), kMinChannelMonitorSampleCount);
+        LogInfo("Too few samples (%d <= %d) to select channel", Get<ChannelMonitor>().GetSampleCount(),
+                kMinChannelMonitorSampleCount);
         ExitNow(error = kErrorInvalidState);
     }
 
@@ -200,10 +202,8 @@
     favoredBest   = Get<ChannelMonitor>().FindBestChannels(favoredAndSupported, favoredOccupancy);
     supportedBest = Get<ChannelMonitor>().FindBestChannels(mSupportedChannelMask, supportedOccupancy);
 
-    otLogInfoUtil("ChannelManager: Best favored %s, occupancy 0x%04x", favoredBest.ToString().AsCString(),
-                  favoredOccupancy);
-    otLogInfoUtil("ChannelManager: Best overall %s, occupancy 0x%04x", supportedBest.ToString().AsCString(),
-                  supportedOccupancy);
+    LogInfo("Best favored %s, occupancy 0x%04x", favoredBest.ToString().AsCString(), favoredOccupancy);
+    LogInfo("Best overall %s, occupancy 0x%04x", supportedBest.ToString().AsCString(), supportedOccupancy);
 
     // Prefer favored channels unless there is no favored channel,
     // or the occupancy rate of the best favored channel is worse
@@ -214,7 +214,7 @@
     {
         if (!favoredBest.IsEmpty())
         {
-            otLogInfoUtil("ChannelManager: Preferring an unfavored channel due to high occupancy rate diff");
+            LogInfo("Preferring an unfavored channel due to high occupancy rate diff");
         }
 
         favoredBest      = supportedBest;
@@ -235,8 +235,8 @@
     uint16_t ccaFailureRate = Get<Mac::Mac>().GetCcaFailureRate();
     bool     shouldAttempt  = (ccaFailureRate >= mCcaFailureRateThreshold);
 
-    otLogInfoUtil("ChannelManager: CCA-err-rate: 0x%04x %s 0x%04x, selecting channel: %s", ccaFailureRate,
-                  shouldAttempt ? ">=" : "<", mCcaFailureRateThreshold, ToYesNo(shouldAttempt));
+    LogInfo("CCA-err-rate: 0x%04x %s 0x%04x, selecting channel: %s", ccaFailureRate, shouldAttempt ? ">=" : "<",
+            mCcaFailureRateThreshold, ToYesNo(shouldAttempt));
 
     return shouldAttempt;
 }
@@ -247,7 +247,7 @@
     uint8_t  curChannel, newChannel;
     uint16_t curOccupancy, newOccupancy;
 
-    otLogInfoUtil("ChannelManager: Request to select channel (skip quality check: %s)", ToYesNo(aSkipQualityCheck));
+    LogInfo("Request to select channel (skip quality check: %s)", ToYesNo(aSkipQualityCheck));
 
     VerifyOrExit(!Get<Mle::Mle>().IsDisabled(), error = kErrorInvalidState);
 
@@ -260,12 +260,12 @@
 
     if (newChannel == curChannel)
     {
-        otLogInfoUtil("ChannelManager: Already on best possible channel %d", curChannel);
+        LogInfo("Already on best possible channel %d", curChannel);
         ExitNow();
     }
 
-    otLogInfoUtil("ChannelManager: Cur channel %d, occupancy 0x%04x - Best channel %d, occupancy 0x%04x", curChannel,
-                  curOccupancy, newChannel, newOccupancy);
+    LogInfo("Cur channel %d, occupancy 0x%04x - Best channel %d, occupancy 0x%04x", curChannel, curOccupancy,
+            newChannel, newOccupancy);
 
     // Switch only if new channel's occupancy rate is better than current
     // channel's occupancy rate by threshold `kThresholdToChangeChannel`.
@@ -273,7 +273,7 @@
     if ((newOccupancy >= curOccupancy) ||
         (static_cast<uint16_t>(curOccupancy - newOccupancy) < kThresholdToChangeChannel))
     {
-        otLogInfoUtil("ChannelManager: Occupancy rate diff too small to change channel");
+        LogInfo("Occupancy rate diff too small to change channel");
         ExitNow();
     }
 
@@ -283,7 +283,7 @@
 
     if (error != kErrorNone)
     {
-        otLogInfoUtil("ChannelManager: Request to select better channel failed, error: %s", ErrorToString(error));
+        LogInfo("Request to select better channel failed, error: %s", ErrorToString(error));
     }
 
     return error;
@@ -339,21 +339,21 @@
 {
     mSupportedChannelMask.SetMask(aChannelMask & Get<Mac::Mac>().GetSupportedChannelMask().GetMask());
 
-    otLogInfoUtil("ChannelManager: Supported channels: %s", mSupportedChannelMask.ToString().AsCString());
+    LogInfo("Supported channels: %s", mSupportedChannelMask.ToString().AsCString());
 }
 
 void ChannelManager::SetFavoredChannels(uint32_t aChannelMask)
 {
     mFavoredChannelMask.SetMask(aChannelMask & Get<Mac::Mac>().GetSupportedChannelMask().GetMask());
 
-    otLogInfoUtil("ChannelManager: Favored channels: %s", mFavoredChannelMask.ToString().AsCString());
+    LogInfo("Favored channels: %s", mFavoredChannelMask.ToString().AsCString());
 }
 
 void ChannelManager::SetCcaFailureRateThreshold(uint16_t aThreshold)
 {
     mCcaFailureRateThreshold = aThreshold;
 
-    otLogInfoUtil("ChannelManager: CCA threshold: 0x%04x", mCcaFailureRateThreshold);
+    LogInfo("CCA threshold: 0x%04x", mCcaFailureRateThreshold);
 }
 
 } // namespace Utils
diff --git a/src/core/utils/channel_monitor.cpp b/src/core/utils/channel_monitor.cpp
index a6fe604..8925c64 100644
--- a/src/core/utils/channel_monitor.cpp
+++ b/src/core/utils/channel_monitor.cpp
@@ -37,12 +37,14 @@
 
 #include "common/code_utils.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "common/random.hpp"
 
 namespace ot {
 namespace Utils {
 
+RegisterLogModule("ChannelMonitor");
+
 const uint32_t ChannelMonitor::mScanChannelMasks[kNumChannelMasks] = {
 #if OPENTHREAD_CONFIG_RADIO_915MHZ_OQPSK_SUPPORT
     OT_CHANNEL_1_MASK | OT_CHANNEL_5_MASK | OT_CHANNEL_9_MASK,
@@ -74,7 +76,7 @@
     VerifyOrExit(!IsRunning(), error = kErrorAlready);
     Clear();
     mTimer.Start(kTimerInterval);
-    otLogDebgUtil("ChannelMonitor: Starting");
+    LogDebg("Starting");
 
 exit:
     return error;
@@ -86,7 +88,7 @@
 
     VerifyOrExit(IsRunning(), error = kErrorAlready);
     mTimer.Stop();
-    otLogDebgUtil("ChannelMonitor: Stopping");
+    LogDebg("Stopping");
 
 exit:
     return error;
@@ -98,7 +100,7 @@
     mSampleCount      = 0;
     memset(mChannelOccupancy, 0, sizeof(mChannelOccupancy));
 
-    otLogDebgUtil("ChannelMonitor: Clearing data");
+    LogDebg("Clearing data");
 }
 
 uint16_t ChannelMonitor::GetChannelOccupancy(uint8_t aChannel) const
@@ -154,7 +156,7 @@
 
         OT_ASSERT(channelIndex < kNumChannels);
 
-        otLogDebgUtil("ChannelMonitor: channel: %d, rssi:%d", aResult->mChannel, aResult->mMaxRssi);
+        LogDebg("channel: %d, rssi:%d", aResult->mChannel, aResult->mMaxRssi);
 
         if (aResult->mMaxRssi != OT_RADIO_RSSI_INVALID)
         {
@@ -189,7 +191,7 @@
 
 void ChannelMonitor::LogResults(void)
 {
-#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_UTIL == 1)
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
     const size_t        kStringSize = 128;
     String<kStringSize> logString;
 
@@ -198,7 +200,7 @@
         logString.Append("%02x ", channel >> 8);
     }
 
-    otLogInfoUtil("ChannelMonitor: %u [%s]", mSampleCount, logString.AsCString());
+    LogInfo("%u [%s]", mSampleCount, logString.AsCString());
 #endif
 }
 
diff --git a/src/core/utils/child_supervision.cpp b/src/core/utils/child_supervision.cpp
index 7f6368a..b0f45bd 100644
--- a/src/core/utils/child_supervision.cpp
+++ b/src/core/utils/child_supervision.cpp
@@ -37,12 +37,14 @@
 #include "common/code_utils.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "thread/thread_netif.hpp"
 
 namespace ot {
 namespace Utils {
 
+RegisterLogModule("ChildSupervsn");
+
 #if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE
 
 #if OPENTHREAD_FTD
@@ -94,7 +96,7 @@
     SuccessOrExit(Get<ThreadNetif>().SendMessage(*message));
     message = nullptr;
 
-    otLogInfoUtil("Sending supervision message to child 0x%04x", aChild.GetRloc16());
+    LogInfo("Sending supervision message to child 0x%04x", aChild.GetRloc16());
 
 exit:
     FreeMessage(message);
@@ -132,13 +134,13 @@
     if (shouldRun && !Get<TimeTicker>().IsReceiverRegistered(TimeTicker::kChildSupervisor))
     {
         Get<TimeTicker>().RegisterReceiver(TimeTicker::kChildSupervisor);
-        otLogInfoUtil("Starting Child Supervision");
+        LogInfo("Starting Child Supervision");
     }
 
     if (!shouldRun && Get<TimeTicker>().IsReceiverRegistered(TimeTicker::kChildSupervisor))
     {
         Get<TimeTicker>().UnregisterReceiver(TimeTicker::kChildSupervisor);
-        otLogInfoUtil("Stopping Child Supervision");
+        LogInfo("Stopping Child Supervision");
     }
 }
 
@@ -213,7 +215,7 @@
 {
     VerifyOrExit(Get<Mle::MleRouter>().IsChild() && !Get<MeshForwarder>().GetRxOnWhenIdle());
 
-    otLogWarnUtil("Supervision timeout. No frame from parent in %d sec", mTimeout);
+    LogWarn("Supervision timeout. No frame from parent in %d sec", mTimeout);
 
     IgnoreError(Get<Mle::MleRouter>().SendChildUpdateRequest());
 
diff --git a/src/core/utils/flash.hpp b/src/core/utils/flash.hpp
index 84c83b5..795a47a 100644
--- a/src/core/utils/flash.hpp
+++ b/src/core/utils/flash.hpp
@@ -69,15 +69,15 @@
     /**
      * This method fetches the value identified by @p aKey.
      *
-     * @param[in]     aKey          The key associated with the requested value.
-     * @param[in]     aIndex        The index of the specific item to get.
-     * @param[out]    aValue        A pointer to where the value of the setting should be written.
-     *                              May be `nullptr` if just testing for the presence or length of a key.
-     * @param[inout]  aValueLength  A pointer to the length of the value.
-     *                              When called, this should point to an integer containing the maximum bytes that
-     *                              can be written to @p aValue.
-     *                              At return, the actual length of the setting is written.
-     *                              May be `nullptr` if performing a presence check.
+     * @param[in]      aKey          The key associated with the requested value.
+     * @param[in]      aIndex        The index of the specific item to get.
+     * @param[out]     aValue        A pointer to where the value of the setting should be written.
+     *                               May be `nullptr` if just testing for the presence or length of a key.
+     * @param[in,out]  aValueLength  A pointer to the length of the value.
+     *                               When called, this should point to an integer containing the maximum bytes that
+     *                               can be written to @p aValue.
+     *                               At return, the actual length of the setting is written.
+     *                               May be `nullptr` if performing a presence check.
      *
      * @retval kErrorNone       The value was fetched successfully.
      * @retval kErrorNotFound   The key was not found.
diff --git a/src/core/utils/history_tracker.hpp b/src/core/utils/history_tracker.hpp
index a367098..3a0a2ee 100644
--- a/src/core/utils/history_tracker.hpp
+++ b/src/core/utils/history_tracker.hpp
@@ -139,10 +139,11 @@
     /**
      * This method iterates over the entries in the network info history list.
      *
-     * @param[inout] aIterator  An iterator. MUST be initialized.
-     * @param[out]   aEntryAge  A reference to a variable to output the entry's age.
-     *                          Age is provided as the duration (in milliseconds) from when entry was recorded to
-     *                          @p aIterator initialization time. It is set to `kMaxAge` for entries older than max age.
+     * @param[in,out] aIterator  An iterator. MUST be initialized.
+     * @param[out]    aEntryAge  A reference to a variable to output the entry's age.
+     *                           Age is provided as the duration (in milliseconds) from when entry was recorded to
+     *                           @p aIterator initialization time. It is set to `kMaxAge` for entries older than max
+     *                           age.
      *
      * @returns A pointer to `NetworkInfo` entry or `nullptr` if no more entries in the list.
      *
@@ -155,10 +156,11 @@
     /**
      * This method iterates over the entries in the unicast address history list.
      *
-     * @param[inout] aIterator  An iterator. MUST be initialized.
-     * @param[out]   aEntryAge  A reference to a variable to output the entry's age.
-     *                          Age is provided as the duration (in milliseconds) from when entry was recorded to
-     *                          @p aIterator initialization time. It is set to `kMaxAge` for entries older than max age.
+     * @param[in,out] aIterator  An iterator. MUST be initialized.
+     * @param[out]    aEntryAge  A reference to a variable to output the entry's age.
+     *                           Age is provided as the duration (in milliseconds) from when entry was recorded to
+     *                           @p aIterator initialization time. It is set to `kMaxAge` for entries older than max
+     *                           age.
      *
      * @returns A pointer to `UnicastAddress` entry or `nullptr` if no more entries in the list.
      *
@@ -171,10 +173,11 @@
     /**
      * This method iterates over the entries in the multicast address history list.
      *
-     * @param[inout] aIterator  An iterator. MUST be initialized.
-     * @param[out]   aEntryAge  A reference to a variable to output the entry's age.
-     *                          Age is provided as the duration (in milliseconds) from when entry was recorded to
-     *                          @p aIterator initialization time. It is set to `kMaxAge` for entries older than max age.
+     * @param[in,out] aIterator  An iterator. MUST be initialized.
+     * @param[out]    aEntryAge  A reference to a variable to output the entry's age.
+     *                           Age is provided as the duration (in milliseconds) from when entry was recorded to
+     *                           @p aIterator initialization time. It is set to `kMaxAge` for entries older than max
+     *                           age.
      *
      * @returns A pointer to `MulticastAddress` entry or `nullptr` if no more entries in the list.
      *
@@ -187,10 +190,11 @@
     /**
      * This method iterates over the entries in the RX history list.
      *
-     * @param[inout] aIterator  An iterator. MUST be initialized.
-     * @param[out]   aEntryAge  A reference to a variable to output the entry's age.
-     *                          Age is provided as the duration (in milliseconds) from when entry was recorded to
-     *                          @p aIterator initialization time. It is set to `kMaxAge` for entries older than max age.
+     * @param[in,out] aIterator  An iterator. MUST be initialized.
+     * @param[out]    aEntryAge  A reference to a variable to output the entry's age.
+     *                           Age is provided as the duration (in milliseconds) from when entry was recorded to
+     *                           @p aIterator initialization time. It is set to `kMaxAge` for entries older than max
+     *                           age.
      *
      * @returns A pointer to `MessageInfo` entry or `nullptr` if no more entries in the list.
      *
@@ -203,10 +207,11 @@
     /**
      * This method iterates over the entries in the TX history list.
      *
-     * @param[inout] aIterator  An iterator. MUST be initialized.
-     * @param[out]   aEntryAge  A reference to a variable to output the entry's age.
-     *                          Age is provided as the duration (in milliseconds) from when entry was recorded to
-     *                          @p aIterator initialization time. It is set to `kMaxAge` for entries older than max age.
+     * @param[in,out] aIterator  An iterator. MUST be initialized.
+     * @param[out]    aEntryAge  A reference to a variable to output the entry's age.
+     *                           Age is provided as the duration (in milliseconds) from when entry was recorded to
+     *                           @p aIterator initialization time. It is set to `kMaxAge` for entries older than max
+     *                           age.
      *
      * @returns A pointer to `MessageInfo` entry or `nullptr` if no more entries in the list.
      *
diff --git a/src/core/utils/jam_detector.cpp b/src/core/utils/jam_detector.cpp
index 44353ee..ce4a4bc 100644
--- a/src/core/utils/jam_detector.cpp
+++ b/src/core/utils/jam_detector.cpp
@@ -38,13 +38,15 @@
 #include "common/code_utils.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "common/random.hpp"
 #include "thread/thread_netif.hpp"
 
 namespace ot {
 namespace Utils {
 
+RegisterLogModule("JamDetector");
+
 JamDetector::JamDetector(Instance &aInstance)
     : InstanceLocator(aInstance)
     , mHandler(nullptr)
@@ -73,7 +75,7 @@
     mContext = aContext;
     mEnabled = true;
 
-    otLogInfoUtil("JamDetector - Started");
+    LogInfo("Started");
 
     CheckState();
 
@@ -92,7 +94,7 @@
 
     mTimer.Stop();
 
-    otLogInfoUtil("JamDetector - Stopped");
+    LogInfo("Stopped");
 
 exit:
     return error;
@@ -128,7 +130,7 @@
 void JamDetector::SetRssiThreshold(int8_t aThreshold)
 {
     mRssiThreshold = aThreshold;
-    otLogInfoUtil("JamDetector - RSSI threshold set to %d", mRssiThreshold);
+    LogInfo("RSSI threshold set to %d", mRssiThreshold);
 }
 
 Error JamDetector::SetWindow(uint8_t aWindow)
@@ -139,7 +141,7 @@
     VerifyOrExit(aWindow <= kMaxWindow, error = kErrorInvalidArgs);
 
     mWindow = aWindow;
-    otLogInfoUtil("JamDetector - window set to %d", mWindow);
+    LogInfo("window set to %d", mWindow);
 
 exit:
     return error;
@@ -153,7 +155,7 @@
     VerifyOrExit(aBusyPeriod <= mWindow, error = kErrorInvalidArgs);
 
     mBusyPeriod = aBusyPeriod;
-    otLogInfoUtil("JamDetector - busy period set to %d", mBusyPeriod);
+    LogInfo("busy period set to %d", mBusyPeriod);
 
 exit:
     return error;
@@ -261,7 +263,7 @@
     {
         mJamState           = aNewState;
         shouldInvokeHandler = true;
-        otLogInfoUtil("JamDetector - jamming %s", mJamState ? "detected" : "cleared");
+        LogInfo("Jamming %s", mJamState ? "detected" : "cleared");
     }
 
     if (shouldInvokeHandler)
diff --git a/src/core/utils/jam_detector.hpp b/src/core/utils/jam_detector.hpp
index 02bb5f2..98dc5e2 100644
--- a/src/core/utils/jam_detector.hpp
+++ b/src/core/utils/jam_detector.hpp
@@ -111,7 +111,7 @@
     /**
      * Set the Jam Detection RSSI Threshold (in dBm).
      *
-     * @param[in]  aRssiThreshold  The RSSI threshold.
+     * @param[in]  aThreshold  The RSSI threshold.
      *
      */
     void SetRssiThreshold(int8_t aThreshold);
diff --git a/src/core/utils/otns.cpp b/src/core/utils/otns.cpp
index 7bcd13d..0030fba 100644
--- a/src/core/utils/otns.cpp
+++ b/src/core/utils/otns.cpp
@@ -38,11 +38,13 @@
 
 #include "common/debug.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 
 namespace ot {
 namespace Utils {
 
+RegisterLogModule("Otns");
+
 const int kMaxStatusStringLength = 128;
 
 void Otns::EmitShortAddress(uint16_t aShortAddress)
@@ -170,7 +172,7 @@
 exit:
     if (error != kErrorNone)
     {
-        otLogWarnCore("Otns::EmitCoapSend failed: %s", ErrorToString(error));
+        LogWarn("EmitCoapSend failed: %s", ErrorToString(error));
     }
 }
 
@@ -186,7 +188,7 @@
 exit:
     if (error != kErrorNone)
     {
-        otLogWarnCore("Otns::EmitCoapReceive failed: %s", ErrorToString(error));
+        LogWarn("EmitCoapReceive failed: %s", ErrorToString(error));
     }
 }
 
@@ -203,7 +205,7 @@
 exit:
     if (error != kErrorNone)
     {
-        otLogWarnCore("Otns::EmitCoapSendFailure failed: %s", ErrorToString(error));
+        LogWarn("EmitCoapSendFailure failed: %s", ErrorToString(error));
     }
 }
 
diff --git a/src/core/utils/parse_cmdline.hpp b/src/core/utils/parse_cmdline.hpp
index 88b0cc8..6b9025f 100644
--- a/src/core/utils/parse_cmdline.hpp
+++ b/src/core/utils/parse_cmdline.hpp
@@ -249,10 +249,10 @@
  * This function correctly handles hex strings with even or odd length. For example, "AABBCCDD" (with even length) is
  * parsed as {0xaa, 0xbb, 0xcc, 0xdd} and "123" (with odd length) is parsed as {0x01, 0x23}.
  *
- * @param[in]     aString   The string to parse.
- * @param[inout]  aSize     On entry indicates the number of bytes in @p aBuffer (max size of @p aBuffer).
- *                          On exit provides number of bytes parsed and copied into @p aBuffer.
- * @param[out]    aBuffer   A pointer to a buffer to output the parsed byte sequence.
+ * @param[in]      aString   The string to parse.
+ * @param[in,out]  aSize     On entry indicates the number of bytes in @p aBuffer (max size of @p aBuffer).
+ *                           On exit provides number of bytes parsed and copied into @p aBuffer.
+ * @param[out]     aBuffer   A pointer to a buffer to output the parsed byte sequence.
  *
  * @retval kErrorNone        The string was parsed successfully.
  * @retval kErrorInvalidArgs The string does not contain valid format or too many bytes.
@@ -271,10 +271,10 @@
  * This function correctly handles hex strings with even or odd length. For example, "AABBCCDD" (with even length) is
  * parsed as {0xaa, 0xbb, 0xcc, 0xdd} and "123" (with odd length) is parsed as {0x01, 0x23}.
  *
- * @param[inout] aString     A reference to string to parse. On successful parse, updated to skip parsed digits.
- * @param[inout] aSize       On entry indicates the segment size (number of bytes in @p aBuffer).
+ * @param[in,out] aString    A reference to string to parse. On successful parse, updated to skip parsed digits.
+ * @param[in,out] aSize      On entry indicates the segment size (number of bytes in @p aBuffer).
  *                           On exit provides number of bytes parsed and copied into @p aBuffer.
- * @param[out]   aBuffer     A pointer to a buffer to output the parsed byte sequence.
+ * @param[out]    aBuffer    A pointer to a buffer to output the parsed byte sequence.
  *
  * @retval kErrorNone        The string was parsed successfully to the end of string.
  * @retval kErrorPedning     The string segment was parsed successfully, but there are additional bytes remaining
@@ -557,9 +557,9 @@
      *
      * This method verifies that the parsed hex string bytes fit in @p aBuffer with its given @p aSize.
      *
-     * @param[inout]  aSize     On entry indicates the number of bytes in @p aBuffer (max size of @p aBuffer).
+     * @param[in,out]  aSize    On entry indicates the number of bytes in @p aBuffer (max size of @p aBuffer).
      *                          On exit provides number of bytes parsed and copied into @p aBuffer.
-     * @param[out]    aBuffer   A pointer to a buffer to output the parsed byte sequence.
+     * @param[out]     aBuffer  A pointer to a buffer to output the parsed byte sequence.
      *
      * @retval kErrorNone        The argument was parsed successfully.
      * @retval kErrorInvalidArgs The argument does not contain valid format or too many bytes.
diff --git a/src/core/utils/slaac_address.cpp b/src/core/utils/slaac_address.cpp
index ccf2994..82cadfc 100644
--- a/src/core/utils/slaac_address.cpp
+++ b/src/core/utils/slaac_address.cpp
@@ -35,10 +35,11 @@
 
 #if OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE
 
+#include "common/array.hpp"
 #include "common/code_utils.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "common/random.hpp"
 #include "common/settings.hpp"
 #include "crypto/sha256.hpp"
@@ -47,6 +48,8 @@
 namespace ot {
 namespace Utils {
 
+RegisterLogModule("Slaac");
+
 Slaac::Slaac(Instance &aInstance)
     : InstanceLocator(aInstance)
     , mEnabled(true)
@@ -59,7 +62,7 @@
 {
     VerifyOrExit(!mEnabled);
 
-    otLogInfoUtil("SLAAC:: Enabling");
+    LogInfo("Enabling");
     mEnabled = true;
     Update(kModeAdd);
 
@@ -71,7 +74,7 @@
 {
     VerifyOrExit(mEnabled);
 
-    otLogInfoUtil("SLAAC:: Disabling");
+    LogInfo("Disabling");
     mEnabled = false;
     Update(kModeRemove);
 
@@ -84,7 +87,7 @@
     VerifyOrExit(aFilter != mFilter);
 
     mFilter = aFilter;
-    otLogInfoUtil("SLAAC: Filter %s", (mFilter != nullptr) ? "updated" : "disabled");
+    LogInfo("Filter %s", (mFilter != nullptr) ? "updated" : "disabled");
 
     VerifyOrExit(mEnabled);
     Update(kModeAdd | kModeRemove);
@@ -183,7 +186,7 @@
 
             if (!found)
             {
-                otLogInfoUtil("SLAAC: Removing address %s", slaacAddr.GetAddress().ToString().AsCString());
+                LogInfo("Removing address %s", slaacAddr.GetAddress().ToString().AsCString());
 
                 Get<ThreadNetif>().RemoveUnicastAddress(slaacAddr);
                 slaacAddr.mValid = false;
@@ -233,7 +236,7 @@
 
                     IgnoreError(GenerateIid(slaacAddr));
 
-                    otLogInfoUtil("SLAAC: Adding address %s", slaacAddr.GetAddress().ToString().AsCString());
+                    LogInfo("Adding address %s", slaacAddr.GetAddress().ToString().AsCString());
 
                     Get<ThreadNetif>().AddUnicastAddress(slaacAddr);
 
@@ -243,8 +246,8 @@
 
                 if (!added)
                 {
-                    otLogWarnUtil("SLAAC: Failed to add - max %d addresses supported and already in use",
-                                  OT_ARRAY_LENGTH(mAddresses));
+                    LogWarn("Failed to add - max %d addresses supported and already in use",
+                            GetArrayLength(mAddresses));
                 }
             }
         }
@@ -315,7 +318,7 @@
         ExitNow(error = kErrorNone);
     }
 
-    otLogWarnUtil("SLAAC: Failed to generate a non-reserved IID after %d attempts", kMaxIidCreationAttempts);
+    LogWarn("Failed to generate a non-reserved IID after %d attempts", kMaxIidCreationAttempts);
 
 exit:
     return error;
@@ -340,7 +343,7 @@
 
     IgnoreError(Get<Settings>().Save<Settings::SlaacIidSecretKey>(aKey));
 
-    otLogInfoUtil("SLAAC: Generated and saved secret key");
+    LogInfo("Generated and saved secret key");
 
 exit:
     return;
diff --git a/src/core/utils/slaac_address.hpp b/src/core/utils/slaac_address.hpp
index 23f3e61..db7913b 100644
--- a/src/core/utils/slaac_address.hpp
+++ b/src/core/utils/slaac_address.hpp
@@ -126,12 +126,12 @@
     /**
      * This method generates the IID of an IPv6 address.
      *
-     * @param[inout]  aAddress             A reference to the address that will be filled with the IID generated.
+     * @param[in,out]  aAddress            A reference to the address that will be filled with the IID generated.
      *                                     Note the prefix of the address must already be filled and will be used
      *                                     to generate the IID.
-     * @param[in]     aNetworkId           A pointer to a byte array of Network_ID to generate IID.
-     * @param[in]     aNetworkIdLength     The size of array @p aNetworkId.
-     * @param[inout]  aDadCounter          A pointer to the DAD_Counter that is employed to resolve Duplicate
+     * @param[in]      aNetworkId          A pointer to a byte array of Network_ID to generate IID.
+     * @param[in]      aNetworkIdLength    The size of array @p aNetworkId.
+     * @param[in,out]  aDadCounter         A pointer to the DAD_Counter that is employed to resolve Duplicate
      *                                     Address Detection connflicts.
      *
      * @retval kErrorNone    If successfully generated the IID.
diff --git a/src/core/utils/srp_client_buffers.hpp b/src/core/utils/srp_client_buffers.hpp
index fc49441..7746de8 100644
--- a/src/core/utils/srp_client_buffers.hpp
+++ b/src/core/utils/srp_client_buffers.hpp
@@ -40,6 +40,7 @@
 
 #include <openthread/srp_client_buffers.h>
 
+#include "common/array.hpp"
 #include "common/as_core_type.hpp"
 #include "common/clearable.hpp"
 #include "common/locator.hpp"
@@ -165,7 +166,7 @@
          */
         const char **GetSubTypeLabelsArray(uint16_t &aArrayLength)
         {
-            aArrayLength = OT_ARRAY_LENGTH(mSubTypeLabels);
+            aArrayLength = GetArrayLength(mSubTypeLabels);
             return mSubTypeLabels;
         }
 
@@ -213,7 +214,7 @@
      */
     Ip6::Address *GetHostAddressesArray(uint8_t &aArrayLength)
     {
-        aArrayLength = OT_ARRAY_LENGTH(mHostAddresses);
+        aArrayLength = static_cast<uint8_t>(GetArrayLength(mHostAddresses));
         return &mHostAddresses[0];
     }
 
diff --git a/src/lib/hdlc/hdlc.hpp b/src/lib/hdlc/hdlc.hpp
index ea97ce7..84581d1 100644
--- a/src/lib/hdlc/hdlc.hpp
+++ b/src/lib/hdlc/hdlc.hpp
@@ -39,6 +39,7 @@
 
 #include <openthread/error.h>
 
+#include "common/array.hpp"
 #include "common/code_utils.hpp"
 #include "common/debug.hpp"
 #include "common/encoding.hpp"
@@ -242,7 +243,7 @@
     {
         otError error = OT_ERROR_NO_BUFS;
 
-        if (GetFrame() + aLength <= OT_ARRAY_END(mBuffer))
+        if (GetFrame() + aLength <= GetArrayEnd(mBuffer))
         {
             mWritePointer    = GetFrame() + aLength;
             mRemainingLength = static_cast<uint16_t>(mBuffer + kSize - mWritePointer);
@@ -273,7 +274,7 @@
     {
         otError error = OT_ERROR_NO_BUFS;
 
-        if (mWriteFrameStart + kHeaderSize + aSkipLength <= OT_ARRAY_END(mBuffer))
+        if (mWriteFrameStart + kHeaderSize + aSkipLength <= GetArrayEnd(mBuffer))
         {
             Encoding::LittleEndian::WriteUint16(aSkipLength, mWriteFrameStart + kHeaderSkipLengthOffset);
             mWritePointer    = GetFrame();
@@ -352,11 +353,11 @@
     /**
      * This method iterates through previously saved frames in the buffer, getting a next frame in the queue.
      *
-     * @param[inout] aFrame   On entry, should point to a previous saved frame or nullptr to get the first frame.
-     *                        On exit, the pointer variable is updated to next frame or set to nullptr if there are
-     * none.
-     * @param[inout] aLength  On entry, should be a reference to the frame length of the previous saved frame.
-     *                        On exit, the reference is updated to the frame length (number of bytes) of next frame.
+     * @param[in,out] aFrame   On entry, should point to a previous saved frame or nullptr to get the first frame.
+     *                         On exit, the pointer variable is updated to next frame or set to nullptr if there are
+     *                         none.
+     * @param[in,out] aLength  On entry, should be a reference to the frame length of the previous saved frame.
+     *                         On exit, the reference is updated to the frame length (number of bytes) of next frame.
      *
      * @retval OT_ERROR_NONE       Updated @aFrame and @aLength successfully with the next saved frame.
      * @retval OT_ERROR_NOT_FOUND  No more saved frame in the buffer.
@@ -366,7 +367,7 @@
     {
         otError error = OT_ERROR_NONE;
 
-        OT_ASSERT(aFrame == nullptr || (mBuffer <= aFrame && aFrame < OT_ARRAY_END(mBuffer)));
+        OT_ASSERT(aFrame == nullptr || (mBuffer <= aFrame && aFrame < GetArrayEnd(mBuffer)));
 
         aFrame = (aFrame == nullptr) ? mBuffer : aFrame + aLength;
 
diff --git a/src/lib/platform/Makefile.am b/src/lib/platform/Makefile.am
index 70f8611..eb8d139 100644
--- a/src/lib/platform/Makefile.am
+++ b/src/lib/platform/Makefile.am
@@ -40,4 +40,8 @@
     exit_code.h                               \
     $(NULL)
 
+noinst_HEADERS                              = \
+    reset_util.h                              \
+    $(NULL)
+
 include $(abs_top_nlbuild_autotools_dir)/automake/post.am
diff --git a/src/lib/platform/reset_util.h b/src/lib/platform/reset_util.h
new file mode 100644
index 0000000..1cbe45f
--- /dev/null
+++ b/src/lib/platform/reset_util.h
@@ -0,0 +1,67 @@
+/*
+ *  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.
+ */
+
+#ifndef OT_LIB_PLATFORM_RESET_UTIL_H_
+#define OT_LIB_PLATFORM_RESET_UTIL_H_
+
+#if defined(OPENTHREAD_ENABLE_COVERAGE) && OPENTHREAD_ENABLE_COVERAGE && defined(__GNUC__)
+#if __GNUC__ >= 11
+void __gcov_dump();
+void __gcov_reset();
+
+static void flush_gcov(void)
+{
+    __gcov_dump();
+    __gcov_reset();
+}
+#else
+void __gcov_flush(void);
+#define flush_gcov __gcov_flush
+#endif // __GNUC__ >= 11
+#else
+#define flush_gcov()
+#endif // defined(OPENTHREAD_ENABLE_COVERAGE) && OPENTHREAD_ENABLE_COVERAGE && defined(__GNUC__)
+
+#if defined(__linux__) || defined(__APPLE__)
+#include <setjmp.h>
+#include <unistd.h>
+jmp_buf gResetJump;
+
+#define OT_SETUP_RESET_JUMP(kArgv) \
+    if (setjmp(gResetJump))        \
+    {                              \
+        alarm(0);                  \
+        flush_gcov();              \
+        execvp(kArgv[0], kArgv);   \
+    }
+
+#else
+#define OT_SETUP_RESET_JUMP(ARGV)
+#endif // defined(__linux__) || defined(__APPLE__)
+
+#endif // OT_LIB_PLATFORM_RESET_UTIL_H_
diff --git a/src/lib/spinel/radio_spinel.hpp b/src/lib/spinel/radio_spinel.hpp
index 7953a2b..c047dc8 100644
--- a/src/lib/spinel/radio_spinel.hpp
+++ b/src/lib/spinel/radio_spinel.hpp
@@ -858,7 +858,7 @@
 private:
     enum
     {
-        kMaxSpinelFrame        = SpinelInterface::kMaxFrameSize,
+        kMaxSpinelFrame        = SPINEL_FRAME_MAX_SIZE,
         kMaxWaitTime           = 2000, ///< Max time to wait for response in milliseconds.
         kVersionStringSize     = 128,  ///< Max size of version string.
         kCapsBufferSize        = 100,  ///< Max buffer size used to store `SPINEL_PROP_CAPS` value.
diff --git a/src/lib/spinel/radio_spinel_impl.hpp b/src/lib/spinel/radio_spinel_impl.hpp
index 9faf915..d5da5ba 100644
--- a/src/lib/spinel/radio_spinel_impl.hpp
+++ b/src/lib/spinel/radio_spinel_impl.hpp
@@ -37,13 +37,13 @@
 #include <stdlib.h>
 
 #include <openthread/dataset.h>
+#include <openthread/logging.h>
 #include <openthread/platform/diag.h>
 #include <openthread/platform/time.h>
 
 #include "common/code_utils.hpp"
 #include "common/encoding.hpp"
 #include "common/instance.hpp"
-#include "common/logging.hpp"
 #include "common/new.hpp"
 #include "common/settings.hpp"
 #include "lib/platform/exit_code.h"
@@ -160,7 +160,7 @@
     OT_UNUSED_VARIABLE(aText);
     OT_UNUSED_VARIABLE(aError);
 
-    if (aError != OT_ERROR_NONE)
+    if (aError != OT_ERROR_NONE && aError != OT_ERROR_NO_ACK)
     {
         otLogWarnPlat("%s: %s", aText, otThreadErrorToString(aError));
     }
@@ -431,7 +431,7 @@
 {
     otError error = OT_ERROR_NONE;
 
-    Instance::Get().template Get<SettingsDriver>().Init();
+    Instance::Get().template Get<SettingsDriver>().Init(nullptr, 0);
 
     otLogInfoPlat("Trying to get saved dataset from NCP");
     SuccessOrExit(
@@ -1699,15 +1699,25 @@
 template <typename InterfaceType, typename ProcessContextType>
 spinel_tid_t RadioSpinel<InterfaceType, ProcessContextType>::GetNextTid(void)
 {
-    spinel_tid_t tid = 0;
+    spinel_tid_t tid = mCmdNextTid;
 
-    if (((1 << mCmdNextTid) & mCmdTidsInUse) == 0)
+    while (((1 << tid) & mCmdTidsInUse) != 0)
     {
-        tid         = mCmdNextTid;
-        mCmdNextTid = SPINEL_GET_NEXT_TID(mCmdNextTid);
-        mCmdTidsInUse |= (1 << tid);
+        tid = SPINEL_GET_NEXT_TID(tid);
+
+        if (tid == mCmdNextTid)
+        {
+            // We looped back to `mCmdNextTid` indicating that all
+            // TIDs are in-use.
+
+            ExitNow(tid = 0);
+        }
     }
 
+    mCmdTidsInUse |= (1 << tid);
+    mCmdNextTid = SPINEL_GET_NEXT_TID(tid);
+
+exit:
     return tid;
 }
 
diff --git a/src/lib/spinel/spinel_buffer.hpp b/src/lib/spinel/spinel_buffer.hpp
index 056034b..6377a1f 100644
--- a/src/lib/spinel/spinel_buffer.hpp
+++ b/src/lib/spinel/spinel_buffer.hpp
@@ -61,8 +61,8 @@
      */
     enum Priority
     {
-        kPriorityLow  = 0, //< Indicates low/normal priority for a frame.
-        kPriorityHigh = 1, //< Indicates high priority for a frame.
+        kPriorityLow  = 0, ///< Indicates low/normal priority for a frame.
+        kPriorityHigh = 1, ///< Indicates high priority for a frame.
     };
 
     /**
@@ -98,8 +98,8 @@
         }
 
     private:
-        uint8_t *mPosition;    //< Pointer into buffer corresponding to saved write position.
-        uint8_t *mSegmentHead; //< Pointer to segment head.
+        uint8_t *mPosition;    // Pointer into buffer corresponding to saved write position.
+        uint8_t *mSegmentHead; // Pointer to segment head.
 
         friend class Buffer;
     };
diff --git a/src/lib/spinel/spinel_decoder.hpp b/src/lib/spinel/spinel_decoder.hpp
index 77328d7..bdd3861 100644
--- a/src/lib/spinel/spinel_decoder.hpp
+++ b/src/lib/spinel/spinel_decoder.hpp
@@ -471,7 +471,7 @@
      *
      * @param[out] aData                Reference to pointer variable to output the data.
      *                                  On success, the pointer variable is updated.
-     * @param[out] aDataLength          Reference to variable to output the data length (number of bytes).
+     * @param[out] aDataLen             Reference to variable to output the data length (number of bytes).
      *
      * @retval OT_ERROR_NONE            Successfully read the value.
      * @retval OT_ERROR_PARSE           Failed to parse/decode the value.
@@ -488,7 +488,7 @@
      *
      * @param[out] aData                Reference to pointer variable to output the data.
      *                                  On success, the pointer variable is updated.
-     * @param[out] aDataLength          Reference to variable to out the data length (number of bytes).
+     * @param[out] aDataLen             Reference to variable to out the data length (number of bytes).
      *
      * @retval OT_ERROR_NONE            Successfully read the value.
      * @retval OT_ERROR_PARSE           Failed to parse/decode the value.
diff --git a/src/lib/spinel/spinel_encoder.hpp b/src/lib/spinel/spinel_encoder.hpp
index 44ea078..0790054 100644
--- a/src/lib/spinel/spinel_encoder.hpp
+++ b/src/lib/spinel/spinel_encoder.hpp
@@ -435,7 +435,7 @@
      * If no buffer space is available, this method will discard and clear the current input frame and return the
      * error status `OT_ERROR_NO_BUFS`.
      *
-     * @param[in]  aExtAddress          A pointer to a buffer containing the EUI64 value.
+     * @param[in]  aEui64               A pointer to a buffer containing the EUI64 value.
      *
      * @retval OT_ERROR_NONE            Successfully added given value to the frame.
      * @retval OT_ERROR_NO_BUFS         Insufficient buffer space available to add the EUI64 value.
diff --git a/src/ncp/ncp_base.cpp b/src/ncp/ncp_base.cpp
index 1b710c6..1c35701 100644
--- a/src/ncp/ncp_base.cpp
+++ b/src/ncp/ncp_base.cpp
@@ -2630,14 +2630,4 @@
     va_end(args);
 }
 
-extern "C" void otPlatLogLine(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aLogLine)
-{
-    ot::Ncp::NcpBase *ncp = ot::Ncp::NcpBase::GetNcpInstance();
-
-    if (ncp != nullptr)
-    {
-        ncp->Log(aLogLevel, aLogRegion, aLogLine);
-    }
-}
-
 #endif // (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_APP)
diff --git a/src/ncp/ncp_base_mtd.cpp b/src/ncp/ncp_base_mtd.cpp
index 2810a86..bd1f227 100644
--- a/src/ncp/ncp_base_mtd.cpp
+++ b/src/ncp/ncp_base_mtd.cpp
@@ -2682,27 +2682,28 @@
 
 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_MSG_BUFFER_COUNTERS>(void)
 {
-    otError      error;
+    otError      error = OT_ERROR_NONE;
     otBufferInfo bufferInfo;
 
     otMessageGetBufferInfo(mInstance, &bufferInfo);
 
     SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.mTotalBuffers));
     SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.mFreeBuffers));
-    SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.m6loSendMessages));
-    SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.m6loSendBuffers));
-    SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.m6loReassemblyMessages));
-    SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.m6loReassemblyBuffers));
-    SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.mIp6Messages));
-    SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.mIp6Buffers));
-    SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.mMplMessages));
-    SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.mMplBuffers));
-    SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.mMleMessages));
-    SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.mMleBuffers));
-    SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.mArpMessages));
-    SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.mArpBuffers));
-    SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.mCoapMessages));
-    SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.mCoapBuffers));
+
+    SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.m6loSendQueue.mNumMessages));
+    SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.m6loSendQueue.mNumBuffers));
+    SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.m6loReassemblyQueue.mNumMessages));
+    SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.m6loReassemblyQueue.mNumBuffers));
+    SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.mIp6Queue.mNumMessages));
+    SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.mIp6Queue.mNumBuffers));
+    SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.mMplQueue.mNumMessages));
+    SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.mMplQueue.mNumBuffers));
+    SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.mMleQueue.mNumMessages));
+    SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.mMleQueue.mNumBuffers));
+    SuccessOrExit(error = mEncoder.WriteUint16(0)); // Write zero for ARP for backward compatibility.
+    SuccessOrExit(error = mEncoder.WriteUint16(0));
+    SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.mCoapQueue.mNumMessages));
+    SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.mCoapQueue.mNumBuffers));
 
 exit:
     return error;
@@ -4221,11 +4222,6 @@
     {
         uint8_t flags = static_cast<uint8_t>(aResult->mVersion << SPINEL_BEACON_THREAD_FLAG_VERSION_SHIFT);
 
-        if (aResult->mIsJoinable)
-        {
-            flags |= SPINEL_BEACON_THREAD_FLAG_JOINABLE;
-        }
-
         if (aResult->mIsNative)
         {
             flags |= SPINEL_BEACON_THREAD_FLAG_NATIVE;
diff --git a/src/posix/CMakeLists.txt b/src/posix/CMakeLists.txt
index 1353ce0..2563451 100644
--- a/src/posix/CMakeLists.txt
+++ b/src/posix/CMakeLists.txt
@@ -85,7 +85,7 @@
 
 if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
     set(CPACK_GENERATOR "DEB")
-    set(CPACK_DEBIAN_PACKAGE_MAINTAINER "OpenThread Authors <openthread-users@googlegroups.com")
-    set(CPACK_PACKAGE_CONTACT "OpenThread Authors <openthread-users@googlegroups.com")
+    set(CPACK_DEBIAN_PACKAGE_MAINTAINER "OpenThread Authors (https://github.com/openthread/openthread)")
+    set(CPACK_PACKAGE_CONTACT "OpenThread Authors (https://github.com/openthread/openthread)")
     include(CPack)
 endif()
diff --git a/src/posix/main.c b/src/posix/main.c
index ebecb47..df014ea 100644
--- a/src/posix/main.c
+++ b/src/posix/main.c
@@ -64,14 +64,11 @@
 #include "cli/cli_config.h"
 #endif
 #include <common/code_utils.hpp>
-#include <common/logging.hpp>
 #include <lib/platform/exit_code.h>
 #include <openthread/openthread-system.h>
 #include <openthread/platform/misc.h>
 
-#ifndef OPENTHREAD_ENABLE_COVERAGE
-#define OPENTHREAD_ENABLE_COVERAGE 0
-#endif
+#include "lib/platform/reset_util.h"
 
 /**
  * This function initializes NCP app.
@@ -90,7 +87,7 @@
 /**
  * This function updates the file descriptor sets with file descriptors used by console.
  *
- * @param[inout]    aMainloop   A pointer to the mainloop context.
+ * @param[in,out]   aMainloop   A pointer to the mainloop context.
  *
  */
 void otAppNcpProcess(const otSysMainloopContext *aContext);
@@ -112,7 +109,7 @@
 /**
  * This function updates the file descriptor sets with file descriptors used by console.
  *
- * @param[inout]    aMainloop   A pointer to the mainloop context.
+ * @param[in,out]   aMainloop   A pointer to the mainloop context.
  *
  */
 void otAppCliUpdate(otSysMainloopContext *aMainloop);
@@ -133,10 +130,6 @@
     bool             mIsVerbose;         ///< Whether to print log to stderr.
 } PosixConfig;
 
-static jmp_buf gResetJump;
-
-void __gcov_flush();
-
 /**
  * This enumeration defines the argument return values.
  *
@@ -371,14 +364,7 @@
     prctl(PR_SET_PDEATHSIG, SIGHUP);
 #endif
 
-    if (setjmp(gResetJump))
-    {
-        alarm(0);
-#if OPENTHREAD_ENABLE_COVERAGE
-        __gcov_flush();
-#endif
-        execvp(argv[0], argv);
-    }
+    OT_SETUP_RESET_JUMP(argv);
 
     ParseArg(argc, argv, &config);
     openlog(argv[0], LOG_PID | (config.mIsVerbose ? LOG_PERROR : 0), LOG_DAEMON);
diff --git a/src/posix/platform/CMakeLists.txt b/src/posix/platform/CMakeLists.txt
index 6ba5e32..272b08e 100644
--- a/src/posix/platform/CMakeLists.txt
+++ b/src/posix/platform/CMakeLists.txt
@@ -62,6 +62,13 @@
     )
 endif()
 
+option(OT_POSIX_SECURE_SETTINGS "enable secure settings" OFF)
+if (OT_POSIX_SECURE_SETTINGS)
+    target_compile_definitions(ot-posix-config
+        INTERFACE "OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE=1"
+    )
+endif()
+
 set(OT_POSIX_CONFIG_RCP_BUS "" CACHE STRING "RCP bus type")
 if(OT_POSIX_CONFIG_RCP_BUS)
     target_compile_definitions(ot-posix-config
diff --git a/src/posix/platform/firewall.cpp b/src/posix/platform/firewall.cpp
index 671dff7..19ad8c5 100644
--- a/src/posix/platform/firewall.cpp
+++ b/src/posix/platform/firewall.cpp
@@ -36,8 +36,10 @@
 
 #include <string.h>
 
+#include <openthread/logging.h>
+#include <openthread/netdata.h>
+
 #include "common/code_utils.hpp"
-#include "openthread/netdata.h"
 #include "posix/platform/utils.hpp"
 
 namespace ot {
diff --git a/src/posix/platform/firewall.hpp b/src/posix/platform/firewall.hpp
index 8c4378a..a564c56 100644
--- a/src/posix/platform/firewall.hpp
+++ b/src/posix/platform/firewall.hpp
@@ -33,7 +33,6 @@
 
 #if OPENTHREAD_POSIX_CONFIG_FIREWALL_ENABLE
 
-#include "common/logging.hpp"
 #include "openthread/thread.h"
 
 namespace ot {
diff --git a/src/posix/platform/hdlc_interface.cpp b/src/posix/platform/hdlc_interface.cpp
index 264a123..391fdac 100644
--- a/src/posix/platform/hdlc_interface.cpp
+++ b/src/posix/platform/hdlc_interface.cpp
@@ -57,8 +57,9 @@
 #include <termios.h>
 #include <unistd.h>
 
+#include <openthread/logging.h>
+
 #include "common/code_utils.hpp"
-#include "common/logging.hpp"
 
 #ifdef __APPLE__
 
diff --git a/src/posix/platform/hdlc_interface.hpp b/src/posix/platform/hdlc_interface.hpp
index 73c1a39..32ada03 100644
--- a/src/posix/platform/hdlc_interface.hpp
+++ b/src/posix/platform/hdlc_interface.hpp
@@ -120,10 +120,10 @@
     /**
      * This method updates the file descriptor sets with file descriptors used by the radio driver.
      *
-     * @param[inout]  aReadFdSet   A reference to the read file descriptors.
-     * @param[inout]  aWriteFdSet  A reference to the write file descriptors.
-     * @param[inout]  aMaxFd       A reference to the max file descriptor.
-     * @param[inout]  aTimeout     A reference to the timeout.
+     * @param[in,out]  aReadFdSet   A reference to the read file descriptors.
+     * @param[in,out]  aWriteFdSet  A reference to the write file descriptors.
+     * @param[in,out]  aMaxFd       A reference to the max file descriptor.
+     * @param[in,out]  aTimeout     A reference to the timeout.
      *
      */
     void UpdateFdSet(fd_set &aReadFdSet, fd_set &aWriteFdSet, int &aMaxFd, struct timeval &aTimeout);
diff --git a/src/posix/platform/include/openthread/openthread-system.h b/src/posix/platform/include/openthread/openthread-system.h
index dfc4f1f..e821070 100644
--- a/src/posix/platform/include/openthread/openthread-system.h
+++ b/src/posix/platform/include/openthread/openthread-system.h
@@ -123,7 +123,7 @@
  * This function updates the file descriptor sets with file descriptors used by OpenThread drivers.
  *
  * @param[in]       aInstance   The OpenThread instance structure.
- * @param[inout]    aMainloop   A pointer to the mainloop context.
+ * @param[in,out]   aMainloop   A pointer to the mainloop context.
  *
  */
 void otSysMainloopUpdate(otInstance *aInstance, otSysMainloopContext *aMainloop);
@@ -131,7 +131,7 @@
 /**
  * This function polls OpenThread's mainloop.
  *
- * @param[inout]    aMainloop   A pointer to the mainloop context.
+ * @param[in,out]   aMainloop   A pointer to the mainloop context.
  *
  * @returns value returned from select().
  *
diff --git a/src/posix/platform/include/openthread/platform/secure_settings.h b/src/posix/platform/include/openthread/platform/secure_settings.h
index 90ce5c3..d206c26 100644
--- a/src/posix/platform/include/openthread/platform/secure_settings.h
+++ b/src/posix/platform/include/openthread/platform/secure_settings.h
@@ -79,15 +79,15 @@
  * Note that the underlying storage implementation is not required to maintain the order of settings with multiple
  * values. The order of such values MAY change after ANY write operation to the store.
  *
- * @param[in]     aInstance     The OpenThread instance structure.
- * @param[in]     aKey          The key associated with the requested setting.
- * @param[in]     aIndex        The index of the specific item to get.
- * @param[out]    aValue        A pointer to where the value of the setting should be written. May be set to NULL if
- *                              just testing for the presence or length of a setting.
- * @param[inout]  aValueLength  A pointer to the length of the value. When called, this pointer should point to an
- *                              integer containing the maximum value size that can be written to aValue. At return,
- *                              the actual length of the setting is written. This may be set to NULL if performing
- *                              a presence check.
+ * @param[in]      aInstance     The OpenThread instance structure.
+ * @param[in]      aKey          The key associated with the requested setting.
+ * @param[in]      aIndex        The index of the specific item to get.
+ * @param[out]     aValue        A pointer to where the value of the setting should be written. May be set to NULL if
+ *                               just testing for the presence or length of a setting.
+ * @param[in,out]  aValueLength  A pointer to the length of the value. When called, this pointer should point to an
+ *                               integer containing the maximum value size that can be written to aValue. At return,
+ *                               the actual length of the setting is written. This may be set to NULL if performing
+ *                               a presence check.
  *
  * @retval OT_ERROR_NONE             The given setting was found and fetched successfully.
  * @retval OT_ERROR_NOT_FOUND        The given setting was not found in the setting store.
diff --git a/src/posix/platform/infra_if.cpp b/src/posix/platform/infra_if.cpp
index 9f93d2b..11f1562 100644
--- a/src/posix/platform/infra_if.cpp
+++ b/src/posix/platform/infra_if.cpp
@@ -331,7 +331,7 @@
 #ifdef __linux__
     rval = setsockopt(mInfraIfIcmp6Socket, SOL_SOCKET, SO_BINDTODEVICE, mInfraIfName, strlen(mInfraIfName));
 #else  // __NetBSD__ || __FreeBSD__ || __APPLE__
-    rval = setsockopt(mInfraIfIcmp6Socket, IPPROTO_IP, IP_BOUND_IF, &mInfraIfIndex, sizeof(mInfraIfIndex));
+    rval = setsockopt(mInfraIfIcmp6Socket, IPPROTO_IPV6, IPV6_BOUND_IF, &mInfraIfIndex, sizeof(mInfraIfIndex));
 #endif // __linux__
     VerifyOrDie(rval == 0, OT_EXIT_ERROR_ERRNO);
 
diff --git a/src/posix/platform/infra_if.hpp b/src/posix/platform/infra_if.hpp
index 11d777f..ccd2b12 100644
--- a/src/posix/platform/infra_if.hpp
+++ b/src/posix/platform/infra_if.hpp
@@ -53,7 +53,7 @@
     /**
      * This method updates the fd_set and timeout for mainloop.
      *
-     * @param[inout]    aContext    A reference to the mainloop context.
+     * @param[in,out]   aContext    A reference to the mainloop context.
      *
      */
     void Update(otSysMainloopContext &aContext) override;
diff --git a/src/posix/platform/mainloop.hpp b/src/posix/platform/mainloop.hpp
index e6e7e25..d4f6ac8 100644
--- a/src/posix/platform/mainloop.hpp
+++ b/src/posix/platform/mainloop.hpp
@@ -52,7 +52,7 @@
     /**
      * This method registers events in the mainloop.
      *
-     * @param[inout]    aContext    A reference to the mainloop context.
+     * @param[in,out]   aContext    A reference to the mainloop context.
      *
      */
     virtual void Update(otSysMainloopContext &aContext) = 0;
@@ -79,7 +79,7 @@
     /**
      * This method updates event polls in the mainloop context.
      *
-     * @param[inout]    aContext    A reference to the mainloop context.
+     * @param[in,out]   aContext    A reference to the mainloop context.
      *
      */
     void Update(otSysMainloopContext &aContext);
diff --git a/src/posix/platform/misc.cpp b/src/posix/platform/misc.cpp
index 5de8b9d..77ef07a 100644
--- a/src/posix/platform/misc.cpp
+++ b/src/posix/platform/misc.cpp
@@ -35,10 +35,10 @@
 #include <sys/socket.h>
 #include <unistd.h>
 
+#include <openthread/logging.h>
 #include <openthread/platform/misc.h>
 
 #include "common/code_utils.hpp"
-#include "common/logging.hpp"
 
 otPlatResetReason          gPlatResetReason   = OT_PLAT_RESET_REASON_POWER_ON;
 static otPlatMcuPowerState gPlatMcuPowerState = OT_PLAT_MCU_POWER_STATE_ON;
diff --git a/src/posix/platform/multicast_routing.cpp b/src/posix/platform/multicast_routing.cpp
index ab35d03..6580782 100644
--- a/src/posix/platform/multicast_routing.cpp
+++ b/src/posix/platform/multicast_routing.cpp
@@ -46,9 +46,10 @@
 #endif
 
 #include <openthread/backbone_router_ftd.h>
+#include <openthread/logging.h>
 
+#include "core/common/arg_macros.hpp"
 #include "core/common/debug.hpp"
-#include "core/common/logging.hpp"
 
 namespace ot {
 namespace Posix {
@@ -479,7 +480,7 @@
 
 void MulticastRoutingManager::DumpMulticastForwardingCache(void) const
 {
-#if OPENTHREAD_CONFIG_LOG_PLATFORM && OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_DEBG
+#if OPENTHREAD_CONFIG_LOG_PLATFORM && (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_DEBG)
     otLogDebgPlat("MulticastRoutingManager: ==================== MFC ENTRIES ====================");
 
     for (const MulticastForwardingCache &mfc : mMulticastForwardingCacheTable)
diff --git a/src/posix/platform/netif.cpp b/src/posix/platform/netif.cpp
index d47210e..572d59f 100644
--- a/src/posix/platform/netif.cpp
+++ b/src/posix/platform/netif.cpp
@@ -137,16 +137,17 @@
 
 #endif // defined(__APPLE__) || defined(__NetBSD__) || defined(__FreeBSD__)
 
+#include <openthread/border_router.h>
 #include <openthread/icmp6.h>
 #include <openthread/instance.h>
 #include <openthread/ip6.h>
+#include <openthread/logging.h>
 #include <openthread/message.h>
 #include <openthread/netdata.h>
 #include <openthread/platform/misc.h>
 
 #include "common/code_utils.hpp"
 #include "common/debug.hpp"
-#include "common/logging.hpp"
 #include "net/ip6_address.hpp"
 
 unsigned int gNetifIndex = 0;
@@ -193,6 +194,13 @@
 static uint32_t sNetlinkSequence = 0; ///< Netlink message sequence.
 #endif
 
+#if OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE && __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__
 static constexpr uint32_t kExternalRoutePriority  = OPENTHREAD_POSIX_CONFIG_EXTERNAL_ROUTE_PRIORITY;
 static constexpr uint8_t  kMaxExternalRoutesNum   = OPENTHREAD_POSIX_CONFIG_MAX_EXTERNAL_ROUTE_NUM;
@@ -322,10 +330,42 @@
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wcast-align"
 
-static void UpdateUnicastLinux(const otIp6AddressInfo &aAddressInfo, bool aIsAdded)
+void AddRtAttr(struct nlmsghdr *aHeader, uint32_t aMaxLen, uint8_t aType, const void *aData, uint8_t aLen)
 {
+    uint8_t        len = RTA_LENGTH(aLen);
     struct rtattr *rta;
 
+    assert(NLMSG_ALIGN(aHeader->nlmsg_len) + RTA_ALIGN(len) <= aMaxLen);
+    OT_UNUSED_VARIABLE(aMaxLen);
+
+    rta           = (struct rtattr *)((char *)(aHeader) + NLMSG_ALIGN((aHeader)->nlmsg_len));
+    rta->rta_type = aType;
+    rta->rta_len  = len;
+    if (aLen)
+    {
+        memcpy(RTA_DATA(rta), aData, aLen);
+    }
+    aHeader->nlmsg_len = NLMSG_ALIGN(aHeader->nlmsg_len) + RTA_ALIGN(len);
+}
+
+void AddRtAttrUint32(struct nlmsghdr *aHeader, uint32_t aMaxLen, uint8_t aType, uint32_t aData)
+{
+    AddRtAttr(aHeader, aMaxLen, aType, &aData, sizeof(aData));
+}
+
+#if OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE
+static bool IsOmrAddress(otInstance *aInstance, const otIp6AddressInfo &aAddressInfo)
+{
+    otIp6Prefix addressPrefix{*aAddressInfo.mAddress, aAddressInfo.mPrefixLength};
+
+    return otNetDataContainsOmrPrefix(aInstance, &addressPrefix);
+}
+#endif
+
+static void UpdateUnicastLinux(otInstance *aInstance, const otIp6AddressInfo &aAddressInfo, bool aIsAdded)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+
     struct
     {
         struct nlmsghdr  nh;
@@ -347,28 +387,37 @@
     req.ifa.ifa_scope     = aAddressInfo.mScope;
     req.ifa.ifa_index     = gNetifIndex;
 
-    rta           = reinterpret_cast<struct rtattr *>((reinterpret_cast<char *>(&req)) + NLMSG_ALIGN(req.nh.nlmsg_len));
-    rta->rta_type = IFA_LOCAL;
-    rta->rta_len  = RTA_LENGTH(sizeof(*aAddressInfo.mAddress));
-
-    memcpy(RTA_DATA(rta), aAddressInfo.mAddress, sizeof(*aAddressInfo.mAddress));
-
-    req.nh.nlmsg_len = NLMSG_ALIGN(req.nh.nlmsg_len) + rta->rta_len;
+    AddRtAttr(&req.nh, sizeof(req), IFA_LOCAL, aAddressInfo.mAddress, sizeof(*aAddressInfo.mAddress));
 
     if (!aAddressInfo.mPreferred)
     {
         struct ifa_cacheinfo cacheinfo;
 
-        rta           = reinterpret_cast<struct rtattr *>((reinterpret_cast<char *>(rta)) + rta->rta_len);
-        rta->rta_type = IFA_CACHEINFO;
-        rta->rta_len  = RTA_LENGTH(sizeof(cacheinfo));
-
         memset(&cacheinfo, 0, sizeof(cacheinfo));
         cacheinfo.ifa_valid = UINT32_MAX;
 
-        memcpy(RTA_DATA(rta), &cacheinfo, sizeof(cacheinfo));
+        AddRtAttr(&req.nh, sizeof(req), IFA_CACHEINFO, &cacheinfo, sizeof(cacheinfo));
+    }
 
-        req.nh.nlmsg_len += rta->rta_len;
+#if OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE
+    if (IsOmrAddress(aInstance, aAddressInfo))
+    {
+        // Remove prefix route for OMR address if `OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE` is enabled to
+        // avoid having two routes.
+        if (aIsAdded)
+        {
+            AddRtAttrUint32(&req.nh, sizeof(req), IFA_FLAGS, IFA_F_NOPREFIXROUTE);
+        }
+    }
+    else
+#endif
+    {
+#if OPENTHREAD_POSIX_CONFIG_NETIF_PREFIX_ROUTE_METRIC > 0
+        if (aAddressInfo.mScope > ot::Ip6::Address::kLinkLocalScope)
+        {
+            AddRtAttrUint32(&req.nh, sizeof(req), IFA_RT_PRIORITY, OPENTHREAD_POSIX_CONFIG_NETIF_PREFIX_ROUTE_METRIC);
+        }
+#endif
     }
 
     if (send(sNetlinkFd, &req, req.nh.nlmsg_len, 0) != -1)
@@ -394,7 +443,7 @@
     assert(sIpFd >= 0);
 
 #if defined(__linux__)
-    UpdateUnicastLinux(aAddressInfo, aIsAdded);
+    UpdateUnicastLinux(aInstance, aAddressInfo, aIsAdded);
 #elif defined(__APPLE__) || defined(__NetBSD__) || defined(__FreeBSD__)
     {
         int                 rval;
@@ -515,31 +564,10 @@
     }
 }
 
-#if OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE && __linux__
-void AddRtAttr(struct nlmsghdr *aHeader, uint32_t aMaxLen, uint8_t aType, const void *aData, uint8_t aLen)
-{
-    uint8_t        len = RTA_LENGTH(aLen);
-    struct rtattr *rta;
+#if __linux__ && \
+    (OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE || OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE)
 
-    assert(NLMSG_ALIGN(aHeader->nlmsg_len) + RTA_ALIGN(len) <= aMaxLen);
-    OT_UNUSED_VARIABLE(aMaxLen);
-
-    rta           = (struct rtattr *)((char *)(aHeader) + NLMSG_ALIGN((aHeader)->nlmsg_len));
-    rta->rta_type = aType;
-    rta->rta_len  = len;
-    if (aLen)
-    {
-        memcpy(RTA_DATA(rta), aData, aLen);
-    }
-    aHeader->nlmsg_len = NLMSG_ALIGN(aHeader->nlmsg_len) + RTA_ALIGN(len);
-}
-
-void AddRtAttrUint32(struct nlmsghdr *aHeader, uint32_t aMaxLen, uint8_t aType, uint32_t aData)
-{
-    AddRtAttr(aHeader, aMaxLen, aType, &aData, sizeof(aData));
-}
-
-static otError AddExternalRoute(const otIp6Prefix &aPrefix)
+static otError AddRoute(const otIp6Prefix &aPrefix, uint32_t aPriority)
 {
     constexpr unsigned int kBufSize = 128;
     struct
@@ -555,7 +583,6 @@
 
     VerifyOrExit(netifIdx > 0, error = OT_ERROR_INVALID_STATE);
     VerifyOrExit(sNetlinkFd >= 0, error = OT_ERROR_INVALID_STATE);
-    VerifyOrExit(sAddedExternalRoutesNum < kMaxExternalRoutesNum, error = OT_ERROR_NO_BUFS);
 
     req.header.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL;
 
@@ -577,7 +604,7 @@
     otIp6AddressToString(&aPrefix.mPrefix, addrBuf, OT_IP6_ADDRESS_STRING_SIZE);
     inet_pton(AF_INET6, addrBuf, data);
     AddRtAttr(reinterpret_cast<nlmsghdr *>(&req), sizeof(req), RTA_DST, data, sizeof(data));
-    AddRtAttrUint32(&req.header, sizeof(req), RTA_PRIORITY, kExternalRoutePriority);
+    AddRtAttrUint32(&req.header, sizeof(req), RTA_PRIORITY, aPriority);
     AddRtAttrUint32(&req.header, sizeof(req), RTA_OIF, netifIdx);
 
     if (send(sNetlinkFd, &req, sizeof(req), 0) < 0)
@@ -589,7 +616,7 @@
     return error;
 }
 
-static otError DeleteExternalRoute(const otIp6Prefix &aPrefix)
+static otError DeleteRoute(const otIp6Prefix &aPrefix)
 {
     constexpr unsigned int kBufSize = 512;
     struct
@@ -638,6 +665,105 @@
     return error;
 }
 
+#endif // __linux__ && (OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE ||
+       // OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE)
+
+#if OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE && __linux__
+
+static bool HasAddedOmrRoute(const otIp6Prefix &aOmrPrefix)
+{
+    bool found = false;
+
+    for (uint8_t i = 0; i < sAddedOmrRoutesNum; ++i)
+    {
+        if (otIp6ArePrefixesEqual(&sAddedOmrRoutes[i], &aOmrPrefix))
+        {
+            found = true;
+            break;
+        }
+    }
+
+    return found;
+}
+
+static otError AddOmrRoute(const otIp6Prefix &aPrefix)
+{
+    otError error;
+
+    VerifyOrExit(sAddedOmrRoutesNum < kMaxOmrRoutesNum, error = OT_ERROR_NO_BUFS);
+
+    error = AddRoute(aPrefix, kOmrRoutesPriority);
+exit:
+    return error;
+}
+
+static void UpdateOmrRoutes(otInstance *aInstance)
+{
+    otError               error;
+    otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
+    otBorderRouterConfig  config;
+    char                  prefixString[OT_IP6_PREFIX_STRING_SIZE];
+
+    // Remove kernel routes if the OMR prefix is removed
+    for (int i = 0; i < static_cast<int>(sAddedOmrRoutesNum); ++i)
+    {
+        if (otNetDataContainsOmrPrefix(aInstance, &sAddedOmrRoutes[i]))
+        {
+            continue;
+        }
+
+        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));
+        }
+        else
+        {
+            sAddedOmrRoutes[i] = sAddedOmrRoutes[sAddedOmrRoutesNum - 1];
+            --sAddedOmrRoutesNum;
+            --i;
+            otLogInfoPlat("[netif] Successfully deleted an OMR route %s in kernel", prefixString);
+        }
+    }
+
+    // Add kernel routes for OMR prefixes in Network Data
+    while (otNetDataGetNextOnMeshPrefix(aInstance, &iterator, &config) == OT_ERROR_NONE)
+    {
+        if (HasAddedOmrRoute(config.mPrefix))
+        {
+            continue;
+        }
+
+        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));
+        }
+        else
+        {
+            sAddedOmrRoutes[sAddedOmrRoutesNum++] = config.mPrefix;
+            otLogInfoPlat("[netif] Successfully added an OMR route %s in kernel: %s", prefixString);
+        }
+    }
+}
+
+#endif // OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE && __linux__
+
+#if OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE && __linux__
+
+static otError AddExternalRoute(const otIp6Prefix &aPrefix)
+{
+    otError error;
+
+    VerifyOrExit(sAddedExternalRoutesNum < kMaxExternalRoutesNum, error = OT_ERROR_NO_BUFS);
+
+    error = AddRoute(aPrefix, kExternalRoutePriority);
+exit:
+    return error;
+}
+
 bool HasExternalRouteInNetData(otInstance *aInstance, const otIp6Prefix &aExternalRoute)
 {
     otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
@@ -683,9 +809,10 @@
         {
             continue;
         }
-        if ((error = DeleteExternalRoute(sAddedExternalRoutes[i])) != OT_ERROR_NONE)
+
+        otIp6PrefixToString(&sAddedExternalRoutes[i], prefixString, sizeof(prefixString));
+        if ((error = DeleteRoute(sAddedExternalRoutes[i])) != OT_ERROR_NONE)
         {
-            otIp6PrefixToString(&sAddedExternalRoutes[i], prefixString, sizeof(prefixString));
             otLogWarnPlat("[netif] Failed to delete an external route %s in kernel: %s", prefixString,
                           otThreadErrorToString(error));
         }
@@ -694,6 +821,7 @@
             sAddedExternalRoutes[i] = sAddedExternalRoutes[sAddedExternalRoutesNum - 1];
             --sAddedExternalRoutesNum;
             --i;
+            otLogWarnPlat("[netif] Successfully deleted an external route %s in kernel", prefixString);
         }
     }
 
@@ -705,15 +833,17 @@
         }
         VerifyOrExit(sAddedExternalRoutesNum < kMaxExternalRoutesNum,
                      otLogWarnPlat("[netif] No buffer to add more external routes in kernel"));
+
+        otIp6PrefixToString(&config.mPrefix, prefixString, sizeof(prefixString));
         if ((error = AddExternalRoute(config.mPrefix)) != OT_ERROR_NONE)
         {
-            otIp6PrefixToString(&config.mPrefix, prefixString, sizeof(prefixString));
             otLogWarnPlat("[netif] 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: %s", prefixString);
         }
     }
 exit:
@@ -739,18 +869,18 @@
     {
         UpdateLink(aInstance);
     }
-#if OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE && __linux__
     if (OT_CHANGED_THREAD_NETDATA & aFlags)
     {
+#if OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE && __linux__
+        UpdateOmrRoutes(aInstance);
+#endif
+#if OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE && __linux__
         UpdateExternalRoutes(aInstance);
-    }
 #endif
 #if OPENTHREAD_POSIX_CONFIG_FIREWALL_ENABLE
-    if (OT_CHANGED_THREAD_NETDATA & aFlags)
-    {
         ot::Posix::UpdateIpSets(aInstance);
-    }
 #endif
+    }
 }
 
 static void processReceive(otMessage *aMessage, void *aContext)
@@ -776,7 +906,7 @@
 
 #if OPENTHREAD_POSIX_LOG_TUN_PACKETS
     otLogInfoPlat("[netif] Packet from NCP (%u bytes)", static_cast<uint16_t>(length));
-    otDumpInfo(OT_LOG_REGION_PLATFORM, "", &packet[offset], length);
+    otDumpInfoPlat("", &packet[offset], length);
 #endif
 
 #if defined(__APPLE__) || defined(__NetBSD__) || defined(__FreeBSD__)
@@ -831,7 +961,7 @@
 
 #if OPENTHREAD_POSIX_LOG_TUN_PACKETS
     otLogInfoPlat("[netif] Packet to NCP (%hu bytes)", static_cast<uint16_t>(rval));
-    otDumpInfo(OT_LOG_REGION_PLATFORM, "", &packet[offset], static_cast<size_t>(rval));
+    otDumpInfoPlat("", &packet[offset], static_cast<size_t>(rval));
 #endif
 
     SuccessOrExit(error = otMessageAppend(message, &packet[offset], static_cast<uint16_t>(rval)));
@@ -849,7 +979,7 @@
     {
         if (error == OT_ERROR_DROP)
         {
-            otLogNotePlat("[netif] Message dropped by Thread", otThreadErrorToString(error));
+            otLogInfoPlat("[netif] Message dropped by Thread", otThreadErrorToString(error));
         }
         else
         {
@@ -863,7 +993,7 @@
     OT_UNUSED_VARIABLE(aAddress);
 
     if ((error == OT_ERROR_NONE) || ((isAdd) && (error == OT_ERROR_ALREADY || error == OT_ERROR_REJECTED)) ||
-        ((!isAdd) && (error == OT_ERROR_NOT_FOUND)))
+        ((!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(),
@@ -950,7 +1080,7 @@
                 }
 
                 logAddrEvent(/* isAdd */ false, addr, error);
-                if (error == OT_ERROR_NOT_FOUND)
+                if (error == OT_ERROR_NOT_FOUND || error == OT_ERROR_REJECTED)
                 {
                     error = OT_ERROR_NONE;
                 }
diff --git a/src/posix/platform/openthread-posix-config.h b/src/posix/platform/openthread-posix-config.h
index 9abf0ad..62e1324 100644
--- a/src/posix/platform/openthread-posix-config.h
+++ b/src/posix/platform/openthread-posix-config.h
@@ -127,6 +127,55 @@
 #endif
 
 /**
+ * @def OPENTHREAD_POSIX_CONFIG_NETIF_PREFIX_ROUTE_METRIC
+ *
+ * This setting configures the prefix route metric on the Thread network interface.
+ * Define as 0 to use use the default prefix route metric.
+ *
+ * Note: The feature works on Linux kernel v4.18+.
+ *
+ */
+#ifndef OPENTHREAD_POSIX_CONFIG_NETIF_PREFIX_ROUTE_METRIC
+#define OPENTHREAD_POSIX_CONFIG_NETIF_PREFIX_ROUTE_METRIC 0
+#endif
+
+/**
+ * @def OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE
+ *
+ * Define as 1 to add OMR routes to POSIX kernel when OMR prefixes are changed in netdata.
+ *
+ * Note: This feature can be used to add OMR routes with non-default priority. Unlike
+ * `OPENTHREAD_POSIX_CONFIG_NETIF_PREFIX_ROUTE_METRIC`, it works on Linux kernels before v4.18.
+ * However, `OPENTHREAD_POSIX_CONFIG_NETIF_PREFIX_ROUTE_METRIC` should be preferred on Linux kernel v4.18+.
+ *
+ */
+#ifndef OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE
+#define OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE 0
+#endif
+
+/**
+ * @def OPENTHREAD_POSIX_CONFIG_OMR_ROUTES_PRIORITY
+ *
+ * This macro defines the priority of OMR routes added to kernel. The larger the number, the lower the priority. We
+ * need to assign a high priority to such routes so that kernel prefers the Thread link rather than infrastructure.
+ * Otherwise we may unnecessarily transmit packets via infrastructure, which potentially causes looping issue.
+ *
+ */
+#ifndef OPENTHREAD_POSIX_CONFIG_OMR_ROUTES_PRIORITY
+#define OPENTHREAD_POSIX_CONFIG_OMR_ROUTES_PRIORITY 1
+#endif
+
+/**
+ * @def OPENTHREAD_POSIX_CONFIG_MAX_OMR_ROUTES_NUM
+ *
+ * This macro defines the max number of OMR routes that can be added to kernel.
+ *
+ */
+#ifndef OPENTHREAD_POSIX_CONFIG_MAX_OMR_ROUTES_NUM
+#define OPENTHREAD_POSIX_CONFIG_MAX_OMR_ROUTES_NUM OPENTHREAD_CONFIG_IP6_SLAAC_NUM_ADDRESSES
+#endif
+
+/**
  * @def OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE
  *
  * Define as 1 to add external routes to POSIX kernel when external routes are changed in netdata.
@@ -218,4 +267,15 @@
 #error "OPENTHREAD_CONFIG_POSIX_TREL_USE_NETLINK_SOCKET was removed (no longer applicable with TREL over DNS-SD)."
 #endif
 
+/**
+ * @def OPENTHREAD_POSIX_CONFIG_TREL_UDP_PORT
+ *
+ * This setting configures the TREL UDP port number.
+ * Define as 0 to use an ephemeral port number.
+ *
+ */
+#ifndef OPENTHREAD_POSIX_CONFIG_TREL_UDP_PORT
+#define OPENTHREAD_POSIX_CONFIG_TREL_UDP_PORT 0
+#endif
+
 #endif // OPENTHREAD_PLATFORM_CONFIG_H_
diff --git a/src/posix/platform/platform-posix.h b/src/posix/platform/platform-posix.h
index 6ecbc50..919a319 100644
--- a/src/posix/platform/platform-posix.h
+++ b/src/posix/platform/platform-posix.h
@@ -48,11 +48,10 @@
 #include <openthread/error.h>
 #include <openthread/instance.h>
 #include <openthread/ip6.h>
+#include <openthread/logging.h>
 #include <openthread/openthread-system.h>
 #include <openthread/platform/time.h>
 
-#include "common/logging.hpp"
-
 #include "lib/platform/exit_code.h"
 #include "lib/url/url.hpp"
 
@@ -184,10 +183,10 @@
 /**
  * This function updates the file descriptor sets with file descriptors used by the radio driver.
  *
- * @param[inout]  aReadFdSet   A pointer to the read file descriptors.
- * @param[inout]  aWriteFdSet  A pointer to the write file descriptors.
- * @param[inout]  aMaxFd       A pointer to the max file descriptor.
- * @param[inout]  aTimeout     A pointer to the timeout.
+ * @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.
+ * @param[in,out]  aTimeout     A pointer to the timeout.
  *
  */
 void platformRadioUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, int *aMaxFd, struct timeval *aTimeout);
@@ -219,9 +218,9 @@
 /**
  * This function updates the file descriptor sets with file descriptors used by the UART driver.
  *
- * @param[inout]  aReadFdSet   A pointer to the read file descriptors.
- * @param[inout]  aWriteFdSet  A pointer to the write file descriptors.
- * @param[inout]  aMaxFd       A pointer to the max file descriptor.
+ * @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 platformUartUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, fd_set *aErrorFdSet, int *aMaxFd);
@@ -275,10 +274,10 @@
 /**
  * This function updates the file descriptor sets with file descriptors used by platform netif module.
  *
- * @param[inout]  aReadFdSet    A pointer to the read file descriptors.
- * @param[inout]  aWriteFdSet   A pointer to the write file descriptors.
- * @param[inout]  aErrorFdSet   A pointer to the error file descriptors.
- * @param[inout]  aMaxFd        A pointer to the max file descriptor.
+ * @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]  aErrorFdSet   A pointer to the error file descriptors.
+ * @param[in,out]  aMaxFd        A pointer to the max file descriptor.
  *
  */
 void platformNetifUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, fd_set *aErrorFdSet, int *aMaxFd);
@@ -333,11 +332,11 @@
  * This function updates the file descriptor sets with file descriptors
  * used by the virtual time simulation.
  *
- * @param[inout]  aReadFdSet   A pointer to the read file descriptors.
- * @param[inout]  aWriteFdSet  A pointer to the write file descriptors.
- * @param[inout]  aErrorFdSet  A pointer to the error file descriptors.
- * @param[inout]  aMaxFd       A pointer to the max file descriptor.
- * @param[inout]  aTimeout     A pointer to the timeout.
+ * @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]  aErrorFdSet  A pointer to the error file descriptors.
+ * @param[in,out]  aMaxFd       A pointer to the max file descriptor.
+ * @param[in,out]  aTimeout     A pointer to the timeout.
  *
  */
 void virtualTimeUpdateFdSet(fd_set *        aReadFdSet,
@@ -403,10 +402,10 @@
 /**
  * This function updates the file descriptor sets with file descriptors used by the TREL driver.
  *
- * @param[inout]  aReadFdSet   A pointer to the read file descriptors.
- * @param[inout]  aWriteFdSet  A pointer to the write file descriptors.
- * @param[inout]  aMaxFd       A pointer to the max file descriptor.
- * @param[inout]  aTimeout     A pointer to the timeout.
+ * @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.
+ * @param[in,out]  aTimeout     A pointer to the timeout.
  *
  */
 void platformTrelUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, int *aMaxFd, struct timeval *aTimeout);
diff --git a/src/posix/platform/radio.cpp b/src/posix/platform/radio.cpp
index 4109752..546e027 100644
--- a/src/posix/platform/radio.cpp
+++ b/src/posix/platform/radio.cpp
@@ -166,6 +166,15 @@
         VerifyOrDie(str == nullptr, OT_EXIT_INVALID_ARGUMENTS);
     }
 #endif // OPENTHREAD_POSIX_CONFIG_MAX_POWER_TABLE_ENABLE
+#if OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
+    {
+        const char *enableCoex = mRadioUrl.GetValue("enable-coex");
+        if (enableCoex != nullptr)
+        {
+            SuccessOrDie(sRadioSpinel.SetCoexEnabled(enableCoex[0] != '0'));
+        }
+    }
+#endif // OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
 }
 
 } // namespace Posix
diff --git a/src/posix/platform/radio_url.cpp b/src/posix/platform/radio_url.cpp
index b8ae8c5..54e9be0 100644
--- a/src/posix/platform/radio_url.cpp
+++ b/src/posix/platform/radio_url.cpp
@@ -91,6 +91,8 @@
     return "RadioURL:\n" OT_RADIO_URL_HELP_BUS OT_RADIO_URL_HELP_MAX_POWER_TABLE
            "    region[=region-code]          Set the radio's region code.\n"
            "    cca-threshold[=dbm]           Set the radio's CCA ED threshold in dBm measured at antenna connector.\n"
+           "    enable-coex[=1|0]             If not specified, RCP coex operates with its default configuration.\n"
+           "                                  Disable coex with 0, and enable it with other values.\n"
            "    fem-lnagain[=dbm]             Set the Rx LNA gain in dBm of the external FEM.\n"
            "    ncp-dataset                   Retrieve dataset from ncp.\n"
            "    no-reset                      Do not send Spinel reset command to RCP on initialization.\n"
diff --git a/src/posix/platform/settings.cpp b/src/posix/platform/settings.cpp
index ec105d9..7ea03ab 100644
--- a/src/posix/platform/settings.cpp
+++ b/src/posix/platform/settings.cpp
@@ -44,6 +44,7 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
+#include <openthread/logging.h>
 #include <openthread/platform/misc.h>
 #include <openthread/platform/radio.h>
 #include <openthread/platform/settings.h>
@@ -53,6 +54,7 @@
 
 #include "common/code_utils.hpp"
 #include "common/encoding.hpp"
+#include "posix/platform/settings.hpp"
 
 #include "system.hpp"
 
@@ -60,29 +62,19 @@
 
 static int sSettingsFd = -1;
 
-static otError platformSettingsDelete(otInstance *aInstance, uint16_t aKey, int aIndex, int *aSwapFd);
-
 #if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
-static const uint16_t *sKeys       = nullptr;
-static uint16_t        sKeysLength = 0;
+static const uint16_t *sSensitiveKeys       = nullptr;
+static uint16_t        sSensitiveKeysLength = 0;
 
-void otPlatSettingsSetCriticalKeys(otInstance *aInstance, const uint16_t *aKeys, uint16_t aKeysLength)
-{
-    OT_UNUSED_VARIABLE(aInstance);
-
-    sKeys       = aKeys;
-    sKeysLength = aKeysLength;
-}
-
-static bool isCriticalKey(uint16_t aKey)
+static bool isSensitiveKey(uint16_t aKey)
 {
     bool ret = false;
 
-    VerifyOrExit(sKeys != nullptr);
+    VerifyOrExit(sSensitiveKeys != nullptr);
 
-    for (uint16_t i = 0; i < sKeysLength; i++)
+    for (uint16_t i = 0; i < sSensitiveKeysLength; i++)
     {
-        VerifyOrExit(aKey != sKeys[i], ret = true);
+        VerifyOrExit(aKey != sSensitiveKeys[i], ret = true);
     }
 
 exit:
@@ -166,23 +158,29 @@
     VerifyOrDie(0 == unlink(swapFileName), OT_EXIT_ERROR_ERRNO);
 }
 
-void otPlatSettingsInit(otInstance *aInstance)
+void otPlatSettingsInit(otInstance *aInstance, const uint16_t *aSensitiveKeys, uint16_t aSensitiveKeysLength)
 {
+#if !OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
+    OT_UNUSED_VARIABLE(aSensitiveKeys);
+    OT_UNUSED_VARIABLE(aSensitiveKeysLength);
+#endif
+
     otError error = OT_ERROR_NONE;
 
+#if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
+    sSensitiveKeys       = aSensitiveKeys;
+    sSensitiveKeysLength = aSensitiveKeysLength;
+#endif
+
     // Don't touch the settings file the system runs in dry-run mode.
     VerifyOrExit(!IsSystemDryRun());
 
-#if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
-    otPosixSecureSettingsInit(aInstance);
-#endif
-
     {
         struct stat st;
 
         if (stat(OPENTHREAD_CONFIG_POSIX_SETTINGS_PATH, &st) == -1)
         {
-            mkdir(OPENTHREAD_CONFIG_POSIX_SETTINGS_PATH, 0755);
+            VerifyOrDie(mkdir(OPENTHREAD_CONFIG_POSIX_SETTINGS_PATH, 0755) == 0, OT_EXIT_ERROR_ERRNO);
         }
     }
 
@@ -211,6 +209,10 @@
         VerifyOrExit(offset == lseek(sSettingsFd, length, SEEK_CUR), error = OT_ERROR_PARSE);
     }
 
+#if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
+    otPosixSecureSettingsInit(aInstance);
+#endif
+
 exit:
     if (error == OT_ERROR_PARSE)
     {
@@ -222,6 +224,8 @@
 {
     OT_UNUSED_VARIABLE(aInstance);
 
+    VerifyOrExit(!IsSystemDryRun());
+
 #if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
     otPosixSecureSettingsDeinit(aInstance);
 #endif
@@ -237,17 +241,101 @@
 {
     OT_UNUSED_VARIABLE(aInstance);
 
-    otError     error  = OT_ERROR_NOT_FOUND;
-    const off_t size   = lseek(sSettingsFd, 0, SEEK_END);
-    off_t       offset = lseek(sSettingsFd, 0, SEEK_SET);
+    otError error = OT_ERROR_NOT_FOUND;
 
     VerifyOrExit(!IsSystemDryRun());
 #if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
-    if (isCriticalKey(aKey))
+    if (isSensitiveKey(aKey))
     {
-        ExitNow(error = otPosixSecureSettingsGet(aInstance, aKey, aIndex, aValue, aValueLength));
+        error = otPosixSecureSettingsGet(aInstance, aKey, aIndex, aValue, aValueLength);
     }
+    else
 #endif
+    {
+        error = ot::Posix::PlatformSettingsGet(aInstance, aKey, aIndex, aValue, aValueLength);
+    }
+
+exit:
+    VerifyOrDie(error != OT_ERROR_PARSE, OT_EXIT_FAILURE);
+    return error;
+}
+
+otError otPlatSettingsSet(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength)
+{
+    otError error = OT_ERROR_NONE;
+
+#if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
+    if (isSensitiveKey(aKey))
+    {
+        error = otPosixSecureSettingsSet(aInstance, aKey, aValue, aValueLength);
+    }
+    else
+#endif
+    {
+        ot::Posix::PlatformSettingsSet(aInstance, aKey, aValue, aValueLength);
+    }
+
+    return error;
+}
+
+otError otPlatSettingsAdd(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+
+    otError error = OT_ERROR_NONE;
+
+#if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
+    if (isSensitiveKey(aKey))
+    {
+        error = otPosixSecureSettingsAdd(aInstance, aKey, aValue, aValueLength);
+    }
+    else
+#endif
+    {
+        ot::Posix::PlatformSettingsAdd(aInstance, aKey, aValue, aValueLength);
+    }
+
+    return error;
+}
+
+otError otPlatSettingsDelete(otInstance *aInstance, uint16_t aKey, int aIndex)
+{
+    otError error;
+
+#if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
+    if (isSensitiveKey(aKey))
+    {
+        error = otPosixSecureSettingsDelete(aInstance, aKey, aIndex);
+    }
+    else
+#endif
+    {
+        error = ot::Posix::PlatformSettingsDelete(aInstance, aKey, aIndex, nullptr);
+    }
+
+    return error;
+}
+
+void otPlatSettingsWipe(otInstance *aInstance)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+#if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
+    otPosixSecureSettingsWipe(aInstance);
+#endif
+
+    VerifyOrDie(0 == ftruncate(sSettingsFd, 0), OT_EXIT_ERROR_ERRNO);
+}
+
+namespace ot {
+namespace Posix {
+
+otError PlatformSettingsGet(otInstance *aInstance, uint16_t aKey, int aIndex, uint8_t *aValue, uint16_t *aValueLength)
+{
+    OT_UNUSED_VARIABLE(aInstance);
+
+    otError     error  = OT_ERROR_NOT_FOUND;
+    const off_t size   = lseek(sSettingsFd, 0, SEEK_END);
+    off_t       offset = lseek(sSettingsFd, 0, SEEK_SET);
 
     VerifyOrExit(offset == 0 && size >= 0, error = OT_ERROR_PARSE);
 
@@ -294,23 +382,14 @@
     }
 
 exit:
-    VerifyOrDie(error != OT_ERROR_PARSE, OT_EXIT_FAILURE);
     return error;
 }
 
-otError otPlatSettingsSet(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength)
+void PlatformSettingsSet(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength)
 {
-    int     swapFd = -1;
-    otError error  = OT_ERROR_NONE;
+    int swapFd = -1;
 
-#if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
-    if (isCriticalKey(aKey))
-    {
-        ExitNow(error = otPosixSecureSettingsSet(aInstance, aKey, aValue, aValueLength));
-    }
-#endif
-
-    switch (platformSettingsDelete(aInstance, aKey, -1, &swapFd))
+    switch (PlatformSettingsDelete(aInstance, aKey, -1, &swapFd))
     {
     case OT_ERROR_NONE:
     case OT_ERROR_NOT_FOUND:
@@ -327,27 +406,12 @@
                 OT_EXIT_FAILURE);
 
     swapPersist(aInstance, swapFd);
-
-#if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
-exit:
-#endif
-    return error;
 }
 
-otError otPlatSettingsAdd(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength)
+void PlatformSettingsAdd(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength)
 {
-    OT_UNUSED_VARIABLE(aInstance);
-
-    otError error  = OT_ERROR_NONE;
-    off_t   size   = lseek(sSettingsFd, 0, SEEK_END);
-    int     swapFd = swapOpen(aInstance);
-
-#if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
-    if (isCriticalKey(aKey))
-    {
-        ExitNow(error = otPosixSecureSettingsAdd(aInstance, aKey, aValue, aValueLength));
-    }
-#endif
+    off_t size   = lseek(sSettingsFd, 0, SEEK_END);
+    int   swapFd = swapOpen(aInstance);
 
     if (size > 0)
     {
@@ -361,51 +425,10 @@
                 OT_EXIT_FAILURE);
 
     swapPersist(aInstance, swapFd);
-
-#if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
-exit:
-#endif
-    return error;
 }
 
-otError otPlatSettingsDelete(otInstance *aInstance, uint16_t aKey, int aIndex)
+otError PlatformSettingsDelete(otInstance *aInstance, uint16_t aKey, int aIndex, int *aSwapFd)
 {
-    otError error;
-
-#if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
-    if (isCriticalKey(aKey))
-    {
-        error = otPosixSecureSettingsDelete(aInstance, aKey, aIndex);
-    }
-    else
-#endif
-    {
-        error = platformSettingsDelete(aInstance, aKey, aIndex, nullptr);
-    }
-
-    return error;
-}
-
-/**
- * This function removes a setting either from swap file or persisted file.
- *
- * @param[in]  aInstance  The OpenThread instance structure.
- * @param[in]  aKey       The key associated with the requested setting.
- * @param[in]  aIndex     The index of the value to be removed. If set to -1, all values for this aKey will be removed.
- * @param[out] aSwapFd    A optional pointer to receive file descriptor of the generated swap file descriptor.
- *
- * @note
- *   If @p aSwapFd is null, operate deleting on the setting file.
- *   If @p aSwapFd is not null, operate on the swap file, and aSwapFd will point to the swap file descriptor.
- *
- * @retval OT_ERROR_NONE        The given key and index was found and removed successfully.
- * @retval OT_ERROR_NOT_FOUND   The given key or index was not found in the setting store.
- *
- */
-static otError platformSettingsDelete(otInstance *aInstance, uint16_t aKey, int aIndex, int *aSwapFd)
-{
-    OT_UNUSED_VARIABLE(aInstance);
-
     otError error  = OT_ERROR_NOT_FOUND;
     off_t   size   = lseek(sSettingsFd, 0, SEEK_END);
     off_t   offset = lseek(sSettingsFd, 0, SEEK_SET);
@@ -480,15 +503,21 @@
     return error;
 }
 
-void otPlatSettingsWipe(otInstance *aInstance)
+#if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
+void PlatformSettingsGetSensitiveKeys(otInstance *aInstance, const uint16_t **aKeys, uint16_t *aKeysLength)
 {
     OT_UNUSED_VARIABLE(aInstance);
-#if OPENTHREAD_POSIX_CONFIG_SECURE_SETTINGS_ENABLE
-    otPosixSecureSettingsWipe(aInstance);
+
+    assert(aKeys != nullptr);
+    assert(aKeysLength != nullptr);
+
+    *aKeys       = sSensitiveKeys;
+    *aKeysLength = sSensitiveKeysLength;
+}
 #endif
 
-    VerifyOrDie(0 == ftruncate(sSettingsFd, 0), OT_EXIT_ERROR_ERRNO);
-}
+} // namespace Posix
+} // namespace ot
 
 #ifndef SELF_TEST
 #define SELF_TEST 0
@@ -496,6 +525,17 @@
 
 #if SELF_TEST
 
+void otLogCritPlat(const char *aFormat, ...)
+{
+    OT_UNUSED_VARIABLE(aFormat);
+}
+
+const char *otExitCodeToString(uint8_t aExitCode)
+{
+    OT_UNUSED_VARIABLE(aExitCode);
+    return "";
+}
+
 void otPlatRadioGetIeeeEui64(otInstance *aInstance, uint8_t *aIeeeEui64)
 {
     OT_UNUSED_VARIABLE(aInstance);
@@ -519,7 +559,7 @@
         data[i] = i;
     }
 
-    otPlatSettingsInit(instance);
+    otPlatSettingsInit(instance, nullptr, 0);
 
     // verify empty situation
     otPlatSettingsWipe(instance);
diff --git a/src/posix/platform/settings.hpp b/src/posix/platform/settings.hpp
new file mode 100644
index 0000000..6afb0f7
--- /dev/null
+++ b/src/posix/platform/settings.hpp
@@ -0,0 +1,103 @@
+/*
+ *  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.
+ */
+
+#ifndef POSIX_PLATFORM_SETTINGS_HPP_
+#define POSIX_PLATFORM_SETTINGS_HPP_
+
+namespace ot {
+namespace Posix {
+
+/**
+ * This function gets a setting from the persisted file.
+ *
+ * @param[in]      aInstance     The OpenThread instance structure.
+ * @param[in]      aKey          The key associated with the requested setting.
+ * @param[in]      aIndex        The index of the specific item to get.
+ * @param[out]     aValue        A pointer to where the value of the setting should be written.
+ * @param[in,out]  aValueLength  A pointer to the length of the value.
+ *
+ * @retval OT_ERROR_NONE        The given setting was found and fetched successfully.
+ * @retval OT_ERROR_NOT_FOUND   The given key or index was not found in the setting store.
+ *
+ */
+otError PlatformSettingsGet(otInstance *aInstance, uint16_t aKey, int aIndex, uint8_t *aValue, uint16_t *aValueLength);
+
+/**
+ * This function sets a setting in the persisted file.
+ *
+ * @param[in]  aInstance     The OpenThread instance structure.
+ * @param[in]  aKey          The key associated with the requested setting.
+ * @param[in]  aValue        A pointer to where the new value of the setting should be read from.
+ * @param[in]  aValueLength  The length of the data pointed to by aValue.
+ *
+ */
+void PlatformSettingsSet(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength);
+
+/**
+ * This function adds a setting to the persisted file.
+ *
+ * @param[in]  aInstance     The OpenThread instance structure.
+ * @param[in]  aKey          The key associated with the requested setting.
+ * @param[in]  aValue        A pointer to where the new value of the setting should be read from.
+ * @param[in]  aValueLength  The length of the data pointed to by aValue.
+ *
+ */
+void PlatformSettingsAdd(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength);
+
+/**
+ * This function removes a setting either from swap file or persisted file.
+ *
+ * @param[in]  aInstance  The OpenThread instance structure.
+ * @param[in]  aKey       The key associated with the requested setting.
+ * @param[in]  aIndex     The index of the value to be removed. If set to -1, all values for this aKey will be removed.
+ * @param[out] aSwapFd    A optional pointer to receive file descriptor of the generated swap file descriptor.
+ *
+ * @note
+ *   If @p aSwapFd is null, operate deleting on the setting file.
+ *   If @p aSwapFd is not null, operate on the swap file, and aSwapFd will point to the swap file descriptor.
+ *
+ * @retval OT_ERROR_NONE        The given key and index was found and removed successfully.
+ * @retval OT_ERROR_NOT_FOUND   The given key or index was not found in the setting store.
+ *
+ */
+otError PlatformSettingsDelete(otInstance *aInstance, uint16_t aKey, int aIndex, int *aSwapFd);
+
+/**
+ * This function gets the sensitive keys that should be stored in the secure area.
+ *
+ * @param[in]   aInstance    The OpenThread instance structure.
+ * @param[out]  aKeys        A pointer to where the pointer to the array containing sensitive keys should be written.
+ * @param[out]  aKeysLength  A pointer to where the count of sensitive keys should be written.
+ *
+ */
+void PlatformSettingsGetSensitiveKeys(otInstance *aInstance, const uint16_t **aKeys, uint16_t *aKeysLength);
+
+} // namespace Posix
+} // namespace ot
+
+#endif // POSIX_PLATFORM_SETTINGS_HPP_
diff --git a/src/posix/platform/spi_interface.cpp b/src/posix/platform/spi_interface.cpp
index 3d58202..752dd44 100644
--- a/src/posix/platform/spi_interface.cpp
+++ b/src/posix/platform/spi_interface.cpp
@@ -88,6 +88,7 @@
     , mSpiTxPayloadSize(0)
     , mDidPrintRateLimitLog(false)
     , mSpiSlaveDataLen(0)
+    , mDidRxFrame(false)
 {
 }
 
@@ -398,8 +399,8 @@
 
     if (ret != -1)
     {
-        otDumpDebg(OT_LOG_REGION_PLATFORM, "SPI-TX", mSpiTxFrameBuffer, transfer[1].len);
-        otDumpDebg(OT_LOG_REGION_PLATFORM, "SPI-RX", aSpiRxFrameBuffer, transfer[1].len);
+        otDumpDebgPlat("SPI-TX", mSpiTxFrameBuffer, static_cast<uint16_t>(transfer[1].len));
+        otDumpDebgPlat("SPI-RX", aSpiRxFrameBuffer, static_cast<uint16_t>(transfer[1].len));
 
         mSpiFrameCount++;
     }
@@ -528,8 +529,8 @@
 
                 otLogWarnPlat("Garbage in header : %02X %02X %02X %02X %02X", spiRxFrame[0], spiRxFrame[1],
                               spiRxFrame[2], spiRxFrame[3], spiRxFrame[4]);
-                otDumpDebg(OT_LOG_REGION_PLATFORM, "SPI-TX", mSpiTxFrameBuffer, spiTransferBytes);
-                otDumpDebg(OT_LOG_REGION_PLATFORM, "SPI-RX", spiRxFrameBuffer, spiTransferBytes);
+                otDumpDebgPlat("SPI-TX", mSpiTxFrameBuffer, spiTransferBytes);
+                otDumpDebgPlat("SPI-RX", spiRxFrameBuffer, spiTransferBytes);
             }
 
             mSpiTxRefusedCount++;
@@ -547,8 +548,8 @@
 
             otLogWarnPlat("Garbage in header : %02X %02X %02X %02X %02X", spiRxFrame[0], spiRxFrame[1], spiRxFrame[2],
                           spiRxFrame[3], spiRxFrame[4]);
-            otDumpDebg(OT_LOG_REGION_PLATFORM, "SPI-TX", mSpiTxFrameBuffer, spiTransferBytes);
-            otDumpDebg(OT_LOG_REGION_PLATFORM, "SPI-RX", spiRxFrameBuffer, spiTransferBytes);
+            otDumpDebgPlat("SPI-TX", mSpiTxFrameBuffer, spiTransferBytes);
+            otDumpDebgPlat("SPI-RX", spiRxFrameBuffer, spiTransferBytes);
 
             ExitNow();
         }
@@ -579,6 +580,7 @@
             // Upper layer will free the frame buffer.
             discardRxFrame = false;
 
+            mDidRxFrame = true;
             mReceiveFrameCallback(mReceiveFrameContext);
         }
     }
@@ -731,7 +733,14 @@
 
 void SpiInterface::Process(const RadioProcessContext &aContext)
 {
-    if (FD_ISSET(mIntGpioValueFd, aContext.mReadFdSet))
+    Process(aContext.mReadFdSet, aContext.mWriteFdSet);
+}
+
+void SpiInterface::Process(const fd_set *aReadFdSet, const fd_set *aWriteFdSet)
+{
+    OT_UNUSED_VARIABLE(aWriteFdSet);
+
+    if (FD_ISSET(mIntGpioValueFd, aReadFdSet))
     {
         struct gpioevent_data event;
 
@@ -751,68 +760,48 @@
 
 otError SpiInterface::WaitForFrame(uint64_t aTimeoutUs)
 {
-    otError        error      = OT_ERROR_NONE;
-    struct timeval spiTimeout = {kSecPerDay, 0};
-    struct timeval timeout;
-    fd_set         readFdSet;
-    int            ret;
-    bool           isDataReady = false;
+    otError  error = OT_ERROR_NONE;
+    uint64_t now   = otPlatTimeGet();
+    uint64_t end   = now + aTimeoutUs;
 
-    timeout.tv_sec  = static_cast<time_t>(aTimeoutUs / US_PER_S);
-    timeout.tv_usec = static_cast<suseconds_t>(aTimeoutUs % US_PER_S);
+    mDidRxFrame = false;
 
-    FD_ZERO(&readFdSet);
-
-    if (mIntGpioValueFd >= 0)
+    while (now < end)
     {
-        if ((isDataReady = CheckInterrupt()))
+        fd_set         readFdSet;
+        fd_set         writeFdSet;
+        int            maxFds = -1;
+        struct timeval timeout;
+        int            ret;
+
+        timeout.tv_sec  = static_cast<time_t>((end - now) / US_PER_S);
+        timeout.tv_usec = static_cast<suseconds_t>((end - now) % US_PER_S);
+
+        FD_ZERO(&readFdSet);
+        FD_ZERO(&writeFdSet);
+
+        UpdateFdSet(readFdSet, writeFdSet, maxFds, timeout);
+
+        ret = select(maxFds + 1, &readFdSet, &writeFdSet, nullptr, &timeout);
+
+        if (ret >= 0)
         {
-            // Interrupt pin is asserted, set the timeout to be 0.
-            spiTimeout.tv_sec  = 0;
-            spiTimeout.tv_usec = 0;
+            Process(&readFdSet, &writeFdSet);
+
+            if (mDidRxFrame)
+            {
+                ExitNow();
+            }
         }
-        else
+        else if (errno != EINTR)
         {
-            // The interrupt pin was not asserted, so we wait for the interrupt pin to be asserted by adding it to the
-            // read set.
-            FD_SET(mIntGpioValueFd, &readFdSet);
+            DieNow(OT_EXIT_ERROR_ERRNO);
         }
-    }
-    else
-    {
-        // In this case we don't have an interrupt, so we revert to SPI polling.
-        spiTimeout.tv_sec  = 0;
-        spiTimeout.tv_usec = kSpiPollPeriodUs;
+
+        now = otPlatTimeGet();
     }
 
-    if (timercmp(&spiTimeout, &timeout, <))
-    {
-        timeout = spiTimeout;
-    }
-
-    ret = select(mIntGpioValueFd + 1, &readFdSet, nullptr, nullptr, &timeout);
-
-    if (ret > 0 && FD_ISSET(mIntGpioValueFd, &readFdSet))
-    {
-        struct gpioevent_data event;
-
-        // Read event data to clear interrupt.
-        VerifyOrDie(read(mIntGpioValueFd, &event, sizeof(event)) != -1, OT_EXIT_FAILURE);
-        isDataReady = true;
-    }
-
-    if (isDataReady)
-    {
-        IgnoreError(PushPullSpi());
-    }
-    else if (ret == 0)
-    {
-        ExitNow(error = OT_ERROR_RESPONSE_TIMEOUT);
-    }
-    else if (errno != EINTR)
-    {
-        DieNow(OT_EXIT_ERROR_ERRNO);
-    }
+    error = OT_ERROR_RESPONSE_TIMEOUT;
 
 exit:
     return error;
diff --git a/src/posix/platform/spi_interface.hpp b/src/posix/platform/spi_interface.hpp
index cff2bca..13bf00f 100644
--- a/src/posix/platform/spi_interface.hpp
+++ b/src/posix/platform/spi_interface.hpp
@@ -122,10 +122,10 @@
     /**
      * This method updates the file descriptor sets with file descriptors used by the radio driver.
      *
-     * @param[inout]  aReadFdSet   A reference to the read file descriptors.
-     * @param[inout]  aWriteFdSet  A reference to the write file descriptors.
-     * @param[inout]  aMaxFd       A reference to the max file descriptor.
-     * @param[inout]  aTimeout     A reference to the timeout.
+     * @param[in,out]  aReadFdSet   A reference to the read file descriptors.
+     * @param[in,out]  aWriteFdSet  A reference to the write file descriptors.
+     * @param[in,out]  aMaxFd       A reference to the max file descriptor.
+     * @param[in,out]  aTimeout     A reference to the timeout.
      *
      */
     void UpdateFdSet(fd_set &aReadFdSet, fd_set &aWriteFdSet, int &aMaxFd, struct timeval &aTimeout);
@@ -173,6 +173,7 @@
     uint8_t *GetRealRxFrameStart(uint8_t *aSpiRxFrameBuffer, uint8_t aAlignAllowance, uint16_t &aSkipLength);
     otError  DoSpiTransfer(uint8_t *aSpiRxFrameBuffer, uint32_t aTransferLength);
     otError  PushPullSpi(void);
+    void     Process(const fd_set *aReadFdSet, const fd_set *aWriteFdSet);
 
     bool CheckInterrupt(void);
     void LogStats(void);
@@ -245,6 +246,8 @@
     bool     mDidPrintRateLimitLog;
     uint16_t mSpiSlaveDataLen;
 
+    bool mDidRxFrame;
+
     // Non-copyable, intentionally not implemented.
     SpiInterface(const SpiInterface &);
     SpiInterface &operator=(const SpiInterface &);
diff --git a/src/posix/platform/system.cpp b/src/posix/platform/system.cpp
index b029373..c3e176a 100644
--- a/src/posix/platform/system.cpp
+++ b/src/posix/platform/system.cpp
@@ -161,6 +161,8 @@
 
 void platformSetUp(void)
 {
+    VerifyOrExit(!gDryRun);
+
 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
     platformBackboneSetUp();
 #endif
@@ -184,6 +186,9 @@
 #if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE || OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
     SuccessOrDie(otSetStateChangedCallback(gInstance, processStateChange, gInstance));
 #endif
+
+exit:
+    return;
 }
 
 otInstance *otSysInit(otPlatformConfig *aPlatformConfig)
@@ -203,6 +208,8 @@
 
 void platformTearDown(void)
 {
+    VerifyOrExit(!gDryRun);
+
 #if OPENTHREAD_POSIX_CONFIG_DAEMON_ENABLE
     ot::Posix::Daemon::Get().TearDown();
 #endif
@@ -222,6 +229,9 @@
 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
     platformBackboneTearDown();
 #endif
+
+exit:
+    return;
 }
 
 void platformDeinit(void)
@@ -230,6 +240,10 @@
     virtualTimeDeinit();
 #endif
     platformRadioDeinit();
+
+    // For Dry-Run option, only the radio is initialized.
+    VerifyOrExit(!gDryRun);
+
 #if OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE
     ot::Posix::Udp::Get().Deinit();
 #endif
@@ -247,6 +261,9 @@
 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
     platformBackboneDeinit();
 #endif
+
+exit:
+    return;
 }
 
 void otSysDeinit(void)
@@ -263,9 +280,9 @@
 /**
  * This function try selecting the given file descriptors in nonblocking mode.
  *
- * @param[inout]    aReadFdSet   A pointer to the read file descriptors.
- * @param[inout]    aWriteFdSet  A pointer to the write file descriptors.
- * @param[inout]    aErrorFdSet  A pointer to the error file descriptors.
+ * @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]   aErrorFdSet  A pointer to the error file descriptors.
  * @param[in]       aMaxFd       The max file descriptor.
  *
  * @returns The value returned from select().
@@ -379,15 +396,6 @@
 #endif
 }
 
-#if OPENTHREAD_CONFIG_OTNS_ENABLE
-
-void otPlatOtnsStatus(const char *aStatus)
-{
-    otLogOtns("[OTNS] %s", aStatus);
-}
-
-#endif
-
 bool IsSystemDryRun(void)
 {
     return gDryRun;
diff --git a/src/posix/platform/trel.cpp b/src/posix/platform/trel.cpp
index 9175875..f3ec6cf 100644
--- a/src/posix/platform/trel.cpp
+++ b/src/posix/platform/trel.cpp
@@ -42,12 +42,12 @@
 #include <sys/socket.h>
 #include <unistd.h>
 
+#include <openthread/logging.h>
 #include <openthread/platform/trel.h>
 
 #include "radio_url.hpp"
 #include "system.hpp"
 #include "common/code_utils.hpp"
-#include "common/logging.hpp"
 
 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
 
@@ -73,8 +73,6 @@
 static bool sEnabled     = false;
 static int  sSocket      = -1;
 
-#if OPENTHREAD_CONFIG_LOG_PLATFORM && (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_DEBG)
-
 static const char *Ip6AddrToString(const void *aAddress)
 {
     static char string[INET6_ADDRSTRLEN];
@@ -117,8 +115,6 @@
     return string;
 }
 
-#endif // #if OPENTHREAD_CONFIG_LOG_PLATFORM && (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_DEBG)
-
 static void PrepareSocket(uint16_t &aUdpPort)
 {
     int                 val;
@@ -141,7 +137,7 @@
     memset(&sockAddr, 0, sizeof(sockAddr));
     sockAddr.sin6_family = AF_INET6;
     sockAddr.sin6_addr   = in6addr_any;
-    sockAddr.sin6_port   = 0;
+    sockAddr.sin6_port   = OPENTHREAD_POSIX_CONFIG_TREL_UDP_PORT;
 
     if (bind(sSocket, (struct sockaddr *)&sockAddr, sizeof(sockAddr)) == -1)
     {
diff --git a/src/posix/platform/utils.cpp b/src/posix/platform/utils.cpp
index 5218841..e497404 100644
--- a/src/posix/platform/utils.cpp
+++ b/src/posix/platform/utils.cpp
@@ -37,8 +37,9 @@
 #include <stdio.h>
 #include <string.h>
 
+#include <openthread/logging.h>
+
 #include "common/code_utils.hpp"
-#include "common/logging.hpp"
 
 namespace ot {
 namespace Posix {
diff --git a/tests/fuzz/fuzzer_platform.cpp b/tests/fuzz/fuzzer_platform.cpp
index a8ae28b..7449937 100644
--- a/tests/fuzz/fuzzer_platform.cpp
+++ b/tests/fuzz/fuzzer_platform.cpp
@@ -441,9 +441,11 @@
     return OT_ERROR_NONE;
 }
 
-void otPlatSettingsInit(otInstance *aInstance)
+void otPlatSettingsInit(otInstance *aInstance, const uint16_t *aSensitiveKeys, uint16_t aSensitiveKeysLength)
 {
     OT_UNUSED_VARIABLE(aInstance);
+    OT_UNUSED_VARIABLE(aSensitiveKeys);
+    OT_UNUSED_VARIABLE(aSensitiveKeysLength);
 }
 
 void otPlatSettingsDeinit(otInstance *aInstance)
diff --git a/tests/scripts/expect/cli-commissioner-multiple-ftds.exp b/tests/scripts/expect/cli-commissioner-multiple-ftds.exp
new file mode 100755
index 0000000..7d43522
--- /dev/null
+++ b/tests/scripts/expect/cli-commissioner-multiple-ftds.exp
@@ -0,0 +1,139 @@
+#!/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"
+source "tests/scripts/expect/_multinode.exp"
+
+spawn_node 3
+spawn_node 2
+spawn_node 1
+
+setup_leader
+setup_node 3 "rdn" "router"
+setup_node 2 "rdn" "router"
+
+sleep 2
+
+###########################################
+# Verify topology and commissioners
+
+switch_node 3
+send "state\n"
+expect "router"
+send "commissioner state\n"
+expect "disabled"
+
+switch_node 2
+send "state\n"
+expect "router"
+send "commissioner state\n"
+expect "disabled"
+
+switch_node 1
+send "state\n"
+expect "leader"
+send "commissioner state\n"
+expect "active"
+
+###########################################
+# Starting a commissioner on the same partition with the same ID makes
+# the active commissioner resign and become disabled.
+
+switch_node 2
+send "commissioner start\n"
+expect "Done"
+wait_for "commissioner state" "active"
+switch_node 1
+sleep 5
+wait_for "commissioner state" "disabled"
+
+switch_node 3
+send "commissioner start\n"
+expect "Done"
+wait_for "commissioner state" "active"
+sleep 5
+switch_node 2
+wait_for "commissioner state" "disabled"
+
+switch_node 1
+send "commissioner start\n"
+expect "Done"
+wait_for "commissioner state" "active"
+sleep 5
+switch_node 3
+wait_for "commissioner state" "disabled"
+
+###########################################
+# Starting another commissioner on the same partition, using a different ID avoids
+# the previous problem of overriding the active commissioner.
+
+switch_node 2
+send "commissioner id COMMISSIONER_2\n"
+expect "Done"
+send "commissioner start\n"
+expect "Done"
+wait_for "commissioner state" "petitioning"
+wait_for "commissioner state" "disabled"
+
+switch_node 1
+send "commissioner state\n"
+expect "active"
+
+switch_node 3
+send "commissioner id COMMISSIONER_3\n"
+expect "Done"
+send "commissioner start\n"
+expect "Done"
+wait_for "commissioner state" "petitioning"
+wait_for "commissioner state" "disabled"
+
+switch_node 1
+send "commissioner state\n"
+expect "active"
+
+###########################################
+# Stop active commissioner and start another one
+send "commissioner stop\n"
+
+switch_node 2
+send "commissioner id COMMISSIONER_2\n"
+expect "Done"
+send "commissioner start\n"
+expect "Done"
+wait_for "commissioner state" "petitioning"
+wait_for "commissioner state" "active"
+
+switch_node 1
+send "commissioner start\n"
+expect "Done"
+wait_for "commissioner state" "petitioning"
+wait_for "commissioner state" "disabled"
+
+###########################################
+dispose_all
diff --git a/tests/scripts/expect/cli-commissioner.exp b/tests/scripts/expect/cli-commissioner.exp
index 2f8df33..2c6737e 100755
--- a/tests/scripts/expect/cli-commissioner.exp
+++ b/tests/scripts/expect/cli-commissioner.exp
@@ -41,16 +41,34 @@
 expect_line "Done"
 wait_for "netdata steeringdata check $eui64" "NotFound"
 
+# Stop commissioner so node 2 can start a new one with different id
+send "commissioner stop\n"
+expect "Done"
+
 switch_node 2
 send "commissioner state\n"
 expect "disabled"
 expect_line "Done"
+send "commissioner id\n"
+expect "OpenThread Commissioner"
+expect_line "Done"
+send "commissioner id reallyAndUnnecessaryLongOpenthreadComisionerCustomIdShouldNotFit\n"
+expect "Error 7: InvalidArgs"
+send "commissioner id reallyAndUnnecessaryLongOpenthreadCommissionerCustomIdShouldFit\n"
+expect "Done"
+send "commissioner id customId\n"
+expect "Done"
+send "commissioner id\n"
+expect "customId"
+expect_line "Done"
 send "commissioner start\n"
 expect_line "Done"
 expect "Commissioner: active"
 send "commissioner state\n"
 expect "active"
 expect_line "Done"
+send "commissioner id AnotherCustomId\n"
+expect_line "Error 13: InvalidState"
 send "commissioner provisioningurl openthread.io\n"
 expect_line "Done"
 send "commissioner joiner add * J01NME 1\n"
diff --git a/tests/scripts/expect/cli-dataset.exp b/tests/scripts/expect/cli-dataset.exp
index a660c44..0b75300 100755
--- a/tests/scripts/expect/cli-dataset.exp
+++ b/tests/scripts/expect/cli-dataset.exp
@@ -102,10 +102,10 @@
 send "dataset pskc\n"
 expect "00112233445566778899aabbccddeeff"
 expect_line "Done"
-send "dataset securitypolicy 678 onrcb\n"
+send "dataset securitypolicy 678 onrc\n"
 expect_line "Done"
 send "dataset securitypolicy\n"
-expect "678 onrcb"
+expect "678 onrc"
 expect_line "Done"
 send "dataset pendingtimestamp 100\n"
 expect_line "Done"
@@ -135,7 +135,7 @@
 expect "Network Name: OT-network"
 expect "PAN ID: 0xface"
 expect "PSKc: 00112233445566778899aabbccddeeff"
-expect "Security Policy: 678 onrcb"
+expect "Security Policy: 678 onrc"
 expect_line "Done"
 send "dataset pending -x\n"
 expect "dataset pending -x"
@@ -161,7 +161,7 @@
 expect "Network Name: OT-network"
 expect "PAN ID: 0xface"
 expect "PSKc: 00112233445566778899aabbccddeeff"
-expect "Security Policy: 678 onrcb"
+expect "Security Policy: 678 onrc"
 expect_line "Done"
 
 sleep 30
@@ -183,7 +183,7 @@
 expect "Network Name: OT-network"
 expect "PAN ID: 0xface"
 expect "PSKc: 00112233445566778899aabbccddeeff"
-expect "Security Policy: 678 onrcb"
+expect "Security Policy: 678 onrc"
 expect_line "Done"
 send "dataset clear\n"
 expect_line "Done"
@@ -203,7 +203,7 @@
 expect "Network Name: OT-network"
 expect "PAN ID: 0xface"
 expect "PSKc: 00112233445566778899aabbccddeeff"
-expect "Security Policy: 678 onrcb"
+expect "Security Policy: 678 onrc"
 expect_line "Done"
 send "dataset init pending\n"
 expect "Error 23: NotFound"
diff --git a/tests/scripts/expect/cli-misc.exp b/tests/scripts/expect/cli-misc.exp
index 89da807..8b8b4d5 100755
--- a/tests/scripts/expect/cli-misc.exp
+++ b/tests/scripts/expect/cli-misc.exp
@@ -180,6 +180,13 @@
 send "ba state\n"
 expect "Done"
 
+send "prefix meshlocal fd00:dead:beef:cafe::/96\n"
+expect_line "Error 7: InvalidArgs"
+send "prefix meshlocal fd00:dead:beef:cafe::/64\n"
+expect_line "Done"
+send "prefix meshlocal\n"
+expect_line "fd00:dead:beef:cafe::/64"
+
 send "invalidcommand\n"
 expect_line "Error 35: InvalidCommand"
 
diff --git a/tests/scripts/expect/cli-networkname.exp b/tests/scripts/expect/cli-networkname.exp
index 20c09c6..fc4edb1 100755
--- a/tests/scripts/expect/cli-networkname.exp
+++ b/tests/scripts/expect/cli-networkname.exp
@@ -43,7 +43,9 @@
 expect_line "Done"
 
 switch_node 2
-send "scan\n"
+send "ifconfig up\n"
+expect_line "Done"
+send "discover\n"
 expect "Thread Network"
 expect_line "Done"
 
@@ -58,7 +60,7 @@
 expect_line "Done"
 
 switch_node 2
-send "scan\n"
+send "discover\n"
 expect "Thread 网络"
 expect_line "Done"
 
@@ -73,7 +75,7 @@
 expect_line "Done"
 
 switch_node 2
-send "scan\n"
+send "discover\n"
 expect "スレッド"
 expect_line "Done"
 
diff --git a/tests/scripts/expect/cli-scan-discover.exp b/tests/scripts/expect/cli-scan-discover.exp
index 3c1d193..78226c7 100755
--- a/tests/scripts/expect/cli-scan-discover.exp
+++ b/tests/scripts/expect/cli-scan-discover.exp
@@ -60,9 +60,9 @@
 
 switch_node 3
 send "scan $channel\n"
-expect "| J | Network Name     | Extended PAN     | PAN  | MAC Address      | Ch | dBm | LQI |"
-expect "+---+------------------+------------------+------+------------------+----+-----+-----+"
-wait_for "" "\\| \\d \\| $network +\\| $extpan \\| $pan \\| $extaddr \\| +$channel \\| +-?\\d+ \\| +\\d \\|"
+expect "| PAN  | MAC Address      | Ch | dBm | LQI |"
+expect "+------+------------------+----+-----+-----+"
+wait_for "" "\\| $pan \\| $extaddr \\| +$channel \\| +-?\\d+ \\| +\\d \\|"
 wait_for "" "Done"
 send "scan energy 100\n"
 expect "| Ch | RSSI |"
@@ -83,9 +83,9 @@
 send "ifconfig up\n"
 expect_line "Done"
 send "discover $channel\n"
-expect "| J | Network Name     | Extended PAN     | PAN  | MAC Address      | Ch | dBm | LQI |"
-expect "+---+------------------+------------------+------+------------------+----+-----+-----+"
-wait_for "" "\\| \\d \\| $network +\\| $extpan \\| $pan \\| $extaddr \\| +$channel \\| +-?\\d+ \\| +\\d \\|"
+expect "| Network Name     | Extended PAN     | PAN  | MAC Address      | Ch | dBm | LQI |"
+expect "+------------------+------------------+------+------------------+----+-----+-----+"
+wait_for "" "\\| $network +\\| $extpan \\| $pan \\| $extaddr \\| +$channel \\| +-?\\d+ \\| +\\d \\|"
 wait_for "" "Done"
 send "discover something_invalid\n"
 expect "Error 7: InvalidArgs"
diff --git a/tests/scripts/expect/posix-diag-rcp.exp b/tests/scripts/expect/posix-diag-rcp.exp
index 2e4732e..08b9553 100755
--- a/tests/scripts/expect/posix-diag-rcp.exp
+++ b/tests/scripts/expect/posix-diag-rcp.exp
@@ -53,5 +53,17 @@
 expect_line "Done"
 send "diag rcp power 10\n"
 expect_line "Done"
+send "diag rcp echo\n"
+expect "failed"
+expect "status 0x7"
+expect_line "Done"
+send "diag rcp echo 0123456789\n"
+expect_line "0123456789"
+expect_line "Done"
+send "diag rcp echo -n 0\n"
+expect_line "Done"
+send "diag rcp echo -n 253\n"
+expect_line {[0-9]{253}}
+expect_line "Done"
 
 dispose_all
diff --git a/tests/scripts/expect/posix-scan-tx-to-sleep.exp b/tests/scripts/expect/posix-scan-tx-to-sleep.exp
index a613737..2ac44a8 100755
--- a/tests/scripts/expect/posix-scan-tx-to-sleep.exp
+++ b/tests/scripts/expect/posix-scan-tx-to-sleep.exp
@@ -49,17 +49,6 @@
 expect -re {([0-9a-f]{4})}
 set pan $expect_out(1,string)
 expect_line "Done"
-send "extpanid\n"
-expect "extpanid"
-expect -re {([0-9a-f]{16})}
-set extpan $expect_out(1,string)
-expect_line "Done"
-expect "> "
-send "networkname\n"
-expect "networkname"
-expect -re {[\r\n]([^\r\n]+)[\r\n]}
-set network $expect_out(1,string)
-expect_line "Done"
 send "channel\n"
 expect "channel"
 expect -re {(\d+)}
@@ -68,9 +57,9 @@
 
 spawn_node 2 "rcp" "spinel+hdlc+uart://$env(OT_SIMULATION_APPS)/ncp/ot-rcp?forkpty-arg=--sleep-to-tx&forkpty-arg=2"
 send "scan\n"
-expect "| J | Network Name     | Extended PAN     | PAN  | MAC Address      | Ch | dBm | LQI |"
-expect "+---+------------------+------------------+------+------------------+----+-----+-----+"
-wait_for "" "\\| \\d \\| $network +\\| $extpan \\| $pan \\| $extaddr \\| +$channel \\| +-?\\d+ \\| +\\d \\|"
+expect "| PAN  | MAC Address      | Ch | dBm | LQI |"
+expect "+------+------------------+----+-----+-----+"
+wait_for "" "\\| $pan \\| $extaddr \\| +$channel \\| +-?\\d+ \\| +\\d \\|"
 wait_for "" "Done"
 
 dispose_all
diff --git a/etc/cmake/checks.cmake b/tests/scripts/expect/posix-url-enable-coex.exp
old mode 100644
new mode 100755
similarity index 76%
rename from etc/cmake/checks.cmake
rename to tests/scripts/expect/posix-url-enable-coex.exp
index ab7bb88..2fa3fb7
--- a/etc/cmake/checks.cmake
+++ b/tests/scripts/expect/posix-url-enable-coex.exp
@@ -1,5 +1,6 @@
+#!/usr/bin/expect -f
 #
-#  Copyright (c) 2019, The OpenThread Authors.
+#  Copyright (c) 2022, The OpenThread Authors.
 #  All rights reserved.
 #
 #  Redistribution and use in source and binary forms, with or without
@@ -26,4 +27,16 @@
 #  POSSIBILITY OF SUCH DAMAGE.
 #
 
-configure_file(${PROJECT_SOURCE_DIR}/etc/cmake/openthread-config-generic.h.in ${PROJECT_BINARY_DIR}/etc/cmake/openthread-config-generic.h)
+source "tests/scripts/expect/_common.exp"
+
+spawn_node 1 "rcp" "spinel+hdlc+uart://$env(OT_SIMULATION_APPS)/ncp/ot-rcp?enable-coex&forkpty-arg=1"
+send "coex\n"
+expect_line "Enabled"
+expect_line "Done"
+dispose_node 1
+
+spawn_node 1 "rcp" "spinel+hdlc+uart://$env(OT_SIMULATION_APPS)/ncp/ot-rcp?enable-coex=0&forkpty-arg=1"
+send "coex\n"
+expect_line "Disabled"
+expect_line "Done"
+dispose_node 1
diff --git a/tests/scripts/expect/tun-dns-client.exp b/tests/scripts/expect/tun-dns-client.exp
index 36909ba..3a2ac64 100755
--- a/tests/scripts/expect/tun-dns-client.exp
+++ b/tests/scripts/expect/tun-dns-client.exp
@@ -40,8 +40,18 @@
 wait_for "state" "leader"
 expect_line "Done"
 
+send "route add 2001:dead:beef:cafe::/64 s n med\n"
+expect_line "Done"
+
+send "netdata register\n"
+expect_line "Done"
+
 send "dns resolve ipv6.google.com ::1 53\n"
 expect "DNS response for ipv6.google.com"
 expect_line "Done"
 
+send "dns resolve4 ipv6.google.com ::1 53\n"
+expect "DNS response for ipv6.google.com"
+expect_line "Done"
+
 dispose_all
diff --git a/tests/scripts/thread-cert/Cert_5_8_04_SecurityPolicyTLV.py b/tests/scripts/thread-cert/Cert_5_8_04_SecurityPolicyTLV.py
index 2bf8674..28dc68c 100755
--- a/tests/scripts/thread-cert/Cert_5_8_04_SecurityPolicyTLV.py
+++ b/tests/scripts/thread-cert/Cert_5_8_04_SecurityPolicyTLV.py
@@ -77,7 +77,7 @@
                 'timestamp': 1,
                 'channel': 19,
                 'network_key': '00112233445566778899aabbccddeeff',
-                'security_policy': [3600, 'onrcb']
+                'security_policy': [3600, 'onrc']
             },
             'mode': 'rdn',
         },
@@ -87,7 +87,7 @@
                 'timestamp': 1,
                 'channel': 19,
                 'network_key': '00112233445566778899aabbccddeeff',
-                'security_policy': [3600, 'onrcb']
+                'security_policy': [3600, 'onrc']
             },
             'mode': 'rdn',
             'allowlist': [LEADER]
@@ -103,7 +103,7 @@
                 'timestamp': 1,
                 'channel': 19,
                 'network_key': '00112233445566778899aabbccddeeff',
-                'security_policy': [3600, 'onrcb']
+                'security_policy': [3600, 'onrc']
             },
             'mode': 'rdn',
         },
@@ -130,10 +130,10 @@
         self.simulator.go(5)
 
         # Step 5
-        # Disabling O-Bit security_policy = [3600, 0b01111000]
+        # Disabling O-Bit security_policy = [3600, 0b01110000]
         self.nodes[COMMISSIONER_1].send_mgmt_active_set(
             active_timestamp=15,
-            security_policy=[3600, 'nrcb'],
+            security_policy=[3600, 'nrc'],
         )
         self.simulator.go(5)
 
@@ -143,10 +143,10 @@
         self.simulator.go(5)
 
         # Step 9
-        # Disabling N-Bit security_policy = [3600, 0b10111000]
+        # Disabling N-Bit security_policy = [3600, 0b10110000]
         self.nodes[COMMISSIONER_1].send_mgmt_active_set(
             active_timestamp=20,
-            security_policy=[3600, 'orcb'],
+            security_policy=[3600, 'orc'],
         )
         self.simulator.go(5)
 
@@ -173,10 +173,10 @@
         self.simulator.go(20)
 
         # Step 17
-        # Disabling R-Bit security_policy = [3600, 0b11011000]
+        # Disabling R-Bit security_policy = [3600, 0b11010000]
         self.nodes[COMMISSIONER_1].send_mgmt_active_set(
             active_timestamp=30,
-            security_policy=[3600, 'oncb'],
+            security_policy=[3600, 'onc'],
         )
         self.simulator.go(5)
 
@@ -209,7 +209,7 @@
         #             2.04 Changed
         #         CoAP Payload
         #             Security Policy TLV
-        #                 Bits “O”,”N”,”R”,”C”,”B” should be set to 1
+        #                 Bits “O”,”N”,”R”,”C” should be set to 1
         pkts.filter_wpan_src64(LEADER).\
             filter_ipv6_dst(COMMISSIONER_1_RLOC).\
             filter_coap_ack(MGMT_ACTIVE_GET_URI).\
@@ -217,9 +217,8 @@
                              p.thread_meshcop.tlv.sec_policy_o == 1 and
                              p.thread_meshcop.tlv.sec_policy_n == 1 and
                              p.thread_meshcop.tlv.sec_policy_r == 1 and
-                             p.thread_meshcop.tlv.sec_policy_c == 1 and
-                             p.thread_meshcop.tlv.sec_policy_b == 1) or
-                            (p.thread_meshcop.tlv.unknown == '0e10ff')).\
+                             p.thread_meshcop.tlv.sec_policy_c == 1) or
+                            (p.thread_meshcop.tlv.unknown == '0e10f7')).\
             must_next()
 
         # Step 5: Commissioner_1 sends MGMT_ACTIVE_SET.req to Leader
@@ -239,7 +238,7 @@
                              }  == set(p.thread_meshcop.tlv.type) and\
                    p.thread_meshcop.tlv.active_tstamp == 15 and\
                    (p.thread_meshcop.tlv.sec_policy_o == 0 or
-                    p.thread_meshcop.tlv.unknown == '0e107f')).\
+                    p.thread_meshcop.tlv.unknown == '0e1077')).\
             must_next()
 
         # Step 6: Leader MUST send MGMT_ACTIVE_SET.rsp to the Commissioner_1
@@ -292,7 +291,7 @@
                              }  == set(p.thread_meshcop.tlv.type) and\
                    p.thread_meshcop.tlv.active_tstamp == 20 and\
                    (p.thread_meshcop.tlv.sec_policy_n == 0 or
-                    p.thread_meshcop.tlv.unknown == '0e10bf')).\
+                    p.thread_meshcop.tlv.unknown == '0e10b7')).\
             must_next()
 
         # Step 10: Leader MUST send MGMT_ACTIVE_SET.rsp to the Commissioner_1
@@ -376,7 +375,7 @@
                              }  == set(p.thread_meshcop.tlv.type) and\
                    p.thread_meshcop.tlv.active_tstamp == 30 and\
                    (p.thread_meshcop.tlv.sec_policy_r == 0 or
-                    p.thread_meshcop.tlv.unknown == '0e10df')).\
+                    p.thread_meshcop.tlv.unknown == '0e10d7')).\
             must_next()
 
         # Step 18: Leader MUST send MGMT_ACTIVE_SET.rsp to the Commissioner_1
@@ -407,7 +406,7 @@
             filter(lambda p:
                    p.mle.tlv.active_tstamp == 30 and\
                    (p.thread_meshcop.tlv.sec_policy_r == 0 or
-                    p.thread_meshcop.tlv.unknown == '0e10df')).\
+                    p.thread_meshcop.tlv.unknown == '0e10d7')).\
             must_next()
 
 
diff --git a/tests/scripts/thread-cert/Cert_9_2_17_Orphan.py b/tests/scripts/thread-cert/Cert_9_2_17_Orphan.py
index c9faea1..ee14763 100755
--- a/tests/scripts/thread-cert/Cert_9_2_17_Orphan.py
+++ b/tests/scripts/thread-cert/Cert_9_2_17_Orphan.py
@@ -116,10 +116,10 @@
 
         # Step 1: Ensure the topology is formed correctly
         # Verify that Leader_1 & Leader_2 are sending MLE Advertisements on separate channels.
+        pkts.copy().filter_wpan_src64(LEADER_1).filter_mle_cmd(MLE_ADVERTISEMENT).must_next()
+        pkts.copy().filter_wpan_src64(LEADER_2).filter_mle_cmd(MLE_ADVERTISEMENT).must_next()
         pkts.filter_wpan_src64(LEADER_1).filter_mle_cmd(MLE_CHILD_ID_RESPONSE).must_next().must_verify(
             lambda p: p.wpan.dst64 == ED and p.thread_meshcop.tlv.channel == [CHANNEL1])
-        pkts.filter_wpan_src64(LEADER_1).filter_mle_cmd(MLE_ADVERTISEMENT).must_next()
-        pkts.copy().filter_wpan_src64(LEADER_2).filter_mle_cmd(MLE_ADVERTISEMENT).must_next()
 
         # Step 4: powers-down Leader_1 and enables connectivity between the ED and Leader_2
         # ED MUST send a MLE Parent Request
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 86658ab..55b931d 100755
--- a/tests/scripts/thread-cert/border_router/test_advertising_proxy.py
+++ b/tests/scripts/thread-cert/border_router/test_advertising_proxy.py
@@ -280,5 +280,48 @@
         self.assertEqual(ipaddress.ip_address(server_host['addresses'][0]), ipaddress.ip_address(host_addr))
 
 
+class SrpClientRemoveNonExistingHost(thread_cert.TestCase):
+    USE_MESSAGE_FACTORY = False
+
+    TOPOLOGY = {
+        BR: {
+            'name': 'BR',
+            'allowlist': [ROUTER],
+            'is_otbr': True,
+            'version': '1.2',
+        },
+        ROUTER: {
+            'name': 'Router',
+            'allowlist': [BR],
+            'version': '1.2',
+        }
+    }
+
+    def test(self):
+        server = self.nodes[BR]
+        client = self.nodes[ROUTER]
+
+        server.srp_server_set_enabled(True)
+        server.srp_server_set_lease_range(LEASE, LEASE, KEY_LEASE, KEY_LEASE)
+        server.start()
+        self.simulator.go(10)
+        self.assertEqual('leader', server.get_state())
+        self.assertEqual(server.srp_server_get_state(), 'running')
+
+        client.start()
+        self.simulator.go(5)
+        self.assertEqual('router', client.get_state())
+
+        # Immediately remove a non-existing host.
+
+        client.srp_client_enable_auto_start_mode()
+        client.srp_client_set_host_name('my-host')
+        self.assertEqual('ToAdd', client.srp_client_get_host_state())
+
+        client.srp_client_remove_host(remove_key=True, send_unreg_to_server=True)
+        self.simulator.go(2)
+        self.assertEqual('Removed', client.srp_client_get_host_state())
+
+
 if __name__ == '__main__':
     unittest.main()
diff --git a/tests/scripts/thread-cert/border_router/test_dnssd_server_multi_border_routers.py b/tests/scripts/thread-cert/border_router/test_dnssd_server_multi_border_routers.py
new file mode 100644
index 0000000..b783980
--- /dev/null
+++ b/tests/scripts/thread-cert/border_router/test_dnssd_server_multi_border_routers.py
@@ -0,0 +1,379 @@
+#!/usr/bin/env python3
+#
+#  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.
+#
+import ipaddress
+import json
+import logging
+import unittest
+
+import config
+import thread_cert
+
+# Test description:
+#   This test verifies DNS-SD server can work well in a multiple border router scenario. The DNS-SD server can answer
+#   queries from the Host.
+#   BR1 is the SRP server and BR2 is the DNS-SD server.
+# Topology:
+#    ----------------(eth)-----------------------
+#           |              |         |
+#          BR1 ---------- BR2      HOST
+#           |
+#        +--+----+
+#        |       |
+#    CLIENT1  CLIENT2
+#
+
+BR1 = 1
+BR2 = 2
+ED1, ED2 = 3, 4
+HOST = 5
+
+DOMAIN = 'default.service.arpa.'
+SERVICE = '_testsrv._udp'
+SERVICE_FULL_NAME = f'{SERVICE}.{DOMAIN}'
+
+VALID_SERVICE_NAMES = [
+    '_abc._udp.default.service.arpa.',
+    '_abc._tcp.default.service.arpa.',
+]
+
+WRONG_SERVICE_NAMES = [
+    '_testsrv._udp.default.service.xxxx.',
+    '_testsrv._txp,default.service.arpa.',
+]
+
+
+class TestDnssdServerOnMultiBr(thread_cert.TestCase):
+    USE_MESSAGE_FACTORY = False
+
+    TOPOLOGY = {
+        BR1: {
+            'name': 'BR1',
+            'is_otbr': True,
+            'version': '1.2',
+            'allowlist': [BR2, ED1, ED2],
+        },
+        BR2: {
+            'name': 'BR2',
+            'is_otbr': True,
+            'version': '1.2',
+            'allowlist': [BR1],
+        },
+        ED1: {
+            'name': 'ED1',
+            'mode': 'rn',
+            'allowlist': [BR1]
+        },
+        ED2: {
+            'name': 'ED2',
+            'mode': 'rn',
+            'allowlist': [BR1],
+        },
+        HOST: {
+            'name': 'Host',
+            'is_host': True
+        },
+    }
+
+    def test(self):
+        br1 = self.nodes[BR1]
+        br2 = self.nodes[BR2]
+        ed1 = self.nodes[ED1]
+        ed2 = self.nodes[ED2]
+        host = self.nodes[HOST]
+
+        host.start(start_radvd=False)
+        self.simulator.go(5)
+
+        br1.start()
+        self.simulator.go(5)
+        self.assertEqual('leader', br1.get_state())
+        br1.srp_server_set_enabled(True)
+
+        br2.stop_mdns_service()
+        br2.stop_otbr_service()
+
+        ed1.start()
+        self.simulator.go(5)
+        self.assertEqual('child', ed1.get_state())
+
+        ed2.start()
+        self.simulator.go(5)
+        self.assertEqual('child', ed2.get_state())
+
+        self.simulator.go(10)
+
+        client1_addrs = [ed1.get_ip6_address(config.ADDRESS_TYPE.OMR)[0], ed1.get_mleid()]
+        self._config_srp_client_services(ed1, 'ins1', 'host1', 11111, 1, 1, client1_addrs)
+
+        client2_addrs = [ed2.get_ip6_address(config.ADDRESS_TYPE.OMR)[0], ed2.get_mleid()]
+        self._config_srp_client_services(ed2, 'ins2', 'host2', 22222, 2, 2, client2_addrs)
+        self.simulator.go(5)
+
+        # start BR2 late to ensure it doesn't have mDNS cache
+        br2.start_mdns_service()
+        br2.start_otbr_service()
+        br2.start()
+
+        self.simulator.go(5)
+
+        br2_addr = br2.get_ip6_address(config.ADDRESS_TYPE.OMR)[0]
+
+        ins1_full_name = f'ins1.{SERVICE_FULL_NAME}'
+        ins2_full_name = f'ins2.{SERVICE_FULL_NAME}'
+        host1_full_name = f'host1.{DOMAIN}'
+        host2_full_name = f'host2.{DOMAIN}'
+        EMPTY_TXT = {}
+
+        def is_int(x):
+            return isinstance(x, int)
+
+        # check if AAAA query works
+        dig_result = host.dns_dig(br2_addr, host1_full_name, 'AAAA')
+        self._assert_dig_result_matches(dig_result, {
+            'QUESTION': [(host1_full_name, 'IN', 'AAAA')],
+            'ANSWER': [(host1_full_name, 'IN', 'AAAA', client1_addrs[0]),],
+        })
+
+        dig_result = host.dns_dig(br2_addr, host2_full_name, 'AAAA')
+        self._assert_dig_result_matches(dig_result, {
+            'QUESTION': [(host2_full_name, 'IN', 'AAAA')],
+            'ANSWER': [(host2_full_name, 'IN', 'AAAA', client2_addrs[0]),],
+        })
+
+        # check if SRV query works
+        dig_result = host.dns_dig(br2_addr, ins1_full_name, 'SRV')
+        self._assert_dig_result_matches(
+            dig_result, {
+                'QUESTION': [(ins1_full_name, 'IN', 'SRV')],
+                'ANSWER': [(ins1_full_name, 'IN', 'SRV', is_int, is_int, 11111, host1_full_name),],
+                'ADDITIONAL': [(host1_full_name, 'IN', 'AAAA', client1_addrs[0]),],
+            })
+
+        dig_result = host.dns_dig(br2_addr, ins2_full_name, 'SRV')
+        self._assert_dig_result_matches(
+            dig_result, {
+                'QUESTION': [(ins2_full_name, 'IN', 'SRV')],
+                'ANSWER': [(ins2_full_name, 'IN', 'SRV', is_int, is_int, 22222, host2_full_name),],
+                'ADDITIONAL': [(host2_full_name, 'IN', 'AAAA', client2_addrs[0]),],
+            })
+
+        # check if TXT query works
+        dig_result = host.dns_dig(br2_addr, ins1_full_name, 'TXT')
+        self._assert_dig_result_matches(dig_result, {
+            'QUESTION': [(ins1_full_name, 'IN', 'TXT')],
+            'ANSWER': [(ins1_full_name, 'IN', 'TXT', EMPTY_TXT)],
+        })
+
+        dig_result = host.dns_dig(br2_addr, ins2_full_name, 'TXT')
+        self._assert_dig_result_matches(dig_result, {
+            'QUESTION': [(ins2_full_name, 'IN', 'TXT')],
+            'ANSWER': [(ins2_full_name, 'IN', 'TXT', EMPTY_TXT)],
+        })
+
+        # check if PTR query works
+        dig_result = host.dns_dig(br2_addr, SERVICE_FULL_NAME, 'PTR')
+
+        self._assert_dig_result_matches_any(dig_result, [{
+            'QUESTION': [(SERVICE_FULL_NAME, 'IN', 'PTR')],
+            'ANSWER': [(SERVICE_FULL_NAME, 'IN', 'PTR', f'ins1.{SERVICE_FULL_NAME}')],
+            'ADDITIONAL': [
+                (ins1_full_name, 'IN', 'SRV', is_int, is_int, 11111, host1_full_name),
+                (ins1_full_name, 'IN', 'TXT', EMPTY_TXT),
+                (host1_full_name, 'IN', 'AAAA', client1_addrs[0]),
+            ],
+        }, {
+            'QUESTION': [(SERVICE_FULL_NAME, 'IN', 'PTR')],
+            'ANSWER': [(SERVICE_FULL_NAME, 'IN', 'PTR', f'ins2.{SERVICE_FULL_NAME}')],
+            'ADDITIONAL': [
+                (ins2_full_name, 'IN', 'SRV', is_int, is_int, 22222, host2_full_name),
+                (ins2_full_name, 'IN', 'TXT', EMPTY_TXT),
+                (host2_full_name, 'IN', 'AAAA', client2_addrs[0]),
+            ],
+        }])
+
+        # check some invalid queries
+        for qtype in ['A', 'CNAME']:
+            dig_result = host.dns_dig(br2_addr, host1_full_name, qtype)
+            self._assert_dig_result_matches(dig_result, {
+                'status': 'NOTIMP',
+            })
+
+        for service_name in WRONG_SERVICE_NAMES:
+            dig_result = host.dns_dig(br2_addr, service_name, 'PTR')
+            self._assert_dig_result_matches(dig_result, {
+                'status': 'NXDOMAIN',
+            })
+
+        # verify Discovery Proxy works for _meshcop._udp
+        self._verify_discovery_proxy_meshcop(br2_addr, br2.get_network_name(), host)
+
+    def _verify_discovery_proxy_meshcop(self, server_addr, network_name, digger):
+        dp_service_name = '_meshcop._udp.default.service.arpa.'
+        dp_hostname = lambda x: x.endswith('.default.service.arpa.')
+
+        def check_border_agent_port(port):
+            return 0 < port <= 65535
+
+        dig_result = digger.dns_dig(server_addr, dp_service_name, 'PTR')
+        for answer in dig_result['ANSWER']:
+            if len(answer) >= 2 and answer[-2] == 'PTR':
+                dp_instance_name = answer[-1]
+                break
+        self._assert_dig_result_matches(
+            dig_result, {
+                'QUESTION': [(dp_service_name, 'IN', 'PTR'),],
+                'ANSWER': [(dp_service_name, 'IN', 'PTR', dp_instance_name),],
+                'ADDITIONAL': [
+                    (dp_instance_name, 'IN', 'SRV', 0, 0, check_border_agent_port, dp_hostname),
+                    (dp_instance_name, 'IN', 'TXT', lambda txt: (isinstance(txt, dict) and txt.get(
+                        'nn') == network_name and 'xp' in txt and 'tv' in txt and 'xa' in txt)),
+                ],
+            })
+
+        # Find the actual host name and IPv6 address
+        dp_ip6_address = None
+        for rr in dig_result['ADDITIONAL']:
+            if rr[3] == 'SRV':
+                dp_hostname = rr[7]
+            elif rr[3] == 'AAAA':
+                dp_ip6_address = rr[4]
+
+        assert isinstance(dp_hostname, str), dig_result
+
+        dig_result = digger.dns_dig(server_addr, dp_instance_name, 'SRV')
+        self._assert_dig_result_matches(
+            dig_result, {
+                'QUESTION': [(dp_instance_name, 'IN', 'SRV'),],
+                'ANSWER': [(dp_instance_name, 'IN', 'SRV', 0, 0, check_border_agent_port, dp_hostname),],
+                'ADDITIONAL': [(dp_instance_name, 'IN', 'TXT', lambda txt: (isinstance(txt, dict) and txt.get(
+                    'nn') == network_name and 'xp' in txt and 'tv' in txt and 'xa' in txt)),],
+            })
+
+        dig_result = digger.dns_dig(server_addr, dp_instance_name, 'TXT')
+        self._assert_dig_result_matches(
+            dig_result, {
+                'QUESTION': [(dp_instance_name, 'IN', 'TXT'),],
+                'ANSWER': [(dp_instance_name, 'IN', 'TXT', lambda txt: (isinstance(txt, dict) and txt.get(
+                    'nn') == network_name and 'xp' in txt and 'tv' in txt and 'xa' in txt)),],
+                'ADDITIONAL': [(dp_instance_name, 'IN', 'SRV', 0, 0, check_border_agent_port, dp_hostname),],
+            })
+
+        if dp_ip6_address is not None:
+            dig_result = digger.dns_dig(server_addr, dp_hostname, 'AAAA')
+
+            self._assert_dig_result_matches(dig_result, {
+                'QUESTION': [(dp_hostname, 'IN', 'AAAA'),],
+                'ANSWER': [(dp_hostname, 'IN', 'AAAA', dp_ip6_address),],
+            })
+
+    def _config_srp_client_services(self, client, instancename, hostname, port, priority, weight, addrs):
+        client.srp_client_enable_auto_start_mode()
+        client.srp_client_set_host_name(hostname)
+        client.srp_client_set_host_address(*addrs)
+        client.srp_client_add_service(instancename, SERVICE, port, priority, weight)
+
+        self.simulator.go(5)
+        self.assertEqual(client.srp_client_get_host_state(), 'Registered')
+
+    def _assert_have_question(self, dig_result, question):
+        for dig_question in dig_result['QUESTION']:
+            if self._match_record(dig_question, question):
+                return
+
+        self.fail((dig_result, question))
+
+    def _assert_have_answer(self, dig_result, record, additional=False):
+        for dig_answer in dig_result['ANSWER' if not additional else 'ADDITIONAL']:
+            dig_answer = list(dig_answer)
+            dig_answer[1:2] = []  # remove TTL from answer
+
+            record = list(record)
+
+            # convert IPv6 addresses to `ipaddress.IPv6Address` before matching
+            if dig_answer[2] == 'AAAA':
+                dig_answer[3] = ipaddress.IPv6Address(dig_answer[3])
+
+            if record[2] == 'AAAA':
+                record[3] = ipaddress.IPv6Address(record[3])
+
+            if self._match_record(dig_answer, record):
+                return
+
+            print('not match: ', dig_answer, record,
+                  list(a == b or (callable(b) and b(a)) for a, b in zip(dig_answer, record)))
+
+        self.fail((record, dig_result))
+
+    def _match_record(self, record, match):
+        assert not any(callable(elem) for elem in record), record
+
+        if record == match:
+            return True
+
+        return all(a == b or (callable(b) and b(a)) for a, b in zip(record, match))
+
+    def _assert_dig_result_matches(self, dig_result, expected_result):
+        self.assertEqual(dig_result['opcode'], expected_result.get('opcode', 'QUERY'), dig_result)
+        self.assertEqual(dig_result['status'], expected_result.get('status', 'NOERROR'), dig_result)
+
+        if 'QUESTION' in expected_result:
+            self.assertEqual(len(dig_result['QUESTION']), len(expected_result['QUESTION']), dig_result)
+
+            for question in expected_result['QUESTION']:
+                self._assert_have_question(dig_result, question)
+
+        if 'ANSWER' in expected_result:
+            self.assertEqual(len(dig_result['ANSWER']), len(expected_result['ANSWER']), dig_result)
+
+            for record in expected_result['ANSWER']:
+                self._assert_have_answer(dig_result, record, additional=False)
+
+        if 'ADDITIONAL' in expected_result:
+            self.assertGreaterEqual(len(dig_result['ADDITIONAL']), len(expected_result['ADDITIONAL']), dig_result)
+
+            for record in expected_result['ADDITIONAL']:
+                self._assert_have_answer(dig_result, record, additional=True)
+
+        logging.info("dig result matches:\r%s", json.dumps(dig_result, indent=True))
+
+    def _assert_dig_result_matches_any(self, dig_result, expected_results):
+        logging.info(f'dig_result = {dig_result}')
+        for expected_result in expected_results:
+            try:
+                self._assert_dig_result_matches(dig_result, expected_result)
+            except Exception:
+                continue
+            else:
+                return
+
+        self.fail("failed to find any matched result")
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/scripts/thread-cert/border_router/test_firewall.py b/tests/scripts/thread-cert/border_router/test_firewall.py
index 8725584..354a010 100644
--- a/tests/scripts/thread-cert/border_router/test_firewall.py
+++ b/tests/scripts/thread-cert/border_router/test_firewall.py
@@ -236,8 +236,9 @@
             vars['Router_1_RLOC16']).filter_ping_request(identifier=_pkt.icmpv6.echo.identifier).must_not_next()
 
         # 8. Host pings router1's link-local address from host's infra address.
-        _pkt = pkts.filter_eth_src(vars['Host_ETH']).filter_ipv6_dst(
-            vars['Router_1_LLA']).filter_ping_request().must_next()
+        # Skip this scenario as for now
+        # _pkt = pkts.filter_eth_src(vars['Host_ETH']).filter_ipv6_dst(
+        #     vars['Router_1_LLA']).filter_ping_request().must_next()
         pkts.filter_wpan_src64(vars['BR_1']).filter_wpan_dst16(
             vars['Router_1_RLOC16']).filter_ping_request(identifier=_pkt.icmpv6.echo.identifier).must_not_next()
 
diff --git a/tests/scripts/thread-cert/border_router/test_mdns_restart.py b/tests/scripts/thread-cert/border_router/test_mdns_restart.py
new file mode 100755
index 0000000..41a9d86
--- /dev/null
+++ b/tests/scripts/thread-cert/border_router/test_mdns_restart.py
@@ -0,0 +1,168 @@
+#!/usr/bin/env python3
+#
+#  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.
+#
+import logging
+import unittest
+from ipaddress import IPv6Network
+
+import config
+import thread_cert
+
+# Test description:
+#   This test verifies that Discovery Proxy and Advertising Proxy of otbr-agent work well after the mDNS daemon
+#   (mdns or avahi-daemon) restarts.
+#
+# Topology:
+#    ----------------(eth)------------------
+#           |                  |     |
+#          BR1 (Leader) ----- BR2   HOST
+#           |                  |
+#           ED1                ED2
+#
+
+BR1 = 1
+BR2 = 2
+HOST = 3
+ED1 = 4
+ED2 = 5
+
+LEASE = 200  # Seconds
+KEY_LEASE = 200  # Seconds
+
+
+class MdnsRestart(thread_cert.TestCase):
+    USE_MESSAGE_FACTORY = False
+
+    TOPOLOGY = {
+        BR1: {
+            'name': 'BR1',
+            'allowlist': [BR2, ED1],
+            'is_otbr': True,
+            'version': '1.2',
+        },
+        BR2: {
+            'name': 'BR2',
+            'allowlist': [BR1, ED2],
+            'is_otbr': True,
+            'version': '1.2',
+        },
+        HOST: {
+            'name': 'Host',
+            'is_host': True,
+        },
+        ED1: {
+            'name': 'ED1',
+            'allowlist': [BR1],
+            'version': '1.2',
+            'mode': 'rn',
+        },
+        ED2: {
+            'name': 'ED2',
+            'allowlist': [BR2],
+            'version': '1.2',
+            'mode': 'rn',
+        },
+    }
+
+    def test(self):
+        br1 = self.nodes[BR1]
+        br2 = self.nodes[BR2]
+        host = self.nodes[HOST]
+        ed1 = self.nodes[ED1]
+        ed2 = self.nodes[ED2]
+
+        host.start(start_radvd=False)
+        self.simulator.go(5)
+
+        br1.start()
+        self.simulator.go(5)
+        self.assertEqual('leader', br1.get_state())
+
+        self.simulator.go(5)
+
+        br2.start()
+        self.simulator.go(5)
+        self.assertEqual('router', br2.get_state())
+
+        ed1.start()
+        ed2.start()
+        self.simulator.go(5)
+        self.assertEqual('child', ed1.get_state())
+        self.assertEqual('child', ed2.get_state())
+
+        br1.srp_server_set_enabled(True)
+        br1.srp_server_set_lease_range(LEASE, LEASE, KEY_LEASE, KEY_LEASE)
+        br2.srp_server_set_enabled(True)
+        br2.srp_server_set_lease_range(LEASE, LEASE, KEY_LEASE, KEY_LEASE)
+        self.simulator.go(3)
+        self.assertEqual(br1.srp_server_get_state(), 'running')
+        self.assertEqual(br2.srp_server_get_state(), 'running')
+
+        # ED1 and ED2 register a service by SRP respectively.
+
+        ed1.srp_client_set_host_name('ed1-host')
+        ed1.srp_client_set_host_address(ed1.get_ip6_address(config.ADDRESS_TYPE.OMR)[0])
+        ed1.srp_client_add_service('ed1', '_ed1._tcp', 12345, txt_entries=['id=ed1'])
+        ed1.srp_client_enable_auto_start_mode()
+
+        ed2.srp_client_set_host_name('ed2-host')
+        ed2.srp_client_set_host_address(ed2.get_ip6_address(config.ADDRESS_TYPE.OMR)[0])
+        ed2.srp_client_add_service('ed2', '_ed2._tcp', 12345, txt_entries=['id=ed2'])
+        ed2.srp_client_enable_auto_start_mode()
+
+        self.simulator.go(10)
+
+        self.assertEqual(len(host.browse_mdns_services('_meshcop._udp')), 2)
+        self.assertEqual(len(host.browse_mdns_services('_ed1._tcp')), 1)
+        self.assertEqual(len(host.browse_mdns_services('_ed2._tcp')), 1)
+
+        # BR1's mdns restart
+        br1.stop_mdns_service()
+        br1.start_mdns_service()
+
+        self.simulator.go(10)
+
+        self.assertEqual(len(host.browse_mdns_services('_meshcop._udp')), 2)
+        self.assertEqual(len(host.browse_mdns_services('_ed1._tcp')), 1)
+        self.assertEqual(len(host.browse_mdns_services('_ed2._tcp')), 1)
+
+        ed1.dns_set_config(br1.get_ip6_address(config.ADDRESS_TYPE.ML_EID))
+        browsed_services = ed1.dns_browse('_ed2._tcp.default.service.arpa')
+        self.assertEqual(len(browsed_services), 1)
+        self.assertEqual(browsed_services['ed2']['port'], 12345)
+        self.assertEqual(browsed_services['ed2']['txt_data'], 'id=656432')
+
+        ed2.dns_set_config(br1.get_ip6_address(config.ADDRESS_TYPE.ML_EID))
+        browsed_services = ed2.dns_browse('_ed1._tcp.default.service.arpa')
+        self.assertEqual(len(browsed_services), 1)
+        self.assertEqual(browsed_services['ed1']['port'], 12345)
+        self.assertEqual(browsed_services['ed1']['txt_data'], 'id=656431')
+
+
+if __name__ == '__main__':
+    unittest.main()
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 8441ea0..0e122d1 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
@@ -132,23 +132,23 @@
         logging.info("ROUTER2 addrs: %r", router2.get_addrs())
         logging.info("HOST    addrs: %r", host.get_addrs())
 
-        self.assertEqual(len(br1.get_prefixes()), 1)
-        self.assertEqual(len(router1.get_prefixes()), 1)
-        self.assertEqual(len(br2.get_prefixes()), 1)
-        self.assertEqual(len(router2.get_prefixes()), 1)
+        self.assertEqual(len(br1.get_netdata_omr_prefixes()), 1)
+        self.assertEqual(len(router1.get_netdata_omr_prefixes()), 1)
+        self.assertEqual(len(br2.get_netdata_omr_prefixes()), 1)
+        self.assertEqual(len(router2.get_netdata_omr_prefixes()), 1)
 
-        br1_omr_prefix = br1.get_omr_prefix()
-        self.assertEqual(br1_omr_prefix, br1.get_prefixes()[0].split(' ')[0])
+        br1_omr_prefix = br1.get_br_omr_prefix()
+        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_routes()), 2)
-        self.assertEqual(len(router1.get_routes()), 2)
-        self.assertEqual(len(br2.get_routes()), 2)
-        self.assertEqual(len(router2.get_routes()), 2)
+        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)
 
-        br1_on_link_prefix = br1.get_on_link_prefix()
-        self.assertEqual(br1_on_link_prefix, br1.get_routes()[0].split(' ')[0])
-        self.assertEqual(br1_on_link_prefix, br1.get_routes()[1].split(' ')[0])
+        br1_on_link_prefix = br1.get_br_on_link_prefix()
+        self.assertEqual(br1_on_link_prefix, br1.get_netdata_non_nat64_prefixes()[0])
+        self.assertEqual(br1_on_link_prefix, br1.get_netdata_non_nat64_prefixes()[0])
 
         self.assertEqual(len(br1.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1)
         self.assertEqual(len(router1.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1)
@@ -183,25 +183,23 @@
 
         self.assertGreaterEqual(len(host.get_addrs()), 3)
 
-        self.assertEqual(len(br1.get_prefixes()), 1)
-        self.assertEqual(len(router1.get_prefixes()), 1)
-        self.assertEqual(len(br2.get_prefixes()), 1)
-        self.assertEqual(len(router2.get_prefixes()), 1)
+        self.assertEqual(len(br1.get_netdata_omr_prefixes()), 1)
+        self.assertEqual(len(router1.get_netdata_omr_prefixes()), 1)
+        self.assertEqual(len(br2.get_netdata_omr_prefixes()), 1)
+        self.assertEqual(len(router2.get_netdata_omr_prefixes()), 1)
 
-        br2_omr_prefix = br2.get_omr_prefix()
-        self.assertEqual(br2_omr_prefix, br2.get_prefixes()[0].split(' ')[0])
+        br2_omr_prefix = br2.get_br_omr_prefix()
+        self.assertEqual(br2_omr_prefix, br2.get_netdata_omr_prefixes()[0])
 
         # Only BR2 will keep the route for BR1's on-link prefix
         # and add route for on-link prefix of its own.
-        self.assertEqual(len(br1.get_routes()), 2)
-        self.assertEqual(len(router1.get_routes()), 2)
-        self.assertEqual(len(br2.get_routes()), 2)
-        self.assertEqual(len(router2.get_routes()), 2)
+        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)
 
-        br2_external_routes = [route.split(' ')[0] for route in br2.get_routes()]
-
-        br2_on_link_prefix = br2.get_on_link_prefix()
-        self.assertEqual(set(map(IPv6Network, br2_external_routes)),
+        br2_on_link_prefix = br2.get_br_on_link_prefix()
+        self.assertEqual(set(map(IPv6Network, br2.get_netdata_non_nat64_prefixes())),
                          set(map(IPv6Network, [br1_on_link_prefix, br2_on_link_prefix])))
 
         self.assertEqual(len(br1.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1)
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 b0e9f92..ca7e382 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
@@ -108,6 +108,9 @@
         self.simulator.go(5)
         self.assertEqual('router', router2.get_state())
 
+        # Wait for network to stabilize
+        self.simulator.go(15)
+
         self.collect_ipaddrs()
 
         logging.info("BR1     addrs: %r", br1.get_addrs())
@@ -115,22 +118,22 @@
         logging.info("BR2     addrs: %r", br2.get_addrs())
         logging.info("ROUTER2 addrs: %r", router2.get_addrs())
 
-        self.assertTrue(len(br1.get_prefixes()) == 1)
-        self.assertTrue(len(router1.get_prefixes()) == 1)
-        self.assertTrue(len(br2.get_prefixes()) == 1)
-        self.assertTrue(len(router2.get_prefixes()) == 1)
+        self.assertTrue(len(br1.get_netdata_omr_prefixes()) == 1)
+        self.assertTrue(len(router1.get_netdata_omr_prefixes()) == 1)
+        self.assertTrue(len(br2.get_netdata_omr_prefixes()) == 1)
+        self.assertTrue(len(router2.get_netdata_omr_prefixes()) == 1)
 
-        br1_omr_prefix = br1.get_omr_prefix()
-        br2_omr_prefix = br2.get_omr_prefix()
+        br1_omr_prefix = br1.get_br_omr_prefix()
+        br2_omr_prefix = br2.get_br_omr_prefix()
 
         self.assertNotEqual(br1_omr_prefix, br2_omr_prefix)
 
         # Each BR should independently register an external route for the on-link prefix
         # and OMR prefix in another Thread Network.
-        self.assertTrue(len(br1.get_routes()) == 2)
-        self.assertTrue(len(router1.get_routes()) == 2)
-        self.assertTrue(len(br2.get_routes()) == 2)
-        self.assertTrue(len(router2.get_routes()) == 2)
+        self.assertTrue(len(br1.get_netdata_non_nat64_prefixes()) == 2)
+        self.assertTrue(len(router1.get_netdata_non_nat64_prefixes()) == 2)
+        self.assertTrue(len(br2.get_netdata_non_nat64_prefixes()) == 2)
+        self.assertTrue(len(router2.get_netdata_non_nat64_prefixes()) == 2)
 
         br1_external_routes = br1.get_routes()
         br2_external_routes = br2.get_routes()
diff --git a/tests/scripts/thread-cert/border_router/test_nat64_multi_border_routers.py b/tests/scripts/thread-cert/border_router/test_nat64_multi_border_routers.py
new file mode 100644
index 0000000..64e5ac8
--- /dev/null
+++ b/tests/scripts/thread-cert/border_router/test_nat64_multi_border_routers.py
@@ -0,0 +1,139 @@
+#!/usr/bin/env python3
+#
+#  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.
+#
+import unittest
+
+import thread_cert
+
+# Test description:
+#   This test verifies that a single NAT64 prefix is advertised when there are
+#   multiple Border Routers in the same Thread and infrastructure network.
+#
+#   TODO: add checks for outbound connectivity from Thread device to IPv4 host
+#         after OTBR change is ready.
+#
+# Topology:
+#    ----------------(eth)--------------------------
+#           |                 |             |
+#          BR1 (Leader) ---- BR2           HOST
+#           |
+#        ROUTER
+#
+
+BR1 = 1
+ROUTER = 2
+BR2 = 3
+HOST = 4
+
+
+class Nat64MultiBorderRouter(thread_cert.TestCase):
+    USE_MESSAGE_FACTORY = False
+
+    TOPOLOGY = {
+        BR1: {
+            'name': 'BR1',
+            'allowlist': [ROUTER, BR2],
+            'is_otbr': True,
+            'version': '1.2',
+        },
+        ROUTER: {
+            'name': 'Router',
+            'allowlist': [BR1],
+            'version': '1.2',
+        },
+        BR2: {
+            'name': 'BR2',
+            'allowlist': [BR1],
+            'is_otbr': True,
+            'version': '1.2'
+        },
+        HOST: {
+            'name': 'Host',
+            'is_host': True
+        },
+    }
+
+    def test(self):
+        br1 = self.nodes[BR1]
+        router = self.nodes[ROUTER]
+        br2 = self.nodes[BR2]
+        host = self.nodes[HOST]
+
+        host.start(start_radvd=False)
+        self.simulator.go(5)
+
+        br1.start()
+        self.simulator.go(5)
+        self.assertEqual('leader', br1.get_state())
+
+        router.start()
+        self.simulator.go(5)
+        self.assertEqual('router', router.get_state())
+
+        #
+        # Case 1. BR2 joins the network later and it will not add
+        #         its local nat64 prefix to Network Data.
+        #
+        br2.start()
+        self.simulator.go(5)
+        self.assertEqual('router', br2.get_state())
+
+        # Only 1 NAT64 prefix in Network Data.
+        self.assertEqual(len(br1.get_netdata_nat64_prefix()), 1)
+        self.assertEqual(len(br2.get_netdata_nat64_prefix()), 1)
+        self.assertEqual(br1.get_netdata_nat64_prefix()[0], br2.get_netdata_nat64_prefix()[0])
+        nat64_prefix = br1.get_netdata_nat64_prefix()[0]
+
+        # The NAT64 prefix in Network Data is same as BR1's local NAT64 prefix.
+        br1_nat64_prefix = br1.get_br_nat64_prefix()
+        br2_nat64_prefix = br2.get_br_nat64_prefix()
+        self.assertEqual(nat64_prefix, br1_nat64_prefix)
+        self.assertNotEqual(nat64_prefix, br2_nat64_prefix)
+
+        #
+        # Case 2. Disable and re-enable border routing on BR1.
+        #
+        br1.disable_br()
+        self.simulator.go(5)
+
+        # BR1 withdraws its prefix and BR2 advertises its prefix.
+        self.assertEqual(len(br1.get_netdata_nat64_prefix()), 1)
+        self.assertEqual(br2_nat64_prefix, br1.get_netdata_nat64_prefix()[0])
+        self.assertNotEqual(br1_nat64_prefix, br1.get_netdata_nat64_prefix()[0])
+
+        br1.enable_br()
+        self.simulator.go(5)
+
+        # NAT64 prefix in Network Data is still advertised by BR2.
+        self.assertEqual(len(br1.get_netdata_nat64_prefix()), 1)
+        self.assertEqual(br2_nat64_prefix, br1.get_netdata_nat64_prefix()[0])
+        self.assertNotEqual(br1_nat64_prefix, br1.get_netdata_nat64_prefix()[0])
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tests/scripts/thread-cert/border_router/test_nat64_single_border_router.py b/tests/scripts/thread-cert/border_router/test_nat64_single_border_router.py
new file mode 100644
index 0000000..f28158a
--- /dev/null
+++ b/tests/scripts/thread-cert/border_router/test_nat64_single_border_router.py
@@ -0,0 +1,156 @@
+#!/usr/bin/env python3
+#
+#  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.
+#
+import unittest
+
+import thread_cert
+
+# Test description:
+#   This test verifies the advertisement of NAT64 prefix in Thread network.
+#
+#   TODO: add checks for outbound connectivity from Thread device to IPv4 host
+#         after OTBR change is ready.
+#
+# Topology:
+#    ----------------(eth)--------------------
+#           |                 |
+#          BR (Leader)      HOST
+#           |
+#        ROUTER
+#
+
+BR = 1
+ROUTER = 2
+HOST = 3
+
+# The prefix is set small enough that a random-generated NAT64 prefix is very
+# likely greater than it. So that the BR will remove the random-generated one.
+SMALL_NAT64_PREFIX = "fd00:00:00:01:00:00::/96"
+
+
+class Nat64SingleBorderRouter(thread_cert.TestCase):
+    USE_MESSAGE_FACTORY = False
+
+    TOPOLOGY = {
+        BR: {
+            'name': 'BR',
+            'allowlist': [ROUTER],
+            'is_otbr': True,
+            'version': '1.2',
+        },
+        ROUTER: {
+            'name': 'Router',
+            'allowlist': [BR],
+            'version': '1.2',
+        },
+        HOST: {
+            'name': 'Host',
+            'is_host': True
+        },
+    }
+
+    def test(self):
+        br = self.nodes[BR]
+        router = self.nodes[ROUTER]
+        host = self.nodes[HOST]
+
+        host.start(start_radvd=False)
+        self.simulator.go(5)
+
+        br.start()
+        self.simulator.go(5)
+        self.assertEqual('leader', br.get_state())
+
+        router.start()
+        self.simulator.go(5)
+        self.assertEqual('router', router.get_state())
+
+        #
+        # Case 1. Border router advertises its local NAT64 prefix.
+        #
+        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(nat64_prefix, local_nat64_prefix)
+
+        #
+        # Case 2.
+        # User adds a smaller NAT64 prefix and the local prefix is withdrawn.
+        # User removes the smaller NAT64 prefix and the local prefix is re-added.
+        #
+        br.add_route(SMALL_NAT64_PREFIX, stable=False, nat64=True)
+        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])
+
+        br.remove_route(SMALL_NAT64_PREFIX)
+        br.register_netdata()
+        self.simulator.go(5)
+
+        self.assertEqual(len(br.get_netdata_nat64_prefix()), 1)
+        self.assertEqual(local_nat64_prefix, br.get_netdata_nat64_prefix()[0])
+
+        #
+        # Case 3. Disable and re-enable border routing on the border router.
+        #
+        br.disable_br()
+        self.simulator.go(5)
+
+        # NAT64 prefix is withdrawn from Network Data.
+        self.assertEqual(len(br.get_netdata_nat64_prefix()), 0)
+
+        br.enable_br()
+        self.simulator.go(5)
+
+        # 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])
+
+        #
+        # Case 4. Disable and re-enable ethernet on the border router.
+        #
+        br.disable_ether()
+        self.simulator.go(5)
+
+        # NAT64 prefix is withdrawn from Network Data.
+        self.assertEqual(len(br.get_netdata_nat64_prefix()), 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])
+
+
+if __name__ == '__main__':
+    unittest.main()
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 bf47e1d..17a201b 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
@@ -130,6 +130,12 @@
         self.check_meshcop_service(br1, host)
         self.check_meshcop_service(br2, host)
 
+        br1.factory_reset()
+        br1.set_network_name('ot-br-1-3')
+        self.assertEqual(len(host.browse_mdns_services('_meshcop._udp')), 2)
+        self.check_meshcop_service(br1, host)
+        self.check_meshcop_service(br2, host)
+
     def check_meshcop_service(self, br, host):
         services = self.discover_all_meshcop_services(host)
         for service in services:
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 3c4f484..dde4de5 100755
--- a/tests/scripts/thread-cert/border_router/test_radvd_coexist.py
+++ b/tests/scripts/thread-cert/border_router/test_radvd_coexist.py
@@ -100,10 +100,10 @@
         logging.info("ROUTER  addrs: %r", router.get_addrs())
         logging.info("HOST    addrs: %r", host.get_addrs())
 
-        self.assertEqual(len(br.get_prefixes()), 1)
-        self.assertEqual(len(router.get_prefixes()), 1)
-        self.assertEqual(len(br.get_routes()), 1)
-        self.assertEqual(len(router.get_routes()), 1)
+        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_ip6_address(config.ADDRESS_TYPE.OMR)), 1)
         self.assertEqual(len(router.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1)
@@ -120,10 +120,10 @@
         br.stop_radvd_service()
         self.simulator.go(15)
 
-        self.assertEqual(len(br.get_prefixes()), 1)
-        self.assertEqual(len(router.get_prefixes()), 1)
-        self.assertEqual(len(br.get_routes()), 1)
-        self.assertEqual(len(router.get_routes()), 1)
+        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_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 756786c..d30dfbb 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
@@ -102,13 +102,13 @@
         logging.info("ROUTER  addrs: %r", router.get_addrs())
         logging.info("HOST    addrs: %r", host.get_addrs())
 
-        self.assertEqual(len(br.get_prefixes()), 1)
-        self.assertEqual(len(router.get_prefixes()), 1)
-        self.assertEqual(len(br.get_routes()), 1)
-        self.assertEqual(len(router.get_routes()), 1)
+        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)
 
-        omr_prefix = br.get_prefixes()[0]
-        external_route = br.get_routes()[0]
+        omr_prefix = br.get_br_omr_prefix()
+        on_link_prefix = br.get_br_on_link_prefix()
 
         self.assertEqual(len(br.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1)
         self.assertEqual(len(router.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1)
@@ -142,10 +142,10 @@
 
         self.assertGreaterEqual(len(host.get_addrs()), 2)
 
-        self.assertEqual(len(br.get_prefixes()), 2)
-        self.assertEqual(len(router.get_prefixes()), 2)
-        self.assertEqual(len(br.get_routes()), 1)
-        self.assertEqual(len(router.get_routes()), 1)
+        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_ip6_address(config.ADDRESS_TYPE.OMR)), 2)
         self.assertEqual(len(router.get_ip6_address(config.ADDRESS_TYPE.OMR)), 2)
@@ -168,16 +168,16 @@
         logging.info("ROUTER addrs: %r", router.get_addrs())
         logging.info("HOST    addrs: %r", host.get_addrs())
 
-        self.assertEqual(len(br.get_prefixes()), 1)
-        self.assertEqual(len(router.get_prefixes()), 1)
-        self.assertEqual(len(br.get_routes()), 1)
-        self.assertEqual(len(router.get_routes()), 1)
+        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)
 
         # The same local OMR and on-link prefix should be re-register.
-        self.assertEqual(br.get_prefixes(), [omr_prefix])
-        self.assertEqual(router.get_prefixes(), [omr_prefix])
-        self.assertEqual(br.get_routes(), [external_route])
-        self.assertEqual(router.get_routes(), [external_route])
+        self.assertEqual(br.get_netdata_omr_prefixes(), [omr_prefix])
+        self.assertEqual(router.get_netdata_omr_prefixes(), [omr_prefix])
+        self.assertEqual(br.get_netdata_non_nat64_prefixes(), [on_link_prefix])
+        self.assertEqual(router.get_netdata_non_nat64_prefixes(), [on_link_prefix])
 
         self.assertEqual(len(br.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1)
         self.assertEqual(len(router.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1)
@@ -222,16 +222,16 @@
         logging.info("ROUTER addrs: %r", router.get_addrs())
         logging.info("HOST    addrs: %r", host.get_addrs())
 
-        self.assertEqual(len(br.get_prefixes()), 1)
-        self.assertEqual(len(router.get_prefixes()), 1)
-        self.assertEqual(len(br.get_routes()), 1)
-        self.assertEqual(len(router.get_routes()), 1)
+        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)
 
         # The same local OMR and on-link prefix should be re-registered.
-        self.assertEqual(br.get_prefixes(), [omr_prefix])
-        self.assertEqual(router.get_prefixes(), [omr_prefix])
-        self.assertEqual(br.get_routes(), [external_route])
-        self.assertEqual(router.get_routes(), [external_route])
+        self.assertEqual(br.get_netdata_omr_prefixes(), [omr_prefix])
+        self.assertEqual(router.get_netdata_omr_prefixes(), [omr_prefix])
+        self.assertEqual(br.get_netdata_non_nat64_prefixes(), [on_link_prefix])
+        self.assertEqual(router.get_netdata_non_nat64_prefixes(), [on_link_prefix])
 
         self.assertEqual(len(br.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1)
         self.assertEqual(len(router.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1)
@@ -267,25 +267,25 @@
 
         br.enable_ether()
 
-        # The routing manager may fail to send RS and will wait for 60 seconds
+        # The routing manager may fail to send RS and will wait for 4 seconds
         # before retrying.
-        self.simulator.go(80)
+        self.simulator.go(20)
         self.collect_ipaddrs()
 
         logging.info("BR     addrs: %r", br.get_addrs())
         logging.info("ROUTER addrs: %r", router.get_addrs())
         logging.info("HOST    addrs: %r", host.get_addrs())
 
-        self.assertEqual(len(br.get_prefixes()), 1)
-        self.assertEqual(len(router.get_prefixes()), 1)
-        self.assertEqual(len(br.get_routes()), 1)
-        self.assertEqual(len(router.get_routes()), 1)
+        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)
 
         # The same local OMR and on-link prefix should be re-registered.
-        self.assertEqual(br.get_prefixes(), [omr_prefix])
-        self.assertEqual(router.get_prefixes(), [omr_prefix])
-        self.assertEqual(br.get_routes(), [external_route])
-        self.assertEqual(router.get_routes(), [external_route])
+        self.assertEqual(br.get_netdata_omr_prefixes(), [omr_prefix])
+        self.assertEqual(router.get_netdata_omr_prefixes(), [omr_prefix])
+        self.assertEqual(br.get_netdata_non_nat64_prefixes(), [on_link_prefix])
+        self.assertEqual(router.get_netdata_non_nat64_prefixes(), [on_link_prefix])
 
         self.assertEqual(len(br.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1)
         self.assertEqual(len(router.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1)
@@ -319,8 +319,8 @@
         br.start_radvd_service(prefix=config.ONLINK_GUA_PREFIX, slaac=True)
         self.simulator.go(5)
 
-        self.assertEqual(len(br.get_routes()), 2)
-        self.assertEqual(len(router.get_routes()), 2)
+        self.assertEqual(len(br.get_netdata_non_nat64_prefixes()), 2)
+        self.assertEqual(len(router.get_netdata_non_nat64_prefixes()), 2)
 
         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_vicarious_router_solicit.py b/tests/scripts/thread-cert/border_router/test_vicarious_router_solicit.py
index ee56842..4befa11 100755
--- a/tests/scripts/thread-cert/border_router/test_vicarious_router_solicit.py
+++ b/tests/scripts/thread-cert/border_router/test_vicarious_router_solicit.py
@@ -122,11 +122,11 @@
         logging.info("ROUTER1 addrs: %r", router1.get_addrs())
         logging.info("HOST    addrs: %r", host.get_addrs())
 
-        self.assertEqual(len(br1.get_routes()), 1)
-        br1_on_link_prefix = br1.get_routes()[0].split(' ')[0]
-        self.assertEqual(IPv6Network(br1_on_link_prefix), IPv6Network(ON_LINK_PREFIX))
+        self.assertEqual(len(br1.get_netdata_non_nat64_prefixes()), 1)
+        on_link_prefix = br1.get_netdata_non_nat64_prefixes()[0]
+        self.assertEqual(IPv6Network(on_link_prefix), IPv6Network(ON_LINK_PREFIX))
 
-        host_on_link_addr = host.get_matched_ula_addresses(br1_on_link_prefix)[0]
+        host_on_link_addr = host.get_matched_ula_addresses(on_link_prefix)[0]
         self.assertTrue(router1.ping(host_on_link_addr))
         self.assertTrue(
             host.ping(router1.get_ip6_address(config.ADDRESS_TYPE.OMR)[0], backbone=True, interface=host_on_link_addr))
@@ -151,29 +151,27 @@
         logging.info("ROUTER2 addrs: %r", router2.get_addrs())
         logging.info("HOST    addrs: %r", host.get_addrs())
 
-        self.assertTrue(len(br1.get_prefixes()) == 1)
-        self.assertTrue(len(router1.get_prefixes()) == 1)
-        self.assertTrue(len(br2.get_prefixes()) == 1)
-        self.assertTrue(len(router2.get_prefixes()) == 1)
+        self.assertTrue(len(br1.get_netdata_omr_prefixes()) == 1)
+        self.assertTrue(len(router1.get_netdata_omr_prefixes()) == 1)
+        self.assertTrue(len(br2.get_netdata_omr_prefixes()) == 1)
+        self.assertTrue(len(router2.get_netdata_omr_prefixes()) == 1)
 
-        br1_omr_prefix = br1.get_omr_prefix()
-        br2_omr_prefix = br2.get_omr_prefix()
+        br1_omr_prefix = br1.get_br_omr_prefix()
+        br2_omr_prefix = br2.get_br_omr_prefix()
         self.assertNotEqual(br1_omr_prefix, br2_omr_prefix)
 
         # Verify that the Border Routers starts advertsing new on-link prefix
         # 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_routes()), 3)
-        self.assertEqual(len(router1.get_routes()), 3)
-        self.assertEqual(len(br2.get_routes()), 2)
-        self.assertEqual(len(router2.get_routes()), 2)
+        self.assertEqual(len(br1.get_netdata_non_nat64_prefixes()), 3)
+        self.assertEqual(len(router1.get_netdata_non_nat64_prefixes()), 3)
+        self.assertEqual(len(br2.get_netdata_non_nat64_prefixes()), 2)
+        self.assertEqual(len(router2.get_netdata_non_nat64_prefixes()), 2)
 
-        br1_external_routes = [route.split(' ')[0] for route in br1.get_routes()]
-        br2_external_routes = [route.split(' ')[0] for route in br2.get_routes()]
-
-        on_link_prefixes = list(set(br1_external_routes).intersection(br2_external_routes))
+        on_link_prefixes = list(
+            set(br1.get_netdata_non_nat64_prefixes()).intersection(br2.get_netdata_non_nat64_prefixes()))
         self.assertEqual(len(on_link_prefixes), 1)
-        self.assertEqual(IPv6Network(on_link_prefixes[0]), IPv6Network(br2.get_on_link_prefix()))
+        self.assertEqual(IPv6Network(on_link_prefixes[0]), IPv6Network(br2.get_br_on_link_prefix()))
 
         router1_omr_addr = router1.get_ip6_address(config.ADDRESS_TYPE.OMR)[0]
         router2_omr_addr = router2.get_ip6_address(config.ADDRESS_TYPE.OMR)[0]
diff --git a/tests/scripts/thread-cert/config.py b/tests/scripts/thread-cert/config.py
index 22958f6..62f36bb 100755
--- a/tests/scripts/thread-cert/config.py
+++ b/tests/scripts/thread-cert/config.py
@@ -45,7 +45,6 @@
 from tlvs_parsing import SubTlvsFactory
 
 # This extended address will generate the MESH_LOCAL_PREFIX
-EXTENDED_PANID = '000db80000000000'
 MESH_LOCAL_PREFIX = 'fd00:db8::/64'
 MESH_LOCAL_PREFIX_REGEX_PATTERN = '^fd00:0?db8:0{0,4}:0{0,4}'
 ROUTING_LOCATOR = '64/:0:ff:fe00:/16'
diff --git a/tests/scripts/thread-cert/find_border_agents.py b/tests/scripts/thread-cert/find_border_agents.py
new file mode 100644
index 0000000..7a7306d
--- /dev/null
+++ b/tests/scripts/thread-cert/find_border_agents.py
@@ -0,0 +1,98 @@
+#!/usr/bin/env python3
+#
+#  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.
+
+import ipaddress
+import sys
+import time
+from zeroconf import IPVersion, ServiceBrowser, ServiceStateChange, Zeroconf, DNSAddress, DNSService, DNSText
+
+
+def on_service_state_change(zeroconf, service_type, name, state_change):
+    if state_change is ServiceStateChange.Added:
+        zeroconf.get_service_info(service_type, name)
+
+
+class BorderAgent(object):
+    alias = None
+    server_name = None
+    link_local_addr = None
+    port = None
+    thread_status = None
+
+    def __init__(self, alias):
+        self.alias = alias
+
+    def __repr__(self):
+        return str([self.alias, self.link_local_addr, self.port, self.thread_status])
+
+
+def parse_cache(cache):
+    border_agents = []
+
+    # Find all border routers
+    for ptr in cache['_meshcop._udp.local.']:
+        border_agents.append(BorderAgent(ptr.alias))
+
+    # Find server name, port and Thread Interface status for each border router
+    for ba in border_agents:
+        for record in cache[ba.alias.lower()]:
+            if isinstance(record, DNSService):
+                ba.server_name = record.server
+                ba.port = record.port
+            elif isinstance(record, DNSText):
+                text = bytearray(record.text)
+                sb = text.split(b'sb=')[1][0:4]
+                ba.thread_status = (sb[3] & 0x18) >> 3
+
+    # Find link local address for each border router
+    for ba in border_agents:
+        for record in cache[ba.server_name.lower()]:
+            if isinstance(record, DNSAddress):
+                addr = ipaddress.ip_address(record.address)
+                if isinstance(addr, ipaddress.IPv6Address) and addr.is_link_local:
+                    ba.link_local_addr = str(addr)
+                    break
+
+    return border_agents
+
+
+def main():
+    # Browse border agents
+    zeroconf = Zeroconf(ip_version=IPVersion.V6Only)
+    ServiceBrowser(zeroconf, "_meshcop._udp.local.", handlers=[on_service_state_change])
+    time.sleep(2)
+    cache = zeroconf.cache.cache
+    zeroconf.close()
+
+    border_agents = parse_cache(cache)
+    for ba in border_agents:
+        print(ba)
+
+
+if __name__ == '__main__':
+    main()
diff --git a/tests/scripts/thread-cert/node.py b/tests/scripts/thread-cert/node.py
index 3191a50..50b3694 100755
--- a/tests/scripts/thread-cert/node.py
+++ b/tests/scripts/thread-cert/node.py
@@ -104,6 +104,7 @@
         return path
 
     def _launch_docker(self):
+        logging.info(f'Docker image: {config.OTBR_DOCKER_IMAGE}')
         subprocess.check_call(f"docker rm -f {self._docker_name} || true", shell=True)
         CI_ENV = os.getenv('CI_ENV', '').split()
         os.makedirs('/tmp/coverage/', exist_ok=True)
@@ -161,6 +162,14 @@
         self.stop_ot_ctl()
         self.bash('service otbr-agent stop')
 
+    def stop_mdns_service(self):
+        self.bash('service avahi-daemon stop')
+        self.bash('service mdns stop')
+
+    def start_mdns_service(self):
+        self.bash('service avahi-daemon start')
+        self.bash('service mdns start')
+
     def start_ot_ctl(self):
         cmd = f'docker exec -i {self._docker_name} ot-ctl'
         self.pexpect = pexpect.popen_spawn.PopenSpawn(cmd, timeout=30)
@@ -605,7 +614,7 @@
 
         super().__init__(nodeid, **kwargs)
 
-        self.set_extpanid(config.EXTENDED_PANID)
+        self.set_mesh_local_prefix(config.MESH_LOCAL_PREFIX)
         self.set_addr64('%016x' % (thread_cert.EXTENDED_ADDRESS_BASE + nodeid))
 
     def _expect(self, pattern, timeout=-1, *args, **kwargs):
@@ -678,7 +687,7 @@
         return lines
 
     def __is_logging_line(self, line: str) -> bool:
-        return len(line) >= 6 and line[:6] in {'[DEBG]', '[INFO]', '[NOTE]', '[WARN]', '[CRIT]', '[NONE]'}
+        return len(line) >= 3 and line[:3] in {'[D]', '[I]', '[N]', '[W]', '[C]', '[-]'}
 
     def read_cert_messages_in_commissioning_log(self, timeout=-1):
         """Get the log of the traffic after DTLS handshake.
@@ -1408,6 +1417,14 @@
         self.send_command('extpanid')
         return self._expect_result('[0-9a-fA-F]{16}')
 
+    def get_mesh_local_prefix(self):
+        self.send_command('prefix meshlocal')
+        return self._expect_command_output()[0]
+
+    def set_mesh_local_prefix(self, mesh_local_prefix):
+        self.send_command('prefix meshlocal %s' % mesh_local_prefix)
+        self._expect_done()
+
     def get_joiner_id(self):
         self.send_command('joiner id')
         return self._expect_result('[0-9a-fA-F]{16}')
@@ -1897,16 +1914,41 @@
         self.send_command('br disable')
         self._expect_done()
 
-    def get_omr_prefix(self):
+    def get_br_omr_prefix(self):
         cmd = 'br omrprefix'
         self.send_command(cmd)
         return self._expect_command_output()[0]
 
-    def get_on_link_prefix(self):
+    def get_netdata_omr_prefixes(self):
+        prefixes = [prefix.split(' ')[0] for prefix in self.get_prefixes()]
+        return prefixes
+
+    def get_br_on_link_prefix(self):
         cmd = 'br onlinkprefix'
         self.send_command(cmd)
         return self._expect_command_output()[0]
 
+    def get_netdata_non_nat64_prefixes(self):
+        prefixes = []
+        routes = self.get_routes()
+        for route in routes:
+            if 'n' not in route.split(' ')[1]:
+                prefixes.append(route.split(' ')[0])
+        return prefixes
+
+    def get_br_nat64_prefix(self):
+        cmd = 'br nat64prefix'
+        self.send_command(cmd)
+        return self._expect_command_output()[0]
+
+    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']
 
@@ -1944,10 +1986,12 @@
 
         return netdata
 
-    def add_route(self, prefix, stable=False, prf='med'):
+    def add_route(self, prefix, stable=False, nat64=False, prf='med'):
         cmd = 'route add %s ' % prefix
         if stable:
             cmd += 's'
+        if nat64:
+            cmd += 'n'
         cmd += ' %s' % prf
         self.send_command(cmd)
         self._expect_done()
@@ -2049,15 +2093,11 @@
         if result == 1:
             networks = []
             for line in self._expect_command_output()[2:]:
-                _, J, networkname, extpanid, panid, extaddr, channel, dbm, lqi, _ = map(str.strip, line.split('|'))
-                J = bool(int(J))
+                _, panid, extaddr, channel, dbm, lqi, _ = map(str.strip, line.split('|'))
                 panid = int(panid, 16)
                 channel, dbm, lqi = map(int, (channel, dbm, lqi))
 
                 networks.append({
-                    'joinable': J,
-                    'networkname': networkname,
-                    'extpanid': extpanid,
                     'panid': panid,
                     'extaddr': extaddr,
                     'channel': channel,
@@ -2113,7 +2153,13 @@
         return result
 
     def reset(self):
-        self.send_command('reset', expect_command_echo=False)
+        self._reset('reset')
+
+    def factory_reset(self):
+        self._reset('factoryreset')
+
+    def _reset(self, cmd):
+        self.send_command(cmd, expect_command_echo=False)
         time.sleep(self.RESET_DELAY)
         # Send a "version" command and drain the CLI output after reset
         self.send_command('version', expect_command_echo=False)
@@ -3106,13 +3152,13 @@
         """Enable the ethernet interface.
         """
 
-        self.bash(f'ifconfig {self.ETH_DEV} up')
+        self.bash(f'ip link set {self.ETH_DEV} up')
 
     def disable_ether(self):
         """Disable the ethernet interface.
         """
 
-        self.bash(f'ifconfig {self.ETH_DEV} down')
+        self.bash(f'ip link set {self.ETH_DEV} down')
 
     def get_ether_addrs(self):
         output = self.bash(f'ip -6 addr list dev {self.ETH_DEV}')
diff --git a/tests/scripts/thread-cert/pktverify/consts.py b/tests/scripts/thread-cert/pktverify/consts.py
index 5365834..3946d28 100644
--- a/tests/scripts/thread-cert/pktverify/consts.py
+++ b/tests/scripts/thread-cert/pktverify/consts.py
@@ -391,6 +391,20 @@
 
 # ICMPv6 Types
 ICMPV6_TYPE_DESTINATION_UNREACHABLE = 1
+ICMPV6_TYPE_PACKET_TO_BIG = 2
+ICMPV6_TYPE_TIME_EXCEEDED = 3
+ICMPV6_TYPE_PARAMETER_PROBLEM = 4
+ICMPV6_TYPE_ECHO_REQUEST = 128
+ICMPV6_TYPE_ECHO_REPLY = 129
+
+THREAD_ALLOWED_ICMPV6_TYPES = [
+    ICMPV6_TYPE_DESTINATION_UNREACHABLE,
+    ICMPV6_TYPE_PACKET_TO_BIG,
+    ICMPV6_TYPE_TIME_EXCEEDED,
+    ICMPV6_TYPE_PARAMETER_PROBLEM,
+    ICMPV6_TYPE_ECHO_REQUEST,
+    ICMPV6_TYPE_ECHO_REPLY,
+]
 
 # Link Metrics
 LINK_METRICS_STATUS_SUCCESS = 0
diff --git a/tests/scripts/thread-cert/pktverify/packet_filter.py b/tests/scripts/thread-cert/pktverify/packet_filter.py
index 7fc21b4..3073304 100644
--- a/tests/scripts/thread-cert/pktverify/packet_filter.py
+++ b/tests/scripts/thread-cert/pktverify/packet_filter.py
@@ -34,6 +34,7 @@
 from pktverify import consts, errors
 from pktverify.addrs import EthAddr, ExtAddr, Ipv6Addr
 from pktverify.bytes import Bytes
+from pktverify.consts import THREAD_ALLOWED_ICMPV6_TYPES
 from pktverify.packet import Packet
 from pktverify.utils import make_filter_func
 
@@ -618,6 +619,10 @@
     def filter_icmpv6_nd_ra(self):
         return self.filter(lambda p: p.icmpv6.is_router_advertisement)
 
+    def filter_thread_unallowed_icmpv6(self):
+        return self.filter('wpan and icmpv6 and icmpv6.type not in {THREAD_ALLOWED_ICMPV6_TYPES}',
+                           THREAD_ALLOWED_ICMPV6_TYPES=THREAD_ALLOWED_ICMPV6_TYPES)
+
     def filter_has_bbr_dataset(self):
         return self.filter("""
                 thread_nwd.tlv.server.has('16')
diff --git a/tests/scripts/thread-cert/test_mac_scan.py b/tests/scripts/thread-cert/test_mac_scan.py
index e3214b8..ae493a5 100755
--- a/tests/scripts/thread-cert/test_mac_scan.py
+++ b/tests/scripts/thread-cert/test_mac_scan.py
@@ -70,8 +70,6 @@
         self.assertEqual(len(results), 1)
         network = results[0]
         self.assertEqual(network['extaddr'], self.nodes[ROUTER].get_addr64())
-        self.assertEqual(network['extpanid'], self.nodes[ROUTER].get_extpanid())
-        self.assertEqual(network['networkname'], self.nodes[ROUTER].get_network_name())
         self.assertEqual(network['channel'], CHANNEL)
 
 
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 2b18264..9fc4c09 100755
--- a/tests/scripts/thread-cert/test_srp_auto_start_mode.py
+++ b/tests/scripts/thread-cert/test_srp_auto_start_mode.py
@@ -31,23 +31,22 @@
 import unittest
 
 import command
+import config
 import thread_cert
 
 # Test description:
 #   This test verifies SRP client auto-start functionality that SRP client can
-#   correctly discover and connect to SRP server.
+#   correctly discovers and connects to SRP server.
 #
 # Topology:
 #
-#   CLIENT (leader) -- SERVER1 (router)
-#      |
-#      |
-#   SERVER2 (router)
+#   Four routers, one acting as SRP client, others as SRP server.
 #
 
 CLIENT = 1
 SERVER1 = 2
 SERVER2 = 3
+SERVER3 = 4
 
 
 class SrpAutoStartMode(thread_cert.TestCase):
@@ -57,17 +56,18 @@
     TOPOLOGY = {
         CLIENT: {
             'name': 'SRP_CLIENT',
-            'networkkey': '00112233445566778899aabbccddeeff',
             'mode': 'rdn',
         },
         SERVER1: {
             'name': 'SRP_SERVER1',
-            'networkkey': '00112233445566778899aabbccddeeff',
             'mode': 'rdn',
         },
         SERVER2: {
             'name': 'SRP_SERVER2',
-            'networkkey': '00112233445566778899aabbccddeeff',
+            'mode': 'rdn',
+        },
+        SERVER3: {
+            'name': 'SRP_SERVER3',
             'mode': 'rdn',
         },
     }
@@ -76,27 +76,38 @@
         client = self.nodes[CLIENT]
         server1 = self.nodes[SERVER1]
         server2 = self.nodes[SERVER2]
+        server3 = self.nodes[SERVER3]
 
-        #
-        # 0. Start the server & client devices.
-        #
+        #-------------------------------------------------------------------
+        # Form the network.
 
         client.srp_server_set_enabled(False)
         client.start()
         self.simulator.go(5)
         self.assertEqual(client.get_state(), 'leader')
 
-        server1.srp_server_set_enabled(True)
-        server2.srp_server_set_enabled(False)
         server1.start()
         server2.start()
+        server3.start()
         self.simulator.go(5)
         self.assertEqual(server1.get_state(), 'router')
         self.assertEqual(server2.get_state(), 'router')
+        self.assertEqual(server3.get_state(), 'router')
 
-        #
-        # 1. Enable auto start mode on client and check that server1 is used.
-        #
+        server1_mleid = server1.get_mleid()
+        server2_mleid = server2.get_mleid()
+        server3_mleid = server3.get_mleid()
+        anycast_port = 53
+
+        #-------------------------------------------------------------------
+        # Enable server1 with unicast address mode
+
+        server1.srp_server_set_addr_mode('unicast')
+        server1.srp_server_set_enabled(True)
+        self.simulator.go(5)
+
+        #-------------------------------------------------------------------
+        # Enable 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()
@@ -104,42 +115,174 @@
         self.simulator.go(2)
 
         self.assertEqual(client.srp_client_get_state(), 'Enabled')
-        self.assertTrue(server1.has_ipaddr(client.srp_client_get_server_address()))
+        self.assertEqual(client.srp_client_get_server_address(), server1_mleid)
 
-        #
-        # 2. Disable server1 and check client is stopped/disabled.
-        #
+        #-------------------------------------------------------------------
+        # Disable server1 and check client is stopped/disabled.
 
         server1.srp_server_set_enabled(False)
         self.simulator.go(5)
         self.assertEqual(client.srp_client_get_state(), 'Disabled')
 
-        #
-        # 3. Enable server2 and check client starts again.
-        #
+        #-------------------------------------------------------------------
+        # Enable server2 with unicast address mode and check client starts
+        # again.
 
+        server1.srp_server_set_addr_mode('unicast')
         server2.srp_server_set_enabled(True)
         self.simulator.go(5)
         self.assertEqual(client.srp_client_get_state(), 'Enabled')
-        server2_address = client.srp_client_get_server_address()
+        self.assertEqual(client.srp_client_get_server_address(), server2_mleid)
 
-        #
-        # 4. Enable both servers and check client stays with server2.
-        #
+        #-------------------------------------------------------------------
+        # Enable server1 and check that client stays with server2
 
         server1.srp_server_set_enabled(True)
         self.simulator.go(5)
         self.assertEqual(client.srp_client_get_state(), 'Enabled')
-        self.assertEqual(client.srp_client_get_server_address(), server2_address)
+        self.assertEqual(client.srp_client_get_server_address(), server2_mleid)
 
-        #
-        # 5. Disable server2 and check client switches to server1.
-        #
+        #-------------------------------------------------------------------
+        # Disable server2 and check client switches to server1.
 
         server2.srp_server_set_enabled(False)
         self.simulator.go(5)
         self.assertEqual(client.srp_client_get_state(), 'Enabled')
-        self.assertNotEqual(client.srp_client_get_server_address(), server2_address)
+        self.assertEqual(client.srp_client_get_server_address(), server1_mleid)
+
+        #-------------------------------------------------------------------
+        # Enable server2 with anycast mode seq-num 1, and check that client
+        # switched to it.
+
+        server2.srp_server_set_addr_mode('anycast')
+        server2.srp_server_set_anycast_seq_num(1)
+        server2.srp_server_set_enabled(True)
+        self.simulator.go(5)
+        server2_alocs = server2.get_ip6_address(config.ADDRESS_TYPE.ALOC)
+        self.assertEqual(server2.srp_server_get_anycast_seq_num(), 1)
+        self.assertEqual(client.srp_client_get_state(), 'Enabled')
+        self.assertIn(client.srp_client_get_server_address(), server2_alocs)
+        self.assertEqual(client.srp_client_get_server_port(), anycast_port)
+
+        #-------------------------------------------------------------------
+        # Enable server3 with anycast mode seq-num 2, and check that client
+        # switched to it since seq number is higher.
+
+        server3.srp_server_set_addr_mode('anycast')
+        server3.srp_server_set_anycast_seq_num(2)
+        server3.srp_server_set_enabled(True)
+        self.simulator.go(5)
+        server3_alocs = server3.get_ip6_address(config.ADDRESS_TYPE.ALOC)
+        self.assertEqual(server3.srp_server_get_anycast_seq_num(), 2)
+        self.assertEqual(client.srp_client_get_state(), 'Enabled')
+        self.assertIn(client.srp_client_get_server_address(), server3_alocs)
+        self.assertEqual(client.srp_client_get_server_port(), anycast_port)
+
+        #-------------------------------------------------------------------
+        # Disable server3 and check that client goes back to server2.
+
+        server3.srp_server_set_enabled(False)
+        self.simulator.go(5)
+        self.assertEqual(client.srp_client_get_state(), 'Enabled')
+        self.assertIn(client.srp_client_get_server_address(), server2_alocs)
+        self.assertEqual(client.srp_client_get_server_port(), anycast_port)
+
+        #-------------------------------------------------------------------
+        # Enable server3 with anycast mode seq-num 0 (which is smaller than
+        # server2 seq-num 1) and check that client stays with server2.
+
+        server3.srp_server_set_anycast_seq_num(0)
+        server3.srp_server_set_enabled(True)
+        self.simulator.go(5)
+        self.assertEqual(server3.srp_server_get_anycast_seq_num(), 0)
+        self.assertEqual(client.srp_client_get_state(), 'Enabled')
+        self.assertIn(client.srp_client_get_server_address(), server2_alocs)
+        self.assertEqual(client.srp_client_get_server_port(), anycast_port)
+
+        #-------------------------------------------------------------------
+        # Disable server2 and check that client goes back to server3.
+
+        server2.srp_server_set_enabled(False)
+        self.simulator.go(5)
+        self.assertEqual(client.srp_client_get_state(), 'Enabled')
+        server3_alocs = server3.get_ip6_address(config.ADDRESS_TYPE.ALOC)
+        self.assertIn(client.srp_client_get_server_address(), server3_alocs)
+        self.assertEqual(client.srp_client_get_server_port(), anycast_port)
+
+        #-------------------------------------------------------------------
+        # Disable server3 and check that client goes back to server1 with
+        # unicast address.
+
+        server3.srp_server_set_enabled(False)
+        self.simulator.go(5)
+        self.assertEqual(client.srp_client_get_state(), 'Enabled')
+        self.assertEqual(client.srp_client_get_server_address(), server1_mleid)
+
+        #-------------------------------------------------------------------
+        # Enable server2 with anycast mode seq-num 5, and check that client
+        # switched to it.
+
+        server2.srp_server_set_addr_mode('anycast')
+        server2.srp_server_set_anycast_seq_num(5)
+        server2.srp_server_set_enabled(True)
+        self.simulator.go(5)
+        server2_alocs = server2.get_ip6_address(config.ADDRESS_TYPE.ALOC)
+        self.assertEqual(server2.srp_server_get_anycast_seq_num(), 5)
+        self.assertEqual(client.srp_client_get_state(), 'Enabled')
+        self.assertIn(client.srp_client_get_server_address(), server2_alocs)
+        self.assertEqual(client.srp_client_get_server_port(), anycast_port)
+
+        #-------------------------------------------------------------------
+        # Publish an entry on server3 with specific unicast address
+        # This entry should be now preferred over anycast of server2.
+
+        unicast_addr3 = 'fd00:0:0:0:0:3333:beef:cafe'
+        unicast_port3 = 1234
+        server3.netdata_publish_dnssrp_unicast(unicast_addr3, unicast_port3)
+        self.simulator.go(65)
+        self.assertEqual(client.srp_client_get_state(), 'Enabled')
+        self.assertEqual(client.srp_client_get_server_address(), unicast_addr3)
+        self.assertEqual(client.srp_client_get_server_port(), unicast_port3)
+
+        #-------------------------------------------------------------------
+        # Publish an entry on server1 with specific unicast address
+        # Client should still stay with server3 which it originally selected.
+
+        unicast_addr1 = 'fd00:0:0:0:0:2222:beef:cafe'
+        unicast_port1 = 10203
+        server1.srp_server_set_enabled(False)
+        server1.netdata_publish_dnssrp_unicast(unicast_addr1, unicast_port1)
+        self.simulator.go(65)
+        self.assertEqual(client.srp_client_get_state(), 'Enabled')
+        self.assertEqual(client.srp_client_get_server_address(), unicast_addr3)
+        self.assertEqual(client.srp_client_get_server_port(), unicast_port3)
+
+        #-------------------------------------------------------------------
+        # Unpublish the entry on server3. Now client should switch to entry
+        # from server1.
+
+        server3.netdata_unpublish_dnssrp()
+        self.simulator.go(65)
+        self.assertEqual(client.srp_client_get_state(), 'Enabled')
+        self.assertEqual(client.srp_client_get_server_address(), unicast_addr1)
+        self.assertEqual(client.srp_client_get_server_port(), unicast_port1)
+
+        #-------------------------------------------------------------------
+        # Unpublish the entry on server1 and check client goes back to anycast
+        # entry from server2.
+
+        server1.netdata_unpublish_dnssrp()
+        self.simulator.go(65)
+        self.assertEqual(client.srp_client_get_state(), 'Enabled')
+        self.assertIn(client.srp_client_get_server_address(), server2_alocs)
+        self.assertEqual(client.srp_client_get_server_port(), anycast_port)
+
+        #-------------------------------------------------------------------
+        # Finally disable server2, and check that client is disabled.
+
+        server2.srp_server_set_enabled(False)
+        self.simulator.go(5)
+        self.assertEqual(client.srp_client_get_state(), 'Disabled')
 
 
 if __name__ == '__main__':
diff --git a/tests/scripts/thread-cert/test_srp_name_conflicts.py b/tests/scripts/thread-cert/test_srp_name_conflicts.py
index eb708fc..b7fc124 100755
--- a/tests/scripts/thread-cert/test_srp_name_conflicts.py
+++ b/tests/scripts/thread-cert/test_srp_name_conflicts.py
@@ -177,7 +177,35 @@
         client_2.srp_client_stop()
 
         #
-        # 4. Register with different host & service instance name, it should succeed.
+        # 4. Register the same service instance label with a different service name
+        # from the second client and it should pass.
+        #
+
+        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)
+
+        # It is expected that the registration will be accepted.
+        client_2_service = client_2.srp_client_get_services()[0]
+        self.assertEqual(client_2_service['state'], 'Registered')
+        self.assertEqual(client_2.srp_client_get_host_state(), 'Registered')
+
+        self.assertEqual(len(server.srp_server_get_services()), 2)
+        self.assertEqual(len(server.srp_server_get_hosts()), 2)
+        self.assertEqual(server.srp_server_get_host('my-host-2')['deleted'], 'false')
+        self.assertEqual(server.srp_server_get_service('my-service-1', '_ipps2._tcp')['deleted'], 'false')
+
+        # Remove the host and all services registered on the SRP server.
+        client_2.srp_client_remove_host(remove_key=True)
+        self.simulator.go(2)
+
+        client_2.srp_client_clear_host()
+        client_2.srp_client_stop()
+
+        #
+        # 5. Register with different host & service instance name, it should succeed.
         #
 
         client_2.srp_client_set_host_name('my-host-2')
@@ -204,7 +232,7 @@
         client_2.srp_client_stop()
 
         #
-        # 5. Register with the same service instance name before its KEY LEASE expires,
+        # 6. Register with the same service instance full name before its KEY LEASE expires,
         #    it is expected to fail.
         #
 
@@ -234,7 +262,7 @@
         client_2.srp_client_stop()
 
         #
-        # 6. The service instance name can be re-used by another client when
+        # 7. The service instance name can be re-used by another client when
         #    the service has been permanently removed (the KEY resource is
         #    removed) from the host.
         #
diff --git a/tests/scripts/thread-cert/thread_cert.py b/tests/scripts/thread-cert/thread_cert.py
index 341fa98..0dacebd 100755
--- a/tests/scripts/thread-cert/thread_cert.py
+++ b/tests/scripts/thread-cert/thread_cert.py
@@ -327,6 +327,7 @@
     def _verify_packets(self, test_info_path: str):
         pv = PacketVerifier(test_info_path, self.CASE_WIRESHARK_PREFS)
         pv.add_common_vars()
+        pv.pkts.filter_thread_unallowed_icmpv6().must_not_next()
         self.verify(pv)
         print("Packet verification passed: %s" % test_info_path, file=sys.stderr)
 
diff --git a/tests/toranj/README.md b/tests/toranj/README.md
index 265cc7f..d7024f8 100644
--- a/tests/toranj/README.md
+++ b/tests/toranj/README.md
@@ -1,372 +1,25 @@
 # `toranj` test framework
 
-`toranj` is a test framework for OpenThread and `wpantund`.
+`toranj` is a test framework for OpenThread.
 
-- It enables testing of combined behavior of OpenThread (in NCP mode), spinel interface, and `wpantund` driver on linux.
+It provides two modes:
+
+- `toranj-cli` which enables testing of OpenThread using its CLI interface.
+- `toranj-ncp` which enables testing of the combined behavior of OpenThread (in NCP mode), spinel interface, and `wpantund` driver on linux.
+
+`toranj` features:
+
+- It is developed in Python.
 - It can be used to simulate multiple nodes forming complex network topologies.
 - It allows testing of network interactions between many nodes (IPv6 traffic exchanges).
+- `toranj` in NCP mode runs `wpantund` natively with OpenThread in NCP mode on simulation platform (real-time).
+- `toranj` in CLI mode runs `ot-cli-ftd` on simulation platform (real-time).
+- `toranj` tests run as part of GitHub Actions pull request validation in OpenThread and `wpantund` GitHub projects.
 
-`toranj` is developed in Python. `toranj` runs wpantund natively with OpenThread in NCP mode on POSIX simulation platform. `toranj` tests will run as part of GitHub Actions pull request validation in OpenThread and/or `wpantund` GitHub projects.
+## `toranj` modes
 
-## Setup
-
-`toranj` requires `wpantund` to be installed.
-
-- Please follow [`wpantund` installation guide](https://github.com/openthread/wpantund/blob/master/INSTALL.md#wpantund-installation-guide). Note that `toranj` expects `wpantund` installed from latest master branch.
-- Alternative way to install `wpantund` is to use the same commands from git workflow [Simulation](https://github.com/openthread/openthread/blob/4b55284bd20f99a88e8e2c617ba358a0a5547f5d/.github/workflows/simulation.yml#L336-L341) for build target `toranj-test-framework`.
-
-To run all tests, `start` script can be used. This script will build OpenThread with proper configuration options and starts running all test.
-
-```bash
-    cd tests/toranj/    # from OpenThread repo root
-    ./start.sh
-```
-
-Each test-case has its own script following naming model `test-nnn-name.py` (e.g., `test-001-get-set.py`).
-
-To run a specific test
-
-```bash
-    sudo python test-001-get-set.py
-```
-
-## `toranj` Components
-
-`wpan` python module defines the `toranj` test components.
-
-### `wpan.Node()` Class
-
-`wpan.Node()` class creates a Thread node instance. It creates a sub-process to run `wpantund` and OpenThread, and provides methods to control the node.
-
-```python
->>> import wpan
->>> node1 = wpan.Node()
->>> node1
-Node (index=1, interface_name=wpan1)
->>> node2 = wpan.Node()
->>> node2
-Node (index=2, interface_name=wpan2)
-```
-
-Note: You may need to run as `sudo` to allow `wpantund` to create tunnel interface (i.e., use `sudo python`).
-
-### `wpan.Node` methods providing `wpanctl` commands
-
-`wpan.Node()` provides methods matching all `wpanctl` commands.
-
-- Get the value of a `wpantund` property, set the value, or add/remove value to/from a list based property:
-
-```python
-    node.get(prop_name)
-    node.set(prop_name, value, binary_data=False)
-    node.add(prop_name, value, binary_data=False)
-    node.remove(prop_name, value, binary_data=False)
-```
-
-Example:
-
-```python
->>> node.get(wpan.WPAN_NAME)
-'"test-network"'
->>> node.set(wpan.WPAN_NAME, 'my-network')
->>> node.get(wpan.WPAN_NAME)
-'"my-network"'
->>> node.set(wpan.WPAN_KEY, '65F2C35C7B543BAC1F3E26BB9F866C1D', binary_data=True)
->>> node.get(wpan.WPAN_KEY)
-'[65F2C35C7B543BAC1F3E26BB9F866C1D]'
-```
-
-- Common network operations:
-
-```python
-    node.reset()            # Reset the NCP
-    node.status()           # Get current status
-    node.leave()            # Leave the current network, clear all persistent data
-
-    # Form a network in given channel (if none given use a random one)
-    node.form(name, channel=None)
-
-    # Join a network with given info.
-    # node_type can be JOIN_TYPE_ROUTER, JOIN_TYPE_END_DEVICE, JOIN_TYPE_SLEEPY_END_DEVICE
-    node.join(name, channel=None, node_type=None, panid=None, xpanid=None)
-```
-
-Example:
-
-```python
->>> result = node.status()
->>> print result
-wpan1 => [
-    "NCP:State" => "offline"
-    "Daemon:Enabled" => true
-    "NCP:Version" => "OPENTHREAD/20170716-00460-ga438cef0c-dirty; NONE; Feb 12 2018 11:47:01"
-    "Daemon:Version" => "0.08.00d (0.07.01-191-g63265f7; Feb  2 2018 18:05:47)"
-    "Config:NCP:DriverName" => "spinel"
-    "NCP:HardwareAddress" => [18B4300000000001]
-]
->>>
->>> node.form("test-network", channel=12)
-'Forming WPAN "test-network" as node type "router"\nSuccessfully formed!'
->>>
->>> print node.status()
-wpan1 => [
-    "NCP:State" => "associated"
-    "Daemon:Enabled" => true
-    "NCP:Version" => "OPENTHREAD/20170716-00460-ga438cef0c-dirty; NONE; Feb 12 2018 11:47:01"
-    "Daemon:Version" => "0.08.00d (0.07.01-191-g63265f7; Feb  2 2018 18:05:47)"
-    "Config:NCP:DriverName" => "spinel"
-    "NCP:HardwareAddress" => [18B4300000000001]
-    "NCP:Channel" => 12
-    "Network:NodeType" => "leader"
-    "Network:Name" => "test-network"
-    "Network:XPANID" => 0xA438CF5973FD86B2
-    "Network:PANID" => 0x9D81
-    "IPv6:MeshLocalAddress" => "fda4:38cf:5973:0:b899:3436:15c6:941d"
-    "IPv6:MeshLocalPrefix" => "fda4:38cf:5973::/64"
-    "com.nestlabs.internal:Network:AllowingJoin" => false
-]
-```
-
-- Scan:
-
-```python
-    node.active_scan(channel=None)
-    node.energy_scan(channel=None)
-    node.discover_scan(channel=None, joiner_only=False, enable_filtering=False, panid_filter=None)
-    node.permit_join(duration_sec=None, port=None, udp=True, tcp=True)
-```
-
-- On-mesh prefixes and off-mesh routes:
-
-```python
-    node.config_gateway(prefix, default_route=False)
-    node.add_route(route_prefix, prefix_len_in_bytes=None, priority=None)
-    node.remove_route(route_prefix, prefix_len_in_bytes=None, priority=None)
-```
-
-A direct `wpanctl` command can be issued using `node.wpanctl(command)` with a given `command` string.
-
-`wpan` module provides variables for different `wpantund` properties. Some commonly used are:
-
-- Network/NCP properties: WPAN_STATE, WPAN_NAME, WPAN_PANID, WPAN_XPANID, WPAN_KEY, WPAN_CHANNEL, WPAN_HW_ADDRESS, WPAN_EXT_ADDRESS, WPAN_POLL_INTERVAL, WPAN_NODE_TYPE, WPAN_ROLE, WPAN_PARTITION_ID
-
-- IPv6 Addresses: WPAN_IP6_LINK_LOCAL_ADDRESS, WPAN_IP6_MESH_LOCAL_ADDRESS, WPAN_IP6_MESH_LOCAL_PREFIX, WPAN_IP6_ALL_ADDRESSES, WPAN_IP6_MULTICAST_ADDRESSES
-
-- Thread Properties: WPAN_THREAD_RLOC16, WPAN_THREAD_ROUTER_ID, WPAN_THREAD_LEADER_ADDRESS, WPAN_THREAD_LEADER_ROUTER_ID, WPAN_THREAD_LEADER_WEIGHT, WPAN_THREAD_LEADER_NETWORK_DATA,
-
-        WPAN_THREAD_CHILD_TABLE, WPAN_THREAD_CHILD_TABLE_ADDRESSES, WPAN_THREAD_NEIGHBOR_TABLE,
-        WPAN_THREAD_ROUTER_TABLE
-
-Method `join_node()` can be used by a node to join another node:
-
-```python
-    # `node1` joining `node2`'s network as a router
-    node1.join_node(node2, node_type=JOIN_TYPE_ROUTER)
-```
-
-Method `allowlist_node()` can be used to add a given node to the allowlist of the device and enables allowlisting:
-
-```python
-    # `node2` is added to the allowlist of `node1` and allowlisting is enabled on `node1`
-    node1.allowlist_node(node2)
-```
-
-#### Example (simple 3-node topology)
-
-Script below shows how to create a 3-node network topology with `node1` and `node2` being routers, and `node3` an end-device connected to `node2`:
-
-```python
->>> import wpan
->>> node1 = wpan.Node()
->>> node2 = wpan.Node()
->>> node3 = wpan.Node()
-
->>> wpan.Node.init_all_nodes()
-
->>> node1.form("test-PAN")
-'Forming WPAN "test-PAN" as node type "router"\nSuccessfully formed!'
-
->>> node1.allowlist_node(node2)
->>> node2.allowlist_node(node1)
-
->>> node2.join_node(node1, wpan.JOIN_TYPE_ROUTER)
-'Joining "test-PAN" C474513CB487778D as node type "router"\nSuccessfully Joined!'
-
->>> node3.allowlist_node(node2)
->>> node2.allowlist_node(node3)
-
->>> node3.join_node(node2, wpan.JOIN_TYPE_END_DEVICE)
-'Joining "test-PAN" C474513CB487778D as node type "end-device"\nSuccessfully Joined!'
-
->>> print node2.get(wpan.WPAN_THREAD_NEIGHBOR_TABLE)
-[
-    "EAC1672C3EAB30A4, RLOC16:9401, LQIn:3, AveRssi:-20, LastRssi:-20, Age:30, LinkFC:6, MleFC:0, IsChild:yes, RxOnIdle:yes, FTD:yes, SecDataReq:yes, FullNetData:yes"
-    "A2042C8762576FD5, RLOC16:dc00, LQIn:3, AveRssi:-20, LastRssi:-20, Age:5, LinkFC:21, MleFC:18, IsChild:no, RxOnIdle:yes, FTD:yes, SecDataReq:no, FullNetData:yes"
-]
->>> print node1.get(wpan.WPAN_THREAD_NEIGHBOR_TABLE)
-[
-    "960947C53415DAA1, RLOC16:9400, LQIn:3, AveRssi:-20, LastRssi:-20, Age:18, LinkFC:15, MleFC:11, IsChild:no, RxOnIdle:yes, FTD:yes, SecDataReq:no, FullNetData:yes"
-]
-
-```
-
-### IPv6 Message Exchange
-
-`toranj` allows a test-case to define traffic patterns (IPv6 message exchange) between different nodes. Message exchanges (tx/rx) are prepared and then an async rx/tx operation starts. The success and failure of tx/rx operations can then be verified by the test case.
-
-`wpan.Node` method `prepare_tx()` prepares a UDP6 transmission from a node.
-
-```python
-    node1.prepare_tx(src, dst, data, count)
-```
-
-- `src` and `dst` can be
-
-  - either a string containing an IPv6 address
-  - or a tuple (ipv6 address as string, port). if no port is given, a random port number is used.
-
-- `data` can be
-
-  - either a string containing the message to be sent,
-  - or an int indicating size of the message (a random message with the given length will be generated).
-
-- `count` gives number of times the message will be sent (default is 1).
-
-`prepare_tx` returns a `wpan.AsyncSender` object. The sender object can be used to check success/failure of tx operation.
-
-`wpan.Node` method `prepare_rx()` prepares a node to listen for UDP messages from a sender.
-
-```python
-    node2.prepare_rx(sender)
-```
-
-- `sender` should be an `wpan.AsyncSender` object returned from previous `prepare_tx`.
-- `prepare_rx()` returns a `wpan.AsyncReceiver` object to help test to check success/failure of rx operation.
-
-After all exchanges are prepared, static method `perform_async_tx_rx()` should be used to start all previously prepared rx and tx operations.
-
-```python
-    wpan.Node.perform_async_tx_rx(timeout)
-```
-
-- `timeout` gives amount of time (in seconds) to wait for all operations to finish. (default is 20 seconds)
-
-After `perform_async_tx_rx()` is done, the `AsyncSender` and `AsyncReceiver` objects can check if operations were successful (using property `was_successful`)
-
-#### Example
-
-Sending 10 messages containing `"Hello there!"` from `node1` to `node2` using their mesh-local addresses:
-
-```python
-# `node1` and `node2` are already joined and are part of the same Thread network.
-
-# Get the mesh local addresses
->>> mladdr1 = node1.get(wpan.WPAN_IP6_MESH_LOCAL_ADDRESS)[1:-1]  # remove `"` from start/end of string
->>> mladdr2 = node2.get(wpan.WPAN_IP6_MESH_LOCAL_ADDRESS)[1:-1]
-
->>> print (mladdr1, mladdr2)
-('fda4:38cf:5973:0:b899:3436:15c6:941d', 'fda4:38cf:5973:0:5836:fa55:7394:6d4b')
-
-# prepare a `sender` and corresponding `recver`
->>> sender = node1.prepare_tx((mladdr1, 1234), (mladdr2, 2345), "Hello there!", 10)
->>> recver = node2.prepare_rx(sender)
-
-# perform async message transfer
->>> wpan.Node.perform_async_tx_rx()
-
-# check status of `sender` and `recver`
->>> sender.was_successful
-True
->>> recver.was_successful
-True
-
-# `sender` or `recver` can provide info about the exchange
-
->>> sender.src_addr
-'fda4:38cf:5973:0:b899:3436:15c6:941d'
->>> sender.src_port
-1234
->>> sender.dst_addr
-'fda4:38cf:5973:0:5836:fa55:7394:6d4b'
->>> sender.dst_port
-2345
->>> sender.msg
-'Hello there!'
->>> sender.count
-10
-
-# get all received msg by `recver` as list of tuples `(msg, (src_address, src_port))`
->>> recver.all_rx_msg
-[('Hello there!', ('fda4:38cf:5973:0:b899:3436:15c6:941d', 1234)), ...  ]
-```
-
-### Logs and Verbose mode
-
-Every `wpan.Node()` instance will save its corresponding `wpantund` logs. By default the logs are saved in a file `wpantun-log<node_index>.log`. By setting `wpan.Node__TUND_LOG_TO_FILE` to `False` the logs are written to `stdout` as the test-cases are executed.
-
-When `start.sh` script is used to run all test-cases, if any test fails, to help with debugging of the issue, the last 30 lines of `wpantund` logs of every node involved in the test-case is dumped to `stdout`.
-
-A `wpan.Node()` instance can also provide additional logs and info as the test-cases are run (verbose mode). It can be enabled for a node instance when it is created:
-
-```python
-    node = wpan.Node(verbose=True)     # `node` instance will provide extra logs.
-```
-
-Alternatively, `wpan.Node._VERBOSE` settings can be changed to enable verbose logging for all nodes. The default value of `wpan.Node._VERBOSE` is determined from environment variable `TORANJ_VERBOSE` (verbose mode is enabled when env variable is set to any of `1`, `True`, `Yes`, `Y`, `On` (case-insensitive)), otherwise it is disabled. When `TORANJ_VERBOSE` is enabled, the OpenThread logging is also enabled (and collected in `wpantund-log<node_index>.log`files) on all nodes.
-
-Here is example of small test script and its corresponding log output with `verbose` mode enabled:
-
-```python
-node1 = wpan.Node(verbose=True)
-node2 = wpan.Node(verbose=True)
-
-wpan.Node.init_all_nodes()
-
-node1.form("toranj-net")
-node2.active_scan()
-
-node2.join_node(node1)
-verify(node2.get(wpan.WPAN_STATE) == wpan.STATE_ASSOCIATED)
-
-lladdr1 = node1.get(wpan.WPAN_IP6_LINK_LOCAL_ADDRESS)[1:-1]
-lladdr2 = node2.get(wpan.WPAN_IP6_LINK_LOCAL_ADDRESS)[1:-1]
-
-sender = node1.prepare_tx(lladdr1, lladdr2, 20)
-recver = node2.prepare_rx(sender)
-
-wpan.Node.perform_async_tx_rx()
-
-```
-
-```
-$ Node1.__init__() cmd: /usr/local/sbin/wpantund -o Config:NCP:SocketPath "system:../../examples/apps/ncp/ot-ncp-ftd 1" -o Config:TUN:InterfaceName wpan1 -o Config:NCP:DriverName spinel -o Daemon:SyslogMask "all -debug"
-$ Node2.__init__() cmd: /usr/local/sbin/wpantund -o Config:NCP:SocketPath "system:../../examples/apps/ncp/ot-ncp-ftd 2" -o Config:TUN:InterfaceName wpan2 -o Config:NCP:DriverName spinel -o Daemon:SyslogMask "all -debug"
-$ Node1.wpanctl('leave') -> 'Leaving current WPAN. . .'
-$ Node2.wpanctl('leave') -> 'Leaving current WPAN. . .'
-$ Node1.wpanctl('form "toranj-net"'):
-     Forming WPAN "toranj-net" as node type "router"
-     Successfully formed!
-$ Node2.wpanctl('scan'):
-        | Joinable | NetworkName        | PAN ID | Ch | XPanID           | HWAddr           | RSSI
-     ---+----------+--------------------+--------+----+------------------+------------------+------
-      1 |       NO | "toranj-net"       | 0x9DEB | 16 | 8CC6CFC810F23E1B | BEECDAF3439DC931 |  -20
-$ Node1.wpanctl('get -v NCP:State') -> '"associated"'
-$ Node1.wpanctl('get -v Network:Name') -> '"toranj-net"'
-$ Node1.wpanctl('get -v Network:PANID') -> '0x9DEB'
-$ Node1.wpanctl('get -v Network:XPANID') -> '0x8CC6CFC810F23E1B'
-$ Node1.wpanctl('get -v Network:Key') -> '[BA2733A5D81EAB8FFB3C9A7383CB6045]'
-$ Node1.wpanctl('get -v NCP:Channel') -> '16'
-$ Node2.wpanctl('set Network:Key -d -v BA2733A5D81EAB8FFB3C9A7383CB6045') -> ''
-$ Node2.wpanctl('join "toranj-net" -c 16 -T r -p 0x9DEB -x 0x8CC6CFC810F23E1B'):
-     Joining "toranj-net" 8CC6CFC810F23E1B as node type "router"
-     Successfully Joined!
-$ Node2.wpanctl('get -v NCP:State') -> '"associated"'
-$ Node1.wpanctl('get -v IPv6:LinkLocalAddress') -> '"fe80::bcec:daf3:439d:c931"'
-$ Node2.wpanctl('get -v IPv6:LinkLocalAddress') -> '"fe80::ec08:f348:646f:d37d"'
-- Node1 sent 20 bytes (":YeQuNKjuOtd%H#ipM7P") to [fe80::ec08:f348:646f:d37d]:404 from [fe80::bcec:daf3:439d:c931]:12557
-- Node2 received 20 bytes (":YeQuNKjuOtd%H#ipM7P") on port 404 from [fe80::bcec:daf3:439d:c931]:12557
-
-```
+- [`toranj-cli` guide](README_CLI.md)
+- [`toranj-ncp` guide](README_NCP.md)
 
 ---
 
diff --git a/tests/toranj/README_CLI.md b/tests/toranj/README_CLI.md
new file mode 100644
index 0000000..024a321
--- /dev/null
+++ b/tests/toranj/README_CLI.md
@@ -0,0 +1,188 @@
+# `toranj-cli`
+
+`toranj-cli` is a test framework for OpenThread using its CLI interface.
+
+`toranj` features:
+
+- It is developed in Python.
+- It can be used to simulate multiple nodes forming complex network topologies.
+- It allows testing of network interactions between many nodes.
+- `toranj` in CLI mode runs `ot-cli-ftd` on simulation platform (real-time).
+
+## Setup
+
+To build OpenThread with `toranj` configuration, the `test/toranj/build.sh` script can be used:
+
+```bash
+$ ./tests/toranj/build.sh cmake
+====================================================================================================
+Building OpenThread (NCP/CLI for FTD/MTD/RCP mode) with simulation platform using cmake
+====================================================================================================
+-- OpenThread Source Directory: /Users/abtink/GitHub/openthread
+-- OpenThread CMake build type: Debug
+-- Package Name: OPENTHREAD
+...
+
+```
+
+Or to build using autoconf/make we can use:
+
+```bash
+$ ./tests/toranj/build.sh cli
+====================================================================================================
+Building OpenThread (NCP/CLI for FTD/MTD/RCP mode) with simulation platform using cmake
+====================================================================================================
+-- OpenThread Source Directory: /Users/abtink/GitHub/openthread
+-- OpenThread CMake build type: Debug
+-- Package Name: OPENTHREAD
+...
+
+```
+
+The `toranj-cli` tests are included in `tests/toranj/cli` folder. Each test-case has its own script following naming model `test-nnn-name.py` (e.g., `test-001-get-set.py`).
+
+To run a specific test:
+
+```bash
+$ cd tests/toranj/cli
+$ python3 test-001-get-set.py
+```
+
+To run all CLI tests, `start` script can be used. This script will build OpenThread with proper configuration options and starts running all tests.
+
+```bash
+# From OpenThread repo root folder
+$ top_builddir=($pwd) TORANJ_CLI=1 ./tests/toranj/start.sh
+```
+
+## `toranj-cli` Components
+
+`cli` python module defines the `toranj-cli` test components.
+
+### `cli.Node()` Class
+
+`cli.Node()` class creates a Thread node instance. It creates a sub-process to run `ot-cli-ftd` and provides methods to control the node and issue CLI commands.
+
+```python
+>>> import cli
+>>> node1 = cli.Node()
+>>> node1
+Node (index=1)
+>>> node2 = cli.Node()
+>>> node2
+Node (index=2)
+```
+
+Note: You may need to run as `sudo` to allow log file to be written (i.e., use `sudo python` or `sudo python3`).
+
+### `cli.Node` methods
+
+`cli.Node()` provides methods matching different CLI commands, in addition to some helper methods for common operations.
+
+Example:
+
+```python
+>>> node.get_state()
+'disabled'
+>>> node.get_channel()
+'11'
+>>> node.set_channel(12)
+>>> node.get_channel()
+'12'
+>>> node.set_network_key('11223344556677889900aabbccddeeff')
+>>> node.get_network_key()
+'11223344556677889900aabbccddeeff'
+```
+
+Common network operations:
+
+```python
+    # Form a Thread network with all the given parameters.
+    node.form(network_name=None, network_key=None, channel=None, panid=0x1234, xpanid=None):
+
+    # Try to join an existing network as specified by `another_node`.
+    # `type` can be `JOIN_TYPE_ROUTER`, `JOIN_TYPE_END_DEVICE, or `JOIN_TYPE_SLEEPY_END_DEVICE`
+    node.join(another_node, type=JOIN_TYPE_ROUTER):
+```
+
+A direct CLI command can be issued using `node.cli(command)` with a given `command` string.
+
+```python
+>>> node.cli('uptime')
+['00:36:18.778']
+```
+
+Method `allowlist_node()` can be used to add a given node to the allowlist of the device and enables allowlisting:
+
+```python
+    # `node2` is added to the allowlist of `node1` and allowlisting is enabled on `node1`
+    node1.allowlist_node(node2)
+```
+
+#### Example (simple 3-node topology)
+
+Script below shows how to create a 3-node network topology with `node1` and `node2` being routers, and `node3` an end-device connected to `node2`:
+
+```python
+>>> import cli
+>>> node1 = cli.Node()
+>>> node2 = cli.Node()
+>>> node3 = cli.Node()
+
+>>> node1.form('test')
+>>> node1.get_state()
+'leader'
+
+>>> node1.allowlist_node(node2)
+>>> node1.allowlist_node(node3)
+
+>>> node2.join(node1, cli.JOIN_TYPE_ROUTER)
+>>> node2.get_state()
+'router'
+
+>>> node3.join(node1, cli.JOIN_TYPE_END_DEVICE)
+>>> node3.get_state()
+'child'
+
+>>> node1.cli('neighbor list')
+['0x1c01 0x0400 ']
+```
+
+### Logs and Verbose mode
+
+Every `cli.Node()` instance will save its corresponding logs. By default the logs are saved in a file `ot-logs<node_index>.log`.
+
+When `start.sh` script is used to run all test-cases, if any test fails, to help with debugging of the issue, the last 30 lines of logs of every node involved in the test-case are dumped to `stdout`.
+
+A `cli.Node()` instance can also provide additional logs and info as the test-cases are run (verbose mode). It can be enabled for a node instance when it is created:
+
+```python
+>>> import cli
+>>> node = cli.Node(verbose=True)
+$ Node1.__init__() cmd: `../../../examples/apps/cli/ot-cli-ftd --time-speed=1 1`
+
+>>> node.get_state()
+$ Node1.cli('state') -> disabled
+'disabled'
+
+>>> node.form('test')
+$ Node1.cli('networkname test')
+$ Node1.cli('panid 4660')
+$ Node1.cli('ifconfig up')
+$ Node1.cli('thread start')
+$ Node1.cli('state') -> detached
+$ Node1.cli('state') -> detached
+...
+$ Node1.cli('state') -> leader
+```
+
+Alternatively, `cli.Node._VERBOSE` settings can be changed to enable verbose logging for all nodes. The default value of `cli.Node._VERBOSE` is determined from environment variable `TORANJ_VERBOSE` (verbose mode is enabled when env variable is set to any of `1`, `True`, `Yes`, `Y`, `On` (case-insensitive)), otherwise it is disabled.
+
+## `toranj-cli` and `thread-cert` test framework
+
+`toranj-cli` uses CLI commands to test the behavior of OpenThread with simulation platform. `thread-cert` scripts (in `tests/scripts/thread-cert`) also use CLI commands. However, these two test frameworks have certain differences and are intended for different situations. The `toranj` test cases run in real-time (though it is possible to run with a time speed-up factor) while the `thread-cert` scripts use virtual-time and event-based simulation model.
+
+- `toranj` test cases are useful to validate the real-time (non event-based) simulation platform implementation itself.
+- `toranj` test cases can be used in situations where the platform layer may not support event-based model.
+- `toranj` frameworks allows for more interactive testing (e.g., read–eval–print loop (REPL) model in python) and do not need a separate process to run to handle/dispatch events (which is required for the virtual-time simulation model).
+- `thread-cert` test cases can run quickly (due to virtual time emulation), but the test script itself needs to manage the flow and advancement of time.
diff --git a/tests/toranj/README_NCP.md b/tests/toranj/README_NCP.md
new file mode 100644
index 0000000..6262b7e
--- /dev/null
+++ b/tests/toranj/README_NCP.md
@@ -0,0 +1,369 @@
+# `toranj-ncp` test framework
+
+`toranj-ncp` is a test framework for OpenThread enabling testing of the combined behavior of OpenThread (in NCP mode), spinel interface, and `wpantund` driver on linux.
+
+`toranj` features:
+
+- It is developed in Python.
+- It can be used to simulate multiple nodes forming complex network topologies.
+- It allows testing of network interactions between many nodes (IPv6 traffic exchanges).
+- `toranj` in NCP mode runs `wpantund` natively with OpenThread in NCP mode on simulation platform (real-time).
+
+## Setup
+
+`toranj-ncp` requires `wpantund` to be installed.
+
+- Please follow [`wpantund` installation guide](https://github.com/openthread/wpantund/blob/master/INSTALL.md#wpantund-installation-guide). Note that `toranj` expects `wpantund` installed from latest master branch.
+- Alternative way to install `wpantund` is to use the same commands from git workflow [Simulation](https://github.com/openthread/openthread/blob/4b55284bd20f99a88e8e2c617ba358a0a5547f5d/.github/workflows/simulation.yml#L336-L341) for build target `toranj-test-framework`.
+
+To run all tests, `start` script can be used. This script will build OpenThread with proper configuration options and starts running all test.
+
+```bash
+    cd tests/toranj/    # from OpenThread repo root
+    TORANJ_CLI=0 ./start.sh
+```
+
+The `toranj-ncp` tests are included in `tests/toranj/ncp` folder. Each test-case has its own script following naming model `test-nnn-name.py` (e.g., `test-001-get-set.py`).
+
+To run a specific test
+
+```bash
+    sudo python ncp/test-001-get-set.py
+```
+
+## `toranj` Components
+
+`wpan` python module defines the `toranj` test components.
+
+### `wpan.Node()` Class
+
+`wpan.Node()` class creates a Thread node instance. It creates a sub-process to run `wpantund` and OpenThread, and provides methods to control the node.
+
+```python
+>>> import wpan
+>>> node1 = wpan.Node()
+>>> node1
+Node (index=1, interface_name=wpan1)
+>>> node2 = wpan.Node()
+>>> node2
+Node (index=2, interface_name=wpan2)
+```
+
+Note: You may need to run as `sudo` to allow `wpantund` to create tunnel interface (i.e., use `sudo python`).
+
+### `wpan.Node` methods providing `wpanctl` commands
+
+`wpan.Node()` provides methods matching all `wpanctl` commands.
+
+- Get the value of a `wpantund` property, set the value, or add/remove value to/from a list based property:
+
+```python
+    node.get(prop_name)
+    node.set(prop_name, value, binary_data=False)
+    node.add(prop_name, value, binary_data=False)
+    node.remove(prop_name, value, binary_data=False)
+```
+
+Example:
+
+```python
+>>> node.get(wpan.WPAN_NAME)
+'"test-network"'
+>>> node.set(wpan.WPAN_NAME, 'my-network')
+>>> node.get(wpan.WPAN_NAME)
+'"my-network"'
+>>> node.set(wpan.WPAN_KEY, '65F2C35C7B543BAC1F3E26BB9F866C1D', binary_data=True)
+>>> node.get(wpan.WPAN_KEY)
+'[65F2C35C7B543BAC1F3E26BB9F866C1D]'
+```
+
+- Common network operations:
+
+```python
+    node.reset()            # Reset the NCP
+    node.status()           # Get current status
+    node.leave()            # Leave the current network, clear all persistent data
+
+    # Form a network in given channel (if none given use a random one)
+    node.form(name, channel=None)
+
+    # Join a network with given info.
+    # node_type can be JOIN_TYPE_ROUTER, JOIN_TYPE_END_DEVICE, JOIN_TYPE_SLEEPY_END_DEVICE
+    node.join(name, channel=None, node_type=None, panid=None, xpanid=None)
+```
+
+Example:
+
+```python
+>>> result = node.status()
+>>> print result
+wpan1 => [
+    "NCP:State" => "offline"
+    "Daemon:Enabled" => true
+    "NCP:Version" => "OPENTHREAD/20170716-00460-ga438cef0c-dirty; NONE; Feb 12 2018 11:47:01"
+    "Daemon:Version" => "0.08.00d (0.07.01-191-g63265f7; Feb  2 2018 18:05:47)"
+    "Config:NCP:DriverName" => "spinel"
+    "NCP:HardwareAddress" => [18B4300000000001]
+]
+>>>
+>>> node.form("test-network", channel=12)
+'Forming WPAN "test-network" as node type "router"\nSuccessfully formed!'
+>>>
+>>> print node.status()
+wpan1 => [
+    "NCP:State" => "associated"
+    "Daemon:Enabled" => true
+    "NCP:Version" => "OPENTHREAD/20170716-00460-ga438cef0c-dirty; NONE; Feb 12 2018 11:47:01"
+    "Daemon:Version" => "0.08.00d (0.07.01-191-g63265f7; Feb  2 2018 18:05:47)"
+    "Config:NCP:DriverName" => "spinel"
+    "NCP:HardwareAddress" => [18B4300000000001]
+    "NCP:Channel" => 12
+    "Network:NodeType" => "leader"
+    "Network:Name" => "test-network"
+    "Network:XPANID" => 0xA438CF5973FD86B2
+    "Network:PANID" => 0x9D81
+    "IPv6:MeshLocalAddress" => "fda4:38cf:5973:0:b899:3436:15c6:941d"
+    "IPv6:MeshLocalPrefix" => "fda4:38cf:5973::/64"
+]
+```
+
+- Scan:
+
+```python
+    node.active_scan(channel=None)
+    node.energy_scan(channel=None)
+    node.discover_scan(channel=None, joiner_only=False, enable_filtering=False, panid_filter=None)
+    node.permit_join(duration_sec=None, port=None, udp=True, tcp=True)
+```
+
+- On-mesh prefixes and off-mesh routes:
+
+```python
+    node.config_gateway(prefix, default_route=False)
+    node.add_route(route_prefix, prefix_len_in_bytes=None, priority=None)
+    node.remove_route(route_prefix, prefix_len_in_bytes=None, priority=None)
+```
+
+A direct `wpanctl` command can be issued using `node.wpanctl(command)` with a given `command` string.
+
+`wpan` module provides variables for different `wpantund` properties. Some commonly used are:
+
+- Network/NCP properties: WPAN_STATE, WPAN_NAME, WPAN_PANID, WPAN_XPANID, WPAN_KEY, WPAN_CHANNEL, WPAN_HW_ADDRESS, WPAN_EXT_ADDRESS, WPAN_POLL_INTERVAL, WPAN_NODE_TYPE, WPAN_ROLE, WPAN_PARTITION_ID
+
+- IPv6 Addresses: WPAN_IP6_LINK_LOCAL_ADDRESS, WPAN_IP6_MESH_LOCAL_ADDRESS, WPAN_IP6_MESH_LOCAL_PREFIX, WPAN_IP6_ALL_ADDRESSES, WPAN_IP6_MULTICAST_ADDRESSES
+
+- Thread Properties: WPAN_THREAD_RLOC16, WPAN_THREAD_ROUTER_ID, WPAN_THREAD_LEADER_ADDRESS, WPAN_THREAD_LEADER_ROUTER_ID, WPAN_THREAD_LEADER_WEIGHT, WPAN_THREAD_LEADER_NETWORK_DATA,
+
+        WPAN_THREAD_CHILD_TABLE, WPAN_THREAD_CHILD_TABLE_ADDRESSES, WPAN_THREAD_NEIGHBOR_TABLE,
+        WPAN_THREAD_ROUTER_TABLE
+
+Method `join_node()` can be used by a node to join another node:
+
+```python
+    # `node1` joining `node2`'s network as a router
+    node1.join_node(node2, node_type=JOIN_TYPE_ROUTER)
+```
+
+Method `allowlist_node()` can be used to add a given node to the allowlist of the device and enables allowlisting:
+
+```python
+    # `node2` is added to the allowlist of `node1` and allowlisting is enabled on `node1`
+    node1.allowlist_node(node2)
+```
+
+#### Example (simple 3-node topology)
+
+Script below shows how to create a 3-node network topology with `node1` and `node2` being routers, and `node3` an end-device connected to `node2`:
+
+```python
+>>> import wpan
+>>> node1 = wpan.Node()
+>>> node2 = wpan.Node()
+>>> node3 = wpan.Node()
+
+>>> wpan.Node.init_all_nodes()
+
+>>> node1.form("test-PAN")
+'Forming WPAN "test-PAN" as node type "router"\nSuccessfully formed!'
+
+>>> node1.allowlist_node(node2)
+>>> node2.allowlist_node(node1)
+
+>>> node2.join_node(node1, wpan.JOIN_TYPE_ROUTER)
+'Joining "test-PAN" C474513CB487778D as node type "router"\nSuccessfully Joined!'
+
+>>> node3.allowlist_node(node2)
+>>> node2.allowlist_node(node3)
+
+>>> node3.join_node(node2, wpan.JOIN_TYPE_END_DEVICE)
+'Joining "test-PAN" C474513CB487778D as node type "end-device"\nSuccessfully Joined!'
+
+>>> print node2.get(wpan.WPAN_THREAD_NEIGHBOR_TABLE)
+[
+    "EAC1672C3EAB30A4, RLOC16:9401, LQIn:3, AveRssi:-20, LastRssi:-20, Age:30, LinkFC:6, MleFC:0, IsChild:yes, RxOnIdle:yes, FTD:yes, SecDataReq:yes, FullNetData:yes"
+    "A2042C8762576FD5, RLOC16:dc00, LQIn:3, AveRssi:-20, LastRssi:-20, Age:5, LinkFC:21, MleFC:18, IsChild:no, RxOnIdle:yes, FTD:yes, SecDataReq:no, FullNetData:yes"
+]
+>>> print node1.get(wpan.WPAN_THREAD_NEIGHBOR_TABLE)
+[
+    "960947C53415DAA1, RLOC16:9400, LQIn:3, AveRssi:-20, LastRssi:-20, Age:18, LinkFC:15, MleFC:11, IsChild:no, RxOnIdle:yes, FTD:yes, SecDataReq:no, FullNetData:yes"
+]
+
+```
+
+### IPv6 Message Exchange
+
+`toranj` allows a test-case to define traffic patterns (IPv6 message exchange) between different nodes. Message exchanges (tx/rx) are prepared and then an async rx/tx operation starts. The success and failure of tx/rx operations can then be verified by the test case.
+
+`wpan.Node` method `prepare_tx()` prepares a UDP6 transmission from a node.
+
+```python
+    node1.prepare_tx(src, dst, data, count)
+```
+
+- `src` and `dst` can be
+
+  - either a string containing an IPv6 address
+  - or a tuple (ipv6 address as string, port). if no port is given, a random port number is used.
+
+- `data` can be
+
+  - either a string containing the message to be sent,
+  - or an int indicating size of the message (a random message with the given length will be generated).
+
+- `count` gives number of times the message will be sent (default is 1).
+
+`prepare_tx` returns a `wpan.AsyncSender` object. The sender object can be used to check success/failure of tx operation.
+
+`wpan.Node` method `prepare_rx()` prepares a node to listen for UDP messages from a sender.
+
+```python
+    node2.prepare_rx(sender)
+```
+
+- `sender` should be an `wpan.AsyncSender` object returned from previous `prepare_tx`.
+- `prepare_rx()` returns a `wpan.AsyncReceiver` object to help test to check success/failure of rx operation.
+
+After all exchanges are prepared, static method `perform_async_tx_rx()` should be used to start all previously prepared rx and tx operations.
+
+```python
+    wpan.Node.perform_async_tx_rx(timeout)
+```
+
+- `timeout` gives amount of time (in seconds) to wait for all operations to finish. (default is 20 seconds)
+
+After `perform_async_tx_rx()` is done, the `AsyncSender` and `AsyncReceiver` objects can check if operations were successful (using property `was_successful`)
+
+#### Example
+
+Sending 10 messages containing `"Hello there!"` from `node1` to `node2` using their mesh-local addresses:
+
+```python
+# `node1` and `node2` are already joined and are part of the same Thread network.
+
+# Get the mesh local addresses
+>>> mladdr1 = node1.get(wpan.WPAN_IP6_MESH_LOCAL_ADDRESS)[1:-1]  # remove `"` from start/end of string
+>>> mladdr2 = node2.get(wpan.WPAN_IP6_MESH_LOCAL_ADDRESS)[1:-1]
+
+>>> print (mladdr1, mladdr2)
+('fda4:38cf:5973:0:b899:3436:15c6:941d', 'fda4:38cf:5973:0:5836:fa55:7394:6d4b')
+
+# prepare a `sender` and corresponding `recver`
+>>> sender = node1.prepare_tx((mladdr1, 1234), (mladdr2, 2345), "Hello there!", 10)
+>>> recver = node2.prepare_rx(sender)
+
+# perform async message transfer
+>>> wpan.Node.perform_async_tx_rx()
+
+# check status of `sender` and `recver`
+>>> sender.was_successful
+True
+>>> recver.was_successful
+True
+
+# `sender` or `recver` can provide info about the exchange
+
+>>> sender.src_addr
+'fda4:38cf:5973:0:b899:3436:15c6:941d'
+>>> sender.src_port
+1234
+>>> sender.dst_addr
+'fda4:38cf:5973:0:5836:fa55:7394:6d4b'
+>>> sender.dst_port
+2345
+>>> sender.msg
+'Hello there!'
+>>> sender.count
+10
+
+# get all received msg by `recver` as list of tuples `(msg, (src_address, src_port))`
+>>> recver.all_rx_msg
+[('Hello there!', ('fda4:38cf:5973:0:b899:3436:15c6:941d', 1234)), ...  ]
+```
+
+### Logs and Verbose mode
+
+Every `wpan.Node()` instance will save its corresponding `wpantund` logs. By default the logs are saved in a file `wpantun-log<node_index>.log`. By setting `wpan.Node__TUND_LOG_TO_FILE` to `False` the logs are written to `stdout` as the test-cases are executed.
+
+When `start.sh` script is used to run all test-cases, if any test fails, to help with debugging of the issue, the last 30 lines of `wpantund` logs of every node involved in the test-case is dumped to `stdout`.
+
+A `wpan.Node()` instance can also provide additional logs and info as the test-cases are run (verbose mode). It can be enabled for a node instance when it is created:
+
+```python
+    node = wpan.Node(verbose=True)     # `node` instance will provide extra logs.
+```
+
+Alternatively, `wpan.Node._VERBOSE` settings can be changed to enable verbose logging for all nodes. The default value of `wpan.Node._VERBOSE` is determined from environment variable `TORANJ_VERBOSE` (verbose mode is enabled when env variable is set to any of `1`, `True`, `Yes`, `Y`, `On` (case-insensitive)), otherwise it is disabled. When `TORANJ_VERBOSE` is enabled, the OpenThread logging is also enabled (and collected in `wpantund-log<node_index>.log`files) on all nodes.
+
+Here is example of small test script and its corresponding log output with `verbose` mode enabled:
+
+```python
+node1 = wpan.Node(verbose=True)
+node2 = wpan.Node(verbose=True)
+
+wpan.Node.init_all_nodes()
+
+node1.form("toranj-net")
+node2.active_scan()
+
+node2.join_node(node1)
+verify(node2.get(wpan.WPAN_STATE) == wpan.STATE_ASSOCIATED)
+
+lladdr1 = node1.get(wpan.WPAN_IP6_LINK_LOCAL_ADDRESS)[1:-1]
+lladdr2 = node2.get(wpan.WPAN_IP6_LINK_LOCAL_ADDRESS)[1:-1]
+
+sender = node1.prepare_tx(lladdr1, lladdr2, 20)
+recver = node2.prepare_rx(sender)
+
+wpan.Node.perform_async_tx_rx()
+
+```
+
+```
+$ Node1.__init__() cmd: /usr/local/sbin/wpantund -o Config:NCP:SocketPath "system:../../examples/apps/ncp/ot-ncp-ftd 1" -o Config:TUN:InterfaceName wpan1 -o Config:NCP:DriverName spinel -o Daemon:SyslogMask "all -debug"
+$ Node2.__init__() cmd: /usr/local/sbin/wpantund -o Config:NCP:SocketPath "system:../../examples/apps/ncp/ot-ncp-ftd 2" -o Config:TUN:InterfaceName wpan2 -o Config:NCP:DriverName spinel -o Daemon:SyslogMask "all -debug"
+$ Node1.wpanctl('leave') -> 'Leaving current WPAN. . .'
+$ Node2.wpanctl('leave') -> 'Leaving current WPAN. . .'
+$ Node1.wpanctl('form "toranj-net"'):
+     Forming WPAN "toranj-net" as node type "router"
+     Successfully formed!
+$ Node2.wpanctl('scan'):
+        | PAN ID | Ch | XPanID           | HWAddr           | RSSI
+     ---+--------+----+------------------+------------------+------
+      1 | 0x9DEB | 16 | 8CC6CFC810F23E1B | BEECDAF3439DC931 |  -20
+$ Node1.wpanctl('get -v NCP:State') -> '"associated"'
+$ Node1.wpanctl('get -v Network:Name') -> '"toranj-net"'
+$ Node1.wpanctl('get -v Network:PANID') -> '0x9DEB'
+$ Node1.wpanctl('get -v Network:XPANID') -> '0x8CC6CFC810F23E1B'
+$ Node1.wpanctl('get -v Network:Key') -> '[BA2733A5D81EAB8FFB3C9A7383CB6045]'
+$ Node1.wpanctl('get -v NCP:Channel') -> '16'
+$ Node2.wpanctl('set Network:Key -d -v BA2733A5D81EAB8FFB3C9A7383CB6045') -> ''
+$ Node2.wpanctl('join "toranj-net" -c 16 -T r -p 0x9DEB -x 0x8CC6CFC810F23E1B'):
+     Joining "toranj-net" 8CC6CFC810F23E1B as node type "router"
+     Successfully Joined!
+$ Node2.wpanctl('get -v NCP:State') -> '"associated"'
+$ Node1.wpanctl('get -v IPv6:LinkLocalAddress') -> '"fe80::bcec:daf3:439d:c931"'
+$ Node2.wpanctl('get -v IPv6:LinkLocalAddress') -> '"fe80::ec08:f348:646f:d37d"'
+- Node1 sent 20 bytes (":YeQuNKjuOtd%H#ipM7P") to [fe80::ec08:f348:646f:d37d]:404 from [fe80::bcec:daf3:439d:c931]:12557
+- Node2 received 20 bytes (":YeQuNKjuOtd%H#ipM7P") on port 404 from [fe80::bcec:daf3:439d:c931]:12557
+
+```
diff --git a/tests/toranj/build.sh b/tests/toranj/build.sh
index adcb71c..4c25b1a 100755
--- a/tests/toranj/build.sh
+++ b/tests/toranj/build.sh
@@ -38,6 +38,10 @@
     echo "        ncp-15.4        : Build OpenThread NCP FTD mode with simulation platform - 15.4 radio"
     echo "        ncp-trel        : Build OpenThread NCP FTD mode with simulation platform - TREL radio "
     echo "        ncp-15.4+trel   : Build OpenThread NCP FTD mode with simulation platform - multi radio (15.4+TREL)"
+    echo "        cli             : Build OpenThread CLI FTD mode with simulation platform"
+    echo "        cli-15.4        : Build OpenThread CLI FTD mode with simulation platform - 15.4 radio"
+    echo "        cli-trel        : Build OpenThread CLI FTD mode with simulation platform - TREL radio "
+    echo "        cli-15.4+trel   : Build OpenThread CLI FTD mode with simulation platform - multi radio (15.4+TREL)"
     echo "        rcp             : Build OpenThread RCP (NCP in radio mode) with simulation platform"
     echo "        posix           : Build OpenThread POSIX NCP"
     echo "        posix-15.4      : Build OpenThread POSIX NCP - 15.4 radio"
@@ -92,7 +96,7 @@
 
 build_config=$1
 
-configure_options=(
+ncp_configure_options=(
     "--disable-docs"
     "--enable-tests=$tests"
     "--enable-coverage=$coverage"
@@ -100,6 +104,14 @@
     "--enable-ncp"
 )
 
+cli_configure_options=(
+    "--disable-docs"
+    "--enable-tests=$tests"
+    "--enable-coverage=$coverage"
+    "--enable-ftd"
+    "--enable-cli"
+)
+
 posix_configure_options=(
     "--disable-docs"
     "--enable-tests=$tests"
@@ -127,7 +139,7 @@
         ${top_srcdir}/configure \
             CPPFLAGS="$cppflags_config" \
             --with-examples=simulation \
-            "${configure_options[@]}" || die
+            "${ncp_configure_options[@]}" || die
         make -j 8 || die
         ;;
 
@@ -143,7 +155,7 @@
         ${top_srcdir}/configure \
             CPPFLAGS="$cppflags_config" \
             --with-examples=simulation \
-            "${configure_options[@]}" || die
+            "${ncp_configure_options[@]}" || die
         make -j 8 || die
         cp -p ${top_builddir}/examples/apps/ncp/ot-ncp-ftd ${top_builddir}/examples/apps/ncp/ot-ncp-ftd-15.4
         ;;
@@ -160,7 +172,7 @@
         ${top_srcdir}/configure \
             CPPFLAGS="$cppflags_config" \
             --with-examples=simulation \
-            "${configure_options[@]}" || die
+            "${ncp_configure_options[@]}" || die
         make -j 8 || die
         cp -p ${top_builddir}/examples/apps/ncp/ot-ncp-ftd ${top_builddir}/examples/apps/ncp/ot-ncp-ftd-trel
         ;;
@@ -177,11 +189,76 @@
         ${top_srcdir}/configure \
             CPPFLAGS="$cppflags_config" \
             --with-examples=simulation \
-            "${configure_options[@]}" || die
+            "${ncp_configure_options[@]}" || die
         make -j 8 || die
         cp -p ${top_builddir}/examples/apps/ncp/ot-ncp-ftd ${top_builddir}/examples/apps/ncp/ot-ncp-ftd-15.4-trel
         ;;
 
+    cli | cli-)
+        echo "==================================================================================================="
+        echo "Building OpenThread CLI FTD mode with simulation platform (radios determined by config)"
+        echo "==================================================================================================="
+        ./bootstrap || die "bootstrap failed"
+        cd "${top_builddir}" || die "cd failed"
+        cppflags_config='-DOPENTHREAD_PROJECT_CORE_CONFIG_FILE=\"../tests/toranj/openthread-core-toranj-config-simulation.h\"'
+        ${top_srcdir}/configure \
+            CPPFLAGS="$cppflags_config" \
+            --with-examples=simulation \
+            "${cli_configure_options[@]}" || die
+        make -j 8 || die
+        ;;
+
+    cli-15.4)
+        echo "==================================================================================================="
+        echo "Building OpenThread CLI FTD mode with simulation platform - 15.4 radio"
+        echo "==================================================================================================="
+        cppflags_config='-DOPENTHREAD_PROJECT_CORE_CONFIG_FILE=\"../tests/toranj/openthread-core-toranj-config-simulation.h\"'
+        cppflags_config="${cppflags_config} -DOPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE=1"
+        cppflags_config="${cppflags_config} -DOPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE=0"
+        ./bootstrap || die "bootstrap failed"
+        cd "${top_builddir}" || die "cd failed"
+        ${top_srcdir}/configure \
+            CPPFLAGS="$cppflags_config" \
+            --with-examples=simulation \
+            "${cli_configure_options[@]}" || die
+        make -j 8 || die
+        cp -p ${top_builddir}/examples/apps/cli/ot-cli-ftd ${top_builddir}/examples/apps/cli/ot-cli-ftd-15.4
+        ;;
+
+    cli-trel)
+        echo "==================================================================================================="
+        echo "Building OpenThread CLI FTD mode with simulation platform - TREL radio"
+        echo "==================================================================================================="
+        cppflags_config='-DOPENTHREAD_PROJECT_CORE_CONFIG_FILE=\"../tests/toranj/openthread-core-toranj-config-simulation.h\"'
+        cppflags_config="${cppflags_config} -DOPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE=0"
+        cppflags_config="${cppflags_config} -DOPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE=1"
+        ./bootstrap || die "bootstrap failed"
+        cd "${top_builddir}" || die "cd failed"
+        ${top_srcdir}/configure \
+            CPPFLAGS="$cppflags_config" \
+            --with-examples=simulation \
+            "${cli_configure_options[@]}" || die
+        make -j 8 || die
+        cp -p ${top_builddir}/examples/apps/cli/ot-cli-ftd ${top_builddir}/examples/apps/cli/ot-cli-ftd-trel
+        ;;
+
+    cli-15.4+trel | cli-trel+15.4)
+        echo "==================================================================================================="
+        echo "Building OpenThread NCP FTD mode with simulation platform - multi radio (15.4 + TREL)"
+        echo "==================================================================================================="
+        cppflags_config='-DOPENTHREAD_PROJECT_CORE_CONFIG_FILE=\"../tests/toranj/openthread-core-toranj-config-simulation.h\"'
+        cppflags_config="${cppflags_config} -DOPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE=1"
+        cppflags_config="${cppflags_config} -DOPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE=1"
+        ./bootstrap || die "bootstrap failed"
+        cd "${top_builddir}" || die "cd failed"
+        ${top_srcdir}/configure \
+            CPPFLAGS="$cppflags_config" \
+            --with-examples=simulation \
+            "${cli_configure_options[@]}" || die
+        make -j 8 || die
+        cp -p ${top_builddir}/examples/apps/cli/ot-cli-ftd ${top_builddir}/examples/apps/cli/ot-cli-ftd-15.4-trel
+        ;;
+
     rcp)
         echo "===================================================================================================="
         echo "Building OpenThread RCP (NCP in radio mode) with simulation platform"
diff --git a/tests/toranj/cli/cli.py b/tests/toranj/cli/cli.py
new file mode 100644
index 0000000..184271b
--- /dev/null
+++ b/tests/toranj/cli/cli.py
@@ -0,0 +1,630 @@
+#!/usr/bin/env python3
+#
+#  Copyright (c) 2021, 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 sys
+import os
+import time
+import re
+import random
+import string
+import subprocess
+import pexpect
+import pexpect.popen_spawn
+import signal
+import inspect
+import weakref
+
+# ----------------------------------------------------------------------------------------------------------------------
+# Constants
+
+JOIN_TYPE_ROUTER = 'router'
+JOIN_TYPE_END_DEVICE = 'ed'
+JOIN_TYPE_SLEEPY_END_DEVICE = 'sed'
+
+# ----------------------------------------------------------------------------------------------------------------------
+
+
+def _log(text, new_line=True, flush=True):
+    sys.stdout.write(text)
+    if new_line:
+        sys.stdout.write('\n')
+    if flush:
+        sys.stdout.flush()
+
+
+# ----------------------------------------------------------------------------------------------------------------------
+# CliError class
+
+
+class CliError(Exception):
+
+    def __init__(self, error_code, message):
+        self._error_code = error_code
+        self._message = message
+
+    @property
+    def error_code(self):
+        return self._error_code
+
+    @property
+    def message(self):
+        return self._message
+
+
+# ----------------------------------------------------------------------------------------------------------------------
+# Node class
+
+
+class Node(object):
+    """ An OT CLI instance """
+
+    # defines the default verbosity setting (can be changed per `Node`)
+    _VERBOSE = os.getenv('TORANJ_VERBOSE', 'no').lower() in ['true', '1', 't', 'y', 'yes', 'on']
+
+    _SPEED_UP_FACTOR = 1  # defines the default time speed up factor
+
+    # Determine whether to save logs in a file.
+    _SAVE_LOGS = True
+
+    # name of  log file (if _SAVE_LOGS is `True`)
+    _LOG_FNAME = 'ot-logs'
+
+    _OT_BUILDDIR = os.getenv('top_builddir', '../../..')
+
+    _OT_CLI_FTD = '%s/examples/apps/cli/ot-cli-ftd' % _OT_BUILDDIR
+
+    _WAIT_TIME = 10
+
+    _START_INDEX = 1
+    _cur_index = _START_INDEX
+
+    _all_nodes = weakref.WeakSet()
+
+    def __init__(self, verbose=_VERBOSE):
+        """Creates a new `Node` instance"""
+
+        index = Node._cur_index
+        Node._cur_index += 1
+
+        self._index = index
+        self._verbose = verbose
+
+        if Node._SAVE_LOGS:
+            self._log_file = open(self._LOG_FNAME + str(index) + '.log', 'wb')
+        else:
+            self._log_file = None
+
+        cmd = f'{self._OT_CLI_FTD} --time-speed={self._SPEED_UP_FACTOR} {self._index}'
+
+        if self._verbose:
+            _log(f'$ Node{index}.__init__() cmd: `{cmd}`')
+
+        self._cli_process = pexpect.popen_spawn.PopenSpawn(cmd, logfile=self._log_file)
+        Node._all_nodes.add(self)
+
+    def __del__(self):
+        self._finalize()
+
+    def __repr__(self):
+        return f'Node(index={self._index})'
+
+    @property
+    def index(self):
+        return self._index
+
+    # ------------------------------------------------------------------------------------------------------------------
+    # Executing a `cli` command
+
+    def cli(self, *args):
+        """ Issues a CLI command on the given node and returns the resulting output.
+
+            The returned result is a list of strings (with `\r\n` removed) as outputted by the CLI.
+            If executing the command fails, `CliError` is raised with error code and error message.
+        """
+
+        cmd = ' '.join([f'{arg}' for arg in args if arg is not None]).strip()
+
+        if self._verbose:
+            _log(f'$ Node{self._index}.cli(\'{cmd}\')', new_line=False)
+
+        self._cli_process.send(cmd + '\n')
+        index = self._cli_process.expect(['(.*)Done\r\n', '.*Error (\d+):(.*)\r\n'])
+
+        if index == 0:
+            result = [
+                line for line in self._cli_process.match.group(1).decode().splitlines()
+                if not self._is_ot_logg_line(line) if not line.strip().endswith(cmd)
+            ]
+
+            if self._verbose:
+                if len(result) > 1:
+                    _log(':')
+                    for line in result:
+                        _log('     ' + line)
+                elif len(result) == 1:
+                    _log(f' -> {result[0]}')
+                else:
+                    _log('')
+
+            return result
+        else:
+            match = self._cli_process.match
+            e = CliError(int(match.group(1).decode()), match.group(2).decode().strip())
+            if self._verbose:
+                _log(f': Error {e.message} ({e.error_code})')
+            raise e
+
+    def _is_ot_logg_line(self, line):
+        return any(level in line for level in [' [D] ', ' [I] ', ' [N] ', ' [W] ', ' [C] ', ' [-] '])
+
+    def _cli_no_output(self, cmd, *args):
+        outputs = self.cli(cmd, *args)
+        verify(len(outputs) == 0)
+
+    def _cli_single_output(self, cmd, expected_outputs=None):
+        outputs = self.cli(cmd)
+        verify(len(outputs) == 1)
+        verify((expected_outputs is None) or (outputs[0] in expected_outputs))
+        return outputs[0]
+
+    def _finalize(self):
+        if self._cli_process.proc.poll() is None:
+            if self._verbose:
+                _log(f'$ Node{self.index} terminating')
+            self._cli_process.send('exit\n')
+            self._cli_process.wait()
+
+    # ------------------------------------------------------------------------------------------------------------------
+    # cli commands
+
+    def get_state(self):
+        return self._cli_single_output('state', ['detached', 'child', 'router', 'leader', 'disabled'])
+
+    def get_channel(self):
+        return self._cli_single_output('channel')
+
+    def set_channel(self, channel):
+        self._cli_no_output('channel', channel)
+
+    def get_ext_addr(self):
+        return self._cli_single_output('extaddr')
+
+    def set_ext_addr(self, ext_addr):
+        self._cli_no_output('extaddr', ext_addr)
+
+    def get_ext_panid(self):
+        return self._cli_single_output('extpanid')
+
+    def set_ext_panid(self, ext_panid):
+        self._cli_no_output('extpanid', ext_panid)
+
+    def get_mode(self):
+        return self._cli_single_output('mode')
+
+    def set_mode(self, mode):
+        self._cli_no_output('mode', mode)
+
+    def get_network_key(self):
+        return self._cli_single_output('networkkey')
+
+    def set_network_key(self, networkkey):
+        self._cli_no_output('networkkey', networkkey)
+
+    def get_network_name(self):
+        return self._cli_single_output('networkname')
+
+    def set_network_name(self, network_name):
+        self._cli_no_output('networkname', network_name)
+
+    def get_panid(self):
+        return self._cli_single_output('panid')
+
+    def set_panid(self, panid):
+        self._cli_no_output('panid', panid)
+
+    def get_router_upgrade_threshold(self):
+        return self._cli_single_output('routerupgradethreshold')
+
+    def set_router_upgrade_threshold(self, threshold):
+        self._cli_no_output('routerupgradethreshold', threshold)
+
+    def get_router_selection_jitter(self):
+        return self._cli_single_output('routerselectionjitter')
+
+    def set_router_selection_jitter(self, jitter):
+        self._cli_no_output('routerselectionjitter', jitter)
+
+    def interface_up(self):
+        self._cli_no_output('ifconfig up')
+
+    def interface_down(self):
+        self._cli_no_output('ifconfig down')
+
+    def get_interface_state(self):
+        return self._cli_single_output('ifconfig')
+
+    def thread_start(self):
+        self._cli_no_output('thread start')
+
+    def thread_stop(self):
+        self._cli_no_output('thread stop')
+
+    def get_ip_addrs(self):
+        return self.cli('ipaddr')
+
+    def get_mleid_ip_addr(self):
+        return self._cli_single_output('ipaddr mleid')
+
+    def get_linklocal_ip_addr(self):
+        return self._cli_single_output('ipaddr linklocal')
+
+    def get_rloc_ip_addr(self):
+        return self._cli_single_output('ipaddr rloc')
+
+    #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+    # SRP client
+
+    def srp_client_start(self, server_address, server_port):
+        self._cli_no_output('srp client start', server_address, server_port)
+
+    def srp_client_stop(self):
+        self._cli_no_output('srp client stop')
+
+    def srp_client_get_state(self):
+        return self._cli_single_output('srp client state', ['Enabled', 'Disabled'])
+
+    def srp_client_get_auto_start_mode(self):
+        return self._cli_single_output('srp client autostart', ['Enabled', 'Disabled'])
+
+    def srp_client_enable_auto_start_mode(self):
+        self._cli_no_output('srp client autostart enable')
+
+    def srp_client_disable_auto_start_mode(self):
+        self._cli_no_output('srp client autostart disable')
+
+    def srp_client_get_server_address(self):
+        return self._cli_single_output('srp client server address')
+
+    def srp_client_get_server_port(self):
+        return self._cli_single_output('srp client server port')
+
+    def srp_client_get_host_state(self):
+        return self._cli_single_output('srp client host state')
+
+    def srp_client_set_host_name(self, name):
+        self._cli_no_output('srp client host name', name)
+
+    def srp_client_get_host_name(self):
+        return self._cli_single_output('srp client host name')
+
+    def srp_client_remove_host(self, remove_key=False, send_unreg_to_server=False):
+        self._cli_no_output('srp client host remove', int(remove_key), int(send_unreg_to_server))
+
+    def srp_client_clear_host(self):
+        self._cli_no_output('srp client host clear')
+
+    def srp_client_set_host_address(self, *addrs):
+        self._cli_no_output('srp client host address', *addrs)
+
+    def srp_client_get_host_address(self):
+        return self.cli('srp client host address')
+
+    def srp_client_add_service(self, instance_name, service_name, port, priority=0, weight=0, txt_entries=[]):
+        txt_record = "".join(self._encode_txt_entry(entry) for entry in txt_entries)
+        self._cli_no_output('srp client service add', instance_name, service_name, port, priority, weight, txt_record)
+
+    def srp_client_remove_service(self, instance_name, service_name):
+        self._cli_no_output('srp client service remove', instance_name, service_name)
+
+    def srp_client_clear_service(self, instance_name, service_name):
+        self._cli_no_output('srp client service clear', instance_name, service_name)
+
+    def srp_client_get_services(self):
+        outputs = self.cli('srp client service')
+        return [self._parse_srp_client_service(line) for line in outputs]
+
+    def _encode_txt_entry(self, entry):
+        """Encodes the TXT entry to the DNS-SD TXT record format as a HEX string.
+
+           Example usage:
+           self._encode_txt_entries(['abc'])     -> '03616263'
+           self._encode_txt_entries(['def='])    -> '046465663d'
+           self._encode_txt_entries(['xyz=XYZ']) -> '0778797a3d58595a'
+        """
+        return '{:02x}'.format(len(entry)) + "".join("{:02x}".format(ord(c)) for c in entry)
+
+    def _parse_srp_client_service(self, line):
+        """Parse one line of srp service list into a dictionary which
+           maps string keys to string values.
+
+           Example output for input
+           'instance:\"%s\", name:\"%s\", state:%s, port:%d, priority:%d, weight:%d"'
+           {
+               'instance': 'my-service',
+               'name': '_ipps._udp',
+               'state': 'ToAdd',
+               'port': '12345',
+               'priority': '0',
+               'weight': '0'
+           }
+
+           Note that value of 'port', 'priority' and 'weight' are represented
+           as strings but not integers.
+        """
+        key_values = [word.strip().split(':') for word in line.split(', ')]
+        return {key_value[0].strip(): key_value[1].strip('"') for key_value in key_values}
+
+    #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+    # SRP server
+
+    def srp_server_get_state(self):
+        return self._cli_single_output('srp server state', ['disabled', 'running', 'stopped'])
+
+    def srp_server_get_addr_mode(self):
+        return self._cli_single_output('srp server addrmode', ['unicast', 'anycast'])
+
+    def srp_server_set_addr_mode(self, mode):
+        self._cli_no_output('srp server addrmode', mode)
+
+    def srp_server_get_anycast_seq_num(self):
+        return self._cli_single_output('srp server seqnum')
+
+    def srp_server_set_anycast_seq_num(self, seqnum):
+        self._cli_no_output('srp server seqnum', seqnum)
+
+    def srp_server_enable(self):
+        self._cli_no_output('srp server enable')
+
+    def srp_server_disable(self):
+        self._cli_no_output('srp server 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)
+
+    def srp_server_get_hosts(self):
+        """Returns the host list on the SRP server as a list of property
+           dictionary.
+
+           Example output:
+           [{
+               'fullname': 'my-host.default.service.arpa.',
+               'name': 'my-host',
+               'deleted': 'false',
+               'addresses': ['2001::1', '2001::2']
+           }]
+        """
+        outputs = self.cli('srp server host')
+        host_list = []
+        while outputs:
+            host = {}
+            host['fullname'] = outputs.pop(0).strip()
+            host['name'] = host['fullname'].split('.')[0]
+            host['deleted'] = outputs.pop(0).strip().split(':')[1].strip()
+            if host['deleted'] == 'true':
+                host_list.append(host)
+                continue
+            addresses = outputs.pop(0).strip().split('[')[1].strip(' ]').split(',')
+            map(str.strip, addresses)
+            host['addresses'] = [addr for addr in addresses if addr]
+            host_list.append(host)
+        return host_list
+
+    def srp_server_get_host(self, host_name):
+        """Returns host on the SRP server that matches given host name.
+
+           Example usage:
+           self.srp_server_get_host("my-host")
+        """
+        for host in self.srp_server_get_hosts():
+            if host_name == host['name']:
+                return host
+
+    def srp_server_get_services(self):
+        """Returns the service list on the SRP server as a list of property
+           dictionary.
+
+           Example output:
+           [{
+               'fullname': 'my-service._ipps._tcp.default.service.arpa.',
+               'instance': 'my-service',
+               'name': '_ipps._tcp',
+               'deleted': 'false',
+               'port': '12345',
+               'priority': '0',
+               'weight': '0',
+               'TXT': ['abc=010203'],
+               'host_fullname': 'my-host.default.service.arpa.',
+               'host': 'my-host',
+               'addresses': ['2001::1', '2001::2']
+           }]
+
+           Note that the TXT data is output as a HEX string.
+        """
+        outputs = self.cli('srp server service')
+        service_list = []
+        while outputs:
+            service = {}
+            service['fullname'] = outputs.pop(0).strip()
+            name_labels = service['fullname'].split('.')
+            service['instance'] = name_labels[0]
+            service['name'] = '.'.join(name_labels[1:3])
+            service['deleted'] = outputs.pop(0).strip().split(':')[1].strip()
+            if service['deleted'] == 'true':
+                service_list.append(service)
+                continue
+            # 'subtypes', port', 'priority', 'weight'
+            for i in range(0, 4):
+                key_value = outputs.pop(0).strip().split(':')
+                service[key_value[0].strip()] = key_value[1].strip()
+            txt_entries = outputs.pop(0).strip().split('[')[1].strip(' ]').split(',')
+            txt_entries = map(str.strip, txt_entries)
+            service['TXT'] = [txt for txt in txt_entries if txt]
+            service['host_fullname'] = outputs.pop(0).strip().split(':')[1].strip()
+            service['host'] = service['host_fullname'].split('.')[0]
+            addresses = outputs.pop(0).strip().split('[')[1].strip(' ]').split(',')
+            addresses = map(str.strip, addresses)
+            service['addresses'] = [addr for addr in addresses if addr]
+            service_list.append(service)
+        return service_list
+
+    def srp_server_get_service(self, instance_name, service_name):
+        """Returns service on the SRP server that matches given instance
+           name and service name.
+
+           Example usage:
+           self.srp_server_get_service("my-service", "_ipps._tcp")
+        """
+        for service in self.srp_server_get_services():
+            if (instance_name == service['instance'] and service_name == service['name']):
+                return service
+
+    # ------------------------------------------------------------------------------------------------------------------
+    # Helper methods
+
+    def form(self, network_name=None, network_key=None, channel=None, panid=0x1234, xpanid=None):
+        if network_name is not None:
+            self.set_network_name(network_name)
+        if network_key is not None:
+            self.set_network_key(network_key)
+        if channel is not None:
+            self.set_channel(channel)
+        if xpanid is not None:
+            self.set_ext_panid(xpanid)
+        self.set_panid(panid)
+        self.interface_up()
+        self.thread_start()
+        verify_within(_check_node_is_leader, self._WAIT_TIME, arg=self)
+
+    def join(self, node, type=JOIN_TYPE_ROUTER):
+        self.set_network_name(node.get_network_name())
+        self.set_network_key(node.get_network_key())
+        self.set_channel(node.get_channel())
+        self.set_panid(node.get_panid())
+        if type == JOIN_TYPE_END_DEVICE:
+            self.set_mode('rn')
+        elif type == JOIN_TYPE_SLEEPY_END_DEVICE:
+            self.set_mode('-')
+        else:
+            self.set_mode('rdn')
+            self.set_router_selection_jitter(1)
+        self.interface_up()
+        self.thread_start()
+        if type == JOIN_TYPE_ROUTER:
+            verify_within(_check_node_is_router, self._WAIT_TIME, arg=self)
+        else:
+            verify_within(_check_node_is_child, self._WAIT_TIME, arg=self)
+
+    def allowlist_node(self, node):
+        """Adds a given node to the allowlist of `self` and enables allowlisting on `self`"""
+        self._cli_no_output('macfilter addr add', node.get_ext_addr())
+        self._cli_no_output('macfilter addr allowlist')
+
+    def un_allowlist_node(self, node):
+        """Removes a given node (of node `Node) from the allowlist"""
+        self._cli_no_output('macfilter addr remove', node.get_ext_addr())
+
+    # ------------------------------------------------------------------------------------------------------------------
+    # class methods
+
+    @classmethod
+    def finalize_all_nodes(cls):
+        """Finalizes all previously created `Node` instances (stops the CLI process)"""
+        for node in Node._all_nodes:
+            node._finalize()
+
+    @classmethod
+    def set_time_speedup_factor(cls, factor):
+        """Sets up the time speed up factor - should be set before creating any `Node` objects"""
+        if len(Node._all_nodes) != 0:
+            raise Node._NodeError('set_time_speedup_factor() cannot be called after creating a `Node`')
+        Node._SPEED_UP_FACTOR = factor
+
+
+def _check_node_is_leader(node):
+    verify(node.get_state() == 'leader')
+
+
+def _check_node_is_router(node):
+    verify(node.get_state() == 'router')
+
+
+def _check_node_is_child(node):
+    verify(node.get_state() == 'child')
+
+
+# ----------------------------------------------------------------------------------------------------------------------
+
+
+class VerifyError(Exception):
+    pass
+
+
+_is_in_verify_within = False
+
+
+def verify(condition):
+    """Verifies that a `condition` is true, otherwise raises a VerifyError"""
+    global _is_in_verify_within
+    if not condition:
+        calling_frame = inspect.currentframe().f_back
+        error_message = 'verify() failed at line {} in "{}"'.format(calling_frame.f_lineno,
+                                                                    calling_frame.f_code.co_filename)
+        if not _is_in_verify_within:
+            print(error_message)
+        raise VerifyError(error_message)
+
+
+def verify_within(condition_checker_func, wait_time, arg=None, delay_time=0.1):
+    """Verifies that a given function `condition_checker_func` passes successfully within a given wait timeout.
+       `wait_time` is maximum time waiting for condition_checker to pass (in seconds).
+       `arg` is optional parameter and if it s not None, will be passed to `condition_checker_func()`
+       `delay_time` specifies a delay interval added between failed attempts (in seconds).
+    """
+    global _is_in_verify_within
+    start_time = time.time()
+    old_is_in_verify_within = _is_in_verify_within
+    _is_in_verify_within = True
+    while True:
+        try:
+            if arg is None:
+                condition_checker_func()
+            else:
+                condition_checker_func(arg)
+        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)
+                raise e
+        except BaseException:
+            raise
+        else:
+            break
+        if delay_time != 0:
+            time.sleep(delay_time)
+    _is_in_verify_within = old_is_in_verify_within
diff --git a/tests/toranj/test-008-permit-join.py b/tests/toranj/cli/test-001-get-set.py
old mode 100644
new mode 100755
similarity index 65%
copy from tests/toranj/test-008-permit-join.py
copy to tests/toranj/cli/test-001-get-set.py
index 63ded32..f64b05a
--- a/tests/toranj/test-008-permit-join.py
+++ b/tests/toranj/cli/test-001-get-set.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
 #
-#  Copyright (c) 2018, The OpenThread Authors.
+#  Copyright (c) 2021, The OpenThread Authors.
 #  All rights reserved.
 #
 #  Redistribution and use in source and binary forms, with or without
@@ -26,58 +26,65 @@
 #  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 #  POSSIBILITY OF SUCH DAMAGE.
 
-import time
-import wpan
-from wpan import verify
+from cli import verify
+import cli
+
+import sys
+
+print(sys.version)
 
 # -----------------------------------------------------------------------------------------------------------------------
-# Test description: check wpantund `permit-join` functionality and timeout
+# Test description: simple CLI get and set commands
 
 test_name = __file__[:-3] if __file__.endswith('.py') else __file__
 print('-' * 120)
 print('Starting \'{}\''.format(test_name))
 
 # -----------------------------------------------------------------------------------------------------------------------
-# Creating `wpan.Nodes` instances
+# Creating `Nodes` instances
 
-node = wpan.Node()
-
-# -----------------------------------------------------------------------------------------------------------------------
-# Init all nodes
-
-wpan.Node.init_all_nodes()
-
-# -----------------------------------------------------------------------------------------------------------------------
-# Build network topology
-
-node.form("permit-join-test")
+node = cli.Node()
 
 # -----------------------------------------------------------------------------------------------------------------------
 # Test implementation
 
-verify(node.get(wpan.WPAN_NETWORK_ALLOW_JOIN) == 'false')
+node.set_channel(21)
+verify(node.get_channel() == '21')
 
-node.permit_join()
-verify(node.get(wpan.WPAN_NETWORK_ALLOW_JOIN) == 'true')
+ext_addr = '1122334455667788'
+node.set_ext_addr(ext_addr)
+verify(node.get_ext_addr() == ext_addr)
 
-node.permit_join('0')
-verify(node.get(wpan.WPAN_NETWORK_ALLOW_JOIN) == 'false')
+ext_panid = '1020031510006016'
+node.set_ext_panid(ext_panid)
+verify(node.get_ext_panid() == ext_panid)
 
-node.permit_join(port='1234')
-verify(node.get(wpan.WPAN_NETWORK_ALLOW_JOIN) == 'true')
+key = '0123456789abcdeffecdba9876543210'
+node.set_network_key(key)
+verify(node.get_network_key() == key)
 
-node.permit_join('0')
-verify(node.get(wpan.WPAN_NETWORK_ALLOW_JOIN) == 'false')
+panid = '0xabba'
+node.set_panid(panid)
+verify(node.get_panid() == panid)
 
-# check the timeout
-node.permit_join('1')
-verify(node.get(wpan.WPAN_NETWORK_ALLOW_JOIN) == 'true')
-time.sleep(1.5)
-verify(node.get(wpan.WPAN_NETWORK_ALLOW_JOIN) == 'false')
+mode = 'rd'
+node.set_mode(mode)
+verify(node.get_mode() == mode)
+
+threshold = '1'
+node.set_router_upgrade_threshold(threshold)
+verify(node.get_router_upgrade_threshold() == threshold)
+
+jitter = '100'
+node.set_router_selection_jitter(jitter)
+verify(node.get_router_selection_jitter() == jitter)
+
+verify(node.get_interface_state() == 'down')
+verify(node.get_state() == 'disabled')
 
 # -----------------------------------------------------------------------------------------------------------------------
 # Test finished
 
-wpan.Node.finalize_all_nodes()
+cli.Node.finalize_all_nodes()
 
 print('\'{}\' passed.'.format(test_name))
diff --git a/tests/toranj/test-008-permit-join.py b/tests/toranj/cli/test-002-form.py
old mode 100644
new mode 100755
similarity index 65%
copy from tests/toranj/test-008-permit-join.py
copy to tests/toranj/cli/test-002-form.py
index 63ded32..eea06d5
--- a/tests/toranj/test-008-permit-join.py
+++ b/tests/toranj/cli/test-002-form.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
 #
-#  Copyright (c) 2018, The OpenThread Authors.
+#  Copyright (c) 2021, The OpenThread Authors.
 #  All rights reserved.
 #
 #  Redistribution and use in source and binary forms, with or without
@@ -26,58 +26,53 @@
 #  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 #  POSSIBILITY OF SUCH DAMAGE.
 
-import time
-import wpan
-from wpan import verify
+from cli import verify
+from cli import verify_within
+import cli
 
 # -----------------------------------------------------------------------------------------------------------------------
-# Test description: check wpantund `permit-join` functionality and timeout
+# Test description: forming a Thread network
 
 test_name = __file__[:-3] if __file__.endswith('.py') else __file__
 print('-' * 120)
 print('Starting \'{}\''.format(test_name))
 
 # -----------------------------------------------------------------------------------------------------------------------
-# Creating `wpan.Nodes` instances
+# Creating `Nodes` instances
 
-node = wpan.Node()
+speedup = 4
+cli.Node.set_time_speedup_factor(speedup)
 
-# -----------------------------------------------------------------------------------------------------------------------
-# Init all nodes
-
-wpan.Node.init_all_nodes()
-
-# -----------------------------------------------------------------------------------------------------------------------
-# Build network topology
-
-node.form("permit-join-test")
+node = cli.Node()
 
 # -----------------------------------------------------------------------------------------------------------------------
 # Test implementation
 
-verify(node.get(wpan.WPAN_NETWORK_ALLOW_JOIN) == 'false')
+WAIT_TIME = 5
 
-node.permit_join()
-verify(node.get(wpan.WPAN_NETWORK_ALLOW_JOIN) == 'true')
+verify(node.get_state() == 'disabled')
+node.form('test')
 
-node.permit_join('0')
-verify(node.get(wpan.WPAN_NETWORK_ALLOW_JOIN) == 'false')
+verify(node.get_network_name() == 'test')
 
-node.permit_join(port='1234')
-verify(node.get(wpan.WPAN_NETWORK_ALLOW_JOIN) == 'true')
+node.interface_down()
+verify(node.get_state() == 'disabled')
 
-node.permit_join('0')
-verify(node.get(wpan.WPAN_NETWORK_ALLOW_JOIN) == 'false')
+node.form('form-test',
+          channel=21,
+          panid=0x5678,
+          xpanid='1020031510006016',
+          network_key='0123456789abcdeffecdba9876543210')
 
-# check the timeout
-node.permit_join('1')
-verify(node.get(wpan.WPAN_NETWORK_ALLOW_JOIN) == 'true')
-time.sleep(1.5)
-verify(node.get(wpan.WPAN_NETWORK_ALLOW_JOIN) == 'false')
+verify(node.get_network_name() == 'form-test')
+verify(node.get_channel() == '21')
+verify(node.get_panid() == '0x5678')
+verify(node.get_ext_panid() == '1020031510006016')
+verify(node.get_network_key() == '0123456789abcdeffecdba9876543210')
 
 # -----------------------------------------------------------------------------------------------------------------------
 # Test finished
 
-wpan.Node.finalize_all_nodes()
+cli.Node.finalize_all_nodes()
 
 print('\'{}\' passed.'.format(test_name))
diff --git a/tests/toranj/test-008-permit-join.py b/tests/toranj/cli/test-003-join.py
old mode 100644
new mode 100755
similarity index 66%
rename from tests/toranj/test-008-permit-join.py
rename to tests/toranj/cli/test-003-join.py
index 63ded32..f1cc05a
--- a/tests/toranj/test-008-permit-join.py
+++ b/tests/toranj/cli/test-003-join.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
 #
-#  Copyright (c) 2018, The OpenThread Authors.
+#  Copyright (c) 2021, The OpenThread Authors.
 #  All rights reserved.
 #
 #  Redistribution and use in source and binary forms, with or without
@@ -26,58 +26,55 @@
 #  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 #  POSSIBILITY OF SUCH DAMAGE.
 
-import time
-import wpan
-from wpan import verify
+from cli import verify
+from cli import verify_within
+import cli
 
 # -----------------------------------------------------------------------------------------------------------------------
-# Test description: check wpantund `permit-join` functionality and timeout
+# Test description: joining (as router, end-device, sleepy) - two node network
 
 test_name = __file__[:-3] if __file__.endswith('.py') else __file__
 print('-' * 120)
 print('Starting \'{}\''.format(test_name))
 
 # -----------------------------------------------------------------------------------------------------------------------
-# Creating `wpan.Nodes` instances
+# Creating `cli.Nodes` instances
 
-node = wpan.Node()
+speedup = 4
+cli.Node.set_time_speedup_factor(speedup)
 
-# -----------------------------------------------------------------------------------------------------------------------
-# Init all nodes
+node1 = cli.Node()
+node2 = cli.Node()
 
-wpan.Node.init_all_nodes()
-
-# -----------------------------------------------------------------------------------------------------------------------
-# Build network topology
-
-node.form("permit-join-test")
+WAIT_TIME = 5
 
 # -----------------------------------------------------------------------------------------------------------------------
 # Test implementation
 
-verify(node.get(wpan.WPAN_NETWORK_ALLOW_JOIN) == 'false')
+node1.allowlist_node(node2)
+node2.allowlist_node(node1)
 
-node.permit_join()
-verify(node.get(wpan.WPAN_NETWORK_ALLOW_JOIN) == 'true')
+node1.form('join-net')
+verify(node1.get_state() == 'leader')
 
-node.permit_join('0')
-verify(node.get(wpan.WPAN_NETWORK_ALLOW_JOIN) == 'false')
+node2.join(node1)
+verify(node2.get_state() == 'router')
 
-node.permit_join(port='1234')
-verify(node.get(wpan.WPAN_NETWORK_ALLOW_JOIN) == 'true')
+node2.interface_down()
 
-node.permit_join('0')
-verify(node.get(wpan.WPAN_NETWORK_ALLOW_JOIN) == 'false')
+node2.join(node1, cli.JOIN_TYPE_END_DEVICE)
+verify(node2.get_state() == 'child')
+verify(node2.get_mode() == 'rn')
 
-# check the timeout
-node.permit_join('1')
-verify(node.get(wpan.WPAN_NETWORK_ALLOW_JOIN) == 'true')
-time.sleep(1.5)
-verify(node.get(wpan.WPAN_NETWORK_ALLOW_JOIN) == 'false')
+node2.interface_down()
+
+node2.join(node1, cli.JOIN_TYPE_SLEEPY_END_DEVICE)
+verify(node2.get_state() == 'child')
+verify(node2.get_mode() == '-')
 
 # -----------------------------------------------------------------------------------------------------------------------
 # Test finished
 
-wpan.Node.finalize_all_nodes()
+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
new file mode 100755
index 0000000..a74d261
--- /dev/null
+++ b/tests/toranj/cli/test-400-srp-client-server.py
@@ -0,0 +1,130 @@
+#!/usr/bin/env python3
+#
+#  Copyright (c) 2021, 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
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Test description: joining (as router, end-device, sleepy) - two node network
+
+test_name = __file__[:-3] if __file__.endswith('.py') else __file__
+print('-' * 120)
+print('Starting \'{}\''.format(test_name))
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Creating `cli.Nodes` instances
+
+speedup = 4
+cli.Node.set_time_speedup_factor(speedup)
+
+server = cli.Node()
+client = cli.Node()
+
+WAIT_TIME = 5
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Test implementation
+
+server.form('srp-test')
+verify(server.get_state() == 'leader')
+
+client.join(server)
+verify(client.get_state() == 'router')
+
+# Check initial state of SRP client and server
+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')
+
+# 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')
+client.srp_client_add_service('ins', '_test._udp', 777, 2, 1)
+
+
+def check_server_has_service():
+    verify(len(server.srp_server_get_hosts()) > 0)
+
+
+verify_within(check_server_has_service, WAIT_TIME)
+
+# Check state and service/host info on client and server
+
+verify(client.srp_client_get_auto_start_mode() == 'Enabled')
+verify(client.srp_client_get_state() == 'Enabled')
+verify(client.srp_client_get_server_address() == server.get_mleid_ip_addr())
+
+verify(client.srp_client_get_host_state() == 'Registered')
+verify(client.srp_client_get_host_name() == 'host')
+addresses = client.srp_client_get_host_address()
+verify(len(addresses) == 1)
+verify(addresses[0] == 'fd00:0:0:0:0:0:0:cafe')
+
+services = client.srp_client_get_services()
+verify(len(services) == 1)
+service = services[0]
+verify(service['instance'] == 'ins')
+verify(service['name'] == '_test._udp')
+verify(service['state'] == 'Registered')
+verify(service['port'] == '777')
+verify(service['priority'] == '2')
+verify(service['weight'] == '1')
+
+verify(server.srp_server_get_state() == 'running')
+
+hosts = server.srp_server_get_hosts()
+verify(len(hosts) == 1)
+host = hosts[0]
+verify(host['name'] == 'host')
+verify(host['deleted'] == 'false')
+verify(host['addresses'] == ['fd00:0:0:0:0:0:0:cafe'])
+
+services = server.srp_server_get_services()
+verify(len(services) == 1)
+service = services[0]
+verify(service['instance'] == 'ins')
+verify(service['name'] == '_test._udp')
+verify(service['deleted'] == 'false')
+verify(service['subtypes'] == '(null)')
+verify(service['port'] == '777')
+verify(service['priority'] == '2')
+verify(service['weight'] == '1')
+verify(service['host'] == 'host')
+verify(service['addresses'] == ['fd00:0:0:0:0:0:0:cafe'])
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Test finished
+
+cli.Node.finalize_all_nodes()
+
+print('\'{}\' passed.'.format(test_name))
diff --git a/tests/toranj/test-001-get-set.py b/tests/toranj/ncp/test-001-get-set.py
similarity index 99%
rename from tests/toranj/test-001-get-set.py
rename to tests/toranj/ncp/test-001-get-set.py
index 2daed01..6dde37a 100644
--- a/tests/toranj/test-001-get-set.py
+++ b/tests/toranj/ncp/test-001-get-set.py
@@ -101,7 +101,6 @@
     wpan.WPAN_PARTITION_ID,
     wpan.WPAN_NCP_VERSION,
     wpan.WPAN_NCP_MCU_POWER_STATE,
-    wpan.WPAN_NETWORK_ALLOW_JOIN,
     wpan.WPAN_NETWORK_PASSTHRU_PORT,
     wpan.WPAN_IP6_LINK_LOCAL_ADDRESS,
     wpan.WPAN_IP6_MESH_LOCAL_ADDRESS,
diff --git a/tests/toranj/test-002-form.py b/tests/toranj/ncp/test-002-form.py
similarity index 100%
rename from tests/toranj/test-002-form.py
rename to tests/toranj/ncp/test-002-form.py
diff --git a/tests/toranj/test-003-join.py b/tests/toranj/ncp/test-003-join.py
similarity index 100%
rename from tests/toranj/test-003-join.py
rename to tests/toranj/ncp/test-003-join.py
diff --git a/tests/toranj/test-004-scan.py b/tests/toranj/ncp/test-004-scan.py
similarity index 100%
rename from tests/toranj/test-004-scan.py
rename to tests/toranj/ncp/test-004-scan.py
diff --git a/tests/toranj/test-005-discover-scan.py b/tests/toranj/ncp/test-005-discover-scan.py
similarity index 100%
rename from tests/toranj/test-005-discover-scan.py
rename to tests/toranj/ncp/test-005-discover-scan.py
diff --git a/tests/toranj/test-006-traffic-router-end-device.py b/tests/toranj/ncp/test-006-traffic-router-end-device.py
similarity index 100%
rename from tests/toranj/test-006-traffic-router-end-device.py
rename to tests/toranj/ncp/test-006-traffic-router-end-device.py
diff --git a/tests/toranj/test-007-traffic-router-sleepy.py b/tests/toranj/ncp/test-007-traffic-router-sleepy.py
similarity index 100%
rename from tests/toranj/test-007-traffic-router-sleepy.py
rename to tests/toranj/ncp/test-007-traffic-router-sleepy.py
diff --git a/tests/toranj/test-009-insecure-traffic-join.py b/tests/toranj/ncp/test-009-insecure-traffic-join.py
similarity index 98%
rename from tests/toranj/test-009-insecure-traffic-join.py
rename to tests/toranj/ncp/test-009-insecure-traffic-join.py
index aa0dfe1..4026737 100644
--- a/tests/toranj/test-009-insecure-traffic-join.py
+++ b/tests/toranj/ncp/test-009-insecure-traffic-join.py
@@ -60,7 +60,6 @@
 
 # Make node1 joinable and set the insecure port
 node1.permit_join(duration_sec='100', port=str(insecure_port))
-verify(node1.get(wpan.WPAN_NETWORK_ALLOW_JOIN) == 'true')
 
 # Join node1 network from node2 without setting the key
 node2.join_node(node1, should_set_key=False)
diff --git a/tests/toranj/test-010-on-mesh-prefix-config-gateway.py b/tests/toranj/ncp/test-010-on-mesh-prefix-config-gateway.py
similarity index 100%
rename from tests/toranj/test-010-on-mesh-prefix-config-gateway.py
rename to tests/toranj/ncp/test-010-on-mesh-prefix-config-gateway.py
diff --git a/tests/toranj/test-011-child-table.py b/tests/toranj/ncp/test-011-child-table.py
similarity index 100%
rename from tests/toranj/test-011-child-table.py
rename to tests/toranj/ncp/test-011-child-table.py
diff --git a/tests/toranj/test-012-multi-hop-traffic.py b/tests/toranj/ncp/test-012-multi-hop-traffic.py
similarity index 100%
rename from tests/toranj/test-012-multi-hop-traffic.py
rename to tests/toranj/ncp/test-012-multi-hop-traffic.py
diff --git a/tests/toranj/test-013-off-mesh-route-traffic.py b/tests/toranj/ncp/test-013-off-mesh-route-traffic.py
similarity index 100%
rename from tests/toranj/test-013-off-mesh-route-traffic.py
rename to tests/toranj/ncp/test-013-off-mesh-route-traffic.py
diff --git a/tests/toranj/test-014-ip6-address-add.py b/tests/toranj/ncp/test-014-ip6-address-add.py
similarity index 100%
rename from tests/toranj/test-014-ip6-address-add.py
rename to tests/toranj/ncp/test-014-ip6-address-add.py
diff --git a/tests/toranj/test-015-same-prefix-on-multiple-nodes.py b/tests/toranj/ncp/test-015-same-prefix-on-multiple-nodes.py
similarity index 100%
rename from tests/toranj/test-015-same-prefix-on-multiple-nodes.py
rename to tests/toranj/ncp/test-015-same-prefix-on-multiple-nodes.py
diff --git a/tests/toranj/test-016-neighbor-table.py b/tests/toranj/ncp/test-016-neighbor-table.py
similarity index 100%
rename from tests/toranj/test-016-neighbor-table.py
rename to tests/toranj/ncp/test-016-neighbor-table.py
diff --git a/tests/toranj/test-017-parent-reset-child-recovery.py b/tests/toranj/ncp/test-017-parent-reset-child-recovery.py
similarity index 100%
rename from tests/toranj/test-017-parent-reset-child-recovery.py
rename to tests/toranj/ncp/test-017-parent-reset-child-recovery.py
diff --git a/tests/toranj/test-018-child-supervision.py b/tests/toranj/ncp/test-018-child-supervision.py
similarity index 100%
rename from tests/toranj/test-018-child-supervision.py
rename to tests/toranj/ncp/test-018-child-supervision.py
diff --git a/tests/toranj/test-019-inform-previous-parent.py b/tests/toranj/ncp/test-019-inform-previous-parent.py
similarity index 100%
rename from tests/toranj/test-019-inform-previous-parent.py
rename to tests/toranj/ncp/test-019-inform-previous-parent.py
diff --git a/tests/toranj/test-020-router-table.py b/tests/toranj/ncp/test-020-router-table.py
similarity index 100%
rename from tests/toranj/test-020-router-table.py
rename to tests/toranj/ncp/test-020-router-table.py
diff --git a/tests/toranj/test-021-address-cache-table.py b/tests/toranj/ncp/test-021-address-cache-table.py
similarity index 100%
rename from tests/toranj/test-021-address-cache-table.py
rename to tests/toranj/ncp/test-021-address-cache-table.py
diff --git a/tests/toranj/test-022-multicast-ip6-address.py b/tests/toranj/ncp/test-022-multicast-ip6-address.py
similarity index 100%
rename from tests/toranj/test-022-multicast-ip6-address.py
rename to tests/toranj/ncp/test-022-multicast-ip6-address.py
diff --git a/tests/toranj/test-023-multicast-traffic.py b/tests/toranj/ncp/test-023-multicast-traffic.py
similarity index 100%
rename from tests/toranj/test-023-multicast-traffic.py
rename to tests/toranj/ncp/test-023-multicast-traffic.py
diff --git a/tests/toranj/test-024-partition-merge.py b/tests/toranj/ncp/test-024-partition-merge.py
similarity index 100%
rename from tests/toranj/test-024-partition-merge.py
rename to tests/toranj/ncp/test-024-partition-merge.py
diff --git a/tests/toranj/test-025-network-data-timeout.py b/tests/toranj/ncp/test-025-network-data-timeout.py
similarity index 100%
rename from tests/toranj/test-025-network-data-timeout.py
rename to tests/toranj/ncp/test-025-network-data-timeout.py
diff --git a/tests/toranj/test-026-slaac-address-wpantund.py b/tests/toranj/ncp/test-026-slaac-address-wpantund.py
similarity index 100%
rename from tests/toranj/test-026-slaac-address-wpantund.py
rename to tests/toranj/ncp/test-026-slaac-address-wpantund.py
diff --git a/tests/toranj/test-027-child-mode-change.py b/tests/toranj/ncp/test-027-child-mode-change.py
similarity index 100%
rename from tests/toranj/test-027-child-mode-change.py
rename to tests/toranj/ncp/test-027-child-mode-change.py
diff --git a/tests/toranj/test-028-router-leader-reset-recovery.py b/tests/toranj/ncp/test-028-router-leader-reset-recovery.py
similarity index 100%
rename from tests/toranj/test-028-router-leader-reset-recovery.py
rename to tests/toranj/ncp/test-028-router-leader-reset-recovery.py
diff --git a/tests/toranj/test-029-data-poll-interval.py b/tests/toranj/ncp/test-029-data-poll-interval.py
similarity index 100%
rename from tests/toranj/test-029-data-poll-interval.py
rename to tests/toranj/ncp/test-029-data-poll-interval.py
diff --git a/tests/toranj/test-030-slaac-address-ncp.py b/tests/toranj/ncp/test-030-slaac-address-ncp.py
similarity index 100%
rename from tests/toranj/test-030-slaac-address-ncp.py
rename to tests/toranj/ncp/test-030-slaac-address-ncp.py
diff --git a/tests/toranj/test-031-meshcop-joiner-commissioner.py b/tests/toranj/ncp/test-031-meshcop-joiner-commissioner.py
similarity index 100%
rename from tests/toranj/test-031-meshcop-joiner-commissioner.py
rename to tests/toranj/ncp/test-031-meshcop-joiner-commissioner.py
diff --git a/tests/toranj/test-032-child-attach-with-multiple-ip-addresses.py b/tests/toranj/ncp/test-032-child-attach-with-multiple-ip-addresses.py
similarity index 100%
rename from tests/toranj/test-032-child-attach-with-multiple-ip-addresses.py
rename to tests/toranj/ncp/test-032-child-attach-with-multiple-ip-addresses.py
diff --git a/tests/toranj/test-033-mesh-local-prefix-change.py b/tests/toranj/ncp/test-033-mesh-local-prefix-change.py
similarity index 100%
rename from tests/toranj/test-033-mesh-local-prefix-change.py
rename to tests/toranj/ncp/test-033-mesh-local-prefix-change.py
diff --git a/tests/toranj/test-034-poor-link-parent-child-attach.py b/tests/toranj/ncp/test-034-poor-link-parent-child-attach.py
similarity index 100%
rename from tests/toranj/test-034-poor-link-parent-child-attach.py
rename to tests/toranj/ncp/test-034-poor-link-parent-child-attach.py
diff --git a/tests/toranj/test-035-child-timeout-large-data-poll.py b/tests/toranj/ncp/test-035-child-timeout-large-data-poll.py
similarity index 100%
rename from tests/toranj/test-035-child-timeout-large-data-poll.py
rename to tests/toranj/ncp/test-035-child-timeout-large-data-poll.py
diff --git a/tests/toranj/test-036-wpantund-host-route-management.py b/tests/toranj/ncp/test-036-wpantund-host-route-management.py
similarity index 100%
rename from tests/toranj/test-036-wpantund-host-route-management.py
rename to tests/toranj/ncp/test-036-wpantund-host-route-management.py
diff --git a/tests/toranj/test-037-wpantund-auto-add-route-for-on-mesh-prefix.py b/tests/toranj/ncp/test-037-wpantund-auto-add-route-for-on-mesh-prefix.py
similarity index 100%
rename from tests/toranj/test-037-wpantund-auto-add-route-for-on-mesh-prefix.py
rename to tests/toranj/ncp/test-037-wpantund-auto-add-route-for-on-mesh-prefix.py
diff --git a/tests/toranj/test-038-clear-address-cache-for-sed.py b/tests/toranj/ncp/test-038-clear-address-cache-for-sed.py
similarity index 100%
rename from tests/toranj/test-038-clear-address-cache-for-sed.py
rename to tests/toranj/ncp/test-038-clear-address-cache-for-sed.py
diff --git a/tests/toranj/test-039-address-cache-table-snoop.py b/tests/toranj/ncp/test-039-address-cache-table-snoop.py
similarity index 100%
rename from tests/toranj/test-039-address-cache-table-snoop.py
rename to tests/toranj/ncp/test-039-address-cache-table-snoop.py
diff --git a/tests/toranj/test-040-network-data-stable-full.py b/tests/toranj/ncp/test-040-network-data-stable-full.py
similarity index 100%
rename from tests/toranj/test-040-network-data-stable-full.py
rename to tests/toranj/ncp/test-040-network-data-stable-full.py
diff --git a/tests/toranj/test-041-lowpan-fragmentation.py b/tests/toranj/ncp/test-041-lowpan-fragmentation.py
similarity index 100%
rename from tests/toranj/test-041-lowpan-fragmentation.py
rename to tests/toranj/ncp/test-041-lowpan-fragmentation.py
diff --git a/tests/toranj/test-042-meshcop-joiner-discerner.py b/tests/toranj/ncp/test-042-meshcop-joiner-discerner.py
similarity index 100%
rename from tests/toranj/test-042-meshcop-joiner-discerner.py
rename to tests/toranj/ncp/test-042-meshcop-joiner-discerner.py
diff --git a/tests/toranj/test-043-meshcop-joiner-router.py b/tests/toranj/ncp/test-043-meshcop-joiner-router.py
similarity index 100%
rename from tests/toranj/test-043-meshcop-joiner-router.py
rename to tests/toranj/ncp/test-043-meshcop-joiner-router.py
diff --git a/tests/toranj/test-100-mcu-power-state.py b/tests/toranj/ncp/test-100-mcu-power-state.py
similarity index 100%
rename from tests/toranj/test-100-mcu-power-state.py
rename to tests/toranj/ncp/test-100-mcu-power-state.py
diff --git a/tests/toranj/test-600-channel-manager-properties.py b/tests/toranj/ncp/test-600-channel-manager-properties.py
similarity index 100%
rename from tests/toranj/test-600-channel-manager-properties.py
rename to tests/toranj/ncp/test-600-channel-manager-properties.py
diff --git a/tests/toranj/test-601-channel-manager-channel-change.py b/tests/toranj/ncp/test-601-channel-manager-channel-change.py
similarity index 100%
rename from tests/toranj/test-601-channel-manager-channel-change.py
rename to tests/toranj/ncp/test-601-channel-manager-channel-change.py
diff --git a/tests/toranj/test-602-channel-manager-channel-select.py b/tests/toranj/ncp/test-602-channel-manager-channel-select.py
similarity index 100%
rename from tests/toranj/test-602-channel-manager-channel-select.py
rename to tests/toranj/ncp/test-602-channel-manager-channel-select.py
diff --git a/tests/toranj/test-603-channel-manager-announce-recovery.py b/tests/toranj/ncp/test-603-channel-manager-announce-recovery.py
similarity index 100%
rename from tests/toranj/test-603-channel-manager-announce-recovery.py
rename to tests/toranj/ncp/test-603-channel-manager-announce-recovery.py
diff --git a/tests/toranj/test-700-multi-radio-join.py b/tests/toranj/ncp/test-700-multi-radio-join.py
similarity index 100%
rename from tests/toranj/test-700-multi-radio-join.py
rename to tests/toranj/ncp/test-700-multi-radio-join.py
diff --git a/tests/toranj/test-701-multi-radio-probe.py b/tests/toranj/ncp/test-701-multi-radio-probe.py
similarity index 100%
rename from tests/toranj/test-701-multi-radio-probe.py
rename to tests/toranj/ncp/test-701-multi-radio-probe.py
diff --git a/tests/toranj/test-702-multi-radio-discovery-by-rx.py b/tests/toranj/ncp/test-702-multi-radio-discovery-by-rx.py
similarity index 100%
rename from tests/toranj/test-702-multi-radio-discovery-by-rx.py
rename to tests/toranj/ncp/test-702-multi-radio-discovery-by-rx.py
diff --git a/tests/toranj/test-703-multi-radio-mesh-header-msg.py b/tests/toranj/ncp/test-703-multi-radio-mesh-header-msg.py
similarity index 100%
rename from tests/toranj/test-703-multi-radio-mesh-header-msg.py
rename to tests/toranj/ncp/test-703-multi-radio-mesh-header-msg.py
diff --git a/tests/toranj/test-704-multi-radio-scan.py b/tests/toranj/ncp/test-704-multi-radio-scan.py
similarity index 100%
rename from tests/toranj/test-704-multi-radio-scan.py
rename to tests/toranj/ncp/test-704-multi-radio-scan.py
diff --git a/tests/toranj/test-705-multi-radio-discover-scan.py b/tests/toranj/ncp/test-705-multi-radio-discover-scan.py
similarity index 100%
rename from tests/toranj/test-705-multi-radio-discover-scan.py
rename to tests/toranj/ncp/test-705-multi-radio-discover-scan.py
diff --git a/tests/toranj/test-nnn-template.py b/tests/toranj/ncp/test-nnn-template.py
similarity index 100%
rename from tests/toranj/test-nnn-template.py
rename to tests/toranj/ncp/test-nnn-template.py
diff --git a/tests/toranj/wpan.py b/tests/toranj/ncp/wpan.py
similarity index 98%
rename from tests/toranj/wpan.py
rename to tests/toranj/ncp/wpan.py
index 283df0a..1dc313a 100644
--- a/tests/toranj/wpan.py
+++ b/tests/toranj/ncp/wpan.py
@@ -56,7 +56,6 @@
 WPAN_PARTITION_ID = 'Network:PartitionId'
 WPAN_NCP_VERSION = 'NCP:Version'
 WPAN_NCP_MCU_POWER_STATE = "NCP:MCUPowerState"
-WPAN_NETWORK_ALLOW_JOIN = 'com.nestlabs.internal:Network:AllowingJoin'
 WPAN_NETWORK_PASSTHRU_PORT = 'com.nestlabs.internal:Network:PassthruPort'
 WPAN_RCP_VERSION = "POSIXApp:RCPVersion"
 
@@ -278,7 +277,7 @@
 RADIO_LINK_TREL_UDP6 = "TREL_UDP6"
 RADIO_LINK_TOBLE = "TOBLE"
 
-_OT_BUILDDIR = os.getenv('top_builddir', '../..')
+_OT_BUILDDIR = os.getenv('top_builddir', '../../..')
 _WPANTUND_PREFIX = os.getenv('WPANTUND_PREFIX', '/usr/local')
 
 # -----------------------------------------------------------------------------------------------------------------------
@@ -568,7 +567,6 @@
         """Checks if node is in the scan results
            `scan_result` must be an array of `ScanResult` object (see `parse_scan_result`).
         """
-        joinable = (self.get(WPAN_NETWORK_ALLOW_JOIN) == 'true')
         panid = self.get(WPAN_PANID)
         xpanid = self.get(WPAN_XPANID)[2:]
         name = self.get(WPAN_NAME)[1:-1]
@@ -576,11 +574,12 @@
         ext_address = self.get(WPAN_EXT_ADDRESS)[1:-1]
 
         for item in scan_result:
-            if all([
-                    item.network_name == name, item.panid == panid, item.xpanid == xpanid,
-                    item.channel == channel, item.ext_address == ext_address,
-                (item.type == ScanResult.TYPE_DISCOVERY_SCAN) or (item.joinable == joinable)
-            ]):
+            if all([item.panid == panid, item.channel == channel, item.ext_address == ext_address]):
+                if (item.type == ScanResult.TYPE_DISCOVERY_SCAN):
+                    if all([item.network_name == name, item.xpanid == xpanid]):
+                        return True
+                    else:
+                        continue
                 return True
 
         return False
@@ -1061,16 +1060,17 @@
 
         items = [item.strip() for item in result_text.split('|')]
 
-        if len(items) == 8:
+        if len(items) == 2:
+            self._type = ScanResult.TYPE_ENERGY_SCAN
+            self._channel = items[0]
+            self._rssi = items[1]
+        if len(items) == 7 and '------ NONE ------' in items[1]:
             self._type = ScanResult.TYPE_ACTIVE_SCAN
             self._index = items[0]
-            self._joinable = (items[1] == 'YES')
-            self._network_name = items[2][1:-1]
-            self._panid = items[3]
-            self._channel = items[4]
-            self._xpanid = items[5]
-            self._ext_address = items[6]
-            self._rssi = items[7]
+            self._panid = items[2]
+            self._channel = items[3]
+            self._ext_address = items[5]
+            self._rssi = items[6]
         elif len(items) == 7:
             self._type = ScanResult.TYPE_DISCOVERY_SCAN
             self._index = items[0]
@@ -1080,12 +1080,8 @@
             self._xpanid = items[4]
             self._ext_address = items[5]
             self._rssi = items[6]
-        elif len(items) == 2:
-            self._type = ScanResult.TYPE_ENERGY_SCAN
-            self._channel = items[0]
-            self._rssi = items[1]
         else:
-            raise ValueError('"{}" does not seem to be a valid scan result string'.result_text)
+            raise ValueError('"%s" does not seem to be a valid scan result string' % result_text)
 
     @property
     def type(self):
diff --git a/tests/toranj/openthread-core-toranj-config.h b/tests/toranj/openthread-core-toranj-config.h
index d2d7ca3..c04317d 100644
--- a/tests/toranj/openthread-core-toranj-config.h
+++ b/tests/toranj/openthread-core-toranj-config.h
@@ -317,14 +317,6 @@
 #define OPENTHREAD_CONFIG_LOG_PREPEND_LEVEL 1
 
 /**
- * @def OPENTHREAD_CONFIG_LOG_PREPEND_REGION
- *
- * Define to prepend the log region to all log messages
- *
- */
-#define OPENTHREAD_CONFIG_LOG_PREPEND_REGION 1
-
-/**
  * @def OPENTHREAD_CONFIG_LOG_SUFFIX
  *
  * Define suffix to append at the end of logs.
diff --git a/tests/toranj/start.sh b/tests/toranj/start.sh
index be232bc..3831afc 100755
--- a/tests/toranj/start.sh
+++ b/tests/toranj/start.sh
@@ -69,8 +69,7 @@
 {
     counter=0
     while true; do
-
-        if sudo -E python "$1"; then
+        if sudo -E "${python_app}" "$1"; then
             cleanup
             return
         fi
@@ -85,7 +84,7 @@
         fi
 
         echo " *** TEST FAILED"
-        tail -n 40 wpantund-logs*.log
+        tail -n 40 "${log_file_name}"*.log
         exit 1
     done
 }
@@ -102,85 +101,103 @@
     coverage_option=""
 fi
 
+if [ "$TORANJ_CLI" = 1 ]; then
+    app_name="cli"
+    python_app="python3"
+    log_file_name="ot-logs"
+else
+    app_name="ncp"
+    python_app="python"
+    log_file_name="wpantund-logs"
+fi
+
 if [ "$TORANJ_RADIO" = "multi" ]; then
     # Build all combinations
-    ./build.sh "${coverage_option}" ncp-15.4 || die "ncp-15.4 build failed"
+    ./build.sh "${coverage_option}" "${app_name}"-15.4 || die "${app_name}-15.4 build failed"
     (cd ${top_builddir} && make clean) || die "cd and clean failed"
-    ./build.sh "${coverage_option}" ncp-trel || die "ncp-trel build failed"
+    ./build.sh "${coverage_option}" "${app_name}"-trel || die "${app_name}-trel build failed"
     (cd ${top_builddir} && make clean) || die "cd and clean failed"
-    ./build.sh "${coverage_option}" ncp-15.4+trel || die "ncp-15.4+trel build failed"
+    ./build.sh "${coverage_option}" "${app_name}"-15.4+trel || die "${app_name}-15.4+trel build failed"
     (cd ${top_builddir} && make clean) || die "cd and clean failed"
 else
-    ./build.sh "${coverage_option}" ncp-"${TORANJ_RADIO}" || die "ncp build failed"
+    ./build.sh "${coverage_option}" "${app_name}"-"${TORANJ_RADIO}" || die "build failed"
 fi
 
 cleanup
 
-if [ "$TORANJ_RADIO" = "multi" ]; then
-    run test-700-multi-radio-join.py
-    run test-701-multi-radio-probe.py
-    run test-702-multi-radio-discovery-by-rx.py
-    run test-703-multi-radio-mesh-header-msg.py
-    run test-704-multi-radio-scan.py
-    run test-705-multi-radio-discover-scan.py
+if [ "$TORANJ_CLI" = 1 ]; then
+    run cli/test-001-get-set.py
+    run cli/test-002-form.py
+    run cli/test-003-join.py
+    run cli/test-400-srp-client-server.py
 
     exit 0
 fi
 
-run test-001-get-set.py
-run test-002-form.py
-run test-003-join.py
-run test-004-scan.py
-run test-005-discover-scan.py
-run test-006-traffic-router-end-device.py
-run test-007-traffic-router-sleepy.py
-run test-008-permit-join.py
-run test-009-insecure-traffic-join.py
-run test-010-on-mesh-prefix-config-gateway.py
-run test-011-child-table.py
-run test-012-multi-hop-traffic.py
-run test-013-off-mesh-route-traffic.py
-run test-014-ip6-address-add.py
-run test-015-same-prefix-on-multiple-nodes.py
-run test-016-neighbor-table.py
-run test-017-parent-reset-child-recovery.py
-run test-018-child-supervision.py
-run test-019-inform-previous-parent.py
-run test-020-router-table.py
-run test-021-address-cache-table.py
-run test-022-multicast-ip6-address.py
-run test-023-multicast-traffic.py
-run test-024-partition-merge.py
-run test-025-network-data-timeout.py
-run test-026-slaac-address-wpantund.py
-run test-027-child-mode-change.py
-run test-028-router-leader-reset-recovery.py
-run test-029-data-poll-interval.py
-run test-030-slaac-address-ncp.py
-run test-031-meshcop-joiner-commissioner.py
-run test-032-child-attach-with-multiple-ip-addresses.py
-run test-033-mesh-local-prefix-change.py
-run test-034-poor-link-parent-child-attach.py
-run test-035-child-timeout-large-data-poll.py
-run test-036-wpantund-host-route-management.py
-run test-037-wpantund-auto-add-route-for-on-mesh-prefix.py
-run test-038-clear-address-cache-for-sed.py
-run test-039-address-cache-table-snoop.py
-run test-040-network-data-stable-full.py
-run test-041-lowpan-fragmentation.py
-run test-042-meshcop-joiner-discerner.py
-run test-043-meshcop-joiner-router.py
-run test-100-mcu-power-state.py
-run test-600-channel-manager-properties.py
-run test-601-channel-manager-channel-change.py
+if [ "$TORANJ_RADIO" = "multi" ]; then
+    run ncp/test-700-multi-radio-join.py
+    run ncp/test-701-multi-radio-probe.py
+    run ncp/test-702-multi-radio-discovery-by-rx.py
+    run ncp/test-703-multi-radio-mesh-header-msg.py
+    run ncp/test-704-multi-radio-scan.py
+    run ncp/test-705-multi-radio-discover-scan.py
+
+    exit 0
+fi
+
+run ncp/test-001-get-set.py
+run ncp/test-002-form.py
+run ncp/test-003-join.py
+run ncp/test-004-scan.py
+run ncp/test-005-discover-scan.py
+run ncp/test-006-traffic-router-end-device.py
+run ncp/test-007-traffic-router-sleepy.py
+run ncp/test-009-insecure-traffic-join.py
+run ncp/test-010-on-mesh-prefix-config-gateway.py
+run ncp/test-011-child-table.py
+run ncp/test-012-multi-hop-traffic.py
+run ncp/test-013-off-mesh-route-traffic.py
+run ncp/test-014-ip6-address-add.py
+run ncp/test-015-same-prefix-on-multiple-nodes.py
+run ncp/test-016-neighbor-table.py
+run ncp/test-017-parent-reset-child-recovery.py
+run ncp/test-018-child-supervision.py
+run ncp/test-019-inform-previous-parent.py
+run ncp/test-020-router-table.py
+run ncp/test-021-address-cache-table.py
+run ncp/test-022-multicast-ip6-address.py
+run ncp/test-023-multicast-traffic.py
+run ncp/test-024-partition-merge.py
+run ncp/test-025-network-data-timeout.py
+run ncp/test-026-slaac-address-wpantund.py
+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-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
+run ncp/test-034-poor-link-parent-child-attach.py
+run ncp/test-035-child-timeout-large-data-poll.py
+run ncp/test-036-wpantund-host-route-management.py
+run ncp/test-037-wpantund-auto-add-route-for-on-mesh-prefix.py
+run ncp/test-038-clear-address-cache-for-sed.py
+run ncp/test-039-address-cache-table-snoop.py
+run ncp/test-040-network-data-stable-full.py
+run ncp/test-041-lowpan-fragmentation.py
+run ncp/test-042-meshcop-joiner-discerner.py
+run ncp/test-043-meshcop-joiner-router.py
+run ncp/test-100-mcu-power-state.py
+run ncp/test-600-channel-manager-properties.py
+run ncp/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.
 
 if [ "$TORANJ_RADIO" != "trel" ]; then
-    run test-602-channel-manager-channel-select.py
+    run ncp/test-602-channel-manager-channel-select.py
 fi
 
-run test-603-channel-manager-announce-recovery.py
+run ncp/test-603-channel-manager-announce-recovery.py
 
 exit 0
diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt
index 8db8fbf..a7d58ed 100644
--- a/tests/unit/CMakeLists.txt
+++ b/tests/unit/CMakeLists.txt
@@ -341,6 +341,27 @@
 
 add_test(NAME ot-test-heap COMMAND ot-test-heap)
 
+add_executable(ot-test-heap-array
+    test_heap_array.cpp
+)
+
+target_include_directories(ot-test-heap-array
+    PRIVATE
+        ${COMMON_INCLUDES}
+)
+
+target_compile_options(ot-test-heap-array
+    PRIVATE
+        ${COMMON_COMPILE_OPTIONS}
+)
+
+target_link_libraries(ot-test-heap-array
+    PRIVATE
+        ${COMMON_LIBS}
+)
+
+add_test(NAME ot-test-heap-array COMMAND ot-test-heap-array)
+
 add_executable(ot-test-heap-string
     test_heap_string.cpp
 )
@@ -410,6 +431,27 @@
 
 add_test(NAME ot-test-hmac-sha256 COMMAND ot-test-hmac-sha256)
 
+add_executable(ot-test-ip6-header
+    test_ip6_header.cpp
+)
+
+target_include_directories(ot-test-ip6-header
+    PRIVATE
+        ${COMMON_INCLUDES}
+)
+
+target_compile_options(ot-test-ip6-header
+    PRIVATE
+        ${COMMON_COMPILE_OPTIONS}
+)
+
+target_link_libraries(ot-test-ip6-header
+    PRIVATE
+        ${COMMON_LIBS}
+)
+
+add_test(NAME ot-test-ip6-header COMMAND ot-test-ip6-header)
+
 add_executable(ot-test-ip-address
     test_ip_address.cpp
 )
@@ -603,6 +645,27 @@
     test_ndproxy_table.cpp
 )
 
+add_executable(ot-test-network-name
+    test_network_name.cpp
+)
+
+target_include_directories(ot-test-network-name
+    PRIVATE
+        ${COMMON_INCLUDES}
+)
+
+target_compile_options(ot-test-network-name
+    PRIVATE
+        ${COMMON_COMPILE_OPTIONS}
+)
+
+target_link_libraries(ot-test-network-name
+    PRIVATE
+        ${COMMON_LIBS}
+)
+
+add_test(NAME ot-test-network-name COMMAND ot-test-network-name)
+
 target_include_directories(ot-test-ndproxy-table
     PRIVATE
         ${COMMON_INCLUDES}
diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am
index ac7df2f..df54384 100644
--- a/tests/unit/Makefile.am
+++ b/tests/unit/Makefile.am
@@ -124,9 +124,11 @@
     ot-test-ecdsa                                                     \
     ot-test-flash                                                     \
     ot-test-heap                                                      \
+    ot-test-heap-array                                                \
     ot-test-heap-string                                               \
     ot-test-hkdf-sha256                                               \
     ot-test-hmac-sha256                                               \
+    ot-test-ip6-header                                                \
     ot-test-ip-address                                                \
     ot-test-link-quality                                              \
     ot-test-linked-list                                               \
@@ -140,6 +142,7 @@
     ot-test-ndproxy-table                                             \
     ot-test-netif                                                     \
     ot-test-network-data                                              \
+    ot-test-network-name                                              \
     ot-test-pool                                                      \
     ot-test-priority-queue                                            \
     ot-test-pskc                                                      \
@@ -243,6 +246,10 @@
 ot_test_heap_LIBTOOLFLAGS           = $(COMMON_LIBTOOLFLAGS)
 ot_test_heap_SOURCES                = $(COMMON_SOURCES) test_heap.cpp
 
+ot_test_heap_array_LDADD           = $(COMMON_LDADD)
+ot_test_heap_array_LIBTOOLFLAGS    = $(COMMON_LIBTOOLFLAGS)
+ot_test_heap_array_SOURCES         = $(COMMON_SOURCES) test_heap_array.cpp
+
 ot_test_heap_string_LDADD           = $(COMMON_LDADD)
 ot_test_heap_string_LIBTOOLFLAGS    = $(COMMON_LIBTOOLFLAGS)
 ot_test_heap_string_SOURCES         = $(COMMON_SOURCES) test_heap_string.cpp
@@ -255,6 +262,10 @@
 ot_test_hmac_sha256_LIBTOOLFLAGS    = $(COMMON_LIBTOOLFLAGS)
 ot_test_hmac_sha256_SOURCES         = $(COMMON_SOURCES) test_hmac_sha256.cpp
 
+ot_test_ip6_header_LDADD            = $(COMMON_LDADD)
+ot_test_ip6_header_LIBTOOLFLAGS     = $(COMMON_LIBTOOLFLAGS)
+ot_test_ip6_header_SOURCES          = $(COMMON_SOURCES) test_ip6_header.cpp
+
 ot_test_ip_address_LDADD            = $(COMMON_LDADD)
 ot_test_ip_address_LIBTOOLFLAGS     = $(COMMON_LIBTOOLFLAGS)
 ot_test_ip_address_SOURCES          = $(COMMON_SOURCES) test_ip_address.cpp
@@ -291,6 +302,10 @@
 ot_test_multicast_listeners_table_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS)
 ot_test_multicast_listeners_table_SOURCES = $(COMMON_SOURCES) test_multicast_listeners_table.cpp
 
+ot_test_network_name_LDADD          = $(COMMON_LDADD)
+ot_test_network_name_LIBTOOLFLAGS   = $(COMMON_LIBTOOLFLAGS)
+ot_test_network_name_SOURCES        = $(COMMON_SOURCES) test_network_name.cpp
+
 ot_test_spinel_buffer_LDADD         = $(COMMON_LDADD)
 ot_test_spinel_buffer_LIBTOOLFLAGS  = $(COMMON_LIBTOOLFLAGS)
 ot_test_spinel_buffer_SOURCES       = $(COMMON_SOURCES) test_spinel_buffer.cpp
diff --git a/tests/unit/test_aes.cpp b/tests/unit/test_aes.cpp
index 6a9f516..ce75b28 100644
--- a/tests/unit/test_aes.cpp
+++ b/tests/unit/test_aes.cpp
@@ -32,7 +32,7 @@
 #include "crypto/aes_ccm.hpp"
 
 #include "test_platform.h"
-#include "test_util.h"
+#include "test_util.hpp"
 
 /**
  * Verifies test vectors from IEEE 802.15.4-2006 Annex C Section C.2.1
@@ -88,7 +88,7 @@
 /**
  * Verifies test vectors from IEEE 802.15.4-2006 Annex C Section C.2.3
  */
-void TestMacCommandFrame()
+void TestMacCommandFrame(void)
 {
     uint8_t key[] = {
         0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
@@ -100,8 +100,9 @@
         0x00, 0x00, 0x01, 0xCE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     };
 
-    uint32_t headerLength = 29, payloadLength = 1;
-    uint8_t  tagLength = 8;
+    static constexpr uint32_t kHeaderLength  = 29;
+    static constexpr uint32_t kPayloadLength = 1;
+    static constexpr uint8_t  kTagLength     = 8;
 
     uint8_t encrypted[] = {
         0x2B, 0xDC, 0x84, 0x21, 0x43, 0x02, 0x00, 0x00, 0x00, 0x00, 0x48, 0xDE, 0xAC,
@@ -119,28 +120,144 @@
         0xAC, 0xDE, 0x48, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x06,
     };
 
+    uint8_t tag[kTagLength];
+
+    ot::Instance *     instance = testInitInstance();
+    ot::Message *      message;
     ot::Crypto::AesCcm aesCcm;
+
+    VerifyOrQuit(instance != nullptr);
+
     aesCcm.SetKey(key, sizeof(key));
-    aesCcm.Init(headerLength, payloadLength, tagLength, nonce, sizeof(nonce));
-    aesCcm.Header(test, headerLength);
-    aesCcm.Payload(test + headerLength, test + headerLength, payloadLength, ot::Crypto::AesCcm::kEncrypt);
-    VerifyOrQuit(aesCcm.GetTagLength() == tagLength);
-    aesCcm.Finalize(test + headerLength + payloadLength);
+    aesCcm.Init(kHeaderLength, kPayloadLength, kTagLength, nonce, sizeof(nonce));
+    aesCcm.Header(test, kHeaderLength);
+    aesCcm.Payload(test + kHeaderLength, test + kHeaderLength, kPayloadLength, ot::Crypto::AesCcm::kEncrypt);
+    VerifyOrQuit(aesCcm.GetTagLength() == kTagLength);
+    aesCcm.Finalize(test + kHeaderLength + kPayloadLength);
     VerifyOrQuit(memcmp(test, encrypted, sizeof(encrypted)) == 0);
 
-    aesCcm.Init(headerLength, payloadLength, tagLength, nonce, sizeof(nonce));
-    aesCcm.Header(test, headerLength);
-    aesCcm.Payload(test + headerLength, test + headerLength, payloadLength, ot::Crypto::AesCcm::kDecrypt);
-    VerifyOrQuit(aesCcm.GetTagLength() == tagLength);
-    aesCcm.Finalize(test + headerLength + payloadLength);
+    aesCcm.Init(kHeaderLength, kPayloadLength, kTagLength, nonce, sizeof(nonce));
+    aesCcm.Header(test, kHeaderLength);
+    aesCcm.Payload(test + kHeaderLength, test + kHeaderLength, kPayloadLength, ot::Crypto::AesCcm::kDecrypt);
+    VerifyOrQuit(aesCcm.GetTagLength() == kTagLength);
+    aesCcm.Finalize(test + kHeaderLength + kPayloadLength);
 
     VerifyOrQuit(memcmp(test, decrypted, sizeof(decrypted)) == 0);
+
+    // Verify encryption/decryption in place within a message.
+
+    message = instance->Get<ot::MessagePool>().Allocate(ot::Message::kTypeIp6);
+    VerifyOrQuit(message != nullptr);
+
+    SuccessOrQuit(message->AppendBytes(test, kHeaderLength + kPayloadLength));
+
+    aesCcm.Init(kHeaderLength, kPayloadLength, kTagLength, nonce, sizeof(nonce));
+    aesCcm.Header(test, kHeaderLength);
+
+    aesCcm.Payload(*message, kHeaderLength, kPayloadLength, ot::Crypto::AesCcm::kEncrypt);
+    VerifyOrQuit(aesCcm.GetTagLength() == kTagLength);
+    aesCcm.Finalize(tag);
+    SuccessOrQuit(message->Append(tag));
+    VerifyOrQuit(message->GetLength() == sizeof(encrypted));
+    VerifyOrQuit(message->Compare(0, encrypted));
+
+    aesCcm.Init(kHeaderLength, kPayloadLength, kTagLength, nonce, sizeof(nonce));
+    aesCcm.Header(test, kHeaderLength);
+    aesCcm.Payload(*message, kHeaderLength, kPayloadLength, ot::Crypto::AesCcm::kDecrypt);
+
+    VerifyOrQuit(message->GetLength() == sizeof(encrypted));
+    VerifyOrQuit(message->Compare(0, decrypted));
+
+    message->Free();
+    testFreeInstance(instance);
+}
+
+/**
+ * Verifies in-place encryption/decryption.
+ *
+ */
+void TestInPlaceAesCcmProcessing(void)
+{
+    static constexpr uint16_t kTagLength    = 4;
+    static constexpr uint32_t kHeaderLength = 19;
+
+    static const uint8_t kKey[] = {
+        0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
+    };
+
+    static const uint8_t kNonce[] = {
+        0xac, 0xde, 0x48, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x06,
+    };
+
+    static uint16_t kMessageLengths[] = {30, 400, 800};
+
+    uint8_t tag[kTagLength];
+    uint8_t header[kHeaderLength];
+
+    ot::Crypto::AesCcm aesCcm;
+    ot::Instance *     instance = testInitInstance();
+    ot::Message *      message;
+    ot::Message *      messageClone;
+
+    VerifyOrQuit(instance != nullptr);
+
+    message = instance->Get<ot::MessagePool>().Allocate(ot::Message::kTypeIp6);
+    VerifyOrQuit(message != nullptr);
+
+    aesCcm.SetKey(kKey, sizeof(kKey));
+
+    for (uint16_t msgLength : kMessageLengths)
+    {
+        printf("msgLength %d\n", msgLength);
+
+        SuccessOrQuit(message->SetLength(0));
+
+        for (uint16_t i = msgLength; i != 0; i--)
+        {
+            SuccessOrQuit(message->Append<uint8_t>(i & 0xff));
+        }
+
+        messageClone = message->Clone();
+        VerifyOrQuit(messageClone != nullptr);
+        VerifyOrQuit(messageClone->GetLength() == msgLength);
+
+        SuccessOrQuit(message->Read(0, header));
+
+        // Encrypt in place
+        aesCcm.Init(kHeaderLength, msgLength - kHeaderLength, kTagLength, kNonce, sizeof(kNonce));
+        aesCcm.Header(header);
+        aesCcm.Payload(*message, kHeaderLength, msgLength - kHeaderLength, ot::Crypto::AesCcm::kEncrypt);
+
+        // Append the tag
+        aesCcm.Finalize(tag);
+        SuccessOrQuit(message->Append(tag));
+
+        VerifyOrQuit(message->GetLength() == msgLength + kTagLength);
+
+        // Decrpt in place
+        aesCcm.Init(kHeaderLength, msgLength - kHeaderLength, kTagLength, kNonce, sizeof(kNonce));
+        aesCcm.Header(header);
+        aesCcm.Payload(*message, kHeaderLength, msgLength - kHeaderLength, ot::Crypto::AesCcm::kDecrypt);
+
+        // Check the tag against what is the message
+        aesCcm.Finalize(tag);
+        VerifyOrQuit(message->Compare(msgLength, tag));
+
+        // Check that decrypted message is the same as original (cloned) message
+        VerifyOrQuit(message->CompareBytes(0, *messageClone, 0, msgLength));
+
+        messageClone->Free();
+    }
+
+    message->Free();
+    testFreeInstance(instance);
 }
 
 int main(void)
 {
     TestMacBeaconFrame();
     TestMacCommandFrame();
+    TestInPlaceAesCcmProcessing();
     printf("All tests passed\n");
     return 0;
 }
diff --git a/tests/unit/test_child_table.cpp b/tests/unit/test_child_table.cpp
index 1ce75fc..3281baa 100644
--- a/tests/unit/test_child_table.cpp
+++ b/tests/unit/test_child_table.cpp
@@ -31,6 +31,7 @@
 #include <openthread/config.h>
 
 #include "test_util.h"
+#include "common/array.hpp"
 #include "common/code_utils.hpp"
 #include "common/instance.hpp"
 #include "thread/child_table.hpp"
@@ -290,7 +291,7 @@
         },
     };
 
-    const uint16_t testListLength = OT_ARRAY_LENGTH(testChildList);
+    const uint16_t testListLength = GetArrayLength(testChildList);
 
     uint16_t testNumAllowedChildren = 2;
 
diff --git a/tests/unit/test_data.cpp b/tests/unit/test_data.cpp
index d1c8948..bd401d9 100644
--- a/tests/unit/test_data.cpp
+++ b/tests/unit/test_data.cpp
@@ -45,8 +45,8 @@
     Data     data;
     Data     data2;
     uint8_t  buffer[sizeof(kData) + 1];
-    uint8_t  u8;
-    uint16_t u16;
+    uint8_t  u8  = 0x12;
+    uint16_t u16 = 0x3456;
 
     data.Clear();
     data2.Clear();
diff --git a/tests/unit/test_dns.cpp b/tests/unit/test_dns.cpp
index f5103b7..074cb9c 100644
--- a/tests/unit/test_dns.cpp
+++ b/tests/unit/test_dns.cpp
@@ -33,6 +33,7 @@
 #include "test_platform.h"
 #include "test_util.hpp"
 
+#include "common/array.hpp"
 #include "common/instance.hpp"
 #include "net/dns_types.hpp"
 
@@ -1095,7 +1096,7 @@
     printf("Use FindRecord() to search for specific records:\n");
     printf(" Answer Section\n");
 
-    for (index = 0; index < OT_ARRAY_LENGTH(kInstanceNames); index++)
+    for (index = 0; index < GetArrayLength(kInstanceNames); index++)
     {
         offset = answerSectionOffset;
         SuccessOrQuit(
@@ -1248,13 +1249,13 @@
     VerifyOrQuit((message = messagePool->Allocate(Message::kTypeIp6)) != nullptr);
 
     data.Init(txtData, sizeof(txtData));
-    SuccessOrQuit(Dns::TxtEntry::AppendEntries(kTxtEntries, OT_ARRAY_LENGTH(kTxtEntries), data));
+    SuccessOrQuit(Dns::TxtEntry::AppendEntries(kTxtEntries, GetArrayLength(kTxtEntries), data));
     VerifyOrQuit(data.GetBytes() == txtData);
     txtDataLength = data.GetLength();
     VerifyOrQuit(txtDataLength < kMaxTxtDataSize, "TXT data is too long");
     DumpBuffer("txt data", txtData, txtDataLength);
 
-    SuccessOrQuit(Dns::TxtEntry::AppendEntries(kTxtEntries, OT_ARRAY_LENGTH(kTxtEntries), *message));
+    SuccessOrQuit(Dns::TxtEntry::AppendEntries(kTxtEntries, GetArrayLength(kTxtEntries), *message));
     VerifyOrQuit(txtDataLength == message->GetLength());
     VerifyOrQuit(message->CompareBytes(0, txtData, txtDataLength));
 
diff --git a/tests/unit/test_ecdsa.cpp b/tests/unit/test_ecdsa.cpp
index 852c20b..533ebcc 100644
--- a/tests/unit/test_ecdsa.cpp
+++ b/tests/unit/test_ecdsa.cpp
@@ -129,7 +129,7 @@
 {
     Instance *instance = testInitInstance();
 
-    const char *kMessage = "You are not a drop in the ocean. You are the entire ocean in a drop.";
+    const char kMessage[] = "You are not a drop in the ocean. You are the entire ocean in a drop.";
 
     Ecdsa::P256::KeyPair   keyPair;
     Ecdsa::P256::PublicKey publicKey;
diff --git a/tests/unit/test_heap_array.cpp b/tests/unit/test_heap_array.cpp
new file mode 100644
index 0000000..2ed9535
--- /dev/null
+++ b/tests/unit/test_heap_array.cpp
@@ -0,0 +1,494 @@
+/*
+ *  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.
+ */
+
+#include "test_platform.h"
+
+#include <string.h>
+
+#include <openthread/config.h>
+
+#include "test_util.hpp"
+
+#include "common/heap_array.hpp"
+#include "common/type_traits.hpp"
+
+namespace ot {
+
+// Counters tracking number of times `Entry` constructor and
+// destructor are invoked. These are used to verify that the `Array`
+// properly calls constructor/destructor when allocating and copying
+// array buffer.
+static uint16_t sConstructorCalls = 0;
+static uint16_t sDestructorCalls  = 0;
+
+class Entry
+{
+public:
+    Entry(void)
+        : mValue(0)
+        , mInitialized(true)
+    {
+        sConstructorCalls++;
+    }
+
+    explicit Entry(uint16_t aValue)
+        : mValue(aValue)
+        , mInitialized(true)
+    {
+        sConstructorCalls++;
+    }
+
+    Entry(const Entry &aEntry)
+        : mValue(aEntry.mValue)
+        , mInitialized(true)
+    {
+        sConstructorCalls++;
+    }
+
+    ~Entry(void) { sDestructorCalls++; }
+
+    uint16_t GetValue(void) const { return mValue; }
+    void     SetValue(uint16_t aValue) { mValue = aValue; }
+    bool     IsInitialized(void) const { return mInitialized; }
+    bool     operator==(const Entry &aOther) const { return mValue == aOther.mValue; }
+    bool     Matches(uint16_t aValue) const { return mValue == aValue; }
+
+private:
+    uint16_t mValue;
+    bool     mInitialized;
+};
+
+template <typename EntryType>
+void VerifyEntry(const EntryType &aEntry, const Heap::Array<EntryType, 2> &aArray, int aExpectedValue)
+{
+    // Verify the entry in a given array with an expected value.
+    // Specializations of this template are defined below for `EntryType`
+    // being `uint16_t` or `Entry` class.
+
+    OT_UNUSED_VARIABLE(aEntry);
+    OT_UNUSED_VARIABLE(aArray);
+    OT_UNUSED_VARIABLE(aExpectedValue);
+
+    VerifyOrQuit(false, "Specializations of this template method MUST be used instead");
+}
+
+template <> void VerifyEntry(const uint16_t &aEntry, const Heap::Array<uint16_t, 2> &aArray, int aExpectedValue)
+{
+    OT_UNUSED_VARIABLE(aArray);
+    VerifyOrQuit(aEntry == static_cast<uint16_t>(aExpectedValue));
+}
+
+template <> void VerifyEntry(const Entry &aEntry, const Heap::Array<Entry, 2> &aArray, int aExpectedValue)
+{
+    VerifyOrQuit(aEntry.IsInitialized());
+    VerifyOrQuit(aEntry.GetValue() == static_cast<uint16_t>(aExpectedValue));
+
+    VerifyOrQuit(aArray.ContainsMatching(aEntry.GetValue()));
+    VerifyOrQuit(aArray.FindMatching(aEntry.GetValue()) == &aEntry);
+}
+
+template <typename EntryType, typename... Args> void VerifyArray(const Heap::Array<EntryType, 2> &aArray, Args... aArgs)
+{
+    // Verify that array content matches the `aArgs` sequence
+    // (which can be empty).
+
+    constexpr uint16_t kUnusedValue = 0xffff;
+
+    int      values[] = {aArgs..., 0};
+    uint16_t index    = 0;
+
+    printf(" - Array (len:%u, capacity:%u) = { ", aArray.GetLength(), aArray.GetCapacity());
+
+    VerifyOrQuit(aArray.GetLength() == sizeof...(aArgs));
+
+    if (aArray.GetLength() == 0)
+    {
+        VerifyOrQuit(aArray.AsCArray() == nullptr);
+        VerifyOrQuit(aArray.Front() == nullptr);
+        VerifyOrQuit(aArray.Back() == nullptr);
+    }
+    else
+    {
+        VerifyOrQuit(aArray.AsCArray() != nullptr);
+    }
+
+    for (const EntryType &entry : aArray)
+    {
+        VerifyOrQuit(index < aArray.GetLength());
+
+        VerifyEntry(entry, aArray, values[index]);
+
+        VerifyOrQuit(aArray.Contains(entry));
+        VerifyOrQuit(aArray.Find(entry) == &entry);
+        VerifyOrQuit(aArray.IndexOf(entry) == index);
+
+        if (index == 0)
+        {
+            VerifyOrQuit(aArray.Front() == &entry);
+        }
+
+        if (index == aArray.GetLength())
+        {
+            VerifyOrQuit(aArray.Back() == &entry);
+        }
+
+        printf("%u ", values[index]);
+
+        index++;
+    }
+
+    VerifyOrQuit(index == aArray.GetLength());
+
+    VerifyOrQuit(!aArray.Contains(EntryType(kUnusedValue)));
+    VerifyOrQuit(aArray.Find(EntryType(kUnusedValue)) == nullptr);
+
+    if (TypeTraits::IsSame<EntryType, Entry>::kValue)
+    {
+        printf("} (constructor-calls:%u, destructor-calls:%u)\n", sConstructorCalls, sDestructorCalls);
+        VerifyOrQuit(sConstructorCalls - sDestructorCalls == aArray.GetLength());
+    }
+    else
+    {
+        printf("}\n");
+    }
+}
+
+void TestHeapArrayOfUint16(void)
+{
+    Heap::Array<uint16_t, 2> array;
+    Heap::Array<uint16_t, 2> array2;
+    uint16_t *               entry;
+
+    printf("\n\n====================================================================================\n");
+    printf("TestHeapArrayOfUint16\n\n");
+
+    printf("------------------------------------------------------------------------------------\n");
+    printf("After constructor\n");
+    VerifyOrQuit(array.GetCapacity() == 0);
+    VerifyArray(array);
+
+    printf("------------------------------------------------------------------------------------\n");
+    printf("PushBack(aEntry)\n");
+
+    SuccessOrQuit(array.PushBack(1));
+    VerifyArray(array, 1);
+    VerifyOrQuit(array.GetCapacity() == 2);
+
+    SuccessOrQuit(array.PushBack(2));
+    VerifyArray(array, 1, 2);
+    VerifyOrQuit(array.GetCapacity() == 2);
+
+    SuccessOrQuit(array.PushBack(3));
+    VerifyArray(array, 1, 2, 3);
+    VerifyOrQuit(array.GetCapacity() == 4);
+
+    printf("------------------------------------------------------------------------------------\n");
+    printf("entry = PushBack()\n");
+
+    entry = array.PushBack();
+    VerifyOrQuit(entry != nullptr);
+    *entry = 4;
+    VerifyArray(array, 1, 2, 3, 4);
+    VerifyOrQuit(array.GetCapacity() == 4);
+
+    entry = array.PushBack();
+    VerifyOrQuit(entry != nullptr);
+    *entry = 5;
+    VerifyArray(array, 1, 2, 3, 4, 5);
+    VerifyOrQuit(array.GetCapacity() == 6);
+
+    printf("------------------------------------------------------------------------------------\n");
+    printf("Clear()\n");
+
+    array.Clear();
+    VerifyArray(array);
+    VerifyOrQuit(array.GetCapacity() == 6);
+
+    *array.PushBack() = 11;
+    SuccessOrQuit(array.PushBack(22));
+    SuccessOrQuit(array.PushBack(33));
+    SuccessOrQuit(array.PushBack(44));
+    *array.PushBack() = 55;
+
+    VerifyArray(array, 11, 22, 33, 44, 55);
+    VerifyOrQuit(array.GetCapacity() == 6);
+
+    SuccessOrQuit(array.PushBack(66));
+    SuccessOrQuit(array.PushBack(77));
+    VerifyArray(array, 11, 22, 33, 44, 55, 66, 77);
+    VerifyOrQuit(array.GetCapacity() == 8);
+
+    printf("------------------------------------------------------------------------------------\n");
+    printf("PopBack()\n");
+
+    array.PopBack();
+    VerifyArray(array, 11, 22, 33, 44, 55, 66);
+    VerifyOrQuit(array.GetCapacity() == 8);
+
+    array.PopBack();
+    array.PopBack();
+    array.PopBack();
+    array.PopBack();
+    array.PopBack();
+    VerifyArray(array, 11);
+    VerifyOrQuit(array.GetCapacity() == 8);
+
+    array.PopBack();
+    VerifyArray(array);
+    VerifyOrQuit(array.GetCapacity() == 8);
+
+    array.PopBack();
+    VerifyArray(array);
+    VerifyOrQuit(array.GetCapacity() == 8);
+
+    for (uint16_t num = 0; num < 11; num++)
+    {
+        SuccessOrQuit(array.PushBack(num + 0x100));
+    }
+
+    VerifyArray(array, 0x100, 0x101, 0x102, 0x103, 0x104, 0x105, 0x106, 0x107, 0x108, 0x109, 0x10a);
+    VerifyOrQuit(array.GetCapacity() == 12);
+
+    printf("------------------------------------------------------------------------------------\n");
+    printf("Free()\n");
+
+    array.Free();
+    VerifyArray(array);
+    VerifyOrQuit(array.GetCapacity() == 0);
+
+    array.Free();
+    VerifyArray(array);
+    VerifyOrQuit(array.GetCapacity() == 0);
+
+    printf("------------------------------------------------------------------------------------\n");
+    printf("ReserveCapacity()\n");
+
+    SuccessOrQuit(array.ReserveCapacity(5));
+    VerifyArray(array);
+    VerifyOrQuit(array.GetCapacity() == 5);
+
+    SuccessOrQuit(array.PushBack(0));
+    VerifyArray(array, 0);
+    VerifyOrQuit(array.GetCapacity() == 5);
+
+    for (uint16_t num = 1; num < 5; num++)
+    {
+        SuccessOrQuit(array.PushBack(num));
+    }
+
+    VerifyArray(array, 0, 1, 2, 3, 4);
+    VerifyOrQuit(array.GetCapacity() == 5);
+
+    SuccessOrQuit(array.PushBack(5));
+    VerifyArray(array, 0, 1, 2, 3, 4, 5);
+    VerifyOrQuit(array.GetCapacity() == 7);
+
+    SuccessOrQuit(array.ReserveCapacity(3));
+    VerifyArray(array, 0, 1, 2, 3, 4, 5);
+    VerifyOrQuit(array.GetCapacity() == 7);
+
+    SuccessOrQuit(array.ReserveCapacity(10));
+    VerifyArray(array, 0, 1, 2, 3, 4, 5);
+    VerifyOrQuit(array.GetCapacity() == 10);
+
+    printf("------------------------------------------------------------------------------------\n");
+    printf("TakeFrom()\n");
+
+    for (uint16_t num = 0; num < 7; num++)
+    {
+        SuccessOrQuit(array2.PushBack(num + 0x20));
+    }
+
+    VerifyArray(array2, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26);
+
+    array2.TakeFrom(static_cast<Heap::Array<uint16_t, 2> &&>(array));
+
+    VerifyArray(array);
+    VerifyOrQuit(array.GetCapacity() == 0);
+
+    VerifyArray(array2, 0, 1, 2, 3, 4, 5);
+    VerifyOrQuit(array2.GetCapacity() == 10);
+
+    printf("\n -- PASS\n");
+}
+
+void TestHeapArray(void)
+{
+    VerifyOrQuit(sConstructorCalls == 0);
+    VerifyOrQuit(sDestructorCalls == 0);
+
+    printf("\n\n====================================================================================\n");
+    printf("TestHeapArray\n\n");
+
+    {
+        Heap::Array<Entry, 2> array;
+        Heap::Array<Entry, 2> array2;
+        Entry *               entry;
+
+        printf("------------------------------------------------------------------------------------\n");
+        printf("After constructor\n");
+        VerifyOrQuit(array.GetCapacity() == 0);
+        VerifyArray(array);
+
+        printf("------------------------------------------------------------------------------------\n");
+        printf("PushBack(aEntry)\n");
+
+        SuccessOrQuit(array.PushBack(Entry(1)));
+        VerifyArray(array, 1);
+        VerifyOrQuit(array.GetCapacity() == 2);
+
+        SuccessOrQuit(array.PushBack(Entry(2)));
+        VerifyArray(array, 1, 2);
+        VerifyOrQuit(array.GetCapacity() == 2);
+
+        SuccessOrQuit(array.PushBack(Entry(3)));
+        VerifyArray(array, 1, 2, 3);
+        VerifyOrQuit(array.GetCapacity() == 4);
+
+        entry = array.PushBack();
+        VerifyOrQuit(entry != nullptr);
+        VerifyOrQuit(entry->IsInitialized());
+        VerifyOrQuit(entry->GetValue() == 0);
+        entry->SetValue(4);
+        VerifyArray(array, 1, 2, 3, 4);
+        VerifyOrQuit(array.GetCapacity() == 4);
+
+        entry = array.PushBack();
+        VerifyOrQuit(entry != nullptr);
+        VerifyOrQuit(entry->IsInitialized());
+        VerifyOrQuit(entry->GetValue() == 0);
+        entry->SetValue(5);
+        VerifyArray(array, 1, 2, 3, 4, 5);
+        VerifyOrQuit(array.GetCapacity() == 6);
+
+        printf("------------------------------------------------------------------------------------\n");
+        printf("PopBack()\n");
+
+        array.PopBack();
+        VerifyArray(array, 1, 2, 3, 4);
+        VerifyOrQuit(array.GetCapacity() == 6);
+
+        array.PopBack();
+        VerifyArray(array, 1, 2, 3);
+        VerifyOrQuit(array.GetCapacity() == 6);
+
+        SuccessOrQuit(array.PushBack(Entry(7)));
+        VerifyArray(array, 1, 2, 3, 7);
+        VerifyOrQuit(array.GetCapacity() == 6);
+
+        array.PopBack();
+        VerifyArray(array, 1, 2, 3);
+        VerifyOrQuit(array.GetCapacity() == 6);
+
+        printf("------------------------------------------------------------------------------------\n");
+        printf("Clear()\n");
+
+        array.Clear();
+        VerifyArray(array);
+        VerifyOrQuit(array.GetCapacity() == 6);
+
+        for (uint16_t num = 0; num < 11; num++)
+        {
+            SuccessOrQuit(array.PushBack(Entry(num)));
+        }
+
+        VerifyArray(array, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
+        VerifyOrQuit(array.GetCapacity() == 12);
+
+        printf("------------------------------------------------------------------------------------\n");
+        printf("Free()\n");
+        array.Free();
+        VerifyArray(array);
+        VerifyOrQuit(array.GetCapacity() == 0);
+
+        printf("------------------------------------------------------------------------------------\n");
+        printf("ReserveCapacity()\n");
+
+        SuccessOrQuit(array.ReserveCapacity(5));
+        VerifyArray(array);
+        VerifyOrQuit(array.GetCapacity() == 5);
+
+        SuccessOrQuit(array.PushBack(Entry(0)));
+        VerifyArray(array, 0);
+        VerifyOrQuit(array.GetCapacity() == 5);
+
+        for (uint16_t num = 1; num < 5; num++)
+        {
+            SuccessOrQuit(array.PushBack(Entry(num)));
+        }
+
+        VerifyArray(array, 0, 1, 2, 3, 4);
+        VerifyOrQuit(array.GetCapacity() == 5);
+
+        SuccessOrQuit(array.PushBack(Entry(5)));
+        VerifyArray(array, 0, 1, 2, 3, 4, 5);
+        VerifyOrQuit(array.GetCapacity() == 7);
+
+        SuccessOrQuit(array.ReserveCapacity(3));
+        VerifyArray(array, 0, 1, 2, 3, 4, 5);
+        VerifyOrQuit(array.GetCapacity() == 7);
+
+        SuccessOrQuit(array.ReserveCapacity(10));
+        VerifyArray(array, 0, 1, 2, 3, 4, 5);
+        VerifyOrQuit(array.GetCapacity() == 10);
+
+        printf("------------------------------------------------------------------------------------\n");
+        printf("TakeFrom()\n");
+
+        for (uint16_t num = 0; num < 7; num++)
+        {
+            SuccessOrQuit(array2.PushBack(Entry(num + 0x20)));
+        }
+
+        array2.TakeFrom(static_cast<Heap::Array<Entry, 2> &&>(array));
+
+        VerifyOrQuit(array.GetLength() == 0);
+        VerifyOrQuit(array.GetCapacity() == 0);
+
+        VerifyArray(array2, 0, 1, 2, 3, 4, 5);
+        VerifyOrQuit(array2.GetCapacity() == 10);
+    }
+
+    printf("------------------------------------------------------------------------------------\n");
+    printf("Array destructor\n");
+    printf(" - (constructor-calls:%u, destructor-calls:%u)\n", sConstructorCalls, sDestructorCalls);
+    VerifyOrQuit(sConstructorCalls == sDestructorCalls,
+                 "Array destructor failed to invoke destructor on all its existing entries");
+
+    printf("\n -- PASS\n");
+}
+
+} // namespace ot
+
+int main(void)
+{
+    ot::TestHeapArrayOfUint16();
+    ot::TestHeapArray();
+    printf("\nAll tests passed.\n");
+    return 0;
+}
diff --git a/tests/unit/test_hkdf_sha256.cpp b/tests/unit/test_hkdf_sha256.cpp
index 08cfb4b..ca12be6 100644
--- a/tests/unit/test_hkdf_sha256.cpp
+++ b/tests/unit/test_hkdf_sha256.cpp
@@ -28,13 +28,14 @@
 
 #include <openthread/config.h>
 
-#include "common/debug.hpp"
-#include "crypto/hkdf_sha256.hpp"
-
 #include "test_platform.h"
 #include "test_util.h"
 #include "test_util.hpp"
 
+#include "common/array.hpp"
+#include "common/debug.hpp"
+#include "crypto/hkdf_sha256.hpp"
+
 struct TestVector
 {
     const uint8_t *mInKey;
@@ -125,7 +126,7 @@
 
     VerifyOrQuit(instance != nullptr);
 
-    for (const TestVector *test = &kTestVectors[0]; test < OT_ARRAY_END(kTestVectors); test++)
+    for (const TestVector *test = &kTestVectors[0]; test < ot::GetArrayEnd(kTestVectors); test++)
     {
         ot::Crypto::HkdfSha256 hkdf;
         uint8_t                outKey[kMaxOuttKey];
diff --git a/tests/unit/test_hmac_sha256.cpp b/tests/unit/test_hmac_sha256.cpp
index 7c67d2a..5c9a978 100644
--- a/tests/unit/test_hmac_sha256.cpp
+++ b/tests/unit/test_hmac_sha256.cpp
@@ -28,6 +28,7 @@
 
 #include <openthread/config.h>
 
+#include "common/array.hpp"
 #include "common/debug.hpp"
 #include "common/message.hpp"
 #include "crypto/hmac_sha256.hpp"
@@ -87,7 +88,7 @@
     Instance *   instance = testInitInstance();
     MessagePool *messagePool;
     Message *    message;
-    uint16_t     offsets[OT_ARRAY_LENGTH(kTestCases)];
+    uint16_t     offsets[GetArrayLength(kTestCases)];
     uint8_t      index;
 
     VerifyOrQuit(instance != nullptr);
@@ -228,7 +229,7 @@
     Instance *   instance = testInitInstance();
     MessagePool *messagePool;
     Message *    message;
-    uint16_t     offsets[OT_ARRAY_LENGTH(kTestCases)];
+    uint16_t     offsets[GetArrayLength(kTestCases)];
     uint8_t      index;
 
     printf("TestHmacSha256\n");
diff --git a/tests/unit/test_ip6_header.cpp b/tests/unit/test_ip6_header.cpp
new file mode 100644
index 0000000..eab543d
--- /dev/null
+++ b/tests/unit/test_ip6_header.cpp
@@ -0,0 +1,142 @@
+/*
+ *  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.
+ */
+
+#include "common/encoding.hpp"
+#include "net/ip6_headers.hpp"
+
+#include "test_util.hpp"
+
+using ot::Encoding::BigEndian::ReadUint16;
+
+namespace ot {
+namespace Ip6 {
+
+void VerifyVersionTcFlow(const Header &aHeader, uint8_t aDscp, Ecn aEcn, uint32_t aFlow)
+{
+    uint8_t  expectedTc        = static_cast<uint8_t>((aDscp << 2) + aEcn);
+    uint32_t expectedVerTcFlow = 0x60000000 + (static_cast<uint32_t>(expectedTc) << 20) + aFlow;
+
+    printf("%08x {dscp:%d, ecn:%d, flow:%d}\n", aHeader.GetVerionTrafficClassFlow(), aHeader.GetDscp(),
+           aHeader.GetEcn(), aHeader.GetFlow());
+
+    VerifyOrQuit(aHeader.IsVersion6());
+    VerifyOrQuit(aHeader.GetDscp() == aDscp);
+    VerifyOrQuit(aHeader.GetEcn() == aEcn);
+    VerifyOrQuit(aHeader.GetFlow() == aFlow);
+    VerifyOrQuit(aHeader.GetTrafficClass() == expectedTc);
+    VerifyOrQuit(aHeader.GetVerionTrafficClassFlow() == expectedVerTcFlow);
+}
+
+void TestIp6Header(void)
+{
+    static constexpr uint16_t kPayloadLength = 650;
+    static constexpr uint8_t  kHopLimit      = 0xd1;
+
+    const uint32_t kFlows[] = {0x0, 0x1, 0xfff, 0xffff, 0xff000, 0xfffff};
+    const uint8_t  kDscps[] = {0x0, 0x1, 0x3, 0xf, 0x30, 0x2f, 0x3f};
+    const Ecn      kEcns[]  = {kEcnNotCapable, kEcnCapable0, kEcnCapable1, kEcnMarked};
+
+    Header         header;
+    Address        source;
+    Address        destination;
+    const uint8_t *headerBytes = reinterpret_cast<const uint8_t *>(&header);
+
+    SuccessOrQuit(source.FromString("0102:0304:0506:0708:090a:0b0c:0d0e:0f12"), "Address::FromString() failed");
+    SuccessOrQuit(destination.FromString("1122:3344:5566::7788:99aa:bbcc:ddee:ff23"), "Address::FromString() failed");
+
+    header.InitVersionTrafficClassFlow();
+    VerifyVersionTcFlow(header, kDscpCs0, kEcnNotCapable, 0);
+
+    header.Clear();
+    header.InitVersionTrafficClassFlow();
+    VerifyOrQuit(header.IsValid());
+    VerifyOrQuit(header.GetPayloadLength() == 0);
+    VerifyOrQuit(header.GetNextHeader() == 0);
+    VerifyOrQuit(header.GetHopLimit() == 0);
+    VerifyOrQuit(header.GetSource().IsUnspecified());
+    VerifyOrQuit(header.GetDestination().IsUnspecified());
+
+    header.SetPayloadLength(kPayloadLength);
+    header.SetNextHeader(kProtoUdp);
+    header.SetHopLimit(kHopLimit);
+    header.SetSource(source);
+    header.SetDestination(destination);
+
+    VerifyOrQuit(header.IsValid());
+    VerifyVersionTcFlow(header, kDscpCs0, kEcnNotCapable, 0);
+    VerifyOrQuit(header.GetPayloadLength() == kPayloadLength);
+    VerifyOrQuit(header.GetNextHeader() == kProtoUdp);
+    VerifyOrQuit(header.GetHopLimit() == kHopLimit);
+    VerifyOrQuit(header.GetSource() == source);
+    VerifyOrQuit(header.GetDestination() == destination);
+
+    // Verify the offsets to different fields.
+
+    VerifyOrQuit(ReadUint16(headerBytes + Header::kPayloadLengthFieldOffset) == kPayloadLength,
+                 "kPayloadLengthFieldOffset is incorrect");
+    VerifyOrQuit(headerBytes[Header::kNextHeaderFieldOffset] == kProtoUdp, "kNextHeaderFieldOffset is incorrect");
+    VerifyOrQuit(headerBytes[Header::kHopLimitFieldOffset] == kHopLimit, "kHopLimitFieldOffset is incorrect");
+    VerifyOrQuit(memcmp(&headerBytes[Header::kSourceFieldOffset], &source, sizeof(source)) == 0,
+                 "kSourceFieldOffset is incorrect");
+    VerifyOrQuit(memcmp(&headerBytes[Header::kDestinationFieldOffset], &destination, sizeof(destination)) == 0,
+                 "kSourceFieldOffset is incorrect");
+
+    for (uint32_t flow : kFlows)
+    {
+        for (uint8_t dscp : kDscps)
+        {
+            for (Ecn ecn : kEcns)
+            {
+                printf("Expecting {dscp:%-2d, ecn:%d, flow:%-7d} => ", dscp, ecn, flow);
+                header.SetEcn(ecn);
+                header.SetDscp(dscp);
+                header.SetFlow(flow);
+                VerifyVersionTcFlow(header, dscp, ecn, flow);
+            }
+        }
+    }
+
+    // Verify out of range values.
+    header.InitVersionTrafficClassFlow();
+
+    header.SetFlow(0xff000001);
+    VerifyVersionTcFlow(header, 0, kEcnNotCapable, 1);
+
+    header.SetDscp(0xef);
+    VerifyVersionTcFlow(header, 0x2f, kEcnNotCapable, 1);
+}
+
+} // namespace Ip6
+} // namespace ot
+
+int main(void)
+{
+    ot::Ip6::TestIp6Header();
+    printf("All tests passed\n");
+    return 0;
+}
diff --git a/tests/unit/test_ip_address.cpp b/tests/unit/test_ip_address.cpp
index b1642eb..09d656d 100644
--- a/tests/unit/test_ip_address.cpp
+++ b/tests/unit/test_ip_address.cpp
@@ -28,15 +28,11 @@
 
 #include <limits.h>
 
-#include "common/encoding.hpp"
 #include "net/ip4_address.hpp"
 #include "net/ip6_address.hpp"
-#include "net/ip6_headers.hpp"
 
 #include "test_util.h"
 
-using ot::Encoding::BigEndian::ReadUint16;
-
 template <typename AddressType> struct TestVector
 {
     const char *  mString;
@@ -407,61 +403,6 @@
     }
 }
 
-void TestIp6Header(void)
-{
-    ot::Ip6::Header  header;
-    ot::Ip6::Address source;
-    ot::Ip6::Address destination;
-    const uint8_t *  headerBytes = reinterpret_cast<const uint8_t *>(&header);
-
-    enum : uint16_t
-    {
-        kPayloadLength = 650,
-    };
-
-    enum : uint8_t
-    {
-        kHopLimit = 0xd1,
-    };
-
-    memset(&header, 0, sizeof(header));
-
-    SuccessOrQuit(source.FromString("0102:0304:0506:0708:090a:0b0c:0d0e:0f12"), "Address::FromString() failed");
-    SuccessOrQuit(destination.FromString("1122:3344:5566::7788:99aa:bbcc:ddee:ff23"), "Address::FromString() failed");
-
-    header.Init();
-    VerifyOrQuit(header.IsVersion6(), "Header::Init() failed");
-
-    header.SetDscp(ot::Ip6::kDscpCs7);
-    header.SetPayloadLength(kPayloadLength);
-    header.SetNextHeader(ot::Ip6::kProtoUdp);
-    header.SetHopLimit(kHopLimit);
-    header.SetSource(source);
-    header.SetDestination(destination);
-
-    VerifyOrQuit(header.IsValid());
-    VerifyOrQuit(header.IsVersion6());
-
-    VerifyOrQuit(header.GetDscp() == ot::Ip6::kDscpCs7);
-    VerifyOrQuit(header.GetPayloadLength() == kPayloadLength);
-    VerifyOrQuit(header.GetNextHeader() == ot::Ip6::kProtoUdp);
-    VerifyOrQuit(header.GetHopLimit() == kHopLimit);
-    VerifyOrQuit(header.GetSource() == source);
-    VerifyOrQuit(header.GetDestination() == destination);
-
-    // Verify the offsets to different fields.
-
-    VerifyOrQuit(ReadUint16(headerBytes + ot::Ip6::Header::kPayloadLengthFieldOffset) == kPayloadLength,
-                 "kPayloadLengthFieldOffset is incorrect");
-    VerifyOrQuit(headerBytes[ot::Ip6::Header::kNextHeaderFieldOffset] == ot::Ip6::kProtoUdp,
-                 "kNextHeaderFieldOffset is incorrect");
-    VerifyOrQuit(headerBytes[ot::Ip6::Header::kHopLimitFieldOffset] == kHopLimit, "kHopLimitFieldOffset is incorrect");
-    VerifyOrQuit(memcmp(&headerBytes[ot::Ip6::Header::kSourceFieldOffset], &source, sizeof(source)) == 0,
-                 "kSourceFieldOffset is incorrect");
-    VerifyOrQuit(memcmp(&headerBytes[ot::Ip6::Header::kDestinationFieldOffset], &destination, sizeof(destination)) == 0,
-                 "kSourceFieldOffset is incorrect");
-}
-
 int main(void)
 {
     TestIp6AddressSetPrefix();
@@ -469,7 +410,6 @@
     TestIp6AddressFromString();
     TestIp6Prefix();
     TestIp4Ip6Translation();
-    TestIp6Header();
     printf("All tests passed\n");
     return 0;
 }
diff --git a/tests/unit/test_link_quality.cpp b/tests/unit/test_link_quality.cpp
index e4c9261..f9162e3 100644
--- a/tests/unit/test_link_quality.cpp
+++ b/tests/unit/test_link_quality.cpp
@@ -26,12 +26,13 @@
  *  POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include "common/code_utils.hpp"
-#include "thread/link_quality.hpp"
-
 #include "test_platform.h"
 #include "test_util.h"
 
+#include "common/array.hpp"
+#include "common/code_utils.hpp"
+#include "thread/link_quality.hpp"
+
 namespace ot {
 
 static ot::Instance *sInstance;
@@ -412,7 +413,7 @@
 
     // Adding success/failure at different rates and checking the RateTracker rate for every sample
 
-    for (uint16_t testRound = 0; testRound < OT_ARRAY_LENGTH(kWeightLimit) * 2; testRound++)
+    for (uint16_t testRound = 0; testRound < GetArrayLength(kWeightLimit) * 2; testRound++)
     {
         uint16_t weightLimit;
         bool     reverseLogic;
diff --git a/tests/unit/test_lowpan.hpp b/tests/unit/test_lowpan.hpp
index 64c52d7..43d1c96 100644
--- a/tests/unit/test_lowpan.hpp
+++ b/tests/unit/test_lowpan.hpp
@@ -117,7 +117,7 @@
                      const char *aSource,
                      const char *aDestination)
     {
-        mIpHeader.Init(aVersionClassFlow);
+        mIpHeader.SetVerionTrafficClassFlow(aVersionClassFlow);
         mIpHeader.SetPayloadLength(aPayloadLength);
         mIpHeader.SetNextHeader(aNextHeader);
         mIpHeader.SetHopLimit(aHopLimit);
@@ -143,7 +143,7 @@
                              const char *aSource,
                              const char *aDestination)
     {
-        mIpTunneledHeader.Init(aVersionClassFlow);
+        mIpTunneledHeader.SetVerionTrafficClassFlow(aVersionClassFlow);
         mIpTunneledHeader.SetPayloadLength(aPayloadLength);
         mIpTunneledHeader.SetNextHeader(aNextHeader);
         mIpTunneledHeader.SetHopLimit(aHopLimit);
diff --git a/tests/unit/test_mac_frame.cpp b/tests/unit/test_mac_frame.cpp
index b5d5796..5aac167 100644
--- a/tests/unit/test_mac_frame.cpp
+++ b/tests/unit/test_mac_frame.cpp
@@ -154,85 +154,6 @@
     testFreeInstance(instance);
 }
 
-void CompareNetworkName(const Mac::NetworkName &aNetworkName, const char *aNameString)
-{
-    uint8_t len = static_cast<uint8_t>(strlen(aNameString));
-
-    VerifyOrQuit(strcmp(aNetworkName.GetAsCString(), aNameString) == 0);
-
-    VerifyOrQuit(aNetworkName.GetAsData().GetLength() == len);
-    VerifyOrQuit(memcmp(aNetworkName.GetAsData().GetBuffer(), aNameString, len) == 0);
-}
-
-void TestMacNetworkName(void)
-{
-    const char kEmptyName[]   = "";
-    const char kName1[]       = "network";
-    const char kName2[]       = "network-name";
-    const char kLongName[]    = "0123456789abcdef";
-    const char kTooLongName[] = "0123456789abcdef0";
-
-    char             buffer[sizeof(kTooLongName) + 2];
-    uint8_t          len;
-    Mac::NetworkName networkName;
-    Mac::NetworkName networkName2;
-
-    CompareNetworkName(networkName, kEmptyName);
-
-    SuccessOrQuit(networkName.Set(Mac::NameData(kName1, sizeof(kName1))));
-    CompareNetworkName(networkName, kName1);
-
-    VerifyOrQuit(networkName.Set(Mac::NameData(kName1, sizeof(kName1))) == kErrorAlready, "failed to detect duplicate");
-    CompareNetworkName(networkName, kName1);
-
-    VerifyOrQuit(networkName.Set(Mac::NameData(kName1, sizeof(kName1) - 1)) == kErrorAlready,
-                 "failed to detect duplicate");
-
-    SuccessOrQuit(networkName.Set(Mac::NameData(kName2, sizeof(kName2))));
-    CompareNetworkName(networkName, kName2);
-
-    VerifyOrQuit(networkName.Set(Mac::NameData(kEmptyName, 0)) == kErrorInvalidArgs);
-
-    SuccessOrQuit(networkName.Set(Mac::NameData(kLongName, sizeof(kLongName))));
-    CompareNetworkName(networkName, kLongName);
-
-    VerifyOrQuit(networkName.Set(Mac::NameData(kLongName, sizeof(kLongName) - 1)) == kErrorAlready,
-                 "failed to detect duplicate");
-
-    VerifyOrQuit(networkName.Set(kEmptyName) == kErrorInvalidArgs);
-
-    SuccessOrQuit(networkName.Set(Mac::NameData(kName1, sizeof(kName1))));
-
-    VerifyOrQuit(networkName.Set(Mac::NameData(kTooLongName, sizeof(kTooLongName))) == kErrorInvalidArgs,
-                 "accepted an invalid (too long) name");
-
-    CompareNetworkName(networkName, kName1);
-
-    memset(buffer, 'a', sizeof(buffer));
-    len = networkName.GetAsData().CopyTo(buffer, 1);
-    VerifyOrQuit(len == 1, "NameData::CopyTo() failed");
-    VerifyOrQuit(buffer[0] == kName1[0], "NameData::CopyTo() failed");
-    VerifyOrQuit(buffer[1] == 'a', "NameData::CopyTo() failed");
-
-    memset(buffer, 'a', sizeof(buffer));
-    len = networkName.GetAsData().CopyTo(buffer, sizeof(kName1) - 1);
-    VerifyOrQuit(len == sizeof(kName1) - 1, "NameData::CopyTo() failed");
-    VerifyOrQuit(memcmp(buffer, kName1, sizeof(kName1) - 1) == 0, "NameData::CopyTo() failed");
-    VerifyOrQuit(buffer[sizeof(kName1)] == 'a', "NameData::CopyTo() failed");
-
-    memset(buffer, 'a', sizeof(buffer));
-    len = networkName.GetAsData().CopyTo(buffer, sizeof(buffer));
-    VerifyOrQuit(len == sizeof(kName1) - 1, "NameData::CopyTo() failed");
-    VerifyOrQuit(memcmp(buffer, kName1, sizeof(kName1) - 1) == 0, "NameData::CopyTo() failed");
-    VerifyOrQuit(buffer[sizeof(kName1)] == 0, "NameData::CopyTo() failed");
-
-    SuccessOrQuit(networkName2.Set(Mac::NameData(kName1, sizeof(kName1))));
-    VerifyOrQuit(networkName == networkName2);
-
-    SuccessOrQuit(networkName2.Set(kName2));
-    VerifyOrQuit(networkName != networkName2);
-}
-
 void TestMacHeader(void)
 {
     static const struct
@@ -650,7 +571,6 @@
 int main(void)
 {
     ot::TestMacAddress();
-    ot::TestMacNetworkName();
     ot::TestMacHeader();
     ot::TestMacChannelMask();
     ot::TestMacFrameApi();
diff --git a/tests/unit/test_message_queue.cpp b/tests/unit/test_message_queue.cpp
index 46031d2..b8cedc6 100644
--- a/tests/unit/test_message_queue.cpp
+++ b/tests/unit/test_message_queue.cpp
@@ -46,9 +46,10 @@
 // This function verifies the content of the message queue to match the passed in messages
 void VerifyMessageQueueContent(ot::MessageQueue &aMessageQueue, int aExpectedLength, ...)
 {
-    va_list      args;
-    ot::Message *message;
-    ot::Message *msgArg;
+    const ot::MessageQueue &constQueue = aMessageQueue;
+    va_list                 args;
+    ot::Message *           message;
+    ot::Message *           msgArg;
 
     va_start(args, aExpectedLength);
 
@@ -69,17 +70,41 @@
             aExpectedLength--;
         }
 
-        VerifyOrQuit(aExpectedLength == 0, "less entries than expected");
+        VerifyOrQuit(aExpectedLength == 0, "fewer entries than expected");
     }
 
     va_end(args);
+
+    // Check range-based `for` loop iteration using non-const iterator
+
+    message = aMessageQueue.GetHead();
+
+    for (ot::Message &msg : aMessageQueue)
+    {
+        VerifyOrQuit(message == &msg, "`for` loop iteration does not match expected");
+        message = message->GetNext();
+    }
+
+    VerifyOrQuit(message == nullptr, "`for` loop iteration resulted in fewer entries than expected");
+
+    // Check  range-base `for` iteration using const iterator
+
+    message = aMessageQueue.GetHead();
+
+    for (const ot::Message &constMsg : constQueue)
+    {
+        VerifyOrQuit(message == &constMsg, "`for` loop iteration does not match expected");
+        message = message->GetNext();
+    }
+
+    VerifyOrQuit(message == nullptr, "`for` loop iteration resulted in fewer entries than expected");
 }
 
 void TestMessageQueue(void)
 {
-    ot::MessageQueue messageQueue;
-    ot::Message *    messages[kNumTestMessages];
-    uint16_t         msgCount, bufferCount;
+    ot::MessageQueue       messageQueue;
+    ot::Message *          messages[kNumTestMessages];
+    ot::MessageQueue::Info info;
 
     sInstance = testInitInstance();
     VerifyOrQuit(sInstance != nullptr);
@@ -119,8 +144,9 @@
     VerifyMessageQueueContent(messageQueue, 5, messages[0], messages[1], messages[2], messages[3], messages[4]);
 
     // Check the GetInfo()
-    messageQueue.GetInfo(msgCount, bufferCount);
-    VerifyOrQuit(msgCount == 5, "MessageQueue::GetInfo() failed.");
+    memset(&info, 0, sizeof(info));
+    messageQueue.GetInfo(info);
+    VerifyOrQuit(info.mNumMessages == 5, "MessageQueue::GetInfo() failed.");
 
     // Remove from head
     messageQueue.Dequeue(*messages[0]);
@@ -174,6 +200,47 @@
     messageQueue.Dequeue(*messages[0]);
     VerifyMessageQueueContent(messageQueue, 0);
 
+    // Range-based `for` and dequeue during iteration
+
+    for (uint16_t removeIndex = 0; removeIndex < 5; removeIndex++)
+    {
+        uint16_t index = 0;
+
+        messageQueue.Enqueue(*messages[0]);
+        messageQueue.Enqueue(*messages[1]);
+        messageQueue.Enqueue(*messages[2]);
+        messageQueue.Enqueue(*messages[3]);
+        messageQueue.Enqueue(*messages[4]);
+        VerifyMessageQueueContent(messageQueue, 5, messages[0], messages[1], messages[2], messages[3], messages[4]);
+
+        // While iterating over the queue remove the entry at `removeIndex`
+        for (ot::Message &message : messageQueue)
+        {
+            if (index == removeIndex)
+            {
+                messageQueue.Dequeue(message);
+            }
+
+            VerifyOrQuit(&message == messages[index++]);
+        }
+
+        index = 0;
+
+        // Iterate over the queue and remove all
+        for (ot::Message &message : messageQueue)
+        {
+            if (index == removeIndex)
+            {
+                index++;
+            }
+
+            VerifyOrQuit(&message == messages[index++]);
+            messageQueue.Dequeue(message);
+        }
+
+        VerifyMessageQueueContent(messageQueue, 0);
+    }
+
     testFreeInstance(sInstance);
 }
 
diff --git a/tests/unit/test_netif.cpp b/tests/unit/test_netif.cpp
index 9ff41f3..d82b051 100644
--- a/tests/unit/test_netif.cpp
+++ b/tests/unit/test_netif.cpp
@@ -168,11 +168,11 @@
     VerifyMulticastAddressList(netif, &addresses[0], 8);
 
     IgnoreError(address.FromString(kTestAddress1)); // same as netifAddress (internal)
-    VerifyOrQuit(netif.UnsubscribeExternalMulticast(address) == kErrorInvalidArgs,
+    VerifyOrQuit(netif.UnsubscribeExternalMulticast(address) == kErrorRejected,
                  "UnsubscribeExternalMulticast() did not fail when address was not external");
 
     IgnoreError(address.FromString(kRealmLocalAllMpl));
-    VerifyOrQuit(netif.UnsubscribeExternalMulticast(address) == kErrorInvalidArgs,
+    VerifyOrQuit(netif.UnsubscribeExternalMulticast(address) == kErrorRejected,
                  "UnsubscribeExternalMulticast() did not fail when address was fixed address");
 
     netif.UnsubscribeAllRoutersMulticast();
diff --git a/tests/unit/test_network_data.cpp b/tests/unit/test_network_data.cpp
index 31438a1..65499c0 100644
--- a/tests/unit/test_network_data.cpp
+++ b/tests/unit/test_network_data.cpp
@@ -28,6 +28,7 @@
 
 #include <openthread/config.h>
 
+#include "common/array.hpp"
 #include "common/code_utils.hpp"
 #include "common/instance.hpp"
 #include "thread/network_data_leader.hpp"
@@ -42,7 +43,7 @@
 
 void PrintExternalRouteConfig(const ExternalRouteConfig &aConfig)
 {
-    printf("\nprefix:");
+    printf("\nroute-prefix:");
 
     for (uint8_t b : aConfig.mPrefix.mPrefix.mFields.m8)
     {
@@ -53,6 +54,19 @@
            aConfig.mRloc16, aConfig.mPreference, aConfig.mNat64, aConfig.mStable, aConfig.mNextHopIsThisDevice);
 }
 
+void PrintOnMeshPrefixConfig(const OnMeshPrefixConfig &aConfig)
+{
+    printf("\non-mesh-prefix:");
+
+    for (uint8_t b : aConfig.mPrefix.mPrefix.mFields.m8)
+    {
+        printf("%02x", b);
+    }
+
+    printf(", length:%d, rloc16:%04x, preference:%d, stable:%d, def-route:%d", aConfig.mPrefix.mLength, aConfig.mRloc16,
+           aConfig.mPreference, aConfig.mStable, aConfig.mDefaultRoute);
+}
+
 // Returns true if the two given ExternalRouteConfig match (intentionally ignoring mNextHopIsThisDevice).
 bool CompareExternalRouteConfig(const otExternalRouteConfig &aConfig1, const otExternalRouteConfig &aConfig2)
 {
@@ -62,11 +76,42 @@
            (aConfig1.mPreference == aConfig2.mPreference) && (aConfig1.mStable == aConfig2.mStable);
 }
 
+// Returns true if the two given OnMeshprefix match.
+bool CompareOnMeshPrefixConfig(const otBorderRouterConfig &aConfig1, const otBorderRouterConfig &aConfig2)
+{
+    return (memcmp(aConfig1.mPrefix.mPrefix.mFields.m8, aConfig2.mPrefix.mPrefix.mFields.m8,
+                   sizeof(aConfig1.mPrefix.mPrefix)) == 0) &&
+           (aConfig1.mPrefix.mLength == aConfig2.mPrefix.mLength) && (aConfig1.mRloc16 == aConfig2.mRloc16) &&
+           (aConfig1.mPreference == aConfig2.mPreference) && (aConfig1.mStable == aConfig2.mStable) &&
+           (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])
+{
+    VerifyOrQuit(aRlocsLength == kLength);
+
+    printf("\nRLOCs: { ");
+
+    for (uint8_t index = 0; index < aRlocsLength; index++)
+    {
+        VerifyOrQuit(aRlocs[index] == aExpectedRlocs[index]);
+        printf("0x%04x ", aRlocs[index]);
+    }
+
+    printf("}");
+}
+
 void TestNetworkDataIterator(void)
 {
+    static constexpr uint8_t kMaxRlocsArray = 10;
+
     ot::Instance *      instance;
     Iterator            iter = kIteratorInit;
-    ExternalRouteConfig config;
+    ExternalRouteConfig rconfig;
+    OnMeshPrefixConfig  pconfig;
+    uint16_t            rlocs[kMaxRlocsArray];
+    uint8_t             rlocsLength;
 
     instance = testInitInstance();
     VerifyOrQuit(instance != nullptr);
@@ -104,6 +149,8 @@
             },
         };
 
+        const uint16_t kRlocs[] = {0xc800, 0x5400};
+
         NetworkData netData(*instance, kNetworkData, sizeof(kNetworkData));
 
         iter = OT_NETWORK_DATA_ITERATOR_INIT;
@@ -113,10 +160,25 @@
 
         for (const auto &route : routes)
         {
-            SuccessOrQuit(netData.GetNextExternalRoute(iter, config));
-            PrintExternalRouteConfig(config);
-            VerifyOrQuit(CompareExternalRouteConfig(config, route));
+            SuccessOrQuit(netData.GetNextExternalRoute(iter, rconfig));
+            PrintExternalRouteConfig(rconfig);
+            VerifyOrQuit(CompareExternalRouteConfig(rconfig, route));
         }
+
+        rlocsLength = GetArrayLength(rlocs);
+        SuccessOrQuit(netData.FindBorderRouters(kAnyRole, rlocs, rlocsLength));
+        VerifyRlocsArray(rlocs, rlocsLength, kRlocs);
+        VerifyOrQuit(netData.CountBorderRouters(kAnyRole) == GetArrayLength(kRlocs));
+
+        rlocsLength = GetArrayLength(rlocs);
+        SuccessOrQuit(netData.FindBorderRouters(kRouterRoleOnly, rlocs, rlocsLength));
+        VerifyRlocsArray(rlocs, rlocsLength, kRlocs);
+        VerifyOrQuit(netData.CountBorderRouters(kRouterRoleOnly) == GetArrayLength(kRlocs));
+
+        rlocsLength = GetArrayLength(rlocs);
+        SuccessOrQuit(netData.FindBorderRouters(kChildRoleOnly, rlocs, rlocsLength));
+        VerifyOrQuit(rlocsLength == 0);
+        VerifyOrQuit(netData.CountBorderRouters(kChildRoleOnly) == 0);
     }
 
     {
@@ -124,7 +186,7 @@
             0x08, 0x04, 0x0B, 0x02, 0x00, 0x00, 0x03, 0x1E, 0x00, 0x40, 0xFD, 0x00, 0x12, 0x34, 0x56, 0x78, 0x00, 0x00,
             0x07, 0x02, 0x11, 0x40, 0x00, 0x03, 0x10, 0x00, 0x40, 0x01, 0x03, 0x54, 0x00, 0x00, 0x05, 0x04, 0x54, 0x00,
             0x31, 0x00, 0x02, 0x0F, 0x00, 0x40, 0xFD, 0x00, 0xAB, 0xBA, 0xCD, 0xDC, 0x00, 0x00, 0x00, 0x03, 0x10, 0x00,
-            0x20, 0x03, 0x0E, 0x00, 0x20, 0xFD, 0x00, 0xAB, 0xBA, 0x01, 0x06, 0x54, 0x00, 0x00, 0x04, 0x00, 0x00,
+            0x20, 0x03, 0x0E, 0x00, 0x20, 0xFD, 0x00, 0xAB, 0xBA, 0x01, 0x06, 0x54, 0x00, 0x00, 0x04, 0x01, 0x00,
         };
 
         otExternalRouteConfig routes[] = {
@@ -182,7 +244,7 @@
                        0x00}}},
                     32,
                 },
-                0x0400, // mRloc16
+                0x0401, // mRloc16
                 0,      // mPreference
                 false,  // mNat64
                 true,   // mStable
@@ -190,6 +252,10 @@
             },
         };
 
+        const uint16_t kRlocsAnyRole[]    = {0x1000, 0x5400, 0x0401};
+        const uint16_t kRlocsRouterRole[] = {0x1000, 0x5400};
+        const uint16_t kRlocsChildRole[]  = {0x0401};
+
         NetworkData netData(*instance, kNetworkData, sizeof(kNetworkData));
 
         iter = OT_NETWORK_DATA_ITERATOR_INIT;
@@ -199,10 +265,192 @@
 
         for (const auto &route : routes)
         {
-            SuccessOrQuit(netData.GetNextExternalRoute(iter, config));
-            PrintExternalRouteConfig(config);
-            VerifyOrQuit(CompareExternalRouteConfig(config, route));
+            SuccessOrQuit(netData.GetNextExternalRoute(iter, rconfig));
+            PrintExternalRouteConfig(rconfig);
+            VerifyOrQuit(CompareExternalRouteConfig(rconfig, route));
         }
+
+        rlocsLength = GetArrayLength(rlocs);
+        SuccessOrQuit(netData.FindBorderRouters(kAnyRole, rlocs, rlocsLength));
+        VerifyRlocsArray(rlocs, rlocsLength, kRlocsAnyRole);
+        VerifyOrQuit(netData.CountBorderRouters(kAnyRole) == GetArrayLength(kRlocsAnyRole));
+
+        rlocsLength = GetArrayLength(rlocs);
+        SuccessOrQuit(netData.FindBorderRouters(kRouterRoleOnly, rlocs, rlocsLength));
+        VerifyRlocsArray(rlocs, rlocsLength, kRlocsRouterRole);
+        VerifyOrQuit(netData.CountBorderRouters(kRouterRoleOnly) == GetArrayLength(kRlocsRouterRole));
+
+        rlocsLength = GetArrayLength(rlocs);
+        SuccessOrQuit(netData.FindBorderRouters(kChildRoleOnly, rlocs, rlocsLength));
+        VerifyRlocsArray(rlocs, rlocsLength, 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);
+    }
+
+    {
+        const uint8_t kNetworkData[] = {
+            0x08, 0x04, 0x0b, 0x02, 0x36, 0xcc, 0x03, 0x1c, 0x00, 0x40, 0xfd, 0x00, 0xbe, 0xef, 0xca, 0xfe,
+            0x00, 0x00, 0x05, 0x0c, 0x28, 0x00, 0x33, 0x00, 0x28, 0x01, 0x33, 0x00, 0x4c, 0x00, 0x31, 0x00,
+            0x07, 0x02, 0x11, 0x40, 0x03, 0x14, 0x00, 0x40, 0xfd, 0x00, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00,
+            0x05, 0x04, 0x28, 0x00, 0x73, 0x00, 0x07, 0x02, 0x12, 0x40, 0x03, 0x12, 0x00, 0x40, 0xfd, 0x00,
+            0x33, 0x33, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0xec, 0x00, 0x00, 0x28, 0x01, 0xc0,
+        };
+
+        otExternalRouteConfig routes[] = {
+            {
+                {
+                    {{{0xfd, 0x00, 0x33, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                       0x00}}},
+                    64,
+                },
+                0xec00, // mRloc16
+                0,      // mPreference
+                false,  // mNat64
+                true,   // mStable
+                false,  // mNextHopIsThisDevice
+            },
+            {
+                {
+                    {{{0xfd, 0x00, 0x33, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                       0x00}}},
+                    64,
+                },
+                0x2801, // mRloc16
+                -1,     // mPreference
+                false,  // mNat64
+                true,   // mStable
+                false,  // mNextHopIsThisDevice
+            },
+        };
+
+        otBorderRouterConfig prefixes[] = {
+            {
+                {
+                    {{{0xfd, 0x00, 0xbe, 0xef, 0xca, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                       0x00}}},
+                    64,
+                },
+                0,      // mPreference
+                true,   // mPreferred
+                true,   // mSlaac
+                false,  // mDhcp
+                true,   // mConfigure
+                true,   // mDefaultRoute
+                true,   // mOnMesh
+                true,   // mStable
+                false,  // mNdDns
+                false,  // mDp
+                0x2800, // mRloc16
+            },
+            {
+                {
+                    {{{0xfd, 0x00, 0xbe, 0xef, 0xca, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                       0x00}}},
+                    64,
+                },
+                0,      // mPreference
+                true,   // mPreferred
+                true,   // mSlaac
+                false,  // mDhcp
+                true,   // mConfigure
+                true,   // mDefaultRoute
+                true,   // mOnMesh
+                true,   // mStable
+                false,  // mNdDns
+                false,  // mDp
+                0x2801, // mRloc16
+            },
+            {
+                {
+                    {{{0xfd, 0x00, 0xbe, 0xef, 0xca, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                       0x00}}},
+                    64,
+                },
+                0,      // mPreference
+                true,   // mPreferred
+                true,   // mSlaac
+                false,  // mDhcp
+                true,   // mConfigure
+                false,  // mDefaultRoute
+                true,   // mOnMesh
+                true,   // mStable
+                false,  // mNdDns
+                false,  // mDp
+                0x4c00, // mRloc16
+            },
+            {
+                {
+                    {{{0xfd, 0x00, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                       0x00}}},
+                    64,
+                },
+                1,      // mPreference
+                true,   // mPreferred
+                true,   // mSlaac
+                false,  // mDhcp
+                true,   // mConfigure
+                true,   // mDefaultRoute
+                true,   // mOnMesh
+                true,   // mStable
+                false,  // mNdDns
+                false,  // mDp
+                0x2800, // mRloc16
+            },
+        };
+
+        const uint16_t kRlocsAnyRole[]    = {0xec00, 0x2801, 0x2800};
+        const uint16_t kRlocsRouterRole[] = {0xec00, 0x2800};
+        const uint16_t kRlocsChildRole[]  = {0x2801};
+
+        NetworkData netData(*instance, kNetworkData, sizeof(kNetworkData));
+
+        printf("\nTest #3: Network data 3");
+        printf("\n-------------------------------------------------");
+
+        iter = OT_NETWORK_DATA_ITERATOR_INIT;
+
+        for (const auto &route : routes)
+        {
+            SuccessOrQuit(netData.GetNextExternalRoute(iter, rconfig));
+            PrintExternalRouteConfig(rconfig);
+            VerifyOrQuit(CompareExternalRouteConfig(rconfig, route));
+        }
+
+        iter = OT_NETWORK_DATA_ITERATOR_INIT;
+
+        for (const auto &prefix : prefixes)
+        {
+            SuccessOrQuit(netData.GetNextOnMeshPrefix(iter, pconfig));
+            PrintOnMeshPrefixConfig(pconfig);
+            VerifyOrQuit(CompareOnMeshPrefixConfig(pconfig, prefix));
+        }
+
+        rlocsLength = GetArrayLength(rlocs);
+        SuccessOrQuit(netData.FindBorderRouters(kAnyRole, rlocs, rlocsLength));
+        VerifyRlocsArray(rlocs, rlocsLength, kRlocsAnyRole);
+        VerifyOrQuit(netData.CountBorderRouters(kAnyRole) == GetArrayLength(kRlocsAnyRole));
+
+        rlocsLength = GetArrayLength(rlocs);
+        SuccessOrQuit(netData.FindBorderRouters(kRouterRoleOnly, rlocs, rlocsLength));
+        VerifyRlocsArray(rlocs, rlocsLength, kRlocsRouterRole);
+        VerifyOrQuit(netData.CountBorderRouters(kRouterRoleOnly) == GetArrayLength(kRlocsRouterRole));
+
+        rlocsLength = GetArrayLength(rlocs);
+        SuccessOrQuit(netData.FindBorderRouters(kChildRoleOnly, rlocs, rlocsLength));
+        VerifyRlocsArray(rlocs, rlocsLength, kRlocsChildRole);
+        VerifyOrQuit(netData.CountBorderRouters(kChildRoleOnly) == GetArrayLength(kRlocsChildRole));
     }
 
     testFreeInstance(instance);
@@ -318,6 +566,11 @@
 
 void TestNetworkDataDsnSrpServices(void)
 {
+    static const char *kOriginStrings[] = {
+        "service-data", // (0) Service::DnsSrpUnicast::kFromServiceData
+        "server-data",  // (1) Service::DnsSrpUnicast::kFromServerData
+    };
+
     class TestLeader : public Leader
     {
     public:
@@ -353,8 +606,9 @@
 
         struct UnicastEntry
         {
-            const char *mAddress;
-            uint16_t    mPort;
+            const char *                   mAddress;
+            uint16_t                       mPort;
+            Service::DnsSrpUnicast::Origin mOrigin;
 
             bool Matches(Service::DnsSrpUnicast::Info aInfo) const
             {
@@ -363,7 +617,7 @@
                 SuccessOrQuit(sockAddr.GetAddress().FromString(mAddress));
                 sockAddr.SetPort(mPort);
 
-                return (aInfo.mSockAddr == sockAddr);
+                return (aInfo.mSockAddr == sockAddr) && (aInfo.mOrigin == mOrigin);
             }
         };
 
@@ -384,9 +638,11 @@
         };
 
         const UnicastEntry kUnicastEntries[] = {
-            {"fdde:ad00:beef:0:2d0e:c627:5556:18d9", 0x1234}, {"fd00:aabb:ccdd:eeff:11:2233:4455:6677", 0xabcd},
-            {"fdde:ad00:beef:0:0:ff:fe00:2800", 0x5678},      {"fd00:1234:5678:9abc:def0:123:4567:89ab", 0x0e},
-            {"fdde:ad00:beef:0:0:ff:fe00:6c00", 0xcd12},
+            {"fdde:ad00:beef:0:2d0e:c627:5556:18d9", 0x1234, Service::DnsSrpUnicast::kFromServiceData},
+            {"fd00:aabb:ccdd:eeff:11:2233:4455:6677", 0xabcd, Service::DnsSrpUnicast::kFromServerData},
+            {"fdde:ad00:beef:0:0:ff:fe00:2800", 0x5678, Service::DnsSrpUnicast::kFromServerData},
+            {"fd00:1234:5678:9abc:def0:123:4567:89ab", 0x0e, Service::DnsSrpUnicast::kFromServerData},
+            {"fdde:ad00:beef:0:0:ff:fe00:6c00", 0xcd12, Service::DnsSrpUnicast::kFromServerData},
         };
 
         const uint8_t kPreferredAnycastEntryIndex = 2;
@@ -436,7 +692,8 @@
         for (const UnicastEntry &entry : kUnicastEntries)
         {
             SuccessOrQuit(manager.GetNextDnsSrpUnicastInfo(iterator, unicastInfo));
-            printf("\nunicastInfo %s", unicastInfo.mSockAddr.ToString().AsCString());
+            printf("\nunicastInfo { %s, origin:%s }", unicastInfo.mSockAddr.ToString().AsCString(),
+                   kOriginStrings[unicastInfo.mOrigin]);
 
             VerifyOrQuit(entry.Matches(unicastInfo), "GetNextDnsSrpUnicastInfo() returned incorrect info");
         }
@@ -450,6 +707,187 @@
     testFreeInstance(instance);
 }
 
+void TestNetworkDataDsnSrpAnycastSeqNumSelection(void)
+{
+    class TestLeader : public Leader
+    {
+    public:
+        void Populate(const uint8_t *aTlvs, uint8_t aTlvsLength)
+        {
+            memcpy(GetBytes(), aTlvs, aTlvsLength);
+            SetLength(aTlvsLength);
+        }
+    };
+
+    struct TestInfo
+    {
+        const uint8_t *mNetworkData;
+        uint8_t        mNetworkDataLength;
+        const uint8_t *mSeqNumbers;
+        uint8_t        mSeqNumbersLength;
+        uint8_t        mPreferredSeqNum;
+    };
+
+    ot::Instance *instance;
+
+    printf("\n\n-------------------------------------------------");
+    printf("\nTestNetworkDataDsnSrpAnycastSeqNumSelection()\n");
+
+    instance = testInitInstance();
+    VerifyOrQuit(instance != nullptr);
+
+    const uint8_t kNetworkData1[] = {
+        0x08, 0x04, 0x0b, 0x02, 0x50, 0xb0,                         // Service TLV
+        0x0b, 0x08, 0x80, 0x02, 0x5c, 0x01, 0x0d, 0x02, 0x50, 0x00, // Server sub-TLV
+        0x0b, 0x08, 0x81, 0x02, 0x5c, 0x81, 0x0d, 0x02, 0x50, 0x01, // Server sub-TLV
+    };
+    const uint8_t kSeqNumbers1[]    = {1, 129};
+    const uint8_t kPreferredSeqNum1 = 129;
+
+    const uint8_t kNetworkData2[] = {
+        0x08, 0x04, 0x0b, 0x02, 0x50, 0xb0,                         // Service TLV
+        0x0b, 0x08, 0x80, 0x02, 0x5c, 0x85, 0x0d, 0x02, 0x50, 0x00, // Server sub-TLV
+        0x0b, 0x08, 0x81, 0x02, 0x5c, 0x05, 0x0d, 0x02, 0x50, 0x01, // Server sub-TLV
+    };
+    const uint8_t kSeqNumbers2[]    = {133, 5};
+    const uint8_t kPreferredSeqNum2 = 133;
+
+    const uint8_t kNetworkData3[] = {
+        0x08, 0x04, 0x0b, 0x02, 0x50, 0xb0,                         // Service TLV
+        0x0b, 0x08, 0x80, 0x02, 0x5c, 0x01, 0x0d, 0x02, 0x50, 0x00, // Server sub-TLV
+        0x0b, 0x08, 0x81, 0x02, 0x5c, 0x02, 0x0d, 0x02, 0x50, 0x01, // Server sub-TLV
+        0x0b, 0x08, 0x82, 0x02, 0x5c, 0xff, 0x0d, 0x02, 0x50, 0x02, // Server sub-TLV
+    };
+    const uint8_t kSeqNumbers3[]    = {1, 2, 255};
+    const uint8_t kPreferredSeqNum3 = 2;
+
+    const uint8_t kNetworkData4[] = {
+        0x08, 0x04, 0x0b, 0x02, 0x50, 0xb0,                         // Service TLV
+        0x0b, 0x08, 0x80, 0x02, 0x5c, 0x0a, 0x0d, 0x02, 0x50, 0x00, // Server sub-TLV
+        0x0b, 0x08, 0x81, 0x02, 0x5c, 0x82, 0x0d, 0x02, 0x50, 0x01, // Server sub-TLV
+        0x0b, 0x08, 0x82, 0x02, 0x5c, 0xfa, 0x0d, 0x02, 0x50, 0x02, // Server sub-TLV
+    };
+    const uint8_t kSeqNumbers4[]    = {10, 130, 250};
+    const uint8_t kPreferredSeqNum4 = 250;
+
+    const uint8_t kNetworkData5[] = {
+        0x08, 0x04, 0x0b, 0x02, 0x50, 0xb0,                         // Service TLV
+        0x0b, 0x08, 0x80, 0x02, 0x5c, 0x82, 0x0d, 0x02, 0x50, 0x00, // Server sub-TLV
+        0x0b, 0x08, 0x81, 0x02, 0x5c, 0xfa, 0x0d, 0x02, 0x50, 0x01, // Server sub-TLV
+        0x0b, 0x08, 0x82, 0x02, 0x5c, 0x0a, 0x0d, 0x02, 0x50, 0x02, // Server sub-TLV
+    };
+    const uint8_t kSeqNumbers5[]    = {130, 250, 10};
+    const uint8_t kPreferredSeqNum5 = 250;
+
+    const uint8_t kNetworkData6[] = {
+        0x08, 0x04, 0x0b, 0x02, 0x50, 0xb0,                         // Service TLV
+        0x0b, 0x08, 0x80, 0x02, 0x5c, 0xfa, 0x0d, 0x02, 0x50, 0x00, // Server sub-TLV
+        0x0b, 0x08, 0x81, 0x02, 0x5c, 0x0a, 0x0d, 0x02, 0x50, 0x01, // Server sub-TLV
+        0x0b, 0x08, 0x82, 0x02, 0x5c, 0x82, 0x0d, 0x02, 0x50, 0x02, // Server sub-TLV
+    };
+    const uint8_t kSeqNumbers6[]    = {250, 10, 130};
+    const uint8_t kPreferredSeqNum6 = 250;
+
+    const uint8_t kNetworkData7[] = {
+        0x08, 0x04, 0x0b, 0x02, 0x50, 0xb0,                         // Service TLV
+        0x0b, 0x08, 0x80, 0x02, 0x5c, 0xfa, 0x0d, 0x02, 0x50, 0x00, // Server sub-TLV
+        0x0b, 0x08, 0x81, 0x02, 0x5c, 0x0a, 0x0d, 0x02, 0x50, 0x01, // Server sub-TLV
+        0x0b, 0x08, 0x82, 0x02, 0x5c, 0x8A, 0x0d, 0x02, 0x50, 0x02, // Server sub-TLV
+    };
+    const uint8_t kSeqNumbers7[]    = {250, 10, 138};
+    const uint8_t kPreferredSeqNum7 = 250;
+
+    const uint8_t kNetworkData8[] = {
+        0x08, 0x04, 0x0b, 0x02, 0x50, 0xb0,                         // Service TLV
+        0x0b, 0x08, 0x80, 0x02, 0x5c, 0x01, 0x0d, 0x02, 0x50, 0x00, // Server sub-TLV
+        0x0b, 0x08, 0x81, 0x02, 0x5c, 0x02, 0x0d, 0x02, 0x50, 0x01, // Server sub-TLV
+        0x0b, 0x08, 0x82, 0x02, 0x5c, 0xff, 0x0d, 0x02, 0x50, 0x02, // Server sub-TLV
+        0x0b, 0x08, 0x83, 0x02, 0x5c, 0xfe, 0x0d, 0x02, 0x50, 0x03, // Server sub-TLV
+
+    };
+    const uint8_t kSeqNumbers8[]    = {1, 2, 255, 254};
+    const uint8_t kPreferredSeqNum8 = 2;
+
+    const uint8_t kNetworkData9[] = {
+        0x08, 0x04, 0x0b, 0x02, 0x50, 0xb0,                         // Service TLV
+        0x0b, 0x08, 0x80, 0x02, 0x5c, 0x01, 0x0d, 0x02, 0x50, 0x00, // Server sub-TLV
+        0x0b, 0x08, 0x81, 0x02, 0x5c, 0x02, 0x0d, 0x02, 0x50, 0x01, // Server sub-TLV
+        0x0b, 0x08, 0x82, 0x02, 0x5c, 0xff, 0x0d, 0x02, 0x50, 0x02, // Server sub-TLV
+        0x0b, 0x08, 0x83, 0x02, 0x5c, 0xfe, 0x0d, 0x02, 0x50, 0x03, // Server sub-TLV
+
+    };
+    const uint8_t kSeqNumbers9[]    = {1, 2, 255, 254};
+    const uint8_t kPreferredSeqNum9 = 2;
+
+    const uint8_t kNetworkData10[] = {
+        0x08, 0x04, 0x0b, 0x02, 0x50, 0xb0,                         // Service TLV
+        0x0b, 0x08, 0x80, 0x02, 0x5c, 0xfe, 0x0d, 0x02, 0x50, 0x00, // Server sub-TLV
+        0x0b, 0x08, 0x81, 0x02, 0x5c, 0x02, 0x0d, 0x02, 0x50, 0x01, // Server sub-TLV
+        0x0b, 0x08, 0x82, 0x02, 0x5c, 0x78, 0x0d, 0x02, 0x50, 0x02, // Server sub-TLV
+        0x0b, 0x08, 0x83, 0x02, 0x5c, 0x01, 0x0d, 0x02, 0x50, 0x03, // Server sub-TLV
+
+    };
+    const uint8_t kSeqNumbers10[]    = {254, 2, 120, 1};
+    const uint8_t kPreferredSeqNum10 = 120;
+
+    const uint8_t kNetworkData11[] = {
+        0x08, 0x04, 0x0b, 0x02, 0x50, 0xb0,                         // Service TLV
+        0x0b, 0x08, 0x80, 0x02, 0x5c, 0xf0, 0x0d, 0x02, 0x50, 0x00, // Server sub-TLV
+        0x0b, 0x08, 0x81, 0x02, 0x5c, 0x02, 0x0d, 0x02, 0x50, 0x01, // Server sub-TLV
+        0x0b, 0x08, 0x82, 0x02, 0x5c, 0x78, 0x0d, 0x02, 0x50, 0x02, // Server sub-TLV
+        0x0b, 0x08, 0x83, 0x02, 0x5c, 0x01, 0x0d, 0x02, 0x50, 0x03, // Server sub-TLV
+
+    };
+    const uint8_t kSeqNumbers11[]    = {240, 2, 120, 1};
+    const uint8_t kPreferredSeqNum11 = 240;
+
+    const TestInfo kTests[] = {
+        {kNetworkData1, sizeof(kNetworkData1), kSeqNumbers1, sizeof(kSeqNumbers1), kPreferredSeqNum1},
+        {kNetworkData2, sizeof(kNetworkData2), kSeqNumbers2, sizeof(kSeqNumbers2), kPreferredSeqNum2},
+        {kNetworkData3, sizeof(kNetworkData3), kSeqNumbers3, sizeof(kSeqNumbers3), kPreferredSeqNum3},
+        {kNetworkData4, sizeof(kNetworkData4), kSeqNumbers4, sizeof(kSeqNumbers4), kPreferredSeqNum4},
+        {kNetworkData5, sizeof(kNetworkData5), kSeqNumbers5, sizeof(kSeqNumbers5), kPreferredSeqNum5},
+        {kNetworkData6, sizeof(kNetworkData6), kSeqNumbers6, sizeof(kSeqNumbers6), kPreferredSeqNum6},
+        {kNetworkData7, sizeof(kNetworkData7), kSeqNumbers7, sizeof(kSeqNumbers7), kPreferredSeqNum7},
+        {kNetworkData8, sizeof(kNetworkData8), kSeqNumbers8, sizeof(kSeqNumbers8), kPreferredSeqNum8},
+        {kNetworkData9, sizeof(kNetworkData9), kSeqNumbers9, sizeof(kSeqNumbers9), kPreferredSeqNum9},
+        {kNetworkData10, sizeof(kNetworkData10), kSeqNumbers10, sizeof(kSeqNumbers10), kPreferredSeqNum10},
+        {kNetworkData11, sizeof(kNetworkData11), kSeqNumbers11, sizeof(kSeqNumbers11), kPreferredSeqNum11},
+    };
+
+    Service::Manager &manager   = instance->Get<Service::Manager>();
+    uint8_t           testIndex = 0;
+
+    for (const TestInfo &test : kTests)
+    {
+        Service::Manager::Iterator   iterator;
+        Service::DnsSrpAnycast::Info anycastInfo;
+
+        reinterpret_cast<TestLeader &>(instance->Get<Leader>()).Populate(test.mNetworkData, test.mNetworkDataLength);
+
+        printf("\n- - - - - - - - - - - - - - - - - - - -");
+        printf("\nDNS/SRP Anycast Service entries for test %d", ++testIndex);
+
+        for (uint8_t index = 0; index < test.mSeqNumbersLength; index++)
+        {
+            SuccessOrQuit(manager.GetNextDnsSrpAnycastInfo(iterator, anycastInfo));
+
+            printf("\n { %s, seq:%d }", anycastInfo.mAnycastAddress.ToString().AsCString(),
+                   anycastInfo.mSequenceNumber);
+
+            VerifyOrQuit(anycastInfo.mSequenceNumber == test.mSeqNumbers[index]);
+        }
+
+        VerifyOrQuit(manager.GetNextDnsSrpAnycastInfo(iterator, anycastInfo) == kErrorNotFound);
+        SuccessOrQuit(manager.FindPreferredDnsSrpAnycastInfo(anycastInfo));
+
+        printf("\n preferred -> seq:%d ", anycastInfo.mSequenceNumber);
+        VerifyOrQuit(anycastInfo.mSequenceNumber == test.mPreferredSeqNum);
+    }
+
+    testFreeInstance(instance);
+}
+
 } // namespace NetworkData
 } // namespace ot
 
@@ -460,6 +898,7 @@
     ot::NetworkData::TestNetworkDataFindNextService();
 #endif
     ot::NetworkData::TestNetworkDataDsnSrpServices();
+    ot::NetworkData::TestNetworkDataDsnSrpAnycastSeqNumSelection();
 
     printf("\nAll tests passed\n");
     return 0;
diff --git a/tests/unit/test_network_name.cpp b/tests/unit/test_network_name.cpp
new file mode 100644
index 0000000..c1f448f
--- /dev/null
+++ b/tests/unit/test_network_name.cpp
@@ -0,0 +1,124 @@
+/*
+ *  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.
+ */
+
+#include "common/code_utils.hpp"
+#include "common/debug.hpp"
+#include "meshcop/network_name.hpp"
+
+#include "test_util.h"
+
+namespace ot {
+
+void CompareNetworkName(const MeshCoP::NetworkName &aNetworkName, const char *aNameString)
+{
+    uint8_t len = static_cast<uint8_t>(strlen(aNameString));
+
+    VerifyOrQuit(strcmp(aNetworkName.GetAsCString(), aNameString) == 0);
+
+    VerifyOrQuit(aNetworkName.GetAsData().GetLength() == len);
+    VerifyOrQuit(memcmp(aNetworkName.GetAsData().GetBuffer(), aNameString, len) == 0);
+}
+
+void TestNetworkName(void)
+{
+    const char kEmptyName[]   = "";
+    const char kName1[]       = "network";
+    const char kName2[]       = "network-name";
+    const char kLongName[]    = "0123456789abcdef";
+    const char kTooLongName[] = "0123456789abcdef0";
+
+    char                 buffer[sizeof(kTooLongName) + 2];
+    uint8_t              len;
+    MeshCoP::NetworkName networkName;
+    MeshCoP::NetworkName networkName2;
+
+    CompareNetworkName(networkName, kEmptyName);
+
+    SuccessOrQuit(networkName.Set(MeshCoP::NameData(kName1, sizeof(kName1))));
+    CompareNetworkName(networkName, kName1);
+
+    VerifyOrQuit(networkName.Set(MeshCoP::NameData(kName1, sizeof(kName1))) == kErrorAlready,
+                 "failed to detect duplicate");
+    CompareNetworkName(networkName, kName1);
+
+    VerifyOrQuit(networkName.Set(MeshCoP::NameData(kName1, sizeof(kName1) - 1)) == kErrorAlready,
+                 "failed to detect duplicate");
+
+    SuccessOrQuit(networkName.Set(MeshCoP::NameData(kName2, sizeof(kName2))));
+    CompareNetworkName(networkName, kName2);
+
+    VerifyOrQuit(networkName.Set(MeshCoP::NameData(kEmptyName, 0)) == kErrorInvalidArgs);
+
+    SuccessOrQuit(networkName.Set(MeshCoP::NameData(kLongName, sizeof(kLongName))));
+    CompareNetworkName(networkName, kLongName);
+
+    VerifyOrQuit(networkName.Set(MeshCoP::NameData(kLongName, sizeof(kLongName) - 1)) == kErrorAlready,
+                 "failed to detect duplicate");
+
+    VerifyOrQuit(networkName.Set(kEmptyName) == kErrorInvalidArgs);
+
+    SuccessOrQuit(networkName.Set(MeshCoP::NameData(kName1, sizeof(kName1))));
+
+    VerifyOrQuit(networkName.Set(MeshCoP::NameData(kTooLongName, sizeof(kTooLongName))) == kErrorInvalidArgs,
+                 "accepted an invalid (too long) name");
+
+    CompareNetworkName(networkName, kName1);
+
+    memset(buffer, 'a', sizeof(buffer));
+    len = networkName.GetAsData().CopyTo(buffer, 1);
+    VerifyOrQuit(len == 1, "NameData::CopyTo() failed");
+    VerifyOrQuit(buffer[0] == kName1[0], "NameData::CopyTo() failed");
+    VerifyOrQuit(buffer[1] == 'a', "NameData::CopyTo() failed");
+
+    memset(buffer, 'a', sizeof(buffer));
+    len = networkName.GetAsData().CopyTo(buffer, sizeof(kName1) - 1);
+    VerifyOrQuit(len == sizeof(kName1) - 1, "NameData::CopyTo() failed");
+    VerifyOrQuit(memcmp(buffer, kName1, sizeof(kName1) - 1) == 0, "NameData::CopyTo() failed");
+    VerifyOrQuit(buffer[sizeof(kName1)] == 'a', "NameData::CopyTo() failed");
+
+    memset(buffer, 'a', sizeof(buffer));
+    len = networkName.GetAsData().CopyTo(buffer, sizeof(buffer));
+    VerifyOrQuit(len == sizeof(kName1) - 1, "NameData::CopyTo() failed");
+    VerifyOrQuit(memcmp(buffer, kName1, sizeof(kName1) - 1) == 0, "NameData::CopyTo() failed");
+    VerifyOrQuit(buffer[sizeof(kName1)] == 0, "NameData::CopyTo() failed");
+
+    SuccessOrQuit(networkName2.Set(MeshCoP::NameData(kName1, sizeof(kName1))));
+    VerifyOrQuit(networkName == networkName2);
+
+    SuccessOrQuit(networkName2.Set(kName2));
+    VerifyOrQuit(networkName != networkName2);
+}
+
+} // namespace ot
+
+int main(void)
+{
+    ot::TestNetworkName();
+    printf("All tests passed\n");
+    return 0;
+}
diff --git a/tests/unit/test_platform.cpp b/tests/unit/test_platform.cpp
index ce6ab2a..9d01cd3 100644
--- a/tests/unit/test_platform.cpp
+++ b/tests/unit/test_platform.cpp
@@ -330,7 +330,7 @@
 {
 }
 
-OT_TOOL_WEAK void otPlatSettingsInit(otInstance *)
+OT_TOOL_WEAK void otPlatSettingsInit(otInstance *, const uint16_t *, uint16_t)
 {
 }
 
diff --git a/tests/unit/test_priority_queue.cpp b/tests/unit/test_priority_queue.cpp
index 30a3a77..51f6b72 100644
--- a/tests/unit/test_priority_queue.cpp
+++ b/tests/unit/test_priority_queue.cpp
@@ -42,15 +42,17 @@
 // This function verifies the content of the priority queue to match the passed in messages
 void VerifyPriorityQueueContent(ot::PriorityQueue &aPriorityQueue, int aExpectedLength, ...)
 {
-    va_list      args;
-    ot::Message *message;
-    ot::Message *msgArg;
-    int8_t       curPriority = ot::Message::kNumPriorities;
-    uint16_t     msgCount, bufCount;
+    const ot::PriorityQueue &constQueue = aPriorityQueue;
+    va_list                  args;
+    ot::Message *            message;
+    ot::Message *            msgArg;
+    int8_t                   curPriority = ot::Message::kNumPriorities;
+    ot::PriorityQueue::Info  info;
 
     // Check the `GetInfo`
-    aPriorityQueue.GetInfo(msgCount, bufCount);
-    VerifyOrQuit(msgCount == aExpectedLength, "GetInfo() result does not match expected len.");
+    memset(&info, 0, sizeof(info));
+    aPriorityQueue.GetInfo(info);
+    VerifyOrQuit(info.mNumMessages == aExpectedLength, "GetInfo() result does not match expected len.");
 
     va_start(args, aExpectedLength);
 
@@ -106,6 +108,30 @@
     }
 
     va_end(args);
+
+    // Check range-based `for` loop iteration using non-const iterator
+
+    message = aPriorityQueue.GetHead();
+
+    for (ot::Message &msg : aPriorityQueue)
+    {
+        VerifyOrQuit(message == &msg, "`for` loop iteration does not match expected");
+        message = message->GetNext();
+    }
+
+    VerifyOrQuit(message == nullptr, "`for` loop iteration resulted in fewer entries than expected");
+
+    // Check  range-base `for` iteration using const iterator
+
+    message = aPriorityQueue.GetHead();
+
+    for (const ot::Message &constMsg : constQueue)
+    {
+        VerifyOrQuit(message == &constMsg, "`for` loop iteration does not match expected");
+        message = message->GetNext();
+    }
+
+    VerifyOrQuit(message == nullptr, "`for` loop iteration resulted in fewer entries than expected");
 }
 
 // This function verifies the content of the message queue to match the passed in messages
@@ -290,6 +316,56 @@
     VerifyPriorityQueueContent(queue, 1, msgNor[0]);
     VerifyMsgQueueContent(messageQueue, 1, msgNor[1]);
 
+    queue.Dequeue(*msgNor[0]);
+    VerifyPriorityQueueContent(queue, 0);
+    messageQueue.Dequeue(*msgNor[1]);
+    VerifyMsgQueueContent(messageQueue, 0);
+
+    for (ot::Message *message : msgNor)
+    {
+        SuccessOrQuit(message->SetPriority(ot::Message::kPriorityNormal));
+    }
+
+    // Range-based `for` and dequeue during iteration
+
+    for (uint16_t removeIndex = 0; removeIndex < 4; removeIndex++)
+    {
+        uint16_t index = 0;
+
+        queue.Enqueue(*msgNor[0]);
+        queue.Enqueue(*msgNor[1]);
+        queue.Enqueue(*msgNor[2]);
+        queue.Enqueue(*msgNor[3]);
+        VerifyPriorityQueueContent(queue, 4, msgNor[0], msgNor[1], msgNor[2], msgNor[3]);
+
+        // While iterating over the queue remove the entry at `removeIndex`
+        for (ot::Message &message : queue)
+        {
+            if (index == removeIndex)
+            {
+                queue.Dequeue(message);
+            }
+
+            VerifyOrQuit(&message == msgNor[index++]);
+        }
+
+        index = 0;
+
+        // Iterate over the queue and remove all
+        for (ot::Message &message : queue)
+        {
+            if (index == removeIndex)
+            {
+                index++;
+            }
+
+            VerifyOrQuit(&message == msgNor[index++]);
+            queue.Dequeue(message);
+        }
+
+        VerifyPriorityQueueContent(queue, 0);
+    }
+
     testFreeInstance(instance);
 }
 
diff --git a/tests/unit/test_pskc.cpp b/tests/unit/test_pskc.cpp
index 19dbed6..3eedad9 100644
--- a/tests/unit/test_pskc.cpp
+++ b/tests/unit/test_pskc.cpp
@@ -27,7 +27,6 @@
  */
 #include <openthread/config.h>
 
-#include "common/logging.hpp"
 #include "meshcop/commissioner.hpp"
 #include "meshcop/meshcop.hpp"
 
@@ -44,8 +43,9 @@
     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::Mac::NetworkName *>("OpenThread"),
-                                            static_cast<const ot::Mac::ExtendedPanId &>(xpanid), pskc));
+    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);
     testFreeInstance(instance);
 }
@@ -74,8 +74,9 @@
                               "123456781234567";
 
     otInstance *instance = testInitInstance();
-    SuccessOrQuit(ot::MeshCoP::GeneratePskc(passphrase, *reinterpret_cast<const ot::Mac::NetworkName *>("OpenThread"),
-                                            static_cast<const ot::Mac::ExtendedPanId &>(xpanid), pskc));
+    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);
     testFreeInstance(instance);
 }
@@ -89,8 +90,9 @@
     const char            passphrase[]   = "12SECRETPASSWORD34";
 
     otInstance *instance = testInitInstance();
-    SuccessOrQuit(ot::MeshCoP::GeneratePskc(passphrase, *reinterpret_cast<const ot::Mac::NetworkName *>("Test Network"),
-                                            static_cast<const ot::Mac::ExtendedPanId &>(xpanid), pskc));
+    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);
     testFreeInstance(instance);
 }
diff --git a/tests/unit/test_timer.cpp b/tests/unit/test_timer.cpp
index 660b56c..00cc02e 100644
--- a/tests/unit/test_timer.cpp
+++ b/tests/unit/test_timer.cpp
@@ -28,6 +28,7 @@
 
 #include "test_platform.h"
 
+#include "common/array.hpp"
 #include "common/code_utils.hpp"
 #include "common/debug.hpp"
 #include "common/instance.hpp"
@@ -585,7 +586,7 @@
 
     size_t i;
 
-    for (i = 0; i < OT_ARRAY_LENGTH(kTimeShift); i++)
+    for (i = 0; i < ot::GetArrayLength(kTimeShift); i++)
     {
         TenTimers<TimerType>(kTimeShift[i]);
     }
diff --git a/third_party/mbedtls/BUILD.gn b/third_party/mbedtls/BUILD.gn
index 30af4ee..e2a041a 100644
--- a/third_party/mbedtls/BUILD.gn
+++ b/third_party/mbedtls/BUILD.gn
@@ -133,6 +133,7 @@
   "repo/library/camellia.c",
   "repo/library/ccm.c",
   "repo/library/certs.c",
+  "repo/library/constant_time.c",
   "repo/library/chacha20.c",
   "repo/library/chachapoly.c",
   "repo/library/cipher.c",
diff --git a/third_party/mbedtls/Makefile.am b/third_party/mbedtls/Makefile.am
index cf62031..4a6f4f6 100644
--- a/third_party/mbedtls/Makefile.am
+++ b/third_party/mbedtls/Makefile.am
@@ -67,6 +67,9 @@
     repo/library/cipher_wrap.c                  \
     repo/library/cmac.c                         \
     repo/library/common.h                       \
+    repo/library/constant_time.c                \
+    repo/library/constant_time_internal.h       \
+    repo/library/constant_time_invasive.h       \
     repo/library/ctr_drbg.c                     \
     repo/library/debug.c                        \
     repo/library/des.c                          \
@@ -76,6 +79,7 @@
     repo/library/ecjpake.c                      \
     repo/library/ecp.c                          \
     repo/library/ecp_curves.c                   \
+    repo/library/ecp_invasive.h                 \
     repo/library/entropy.c                      \
     repo/library/entropy_poll.c                 \
     repo/library/error.c                        \
@@ -87,6 +91,12 @@
     repo/library/md2.c                          \
     repo/library/md4.c                          \
     repo/library/md5.c                          \
+    repo/library/mps_common.h                   \
+    repo/library/mps_error.h                    \
+    repo/library/mps_reader.c                   \
+    repo/library/mps_reader.h                   \
+    repo/library/mps_trace.c                    \
+    repo/library/mps_trace.h                    \
     repo/library/memory_buffer_alloc.c          \
     repo/library/net_sockets.c                  \
     repo/library/nist_kw.c                      \
@@ -104,11 +114,25 @@
     repo/library/platform_util.c                \
     repo/library/poly1305.c                     \
     repo/library/psa_crypto.c                   \
+    repo/library/psa_crypto_aead.c              \
+    repo/library/psa_crypto_aead.h              \
     repo/library/psa_crypto_core.h              \
+    repo/library/psa_crypto_cipher.c            \
+    repo/library/psa_crypto_cipher.h            \
+    repo/library/psa_crypto_client.c            \
     repo/library/psa_crypto_driver_wrappers.c   \
     repo/library/psa_crypto_driver_wrappers.h   \
+    repo/library/psa_crypto_ecp.c               \
+    repo/library/psa_crypto_ecp.h               \
+    repo/library/psa_crypto_hash.c              \
+    repo/library/psa_crypto_hash.h              \
     repo/library/psa_crypto_invasive.h          \
     repo/library/psa_crypto_its.h               \
+    repo/library/psa_crypto_mac.c               \
+    repo/library/psa_crypto_mac.h               \
+    repo/library/psa_crypto_random_impl.h       \
+    repo/library/psa_crypto_rsa.c               \
+    repo/library/psa_crypto_rsa.h               \
     repo/library/psa_crypto_se.c                \
     repo/library/psa_crypto_se.h                \
     repo/library/psa_crypto_slot_management.c   \
@@ -126,7 +150,6 @@
     repo/library/ssl_ciphersuites.c             \
     repo/library/ssl_cli.c                      \
     repo/library/ssl_cookie.c                   \
-    repo/library/ssl_invasive.h                 \
     repo/library/ssl_msg.c                      \
     repo/library/ssl_srv.c                      \
     repo/library/ssl_ticket.c                   \
diff --git a/third_party/mbedtls/README.md b/third_party/mbedtls/README.md
index 48dec54..2af292d 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.25.0
+https://github.com/ARMmbed/mbedtls/releases/tag/mbedtls-2.28.0
 
 ## Version
 
-2.25.0
+2.28.0
 
 ## License
 
diff --git a/third_party/mbedtls/repo/.github/issue_template.md b/third_party/mbedtls/repo/.github/issue_template.md
index 18b87fc..370066f 100644
--- a/third_party/mbedtls/repo/.github/issue_template.md
+++ b/third_party/mbedtls/repo/.github/issue_template.md
@@ -1,7 +1,17 @@
-Note: This is just a template, so feel free to use/remove the unnecessary things
+_Note:_ this is a template, please remove the parts that are not
+applicable (these initial notes, and the "Bug" section for a Feature request
+and vice-versa).
 
+**Note:** to report a security vulnerability, see
+[SECURITY.md](../SECURITY.md). Please do not use github issues for
+vulnerabilities.
+
+_Note:_ to get support, see [SUPPORT.md](../SUPPORT.md). Please do not use
+github issues for questions.
+
+---------------------------------------------------------------
 ### Description
-- Type: Bug | Enhancement\Feature Request
+- Type: Bug | Enhancement / Feature Request
 - Priority: Blocker | Major | Minor
 
 ---------------------------------------------------------------
@@ -28,14 +38,9 @@
 **Steps to reproduce**  
 
 ----------------------------------------------------------------
-## Enhancement\Feature Request
-
-**Justification - why does the library need this feature?**  
+## Enhancement / Feature Request
 
 **Suggested enhancement**  
 
------------------------------------------------------------------
+**Justification - why does the library need this feature?**  
 
-## Question
-
-**Please first check for answers in the [Mbed TLS knowledge Base](https://tls.mbed.org/kb). If you can't find the answer you're looking for then please use the [Mbed TLS mailing list](https://lists.trustedfirmware.org/mailman/listinfo/mbed-tls)**
diff --git a/third_party/mbedtls/repo/.gitignore b/third_party/mbedtls/repo/.gitignore
index 39cdc4e..9b185c6 100644
--- a/third_party/mbedtls/repo/.gitignore
+++ b/third_party/mbedtls/repo/.gitignore
@@ -18,6 +18,7 @@
 Coverage
 *.gcno
 *.gcda
+coverage-summary.txt
 
 # generated by scripts/memory.sh
 massif-*
@@ -48,6 +49,9 @@
 # Generated documentation:
 /apidoc
 
+# PSA Crypto compliance test repo, cloned by test_psa_compliance.py
+/psa-arch-tests
+
 # Editor navigation files:
 /GPATH
 /GRTAGS
diff --git a/third_party/mbedtls/repo/.mypy.ini b/third_party/mbedtls/repo/.mypy.ini
new file mode 100644
index 0000000..6b831dd
--- /dev/null
+++ b/third_party/mbedtls/repo/.mypy.ini
@@ -0,0 +1,4 @@
+[mypy]
+mypy_path = scripts
+namespace_packages = True
+warn_unused_configs = True
diff --git a/third_party/mbedtls/repo/.pylintrc b/third_party/mbedtls/repo/.pylintrc
index ad25a7c..d217ff6 100644
--- a/third_party/mbedtls/repo/.pylintrc
+++ b/third_party/mbedtls/repo/.pylintrc
@@ -1,3 +1,6 @@
+[MASTER]
+init-hook='import sys; sys.path.append("scripts")'
+
 [BASIC]
 # We're ok with short funtion argument names.
 # [invalid-name]
@@ -12,9 +15,9 @@
 # [missing-docstring]
 docstring-min-length=10
 
-# Allow longer methods than the default.
+# No upper limit on method names. Pylint <2.1.0 has an upper limit of 30.
 # [invalid-name]
-method-rgx=[a-z_][a-z0-9_]{2,35}$
+method-rgx=[a-z_][a-z0-9_]{2,}$
 
 # Allow module names containing a dash (but no underscore or uppercase letter).
 # They are whole programs, not meant to be included by another module.
diff --git a/third_party/mbedtls/repo/.travis.yml b/third_party/mbedtls/repo/.travis.yml
index 76cb1c5..3354607 100644
--- a/third_party/mbedtls/repo/.travis.yml
+++ b/third_party/mbedtls/repo/.travis.yml
@@ -14,38 +14,25 @@
           - 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:
-        - pip install pylint==2.4.4
+        - scripts/min_requirements.py
       script:
         - tests/scripts/all.sh -k 'check_*'
         - tests/scripts/all.sh -k test_default_out_of_box
-        - tests/scripts/test-ref-configs.pl
-        - tests/scripts/all.sh -k build_arm_none_eabi_gcc_arm5vte build_arm_none_eabi_gcc_m0plus
+        - 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
 
     - name: full configuration
       script:
         - tests/scripts/all.sh -k test_full_cmake_gcc_asan
 
-    - name: macOS
-      os: osx
-      compiler: clang
-      script:
-        - tests/scripts/all.sh -k test_default_out_of_box
-
     - name: Windows
       os: windows
-      before_install:
-        - choco install python --version=3.5.4
-      env:
-        # Add the directory where the Choco package goes
-        - PATH=/c/Python35:/c/Python35/Scripts:$PATH
       script:
-        - type python; python --version
-        - python scripts/generate_psa_constants.py
-        # Logs appear out of sequence on Windows. Give time to catch up.
-        - sleep 5
         - scripts/windows_msbuild.bat v141 # Visual Studio 2017
 
 after_failure:
diff --git a/third_party/mbedtls/repo/BRANCHES.md b/third_party/mbedtls/repo/BRANCHES.md
new file mode 100644
index 0000000..ee91f76
--- /dev/null
+++ b/third_party/mbedtls/repo/BRANCHES.md
@@ -0,0 +1,54 @@
+# Maintained branches
+
+At any point in time, we have a number of maintained branches consisting of:
+
+- The [`master`](https://github.com/ARMmbed/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:
+  this is where new features land,
+  as well as bug fixes and security fixes.
+- One or more long-time support (LTS) branches:
+  these only get bug fixes and security fixes.
+
+We use [Semantic Versioning](https://semver.org/). In particular, we maintain
+API compatibility in the `master` branch between major version changes. We
+also maintain ABI compatibility within LTS branches; see the next section for
+details.
+
+## Backwards Compatibility
+
+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
+undocumented features, then you should be able to re-compile it without
+modification with any later release x.y'.z' with the same major version
+number, and your code will still build, be secure, and work.
+
+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
+compatibility, we will put security first, but always attempt to provide a
+compatibility option.
+
+For the LTS branches, additionally we try very hard to also maintain ABI
+compatibility (same definition as API except with re-linking instead of
+re-compiling) and to avoid any increase in code size or RAM usage, or in the
+minimum version of tools needed to build the code. The only exception, as
+before, is in case those goals would conflict with fixing a security issue, we
+will put security first but provide a compatibility option. (So far we never
+had to break ABI compatibility in an LTS branch, but we occasionally had to
+increase code size for a security fix.)
+
+For contributors, see the [Backwards Compatibility section of
+CONTRIBUTING](CONTRIBUTING.md#cackwords-compatibility).
+
+## 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.
+
+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
new file mode 100644
index 0000000..e8705ff
--- /dev/null
+++ b/third_party/mbedtls/repo/BUGS.md
@@ -0,0 +1,20 @@
+## Known issues
+
+Known issues in Mbed TLS are [tracked on GitHub](https://github.com/ARMmbed/mbedtls/issues).
+
+## Reporting a bug
+
+If you think you've found a bug in Mbed TLS, please follow these steps:
+
+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
+   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).
+
+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 ac24bf4..14ca7b6 100644
--- a/third_party/mbedtls/repo/CMakeLists.txt
+++ b/third_party/mbedtls/repo/CMakeLists.txt
@@ -21,6 +21,18 @@
 #
 
 cmake_minimum_required(VERSION 2.8.12)
+
+# https://cmake.org/cmake/help/latest/policy/CMP0011.html
+# Setting this policy is required in CMake >= 3.18.0, otherwise a warning is generated. The OLD
+# policy setting is deprecated, and will be removed in future versions.
+cmake_policy(SET CMP0011 NEW)
+# https://cmake.org/cmake/help/latest/policy/CMP0012.html
+# Setting the CMP0012 policy to NEW is required for FindPython3 to work with CMake 3.18.2
+# (there is a bug in this particular version), otherwise, setting the CMP0012 policy is required
+# for CMake versions >= 3.18.3 otherwise a deprecated warning is generated. The OLD policy setting
+# is deprecated and will be removed in future versions.
+cmake_policy(SET CMP0012 NEW)
+
 if(TEST_CPP)
     project("mbed TLS" C CXX)
 else()
@@ -167,6 +179,9 @@
     execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion
                     OUTPUT_VARIABLE GCC_VERSION)
     set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wwrite-strings")
+    if (GCC_VERSION VERSION_GREATER 3.0 OR GCC_VERSION VERSION_EQUAL 3.0)
+        set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wformat=2 -Wno-format-nonliteral")
+    endif()
     if (GCC_VERSION VERSION_GREATER 4.3 OR GCC_VERSION VERSION_EQUAL 4.3)
         set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wvla")
     endif()
@@ -182,6 +197,9 @@
             set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wformat-signedness")
         endif()
     endif()
+    if (GCC_VERSION VERSION_GREATER 7.0 OR GCC_VERSION VERSION_EQUAL 7.0)
+      set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wformat-overflow=2 -Wformat-truncation")
+    endif()
     set(CMAKE_C_FLAGS_RELEASE     "-O2")
     set(CMAKE_C_FLAGS_DEBUG       "-O0 -g3")
     set(CMAKE_C_FLAGS_COVERAGE    "-O0 -g3 --coverage")
@@ -192,7 +210,7 @@
 endif(CMAKE_COMPILER_IS_GNU)
 
 if(CMAKE_COMPILER_IS_CLANG)
-    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wwrite-strings -Wpointer-arith -Wimplicit-fallthrough -Wshadow -Wvla")
+    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wwrite-strings -Wpointer-arith -Wimplicit-fallthrough -Wshadow -Wvla -Wformat=2 -Wno-format-nonliteral")
     set(CMAKE_C_FLAGS_RELEASE     "-O2")
     set(CMAKE_C_FLAGS_DEBUG       "-O0 -g3")
     set(CMAKE_C_FLAGS_COVERAGE    "-O0 -g3 --coverage")
diff --git a/third_party/mbedtls/repo/CONTRIBUTING.md b/third_party/mbedtls/repo/CONTRIBUTING.md
index 9b02ba5..b3a9547 100644
--- a/third_party/mbedtls/repo/CONTRIBUTING.md
+++ b/third_party/mbedtls/repo/CONTRIBUTING.md
@@ -22,9 +22,10 @@
 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).
 
-API/ABI 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.
+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.
 
@@ -48,6 +49,9 @@
 
 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.
 
+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)
diff --git a/third_party/mbedtls/repo/ChangeLog b/third_party/mbedtls/repo/ChangeLog
index fb231aa..021012a 100644
--- a/third_party/mbedtls/repo/ChangeLog
+++ b/third_party/mbedtls/repo/ChangeLog
@@ -1,5 +1,437 @@
 mbed TLS ChangeLog (Sorted per branch, date)
 
+= mbed TLS 2.28.0 branch released 2021-12-17
+
+API changes
+   * Some fields of mbedtls_ssl_session and mbedtls_ssl_config are in a
+     different order. This only affects applications that define such
+     structures directly or serialize them.
+
+Requirement changes
+   * Sign-magnitude and one's complement representations for signed integers are
+     not supported. Two's complement is the only supported representation.
+
+Removals
+   * Remove config option MBEDTLS_TLS_DEFAULT_ALLOW_SHA1_IN_CERTIFICATES,
+     which allowed SHA-1 in the default TLS configuration for certificate
+     signing. It was intended to facilitate the transition in environments
+     with SHA-1 certificates. SHA-1 is considered a weak message digest and
+     its use constitutes a security risk.
+   * Remove the partial support for running unit tests via Greentea on Mbed OS,
+     which had been unmaintained since 2018.
+
+Features
+   * The identifier of the CID TLS extension can be configured by defining
+     MBEDTLS_TLS_EXT_CID at compile time.
+   * Warn if errors from certain functions are ignored. This is currently
+     supported on GCC-like compilers and on MSVC and can be configured through
+     the macro MBEDTLS_CHECK_RETURN. The warnings are always enabled
+     (where supported) for critical functions where ignoring the return
+     value is almost always a bug. Enable the new configuration option
+     MBEDTLS_CHECK_RETURN_WARNING to get warnings for other functions. This
+     is currently implemented in the AES, DES and md modules, and will be
+     extended to other modules in the future.
+   * Add missing PSA macros declared by PSA Crypto API 1.0.0:
+     PSA_ALG_IS_SIGN_HASH, PSA_ALG_NONE, PSA_HASH_BLOCK_LENGTH, PSA_KEY_ID_NULL.
+   * Add new API mbedtls_ct_memcmp for constant time buffer comparison.
+   * Add PSA API definition for ARIA.
+
+Security
+   * Zeroize several intermediate variables used to calculate the expected
+     value when verifying a MAC or AEAD tag. This hardens the library in
+     case the value leaks through a memory disclosure vulnerability. For
+     example, a memory disclosure vulnerability could have allowed a
+     man-in-the-middle to inject fake ciphertext into a DTLS connection.
+   * In psa_cipher_generate_iv() and psa_cipher_encrypt(), do not read back
+     from the output buffer. This fixes a potential policy bypass or decryption
+     oracle vulnerability if the output buffer is in memory that is shared with
+     an untrusted application.
+   * Fix a double-free that happened after mbedtls_ssl_set_session() or
+     mbedtls_ssl_get_session() failed with MBEDTLS_ERR_SSL_ALLOC_FAILED
+     (out of memory). After that, calling mbedtls_ssl_session_free()
+     and mbedtls_ssl_free() would cause an internal session buffer to
+     be free()'d twice.
+
+Bugfix
+   * Stop using reserved identifiers as local variables. Fixes #4630.
+   * The GNU makefiles invoke python3 in preference to python except on Windows.
+     The check was accidentally not performed when cross-compiling for Windows
+     on Linux. Fix this. Fixes #4774.
+   * Prevent divide by zero if either of PSA_CIPHER_ENCRYPT_OUTPUT_SIZE() or
+     PSA_CIPHER_UPDATE_OUTPUT_SIZE() were called using an asymmetric key type.
+   * Fix a parameter set but unused in psa_crypto_cipher.c. Fixes #4935.
+   * Don't use the obsolete header path sys/fcntl.h in unit tests.
+     These header files cause compilation errors in musl.
+     Fixes #4969.
+   * Fix missing constraints on x86_64 and aarch64 assembly code
+     for bignum multiplication that broke some bignum operations with
+     (at least) Clang 12.
+     Fixes #4116, #4786, #4917, #4962.
+   * Fix mbedtls_cipher_crypt: AES-ECB when MBEDTLS_USE_PSA_CRYPTO is enabled.
+   * Failures of alternative implementations of AES or DES single-block
+     functions enabled with MBEDTLS_AES_ENCRYPT_ALT, MBEDTLS_AES_DECRYPT_ALT,
+     MBEDTLS_DES_CRYPT_ECB_ALT or MBEDTLS_DES3_CRYPT_ECB_ALT were ignored.
+     This does not concern the implementation provided with Mbed TLS,
+     where this function cannot fail, or full-module replacements with
+     MBEDTLS_AES_ALT or MBEDTLS_DES_ALT. Reported by Armelle Duboc in #1092.
+   * Some failures of HMAC operations were ignored. These failures could only
+     happen with an alternative implementation of the underlying hash module.
+   * Fix the error returned by psa_generate_key() for a public key. Fixes #4551.
+   * Fix the build of sample programs when neither MBEDTLS_ERROR_C nor
+     MBEDTLS_ERROR_STRERROR_DUMMY is enabled.
+   * Fix PSA_ALG_RSA_PSS verification accepting an arbitrary salt length.
+     This algorithm now accepts only the same salt length for verification
+     that it produces when signing, as documented. Use the new algorithm
+     PSA_ALG_RSA_PSS_ANY_SALT to accept any salt length. Fixes #4946.
+   * The existing predicate macro name PSA_ALG_IS_HASH_AND_SIGN is now reserved
+     for algorithm values that fully encode the hashing step, as per the PSA
+     Crypto API specification. This excludes PSA_ALG_RSA_PKCS1V15_SIGN_RAW and
+     PSA_ALG_ECDSA_ANY. The new predicate macro PSA_ALG_IS_SIGN_HASH covers
+     all algorithms that can be used with psa_{sign,verify}_hash(), including
+     these two.
+   * Fix issue in Makefile on Linux with SHARED=1, that caused shared libraries
+     not to list other shared libraries they need.
+   * Fix a bug in mbedtls_gcm_starts() when the bit length of the iv
+     exceeds 2^32. Fixes #4884.
+   * Fix an uninitialized variable warning in test_suite_ssl.function with GCC
+     version 11.
+   * Fix the build when no SHA2 module is included. Fixes #4930.
+   * Fix the build when only the bignum module is included. Fixes #4929.
+   * Fix a potential invalid pointer dereference and infinite loop bugs in
+     pkcs12 functions when the password is empty. Fix the documentation to
+     better describe the inputs to these functions and their possible values.
+     Fixes #5136.
+   * The key usage flags PSA_KEY_USAGE_SIGN_MESSAGE now allows the MAC
+     operations psa_mac_compute() and psa_mac_sign_setup().
+   * The key usage flags PSA_KEY_USAGE_VERIFY_MESSAGE now allows the MAC
+     operations psa_mac_verify() and psa_mac_verify_setup().
+
+Changes
+   * Set config option MBEDTLS_TLS_DEFAULT_ALLOW_SHA1_IN_KEY_EXCHANGE to be
+     disabled by default.
+   * Improve the performance of base64 constant-flow code. The result is still
+     slower than the original non-constant-flow implementation, but much faster
+     than the previous constant-flow implementation. Fixes #4814.
+   * Indicate in the error returned if the nonce length used with
+     ChaCha20-Poly1305 is invalid, and not just unsupported.
+   * The mbedcrypto library includes a new source code module constant_time.c,
+     containing various functions meant to resist timing side channel attacks.
+     This module does not have a separate configuration option, and functions
+     from this module will be included in the build as required. Currently
+     most of the interface of this module is private and may change at any
+     time.
+
+= mbed TLS 2.27.0 branch released 2021-07-07
+
+API changes
+   * Update AEAD output size macros to bring them in line with the PSA Crypto
+     API version 1.0 spec. This version of the spec parameterizes them on the
+     key type used, as well as the key bit-size in the case of
+     PSA_AEAD_TAG_LENGTH.
+     The old versions of these macros were renamed and deprecated as follows:
+     - PSA_AEAD_TAG_LENGTH          -> PSA_AEAD_TAG_LENGTH_1_ARG
+     - PSA_AEAD_ENCRYPT_OUTPUT_SIZE -> PSA_AEAD_ENCRYPT_OUTPUT_SIZE_2_ARG
+     - PSA_AEAD_DECRYPT_OUTPUT_SIZE -> PSA_AEAD_DECRYPT_OUTPUT_SIZE_2_ARG
+     - PSA_AEAD_UPDATE_OUTPUT_SIZE  -> PSA_AEAD_UPDATE_OUTPUT_SIZE_2_ARG
+     - PSA_AEAD_FINISH_OUTPUT_SIZE  -> PSA_AEAD_FINISH_OUTPUT_SIZE_1_ARG
+     - PSA_AEAD_VERIFY_OUTPUT_SIZE  -> PSA_AEAD_VERIFY_OUTPUT_SIZE_1_ARG
+   * Implement one-shot cipher functions, psa_cipher_encrypt and
+     psa_cipher_decrypt, according to the PSA Crypto API 1.0.0
+     specification.
+
+Requirement changes
+   * The library now uses the %zu format specifier with the printf() family of
+     functions, so requires a toolchain that supports it. This change does not
+     affect the maintained LTS branches, so when contributing changes please
+     bear this in mind and do not add them to backported code.
+
+Features
+   * Add mbedtls_rsa_rsassa_pss_sign_ext() function allowing to generate a
+     signature with a specific salt length. This function allows to validate
+     test cases provided in the NIST's CAVP test suite. Contributed by Cédric
+     Meuter in PR #3183.
+   * Added support for built-in driver keys through the PSA opaque crypto
+     driver interface. Refer to the documentation of
+     MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS for more information.
+   * Implement psa_sign_message() and psa_verify_message().
+   * The new function mbedtls_mpi_random() generates a random value in a
+     given range uniformly.
+   * Implement psa_mac_compute() and psa_mac_verify() as defined in the
+     PSA Cryptograpy API 1.0.0 specification.
+   * MBEDTLS_ECP_MAX_BITS is now determined automatically from the configured
+     curves and no longer needs to be configured explicitly to save RAM.
+
+Security
+   * Fix a bias in the generation of finite-field Diffie-Hellman-Merkle (DHM)
+     private keys and of blinding values for DHM and elliptic curves (ECP)
+     computations. Reported by FlorianF89 in #4245.
+   * Fix a potential side channel vulnerability in ECDSA ephemeral key generation.
+     An adversary who is capable of very precise timing measurements could
+     learn partial information about the leading bits of the nonce used for the
+     signature, allowing the recovery of the private key after observing a
+     large number of signature operations. This completes a partial fix in
+     Mbed TLS 2.20.0.
+   * 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
+     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.
+
+Bugfix
+   * Add printf function attributes to mbedtls_debug_print_msg to ensure we
+     get printf format specifier warnings.
+   * Fix premature fopen() call in mbedtls_entropy_write_seed_file which may
+     lead to seed file corruption in the case where the path to the seed file is
+     equal to MBEDTLS_PLATFORM_STD_NV_SEED_FILE. Contributed by Victor
+     Krasnoshchok in #3616.
+   * PSA functions other than psa_open_key now return PSA_ERROR_INVALID_HANDLE
+     rather than PSA_ERROR_DOES_NOT_EXIST for an invalid handle, bringing them
+     in line with version 1.0.0 of the specification. Fix #4162.
+   * PSA functions creating a key now return PSA_ERROR_INVALID_ARGUMENT rather
+     than PSA_ERROR_INVALID_HANDLE when the identifier specified for the key
+     to create is not valid, bringing them in line with version 1.0.0 of the
+     specification. Fix #4271.
+   * Fix some cases in the bignum module where the library constructed an
+     unintended representation of the value 0 which was not processed
+     correctly by some bignum operations. This could happen when
+     mbedtls_mpi_read_string() was called on "-0", or when
+     mbedtls_mpi_mul_mpi() and mbedtls_mpi_mul_int() was called with one of
+     the arguments being negative and the other being 0. Fixes #4643.
+   * Fix a bug in ECDSA that would cause it to fail when the hash is all-bits
+     zero. Fixes #1792
+   * Fix a compilation error when MBEDTLS_ECP_RANDOMIZE_MXZ_ALT is
+     defined. Fixes #4217.
+   * Fix an incorrect error code when parsing a PKCS#8 private key.
+   * In a TLS client, enforce the Diffie-Hellman minimum parameter size
+     set with mbedtls_ssl_conf_dhm_min_bitlen() precisely. Before, the
+     minimum size was rounded down to the nearest multiple of 8.
+   * In library/net_sockets.c, _POSIX_C_SOURCE and _XOPEN_SOURCE are
+     defined to specific values.  If the code is used in a context
+     where these are already defined, this can result in a compilation
+     error.  Instead, assume that if they are defined, the values will
+     be adequate to build Mbed TLS.
+   * The cipher suite TLS-RSA-WITH-CAMELLIA-256-GCM-SHA384 was not available
+     when SHA-1 was disabled and was offered when SHA-1 was enabled but SHA-384
+     was disabled. Fix the dependency. Fixes #4472.
+   * Do not offer SHA384 cipher suites when SHA-384 is disabled. Fixes #4499.
+   * With MBEDTLS_PSA_CRYPTO_C disabled, some functions were getting built
+     nonetheless, resulting in undefined reference errors when building a
+     shared library. Reported by Guillermo Garcia M. in #4411.
+   * Fix test suite code on platforms where int32_t is not int, such as
+     Arm Cortex-M. Fixes #4530.
+   * Fix some issues affecting MBEDTLS_ARIA_ALT implementations: a misplaced
+     directive in a header and a missing initialization in the self-test.
+   * Fix a missing initialization in the Camellia self-test, affecting
+     MBEDTLS_CAMELLIA_ALT implementations.
+   * Restore the ability to configure PSA via Mbed TLS options to support RSA
+     key pair operations but exclude RSA key generation. When MBEDTLS_GENPRIME
+     is not defined PSA will no longer attempt to use mbedtls_rsa_gen_key().
+     Fixes #4512.
+   * Fix a regression introduced in 2.24.0 which broke (D)TLS CBC ciphersuites
+     (when the encrypt-then-MAC extension is not in use) with some ALT
+     implementations of the underlying hash (SHA-1, SHA-256, SHA-384), causing
+     the affected side to wrongly reject valid messages. Fixes #4118.
+   * Remove outdated check-config.h check that prevented implementing the
+     timing module on Mbed OS. Fixes #4633.
+   * Fix PSA_ALG_TLS12_PRF and PSA_ALG_TLS12_PSK_TO_MS being too permissive
+     about missing inputs.
+   * Fix mbedtls_net_poll() and mbedtls_net_recv_timeout() often failing with
+     MBEDTLS_ERR_NET_POLL_FAILED on Windows. Fixes #4465.
+   * Fix a resource leak in a test suite with an alternative AES
+     implementation. Fixes #4176.
+   * Fix a crash in mbedtls_mpi_debug_mpi on a bignum having 0 limbs. This
+     could notably be triggered by setting the TLS debug level to 3 or above
+     and using a Montgomery curve for the key exchange. Reported by lhuang04
+     in #4578. Fixes #4608.
+   * psa_verify_hash() was relying on implementation-specific behavior of
+     mbedtls_rsa_rsassa_pss_verify() and was causing failures in some _ALT
+     implementations. This reliance is now removed. Fixes #3990.
+   * Disallow inputs of length different from the corresponding hash when
+     signing or verifying with PSA_ALG_RSA_PSS (The PSA Crypto API mandates
+     that PSA_ALG_RSA_PSS uses the same hash throughout the algorithm.)
+   * Fix a null pointer dereference when mbedtls_mpi_exp_mod() was called with
+     A=0 represented with 0 limbs. Up to and including Mbed TLS 2.26, this bug
+     could not be triggered by code that constructed A with one of the
+     mbedtls_mpi_read_xxx functions (including in particular TLS code) since
+     those always built an mpi object with at least one limb.
+     Credit to OSS-Fuzz. Fixes #4641.
+   * Fix mbedtls_mpi_gcd(G,A,B) when the value of B is zero. This had no
+     effect on Mbed TLS's internal use of mbedtls_mpi_gcd(), but may affect
+     applications that call mbedtls_mpi_gcd() directly. Fixes #4642.
+   * The PSA API no longer allows the creation or destruction of keys with a
+     read-only lifetime. The persistence level PSA_KEY_PERSISTENCE_READ_ONLY
+     can now only be used as intended, for keys that cannot be modified through
+     normal use of the API.
+   * When MBEDTLS_PSA_CRYPTO_SPM is enabled, crypto_spe.h was not included
+     in all the right places. Include it from crypto_platform.h, which is
+     the natural place. Fixes #4649.
+   * mbedtls_pk_sign() and mbedtls_pk_verify() and their extended and
+     restartable variants now always honor the specified hash length if
+     nonzero. Before, for RSA, hash_len was ignored in favor of the length of
+     the specified hash algorithm.
+   * Fix which alert is sent in some cases to conform to the
+     applicable RFC: on an invalid Finished message value, an
+     invalid max_fragment_length extension, or an
+     unsupported extension used by the server.
+   * Correct (change from 12 to 13 bytes) the value of the macro describing the
+     maximum nonce length returned by psa_aead_generate_nonce().
+
+Changes
+   * Add extra printf compiler warning flags to builds.
+   * Fix memsan build false positive in x509_crt.c with Clang 11
+   * Fix the setting of the read timeout in the DTLS sample programs.
+   * Remove the AES sample application programs/aes/aescrypt2 which shows
+     bad cryptographic practice. Fix #1906.
+   * Alternative implementations of CMAC may now opt to not support 3DES as a
+     CMAC block cipher, and still pass the CMAC self test.
+   * Remove configs/config-psa-crypto.h, which was identical to the default
+     configuration except for having some extra cryptographic mechanisms
+     enabled and for unintended differences. This configuration was primarily
+     intended to demonstrate the PSA API, and lost most of its usefulness when
+     MBEDTLS_PSA_CRYPTO_C became enabled by default.
+   * When building the test suites with GNU make, invoke python3 or python, not
+     python2, which is no longer supported upstream.
+   * When using session cache based session resumption on the server,
+     double-check that custom session cache implementations return
+     sessions which are consistent with the negotiated ciphersuite
+     and compression method.
+   * Fix build failure on MinGW toolchain when __USE_MING_ANSI_STDIO is on.
+     When that flag is on, standard GNU C printf format specifiers
+     should be used.
+   * Reduce the default value of MBEDTLS_ECP_WINDOW_SIZE. This reduces RAM usage
+     during ECC operations at a negligible performance cost.
+   * mbedtls_mpi_read_binary(), mbedtls_mpi_read_binary_le() and
+     mbedtls_mpi_read_string() now construct an mbedtls_mpi object with 0 limbs
+     when their input has length 0. Note that this is an implementation detail
+     and can change at any time, so this change should be transparent, but it
+     may result in mbedtls_mpi_write_binary() or mbedtls_mpi_write_string()
+     now writing an empty string where it previously wrote one or more
+     zero digits when operating from values constructed with an mpi_read
+     function and some mpi operations.
+   * Implicitly add PSA_KEY_USAGE_SIGN_MESSAGE key usage policy flag when
+     PSA_KEY_USAGE_SIGN_HASH flag is set and PSA_KEY_USAGE_VERIFY_MESSAGE flag
+     when PSA_KEY_USAGE_VERIFY_HASH flag is set. This usage flag extension
+     is also applied when loading a key from storage.
+
+= mbed TLS 2.26.0 branch released 2021-03-08
+
+API changes
+   * Renamed the PSA Crypto API output buffer size macros to bring them in line
+     with version 1.0.0 of the specification.
+   * The API glue function mbedtls_ecc_group_of_psa() now takes the curve size
+     in bits rather than bytes, with an additional flag to indicate if the
+     size may have been rounded up to a whole number of bytes.
+   * Renamed the PSA Crypto API AEAD tag length macros to bring them in line
+     with version 1.0.0 of the specification.
+
+Default behavior changes
+   * In mbedtls_rsa_context objects, the ver field was formerly documented
+     as always 0. It is now reserved for internal purposes and may take
+     different values.
+
+New deprecations
+   * PSA_KEY_EXPORT_MAX_SIZE, PSA_HASH_SIZE, PSA_MAC_FINAL_SIZE,
+     PSA_BLOCK_CIPHER_BLOCK_SIZE, PSA_MAX_BLOCK_CIPHER_BLOCK_SIZE and
+     PSA_ALG_TLS12_PSK_TO_MS_MAX_PSK_LEN have been renamed, and the old names
+     deprecated.
+   * PSA_ALG_AEAD_WITH_DEFAULT_TAG_LENGTH and PSA_ALG_AEAD_WITH_TAG_LENGTH
+     have been renamed, and the old names deprecated.
+
+Features
+   * The PSA crypto subsystem can now use HMAC_DRBG instead of CTR_DRBG.
+     CTR_DRBG is used by default if it is available, but you can override
+     this choice by setting MBEDTLS_PSA_HMAC_DRBG_MD_TYPE at compile time.
+     Fix #3354.
+   * Automatic fallback to a software implementation of ECP when
+     MBEDTLS_ECP_xxx_ALT accelerator hooks are in use can now be turned off
+     through setting the new configuration flag MBEDTLS_ECP_NO_FALLBACK.
+   * The PSA crypto subsystem can now be configured to use less static RAM by
+     tweaking the setting for the maximum amount of keys simultaneously in RAM.
+     MBEDTLS_PSA_KEY_SLOT_COUNT sets the maximum number of volatile keys that
+     can exist simultaneously. It has a sensible default if not overridden.
+   * Partial implementation of the PSA crypto driver interface: Mbed TLS can
+     now use an external random generator instead of the library's own
+     entropy collection and DRBG code. Enable MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG
+     and see the documentation of mbedtls_psa_external_get_random() for details.
+   * Applications using both mbedtls_xxx and psa_xxx functions (for example,
+     applications using TLS and MBEDTLS_USE_PSA_CRYPTO) can now use the PSA
+     random generator with mbedtls_xxx functions. See the documentation of
+     mbedtls_psa_get_random() for details.
+   * In the PSA API, the policy for a MAC or AEAD algorithm can specify a
+     minimum MAC or tag length thanks to the new wildcards
+     PSA_ALG_AT_LEAST_THIS_LENGTH_MAC and
+     PSA_ALG_AEAD_WITH_AT_LEAST_THIS_LENGTH_TAG.
+
+Security
+   * Fix a security reduction in CTR_DRBG when the initial seeding obtained a
+     nonce from entropy. Applications were affected if they called
+     mbedtls_ctr_drbg_set_nonce_len(), if they called
+     mbedtls_ctr_drbg_set_entropy_len() with a size that was 3/2 times the key
+     length, or when the entropy module uses SHA-256 and CTR_DRBG uses AES-256.
+     In such cases, a random nonce was necessary to achieve the advertised
+     security strength, but the code incorrectly used a constant instead of
+     entropy from the nonce.
+     Found by John Stroebel in #3819 and fixed in #3973.
+   * Fix a buffer overflow in mbedtls_mpi_sub_abs() when calculating
+     |A| - |B| where |B| is larger than |A| and has more limbs (so the
+     function should return MBEDTLS_ERR_MPI_NEGATIVE_VALUE). Only
+     applications calling mbedtls_mpi_sub_abs() directly are affected:
+     all calls inside the library were safe since this function is
+     only called with |A| >= |B|. Reported by Guido Vranken in #4042.
+   * Fix an errorneous estimation for an internal buffer in
+     mbedtls_pk_write_key_pem(). If MBEDTLS_MPI_MAX_SIZE is set to an odd
+     value the function might fail to write a private RSA keys of the largest
+     supported size.
+     Found by Daniel Otte, reported in #4093 and fixed in #4094.
+   * Fix a stack buffer overflow with mbedtls_net_poll() and
+     mbedtls_net_recv_timeout() when given a file descriptor that is
+     beyond FD_SETSIZE. Reported by FigBug in #4169.
+   * Guard against strong local side channel attack against base64 tables by
+     making access aceess to them use constant flow code.
+
+Bugfix
+   * Fix use-after-scope error in programs/ssl/ssl_client2.c and ssl_server2.c
+   * Fix memory leak that occured when calling psa_close_key() on a
+     wrapped key with MBEDTLS_PSA_CRYPTO_SE_C defined.
+   * Fix an incorrect error code if an RSA private operation glitched.
+   * Fix a memory leak in an error case in psa_generate_derived_key_internal().
+   * Fix a resource leak in CTR_DRBG and HMAC_DRBG when MBEDTLS_THREADING_C
+     is enabled, on platforms where initializing a mutex allocates resources.
+     This was a regression introduced in the previous release. Reported in
+     #4017, #4045 and #4071.
+   * Ensure that calling mbedtls_rsa_free() or mbedtls_entropy_free()
+     twice is safe. This happens for RSA when some Mbed TLS library functions
+     fail. Such a double-free was not safe when MBEDTLS_THREADING_C was
+     enabled on platforms where freeing a mutex twice is not safe.
+   * Fix a resource leak in a bad-arguments case of mbedtls_rsa_gen_key()
+     when MBEDTLS_THREADING_C is enabled on platforms where initializing
+     a mutex allocates resources.
+   * Fixes a bug where, if the library was configured to include support for
+     both the old SE interface and the new PSA driver interface, external keys were
+     not loaded from storage. This was fixed by #3996.
+   * This change makes 'mbedtls_x509write_crt_set_basic_constraints'
+     consistent with RFC 5280 4.2.1.9 which says: "Conforming CAs MUST
+     include this extension in all CA certificates that contain public keys
+     used to validate digital signatures on certificates and MUST mark the
+     extension as critical in such certificates." Previous to this change,
+     the extension was always marked as non-critical. This was fixed by
+     #3698.
+
+Changes
+   * A new library C file psa_crypto_client.c has been created to contain
+     the PSA code needed by a PSA crypto client when the PSA crypto
+     implementation is not included into the library.
+   * On recent enough versions of FreeBSD and DragonFlyBSD, the entropy module
+     now uses the getrandom syscall instead of reading from /dev/urandom.
+
 = mbed TLS 2.25.0 branch released 2020-12-11
 
 API changes
@@ -20,7 +452,7 @@
      warning on CMake 3.19.0. #3801
 
 New deprecations
-   * PSA_KEY_TYPE_CHACHA20 and PSA_KEY_TYPE_ARC4 have been deprecated.
+   * PSA_ALG_CHACHA20 and PSA_ALG_ARC4 have been deprecated.
      Use PSA_ALG_STREAM_CIPHER instead.
    * The functions mbedtls_cipher_auth_encrypt() and
      mbedtls_cipher_auth_decrypt() are deprecated in favour of the new
@@ -91,7 +523,7 @@
      Johan Malmgren and Johan Uppman Bruce from Sectra.
 
 Bugfix
-   * Fix an invalid (but nonzero) return code from mbedtls_pk_parse_subpubkey()
+   * Fix an invalid (but non-zero) return code from mbedtls_pk_parse_subpubkey()
      when the input has trailing garbage. Fixes #2512.
    * Fix build failure in configurations where MBEDTLS_USE_PSA_CRYPTO is
      enabled but ECDSA is disabled. Contributed by jdurkop. Fixes #3294.
diff --git a/third_party/mbedtls/repo/Makefile b/third_party/mbedtls/repo/Makefile
index 9344e71..6a8b230 100644
--- a/third_party/mbedtls/repo/Makefile
+++ b/third_party/mbedtls/repo/Makefile
@@ -138,11 +138,11 @@
 	tests/suites/*.function \
 )
 # Exuberant-ctags invocation. Other ctags implementations may require different options.
-CTAGS = ctags --langmap=c:+.h.function -o
+CTAGS = ctags --langmap=c:+.h.function --line-directives=no -o
 tags: $(C_SOURCE_FILES)
 	$(CTAGS) $@ $(C_SOURCE_FILES)
 TAGS: $(C_SOURCE_FILES)
-	etags -o $@ $(C_SOURCE_FILES)
+	etags --no-line-directive -o $@ $(C_SOURCE_FILES)
 global: GPATH GRTAGS GSYMS GTAGS
 GPATH GRTAGS GSYMS GTAGS: $(C_SOURCE_FILES)
 	ls $(C_SOURCE_FILES) | gtags -f - --gtagsconf .globalrc
diff --git a/third_party/mbedtls/repo/README.md b/third_party/mbedtls/repo/README.md
index ac2a6ab..eb3829c 100644
--- a/third_party/mbedtls/repo/README.md
+++ b/third_party/mbedtls/repo/README.md
@@ -25,6 +25,8 @@
 1. Run `make apidoc`.
 1. Browse `apidoc/index.html` or `apidoc/modules.html`.
 
+For other sources of documentation, see the [SUPPORT](SUPPORT.md) document.
+
 Compiling
 ---------
 
@@ -36,7 +38,7 @@
 
 The main systems used for development are CMake and GNU Make. Those systems are always complete and up-to-date. The others should reflect all changes present in the CMake and Make build system, although features may not be ported there automatically.
 
-The Make and CMake build systems create three libraries: libmbedcrypto, libmbedx509, and libmbedtls. Note that libmbedtls depends on libmbedx509 and libmbedcrypto, and libmbedx509 depends on libmbedcrypto. As a result, some linkers will expect flags to be in a specific order, for example the GNU linker wants `-lmbedtls -lmbedx509 -lmbedcrypto`. Also, when loading shared libraries using dlopen(), you'll need to load libmbedcrypto first, then libmbedx509, before you can load libmbedtls.
+The Make and CMake build systems create three libraries: libmbedcrypto, libmbedx509, and libmbedtls. Note that libmbedtls depends on libmbedx509 and libmbedcrypto, and libmbedx509 depends on libmbedcrypto. As a result, some linkers will expect flags to be in a specific order, for example the GNU linker wants `-lmbedtls -lmbedx509 -lmbedcrypto`.
 
 ### Tool versions
 
@@ -195,6 +197,14 @@
 -   [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)
 
+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:
+
+- Bytes must be 8 bits.
+- All-bits-zero must be a valid representation of a null pointer.
+- Signed integers must be represented using two's complement.
+- `int` and `size_t` must be at least 32 bits wide.
+- The types `uint8_t`, `uint16_t`, `uint32_t` and their signed equivalents must be available.
+
 PSA cryptography API
 --------------------
 
@@ -245,3 +255,10 @@
 ------------
 
 We gratefully accept bug reports and contributions from the community. Please see the [contributing guidelines](CONTRIBUTING.md) for details on how to do this.
+
+Contact
+-------
+
+* 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).
+* 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
new file mode 100644
index 0000000..bd18f6c
--- /dev/null
+++ b/third_party/mbedtls/repo/SECURITY.md
@@ -0,0 +1,20 @@
+## Reporting Vulneratibilities
+
+If you think you have found an Mbed TLS security vulnerability, then please
+send an email to the security team at
+<mbed-tls-security@lists.trustedfirmware.org>.
+
+## Security Incident Handling Process
+
+Our security process is detailled in our
+[security
+center](https://developer.trustedfirmware.org/w/mbed-tls/security-center/).
+
+Its primary goal is to ensure fixes are ready to be deployed when the issue
+goes public.
+
+## Maintained branches
+
+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.
diff --git a/third_party/mbedtls/repo/SUPPORT.md b/third_party/mbedtls/repo/SUPPORT.md
new file mode 100644
index 0000000..1bc0695
--- /dev/null
+++ b/third_party/mbedtls/repo/SUPPORT.md
@@ -0,0 +1,15 @@
+## Documentation
+
+Here are some useful sources of information about using Mbed TLS:
+
+- API documentation, see the [Documentation section of the
+  README](README.md#License);
+- the `docs` directory in the source tree;
+- the [Mbed TLS knowledge Base](https://tls.mbed.org/kb);
+- the [Mbed TLS mailing-list
+  archives](https://lists.trustedfirmware.org/pipermail/mbed-tls/).
+
+## 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).
diff --git a/third_party/mbedtls/repo/configs/config-psa-crypto.h b/third_party/mbedtls/repo/configs/config-psa-crypto.h
deleted file mode 100644
index b98fc9c..0000000
--- a/third_party/mbedtls/repo/configs/config-psa-crypto.h
+++ /dev/null
@@ -1,3371 +0,0 @@
-/**
- * \file config.h
- *
- * \brief Configuration options (set of defines)
- *
- *  This set of compile-time options may be used to enable
- *  or disable features selectively, and reduce the global
- *  memory footprint.
- */
-/*
- *  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.
- */
-
-#ifndef MBEDTLS_CONFIG_H
-#define MBEDTLS_CONFIG_H
-
-#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_DEPRECATE)
-#define _CRT_SECURE_NO_DEPRECATE 1
-#endif
-
-/**
- * \name SECTION: System support
- *
- * This section sets system specific settings.
- * \{
- */
-
-/**
- * \def MBEDTLS_HAVE_ASM
- *
- * The compiler has support for asm().
- *
- * Requires support for asm() in compiler.
- *
- * Used in:
- *      library/aria.c
- *      library/timing.c
- *      include/mbedtls/bn_mul.h
- *
- * Required by:
- *      MBEDTLS_AESNI_C
- *      MBEDTLS_PADLOCK_C
- *
- * Comment to disable the use of assembly code.
- */
-#define MBEDTLS_HAVE_ASM
-
-/**
- * \def MBEDTLS_NO_UDBL_DIVISION
- *
- * The platform lacks support for double-width integer division (64-bit
- * division on a 32-bit platform, 128-bit division on a 64-bit platform).
- *
- * Used in:
- *      include/mbedtls/bignum.h
- *      library/bignum.c
- *
- * The bignum code uses double-width division to speed up some operations.
- * Double-width division is often implemented in software that needs to
- * be linked with the program. The presence of a double-width integer
- * type is usually detected automatically through preprocessor macros,
- * but the automatic detection cannot know whether the code needs to
- * and can be linked with an implementation of division for that type.
- * By default division is assumed to be usable if the type is present.
- * Uncomment this option to prevent the use of double-width division.
- *
- * Note that division for the native integer type is always required.
- * Furthermore, a 64-bit type is always required even on a 32-bit
- * platform, but it need not support multiplication or division. In some
- * cases it is also desirable to disable some double-width operations. For
- * example, if double-width division is implemented in software, disabling
- * it can reduce code size in some embedded targets.
- */
-//#define MBEDTLS_NO_UDBL_DIVISION
-
-/**
- * \def MBEDTLS_NO_64BIT_MULTIPLICATION
- *
- * The platform lacks support for 32x32 -> 64-bit multiplication.
- *
- * Used in:
- *      library/poly1305.c
- *
- * Some parts of the library may use multiplication of two unsigned 32-bit
- * operands with a 64-bit result in order to speed up computations. On some
- * platforms, this is not available in hardware and has to be implemented in
- * software, usually in a library provided by the toolchain.
- *
- * Sometimes it is not desirable to have to link to that library. This option
- * removes the dependency of that library on platforms that lack a hardware
- * 64-bit multiplier by embedding a software implementation in Mbed TLS.
- *
- * Note that depending on the compiler, this may decrease performance compared
- * to using the library function provided by the toolchain.
- */
-//#define MBEDTLS_NO_64BIT_MULTIPLICATION
-
-/**
- * \def MBEDTLS_HAVE_SSE2
- *
- * CPU supports SSE2 instruction set.
- *
- * Uncomment if the CPU supports SSE2 (IA-32 specific).
- */
-//#define MBEDTLS_HAVE_SSE2
-
-/**
- * \def MBEDTLS_HAVE_TIME
- *
- * System has time.h and time().
- * The time does not need to be correct, only time differences are used,
- * by contrast with MBEDTLS_HAVE_TIME_DATE
- *
- * Defining MBEDTLS_HAVE_TIME allows you to specify MBEDTLS_PLATFORM_TIME_ALT,
- * MBEDTLS_PLATFORM_TIME_MACRO, MBEDTLS_PLATFORM_TIME_TYPE_MACRO and
- * MBEDTLS_PLATFORM_STD_TIME.
- *
- * Comment if your system does not support time functions
- */
-#define MBEDTLS_HAVE_TIME
-
-/**
- * \def MBEDTLS_HAVE_TIME_DATE
- *
- * System has time.h, time(), and an implementation for
- * mbedtls_platform_gmtime_r() (see below).
- * The time needs to be correct (not necessarily very accurate, but at least
- * the date should be correct). This is used to verify the validity period of
- * X.509 certificates.
- *
- * Comment if your system does not have a correct clock.
- *
- * \note mbedtls_platform_gmtime_r() is an abstraction in platform_util.h that
- * behaves similarly to the gmtime_r() function from the C standard. Refer to
- * the documentation for mbedtls_platform_gmtime_r() for more information.
- *
- * \note It is possible to configure an implementation for
- * mbedtls_platform_gmtime_r() at compile-time by using the macro
- * MBEDTLS_PLATFORM_GMTIME_R_ALT.
- */
-#define MBEDTLS_HAVE_TIME_DATE
-
-/**
- * \def MBEDTLS_PLATFORM_MEMORY
- *
- * Enable the memory allocation layer.
- *
- * 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
- * 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
- * MBEDTLS_PLATFORM_{CALLOC,FREE}_MACROs will allow you to specify the
- * alternate function at compile time.
- *
- * Requires: MBEDTLS_PLATFORM_C
- *
- * Enable this layer to allow use of alternative memory allocators.
- */
-//#define MBEDTLS_PLATFORM_MEMORY
-
-/**
- * \def MBEDTLS_PLATFORM_NO_STD_FUNCTIONS
- *
- * Do not assign standard functions in the platform layer (e.g. calloc() to
- * MBEDTLS_PLATFORM_STD_CALLOC and printf() to MBEDTLS_PLATFORM_STD_PRINTF)
- *
- * This makes sure there are no linking errors on platforms that do not support
- * these functions. You will HAVE to provide alternatives, either at runtime
- * via the platform_set_xxx() functions or at compile time by setting
- * the MBEDTLS_PLATFORM_STD_XXX defines, or enabling a
- * MBEDTLS_PLATFORM_XXX_MACRO.
- *
- * Requires: MBEDTLS_PLATFORM_C
- *
- * Uncomment to prevent default assignment of standard functions in the
- * platform layer.
- */
-//#define MBEDTLS_PLATFORM_NO_STD_FUNCTIONS
-
-/**
- * \def MBEDTLS_PLATFORM_EXIT_ALT
- *
- * 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
- * provide a function "mbedtls_platform_set_printf()" that allows you to set an
- * alternative printf function pointer.
- *
- * All these define require MBEDTLS_PLATFORM_C to be defined!
- *
- * \note MBEDTLS_PLATFORM_SNPRINTF_ALT is required on Windows;
- * it will be enabled automatically by check_config.h
- *
- * \warning MBEDTLS_PLATFORM_XXX_ALT cannot be defined at the same time as
- * MBEDTLS_PLATFORM_XXX_MACRO!
- *
- * Requires: MBEDTLS_PLATFORM_TIME_ALT requires MBEDTLS_HAVE_TIME
- *
- * Uncomment a macro to enable alternate implementation of specific base
- * platform function
- */
-//#define MBEDTLS_PLATFORM_EXIT_ALT
-//#define MBEDTLS_PLATFORM_TIME_ALT
-//#define MBEDTLS_PLATFORM_FPRINTF_ALT
-//#define MBEDTLS_PLATFORM_PRINTF_ALT
-//#define MBEDTLS_PLATFORM_SNPRINTF_ALT
-//#define MBEDTLS_PLATFORM_VSNPRINTF_ALT
-//#define MBEDTLS_PLATFORM_NV_SEED_ALT
-//#define MBEDTLS_PLATFORM_SETUP_TEARDOWN_ALT
-
-/**
- * \def MBEDTLS_DEPRECATED_WARNING
- *
- * Mark deprecated functions so that they generate a warning if used.
- * Functions deprecated in one version will usually be removed in the next
- * version. You can enable this to help you prepare the transition to a new
- * major version by making sure your code is not using these functions.
- *
- * This only works with GCC and Clang. With other compilers, you may want to
- * use MBEDTLS_DEPRECATED_REMOVED
- *
- * Uncomment to get warnings on using deprecated functions.
- */
-//#define MBEDTLS_DEPRECATED_WARNING
-
-/**
- * \def MBEDTLS_DEPRECATED_REMOVED
- *
- * Remove deprecated functions so that they generate an error if used.
- * Functions deprecated in one version will usually be removed in the next
- * version. You can enable this to help you prepare the transition to a new
- * major version by making sure your code is not using these functions.
- *
- * Uncomment to get errors on using deprecated functions.
- */
-//#define MBEDTLS_DEPRECATED_REMOVED
-
-/**
- * \def MBEDTLS_CHECK_PARAMS
- *
- * This configuration option controls whether the library validates more of
- * the parameters passed to it.
- *
- * When this flag is not defined, the library only attempts to validate an
- * input parameter if: (1) they may come from the outside world (such as the
- * network, the filesystem, etc.) or (2) not validating them could result in
- * internal memory errors such as overflowing a buffer controlled by the
- * library. On the other hand, it doesn't attempt to validate parameters whose
- * values are fully controlled by the application (such as pointers).
- *
- * When this flag is defined, the library additionally attempts to validate
- * parameters that are fully controlled by the application, and should always
- * be valid if the application code is fully correct and trusted.
- *
- * For example, when a function accepts as input a pointer to a buffer that may
- * contain untrusted data, and its documentation mentions that this pointer
- * must not be NULL:
- * - the pointer is checked to be non-NULL only if this option is enabled
- * - the content of the buffer is always validated
- *
- * When this flag is defined, if a library function receives a parameter that
- * is invalid, it will:
- * - invoke the macro MBEDTLS_PARAM_FAILED() which by default expands to a
- *   call to the function mbedtls_param_failed()
- * - immediately return (with a specific error code unless the function
- *   returns void and can't communicate an error).
- *
- * When defining this flag, you also need to:
- * - either provide a definition of the function mbedtls_param_failed() in
- *   your application (see platform_util.h for its prototype) as the library
- *   calls that function, but does not provide a default definition for it,
- * - or provide a different definition of the macro MBEDTLS_PARAM_FAILED()
- *   below if the above mechanism is not flexible enough to suit your needs.
- *   See the documentation of this macro later in this file.
- *
- * Uncomment to enable validation of application-controlled parameters.
- */
-//#define MBEDTLS_CHECK_PARAMS
-
-/* \} name SECTION: System 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.
- * \{
- */
-
-/**
- * \def MBEDTLS_TIMING_ALT
- *
- * Uncomment to provide your own alternate implementation for mbedtls_timing_hardclock(),
- * mbedtls_timing_get_timer(), mbedtls_set_alarm(), mbedtls_set/get_delay()
- *
- * Only works if you have MBEDTLS_TIMING_C enabled.
- *
- * You will need to provide a header "timing_alt.h" and an implementation at
- * compile time.
- */
-//#define MBEDTLS_TIMING_ALT
-
-/**
- * \def MBEDTLS_AES_ALT
- *
- * 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.
- *
- * 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
- * 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.
- *
- * Uncomment a macro to enable alternate implementation of the corresponding
- * module.
- *
- * \warning   MD2, MD4, MD5, ARC4, DES and SHA-1 are considered weak and their
- *            use constitutes a security risk. If possible, we recommend
- *            avoiding dependencies on them, and considering stronger message
- *            digests and ciphers instead.
- *
- */
-//#define MBEDTLS_AES_ALT
-//#define MBEDTLS_ARC4_ALT
-//#define MBEDTLS_ARIA_ALT
-//#define MBEDTLS_BLOWFISH_ALT
-//#define MBEDTLS_CAMELLIA_ALT
-//#define MBEDTLS_CCM_ALT
-//#define MBEDTLS_CHACHA20_ALT
-//#define MBEDTLS_CHACHAPOLY_ALT
-//#define MBEDTLS_CMAC_ALT
-//#define MBEDTLS_DES_ALT
-//#define MBEDTLS_DHM_ALT
-//#define MBEDTLS_ECJPAKE_ALT
-//#define MBEDTLS_GCM_ALT
-//#define MBEDTLS_NIST_KW_ALT
-//#define MBEDTLS_MD2_ALT
-//#define MBEDTLS_MD4_ALT
-//#define MBEDTLS_MD5_ALT
-//#define MBEDTLS_POLY1305_ALT
-//#define MBEDTLS_RIPEMD160_ALT
-//#define MBEDTLS_RSA_ALT
-//#define MBEDTLS_SHA1_ALT
-//#define MBEDTLS_SHA256_ALT
-//#define MBEDTLS_SHA512_ALT
-//#define MBEDTLS_XTEA_ALT
-
-/*
- * When replacing the elliptic curve module, pleace consider, that it is
- * implemented with two .c files:
- *      - ecp.c
- *      - ecp_curves.c
- * You can replace them very much like all the other MBEDTLS__MODULE_NAME__ALT
- * macros as described above. The only difference is that you have to make sure
- * that you provide functionality for both .c files.
- */
-//#define MBEDTLS_ECP_ALT
-
-/**
- * \def MBEDTLS_MD2_PROCESS_ALT
- *
- * 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
- * used, in contrast to the MBEDTLS__MODULE_NAME__ALT flags.
- *
- * 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
- * with this definition.
- *
- * \note Because of a signature change, the core AES encryption and decryption routines are
- *       currently named mbedtls_aes_internal_encrypt and mbedtls_aes_internal_decrypt,
- *       respectively. When setting up alternative implementations, these functions should
- *       be overridden, but the wrapper functions mbedtls_aes_decrypt and mbedtls_aes_encrypt
- *       must stay untouched.
- *
- * \note If you use the AES_xxx_ALT macros, then is is recommended to also set
- *       MBEDTLS_AES_ROM_TABLES in order to help the linker garbage-collect the AES
- *       tables.
- *
- * Uncomment a macro to enable alternate implementation of the corresponding
- * function.
- *
- * \warning   MD2, MD4, MD5, DES and SHA-1 are considered weak and their use
- *            constitutes a security risk. If possible, we recommend avoiding
- *            dependencies on them, and considering stronger message digests
- *            and ciphers instead.
- *
- */
-//#define MBEDTLS_MD2_PROCESS_ALT
-//#define MBEDTLS_MD4_PROCESS_ALT
-//#define MBEDTLS_MD5_PROCESS_ALT
-//#define MBEDTLS_RIPEMD160_PROCESS_ALT
-//#define MBEDTLS_SHA1_PROCESS_ALT
-//#define MBEDTLS_SHA256_PROCESS_ALT
-//#define MBEDTLS_SHA512_PROCESS_ALT
-//#define MBEDTLS_DES_SETKEY_ALT
-//#define MBEDTLS_DES_CRYPT_ECB_ALT
-//#define MBEDTLS_DES3_CRYPT_ECB_ALT
-//#define MBEDTLS_AES_SETKEY_ENC_ALT
-//#define MBEDTLS_AES_SETKEY_DEC_ALT
-//#define MBEDTLS_AES_ENCRYPT_ALT
-//#define MBEDTLS_AES_DECRYPT_ALT
-//#define MBEDTLS_ECDH_GEN_PUBLIC_ALT
-//#define MBEDTLS_ECDH_COMPUTE_SHARED_ALT
-//#define MBEDTLS_ECDSA_VERIFY_ALT
-//#define MBEDTLS_ECDSA_SIGN_ALT
-//#define MBEDTLS_ECDSA_GENKEY_ALT
-
-/**
- * \def MBEDTLS_ECP_INTERNAL_ALT
- *
- * 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
- * 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
- * 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.
- *
- * Any of these options become available by defining MBEDTLS_ECP_INTERNAL_ALT
- * and implementing the following functions:
- *      unsigned char mbedtls_internal_ecp_grp_capable(
- *          const mbedtls_ecp_group *grp )
- *      int  mbedtls_internal_ecp_init( const mbedtls_ecp_group *grp )
- *      void mbedtls_internal_ecp_free( const mbedtls_ecp_group *grp )
- * The mbedtls_internal_ecp_grp_capable function should return 1 if the
- * replacement functions implement arithmetic for the given group and 0
- * otherwise.
- * The functions mbedtls_internal_ecp_init and mbedtls_internal_ecp_free are
- * called before and after each point operation and provide an opportunity to
- * implement optimized set up and tear down instructions.
- *
- * Example: In case you uncomment MBEDTLS_ECP_INTERNAL_ALT and
- * 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 is
- * supported (your mbedtls_internal_ecp_grp_capable function returns 1 when
- * receives it as an argument). If the group is not supported then the original
- * implementation is used. The other functions and the definition of
- * mbedtls_ecp_group and mbedtls_ecp_point will not change, so your
- * implementation of mbedtls_internal_ecp_double_jac and
- * mbedtls_internal_ecp_grp_capable must be compatible with this definition.
- *
- * Uncomment a macro to enable alternate implementation of the corresponding
- * function.
- */
-/* Required for all the functions in this section */
-//#define MBEDTLS_ECP_INTERNAL_ALT
-/* Support for Weierstrass curves with Jacobi representation */
-//#define MBEDTLS_ECP_RANDOMIZE_JAC_ALT
-//#define MBEDTLS_ECP_ADD_MIXED_ALT
-//#define MBEDTLS_ECP_DOUBLE_JAC_ALT
-//#define MBEDTLS_ECP_NORMALIZE_JAC_MANY_ALT
-//#define MBEDTLS_ECP_NORMALIZE_JAC_ALT
-/* Support for curves with Montgomery arithmetic */
-//#define MBEDTLS_ECP_DOUBLE_ADD_MXZ_ALT
-//#define MBEDTLS_ECP_RANDOMIZE_MXZ_ALT
-//#define MBEDTLS_ECP_NORMALIZE_MXZ_ALT
-
-/**
- * \def MBEDTLS_TEST_NULL_ENTROPY
- *
- * 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).
- *
- * WARNING! This switch MUST be disabled in production builds, and is suitable
- * only for development.
- * Enabling the switch negates any security provided by the library.
- *
- * Requires MBEDTLS_ENTROPY_C, MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES
- *
- */
-//#define MBEDTLS_TEST_NULL_ENTROPY
-
-/**
- * \def MBEDTLS_ENTROPY_HARDWARE_ALT
- *
- * 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
- * prototype as declared in entropy_poll.h, and accept NULL as first argument.
- *
- * Uncomment to use your own hardware entropy collector.
- */
-//#define MBEDTLS_ENTROPY_HARDWARE_ALT
-
-/**
- * \def MBEDTLS_AES_ROM_TABLES
- *
- * Use precomputed AES tables stored in ROM.
- *
- * Uncomment this macro to use precomputed AES tables stored in ROM.
- * Comment this macro to generate AES tables in RAM at runtime.
- *
- * Tradeoff: Using precomputed ROM tables reduces RAM usage by ~8kb
- * (or ~2kb if \c MBEDTLS_AES_FEWER_TABLES is used) and reduces the
- * initialization time before the first AES operation can be performed.
- * It comes at the cost of additional ~8kb ROM use (resp. ~2kb if \c
- * MBEDTLS_AES_FEWER_TABLES below is used), and potentially degraded
- * performance if ROM access is slower than RAM access.
- *
- * This option is independent of \c MBEDTLS_AES_FEWER_TABLES.
- *
- */
-//#define MBEDTLS_AES_ROM_TABLES
-
-/**
- * \def MBEDTLS_AES_FEWER_TABLES
- *
- * Use less ROM/RAM for AES tables.
- *
- * Uncommenting this macro omits 75% of the AES tables from
- * ROM / RAM (depending on the value of \c MBEDTLS_AES_ROM_TABLES)
- * by computing their values on the fly during operations
- * (the tables are entry-wise rotations of one another).
- *
- * Tradeoff: Uncommenting this reduces the RAM / ROM footprint
- * by ~6kb but at the cost of more arithmetic operations during
- * runtime. Specifically, one has to compare 4 accesses within
- * different tables to 4 accesses with additional arithmetic
- * operations within the same table. The performance gain/loss
- * depends on the system and memory details.
- *
- * This option is independent of \c MBEDTLS_AES_ROM_TABLES.
- *
- */
-//#define MBEDTLS_AES_FEWER_TABLES
-
-/**
- * \def MBEDTLS_CAMELLIA_SMALL_MEMORY
- *
- * Use less ROM for the Camellia implementation (saves about 768 bytes).
- *
- * Uncomment this macro to use less memory for Camellia.
- */
-//#define MBEDTLS_CAMELLIA_SMALL_MEMORY
-
-/**
- * \def MBEDTLS_CIPHER_MODE_CBC
- *
- * Enable Cipher Block Chaining mode (CBC) for symmetric ciphers.
- */
-#define MBEDTLS_CIPHER_MODE_CBC
-
-/**
- * \def MBEDTLS_CIPHER_MODE_CFB
- *
- * Enable Cipher Feedback mode (CFB) for symmetric ciphers.
- */
-#define MBEDTLS_CIPHER_MODE_CFB
-
-/**
- * \def MBEDTLS_CIPHER_MODE_CTR
- *
- * Enable Counter Block Cipher mode (CTR) for symmetric ciphers.
- */
-#define MBEDTLS_CIPHER_MODE_CTR
-
-/**
- * \def MBEDTLS_CIPHER_MODE_OFB
- *
- * Enable Output Feedback mode (OFB) for symmetric ciphers.
- */
-#define MBEDTLS_CIPHER_MODE_OFB
-
-/**
- * \def MBEDTLS_CIPHER_MODE_XTS
- *
- * Enable Xor-encrypt-xor with ciphertext stealing mode (XTS) for AES.
- */
-#define MBEDTLS_CIPHER_MODE_XTS
-
-/**
- * \def MBEDTLS_CIPHER_NULL_CIPHER
- *
- * Enable NULL cipher.
- * Warning: Only do so when you know what you are doing. This allows for
- * encryption or channels without any security!
- *
- * Requires MBEDTLS_ENABLE_WEAK_CIPHERSUITES as well to enable
- * the following ciphersuites:
- *      MBEDTLS_TLS_ECDH_ECDSA_WITH_NULL_SHA
- *      MBEDTLS_TLS_ECDH_RSA_WITH_NULL_SHA
- *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_NULL_SHA
- *      MBEDTLS_TLS_ECDHE_RSA_WITH_NULL_SHA
- *      MBEDTLS_TLS_ECDHE_PSK_WITH_NULL_SHA384
- *      MBEDTLS_TLS_ECDHE_PSK_WITH_NULL_SHA256
- *      MBEDTLS_TLS_ECDHE_PSK_WITH_NULL_SHA
- *      MBEDTLS_TLS_DHE_PSK_WITH_NULL_SHA384
- *      MBEDTLS_TLS_DHE_PSK_WITH_NULL_SHA256
- *      MBEDTLS_TLS_DHE_PSK_WITH_NULL_SHA
- *      MBEDTLS_TLS_RSA_WITH_NULL_SHA256
- *      MBEDTLS_TLS_RSA_WITH_NULL_SHA
- *      MBEDTLS_TLS_RSA_WITH_NULL_MD5
- *      MBEDTLS_TLS_RSA_PSK_WITH_NULL_SHA384
- *      MBEDTLS_TLS_RSA_PSK_WITH_NULL_SHA256
- *      MBEDTLS_TLS_RSA_PSK_WITH_NULL_SHA
- *      MBEDTLS_TLS_PSK_WITH_NULL_SHA384
- *      MBEDTLS_TLS_PSK_WITH_NULL_SHA256
- *      MBEDTLS_TLS_PSK_WITH_NULL_SHA
- *
- * Uncomment this macro to enable the NULL cipher and ciphersuites
- */
-//#define MBEDTLS_CIPHER_NULL_CIPHER
-
-/**
- * \def MBEDTLS_CIPHER_PADDING_PKCS7
- *
- * MBEDTLS_CIPHER_PADDING_XXX: Uncomment or comment macros to add support for
- * specific padding modes in the cipher layer with cipher modes that support
- * padding (e.g. CBC)
- *
- * If you disable all padding modes, only full blocks can be used with CBC.
- *
- * Enable padding modes in the cipher layer.
- */
-#define MBEDTLS_CIPHER_PADDING_PKCS7
-#define MBEDTLS_CIPHER_PADDING_ONE_AND_ZEROS
-#define MBEDTLS_CIPHER_PADDING_ZEROS_AND_LEN
-#define MBEDTLS_CIPHER_PADDING_ZEROS
-
-/**
- * \def MBEDTLS_ENABLE_WEAK_CIPHERSUITES
- *
- * Enable weak ciphersuites in SSL / TLS.
- * Warning: Only do so when you know what you are doing. This allows for
- * channels with virtually no security at all!
- *
- * This enables the following ciphersuites:
- *      MBEDTLS_TLS_RSA_WITH_DES_CBC_SHA
- *      MBEDTLS_TLS_DHE_RSA_WITH_DES_CBC_SHA
- *
- * Uncomment this macro to enable weak ciphersuites
- *
- * \warning   DES is considered a weak cipher and its use constitutes a
- *            security risk. We recommend considering stronger ciphers instead.
- */
-//#define MBEDTLS_ENABLE_WEAK_CIPHERSUITES
-
-/**
- * \def MBEDTLS_REMOVE_ARC4_CIPHERSUITES
- *
- * Remove RC4 ciphersuites by default in SSL / TLS.
- * This flag removes the ciphersuites based on RC4 from the default list as
- * returned by mbedtls_ssl_list_ciphersuites(). However, it is still possible to
- * enable (some of) them with mbedtls_ssl_conf_ciphersuites() by including them
- * explicitly.
- *
- * Uncomment this macro to remove RC4 ciphersuites by default.
- */
-#define MBEDTLS_REMOVE_ARC4_CIPHERSUITES
-
-/**
- * \def MBEDTLS_ECP_DP_SECP192R1_ENABLED
- *
- * MBEDTLS_ECP_XXXX_ENABLED: Enables specific curves within the Elliptic Curve
- * module.  By default all supported curves are enabled.
- *
- * Comment macros to disable the curve and functions for it
- */
-#define MBEDTLS_ECP_DP_SECP192R1_ENABLED
-#define MBEDTLS_ECP_DP_SECP224R1_ENABLED
-#define MBEDTLS_ECP_DP_SECP256R1_ENABLED
-#define MBEDTLS_ECP_DP_SECP384R1_ENABLED
-#define MBEDTLS_ECP_DP_SECP521R1_ENABLED
-#define MBEDTLS_ECP_DP_SECP192K1_ENABLED
-#define MBEDTLS_ECP_DP_SECP224K1_ENABLED
-#define MBEDTLS_ECP_DP_SECP256K1_ENABLED
-#define MBEDTLS_ECP_DP_BP256R1_ENABLED
-#define MBEDTLS_ECP_DP_BP384R1_ENABLED
-#define MBEDTLS_ECP_DP_BP512R1_ENABLED
-#define MBEDTLS_ECP_DP_CURVE25519_ENABLED
-#define MBEDTLS_ECP_DP_CURVE448_ENABLED
-
-/**
- * \def MBEDTLS_ECP_NIST_OPTIM
- *
- * Enable specific 'modulo p' routines for each NIST prime.
- * Depending on the prime and architecture, makes operations 4 to 8 times
- * faster on the corresponding curve.
- *
- * Comment this macro to disable NIST curves optimisation.
- */
-#define MBEDTLS_ECP_NIST_OPTIM
-
-/**
- * \def MBEDTLS_ECP_RESTARTABLE
- *
- * Enable "non-blocking" ECC operations that can return early and be resumed.
- *
- * This allows various functions to pause by returning
- * #MBEDTLS_ERR_ECP_IN_PROGRESS (or, for functions in the SSL module,
- * #MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS) and then be called later again in
- * order to further progress and eventually complete their operation. This is
- * controlled through mbedtls_ecp_set_max_ops() which limits the maximum
- * number of ECC operations a function may perform before pausing; see
- * mbedtls_ecp_set_max_ops() for more information.
- *
- * 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.
- *
- * \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 and MBEDTLS_ECDSA_XXX_ALT.
- */
-//#define MBEDTLS_ECP_RESTARTABLE
-
-/**
- * \def MBEDTLS_ECDSA_DETERMINISTIC
- *
- * Enable deterministic ECDSA (RFC 6979).
- * Standard ECDSA is "fragile" in the sense that lack of entropy when signing
- * may result in a compromise of the long-term signing key. This is avoided by
- * the deterministic variant.
- *
- * Requires: MBEDTLS_HMAC_DRBG_C
- *
- * Comment this macro to disable deterministic ECDSA.
- */
-#define MBEDTLS_ECDSA_DETERMINISTIC
-
-/**
- * \def MBEDTLS_KEY_EXCHANGE_PSK_ENABLED
- *
- * Enable the PSK based ciphersuite modes in SSL / TLS.
- *
- * This enables the following ciphersuites (if other requisites are
- * enabled as well):
- *      MBEDTLS_TLS_PSK_WITH_AES_256_GCM_SHA384
- *      MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA384
- *      MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA
- *      MBEDTLS_TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384
- *      MBEDTLS_TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384
- *      MBEDTLS_TLS_PSK_WITH_AES_128_GCM_SHA256
- *      MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA256
- *      MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA
- *      MBEDTLS_TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256
- *      MBEDTLS_TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256
- *      MBEDTLS_TLS_PSK_WITH_3DES_EDE_CBC_SHA
- *      MBEDTLS_TLS_PSK_WITH_RC4_128_SHA
- */
-#define MBEDTLS_KEY_EXCHANGE_PSK_ENABLED
-
-/**
- * \def MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED
- *
- * Enable the DHE-PSK based ciphersuite modes in SSL / TLS.
- *
- * Requires: MBEDTLS_DHM_C
- *
- * This enables the following ciphersuites (if other requisites are
- * enabled as well):
- *      MBEDTLS_TLS_DHE_PSK_WITH_AES_256_GCM_SHA384
- *      MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384
- *      MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA
- *      MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384
- *      MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384
- *      MBEDTLS_TLS_DHE_PSK_WITH_AES_128_GCM_SHA256
- *      MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256
- *      MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA
- *      MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256
- *      MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256
- *      MBEDTLS_TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA
- *      MBEDTLS_TLS_DHE_PSK_WITH_RC4_128_SHA
- *
- * \warning    Using DHE constitutes a security risk as it
- *             is not possible to validate custom DH parameters.
- *             If possible, it is recommended users should consider
- *             preferring other methods of key exchange.
- *             See dhm.h for more details.
- *
- */
-#define MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED
-
-/**
- * \def MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED
- *
- * Enable the ECDHE-PSK based ciphersuite modes in SSL / TLS.
- *
- * Requires: MBEDTLS_ECDH_C
- *
- * This enables the following ciphersuites (if other requisites are
- * enabled as well):
- *      MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384
- *      MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA
- *      MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384
- *      MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256
- *      MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA
- *      MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256
- *      MBEDTLS_TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA
- *      MBEDTLS_TLS_ECDHE_PSK_WITH_RC4_128_SHA
- */
-#define MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED
-
-/**
- * \def MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED
- *
- * Enable the RSA-PSK based ciphersuite modes in SSL / TLS.
- *
- * Requires: MBEDTLS_RSA_C, MBEDTLS_PKCS1_V15,
- *           MBEDTLS_X509_CRT_PARSE_C
- *
- * This enables the following ciphersuites (if other requisites are
- * enabled as well):
- *      MBEDTLS_TLS_RSA_PSK_WITH_AES_256_GCM_SHA384
- *      MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA384
- *      MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA
- *      MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384
- *      MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384
- *      MBEDTLS_TLS_RSA_PSK_WITH_AES_128_GCM_SHA256
- *      MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA256
- *      MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA
- *      MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256
- *      MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256
- *      MBEDTLS_TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA
- *      MBEDTLS_TLS_RSA_PSK_WITH_RC4_128_SHA
- */
-#define MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED
-
-/**
- * \def MBEDTLS_KEY_EXCHANGE_RSA_ENABLED
- *
- * Enable the RSA-only based ciphersuite modes in SSL / TLS.
- *
- * Requires: MBEDTLS_RSA_C, MBEDTLS_PKCS1_V15,
- *           MBEDTLS_X509_CRT_PARSE_C
- *
- * This enables the following ciphersuites (if other requisites are
- * enabled as well):
- *      MBEDTLS_TLS_RSA_WITH_AES_256_GCM_SHA384
- *      MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA256
- *      MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA
- *      MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384
- *      MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256
- *      MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA
- *      MBEDTLS_TLS_RSA_WITH_AES_128_GCM_SHA256
- *      MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA256
- *      MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA
- *      MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256
- *      MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256
- *      MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA
- *      MBEDTLS_TLS_RSA_WITH_3DES_EDE_CBC_SHA
- *      MBEDTLS_TLS_RSA_WITH_RC4_128_SHA
- *      MBEDTLS_TLS_RSA_WITH_RC4_128_MD5
- */
-#define MBEDTLS_KEY_EXCHANGE_RSA_ENABLED
-
-/**
- * \def MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED
- *
- * Enable the DHE-RSA based ciphersuite modes in SSL / TLS.
- *
- * Requires: MBEDTLS_DHM_C, MBEDTLS_RSA_C, MBEDTLS_PKCS1_V15,
- *           MBEDTLS_X509_CRT_PARSE_C
- *
- * This enables the following ciphersuites (if other requisites are
- * enabled as well):
- *      MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
- *      MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
- *      MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA
- *      MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384
- *      MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256
- *      MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA
- *      MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
- *      MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
- *      MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA
- *      MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256
- *      MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256
- *      MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA
- *      MBEDTLS_TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA
- *
- * \warning    Using DHE constitutes a security risk as it
- *             is not possible to validate custom DH parameters.
- *             If possible, it is recommended users should consider
- *             preferring other methods of key exchange.
- *             See dhm.h for more details.
- *
- */
-#define MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED
-
-/**
- * \def MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED
- *
- * Enable the ECDHE-RSA based ciphersuite modes in SSL / TLS.
- *
- * Requires: MBEDTLS_ECDH_C, MBEDTLS_RSA_C, MBEDTLS_PKCS1_V15,
- *           MBEDTLS_X509_CRT_PARSE_C
- *
- * This enables the following ciphersuites (if other requisites are
- * enabled as well):
- *      MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
- *      MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
- *      MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
- *      MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384
- *      MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384
- *      MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
- *      MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
- *      MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
- *      MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256
- *      MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256
- *      MBEDTLS_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
- *      MBEDTLS_TLS_ECDHE_RSA_WITH_RC4_128_SHA
- */
-#define MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED
-
-/**
- * \def MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
- *
- * Enable the ECDHE-ECDSA based ciphersuite modes in SSL / TLS.
- *
- * Requires: MBEDTLS_ECDH_C, MBEDTLS_ECDSA_C, MBEDTLS_X509_CRT_PARSE_C,
- *
- * This enables the following ciphersuites (if other requisites are
- * enabled as well):
- *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
- *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
- *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
- *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384
- *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384
- *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
- *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
- *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
- *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256
- *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256
- *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA
- *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_RC4_128_SHA
- */
-#define MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
-
-/**
- * \def MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED
- *
- * Enable the ECDH-ECDSA based ciphersuite modes in SSL / TLS.
- *
- * Requires: MBEDTLS_ECDH_C, MBEDTLS_X509_CRT_PARSE_C
- *
- * This enables the following ciphersuites (if other requisites are
- * enabled as well):
- *      MBEDTLS_TLS_ECDH_ECDSA_WITH_RC4_128_SHA
- *      MBEDTLS_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA
- *      MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA
- *      MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA
- *      MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256
- *      MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384
- *      MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256
- *      MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384
- *      MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256
- *      MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384
- *      MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256
- *      MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384
- */
-#define MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED
-
-/**
- * \def MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED
- *
- * Enable the ECDH-RSA based ciphersuite modes in SSL / TLS.
- *
- * Requires: MBEDTLS_ECDH_C, MBEDTLS_X509_CRT_PARSE_C
- *
- * This enables the following ciphersuites (if other requisites are
- * enabled as well):
- *      MBEDTLS_TLS_ECDH_RSA_WITH_RC4_128_SHA
- *      MBEDTLS_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA
- *      MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA
- *      MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA
- *      MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256
- *      MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384
- *      MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256
- *      MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384
- *      MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256
- *      MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384
- *      MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256
- *      MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384
- */
-#define MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED
-
-/**
- * \def MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED
- *
- * Enable the ECJPAKE based ciphersuite modes in SSL / TLS.
- *
- * \warning This is currently experimental. EC J-PAKE support is based on the
- * Thread v1.0.0 specification; incompatible changes to the specification
- * might still happen. For this reason, this is disabled by default.
- *
- * Requires: MBEDTLS_ECJPAKE_C
- *           MBEDTLS_SHA256_C
- *           MBEDTLS_ECP_DP_SECP256R1_ENABLED
- *
- * This enables the following ciphersuites (if other requisites are
- * enabled as well):
- *      MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8
- */
-//#define MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED
-
-/**
- * \def MBEDTLS_PK_PARSE_EC_EXTENDED
- *
- * Enhance support for reading EC keys using variants of SEC1 not allowed by
- * RFC 5915 and RFC 5480.
- *
- * Currently this means parsing the SpecifiedECDomain choice of EC
- * parameters (only known groups are supported, not arbitrary domains, to
- * avoid validation issues).
- *
- * Disable if you only need to support RFC 5915 + 5480 key formats.
- */
-#define MBEDTLS_PK_PARSE_EC_EXTENDED
-
-/**
- * \def MBEDTLS_ERROR_STRERROR_DUMMY
- *
- * Enable a dummy error function to make use of mbedtls_strerror() in
- * third party libraries easier when MBEDTLS_ERROR_C is disabled
- * (no effect when MBEDTLS_ERROR_C is enabled).
- *
- * You can safely disable this if MBEDTLS_ERROR_C is enabled, or if you're
- * not using mbedtls_strerror() or error_strerror() in your application.
- *
- * Disable if you run into name conflicts and want to really remove the
- * mbedtls_strerror()
- */
-#define MBEDTLS_ERROR_STRERROR_DUMMY
-
-/**
- * \def MBEDTLS_GENPRIME
- *
- * Enable the prime-number generation code.
- *
- * Requires: MBEDTLS_BIGNUM_C
- */
-#define MBEDTLS_GENPRIME
-
-/**
- * \def MBEDTLS_FS_IO
- *
- * Enable functions that use the filesystem.
- */
-#define MBEDTLS_FS_IO
-
-/**
- * \def MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES
- *
- * Do not add default entropy sources. These are the platform specific,
- * mbedtls_timing_hardclock and HAVEGE based poll functions.
- *
- * This is useful to have more control over the added entropy sources in an
- * application.
- *
- * Uncomment this macro to prevent loading of default entropy functions.
- */
-//#define MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES
-
-/**
- * \def MBEDTLS_NO_PLATFORM_ENTROPY
- *
- * Do not use built-in platform entropy functions.
- * This is useful if your platform does not support
- * standards like the /dev/urandom or Windows CryptoAPI.
- *
- * Uncomment this macro to disable the built-in platform entropy functions.
- */
-//#define MBEDTLS_NO_PLATFORM_ENTROPY
-
-/**
- * \def MBEDTLS_ENTROPY_FORCE_SHA256
- *
- * Force the entropy accumulator to use a SHA-256 accumulator instead of the
- * default SHA-512 based one (if both are available).
- *
- * Requires: MBEDTLS_SHA256_C
- *
- * On 32-bit systems SHA-256 can be much faster than SHA-512. Use this option
- * if you have performance concerns.
- *
- * This option is only useful if both MBEDTLS_SHA256_C and
- * MBEDTLS_SHA512_C are defined. Otherwise the available hash module is used.
- */
-//#define MBEDTLS_ENTROPY_FORCE_SHA256
-
-/**
- * \def MBEDTLS_ENTROPY_NV_SEED
- *
- * Enable the non-volatile (NV) seed file-based entropy source.
- * (Also enables the NV seed read/write functions in the platform layer)
- *
- * This is crucial (if not required) on systems that do not have a
- * cryptographic entropy source (in hardware or kernel) available.
- *
- * Requires: MBEDTLS_ENTROPY_C, MBEDTLS_PLATFORM_C
- *
- * \note The read/write functions that are used by the entropy source are
- *       determined in the platform layer, and can be modified at runtime and/or
- *       compile-time depending on the flags (MBEDTLS_PLATFORM_NV_SEED_*) used.
- *
- * \note If you use the default implementation functions that read a seedfile
- *       with regular fopen(), please make sure you make a seedfile with the
- *       proper name (defined in MBEDTLS_PLATFORM_STD_NV_SEED_FILE) and at
- *       least MBEDTLS_ENTROPY_BLOCK_SIZE bytes in size that can be read from
- *       and written to or you will get an entropy source error! The default
- *       implementation will only use the first MBEDTLS_ENTROPY_BLOCK_SIZE
- *       bytes from the file.
- *
- * \note The entropy collector will write to the seed file before entropy is
- *       given to an external source, to update it.
- */
-//#define MBEDTLS_ENTROPY_NV_SEED
-
-/* MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER
- *
- * Enable key identifiers that encode a key owner identifier.
- *
- * This is only meaningful when building the library as part of a
- * multi-client service. When you activate this option, you must provide an
- * implementation of the type mbedtls_key_owner_id_t and a translation from
- * mbedtls_svc_key_id_t to file name in all the storage backends that you
- * you wish to support.
- *
- * Note that this option is meant for internal use only and may be removed
- * without notice.
- */
-//#define MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER
-
-/**
- * \def MBEDTLS_MEMORY_DEBUG
- *
- * Enable debugging of buffer allocator memory issues. Automatically prints
- * (to stderr) all (fatal) messages on memory allocation issues. Enables
- * function for 'debug output' of allocated memory.
- *
- * Requires: MBEDTLS_MEMORY_BUFFER_ALLOC_C
- *
- * Uncomment this macro to let the buffer allocator print out error messages.
- */
-//#define MBEDTLS_MEMORY_DEBUG
-
-/**
- * \def MBEDTLS_MEMORY_BACKTRACE
- *
- * Include backtrace information with each allocated block.
- *
- * Requires: MBEDTLS_MEMORY_BUFFER_ALLOC_C
- *           GLIBC-compatible backtrace() an backtrace_symbols() support
- *
- * Uncomment this macro to include backtrace information
- */
-//#define MBEDTLS_MEMORY_BACKTRACE
-
-/**
- * \def MBEDTLS_PK_RSA_ALT_SUPPORT
- *
- * Support external private RSA keys (eg from a HSM) in the PK layer.
- *
- * Comment this macro to disable support for external private RSA keys.
- */
-#define MBEDTLS_PK_RSA_ALT_SUPPORT
-
-/**
- * \def MBEDTLS_PKCS1_V15
- *
- * Enable support for PKCS#1 v1.5 encoding.
- *
- * Requires: MBEDTLS_RSA_C
- *
- * This enables support for PKCS#1 v1.5 operations.
- */
-#define MBEDTLS_PKCS1_V15
-
-/**
- * \def MBEDTLS_PKCS1_V21
- *
- * Enable support for PKCS#1 v2.1 encoding.
- *
- * Requires: MBEDTLS_MD_C, MBEDTLS_RSA_C
- *
- * This enables support for RSAES-OAEP and RSASSA-PSS operations.
- */
-#define MBEDTLS_PKCS1_V21
-
-/**
- * \def MBEDTLS_PSA_CRYPTO_SPM
- *
- * When MBEDTLS_PSA_CRYPTO_SPM is defined, the code is built for SPM (Secure
- * Partition Manager) integration which separates the code into two parts: a
- * NSPE (Non-Secure Process Environment) and an SPE (Secure Process
- * Environment).
- *
- * Module:  library/psa_crypto.c
- * Requires: MBEDTLS_PSA_CRYPTO_C
- *
- */
-//#define MBEDTLS_PSA_CRYPTO_SPM
-
-/**
- * \def MBEDTLS_PSA_INJECT_ENTROPY
- *
- * Enable support for entropy injection at first boot. This feature is
- * required on systems that do not have a built-in entropy source (TRNG).
- * This feature is currently not supported on systems that have a built-in
- * entropy source.
- *
- * Requires: MBEDTLS_PSA_CRYPTO_STORAGE_C, MBEDTLS_ENTROPY_NV_SEED
- *
- */
-//#define MBEDTLS_PSA_INJECT_ENTROPY
-
-/**
- * \def MBEDTLS_RSA_NO_CRT
- *
- * Do not use the Chinese Remainder Theorem
- * for the RSA private operation.
- *
- * Uncomment this macro to disable the use of CRT in RSA.
- *
- */
-//#define MBEDTLS_RSA_NO_CRT
-
-/**
- * \def MBEDTLS_SELF_TEST
- *
- * Enable the checkup functions (*_self_test).
- */
-#define MBEDTLS_SELF_TEST
-
-/**
- * \def MBEDTLS_SHA256_SMALLER
- *
- * 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
- * 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
- * 30%.
- *
- * Uncomment to enable the smaller implementation of SHA256.
- */
-//#define MBEDTLS_SHA256_SMALLER
-
-/**
- * \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
- * with other servers, only debugging of failures is harder.
- *
- * The advantage of not sending alert messages, is that no information is given
- * about reasons for failures thus preventing adversaries of gaining intel.
- *
- * Enable sending of all alert messages
- */
-#define MBEDTLS_SSL_ALL_ALERT_MESSAGES
-
-/**
- * \def MBEDTLS_SSL_ASYNC_PRIVATE
- *
- * Enable asynchronous external private key operations in SSL. This allows
- * you to configure an SSL connection to call an external cryptographic
- * module to perform private key operations instead of performing the
- * operation inside the library.
- *
- */
-//#define MBEDTLS_SSL_ASYNC_PRIVATE
-
-/**
- * \def MBEDTLS_SSL_DEBUG_ALL
- *
- * Enable the debug messages in SSL module for all issues.
- * Debug messages have been disabled in some places to prevent timing
- * attacks due to (unbalanced) debugging function calls.
- *
- * If you need all error reporting you should enable this during debugging,
- * but remove this for production servers that should log as well.
- *
- * Uncomment this macro to report all debug messages on errors introducing
- * a timing side-channel.
- *
- */
-//#define MBEDTLS_SSL_DEBUG_ALL
-
-/** \def MBEDTLS_SSL_ENCRYPT_THEN_MAC
- *
- * Enable support for Encrypt-then-MAC, RFC 7366.
- *
- * This allows peers that both support it to use a more robust protection for
- * ciphersuites using CBC, providing deep resistance against timing attacks
- * on the padding or underlying cipher.
- *
- * This only affects CBC ciphersuites, and is useless if none is defined.
- *
- * Requires: MBEDTLS_SSL_PROTO_TLS1    or
- *           MBEDTLS_SSL_PROTO_TLS1_1  or
- *           MBEDTLS_SSL_PROTO_TLS1_2
- *
- * Comment this macro to disable support for Encrypt-then-MAC
- */
-#define MBEDTLS_SSL_ENCRYPT_THEN_MAC
-
-/** \def MBEDTLS_SSL_EXTENDED_MASTER_SECRET
- *
- * Enable support for Extended Master Secret, aka Session Hash
- * (draft-ietf-tls-session-hash-02).
- *
- * This was introduced as "the proper fix" to the Triple Handshake familiy 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.
- *
- * Requires: MBEDTLS_SSL_PROTO_TLS1    or
- *           MBEDTLS_SSL_PROTO_TLS1_1  or
- *           MBEDTLS_SSL_PROTO_TLS1_2
- *
- * Comment this macro to disable support for Extended Master Secret.
- */
-#define MBEDTLS_SSL_EXTENDED_MASTER_SECRET
-
-/**
- * \def MBEDTLS_SSL_FALLBACK_SCSV
- *
- * Enable support for FALLBACK_SCSV (draft-ietf-tls-downgrade-scsv-00).
- *
- * For servers, it is recommended to always enable this, unless you support
- * only one version of TLS, or know for sure that none of your clients
- * implements a fallback strategy.
- *
- * For clients, you only need this if you're using a fallback strategy, which
- * is not recommended in the first place, unless you absolutely need it to
- * interoperate with buggy (version-intolerant) servers.
- *
- * Comment this macro to disable support for FALLBACK_SCSV
- */
-#define MBEDTLS_SSL_FALLBACK_SCSV
-
-/**
- * \def MBEDTLS_SSL_HW_RECORD_ACCEL
- *
- * Enable hooking functions in SSL module for hardware acceleration of
- * individual records.
- *
- * Uncomment this macro to enable hooking functions.
- */
-//#define MBEDTLS_SSL_HW_RECORD_ACCEL
-
-/**
- * \def MBEDTLS_SSL_CBC_RECORD_SPLITTING
- *
- * Enable 1/n-1 record splitting for CBC mode in SSLv3 and TLS 1.0.
- *
- * This is a countermeasure to the BEAST attack, which also minimizes the risk
- * of interoperability issues compared to sending 0-length records.
- *
- * Comment this macro to disable 1/n-1 record splitting.
- */
-#define MBEDTLS_SSL_CBC_RECORD_SPLITTING
-
-/**
- * \def MBEDTLS_SSL_RENEGOTIATION
- *
- * Enable support for TLS renegotiation.
- *
- * The two main uses of renegotiation are (1) refresh keys on long-lived
- * connections and (2) client authentication after the initial handshake.
- * If you don't need renegotiation, it's probably better to disable it, since
- * it has been associated with security issues in the past and is easy to
- * misuse/misunderstand.
- *
- * Comment this to disable support for renegotiation.
- *
- * \note   Even if this option is disabled, both client and server are aware
- *         of the Renegotiation Indication Extension (RFC 5746) used to
- *         prevent the SSL renegotiation attack (see RFC 5746 Sect. 1).
- *         (See \c mbedtls_ssl_conf_legacy_renegotiation for the
- *          configuration of this extension).
- *
- */
-#define MBEDTLS_SSL_RENEGOTIATION
-
-/**
- * \def MBEDTLS_SSL_SRV_SUPPORT_SSLV2_CLIENT_HELLO
- *
- * Enable support for receiving and parsing SSLv2 Client Hello messages for the
- * SSL Server module (MBEDTLS_SSL_SRV_C).
- *
- * Uncomment this macro to enable support for SSLv2 Client Hello messages.
- */
-//#define MBEDTLS_SSL_SRV_SUPPORT_SSLV2_CLIENT_HELLO
-
-/**
- * \def MBEDTLS_SSL_SRV_RESPECT_CLIENT_PREFERENCE
- *
- * Pick the ciphersuite according to the client's preferences rather than ours
- * in the SSL Server module (MBEDTLS_SSL_SRV_C).
- *
- * Uncomment this macro to respect client's ciphersuite order
- */
-//#define MBEDTLS_SSL_SRV_RESPECT_CLIENT_PREFERENCE
-
-/**
- * \def MBEDTLS_SSL_MAX_FRAGMENT_LENGTH
- *
- * Enable support for RFC 6066 max_fragment_length extension in SSL.
- *
- * Comment this macro to disable support for the max_fragment_length extension
- */
-#define MBEDTLS_SSL_MAX_FRAGMENT_LENGTH
-
-/**
- * \def MBEDTLS_SSL_PROTO_SSL3
- *
- * Enable support for SSL 3.0.
- *
- * Requires: MBEDTLS_MD5_C
- *           MBEDTLS_SHA1_C
- *
- * Comment this macro to disable support for SSL 3.0
- */
-//#define MBEDTLS_SSL_PROTO_SSL3
-
-/**
- * \def MBEDTLS_SSL_PROTO_TLS1
- *
- * Enable support for TLS 1.0.
- *
- * Requires: MBEDTLS_MD5_C
- *           MBEDTLS_SHA1_C
- *
- * Comment this macro to disable support for TLS 1.0
- */
-#define MBEDTLS_SSL_PROTO_TLS1
-
-/**
- * \def MBEDTLS_SSL_PROTO_TLS1_1
- *
- * Enable support for TLS 1.1 (and DTLS 1.0 if DTLS is enabled).
- *
- * Requires: MBEDTLS_MD5_C
- *           MBEDTLS_SHA1_C
- *
- * Comment this macro to disable support for TLS 1.1 / DTLS 1.0
- */
-#define MBEDTLS_SSL_PROTO_TLS1_1
-
-/**
- * \def MBEDTLS_SSL_PROTO_TLS1_2
- *
- * Enable support for TLS 1.2 (and DTLS 1.2 if DTLS is enabled).
- *
- * Requires: MBEDTLS_SHA1_C or MBEDTLS_SHA256_C or MBEDTLS_SHA512_C
- *           (Depends on ciphersuites)
- *
- * Comment this macro to disable support for TLS 1.2 / DTLS 1.2
- */
-#define MBEDTLS_SSL_PROTO_TLS1_2
-
-/**
- * \def MBEDTLS_SSL_PROTO_DTLS
- *
- * Enable support for DTLS (all available versions).
- *
- * Enable this and MBEDTLS_SSL_PROTO_TLS1_1 to enable DTLS 1.0,
- * and/or this and MBEDTLS_SSL_PROTO_TLS1_2 to enable DTLS 1.2.
- *
- * Requires: MBEDTLS_SSL_PROTO_TLS1_1
- *        or MBEDTLS_SSL_PROTO_TLS1_2
- *
- * Comment this macro to disable support for DTLS
- */
-#define MBEDTLS_SSL_PROTO_DTLS
-
-/**
- * \def MBEDTLS_SSL_ALPN
- *
- * Enable support for RFC 7301 Application Layer Protocol Negotiation.
- *
- * Comment this macro to disable support for ALPN.
- */
-#define MBEDTLS_SSL_ALPN
-
-/**
- * \def MBEDTLS_SSL_DTLS_ANTI_REPLAY
- *
- * Enable support for the anti-replay mechanism in DTLS.
- *
- * Requires: MBEDTLS_SSL_TLS_C
- *           MBEDTLS_SSL_PROTO_DTLS
- *
- * \warning Disabling this is often a security risk!
- * See mbedtls_ssl_conf_dtls_anti_replay() for details.
- *
- * Comment this to disable anti-replay in DTLS.
- */
-#define MBEDTLS_SSL_DTLS_ANTI_REPLAY
-
-/**
- * \def MBEDTLS_SSL_DTLS_HELLO_VERIFY
- *
- * Enable support for HelloVerifyRequest on DTLS servers.
- *
- * This feature is highly recommended to prevent DTLS servers being used as
- * amplifiers in DoS attacks against other hosts. It should always be enabled
- * 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)
- *
- * Requires: MBEDTLS_SSL_PROTO_DTLS
- *
- * Comment this to disable support for HelloVerifyRequest.
- */
-#define MBEDTLS_SSL_DTLS_HELLO_VERIFY
-
-/**
- * \def MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE
- *
- * Enable server-side support for clients that reconnect from the same port.
- *
- * Some clients unexpectedly close the connection and try to reconnect using the
- * same source port. This needs special support from the server to handle the
- * new connection securely, as described in section 4.2.8 of RFC 6347. This
- * flag enables that support.
- *
- * Requires: MBEDTLS_SSL_DTLS_HELLO_VERIFY
- *
- * Comment this to disable support for clients reusing the source port.
- */
-#define MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE
-
-/**
- * \def MBEDTLS_SSL_DTLS_BADMAC_LIMIT
- *
- * Enable support for a limit of records with bad MAC.
- *
- * See mbedtls_ssl_conf_dtls_badmac_limit().
- *
- * Requires: MBEDTLS_SSL_PROTO_DTLS
- */
-#define MBEDTLS_SSL_DTLS_BADMAC_LIMIT
-
-/**
- * \def MBEDTLS_SSL_SESSION_TICKETS
- *
- * Enable support for RFC 5077 session tickets in SSL.
- * Client-side, provides full support for session tickets (maintenance of a
- * session store remains the responsibility of the application, though).
- * Server-side, you also need to provide callbacks for writing and parsing
- * tickets, including authenticated encryption and key management. Example
- * callbacks are provided by MBEDTLS_SSL_TICKET_C.
- *
- * Comment this macro to disable support for SSL session tickets
- */
-#define MBEDTLS_SSL_SESSION_TICKETS
-
-/**
- * \def MBEDTLS_SSL_EXPORT_KEYS
- *
- * Enable support for exporting key block and master secret.
- * This is required for certain users of TLS, e.g. EAP-TLS.
- *
- * Comment this macro to disable support for key export
- */
-#define MBEDTLS_SSL_EXPORT_KEYS
-
-/**
- * \def MBEDTLS_SSL_SERVER_NAME_INDICATION
- *
- * Enable support for RFC 6066 server name indication (SNI) in SSL.
- *
- * Requires: MBEDTLS_X509_CRT_PARSE_C
- *
- * Comment this macro to disable support for server name indication in SSL
- */
-#define MBEDTLS_SSL_SERVER_NAME_INDICATION
-
-/**
- * \def MBEDTLS_SSL_TRUNCATED_HMAC
- *
- * Enable support for RFC 6066 truncated HMAC in SSL.
- *
- * Comment this macro to disable support for truncated HMAC in SSL
- */
-#define MBEDTLS_SSL_TRUNCATED_HMAC
-
-/**
- * \def MBEDTLS_SSL_TRUNCATED_HMAC_COMPAT
- *
- * Fallback to old (pre-2.7), non-conforming implementation of the truncated
- * HMAC extension which also truncates the HMAC key. Note that this option is
- * only meant for a transitory upgrade period and is likely to be removed in
- * a future version of the library.
- *
- * \warning The old implementation is non-compliant and has a security weakness
- *          (2^80 brute force attack on the HMAC key used for a single,
- *          uninterrupted connection). This should only be enabled temporarily
- *          when (1) the use of truncated HMAC is essential in order to save
- *          bandwidth, and (2) the peer is an Mbed TLS stack that doesn't use
- *          the fixed implementation yet (pre-2.7).
- *
- * \deprecated This option is deprecated and will likely be removed in a
- *             future version of Mbed TLS.
- *
- * Uncomment to fallback to old, non-compliant truncated HMAC implementation.
- *
- * Requires: MBEDTLS_SSL_TRUNCATED_HMAC
- */
-//#define MBEDTLS_SSL_TRUNCATED_HMAC_COMPAT
-
-/**
- * \def MBEDTLS_THREADING_ALT
- *
- * Provide your own alternate threading implementation.
- *
- * Requires: MBEDTLS_THREADING_C
- *
- * Uncomment this to allow your own alternate threading implementation.
- */
-//#define MBEDTLS_THREADING_ALT
-
-/**
- * \def MBEDTLS_THREADING_PTHREAD
- *
- * Enable the pthread wrapper layer for the threading layer.
- *
- * Requires: MBEDTLS_THREADING_C
- *
- * Uncomment this to enable pthread mutexes.
- */
-//#define MBEDTLS_THREADING_PTHREAD
-
-/**
- * \def MBEDTLS_USE_PSA_CRYPTO
- *
- * Make the X.509 and TLS library use PSA for cryptographic operations, see
- * #MBEDTLS_PSA_CRYPTO_C.
- *
- * Note: this option is still in progress, the full X.509 and TLS modules are
- * not covered yet, but parts that are not ported to PSA yet will still work
- * as usual, so enabling this option should not break backwards compatibility.
- *
- * \warning  Support for PSA is still an experimental feature.
- *           Any public API that depends on this option may change
- *           at any time until this warning is removed.
- *
- * Requires: MBEDTLS_PSA_CRYPTO_C.
- */
-//#define MBEDTLS_USE_PSA_CRYPTO
-
-/**
- * \def MBEDTLS_VERSION_FEATURES
- *
- * Allow run-time checking of compile-time enabled features. Thus allowing users
- * to check at run-time if the library is for instance compiled with threading
- * support via mbedtls_version_check_feature().
- *
- * Requires: MBEDTLS_VERSION_C
- *
- * Comment this to disable run-time checking and save ROM space
- */
-#define MBEDTLS_VERSION_FEATURES
-
-/**
- * \def MBEDTLS_X509_ALLOW_EXTENSIONS_NON_V3
- *
- * If set, the X509 parser will not break-off when parsing an X509 certificate
- * and encountering an extension in a v1 or v2 certificate.
- *
- * Uncomment to prevent an error.
- */
-//#define MBEDTLS_X509_ALLOW_EXTENSIONS_NON_V3
-
-/**
- * \def MBEDTLS_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION
- *
- * If set, the X509 parser will not break-off when parsing an X509 certificate
- * and encountering an unknown critical extension.
- *
- * \warning Depending on your PKI use, enabling this can be a security risk!
- *
- * Uncomment to prevent an error.
- */
-//#define MBEDTLS_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION
-
-/**
- * \def MBEDTLS_X509_CHECK_KEY_USAGE
- *
- * Enable verification of the keyUsage extension (CA and leaf certificates).
- *
- * Disabling this avoids problems with mis-issued and/or misused
- * (intermediate) CA and leaf certificates.
- *
- * \warning Depending on your PKI use, disabling this can be a security risk!
- *
- * Comment to skip keyUsage checking for both CA and leaf certificates.
- */
-#define MBEDTLS_X509_CHECK_KEY_USAGE
-
-/**
- * \def MBEDTLS_X509_CHECK_EXTENDED_KEY_USAGE
- *
- * Enable verification of the extendedKeyUsage extension (leaf certificates).
- *
- * Disabling this avoids problems with mis-issued and/or misused certificates.
- *
- * \warning Depending on your PKI use, disabling this can be a security risk!
- *
- * Comment to skip extendedKeyUsage checking for certificates.
- */
-#define MBEDTLS_X509_CHECK_EXTENDED_KEY_USAGE
-
-/**
- * \def MBEDTLS_X509_RSASSA_PSS_SUPPORT
- *
- * Enable parsing and verification of X.509 certificates, CRLs and CSRS
- * signed with RSASSA-PSS (aka PKCS#1 v2.1).
- *
- * Comment this macro to disallow using RSASSA-PSS in certificates.
- */
-#define MBEDTLS_X509_RSASSA_PSS_SUPPORT
-
-/**
- * \def MBEDTLS_ZLIB_SUPPORT
- *
- * If set, the SSL/TLS module uses ZLIB to support compression and
- * decompression of packet data.
- *
- * \warning TLS-level compression MAY REDUCE SECURITY! See for example the
- * CRIME attack. Before enabling this option, you should examine with care if
- * CRIME or similar exploits may be applicable to your use case.
- *
- * \note Currently compression can't be used with DTLS.
- *
- * \deprecated This feature is deprecated and will be removed
- *             in the next major revision of the library.
- *
- * Used in: library/ssl_tls.c
- *          library/ssl_cli.c
- *          library/ssl_srv.c
- *
- * This feature requires zlib library and headers to be present.
- *
- * Uncomment to enable use of ZLIB
- */
-//#define MBEDTLS_ZLIB_SUPPORT
-/* \} name SECTION: mbed TLS feature support */
-
-/**
- * \name SECTION: mbed TLS modules
- *
- * This section enables or disables entire modules in mbed TLS
- * \{
- */
-
-/**
- * \def MBEDTLS_AESNI_C
- *
- * Enable AES-NI support on x86-64.
- *
- * Module:  library/aesni.c
- * Caller:  library/aes.c
- *
- * Requires: MBEDTLS_HAVE_ASM
- *
- * This modules adds support for the AES-NI instructions on x86-64
- */
-#define MBEDTLS_AESNI_C
-
-/**
- * \def MBEDTLS_AES_C
- *
- * Enable the AES block cipher.
- *
- * Module:  library/aes.c
- * Caller:  library/cipher.c
- *          library/pem.c
- *          library/ctr_drbg.c
- *
- * This module enables the following ciphersuites (if other requisites are
- * enabled as well):
- *      MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA
- *      MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA
- *      MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA
- *      MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA
- *      MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256
- *      MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384
- *      MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256
- *      MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384
- *      MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256
- *      MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384
- *      MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256
- *      MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384
- *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
- *      MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
- *      MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
- *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
- *      MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
- *      MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
- *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
- *      MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
- *      MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA
- *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
- *      MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
- *      MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
- *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
- *      MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
- *      MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
- *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
- *      MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
- *      MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA
- *      MBEDTLS_TLS_DHE_PSK_WITH_AES_256_GCM_SHA384
- *      MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384
- *      MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384
- *      MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA
- *      MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA
- *      MBEDTLS_TLS_DHE_PSK_WITH_AES_128_GCM_SHA256
- *      MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256
- *      MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256
- *      MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA
- *      MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA
- *      MBEDTLS_TLS_RSA_WITH_AES_256_GCM_SHA384
- *      MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA256
- *      MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA
- *      MBEDTLS_TLS_RSA_WITH_AES_128_GCM_SHA256
- *      MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA256
- *      MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA
- *      MBEDTLS_TLS_RSA_PSK_WITH_AES_256_GCM_SHA384
- *      MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA384
- *      MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA
- *      MBEDTLS_TLS_RSA_PSK_WITH_AES_128_GCM_SHA256
- *      MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA256
- *      MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA
- *      MBEDTLS_TLS_PSK_WITH_AES_256_GCM_SHA384
- *      MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA384
- *      MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA
- *      MBEDTLS_TLS_PSK_WITH_AES_128_GCM_SHA256
- *      MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA256
- *      MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA
- *
- * PEM_PARSE uses AES for decrypting encrypted keys.
- */
-#define MBEDTLS_AES_C
-
-/**
- * \def MBEDTLS_ARC4_C
- *
- * Enable the ARCFOUR stream cipher.
- *
- * Module:  library/arc4.c
- * Caller:  library/cipher.c
- *
- * This module enables the following ciphersuites (if other requisites are
- * enabled as well):
- *      MBEDTLS_TLS_ECDH_ECDSA_WITH_RC4_128_SHA
- *      MBEDTLS_TLS_ECDH_RSA_WITH_RC4_128_SHA
- *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_RC4_128_SHA
- *      MBEDTLS_TLS_ECDHE_RSA_WITH_RC4_128_SHA
- *      MBEDTLS_TLS_ECDHE_PSK_WITH_RC4_128_SHA
- *      MBEDTLS_TLS_DHE_PSK_WITH_RC4_128_SHA
- *      MBEDTLS_TLS_RSA_WITH_RC4_128_SHA
- *      MBEDTLS_TLS_RSA_WITH_RC4_128_MD5
- *      MBEDTLS_TLS_RSA_PSK_WITH_RC4_128_SHA
- *      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
- *            it, and considering stronger ciphers instead.
- *
- */
-#define MBEDTLS_ARC4_C
-
-/**
- * \def MBEDTLS_ASN1_PARSE_C
- *
- * Enable the generic ASN1 parser.
- *
- * Module:  library/asn1.c
- * Caller:  library/x509.c
- *          library/dhm.c
- *          library/pkcs12.c
- *          library/pkcs5.c
- *          library/pkparse.c
- */
-#define MBEDTLS_ASN1_PARSE_C
-
-/**
- * \def MBEDTLS_ASN1_WRITE_C
- *
- * Enable the generic ASN1 writer.
- *
- * Module:  library/asn1write.c
- * Caller:  library/ecdsa.c
- *          library/pkwrite.c
- *          library/x509_create.c
- *          library/x509write_crt.c
- *          library/x509write_csr.c
- */
-#define MBEDTLS_ASN1_WRITE_C
-
-/**
- * \def MBEDTLS_BASE64_C
- *
- * Enable the Base64 module.
- *
- * Module:  library/base64.c
- * Caller:  library/pem.c
- *
- * This module is required for PEM support (required by X.509).
- */
-#define MBEDTLS_BASE64_C
-
-/**
- * \def MBEDTLS_BIGNUM_C
- *
- * Enable the multi-precision integer library.
- *
- * Module:  library/bignum.c
- * Caller:  library/dhm.c
- *          library/ecp.c
- *          library/ecdsa.c
- *          library/rsa.c
- *          library/rsa_internal.c
- *          library/ssl_tls.c
- *
- * This module is required for RSA, DHM and ECC (ECDH, ECDSA) support.
- */
-#define MBEDTLS_BIGNUM_C
-
-/**
- * \def MBEDTLS_BLOWFISH_C
- *
- * Enable the Blowfish block cipher.
- *
- * Module:  library/blowfish.c
- */
-#define MBEDTLS_BLOWFISH_C
-
-/**
- * \def MBEDTLS_CAMELLIA_C
- *
- * Enable the Camellia block cipher.
- *
- * Module:  library/camellia.c
- * Caller:  library/cipher.c
- *
- * This module enables the following ciphersuites (if other requisites are
- * enabled as well):
- *      MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256
- *      MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384
- *      MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256
- *      MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384
- *      MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256
- *      MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384
- *      MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256
- *      MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384
- *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384
- *      MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384
- *      MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384
- *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384
- *      MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384
- *      MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256
- *      MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA
- *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256
- *      MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256
- *      MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256
- *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256
- *      MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256
- *      MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256
- *      MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA
- *      MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384
- *      MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384
- *      MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384
- *      MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256
- *      MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256
- *      MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256
- *      MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384
- *      MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256
- *      MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA
- *      MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256
- *      MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256
- *      MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA
- *      MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384
- *      MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384
- *      MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256
- *      MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256
- *      MBEDTLS_TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384
- *      MBEDTLS_TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384
- *      MBEDTLS_TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256
- *      MBEDTLS_TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256
- */
-#define MBEDTLS_CAMELLIA_C
-
-/**
- * \def MBEDTLS_ARIA_C
- *
- * Enable the ARIA block cipher.
- *
- * Module:  library/aria.c
- * Caller:  library/cipher.c
- *
- * This module enables the following ciphersuites (if other requisites are
- * enabled as well):
- *
- *      MBEDTLS_TLS_RSA_WITH_ARIA_128_CBC_SHA256
- *      MBEDTLS_TLS_RSA_WITH_ARIA_256_CBC_SHA384
- *      MBEDTLS_TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256
- *      MBEDTLS_TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384
- *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256
- *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384
- *      MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256
- *      MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384
- *      MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256
- *      MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384
- *      MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256
- *      MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384
- *      MBEDTLS_TLS_RSA_WITH_ARIA_128_GCM_SHA256
- *      MBEDTLS_TLS_RSA_WITH_ARIA_256_GCM_SHA384
- *      MBEDTLS_TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256
- *      MBEDTLS_TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384
- *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256
- *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384
- *      MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256
- *      MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384
- *      MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256
- *      MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384
- *      MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256
- *      MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384
- *      MBEDTLS_TLS_PSK_WITH_ARIA_128_CBC_SHA256
- *      MBEDTLS_TLS_PSK_WITH_ARIA_256_CBC_SHA384
- *      MBEDTLS_TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256
- *      MBEDTLS_TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384
- *      MBEDTLS_TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256
- *      MBEDTLS_TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384
- *      MBEDTLS_TLS_PSK_WITH_ARIA_128_GCM_SHA256
- *      MBEDTLS_TLS_PSK_WITH_ARIA_256_GCM_SHA384
- *      MBEDTLS_TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256
- *      MBEDTLS_TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384
- *      MBEDTLS_TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256
- *      MBEDTLS_TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384
- *      MBEDTLS_TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256
- *      MBEDTLS_TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384
- */
-//#define MBEDTLS_ARIA_C
-
-/**
- * \def MBEDTLS_CCM_C
- *
- * Enable the Counter with CBC-MAC (CCM) mode for 128-bit block cipher.
- *
- * Module:  library/ccm.c
- *
- * Requires: MBEDTLS_AES_C or MBEDTLS_CAMELLIA_C
- *
- * This module enables the AES-CCM ciphersuites, if other requisites are
- * enabled as well.
- */
-#define MBEDTLS_CCM_C
-
-/**
- * \def MBEDTLS_CERTS_C
- *
- * Enable the test certificates.
- *
- * Module:  library/certs.c
- * Caller:
- *
- * This module is used for testing (ssl_client/server).
- */
-#define MBEDTLS_CERTS_C
-
-/**
- * \def MBEDTLS_CHACHA20_C
- *
- * Enable the ChaCha20 stream cipher.
- *
- * Module:  library/chacha20.c
- */
-#define MBEDTLS_CHACHA20_C
-
-/**
- * \def MBEDTLS_CHACHAPOLY_C
- *
- * Enable the ChaCha20-Poly1305 AEAD algorithm.
- *
- * Module:  library/chachapoly.c
- *
- * This module requires: MBEDTLS_CHACHA20_C, MBEDTLS_POLY1305_C
- */
-#define MBEDTLS_CHACHAPOLY_C
-
-/**
- * \def MBEDTLS_CIPHER_C
- *
- * Enable the generic cipher layer.
- *
- * Module:  library/cipher.c
- * Caller:  library/ssl_tls.c
- *
- * Uncomment to enable generic cipher wrappers.
- */
-#define MBEDTLS_CIPHER_C
-
-/**
- * \def MBEDTLS_CMAC_C
- *
- * Enable the CMAC (Cipher-based Message Authentication Code) mode for block
- * ciphers.
- *
- * Module:  library/cmac.c
- *
- * Requires: MBEDTLS_AES_C or MBEDTLS_DES_C
- *
- */
-#define MBEDTLS_CMAC_C
-
-/**
- * \def MBEDTLS_CTR_DRBG_C
- *
- * Enable the CTR_DRBG AES-based random generator.
- * The CTR_DRBG generator uses AES-256 by default.
- * To use AES-128 instead, enable MBEDTLS_CTR_DRBG_USE_128_BIT_KEY below.
- *
- * Module:  library/ctr_drbg.c
- * Caller:
- *
- * Requires: MBEDTLS_AES_C
- *
- * This module provides the CTR_DRBG AES random number generator.
- */
-#define MBEDTLS_CTR_DRBG_C
-
-/**
- * \def MBEDTLS_DEBUG_C
- *
- * Enable the debug functions.
- *
- * Module:  library/debug.c
- * Caller:  library/ssl_cli.c
- *          library/ssl_srv.c
- *          library/ssl_tls.c
- *
- * This module provides debugging functions.
- */
-#define MBEDTLS_DEBUG_C
-
-/**
- * \def MBEDTLS_DES_C
- *
- * Enable the DES block cipher.
- *
- * Module:  library/des.c
- * Caller:  library/pem.c
- *          library/cipher.c
- *
- * This module enables the following ciphersuites (if other requisites are
- * enabled as well):
- *      MBEDTLS_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA
- *      MBEDTLS_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA
- *      MBEDTLS_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA
- *      MBEDTLS_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
- *      MBEDTLS_TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA
- *      MBEDTLS_TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA
- *      MBEDTLS_TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA
- *      MBEDTLS_TLS_RSA_WITH_3DES_EDE_CBC_SHA
- *      MBEDTLS_TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA
- *      MBEDTLS_TLS_PSK_WITH_3DES_EDE_CBC_SHA
- *
- * PEM_PARSE uses DES/3DES for decrypting encrypted keys.
- *
- * \warning   DES is considered a weak cipher and its use constitutes a
- *            security risk. We recommend considering stronger ciphers instead.
- */
-#define MBEDTLS_DES_C
-
-/**
- * \def MBEDTLS_DHM_C
- *
- * Enable the Diffie-Hellman-Merkle module.
- *
- * Module:  library/dhm.c
- * Caller:  library/ssl_cli.c
- *          library/ssl_srv.c
- *
- * This module is used by the following key exchanges:
- *      DHE-RSA, DHE-PSK
- *
- * \warning    Using DHE constitutes a security risk as it
- *             is not possible to validate custom DH parameters.
- *             If possible, it is recommended users should consider
- *             preferring other methods of key exchange.
- *             See dhm.h for more details.
- *
- */
-#define MBEDTLS_DHM_C
-
-/**
- * \def MBEDTLS_ECDH_C
- *
- * Enable the elliptic curve Diffie-Hellman library.
- *
- * Module:  library/ecdh.c
- * Caller:  library/ssl_cli.c
- *          library/ssl_srv.c
- *
- * This module is used by the following key exchanges:
- *      ECDHE-ECDSA, ECDHE-RSA, DHE-PSK
- *
- * Requires: MBEDTLS_ECP_C
- */
-#define MBEDTLS_ECDH_C
-
-/**
- * \def MBEDTLS_ECDSA_C
- *
- * Enable the elliptic curve DSA library.
- *
- * Module:  library/ecdsa.c
- * Caller:
- *
- * This module is used by the following key exchanges:
- *      ECDHE-ECDSA
- *
- * Requires: MBEDTLS_ECP_C, MBEDTLS_ASN1_WRITE_C, MBEDTLS_ASN1_PARSE_C
- */
-#define MBEDTLS_ECDSA_C
-
-/**
- * \def MBEDTLS_ECJPAKE_C
- *
- * Enable the elliptic curve J-PAKE library.
- *
- * \warning This is currently experimental. EC J-PAKE support is based on the
- * Thread v1.0.0 specification; incompatible changes to the specification
- * might still happen. For this reason, this is disabled by default.
- *
- * Module:  library/ecjpake.c
- * Caller:
- *
- * This module is used by the following key exchanges:
- *      ECJPAKE
- *
- * Requires: MBEDTLS_ECP_C, MBEDTLS_MD_C
- */
-//#define MBEDTLS_ECJPAKE_C
-
-/**
- * \def MBEDTLS_ECP_C
- *
- * Enable the elliptic curve over GF(p) library.
- *
- * Module:  library/ecp.c
- * Caller:  library/ecdh.c
- *          library/ecdsa.c
- *          library/ecjpake.c
- *
- * Requires: MBEDTLS_BIGNUM_C and at least one MBEDTLS_ECP_DP_XXX_ENABLED
- */
-#define MBEDTLS_ECP_C
-
-/**
- * \def MBEDTLS_ENTROPY_C
- *
- * Enable the platform-specific entropy code.
- *
- * Module:  library/entropy.c
- * Caller:
- *
- * Requires: MBEDTLS_SHA512_C or MBEDTLS_SHA256_C
- *
- * This module provides a generic entropy pool
- */
-#define MBEDTLS_ENTROPY_C
-
-/**
- * \def MBEDTLS_ERROR_C
- *
- * Enable error code to error string conversion.
- *
- * Module:  library/error.c
- * Caller:
- *
- * This module enables mbedtls_strerror().
- */
-#define MBEDTLS_ERROR_C
-
-/**
- * \def MBEDTLS_GCM_C
- *
- * Enable the Galois/Counter Mode (GCM) for AES.
- *
- * Module:  library/gcm.c
- *
- * Requires: MBEDTLS_AES_C or MBEDTLS_CAMELLIA_C
- *
- * This module enables the AES-GCM and CAMELLIA-GCM ciphersuites, if other
- * requisites are enabled as well.
- */
-#define MBEDTLS_GCM_C
-
-/**
- * \def MBEDTLS_HAVEGE_C
- *
- * Enable the HAVEGE random generator.
- *
- * Warning: the HAVEGE random generator is not suitable for virtualized
- *          environments
- *
- * Warning: the HAVEGE random generator is dependent on timing and specific
- *          processor traits. It is therefore not advised to use HAVEGE as
- *          your applications primary random generator or primary entropy pool
- *          input. As a secondary input to your entropy pool, it IS able add
- *          the (limited) extra entropy it provides.
- *
- * Module:  library/havege.c
- * Caller:
- *
- * Requires: MBEDTLS_TIMING_C
- *
- * Uncomment to enable the HAVEGE random generator.
- */
-//#define MBEDTLS_HAVEGE_C
-
-/**
- * \def MBEDTLS_HKDF_C
- *
- * Enable the HKDF algorithm (RFC 5869).
- *
- * Module:  library/hkdf.c
- * Caller:
- *
- * Requires: MBEDTLS_MD_C
- *
- * This module adds support for the Hashed Message Authentication Code
- * (HMAC)-based key derivation function (HKDF).
- */
-#define MBEDTLS_HKDF_C
-
-/**
- * \def MBEDTLS_HMAC_DRBG_C
- *
- * Enable the HMAC_DRBG random generator.
- *
- * Module:  library/hmac_drbg.c
- * Caller:
- *
- * Requires: MBEDTLS_MD_C
- *
- * Uncomment to enable the HMAC_DRBG random number geerator.
- */
-#define MBEDTLS_HMAC_DRBG_C
-
-/**
- * \def MBEDTLS_NIST_KW_C
- *
- * Enable the Key Wrapping mode for 128-bit block ciphers,
- * as defined in NIST SP 800-38F. Only KW and KWP modes
- * are supported. At the moment, only AES is approved by NIST.
- *
- * Module:  library/nist_kw.c
- *
- * Requires: MBEDTLS_AES_C and MBEDTLS_CIPHER_C
- */
-//#define MBEDTLS_NIST_KW_C
-
-/**
- * \def MBEDTLS_MD_C
- *
- * Enable the generic message digest layer.
- *
- * Module:  library/md.c
- * Caller:
- *
- * Uncomment to enable generic message digest wrappers.
- */
-#define MBEDTLS_MD_C
-
-/**
- * \def MBEDTLS_MD2_C
- *
- * Enable the MD2 hash algorithm.
- *
- * Module:  library/md2.c
- * Caller:
- *
- * Uncomment to enable support for (rare) MD2-signed X.509 certs.
- *
- * \warning   MD2 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_MD2_C
-
-/**
- * \def MBEDTLS_MD4_C
- *
- * Enable the MD4 hash algorithm.
- *
- * Module:  library/md4.c
- * Caller:
- *
- * Uncomment to enable support for (rare) MD4-signed X.509 certs.
- *
- * \warning   MD4 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_MD4_C
-
-/**
- * \def MBEDTLS_MD5_C
- *
- * Enable the MD5 hash algorithm.
- *
- * Module:  library/md5.c
- * Caller:  library/md.c
- *          library/pem.c
- *          library/ssl_tls.c
- *
- * This module is required for SSL/TLS up to version 1.1, and for TLS 1.2
- * depending on the handshake parameters. Further, it is used for checking
- * MD5-signed certificates, and for PBKDF1 when decrypting PEM-encoded
- * encrypted keys.
- *
- * \warning   MD5 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_MD5_C
-
-/**
- * \def MBEDTLS_MEMORY_BUFFER_ALLOC_C
- *
- * Enable the buffer allocator implementation that makes use of a (stack)
- * based buffer to 'allocate' dynamic memory. (replaces calloc() and free()
- * calls)
- *
- * Module:  library/memory_buffer_alloc.c
- *
- * Requires: MBEDTLS_PLATFORM_C
- *           MBEDTLS_PLATFORM_MEMORY (to use it within mbed TLS)
- *
- * Enable this module to enable the buffer memory allocator.
- */
-//#define MBEDTLS_MEMORY_BUFFER_ALLOC_C
-
-/**
- * \def MBEDTLS_NET_C
- *
- * Enable the TCP and UDP over IPv6/IPv4 networking routines.
- *
- * \note This module only works on POSIX/Unix (including Linux, BSD and OS X)
- * and Windows. For other platforms, you'll want to disable it, and write your
- * own networking callbacks to be passed to \c mbedtls_ssl_set_bio().
- *
- * \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
- *
- * Module:  library/net_sockets.c
- *
- * This module provides networking routines.
- */
-#define MBEDTLS_NET_C
-
-/**
- * \def MBEDTLS_OID_C
- *
- * Enable the OID database.
- *
- * Module:  library/oid.c
- * Caller:  library/asn1write.c
- *          library/pkcs5.c
- *          library/pkparse.c
- *          library/pkwrite.c
- *          library/rsa.c
- *          library/x509.c
- *          library/x509_create.c
- *          library/x509_crl.c
- *          library/x509_crt.c
- *          library/x509_csr.c
- *          library/x509write_crt.c
- *          library/x509write_csr.c
- *
- * This modules translates between OIDs and internal values.
- */
-#define MBEDTLS_OID_C
-
-/**
- * \def MBEDTLS_PADLOCK_C
- *
- * Enable VIA Padlock support on x86.
- *
- * Module:  library/padlock.c
- * Caller:  library/aes.c
- *
- * Requires: MBEDTLS_HAVE_ASM
- *
- * This modules adds support for the VIA PadLock on x86.
- */
-#define MBEDTLS_PADLOCK_C
-
-/**
- * \def MBEDTLS_PEM_PARSE_C
- *
- * Enable PEM decoding / parsing.
- *
- * Module:  library/pem.c
- * Caller:  library/dhm.c
- *          library/pkparse.c
- *          library/x509_crl.c
- *          library/x509_crt.c
- *          library/x509_csr.c
- *
- * Requires: MBEDTLS_BASE64_C
- *
- * This modules adds support for decoding / parsing PEM files.
- */
-#define MBEDTLS_PEM_PARSE_C
-
-/**
- * \def MBEDTLS_PEM_WRITE_C
- *
- * Enable PEM encoding / writing.
- *
- * Module:  library/pem.c
- * Caller:  library/pkwrite.c
- *          library/x509write_crt.c
- *          library/x509write_csr.c
- *
- * Requires: MBEDTLS_BASE64_C
- *
- * This modules adds support for encoding / writing PEM files.
- */
-#define MBEDTLS_PEM_WRITE_C
-
-/**
- * \def MBEDTLS_PK_C
- *
- * Enable the generic public (asymetric) key layer.
- *
- * Module:  library/pk.c
- * Caller:  library/ssl_tls.c
- *          library/ssl_cli.c
- *          library/ssl_srv.c
- *
- * Requires: MBEDTLS_RSA_C or MBEDTLS_ECP_C
- *
- * Uncomment to enable generic public key wrappers.
- */
-#define MBEDTLS_PK_C
-
-/**
- * \def MBEDTLS_PK_PARSE_C
- *
- * Enable the generic public (asymetric) key parser.
- *
- * Module:  library/pkparse.c
- * Caller:  library/x509_crt.c
- *          library/x509_csr.c
- *
- * Requires: MBEDTLS_PK_C
- *
- * Uncomment to enable generic public key parse functions.
- */
-#define MBEDTLS_PK_PARSE_C
-
-/**
- * \def MBEDTLS_PK_WRITE_C
- *
- * Enable the generic public (asymetric) key writer.
- *
- * Module:  library/pkwrite.c
- * Caller:  library/x509write.c
- *
- * Requires: MBEDTLS_PK_C
- *
- * Uncomment to enable generic public key write functions.
- */
-#define MBEDTLS_PK_WRITE_C
-
-/**
- * \def MBEDTLS_PKCS5_C
- *
- * Enable PKCS#5 functions.
- *
- * Module:  library/pkcs5.c
- *
- * Requires: MBEDTLS_MD_C
- *
- * This module adds support for the PKCS#5 functions.
- */
-#define MBEDTLS_PKCS5_C
-
-/**
- * \def MBEDTLS_PKCS11_C
- *
- * Enable wrapper for PKCS#11 smartcard support.
- *
- * Module:  library/pkcs11.c
- * Caller:  library/pk.c
- *
- * Requires: MBEDTLS_PK_C
- *
- * This module enables SSL/TLS PKCS #11 smartcard support.
- * Requires the presence of the PKCS#11 helper library (libpkcs11-helper)
- */
-//#define MBEDTLS_PKCS11_C
-
-/**
- * \def MBEDTLS_PKCS12_C
- *
- * Enable PKCS#12 PBE functions.
- * Adds algorithms for parsing PKCS#8 encrypted private keys
- *
- * Module:  library/pkcs12.c
- * Caller:  library/pkparse.c
- *
- * Requires: MBEDTLS_ASN1_PARSE_C, MBEDTLS_CIPHER_C, MBEDTLS_MD_C
- * Can use:  MBEDTLS_ARC4_C
- *
- * This module enables PKCS#12 functions.
- */
-#define MBEDTLS_PKCS12_C
-
-/**
- * \def MBEDTLS_PLATFORM_C
- *
- * Enable the platform abstraction layer that allows you to re-assign
- * functions like calloc(), free(), snprintf(), printf(), fprintf(), exit().
- *
- * Enabling MBEDTLS_PLATFORM_C enables to use of MBEDTLS_PLATFORM_XXX_ALT
- * or MBEDTLS_PLATFORM_XXX_MACRO directives, allowing the functions mentioned
- * above to be specified at runtime or compile time respectively.
- *
- * \note This abstraction layer must be enabled on Windows (including MSYS2)
- * as other module rely on it for a fixed snprintf implementation.
- *
- * Module:  library/platform.c
- * Caller:  Most other .c files
- *
- * This module enables abstraction of common (libc) functions.
- */
-#define MBEDTLS_PLATFORM_C
-
-/**
- * \def MBEDTLS_POLY1305_C
- *
- * Enable the Poly1305 MAC algorithm.
- *
- * Module:  library/poly1305.c
- * Caller:  library/chachapoly.c
- */
-#define MBEDTLS_POLY1305_C
-
-/**
- * \def MBEDTLS_PSA_CRYPTO_C
- *
- * Enable the Platform Security Architecture cryptography API.
- *
- * Module:  library/psa_crypto.c
- *
- * Requires: MBEDTLS_CTR_DRBG_C, MBEDTLS_ENTROPY_C
- *
- */
-#define MBEDTLS_PSA_CRYPTO_C
-
-/**
- * \def MBEDTLS_PSA_CRYPTO_STORAGE_C
- *
- * Enable the Platform Security Architecture persistent key storage.
- *
- * Module:  library/psa_crypto_storage.c
- *
- * Requires: MBEDTLS_PSA_CRYPTO_C,
- *           either MBEDTLS_PSA_ITS_FILE_C or a native implementation of
- *           the PSA ITS interface
- */
-#define MBEDTLS_PSA_CRYPTO_STORAGE_C
-
-/**
- * \def MBEDTLS_PSA_ITS_FILE_C
- *
- * Enable the emulation of the Platform Security Architecture
- * Internal Trusted Storage (PSA ITS) over files.
- *
- * Module:  library/psa_its_file.c
- *
- * Requires: MBEDTLS_FS_IO
- */
-#define MBEDTLS_PSA_ITS_FILE_C
-
-/**
- * \def MBEDTLS_RIPEMD160_C
- *
- * Enable the RIPEMD-160 hash algorithm.
- *
- * Module:  library/ripemd160.c
- * Caller:  library/md.c
- *
- */
-#define MBEDTLS_RIPEMD160_C
-
-/**
- * \def MBEDTLS_RSA_C
- *
- * Enable the RSA public-key cryptosystem.
- *
- * Module:  library/rsa.c
- *          library/rsa_internal.c
- * Caller:  library/ssl_cli.c
- *          library/ssl_srv.c
- *          library/ssl_tls.c
- *          library/x509.c
- *
- * This module is used by the following key exchanges:
- *      RSA, DHE-RSA, ECDHE-RSA, RSA-PSK
- *
- * Requires: MBEDTLS_BIGNUM_C, MBEDTLS_OID_C
- */
-#define MBEDTLS_RSA_C
-
-/**
- * \def MBEDTLS_SHA1_C
- *
- * Enable the SHA1 cryptographic hash algorithm.
- *
- * Module:  library/sha1.c
- * Caller:  library/md.c
- *          library/ssl_cli.c
- *          library/ssl_srv.c
- *          library/ssl_tls.c
- *          library/x509write_crt.c
- *
- * This module is required for SSL/TLS up to version 1.1, for TLS 1.2
- * depending on the handshake parameters, and for SHA1-signed certificates.
- *
- * \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_SHA1_C
-
-/**
- * \def MBEDTLS_SHA256_C
- *
- * Enable the SHA-224 and SHA-256 cryptographic hash algorithms.
- *
- * Module:  library/sha256.c
- * Caller:  library/entropy.c
- *          library/md.c
- *          library/ssl_cli.c
- *          library/ssl_srv.c
- *          library/ssl_tls.c
- *
- * This module adds support for SHA-224 and SHA-256.
- * This module is required for the SSL/TLS 1.2 PRF function.
- */
-#define MBEDTLS_SHA256_C
-
-/**
- * \def MBEDTLS_SHA512_C
- *
- * Enable the SHA-384 and SHA-512 cryptographic hash algorithms.
- *
- * Module:  library/sha512.c
- * Caller:  library/entropy.c
- *          library/md.c
- *          library/ssl_cli.c
- *          library/ssl_srv.c
- *
- * This module adds support for SHA-384 and SHA-512.
- */
-#define MBEDTLS_SHA512_C
-
-/**
- * \def MBEDTLS_SSL_CACHE_C
- *
- * Enable simple SSL cache implementation.
- *
- * Module:  library/ssl_cache.c
- * Caller:
- *
- * Requires: MBEDTLS_SSL_CACHE_C
- */
-#define MBEDTLS_SSL_CACHE_C
-
-/**
- * \def MBEDTLS_SSL_COOKIE_C
- *
- * Enable basic implementation of DTLS cookies for hello verification.
- *
- * Module:  library/ssl_cookie.c
- * Caller:
- */
-#define MBEDTLS_SSL_COOKIE_C
-
-/**
- * \def MBEDTLS_SSL_TICKET_C
- *
- * Enable an implementation of TLS server-side callbacks for session tickets.
- *
- * Module:  library/ssl_ticket.c
- * Caller:
- *
- * Requires: MBEDTLS_CIPHER_C
- */
-#define MBEDTLS_SSL_TICKET_C
-
-/**
- * \def MBEDTLS_SSL_CLI_C
- *
- * Enable the SSL/TLS client code.
- *
- * Module:  library/ssl_cli.c
- * Caller:
- *
- * Requires: MBEDTLS_SSL_TLS_C
- *
- * This module is required for SSL/TLS client support.
- */
-#define MBEDTLS_SSL_CLI_C
-
-/**
- * \def MBEDTLS_SSL_SRV_C
- *
- * Enable the SSL/TLS server code.
- *
- * Module:  library/ssl_srv.c
- * Caller:
- *
- * Requires: MBEDTLS_SSL_TLS_C
- *
- * This module is required for SSL/TLS server support.
- */
-#define MBEDTLS_SSL_SRV_C
-
-/**
- * \def MBEDTLS_SSL_TLS_C
- *
- * Enable the generic SSL/TLS code.
- *
- * Module:  library/ssl_tls.c
- * Caller:  library/ssl_cli.c
- *          library/ssl_srv.c
- *
- * Requires: MBEDTLS_CIPHER_C, MBEDTLS_MD_C
- *           and at least one of the MBEDTLS_SSL_PROTO_XXX defines
- *
- * This module is required for SSL/TLS.
- */
-#define MBEDTLS_SSL_TLS_C
-
-/**
- * \def MBEDTLS_THREADING_C
- *
- * Enable the threading abstraction layer.
- * 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
- *
- * Module:  library/threading.c
- *
- * This allows different threading implementations (self-implemented or
- * provided).
- *
- * You will have to enable either MBEDTLS_THREADING_ALT or
- * MBEDTLS_THREADING_PTHREAD.
- *
- * Enable this layer to allow use of mutexes within mbed TLS
- */
-//#define MBEDTLS_THREADING_C
-
-/**
- * \def MBEDTLS_TIMING_C
- *
- * Enable the semi-portable timing interface.
- *
- * \note The provided implementation only works on POSIX/Unix (including Linux,
- * BSD and OS X) and Windows. On other platforms, you can either disable that
- * module and provide your own implementations of the callbacks needed by
- * \c mbedtls_ssl_set_timer_cb() for DTLS, or leave it enabled and provide
- * your own implementation of the whole module by setting
- * \c MBEDTLS_TIMING_ALT in the current file.
- *
- * \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
- *
- * Module:  library/timing.c
- * Caller:  library/havege.c
- *
- * This module is used by the HAVEGE random number generator.
- */
-#define MBEDTLS_TIMING_C
-
-/**
- * \def MBEDTLS_VERSION_C
- *
- * Enable run-time version information.
- *
- * Module:  library/version.c
- *
- * This module provides run-time version information.
- */
-#define MBEDTLS_VERSION_C
-
-/**
- * \def MBEDTLS_X509_USE_C
- *
- * Enable X.509 core for using certificates.
- *
- * Module:  library/x509.c
- * Caller:  library/x509_crl.c
- *          library/x509_crt.c
- *          library/x509_csr.c
- *
- * Requires: MBEDTLS_ASN1_PARSE_C, MBEDTLS_BIGNUM_C, MBEDTLS_OID_C,
- *           MBEDTLS_PK_PARSE_C
- *
- * This module is required for the X.509 parsing modules.
- */
-#define MBEDTLS_X509_USE_C
-
-/**
- * \def MBEDTLS_X509_CRT_PARSE_C
- *
- * Enable X.509 certificate parsing.
- *
- * Module:  library/x509_crt.c
- * Caller:  library/ssl_cli.c
- *          library/ssl_srv.c
- *          library/ssl_tls.c
- *
- * Requires: MBEDTLS_X509_USE_C
- *
- * This module is required for X.509 certificate parsing.
- */
-#define MBEDTLS_X509_CRT_PARSE_C
-
-/**
- * \def MBEDTLS_X509_CRL_PARSE_C
- *
- * Enable X.509 CRL parsing.
- *
- * Module:  library/x509_crl.c
- * Caller:  library/x509_crt.c
- *
- * Requires: MBEDTLS_X509_USE_C
- *
- * This module is required for X.509 CRL parsing.
- */
-#define MBEDTLS_X509_CRL_PARSE_C
-
-/**
- * \def MBEDTLS_X509_CSR_PARSE_C
- *
- * Enable X.509 Certificate Signing Request (CSR) parsing.
- *
- * Module:  library/x509_csr.c
- * Caller:  library/x509_crt_write.c
- *
- * Requires: MBEDTLS_X509_USE_C
- *
- * This module is used for reading X.509 certificate request.
- */
-#define MBEDTLS_X509_CSR_PARSE_C
-
-/**
- * \def MBEDTLS_X509_CREATE_C
- *
- * Enable X.509 core for creating certificates.
- *
- * Module:  library/x509_create.c
- *
- * Requires: MBEDTLS_BIGNUM_C, MBEDTLS_OID_C, MBEDTLS_PK_WRITE_C
- *
- * This module is the basis for creating X.509 certificates and CSRs.
- */
-#define MBEDTLS_X509_CREATE_C
-
-/**
- * \def MBEDTLS_X509_CRT_WRITE_C
- *
- * Enable creating X.509 certificates.
- *
- * Module:  library/x509_crt_write.c
- *
- * Requires: MBEDTLS_X509_CREATE_C
- *
- * This module is required for X.509 certificate creation.
- */
-#define MBEDTLS_X509_CRT_WRITE_C
-
-/**
- * \def MBEDTLS_X509_CSR_WRITE_C
- *
- * Enable creating X.509 Certificate Signing Requests (CSR).
- *
- * Module:  library/x509_csr_write.c
- *
- * Requires: MBEDTLS_X509_CREATE_C
- *
- * This module is required for X.509 certificate request writing.
- */
-#define MBEDTLS_X509_CSR_WRITE_C
-
-/**
- * \def MBEDTLS_XTEA_C
- *
- * Enable the XTEA block cipher.
- *
- * Module:  library/xtea.c
- * Caller:
- */
-#define MBEDTLS_XTEA_C
-
-/* \} name SECTION: mbed TLS modules */
-
-/**
- * \name SECTION: Module configuration options
- *
- * This section allows for the setting of module specific sizes and
- * configuration options. The default values are already present in the
- * relevant header files and should suffice for the regular use cases.
- *
- * 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).
- * \{
- */
-
-/* MPI / BIGNUM options */
-//#define MBEDTLS_MPI_WINDOW_SIZE            6 /**< Maximum windows size used. */
-//#define MBEDTLS_MPI_MAX_SIZE            1024 /**< Maximum number of bytes for usable MPIs. */
-
-/* CTR_DRBG options */
-//#define MBEDTLS_CTR_DRBG_ENTROPY_LEN               48 /**< Amount of entropy used per seed by default (48 with SHA-512, 32 with SHA-256) */
-//#define MBEDTLS_CTR_DRBG_RESEED_INTERVAL        10000 /**< Interval before reseed is performed by default */
-//#define MBEDTLS_CTR_DRBG_MAX_INPUT                256 /**< Maximum number of additional input bytes */
-//#define MBEDTLS_CTR_DRBG_MAX_REQUEST             1024 /**< Maximum number of requested bytes per call */
-//#define MBEDTLS_CTR_DRBG_MAX_SEED_INPUT           384 /**< Maximum size of (re)seed buffer */
-//#define MBEDTLS_CTR_DRBG_USE_128_BIT_KEY              /**< Use 128-bit key for CTR_DRBG - may reduce security (see ctr_drbg.h) */
-
-/* HMAC_DRBG options */
-//#define MBEDTLS_HMAC_DRBG_RESEED_INTERVAL   10000 /**< Interval before reseed is performed by default */
-//#define MBEDTLS_HMAC_DRBG_MAX_INPUT           256 /**< Maximum number of additional input bytes */
-//#define MBEDTLS_HMAC_DRBG_MAX_REQUEST        1024 /**< Maximum number of requested bytes per call */
-//#define MBEDTLS_HMAC_DRBG_MAX_SEED_INPUT      384 /**< Maximum size of (re)seed buffer */
-
-/* ECP options */
-//#define MBEDTLS_ECP_MAX_BITS             521 /**< Maximum bit size of groups */
-//#define MBEDTLS_ECP_WINDOW_SIZE            6 /**< Maximum window size used */
-//#define MBEDTLS_ECP_FIXED_POINT_OPTIM      1 /**< Enable fixed-point speed-up */
-
-/* Entropy options */
-//#define MBEDTLS_ENTROPY_MAX_SOURCES                20 /**< Maximum number of sources supported */
-//#define MBEDTLS_ENTROPY_MAX_GATHER                128 /**< Maximum amount requested from entropy sources */
-//#define MBEDTLS_ENTROPY_MIN_HARDWARE               32 /**< Default minimum number of bytes required for the hardware entropy source mbedtls_hardware_poll() before entropy is released */
-
-/* Memory buffer allocator options */
-//#define MBEDTLS_MEMORY_ALIGN_MULTIPLE      4 /**< Align on multiples of this value */
-
-/* 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 */
-//#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 */
-//#define MBEDTLS_PLATFORM_STD_PRINTF        printf /**< Default printf to use, can be undefined */
-/* Note: your snprintf must correctly zero-terminate the buffer! */
-//#define MBEDTLS_PLATFORM_STD_SNPRINTF    snprintf /**< Default snprintf to use, can be undefined */
-//#define MBEDTLS_PLATFORM_STD_EXIT_SUCCESS       0 /**< Default exit value to use, can be undefined */
-//#define MBEDTLS_PLATFORM_STD_EXIT_FAILURE       1 /**< Default exit value to use, can be undefined */
-//#define MBEDTLS_PLATFORM_STD_NV_SEED_READ   mbedtls_platform_std_nv_seed_read /**< Default nv_seed_read function to use, can be undefined */
-//#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 */
-/* 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_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 */
-//#define MBEDTLS_PLATFORM_FPRINTF_MACRO      fprintf /**< Default fprintf macro to use, can be undefined */
-//#define MBEDTLS_PLATFORM_PRINTF_MACRO        printf /**< Default printf macro to use, can be undefined */
-/* Note: your snprintf must correctly zero-terminate the buffer! */
-//#define MBEDTLS_PLATFORM_SNPRINTF_MACRO    snprintf /**< Default snprintf macro to use, can be undefined */
-//#define MBEDTLS_PLATFORM_VSNPRINTF_MACRO    vsnprintf /**< Default vsnprintf macro to use, can be undefined */
-//#define MBEDTLS_PLATFORM_NV_SEED_READ_MACRO   mbedtls_platform_std_nv_seed_read /**< Default nv_seed_read function to use, can be undefined */
-//#define MBEDTLS_PLATFORM_NV_SEED_WRITE_MACRO  mbedtls_platform_std_nv_seed_write /**< Default nv_seed_write function to use, can be undefined */
-
-/**
- * \brief       This macro is invoked by the library when an invalid parameter
- *              is detected that is only checked with MBEDTLS_CHECK_PARAMS
- *              (see the documentation of that option for context).
- *
- *              When you leave this undefined here, a default definition is
- *              provided that invokes the function mbedtls_param_failed(),
- *              which is declared in platform_util.h for the benefit of the
- *              library, but that you need to define in your application.
- *
- *              When you define this here, this replaces the default
- *              definition in platform_util.h (which no longer declares the
- *              function mbedtls_param_failed()) and it is your responsibility
- *              to make sure this macro expands to something suitable (in
- *              particular, that all the necessary declarations are visible
- *              from within the library - you can ensure that by providing
- *              them in this file next to the macro definition).
- *
- *              Note that you may define this macro to expand to nothing, in
- *              which case you don't have to worry about declarations or
- *              definitions. However, you will then be notified about invalid
- *              parameters only in non-void functions, and void function will
- *              just silently return early on invalid parameters, which
- *              partially negates the benefits of enabling
- *              #MBEDTLS_CHECK_PARAMS in the first place, so is discouraged.
- *
- * \param cond  The expression that should evaluate to true, but doesn't.
- */
-//#define MBEDTLS_PARAM_FAILED( cond )               assert( cond )
-
-/* SSL Cache options */
-//#define MBEDTLS_SSL_CACHE_DEFAULT_TIMEOUT       86400 /**< 1 day  */
-//#define MBEDTLS_SSL_CACHE_DEFAULT_MAX_ENTRIES      50 /**< Maximum entries in cache */
-
-/* SSL options */
-
-/** \def MBEDTLS_SSL_MAX_CONTENT_LEN
- *
- * Maximum length (in bytes) of incoming and outgoing plaintext fragments.
- *
- * This determines the size of both the incoming and outgoing TLS I/O buffers
- * in such a way that both are capable of holding the specified amount of
- * plaintext data, regardless of the protection mechanism used.
- *
- * To configure incoming and outgoing I/O buffers separately, use
- * #MBEDTLS_SSL_IN_CONTENT_LEN and #MBEDTLS_SSL_OUT_CONTENT_LEN,
- * which overwrite the value set by this option.
- *
- * \note When using a value less than the default of 16KB on the client, it is
- *       recommended to use the Maximum Fragment Length (MFL) extension to
- *       inform the server about this limitation. On the server, there
- *       is no supported, standardized way of informing the client about
- *       restriction on the maximum size of incoming messages, and unless
- *       the limitation has been communicated by other means, it is recommended
- *       to only change the outgoing buffer size #MBEDTLS_SSL_OUT_CONTENT_LEN
- *       while keeping the default value of 16KB for the incoming buffer.
- *
- * Uncomment to set the maximum plaintext size of both
- * incoming and outgoing I/O buffers.
- */
-//#define MBEDTLS_SSL_MAX_CONTENT_LEN             16384
-
-/** \def MBEDTLS_SSL_IN_CONTENT_LEN
- *
- * Maximum length (in bytes) of incoming plaintext fragments.
- *
- * This determines the size of the incoming TLS I/O buffer in such a way
- * that it is capable of holding the specified amount of plaintext data,
- * regardless of the protection mechanism used.
- *
- * If this option is undefined, it inherits its value from
- * #MBEDTLS_SSL_MAX_CONTENT_LEN.
- *
- * \note When using a value less than the default of 16KB on the client, it is
- *       recommended to use the Maximum Fragment Length (MFL) extension to
- *       inform the server about this limitation. On the server, there
- *       is no supported, standardized way of informing the client about
- *       restriction on the maximum size of incoming messages, and unless
- *       the limitation has been communicated by other means, it is recommended
- *       to only change the outgoing buffer size #MBEDTLS_SSL_OUT_CONTENT_LEN
- *       while keeping the default value of 16KB for the incoming buffer.
- *
- * Uncomment to set the maximum plaintext size of the incoming I/O buffer
- * independently of the outgoing I/O buffer.
- */
-//#define MBEDTLS_SSL_IN_CONTENT_LEN              16384
-
-/** \def MBEDTLS_SSL_OUT_CONTENT_LEN
- *
- * Maximum length (in bytes) of outgoing plaintext fragments.
- *
- * This determines the size of the outgoing TLS I/O buffer in such a way
- * that it is capable of holding the specified amount of plaintext data,
- * regardless of the protection mechanism used.
- *
- * If this option undefined, it inherits its value from
- * #MBEDTLS_SSL_MAX_CONTENT_LEN.
- *
- * It is possible to save RAM by setting a smaller outward buffer, while keeping
- * the default inward 16384 byte buffer to conform to the TLS specification.
- *
- * The minimum required outward buffer size is determined by the handshake
- * protocol's usage. Handshaking will fail if the outward buffer is too small.
- * The specific size requirement depends on the configured ciphers and any
- * certificate data which is sent during the handshake.
- *
- * Uncomment to set the maximum plaintext size of the outgoing I/O buffer
- * independently of the incoming I/O buffer.
- */
-//#define MBEDTLS_SSL_OUT_CONTENT_LEN             16384
-
-/** \def MBEDTLS_SSL_DTLS_MAX_BUFFERING
- *
- * Maximum number of heap-allocated bytes for the purpose of
- * DTLS handshake message reassembly and future message buffering.
- *
- * This should be at least 9/8 * MBEDTLSSL_IN_CONTENT_LEN
- * to account for a reassembled handshake message of maximum size,
- * together with its reassembly bitmap.
- *
- * A value of 2 * MBEDTLS_SSL_IN_CONTENT_LEN (32768 by default)
- * should be sufficient for all practical situations as it allows
- * to reassembly a large handshake message (such as a certificate)
- * while buffering multiple smaller handshake messages.
- *
- */
-//#define MBEDTLS_SSL_DTLS_MAX_BUFFERING             32768
-
-//#define MBEDTLS_SSL_DEFAULT_TICKET_LIFETIME     86400 /**< Lifetime of session tickets (if enabled) */
-//#define MBEDTLS_PSK_MAX_LEN               32 /**< Max size of TLS pre-shared keys, in bytes (default 256 bits) */
-//#define MBEDTLS_SSL_COOKIE_TIMEOUT        60 /**< Default expiration delay of DTLS cookies, in seconds if HAVE_TIME, or in number of cookies issued */
-
-/**
- * Complete list of ciphersuites to use, in order of preference.
- *
- * \warning No dependency checking is done on that field! This option can only
- * be used to restrict the set of available ciphersuites. It is your
- * responsibility to make sure the needed modules are active.
- *
- * Use this to save a few hundred bytes of ROM (default ordering of all
- * available ciphersuites) and a few to a few hundred bytes of RAM.
- *
- * The value below is only an example, not the default.
- */
-//#define MBEDTLS_SSL_CIPHERSUITES MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
-
-/* X509 options */
-//#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 certificate signing.
- * Without this build-time option, SHA-1 support must be activated explicitly
- * through mbedtls_ssl_conf_cert_profile. Turning on this option is not
- * recommended because of it is possible to generate SHA-1 collisions, however
- * this may be safe for legacy infrastructure where additional controls apply.
- *
- * \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_CERTIFICATES
-
-/**
- * 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
-
-/* \} name SECTION: Customisation configuration options */
-
-/* Target and application specific configurations
- *
- * Allow user to override any previous default.
- *
- */
-#if defined(MBEDTLS_USER_CONFIG_FILE)
-#include MBEDTLS_USER_CONFIG_FILE
-#endif
-
-#include "mbedtls/check_config.h"
-
-#endif /* MBEDTLS_CONFIG_H */
diff --git a/third_party/mbedtls/repo/configs/config-suite-b.h b/third_party/mbedtls/repo/configs/config-suite-b.h
index 6eb03a9..9cad382 100644
--- a/third_party/mbedtls/repo/configs/config-suite-b.h
+++ b/third_party/mbedtls/repo/configs/config-suite-b.h
@@ -80,8 +80,7 @@
 #define MBEDTLS_AES_ROM_TABLES
 
 /* Save RAM by adjusting to our exact needs */
-#define MBEDTLS_ECP_MAX_BITS   384
-#define MBEDTLS_MPI_MAX_SIZE    48 // 384 bits is 48 bytes
+#define MBEDTLS_MPI_MAX_SIZE    48 // 48 bytes for a 384-bit elliptic curve
 
 /* Save RAM at the expense of speed, see ecp.h */
 #define MBEDTLS_ECP_WINDOW_SIZE        2
diff --git a/third_party/mbedtls/repo/configs/config-thread.h b/third_party/mbedtls/repo/configs/config-thread.h
index 47dd5e2..8464fcb 100644
--- a/third_party/mbedtls/repo/configs/config-thread.h
+++ b/third_party/mbedtls/repo/configs/config-thread.h
@@ -81,8 +81,7 @@
 #define MBEDTLS_AES_ROM_TABLES
 
 /* Save RAM by adjusting to our exact needs */
-#define MBEDTLS_ECP_MAX_BITS             256
-#define MBEDTLS_MPI_MAX_SIZE              32 // 256 bits is 32 bytes
+#define MBEDTLS_MPI_MAX_SIZE              32 // 32 bytes for a 256-bit elliptic curve
 
 /* 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
diff --git a/third_party/mbedtls/repo/docs/.gitignore b/third_party/mbedtls/repo/docs/.gitignore
index 33ae5ac..23f832b 100644
--- a/third_party/mbedtls/repo/docs/.gitignore
+++ b/third_party/mbedtls/repo/docs/.gitignore
@@ -1,3 +1,2 @@
 *.html
 *.pdf
-!PSACryptoDriverModelSpec.pdf
diff --git a/third_party/mbedtls/repo/docs/PSACryptoDriverModelSpec.pdf b/third_party/mbedtls/repo/docs/PSACryptoDriverModelSpec.pdf
deleted file mode 100644
index cf11380..0000000
--- a/third_party/mbedtls/repo/docs/PSACryptoDriverModelSpec.pdf
+++ /dev/null
Binary files differ
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
new file mode 100644
index 0000000..cd4d427
--- /dev/null
+++ b/third_party/mbedtls/repo/docs/architecture/psa-crypto-implementation-structure.md
@@ -0,0 +1,73 @@
+PSA Cryptograpy API implementation and PSA driver interface
+===========================================================
+
+## Introduction
+
+The [PSA Cryptography API specification](https://armmbed.github.io/mbed-crypto/psa/#application-programming-interface) defines an interface to cryptographic operations for which the Mbed TLS library provides a reference implementation. The PSA Cryptography API specification is complemented by the PSA driver interface specification which defines an interface for cryptoprocessor drivers.
+
+This document describes the high level organization of the Mbed TLS PSA Cryptography API implementation which is tightly related to the PSA driver interface.
+
+## High level organization of the Mbed TLS PSA Cryptography API implementation
+In one sentence, the Mbed TLS PSA Cryptography API implementation is made of a core and PSA drivers as defined in the PSA driver interface. The key point is that software cryptographic operations are organized as PSA drivers: they interact with the core through the PSA driver interface.
+
+### Rationale
+
+* Addressing software and hardware cryptographic implementations through the same C interface reduces the core code size and its call graph complexity. The core and its dispatching to software and hardware implementations are consequently easier to test and validate.
+* The organization of the software cryptographic implementations in drivers promotes modularization of those implementations.
+* As hardware capabilities, software cryptographic functionalities can be described by a JSON driver description file as defined in the PSA driver interface.
+* Along with JSON driver description files, the PSA driver specification defines the deliverables for a driver to be included into the Mbed TLS PSA Cryptography implementation. This provides a natural framework to integrate third party or alternative software implementations of cryptographic operations.
+
+## The Mbed TLS PSA Cryptography API implementation core
+
+The core implements all the APIs as defined in the PSA Cryptography API specification but does not perform on its own any cryptographic operation. The core relies on PSA drivers to actually
+perform the cryptographic operations. The core is responsible for:
+
+* the key store.
+* checking PSA API arguments and translating them into valid arguments for the necessary calls to the PSA driver interface.
+* dispatching the cryptographic operations to the appropriate PSA drivers.
+
+The sketch of an Mbed TLS PSA cryptographic API implementation is thus:
+```C                                                                            
+psa_status_t psa_api( ... )
+{
+    psa_status_t status;
+
+    /* Pre driver interface call processing: validation of arguments, building
+     * of arguments for the call to the driver interface, ... */
+
+    ...
+
+    /* Call to the driver interface */
+    status = psa_driver_wrapper_<entry_point>( ... );
+    if( status != PSA_SUCCESS )
+        return( status );
+
+    /* Post driver interface call processing: validation of the values returned
+     * by the driver, finalization of the values to return to the caller,
+     * clean-up in case of error ... */
+}
+```
+The code of most PSA APIs is expected to match precisely the above layout. However, it is likely that the code structure of some APIs will be more complicated with several calls to the driver interface, mainly to encompass a larger variety of hardware designs. For example, to encompass hardware accelerators that are capable of verifying a MAC and those that are only capable of computing a MAC, the psa_mac_verify() API could call first psa_driver_wrapper_mac_verify() and then fallback to psa_driver_wrapper_mac_compute().
+
+The implementations of `psa_driver_wrapper_<entry_point>` functions are generated by the build system based on the JSON driver description files of the various PSA drivers making up the Mbed TLS PSA Cryptography API implementation. The implementations are generated in a psa_crypto_driver_wrappers.c C file and the function prototypes declared in a psa_crypto_driver_wrappers.h header file.
+
+The psa_driver_wrapper_<entry_point>() functions dispatch cryptographic operations to accelerator drivers, secure element drivers as well as to the software implementations of cryptographic operations.
+
+Note that the implementation allows to build the library with only a C compiler by shipping a generated file corresponding to a pure software implementation. The driver entry points and their code in this generated file are guarded by pre-processor directives based on PSA_WANT_xyz macros (see [Conditional inclusion of cryptographic mechanism through the PSA API in Mbed TLS](psa-conditional-inclusion-c.html). That way, it is possible to compile and include in the library only the desired cryptographic operations.
+
+### Key creation
+
+Key creation implementation in Mbed TLS PSA core is articulated around three internal functions: psa_start_key_creation(), psa_finish_key_creation() and psa_fail_key_creation(). Implementations of key creation PSA APIs, namely psa_import_key(), psa_generate_key(), psa_key_derivation_output_key() and psa_copy_key() go by the following sequence:
+    1. Check the input parameters.
+    2. Call psa_start_key_creation() that allocates a key slot, prepares it with the specified key attributes, and in case of a volatile key assign it a volatile key identifier.
+    3. Generate or copy the key material into the key slot. This entails the allocation of the buffer to store the key material.
+    4. Call psa_finish_key_creation() that mostly saves persistent keys into persistent storage.
+
+In case of any error occurring at step 3 or 4, psa_fail_key_creation() is called. It wipes and cleans the slot especially the key material: reset to zero of the RAM memory that contained the key material, free the allocated buffer.
+
+
+## Mbed TLS PSA Cryptography API implementation drivers
+
+A driver of the Mbed TLS PSA Cryptography API implementation (Mbed TLS PSA driver in the following) is a driver in the sense that it is compliant with the PSA driver interface specification. But it is not an actual driver that drives some hardware. It implements cryptographic operations purely in software.
+
+An Mbed TLS PSA driver C file is named psa_crypto_<driver_name>.c and its associated header file psa_crypto_<driver_name>.h. The functions implementing a driver entry point as defined in the PSA driver interface specification are named as mbedtls_psa_<driver name>_<entry point>(). As an example, the psa_crypto_rsa.c and psa_crypto_rsa.h are the files containing the Mbed TLS PSA driver implementing RSA cryptographic operations. This RSA driver implements among other entry points the "import_key" entry point. The function implementing this entry point is named mbedtls_psa_rsa_import_key().
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 d6769da..086fc1a 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
@@ -4,9 +4,19 @@
 
 The driver interfaces are standardized through PSA Cryptography functional specifications.
 
-## Secure element driver interface
+## Secure element driver interface testing
 
-The 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.
+### Secure element driver interfaces
+
+#### Opaque driver interface
+
+The [unified driver interface](../../proposed/psa-driver-interface.md) supports both transparent drivers (for accelerators) and opaque drivers (for secure elements).
+
+Drivers exposing this interface need to be registered at compile time by declaring their JSON description file.
+
+#### 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 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.
 
@@ -18,6 +28,8 @@
 
 #### SE driver registration
 
+This applies to dynamic drivers only.
+
 * Test `psa_register_se_driver` with valid and with invalid arguments.
 * Make at least one failing call to `psa_register_se_driver` followed by a successful call.
 * Make at least one test that successfully registers the maximum number of drivers and fails to register one more.
@@ -102,14 +114,20 @@
 
 A PKCS#11 driver would be a good candidate. It would be useful as part of our product offering.
 
-## Accelerator driver interface
+## Transparent driver interface testing
 
-The accelerator driver interface is defined by [`psa/crypto_accel_driver.h`](../../../include/psa/crypto_accel_driver.h).
+The [unified driver interface](../../proposed/psa-driver-interface.md) defines interfaces for accelerators.
 
-TODO
+### Test requirements
 
-## Entropy driver interface
+#### Requirements for transparent driver testing
 
-The entropy driver interface is defined by [`psa/crypto_entropy_driver.h`](../../../include/psa/crypto_entropy_driver.h).
+Every cryptographic mechanism for which a transparent driver interface exists (key creation, cryptographic operations, …) must be exercised in at least one build. The test must verify that the driver code is called.
+
+#### Requirements for fallback
+
+The driver interface includes a fallback mechanism so that a driver can reject a request at runtime and let another driver handle the request. For each entry point, there must be at least three test runs with two or more drivers available with driver A configured to fall back to driver B, with one run where A returns `PSA_SUCCESS`, one where A returns `PSA_ERROR_NOT_SUPPORTED` and B is invoked, and one where A returns a different error and B is not invoked.
+
+## Entropy and randomness interface testing
 
 TODO
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
new file mode 100644
index 0000000..e293985
--- /dev/null
+++ b/third_party/mbedtls/repo/docs/architecture/testing/psa-storage-format-testing.md
@@ -0,0 +1,127 @@
+# Mbed TLS PSA keystore format stability testing strategy
+
+## Introduction
+
+The PSA crypto subsystem includes a persistent key store. It is possible to create a persistent key and read it back later. This must work even if Mbed TLS has been upgraded in the meantime (except for deliberate breaks in the backward compatibility of the storage).
+
+The goal of this document is to define a test strategy for the key store that not only validates that it's possible to load a key that was saved with the version of Mbed TLS under test, but also that it's possible to load a key that was saved with previous versions of Mbed TLS.
+
+Interoperability is not a goal: PSA crypto implementations are not intended to have compatible storage formats. Downgrading is not required to work.
+
+## General approach
+
+### Limitations of a direct approach
+
+The goal of storage format stability testing is: as a user of Mbed TLS, I want to store a key under version V and read it back under version W, with W ≥ V.
+
+Doing the testing this way would be difficult because we'd need to have version V of Mbed TLS available when testing version W.
+
+An alternative, semi-direct approach consists of generating test data under version V, and reading it back under version W. Done naively, this would require keeping a large amount of test data (full test coverage multiplied by the number of versions that we want to preserve backward compatibility with).
+
+### Save-and-compare approach
+
+Importing and saving a key is deterministic. Therefore we can ensure the stability of the storage format by creating test cases under a version V of Mbed TLS, where the test case parameters include both the parameters to pass to key creation and the expected state of the storage after the key is created. The test case creates a key as indicated by the parameters, then compares the actual state of the storage with the expected state.
+
+In addition, the test case also loads the key and checks that it has the expected data and metadata. Import-and-save testing and load-and-check testing can be split into separate test functions with the same payloads.
+
+If the test passes with version V, this means that the test data is consistent with what the implementation does. When the test later runs under version W ≥ V, it creates and reads back a storage state which is known to be identical to the state that V would have produced. Thus, this approach validates that W can read storage states created by V.
+
+Note that it is the combination of import-and-save passing on version V and load-and-check passing on version W with the same data that proves that version W can read back what version V wrote. From the perspective of a particular version of the library, the import-and-save tests guarantee forward compatibility while the load-and-check tests guarantee backward compatibility.
+
+Use a similar approach for files other than keys where possible and relevant.
+
+### Keeping up with storage format evolution
+
+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.
+
+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:
+
+* [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).
+
+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.
+
+## Key storage testing
+
+This section describes the desired test cases for keys created with the current storage format version. When the storage format changes, if backward compatibility is desired, old test data should be kept as described under [“Keeping up with storage format evolution”](#keeping-up-with-storage-format-evolution).
+
+### Keystore layout
+
+Objective: test that the key file name corresponds to the key identifier.
+
+Method: Create a key with a given identifier (using `psa_import_key`) and verify that a file with the expected name is created, and no other. Repeat for different identifiers.
+
+### General key format
+
+Objective: test the format of the key file: which field goes where and how big it is.
+
+Method: Create a key with certain metadata with `psa_import_key`. Read the file content and validate that it has the expected layout, deduced from the storage specification. Repeat with different metadata. Ensure that there are test cases covering all fields.
+
+### Enumeration of test cases for keys
+
+Objective: ensure that the coverage is sufficient to have assurance that all keys are stored correctly. This requires a sufficient selection of key types, sizes, policies, etc.
+
+In particular, the tests must validate that each `PSA_xxx` constant that is stored in a key is covered by at least one test case:
+
+* Lifetimes: `PSA_KEY_LIFETIME_xxx`, `PSA_KEY_PERSISTENCE_xxx`, `PSA_KEY_LOCATION_xxx`.
+* Usage flags: `PSA_KEY_USAGE_xxx`.
+* Algorithms in policies: `PSA_ALG_xxx`.
+* Key types: `PSA_KEY_TYPE_xxx`, `PSA_ECC_FAMILY_xxx`, `PSA_DH_FAMILY_xxx`.
+
+In addition, the coverage of key material must ensure that any variation in key representation is detected. See [“Considerations on key material representations”](#Considerations-on-key-material-representations) for considerations regarding key types.
+
+Method: Each test case creates a key with `psa_import_key`, purges it from memory, then reads it back and exercises it.
+
+Generate test cases automatically based on an enumeration of available constants and some knowledge of what attributes (sizes, algorithms, …) and content to use for keys of a certain type.
+
+### Testing with alternative lifetime values
+
+Objective: have test coverage for lifetimes other than the default persistent lifetime (`PSA_KEY_LIFETIME_PERSISTENT`).
+
+Method:
+
+* For alternative locations: have tests conditional on the presence of a driver for that location.
+* For alternative persistence levels: have load-and-check tests for supported persistence levels. We may also want to have negative tests ensuring that keys with a not-supported persistence level are not accidentally created.
+
+### Considerations on key material representations
+
+The risks of incompatibilities in key representations depends on the key type and on the presence of drivers. Compatibility of and with drivers is currently out of scope of this document.
+
+Some types only have one plausible representation. Others admit alternative plausible representations (different encodings, or non-canonical representations).
+Here are some areas to watch for, with an identified risk of incompatibilities.
+
+* HMAC keys longer than the block size: pre-hashed or not?
+* DES keys: was parity enforced?
+* RSA keys: can invalid DER encodings (e.g. leading zeros, ignored sign bit) have been stored?
+* RSA private keys: can invalid CRT parameters have been stored?
+* Montgomery private keys: were they stored in masked form?
+
+## Random generator state
+
+TODO
+
+## Driver state files
+
+Not yet implemented.
+
+TODO
+
+## Storage transaction resumption
+
+Only relevant for secure element support. Not yet fully implemented.
+
+TODO
+
+## PSA ITS over file
+
+TODO
diff --git a/third_party/mbedtls/repo/docs/architecture/tls13-experimental.md b/third_party/mbedtls/repo/docs/architecture/tls13-experimental.md
index 3db16e0..10cbfa1 100644
--- a/third_party/mbedtls/repo/docs/architecture/tls13-experimental.md
+++ b/third_party/mbedtls/repo/docs/architecture/tls13-experimental.md
@@ -47,3 +47,22 @@
   Those functions are implemented in `library/ssl_tls13_keys.c` and
   tested in `test_suite_ssl` using test vectors from RFC 8448 and
   https://tls13.ulfheim.net/.
+
+- New TLS Message Processing Stack (MPS)
+
+  The TLS 1.3 prototype is developed alongside a rewrite of the TLS messaging layer,
+  encompassing low-level details such as record parsing, handshake reassembly, and
+  DTLS retransmission state machine.
+
+  MPS has the following components:
+  - Layer 1 (Datagram handling)
+  - Layer 2 (Record handling)
+  - Layer 3 (Message handling)
+  - Layer 4 (Retransmission State Machine)
+  - Reader  (Abstracted pointer arithmetic and reassembly logic for incoming data)
+  - Writer  (Abstracted pointer arithmetic and fragmentation logic for outgoing data)
+
+  Of those components, the following have been upstreamed
+  as part of `MBEDTLS_SSL_PROTO_TLS1_3_EXPERIMENTAL`:
+
+  - Reader ([`library/mps_reader.h`](../../library/mps_reader.h))
diff --git a/third_party/mbedtls/repo/docs/getting_started.md b/third_party/mbedtls/repo/docs/getting_started.md
index 15d5a31..fdbf0e9 100644
--- a/third_party/mbedtls/repo/docs/getting_started.md
+++ b/third_party/mbedtls/repo/docs/getting_started.md
@@ -76,7 +76,7 @@
 {
     psa_status_t status;
     psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
-    psa_key_id_t key;
+    psa_key_id_t key_id;
 
     printf("Import an AES key...\t");
     fflush(stdout);
@@ -95,7 +95,7 @@
     psa_set_key_bits(&attributes, 128);
 
     /* Import the key */
-    status = psa_import_key(&attributes, key, key_len, &key);
+    status = psa_import_key(&attributes, key, key_len, &key_id);
     if (status != PSA_SUCCESS) {
         printf("Failed to import key\n");
         return;
@@ -106,7 +106,7 @@
     psa_reset_key_attributes(&attributes);
 
     /* Destroy the key */
-    psa_destroy_key(key);
+    psa_destroy_key(key_id);
 
     mbedtls_psa_crypto_free();
 }
@@ -135,7 +135,7 @@
                         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;
+    psa_key_id_t key_id;
 
     printf("Sign a message...\t");
     fflush(stdout);
@@ -154,14 +154,14 @@
     psa_set_key_bits(&attributes, 1024);
 
     /* Import the key */
-    status = psa_import_key(&attributes, key, key_len, &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, PSA_ALG_RSA_PKCS1V15_SIGN_RAW,
+    status = psa_sign_hash(key_id, PSA_ALG_RSA_PKCS1V15_SIGN_RAW,
                            hash, sizeof(hash),
                            signature, sizeof(signature),
                            &signature_length);
@@ -176,7 +176,7 @@
     psa_reset_key_attributes(&attributes);
 
     /* Destroy the key */
-    psa_destroy_key(key);
+    psa_destroy_key(key_id);
 
     mbedtls_psa_crypto_free();
 }
@@ -203,7 +203,7 @@
 void encrypt_with_symmetric_ciphers(const uint8_t *key, size_t key_len)
 {
     enum {
-        block_size = PSA_BLOCK_CIPHER_BLOCK_SIZE(PSA_KEY_TYPE_AES),
+        block_size = PSA_BLOCK_CIPHER_BLOCK_LENGTH(PSA_KEY_TYPE_AES),
     };
     psa_status_t status;
     psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
@@ -213,7 +213,7 @@
     size_t iv_len;
     uint8_t output[block_size];
     size_t output_len;
-    psa_key_id_t key;
+    psa_key_id_t key_id;
     psa_cipher_operation_t operation = PSA_CIPHER_OPERATION_INIT;
 
     printf("Encrypt with cipher...\t");
@@ -232,7 +232,7 @@
     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);
+    status = psa_import_key(&attributes, key, key_len, &key_id);
     if (status != PSA_SUCCESS) {
         printf("Failed to import a key\n");
         return;
@@ -240,7 +240,7 @@
     psa_reset_key_attributes(&attributes);
 
     /* Encrypt the plaintext */
-    status = psa_cipher_encrypt_setup(&operation, key, alg);
+    status = psa_cipher_encrypt_setup(&operation, key_id, alg);
     if (status != PSA_SUCCESS) {
         printf("Failed to begin cipher operation\n");
         return;
@@ -268,7 +268,7 @@
     psa_cipher_abort(&operation);
 
     /* Destroy the key */
-    psa_destroy_key(key);
+    psa_destroy_key(key_id);
 
     mbedtls_psa_crypto_free();
 }
@@ -288,7 +288,7 @@
 void decrypt_with_symmetric_ciphers(const uint8_t *key, size_t key_len)
 {
     enum {
-        block_size = PSA_BLOCK_CIPHER_BLOCK_SIZE(PSA_KEY_TYPE_AES),
+        block_size = PSA_BLOCK_CIPHER_BLOCK_LENGTH(PSA_KEY_TYPE_AES),
     };
     psa_status_t status;
     psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
@@ -298,7 +298,7 @@
     uint8_t iv[block_size] = ENCRYPTED_WITH_IV;
     uint8_t output[block_size];
     size_t output_len;
-    psa_key_id_t key;
+    psa_key_id_t key_id;
 
     printf("Decrypt with cipher...\t");
     fflush(stdout);
@@ -316,7 +316,7 @@
     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);
+    status = psa_import_key(&attributes, key, key_len, &key_id);
     if (status != PSA_SUCCESS) {
         printf("Failed to import a key\n");
         return;
@@ -324,7 +324,7 @@
     psa_reset_key_attributes(&attributes);
 
     /* Decrypt the ciphertext */
-    status = psa_cipher_decrypt_setup(&operation, key, alg);
+    status = psa_cipher_decrypt_setup(&operation, key_id, alg);
     if (status != PSA_SUCCESS) {
         printf("Failed to begin cipher operation\n");
         return;
@@ -352,7 +352,7 @@
     psa_cipher_abort(&operation);
 
     /* Destroy the key */
-    psa_destroy_key(key);
+    psa_destroy_key(key_id);
 
     mbedtls_psa_crypto_free();
 }
@@ -445,7 +445,7 @@
         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_SIZE(alg);
+    size_t expected_hash_len = PSA_HASH_LENGTH(alg);
 
     printf("Verify a hash...\t");
     fflush(stdout);
@@ -482,7 +482,7 @@
     mbedtls_psa_crypto_free();
 ```
 
-The API provides the macro `PSA_HASH_SIZE`, which returns the expected hash length (in bytes) for the specified algorithm.
+The API provides the macro `PSA_HASH_LENGTH`, which returns the expected hash length (in bytes) for the specified algorithm.
 
 #### Handling hash operation contexts
 
@@ -702,7 +702,7 @@
     size_t output_length = 0;
     size_t tag_length = 16;
     psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
-    psa_key_id_t key;
+    psa_key_id_t key_id;
 
     printf("Authenticate encrypt...\t");
     fflush(stdout);
@@ -726,11 +726,11 @@
     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);
+    status = psa_import_key(&attributes, key, sizeof(key), &key_id);
     psa_reset_key_attributes(&attributes);
 
     /* Authenticate and encrypt */
-    status = psa_aead_encrypt(key, PSA_ALG_CCM,
+    status = psa_aead_encrypt(key_id, PSA_ALG_CCM,
                               nonce, sizeof(nonce),
                               additional_data, sizeof(additional_data),
                               input_data, sizeof(input_data),
@@ -747,7 +747,7 @@
     free(output_data);
 
     /* Destroy the key */
-    psa_destroy_key(key);
+    psa_destroy_key(key_id);
 
     mbedtls_psa_crypto_free();
 ```
@@ -773,7 +773,7 @@
     size_t output_size = 0;
     size_t output_length = 0;
     psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
-    psa_key_id_t key;
+    psa_key_id_t key_id;
 
     printf("Authenticate decrypt...\t");
     fflush(stdout);
@@ -797,7 +797,7 @@
     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);
+    status = psa_import_key(&attributes, key_data, sizeof(key_data), &key_id);
     if (status != PSA_SUCCESS) {
         printf("Failed to import a key\n");
         return;
@@ -805,7 +805,7 @@
     psa_reset_key_attributes(&attributes);
 
     /* Authenticate and decrypt */
-    status = psa_aead_decrypt(key, PSA_ALG_CCM,
+    status = psa_aead_decrypt(key_id, PSA_ALG_CCM,
                               nonce, sizeof(nonce),
                               additional_data, sizeof(additional_data),
                               input_data, sizeof(input_data),
@@ -822,7 +822,7 @@
     free(output_data);
 
     /* Destroy the key */
-    psa_destroy_key(key);
+    psa_destroy_key(key_id);
 
     mbedtls_psa_crypto_free();
 ```
@@ -848,7 +848,7 @@
     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;
+    psa_key_id_t key_id;
 
     printf("Generate a key pair...\t");
     fflush(stdout);
@@ -867,14 +867,14 @@
     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);
+    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, exported, sizeof(exported),
+    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);
@@ -884,7 +884,7 @@
     printf("Exported a public key\n");
 
     /* Destroy the key */
-    psa_destroy_key(key);
+    psa_destroy_key(key_id);
 
     mbedtls_psa_crypto_free();
 ```
diff --git a/third_party/mbedtls/repo/docs/proposed/psa-conditional-inclusion-c.md b/third_party/mbedtls/repo/docs/proposed/psa-conditional-inclusion-c.md
index 52138b1..2ddba7f 100644
--- a/third_party/mbedtls/repo/docs/proposed/psa-conditional-inclusion-c.md
+++ b/third_party/mbedtls/repo/docs/proposed/psa-conditional-inclusion-c.md
@@ -5,8 +5,6 @@
 
 This is currently a proposal for Mbed TLS. It is not currently on track for standardization in PSA.
 
-Time-stamp: "2020/11/26 09:30:50 GMT"
-
 ## Introduction
 
 ### Purpose of this specification
@@ -86,9 +84,17 @@
 
 For asymmetric cryptography, `PSA_WANT_KEY_TYPE_xxx_KEY_PAIR` determines whether private-key operations are desired, and `PSA_WANT_KEY_TYPE_xxx_PUBLIC_KEY` determines whether public-key operations are desired. `PSA_WANT_KEY_TYPE_xxx_KEY_PAIR` implicitly enables `PSA_WANT_KEY_TYPE_xxx_PUBLIC_KEY`: there is no way to only include private-key operations (which typically saves little code).
 
-#### Configuration symbols for curves
+#### Configuration symbols for elliptic curves
 
-For elliptic curve key types, only the specified curves are included. To include a curve, include a symbol of the form **`PSA_WANT_ECC_family_size`**. For example: `PSA_WANT_ECC_SECP_R1_256` for secp256r1, `PSA_WANT_ECC_MONTGOMERY_CURVE25519`. It is an error to require an ECC key type but no curve, and Mbed TLS will reject this at compile time.
+For elliptic curve key types, only the specified curves are included. To include a curve, include a symbol of the form **`PSA_WANT_ECC_family_size`**. For example: `PSA_WANT_ECC_SECP_R1_256` for secp256r1, `PSA_WANT_ECC_MONTGOMERY_255` for Curve25519. It is an error to require an ECC key type but no curve, and Mbed TLS will reject this at compile time.
+
+Rationale: this is a deviation of the general principle that `PSA_ECC_FAMILY_xxx` would have a corresponding symbol `PSA_WANT_ECC_FAMILY_xxx`. This deviation is justified by the fact that it is very common to wish to include only certain curves in a family, and that can lead to a significant gain in code size.
+
+#### Configuration symbols for Diffie-Hellman groups
+
+There are no configuration symbols for Diffie-Hellman groups (`PSA_DH_GROUP_xxx`).
+
+Rationale: Finite-field Diffie-Hellman code is usually not specialized for any particular group, so reducing the number of available groups at compile time only saves a little code space. Constrained implementations tend to omit FFDH anyway, so the small code size gain is not important.
 
 #### Configuration symbols for algorithms
 
@@ -195,7 +201,7 @@
 
 #### Naming of symbols
 
-The names of [elliptic curve symbols](#configuration-symbols-for-curves) are a bit weird: `SECP_R1_256` instead of `SECP256R1`. Should we make them more classical, but less systematic?
+The names of [elliptic curve symbols](#configuration-symbols-for-elliptic-curves) are a bit weird: `SECP_R1_256` instead of `SECP256R1`, `MONTGOMERY_255` instead of `CURVE25519`. Should we make them more classical, but less systematic?
 
 #### Impossible combinations
 
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 d825794..23274c7 100644
--- a/third_party/mbedtls/repo/docs/proposed/psa-driver-interface.md
+++ b/third_party/mbedtls/repo/docs/proposed/psa-driver-interface.md
@@ -5,8 +5,6 @@
 
 This specification is work in progress and should be considered to be in a beta stage. There is ongoing work to implement this interface in Mbed TLS, which is the reference implementation of the PSA Cryptography API. At this stage, Arm does not expect major changes, but minor changes are expected based on experience from the first implementation and on external feedback.
 
-Time-stamp: "2020/11/24 11:03:32 GMT"
-
 ## Introduction
 
 ### Purpose of the driver interface
@@ -136,7 +134,7 @@
     "entry_points": ["sign_hash"],
     "algorithms": ["PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_256)",
                    "PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_384)"],
-    "key_types": ["PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_CURVE_SECP_R1)"],
+    "key_types": ["PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)"],
     "key_sizes": [256, 384]
 }
 ```
@@ -166,7 +164,7 @@
 Valid examples:
 ```
 PSA_KEY_TYPE_AES
-PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_CURVE_SECP_R1)
+PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)
 PSA_KEY_TYPE_ECC_KEY_PAIR(_)
 ```
 
@@ -196,6 +194,8 @@
 
 Some entry points are grouped in families that must be implemented as a whole. If a driver supports an entry point family, it must provide all the entry points in the family.
 
+Drivers can also have entry points related to random generation. A transparent driver can provide a [random generation interface](#random-generation-entry-points). Separately, transparent and opaque drivers can have [entropy collection entry points](#entropy-collection-entry-point).
+
 #### General considerations on driver entry point parameters
 
 Buffer parameters for driver entry points obey the following conventions:
@@ -375,6 +375,49 @@
 * For elliptic curve private keys (`PSA_KEY_TYPE_ECC_KEY_PAIR`), check the size and range. TODO: what else?
 * For elliptic curve public keys (`PSA_KEY_TYPE_ECC_PUBLIC_KEY`), check the size and range, and that the point is on the curve. TODO: what else?
 
+### Entropy collection entry point
+
+A driver can declare an entropy source by providing a `"get_entropy"` entry point. This entry point has the following prototype for a driver with the prefix `"acme"`:
+
+```
+psa_status_t acme_get_entropy(uint32_t flags,
+                              size_t *estimate_bits,
+                              uint8_t *output,
+                              size_t output_size);
+```
+
+The semantics of the parameters is as follows:
+
+* `flags`: a bit-mask of [entropy collection flags](#entropy-collection-flags).
+* `estimate_bits`: on success, an estimate of the amount of entropy that is present in the `output` buffer, in bits. This must be at least `1` on success. The value is ignored on failure. Drivers should return a conservative estimate, even in circumstances where the quality of the entropy source is degraded due to environmental conditions (e.g. undervolting, low temperature, etc.).
+* `output`: on success, this buffer contains non-deterministic data with an estimated entropy of at least `*estimate_bits` bits. When the entropy is coming from a hardware peripheral, this should preferably be raw or lightly conditioned measurements from a physical process, such that statistical tests run over a sufficiently large amount of output can confirm the entropy estimates. But this specification also permits entropy sources that are fully conditioned, for example when the PSA Cryptography system is running as an application in an operating system and `"get_entropy"` returns data from the random generator in the operating system's kernel.
+* `output_size`: the size of the `output` buffer in bytes. This size should be large enough to allow a driver to pass unconditioned data with a low density of entropy; for example a peripheral that returns eight bytes of data with an estimated one bit of entropy cannot provide meaningful output in less than 8 bytes.
+
+Note that there is no output parameter indicating how many bytes the driver wrote to the buffer. Such an output length indication is not necessary because the entropy may be located anywhere in the buffer, so the driver may write less than `output_size` bytes but the core does not need to know this. The output parameter `estimate_bits` contains the amount of entropy, expressed in bits, which may be significantly less than `output_size * 8`.
+
+The entry point may return the following statuses:
+
+* `PSA_SUCCESS`: success. The output buffer contains some entropy.
+* `PSA_ERROR_INSUFFICIENT_ENTROPY`: no entropy is available without blocking. This is only permitted if the `PSA_DRIVER_GET_ENTROPY_BLOCK` flag is clear. The core may call `get_entropy` again later, giving time for entropy to be gathered or for adverse environmental conditions to be rectified.
+* Other error codes indicate a transient or permanent failure of the entropy source.
+
+Unlike most other entry points, if multiple transparent drivers include a `"get_entropy"` point, the core will call all of them (as well as the entry points from opaque drivers). Fallback is not applicable to `"get_entropy"`.
+
+#### Entropy collection flags
+
+* `PSA_DRIVER_GET_ENTROPY_BLOCK`: If this flag is set, the driver should block until it has at least one bit of entropy. If this flag is clear, the driver should avoid blocking if no entropy is readily available.
+* `PSA_DRIVER_GET_ENTROPY_KEEPALIVE`: This flag is intended to help with energy management for entropy-generating peripherals. If this flag is set, the driver should expect another call to `acme_get_entropy` after a short time. If this flag is clear, the core is not expecting to call the `"get_entropy"` entry point again within a short amount of time (but it may do so nonetheless).
+
+#### Entropy collection and blocking
+
+The intent of the `BLOCK` and `KEEPALIVE` [flags](#entropy-collection-flags) is to support drivers for TRNG (True Random Number Generator, i.e. an entropy source peripheral) that have a long ramp-up time, especially on platforms with multiple entropy sources.
+
+Here is a suggested call sequence for entropy collection that leverages these flags:
+
+1. The core makes a first round of calls to `"get_entropy"` on every source with the `BLOCK` flag clear and the `KEEPALIVE` flag set, so that drivers can prepare the TRNG peripheral.
+2. The core makes a second round of calls with the `BLOCK` flag set and the `KEEPALIVE` flag clear to gather needed entropy.
+3. If the second round does not collect enough entropy, the core makes more similar rounds, until the total amount of collected entropy is sufficient.
+
 ### Miscellaneous driver entry points
 
 #### Driver initialization
@@ -428,6 +471,109 @@
 3. [Determine the key size](#key-size-determination-on-import) and output it through `*bits`.
 4. Copy the validated key data from `data` to `key_buffer`. The output must be in the canonical format documented for [`psa_export_key()`](https://armmbed.github.io/mbed-crypto/html/api/keys/management.html#c.psa_export_key) or [`psa_export_public_key()`](https://armmbed.github.io/mbed-crypto/html/api/keys/management.html#c.psa_export_public_key), so if the input is not in this format, the entry point must convert it.
 
+### Random generation entry points
+
+A transparent driver may provide an operation family that can be used as a cryptographic random number generator. The random generation mechanism must obey the following requirements:
+
+* The random output must be of cryptographic quality, with a uniform distribution. Therefore, if the random generator includes an entropy source, this entropy source must be fed through a CSPRNG (cryptographically secure pseudo-random number generator).
+* Random generation is expected to be fast. (If a device can provide entropy but is slow at generating random data, declare it as an [entropy driver](#entropy-collection-entry-point) instead.)
+* The random generator should be able to incorporate entropy provided by an outside source. If it isn't, the random generator can only be used if it's the only entropy source on the platform. (A random generator peripheral can be declared as an [entropy source](#entropy-collection-entry-point) instead of a random generator; this way the core will combine it with other entropy sources.)
+* The random generator may either be deterministic (in the sense that it always returns the same data when given the same entropy inputs) or non-deterministic (including its own entropy source). In other words, this interface is suitable both for PRNG (pseudo-random number generator, also known as DRBG (deterministic random bit generator)) and for NRBG (non-deterministic random bit generator).
+
+If no driver implements the random generation entry point family, the core provides an unspecified random generation mechanism.
+
+This operation family requires the following type, entry points and parameters (TODO: where exactly are the parameters in the JSON structure?):
+
+* Type `"random_context_t"`: the type of a random generation context.
+* `"init_random"` (entry point, optional): if this function is present, [the core calls it once](#random-generator-initialization) after allocating a `"random_context_t"` object.
+* `"add_entropy"` (entry point, optional): the core calls this function to [inject entropy](#entropy-injection). This entry point is optional if the driver is for a peripheral that includes an entropy source of its own, however [random generator drivers without entropy injection](#random-generator-drivers-without-entropy-injection) have limited portability since they can only be used on platforms with no other entropy source. This entry point is mandatory if `"initial_entropy_size"` is nonzero.
+* `"get_random"` (entry point, mandatory): the core calls this function whenever it needs to [obtain random data](#the-get_random-entry-point).
+* `"initial_entropy_size"` (integer, mandatory): the minimum number of bytes of entropy that the core must supply before the driver can output random data. This can be `0` if the driver is for a peripheral that includes an entropy source of its own.
+* `"reseed_entropy_size"` (integer, optional): the minimum number of bytes of entropy that the core should supply via [`"add_entropy"`](#entropy-injection) when the driver runs out of entropy. This value is also a hint for the size to supply if the core makes additional calls to `"add_entropy"`, for example to enforce prediction resistance. If omitted, the core should pass an amount of entropy corresponding to the expected security strength of the device (for example, pass 32 bytes of entropy when reseeding to achieve a security strength of 256 bits). If specified, the core should pass the larger of `"reseed_entropy_size"` and the amount corresponding to the security strength.
+
+Random generation is not parametrized by an algorithm. The choice of algorithm is up to the driver.
+
+#### Random generator initialization
+
+The `"init_random"` entry point has the following prototype for a driver with the prefix `"acme"`:
+
+```
+psa_status_t acme_init_random(acme_random_context_t *context);
+```
+
+The core calls this entry point once after allocating a random generation context. Initially, the context object is all-bits-zero.
+
+If a driver does not have an `"init_random"` entry point, the context object passed to the first call to `"add_entropy"` or `"get_random"` will be all-bits-zero.
+
+#### Entropy injection
+
+The `"add_entropy"` entry point has the following prototype for a driver with the prefix `"acme"`:
+
+```
+psa_status_t acme_add_entropy(acme_random_context_t *context,
+                              const uint8_t *entropy,
+                              size_t entropy_size);
+```
+
+The semantics of the parameters is as follows:
+
+* `context`: a random generation context. On the first call to `"add_entropy"`, this object has been initialized by a call to the driver's `"init_random"` entry point if one is present, and to all-bits-zero otherwise.
+* `entropy`: a buffer containing full-entropy data to seed the random generator. “Full-entropy” means that the data is uniformly distributed and independent of any other observable quantity.
+* `entropy_size`: the size of the `entropy` buffer in bytes. It is guaranteed to be at least `1`, but it may be smaller than the amount of entropy that the driver needs to deliver random data, in which case the core will call the `"add_entropy"` entry point again to supply more entropy.
+
+The core calls this function to supply entropy to the driver. The driver must mix this entropy into its internal state. The driver must mix the whole supplied entropy, even if there is more than what the driver requires, to ensure that all entropy sources are mixed into the random generator state. The driver may mix additional entropy of its own.
+
+The core may call this function at any time. For example, to enforce prediction resistance, the core can call `"add_entropy"` immediately after each call to `"get_random"`. The core must call this function in two circumstances:
+
+* Before the first call to the `"get_random"` entry point, to supply `"initial_entropy_size"` bytes of entropy.
+* After a call to the `"get_random"` entry point returns less than the required amount of random data, to supply at least `"reseed_entropy_size"` bytes of entropy.
+
+When the driver requires entropy, the core can supply it with one or more successive calls to the `"add_entropy"` entry point. If the required entropy size is zero, the core does not need to call `"add_entropy"`.
+
+#### Combining entropy sources with a random generation driver
+
+This section provides guidance on combining one or more [entropy sources](#entropy-collection-entry-point) (each having a `"get_entropy"` entry point) with a random generation driver (with an `"add_entropy"` entry point).
+
+Note that `"get_entropy"` returns data with an estimated amount of entropy that is in general less than the buffer size. The core must apply a mixing algorithm to the output of `"get_entropy"` to obtain full-entropy data.
+
+For example, the core may use a simple mixing scheme based on a pseudorandom function family $(F_k)$ with an $E$-bit output where $E = 8 \cdot \mathtt{entropy_size}$ and $\mathtt{entropy_size}$ is the desired amount of entropy in bytes (typically the random driver's `"initial_entropy_size"` property for the initial seeding and the `"reseed_entropy_size"` property for subsequent reseeding). The core calls the `"get_entropy"` points of the available entropy drivers, outputting a string $s_i$ and an entropy estimate $e_i$ on the $i$th call. It does so until the total entropy estimate $e_1 + e_2 + \ldots + e_n$ is at least $E$. The core then calculates $F_k(0)$ where $k = s_1 || s_2 || \ldots || s_n$. This value is a string of $\mathtt{entropy_size}$, and since $(F_k)$ is a pseudorandom function family, $F_k(0)$ is uniformly distributed over strings of $\mathtt{entropy_size}$ bytes. Therefore $F_k(0)$ is a suitable value to pass to `"add_entropy"`.
+
+Note that the mechanism above is only given as an example. Implementations may choose a different mechanism, for example involving multiple pools or intermediate compression functions.
+
+#### Random generator drivers without entropy injection
+
+Random generator drivers should have the capability to inject additional entropy through the `"add_entropy"` entry point. This ensures that the random generator depends on all the entropy sources that are available on the platform. A driver where a call to `"add_entropy"` does not affect the state of the random generator is not compliant with this specification.
+
+However, a driver may omit the `"add_entropy"` entry point. This limits the driver's portability: implementations of the PSA Cryptography specification may reject drivers without an `"add_entropy"` entry point, or only accept such drivers in certain configurations. In particular, the `"add_entropy"` entry point is required if:
+
+* the integration of PSA Cryptography includes an entropy source that is outside the driver; or
+* the core saves random data in persistent storage to be preserved across platform resets.
+
+#### The `"get_random"` entry point
+
+The `"get_random"` entry point has the following prototype for a driver with the prefix `"acme"`:
+
+```
+psa_status_t acme_get_random(acme_random_context_t *context,
+                             uint8_t *output,
+                             size_t output_size,
+                             size_t *output_length);
+```
+
+The semantics of the parameters is as follows:
+
+* `context`: a random generation context. If the driver's `"initial_entropy_size"` property is nonzero, the core must have called `"add_entropy"` at least once with a total of at least `"initial_entropy_size"` bytes of entropy before it calls `"get_random"`. Alternatively, if the driver's `"initial_entropy_size"` property is zero and the core did not call `"add_entropy"`, or if the driver has no `"add_entropy"` entry point, the core must have called `"init_random"` if present, and otherwise the context is all-bits zero.
+* `output`: on success (including partial success), the first `*output_length` bytes of this buffer contain cryptographic-quality random data. The output is not used on error.
+* `output_size`: the size of the `output` buffer in bytes.
+* `*output_length`: on success (including partial success), the number of bytes of random data that the driver has written to the `output` buffer. This is preferably `output_size`, but the driver is allowed to return less data if it runs out of entropy as described below. The core sets this value to 0 on entry. The value is not used on error.
+
+The driver may return the following status codes:
+
+* `PSA_SUCCESS`: the `output` buffer contains `*output_length` bytes of cryptographic-quality random data. Note that this may be less than `output_size`; in this case the core should call the driver's `"add_entropy"` method to supply at least `"reseed_entropy_size"` bytes of entropy before calling `"get_random"` again.
+* `PSA_ERROR_INSUFFICIENT_ENTROPY`: the core must supply additional entropy by calling the `"add_entropy"` entry point with at least `"reseed_entropy_size"` bytes.
+* `PSA_ERROR_NOT_SUPPORTED`: the random generator is not available. This is only permitted if the driver specification for random generation has the [fallback property](#fallback) enabled.
+* Other error codes such as `PSA_ERROR_COMMUNICATION_FAILURE` or `PSA_ERROR_HARDWARE_FAILURE` indicate a transient or permanent error.
+
 ### Fallback
 
 Sometimes cryptographic accelerators only support certain cryptographic mechanisms partially. The capability description language allows specifying some restrictions, including restrictions on key sizes, but it cannot cover all the possibilities that may arise in practice. Furthermore, it may be desirable to deploy the same binary image on different devices, only some of which have a cryptographic accelerators.
@@ -460,7 +606,8 @@
 * `"public_key_size"` (integer or string, optional): this many bytes are included in every key context for a public key. If omitted, this value defaults to 0.
 * `"symmetric_factor"` (integer or string, optional): every key context for a symmetric key includes this many times the key size. If omitted, this value defaults to 0.
 * `"store_public_key"` (boolean, optional): If specified and true, for a key pair, the key context includes space for the public key. If omitted or false, no additional space is added for the public key.
-* `"size_function"` (string, optional): the name of a function that returns the number of bytes that the driver needs in a key context for a key. This may be a pointer to function. This must be a C identifier; more complex expressions are not permitted. If the core uses this function, it supersedes all the other properties.
+* `"size_function"` (string, optional): the name of a function that returns the number of bytes that the driver needs in a key context for a key. This may be a pointer to function. This must be a C identifier; more complex expressions are not permitted. If the core uses this function, it supersedes all the other properties except for `"builtin_key_size"` (where applicable, if present).
+* `"builtin_key_size"` (integer or string, optional): If specified, this overrides all other methods (including the `"size_function"` entry point) to determine the size of the key context for [built-in keys](#built-in-keys). This allows drivers to efficiently represent application keys as wrapped key material, but built-in keys by an internal identifier that takes up less space.
 
 The integer properties must be C language constants. A typical value for `"base_size"` is `sizeof(acme_key_context_t)` where `acme_key_context_t` is a type defined in a driver header file.
 
@@ -516,6 +663,7 @@
 * `"generate_key"`: called by `psa_generate_key()`.
 * `"key_derivation_output_key"`: called by `psa_key_derivation_output_key()`.
 * `"copy_key"`: called by `psa_copy_key()` when copying a key within the same [location](#lifetimes-and-locations).
+* `"get_builtin_key"`: called by functions that access a key to retrieve information about a [built-in key](#built-in-keys).
 
 In addition, secure elements that store the key material internally must provide the following two entry points:
 
@@ -646,6 +794,37 @@
 
 In a multithreaded environment, the driver may only call these two functions from the thread that is executing the entry point.
 
+#### Built-in keys
+
+Opaque drivers may declare built-in keys. Built-in keys can be accessed, but not created, through the PSA Cryptography API.
+
+A built-in key is identified by its location and its **slot number**. Drivers that support built-in keys must provide a `"get_builtin_key"` entry point to retrieve the key data and metadata. The core calls this entry point when it needs to access the key, typically because the application requested an operation on the key. The core may keep information about the key in cache, and successive calls to access the same slot number should return the same data. This entry point has the following prototype:
+
+```
+psa_status_t acme_get_builtin_key(psa_drv_slot_number_t slot_number,
+                                  psa_key_attributes_t *attributes,
+                                  uint8_t *key_buffer,
+                                  size_t key_buffer_size,
+                                  size_t *key_buffer_length);
+```
+
+If this function returns `PSA_SUCCESS` or `PSA_ERROR_BUFFER_TOO_SMALL`, it must fill `attributes` with the attributes of the key (except for the key identifier). On success, this function must also fill `key_buffer` with the key context.
+
+On entry, `psa_get_key_lifetime(attributes)` is the location at which the driver was declared and a persistence level with which the platform is attempting to register the key. The driver entry point may choose to change the lifetime (`psa_set_key_lifetime(attributes, lifetime)`) of the reported key attributes to one with the same location but a different persistence level, in case the driver has more specific knowledge about the actual persistence level of the key which is being retrieved. For example, if a driver knows it cannot delete a key, it may override the persistence level in the lifetime to `PSA_KEY_PERSISTENCE_READ_ONLY`. The standard attributes other than the key identifier and lifetime have the value conveyed by `PSA_KEY_ATTRIBUTES_INIT`.
+
+The output parameter `key_buffer` points to a writable buffer of `key_buffer_size` bytes. If the driver has a [`"builtin_key_size"` property](#key-format-for-opaque-drivers) property, `key_buffer_size` has this value, otherwise `key_buffer_size` has the value determined from the key type and size.
+
+Typically, for a built-in key, the key context is a reference to key material that is kept inside the secure element, similar to the format returned by [`"allocate_key"`](#key-management-in-a-secure-element-with-storage). A driver may have built-in keys even if it doesn't have an `"allocate_key"` entry point.
+
+This entry point may return the following status values:
+
+* `PSA_SUCCESS`: the requested key exists, and the output parameters `attributes` and `key_buffer` contain the key metadata and key context respectively, and `*key_buffer_length` contains the length of the data written to `key_buffer`.
+* `PSA_ERROR_BUFFER_TOO_SMALL`: `key_buffer_size` is insufficient. In this case, the driver must pass the key's attributes in `*attributes`. In particular, `get_builtin_key(slot_number, &attributes, NULL, 0)` is a way for the core to obtain the key's attributes.
+* `PSA_ERROR_DOES_NOT_EXIST`: the requested key does not exist.
+* Other error codes such as `PSA_ERROR_COMMUNICATION_FAILURE` or `PSA_ERROR_HARDWARE_FAILURE` indicate a transient or permanent error.
+
+The core will pass authorized requests to destroy a built-in key to the [`"destroy_key"`](#key-management-in-a-secure-element-with-storage) entry point if there is one. If built-in keys must not be destroyed, it is up to the driver to reject such requests.
+
 ## How to use drivers from an application
 
 ### Using transparent drivers
@@ -661,7 +840,7 @@
 psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
 psa_set_key_lifetime(&attributes, PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION(
         PSA_KEY_PERSISTENCE_DEFAULT, PSA_KEY_LOCATION_acme));
-psa_set_key_identifer(&attributes, 42);
+psa_set_key_identifier(&attributes, 42);
 psa_set_key_type(&attributes, PSA_KEY_TYPE_AES);
 psa_set_key_size(&attributes, 128);
 psa_set_key_algorithm(&attributes, PSA_ALG_GCM);
@@ -704,6 +883,19 @@
 
 ## Open questions
 
+### Value representation
+
+#### Integers
+
+It would be better if there was a uniform requirement on integer values. Do they have to be JSON integers? C preprocessor integers (which could be e.g. a macro defined in some header file)? C compile-time constants (allowing `sizeof`)?
+
+This choice is partly driven by the use of the values, so they might not be uniform. Note that if the value can be zero and it's plausible that the core would want to statically allocate an array of the given size, the core needs to know whether the value is 0 so that it could use code like
+```
+#if ACME_FOO_SIZE != 0
+    uint8_t foo[ACME_FOO_SIZE];
+#endif
+```
+
 ### Driver declarations
 
 #### Declaring driver entry points
@@ -779,6 +971,26 @@
 
 `psa_crypto_driver_get_persistent_state` does not identify the calling driver, so the driver needs to remember which driver it's calling. This may require a thread-local variable in a multithreaded core. Is this ok?
 
+### Randomness
+
+#### Input to `"add_entropy"`
+
+Should the input to the [`"add_entropy"` entry point](#entropy-injection) be a full-entropy buffer (with data from all entropy sources already mixed), raw entropy direct from the entropy sources, or give the core a choice?
+
+* Raw data: drivers must implement entropy mixing. `"add_entropy"` needs an extra parameter to indicate the amount of entropy in the data. The core must not do any conditioning.
+* Choice: drivers must implement entropy mixing. `"add_entropy"` needs an extra parameter to indicate the amount of entropy in the data. The core may do conditioning if it wants, but doesn't have to.
+* Full entropy: drivers don't need to do entropy mixing.
+
+#### Flags for `"get_entropy"`
+
+Are the [entropy collection flags](#entropy-collection-flags) well-chosen?
+
+#### Random generator instantiations
+
+May the core instantiate a random generation context more than once? In other words, can there be multiple objects of type `acme_random_context_t`?
+
+Functionally, one RNG is as good as any. If the core wants some parts of the system to use a deterministic generator for reproducibility, it can't use this interface anyway, since the RNG is not necessarily deterministic. However, for performance on multiprocessor systems, a multithreaded core could prefer to use one RNG instance per thread.
+
 <!--
 Local Variables:
 time-stamp-line-limit: 40
diff --git a/third_party/mbedtls/repo/docs/use-psa-crypto.md b/third_party/mbedtls/repo/docs/use-psa-crypto.md
new file mode 100644
index 0000000..6ec2dca
--- /dev/null
+++ b/third_party/mbedtls/repo/docs/use-psa-crypto.md
@@ -0,0 +1,204 @@
+This document describes the compile-time configuration option
+`MBEDTLS_USE_PSA_CRYPTO` from a user's perspective, more specifically its
+current effects as well as the parts that aren't covered yet.
+
+Current effects
+===============
+
+General limitations
+-------------------
+
+Compile-time: enabling `MBEDTLS_USE_PSA_CRYPTO` requires
+`MBEDTLS_ECP_RESTARTABLE` and
+`MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER` to be disabled.
+
+Effect: `MBEDTLS_USE_PSA_CRYPTO` currently has no effect on TLS 1.3 (which is
+itself experimental and only partially supported so far): TLS 1.3 always uses
+the legacy APIs even when this option is set.
+
+Stability: any API that's only available when `MBEDTLS_USE_PSA_CRYPTO` is
+defined is considered experimental and may change in incompatible ways at any
+time. Said otherwise, these APIs are explicitly excluded from the usual API
+stability promises.
+
+New APIs / API extensions
+-------------------------
+
+Some of these APIs are meant for the application to use in place of
+pre-existing APIs, in order to get access to the benefits; in the sub-sections
+below these are indicated by "Use in (X.509 and) TLS: opt-in", meaning that
+this requires changes to the application code for the (X.509 and) TLS layers
+to pick up the improvements.
+
+Some of these APIs are mostly meant for internal use by the TLS (and X.509)
+layers; they are indicated below by "Use in (X.509 and) TLS: automatic",
+meaning that no changes to the application code are required for the TLS (and
+X.509) layers to pick up the improvements.
+
+### PSA-held (opaque) keys in the PK layer
+
+There is a new API function `mbedtls_pk_setup_opaque()` that can be used to
+wrap a PSA keypair into a PK context. The key can be used for private-key
+operations and its public part can be exported.
+
+Benefits: isolation of long-term secrets, use of PSA Crypto drivers.
+
+Limitations: only for private keys, only ECC. (That is, only ECDSA signature
+generation. Note: currently this will use randomized ECDSA while Mbed TLS uses
+deterministic ECDSA by default.) The following operations are not supported
+with a context set this way, while they would be available with a normal
+`ECKEY` context: `mbedtls_pk_verify()`, `mbedtls_pk_check_pair()`,
+`mbedtls_pk_debug()`.
+
+Use in X.509 and TLS: opt-in. The application needs to construct the PK context
+using the new API in order to get the benefits; it can then pass the
+resulting context to the following existing APIs:
+
+- `mbedtls_ssl_conf_own_cert()` or `mbedtls_ssl_set_hs_own_cert()` to use the
+  key together with a certificate for ECDSA-based key exchanges (note: while
+this is supported on both sides, it's currently only tested client-side);
+- `mbedtls_x509write_csr_set_key()` to generate a CSR (certificate signature
+  request).
+
+In the TLS and X.509 API, there are two other functions which accept a key or
+keypair as a PK context: `mbedtls_x509write_crt_set_subject_key()` and
+`mbedtls_x509write_crt_set_issuer_key()`. Use of opaque contexts here probably
+works but is so far untested.
+
+### PSA-held (opaque) keys for TLS pre-shared keys (PSK)
+
+There are two new API functions `mbedtls_ssl_conf_psk_opaque()` and
+`mbedtls_ssl_set_hs_psk_opaque()`. Call one of these from an application to
+register a PSA key for use with a PSK key exchange.
+
+Benefits: isolation of long-term secrets.
+
+Limitations: the key can only be used with "pure"
+PSK key exchanges (ciphersuites starting with `TLS_PSK_WITH_`), to the
+exclusion of RSA-PSK, DHE-PSK and ECDHE-PSK key exchanges. It is the responsibility of
+the user to make sure that when provisioning an opaque pre-shared key, the
+only PSK ciphersuites that can be negotiated are "pure" PSK; other XXX-PSK key
+exchanges will result in a handshake failure with the handshake function
+returning `MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE`.
+
+Use in TLS: opt-in. The application needs to register the key using the new
+APIs to get the benefits.
+
+### PSA-based operations in the Cipher layer
+
+There is a new API function `mbedtls_cipher_setup_psa()` to set up a context
+that will call PSA to store the key and perform the operations.
+
+Benefits: use of PSA Crypto drivers; partial isolation of short-term secrets
+(still generated outside of PSA, but then held by PSA).
+
+Limitations: the key is still passed in the clear by the application. The
+multi-part APIs are not supported, only the one-shot APIs. The only modes
+supported are ECB, CBC without padding, GCM and CCM (this excludes stream
+ciphers and ChachaPoly); the only cipher supported is AES (this excludes Aria,
+Camellia, and ChachaPoly). (Note: ECB is currently not tested.) (Note: it is
+possible to perform multiple one-shot operations with the same context;
+however this is not unit-tested, only tested via usage in TLS.)
+
+Use in TLS: automatic. Used when the cipher and mode is supported (with
+gracious fallback to the legacy API otherwise) in all places where a cipher is
+used. There are two such places: in `ssl_tls.c` for record protection, and in
+`ssl_ticket.c` for protecting tickets we issue.
+
+Internal changes
+----------------
+
+All of these internal changes are active as soon as `MBEDTLS_USE_PSA_CRYPTO`
+is enabled, no change required on the application side.
+
+### TLS: cipher operations based on PSA
+
+See "PSA-based operations in the Cipher layer" above.
+
+### PK layer: ECDSA verification based on PSA
+
+Scope: `mbedtls_pk_verify()` will call to PSA for ECDSA signature
+verification.
+
+Benefits: use of PSA Crypto drivers.
+
+Use in TLS and X.509: in all places where an ECDSA signature is verified.
+
+### TLS: ECDHE computation based on PSA
+
+Scope: Client-side, for ECDHE-RSA and ECDHE-ECDSA key exchanges, the
+computation of the ECDHE key exchange is done by PSA.
+
+Limitations: client-side only, ECDHE-PSK not covered
+
+Benefits: use of PSA Crypto drivers.
+
+### TLS: handshake hashes and PRF computed with PSA
+
+Scope: with TLS 1.2, the following are computed with PSA:
+- the running handshake hashes;
+- the hash of the ServerKeyExchange part that is signed;
+- the `verify_data` part of the Finished message;
+- the TLS PRF.
+
+Benefits: use of PSA Crypto drivers.
+
+### X.509: some hashes computed with PSA
+
+Scope: the following hashes are computed with PSA:
+- when verifying a certificate chain, hash of the child for verifying the
+  parent's signature;
+- when writing a CSR, hash of the request for self-signing the request.
+
+Benefits: use of PSA Crypto drivers.
+
+Parts that are not covered yet
+==============================
+
+This is only a high-level overview, grouped by theme
+
+TLS: 1.3 experimental support
+-----------------------------
+
+No part of the experimental support for TLS 1.3 is covered at the moment.
+
+TLS: key exchanges / asymmetric crypto
+--------------------------------------
+
+The following key exchanges are not covered at all:
+
+- RSA
+- DHE-RSA
+- DHE-PSK
+- RSA-PSK
+- ECDHE-PSK
+- ECDH-RSA
+- ECDH-ECDSA
+- ECJPAKE
+
+The following key exchanges are only partially covered:
+
+- ECDHE-RSA: RSA operations are not covered and, server-side, the ECDHE
+  operation isn't either
+- ECDHE-ECDSA: server-side, the ECDHE operation isn't covered. (ECDSA
+  signature generation is only covered if using `mbedtls_pk_setup_opaque()`.)
+
+PSK if covered when the application uses `mbedtls_ssl_conf_psk_opaque()` or
+`mbedtls_ssl_set_hs_psk_opaque()`.
+
+TLS: symmetric crypto
+---------------------
+
+- some ciphers not supported via PSA yet: ARIA, Camellia, ChachaPoly (silent
+  fallback to the legacy APIs)
+- the HMAC part of the CBC and NULL ciphersuites
+- the HMAC computation in `ssl_cookie.c`
+
+X.509
+-----
+
+- most hash operations are still done via the legacy API, except the few that
+  are documented above as using PSA
+- RSA PKCS#1 v1.5 signature generation (from PSA-held keys)
+- RSA PKCS#1 v1.5 signature verification
+- RSA-PSS signature verification
diff --git a/third_party/mbedtls/repo/doxygen/input/doc_mainpage.h b/third_party/mbedtls/repo/doxygen/input/doc_mainpage.h
index 5b51bd5..5a0f3ec 100644
--- a/third_party/mbedtls/repo/doxygen/input/doc_mainpage.h
+++ b/third_party/mbedtls/repo/doxygen/input/doc_mainpage.h
@@ -22,7 +22,7 @@
  */
 
 /**
- * @mainpage mbed TLS v2.25.0 source code documentation
+ * @mainpage mbed TLS v2.28.0 source code documentation
  *
  * This documentation describes the internal structure of mbed TLS.  It was
  * automatically generated from specially formatted comment blocks in
diff --git a/third_party/mbedtls/repo/doxygen/mbedtls.doxyfile b/third_party/mbedtls/repo/doxygen/mbedtls.doxyfile
index dd4237a..efd9ac6 100644
--- a/third_party/mbedtls/repo/doxygen/mbedtls.doxyfile
+++ b/third_party/mbedtls/repo/doxygen/mbedtls.doxyfile
@@ -28,7 +28,7 @@
 # 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.25.0"
+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
diff --git a/third_party/mbedtls/repo/include/mbedtls/aes.h b/third_party/mbedtls/repo/include/mbedtls/aes.h
index 052f47c..e280dbb 100644
--- a/third_party/mbedtls/repo/include/mbedtls/aes.h
+++ b/third_party/mbedtls/repo/include/mbedtls/aes.h
@@ -45,6 +45,7 @@
 #else
 #include MBEDTLS_CONFIG_FILE
 #endif
+#include "mbedtls/platform_util.h"
 
 #include <stddef.h>
 #include <stdint.h>
@@ -54,17 +55,22 @@
 #define MBEDTLS_AES_DECRYPT     0 /**< AES decryption. */
 
 /* Error codes in range 0x0020-0x0022 */
-#define MBEDTLS_ERR_AES_INVALID_KEY_LENGTH                -0x0020  /**< Invalid key length. */
-#define MBEDTLS_ERR_AES_INVALID_INPUT_LENGTH              -0x0022  /**< Invalid data input length. */
+/** Invalid key length. */
+#define MBEDTLS_ERR_AES_INVALID_KEY_LENGTH                -0x0020
+/** Invalid data input length. */
+#define MBEDTLS_ERR_AES_INVALID_INPUT_LENGTH              -0x0022
 
 /* Error codes in range 0x0021-0x0025 */
-#define MBEDTLS_ERR_AES_BAD_INPUT_DATA                    -0x0021  /**< Invalid input data. */
+/** Invalid input data. */
+#define MBEDTLS_ERR_AES_BAD_INPUT_DATA                    -0x0021
 
 /* MBEDTLS_ERR_AES_FEATURE_UNAVAILABLE is deprecated and should not be used. */
-#define MBEDTLS_ERR_AES_FEATURE_UNAVAILABLE               -0x0023  /**< Feature not available. For example, an unsupported AES key size. */
+/** Feature not available. For example, an unsupported AES key size. */
+#define MBEDTLS_ERR_AES_FEATURE_UNAVAILABLE               -0x0023
 
 /* MBEDTLS_ERR_AES_HW_ACCEL_FAILED is deprecated and should not be used. */
-#define MBEDTLS_ERR_AES_HW_ACCEL_FAILED                   -0x0025  /**< AES hardware accelerator failed. */
+/** AES hardware accelerator failed. */
+#define MBEDTLS_ERR_AES_HW_ACCEL_FAILED                   -0x0025
 
 #if ( defined(__ARMCC_VERSION) || defined(_MSC_VER) ) && \
     !defined(inline) && !defined(__cplusplus)
@@ -169,6 +175,7 @@
  * \return         \c 0 on success.
  * \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 );
 
@@ -187,6 +194,7 @@
  * \return         \c 0 on success.
  * \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 );
 
@@ -207,6 +215,7 @@
  * \return         \c 0 on success.
  * \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 );
@@ -227,6 +236,7 @@
  * \return         \c 0 on success.
  * \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 );
@@ -255,6 +265,7 @@
 
  * \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],
@@ -302,6 +313,7 @@
  * \return         #MBEDTLS_ERR_AES_INVALID_INPUT_LENGTH
  *                 on failure.
  */
+MBEDTLS_CHECK_RETURN_TYPICAL
 int mbedtls_aes_crypt_cbc( mbedtls_aes_context *ctx,
                     int mode,
                     size_t length,
@@ -346,6 +358,7 @@
  *                     smaller than an AES block in size (16 Bytes) or if \p
  *                     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,
@@ -394,6 +407,7 @@
  *
  * \return         \c 0 on success.
  */
+MBEDTLS_CHECK_RETURN_TYPICAL
 int mbedtls_aes_crypt_cfb128( mbedtls_aes_context *ctx,
                        int mode,
                        size_t length,
@@ -438,6 +452,7 @@
  *
  * \return         \c 0 on success.
  */
+MBEDTLS_CHECK_RETURN_TYPICAL
 int mbedtls_aes_crypt_cfb8( mbedtls_aes_context *ctx,
                     int mode,
                     size_t length,
@@ -492,6 +507,7 @@
  *
  * \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,
@@ -506,10 +522,6 @@
  * \brief      This function performs an AES-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_aes_setkey_enc()
@@ -578,6 +590,7 @@
  *
  * \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,
@@ -598,6 +611,7 @@
  *
  * \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] );
@@ -613,6 +627,7 @@
  *
  * \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] );
@@ -662,6 +677,7 @@
  * \return         \c 0 on success.
  * \return         \c 1 on failure.
  */
+MBEDTLS_CHECK_RETURN_CRITICAL
 int mbedtls_aes_self_test( int verbose );
 
 #endif /* MBEDTLS_SELF_TEST */
diff --git a/third_party/mbedtls/repo/include/mbedtls/arc4.h b/third_party/mbedtls/repo/include/mbedtls/arc4.h
index 17728f4..f4b0f9f 100644
--- a/third_party/mbedtls/repo/include/mbedtls/arc4.h
+++ b/third_party/mbedtls/repo/include/mbedtls/arc4.h
@@ -35,7 +35,8 @@
 #include <stddef.h>
 
 /* MBEDTLS_ERR_ARC4_HW_ACCEL_FAILED is deprecated and should not be used. */
-#define MBEDTLS_ERR_ARC4_HW_ACCEL_FAILED                  -0x0019  /**< ARC4 hardware accelerator failed. */
+/** ARC4 hardware accelerator failed. */
+#define MBEDTLS_ERR_ARC4_HW_ACCEL_FAILED                  -0x0019
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/third_party/mbedtls/repo/include/mbedtls/aria.h b/third_party/mbedtls/repo/include/mbedtls/aria.h
index a4b27b3..226e2db 100644
--- a/third_party/mbedtls/repo/include/mbedtls/aria.h
+++ b/third_party/mbedtls/repo/include/mbedtls/aria.h
@@ -50,25 +50,29 @@
 #if !defined(MBEDTLS_DEPRECATED_REMOVED)
 #define MBEDTLS_ERR_ARIA_INVALID_KEY_LENGTH   MBEDTLS_DEPRECATED_NUMERIC_CONSTANT( -0x005C )
 #endif /* !MBEDTLS_DEPRECATED_REMOVED */
-#define MBEDTLS_ERR_ARIA_BAD_INPUT_DATA -0x005C /**< Bad input data. */
+/** Bad input data. */
+#define MBEDTLS_ERR_ARIA_BAD_INPUT_DATA -0x005C
 
-#define MBEDTLS_ERR_ARIA_INVALID_INPUT_LENGTH -0x005E /**< Invalid data input length. */
+/** Invalid data input length. */
+#define MBEDTLS_ERR_ARIA_INVALID_INPUT_LENGTH -0x005E
 
 /* MBEDTLS_ERR_ARIA_FEATURE_UNAVAILABLE is deprecated and should not be used.
  */
-#define MBEDTLS_ERR_ARIA_FEATURE_UNAVAILABLE  -0x005A  /**< Feature not available. For example, an unsupported ARIA key size. */
+/** Feature not available. For example, an unsupported ARIA key size. */
+#define MBEDTLS_ERR_ARIA_FEATURE_UNAVAILABLE  -0x005A
 
 /* MBEDTLS_ERR_ARIA_HW_ACCEL_FAILED is deprecated and should not be used. */
-#define MBEDTLS_ERR_ARIA_HW_ACCEL_FAILED      -0x0058  /**< ARIA hardware accelerator failed. */
-
-#if !defined(MBEDTLS_ARIA_ALT)
-// Regular implementation
-//
+/** ARIA hardware accelerator failed. */
+#define MBEDTLS_ERR_ARIA_HW_ACCEL_FAILED      -0x0058
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+#if !defined(MBEDTLS_ARIA_ALT)
+// Regular implementation
+//
+
 /**
  * \brief The ARIA context-type definition.
  */
diff --git a/third_party/mbedtls/repo/include/mbedtls/asn1.h b/third_party/mbedtls/repo/include/mbedtls/asn1.h
index 6b79196..10f7905 100644
--- a/third_party/mbedtls/repo/include/mbedtls/asn1.h
+++ b/third_party/mbedtls/repo/include/mbedtls/asn1.h
@@ -46,13 +46,20 @@
  * ASN1 is a standard to specify data structures.
  * \{
  */
-#define MBEDTLS_ERR_ASN1_OUT_OF_DATA                      -0x0060  /**< Out of data when parsing an ASN1 data structure. */
-#define MBEDTLS_ERR_ASN1_UNEXPECTED_TAG                   -0x0062  /**< ASN1 tag was of an unexpected value. */
-#define MBEDTLS_ERR_ASN1_INVALID_LENGTH                   -0x0064  /**< Error when trying to determine the length or invalid length. */
-#define MBEDTLS_ERR_ASN1_LENGTH_MISMATCH                  -0x0066  /**< Actual length differs from expected length. */
-#define MBEDTLS_ERR_ASN1_INVALID_DATA                     -0x0068  /**< Data is invalid. */
-#define MBEDTLS_ERR_ASN1_ALLOC_FAILED                     -0x006A  /**< Memory allocation failed */
-#define MBEDTLS_ERR_ASN1_BUF_TOO_SMALL                    -0x006C  /**< Buffer too small when writing ASN.1 data structure. */
+/** Out of data when parsing an ASN1 data structure. */
+#define MBEDTLS_ERR_ASN1_OUT_OF_DATA                      -0x0060
+/** ASN1 tag was of an unexpected value. */
+#define MBEDTLS_ERR_ASN1_UNEXPECTED_TAG                   -0x0062
+/** Error when trying to determine the length or invalid length. */
+#define MBEDTLS_ERR_ASN1_INVALID_LENGTH                   -0x0064
+/** Actual length differs from expected length. */
+#define MBEDTLS_ERR_ASN1_LENGTH_MISMATCH                  -0x0066
+/** Data is invalid. */
+#define MBEDTLS_ERR_ASN1_INVALID_DATA                     -0x0068
+/** Memory allocation failed */
+#define MBEDTLS_ERR_ASN1_ALLOC_FAILED                     -0x006A
+/** Buffer too small when writing ASN.1 data structure. */
+#define MBEDTLS_ERR_ASN1_BUF_TOO_SMALL                    -0x006C
 
 /* \} name */
 
diff --git a/third_party/mbedtls/repo/include/mbedtls/base64.h b/third_party/mbedtls/repo/include/mbedtls/base64.h
index 7e73a8b..cf4149e 100644
--- a/third_party/mbedtls/repo/include/mbedtls/base64.h
+++ b/third_party/mbedtls/repo/include/mbedtls/base64.h
@@ -30,8 +30,10 @@
 
 #include <stddef.h>
 
-#define MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL               -0x002A  /**< Output buffer too small. */
-#define MBEDTLS_ERR_BASE64_INVALID_CHARACTER              -0x002C  /**< Invalid character in input. */
+/** Output buffer too small. */
+#define MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL               -0x002A
+/** Invalid character in input. */
+#define MBEDTLS_ERR_BASE64_INVALID_CHARACTER              -0x002C
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/third_party/mbedtls/repo/include/mbedtls/bignum.h b/third_party/mbedtls/repo/include/mbedtls/bignum.h
index 637360e..9d2cff3 100644
--- a/third_party/mbedtls/repo/include/mbedtls/bignum.h
+++ b/third_party/mbedtls/repo/include/mbedtls/bignum.h
@@ -35,14 +35,22 @@
 #include <stdio.h>
 #endif
 
-#define MBEDTLS_ERR_MPI_FILE_IO_ERROR                     -0x0002  /**< An error occurred while reading from or writing to a file. */
-#define MBEDTLS_ERR_MPI_BAD_INPUT_DATA                    -0x0004  /**< Bad input parameters to function. */
-#define MBEDTLS_ERR_MPI_INVALID_CHARACTER                 -0x0006  /**< There is an invalid character in the digit string. */
-#define MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL                  -0x0008  /**< The buffer is too small to write to. */
-#define MBEDTLS_ERR_MPI_NEGATIVE_VALUE                    -0x000A  /**< The input arguments are negative or result in illegal output. */
-#define MBEDTLS_ERR_MPI_DIVISION_BY_ZERO                  -0x000C  /**< The input argument for division is zero, which is not allowed. */
-#define MBEDTLS_ERR_MPI_NOT_ACCEPTABLE                    -0x000E  /**< The input arguments are not acceptable. */
-#define MBEDTLS_ERR_MPI_ALLOC_FAILED                      -0x0010  /**< Memory allocation failed. */
+/** An error occurred while reading from or writing to a file. */
+#define MBEDTLS_ERR_MPI_FILE_IO_ERROR                     -0x0002
+/** Bad input parameters to function. */
+#define MBEDTLS_ERR_MPI_BAD_INPUT_DATA                    -0x0004
+/** There is an invalid character in the digit string. */
+#define MBEDTLS_ERR_MPI_INVALID_CHARACTER                 -0x0006
+/** The buffer is too small to write to. */
+#define MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL                  -0x0008
+/** The input arguments are negative or result in illegal output. */
+#define MBEDTLS_ERR_MPI_NEGATIVE_VALUE                    -0x000A
+/** The input argument for division is zero, which is not allowed. */
+#define MBEDTLS_ERR_MPI_DIVISION_BY_ZERO                  -0x000C
+/** The input arguments are not acceptable. */
+#define MBEDTLS_ERR_MPI_NOT_ACCEPTABLE                    -0x000E
+/** Memory allocation failed. */
+#define MBEDTLS_ERR_MPI_ALLOC_FAILED                      -0x0010
 
 #define MBEDTLS_MPI_CHK(f)       \
     do                           \
@@ -829,14 +837,14 @@
  * \param E        The exponent MPI. This must point to an initialized MPI.
  * \param N        The base for the modular reduction. This must point to an
  *                 initialized MPI.
- * \param _RR      A helper MPI depending solely on \p N which can be used to
+ * \param prec_RR  A helper MPI depending solely on \p N which can be used to
  *                 speed-up multiple modular exponentiations for the same value
  *                 of \p N. This may be \c NULL. If it is not \c NULL, it must
  *                 point to an initialized MPI. If it hasn't been used after
  *                 the call to mbedtls_mpi_init(), this function will compute
- *                 the helper value and store it in \p _RR for reuse on
+ *                 the helper value and store it in \p prec_RR for reuse on
  *                 subsequent calls to this function. Otherwise, the function
- *                 will assume that \p _RR holds the helper value set by a
+ *                 will assume that \p prec_RR holds the helper value set by a
  *                 previous call to mbedtls_mpi_exp_mod(), and reuse it.
  *
  * \return         \c 0 if successful.
@@ -848,7 +856,7 @@
  */
 int mbedtls_mpi_exp_mod( mbedtls_mpi *X, const mbedtls_mpi *A,
                          const mbedtls_mpi *E, const mbedtls_mpi *N,
-                         mbedtls_mpi *_RR );
+                         mbedtls_mpi *prec_RR );
 
 /**
  * \brief          Fill an MPI with a number of random bytes.
@@ -871,6 +879,44 @@
                      int (*f_rng)(void *, unsigned char *, size_t),
                      void *p_rng );
 
+/** Generate a random number uniformly in a range.
+ *
+ * This function generates a random number between \p min inclusive and
+ * \p N exclusive.
+ *
+ * The procedure complies with RFC 6979 §3.3 (deterministic ECDSA)
+ * when the RNG is a suitably parametrized instance of HMAC_DRBG
+ * and \p min is \c 1.
+ *
+ * \note           There are `N - min` possible outputs. The lower bound
+ *                 \p min can be reached, but the upper bound \p N cannot.
+ *
+ * \param X        The destination MPI. This must point to an initialized MPI.
+ * \param min      The minimum value to return.
+ *                 It must be nonnegative.
+ * \param N        The upper bound of the range, exclusive.
+ *                 In other words, this is one plus the maximum value to return.
+ *                 \p N must be strictly larger than \p min.
+ * \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.
+ *
+ * \return         \c 0 if successful.
+ * \return         #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed.
+ * \return         #MBEDTLS_ERR_MPI_BAD_INPUT_DATA if \p min or \p N is invalid
+ *                 or if they are incompatible.
+ * \return         #MBEDTLS_ERR_MPI_NOT_ACCEPTABLE if the implementation was
+ *                 unable to find a suitable value within a limited number
+ *                 of attempts. This has a negligible probability if \p N
+ *                 is significantly larger than \p min, which is the case
+ *                 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 );
+
 /**
  * \brief          Compute the greatest common divisor: G = gcd(A, B)
  *
diff --git a/third_party/mbedtls/repo/include/mbedtls/blowfish.h b/third_party/mbedtls/repo/include/mbedtls/blowfish.h
index c2a6ff9..77dca70 100644
--- a/third_party/mbedtls/repo/include/mbedtls/blowfish.h
+++ b/third_party/mbedtls/repo/include/mbedtls/blowfish.h
@@ -43,13 +43,16 @@
 #if !defined(MBEDTLS_DEPRECATED_REMOVED)
 #define MBEDTLS_ERR_BLOWFISH_INVALID_KEY_LENGTH   MBEDTLS_DEPRECATED_NUMERIC_CONSTANT( -0x0016 )
 #endif /* !MBEDTLS_DEPRECATED_REMOVED */
-#define MBEDTLS_ERR_BLOWFISH_BAD_INPUT_DATA -0x0016 /**< Bad input data. */
+/** Bad input data. */
+#define MBEDTLS_ERR_BLOWFISH_BAD_INPUT_DATA -0x0016
 
-#define MBEDTLS_ERR_BLOWFISH_INVALID_INPUT_LENGTH -0x0018 /**< Invalid data input length. */
+/** Invalid data input length. */
+#define MBEDTLS_ERR_BLOWFISH_INVALID_INPUT_LENGTH -0x0018
 
 /* MBEDTLS_ERR_BLOWFISH_HW_ACCEL_FAILED is deprecated and should not be used.
  */
-#define MBEDTLS_ERR_BLOWFISH_HW_ACCEL_FAILED                   -0x0017  /**< Blowfish hardware accelerator failed. */
+/** Blowfish hardware accelerator failed. */
+#define MBEDTLS_ERR_BLOWFISH_HW_ACCEL_FAILED                   -0x0017
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/third_party/mbedtls/repo/include/mbedtls/bn_mul.h b/third_party/mbedtls/repo/include/mbedtls/bn_mul.h
index 17d057f..31137cd 100644
--- a/third_party/mbedtls/repo/include/mbedtls/bn_mul.h
+++ b/third_party/mbedtls/repo/include/mbedtls/bn_mul.h
@@ -44,6 +44,46 @@
 
 #include "mbedtls/bignum.h"
 
+
+/*
+ * Conversion macros for embedded constants:
+ * build lists of mbedtls_mpi_uint's from lists of unsigned char's grouped by 8, 4 or 2
+ */
+#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_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 )
+
+#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_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 )
+
+#endif /* bits in mbedtls_mpi_uint */
+
 #if defined(MBEDTLS_HAVE_ASM)
 
 #ifndef asm
@@ -189,9 +229,9 @@
         "addq   $8, %%rdi\n"
 
 #define MULADDC_STOP                        \
-        : "+c" (c), "+D" (d), "+S" (s)      \
-        : "b" (b)                           \
-        : "rax", "rdx", "r8"                \
+        : "+c" (c), "+D" (d), "+S" (s), "+m" (*(uint64_t (*)[16]) d) \
+        : "b" (b), "m" (*(const uint64_t (*)[16]) s)                 \
+        : "rax", "rdx", "r8"                                         \
     );
 
 #endif /* AMD64 */
@@ -204,18 +244,18 @@
 #define MULADDC_CORE                \
         "ldr x4, [%2], #8   \n\t"   \
         "ldr x5, [%1]       \n\t"   \
-        "mul x6, x4, %3     \n\t"   \
-        "umulh x7, x4, %3   \n\t"   \
+        "mul x6, x4, %4     \n\t"   \
+        "umulh x7, x4, %4   \n\t"   \
         "adds x5, x5, x6    \n\t"   \
         "adc x7, x7, xzr    \n\t"   \
         "adds x5, x5, %0    \n\t"   \
         "adc %0, x7, xzr    \n\t"   \
         "str x5, [%1], #8   \n\t"
 
-#define MULADDC_STOP                        \
-         : "+r" (c),  "+r" (d), "+r" (s)    \
-         : "r" (b)                          \
-         : "x4", "x5", "x6", "x7", "cc"     \
+#define MULADDC_STOP                                                    \
+         : "+r" (c),  "+r" (d), "+r" (s), "+m" (*(uint64_t (*)[16]) d)  \
+         : "r" (b), "m" (*(const uint64_t (*)[16]) s)                   \
+         : "x4", "x5", "x6", "x7", "cc"                                 \
     );
 
 #endif /* Aarch64 */
diff --git a/third_party/mbedtls/repo/include/mbedtls/camellia.h b/third_party/mbedtls/repo/include/mbedtls/camellia.h
index f7d2b23..925a623 100644
--- a/third_party/mbedtls/repo/include/mbedtls/camellia.h
+++ b/third_party/mbedtls/repo/include/mbedtls/camellia.h
@@ -39,13 +39,16 @@
 #if !defined(MBEDTLS_DEPRECATED_REMOVED)
 #define MBEDTLS_ERR_CAMELLIA_INVALID_KEY_LENGTH   MBEDTLS_DEPRECATED_NUMERIC_CONSTANT( -0x0024 )
 #endif /* !MBEDTLS_DEPRECATED_REMOVED */
-#define MBEDTLS_ERR_CAMELLIA_BAD_INPUT_DATA -0x0024 /**< Bad input data. */
+/** Bad input data. */
+#define MBEDTLS_ERR_CAMELLIA_BAD_INPUT_DATA -0x0024
 
-#define MBEDTLS_ERR_CAMELLIA_INVALID_INPUT_LENGTH -0x0026 /**< Invalid data input length. */
+/** Invalid data input length. */
+#define MBEDTLS_ERR_CAMELLIA_INVALID_INPUT_LENGTH -0x0026
 
 /* MBEDTLS_ERR_CAMELLIA_HW_ACCEL_FAILED is deprecated and should not be used.
  */
-#define MBEDTLS_ERR_CAMELLIA_HW_ACCEL_FAILED              -0x0027  /**< Camellia hardware accelerator failed. */
+/** Camellia hardware accelerator failed. */
+#define MBEDTLS_ERR_CAMELLIA_HW_ACCEL_FAILED              -0x0027
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/third_party/mbedtls/repo/include/mbedtls/ccm.h b/third_party/mbedtls/repo/include/mbedtls/ccm.h
index 7193863..ece5a90 100644
--- a/third_party/mbedtls/repo/include/mbedtls/ccm.h
+++ b/third_party/mbedtls/repo/include/mbedtls/ccm.h
@@ -55,11 +55,14 @@
 
 #include "mbedtls/cipher.h"
 
-#define MBEDTLS_ERR_CCM_BAD_INPUT       -0x000D /**< Bad input parameters to the function. */
-#define MBEDTLS_ERR_CCM_AUTH_FAILED     -0x000F /**< Authenticated decryption failed. */
+/** Bad input parameters to the function. */
+#define MBEDTLS_ERR_CCM_BAD_INPUT       -0x000D
+/** Authenticated decryption failed. */
+#define MBEDTLS_ERR_CCM_AUTH_FAILED     -0x000F
 
 /* MBEDTLS_ERR_CCM_HW_ACCEL_FAILED is deprecated and should not be used. */
-#define MBEDTLS_ERR_CCM_HW_ACCEL_FAILED -0x0011 /**< CCM hardware accelerator failed. */
+/** CCM hardware accelerator failed. */
+#define MBEDTLS_ERR_CCM_HW_ACCEL_FAILED -0x0011
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/third_party/mbedtls/repo/include/mbedtls/chacha20.h b/third_party/mbedtls/repo/include/mbedtls/chacha20.h
index e59dd1f..03b4871 100644
--- a/third_party/mbedtls/repo/include/mbedtls/chacha20.h
+++ b/third_party/mbedtls/repo/include/mbedtls/chacha20.h
@@ -41,15 +41,18 @@
 #include <stdint.h>
 #include <stddef.h>
 
-#define MBEDTLS_ERR_CHACHA20_BAD_INPUT_DATA         -0x0051 /**< Invalid input parameter(s). */
+/** Invalid input parameter(s). */
+#define MBEDTLS_ERR_CHACHA20_BAD_INPUT_DATA         -0x0051
 
 /* MBEDTLS_ERR_CHACHA20_FEATURE_UNAVAILABLE is deprecated and should not be
  * used. */
-#define MBEDTLS_ERR_CHACHA20_FEATURE_UNAVAILABLE    -0x0053 /**< Feature not available. For example, s part of the API is not implemented. */
+/** Feature not available. For example, s part of the API is not implemented. */
+#define MBEDTLS_ERR_CHACHA20_FEATURE_UNAVAILABLE    -0x0053
 
 /* MBEDTLS_ERR_CHACHA20_HW_ACCEL_FAILED is deprecated and should not be used.
  */
-#define MBEDTLS_ERR_CHACHA20_HW_ACCEL_FAILED        -0x0055  /**< Chacha20 hardware accelerator failed. */
+/** Chacha20 hardware accelerator failed. */
+#define MBEDTLS_ERR_CHACHA20_HW_ACCEL_FAILED        -0x0055
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/third_party/mbedtls/repo/include/mbedtls/chachapoly.h b/third_party/mbedtls/repo/include/mbedtls/chachapoly.h
index 1007f95..c4ec7b5 100644
--- a/third_party/mbedtls/repo/include/mbedtls/chachapoly.h
+++ b/third_party/mbedtls/repo/include/mbedtls/chachapoly.h
@@ -41,8 +41,10 @@
 /* for shared error codes */
 #include "mbedtls/poly1305.h"
 
-#define MBEDTLS_ERR_CHACHAPOLY_BAD_STATE            -0x0054 /**< The requested operation is not permitted in the current state. */
-#define MBEDTLS_ERR_CHACHAPOLY_AUTH_FAILED          -0x0056 /**< Authenticated decryption failed: data was not authentic. */
+/** The requested operation is not permitted in the current state. */
+#define MBEDTLS_ERR_CHACHAPOLY_BAD_STATE            -0x0054
+/** Authenticated decryption failed: data was not authentic. */
+#define MBEDTLS_ERR_CHACHAPOLY_AUTH_FAILED          -0x0056
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/third_party/mbedtls/repo/include/mbedtls/check_config.h b/third_party/mbedtls/repo/include/mbedtls/check_config.h
index 1ebb706..396fe7d 100644
--- a/third_party/mbedtls/repo/include/mbedtls/check_config.h
+++ b/third_party/mbedtls/repo/include/mbedtls/check_config.h
@@ -55,9 +55,8 @@
 #endif
 #endif /* _WIN32 */
 
-#if defined(TARGET_LIKE_MBED) && \
-    ( defined(MBEDTLS_NET_C) || defined(MBEDTLS_TIMING_C) )
-#error "The NET and TIMING modules are not available for mbed OS - please use the network and timing functions provided by mbed OS"
+#if defined(TARGET_LIKE_MBED) && defined(MBEDTLS_NET_C)
+#error "The NET module is not available for mbed OS - please use the network functions provided by Mbed OS"
 #endif
 
 #if defined(MBEDTLS_DEPRECATED_WARNING) && \
@@ -252,6 +251,10 @@
 #error "MBEDTLS_ECP_NORMALIZE_MXZ_ALT defined, but not all prerequisites"
 #endif
 
+#if defined(MBEDTLS_ECP_NO_FALLBACK) && !defined(MBEDTLS_ECP_INTERNAL_ALT)
+#error "MBEDTLS_ECP_NO_FALLBACK defined, but no alternative implementation enabled"
+#endif
+
 #if defined(MBEDTLS_HAVEGE_C) && !defined(MBEDTLS_TIMING_C)
 #error "MBEDTLS_HAVEGE_C defined, but not all prerequisites"
 #endif
@@ -506,10 +509,6 @@
 #error "MBEDTLS_PLATFORM_STD_CALLOC defined, but not all prerequisites"
 #endif
 
-#if defined(MBEDTLS_PLATFORM_STD_CALLOC) && !defined(MBEDTLS_PLATFORM_MEMORY)
-#error "MBEDTLS_PLATFORM_STD_CALLOC defined, but not all prerequisites"
-#endif
-
 #if defined(MBEDTLS_PLATFORM_STD_FREE) && !defined(MBEDTLS_PLATFORM_MEMORY)
 #error "MBEDTLS_PLATFORM_STD_FREE defined, but not all prerequisites"
 #endif
@@ -572,10 +571,11 @@
 #error "MBEDTLS_PLATFORM_NV_SEED_WRITE_MACRO and MBEDTLS_PLATFORM_STD_NV_SEED_WRITE cannot be defined simultaneously"
 #endif
 
-#if defined(MBEDTLS_PSA_CRYPTO_C) &&            \
-    !( defined(MBEDTLS_CTR_DRBG_C) &&           \
-       defined(MBEDTLS_ENTROPY_C) )
-#error "MBEDTLS_PSA_CRYPTO_C defined, but not all prerequisites"
+#if defined(MBEDTLS_PSA_CRYPTO_C) &&                                    \
+    !( ( ( defined(MBEDTLS_CTR_DRBG_C) || defined(MBEDTLS_HMAC_DRBG_C) ) && \
+         defined(MBEDTLS_ENTROPY_C) ) ||                                \
+       defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) )
+#error "MBEDTLS_PSA_CRYPTO_C defined, but not all prerequisites (missing RNG)"
 #endif
 
 #if defined(MBEDTLS_PSA_CRYPTO_SPM) && !defined(MBEDTLS_PSA_CRYPTO_C)
@@ -604,6 +604,11 @@
 #error "MBEDTLS_PSA_INJECT_ENTROPY is not compatible with actual entropy sources"
 #endif
 
+#if defined(MBEDTLS_PSA_INJECT_ENTROPY) &&              \
+    defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG)
+#error "MBEDTLS_PSA_INJECT_ENTROPY is not compatible with MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG"
+#endif
+
 #if defined(MBEDTLS_PSA_ITS_FILE_C) && \
     !defined(MBEDTLS_FS_IO)
 #error "MBEDTLS_PSA_ITS_FILE_C defined, but not all prerequisites"
@@ -880,6 +885,10 @@
 #error "MBEDTLS_SSL_DTLS_SRTP defined, but not all prerequisites"
 #endif
 
+#if defined(MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH) && ( !defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH) )
+#error "MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH 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
diff --git a/third_party/mbedtls/repo/include/mbedtls/cipher.h b/third_party/mbedtls/repo/include/mbedtls/cipher.h
index 1cafa6e..6d83da8 100644
--- a/third_party/mbedtls/repo/include/mbedtls/cipher.h
+++ b/third_party/mbedtls/repo/include/mbedtls/cipher.h
@@ -54,16 +54,24 @@
 #define inline __inline
 #endif
 
-#define MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE  -0x6080  /**< The selected feature is not available. */
-#define MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA       -0x6100  /**< Bad input parameters. */
-#define MBEDTLS_ERR_CIPHER_ALLOC_FAILED         -0x6180  /**< Failed to allocate memory. */
-#define MBEDTLS_ERR_CIPHER_INVALID_PADDING      -0x6200  /**< Input data contains invalid padding and is rejected. */
-#define MBEDTLS_ERR_CIPHER_FULL_BLOCK_EXPECTED  -0x6280  /**< Decryption of block requires a full block. */
-#define MBEDTLS_ERR_CIPHER_AUTH_FAILED          -0x6300  /**< Authentication failed (for AEAD modes). */
-#define MBEDTLS_ERR_CIPHER_INVALID_CONTEXT      -0x6380  /**< The context is invalid. For example, because it was freed. */
+/** The selected feature is not available. */
+#define MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE  -0x6080
+/** Bad input parameters. */
+#define MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA       -0x6100
+/** Failed to allocate memory. */
+#define MBEDTLS_ERR_CIPHER_ALLOC_FAILED         -0x6180
+/** Input data contains invalid padding and is rejected. */
+#define MBEDTLS_ERR_CIPHER_INVALID_PADDING      -0x6200
+/** Decryption of block requires a full block. */
+#define MBEDTLS_ERR_CIPHER_FULL_BLOCK_EXPECTED  -0x6280
+/** Authentication failed (for AEAD modes). */
+#define MBEDTLS_ERR_CIPHER_AUTH_FAILED          -0x6300
+/** The context is invalid. For example, because it was freed. */
+#define MBEDTLS_ERR_CIPHER_INVALID_CONTEXT      -0x6380
 
 /* MBEDTLS_ERR_CIPHER_HW_ACCEL_FAILED is deprecated and should not be used. */
-#define MBEDTLS_ERR_CIPHER_HW_ACCEL_FAILED      -0x6400  /**< Cipher hardware accelerator failed. */
+/** Cipher hardware accelerator failed. */
+#define MBEDTLS_ERR_CIPHER_HW_ACCEL_FAILED      -0x6400
 
 #define MBEDTLS_CIPHER_VARIABLE_IV_LEN     0x01    /**< Cipher accepts IVs of variable length. */
 #define MBEDTLS_CIPHER_VARIABLE_KEY_LEN    0x02    /**< Cipher accepts keys of variable length. */
diff --git a/third_party/mbedtls/repo/include/mbedtls/cmac.h b/third_party/mbedtls/repo/include/mbedtls/cmac.h
index cb538d0..8934886 100644
--- a/third_party/mbedtls/repo/include/mbedtls/cmac.h
+++ b/third_party/mbedtls/repo/include/mbedtls/cmac.h
@@ -39,7 +39,8 @@
 #endif
 
 /* MBEDTLS_ERR_CMAC_HW_ACCEL_FAILED is deprecated and should not be used. */
-#define MBEDTLS_ERR_CMAC_HW_ACCEL_FAILED -0x007A  /**< CMAC hardware accelerator failed. */
+/** CMAC hardware accelerator failed. */
+#define MBEDTLS_ERR_CMAC_HW_ACCEL_FAILED -0x007A
 
 #define MBEDTLS_AES_BLOCK_SIZE          16
 #define MBEDTLS_DES3_BLOCK_SIZE         8
@@ -73,9 +74,23 @@
 #endif /* !MBEDTLS_CMAC_ALT */
 
 /**
- * \brief               This function sets the CMAC key, and prepares to authenticate
+ * \brief               This function starts a new CMAC computation
+ *                      by setting the CMAC key, and preparing to authenticate
  *                      the input data.
- *                      Must be called with an initialized cipher context.
+ *                      It must be called with an initialized cipher context.
+ *
+ *                      Once this function has completed, data can be supplied
+ *                      to the CMAC computation by calling
+ *                      mbedtls_cipher_cmac_update().
+ *
+ *                      To start a CMAC computation using the same key as a previous
+ *                      CMAC computation, use mbedtls_cipher_cmac_finish().
+ *
+ * \note                When the CMAC implementation is supplied by an alternate
+ *                      implementation (through #MBEDTLS_CMAC_ALT), some ciphers
+ *                      may not be supported by that implementation, and thus
+ *                      return an error. Alternate implementations must support
+ *                      AES-128 and AES-256, and may support AES-192 and 3DES.
  *
  * \param ctx           The cipher context used for the CMAC operation, initialized
  *                      as one of the following types: MBEDTLS_CIPHER_AES_128_ECB,
@@ -95,9 +110,15 @@
  * \brief               This function feeds an input buffer into an ongoing CMAC
  *                      computation.
  *
- *                      It is called between mbedtls_cipher_cmac_starts() or
- *                      mbedtls_cipher_cmac_reset(), and mbedtls_cipher_cmac_finish().
- *                      Can be called repeatedly.
+ *                      The CMAC computation must have previously been started
+ *                      by calling mbedtls_cipher_cmac_starts() or
+ *                      mbedtls_cipher_cmac_reset().
+ *
+ *                      Call this function as many times as needed to input the
+ *                      data to be authenticated.
+ *                      Once all of the required data has been input,
+ *                      call mbedtls_cipher_cmac_finish() to obtain the result
+ *                      of the CMAC operation.
  *
  * \param ctx           The cipher context used for the CMAC operation.
  * \param input         The buffer holding the input data.
@@ -111,12 +132,13 @@
                                 const unsigned char *input, size_t ilen );
 
 /**
- * \brief               This function finishes the CMAC operation, and writes
- *                      the result to the output buffer.
+ * \brief               This function finishes an ongoing CMAC operation, and
+ *                      writes the result to the output buffer.
  *
- *                      It is called after mbedtls_cipher_cmac_update().
- *                      It can be followed by mbedtls_cipher_cmac_reset() and
- *                      mbedtls_cipher_cmac_update(), or mbedtls_cipher_free().
+ *                      It should be followed either by
+ *                      mbedtls_cipher_cmac_reset(), which starts another CMAC
+ *                      operation with the same key, or mbedtls_cipher_free(),
+ *                      which clears the cipher context.
  *
  * \param ctx           The cipher context used for the CMAC operation.
  * \param output        The output buffer for the CMAC checksum result.
@@ -129,12 +151,14 @@
                                 unsigned char *output );
 
 /**
- * \brief               This function prepares the authentication of another
- *                      message with the same key as the previous CMAC
- *                      operation.
+ * \brief               This function starts a new CMAC operation with the same
+ *                      key as the previous one.
  *
- *                      It is called after mbedtls_cipher_cmac_finish()
- *                      and before mbedtls_cipher_cmac_update().
+ *                      It should be called after finishing the previous CMAC
+ *                      operation with mbedtls_cipher_cmac_finish().
+ *                      After calling this function,
+ *                      call mbedtls_cipher_cmac_update() to supply the new
+ *                      CMAC operation with data.
  *
  * \param ctx           The cipher context used for the CMAC operation.
  *
@@ -154,6 +178,11 @@
  *                      The CMAC result is calculated as
  *                      output = generic CMAC(cmac key, input buffer).
  *
+ * \note                When the CMAC implementation is supplied by an alternate
+ *                      implementation (through #MBEDTLS_CMAC_ALT), some ciphers
+ *                      may not be supported by that implementation, and thus
+ *                      return an error. Alternate implementations must support
+ *                      AES-128 and AES-256, and may support AES-192 and 3DES.
  *
  * \param cipher_info   The cipher information.
  * \param key           The CMAC key.
@@ -198,6 +227,13 @@
 /**
  * \brief          The CMAC checkup routine.
  *
+ * \note           In case the CMAC routines are provided by an alternative
+ *                 implementation (i.e. #MBEDTLS_CMAC_ALT is defined), the
+ *                 checkup routine will succeed even if the implementation does
+ *                 not support the less widely used AES-192 or 3DES primitives.
+ *                 The self-test requires at least AES-128 and AES-256 to be
+ *                 supported by the underlying implementation.
+ *
  * \return         \c 0 on success.
  * \return         \c 1 on failure.
  */
diff --git a/third_party/mbedtls/repo/include/mbedtls/config.h b/third_party/mbedtls/repo/include/mbedtls/config.h
index 464b61e..87b4e91 100644
--- a/third_party/mbedtls/repo/include/mbedtls/config.h
+++ b/third_party/mbedtls/repo/include/mbedtls/config.h
@@ -427,7 +427,7 @@
  *       be overridden, but the wrapper functions mbedtls_aes_decrypt and mbedtls_aes_encrypt
  *       must stay untouched.
  *
- * \note If you use the AES_xxx_ALT macros, then is is recommended to also set
+ * \note If you use the AES_xxx_ALT macros, then it is recommended to also set
  *       MBEDTLS_AES_ROM_TABLES in order to help the linker garbage-collect the AES
  *       tables.
  *
@@ -484,6 +484,11 @@
  * is still present and it is used for group structures not supported by the
  * alternative.
  *
+ * The original implementation can in addition be removed by setting the
+ * MBEDTLS_ECP_NO_FALLBACK option, in which case any function for which the
+ * corresponding MBEDTLS_ECP__FUNCTION_NAME__ALT macro is defined will not be
+ * able to fallback to curves not supported by the alternative implementation.
+ *
  * Any of these options become available by defining MBEDTLS_ECP_INTERNAL_ALT
  * and implementing the following functions:
  *      unsigned char mbedtls_internal_ecp_grp_capable(
@@ -497,21 +502,28 @@
  * called before and after each point operation and provide an opportunity to
  * implement optimized set up and tear down instructions.
  *
- * Example: In case you uncomment MBEDTLS_ECP_INTERNAL_ALT and
- * 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 is
- * supported (your mbedtls_internal_ecp_grp_capable function returns 1 when
- * receives it as an argument). If the group is not supported then the original
- * implementation is used. The other functions and the definition of
- * mbedtls_ecp_group and mbedtls_ecp_point will not change, so your
- * implementation of mbedtls_internal_ecp_double_jac and
- * mbedtls_internal_ecp_grp_capable must be compatible with this definition.
+ * Example: In case you set MBEDTLS_ECP_INTERNAL_ALT and
+ * 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
+ * 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).
+ *
+ * The function prototypes and the definition of mbedtls_ecp_group and
+ * mbedtls_ecp_point will not change based on MBEDTLS_ECP_INTERNAL_ALT, so your
+ * implementation of mbedtls_internal_ecp__function_name__ must be compatible
+ * with their definitions.
  *
  * Uncomment a macro to enable alternate implementation of the corresponding
  * function.
  */
 /* Required for all the functions in this section */
 //#define MBEDTLS_ECP_INTERNAL_ALT
+/* Turn off software fallback for curves not supported in hardware */
+//#define MBEDTLS_ECP_NO_FALLBACK
 /* Support for Weierstrass curves with Jacobi representation */
 //#define MBEDTLS_ECP_RANDOMIZE_JAC_ALT
 //#define MBEDTLS_ECP_ADD_MIXED_ALT
@@ -605,6 +617,29 @@
 //#define MBEDTLS_CAMELLIA_SMALL_MEMORY
 
 /**
+ * \def MBEDTLS_CHECK_RETURN_WARNING
+ *
+ * If this macro is defined, emit a compile-time warning if application code
+ * calls a function without checking its return value, but the return value
+ * should generally be checked in portable applications.
+ *
+ * This is only supported on platforms where #MBEDTLS_CHECK_RETURN is
+ * implemented. Otherwise this option has no effect.
+ *
+ * Uncomment to get warnings on using fallible functions without checking
+ * their return value.
+ *
+ * \note  This feature is a work in progress.
+ *        Warnings will be added to more functions in the future.
+ *
+ * \note  A few functions are considered critical, and ignoring the return
+ *        value of these functions will trigger a warning even if this
+ *        macro is not defined. To completely disable return value check
+ *        warnings, define #MBEDTLS_CHECK_RETURN with an empty expansion.
+ */
+//#define MBEDTLS_CHECK_RETURN_WARNING
+
+/**
  * \def MBEDTLS_CIPHER_MODE_CBC
  *
  * Enable Cipher Block Chaining mode (CBC) for symmetric ciphers.
@@ -1326,6 +1361,38 @@
  */
 #define MBEDTLS_PKCS1_V21
 
+/** \def MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS
+ *
+ * Enable support for platform built-in keys. If you enable this feature,
+ * you must implement the function mbedtls_psa_platform_get_builtin_key().
+ * See the documentation of that function for more information.
+ *
+ * Built-in keys are typically derived from a hardware unique key or
+ * stored in a secure element.
+ *
+ * Requires: MBEDTLS_PSA_CRYPTO_C.
+ *
+ * \warning This interface is experimental and may change or be removed
+ * without notice.
+ */
+//#define MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS
+
+/** \def MBEDTLS_PSA_CRYPTO_CLIENT
+ *
+ * Enable support for PSA crypto client.
+ *
+ * \note This option allows to include the code necessary for a PSA
+ *       crypto client when the PSA crypto implementation is not included in
+ *       the library (MBEDTLS_PSA_CRYPTO_C disabled). The code included is the
+ *       code to set and get PSA key attributes.
+ *       The development of PSA drivers partially relying on the library to
+ *       fulfill the hardware gaps is another possible usage of this option.
+ *
+ * \warning This interface is experimental and may change or be removed
+ * without notice.
+ */
+//#define MBEDTLS_PSA_CRYPTO_CLIENT
+
 /** \def MBEDTLS_PSA_CRYPTO_DRIVERS
  *
  * Enable support for the experimental PSA crypto driver interface.
@@ -1337,6 +1404,44 @@
  */
 //#define MBEDTLS_PSA_CRYPTO_DRIVERS
 
+/** \def MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG
+ *
+ * Make the PSA Crypto module use an external random generator provided
+ * by a driver, instead of Mbed TLS's entropy and DRBG modules.
+ *
+ * \note This random generator must deliver random numbers with cryptographic
+ *       quality and high performance. It must supply unpredictable numbers
+ *       with a uniform distribution. The implementation of this function
+ *       is responsible for ensuring that the random generator is seeded
+ *       with sufficient entropy. If you have a hardware TRNG which is slow
+ *       or delivers non-uniform output, declare it as an entropy source
+ *       with mbedtls_entropy_add_source() instead of enabling this option.
+ *
+ * If you enable this option, you must configure the type
+ * ::mbedtls_psa_external_random_context_t in psa/crypto_platform.h
+ * and define a function called mbedtls_psa_external_get_random()
+ * with the following prototype:
+ * ```
+ * psa_status_t mbedtls_psa_external_get_random(
+ *     mbedtls_psa_external_random_context_t *context,
+ *     uint8_t *output, size_t output_size, size_t *output_length);
+ * );
+ * ```
+ * 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.
+ *
+ * Requires: MBEDTLS_PSA_CRYPTO_C
+ *
+ * \warning If you enable this option, code that uses the PSA cryptography
+ *          interface will not use any of the entropy sources set up for
+ *          the entropy module, nor the NV seed that MBEDTLS_ENTROPY_NV_SEED
+ *          enables.
+ *
+ * \note This option is experimental and may be removed without notice.
+ */
+//#define MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG
+
 /**
  * \def MBEDTLS_PSA_CRYPTO_SPM
  *
@@ -1815,7 +1920,7 @@
 /**
  * \def MBEDTLS_SSL_DTLS_SRTP
  *
- * Enable support for negotation of DTLS-SRTP (RFC 5764)
+ * Enable support for negotiation of DTLS-SRTP (RFC 5764)
  * through the use_srtp extension.
  *
  * \note This feature provides the minimum functionality required
@@ -1941,7 +2046,10 @@
 /**
  * \def MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH
  *
- * Enable modifying the maximum I/O buffer size.
+ * When this option is enabled, the SSL buffer will be resized automatically
+ * based on the negotiated maximum fragment length in each direction.
+ *
+ * Requires: MBEDTLS_SSL_MAX_FRAGMENT_LENGTH
  */
 //#define MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH
 
@@ -2034,15 +2142,13 @@
  * will still continue to work as usual, so enabling this option should not
  * break backwards compatibility.
  *
- * \warning The PSA Crypto API is in beta stage. While you're welcome to
- * experiment using it, incompatible API changes are still possible, and some
- * parts may not have reached the same quality as the rest of Mbed TLS yet.
+ * \note See docs/use-psa-crypto.md for a complete description of what this
+ * option currently does, and of parts that are not affected by it so far.
  *
- * \warning This option enables new Mbed TLS APIs that are dependent on the
- * PSA Crypto API, so can't come with the same stability guarantees as the
- * rest of the Mbed TLS APIs. You're welcome to experiment with them, but for
- * now, access to these APIs is opt-in (via enabling the present option), in
- * order to clearly differentiate them from the stable Mbed TLS APIs.
+ * \warning This option enables new Mbed TLS APIs which are currently
+ * considered experimental and may change in incompatible ways at any time.
+ * That is, the APIs enabled by this option are not covered by the usual
+ * promises of API stability.
  *
  * Requires: MBEDTLS_PSA_CRYPTO_C.
  *
@@ -2546,6 +2652,11 @@
  * Enable the CMAC (Cipher-based Message Authentication Code) mode for block
  * ciphers.
  *
+ * \note When #MBEDTLS_CMAC_ALT is active, meaning that the underlying
+ *       implementation of the CMAC algorithm is provided by an alternate
+ *       implementation, that alternate implementation may opt to not support
+ *       AES-192 or 3DES as underlying block ciphers for the CMAC operation.
+ *
  * Module:  library/cmac.c
  *
  * Requires: MBEDTLS_AES_C or MBEDTLS_DES_C
@@ -3109,13 +3220,11 @@
  *
  * Enable the Platform Security Architecture cryptography API.
  *
- * \warning The PSA Crypto API is still beta status. While you're welcome to
- * experiment using it, incompatible API changes are still possible, and some
- * parts may not have reached the same quality as the rest of Mbed TLS yet.
- *
  * Module:  library/psa_crypto.c
  *
- * Requires: MBEDTLS_CTR_DRBG_C, MBEDTLS_ENTROPY_C
+ * Requires: either MBEDTLS_CTR_DRBG_C and MBEDTLS_ENTROPY_C,
+ *           or MBEDTLS_HMAC_DRBG_C and MBEDTLS_ENTROPY_C,
+ *           or MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG.
  *
  */
 #define MBEDTLS_PSA_CRYPTO_C
@@ -3524,8 +3633,8 @@
 //#define MBEDTLS_HMAC_DRBG_MAX_SEED_INPUT      384 /**< Maximum size of (re)seed buffer */
 
 /* ECP options */
-//#define MBEDTLS_ECP_MAX_BITS             521 /**< Maximum bit size of groups */
-//#define MBEDTLS_ECP_WINDOW_SIZE            6 /**< Maximum window size used */
+//#define MBEDTLS_ECP_MAX_BITS             521 /**< Maximum bit size of groups. Normally determined automatically from the configured curves. */
+//#define MBEDTLS_ECP_WINDOW_SIZE            4 /**< Maximum window size used */
 //#define MBEDTLS_ECP_FIXED_POINT_OPTIM      1 /**< Enable fixed-point speed-up */
 
 /* Entropy options */
@@ -3603,6 +3712,53 @@
  */
 //#define MBEDTLS_PARAM_FAILED( cond )               assert( cond )
 
+/** \def MBEDTLS_CHECK_RETURN
+ *
+ * This macro is used at the beginning of the declaration of a function
+ * to indicate that its return value should be checked. It should
+ * instruct the compiler to emit a warning or an error if the function
+ * is called without checking its return value.
+ *
+ * There is a default implementation for popular compilers in platform_util.h.
+ * You can override the default implementation by defining your own here.
+ *
+ * If the implementation here is empty, this will effectively disable the
+ * checking of functions' return values.
+ */
+//#define MBEDTLS_CHECK_RETURN __attribute__((__warn_unused_result__))
+
+/** \def MBEDTLS_IGNORE_RETURN
+ *
+ * This macro requires one argument, which should be a C function call.
+ * If that function call would cause a #MBEDTLS_CHECK_RETURN warning, this
+ * warning is suppressed.
+ */
+//#define MBEDTLS_IGNORE_RETURN( result ) ((void) !(result))
+
+/* PSA options */
+/**
+ * Use HMAC_DRBG with the specified hash algorithm for HMAC_DRBG for the
+ * PSA crypto subsystem.
+ *
+ * If this option is unset:
+ * - If CTR_DRBG is available, the PSA subsystem uses it rather than HMAC_DRBG.
+ * - Otherwise, the PSA subsystem uses HMAC_DRBG with either
+ *   #MBEDTLS_MD_SHA512 or #MBEDTLS_MD_SHA256 based on availability and
+ *   on unspecified heuristics.
+ */
+//#define MBEDTLS_PSA_HMAC_DRBG_MD_TYPE MBEDTLS_MD_SHA256
+
+/** \def MBEDTLS_PSA_KEY_SLOT_COUNT
+ * Restrict the PSA library to supporting a maximum amount of simultaneously
+ * loaded keys. A loaded key is a key stored by the PSA Crypto core as a
+ * volatile key, or a persistent key which is loaded temporarily by the
+ * library as part of a crypto operation in flight.
+ *
+ * If this option is unset, the library will fall back to a default value of
+ * 32 keys.
+ */
+//#define MBEDTLS_PSA_KEY_SLOT_COUNT 32
+
 /* SSL Cache options */
 //#define MBEDTLS_SSL_CACHE_DEFAULT_TIMEOUT       86400 /**< 1 day  */
 //#define MBEDTLS_SSL_CACHE_DEFAULT_MAX_ENTRIES      50 /**< Maximum entries in cache */
@@ -3736,7 +3892,7 @@
  * Maximum number of heap-allocated bytes for the purpose of
  * DTLS handshake message reassembly and future message buffering.
  *
- * This should be at least 9/8 * MBEDTLSSL_IN_CONTENT_LEN
+ * This should be at least 9/8 * MBEDTLS_SSL_IN_CONTENT_LEN
  * to account for a reassembled handshake message of maximum size,
  * together with its reassembly bitmap.
  *
@@ -3752,6 +3908,17 @@
 //#define MBEDTLS_PSK_MAX_LEN               32 /**< Max size of TLS pre-shared keys, in bytes (default 256 bits) */
 //#define MBEDTLS_SSL_COOKIE_TIMEOUT        60 /**< Default expiration delay of DTLS cookies, in seconds if HAVE_TIME, or in number of cookies issued */
 
+/** \def MBEDTLS_TLS_EXT_CID
+ *
+ * At the time of writing, the CID extension has not been assigned its
+ * final value. Set this configuration option to make Mbed TLS use a
+ * different value.
+ *
+ * A future minor revision of Mbed TLS may change the default value of
+ * this option to match evolving standards and usage.
+ */
+//#define MBEDTLS_TLS_EXT_CID                        254
+
 /**
  * Complete list of ciphersuites to use, in order of preference.
  *
@@ -3771,20 +3938,6 @@
 //#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 certificate signing.
- * Without this build-time option, SHA-1 support must be activated explicitly
- * through mbedtls_ssl_conf_cert_profile. Turning on this option is not
- * recommended because of it is possible to generate SHA-1 collisions, however
- * this may be safe for legacy infrastructure where additional controls apply.
- *
- * \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_CERTIFICATES
-
-/**
  * 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.
@@ -3799,7 +3952,7 @@
  *            on it, and considering stronger message digests instead.
  *
  */
-#define MBEDTLS_TLS_DEFAULT_ALLOW_SHA1_IN_KEY_EXCHANGE
+//#define MBEDTLS_TLS_DEFAULT_ALLOW_SHA1_IN_KEY_EXCHANGE
 
 /**
  * Uncomment the macro to let mbed TLS use your alternate implementation of
diff --git a/third_party/mbedtls/repo/include/mbedtls/config_psa.h b/third_party/mbedtls/repo/include/mbedtls/config_psa.h
index 2b4c498..189f6c2 100644
--- a/third_party/mbedtls/repo/include/mbedtls/config_psa.h
+++ b/third_party/mbedtls/repo/include/mbedtls/config_psa.h
@@ -38,6 +38,36 @@
 extern "C" {
 #endif
 
+
+
+/****************************************************************/
+/* De facto synonyms */
+/****************************************************************/
+
+#if defined(PSA_WANT_ALG_ECDSA_ANY) && !defined(PSA_WANT_ALG_ECDSA)
+#define PSA_WANT_ALG_ECDSA PSA_WANT_ALG_ECDSA_ANY
+#elif !defined(PSA_WANT_ALG_ECDSA_ANY) && defined(PSA_WANT_ALG_ECDSA)
+#define PSA_WANT_ALG_ECDSA_ANY PSA_WANT_ALG_ECDSA
+#endif
+
+#if defined(PSA_WANT_ALG_RSA_PKCS1V15_SIGN_RAW) && !defined(PSA_WANT_ALG_RSA_PKCS1V15_SIGN)
+#define PSA_WANT_ALG_RSA_PKCS1V15_SIGN PSA_WANT_ALG_RSA_PKCS1V15_SIGN_RAW
+#elif !defined(PSA_WANT_ALG_RSA_PKCS1V15_SIGN_RAW) && defined(PSA_WANT_ALG_RSA_PKCS1V15_SIGN)
+#define PSA_WANT_ALG_RSA_PKCS1V15_SIGN_RAW PSA_WANT_ALG_RSA_PKCS1V15_SIGN
+#endif
+
+#if defined(PSA_WANT_ALG_RSA_PSS_ANY_SALT) && !defined(PSA_WANT_ALG_RSA_PSS)
+#define PSA_WANT_ALG_RSA_PSS PSA_WANT_ALG_RSA_PSS_ANY_SALT
+#elif !defined(PSA_WANT_ALG_RSA_PSS_ANY_SALT) && defined(PSA_WANT_ALG_RSA_PSS)
+#define PSA_WANT_ALG_RSA_PSS_ANY_SALT PSA_WANT_ALG_RSA_PSS
+#endif
+
+
+
+/****************************************************************/
+/* Require built-in implementations based on PSA requirements */
+/****************************************************************/
+
 #if defined(MBEDTLS_PSA_CRYPTO_CONFIG)
 
 #if defined(PSA_WANT_ALG_DETERMINISTIC_ECDSA)
@@ -63,6 +93,10 @@
 #if !defined(MBEDTLS_PSA_ACCEL_ALG_ECDSA)
 #define MBEDTLS_PSA_BUILTIN_ALG_ECDSA 1
 #define MBEDTLS_ECDSA_C
+#define MBEDTLS_ECP_C
+#define MBEDTLS_BIGNUM_C
+#define MBEDTLS_ASN1_PARSE_C
+#define MBEDTLS_ASN1_WRITE_C
 #endif /* !MBEDTLS_PSA_ACCEL_ALG_ECDSA */
 #endif /* PSA_WANT_ALG_ECDSA */
 
@@ -205,6 +239,8 @@
 #define MBEDTLS_PK_PARSE_C
 #define MBEDTLS_PK_WRITE_C
 #define MBEDTLS_PK_C
+#define MBEDTLS_ASN1_PARSE_C
+#define MBEDTLS_ASN1_WRITE_C
 #endif /* !MBEDTLS_PSA_ACCEL_KEY_TYPE_RSA_KEY_PAIR */
 #endif /* PSA_WANT_KEY_TYPE_RSA_KEY_PAIR */
 
@@ -217,9 +253,311 @@
 #define MBEDTLS_PK_PARSE_C
 #define MBEDTLS_PK_WRITE_C
 #define MBEDTLS_PK_C
+#define MBEDTLS_ASN1_PARSE_C
+#define MBEDTLS_ASN1_WRITE_C
 #endif /* !MBEDTLS_PSA_ACCEL_KEY_TYPE_RSA_PUBLIC_KEY */
 #endif /* PSA_WANT_KEY_TYPE_RSA_PUBLIC_KEY */
 
+/* If any of the block modes are requested that don't have an
+ * associated HW assist, define PSA_HAVE_SOFT_BLOCK_MODE for checking
+ * in the block cipher key types. */
+#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(PSA_WANT_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
+
+#if (defined(PSA_WANT_ALG_GCM) && !defined(MBEDTLS_PSA_ACCEL_ALG_GCM)) || \
+    (defined(PSA_WANT_ALG_CCM) && !defined(MBEDTLS_PSA_ACCEL_ALG_CCM))
+#define PSA_HAVE_SOFT_BLOCK_AEAD 1
+#endif
+
+#if defined(PSA_WANT_KEY_TYPE_AES)
+#if !defined(MBEDTLS_PSA_ACCEL_KEY_TYPE_AES)
+#define PSA_HAVE_SOFT_KEY_TYPE_AES 1
+#endif /* !MBEDTLS_PSA_ACCEL_KEY_TYPE_AES */
+#if defined(PSA_HAVE_SOFT_KEY_TYPE_AES) || \
+    defined(PSA_HAVE_SOFT_BLOCK_MODE) || \
+    defined(PSA_HAVE_SOFT_BLOCK_AEAD)
+#define MBEDTLS_PSA_BUILTIN_KEY_TYPE_AES 1
+#define MBEDTLS_AES_C
+#endif /* PSA_HAVE_SOFT_KEY_TYPE_AES || PSA_HAVE_SOFT_BLOCK_MODE */
+#endif /* PSA_WANT_KEY_TYPE_AES */
+
+#if defined(PSA_WANT_KEY_TYPE_ARC4)
+#if !defined(MBEDTLS_PSA_ACCEL_KEY_TYPE_ARC4)
+#define MBEDTLS_PSA_BUILTIN_KEY_TYPE_ARC4 1
+#define MBEDTLS_ARC4_C
+#endif /*!MBEDTLS_PSA_ACCEL_KEY_TYPE_ARC4 */
+#endif /* PSA_WANT_KEY_TYPE_ARC4 */
+
+#if defined(PSA_WANT_KEY_TYPE_ARIA)
+#if !defined(MBEDTLS_PSA_ACCEL_KEY_TYPE_ARIA)
+#define PSA_HAVE_SOFT_KEY_TYPE_ARIA 1
+#endif /* !MBEDTLS_PSA_ACCEL_KEY_TYPE_ARIA */
+#if defined(PSA_HAVE_SOFT_KEY_TYPE_ARIA) || \
+    defined(PSA_HAVE_SOFT_BLOCK_MODE) || \
+    defined(PSA_HAVE_SOFT_BLOCK_AEAD)
+#define MBEDTLS_PSA_BUILTIN_KEY_TYPE_ARIA 1
+#define MBEDTLS_ARIA_C
+#endif /* PSA_HAVE_SOFT_KEY_TYPE_ARIA || PSA_HAVE_SOFT_BLOCK_MODE */
+#endif /* PSA_WANT_KEY_TYPE_ARIA */
+
+#if defined(PSA_WANT_KEY_TYPE_CAMELLIA)
+#if !defined(MBEDTLS_PSA_ACCEL_KEY_TYPE_CAMELLIA)
+#define PSA_HAVE_SOFT_KEY_TYPE_CAMELLIA 1
+#endif /* !MBEDTLS_PSA_ACCEL_KEY_TYPE_CAMELLIA */
+#if defined(PSA_HAVE_SOFT_KEY_TYPE_CAMELLIA) || \
+    defined(PSA_HAVE_SOFT_BLOCK_MODE) || \
+    defined(PSA_HAVE_SOFT_BLOCK_AEAD)
+#define MBEDTLS_PSA_BUILTIN_KEY_TYPE_CAMELLIA 1
+#define MBEDTLS_CAMELLIA_C
+#endif /* PSA_HAVE_SOFT_KEY_TYPE_CAMELLIA || PSA_HAVE_SOFT_BLOCK_MODE */
+#endif /* PSA_WANT_KEY_TYPE_CAMELLIA */
+
+#if defined(PSA_WANT_KEY_TYPE_DES)
+#if !defined(MBEDTLS_PSA_ACCEL_KEY_TYPE_DES)
+#define PSA_HAVE_SOFT_KEY_TYPE_DES 1
+#endif /* !MBEDTLS_PSA_ACCEL_KEY_TYPE_DES */
+#if defined(PSA_HAVE_SOFT_KEY_TYPE_DES) || \
+    defined(PSA_HAVE_SOFT_BLOCK_MODE)
+#define MBEDTLS_PSA_BUILTIN_KEY_TYPE_DES 1
+#define MBEDTLS_DES_C
+#endif /*PSA_HAVE_SOFT_KEY_TYPE_DES || PSA_HAVE_SOFT_BLOCK_MODE */
+#endif /* PSA_WANT_KEY_TYPE_DES */
+
+#if defined(PSA_WANT_KEY_TYPE_CHACHA20)
+#if !defined(MBEDTLS_PSA_ACCEL_KEY_TYPE_CHACHA20)
+#define MBEDTLS_PSA_BUILTIN_KEY_TYPE_CHACHA20 1
+#define MBEDTLS_CHACHA20_C
+#endif /*!MBEDTLS_PSA_ACCEL_KEY_TYPE_CHACHA20 */
+#endif /* PSA_WANT_KEY_TYPE_CHACHA20 */
+
+/* If any of the software block ciphers are selected, define
+ * PSA_HAVE_SOFT_BLOCK_CIPHER, which can be used in any of these
+ * situations. */
+#if defined(PSA_HAVE_SOFT_KEY_TYPE_AES) || \
+    defined(PSA_HAVE_SOFT_KEY_TYPE_ARIA) || \
+    defined(PSA_HAVE_SOFT_KEY_TYPE_DES) || \
+    defined(PSA_HAVE_SOFT_KEY_TYPE_CAMELLIA)
+#define PSA_HAVE_SOFT_BLOCK_CIPHER 1
+#endif
+
+#if defined(PSA_WANT_ALG_STREAM_CIPHER)
+#define MBEDTLS_PSA_BUILTIN_ALG_STREAM_CIPHER 1
+#endif /* PSA_WANT_ALG_STREAM_CIPHER */
+
+#if defined(PSA_WANT_ALG_CBC_MAC)
+#if !defined(MBEDTLS_PSA_ACCEL_ALG_CBC_MAC)
+#error "CBC-MAC is not yet supported via the PSA API in Mbed TLS."
+#define MBEDTLS_PSA_BUILTIN_ALG_CBC_MAC 1
+#endif /* !MBEDTLS_PSA_ACCEL_ALG_CBC_MAC */
+#endif /* PSA_WANT_ALG_CBC_MAC */
+
+#if defined(PSA_WANT_ALG_CMAC)
+#if !defined(MBEDTLS_PSA_ACCEL_ALG_CMAC) || \
+    defined(PSA_HAVE_SOFT_BLOCK_CIPHER)
+#define MBEDTLS_PSA_BUILTIN_ALG_CMAC 1
+#define MBEDTLS_CMAC_C
+#endif /* !MBEDTLS_PSA_ACCEL_ALG_CMAC */
+#endif /* PSA_WANT_ALG_CMAC */
+
+#if defined(PSA_WANT_ALG_CTR)
+#if !defined(MBEDTLS_PSA_ACCEL_ALG_CTR) || \
+    defined(PSA_HAVE_SOFT_BLOCK_CIPHER)
+#define MBEDTLS_PSA_BUILTIN_ALG_CTR 1
+#define MBEDTLS_CIPHER_MODE_CTR
+#endif
+#endif /* PSA_WANT_ALG_CTR */
+
+#if defined(PSA_WANT_ALG_CFB)
+#if !defined(MBEDTLS_PSA_ACCEL_ALG_CFB) || \
+    defined(PSA_HAVE_SOFT_BLOCK_CIPHER)
+#define MBEDTLS_PSA_BUILTIN_ALG_CFB 1
+#define MBEDTLS_CIPHER_MODE_CFB
+#endif
+#endif /* PSA_WANT_ALG_CFB */
+
+#if defined(PSA_WANT_ALG_OFB)
+#if !defined(MBEDTLS_PSA_ACCEL_ALG_OFB) || \
+    defined(PSA_HAVE_SOFT_BLOCK_CIPHER)
+#define MBEDTLS_PSA_BUILTIN_ALG_OFB 1
+#define MBEDTLS_CIPHER_MODE_OFB
+#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)
+#define MBEDTLS_PSA_BUILTIN_ALG_ECB_NO_PADDING 1
+#endif
+
+#if defined(PSA_WANT_ALG_CBC_NO_PADDING)
+#if !defined(MBEDTLS_PSA_ACCEL_ALG_CBC_NO_PADDING) || \
+    defined(PSA_HAVE_SOFT_BLOCK_CIPHER)
+#define MBEDTLS_CIPHER_MODE_CBC
+#define MBEDTLS_PSA_BUILTIN_ALG_CBC_NO_PADDING 1
+#endif
+#endif /* PSA_WANT_ALG_CBC_NO_PADDING */
+
+#if defined(PSA_WANT_ALG_CBC_PKCS7)
+#if !defined(MBEDTLS_PSA_ACCEL_ALG_CBC_PKCS7) || \
+    defined(PSA_HAVE_SOFT_BLOCK_CIPHER)
+#define MBEDTLS_CIPHER_MODE_CBC
+#define MBEDTLS_PSA_BUILTIN_ALG_CBC_PKCS7 1
+#define MBEDTLS_CIPHER_PADDING_PKCS7
+#endif
+#endif /* PSA_WANT_ALG_CBC_PKCS7 */
+
+#if defined(PSA_WANT_ALG_CCM)
+#if !defined(MBEDTLS_PSA_ACCEL_ALG_CCM) || \
+    defined(PSA_HAVE_SOFT_KEY_TYPE_AES) || \
+    defined(PSA_HAVE_SOFT_KEY_TYPE_ARIA) || \
+    defined(PSA_HAVE_SOFT_KEY_TYPE_CAMELLIA)
+#define MBEDTLS_PSA_BUILTIN_ALG_CCM 1
+#define MBEDTLS_CCM_C
+#endif
+#endif /* PSA_WANT_ALG_CCM */
+
+#if defined(PSA_WANT_ALG_GCM)
+#if !defined(MBEDTLS_PSA_ACCEL_ALG_GCM) || \
+    defined(PSA_HAVE_SOFT_KEY_TYPE_AES) || \
+    defined(PSA_HAVE_SOFT_KEY_TYPE_ARIA) || \
+    defined(PSA_HAVE_SOFT_KEY_TYPE_CAMELLIA)
+#define MBEDTLS_PSA_BUILTIN_ALG_GCM 1
+#define MBEDTLS_GCM_C
+#endif
+#endif /* PSA_WANT_ALG_GCM */
+
+#if defined(PSA_WANT_ALG_CHACHA20_POLY1305)
+#if !defined(MBEDTLS_PSA_ACCEL_ALG_CHACHA20_POLY1305)
+#if defined(PSA_WANT_KEY_TYPE_CHACHA20)
+#define MBEDTLS_CHACHAPOLY_C
+#define MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305 1
+#endif /* PSA_WANT_KEY_TYPE_CHACHA20 */
+#endif /* !MBEDTLS_PSA_ACCEL_ALG_CHACHA20_POLY1305 */
+#endif /* PSA_WANT_ALG_CHACHA20_POLY1305 */
+
+#if defined(PSA_WANT_ECC_BRAINPOOL_P_R1_256)
+#if !defined(MBEDTLS_PSA_ACCEL_ECC_BRAINPOOL_P_R1_256)
+#define MBEDTLS_ECP_DP_BP256R1_ENABLED
+#define MBEDTLS_PSA_BUILTIN_ECC_BRAINPOOL_P_R1_256 1
+#endif /* !MBEDTLS_PSA_ACCEL_ECC_BRAINPOOL_P_R1_256 */
+#endif /* PSA_WANT_ECC_BRAINPOOL_P_R1_256 */
+
+#if defined(PSA_WANT_ECC_BRAINPOOL_P_R1_384)
+#if !defined(MBEDTLS_PSA_ACCEL_ECC_BRAINPOOL_P_R1_384)
+#define MBEDTLS_ECP_DP_BP384R1_ENABLED
+#define MBEDTLS_PSA_BUILTIN_ECC_BRAINPOOL_P_R1_384 1
+#endif /* !MBEDTLS_PSA_ACCEL_ECC_BRAINPOOL_P_R1_384 */
+#endif /* PSA_WANT_ECC_BRAINPOOL_P_R1_384 */
+
+#if defined(PSA_WANT_ECC_BRAINPOOL_P_R1_512)
+#if !defined(MBEDTLS_PSA_ACCEL_ECC_BRAINPOOL_P_R1_512)
+#define MBEDTLS_ECP_DP_BP512R1_ENABLED
+#define MBEDTLS_PSA_BUILTIN_ECC_BRAINPOOL_P_R1_512 1
+#endif /* !MBEDTLS_PSA_ACCEL_ECC_BRAINPOOL_P_R1_512 */
+#endif /* PSA_WANT_ECC_BRAINPOOL_P_R1_512 */
+
+#if defined(PSA_WANT_ECC_MONTGOMERY_255)
+#if !defined(MBEDTLS_PSA_ACCEL_ECC_MONTGOMERY_255)
+#define MBEDTLS_ECP_DP_CURVE25519_ENABLED
+#define MBEDTLS_PSA_BUILTIN_ECC_MONTGOMERY_255 1
+#endif /* !MBEDTLS_PSA_ACCEL_ECC_MONTGOMERY_255 */
+#endif /* PSA_WANT_ECC_MONTGOMERY_255 */
+
+#if defined(PSA_WANT_ECC_MONTGOMERY_448)
+#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).
+ */
+#error "Curve448 is not yet supported via the PSA API in Mbed TLS."
+#define MBEDTLS_ECP_DP_CURVE448_ENABLED
+#define MBEDTLS_PSA_BUILTIN_ECC_MONTGOMERY_448 1
+#endif /* !MBEDTLS_PSA_ACCEL_ECC_MONTGOMERY_448 */
+#endif /* PSA_WANT_ECC_MONTGOMERY_448 */
+
+#if defined(PSA_WANT_ECC_SECP_R1_192)
+#if !defined(MBEDTLS_PSA_ACCEL_ECC_SECP_R1_192)
+#define MBEDTLS_ECP_DP_SECP192R1_ENABLED
+#define MBEDTLS_PSA_BUILTIN_ECC_SECP_R1_192 1
+#endif /* !MBEDTLS_PSA_ACCEL_ECC_SECP_R1_192 */
+#endif /* PSA_WANT_ECC_SECP_R1_192 */
+
+#if defined(PSA_WANT_ECC_SECP_R1_224)
+#if !defined(MBEDTLS_PSA_ACCEL_ECC_SECP_R1_224)
+#define MBEDTLS_ECP_DP_SECP224R1_ENABLED
+#define MBEDTLS_PSA_BUILTIN_ECC_SECP_R1_224 1
+#endif /* !MBEDTLS_PSA_ACCEL_ECC_SECP_R1_224 */
+#endif /* PSA_WANT_ECC_SECP_R1_224 */
+
+#if defined(PSA_WANT_ECC_SECP_R1_256)
+#if !defined(MBEDTLS_PSA_ACCEL_ECC_SECP_R1_256)
+#define MBEDTLS_ECP_DP_SECP256R1_ENABLED
+#define MBEDTLS_PSA_BUILTIN_ECC_SECP_R1_256 1
+#endif /* !MBEDTLS_PSA_ACCEL_ECC_SECP_R1_256 */
+#endif /* PSA_WANT_ECC_SECP_R1_256 */
+
+#if defined(PSA_WANT_ECC_SECP_R1_384)
+#if !defined(MBEDTLS_PSA_ACCEL_ECC_SECP_R1_384)
+#define MBEDTLS_ECP_DP_SECP384R1_ENABLED
+#define MBEDTLS_PSA_BUILTIN_ECC_SECP_R1_384 1
+#endif /* !MBEDTLS_PSA_ACCEL_ECC_SECP_R1_384 */
+#endif /* PSA_WANT_ECC_SECP_R1_384 */
+
+#if defined(PSA_WANT_ECC_SECP_R1_521)
+#if !defined(MBEDTLS_PSA_ACCEL_ECC_SECP_R1_521)
+#define MBEDTLS_ECP_DP_SECP521R1_ENABLED
+#define MBEDTLS_PSA_BUILTIN_ECC_SECP_R1_521 1
+#endif /* !MBEDTLS_PSA_ACCEL_ECC_SECP_R1_521 */
+#endif /* PSA_WANT_ECC_SECP_R1_521 */
+
+#if defined(PSA_WANT_ECC_SECP_K1_192)
+#if !defined(MBEDTLS_PSA_ACCEL_ECC_SECP_K1_192)
+#define MBEDTLS_ECP_DP_SECP192K1_ENABLED
+#define MBEDTLS_PSA_BUILTIN_ECC_SECP_K1_192 1
+#endif /* !MBEDTLS_PSA_ACCEL_ECC_SECP_K1_192 */
+#endif /* PSA_WANT_ECC_SECP_K1_192 */
+
+#if defined(PSA_WANT_ECC_SECP_K1_224)
+#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).
+ */
+#error "SECP224K1 is buggy via the PSA API in Mbed TLS."
+#define MBEDTLS_ECP_DP_SECP224K1_ENABLED
+#define MBEDTLS_PSA_BUILTIN_ECC_SECP_K1_224 1
+#endif /* !MBEDTLS_PSA_ACCEL_ECC_SECP_K1_224 */
+#endif /* PSA_WANT_ECC_SECP_K1_224 */
+
+#if defined(PSA_WANT_ECC_SECP_K1_256)
+#if !defined(MBEDTLS_PSA_ACCEL_ECC_SECP_K1_256)
+#define MBEDTLS_ECP_DP_SECP256K1_ENABLED
+#define MBEDTLS_PSA_BUILTIN_ECC_SECP_K1_256 1
+#endif /* !MBEDTLS_PSA_ACCEL_ECC_SECP_K1_256 */
+#endif /* PSA_WANT_ECC_SECP_K1_256 */
+
+
+
+/****************************************************************/
+/* Infer PSA requirements from Mbed TLS capabilities */
+/****************************************************************/
+
 #else /* MBEDTLS_PSA_CRYPTO_CONFIG */
 
 /*
@@ -227,6 +565,16 @@
  * is not defined
  */
 
+#if defined(MBEDTLS_CCM_C)
+#define MBEDTLS_PSA_BUILTIN_ALG_CCM 1
+#define PSA_WANT_ALG_CCM 1
+#endif /* MBEDTLS_CCM_C */
+
+#if defined(MBEDTLS_CMAC_C)
+#define MBEDTLS_PSA_BUILTIN_ALG_CMAC 1
+#define PSA_WANT_ALG_CMAC 1
+#endif /* MBEDTLS_CMAC_C */
+
 #if defined(MBEDTLS_ECDH_C)
 #define MBEDTLS_PSA_BUILTIN_ALG_ECDH 1
 #define PSA_WANT_ALG_ECDH 1
@@ -235,6 +583,7 @@
 #if defined(MBEDTLS_ECDSA_C)
 #define MBEDTLS_PSA_BUILTIN_ALG_ECDSA 1
 #define PSA_WANT_ALG_ECDSA 1
+#define PSA_WANT_ALG_ECDSA_ANY 1
 
 // Only add in DETERMINISTIC support if ECDSA is also enabled
 #if defined(MBEDTLS_ECDSA_DETERMINISTIC)
@@ -251,6 +600,11 @@
 #define PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY 1
 #endif /* MBEDTLS_ECP_C */
 
+#if defined(MBEDTLS_GCM_C)
+#define MBEDTLS_PSA_BUILTIN_ALG_GCM 1
+#define PSA_WANT_ALG_GCM 1
+#endif /* MBEDTLS_GCM_C */
+
 #if defined(MBEDTLS_HKDF_C)
 #define MBEDTLS_PSA_BUILTIN_ALG_HMAC 1
 #define PSA_WANT_ALG_HMAC 1
@@ -261,6 +615,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 MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF 1
 #define PSA_WANT_ALG_TLS12_PRF 1
 #define MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS 1
@@ -293,7 +648,8 @@
 #define PSA_WANT_ALG_RSA_PKCS1V15_CRYPT 1
 #define MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_SIGN 1
 #define PSA_WANT_ALG_RSA_PKCS1V15_SIGN 1
-#endif /* MBEDTLSS_PKCS1_V15 */
+#define PSA_WANT_ALG_RSA_PKCS1V15_SIGN_RAW 1
+#endif /* MBEDTLS_PKCS1_V15 */
 #if defined(MBEDTLS_PKCS1_V21)
 #define MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP 1
 #define PSA_WANT_ALG_RSA_OAEP 1
@@ -314,6 +670,7 @@
 #if defined(MBEDTLS_SHA256_C)
 #define MBEDTLS_PSA_BUILTIN_ALG_SHA_224 1
 #define MBEDTLS_PSA_BUILTIN_ALG_SHA_256 1
+#define PSA_WANT_ALG_SHA_224 1
 #define PSA_WANT_ALG_SHA_256 1
 #endif
 
@@ -326,8 +683,152 @@
 #define PSA_WANT_ALG_SHA_512 1
 #endif
 
+#if defined(MBEDTLS_AES_C)
+#define PSA_WANT_KEY_TYPE_AES 1
+#define MBEDTLS_PSA_BUILTIN_KEY_TYPE_AES 1
+#endif
+
+#if defined(MBEDTLS_ARC4_C)
+#define PSA_WANT_KEY_TYPE_ARC4 1
+#define PSA_WANT_ALG_STREAM_CIPHER 1
+#define MBEDTLS_PSA_BUILTIN_KEY_TYPE_ARC4 1
+#define MBEDTLS_PSA_BUILTIN_ALG_STREAM_CIPHER 1
+#endif
+
+#if defined(MBEDTLS_ARIA_C)
+#define PSA_WANT_KEY_TYPE_ARIA 1
+#define MBEDTLS_PSA_BUILTIN_KEY_TYPE_ARIA 1
+#endif
+
+#if defined(MBEDTLS_CAMELLIA_C)
+#define PSA_WANT_KEY_TYPE_CAMELLIA 1
+#define MBEDTLS_PSA_BUILTIN_KEY_TYPE_CAMELLIA 1
+#endif
+
+#if defined(MBEDTLS_DES_C)
+#define PSA_WANT_KEY_TYPE_DES 1
+#define MBEDTLS_PSA_BUILTIN_KEY_TYPE_DES 1
+#endif
+
+#if defined(MBEDTLS_CHACHA20_C)
+#define PSA_WANT_KEY_TYPE_CHACHA20 1
+#define PSA_WANT_ALG_STREAM_CIPHER 1
+#define MBEDTLS_PSA_BUILTIN_KEY_TYPE_CHACHA20 1
+#define MBEDTLS_PSA_BUILTIN_ALG_STREAM_CIPHER 1
+#if defined(MBEDTLS_CHACHAPOLY_C)
+#define PSA_WANT_ALG_CHACHA20_POLY1305 1
+#define MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305 1
+#endif
+#endif
+
+#if defined(MBEDTLS_CIPHER_MODE_CBC)
+#define MBEDTLS_PSA_BUILTIN_ALG_CBC_NO_PADDING 1
+#define PSA_WANT_ALG_CBC_NO_PADDING 1
+#if defined(MBEDTLS_CIPHER_PADDING_PKCS7)
+#define MBEDTLS_PSA_BUILTIN_ALG_CBC_PKCS7 1
+#define PSA_WANT_ALG_CBC_PKCS7 1
+#endif
+#endif
+
+#if defined(MBEDTLS_AES_C) || defined(MBEDTLS_DES_C) || \
+    defined(MBEDTLS_ARIA_C) || defined(MBEDTLS_CAMELLIA_C)
+#define MBEDTLS_PSA_BUILTIN_ALG_ECB_NO_PADDING 1
+#define PSA_WANT_ALG_ECB_NO_PADDING 1
+#endif
+
+#if defined(MBEDTLS_CIPHER_MODE_CFB)
+#define MBEDTLS_PSA_BUILTIN_ALG_CFB 1
+#define PSA_WANT_ALG_CFB 1
+#endif
+
+#if defined(MBEDTLS_CIPHER_MODE_CTR)
+#define MBEDTLS_PSA_BUILTIN_ALG_CTR 1
+#define PSA_WANT_ALG_CTR 1
+#endif
+
+#if defined(MBEDTLS_CIPHER_MODE_OFB)
+#define MBEDTLS_PSA_BUILTIN_ALG_OFB 1
+#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
+#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
+#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
+#endif
+
+#if defined(MBEDTLS_ECP_DP_CURVE25519_ENABLED)
+#define MBEDTLS_PSA_BUILTIN_ECC_MONTGOMERY_255 1
+#define PSA_WANT_ECC_MONTGOMERY_255
+#endif
+
+/* Curve448 is not yet supported via the PSA API (https://github.com/ARMmbed/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
+#endif
+
+#if defined(MBEDTLS_ECP_DP_SECP192R1_ENABLED)
+#define MBEDTLS_PSA_BUILTIN_ECC_SECP_R1_192 1
+#define PSA_WANT_ECC_SECP_R1_192
+#endif
+
+#if defined(MBEDTLS_ECP_DP_SECP224R1_ENABLED)
+#define MBEDTLS_PSA_BUILTIN_ECC_SECP_R1_224 1
+#define PSA_WANT_ECC_SECP_R1_224
+#endif
+
+#if defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED)
+#define MBEDTLS_PSA_BUILTIN_ECC_SECP_R1_256 1
+#define PSA_WANT_ECC_SECP_R1_256
+#endif
+
+#if defined(MBEDTLS_ECP_DP_SECP384R1_ENABLED)
+#define MBEDTLS_PSA_BUILTIN_ECC_SECP_R1_384 1
+#define PSA_WANT_ECC_SECP_R1_384
+#endif
+
+#if defined(MBEDTLS_ECP_DP_SECP521R1_ENABLED)
+#define MBEDTLS_PSA_BUILTIN_ECC_SECP_R1_521 1
+#define PSA_WANT_ECC_SECP_R1_521
+#endif
+
+#if defined(MBEDTLS_ECP_DP_SECP192K1_ENABLED)
+#define MBEDTLS_PSA_BUILTIN_ECC_SECP_K1_192 1
+#define PSA_WANT_ECC_SECP_K1_192
+#endif
+
+/* SECP224K1 is buggy via the PSA API (https://github.com/ARMmbed/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
+#endif
+
+#if defined(MBEDTLS_ECP_DP_SECP256K1_ENABLED)
+#define MBEDTLS_PSA_BUILTIN_ECC_SECP_K1_256 1
+#define PSA_WANT_ECC_SECP_K1_256
+#endif
+
 #endif /* MBEDTLS_PSA_CRYPTO_CONFIG */
 
+/* These features are always enabled. */
+#define PSA_WANT_KEY_TYPE_DERIVE 1
+#define PSA_WANT_KEY_TYPE_RAW_DATA 1
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/third_party/mbedtls/repo/include/mbedtls/constant_time.h b/third_party/mbedtls/repo/include/mbedtls/constant_time.h
new file mode 100644
index 0000000..c5de57a
--- /dev/null
+++ b/third_party/mbedtls/repo/include/mbedtls/constant_time.h
@@ -0,0 +1,45 @@
+/**
+ *  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.
+ */
+
+#ifndef MBEDTLS_CONSTANT_TIME_H
+#define MBEDTLS_CONSTANT_TIME_H
+
+#include <stddef.h>
+
+
+/** Constant-time buffer comparison without branches.
+ *
+ * This is equivalent to the standard memcmp function, but is likely to be
+ * compiled to code using bitwise operation rather than a branch.
+ *
+ * This function can be used to write constant-time code by replacing branches
+ * with bit operations using masks.
+ *
+ * \param a     Pointer to the first buffer.
+ * \param b     Pointer to the second buffer.
+ * \param n     The number of bytes to compare in the buffer.
+ *
+ * \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 );
+
+#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 7f1d232..dc4adc8 100644
--- a/third_party/mbedtls/repo/include/mbedtls/ctr_drbg.h
+++ b/third_party/mbedtls/repo/include/mbedtls/ctr_drbg.h
@@ -53,10 +53,14 @@
 #include "mbedtls/threading.h"
 #endif
 
-#define MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED        -0x0034  /**< The entropy source failed. */
-#define MBEDTLS_ERR_CTR_DRBG_REQUEST_TOO_BIG              -0x0036  /**< The requested random buffer length is too big. */
-#define MBEDTLS_ERR_CTR_DRBG_INPUT_TOO_BIG                -0x0038  /**< The input (entropy + additional data) is too large. */
-#define MBEDTLS_ERR_CTR_DRBG_FILE_IO_ERROR                -0x003A  /**< Read or write error in file. */
+/** The entropy source failed. */
+#define MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED        -0x0034
+/** The requested random buffer length is too big. */
+#define MBEDTLS_ERR_CTR_DRBG_REQUEST_TOO_BIG              -0x0036
+/** The input (entropy + additional data) is too large. */
+#define MBEDTLS_ERR_CTR_DRBG_INPUT_TOO_BIG                -0x0038
+/** Read or write error in file. */
+#define MBEDTLS_ERR_CTR_DRBG_FILE_IO_ERROR                -0x003A
 
 #define MBEDTLS_CTR_DRBG_BLOCKSIZE          16 /**< The block size used by the cipher. */
 
@@ -200,6 +204,13 @@
     void *p_entropy;            /*!< The context for the entropy function. */
 
 #if defined(MBEDTLS_THREADING_C)
+    /* Invariant: the mutex is initialized if and only if f_entropy != NULL.
+     * This means that the mutex is initialized during the initial seeding
+     * in mbedtls_ctr_drbg_seed() and freed in mbedtls_ctr_drbg_free().
+     *
+     * Note that this invariant may change without notice. Do not rely on it
+     * and do not access the mutex directly in application code.
+     */
     mbedtls_threading_mutex_t mutex;
 #endif
 }
@@ -264,6 +275,15 @@
  *   make a second call to \p f_entropy.
  */
 #endif
+#if defined(MBEDTLS_THREADING_C)
+/**
+ * \note                When Mbed TLS is built with threading support,
+ *                      after this function returns successfully,
+ *                      it is safe to call mbedtls_ctr_drbg_random()
+ *                      from multiple threads. Other operations, including
+ *                      reseeding, are not thread-safe.
+ */
+#endif /* MBEDTLS_THREADING_C */
 /**
  * - The \p custom string.
  *
@@ -290,6 +310,8 @@
  *                      the same context unless you call
  *                      mbedtls_ctr_drbg_free() and mbedtls_ctr_drbg_init()
  *                      again first.
+ *                      After a failed call to mbedtls_ctr_drbg_seed(),
+ *                      you must call mbedtls_ctr_drbg_free().
  * \param f_entropy     The entropy callback, taking as arguments the
  *                      \p p_entropy context, the buffer to fill, and the
  *                      length of the buffer.
@@ -405,6 +427,11 @@
  * \brief               This function reseeds the CTR_DRBG context, that is
  *                      extracts data from the entropy source.
  *
+ * \note                This function is not thread-safe. It is not safe
+ *                      to call this function if another thread might be
+ *                      concurrently obtaining random numbers from the same
+ *                      context or updating or reseeding the same context.
+ *
  * \param ctx           The CTR_DRBG context.
  * \param additional    Additional data to add to the state. Can be \c NULL.
  * \param len           The length of the additional data.
@@ -422,6 +449,11 @@
 /**
  * \brief              This function updates the state of the CTR_DRBG context.
  *
+ * \note                This function is not thread-safe. It is not safe
+ *                      to call this function if another thread might be
+ *                      concurrently obtaining random numbers from the same
+ *                      context or updating or reseeding the same context.
+ *
  * \param ctx          The CTR_DRBG context.
  * \param additional   The data to update the state with. This must not be
  *                     \c NULL unless \p add_len is \c 0.
@@ -445,6 +477,11 @@
  * This function automatically reseeds if the reseed counter is exceeded
  * or prediction resistance is enabled.
  *
+ * \note                This function is not thread-safe. It is not safe
+ *                      to call this function if another thread might be
+ *                      concurrently obtaining random numbers from the same
+ *                      context or updating or reseeding the same context.
+ *
  * \param p_rng         The CTR_DRBG context. This must be a pointer to a
  *                      #mbedtls_ctr_drbg_context structure.
  * \param output        The buffer to fill.
@@ -473,8 +510,16 @@
  *
  * This function automatically reseeds if the reseed counter is exceeded
  * or prediction resistance is enabled.
- *
- *
+ */
+#if defined(MBEDTLS_THREADING_C)
+/**
+ * \note                When Mbed TLS is built with threading support,
+ *                      it is safe to call mbedtls_ctr_drbg_random()
+ *                      from multiple threads. Other operations, including
+ *                      reseeding, are not thread-safe.
+ */
+#endif /* MBEDTLS_THREADING_C */
+/**
  * \param p_rng         The CTR_DRBG context. This must be a pointer to a
  *                      #mbedtls_ctr_drbg_context structure.
  * \param output        The buffer to fill.
diff --git a/third_party/mbedtls/repo/include/mbedtls/debug.h b/third_party/mbedtls/repo/include/mbedtls/debug.h
index ab5b037..3c08244 100644
--- a/third_party/mbedtls/repo/include/mbedtls/debug.h
+++ b/third_party/mbedtls/repo/include/mbedtls/debug.h
@@ -80,6 +80,55 @@
 
 #endif /* MBEDTLS_DEBUG_C */
 
+/**
+ * \def MBEDTLS_PRINTF_ATTRIBUTE
+ *
+ * Mark a function as having printf attributes, and thus enable checking
+ * via -wFormat and other flags. This does nothing on builds with compilers
+ * that do not support the format attribute
+ *
+ * Module:  library/debug.c
+ * Caller:
+ *
+ * This module provides debugging functions.
+ */
+#if defined(__has_attribute)
+#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)))
+#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)))
+#endif
+#else /* __has_attribute(format) */
+#define MBEDTLS_PRINTF_ATTRIBUTE(string_index, first_to_check)
+#endif /* __has_attribute(format) */
+#else /* defined(__has_attribute) */
+#define MBEDTLS_PRINTF_ATTRIBUTE(string_index, first_to_check)
+#endif
+
+/**
+ * \def MBEDTLS_PRINTF_SIZET
+ *
+ * MBEDTLS_PRINTF_xxx: Due to issues with older window compilers
+ * and MinGW we need to define the printf specifier for size_t
+ * and long long per platform.
+ *
+ * Module:  library/debug.c
+ * Caller:
+ *
+ * This module provides debugging functions.
+ */
+#if (defined(__MINGW32__) && __USE_MINGW_ANSI_STDIO == 0) || (defined(_MSC_VER) && _MSC_VER < 1800)
+   #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) */
+   #define MBEDTLS_PRINTF_SIZET     "zu"
+   #define MBEDTLS_PRINTF_LONGLONG  "lld"
+#endif /* (defined(__MINGW32__)  && __USE_MINGW_ANSI_STDIO == 0) || (defined(_MSC_VER) && _MSC_VER < 1800) */
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -118,7 +167,7 @@
  */
 void mbedtls_debug_print_msg( const mbedtls_ssl_context *ssl, int level,
                               const char *file, int line,
-                              const char *format, ... );
+                              const char *format, ... ) MBEDTLS_PRINTF_ATTRIBUTE(5, 6);
 
 /**
  * \brief   Print the return value of a function to the debug output. This
diff --git a/third_party/mbedtls/repo/include/mbedtls/des.h b/third_party/mbedtls/repo/include/mbedtls/des.h
index 549d19b..325aab5 100644
--- a/third_party/mbedtls/repo/include/mbedtls/des.h
+++ b/third_party/mbedtls/repo/include/mbedtls/des.h
@@ -32,6 +32,7 @@
 #else
 #include MBEDTLS_CONFIG_FILE
 #endif
+#include "mbedtls/platform_util.h"
 
 #include <stddef.h>
 #include <stdint.h>
@@ -39,10 +40,12 @@
 #define MBEDTLS_DES_ENCRYPT     1
 #define MBEDTLS_DES_DECRYPT     0
 
-#define MBEDTLS_ERR_DES_INVALID_INPUT_LENGTH              -0x0032  /**< The data input has an invalid length. */
+/** The data input has an invalid length. */
+#define MBEDTLS_ERR_DES_INVALID_INPUT_LENGTH              -0x0032
 
 /* MBEDTLS_ERR_DES_HW_ACCEL_FAILED is deprecated and should not be used. */
-#define MBEDTLS_ERR_DES_HW_ACCEL_FAILED                   -0x0033  /**< DES hardware accelerator failed. */
+/** DES hardware accelerator failed. */
+#define MBEDTLS_ERR_DES_HW_ACCEL_FAILED                   -0x0033
 
 #define MBEDTLS_DES_KEY_SIZE    8
 
@@ -144,6 +147,7 @@
  *                 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] );
 
 /**
@@ -157,6 +161,7 @@
  *                 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] );
 
 /**
@@ -171,6 +176,7 @@
  *                 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] );
 
 /**
@@ -185,6 +191,7 @@
  *                 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] );
 
 /**
@@ -195,6 +202,7 @@
  *
  * \return         0
  */
+MBEDTLS_CHECK_RETURN_TYPICAL
 int mbedtls_des3_set2key_enc( mbedtls_des3_context *ctx,
                       const unsigned char key[MBEDTLS_DES_KEY_SIZE * 2] );
 
@@ -206,6 +214,7 @@
  *
  * \return         0
  */
+MBEDTLS_CHECK_RETURN_TYPICAL
 int mbedtls_des3_set2key_dec( mbedtls_des3_context *ctx,
                       const unsigned char key[MBEDTLS_DES_KEY_SIZE * 2] );
 
@@ -217,6 +226,7 @@
  *
  * \return         0
  */
+MBEDTLS_CHECK_RETURN_TYPICAL
 int mbedtls_des3_set3key_enc( mbedtls_des3_context *ctx,
                       const unsigned char key[MBEDTLS_DES_KEY_SIZE * 3] );
 
@@ -228,6 +238,7 @@
  *
  * \return         0
  */
+MBEDTLS_CHECK_RETURN_TYPICAL
 int mbedtls_des3_set3key_dec( mbedtls_des3_context *ctx,
                       const unsigned char key[MBEDTLS_DES_KEY_SIZE * 3] );
 
@@ -244,6 +255,7 @@
  *                 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] );
@@ -271,6 +283,7 @@
  *                 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,
@@ -288,6 +301,7 @@
  *
  * \return         0 if successful
  */
+MBEDTLS_CHECK_RETURN_TYPICAL
 int mbedtls_des3_crypt_ecb( mbedtls_des3_context *ctx,
                      const unsigned char input[8],
                      unsigned char output[8] );
@@ -313,6 +327,7 @@
  *
  * \return         0 if successful, or MBEDTLS_ERR_DES_INVALID_INPUT_LENGTH
  */
+MBEDTLS_CHECK_RETURN_TYPICAL
 int mbedtls_des3_crypt_cbc( mbedtls_des3_context *ctx,
                      int mode,
                      size_t length,
@@ -343,6 +358,7 @@
  *
  * \return         0 if successful, or 1 if the test failed
  */
+MBEDTLS_CHECK_RETURN_CRITICAL
 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 c7830b9..c4b15a2 100644
--- a/third_party/mbedtls/repo/include/mbedtls/dhm.h
+++ b/third_party/mbedtls/repo/include/mbedtls/dhm.h
@@ -73,20 +73,31 @@
 /*
  * DHM Error codes
  */
-#define MBEDTLS_ERR_DHM_BAD_INPUT_DATA                    -0x3080  /**< Bad input parameters. */
-#define MBEDTLS_ERR_DHM_READ_PARAMS_FAILED                -0x3100  /**< Reading of the DHM parameters failed. */
-#define MBEDTLS_ERR_DHM_MAKE_PARAMS_FAILED                -0x3180  /**< Making of the DHM parameters failed. */
-#define MBEDTLS_ERR_DHM_READ_PUBLIC_FAILED                -0x3200  /**< Reading of the public values failed. */
-#define MBEDTLS_ERR_DHM_MAKE_PUBLIC_FAILED                -0x3280  /**< Making of the public value failed. */
-#define MBEDTLS_ERR_DHM_CALC_SECRET_FAILED                -0x3300  /**< Calculation of the DHM secret failed. */
-#define MBEDTLS_ERR_DHM_INVALID_FORMAT                    -0x3380  /**< The ASN.1 data is not formatted correctly. */
-#define MBEDTLS_ERR_DHM_ALLOC_FAILED                      -0x3400  /**< Allocation of memory failed. */
-#define MBEDTLS_ERR_DHM_FILE_IO_ERROR                     -0x3480  /**< Read or write of file failed. */
+/** Bad input parameters. */
+#define MBEDTLS_ERR_DHM_BAD_INPUT_DATA                    -0x3080
+/** Reading of the DHM parameters failed. */
+#define MBEDTLS_ERR_DHM_READ_PARAMS_FAILED                -0x3100
+/** Making of the DHM parameters failed. */
+#define MBEDTLS_ERR_DHM_MAKE_PARAMS_FAILED                -0x3180
+/** Reading of the public values failed. */
+#define MBEDTLS_ERR_DHM_READ_PUBLIC_FAILED                -0x3200
+/** Making of the public value failed. */
+#define MBEDTLS_ERR_DHM_MAKE_PUBLIC_FAILED                -0x3280
+/** Calculation of the DHM secret failed. */
+#define MBEDTLS_ERR_DHM_CALC_SECRET_FAILED                -0x3300
+/** The ASN.1 data is not formatted correctly. */
+#define MBEDTLS_ERR_DHM_INVALID_FORMAT                    -0x3380
+/** Allocation of memory failed. */
+#define MBEDTLS_ERR_DHM_ALLOC_FAILED                      -0x3400
+/** Read or write of file failed. */
+#define MBEDTLS_ERR_DHM_FILE_IO_ERROR                     -0x3480
 
 /* MBEDTLS_ERR_DHM_HW_ACCEL_FAILED is deprecated and should not be used. */
-#define MBEDTLS_ERR_DHM_HW_ACCEL_FAILED                   -0x3500  /**< DHM hardware accelerator failed. */
+/** DHM hardware accelerator failed. */
+#define MBEDTLS_ERR_DHM_HW_ACCEL_FAILED                   -0x3500
 
-#define MBEDTLS_ERR_DHM_SET_GROUP_FAILED                  -0x3580  /**< Setting the modulus and generator failed. */
+/** Setting the modulus and generator failed. */
+#define MBEDTLS_ERR_DHM_SET_GROUP_FAILED                  -0x3580
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/third_party/mbedtls/repo/include/mbedtls/ecp.h b/third_party/mbedtls/repo/include/mbedtls/ecp.h
index 34dd0ea..0924341 100644
--- a/third_party/mbedtls/repo/include/mbedtls/ecp.h
+++ b/third_party/mbedtls/repo/include/mbedtls/ecp.h
@@ -45,19 +45,29 @@
 /*
  * ECP error codes
  */
-#define MBEDTLS_ERR_ECP_BAD_INPUT_DATA                    -0x4F80  /**< Bad input parameters to function. */
-#define MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL                  -0x4F00  /**< The buffer is too small to write to. */
-#define MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE               -0x4E80  /**< The requested feature is not available, for example, the requested curve is not supported. */
-#define MBEDTLS_ERR_ECP_VERIFY_FAILED                     -0x4E00  /**< The signature is not valid. */
-#define MBEDTLS_ERR_ECP_ALLOC_FAILED                      -0x4D80  /**< Memory allocation failed. */
-#define MBEDTLS_ERR_ECP_RANDOM_FAILED                     -0x4D00  /**< Generation of random value, such as ephemeral key, failed. */
-#define MBEDTLS_ERR_ECP_INVALID_KEY                       -0x4C80  /**< Invalid private or public key. */
-#define MBEDTLS_ERR_ECP_SIG_LEN_MISMATCH                  -0x4C00  /**< The buffer contains a valid signature followed by more data. */
+/** Bad input parameters to function. */
+#define MBEDTLS_ERR_ECP_BAD_INPUT_DATA                    -0x4F80
+/** The buffer is too small to write to. */
+#define MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL                  -0x4F00
+/** The requested feature is not available, for example, the requested curve is not supported. */
+#define MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE               -0x4E80
+/** The signature is not valid. */
+#define MBEDTLS_ERR_ECP_VERIFY_FAILED                     -0x4E00
+/** Memory allocation failed. */
+#define MBEDTLS_ERR_ECP_ALLOC_FAILED                      -0x4D80
+/** Generation of random value, such as ephemeral key, failed. */
+#define MBEDTLS_ERR_ECP_RANDOM_FAILED                     -0x4D00
+/** Invalid private or public key. */
+#define MBEDTLS_ERR_ECP_INVALID_KEY                       -0x4C80
+/** The buffer contains a valid signature followed by more data. */
+#define MBEDTLS_ERR_ECP_SIG_LEN_MISMATCH                  -0x4C00
 
 /* MBEDTLS_ERR_ECP_HW_ACCEL_FAILED is deprecated and should not be used. */
-#define MBEDTLS_ERR_ECP_HW_ACCEL_FAILED                   -0x4B80  /**< The ECP hardware accelerator failed. */
+/** The ECP hardware accelerator failed. */
+#define MBEDTLS_ERR_ECP_HW_ACCEL_FAILED                   -0x4B80
 
-#define MBEDTLS_ERR_ECP_IN_PROGRESS                       -0x4B00  /**< Operation in progress, call again with the same parameters to continue. */
+/** Operation in progress, call again with the same parameters to continue. */
+#define MBEDTLS_ERR_ECP_IN_PROGRESS                       -0x4B00
 
 /* Flags indicating whether to include code that is specific to certain
  * types of curves. These flags are for internal library use only. */
@@ -96,6 +106,7 @@
  * - Add it at the end of this enum, otherwise you'll break the ABI by
  *   changing the numerical value for existing curves.
  * - Increment MBEDTLS_ECP_DP_MAX below if needed.
+ * - Update the calculation of MBEDTLS_ECP_MAX_BITS_MIN below.
  * - Add the corresponding MBEDTLS_ECP_DP_xxx_ENABLED macro definition to
  *   config.h.
  * - List the curve as a dependency of MBEDTLS_ECP_C and
@@ -171,6 +182,40 @@
 }
 mbedtls_ecp_point;
 
+/* Determine the minimum safe value of MBEDTLS_ECP_MAX_BITS. */
+#if !defined(MBEDTLS_ECP_C)
+#define MBEDTLS_ECP_MAX_BITS_MIN 0
+/* Note: the curves must be listed in DECREASING size! */
+#elif defined(MBEDTLS_ECP_DP_SECP521R1_ENABLED)
+#define MBEDTLS_ECP_MAX_BITS_MIN 521
+#elif defined(MBEDTLS_ECP_DP_BP512R1_ENABLED)
+#define MBEDTLS_ECP_MAX_BITS_MIN 512
+#elif defined(MBEDTLS_ECP_DP_CURVE448_ENABLED)
+#define MBEDTLS_ECP_MAX_BITS_MIN 448
+#elif defined(MBEDTLS_ECP_DP_BP384R1_ENABLED)
+#define MBEDTLS_ECP_MAX_BITS_MIN 384
+#elif defined(MBEDTLS_ECP_DP_SECP384R1_ENABLED)
+#define MBEDTLS_ECP_MAX_BITS_MIN 384
+#elif defined(MBEDTLS_ECP_DP_BP256R1_ENABLED)
+#define MBEDTLS_ECP_MAX_BITS_MIN 256
+#elif defined(MBEDTLS_ECP_DP_SECP256K1_ENABLED)
+#define MBEDTLS_ECP_MAX_BITS_MIN 256
+#elif defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED)
+#define MBEDTLS_ECP_MAX_BITS_MIN 256
+#elif defined(MBEDTLS_ECP_DP_CURVE25519_ENABLED)
+#define MBEDTLS_ECP_MAX_BITS_MIN 255
+#elif defined(MBEDTLS_ECP_DP_SECP224K1_ENABLED)
+#define MBEDTLS_ECP_MAX_BITS_MIN 225 // n is slightly above 2^224
+#elif defined(MBEDTLS_ECP_DP_SECP224R1_ENABLED)
+#define MBEDTLS_ECP_MAX_BITS_MIN 224
+#elif defined(MBEDTLS_ECP_DP_SECP192K1_ENABLED)
+#define MBEDTLS_ECP_MAX_BITS_MIN 192
+#elif defined(MBEDTLS_ECP_DP_SECP192R1_ENABLED)
+#define MBEDTLS_ECP_MAX_BITS_MIN 192
+#else
+#error "MBEDTLS_ECP_C enabled, but no curve?"
+#endif
+
 #if !defined(MBEDTLS_ECP_ALT)
 /*
  * default mbed TLS elliptic curve arithmetic implementation
@@ -245,11 +290,23 @@
  * \{
  */
 
-#if !defined(MBEDTLS_ECP_MAX_BITS)
+#if defined(MBEDTLS_ECP_MAX_BITS)
+
+#if MBEDTLS_ECP_MAX_BITS < MBEDTLS_ECP_MAX_BITS_MIN
+#error "MBEDTLS_ECP_MAX_BITS is smaller than the largest supported curve"
+#endif
+
+#elif defined(MBEDTLS_ECP_C)
 /**
  * The maximum size of the groups, that is, of \c N and \c P.
  */
-#define MBEDTLS_ECP_MAX_BITS     521   /**< The maximum size of groups, in bits. */
+#define MBEDTLS_ECP_MAX_BITS     MBEDTLS_ECP_MAX_BITS_MIN
+
+#else
+/* MBEDTLS_ECP_MAX_BITS is not relevant without MBEDTLS_ECP_C, but set it
+ * to a nonzero value so that code that unconditionally allocates an array
+ * of a size based on it keeps working if built without ECC support. */
+#define MBEDTLS_ECP_MAX_BITS 1
 #endif
 
 #define MBEDTLS_ECP_MAX_BYTES    ( ( MBEDTLS_ECP_MAX_BITS + 7 ) / 8 )
@@ -258,7 +315,8 @@
 #if !defined(MBEDTLS_ECP_WINDOW_SIZE)
 /*
  * Maximum "window" size used for point multiplication.
- * Default: 6.
+ * Default: a point where higher memory usage yields disminishing performance
+ *          returns.
  * Minimum value: 2. Maximum value: 7.
  *
  * Result is an array of at most ( 1 << ( MBEDTLS_ECP_WINDOW_SIZE - 1 ) )
@@ -275,7 +333,7 @@
  *      224       475     475     453     398     342
  *      192       640     640     633     587     476
  */
-#define MBEDTLS_ECP_WINDOW_SIZE    6   /**< The maximum window size used. */
+#define MBEDTLS_ECP_WINDOW_SIZE    4   /**< The maximum window size used. */
 #endif /* MBEDTLS_ECP_WINDOW_SIZE */
 
 #if !defined(MBEDTLS_ECP_FIXED_POINT_OPTIM)
@@ -466,8 +524,7 @@
 
 /**
  * \brief           This function retrieves the information defined in
- *                  mbedtls_ecp_curve_info() for all supported curves in order
- *                  of preference.
+ *                  mbedtls_ecp_curve_info() for all supported curves.
  *
  * \note            This function returns information about all curves
  *                  supported by the library. Some curves may not be
@@ -1180,7 +1237,7 @@
  *
  * \param grp_id    The ECP group identifier.
  * \param key       The destination key.
- * \param buf       The the buffer containing the binary representation of the
+ * \param buf       The buffer containing the binary representation of the
  *                  key. (Big endian integer for Weierstrass curves, byte
  *                  string for Montgomery curves.)
  * \param buflen    The length of the buffer in bytes.
diff --git a/third_party/mbedtls/repo/include/mbedtls/entropy.h b/third_party/mbedtls/repo/include/mbedtls/entropy.h
index 5a9c11c..deb3c50 100644
--- a/third_party/mbedtls/repo/include/mbedtls/entropy.h
+++ b/third_party/mbedtls/repo/include/mbedtls/entropy.h
@@ -48,11 +48,16 @@
 #include "mbedtls/havege.h"
 #endif
 
-#define MBEDTLS_ERR_ENTROPY_SOURCE_FAILED                 -0x003C  /**< Critical entropy source failure. */
-#define MBEDTLS_ERR_ENTROPY_MAX_SOURCES                   -0x003E  /**< No more sources can be added. */
-#define MBEDTLS_ERR_ENTROPY_NO_SOURCES_DEFINED            -0x0040  /**< No sources have been added to poll. */
-#define MBEDTLS_ERR_ENTROPY_NO_STRONG_SOURCE              -0x003D  /**< No strong sources have been added to poll. */
-#define MBEDTLS_ERR_ENTROPY_FILE_IO_ERROR                 -0x003F  /**< Read/write error in file. */
+/** Critical entropy source failure. */
+#define MBEDTLS_ERR_ENTROPY_SOURCE_FAILED                 -0x003C
+/** No more sources can be added. */
+#define MBEDTLS_ERR_ENTROPY_MAX_SOURCES                   -0x003E
+/** No sources have been added to poll. */
+#define MBEDTLS_ERR_ENTROPY_NO_SOURCES_DEFINED            -0x0040
+/** No strong sources have been added to poll. */
+#define MBEDTLS_ERR_ENTROPY_NO_STRONG_SOURCE              -0x003D
+/** Read/write error in file. */
+#define MBEDTLS_ERR_ENTROPY_FILE_IO_ERROR                 -0x003F
 
 /**
  * \name SECTION: Module settings
@@ -120,13 +125,15 @@
  */
 typedef struct mbedtls_entropy_context
 {
-    int accumulator_started;
+    int accumulator_started; /* 0 after init.
+                              * 1 after the first update.
+                              * -1 after free. */
 #if defined(MBEDTLS_ENTROPY_SHA512_ACCUMULATOR)
     mbedtls_sha512_context  accumulator;
-#else
+#elif defined(MBEDTLS_ENTROPY_SHA256_ACCUMULATOR)
     mbedtls_sha256_context  accumulator;
 #endif
-    int             source_count;
+    int             source_count; /* Number of entries used in source. */
     mbedtls_entropy_source_state    source[MBEDTLS_ENTROPY_MAX_SOURCES];
 #if defined(MBEDTLS_HAVEGE_C)
     mbedtls_havege_state    havege_data;
diff --git a/third_party/mbedtls/repo/include/mbedtls/error.h b/third_party/mbedtls/repo/include/mbedtls/error.h
index cd7731e..50f2538 100644
--- a/third_party/mbedtls/repo/include/mbedtls/error.h
+++ b/third_party/mbedtls/repo/include/mbedtls/error.h
@@ -30,6 +30,11 @@
 
 #include <stddef.h>
 
+#if ( defined(__ARMCC_VERSION) || defined(_MSC_VER) ) && \
+    !defined(inline) && !defined(__cplusplus)
+#define inline __inline
+#endif
+
 /**
  * Error code layout.
  *
@@ -111,8 +116,58 @@
 extern "C" {
 #endif
 
-#define MBEDTLS_ERR_ERROR_GENERIC_ERROR       -0x0001  /**< Generic error */
-#define MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED -0x006E  /**< This is a bug in the library */
+/** Generic error */
+#define MBEDTLS_ERR_ERROR_GENERIC_ERROR       -0x0001
+/** This is a bug in the library */
+#define MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED -0x006E
+
+/**
+ * \brief Combines a high-level and low-level error code together.
+ *
+ *        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__ )
+
+#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 );
+#endif
+
+/**
+ * \brief Combines a high-level and low-level error code together.
+ *
+ *        This function can be called directly however it is usually
+ *        called via the #MBEDTLS_ERROR_ADD macro.
+ *
+ *        While a value of zero is not a negative error code, it is still an
+ *        error code (that denotes success) and can be combined with both a
+ *        negative error code or another value of zero.
+ *
+ * \note  When invasive testing is enabled via #MBEDTLS_TEST_HOOKS, also try to
+ *        call \link mbedtls_test_hook_error_add \endlink.
+ *
+ * \param high      high-level error code. See error.h for more details.
+ * \param low       low-level error code. See error.h for more details.
+ * \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 )
+{
+#if defined(MBEDTLS_TEST_HOOKS)
+    if( *mbedtls_test_hook_error_add != NULL )
+        ( *mbedtls_test_hook_error_add )( high, low, file, line );
+#endif
+    (void)file;
+    (void)line;
+
+    return( high + low );
+}
 
 /**
  * \brief Translate a mbed TLS error code into a string representation,
diff --git a/third_party/mbedtls/repo/include/mbedtls/gcm.h b/third_party/mbedtls/repo/include/mbedtls/gcm.h
index 6b67361..9723a17 100644
--- a/third_party/mbedtls/repo/include/mbedtls/gcm.h
+++ b/third_party/mbedtls/repo/include/mbedtls/gcm.h
@@ -44,12 +44,15 @@
 #define MBEDTLS_GCM_ENCRYPT     1
 #define MBEDTLS_GCM_DECRYPT     0
 
-#define MBEDTLS_ERR_GCM_AUTH_FAILED                       -0x0012  /**< Authenticated decryption failed. */
+/** Authenticated decryption failed. */
+#define MBEDTLS_ERR_GCM_AUTH_FAILED                       -0x0012
 
 /* MBEDTLS_ERR_GCM_HW_ACCEL_FAILED is deprecated and should not be used. */
-#define MBEDTLS_ERR_GCM_HW_ACCEL_FAILED                   -0x0013  /**< GCM hardware accelerator failed. */
+/** GCM hardware accelerator failed. */
+#define MBEDTLS_ERR_GCM_HW_ACCEL_FAILED                   -0x0013
 
-#define MBEDTLS_ERR_GCM_BAD_INPUT                         -0x0014  /**< Bad input parameters to function. */
+/** Bad input parameters to function. */
+#define MBEDTLS_ERR_GCM_BAD_INPUT                         -0x0014
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/third_party/mbedtls/repo/include/mbedtls/hkdf.h b/third_party/mbedtls/repo/include/mbedtls/hkdf.h
index 2e6b363..223004b 100644
--- a/third_party/mbedtls/repo/include/mbedtls/hkdf.h
+++ b/third_party/mbedtls/repo/include/mbedtls/hkdf.h
@@ -37,7 +37,8 @@
  *  \name HKDF Error codes
  *  \{
  */
-#define MBEDTLS_ERR_HKDF_BAD_INPUT_DATA  -0x5F80  /**< Bad input parameters to function. */
+/** Bad input parameters to function. */
+#define MBEDTLS_ERR_HKDF_BAD_INPUT_DATA  -0x5F80
 /* \} name */
 
 #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 9116541..79132d4 100644
--- a/third_party/mbedtls/repo/include/mbedtls/hmac_drbg.h
+++ b/third_party/mbedtls/repo/include/mbedtls/hmac_drbg.h
@@ -41,10 +41,14 @@
 /*
  * Error codes
  */
-#define MBEDTLS_ERR_HMAC_DRBG_REQUEST_TOO_BIG              -0x0003  /**< Too many random requested in single call. */
-#define MBEDTLS_ERR_HMAC_DRBG_INPUT_TOO_BIG                -0x0005  /**< Input too large (Entropy + additional). */
-#define MBEDTLS_ERR_HMAC_DRBG_FILE_IO_ERROR                -0x0007  /**< Read/write error in file. */
-#define MBEDTLS_ERR_HMAC_DRBG_ENTROPY_SOURCE_FAILED        -0x0009  /**< The entropy source failed. */
+/** Too many random requested in single call. */
+#define MBEDTLS_ERR_HMAC_DRBG_REQUEST_TOO_BIG              -0x0003
+/** Input too large (Entropy + additional). */
+#define MBEDTLS_ERR_HMAC_DRBG_INPUT_TOO_BIG                -0x0005
+/** Read/write error in file. */
+#define MBEDTLS_ERR_HMAC_DRBG_FILE_IO_ERROR                -0x0007
+/** The entropy source failed. */
+#define MBEDTLS_ERR_HMAC_DRBG_ENTROPY_SOURCE_FAILED        -0x0009
 
 /**
  * \name SECTION: Module settings
@@ -101,6 +105,14 @@
     void *p_entropy;            /*!< context for the entropy function        */
 
 #if defined(MBEDTLS_THREADING_C)
+    /* Invariant: the mutex is initialized if and only if
+     * md_ctx->md_info != NULL. This means that the mutex is initialized
+     * during the initial seeding in mbedtls_hmac_drbg_seed() or
+     * mbedtls_hmac_drbg_seed_buf() and freed in mbedtls_ctr_drbg_free().
+     *
+     * Note that this invariant may change without notice. Do not rely on it
+     * and do not access the mutex directly in application code.
+     */
     mbedtls_threading_mutex_t mutex;
 #endif
 } mbedtls_hmac_drbg_context;
@@ -150,7 +162,17 @@
  * \note                During the initial seeding, this function calls
  *                      the entropy source to obtain a nonce
  *                      whose length is half the entropy length.
- *
+ */
+#if defined(MBEDTLS_THREADING_C)
+/**
+ * \note                When Mbed TLS is built with threading support,
+ *                      after this function returns successfully,
+ *                      it is safe to call mbedtls_hmac_drbg_random()
+ *                      from multiple threads. Other operations, including
+ *                      reseeding, are not thread-safe.
+ */
+#endif /* MBEDTLS_THREADING_C */
+/**
  * \param ctx           HMAC_DRBG context to be seeded.
  * \param md_info       MD algorithm to use for HMAC_DRBG.
  * \param f_entropy     The entropy callback, taking as arguments the
@@ -189,7 +211,17 @@
  *
  * This function is meant for use in algorithms that need a pseudorandom
  * input such as deterministic ECDSA.
- *
+ */
+#if defined(MBEDTLS_THREADING_C)
+/**
+ * \note                When Mbed TLS is built with threading support,
+ *                      after this function returns successfully,
+ *                      it is safe to call mbedtls_hmac_drbg_random()
+ *                      from multiple threads. Other operations, including
+ *                      reseeding, are not thread-safe.
+ */
+#endif /* MBEDTLS_THREADING_C */
+/**
  * \param ctx           HMAC_DRBG context to be initialised.
  * \param md_info       MD algorithm to use for HMAC_DRBG.
  * \param data          Concatenation of the initial entropy string and
@@ -252,6 +284,11 @@
 /**
  * \brief               This function updates the state of the HMAC_DRBG context.
  *
+ * \note                This function is not thread-safe. It is not safe
+ *                      to call this function if another thread might be
+ *                      concurrently obtaining random numbers from the same
+ *                      context or updating or reseeding the same context.
+ *
  * \param ctx           The HMAC_DRBG context.
  * \param additional    The data to update the state with.
  *                      If this is \c NULL, there is no additional data.
@@ -268,6 +305,11 @@
  * \brief               This function reseeds the HMAC_DRBG context, that is
  *                      extracts data from the entropy source.
  *
+ * \note                This function is not thread-safe. It is not safe
+ *                      to call this function if another thread might be
+ *                      concurrently obtaining random numbers from the same
+ *                      context or updating or reseeding the same context.
+ *
  * \param ctx           The HMAC_DRBG context.
  * \param additional    Additional data to add to the state.
  *                      If this is \c NULL, there is no additional data
@@ -293,6 +335,11 @@
  * This function automatically reseeds if the reseed counter is exceeded
  * or prediction resistance is enabled.
  *
+ * \note                This function is not thread-safe. It is not safe
+ *                      to call this function if another thread might be
+ *                      concurrently obtaining random numbers from the same
+ *                      context or updating or reseeding the same context.
+ *
  * \param p_rng         The HMAC_DRBG context. This must be a pointer to a
  *                      #mbedtls_hmac_drbg_context structure.
  * \param output        The buffer to fill.
@@ -322,7 +369,16 @@
  *
  * This function automatically reseeds if the reseed counter is exceeded
  * or prediction resistance is enabled.
- *
+ */
+#if defined(MBEDTLS_THREADING_C)
+/**
+ * \note                When Mbed TLS is built with threading support,
+ *                      it is safe to call mbedtls_ctr_drbg_random()
+ *                      from multiple threads. Other operations, including
+ *                      reseeding, are not thread-safe.
+ */
+#endif /* MBEDTLS_THREADING_C */
+/**
  * \param p_rng         The HMAC_DRBG context. This must be a pointer to a
  *                      #mbedtls_hmac_drbg_context structure.
  * \param output        The buffer to fill.
diff --git a/third_party/mbedtls/repo/include/mbedtls/md.h b/third_party/mbedtls/repo/include/mbedtls/md.h
index e4354ba..84fafd2 100644
--- a/third_party/mbedtls/repo/include/mbedtls/md.h
+++ b/third_party/mbedtls/repo/include/mbedtls/md.h
@@ -32,14 +32,20 @@
 #else
 #include MBEDTLS_CONFIG_FILE
 #endif
+#include "mbedtls/platform_util.h"
 
-#define MBEDTLS_ERR_MD_FEATURE_UNAVAILABLE                -0x5080  /**< The selected feature is not available. */
-#define MBEDTLS_ERR_MD_BAD_INPUT_DATA                     -0x5100  /**< Bad input parameters to function. */
-#define MBEDTLS_ERR_MD_ALLOC_FAILED                       -0x5180  /**< Failed to allocate memory. */
-#define MBEDTLS_ERR_MD_FILE_IO_ERROR                      -0x5200  /**< Opening or reading of file failed. */
+/** The selected feature is not available. */
+#define MBEDTLS_ERR_MD_FEATURE_UNAVAILABLE                -0x5080
+/** Bad input parameters to function. */
+#define MBEDTLS_ERR_MD_BAD_INPUT_DATA                     -0x5100
+/** Failed to allocate memory. */
+#define MBEDTLS_ERR_MD_ALLOC_FAILED                       -0x5180
+/** Opening or reading of file failed. */
+#define MBEDTLS_ERR_MD_FILE_IO_ERROR                      -0x5200
 
 /* MBEDTLS_ERR_MD_HW_ACCEL_FAILED is deprecated and should not be used. */
-#define MBEDTLS_ERR_MD_HW_ACCEL_FAILED                    -0x5280  /**< MD hardware accelerator failed. */
+/** MD hardware accelerator failed. */
+#define MBEDTLS_ERR_MD_HW_ACCEL_FAILED                    -0x5280
 
 #ifdef __cplusplus
 extern "C" {
@@ -205,6 +211,7 @@
  *                  failure.
  * \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 );
 
 /**
@@ -226,6 +233,7 @@
  * \return          \c 0 on success.
  * \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 );
 
@@ -275,6 +283,7 @@
  * \return          #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification
  *                  failure.
  */
+MBEDTLS_CHECK_RETURN_TYPICAL
 int mbedtls_md_starts( mbedtls_md_context_t *ctx );
 
 /**
@@ -293,6 +302,7 @@
  * \return          #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification
  *                  failure.
  */
+MBEDTLS_CHECK_RETURN_TYPICAL
 int mbedtls_md_update( mbedtls_md_context_t *ctx, const unsigned char *input, size_t ilen );
 
 /**
@@ -313,6 +323,7 @@
  * \return          #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification
  *                  failure.
  */
+MBEDTLS_CHECK_RETURN_TYPICAL
 int mbedtls_md_finish( mbedtls_md_context_t *ctx, unsigned char *output );
 
 /**
@@ -333,6 +344,7 @@
  * \return         #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification
  *                 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 );
 
@@ -354,6 +366,7 @@
  *                 the file pointed by \p path.
  * \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 );
 #endif /* MBEDTLS_FS_IO */
@@ -376,6 +389,7 @@
  * \return          #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification
  *                  failure.
  */
+MBEDTLS_CHECK_RETURN_TYPICAL
 int mbedtls_md_hmac_starts( mbedtls_md_context_t *ctx, const unsigned char *key,
                     size_t keylen );
 
@@ -398,6 +412,7 @@
  * \return          #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification
  *                  failure.
  */
+MBEDTLS_CHECK_RETURN_TYPICAL
 int mbedtls_md_hmac_update( mbedtls_md_context_t *ctx, const unsigned char *input,
                     size_t ilen );
 
@@ -419,6 +434,7 @@
  * \return          #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification
  *                  failure.
  */
+MBEDTLS_CHECK_RETURN_TYPICAL
 int mbedtls_md_hmac_finish( mbedtls_md_context_t *ctx, unsigned char *output);
 
 /**
@@ -436,6 +452,7 @@
  * \return          #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification
  *                  failure.
  */
+MBEDTLS_CHECK_RETURN_TYPICAL
 int mbedtls_md_hmac_reset( mbedtls_md_context_t *ctx );
 
 /**
@@ -460,11 +477,13 @@
  * \return         #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification
  *                 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 );
 
 /* Internal use */
+MBEDTLS_CHECK_RETURN_TYPICAL
 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 23c48f4..7f3d5cf 100644
--- a/third_party/mbedtls/repo/include/mbedtls/md2.h
+++ b/third_party/mbedtls/repo/include/mbedtls/md2.h
@@ -36,7 +36,8 @@
 #include <stddef.h>
 
 /* MBEDTLS_ERR_MD2_HW_ACCEL_FAILED is deprecated and should not be used. */
-#define MBEDTLS_ERR_MD2_HW_ACCEL_FAILED                   -0x002B  /**< MD2 hardware accelerator failed */
+/** MD2 hardware accelerator failed */
+#define MBEDTLS_ERR_MD2_HW_ACCEL_FAILED                   -0x002B
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/third_party/mbedtls/repo/include/mbedtls/md4.h b/third_party/mbedtls/repo/include/mbedtls/md4.h
index eeb1670..0238c67 100644
--- a/third_party/mbedtls/repo/include/mbedtls/md4.h
+++ b/third_party/mbedtls/repo/include/mbedtls/md4.h
@@ -37,7 +37,8 @@
 #include <stdint.h>
 
 /* MBEDTLS_ERR_MD4_HW_ACCEL_FAILED is deprecated and should not be used. */
-#define MBEDTLS_ERR_MD4_HW_ACCEL_FAILED                   -0x002D  /**< MD4 hardware accelerator failed */
+/** MD4 hardware accelerator failed */
+#define MBEDTLS_ERR_MD4_HW_ACCEL_FAILED                   -0x002D
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/third_party/mbedtls/repo/include/mbedtls/md5.h b/third_party/mbedtls/repo/include/mbedtls/md5.h
index aaca0f2..73e4dd2 100644
--- a/third_party/mbedtls/repo/include/mbedtls/md5.h
+++ b/third_party/mbedtls/repo/include/mbedtls/md5.h
@@ -36,7 +36,8 @@
 #include <stdint.h>
 
 /* MBEDTLS_ERR_MD5_HW_ACCEL_FAILED is deprecated and should not be used. */
-#define MBEDTLS_ERR_MD5_HW_ACCEL_FAILED                   -0x002F  /**< MD5 hardware accelerator failed */
+/** MD5 hardware accelerator failed */
+#define MBEDTLS_ERR_MD5_HW_ACCEL_FAILED                   -0x002F
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/third_party/mbedtls/repo/include/mbedtls/net_sockets.h b/third_party/mbedtls/repo/include/mbedtls/net_sockets.h
index 55fd18b..ceb7d5f 100644
--- a/third_party/mbedtls/repo/include/mbedtls/net_sockets.h
+++ b/third_party/mbedtls/repo/include/mbedtls/net_sockets.h
@@ -49,19 +49,32 @@
 #include <stddef.h>
 #include <stdint.h>
 
-#define MBEDTLS_ERR_NET_SOCKET_FAILED                     -0x0042  /**< Failed to open a socket. */
-#define MBEDTLS_ERR_NET_CONNECT_FAILED                    -0x0044  /**< The connection to the given server / port failed. */
-#define MBEDTLS_ERR_NET_BIND_FAILED                       -0x0046  /**< Binding of the socket failed. */
-#define MBEDTLS_ERR_NET_LISTEN_FAILED                     -0x0048  /**< Could not listen on the socket. */
-#define MBEDTLS_ERR_NET_ACCEPT_FAILED                     -0x004A  /**< Could not accept the incoming connection. */
-#define MBEDTLS_ERR_NET_RECV_FAILED                       -0x004C  /**< Reading information from the socket failed. */
-#define MBEDTLS_ERR_NET_SEND_FAILED                       -0x004E  /**< Sending information through the socket failed. */
-#define MBEDTLS_ERR_NET_CONN_RESET                        -0x0050  /**< Connection was reset by peer. */
-#define MBEDTLS_ERR_NET_UNKNOWN_HOST                      -0x0052  /**< Failed to get an IP address for the given hostname. */
-#define MBEDTLS_ERR_NET_BUFFER_TOO_SMALL                  -0x0043  /**< Buffer is too small to hold the data. */
-#define MBEDTLS_ERR_NET_INVALID_CONTEXT                   -0x0045  /**< The context is invalid, eg because it was free()ed. */
-#define MBEDTLS_ERR_NET_POLL_FAILED                       -0x0047  /**< Polling the net context failed. */
-#define MBEDTLS_ERR_NET_BAD_INPUT_DATA                    -0x0049  /**< Input invalid. */
+/** Failed to open a socket. */
+#define MBEDTLS_ERR_NET_SOCKET_FAILED                     -0x0042
+/** The connection to the given server / port failed. */
+#define MBEDTLS_ERR_NET_CONNECT_FAILED                    -0x0044
+/** Binding of the socket failed. */
+#define MBEDTLS_ERR_NET_BIND_FAILED                       -0x0046
+/** Could not listen on the socket. */
+#define MBEDTLS_ERR_NET_LISTEN_FAILED                     -0x0048
+/** Could not accept the incoming connection. */
+#define MBEDTLS_ERR_NET_ACCEPT_FAILED                     -0x004A
+/** Reading information from the socket failed. */
+#define MBEDTLS_ERR_NET_RECV_FAILED                       -0x004C
+/** Sending information through the socket failed. */
+#define MBEDTLS_ERR_NET_SEND_FAILED                       -0x004E
+/** Connection was reset by peer. */
+#define MBEDTLS_ERR_NET_CONN_RESET                        -0x0050
+/** Failed to get an IP address for the given hostname. */
+#define MBEDTLS_ERR_NET_UNKNOWN_HOST                      -0x0052
+/** Buffer is too small to hold the data. */
+#define MBEDTLS_ERR_NET_BUFFER_TOO_SMALL                  -0x0043
+/** The context is invalid, eg because it was free()ed. */
+#define MBEDTLS_ERR_NET_INVALID_CONTEXT                   -0x0045
+/** Polling the net context failed. */
+#define MBEDTLS_ERR_NET_POLL_FAILED                       -0x0047
+/** Input invalid. */
+#define MBEDTLS_ERR_NET_BAD_INPUT_DATA                    -0x0049
 
 #define MBEDTLS_NET_LISTEN_BACKLOG         10 /**< The backlog that listen() should use. */
 
@@ -124,6 +137,7 @@
  *
  * \return         0 if successful, or one of:
  *                      MBEDTLS_ERR_NET_SOCKET_FAILED,
+ *                      MBEDTLS_ERR_NET_UNKNOWN_HOST,
  *                      MBEDTLS_ERR_NET_BIND_FAILED,
  *                      MBEDTLS_ERR_NET_LISTEN_FAILED
  *
@@ -143,6 +157,8 @@
  *                  can be NULL if client_ip is null
  *
  * \return          0 if successful, or
+ *                  MBEDTLS_ERR_NET_SOCKET_FAILED,
+ *                  MBEDTLS_ERR_NET_BIND_FAILED,
  *                  MBEDTLS_ERR_NET_ACCEPT_FAILED, or
  *                  MBEDTLS_ERR_NET_BUFFER_TOO_SMALL if buf_size is too small,
  *                  MBEDTLS_ERR_SSL_WANT_READ if bind_fd was set to
@@ -155,6 +171,10 @@
 /**
  * \brief          Check and wait for the context to be ready for read/write
  *
+ * \note           The current implementation of this function uses
+ *                 select() and returns an error if the file descriptor
+ *                 is \c FD_SETSIZE or greater.
+ *
  * \param ctx      Socket to check
  * \param rw       Bitflag composed of MBEDTLS_NET_POLL_READ and
  *                 MBEDTLS_NET_POLL_WRITE specifying the events
@@ -236,16 +256,21 @@
  *                 'timeout' seconds. If no error occurs, the actual amount
  *                 read is returned.
  *
+ * \note           The current implementation of this function uses
+ *                 select() and returns an error if the file descriptor
+ *                 is \c FD_SETSIZE or greater.
+ *
  * \param ctx      Socket
  * \param buf      The buffer to write to
  * \param len      Maximum length of the buffer
  * \param timeout  Maximum number of milliseconds to wait for data
  *                 0 means no timeout (wait forever)
  *
- * \return         the number of bytes received,
- *                 or a non-zero error code:
- *                 MBEDTLS_ERR_SSL_TIMEOUT if the operation timed out,
+ * \return         The number of bytes received if successful.
+ *                 MBEDTLS_ERR_SSL_TIMEOUT if the operation timed out.
  *                 MBEDTLS_ERR_SSL_WANT_READ if interrupted by a signal.
+ *                 Another negative error code (MBEDTLS_ERR_NET_xxx)
+ *                 for other failures.
  *
  * \note           This function will block (until data becomes available or
  *                 timeout is reached) even if the socket is set to
diff --git a/third_party/mbedtls/repo/include/mbedtls/oid.h b/third_party/mbedtls/repo/include/mbedtls/oid.h
index e4c697b..1c39186 100644
--- a/third_party/mbedtls/repo/include/mbedtls/oid.h
+++ b/third_party/mbedtls/repo/include/mbedtls/oid.h
@@ -41,8 +41,10 @@
 #include "mbedtls/md.h"
 #endif
 
-#define MBEDTLS_ERR_OID_NOT_FOUND                         -0x002E  /**< OID is not found. */
-#define MBEDTLS_ERR_OID_BUF_TOO_SMALL                     -0x000B  /**< output buffer is too small */
+/** OID is not found. */
+#define MBEDTLS_ERR_OID_NOT_FOUND                         -0x002E
+/** output buffer is too small */
+#define MBEDTLS_ERR_OID_BUF_TOO_SMALL                     -0x000B
 
 /* This is for the benefit of X.509, but defined here in order to avoid
  * having a "backwards" include of x.509.h here */
diff --git a/third_party/mbedtls/repo/include/mbedtls/padlock.h b/third_party/mbedtls/repo/include/mbedtls/padlock.h
index 78dbeb6..624d02d 100644
--- a/third_party/mbedtls/repo/include/mbedtls/padlock.h
+++ b/third_party/mbedtls/repo/include/mbedtls/padlock.h
@@ -34,7 +34,8 @@
 
 #include "mbedtls/aes.h"
 
-#define MBEDTLS_ERR_PADLOCK_DATA_MISALIGNED               -0x0030  /**< Input data should be aligned. */
+/** Input data should be aligned. */
+#define MBEDTLS_ERR_PADLOCK_DATA_MISALIGNED               -0x0030
 
 #if defined(__has_feature)
 #if __has_feature(address_sanitizer)
@@ -71,7 +72,7 @@
  *
  * \param feature  The feature to detect
  *
- * \return         1 if CPU has support for the feature, 0 otherwise
+ * \return         non-zero if CPU has support for the feature, 0 otherwise
  */
 int mbedtls_padlock_has_support( int feature );
 
diff --git a/third_party/mbedtls/repo/include/mbedtls/pem.h b/third_party/mbedtls/repo/include/mbedtls/pem.h
index 4769bec..dfb4ff2 100644
--- a/third_party/mbedtls/repo/include/mbedtls/pem.h
+++ b/third_party/mbedtls/repo/include/mbedtls/pem.h
@@ -36,15 +36,24 @@
  * PEM data.
  * \{
  */
-#define MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT          -0x1080  /**< No PEM header or footer found. */
-#define MBEDTLS_ERR_PEM_INVALID_DATA                      -0x1100  /**< PEM string is not as expected. */
-#define MBEDTLS_ERR_PEM_ALLOC_FAILED                      -0x1180  /**< Failed to allocate memory. */
-#define MBEDTLS_ERR_PEM_INVALID_ENC_IV                    -0x1200  /**< RSA IV is not in hex-format. */
-#define MBEDTLS_ERR_PEM_UNKNOWN_ENC_ALG                   -0x1280  /**< Unsupported key encryption algorithm. */
-#define MBEDTLS_ERR_PEM_PASSWORD_REQUIRED                 -0x1300  /**< Private key password can't be empty. */
-#define MBEDTLS_ERR_PEM_PASSWORD_MISMATCH                 -0x1380  /**< Given private key password does not allow for correct decryption. */
-#define MBEDTLS_ERR_PEM_FEATURE_UNAVAILABLE               -0x1400  /**< Unavailable feature, e.g. hashing/encryption combination. */
-#define MBEDTLS_ERR_PEM_BAD_INPUT_DATA                    -0x1480  /**< Bad input parameters to function. */
+/** No PEM header or footer found. */
+#define MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT          -0x1080
+/** PEM string is not as expected. */
+#define MBEDTLS_ERR_PEM_INVALID_DATA                      -0x1100
+/** Failed to allocate memory. */
+#define MBEDTLS_ERR_PEM_ALLOC_FAILED                      -0x1180
+/** RSA IV is not in hex-format. */
+#define MBEDTLS_ERR_PEM_INVALID_ENC_IV                    -0x1200
+/** Unsupported key encryption algorithm. */
+#define MBEDTLS_ERR_PEM_UNKNOWN_ENC_ALG                   -0x1280
+/** Private key password can't be empty. */
+#define MBEDTLS_ERR_PEM_PASSWORD_REQUIRED                 -0x1300
+/** Given private key password does not allow for correct decryption. */
+#define MBEDTLS_ERR_PEM_PASSWORD_MISMATCH                 -0x1380
+/** Unavailable feature, e.g. hashing/encryption combination. */
+#define MBEDTLS_ERR_PEM_FEATURE_UNAVAILABLE               -0x1400
+/** Bad input parameters to function. */
+#define MBEDTLS_ERR_PEM_BAD_INPUT_DATA                    -0x1480
 /* \} name */
 
 #ifdef __cplusplus
diff --git a/third_party/mbedtls/repo/include/mbedtls/pk.h b/third_party/mbedtls/repo/include/mbedtls/pk.h
index 7d0f977..8f2abf2 100644
--- a/third_party/mbedtls/repo/include/mbedtls/pk.h
+++ b/third_party/mbedtls/repo/include/mbedtls/pk.h
@@ -52,23 +52,38 @@
 #define inline __inline
 #endif
 
-#define MBEDTLS_ERR_PK_ALLOC_FAILED        -0x3F80  /**< Memory allocation failed. */
-#define MBEDTLS_ERR_PK_TYPE_MISMATCH       -0x3F00  /**< Type mismatch, eg attempt to encrypt with an ECDSA key */
-#define MBEDTLS_ERR_PK_BAD_INPUT_DATA      -0x3E80  /**< Bad input parameters to function. */
-#define MBEDTLS_ERR_PK_FILE_IO_ERROR       -0x3E00  /**< Read/write of file failed. */
-#define MBEDTLS_ERR_PK_KEY_INVALID_VERSION -0x3D80  /**< Unsupported key version */
-#define MBEDTLS_ERR_PK_KEY_INVALID_FORMAT  -0x3D00  /**< Invalid key tag or value. */
-#define MBEDTLS_ERR_PK_UNKNOWN_PK_ALG      -0x3C80  /**< Key algorithm is unsupported (only RSA and EC are supported). */
-#define MBEDTLS_ERR_PK_PASSWORD_REQUIRED   -0x3C00  /**< Private key password can't be empty. */
-#define MBEDTLS_ERR_PK_PASSWORD_MISMATCH   -0x3B80  /**< Given private key password does not allow for correct decryption. */
-#define MBEDTLS_ERR_PK_INVALID_PUBKEY      -0x3B00  /**< The pubkey tag or value is invalid (only RSA and EC are supported). */
-#define MBEDTLS_ERR_PK_INVALID_ALG         -0x3A80  /**< The algorithm tag or value is invalid. */
-#define MBEDTLS_ERR_PK_UNKNOWN_NAMED_CURVE -0x3A00  /**< Elliptic curve is unsupported (only NIST curves are supported). */
-#define MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE -0x3980  /**< Unavailable feature, e.g. RSA disabled for RSA key. */
-#define MBEDTLS_ERR_PK_SIG_LEN_MISMATCH    -0x3900  /**< The buffer contains a valid signature followed by more data. */
+/** Memory allocation failed. */
+#define MBEDTLS_ERR_PK_ALLOC_FAILED        -0x3F80
+/** Type mismatch, eg attempt to encrypt with an ECDSA key */
+#define MBEDTLS_ERR_PK_TYPE_MISMATCH       -0x3F00
+/** Bad input parameters to function. */
+#define MBEDTLS_ERR_PK_BAD_INPUT_DATA      -0x3E80
+/** Read/write of file failed. */
+#define MBEDTLS_ERR_PK_FILE_IO_ERROR       -0x3E00
+/** Unsupported key version */
+#define MBEDTLS_ERR_PK_KEY_INVALID_VERSION -0x3D80
+/** Invalid key tag or value. */
+#define MBEDTLS_ERR_PK_KEY_INVALID_FORMAT  -0x3D00
+/** Key algorithm is unsupported (only RSA and EC are supported). */
+#define MBEDTLS_ERR_PK_UNKNOWN_PK_ALG      -0x3C80
+/** Private key password can't be empty. */
+#define MBEDTLS_ERR_PK_PASSWORD_REQUIRED   -0x3C00
+/** Given private key password does not allow for correct decryption. */
+#define MBEDTLS_ERR_PK_PASSWORD_MISMATCH   -0x3B80
+/** The pubkey tag or value is invalid (only RSA and EC are supported). */
+#define MBEDTLS_ERR_PK_INVALID_PUBKEY      -0x3B00
+/** The algorithm tag or value is invalid. */
+#define MBEDTLS_ERR_PK_INVALID_ALG         -0x3A80
+/** Elliptic curve is unsupported (only NIST curves are supported). */
+#define MBEDTLS_ERR_PK_UNKNOWN_NAMED_CURVE -0x3A00
+/** Unavailable feature, e.g. RSA disabled for RSA key. */
+#define MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE -0x3980
+/** The buffer contains a valid signature followed by more data. */
+#define MBEDTLS_ERR_PK_SIG_LEN_MISMATCH    -0x3900
 
 /* MBEDTLS_ERR_PK_HW_ACCEL_FAILED is deprecated and should not be used. */
-#define MBEDTLS_ERR_PK_HW_ACCEL_FAILED     -0x3880  /**< PK hardware accelerator failed. */
+/** PK hardware accelerator failed. */
+#define MBEDTLS_ERR_PK_HW_ACCEL_FAILED     -0x3880
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/third_party/mbedtls/repo/include/mbedtls/pkcs12.h b/third_party/mbedtls/repo/include/mbedtls/pkcs12.h
index 4b8ce7e..d9e85b1 100644
--- a/third_party/mbedtls/repo/include/mbedtls/pkcs12.h
+++ b/third_party/mbedtls/repo/include/mbedtls/pkcs12.h
@@ -34,10 +34,14 @@
 
 #include <stddef.h>
 
-#define MBEDTLS_ERR_PKCS12_BAD_INPUT_DATA                 -0x1F80  /**< Bad input parameters to function. */
-#define MBEDTLS_ERR_PKCS12_FEATURE_UNAVAILABLE            -0x1F00  /**< Feature not available, e.g. unsupported encryption scheme. */
-#define MBEDTLS_ERR_PKCS12_PBE_INVALID_FORMAT             -0x1E80  /**< PBE ASN.1 data not as expected. */
-#define MBEDTLS_ERR_PKCS12_PASSWORD_MISMATCH              -0x1E00  /**< Given private key password does not allow for correct decryption. */
+/** Bad input parameters to function. */
+#define MBEDTLS_ERR_PKCS12_BAD_INPUT_DATA                 -0x1F80
+/** Feature not available, e.g. unsupported encryption scheme. */
+#define MBEDTLS_ERR_PKCS12_FEATURE_UNAVAILABLE            -0x1F00
+/** PBE ASN.1 data not as expected. */
+#define MBEDTLS_ERR_PKCS12_PBE_INVALID_FORMAT             -0x1E80
+/** Given private key password does not allow for correct decryption. */
+#define MBEDTLS_ERR_PKCS12_PASSWORD_MISMATCH              -0x1E00
 
 #define MBEDTLS_PKCS12_DERIVE_KEY       1   /**< encryption/decryption key */
 #define MBEDTLS_PKCS12_DERIVE_IV        2   /**< initialization vector     */
@@ -75,11 +79,13 @@
  * \brief            PKCS12 Password Based function (encryption / decryption)
  *                   for cipher-based and mbedtls_md-based PBE's
  *
- * \param pbe_params an ASN1 buffer containing the pkcs-12PbeParams structure
- * \param mode       either MBEDTLS_PKCS12_PBE_ENCRYPT or MBEDTLS_PKCS12_PBE_DECRYPT
+ * \param pbe_params an ASN1 buffer containing the pkcs-12 PbeParams structure
+ * \param mode       either #MBEDTLS_PKCS12_PBE_ENCRYPT or
+ *                   #MBEDTLS_PKCS12_PBE_DECRYPT
  * \param cipher_type the cipher used
- * \param md_type     the mbedtls_md used
- * \param pwd        the password used (may be NULL if no password is used)
+ * \param md_type    the mbedtls_md used
+ * \param pwd        Latin1-encoded password used. This may only be \c NULL when
+ *                   \p pwdlen is 0. No null terminator should be used.
  * \param pwdlen     length of the password (may be 0)
  * \param input      the input data
  * \param len        data length
@@ -100,18 +106,24 @@
  *                   to produce pseudo-random bits for a particular "purpose".
  *
  *                   Depending on the given id, this function can produce an
- *                   encryption/decryption key, an nitialization vector or an
+ *                   encryption/decryption key, an initialization vector or an
  *                   integrity key.
  *
  * \param data       buffer to store the derived data in
- * \param datalen    length to fill
- * \param pwd        password to use (may be NULL if no password is used)
- * \param pwdlen     length of the password (may be 0)
- * \param salt       salt buffer to use
- * \param saltlen    length of the salt
- * \param mbedtls_md         mbedtls_md type to use during the derivation
- * \param id         id that describes the purpose (can be MBEDTLS_PKCS12_DERIVE_KEY,
- *                   MBEDTLS_PKCS12_DERIVE_IV or MBEDTLS_PKCS12_DERIVE_MAC_KEY)
+ * \param datalen    length of buffer to fill
+ * \param pwd        The password to use. For compliance with PKCS#12 §B.1, this
+ *                   should be a BMPString, i.e. a Unicode string where each
+ *                   character is encoded as 2 bytes in big-endian order, with
+ *                   no byte order mark and with a null terminator (i.e. the
+ *                   last two bytes should be 0x00 0x00).
+ * \param pwdlen     length of the password (may be 0).
+ * \param salt       Salt buffer to use This may only be \c NULL when
+ *                   \p saltlen is 0.
+ * \param saltlen    length of the salt (may be zero)
+ * \param mbedtls_md mbedtls_md type to use during the derivation
+ * \param id         id that describes the purpose (can be
+ *                   #MBEDTLS_PKCS12_DERIVE_KEY, #MBEDTLS_PKCS12_DERIVE_IV or
+ *                   #MBEDTLS_PKCS12_DERIVE_MAC_KEY)
  * \param iterations number of iterations
  *
  * \return          0 if successful, or a MD, BIGNUM type error.
diff --git a/third_party/mbedtls/repo/include/mbedtls/pkcs5.h b/third_party/mbedtls/repo/include/mbedtls/pkcs5.h
index 8f348ce..696930f 100644
--- a/third_party/mbedtls/repo/include/mbedtls/pkcs5.h
+++ b/third_party/mbedtls/repo/include/mbedtls/pkcs5.h
@@ -36,10 +36,14 @@
 #include <stddef.h>
 #include <stdint.h>
 
-#define MBEDTLS_ERR_PKCS5_BAD_INPUT_DATA                  -0x2f80  /**< Bad input parameters to function. */
-#define MBEDTLS_ERR_PKCS5_INVALID_FORMAT                  -0x2f00  /**< Unexpected ASN.1 data. */
-#define MBEDTLS_ERR_PKCS5_FEATURE_UNAVAILABLE             -0x2e80  /**< Requested encryption or digest alg not available. */
-#define MBEDTLS_ERR_PKCS5_PASSWORD_MISMATCH               -0x2e00  /**< Given private key password does not allow for correct decryption. */
+/** Bad input parameters to function. */
+#define MBEDTLS_ERR_PKCS5_BAD_INPUT_DATA                  -0x2f80
+/** Unexpected ASN.1 data. */
+#define MBEDTLS_ERR_PKCS5_INVALID_FORMAT                  -0x2f00
+/** Requested encryption or digest alg not available. */
+#define MBEDTLS_ERR_PKCS5_FEATURE_UNAVAILABLE             -0x2e80
+/** Given private key password does not allow for correct decryption. */
+#define MBEDTLS_ERR_PKCS5_PASSWORD_MISMATCH               -0x2e00
 
 #define MBEDTLS_PKCS5_DECRYPT      0
 #define MBEDTLS_PKCS5_ENCRYPT      1
diff --git a/third_party/mbedtls/repo/include/mbedtls/platform.h b/third_party/mbedtls/repo/include/mbedtls/platform.h
index fde5ee8..bdef074 100644
--- a/third_party/mbedtls/repo/include/mbedtls/platform.h
+++ b/third_party/mbedtls/repo/include/mbedtls/platform.h
@@ -41,8 +41,10 @@
 #include "mbedtls/platform_time.h"
 #endif
 
-#define MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED     -0x0070 /**< Hardware accelerator failed */
-#define MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED -0x0072 /**< The requested feature is not supported by the platform */
+/** Hardware accelerator failed */
+#define MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED     -0x0070
+/** The requested feature is not supported by the platform */
+#define MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED -0x0072
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/third_party/mbedtls/repo/include/mbedtls/platform_util.h b/third_party/mbedtls/repo/include/mbedtls/platform_util.h
index fbc2a0d..f982db8 100644
--- a/third_party/mbedtls/repo/include/mbedtls/platform_util.h
+++ b/third_party/mbedtls/repo/include/mbedtls/platform_util.h
@@ -132,6 +132,95 @@
 #endif /* MBEDTLS_DEPRECATED_WARNING */
 #endif /* MBEDTLS_DEPRECATED_REMOVED */
 
+/* Implementation of the check-return facility.
+ * See the user documentation in config.h.
+ *
+ * Do not use this macro directly to annotate function: instead,
+ * use one of MBEDTLS_CHECK_RETURN_CRITICAL or MBEDTLS_CHECK_RETURN_TYPICAL
+ * depending on how important it is to check the return value.
+ */
+#if !defined(MBEDTLS_CHECK_RETURN)
+#if defined(__GNUC__)
+#define MBEDTLS_CHECK_RETURN __attribute__((__warn_unused_result__))
+#elif defined(_MSC_VER) && _MSC_VER >= 1700
+#include <sal.h>
+#define MBEDTLS_CHECK_RETURN _Check_return_
+#else
+#define MBEDTLS_CHECK_RETURN
+#endif
+#endif
+
+/** Critical-failure function
+ *
+ * This macro appearing at the beginning of the declaration of a function
+ * indicates that its return value should be checked in all applications.
+ * Omitting the check is very likely to indicate a bug in the application
+ * and will result in a compile-time warning if #MBEDTLS_CHECK_RETURN
+ * is implemented for the compiler in use.
+ *
+ * \note  The use of this macro is a work in progress.
+ *        This macro may be added to more functions in the future.
+ *        Such an extension is not considered an API break, provided that
+ *        there are near-unavoidable circumstances under which the function
+ *        can fail. For example, signature/MAC/AEAD verification functions,
+ *        and functions that require a random generator, are considered
+ *        return-check-critical.
+ */
+#define MBEDTLS_CHECK_RETURN_CRITICAL MBEDTLS_CHECK_RETURN
+
+/** Ordinary-failure function
+ *
+ * This macro appearing at the beginning of the declaration of a function
+ * indicates that its return value should be generally be checked in portable
+ * applications. Omitting the check will result in a compile-time warning if
+ * #MBEDTLS_CHECK_RETURN is implemented for the compiler in use and
+ * #MBEDTLS_CHECK_RETURN_WARNING is enabled in the compile-time configuration.
+ *
+ * You can use #MBEDTLS_IGNORE_RETURN to explicitly ignore the return value
+ * of a function that is annotated with #MBEDTLS_CHECK_RETURN.
+ *
+ * \note  The use of this macro is a work in progress.
+ *        This macro will be added to more functions in the future.
+ *        Eventually this should appear before most functions returning
+ *        an error code (as \c int in the \c mbedtls_xxx API or
+ *        as ::psa_status_t in the \c psa_xxx API).
+ */
+#if defined(MBEDTLS_CHECK_RETURN_WARNING)
+#define MBEDTLS_CHECK_RETURN_TYPICAL MBEDTLS_CHECK_RETURN
+#else
+#define MBEDTLS_CHECK_RETURN_TYPICAL
+#endif
+
+/** Benign-failure function
+ *
+ * This macro appearing at the beginning of the declaration of a function
+ * indicates that it is rarely useful to check its return value.
+ *
+ * This macro has an empty expansion. It exists for documentation purposes:
+ * a #MBEDTLS_CHECK_RETURN_OPTIONAL annotation indicates that the function
+ * has been analyzed for return-check usefuless, whereas the lack of
+ * an annotation indicates that the function has not been analyzed and its
+ * return-check usefulness is unknown.
+ */
+#define MBEDTLS_CHECK_RETURN_OPTIONAL
+
+/** \def MBEDTLS_IGNORE_RETURN
+ *
+ * Call this macro with one argument, a function call, to suppress a warning
+ * from #MBEDTLS_CHECK_RETURN due to that function call.
+ */
+#if !defined(MBEDTLS_IGNORE_RETURN)
+/* GCC doesn't silence the warning with just (void)(result).
+ * (void)!(result) is known to work up at least up to GCC 10, as well
+ * as with Clang and MSVC.
+ *
+ * https://gcc.gnu.org/onlinedocs/gcc-3.4.6/gcc/Non_002dbugs.html
+ * https://stackoverflow.com/questions/40576003/ignoring-warning-wunused-result
+ * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66425#c34
+ */
+#define MBEDTLS_IGNORE_RETURN(result) ( (void) !( result ) )
+#endif
+
 /**
  * \brief       Securely zeroize a buffer
  *
diff --git a/third_party/mbedtls/repo/include/mbedtls/poly1305.h b/third_party/mbedtls/repo/include/mbedtls/poly1305.h
index 905c145..a69ede9 100644
--- a/third_party/mbedtls/repo/include/mbedtls/poly1305.h
+++ b/third_party/mbedtls/repo/include/mbedtls/poly1305.h
@@ -41,15 +41,18 @@
 #include <stdint.h>
 #include <stddef.h>
 
-#define MBEDTLS_ERR_POLY1305_BAD_INPUT_DATA         -0x0057 /**< Invalid input parameter(s). */
+/** Invalid input parameter(s). */
+#define MBEDTLS_ERR_POLY1305_BAD_INPUT_DATA         -0x0057
 
 /* MBEDTLS_ERR_POLY1305_FEATURE_UNAVAILABLE is deprecated and should not be
  * used. */
-#define MBEDTLS_ERR_POLY1305_FEATURE_UNAVAILABLE    -0x0059 /**< Feature not available. For example, s part of the API is not implemented. */
+/** Feature not available. For example, s part of the API is not implemented. */
+#define MBEDTLS_ERR_POLY1305_FEATURE_UNAVAILABLE    -0x0059
 
 /* MBEDTLS_ERR_POLY1305_HW_ACCEL_FAILED is deprecated and should not be used.
  */
-#define MBEDTLS_ERR_POLY1305_HW_ACCEL_FAILED        -0x005B  /**< Poly1305 hardware accelerator failed. */
+/** Poly1305 hardware accelerator failed. */
+#define MBEDTLS_ERR_POLY1305_HW_ACCEL_FAILED        -0x005B
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/third_party/mbedtls/repo/include/mbedtls/psa_util.h b/third_party/mbedtls/repo/include/mbedtls/psa_util.h
index d8a32c5..af7a809 100644
--- a/third_party/mbedtls/repo/include/mbedtls/psa_util.h
+++ b/third_party/mbedtls/repo/include/mbedtls/psa_util.h
@@ -59,6 +59,9 @@
         case MBEDTLS_CIPHER_AES_128_CBC:
         case MBEDTLS_CIPHER_AES_192_CBC:
         case MBEDTLS_CIPHER_AES_256_CBC:
+        case MBEDTLS_CIPHER_AES_128_ECB:
+        case MBEDTLS_CIPHER_AES_192_ECB:
+        case MBEDTLS_CIPHER_AES_256_ECB:
             return( PSA_KEY_TYPE_AES );
 
         /* ARIA not yet supported in PSA. */
@@ -86,14 +89,14 @@
         case MBEDTLS_MODE_ECB:
             return( PSA_ALG_ECB_NO_PADDING );
         case MBEDTLS_MODE_GCM:
-            return( PSA_ALG_AEAD_WITH_TAG_LENGTH( PSA_ALG_GCM, taglen ) );
+            return( PSA_ALG_AEAD_WITH_SHORTENED_TAG( PSA_ALG_GCM, taglen ) );
         case MBEDTLS_MODE_CCM:
-            return( PSA_ALG_AEAD_WITH_TAG_LENGTH( PSA_ALG_CCM, taglen ) );
+            return( PSA_ALG_AEAD_WITH_SHORTENED_TAG( PSA_ALG_CCM, taglen ) );
         case MBEDTLS_MODE_CBC:
             if( taglen == 0 )
                 return( PSA_ALG_CBC_NO_PADDING );
-            /* Intentional fallthrough for taglen != 0 */
-            /* fallthrough */
+            else
+                return( 0 );
         default:
             return( 0 );
     }
@@ -151,7 +154,8 @@
     case MBEDTLS_MD_RIPEMD160:
         return( PSA_ALG_RIPEMD160 );
 #endif
-    case MBEDTLS_MD_NONE:  /* Intentional fallthrough */
+    case MBEDTLS_MD_NONE:
+        return( 0 );
     default:
         return( 0 );
     }
@@ -419,4 +423,90 @@
 
 #endif /* MBEDTLS_USE_PSA_CRYPTO */
 
+/* Expose whatever RNG the PSA subsystem uses to applications using the
+ * mbedtls_xxx API. The declarations and definitions here need to be
+ * consistent with the implementation in library/psa_crypto_random_impl.h.
+ * See that file for implementation documentation. */
+#if defined(MBEDTLS_PSA_CRYPTO_C)
+
+/* The type of a `f_rng` random generator function that many library functions
+ * take.
+ *
+ * This type name is not part of the Mbed TLS stable API. It may be renamed
+ * or moved without warning.
+ */
+typedef int mbedtls_f_rng_t( void *p_rng, unsigned char *output, size_t output_size );
+
+#if defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG)
+
+/** The random generator function for the PSA subsystem.
+ *
+ * This function is suitable as the `f_rng` random generator function
+ * parameter of many `mbedtls_xxx` functions. Use #MBEDTLS_PSA_RANDOM_STATE
+ * to obtain the \p p_rng parameter.
+ *
+ * The implementation of this function depends on the configuration of the
+ * library.
+ *
+ * \note Depending on the configuration, this may be a function or
+ *       a pointer to a function.
+ *
+ * \note This function may only be used if the PSA crypto subsystem is active.
+ *       This means that you must call psa_crypto_init() before any call to
+ *       this function, and you must not call this function after calling
+ *       mbedtls_psa_crypto_free().
+ *
+ * \param p_rng         The random generator context. This must be
+ *                      #MBEDTLS_PSA_RANDOM_STATE. No other state is
+ *                      supported.
+ * \param output        The buffer to fill. It must have room for
+ *                      \c output_size bytes.
+ * \param output_size   The number of bytes to write to \p output.
+ *                      This function may fail if \p output_size is too
+ *                      large. It is guaranteed to accept any output size
+ *                      requested by Mbed TLS library functions. The
+ *                      maximum request size depends on the library
+ *                      configuration.
+ *
+ * \return              \c 0 on success.
+ * \return              An `MBEDTLS_ERR_ENTROPY_xxx`,
+ *                      `MBEDTLS_ERR_PLATFORM_xxx,
+ *                      `MBEDTLS_ERR_CTR_DRBG_xxx` or
+ *                      `MBEDTLS_ERR_HMAC_DRBG_xxx` on error.
+ */
+int mbedtls_psa_get_random( void *p_rng,
+                            unsigned char *output,
+                            size_t output_size );
+
+/** The random generator state for the PSA subsystem.
+ *
+ * This macro expands to an expression which is suitable as the `p_rng`
+ * random generator state parameter of many `mbedtls_xxx` functions.
+ * It must be used in combination with the random generator function
+ * mbedtls_psa_get_random().
+ *
+ * The implementation of this macro depends on the configuration of the
+ * library. Do not make any assumption on its nature.
+ */
+#define MBEDTLS_PSA_RANDOM_STATE NULL
+
+#else /* !defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) */
+
+#if defined(MBEDTLS_CTR_DRBG_C)
+#include "mbedtls/ctr_drbg.h"
+typedef mbedtls_ctr_drbg_context mbedtls_psa_drbg_context_t;
+static mbedtls_f_rng_t *const mbedtls_psa_get_random = mbedtls_ctr_drbg_random;
+#elif defined(MBEDTLS_HMAC_DRBG_C)
+#include "mbedtls/hmac_drbg.h"
+typedef mbedtls_hmac_drbg_context mbedtls_psa_drbg_context_t;
+static mbedtls_f_rng_t *const mbedtls_psa_get_random = mbedtls_hmac_drbg_random;
+#endif
+extern mbedtls_psa_drbg_context_t *const mbedtls_psa_random_state;
+
+#define MBEDTLS_PSA_RANDOM_STATE mbedtls_psa_random_state
+
+#endif /* !defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) */
+
+#endif /* MBEDTLS_PSA_CRYPTO_C */
+
 #endif /* MBEDTLS_PSA_UTIL_H */
diff --git a/third_party/mbedtls/repo/include/mbedtls/ripemd160.h b/third_party/mbedtls/repo/include/mbedtls/ripemd160.h
index 381c725..63270d1 100644
--- a/third_party/mbedtls/repo/include/mbedtls/ripemd160.h
+++ b/third_party/mbedtls/repo/include/mbedtls/ripemd160.h
@@ -33,7 +33,8 @@
 
 /* MBEDTLS_ERR_RIPEMD160_HW_ACCEL_FAILED is deprecated and should not be used.
  */
-#define MBEDTLS_ERR_RIPEMD160_HW_ACCEL_FAILED             -0x0031  /**< RIPEMD160 hardware accelerator failed */
+/** RIPEMD160 hardware accelerator failed */
+#define MBEDTLS_ERR_RIPEMD160_HW_ACCEL_FAILED             -0x0031
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/third_party/mbedtls/repo/include/mbedtls/rsa.h b/third_party/mbedtls/repo/include/mbedtls/rsa.h
index 6a31514..3c481e1 100644
--- a/third_party/mbedtls/repo/include/mbedtls/rsa.h
+++ b/third_party/mbedtls/repo/include/mbedtls/rsa.h
@@ -44,22 +44,33 @@
 /*
  * RSA Error codes
  */
-#define MBEDTLS_ERR_RSA_BAD_INPUT_DATA                    -0x4080  /**< Bad input parameters to function. */
-#define MBEDTLS_ERR_RSA_INVALID_PADDING                   -0x4100  /**< Input data contains invalid padding and is rejected. */
-#define MBEDTLS_ERR_RSA_KEY_GEN_FAILED                    -0x4180  /**< Something failed during generation of a key. */
-#define MBEDTLS_ERR_RSA_KEY_CHECK_FAILED                  -0x4200  /**< Key failed to pass the validity check of the library. */
-#define MBEDTLS_ERR_RSA_PUBLIC_FAILED                     -0x4280  /**< The public key operation failed. */
-#define MBEDTLS_ERR_RSA_PRIVATE_FAILED                    -0x4300  /**< The private key operation failed. */
-#define MBEDTLS_ERR_RSA_VERIFY_FAILED                     -0x4380  /**< The PKCS#1 verification failed. */
-#define MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE                  -0x4400  /**< The output buffer for decryption is not large enough. */
-#define MBEDTLS_ERR_RSA_RNG_FAILED                        -0x4480  /**< The random generator failed to generate non-zeros. */
+/** Bad input parameters to function. */
+#define MBEDTLS_ERR_RSA_BAD_INPUT_DATA                    -0x4080
+/** Input data contains invalid padding and is rejected. */
+#define MBEDTLS_ERR_RSA_INVALID_PADDING                   -0x4100
+/** Something failed during generation of a key. */
+#define MBEDTLS_ERR_RSA_KEY_GEN_FAILED                    -0x4180
+/** Key failed to pass the validity check of the library. */
+#define MBEDTLS_ERR_RSA_KEY_CHECK_FAILED                  -0x4200
+/** The public key operation failed. */
+#define MBEDTLS_ERR_RSA_PUBLIC_FAILED                     -0x4280
+/** The private key operation failed. */
+#define MBEDTLS_ERR_RSA_PRIVATE_FAILED                    -0x4300
+/** The PKCS#1 verification failed. */
+#define MBEDTLS_ERR_RSA_VERIFY_FAILED                     -0x4380
+/** The output buffer for decryption is not large enough. */
+#define MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE                  -0x4400
+/** The random generator failed to generate non-zeros. */
+#define MBEDTLS_ERR_RSA_RNG_FAILED                        -0x4480
 
 /* MBEDTLS_ERR_RSA_UNSUPPORTED_OPERATION is deprecated and should not be used.
  */
-#define MBEDTLS_ERR_RSA_UNSUPPORTED_OPERATION             -0x4500  /**< The implementation does not offer the requested operation, for example, because of security violations or lack of functionality. */
+/** The implementation does not offer the requested operation, for example, because of security violations or lack of functionality. */
+#define MBEDTLS_ERR_RSA_UNSUPPORTED_OPERATION             -0x4500
 
 /* MBEDTLS_ERR_RSA_HW_ACCEL_FAILED is deprecated and should not be used. */
-#define MBEDTLS_ERR_RSA_HW_ACCEL_FAILED                   -0x4580  /**< RSA hardware accelerator failed. */
+/** RSA hardware accelerator failed. */
+#define MBEDTLS_ERR_RSA_HW_ACCEL_FAILED                   -0x4580
 
 /*
  * RSA constants
@@ -97,7 +108,10 @@
  */
 typedef struct mbedtls_rsa_context
 {
-    int ver;                    /*!<  Always 0.*/
+    int ver;                    /*!<  Reserved for internal purposes.
+                                 *    Do not set this field in application
+                                 *    code. Its meaning might change without
+                                 *    notice. */
     size_t len;                 /*!<  The size of \p N in Bytes. */
 
     mbedtls_mpi N;              /*!<  The public modulus. */
@@ -127,6 +141,7 @@
                                      mask generating function used in the
                                      EME-OAEP and EMSA-PSS encodings. */
 #if defined(MBEDTLS_THREADING_C)
+    /* Invariant: the mutex is initialized iff ver != 0. */
     mbedtls_threading_mutex_t mutex;    /*!<  Thread-safety mutex. */
 #endif
 }
@@ -972,12 +987,69 @@
  * \brief          This function performs a PKCS#1 v2.1 PSS signature
  *                 operation (RSASSA-PSS-SIGN).
  *
- * \note           The \p hash_id in the RSA context is the one used for the
- *                 encoding. \p md_alg in the function call is the type of hash
- *                 that is encoded. According to <em>RFC-3447: Public-Key
+ * \note           The \c hash_id set in \p ctx (when calling
+ *                 mbedtls_rsa_init() or by calling mbedtls_rsa_set_padding()
+ *                 afterwards) selects the hash used for the
+ *                 encoding operation and for the mask generation function
+ *                 (MGF1). For more details on the encoding operation and the
+ *                 mask generation function, consult <em>RFC-3447: Public-Key
  *                 Cryptography Standards (PKCS) #1 v2.1: RSA Cryptography
- *                 Specifications</em> it is advised to keep both hashes the
- *                 same.
+ *                 Specifications</em>.
+ *
+ * \note           This function enforces that the provided salt length complies
+ *                 with FIPS 186-4 §5.5 (e) and RFC 8017 (PKCS#1 v2.2) §9.1.1
+ *                 step 3. The constraint is that the hash length plus the salt
+ *                 length plus 2 bytes must be at most the key length. If this
+ *                 constraint is not met, this function returns
+ *                 #MBEDTLS_ERR_RSA_BAD_INPUT_DATA.
+ *
+ * \param ctx      The initialized RSA context to use.
+ * \param f_rng    The RNG function. It must not be \c NULL.
+ * \param p_rng    The RNG context to be passed to \p f_rng. This may be \c NULL
+ *                 if \p f_rng doesn't need a context argument.
+ * \param md_alg   The message-digest algorithm used to hash the original data.
+ *                 Use #MBEDTLS_MD_NONE for signing raw data.
+ * \param hashlen  The length of the message digest.
+ *                 Ths is only used if \p md_alg is #MBEDTLS_MD_NONE.
+ * \param hash     The buffer holding the message digest or raw data.
+ *                 If \p md_alg is #MBEDTLS_MD_NONE, this must be a readable
+ *                 buffer of length \p hashlen Bytes. If \p md_alg is not
+ *                 #MBEDTLS_MD_NONE, it must be a readable buffer of length
+ *                 the size of the hash corresponding to \p md_alg.
+ * \param saltlen  The length of the salt that should be used.
+ *                 If passed #MBEDTLS_RSA_SALT_LEN_ANY, the function will use
+ *                 the largest possible salt length up to the hash length,
+ *                 which is the largest permitted by some standards including
+ *                 FIPS 186-4 §5.5.
+ * \param sig      The buffer to hold the signature. This must be a writable
+ *                 buffer of length \c ctx->len Bytes. For example, \c 256 Bytes
+ *                 for an 2048-bit RSA modulus. A buffer length of
+ *                 #MBEDTLS_MPI_MAX_SIZE is always safe.
+ *
+ * \return         \c 0 if the signing operation was successful.
+ * \return         An \c MBEDTLS_ERR_RSA_XXX error code on failure.
+ */
+int mbedtls_rsa_rsassa_pss_sign_ext( mbedtls_rsa_context *ctx,
+                         int (*f_rng)(void *, unsigned char *, size_t),
+                         void *p_rng,
+                         mbedtls_md_type_t md_alg,
+                         unsigned int hashlen,
+                         const unsigned char *hash,
+                         int saltlen,
+                         unsigned char *sig );
+
+/**
+ * \brief          This function performs a PKCS#1 v2.1 PSS signature
+ *                 operation (RSASSA-PSS-SIGN).
+ *
+ * \note           The \c hash_id set in \p ctx (when calling
+ *                 mbedtls_rsa_init() or by calling mbedtls_rsa_set_padding()
+ *                 afterwards) selects the hash used for the
+ *                 encoding operation and for the mask generation function
+ *                 (MGF1). For more details on the encoding operation and the
+ *                 mask generation function, consult <em>RFC-3447: Public-Key
+ *                 Cryptography Standards (PKCS) #1 v2.1: RSA Cryptography
+ *                 Specifications</em>.
  *
  * \note           This function always uses the maximum possible salt size,
  *                 up to the length of the payload hash. This choice of salt
@@ -1007,7 +1079,7 @@
  * \param md_alg   The message-digest algorithm used to hash the original data.
  *                 Use #MBEDTLS_MD_NONE for signing raw data.
  * \param hashlen  The length of the message digest.
- *                 Ths is only used if \p md_alg is #MBEDTLS_MD_NONE.
+ *                 This is only used if \p md_alg is #MBEDTLS_MD_NONE.
  * \param hash     The buffer holding the message digest or raw data.
  *                 If \p md_alg is #MBEDTLS_MD_NONE, this must be a readable
  *                 buffer of length \p hashlen Bytes. If \p md_alg is not
@@ -1133,16 +1205,15 @@
  * \brief          This function performs a PKCS#1 v2.1 PSS verification
  *                 operation (RSASSA-PSS-VERIFY).
  *
- *                 The hash function for the MGF mask generating function
- *                 is that specified in the RSA context.
- *
- * \note           The \p hash_id in the RSA context is the one used for the
- *                 verification. \p md_alg in the function call is the type of
- *                 hash that is verified. According to <em>RFC-3447: Public-Key
+ * \note           The \c hash_id set in \p ctx (when calling
+ *                 mbedtls_rsa_init() or by calling mbedtls_rsa_set_padding()
+ *                 afterwards) selects the hash used for the
+ *                 encoding operation and for the mask generation function
+ *                 (MGF1). For more details on the encoding operation and the
+ *                 mask generation function, consult <em>RFC-3447: Public-Key
  *                 Cryptography Standards (PKCS) #1 v2.1: RSA Cryptography
- *                 Specifications</em> it is advised to keep both hashes the
- *                 same. If \p hash_id in the RSA context is unset,
- *                 the \p md_alg from the function call is used.
+ *                 Specifications</em>. If the \c hash_id set in \p ctx is
+ *                 #MBEDTLS_MD_NONE, the \p md_alg parameter is used.
  *
  * \deprecated     It is deprecated and discouraged to call this function
  *                 in #MBEDTLS_RSA_PRIVATE mode. Future versions of the library
@@ -1190,13 +1261,12 @@
  * \brief          This function performs a PKCS#1 v2.1 PSS verification
  *                 operation (RSASSA-PSS-VERIFY).
  *
- *                 The hash function for the MGF mask generating function
- *                 is that specified in \p mgf1_hash_id.
- *
  * \note           The \p sig buffer must be as large as the size
  *                 of \p ctx->N. For example, 128 Bytes if RSA-1024 is used.
  *
- * \note           The \p hash_id in the RSA context is ignored.
+ * \note           The \c hash_id set in \p ctx (when calling
+ *                 mbedtls_rsa_init() or by calling mbedtls_rsa_set_padding()
+ *                 afterwards) is ignored.
  *
  * \param ctx      The initialized RSA public key context to use.
  * \param f_rng    The RNG function to use. If \p mode is #MBEDTLS_RSA_PRIVATE,
@@ -1215,7 +1285,13 @@
  *                 buffer of length \p hashlen Bytes. If \p md_alg is not
  *                 #MBEDTLS_MD_NONE, it must be a readable buffer of length
  *                 the size of the hash corresponding to \p md_alg.
- * \param mgf1_hash_id      The message digest used for mask generation.
+ * \param mgf1_hash_id      The message digest algorithm used for the
+ *                          verification operation and the mask generation
+ *                          function (MGF1). For more details on the encoding
+ *                          operation and the mask generation function, consult
+ *                          <em>RFC-3447: Public-Key Cryptography Standards
+ *                          (PKCS) #1 v2.1: RSA Cryptography
+ *                          Specifications</em>.
  * \param expected_salt_len The length of the salt used in padding. Use
  *                          #MBEDTLS_RSA_SALT_LEN_ANY to accept any salt length.
  * \param sig      The buffer holding the signature. This must be a readable
diff --git a/third_party/mbedtls/repo/include/mbedtls/sha1.h b/third_party/mbedtls/repo/include/mbedtls/sha1.h
index 86a3d06..4c3251b 100644
--- a/third_party/mbedtls/repo/include/mbedtls/sha1.h
+++ b/third_party/mbedtls/repo/include/mbedtls/sha1.h
@@ -39,8 +39,10 @@
 #include <stdint.h>
 
 /* MBEDTLS_ERR_SHA1_HW_ACCEL_FAILED is deprecated and should not be used. */
-#define MBEDTLS_ERR_SHA1_HW_ACCEL_FAILED                  -0x0035  /**< SHA-1 hardware accelerator failed */
-#define MBEDTLS_ERR_SHA1_BAD_INPUT_DATA                   -0x0073  /**< SHA-1 input data was malformed. */
+/** SHA-1 hardware accelerator failed */
+#define MBEDTLS_ERR_SHA1_HW_ACCEL_FAILED                  -0x0035
+/** SHA-1 input data was malformed. */
+#define MBEDTLS_ERR_SHA1_BAD_INPUT_DATA                   -0x0073
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/third_party/mbedtls/repo/include/mbedtls/sha256.h b/third_party/mbedtls/repo/include/mbedtls/sha256.h
index 73d9544..5b54be2 100644
--- a/third_party/mbedtls/repo/include/mbedtls/sha256.h
+++ b/third_party/mbedtls/repo/include/mbedtls/sha256.h
@@ -35,8 +35,10 @@
 #include <stdint.h>
 
 /* MBEDTLS_ERR_SHA256_HW_ACCEL_FAILED is deprecated and should not be used. */
-#define MBEDTLS_ERR_SHA256_HW_ACCEL_FAILED                -0x0037  /**< SHA-256 hardware accelerator failed */
-#define MBEDTLS_ERR_SHA256_BAD_INPUT_DATA                 -0x0074  /**< SHA-256 input data was malformed. */
+/** SHA-256 hardware accelerator failed */
+#define MBEDTLS_ERR_SHA256_HW_ACCEL_FAILED                -0x0037
+/** SHA-256 input data was malformed. */
+#define MBEDTLS_ERR_SHA256_BAD_INPUT_DATA                 -0x0074
 
 #ifdef __cplusplus
 extern "C" {
@@ -235,6 +237,9 @@
  *                 be a writable buffer of length \c 32 Bytes.
  * \param is224    Determines which function to use. This must be
  *                 either \c 0 for SHA-256, or \c 1 for SHA-224.
+ *
+ * \return         \c 0 on success.
+ * \return         A negative error code on failure.
  */
 int mbedtls_sha256_ret( const unsigned char *input,
                         size_t ilen,
diff --git a/third_party/mbedtls/repo/include/mbedtls/sha512.h b/third_party/mbedtls/repo/include/mbedtls/sha512.h
index 4a8ab42..cca47c2 100644
--- a/third_party/mbedtls/repo/include/mbedtls/sha512.h
+++ b/third_party/mbedtls/repo/include/mbedtls/sha512.h
@@ -34,8 +34,10 @@
 #include <stdint.h>
 
 /* MBEDTLS_ERR_SHA512_HW_ACCEL_FAILED is deprecated and should not be used. */
-#define MBEDTLS_ERR_SHA512_HW_ACCEL_FAILED                -0x0039  /**< SHA-512 hardware accelerator failed */
-#define MBEDTLS_ERR_SHA512_BAD_INPUT_DATA                 -0x0075  /**< SHA-512 input data was malformed. */
+/** SHA-512 hardware accelerator failed */
+#define MBEDTLS_ERR_SHA512_HW_ACCEL_FAILED                -0x0039
+/** SHA-512 input data was malformed. */
+#define MBEDTLS_ERR_SHA512_BAD_INPUT_DATA                 -0x0075
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/third_party/mbedtls/repo/include/mbedtls/ssl.h b/third_party/mbedtls/repo/include/mbedtls/ssl.h
index 7815ad9..209dbf6 100644
--- a/third_party/mbedtls/repo/include/mbedtls/ssl.h
+++ b/third_party/mbedtls/repo/include/mbedtls/ssl.h
@@ -75,64 +75,122 @@
 /*
  * SSL Error codes
  */
-#define MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE               -0x7080  /**< The requested feature is not available. */
-#define MBEDTLS_ERR_SSL_BAD_INPUT_DATA                    -0x7100  /**< Bad input parameters to function. */
-#define MBEDTLS_ERR_SSL_INVALID_MAC                       -0x7180  /**< Verification of the message MAC failed. */
-#define MBEDTLS_ERR_SSL_INVALID_RECORD                    -0x7200  /**< An invalid SSL record was received. */
-#define MBEDTLS_ERR_SSL_CONN_EOF                          -0x7280  /**< The connection indicated an EOF. */
-#define MBEDTLS_ERR_SSL_UNKNOWN_CIPHER                    -0x7300  /**< An unknown cipher was received. */
-#define MBEDTLS_ERR_SSL_NO_CIPHER_CHOSEN                  -0x7380  /**< The server has no ciphersuites in common with the client. */
-#define MBEDTLS_ERR_SSL_NO_RNG                            -0x7400  /**< No RNG was provided to the SSL module. */
-#define MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE             -0x7480  /**< No client certification received from the client, but required by the authentication mode. */
-#define MBEDTLS_ERR_SSL_CERTIFICATE_TOO_LARGE             -0x7500  /**< Our own certificate(s) is/are too large to send in an SSL message. */
-#define MBEDTLS_ERR_SSL_CERTIFICATE_REQUIRED              -0x7580  /**< The own certificate is not set, but needed by the server. */
-#define MBEDTLS_ERR_SSL_PRIVATE_KEY_REQUIRED              -0x7600  /**< The own private key or pre-shared key is not set, but needed. */
-#define MBEDTLS_ERR_SSL_CA_CHAIN_REQUIRED                 -0x7680  /**< No CA Chain is set, but required to operate. */
-#define MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE                -0x7700  /**< An unexpected message was received from our peer. */
-#define MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE               -0x7780  /**< A fatal alert message was received from our peer. */
-#define MBEDTLS_ERR_SSL_PEER_VERIFY_FAILED                -0x7800  /**< Verification of our peer failed. */
-#define MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY                 -0x7880  /**< The peer notified us that the connection is going to be closed. */
-#define MBEDTLS_ERR_SSL_BAD_HS_CLIENT_HELLO               -0x7900  /**< Processing of the ClientHello handshake message failed. */
-#define MBEDTLS_ERR_SSL_BAD_HS_SERVER_HELLO               -0x7980  /**< Processing of the ServerHello handshake message failed. */
-#define MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE                -0x7A00  /**< Processing of the Certificate handshake message failed. */
-#define MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE_REQUEST        -0x7A80  /**< Processing of the CertificateRequest handshake message failed. */
-#define MBEDTLS_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE        -0x7B00  /**< Processing of the ServerKeyExchange handshake message failed. */
-#define MBEDTLS_ERR_SSL_BAD_HS_SERVER_HELLO_DONE          -0x7B80  /**< Processing of the ServerHelloDone handshake message failed. */
-#define MBEDTLS_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE        -0x7C00  /**< Processing of the ClientKeyExchange handshake message failed. */
-#define MBEDTLS_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE_RP     -0x7C80  /**< Processing of the ClientKeyExchange handshake message failed in DHM / ECDH Read Public. */
-#define MBEDTLS_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE_CS     -0x7D00  /**< Processing of the ClientKeyExchange handshake message failed in DHM / ECDH Calculate Secret. */
-#define MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE_VERIFY         -0x7D80  /**< Processing of the CertificateVerify handshake message failed. */
-#define MBEDTLS_ERR_SSL_BAD_HS_CHANGE_CIPHER_SPEC         -0x7E00  /**< Processing of the ChangeCipherSpec handshake message failed. */
-#define MBEDTLS_ERR_SSL_BAD_HS_FINISHED                   -0x7E80  /**< Processing of the Finished handshake message failed. */
-#define MBEDTLS_ERR_SSL_ALLOC_FAILED                      -0x7F00  /**< Memory allocation failed */
-#define MBEDTLS_ERR_SSL_HW_ACCEL_FAILED                   -0x7F80  /**< Hardware acceleration function returned with error */
-#define MBEDTLS_ERR_SSL_HW_ACCEL_FALLTHROUGH              -0x6F80  /**< Hardware acceleration function skipped / left alone data */
-#define MBEDTLS_ERR_SSL_COMPRESSION_FAILED                -0x6F00  /**< Processing of the compression / decompression failed */
-#define MBEDTLS_ERR_SSL_BAD_HS_PROTOCOL_VERSION           -0x6E80  /**< Handshake protocol not within min/max boundaries */
-#define MBEDTLS_ERR_SSL_BAD_HS_NEW_SESSION_TICKET         -0x6E00  /**< Processing of the NewSessionTicket handshake message failed. */
-#define MBEDTLS_ERR_SSL_SESSION_TICKET_EXPIRED            -0x6D80  /**< Session ticket has expired. */
-#define MBEDTLS_ERR_SSL_PK_TYPE_MISMATCH                  -0x6D00  /**< Public key type mismatch (eg, asked for RSA key exchange and presented EC key) */
-#define MBEDTLS_ERR_SSL_UNKNOWN_IDENTITY                  -0x6C80  /**< Unknown identity received (eg, PSK identity) */
-#define MBEDTLS_ERR_SSL_INTERNAL_ERROR                    -0x6C00  /**< Internal error (eg, unexpected failure in lower-level module) */
-#define MBEDTLS_ERR_SSL_COUNTER_WRAPPING                  -0x6B80  /**< A counter would wrap (eg, too many messages exchanged). */
-#define MBEDTLS_ERR_SSL_WAITING_SERVER_HELLO_RENEGO       -0x6B00  /**< Unexpected message at ServerHello in renegotiation. */
-#define MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED             -0x6A80  /**< DTLS client must retry for hello verification */
-#define MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL                  -0x6A00  /**< A buffer is too small to receive or write a message */
-#define MBEDTLS_ERR_SSL_NO_USABLE_CIPHERSUITE             -0x6980  /**< None of the common ciphersuites is usable (eg, no suitable certificate, see debug messages). */
-#define MBEDTLS_ERR_SSL_WANT_READ                         -0x6900  /**< No data of requested type currently available on underlying transport. */
-#define MBEDTLS_ERR_SSL_WANT_WRITE                        -0x6880  /**< Connection requires a write call. */
-#define MBEDTLS_ERR_SSL_TIMEOUT                           -0x6800  /**< The operation timed out. */
-#define MBEDTLS_ERR_SSL_CLIENT_RECONNECT                  -0x6780  /**< The client initiated a reconnect from the same port. */
-#define MBEDTLS_ERR_SSL_UNEXPECTED_RECORD                 -0x6700  /**< Record header looks valid but is not expected. */
-#define MBEDTLS_ERR_SSL_NON_FATAL                         -0x6680  /**< The alert message received indicates a non-fatal error. */
-#define MBEDTLS_ERR_SSL_INVALID_VERIFY_HASH               -0x6600  /**< Couldn't set the hash for verifying CertificateVerify */
-#define MBEDTLS_ERR_SSL_CONTINUE_PROCESSING               -0x6580  /**< Internal-only message signaling that further message-processing should be done */
-#define MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS                 -0x6500  /**< The asynchronous operation is not completed yet. */
-#define MBEDTLS_ERR_SSL_EARLY_MESSAGE                     -0x6480  /**< Internal-only message signaling that a message arrived early. */
-#define MBEDTLS_ERR_SSL_UNEXPECTED_CID                    -0x6000  /**< An encrypted DTLS-frame with an unexpected CID was received. */
-#define MBEDTLS_ERR_SSL_VERSION_MISMATCH                  -0x5F00  /**< An operation failed due to an unexpected version or configuration. */
-#define MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS                -0x7000  /**< A cryptographic operation is in progress. Try again later. */
-#define MBEDTLS_ERR_SSL_BAD_CONFIG                        -0x5E80  /**< Invalid value in SSL config */
+/** The requested feature is not available. */
+#define MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE               -0x7080
+/** Bad input parameters to function. */
+#define MBEDTLS_ERR_SSL_BAD_INPUT_DATA                    -0x7100
+/** Verification of the message MAC failed. */
+#define MBEDTLS_ERR_SSL_INVALID_MAC                       -0x7180
+/** An invalid SSL record was received. */
+#define MBEDTLS_ERR_SSL_INVALID_RECORD                    -0x7200
+/** The connection indicated an EOF. */
+#define MBEDTLS_ERR_SSL_CONN_EOF                          -0x7280
+/** An unknown cipher was received. */
+#define MBEDTLS_ERR_SSL_UNKNOWN_CIPHER                    -0x7300
+/** The server has no ciphersuites in common with the client. */
+#define MBEDTLS_ERR_SSL_NO_CIPHER_CHOSEN                  -0x7380
+/** No RNG was provided to the SSL module. */
+#define MBEDTLS_ERR_SSL_NO_RNG                            -0x7400
+/** No client certification received from the client, but required by the authentication mode. */
+#define MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE             -0x7480
+/** Our own certificate(s) is/are too large to send in an SSL message. */
+#define MBEDTLS_ERR_SSL_CERTIFICATE_TOO_LARGE             -0x7500
+/** The own certificate is not set, but needed by the server. */
+#define MBEDTLS_ERR_SSL_CERTIFICATE_REQUIRED              -0x7580
+/** The own private key or pre-shared key is not set, but needed. */
+#define MBEDTLS_ERR_SSL_PRIVATE_KEY_REQUIRED              -0x7600
+/** No CA Chain is set, but required to operate. */
+#define MBEDTLS_ERR_SSL_CA_CHAIN_REQUIRED                 -0x7680
+/** An unexpected message was received from our peer. */
+#define MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE                -0x7700
+/** A fatal alert message was received from our peer. */
+#define MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE               -0x7780
+/** Verification of our peer failed. */
+#define MBEDTLS_ERR_SSL_PEER_VERIFY_FAILED                -0x7800
+/** The peer notified us that the connection is going to be closed. */
+#define MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY                 -0x7880
+/** Processing of the ClientHello handshake message failed. */
+#define MBEDTLS_ERR_SSL_BAD_HS_CLIENT_HELLO               -0x7900
+/** Processing of the ServerHello handshake message failed. */
+#define MBEDTLS_ERR_SSL_BAD_HS_SERVER_HELLO               -0x7980
+/** Processing of the Certificate handshake message failed. */
+#define MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE                -0x7A00
+/** Processing of the CertificateRequest handshake message failed. */
+#define MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE_REQUEST        -0x7A80
+/** Processing of the ServerKeyExchange handshake message failed. */
+#define MBEDTLS_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE        -0x7B00
+/** Processing of the ServerHelloDone handshake message failed. */
+#define MBEDTLS_ERR_SSL_BAD_HS_SERVER_HELLO_DONE          -0x7B80
+/** Processing of the ClientKeyExchange handshake message failed. */
+#define MBEDTLS_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE        -0x7C00
+/** Processing of the ClientKeyExchange handshake message failed in DHM / ECDH Read Public. */
+#define MBEDTLS_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE_RP     -0x7C80
+/** Processing of the ClientKeyExchange handshake message failed in DHM / ECDH Calculate Secret. */
+#define MBEDTLS_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE_CS     -0x7D00
+/** Processing of the CertificateVerify handshake message failed. */
+#define MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE_VERIFY         -0x7D80
+/** Processing of the ChangeCipherSpec handshake message failed. */
+#define MBEDTLS_ERR_SSL_BAD_HS_CHANGE_CIPHER_SPEC         -0x7E00
+/** Processing of the Finished handshake message failed. */
+#define MBEDTLS_ERR_SSL_BAD_HS_FINISHED                   -0x7E80
+/** Memory allocation failed */
+#define MBEDTLS_ERR_SSL_ALLOC_FAILED                      -0x7F00
+/** Hardware acceleration function returned with error */
+#define MBEDTLS_ERR_SSL_HW_ACCEL_FAILED                   -0x7F80
+/** Hardware acceleration function skipped / left alone data */
+#define MBEDTLS_ERR_SSL_HW_ACCEL_FALLTHROUGH              -0x6F80
+/** Processing of the compression / decompression failed */
+#define MBEDTLS_ERR_SSL_COMPRESSION_FAILED                -0x6F00
+/** Handshake protocol not within min/max boundaries */
+#define MBEDTLS_ERR_SSL_BAD_HS_PROTOCOL_VERSION           -0x6E80
+/** Processing of the NewSessionTicket handshake message failed. */
+#define MBEDTLS_ERR_SSL_BAD_HS_NEW_SESSION_TICKET         -0x6E00
+/** Session ticket has expired. */
+#define MBEDTLS_ERR_SSL_SESSION_TICKET_EXPIRED            -0x6D80
+/** Public key type mismatch (eg, asked for RSA key exchange and presented EC key) */
+#define MBEDTLS_ERR_SSL_PK_TYPE_MISMATCH                  -0x6D00
+/** Unknown identity received (eg, PSK identity) */
+#define MBEDTLS_ERR_SSL_UNKNOWN_IDENTITY                  -0x6C80
+/** Internal error (eg, unexpected failure in lower-level module) */
+#define MBEDTLS_ERR_SSL_INTERNAL_ERROR                    -0x6C00
+/** A counter would wrap (eg, too many messages exchanged). */
+#define MBEDTLS_ERR_SSL_COUNTER_WRAPPING                  -0x6B80
+/** Unexpected message at ServerHello in renegotiation. */
+#define MBEDTLS_ERR_SSL_WAITING_SERVER_HELLO_RENEGO       -0x6B00
+/** DTLS client must retry for hello verification */
+#define MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED             -0x6A80
+/** A buffer is too small to receive or write a message */
+#define MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL                  -0x6A00
+/** None of the common ciphersuites is usable (eg, no suitable certificate, see debug messages). */
+#define MBEDTLS_ERR_SSL_NO_USABLE_CIPHERSUITE             -0x6980
+/** No data of requested type currently available on underlying transport. */
+#define MBEDTLS_ERR_SSL_WANT_READ                         -0x6900
+/** Connection requires a write call. */
+#define MBEDTLS_ERR_SSL_WANT_WRITE                        -0x6880
+/** The operation timed out. */
+#define MBEDTLS_ERR_SSL_TIMEOUT                           -0x6800
+/** The client initiated a reconnect from the same port. */
+#define MBEDTLS_ERR_SSL_CLIENT_RECONNECT                  -0x6780
+/** Record header looks valid but is not expected. */
+#define MBEDTLS_ERR_SSL_UNEXPECTED_RECORD                 -0x6700
+/** The alert message received indicates a non-fatal error. */
+#define MBEDTLS_ERR_SSL_NON_FATAL                         -0x6680
+/** Couldn't set the hash for verifying CertificateVerify */
+#define MBEDTLS_ERR_SSL_INVALID_VERIFY_HASH               -0x6600
+/** Internal-only message signaling that further message-processing should be done */
+#define MBEDTLS_ERR_SSL_CONTINUE_PROCESSING               -0x6580
+/** The asynchronous operation is not completed yet. */
+#define MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS                 -0x6500
+/** Internal-only message signaling that a message arrived early. */
+#define MBEDTLS_ERR_SSL_EARLY_MESSAGE                     -0x6480
+/** An encrypted DTLS-frame with an unexpected CID was received. */
+#define MBEDTLS_ERR_SSL_UNEXPECTED_CID                    -0x6000
+/** An operation failed due to an unexpected version or configuration. */
+#define MBEDTLS_ERR_SSL_VERSION_MISMATCH                  -0x5F00
+/** A cryptographic operation is in progress. Try again later. */
+#define MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS                -0x7000
+/** Invalid value in SSL config */
+#define MBEDTLS_ERR_SSL_BAD_CONFIG                        -0x5E80
 
 /*
  * Various constants
@@ -412,8 +470,14 @@
 
 /* The value of the CID extension is still TBD as of
  * draft-ietf-tls-dtls-connection-id-05
- * (https://tools.ietf.org/html/draft-ietf-tls-dtls-connection-id-05) */
+ * (https://tools.ietf.org/html/draft-ietf-tls-dtls-connection-id-05).
+ *
+ * A future minor revision of Mbed TLS may change the default value of
+ * this option to match evolving standards and usage.
+ */
+#if !defined(MBEDTLS_TLS_EXT_CID)
 #define MBEDTLS_TLS_EXT_CID                        254 /* TBD */
+#endif
 
 #define MBEDTLS_TLS_EXT_ECJPAKE_KKPP               256 /* experimental */
 
@@ -536,10 +600,11 @@
  * \param buf      Buffer to write the received data to
  * \param len      Length of the receive buffer
  *
- * \return         The callback must return the number of bytes received,
- *                 or a non-zero error code.
- *                 If performing non-blocking I/O, \c MBEDTLS_ERR_SSL_WANT_READ
+ * \returns        If data has been received, the positive number of bytes received.
+ * \returns        \c 0 if the connection has been closed.
+ * \returns        If performing non-blocking I/O, \c MBEDTLS_ERR_SSL_WANT_READ
  *                 must be returned when the operation would block.
+ * \returns        Another negative error code on other kinds of failures.
  *
  * \note           The callback may receive fewer bytes than the length of the
  *                 buffer. It must always return the number of bytes actually
@@ -909,6 +974,10 @@
  */
 struct mbedtls_ssl_session
 {
+#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH)
+    unsigned char mfl_code;     /*!< MaxFragmentLength negotiated by peer */
+#endif /* MBEDTLS_SSL_MAX_FRAGMENT_LENGTH */
+
 #if defined(MBEDTLS_HAVE_TIME)
     mbedtls_time_t start;       /*!< starting time      */
 #endif
@@ -937,10 +1006,6 @@
     uint32_t ticket_lifetime;   /*!< ticket lifetime hint    */
 #endif /* MBEDTLS_SSL_SESSION_TICKETS && MBEDTLS_SSL_CLI_C */
 
-#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH)
-    unsigned char mfl_code;     /*!< MaxFragmentLength negotiated by peer */
-#endif /* MBEDTLS_SSL_MAX_FRAGMENT_LENGTH */
-
 #if defined(MBEDTLS_SSL_TRUNCATED_HMAC)
     int trunc_hmac;             /*!< flag for truncated hmac activation   */
 #endif /* MBEDTLS_SSL_TRUNCATED_HMAC */
@@ -955,7 +1020,98 @@
  */
 struct mbedtls_ssl_config
 {
-    /* Group items by size (largest first) to minimize padding overhead */
+    /* Group items by size and reorder them to maximize usage of immediate offset access.    */
+
+    /*
+     * Numerical settings (char)
+     */
+
+    unsigned char max_major_ver;    /*!< max. major version used            */
+    unsigned char max_minor_ver;    /*!< max. minor version used            */
+    unsigned char min_major_ver;    /*!< min. major version used            */
+    unsigned char min_minor_ver;    /*!< min. minor version used            */
+
+    /*
+     * Flags (could be bit-fields to save RAM, but separate bytes make
+     * the code smaller on architectures with an instruction for direct
+     * byte access).
+     */
+
+    uint8_t endpoint /*bool*/;      /*!< 0: client, 1: server               */
+    uint8_t transport /*bool*/;     /*!< stream (TLS) or datagram (DTLS)    */
+    uint8_t authmode /*2 bits*/;    /*!< MBEDTLS_SSL_VERIFY_XXX             */
+    /* needed even with renego disabled for LEGACY_BREAK_HANDSHAKE          */
+    uint8_t allow_legacy_renegotiation /*2 bits*/; /*!< MBEDTLS_LEGACY_XXX  */
+#if defined(MBEDTLS_ARC4_C)
+    uint8_t arc4_disabled /*bool*/; /*!< blacklist RC4 ciphersuites?        */
+#endif
+#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH)
+    uint8_t mfl_code /*3 bits*/;    /*!< desired fragment length            */
+#endif
+#if defined(MBEDTLS_SSL_ENCRYPT_THEN_MAC)
+    uint8_t encrypt_then_mac /*bool*/;  /*!< negotiate encrypt-then-mac?    */
+#endif
+#if defined(MBEDTLS_SSL_EXTENDED_MASTER_SECRET)
+    uint8_t extended_ms /*bool*/;   /*!< negotiate extended master secret?  */
+#endif
+#if defined(MBEDTLS_SSL_DTLS_ANTI_REPLAY)
+    uint8_t anti_replay /*bool*/;   /*!< detect and prevent replay?         */
+#endif
+#if defined(MBEDTLS_SSL_CBC_RECORD_SPLITTING)
+    uint8_t cbc_record_splitting /*bool*/;  /*!< do cbc record splitting    */
+#endif
+#if defined(MBEDTLS_SSL_RENEGOTIATION)
+    uint8_t disable_renegotiation /*bool*/; /*!< disable renegotiation?     */
+#endif
+#if defined(MBEDTLS_SSL_TRUNCATED_HMAC)
+    uint8_t trunc_hmac /*bool*/;    /*!< negotiate truncated hmac?          */
+#endif
+#if defined(MBEDTLS_SSL_SESSION_TICKETS)
+    uint8_t session_tickets /*bool*/;   /*!< use session tickets?           */
+#endif
+#if defined(MBEDTLS_SSL_FALLBACK_SCSV) && defined(MBEDTLS_SSL_CLI_C)
+    uint8_t fallback /*bool*/;      /*!< is this a fallback?                */
+#endif
+#if defined(MBEDTLS_SSL_SRV_C)
+    uint8_t cert_req_ca_list /*bool*/;  /*!< enable sending CA list in
+                                          Certificate Request messages?     */
+#endif
+#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID)
+    uint8_t ignore_unexpected_cid /*bool*/; /*!< Determines whether DTLS
+                                             *   record with unexpected CID
+                                             *   should lead to failure.    */
+#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */
+#if defined(MBEDTLS_SSL_DTLS_SRTP)
+    uint8_t dtls_srtp_mki_support /*bool*/; /*!< support having mki_value
+                                                 in the use_srtp extension? */
+#endif
+
+    /*
+     * Numerical settings (int or larger)
+     */
+
+    uint32_t read_timeout;          /*!< timeout for mbedtls_ssl_read (ms)  */
+
+#if defined(MBEDTLS_SSL_PROTO_DTLS)
+    uint32_t hs_timeout_min;        /*!< initial value of the handshake
+                                         retransmission timeout (ms)        */
+    uint32_t hs_timeout_max;        /*!< maximum value of the handshake
+                                         retransmission timeout (ms)        */
+#endif
+
+#if defined(MBEDTLS_SSL_RENEGOTIATION)
+    int renego_max_records;         /*!< grace period for renegotiation     */
+    unsigned char renego_period[8]; /*!< value of the record counters
+                                         that triggers renegotiation        */
+#endif
+
+#if defined(MBEDTLS_SSL_DTLS_BADMAC_LIMIT)
+    unsigned int badmac_limit;      /*!< limit of records with a bad MAC    */
+#endif
+
+#if defined(MBEDTLS_DHM_C) && defined(MBEDTLS_SSL_CLI_C)
+    unsigned int dhm_min_bitlen;    /*!< min. bit length of the DHM prime   */
+#endif
 
     /*
      * Pointers
@@ -1109,91 +1265,6 @@
     /*! number of supported profiles */
     size_t dtls_srtp_profile_list_len;
 #endif /* MBEDTLS_SSL_DTLS_SRTP */
-
-    /*
-     * Numerical settings (int then char)
-     */
-
-    uint32_t read_timeout;          /*!< timeout for mbedtls_ssl_read (ms)  */
-
-#if defined(MBEDTLS_SSL_PROTO_DTLS)
-    uint32_t hs_timeout_min;        /*!< initial value of the handshake
-                                         retransmission timeout (ms)        */
-    uint32_t hs_timeout_max;        /*!< maximum value of the handshake
-                                         retransmission timeout (ms)        */
-#endif
-
-#if defined(MBEDTLS_SSL_RENEGOTIATION)
-    int renego_max_records;         /*!< grace period for renegotiation     */
-    unsigned char renego_period[8]; /*!< value of the record counters
-                                         that triggers renegotiation        */
-#endif
-
-#if defined(MBEDTLS_SSL_DTLS_BADMAC_LIMIT)
-    unsigned int badmac_limit;      /*!< limit of records with a bad MAC    */
-#endif
-
-#if defined(MBEDTLS_DHM_C) && defined(MBEDTLS_SSL_CLI_C)
-    unsigned int dhm_min_bitlen;    /*!< min. bit length of the DHM prime   */
-#endif
-
-    unsigned char max_major_ver;    /*!< max. major version used            */
-    unsigned char max_minor_ver;    /*!< max. minor version used            */
-    unsigned char min_major_ver;    /*!< min. major version used            */
-    unsigned char min_minor_ver;    /*!< min. minor version used            */
-
-    /*
-     * Flags (bitfields)
-     */
-
-    unsigned int endpoint : 1;      /*!< 0: client, 1: server               */
-    unsigned int transport : 1;     /*!< stream (TLS) or datagram (DTLS)    */
-    unsigned int authmode : 2;      /*!< MBEDTLS_SSL_VERIFY_XXX             */
-    /* needed even with renego disabled for LEGACY_BREAK_HANDSHAKE          */
-    unsigned int allow_legacy_renegotiation : 2 ; /*!< MBEDTLS_LEGACY_XXX   */
-#if defined(MBEDTLS_ARC4_C)
-    unsigned int arc4_disabled : 1; /*!< blacklist RC4 ciphersuites?        */
-#endif
-#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH)
-    unsigned int mfl_code : 3;      /*!< desired fragment length            */
-#endif
-#if defined(MBEDTLS_SSL_ENCRYPT_THEN_MAC)
-    unsigned int encrypt_then_mac : 1 ; /*!< negotiate encrypt-then-mac?    */
-#endif
-#if defined(MBEDTLS_SSL_EXTENDED_MASTER_SECRET)
-    unsigned int extended_ms : 1;   /*!< negotiate extended master secret?  */
-#endif
-#if defined(MBEDTLS_SSL_DTLS_ANTI_REPLAY)
-    unsigned int anti_replay : 1;   /*!< detect and prevent replay?         */
-#endif
-#if defined(MBEDTLS_SSL_CBC_RECORD_SPLITTING)
-    unsigned int cbc_record_splitting : 1;  /*!< do cbc record splitting    */
-#endif
-#if defined(MBEDTLS_SSL_RENEGOTIATION)
-    unsigned int disable_renegotiation : 1; /*!< disable renegotiation?     */
-#endif
-#if defined(MBEDTLS_SSL_TRUNCATED_HMAC)
-    unsigned int trunc_hmac : 1;    /*!< negotiate truncated hmac?          */
-#endif
-#if defined(MBEDTLS_SSL_SESSION_TICKETS)
-    unsigned int session_tickets : 1;   /*!< use session tickets?           */
-#endif
-#if defined(MBEDTLS_SSL_FALLBACK_SCSV) && defined(MBEDTLS_SSL_CLI_C)
-    unsigned int fallback : 1;      /*!< is this a fallback?                */
-#endif
-#if defined(MBEDTLS_SSL_SRV_C)
-    unsigned int cert_req_ca_list : 1;  /*!< enable sending CA list in
-                                          Certificate Request messages?     */
-#endif
-#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID)
-    unsigned int ignore_unexpected_cid : 1; /*!< Determines whether DTLS
-                                             *   record with unexpected CID
-                                             *   should lead to failure.    */
-#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */
-#if defined(MBEDTLS_SSL_DTLS_SRTP)
-    unsigned int dtls_srtp_mki_support : 1; /* support having mki_value
-                                               in the use_srtp extension     */
-#endif
 };
 
 struct mbedtls_ssl_context
@@ -2987,7 +3058,9 @@
 #if defined(MBEDTLS_ECP_C)
 /**
  * \brief          Set the allowed curves in order of preference.
- *                 (Default: all defined curves.)
+ *                 (Default: all defined curves in order of decreasing size,
+ *                 except that Montgomery curves come last. This order
+ *                 is likely to change in a future version.)
  *
  *                 On server: this only affects selection of the ECDHE curve;
  *                 the curves used for ECDH and ECDSA are determined by the
@@ -3019,7 +3092,9 @@
 #if defined(MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED)
 /**
  * \brief          Set the allowed hashes for signatures during the handshake.
- *                 (Default: all available hashes except MD5.)
+ *                 (Default: all SHA-2 hashes, largest first. Also SHA-1 if
+ *                 the compile-time option
+ *                 `MBEDTLS_TLS_DEFAULT_ALLOW_SHA1_IN_KEY_EXCHANGE` is enabled.)
  *
  * \note           This only affects which hashes are offered and can be used
  *                 for signatures during the handshake. Hashes for message
@@ -4327,7 +4402,7 @@
 /**
  * \brief          TLS-PRF function for key derivation.
  *
- * \param prf      The tls_prf type funtion type to be used.
+ * \param prf      The tls_prf type function type to be used.
  * \param secret   Secret for the key derivation function.
  * \param slen     Length of the secret.
  * \param label    String label for the key derivation function,
@@ -4337,7 +4412,7 @@
  * \param dstbuf   The buffer holding the derived key.
  * \param dlen     Length of the output buffer.
  *
- * \return         0 on sucess. An SSL specific error on failure.
+ * \return         0 on success. An SSL specific error on failure.
  */
 int  mbedtls_ssl_tls_prf( const mbedtls_tls_prf_types prf,
                           const unsigned char *secret, size_t slen,
diff --git a/third_party/mbedtls/repo/include/mbedtls/ssl_internal.h b/third_party/mbedtls/repo/include/mbedtls/ssl_internal.h
index 577c959..6913dc0 100644
--- a/third_party/mbedtls/repo/include/mbedtls/ssl_internal.h
+++ b/third_party/mbedtls/repo/include/mbedtls/ssl_internal.h
@@ -275,26 +275,26 @@
 #endif
 
 #if defined(MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH)
-static inline uint32_t mbedtls_ssl_get_output_buflen( const mbedtls_ssl_context *ctx )
+static inline size_t mbedtls_ssl_get_output_buflen( const mbedtls_ssl_context *ctx )
 {
 #if defined (MBEDTLS_SSL_DTLS_CONNECTION_ID)
-    return (uint32_t) mbedtls_ssl_get_output_max_frag_len( ctx )
+    return mbedtls_ssl_get_output_max_frag_len( ctx )
                + MBEDTLS_SSL_HEADER_LEN + MBEDTLS_SSL_PAYLOAD_OVERHEAD
                + MBEDTLS_SSL_CID_OUT_LEN_MAX;
 #else
-    return (uint32_t) mbedtls_ssl_get_output_max_frag_len( ctx )
+    return mbedtls_ssl_get_output_max_frag_len( ctx )
                + MBEDTLS_SSL_HEADER_LEN + MBEDTLS_SSL_PAYLOAD_OVERHEAD;
 #endif
 }
 
-static inline uint32_t mbedtls_ssl_get_input_buflen( const mbedtls_ssl_context *ctx )
+static inline size_t mbedtls_ssl_get_input_buflen( const mbedtls_ssl_context *ctx )
 {
 #if defined (MBEDTLS_SSL_DTLS_CONNECTION_ID)
-    return (uint32_t) mbedtls_ssl_get_input_max_frag_len( ctx )
+    return mbedtls_ssl_get_input_max_frag_len( ctx )
                + MBEDTLS_SSL_HEADER_LEN + MBEDTLS_SSL_PAYLOAD_OVERHEAD
                + MBEDTLS_SSL_CID_IN_LEN_MAX;
 #else
-    return (uint32_t) mbedtls_ssl_get_input_max_frag_len( ctx )
+    return mbedtls_ssl_get_input_max_frag_len( ctx )
                + MBEDTLS_SSL_HEADER_LEN + MBEDTLS_SSL_PAYLOAD_OVERHEAD;
 #endif
 }
@@ -430,13 +430,63 @@
      * Handshake specific crypto variables
      */
 
-#if defined(MBEDTLS_SSL_PROTO_TLS1_2) && \
+    uint8_t max_major_ver;              /*!< max. major version client*/
+    uint8_t max_minor_ver;              /*!< max. minor version client*/
+    uint8_t resume;                     /*!<  session resume indicator*/
+    uint8_t cli_exts;                   /*!< client extension presence*/
+
+#if defined(MBEDTLS_X509_CRT_PARSE_C) &&        \
+    defined(MBEDTLS_SSL_SERVER_NAME_INDICATION)
+    uint8_t sni_authmode;               /*!< authmode from SNI callback     */
+#endif
+
+#if defined(MBEDTLS_SSL_SESSION_TICKETS)
+    uint8_t new_session_ticket;         /*!< use NewSessionTicket?    */
+#endif /* MBEDTLS_SSL_SESSION_TICKETS */
+
+#if defined(MBEDTLS_SSL_EXTENDED_MASTER_SECRET)
+    uint8_t extended_ms;                /*!< use Extended Master Secret? */
+#endif
+
+#if defined(MBEDTLS_SSL_ASYNC_PRIVATE)
+    uint8_t async_in_progress;          /*!< an asynchronous operation is in progress */
+#endif /* MBEDTLS_SSL_ASYNC_PRIVATE */
+
+#if defined(MBEDTLS_SSL_PROTO_DTLS)
+    unsigned char retransmit_state;     /*!<  Retransmission state           */
+#endif
+
+#if defined(MBEDTLS_SSL_ECP_RESTARTABLE_ENABLED)
+    uint8_t ecrs_enabled;               /*!< Handshake supports EC restart? */
+    enum { /* this complements ssl->state with info on intra-state operations */
+        ssl_ecrs_none = 0,              /*!< nothing going on (yet)         */
+        ssl_ecrs_crt_verify,            /*!< Certificate: crt_verify()      */
+        ssl_ecrs_ske_start_processing,  /*!< ServerKeyExchange: pk_verify() */
+        ssl_ecrs_cke_ecdh_calc_secret,  /*!< ClientKeyExchange: ECDH step 2 */
+        ssl_ecrs_crt_vrfy_sign,         /*!< CertificateVerify: pk_sign()   */
+    } ecrs_state;                       /*!< current (or last) operation    */
+    mbedtls_x509_crt *ecrs_peer_cert;   /*!< The peer's CRT chain.          */
+    size_t ecrs_n;                      /*!< place for saving a length      */
+#endif
+
+#if defined(MBEDTLS_SSL_PROTO_TLS1_2) &&                \
     defined(MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED)
     mbedtls_ssl_sig_hash_set_t hash_algs;             /*!<  Set of suitable sig-hash pairs */
 #endif
+
+    size_t pmslen;                      /*!<  premaster length        */
+
+    mbedtls_ssl_ciphersuite_t const *ciphersuite_info;
+
+    void (*update_checksum)(mbedtls_ssl_context *, const unsigned char *, size_t);
+    void (*calc_verify)(const mbedtls_ssl_context *, unsigned char *, size_t *);
+    void (*calc_finished)(mbedtls_ssl_context *, unsigned char *, int);
+    mbedtls_ssl_tls_prf_cb *tls_prf;
+
 #if defined(MBEDTLS_DHM_C)
     mbedtls_dhm_context dhm_ctx;                /*!<  DHM key exchange        */
 #endif
+
 /* Adding guard for MBEDTLS_ECDSA_C to ensure no compile errors due
  * to guards also being in ssl_srv.c and ssl_cli.c. There is a gap
  * in functionality that access to ecdh_ctx structure is needed for
@@ -461,10 +511,12 @@
     size_t ecjpake_cache_len;                   /*!< Length of cached data */
 #endif
 #endif /* MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */
-#if defined(MBEDTLS_ECDH_C) || defined(MBEDTLS_ECDSA_C) || \
+
+#if defined(MBEDTLS_ECDH_C) || defined(MBEDTLS_ECDSA_C) ||      \
     defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)
     const mbedtls_ecp_curve_info **curves;      /*!<  Supported elliptic curves */
 #endif
+
 #if defined(MBEDTLS_KEY_EXCHANGE_SOME_PSK_ENABLED)
 #if defined(MBEDTLS_USE_PSA_CRYPTO)
     psa_key_id_t psk_opaque;            /*!< Opaque PSK from the callback   */
@@ -472,65 +524,26 @@
     unsigned char *psk;                 /*!<  PSK from the callback         */
     size_t psk_len;                     /*!<  Length of PSK from callback   */
 #endif /* MBEDTLS_KEY_EXCHANGE_SOME_PSK_ENABLED */
+
 #if defined(MBEDTLS_X509_CRT_PARSE_C)
     mbedtls_ssl_key_cert *key_cert;     /*!< chosen key/cert pair (server)  */
 #if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION)
-    int sni_authmode;                   /*!< authmode from SNI callback     */
     mbedtls_ssl_key_cert *sni_key_cert; /*!< key/cert list from SNI         */
     mbedtls_x509_crt *sni_ca_chain;     /*!< trusted CAs from SNI callback  */
     mbedtls_x509_crl *sni_ca_crl;       /*!< trusted CAs CRLs from SNI      */
 #endif /* MBEDTLS_SSL_SERVER_NAME_INDICATION */
 #endif /* MBEDTLS_X509_CRT_PARSE_C */
+
 #if defined(MBEDTLS_SSL_ECP_RESTARTABLE_ENABLED)
-    int ecrs_enabled;                   /*!< Handshake supports EC restart? */
     mbedtls_x509_crt_restart_ctx ecrs_ctx;  /*!< restart context            */
-    enum { /* this complements ssl->state with info on intra-state operations */
-        ssl_ecrs_none = 0,              /*!< nothing going on (yet)         */
-        ssl_ecrs_crt_verify,            /*!< Certificate: crt_verify()      */
-        ssl_ecrs_ske_start_processing,  /*!< ServerKeyExchange: pk_verify() */
-        ssl_ecrs_cke_ecdh_calc_secret,  /*!< ClientKeyExchange: ECDH step 2 */
-        ssl_ecrs_crt_vrfy_sign,         /*!< CertificateVerify: pk_sign()   */
-    } ecrs_state;                       /*!< current (or last) operation    */
-    mbedtls_x509_crt *ecrs_peer_cert;   /*!< The peer's CRT chain.          */
-    size_t ecrs_n;                      /*!< place for saving a length      */
 #endif
-#if defined(MBEDTLS_X509_CRT_PARSE_C) && \
+
+#if defined(MBEDTLS_X509_CRT_PARSE_C) &&        \
     !defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
     mbedtls_pk_context peer_pubkey;     /*!< The public key from the peer.  */
 #endif /* MBEDTLS_X509_CRT_PARSE_C && !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
+
 #if defined(MBEDTLS_SSL_PROTO_DTLS)
-    unsigned int out_msg_seq;           /*!<  Outgoing handshake sequence number */
-    unsigned int in_msg_seq;            /*!<  Incoming handshake sequence number */
-
-    unsigned char *verify_cookie;       /*!<  Cli: HelloVerifyRequest cookie
-                                              Srv: unused                    */
-    unsigned char verify_cookie_len;    /*!<  Cli: cookie length
-                                              Srv: flag for sending a cookie */
-
-    uint32_t retransmit_timeout;        /*!<  Current value of timeout       */
-    unsigned char retransmit_state;     /*!<  Retransmission state           */
-    mbedtls_ssl_flight_item *flight;    /*!<  Current outgoing flight        */
-    mbedtls_ssl_flight_item *cur_msg;   /*!<  Current message in flight      */
-    unsigned char *cur_msg_p;           /*!<  Position in current message    */
-    unsigned int in_flight_start_seq;   /*!<  Minimum message sequence in the
-                                              flight being received          */
-    mbedtls_ssl_transform *alt_transform_out;   /*!<  Alternative transform for
-                                              resending messages             */
-    unsigned char alt_out_ctr[8];       /*!<  Alternative record epoch/counter
-                                              for resending messages         */
-
-#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID)
-    /* The state of CID configuration in this handshake. */
-
-    uint8_t cid_in_use; /*!< This indicates whether the use of the CID extension
-                         *   has been negotiated. Possible values are
-                         *   #MBEDTLS_SSL_CID_ENABLED and
-                         *   #MBEDTLS_SSL_CID_DISABLED. */
-    unsigned char peer_cid[ MBEDTLS_SSL_CID_OUT_LEN_MAX ]; /*! The peer's CID */
-    uint8_t peer_cid_len;                                  /*!< The length of
-                                                            *   \c peer_cid.  */
-#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */
-
     struct
     {
         size_t total_bytes_buffered; /*!< Cumulative size of heap allocated
@@ -557,6 +570,37 @@
 
     } buffering;
 
+    unsigned int out_msg_seq;           /*!<  Outgoing handshake sequence number */
+    unsigned int in_msg_seq;            /*!<  Incoming handshake sequence number */
+
+    unsigned char *verify_cookie;       /*!<  Cli: HelloVerifyRequest cookie
+                                              Srv: unused                    */
+    unsigned char verify_cookie_len;    /*!<  Cli: cookie length
+                                              Srv: flag for sending a cookie */
+
+    uint32_t retransmit_timeout;        /*!<  Current value of timeout       */
+    mbedtls_ssl_flight_item *flight;    /*!<  Current outgoing flight        */
+    mbedtls_ssl_flight_item *cur_msg;   /*!<  Current message in flight      */
+    unsigned char *cur_msg_p;           /*!<  Position in current message    */
+    unsigned int in_flight_start_seq;   /*!<  Minimum message sequence in the
+                                              flight being received          */
+    mbedtls_ssl_transform *alt_transform_out;   /*!<  Alternative transform for
+                                              resending messages             */
+    unsigned char alt_out_ctr[8];       /*!<  Alternative record epoch/counter
+                                              for resending messages         */
+
+#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID)
+    /* The state of CID configuration in this handshake. */
+
+    uint8_t cid_in_use; /*!< This indicates whether the use of the CID extension
+                         *   has been negotiated. Possible values are
+                         *   #MBEDTLS_SSL_CID_ENABLED and
+                         *   #MBEDTLS_SSL_CID_DISABLED. */
+    unsigned char peer_cid[ MBEDTLS_SSL_CID_OUT_LEN_MAX ]; /*! The peer's CID */
+    uint8_t peer_cid_len;                                  /*!< The length of
+                                                            *   \c peer_cid.  */
+#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */
+
     uint16_t mtu;                       /*!<  Handshake mtu, used to fragment outgoing messages */
 #endif /* MBEDTLS_SSL_PROTO_DTLS */
 
@@ -565,8 +609,8 @@
      */
 #if defined(MBEDTLS_SSL_PROTO_SSL3) || defined(MBEDTLS_SSL_PROTO_TLS1) || \
     defined(MBEDTLS_SSL_PROTO_TLS1_1)
-       mbedtls_md5_context fin_md5;
-      mbedtls_sha1_context fin_sha1;
+    mbedtls_md5_context fin_md5;
+    mbedtls_sha1_context fin_sha1;
 #endif
 #if defined(MBEDTLS_SSL_PROTO_TLS1_2)
 #if defined(MBEDTLS_SHA256_C)
@@ -585,35 +629,10 @@
 #endif
 #endif /* MBEDTLS_SSL_PROTO_TLS1_2 */
 
-    void (*update_checksum)(mbedtls_ssl_context *, const unsigned char *, size_t);
-    void (*calc_verify)(const mbedtls_ssl_context *, unsigned char *, size_t *);
-    void (*calc_finished)(mbedtls_ssl_context *, unsigned char *, int);
-    mbedtls_ssl_tls_prf_cb *tls_prf;
-
-    mbedtls_ssl_ciphersuite_t const *ciphersuite_info;
-
-    size_t pmslen;                      /*!<  premaster length        */
-
     unsigned char randbytes[64];        /*!<  random bytes            */
     unsigned char premaster[MBEDTLS_PREMASTER_SIZE];
                                         /*!<  premaster secret        */
 
-    int resume;                         /*!<  session resume indicator*/
-    int max_major_ver;                  /*!< max. major version client*/
-    int max_minor_ver;                  /*!< max. minor version client*/
-    int cli_exts;                       /*!< client extension presence*/
-
-#if defined(MBEDTLS_SSL_SESSION_TICKETS)
-    int new_session_ticket;             /*!< use NewSessionTicket?    */
-#endif /* MBEDTLS_SSL_SESSION_TICKETS */
-#if defined(MBEDTLS_SSL_EXTENDED_MASTER_SECRET)
-    int extended_ms;                    /*!< use Extended Master Secret? */
-#endif
-
-#if defined(MBEDTLS_SSL_ASYNC_PRIVATE)
-    unsigned int async_in_progress : 1; /*!< an asynchronous operation is in progress */
-#endif /* MBEDTLS_SSL_ASYNC_PRIVATE */
-
 #if defined(MBEDTLS_SSL_ASYNC_PRIVATE)
     /** Asynchronous operation context. This field is meant for use by the
      * asynchronous operation callbacks (mbedtls_ssl_config::f_async_sign_start,
@@ -1212,26 +1231,6 @@
 int mbedtls_ssl_session_copy( mbedtls_ssl_session *dst,
                               const mbedtls_ssl_session *src );
 
-/* constant-time buffer comparison */
-static inline int mbedtls_ssl_safer_memcmp( const void *a, const void *b, size_t n )
-{
-    size_t i;
-    volatile const unsigned char *A = (volatile const unsigned char *) a;
-    volatile const unsigned char *B = (volatile const unsigned char *) b;
-    volatile unsigned char diff = 0;
-
-    for( i = 0; i < n; i++ )
-    {
-        /* Read volatile data in order before computing diff.
-         * This avoids IAR compiler warning:
-         * 'the order of volatile accesses is undefined ..' */
-        unsigned char x = A[i], y = B[i];
-        diff |= x ^ y;
-    }
-
-    return( diff );
-}
-
 #if defined(MBEDTLS_SSL_PROTO_SSL3) || defined(MBEDTLS_SSL_PROTO_TLS1) || \
     defined(MBEDTLS_SSL_PROTO_TLS1_1)
 int mbedtls_ssl_get_key_exchange_md_ssl_tls( mbedtls_ssl_context *ssl,
diff --git a/third_party/mbedtls/repo/include/mbedtls/ssl_ticket.h b/third_party/mbedtls/repo/include/mbedtls/ssl_ticket.h
index bf5fc97..a882eed 100644
--- a/third_party/mbedtls/repo/include/mbedtls/ssl_ticket.h
+++ b/third_party/mbedtls/repo/include/mbedtls/ssl_ticket.h
@@ -97,7 +97,7 @@
  *                  Recommended value: 86400 (one day).
  *
  * \note            It is highly recommended to select a cipher that is at
- *                  least as strong as the the strongest ciphersuite
+ *                  least as strong as the strongest ciphersuite
  *                  supported. Usually that means a 256-bit key.
  *
  * \note            The lifetime of the keys is twice the lifetime of tickets.
diff --git a/third_party/mbedtls/repo/include/mbedtls/threading.h b/third_party/mbedtls/repo/include/mbedtls/threading.h
index 8baf15a..d147c73 100644
--- a/third_party/mbedtls/repo/include/mbedtls/threading.h
+++ b/third_party/mbedtls/repo/include/mbedtls/threading.h
@@ -36,16 +36,22 @@
 
 /* MBEDTLS_ERR_THREADING_FEATURE_UNAVAILABLE is deprecated and should not be
  * used. */
-#define MBEDTLS_ERR_THREADING_FEATURE_UNAVAILABLE         -0x001A  /**< The selected feature is not available. */
+/** The selected feature is not available. */
+#define MBEDTLS_ERR_THREADING_FEATURE_UNAVAILABLE         -0x001A
 
-#define MBEDTLS_ERR_THREADING_BAD_INPUT_DATA              -0x001C  /**< Bad input parameters to function. */
-#define MBEDTLS_ERR_THREADING_MUTEX_ERROR                 -0x001E  /**< Locking / unlocking / free failed with error code. */
+/** Bad input parameters to function. */
+#define MBEDTLS_ERR_THREADING_BAD_INPUT_DATA              -0x001C
+/** Locking / unlocking / free failed with error code. */
+#define MBEDTLS_ERR_THREADING_MUTEX_ERROR                 -0x001E
 
 #if defined(MBEDTLS_THREADING_PTHREAD)
 #include <pthread.h>
 typedef struct mbedtls_threading_mutex_t
 {
     pthread_mutex_t mutex;
+    /* is_valid is 0 after a failed init or a free, and nonzero after a
+     * successful init. This field is not considered part of the public
+     * API of Mbed TLS and may change without notice. */
     char is_valid;
 } mbedtls_threading_mutex_t;
 #endif
diff --git a/third_party/mbedtls/repo/include/mbedtls/version.h b/third_party/mbedtls/repo/include/mbedtls/version.h
index 10c4316..b1a92b2 100644
--- a/third_party/mbedtls/repo/include/mbedtls/version.h
+++ b/third_party/mbedtls/repo/include/mbedtls/version.h
@@ -37,7 +37,7 @@
  * Major, Minor, Patchlevel
  */
 #define MBEDTLS_VERSION_MAJOR  2
-#define MBEDTLS_VERSION_MINOR  25
+#define MBEDTLS_VERSION_MINOR  28
 #define MBEDTLS_VERSION_PATCH  0
 
 /**
@@ -45,9 +45,9 @@
  *    MMNNPP00
  *    Major version | Minor version | Patch version
  */
-#define MBEDTLS_VERSION_NUMBER         0x02190000
-#define MBEDTLS_VERSION_STRING         "2.25.0"
-#define MBEDTLS_VERSION_STRING_FULL    "mbed TLS 2.25.0"
+#define MBEDTLS_VERSION_NUMBER         0x021C0000
+#define MBEDTLS_VERSION_STRING         "2.28.0"
+#define MBEDTLS_VERSION_STRING_FULL    "mbed TLS 2.28.0"
 
 #if defined(MBEDTLS_VERSION_C)
 
diff --git a/third_party/mbedtls/repo/include/mbedtls/x509.h b/third_party/mbedtls/repo/include/mbedtls/x509.h
index 08525e2..c177501 100644
--- a/third_party/mbedtls/repo/include/mbedtls/x509.h
+++ b/third_party/mbedtls/repo/include/mbedtls/x509.h
@@ -56,26 +56,46 @@
  * \name X509 Error codes
  * \{
  */
-#define MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE              -0x2080  /**< Unavailable feature, e.g. RSA hashing/encryption combination. */
-#define MBEDTLS_ERR_X509_UNKNOWN_OID                      -0x2100  /**< Requested OID is unknown. */
-#define MBEDTLS_ERR_X509_INVALID_FORMAT                   -0x2180  /**< The CRT/CRL/CSR format is invalid, e.g. different type expected. */
-#define MBEDTLS_ERR_X509_INVALID_VERSION                  -0x2200  /**< The CRT/CRL/CSR version element is invalid. */
-#define MBEDTLS_ERR_X509_INVALID_SERIAL                   -0x2280  /**< The serial tag or value is invalid. */
-#define MBEDTLS_ERR_X509_INVALID_ALG                      -0x2300  /**< The algorithm tag or value is invalid. */
-#define MBEDTLS_ERR_X509_INVALID_NAME                     -0x2380  /**< The name tag or value is invalid. */
-#define MBEDTLS_ERR_X509_INVALID_DATE                     -0x2400  /**< The date tag or value is invalid. */
-#define MBEDTLS_ERR_X509_INVALID_SIGNATURE                -0x2480  /**< The signature tag or value invalid. */
-#define MBEDTLS_ERR_X509_INVALID_EXTENSIONS               -0x2500  /**< The extension tag or value is invalid. */
-#define MBEDTLS_ERR_X509_UNKNOWN_VERSION                  -0x2580  /**< CRT/CRL/CSR has an unsupported version number. */
-#define MBEDTLS_ERR_X509_UNKNOWN_SIG_ALG                  -0x2600  /**< Signature algorithm (oid) is unsupported. */
-#define MBEDTLS_ERR_X509_SIG_MISMATCH                     -0x2680  /**< Signature algorithms do not match. (see \c ::mbedtls_x509_crt sig_oid) */
-#define MBEDTLS_ERR_X509_CERT_VERIFY_FAILED               -0x2700  /**< Certificate verification failed, e.g. CRL, CA or signature check failed. */
-#define MBEDTLS_ERR_X509_CERT_UNKNOWN_FORMAT              -0x2780  /**< Format not recognized as DER or PEM. */
-#define MBEDTLS_ERR_X509_BAD_INPUT_DATA                   -0x2800  /**< Input invalid. */
-#define MBEDTLS_ERR_X509_ALLOC_FAILED                     -0x2880  /**< Allocation of memory failed. */
-#define MBEDTLS_ERR_X509_FILE_IO_ERROR                    -0x2900  /**< Read/write of file failed. */
-#define MBEDTLS_ERR_X509_BUFFER_TOO_SMALL                 -0x2980  /**< Destination buffer is too small. */
-#define MBEDTLS_ERR_X509_FATAL_ERROR                      -0x3000  /**< A fatal error occurred, eg the chain is too long or the vrfy callback failed. */
+/** Unavailable feature, e.g. RSA hashing/encryption combination. */
+#define MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE              -0x2080
+/** Requested OID is unknown. */
+#define MBEDTLS_ERR_X509_UNKNOWN_OID                      -0x2100
+/** The CRT/CRL/CSR format is invalid, e.g. different type expected. */
+#define MBEDTLS_ERR_X509_INVALID_FORMAT                   -0x2180
+/** The CRT/CRL/CSR version element is invalid. */
+#define MBEDTLS_ERR_X509_INVALID_VERSION                  -0x2200
+/** The serial tag or value is invalid. */
+#define MBEDTLS_ERR_X509_INVALID_SERIAL                   -0x2280
+/** The algorithm tag or value is invalid. */
+#define MBEDTLS_ERR_X509_INVALID_ALG                      -0x2300
+/** The name tag or value is invalid. */
+#define MBEDTLS_ERR_X509_INVALID_NAME                     -0x2380
+/** The date tag or value is invalid. */
+#define MBEDTLS_ERR_X509_INVALID_DATE                     -0x2400
+/** The signature tag or value invalid. */
+#define MBEDTLS_ERR_X509_INVALID_SIGNATURE                -0x2480
+/** The extension tag or value is invalid. */
+#define MBEDTLS_ERR_X509_INVALID_EXTENSIONS               -0x2500
+/** CRT/CRL/CSR has an unsupported version number. */
+#define MBEDTLS_ERR_X509_UNKNOWN_VERSION                  -0x2580
+/** Signature algorithm (oid) is unsupported. */
+#define MBEDTLS_ERR_X509_UNKNOWN_SIG_ALG                  -0x2600
+/** Signature algorithms do not match. (see \c ::mbedtls_x509_crt sig_oid) */
+#define MBEDTLS_ERR_X509_SIG_MISMATCH                     -0x2680
+/** Certificate verification failed, e.g. CRL, CA or signature check failed. */
+#define MBEDTLS_ERR_X509_CERT_VERIFY_FAILED               -0x2700
+/** Format not recognized as DER or PEM. */
+#define MBEDTLS_ERR_X509_CERT_UNKNOWN_FORMAT              -0x2780
+/** Input invalid. */
+#define MBEDTLS_ERR_X509_BAD_INPUT_DATA                   -0x2800
+/** Allocation of memory failed. */
+#define MBEDTLS_ERR_X509_ALLOC_FAILED                     -0x2880
+/** Read/write of file failed. */
+#define MBEDTLS_ERR_X509_FILE_IO_ERROR                    -0x2900
+/** Destination buffer is too small. */
+#define MBEDTLS_ERR_X509_BUFFER_TOO_SMALL                 -0x2980
+/** A fatal error occurred, eg the chain is too long or the vrfy callback failed. */
+#define MBEDTLS_ERR_X509_FATAL_ERROR                      -0x3000
 /* \} name */
 
 /**
diff --git a/third_party/mbedtls/repo/include/mbedtls/x509_crt.h b/third_party/mbedtls/repo/include/mbedtls/x509_crt.h
index 8e389f8..64ccb43 100644
--- a/third_party/mbedtls/repo/include/mbedtls/x509_crt.h
+++ b/third_party/mbedtls/repo/include/mbedtls/x509_crt.h
@@ -263,12 +263,21 @@
 /**
  * Default security profile. Should provide a good balance between security
  * and compatibility with current deployments.
+ *
+ * This profile permits:
+ * - SHA2 hashes.
+ * - All supported elliptic curves.
+ * - RSA with 2048 bits and above.
+ *
+ * New minor versions of Mbed TLS may extend this profile, for example if
+ * new curves are added to the library. New minor versions of Mbed TLS will
+ * not reduce this profile unless serious security concerns require it.
  */
 extern const mbedtls_x509_crt_profile mbedtls_x509_crt_profile_default;
 
 /**
  * Expected next default profile. Recommended for new deployments.
- * Currently targets a 128-bit security level, except for RSA-2048.
+ * Currently targets a 128-bit security level, except for allowing RSA-2048.
  */
 extern const mbedtls_x509_crt_profile mbedtls_x509_crt_profile_next;
 
diff --git a/third_party/mbedtls/repo/include/mbedtls/xtea.h b/third_party/mbedtls/repo/include/mbedtls/xtea.h
index 473dd4b..4bdc711 100644
--- a/third_party/mbedtls/repo/include/mbedtls/xtea.h
+++ b/third_party/mbedtls/repo/include/mbedtls/xtea.h
@@ -34,10 +34,12 @@
 #define MBEDTLS_XTEA_ENCRYPT     1
 #define MBEDTLS_XTEA_DECRYPT     0
 
-#define MBEDTLS_ERR_XTEA_INVALID_INPUT_LENGTH             -0x0028  /**< The data input has an invalid length. */
+/** The data input has an invalid length. */
+#define MBEDTLS_ERR_XTEA_INVALID_INPUT_LENGTH             -0x0028
 
 /* MBEDTLS_ERR_XTEA_HW_ACCEL_FAILED is deprecated and should not be used. */
-#define MBEDTLS_ERR_XTEA_HW_ACCEL_FAILED                  -0x0029  /**< XTEA hardware accelerator failed. */
+/** XTEA hardware accelerator failed. */
+#define MBEDTLS_ERR_XTEA_HW_ACCEL_FAILED                  -0x0029
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/third_party/mbedtls/repo/include/psa/crypto.h b/third_party/mbedtls/repo/include/psa/crypto.h
index b41a20b..b0b57c3 100644
--- a/third_party/mbedtls/repo/include/psa/crypto.h
+++ b/third_party/mbedtls/repo/include/psa/crypto.h
@@ -90,10 +90,14 @@
  *
  * \retval #PSA_SUCCESS
  * \retval #PSA_ERROR_INSUFFICIENT_MEMORY
+ * \retval #PSA_ERROR_INSUFFICIENT_STORAGE
  * \retval #PSA_ERROR_COMMUNICATION_FAILURE
  * \retval #PSA_ERROR_HARDWARE_FAILURE
  * \retval #PSA_ERROR_CORRUPTION_DETECTED
  * \retval #PSA_ERROR_INSUFFICIENT_ENTROPY
+ * \retval #PSA_ERROR_STORAGE_FAILURE
+ * \retval #PSA_ERROR_DATA_INVALID
+ * \retval #PSA_ERROR_DATA_CORRUPT
  */
 psa_status_t psa_crypto_init(void);
 
@@ -155,10 +159,10 @@
  * the owner of a key.
  *
  * \param[out] attributes  The attribute structure to write to.
- * \param owner_id         The key owner identifier.
+ * \param owner            The key owner identifier.
  */
 static void mbedtls_set_key_owner_id( psa_key_attributes_t *attributes,
-                                      mbedtls_key_owner_id_t owner_id );
+                                      mbedtls_key_owner_id_t owner );
 #endif
 
 /** Set the location of a persistent key.
@@ -260,6 +264,14 @@
  * - An algorithm value permits this particular algorithm.
  * - An algorithm wildcard built from #PSA_ALG_ANY_HASH allows the specified
  *   signature scheme with any hash algorithm.
+ * - An algorithm built from #PSA_ALG_AT_LEAST_THIS_LENGTH_MAC allows
+ *   any MAC algorithm from the same base class (e.g. CMAC) which
+ *   generates/verifies a MAC length greater than or equal to the length
+ *   encoded in the wildcard algorithm.
+ * - An algorithm built from #PSA_ALG_AEAD_WITH_AT_LEAST_THIS_LENGTH_TAG
+ *   allows any AEAD algorithm from the same base class (e.g. CCM) which
+ *   generates/verifies a tag length greater than or equal to the length
+ *   encoded in the wildcard algorithm.
  *
  * This function overwrites any algorithm policy
  * previously set in \p attributes.
@@ -368,6 +380,8 @@
  * \retval #PSA_ERROR_COMMUNICATION_FAILURE
  * \retval #PSA_ERROR_CORRUPTION_DETECTED
  * \retval #PSA_ERROR_STORAGE_FAILURE
+ * \retval #PSA_ERROR_DATA_CORRUPT
+ * \retval #PSA_ERROR_DATA_INVALID
  * \retval #PSA_ERROR_BAD_STATE
  *         The library has not been previously initialized by psa_crypto_init().
  *         It is implementation-dependent whether a failure to initialize
@@ -501,6 +515,8 @@
  * \retval #PSA_ERROR_INSUFFICIENT_STORAGE
  * \retval #PSA_ERROR_COMMUNICATION_FAILURE
  * \retval #PSA_ERROR_HARDWARE_FAILURE
+ * \retval #PSA_ERROR_DATA_INVALID
+ * \retval #PSA_ERROR_DATA_CORRUPT
  * \retval #PSA_ERROR_STORAGE_FAILURE
  * \retval #PSA_ERROR_CORRUPTION_DETECTED
  * \retval #PSA_ERROR_BAD_STATE
@@ -540,6 +556,10 @@
  * \retval #PSA_ERROR_COMMUNICATION_FAILURE
  *         There was an failure in communication with the cryptoprocessor.
  *         The key material may still be present in the cryptoprocessor.
+ * \retval #PSA_ERROR_DATA_INVALID
+ *         This error is typically a result of either storage corruption on a
+ *         cleartext storage backend, or an attempt to read data that was
+ *         written by an incompatible version of the library.
  * \retval #PSA_ERROR_STORAGE_FAILURE
  *         The storage is corrupted. Implementations shall make a best effort
  *         to erase key material even in this stage, however applications
@@ -625,6 +645,8 @@
  * \retval #PSA_ERROR_INSUFFICIENT_MEMORY
  * \retval #PSA_ERROR_INSUFFICIENT_STORAGE
  * \retval #PSA_ERROR_COMMUNICATION_FAILURE
+ * \retval #PSA_ERROR_DATA_CORRUPT
+ * \retval #PSA_ERROR_DATA_INVALID
  * \retval #PSA_ERROR_STORAGE_FAILURE
  * \retval #PSA_ERROR_HARDWARE_FAILURE
  * \retval #PSA_ERROR_CORRUPTION_DETECTED
@@ -687,6 +709,8 @@
  *   For Weierstrass curves, this is the content of the `privateKey` field of
  *   the `ECPrivateKey` format defined by RFC 5915.  For Montgomery curves,
  *   the format is defined by RFC 7748, and output is masked according to §5.
+ *   For twisted Edwards curves, the private key is as defined by RFC 8032
+ *   (a 32-byte string for Edwards25519, a 57-byte string for Edwards448).
  * - For Diffie-Hellman key exchange key pairs (key types for which
  *   #PSA_KEY_TYPE_IS_DH_KEY_PAIR is true), the
  *   format is the representation of the private key `x` as a big-endian byte
@@ -713,7 +737,7 @@
  * \retval #PSA_ERROR_BUFFER_TOO_SMALL
  *         The size of the \p data buffer is too small. You can determine a
  *         sufficient buffer size by calling
- *         #PSA_KEY_EXPORT_MAX_SIZE(\c type, \c bits)
+ *         #PSA_EXPORT_KEY_OUTPUT_SIZE(\c type, \c bits)
  *         where \c type is the key type
  *         and \c bits is the key size in bits.
  * \retval #PSA_ERROR_COMMUNICATION_FAILURE
@@ -752,7 +776,12 @@
  *      modulus            INTEGER,    -- n
  *      publicExponent     INTEGER  }  -- e
  *   ```
- * - For elliptic curve public keys (key types for which
+ * - For elliptic curve keys on a twisted Edwards curve (key types for which
+ *   #PSA_KEY_TYPE_IS_ECC_PUBLIC_KEY is true and #PSA_KEY_TYPE_ECC_GET_FAMILY
+ *   returns #PSA_ECC_FAMILY_TWISTED_EDWARDS), the public key is as defined
+ *   by RFC 8032
+ *   (a 32-byte string for Edwards25519, a 57-byte string for Edwards448).
+ * - For other elliptic curve public keys (key types for which
  *   #PSA_KEY_TYPE_IS_ECC_PUBLIC_KEY is true), the format is the uncompressed
  *   representation defined by SEC1 &sect;2.3.3 as the content of an ECPoint.
  *   Let `m` be the bit size associated with the curve, i.e. the bit size of
@@ -783,7 +812,7 @@
  * \retval #PSA_ERROR_BUFFER_TOO_SMALL
  *         The size of the \p data buffer is too small. You can determine a
  *         sufficient buffer size by calling
- *         #PSA_KEY_EXPORT_MAX_SIZE(#PSA_KEY_TYPE_PUBLIC_KEY_OF_KEY_PAIR(\c type), \c bits)
+ *         #PSA_EXPORT_KEY_OUTPUT_SIZE(#PSA_KEY_TYPE_PUBLIC_KEY_OF_KEY_PAIR(\c type), \c bits)
  *         where \c type is the key type
  *         and \c bits is the key size in bits.
  * \retval #PSA_ERROR_COMMUNICATION_FAILURE
@@ -822,7 +851,7 @@
  * \param hash_size         Size of the \p hash buffer in bytes.
  * \param[out] hash_length  On success, the number of bytes
  *                          that make up the hash value. This is always
- *                          #PSA_HASH_SIZE(\p alg).
+ *                          #PSA_HASH_LENGTH(\p alg).
  *
  * \retval #PSA_SUCCESS
  *         Success.
@@ -1032,7 +1061,7 @@
  * \param hash_size             Size of the \p hash buffer in bytes.
  * \param[out] hash_length      On success, the number of bytes
  *                              that make up the hash value. This is always
- *                              #PSA_HASH_SIZE(\c alg) where \c alg is the
+ *                              #PSA_HASH_LENGTH(\c alg) where \c alg is the
  *                              hash algorithm that is calculated.
  *
  * \retval #PSA_SUCCESS
@@ -1041,7 +1070,7 @@
  *         The operation state is not valid (it must be active).
  * \retval #PSA_ERROR_BUFFER_TOO_SMALL
  *         The size of the \p hash buffer is too small. You can determine a
- *         sufficient buffer size by calling #PSA_HASH_SIZE(\c alg)
+ *         sufficient buffer size by calling #PSA_HASH_LENGTH(\c alg)
  *         where \c alg is the hash algorithm that is calculated.
  * \retval #PSA_ERROR_INSUFFICIENT_MEMORY
  * \retval #PSA_ERROR_COMMUNICATION_FAILURE
@@ -1479,7 +1508,7 @@
  * \param mac_size          Size of the \p mac buffer in bytes.
  * \param[out] mac_length   On success, the number of bytes
  *                          that make up the MAC value. This is always
- *                          #PSA_MAC_FINAL_SIZE(\c key_type, \c key_bits, \c alg)
+ *                          #PSA_MAC_LENGTH(\c key_type, \c key_bits, \c alg)
  *                          where \c key_type and \c key_bits are the type and
  *                          bit-size respectively of the key and \c alg is the
  *                          MAC algorithm that is calculated.
@@ -1491,7 +1520,7 @@
  *         operation).
  * \retval #PSA_ERROR_BUFFER_TOO_SMALL
  *         The size of the \p mac buffer is too small. You can determine a
- *         sufficient buffer size by calling PSA_MAC_FINAL_SIZE().
+ *         sufficient buffer size by calling PSA_MAC_LENGTH().
  * \retval #PSA_ERROR_INSUFFICIENT_MEMORY
  * \retval #PSA_ERROR_COMMUNICATION_FAILURE
  * \retval #PSA_ERROR_HARDWARE_FAILURE
@@ -2084,9 +2113,16 @@
  *                                authentication tag is appended to the
  *                                encrypted data.
  * \param ciphertext_size         Size of the \p ciphertext buffer in bytes.
- *                                This must be at least
- *                                #PSA_AEAD_ENCRYPT_OUTPUT_SIZE(\p alg,
- *                                \p plaintext_length).
+ *                                This must be appropriate for the selected
+ *                                algorithm and key:
+ *                                - A sufficient output size is
+ *                                  #PSA_AEAD_ENCRYPT_OUTPUT_SIZE(\c key_type,
+ *                                  \p alg, \p plaintext_length) where
+ *                                  \c key_type is the type of \p key.
+ *                                - #PSA_AEAD_ENCRYPT_OUTPUT_MAX_SIZE(\p
+ *                                  plaintext_length) evaluates to the maximum
+ *                                  ciphertext size of any supported AEAD
+ *                                  encryption.
  * \param[out] ciphertext_length  On success, the size of the output
  *                                in the \p ciphertext buffer.
  *
@@ -2100,7 +2136,11 @@
  *         \p alg is not supported or is not an AEAD algorithm.
  * \retval #PSA_ERROR_INSUFFICIENT_MEMORY
  * \retval #PSA_ERROR_BUFFER_TOO_SMALL
- *         \p ciphertext_size is too small
+ *         \p ciphertext_size is too small.
+ *         #PSA_AEAD_ENCRYPT_OUTPUT_SIZE(\c key_type, \p alg,
+ *         \p plaintext_length) or
+ *         #PSA_AEAD_ENCRYPT_OUTPUT_MAX_SIZE(\p plaintext_length) can be used to
+ *         determine the required buffer size.
  * \retval #PSA_ERROR_COMMUNICATION_FAILURE
  * \retval #PSA_ERROR_HARDWARE_FAILURE
  * \retval #PSA_ERROR_CORRUPTION_DETECTED
@@ -2144,9 +2184,16 @@
  * \param ciphertext_length       Size of \p ciphertext in bytes.
  * \param[out] plaintext          Output buffer for the decrypted data.
  * \param plaintext_size          Size of the \p plaintext buffer in bytes.
- *                                This must be at least
- *                                #PSA_AEAD_DECRYPT_OUTPUT_SIZE(\p alg,
- *                                \p ciphertext_length).
+ *                                This must be appropriate for the selected
+ *                                algorithm and key:
+ *                                - A sufficient output size is
+ *                                  #PSA_AEAD_DECRYPT_OUTPUT_SIZE(\c key_type,
+ *                                  \p alg, \p ciphertext_length) where
+ *                                  \c key_type is the type of \p key.
+ *                                - #PSA_AEAD_DECRYPT_OUTPUT_MAX_SIZE(\p
+ *                                  ciphertext_length) evaluates to the maximum
+ *                                  plaintext size of any supported AEAD
+ *                                  decryption.
  * \param[out] plaintext_length   On success, the size of the output
  *                                in the \p plaintext buffer.
  *
@@ -2162,7 +2209,11 @@
  *         \p alg is not supported or is not an AEAD algorithm.
  * \retval #PSA_ERROR_INSUFFICIENT_MEMORY
  * \retval #PSA_ERROR_BUFFER_TOO_SMALL
- *         \p plaintext_size or \p nonce_length is too small
+ *         \p plaintext_size is too small.
+ *         #PSA_AEAD_DECRYPT_OUTPUT_SIZE(\c key_type, \p alg,
+ *         \p ciphertext_length) or
+ *         #PSA_AEAD_DECRYPT_OUTPUT_MAX_SIZE(\p ciphertext_length) can be used
+ *         to determine the required buffer size.
  * \retval #PSA_ERROR_COMMUNICATION_FAILURE
  * \retval #PSA_ERROR_HARDWARE_FAILURE
  * \retval #PSA_ERROR_CORRUPTION_DETECTED
@@ -2583,10 +2634,18 @@
  * \param input_length          Size of the \p input buffer in bytes.
  * \param[out] output           Buffer where the output is to be written.
  * \param output_size           Size of the \p output buffer in bytes.
- *                              This must be at least
- *                              #PSA_AEAD_UPDATE_OUTPUT_SIZE(\c alg,
- *                              \p input_length) where \c alg is the
- *                              algorithm that is being calculated.
+ *                              This must be appropriate for the selected
+ *                                algorithm and key:
+ *                                - A sufficient output size is
+ *                                  #PSA_AEAD_UPDATE_OUTPUT_SIZE(\c key_type,
+ *                                  \c alg, \p input_length) where
+ *                                  \c key_type is the type of key and \c alg is
+ *                                  the algorithm that were used to set up the
+ *                                  operation.
+ *                                - #PSA_AEAD_UPDATE_OUTPUT_MAX_SIZE(\p
+ *                                  input_length) evaluates to the maximum
+ *                                  output size of any supported AEAD
+ *                                  algorithm.
  * \param[out] output_length    On success, the number of bytes
  *                              that make up the returned output.
  *
@@ -2597,9 +2656,9 @@
  *         set, and have lengths set if required by the algorithm).
  * \retval #PSA_ERROR_BUFFER_TOO_SMALL
  *         The size of the \p output buffer is too small.
- *         You can determine a sufficient buffer size by calling
- *         #PSA_AEAD_UPDATE_OUTPUT_SIZE(\c alg, \p input_length)
- *         where \c alg is the algorithm that is being calculated.
+ *         #PSA_AEAD_UPDATE_OUTPUT_SIZE(\c key_type, \c alg, \p input_length) or
+ *         #PSA_AEAD_UPDATE_OUTPUT_MAX_SIZE(\p input_length) can be used to
+ *         determine the required buffer size.
  * \retval #PSA_ERROR_INVALID_ARGUMENT
  *         The total length of input to psa_aead_update_ad() so far is
  *         less than the additional data length that was previously
@@ -2636,9 +2695,7 @@
  * This function has two output buffers:
  * - \p ciphertext contains trailing ciphertext that was buffered from
  *   preceding calls to psa_aead_update().
- * - \p tag contains the authentication tag. Its length is always
- *   #PSA_AEAD_TAG_LENGTH(\c alg) where \c alg is the AEAD algorithm
- *   that the operation performs.
+ * - \p tag contains the authentication tag.
  *
  * When this function returns successfuly, the operation becomes inactive.
  * If this function returns an error status, the operation enters an error
@@ -2648,18 +2705,32 @@
  * \param[out] ciphertext       Buffer where the last part of the ciphertext
  *                              is to be written.
  * \param ciphertext_size       Size of the \p ciphertext buffer in bytes.
- *                              This must be at least
- *                              #PSA_AEAD_FINISH_OUTPUT_SIZE(\c alg) where
- *                              \c alg is the algorithm that is being
- *                              calculated.
+ *                              This must be appropriate for the selected
+ *                              algorithm and key:
+ *                              - A sufficient output size is
+ *                                #PSA_AEAD_FINISH_OUTPUT_SIZE(\c key_type,
+ *                                \c alg) where \c key_type is the type of key
+ *                                and \c alg is the algorithm that were used to
+ *                                set up the operation.
+ *                              - #PSA_AEAD_FINISH_OUTPUT_MAX_SIZE evaluates to
+ *                                the maximum output size of any supported AEAD
+ *                                algorithm.
  * \param[out] ciphertext_length On success, the number of bytes of
  *                              returned ciphertext.
  * \param[out] tag              Buffer where the authentication tag is
  *                              to be written.
  * \param tag_size              Size of the \p tag buffer in bytes.
- *                              This must be at least
- *                              #PSA_AEAD_TAG_LENGTH(\c alg) where \c alg is
- *                              the algorithm that is being calculated.
+ *                              This must be appropriate for the selected
+ *                              algorithm and key:
+ *                              - The exact tag size is #PSA_AEAD_TAG_LENGTH(\c
+ *                                key_type, \c key_bits, \c alg) where
+ *                                \c key_type and \c key_bits are the type and
+ *                                bit-size of the key, and \c alg is the
+ *                                algorithm that were used in the call to
+ *                                psa_aead_encrypt_setup().
+ *                              - #PSA_AEAD_TAG_MAX_SIZE evaluates to the
+ *                                maximum tag size of any supported AEAD
+ *                                algorithm.
  * \param[out] tag_length       On success, the number of bytes
  *                              that make up the returned tag.
  *
@@ -2670,11 +2741,11 @@
  *         operation with a nonce set).
  * \retval #PSA_ERROR_BUFFER_TOO_SMALL
  *         The size of the \p ciphertext or \p tag buffer is too small.
- *         You can determine a sufficient buffer size for \p ciphertext by
- *         calling #PSA_AEAD_FINISH_OUTPUT_SIZE(\c alg)
- *         where \c alg is the algorithm that is being calculated.
- *         You can determine a sufficient buffer size for \p tag by
- *         calling #PSA_AEAD_TAG_LENGTH(\c alg).
+ *         #PSA_AEAD_FINISH_OUTPUT_SIZE(\c key_type, \c alg) or
+ *         #PSA_AEAD_FINISH_OUTPUT_MAX_SIZE can be used to determine the
+ *         required \p ciphertext buffer size. #PSA_AEAD_TAG_LENGTH(\c key_type,
+ *         \c key_bits, \c alg) or #PSA_AEAD_TAG_MAX_SIZE can be used to
+ *         determine the required \p tag buffer size.
  * \retval #PSA_ERROR_INVALID_ARGUMENT
  *         The total length of input to psa_aead_update_ad() so far is
  *         less than the additional data length that was previously
@@ -2733,10 +2804,15 @@
  *                              that could not be processed until the end
  *                              of the input.
  * \param plaintext_size        Size of the \p plaintext buffer in bytes.
- *                              This must be at least
- *                              #PSA_AEAD_VERIFY_OUTPUT_SIZE(\c alg) where
- *                              \c alg is the algorithm that is being
- *                              calculated.
+ *                              This must be appropriate for the selected algorithm and key:
+ *                              - A sufficient output size is
+ *                                #PSA_AEAD_VERIFY_OUTPUT_SIZE(\c key_type,
+ *                                \c alg) where \c key_type is the type of key
+ *                                and \c alg is the algorithm that were used to
+ *                                set up the operation.
+ *                              - #PSA_AEAD_VERIFY_OUTPUT_MAX_SIZE evaluates to
+ *                                the maximum output size of any supported AEAD
+ *                                algorithm.
  * \param[out] plaintext_length On success, the number of bytes of
  *                              returned plaintext.
  * \param[in] tag               Buffer containing the authentication tag.
@@ -2752,9 +2828,9 @@
  *         operation with a nonce set).
  * \retval #PSA_ERROR_BUFFER_TOO_SMALL
  *         The size of the \p plaintext buffer is too small.
- *         You can determine a sufficient buffer size for \p plaintext by
- *         calling #PSA_AEAD_VERIFY_OUTPUT_SIZE(\c alg)
- *         where \c alg is the algorithm that is being calculated.
+ *         #PSA_AEAD_VERIFY_OUTPUT_SIZE(\c key_type, \c alg) or
+ *         #PSA_AEAD_VERIFY_OUTPUT_MAX_SIZE can be used to determine the
+ *         required buffer size.
  * \retval #PSA_ERROR_INVALID_ARGUMENT
  *         The total length of input to psa_aead_update_ad() so far is
  *         less than the additional data length that was previously
@@ -2814,18 +2890,138 @@
  */
 
 /**
+ * \brief Sign a message with a private key. For hash-and-sign algorithms,
+ *        this includes the hashing step.
+ *
+ * \note To perform a multi-part hash-and-sign signature algorithm, first use
+ *       a multi-part hash operation and then pass the resulting hash to
+ *       psa_sign_hash(). PSA_ALG_GET_HASH(\p alg) can be used to determine the
+ *       hash algorithm to use.
+ *
+ * \param[in]  key              Identifier of the key to use for the operation.
+ *                              It must be an asymmetric key pair. The key must
+ *                              allow the usage #PSA_KEY_USAGE_SIGN_MESSAGE.
+ * \param[in]  alg              An asymmetric signature algorithm (PSA_ALG_XXX
+ *                              value such that #PSA_ALG_IS_SIGN_MESSAGE(\p alg)
+ *                              is true), that is compatible with the type of
+ *                              \p key.
+ * \param[in]  input            The input message to sign.
+ * \param[in]  input_length     Size of the \p input buffer in bytes.
+ * \param[out] signature        Buffer where the signature is to be written.
+ * \param[in]  signature_size   Size of the \p signature buffer in bytes. This
+ *                              must be appropriate for the selected
+ *                              algorithm and key:
+ *                              - The required signature size is
+ *                                #PSA_SIGN_OUTPUT_SIZE(\c key_type, \c key_bits, \p alg)
+ *                                where \c key_type and \c key_bits are the type and
+ *                                bit-size respectively of key.
+ *                              - #PSA_SIGNATURE_MAX_SIZE evaluates to the
+ *                                maximum signature size of any supported
+ *                                signature algorithm.
+ * \param[out] signature_length On success, the number of bytes that make up
+ *                              the returned signature value.
+ *
+ * \retval #PSA_SUCCESS
+ * \retval #PSA_ERROR_INVALID_HANDLE
+ * \retval #PSA_ERROR_NOT_PERMITTED
+ *         The key does not have the #PSA_KEY_USAGE_SIGN_MESSAGE flag,
+ *         or it does not permit the requested algorithm.
+ * \retval #PSA_ERROR_BUFFER_TOO_SMALL
+ *         The size of the \p signature buffer is too small. You can
+ *         determine a sufficient buffer size by calling
+ *         #PSA_SIGN_OUTPUT_SIZE(\c key_type, \c key_bits, \p alg)
+ *         where \c key_type and \c key_bits are the type and bit-size
+ *         respectively of \p key.
+ * \retval #PSA_ERROR_NOT_SUPPORTED
+ * \retval #PSA_ERROR_INVALID_ARGUMENT
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY
+ * \retval #PSA_ERROR_COMMUNICATION_FAILURE
+ * \retval #PSA_ERROR_HARDWARE_FAILURE
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED
+ * \retval #PSA_ERROR_STORAGE_FAILURE
+ * \retval #PSA_ERROR_DATA_CORRUPT
+ * \retval #PSA_ERROR_DATA_INVALID
+ * \retval #PSA_ERROR_INSUFFICIENT_ENTROPY
+ * \retval #PSA_ERROR_BAD_STATE
+ *         The library has not been previously initialized by psa_crypto_init().
+ *         It is implementation-dependent whether a failure to initialize
+ *         results in this error code.
+ */
+psa_status_t psa_sign_message( mbedtls_svc_key_id_t key,
+                               psa_algorithm_t alg,
+                               const uint8_t * input,
+                               size_t input_length,
+                               uint8_t * signature,
+                               size_t signature_size,
+                               size_t * signature_length );
+
+/** \brief Verify the signature of a message with a public key, using
+ *         a hash-and-sign verification algorithm.
+ *
+ * \note To perform a multi-part hash-and-sign signature verification
+ *       algorithm, first use a multi-part hash operation to hash the message
+ *       and then pass the resulting hash to psa_verify_hash().
+ *       PSA_ALG_GET_HASH(\p alg) can be used to determine the hash algorithm
+ *       to use.
+ *
+ * \param[in]  key              Identifier of the key to use for the operation.
+ *                              It must be a public key or an asymmetric key
+ *                              pair. The key must allow the usage
+ *                              #PSA_KEY_USAGE_VERIFY_MESSAGE.
+ * \param[in]  alg              An asymmetric signature algorithm (PSA_ALG_XXX
+ *                              value such that #PSA_ALG_IS_SIGN_MESSAGE(\p alg)
+ *                              is true), that is compatible with the type of
+ *                              \p key.
+ * \param[in]  input            The message whose signature is to be verified.
+ * \param[in]  input_length     Size of the \p input buffer in bytes.
+ * \param[out] signature        Buffer containing the signature to verify.
+ * \param[in]  signature_length Size of the \p signature buffer in bytes.
+ *
+ * \retval #PSA_SUCCESS
+ * \retval #PSA_ERROR_INVALID_HANDLE
+ * \retval #PSA_ERROR_NOT_PERMITTED
+ *         The key does not have the #PSA_KEY_USAGE_SIGN_MESSAGE flag,
+ *         or it does not permit the requested algorithm.
+ * \retval #PSA_ERROR_INVALID_SIGNATURE
+ *         The calculation was performed successfully, but the passed signature
+ *         is not a valid signature.
+ * \retval #PSA_ERROR_NOT_SUPPORTED
+ * \retval #PSA_ERROR_INVALID_ARGUMENT
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY
+ * \retval #PSA_ERROR_COMMUNICATION_FAILURE
+ * \retval #PSA_ERROR_HARDWARE_FAILURE
+ * \retval #PSA_ERROR_CORRUPTION_DETECTED
+ * \retval #PSA_ERROR_STORAGE_FAILURE
+ * \retval #PSA_ERROR_DATA_CORRUPT
+ * \retval #PSA_ERROR_DATA_INVALID
+ * \retval #PSA_ERROR_BAD_STATE
+ *         The library has not been previously initialized by psa_crypto_init().
+ *         It is implementation-dependent whether a failure to initialize
+ *         results in this error code.
+ */
+psa_status_t psa_verify_message( mbedtls_svc_key_id_t key,
+                                 psa_algorithm_t alg,
+                                 const uint8_t * input,
+                                 size_t input_length,
+                                 const uint8_t * signature,
+                                 size_t signature_length );
+
+/**
  * \brief Sign a hash or short message with a private key.
  *
  * Note that to perform a hash-and-sign signature algorithm, you must
  * first calculate the hash by calling psa_hash_setup(), psa_hash_update()
- * and psa_hash_finish(). Then pass the resulting hash as the \p hash
+ * and psa_hash_finish(), or alternatively by calling psa_hash_compute().
+ * Then pass the resulting hash as the \p hash
  * parameter to this function. You can use #PSA_ALG_SIGN_GET_HASH(\p alg)
  * to determine the hash algorithm to use.
  *
  * \param key                   Identifier of the key to use for the operation.
  *                              It must be an asymmetric key pair. The key must
  *                              allow the usage #PSA_KEY_USAGE_SIGN_HASH.
- * \param alg                   A signature algorithm that is compatible with
+ * \param alg                   A signature algorithm (PSA_ALG_XXX
+ *                              value such that #PSA_ALG_IS_SIGN_HASH(\p alg)
+ *                              is true), that is compatible with
  *                              the type of \p key.
  * \param[in] hash              The hash or message to sign.
  * \param hash_length           Size of the \p hash buffer in bytes.
@@ -2865,11 +3061,12 @@
                            size_t *signature_length);
 
 /**
- * \brief Verify the signature a hash or short message using a public key.
+ * \brief Verify the signature of a hash or short message using a public key.
  *
  * Note that to perform a hash-and-sign signature algorithm, you must
  * first calculate the hash by calling psa_hash_setup(), psa_hash_update()
- * and psa_hash_finish(). Then pass the resulting hash as the \p hash
+ * and psa_hash_finish(), or alternatively by calling psa_hash_compute().
+ * Then pass the resulting hash as the \p hash
  * parameter to this function. You can use #PSA_ALG_SIGN_GET_HASH(\p alg)
  * to determine the hash algorithm to use.
  *
@@ -2877,7 +3074,9 @@
  *                          must be a public key or an asymmetric key pair. The
  *                          key must allow the usage
  *                          #PSA_KEY_USAGE_VERIFY_HASH.
- * \param alg               A signature algorithm that is compatible with
+ * \param alg               A signature algorithm (PSA_ALG_XXX
+ *                          value such that #PSA_ALG_IS_SIGN_HASH(\p alg)
+ *                          is true), that is compatible with
  *                          the type of \p key.
  * \param[in] hash          The hash or message whose signature is to be
  *                          verified.
@@ -3443,7 +3642,8 @@
  * state and must be aborted by calling psa_key_derivation_abort().
  *
  * How much output is produced and consumed from the operation, and how
- * the key is derived, depends on the key type:
+ * the key is derived, depends on the key type and on the key size
+ * (denoted \c bits below):
  *
  * - For key types for which the key is an arbitrary sequence of bytes
  *   of a given size, this function is functionally equivalent to
@@ -3453,11 +3653,12 @@
  *   if the implementation provides an isolation boundary then
  *   the key material is not exposed outside the isolation boundary.
  *   As a consequence, for these key types, this function always consumes
- *   exactly (\p bits / 8) bytes from the operation.
+ *   exactly (\c bits / 8) bytes from the operation.
  *   The following key types defined in this specification follow this scheme:
  *
  *     - #PSA_KEY_TYPE_AES;
  *     - #PSA_KEY_TYPE_ARC4;
+ *     - #PSA_KEY_TYPE_ARIA;
  *     - #PSA_KEY_TYPE_CAMELLIA;
  *     - #PSA_KEY_TYPE_DERIVE;
  *     - #PSA_KEY_TYPE_HMAC.
@@ -3474,8 +3675,8 @@
  *       string and process it as specified in RFC 7748 &sect;5.
  *
  * - For key types for which the key is represented by a single sequence of
- *   \p bits bits with constraints as to which bit sequences are acceptable,
- *   this function draws a byte string of length (\p bits / 8) bytes rounded
+ *   \c bits bits with constraints as to which bit sequences are acceptable,
+ *   this function draws a byte string of length (\c bits / 8) bytes rounded
  *   up to the nearest whole number of bytes. If the resulting byte string
  *   is acceptable, it becomes the key, otherwise the drawn bytes are discarded.
  *   This process is repeated until an acceptable byte string is drawn.
@@ -3556,6 +3757,8 @@
  * \retval #PSA_ERROR_COMMUNICATION_FAILURE
  * \retval #PSA_ERROR_HARDWARE_FAILURE
  * \retval #PSA_ERROR_CORRUPTION_DETECTED
+ * \retval #PSA_ERROR_DATA_INVALID
+ * \retval #PSA_ERROR_DATA_CORRUPT
  * \retval #PSA_ERROR_STORAGE_FAILURE
  * \retval #PSA_ERROR_BAD_STATE
  *         The library has not been previously initialized by psa_crypto_init().
@@ -3721,6 +3924,8 @@
  * \retval #PSA_ERROR_HARDWARE_FAILURE
  * \retval #PSA_ERROR_CORRUPTION_DETECTED
  * \retval #PSA_ERROR_INSUFFICIENT_STORAGE
+ * \retval #PSA_ERROR_DATA_INVALID
+ * \retval #PSA_ERROR_DATA_CORRUPT
  * \retval #PSA_ERROR_STORAGE_FAILURE
  * \retval #PSA_ERROR_BAD_STATE
  *         The library has not been previously initialized by psa_crypto_init().
diff --git a/third_party/mbedtls/repo/include/psa/crypto_accel_driver.h b/third_party/mbedtls/repo/include/psa/crypto_accel_driver.h
deleted file mode 100644
index 4488ea8..0000000
--- a/third_party/mbedtls/repo/include/psa/crypto_accel_driver.h
+++ /dev/null
@@ -1,823 +0,0 @@
-/**
- * \file psa/crypto_accel_driver.h
- * \brief PSA cryptography accelerator driver module
- *
- * This header declares types and function signatures for cryptography
- * drivers that access key material directly. This is meant for
- * on-chip cryptography accelerators.
- *
- * This file is part of the PSA Crypto Driver Model, containing functions for
- * driver developers to implement to enable hardware to be called in a
- * standardized way by a PSA Cryptographic API implementation. The functions
- * comprising the driver model, which driver authors implement, are not
- * intended to be called by application developers.
- */
-
-/*
- *  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.
- */
-#ifndef PSA_CRYPTO_ACCEL_DRIVER_H
-#define PSA_CRYPTO_ACCEL_DRIVER_H
-
-#include "crypto_driver_common.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/** \defgroup driver_digest Hardware-Accelerated Message Digests
- *
- * Generation and authentication of Message Digests (aka hashes) must be done
- * in parts using the following sequence:
- * - `psa_drv_hash_setup_t`
- * - `psa_drv_hash_update_t`
- * - `psa_drv_hash_update_t`
- * - ...
- * - `psa_drv_hash_finish_t`
- *
- * If a previously started Message Digest operation needs to be terminated
- * before the `psa_drv_hash_finish_t` operation is complete, it should be aborted
- * by the `psa_drv_hash_abort_t`. Failure to do so may result in allocated
- * resources not being freed or in other undefined behavior.
- */
-/**@{*/
-
-/** \brief The hardware-specific hash context structure
- *
- * The contents of this structure are implementation dependent and are
- * therefore not described here
- */
-typedef struct psa_drv_hash_context_s psa_drv_hash_context_t;
-
-/** \brief The function prototype for the start operation of a hash (message
- * digest) operation
- *
- *  Functions that implement this prototype should be named in the following
- * convention:
- * ~~~~~~~~~~~~~{.c}
- * psa_drv_hash_<ALGO>_setup
- * ~~~~~~~~~~~~~
- * Where `ALGO` is the name of the underlying hash function
- *
- * \param[in,out] p_context     A structure that will contain the
- * hardware-specific hash context
- *
- * \retval #PSA_SUCCESS     Success.
- */
-typedef psa_status_t (*psa_drv_hash_setup_t)(psa_drv_hash_context_t *p_context);
-
-/** \brief The function prototype for the update operation of a hash (message
- * digest) operation
- *
- * Functions that implement this prototype should be named in the following
- * convention:
- * ~~~~~~~~~~~~~{.c}
- * psa_drv_hash_<ALGO>_update
- * ~~~~~~~~~~~~~
- * Where `ALGO` is the name of the underlying algorithm
- *
- * \param[in,out] p_context     A hardware-specific structure for the
- *                              previously-established hash operation to be
- *                              continued
- * \param[in] p_input           A buffer containing the message to be appended
- *                              to the hash operation
- * \param[in] input_length      The size in bytes of the input message buffer
- */
-typedef psa_status_t (*psa_drv_hash_update_t)(psa_drv_hash_context_t *p_context,
-                                              const uint8_t *p_input,
-                                              size_t input_length);
-
-/** \brief  The function prototype for the finish operation of a hash (message
- * digest) operation
- *
- * Functions that implement this prototype should be named in the following
- * convention:
- * ~~~~~~~~~~~~~{.c}
- * psa_drv_hash_<ALGO>_finish
- * ~~~~~~~~~~~~~
- * Where `ALGO` is the name of the underlying algorithm
- *
- * \param[in,out] p_context     A hardware-specific structure for the
- *                              previously started hash operation to be
- *                              fiinished
- * \param[out] p_output         A buffer where the generated digest will be
- *                              placed
- * \param[in] output_size       The size in bytes of the buffer that has been
- *                              allocated for the `p_output` buffer
- * \param[out] p_output_length  The number of bytes placed in `p_output` after
- *                              success
- *
- * \retval #PSA_SUCCESS
- *          Success.
- */
-typedef psa_status_t (*psa_drv_hash_finish_t)(psa_drv_hash_context_t *p_context,
-                                              uint8_t *p_output,
-                                              size_t output_size,
-                                              size_t *p_output_length);
-
-/** \brief The function prototype for the abort operation of a hash (message
- * digest) operation
- *
- * Functions that implement this prototype should be named in the following
- * convention:
- * ~~~~~~~~~~~~~{.c}
- * psa_drv_hash_<ALGO>_abort
- * ~~~~~~~~~~~~~
- * Where `ALGO` is the name of the underlying algorithm
- *
- * \param[in,out] p_context A hardware-specific structure for the previously
- *                          started hash operation to be aborted
- */
-typedef void (*psa_drv_hash_abort_t)(psa_drv_hash_context_t *p_context);
-
-/**@}*/
-
-/** \defgroup accel_mac Hardware-Accelerated Message Authentication Code
- * Generation and authentication of Message Authentication Codes (MACs) using
- * cryptographic accelerators can be done either as a single function call (via the
- * `psa_drv_accel_mac_generate_t` or `psa_drv_accel_mac_verify_t`
- * functions), or in parts using the following sequence:
- * - `psa_drv_accel_mac_setup_t`
- * - `psa_drv_accel_mac_update_t`
- * - `psa_drv_accel_mac_update_t`
- * - ...
- * - `psa_drv_accel_mac_finish_t` or `psa_drv_accel_mac_finish_verify_t`
- *
- * If a previously started MAC operation needs to be terminated, it
- * should be done so by the `psa_drv_accel_mac_abort_t`. Failure to do so may
- * result in allocated resources not being freed or in other undefined
- * behavior.
- *
- */
-/**@{*/
-
-/** \brief The hardware-accelerator-specific MAC context structure
- *
- * The contents of this structure are implementation dependent and are
- * therefore not described here.
- */
-typedef struct psa_drv_accel_mac_context_s psa_drv_accel_mac_context_t;
-
-/** \brief The function prototype for the setup operation of a
- * hardware-accelerated MAC operation
- *
- *  Functions that implement this prototype should be named in the following
- * convention:
- * ~~~~~~~~~~~~~{.c}
- * psa_drv_accel_mac_<ALGO>_<MAC_VARIANT>_setup
- * ~~~~~~~~~~~~~
- * Where `ALGO` is the name of the underlying primitive, and `MAC_VARIANT`
- * is the specific variant of a MAC operation (such as HMAC or CMAC)
- *
- * \param[in,out] p_context     A structure that will contain the
- *                              hardware-specific MAC context
- * \param[in] p_key             A buffer containing the cleartext key material
- *                              to be used in the operation
- * \param[in] key_length        The size in bytes of the key material
- *
- * \retval  #PSA_SUCCESS
- *          Success.
- */
-typedef psa_status_t (*psa_drv_accel_mac_setup_t)(psa_drv_accel_mac_context_t *p_context,
-                                                  const uint8_t *p_key,
-                                                  size_t key_length);
-
-/** \brief The function prototype for the update operation of a
- * hardware-accelerated MAC operation
- *
- * Functions that implement this prototype should be named in the following
- * convention:
- * ~~~~~~~~~~~~~{.c}
- * psa_drv_accel_mac_<ALGO>_<MAC_VARIANT>_update
- * ~~~~~~~~~~~~~
- * Where `ALGO` is the name of the underlying algorithm, and `MAC_VARIANT`
- * is the specific variant of a MAC operation (such as HMAC or CMAC)
- *
- * \param[in,out] p_context     A hardware-specific structure for the
- *                              previously-established MAC operation to be
- *                              continued
- * \param[in] p_input           A buffer containing the message to be appended
- *                              to the MAC operation
- * \param[in] input_length      The size in bytes of the input message buffer
- */
-typedef psa_status_t (*psa_drv_accel_mac_update_t)(psa_drv_accel_mac_context_t *p_context,
-                                                   const uint8_t *p_input,
-                                                   size_t input_length);
-
-/** \brief  The function prototype for the finish operation of a
- * hardware-accelerated MAC operation
- *
- * Functions that implement this prototype should be named in the following
- *  convention:
- * ~~~~~~~~~~~~~{.c}
- * psa_drv_accel_mac_<ALGO>_<MAC_VARIANT>_finish
- * ~~~~~~~~~~~~~
- * Where `ALGO` is the name of the underlying algorithm, and `MAC_VARIANT` is
- * the specific variant of a MAC operation (such as HMAC or CMAC)
- *
- * \param[in,out] p_context     A hardware-specific structure for the
- *                              previously started MAC operation to be
- *                              finished
- * \param[out] p_mac            A buffer where the generated MAC will be placed
- * \param[in] mac_length        The size in bytes of the buffer that has been
- *                              allocated for the `p_mac` buffer
- *
- * \retval #PSA_SUCCESS
- *          Success.
- */
-typedef psa_status_t (*psa_drv_accel_mac_finish_t)(psa_drv_accel_mac_context_t *p_context,
-                                                   uint8_t *p_mac,
-                                                   size_t mac_length);
-
-/** \brief The function prototype for the finish and verify operation of a
- * hardware-accelerated MAC operation
- *
- * Functions that implement this prototype should be named in the following
- * convention:
- * ~~~~~~~~~~~~~{.c}
- * psa_drv_accel_mac_<ALGO>_<MAC_VARIANT>_finish_verify
- * ~~~~~~~~~~~~~
- * Where `ALGO` is the name of the underlying algorithm, and `MAC_VARIANT` is
- * the specific variant of a MAC operation (such as HMAC or CMAC)
- *
- * \param[in,out] p_context     A hardware-specific structure for the
- *                              previously started MAC operation to be
- *                              verified and finished
- * \param[in] p_mac             A buffer containing the MAC that will be used
- *                              for verification
- * \param[in] mac_length        The size in bytes of the data in the `p_mac`
- *                              buffer
- *
- * \retval #PSA_SUCCESS
- *          The operation completed successfully and the comparison matched
- */
-typedef psa_status_t (*psa_drv_accel_mac_finish_verify_t)(psa_drv_accel_mac_context_t *p_context,
-                                                          const uint8_t *p_mac,
-                                                          size_t mac_length);
-
-/** \brief The function prototype for the abort operation for a previously
- * started hardware-accelerated MAC operation
- *
- * Functions that implement this prototype should be named in the following
- * convention:
- * ~~~~~~~~~~~~~{.c}
- * psa_drv_accel_mac_<ALGO>_<MAC_VARIANT>_abort
- * ~~~~~~~~~~~~~
- * Where `ALGO` is the name of the underlying algorithm, and `MAC_VARIANT` is
- * the specific variant of a MAC operation (such as HMAC or CMAC)
- *
- * \param[in,out] p_context     A hardware-specific structure for the
- *                              previously started MAC operation to be
- *                              aborted
- *
- */
-typedef psa_status_t (*psa_drv_accel_mac_abort_t)(psa_drv_accel_mac_context_t *p_context);
-
-/** \brief The function prototype for the one-shot operation of a
- * hardware-accelerated MAC operation
- *
- * Functions that implement this prototype should be named in the following
- * convention:
- * ~~~~~~~~~~~~~{.c}
- * psa_drv_accel_mac_<ALGO>_<MAC_VARIANT>
- * ~~~~~~~~~~~~~
- * Where `ALGO` is the name of the underlying algorithm, and `MAC_VARIANT` is
- * the specific variant of a MAC operation (such as HMAC or CMAC)
- *
- * \param[in] p_input        A buffer containing the data to be MACed
- * \param[in] input_length   The length in bytes of the `p_input` data
- * \param[in] p_key          A buffer containing the key material to be used
- *                           for the MAC operation
- * \param[in] key_length     The length in bytes of the `p_key` data
- * \param[in] alg            The algorithm to be performed
- * \param[out] p_mac         The buffer where the resulting MAC will be placed
- *                           upon success
- * \param[in] mac_length     The length in bytes of the `p_mac` buffer
- */
-typedef psa_status_t (*psa_drv_accel_mac_t)(const uint8_t *p_input,
-                                            size_t input_length,
-                                            const uint8_t *p_key,
-                                            size_t key_length,
-                                            psa_algorithm_t alg,
-                                            uint8_t *p_mac,
-                                            size_t mac_length);
-
-/** \brief The function prototype for the one-shot hardware-accelerated MAC
- * Verify operation
- *
- * Functions that implement this prototype should be named in the following
- * convention:
- * ~~~~~~~~~~~~~{.c}
- * psa_drv_accel_mac_<ALGO>_<MAC_VARIANT>_verify
- * ~~~~~~~~~~~~~
- * Where `ALGO` is the name of the underlying algorithm, and `MAC_VARIANT` is
- * the specific variant of a MAC operation (such as HMAC or CMAC)
- *
- * \param[in] p_input        A buffer containing the data to be MACed
- * \param[in] input_length   The length in bytes of the `p_input` data
- * \param[in] p_key          A buffer containing the key material to be used
- *                           for the MAC operation
- * \param[in] key_length     The length in bytes of the `p_key` data
- * \param[in] alg            The algorithm to be performed
- * \param[in] p_mac          The MAC data to be compared
- * \param[in] mac_length     The length in bytes of the `p_mac` buffer
- *
- * \retval #PSA_SUCCESS
- *  The operation completed successfully and the comparison matched
- */
-typedef psa_status_t (*psa_drv_accel_mac_verify_t)(const uint8_t *p_input,
-                                                   size_t input_length,
-                                                   const uint8_t *p_key,
-                                                   size_t key_length,
-                                                   psa_algorithm_t alg,
-                                                   const uint8_t *p_mac,
-                                                   size_t mac_length);
-/**@}*/
-
-/** \defgroup accel_cipher Hardware-Accelerated Block Ciphers
- * Encryption and Decryption using hardware-acceleration in block modes other
- * than ECB must be done in multiple parts, using the following flow:
- * - `psa_drv_accel_ciphersetup_t`
- * - `psa_drv_accel_cipher_set_iv_t` (optional depending upon block mode)
- * - `psa_drv_accel_cipher_update_t`
- * - `psa_drv_accel_cipher_update_t`
- * - ...
- * - `psa_drv_accel_cipher_finish_t`
- *
- * If a previously started hardware-accelerated Cipher operation needs to be
- * terminated, it should be done so by the `psa_drv_accel_cipher_abort_t`.
- * Failure to do so may result in allocated resources not being freed or in
- * other undefined behavior.
- */
-/**@{*/
-
-/** \brief The hardware-accelerator-specific cipher context structure
- *
- * The contents of this structure are implementation dependent and are
- * therefore not described here.
- */
-typedef struct psa_drv_accel_cipher_context_s psa_drv_accel_cipher_context_t;
-
-/** \brief The function prototype for the setup operation of
- * hardware-accelerated block cipher operations.
- *  Functions that implement this prototype should be named in the following
- * conventions:
- * ~~~~~~~~~~~~~{.c}
- * psa_drv_accel_cipher_setup_<CIPHER_NAME>_<MODE>
- * ~~~~~~~~~~~~~
- * Where
- * - `CIPHER_NAME` is the name of the underlying block cipher (i.e. AES or DES)
- * - `MODE` is the block mode of the cipher operation (i.e. CBC or CTR)
- *
- * For stream ciphers:
- * ~~~~~~~~~~~~~{.c}
- * psa_drv_accel_cipher_setup_<CIPHER_NAME>
- * ~~~~~~~~~~~~~
- * Where `CIPHER_NAME` is the name of a stream cipher (i.e. RC4)
- *
- * \param[in,out] p_context     A structure that will contain the
- *                              hardware-specific cipher context
- * \param[in] direction         Indicates if the operation is an encrypt or a
- *                              decrypt
- * \param[in] p_key_data        A buffer containing the cleartext key material
- *                              to be used in the operation
- * \param[in] key_data_size     The size in bytes of the key material
- *
- * \retval #PSA_SUCCESS
- */
-typedef psa_status_t (*psa_drv_accel_cipher_setup_t)(psa_drv_accel_cipher_context_t *p_context,
-                                                     psa_encrypt_or_decrypt_t direction,
-                                                     const uint8_t *p_key_data,
-                                                     size_t key_data_size);
-
-/** \brief The function prototype for the set initialization vector operation
- * of hardware-accelerated block cipher operations
- * Functions that implement this prototype should be named in the following
- * convention:
- * ~~~~~~~~~~~~~{.c}
- * psa_drv_accel_cipher_set_iv_<CIPHER_NAME>_<MODE>
- * ~~~~~~~~~~~~~
- * Where
- * - `CIPHER_NAME` is the name of the underlying block cipher (i.e. AES or DES)
- * - `MODE` is the block mode of the cipher operation (i.e. CBC or CTR)
- *
- * \param[in,out] p_context     A structure that contains the previously setup
- *                              hardware-specific cipher context
- * \param[in] p_iv              A buffer containing the initialization vecotr
- * \param[in] iv_length         The size in bytes of the contents of `p_iv`
- *
- * \retval #PSA_SUCCESS
- */
-typedef psa_status_t (*psa_drv_accel_cipher_set_iv_t)(psa_drv_accel_cipher_context_t *p_context,
-                                                      const uint8_t *p_iv,
-                                                      size_t iv_length);
-
-/** \brief The function prototype for the update operation of
- * hardware-accelerated block cipher operations.
- *
- *  Functions that implement this prototype should be named in the following
- * convention:
- * ~~~~~~~~~~~~~{.c}
- * psa_drv_accel_cipher_update_<CIPHER_NAME>_<MODE>
- * ~~~~~~~~~~~~~
- * Where
- * - `CIPHER_NAME` is the name of the underlying block cipher (i.e. AES or DES)
- * - `MODE` is the block mode of the cipher operation (i.e. CBC or CTR)
- *
- * \param[in,out] p_context         A hardware-specific structure for the
- *                                  previously started cipher operation
- * \param[in] p_input               A buffer containing the data to be
- *                                  encrypted or decrypted
- * \param[in] input_size            The size in bytes of the `p_input` buffer
- * \param[out] p_output             A caller-allocated buffer where the
- *                                  generated output will be placed
- * \param[in] output_size           The size in bytes of the `p_output` buffer
- * \param[out] p_output_length      After completion, will contain the number
- *                                  of bytes placed in the `p_output` buffer
- *
- * \retval #PSA_SUCCESS
- */
-typedef psa_status_t (*psa_drv_accel_cipher_update_t)(psa_drv_accel_cipher_context_t *p_context,
-                                                      const uint8_t *p_input,
-                                                      size_t input_size,
-                                                      uint8_t *p_output,
-                                                      size_t output_size,
-                                                      size_t *p_output_length);
-
-/** \brief The function prototype for the finish operation of
- * hardware-accelerated block cipher operations.
- *
- *  Functions that implement this prototype should be named in the following
- * convention:
- * ~~~~~~~~~~~~~{.c}
- * psa_drv_accel_cipher_finish_<CIPHER_NAME>_<MODE>
- * ~~~~~~~~~~~~~
- * Where
- * - `CIPHER_NAME` is the name of the underlying block cipher (i.e. AES or DES)
- * - `MODE` is the block mode of the cipher operation (i.e. CBC or CTR)
- *
- * \param[in,out] p_context     A hardware-specific structure for the
- *                              previously started cipher operation
- * \param[out] p_output         A caller-allocated buffer where the generated
- *                              output will be placed
- * \param[in] output_size       The size in bytes of the `p_output` buffer
- * \param[out] p_output_length  After completion, will contain the number of
- *                              bytes placed in the `p_output` buffer
- *
- * \retval #PSA_SUCCESS
- */
-typedef psa_status_t (*psa_drv_accel_cipher_finish_t)(psa_drv_accel_cipher_context_t *p_context,
-                                                      uint8_t *p_output,
-                                                      size_t output_size,
-                                                      size_t *p_output_length);
-
-/** \brief The function prototype for the abort operation of
- * hardware-accelerated block cipher operations.
- *
- *  Functions that implement the following prototype should be named in the
- * following convention:
- * ~~~~~~~~~~~~~{.c}
- * psa_drv_accel_cipher_abort_<CIPHER_NAME>_<MODE>
- * ~~~~~~~~~~~~~
- * Where
- * - `CIPHER_NAME` is the name of the underlying block cipher (i.e. AES or DES)
- * - `MODE` is the block mode of the cipher operation (i.e. CBC or CTR)
- *
- * \param[in,out] p_context     A hardware-specific structure for the
- *                              previously started cipher operation
- *
- * \retval #PSA_SUCCESS
- */
-typedef psa_status_t (*psa_drv_accel_cipher_abort_t)(psa_drv_accel_cipher_context_t *p_context);
-
-/**@}*/
-
-/** \defgroup accel_aead Hardware-Accelerated Authenticated Encryption with Additional Data
- *
- * Hardware-accelerated Authenticated Encryption with Additional Data (AEAD)
- * operations must be done in one function call. While this creates a burden
- * for implementers as there must be sufficient space in memory for the entire
- * message, it prevents decrypted data from being made available before the
- * authentication operation is complete and the data is known to be authentic.
- */
-/**@{*/
-
-/** \brief The function prototype for the hardware-accelerated authenticated
- * encryption operation.
- *
- * Functions that implement this prototype should be named in the following
- * convention:
- * ~~~~~~~~~~~~~{.c}
- * psa_drv_accel_aead_<ALGO>_encrypt
- * ~~~~~~~~~~~~~
- * Where `ALGO` is the name of the AEAD algorithm
- *
- * \param[in] p_key                     A pointer to the key material
- * \param[in] key_length                The size in bytes of the key material
- * \param[in] alg                       The AEAD algorithm to compute
- *                                      (\c PSA_ALG_XXX value such that
- *                                      #PSA_ALG_IS_AEAD(`alg`) is true)
- * \param[in] nonce                     Nonce or IV to use
- * \param[in] nonce_length              Size of the `nonce` buffer in bytes
- * \param[in] additional_data           Additional data that will be MACed
- *                                      but not encrypted.
- * \param[in] additional_data_length    Size of `additional_data` in bytes
- * \param[in] plaintext                 Data that will be MACed and
- *                                      encrypted.
- * \param[in] plaintext_length          Size of `plaintext` in bytes
- * \param[out] ciphertext               Output buffer for the authenticated and
- *                                      encrypted data. The additional data is
- *                                      not part of this output. For algorithms
- *                                      where the encrypted data and the
- *                                      authentication tag are defined as
- *                                      separate outputs, the authentication
- *                                      tag is appended to the encrypted data.
- * \param[in] ciphertext_size           Size of the `ciphertext` buffer in
- *                                      bytes
- *                                      This must be at least
- *                                      #PSA_AEAD_ENCRYPT_OUTPUT_SIZE(`alg`,
- *                                      `plaintext_length`).
- * \param[out] ciphertext_length        On success, the size of the output in
- *                                      the `ciphertext` buffer
- *
- * \retval #PSA_SUCCESS
- *
- */
-typedef psa_status_t (*psa_drv_accel_aead_encrypt_t)(const uint8_t *p_key,
-                                                     size_t key_length,
-                                                     psa_algorithm_t alg,
-                                                     const uint8_t *nonce,
-                                                     size_t nonce_length,
-                                                     const uint8_t *additional_data,
-                                                     size_t additional_data_length,
-                                                     const uint8_t *plaintext,
-                                                     size_t plaintext_length,
-                                                     uint8_t *ciphertext,
-                                                     size_t ciphertext_size,
-                                                     size_t *ciphertext_length);
-
-/** \brief The function prototype for the hardware-accelerated authenticated
- * decryption operation.
- *
- * Functions that implement this prototype should be named in the following
- * convention:
- * ~~~~~~~~~~~~~{.c}
- * psa_drv_accel_aead_<ALGO>_decrypt
- * ~~~~~~~~~~~~~
- * Where `ALGO` is the name of the AEAD algorithm
- * \param[in] p_key                     A pointer to the key material
- * \param[in] key_length                The size in bytes of the key material
- * \param[in] alg                       The AEAD algorithm to compute
- *                                      (\c PSA_ALG_XXX value such that
- *                                      #PSA_ALG_IS_AEAD(`alg`) is true)
- * \param[in] nonce                     Nonce or IV to use
- * \param[in] nonce_length              Size of the `nonce` buffer in bytes
- * \param[in] additional_data           Additional data that has been MACed
- *                                      but not encrypted
- * \param[in] additional_data_length    Size of `additional_data` in bytes
- * \param[in] ciphertext                Data that has been MACed and
- *                                      encrypted
- *                                      For algorithms where the encrypted data
- *                                      and the authentication tag are defined
- *                                      as separate inputs, the buffer must
- *                                      contain the encrypted data followed by
- *                                      the authentication tag.
- * \param[in] ciphertext_length         Size of `ciphertext` in bytes
- * \param[out] plaintext                Output buffer for the decrypted data
- * \param[in] plaintext_size            Size of the `plaintext` buffer in
- *                                      bytes
- *                                      This must be at least
- *                                      #PSA_AEAD_DECRYPT_OUTPUT_SIZE(`alg`,
- *                                      `ciphertext_length`).
- * \param[out] plaintext_length         On success, the size of the output
- *                                      in the \b plaintext buffer
- *
- * \retval #PSA_SUCCESS
- *         Success.
- */
-typedef psa_status_t (*psa_drv_accel_aead_decrypt_t)(const uint8_t *p_key,
-                                                     size_t key_length,
-                                                     psa_algorithm_t alg,
-                                                     const uint8_t *nonce,
-                                                     size_t nonce_length,
-                                                     const uint8_t *additional_data,
-                                                     size_t additional_data_length,
-                                                     const uint8_t *ciphertext,
-                                                     size_t ciphertext_length,
-                                                     uint8_t *plaintext,
-                                                     size_t plaintext_size,
-                                                     size_t *plaintext_length);
-
-/**@}*/
-
-/** \defgroup accel_asymmetric Hardware-Accelerated Asymmetric Cryptography
- *
- * Since the amount of data that can (or should) be encrypted or signed using
- * asymmetric keys is limited by the key size, hardware-accelerated asymmetric
- * key operations must be done in single function calls.
- */
-/**@{*/
-
-
-/**
- * \brief The function prototype for the hardware-accelerated asymmetric sign
- * operation.
- *
- * Functions that implement this prototype should be named in the following
- * convention:
- * ~~~~~~~~~~~~~{.c}
- * psa_drv_accel_asymmetric_<ALGO>_sign
- * ~~~~~~~~~~~~~
- * Where `ALGO` is the name of the signing algorithm
- *
- * This function supports any asymmetric-key output from psa_export_key() as
- * the buffer in \p p_key. Refer to the documentation of \ref
- * psa_export_key() for the formats.
- *
- * \param[in] p_key                 A buffer containing the private key
- *                                  material
- * \param[in] key_size              The size in bytes of the `p_key` data
- * \param[in] alg                   A signature algorithm that is compatible
- *                                  with the type of `p_key`
- * \param[in] p_hash                The hash or message to sign
- * \param[in] hash_length           Size of the `p_hash` buffer in bytes
- * \param[out] p_signature          Buffer where the signature is to be written
- * \param[in] signature_size        Size of the `p_signature` buffer in bytes
- * \param[out] p_signature_length   On success, the number of bytes
- *                                  that make up the returned signature value
- *
- * \retval #PSA_SUCCESS
- */
-typedef psa_status_t (*psa_drv_accel_asymmetric_sign_t)(const uint8_t *p_key,
-                                                        size_t key_size,
-                                                        psa_algorithm_t alg,
-                                                        psa_key_type_t key_type,
-                                                        const uint8_t *p_hash,
-                                                        size_t hash_length,
-                                                        uint8_t *p_signature,
-                                                        size_t signature_size,
-                                                        size_t *p_signature_length);
-
-/**
- * \brief The function prototype for the hardware-accelerated signature verify
- * operation
- *
- * Functions that implement this prototype should be named in the following
- * convention:
- * ~~~~~~~~~~~~~{.c}
- * psa_drv_accel_asymmetric_<ALGO>_verify
- * ~~~~~~~~~~~~~
- * Where `ALGO` is the name of the signing algorithm
- *
- * This function supports any output from \ref psa_export_public_key() as the
- * buffer in \p p_key. Refer to the documentation of \ref
- * psa_export_public_key() for the format of public keys and to the
- * documentation of \ref psa_export_key() for the format for other key types.
- *
- * \param[in] p_key             A buffer containing the public key material
- * \param[in] key_size          The size in bytes of the `p_key` data
- * \param[in] alg               A signature algorithm that is compatible with
- *                              the type of `key`
- * \param[in] p_hash            The hash or message whose signature is to be
- *                              verified
- * \param[in] hash_length       Size of the `p_hash` buffer in bytes
- * \param[in] p_signature       Buffer containing the signature to verify
- * \param[in] signature_length  Size of the `p_signature` buffer in bytes
- *
- * \retval #PSA_SUCCESS
- *         The signature is valid.
- */
-typedef psa_status_t (*psa_drv_accel_asymmetric_verify_t)(const uint8_t *p_key,
-                                                          size_t key_size,
-                                                          psa_algorithm_t alg,
-                                                          psa_key_type_t key_type,
-                                                          const uint8_t *p_hash,
-                                                          size_t hash_length,
-                                                          const uint8_t *p_signature,
-                                                          size_t signature_length);
-
-/**
- * \brief The function prototype for the hardware-accelerated asymmetric
- * encrypt operation
- *
- * Functions that implement this prototype should be named in the following
- * convention:
- * ~~~~~~~~~~~~~{.c}
- * psa_drv_accel_asymmetric_<ALGO>_encrypt
- * ~~~~~~~~~~~~~
- * Where `ALGO` is the name of the encryption algorithm
- *
- * This function supports any output from \ref psa_export_public_key() as the
- * buffer in \p p_key. Refer to the documentation of \ref
- * psa_export_public_key() for the format of public keys and to the
- * documentation of \ref psa_export_key() for the format for other key types.
- *
- * \param[in] p_key             A buffer containing the public key material
- * \param[in] key_size          The size in bytes of the `p_key` data
- * \param[in] alg               An asymmetric encryption algorithm that is
- *                              compatible with the type of `key`
- * \param[in] p_input           The message to encrypt
- * \param[in] input_length      Size of the `p_input` buffer in bytes
- * \param[in] p_salt            A salt or label, if supported by the
- *                              encryption algorithm
- *                              If the algorithm does not support a
- *                              salt, pass `NULL`
- *                              If the algorithm supports an optional
- *                              salt and you do not want to pass a salt,
- *                              pass `NULL`.
- *                              For #PSA_ALG_RSA_PKCS1V15_CRYPT, no salt is
- *                              supported.
- * \param[in] salt_length       Size of the `p_salt` buffer in bytes
- *                              If `p_salt` is `NULL`, pass 0.
- * \param[out] p_output         Buffer where the encrypted message is to
- *                              be written
- * \param[in] output_size       Size of the `p_output` buffer in bytes
- * \param[out] p_output_length  On success, the number of bytes
- *                              that make up the returned output
- *
- * \retval #PSA_SUCCESS
- */
-typedef psa_status_t (*psa_drv_accel_asymmetric_encrypt_t)(const uint8_t *p_key,
-                                                           size_t key_size,
-                                                           psa_algorithm_t alg,
-                                                           psa_key_type_t key_type,
-                                                           const uint8_t *p_input,
-                                                           size_t input_length,
-                                                           const uint8_t *p_salt,
-                                                           size_t salt_length,
-                                                           uint8_t *p_output,
-                                                           size_t output_size,
-                                                           size_t *p_output_length);
-
-/**
- * \brief The function prototype for the hardware=acce;erated asymmetric
- * decrypt operation
- *
- * Functions that implement this prototype should be named in the following
- * convention:
- * ~~~~~~~~~~~~~{.c}
- * psa_drv_accel_asymmetric_<ALGO>_decrypt
- * ~~~~~~~~~~~~~
- * Where `ALGO` is the name of the encryption algorithm
- *
- * This function supports any asymmetric-key output from psa_export_key() as
- * the buffer in \p p_key. Refer to the documentation of \ref
- * psa_export_key() for the formats.
- *
- * \param[in] p_key             A buffer containing the private key material
- * \param[in] key_size          The size in bytes of the `p_key` data
- * \param[in] alg               An asymmetric encryption algorithm that is
- *                              compatible with the type of `key`
- * \param[in] p_input           The message to decrypt
- * \param[in] input_length      Size of the `p_input` buffer in bytes
- * \param[in] p_salt            A salt or label, if supported by the
- *                              encryption algorithm
- *                              If the algorithm does not support a
- *                              salt, pass `NULL`.
- *                              If the algorithm supports an optional
- *                              salt and you do not want to pass a salt,
- *                              pass `NULL`.
- *                              For #PSA_ALG_RSA_PKCS1V15_CRYPT, no salt is
- *                              supported
- * \param[in] salt_length       Size of the `p_salt` buffer in bytes
- *                              If `p_salt` is `NULL`, pass 0
- * \param[out] p_output         Buffer where the decrypted message is to
- *                              be written
- * \param[in] output_size       Size of the `p_output` buffer in bytes
- * \param[out] p_output_length  On success, the number of bytes
- *                              that make up the returned output
- *
- * \retval #PSA_SUCCESS
- */
-typedef psa_status_t (*psa_drv_accel_asymmetric_decrypt_t)(const uint8_t *p_key,
-                                                           size_t key_size,
-                                                           psa_algorithm_t alg,
-                                                           psa_key_type_t key_type,
-                                                           const uint8_t *p_input,
-                                                           size_t input_length,
-                                                           const uint8_t *p_salt,
-                                                           size_t salt_length,
-                                                           uint8_t *p_output,
-                                                           size_t output_size,
-                                                           size_t *p_output_length);
-
-/**@}*/
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* PSA_CRYPTO_ACCEL_DRIVER_H */
diff --git a/third_party/mbedtls/repo/include/psa/crypto_builtin_composites.h b/third_party/mbedtls/repo/include/psa/crypto_builtin_composites.h
new file mode 100644
index 0000000..a875b23
--- /dev/null
+++ b/third_party/mbedtls/repo/include/psa/crypto_builtin_composites.h
@@ -0,0 +1,79 @@
+/*
+ *  Context structure declaration of the Mbed TLS software-based PSA drivers
+ *  called through the PSA Crypto driver dispatch layer.
+ *  This file contains the context structures of those algorithms which need to
+ *  rely on other algorithms, i.e. are 'composite' algorithms.
+ *
+ * \note This file may not be included directly. Applications must
+ * include psa/crypto.h.
+ *
+ * \note This header and its content is not part of the Mbed TLS API and
+ * applications must not depend on it. Its main purpose is to define the
+ * multi-part state objects of the Mbed TLS software-based PSA drivers. The
+ * definition of these objects are then used by crypto_struct.h to define the
+ * implementation-defined types of PSA multi-part state objects.
+ */
+/*
+ *  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.
+ */
+
+#ifndef PSA_CRYPTO_BUILTIN_COMPOSITES_H
+#define PSA_CRYPTO_BUILTIN_COMPOSITES_H
+
+#include <psa/crypto_driver_common.h>
+
+/*
+ * MAC multi-part operation definitions.
+ */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CMAC) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_HMAC)
+#define MBEDTLS_PSA_BUILTIN_MAC
+#endif
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_HMAC) || defined(PSA_CRYPTO_DRIVER_TEST)
+typedef struct
+{
+    /** The HMAC algorithm in use */
+    psa_algorithm_t alg;
+    /** The hash context. */
+    struct psa_hash_operation_s hash_ctx;
+    /** The HMAC part of the context. */
+    uint8_t opad[PSA_HMAC_MAX_HASH_BLOCK_SIZE];
+} mbedtls_psa_hmac_operation_t;
+
+#define MBEDTLS_PSA_HMAC_OPERATION_INIT {0, PSA_HASH_OPERATION_INIT, {0}}
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_HMAC */
+
+#include "mbedtls/cmac.h"
+
+typedef struct
+{
+    psa_algorithm_t alg;
+    union
+    {
+        unsigned dummy; /* Make the union non-empty even with no supported algorithms. */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_HMAC) || defined(PSA_CRYPTO_DRIVER_TEST)
+        mbedtls_psa_hmac_operation_t hmac;
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_HMAC */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CMAC) || defined(PSA_CRYPTO_DRIVER_TEST)
+        mbedtls_cipher_context_t cmac;
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CMAC */
+    } ctx;
+} mbedtls_psa_mac_operation_t;
+
+#define MBEDTLS_PSA_MAC_OPERATION_INIT {0, {0}}
+
+#endif /* PSA_CRYPTO_BUILTIN_COMPOSITES_H */
diff --git a/third_party/mbedtls/repo/include/psa/crypto_builtin_primitives.h b/third_party/mbedtls/repo/include/psa/crypto_builtin_primitives.h
new file mode 100644
index 0000000..62a0e6f
--- /dev/null
+++ b/third_party/mbedtls/repo/include/psa/crypto_builtin_primitives.h
@@ -0,0 +1,126 @@
+/*
+ *  Context structure declaration of the Mbed TLS software-based PSA drivers
+ *  called through the PSA Crypto driver dispatch layer.
+ *  This file contains the context structures of those algorithms which do not
+ *  rely on other algorithms, i.e. are 'primitive' algorithms.
+ *
+ * \note This file may not be included directly. Applications must
+ * include psa/crypto.h.
+ *
+ * \note This header and its content is not part of the Mbed TLS API and
+ * applications must not depend on it. Its main purpose is to define the
+ * multi-part state objects of the Mbed TLS software-based PSA drivers. The
+ * definition of these objects are then used by crypto_struct.h to define the
+ * implementation-defined types of PSA multi-part state objects.
+ */
+/*
+ *  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.
+ */
+
+#ifndef PSA_CRYPTO_BUILTIN_PRIMITIVES_H
+#define PSA_CRYPTO_BUILTIN_PRIMITIVES_H
+
+#include <psa/crypto_driver_common.h>
+
+/*
+ * Hash multi-part operation definitions.
+ */
+
+#include "mbedtls/md2.h"
+#include "mbedtls/md4.h"
+#include "mbedtls/md5.h"
+#include "mbedtls/ripemd160.h"
+#include "mbedtls/sha1.h"
+#include "mbedtls/sha256.h"
+#include "mbedtls/sha512.h"
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_MD2) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_MD4) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_MD5) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_RIPEMD160) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_1) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_224) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_256) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_384) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_512)
+#define MBEDTLS_PSA_BUILTIN_HASH
+#endif
+
+typedef struct
+{
+    psa_algorithm_t alg;
+    union
+    {
+        unsigned dummy; /* Make the union non-empty even with no supported algorithms. */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_MD2)
+        mbedtls_md2_context md2;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_MD4)
+        mbedtls_md4_context md4;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_MD5)
+        mbedtls_md5_context md5;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RIPEMD160)
+        mbedtls_ripemd160_context ripemd160;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_1)
+        mbedtls_sha1_context sha1;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_256) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_224)
+        mbedtls_sha256_context sha256;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_512) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_384)
+        mbedtls_sha512_context sha512;
+#endif
+    } ctx;
+} mbedtls_psa_hash_operation_t;
+
+#define MBEDTLS_PSA_HASH_OPERATION_INIT {0, {0}}
+
+/*
+ * Cipher multi-part operation definitions.
+ */
+
+#include "mbedtls/cipher.h"
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_STREAM_CIPHER) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_CTR) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_CFB) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_OFB) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_XTS) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_ECB_NO_PADDING) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_CBC_NO_PADDING) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_CBC_PKCS7)
+#define MBEDTLS_PSA_BUILTIN_CIPHER  1
+#endif
+
+typedef struct {
+    /* Context structure for the Mbed TLS cipher implementation. */
+    psa_algorithm_t alg;
+    uint8_t iv_length;
+    uint8_t block_length;
+    union {
+        unsigned int dummy;
+        mbedtls_cipher_context_t cipher;
+    } ctx;
+} mbedtls_psa_cipher_operation_t;
+
+#define MBEDTLS_PSA_CIPHER_OPERATION_INIT {0, 0, 0, {0}}
+
+#endif /* PSA_CRYPTO_BUILTIN_PRIMITIVES_H */
diff --git a/third_party/mbedtls/repo/include/psa/crypto_compat.h b/third_party/mbedtls/repo/include/psa/crypto_compat.h
index 5bb5669..09ac488 100644
--- a/third_party/mbedtls/repo/include/psa/crypto_compat.h
+++ b/third_party/mbedtls/repo/include/psa/crypto_compat.h
@@ -44,7 +44,7 @@
 
 #define PSA_KEY_HANDLE_INIT MBEDTLS_SVC_KEY_ID_INIT
 
-/** Check wether an handle is null.
+/** Check whether an handle is null.
  *
  * \param handle  Handle
  *
@@ -110,6 +110,18 @@
     MBEDTLS_DEPRECATED_CONSTANT( size_t, PSA_SIGNATURE_MAX_SIZE )
 #define PSA_ASYMMETRIC_SIGN_OUTPUT_SIZE( key_type, key_bits, alg ) \
     MBEDTLS_DEPRECATED_CONSTANT( size_t, PSA_SIGN_OUTPUT_SIZE( key_type, key_bits, alg ) )
+#define PSA_KEY_EXPORT_MAX_SIZE( key_type, key_bits ) \
+    MBEDTLS_DEPRECATED_CONSTANT( size_t, PSA_EXPORT_KEY_OUTPUT_SIZE( key_type, key_bits ) )
+#define PSA_BLOCK_CIPHER_BLOCK_SIZE( type ) \
+    MBEDTLS_DEPRECATED_CONSTANT( size_t, PSA_BLOCK_CIPHER_BLOCK_LENGTH( type ) )
+#define PSA_MAX_BLOCK_CIPHER_BLOCK_SIZE \
+    MBEDTLS_DEPRECATED_CONSTANT( size_t, PSA_BLOCK_CIPHER_BLOCK_MAX_SIZE )
+#define PSA_HASH_SIZE( alg ) \
+    MBEDTLS_DEPRECATED_CONSTANT( size_t, PSA_HASH_LENGTH( alg ) )
+#define PSA_MAC_FINAL_SIZE( key_type, key_bits, alg ) \
+    MBEDTLS_DEPRECATED_CONSTANT( size_t, PSA_MAC_LENGTH( key_type, key_bits, alg ) )
+#define PSA_ALG_TLS12_PSK_TO_MS_MAX_PSK_LEN \
+    MBEDTLS_DEPRECATED_CONSTANT( size_t, PSA_TLS12_PSK_TO_MS_PSK_MAX_SIZE )
 
 /*
  * Deprecated PSA Crypto function names (PSA Crypto API  <= 1.0 beta3)
@@ -249,6 +261,161 @@
 #define PSA_ALG_CHACHA20 \
     MBEDTLS_DEPRECATED_CONSTANT( psa_algorithm_t, PSA_ALG_STREAM_CIPHER )
 
+/*
+ * Renamed AEAD tag length macros (PSA Crypto API  <= 1.0 beta3)
+ */
+#define PSA_ALG_AEAD_WITH_DEFAULT_TAG_LENGTH( aead_alg ) \
+    MBEDTLS_DEPRECATED_CONSTANT( psa_algorithm_t, PSA_ALG_AEAD_WITH_DEFAULT_LENGTH_TAG( aead_alg ) )
+#define PSA_ALG_AEAD_WITH_TAG_LENGTH( aead_alg, tag_length ) \
+    MBEDTLS_DEPRECATED_CONSTANT( psa_algorithm_t, PSA_ALG_AEAD_WITH_SHORTENED_TAG( aead_alg, tag_length ) )
+
+/*
+ * Deprecated PSA AEAD output size macros (PSA Crypto API  <= 1.0 beta3)
+ */
+
+/** The tag size for an AEAD algorithm, in bytes.
+ *
+ * \param alg                 An AEAD algorithm
+ *                            (\c PSA_ALG_XXX value such that
+ *                            #PSA_ALG_IS_AEAD(\p alg) is true).
+ *
+ * \return                    The tag size for the specified algorithm.
+ *                            If the AEAD algorithm does not have an identified
+ *                            tag that can be distinguished from the rest of
+ *                            the ciphertext, return 0.
+ *                            If the AEAD algorithm is not recognized, return 0.
+ */
+#define PSA_AEAD_TAG_LENGTH_1_ARG( alg )     \
+    MBEDTLS_DEPRECATED_CONSTANT( size_t,     \
+        PSA_ALG_IS_AEAD( alg ) ?             \
+        PSA_ALG_AEAD_GET_TAG_LENGTH( alg ) : \
+        0 )
+
+/** The maximum size of the output of psa_aead_encrypt(), in bytes.
+ *
+ * If the size of the ciphertext buffer is at least this large, it is
+ * guaranteed that psa_aead_encrypt() will not fail due to an
+ * insufficient buffer size. Depending on the algorithm, the actual size of
+ * the ciphertext may be smaller.
+ *
+ * \warning This macro may evaluate its arguments multiple times or
+ *          zero times, so you should not pass arguments that contain
+ *          side effects.
+ *
+ * \param alg                 An AEAD algorithm
+ *                            (\c PSA_ALG_XXX value such that
+ *                            #PSA_ALG_IS_AEAD(\p alg) is true).
+ * \param plaintext_length    Size of the plaintext in bytes.
+ *
+ * \return                    The AEAD ciphertext size for the specified
+ *                            algorithm.
+ *                            If the AEAD algorithm is not recognized, return 0.
+ */
+#define PSA_AEAD_ENCRYPT_OUTPUT_SIZE_2_ARG( alg, plaintext_length ) \
+    MBEDTLS_DEPRECATED_CONSTANT( size_t,                            \
+        PSA_ALG_IS_AEAD( alg ) ?                                    \
+        (plaintext_length) + PSA_ALG_AEAD_GET_TAG_LENGTH( alg ) :   \
+        0 )
+
+/** The maximum size of the output of psa_aead_decrypt(), in bytes.
+ *
+ * If the size of the plaintext buffer is at least this large, it is
+ * guaranteed that psa_aead_decrypt() will not fail due to an
+ * insufficient buffer size. Depending on the algorithm, the actual size of
+ * the plaintext may be smaller.
+ *
+ * \warning This macro may evaluate its arguments multiple times or
+ *          zero times, so you should not pass arguments that contain
+ *          side effects.
+ *
+ * \param alg                 An AEAD algorithm
+ *                            (\c PSA_ALG_XXX value such that
+ *                            #PSA_ALG_IS_AEAD(\p alg) is true).
+ * \param ciphertext_length   Size of the plaintext in bytes.
+ *
+ * \return                    The AEAD ciphertext size for the specified
+ *                            algorithm.
+ *                            If the AEAD algorithm is not recognized, return 0.
+ */
+#define PSA_AEAD_DECRYPT_OUTPUT_SIZE_2_ARG( alg, ciphertext_length )   \
+    MBEDTLS_DEPRECATED_CONSTANT( size_t,                               \
+        PSA_ALG_IS_AEAD( alg ) &&                                      \
+            (ciphertext_length) > PSA_ALG_AEAD_GET_TAG_LENGTH( alg ) ? \
+            (ciphertext_length) - PSA_ALG_AEAD_GET_TAG_LENGTH( alg ) : \
+        0 )
+
+/** A sufficient output buffer size for psa_aead_update().
+ *
+ * If the size of the output buffer is at least this large, it is
+ * guaranteed that psa_aead_update() will not fail due to an
+ * insufficient buffer size. The actual size of the output may be smaller
+ * in any given call.
+ *
+ * \warning This macro may evaluate its arguments multiple times or
+ *          zero times, so you should not pass arguments that contain
+ *          side effects.
+ *
+ * \param alg                 An AEAD algorithm
+ *                            (\c PSA_ALG_XXX value such that
+ *                            #PSA_ALG_IS_AEAD(\p alg) is true).
+ * \param input_length        Size of the input in bytes.
+ *
+ * \return                    A sufficient output buffer size for the specified
+ *                            algorithm.
+ *                            If the AEAD algorithm is not recognized, return 0.
+ */
+/* For all the AEAD modes defined in this specification, it is possible
+ * to emit output without delay. However, hardware may not always be
+ * capable of this. So for modes based on a block cipher, allow the
+ * implementation to delay the output until it has a full block. */
+#define PSA_AEAD_UPDATE_OUTPUT_SIZE_2_ARG( alg, input_length )                        \
+    MBEDTLS_DEPRECATED_CONSTANT( size_t,                                              \
+        PSA_ALG_IS_AEAD_ON_BLOCK_CIPHER( alg ) ?                                      \
+        PSA_ROUND_UP_TO_MULTIPLE( PSA_BLOCK_CIPHER_BLOCK_MAX_SIZE, (input_length) ) : \
+        (input_length) )
+
+/** A sufficient ciphertext buffer size for psa_aead_finish().
+ *
+ * If the size of the ciphertext buffer is at least this large, it is
+ * guaranteed that psa_aead_finish() will not fail due to an
+ * insufficient ciphertext buffer size. The actual size of the output may
+ * be smaller in any given call.
+ *
+ * \param alg                 An AEAD algorithm
+ *                            (\c PSA_ALG_XXX value such that
+ *                            #PSA_ALG_IS_AEAD(\p alg) is true).
+ *
+ * \return                    A sufficient ciphertext buffer size for the
+ *                            specified algorithm.
+ *                            If the AEAD algorithm is not recognized, return 0.
+ */
+#define PSA_AEAD_FINISH_OUTPUT_SIZE_1_ARG( alg )                        \
+    MBEDTLS_DEPRECATED_CONSTANT( size_t,                                \
+        PSA_ALG_IS_AEAD_ON_BLOCK_CIPHER( alg ) ?                        \
+        PSA_BLOCK_CIPHER_BLOCK_MAX_SIZE :                               \
+        0 )
+
+/** A sufficient plaintext buffer size for psa_aead_verify().
+ *
+ * If the size of the plaintext buffer is at least this large, it is
+ * guaranteed that psa_aead_verify() will not fail due to an
+ * insufficient plaintext buffer size. The actual size of the output may
+ * be smaller in any given call.
+ *
+ * \param alg                 An AEAD algorithm
+ *                            (\c PSA_ALG_XXX value such that
+ *                            #PSA_ALG_IS_AEAD(\p alg) is true).
+ *
+ * \return                    A sufficient plaintext buffer size for the
+ *                            specified algorithm.
+ *                            If the AEAD algorithm is not recognized, return 0.
+ */
+#define PSA_AEAD_VERIFY_OUTPUT_SIZE_1_ARG( alg )                        \
+    MBEDTLS_DEPRECATED_CONSTANT( size_t,                                \
+        PSA_ALG_IS_AEAD_ON_BLOCK_CIPHER( alg ) ?                        \
+        PSA_BLOCK_CIPHER_BLOCK_MAX_SIZE :                               \
+        0 )
+
 #endif /* MBEDTLS_DEPRECATED_REMOVED */
 
 /** Open a handle to an existing persistent key.
@@ -293,9 +460,9 @@
  *         number of open keys, the number of open key handles, or available
  *         memory.
  * \retval #PSA_ERROR_DOES_NOT_EXIST
- *         There is no persistent key with key identifier \p id.
+ *         There is no persistent key with key identifier \p key.
  * \retval #PSA_ERROR_INVALID_ARGUMENT
- *         \p id is not a valid persistent key identifier.
+ *         \p key is not a valid persistent key identifier.
  * \retval #PSA_ERROR_NOT_PERMITTED
  *         The specified key exists, but the application does not have the
  *         permission to access it. Note that this specification does not
@@ -304,6 +471,8 @@
  * \retval #PSA_ERROR_COMMUNICATION_FAILURE
  * \retval #PSA_ERROR_CORRUPTION_DETECTED
  * \retval #PSA_ERROR_STORAGE_FAILURE
+ * \retval #PSA_ERROR_DATA_INVALID
+ * \retval #PSA_ERROR_DATA_CORRUPT
  * \retval #PSA_ERROR_BAD_STATE
  *         The library has not been previously initialized by psa_crypto_init().
  *         It is implementation-dependent whether a failure to initialize
diff --git a/third_party/mbedtls/repo/include/psa/crypto_config.h b/third_party/mbedtls/repo/include/psa/crypto_config.h
index cf7f63a..e2446cb 100644
--- a/third_party/mbedtls/repo/include/psa/crypto_config.h
+++ b/third_party/mbedtls/repo/include/psa/crypto_config.h
@@ -50,14 +50,29 @@
 #ifndef PSA_CRYPTO_CONFIG_H
 #define PSA_CRYPTO_CONFIG_H
 
+/*
+ * CBC-MAC is not yet supported via the PSA API in Mbed TLS.
+ */
+//#define PSA_WANT_ALG_CBC_MAC                    1
+#define PSA_WANT_ALG_CBC_NO_PADDING             1
+#define PSA_WANT_ALG_CBC_PKCS7                  1
+#define PSA_WANT_ALG_CCM                        1
+#define PSA_WANT_ALG_CMAC                       1
+#define PSA_WANT_ALG_CFB                        1
+#define PSA_WANT_ALG_CHACHA20_POLY1305          1
+#define PSA_WANT_ALG_CMAC                       1
+#define PSA_WANT_ALG_CTR                        1
 #define PSA_WANT_ALG_DETERMINISTIC_ECDSA        1
+#define PSA_WANT_ALG_ECB_NO_PADDING             1
 #define PSA_WANT_ALG_ECDH                       1
 #define PSA_WANT_ALG_ECDSA                      1
+#define PSA_WANT_ALG_GCM                        1
 #define PSA_WANT_ALG_HKDF                       1
 #define PSA_WANT_ALG_HMAC                       1
 #define PSA_WANT_ALG_MD2                        1
 #define PSA_WANT_ALG_MD4                        1
 #define PSA_WANT_ALG_MD5                        1
+#define PSA_WANT_ALG_OFB                        1
 #define PSA_WANT_ALG_RIPEMD160                  1
 #define PSA_WANT_ALG_RSA_OAEP                   1
 #define PSA_WANT_ALG_RSA_PKCS1V15_CRYPT         1
@@ -68,10 +83,46 @@
 #define PSA_WANT_ALG_SHA_256                    1
 #define PSA_WANT_ALG_SHA_384                    1
 #define PSA_WANT_ALG_SHA_512                    1
+#define PSA_WANT_ALG_STREAM_CIPHER              1
 #define PSA_WANT_ALG_TLS12_PRF                  1
 #define PSA_WANT_ALG_TLS12_PSK_TO_MS            1
+#define PSA_WANT_ALG_XTS                        1
+
+#define PSA_WANT_ECC_BRAINPOOL_P_R1_256         1
+#define PSA_WANT_ECC_BRAINPOOL_P_R1_384         1
+#define PSA_WANT_ECC_BRAINPOOL_P_R1_512         1
+#define PSA_WANT_ECC_MONTGOMERY_255             1
+/*
+ * Curve448 is not yet supported via the PSA API in Mbed TLS
+ * (https://github.com/ARMmbed/mbedtls/issues/4249). Thus, do not enable it by
+ * default.
+ */
+//#define PSA_WANT_ECC_MONTGOMERY_448             1
+#define PSA_WANT_ECC_SECP_K1_192                1
+/*
+ * SECP224K1 is buggy via the PSA API in Mbed TLS
+ * (https://github.com/ARMmbed/mbedtls/issues/3541). Thus, do not enable it by
+ * default.
+ */
+//#define PSA_WANT_ECC_SECP_K1_224                1
+#define PSA_WANT_ECC_SECP_K1_256                1
+#define PSA_WANT_ECC_SECP_R1_192                1
+#define PSA_WANT_ECC_SECP_R1_224                1
+#define PSA_WANT_ECC_SECP_R1_256                1
+#define PSA_WANT_ECC_SECP_R1_384                1
+#define PSA_WANT_ECC_SECP_R1_521                1
+
+#define PSA_WANT_KEY_TYPE_DERIVE                1
+#define PSA_WANT_KEY_TYPE_HMAC                  1
+#define PSA_WANT_KEY_TYPE_AES                   1
+#define PSA_WANT_KEY_TYPE_ARC4                  1
+#define PSA_WANT_KEY_TYPE_ARIA                  1
+#define PSA_WANT_KEY_TYPE_CAMELLIA              1
+#define PSA_WANT_KEY_TYPE_CHACHA20              1
+#define PSA_WANT_KEY_TYPE_DES                   1
 #define PSA_WANT_KEY_TYPE_ECC_KEY_PAIR          1
 #define PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY        1
+#define PSA_WANT_KEY_TYPE_RAW_DATA              1
 #define PSA_WANT_KEY_TYPE_RSA_KEY_PAIR          1
 #define PSA_WANT_KEY_TYPE_RSA_PUBLIC_KEY        1
 
diff --git a/third_party/mbedtls/repo/include/psa/crypto_driver_common.h b/third_party/mbedtls/repo/include/psa/crypto_driver_common.h
index 2ce75d2..26363c6 100644
--- a/third_party/mbedtls/repo/include/psa/crypto_driver_common.h
+++ b/third_party/mbedtls/repo/include/psa/crypto_driver_common.h
@@ -5,9 +5,8 @@
  * This file contains common definitions shared by all PSA crypto drivers.
  * Do not include it directly: instead, include the header file(s) for
  * the type(s) of driver that you are implementing. For example, if
- * you are writing a driver for a chip that provides both a hardware
- * random generator and an accelerator for some cryptographic algorithms,
- * include `psa/crypto_entropy_driver.h` and `psa/crypto_accel_driver.h`.
+ * you are writing a dynamically registered driver for a secure element,
+ * include `psa/crypto_se_driver.h`.
  *
  * This file is part of the PSA Crypto Driver Model, containing functions for
  * driver developers to implement to enable hardware to be called in a
@@ -43,6 +42,9 @@
  * of these types. */
 #include "crypto_types.h"
 #include "crypto_values.h"
+/* Include size definitions which are used to size some arrays in operation
+ * structures. */
+#include <psa/crypto_sizes.h>
 
 /** For encrypt-decrypt functions, whether the operation is an encryption
  * or a decryption. */
diff --git a/third_party/mbedtls/repo/include/psa/crypto_driver_contexts_composites.h b/third_party/mbedtls/repo/include/psa/crypto_driver_contexts_composites.h
new file mode 100644
index 0000000..a722009
--- /dev/null
+++ b/third_party/mbedtls/repo/include/psa/crypto_driver_contexts_composites.h
@@ -0,0 +1,93 @@
+/*
+ *  Declaration of context structures for use with the PSA driver wrapper
+ *  interface. This file contains the context structures for 'composite'
+ *  operations, i.e. those operations which need to make use of other operations
+ *  from the primitives (crypto_driver_contexts_primitives.h)
+ *
+ *  Warning: This file will be auto-generated in the future.
+ *
+ * \note This file may not be included directly. Applications must
+ * include psa/crypto.h.
+ *
+ * \note This header and its content is not part of the Mbed TLS API and
+ * applications must not depend on it. Its main purpose is to define the
+ * multi-part state objects of the PSA drivers included in the cryptographic
+ * library. The definition of these objects are then used by crypto_struct.h
+ * to define the implementation-defined types of PSA multi-part state objects.
+ */
+/*  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.
+ */
+
+#ifndef PSA_CRYPTO_DRIVER_CONTEXTS_COMPOSITES_H
+#define PSA_CRYPTO_DRIVER_CONTEXTS_COMPOSITES_H
+
+#include "psa/crypto_driver_common.h"
+
+/* Include the context structure definitions for the Mbed TLS software drivers */
+#include "psa/crypto_builtin_composites.h"
+
+/* Include the context structure definitions for those drivers that were
+ * declared during the autogeneration process. */
+
+#if defined(MBEDTLS_TEST_LIBTESTDRIVER1)
+#include <libtestdriver1/include/psa/crypto.h>
+#endif
+
+#if defined(PSA_CRYPTO_DRIVER_TEST)
+#if defined(MBEDTLS_TEST_LIBTESTDRIVER1) && \
+    defined(LIBTESTDRIVER1_MBEDTLS_PSA_BUILTIN_MAC)
+typedef libtestdriver1_mbedtls_psa_mac_operation_t
+        mbedtls_transparent_test_driver_mac_operation_t;
+typedef libtestdriver1_mbedtls_psa_mac_operation_t
+        mbedtls_opaque_test_driver_mac_operation_t;
+
+#define MBEDTLS_TRANSPARENT_TEST_DRIVER_MAC_OPERATION_INIT \
+        LIBTESTDRIVER1_MBEDTLS_PSA_MAC_OPERATION_INIT
+#define MBEDTLS_OPAQUE_TEST_DRIVER_MAC_OPERATION_INIT \
+        LIBTESTDRIVER1_MBEDTLS_PSA_MAC_OPERATION_INIT
+
+#else
+typedef mbedtls_psa_mac_operation_t
+        mbedtls_transparent_test_driver_mac_operation_t;
+typedef mbedtls_psa_mac_operation_t
+        mbedtls_opaque_test_driver_mac_operation_t;
+
+#define MBEDTLS_TRANSPARENT_TEST_DRIVER_MAC_OPERATION_INIT \
+        MBEDTLS_PSA_MAC_OPERATION_INIT
+#define MBEDTLS_OPAQUE_TEST_DRIVER_MAC_OPERATION_INIT \
+        MBEDTLS_PSA_MAC_OPERATION_INIT
+
+#endif /* MBEDTLS_TEST_LIBTESTDRIVER1 */
+#endif /* PSA_CRYPTO_DRIVER_TEST */
+
+/* Define the context to be used for an operation that is executed through the
+ * PSA Driver wrapper layer as the union of all possible driver's contexts.
+ *
+ * The union members are the driver's context structures, and the member names
+ * are formatted as `'drivername'_ctx`. This allows for procedural generation
+ * of both this file and the content of psa_crypto_driver_wrappers.c */
+
+typedef union {
+    unsigned dummy; /* Make sure this union is always non-empty */
+    mbedtls_psa_mac_operation_t mbedtls_ctx;
+#if defined(PSA_CRYPTO_DRIVER_TEST)
+    mbedtls_transparent_test_driver_mac_operation_t transparent_test_driver_ctx;
+    mbedtls_opaque_test_driver_mac_operation_t opaque_test_driver_ctx;
+#endif
+} psa_driver_mac_context_t;
+
+#endif /* PSA_CRYPTO_DRIVER_CONTEXTS_COMPOSITES_H */
+/* End of automatically generated file. */
diff --git a/third_party/mbedtls/repo/include/psa/crypto_driver_contexts_primitives.h b/third_party/mbedtls/repo/include/psa/crypto_driver_contexts_primitives.h
new file mode 100644
index 0000000..2bb01ed
--- /dev/null
+++ b/third_party/mbedtls/repo/include/psa/crypto_driver_contexts_primitives.h
@@ -0,0 +1,117 @@
+/*
+ *  Declaration of context structures for use with the PSA driver wrapper
+ *  interface. This file contains the context structures for 'primitive'
+ *  operations, i.e. those operations which do not rely on other contexts.
+ *
+ *  Warning: This file will be auto-generated in the future.
+ *
+ * \note This file may not be included directly. Applications must
+ * include psa/crypto.h.
+ *
+ * \note This header and its content is not part of the Mbed TLS API and
+ * applications must not depend on it. Its main purpose is to define the
+ * multi-part state objects of the PSA drivers included in the cryptographic
+ * library. The definition of these objects are then used by crypto_struct.h
+ * to define the implementation-defined types of PSA multi-part state objects.
+ */
+/*  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.
+ */
+
+#ifndef PSA_CRYPTO_DRIVER_CONTEXTS_PRIMITIVES_H
+#define PSA_CRYPTO_DRIVER_CONTEXTS_PRIMITIVES_H
+
+#include "psa/crypto_driver_common.h"
+
+/* Include the context structure definitions for the Mbed TLS software drivers */
+#include "psa/crypto_builtin_primitives.h"
+
+/* Include the context structure definitions for those drivers that were
+ * declared during the autogeneration process. */
+
+#if defined(MBEDTLS_TEST_LIBTESTDRIVER1)
+#include <libtestdriver1/include/psa/crypto.h>
+#endif
+
+#if defined(PSA_CRYPTO_DRIVER_TEST)
+
+#if defined(MBEDTLS_TEST_LIBTESTDRIVER1) && \
+    defined(LIBTESTDRIVER1_MBEDTLS_PSA_BUILTIN_CIPHER)
+typedef libtestdriver1_mbedtls_psa_cipher_operation_t
+        mbedtls_transparent_test_driver_cipher_operation_t;
+
+#define MBEDTLS_TRANSPARENT_TEST_DRIVER_CIPHER_OPERATION_INIT \
+        LIBTESTDRIVER1_MBEDTLS_PSA_CIPHER_OPERATION_INIT
+#else
+typedef mbedtls_psa_cipher_operation_t
+        mbedtls_transparent_test_driver_cipher_operation_t;
+
+#define MBEDTLS_TRANSPARENT_TEST_DRIVER_CIPHER_OPERATION_INIT \
+        MBEDTLS_PSA_CIPHER_OPERATION_INIT
+#endif /* MBEDTLS_TEST_LIBTESTDRIVER1 &&
+          LIBTESTDRIVER1_MBEDTLS_PSA_BUILTIN_CIPHER */
+
+#if defined(MBEDTLS_TEST_LIBTESTDRIVER1) && \
+    defined(LIBTESTDRIVER1_MBEDTLS_PSA_BUILTIN_HASH)
+typedef libtestdriver1_mbedtls_psa_hash_operation_t
+        mbedtls_transparent_test_driver_hash_operation_t;
+
+#define MBEDTLS_TRANSPARENT_TEST_DRIVER_HASH_OPERATION_INIT \
+        LIBTESTDRIVER1_MBEDTLS_PSA_HASH_OPERATION_INIT
+#else
+typedef mbedtls_psa_hash_operation_t
+        mbedtls_transparent_test_driver_hash_operation_t;
+
+#define MBEDTLS_TRANSPARENT_TEST_DRIVER_HASH_OPERATION_INIT \
+        MBEDTLS_PSA_HASH_OPERATION_INIT
+#endif /* MBEDTLS_TEST_LIBTESTDRIVER1 &&
+          LIBTESTDRIVER1_MBEDTLS_PSA_BUILTIN_HASH */
+
+typedef struct {
+    unsigned int initialised : 1;
+    mbedtls_transparent_test_driver_cipher_operation_t ctx;
+} mbedtls_opaque_test_driver_cipher_operation_t;
+
+#define MBEDTLS_OPAQUE_TEST_DRIVER_CIPHER_OPERATION_INIT \
+     { 0, MBEDTLS_TRANSPARENT_TEST_DRIVER_CIPHER_OPERATION_INIT }
+
+#endif /* PSA_CRYPTO_DRIVER_TEST */
+
+/* Define the context to be used for an operation that is executed through the
+ * PSA Driver wrapper layer as the union of all possible driver's contexts.
+ *
+ * The union members are the driver's context structures, and the member names
+ * are formatted as `'drivername'_ctx`. This allows for procedural generation
+ * of both this file and the content of psa_crypto_driver_wrappers.c */
+
+typedef union {
+    unsigned dummy; /* Make sure this union is always non-empty */
+    mbedtls_psa_hash_operation_t mbedtls_ctx;
+#if defined(PSA_CRYPTO_DRIVER_TEST)
+    mbedtls_transparent_test_driver_hash_operation_t test_driver_ctx;
+#endif
+} psa_driver_hash_context_t;
+
+typedef union {
+    unsigned dummy; /* Make sure this union is always non-empty */
+    mbedtls_psa_cipher_operation_t mbedtls_ctx;
+#if defined(PSA_CRYPTO_DRIVER_TEST)
+    mbedtls_transparent_test_driver_cipher_operation_t transparent_test_driver_ctx;
+    mbedtls_opaque_test_driver_cipher_operation_t opaque_test_driver_ctx;
+#endif
+} psa_driver_cipher_context_t;
+
+#endif /* PSA_CRYPTO_DRIVER_CONTEXTS_PRIMITIVES_H */
+/* End of automatically generated file. */
diff --git a/third_party/mbedtls/repo/include/psa/crypto_entropy_driver.h b/third_party/mbedtls/repo/include/psa/crypto_entropy_driver.h
deleted file mode 100644
index 9b6546e..0000000
--- a/third_party/mbedtls/repo/include/psa/crypto_entropy_driver.h
+++ /dev/null
@@ -1,108 +0,0 @@
-/**
- * \file psa/crypto_entropy_driver.h
- * \brief PSA entropy source driver module
- *
- * This header declares types and function signatures for entropy sources.
- *
- * This file is part of the PSA Crypto Driver Model, containing functions for
- * driver developers to implement to enable hardware to be called in a
- * standardized way by a PSA Cryptographic API implementation. The functions
- * comprising the driver model, which driver authors implement, are not
- * intended to be called by application developers.
- */
-
-/*
- *  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.
- */
-#ifndef PSA_CRYPTO_ENTROPY_DRIVER_H
-#define PSA_CRYPTO_ENTROPY_DRIVER_H
-
-#include "crypto_driver_common.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/** \defgroup driver_rng Entropy Generation
- */
-/**@{*/
-
-/** \brief Initialize an entropy driver
- *
- *
- * \param[in,out] p_context             A hardware-specific structure
- *                                      containing any context information for
- *                                      the implementation
- *
- * \retval #PSA_SUCCESS
- */
-typedef psa_status_t (*psa_drv_entropy_init_t)(void *p_context);
-
-/** \brief Get a specified number of bits from the entropy source
- *
- * It retrives `buffer_size` bytes of data from the entropy source. The entropy
- * source will always fill the provided buffer to its full size, however, most
- * entropy sources have biases, and the actual amount of entropy contained in
- * the buffer will be less than the number of bytes.
- * The driver will return the actual number of bytes of entropy placed in the
- * buffer in `p_received_entropy_bytes`.
- * A PSA Crypto API implementation will likely feed the output of this function
- * into a Digital Random Bit Generator (DRBG), and typically has a minimum
- * amount of entropy that it needs.
- * To accomplish this, the PSA Crypto implementation should be designed to call
- * this function multiple times until it has received the required amount of
- * entropy from the entropy source.
- *
- * \param[in,out] p_context                 A hardware-specific structure
- *                                          containing any context information
- *                                          for the implementation
- * \param[out] p_buffer                     A caller-allocated buffer for the
- *                                          retrieved entropy to be placed in
- * \param[in] buffer_size                   The allocated size of `p_buffer`
- * \param[out] p_received_entropy_bits      The amount of entropy (in bits)
- *                                          actually provided in `p_buffer`
- *
- * \retval #PSA_SUCCESS
- */
-typedef psa_status_t (*psa_drv_entropy_get_bits_t)(void *p_context,
-                                                   uint8_t *p_buffer,
-                                                   uint32_t buffer_size,
-                                                   uint32_t *p_received_entropy_bits);
-
-/**
- * \brief A struct containing all of the function pointers needed to interface
- * to an entropy source
- *
- * PSA Crypto API implementations should populate instances of the table as
- * appropriate upon startup.
- *
- * If one of the functions is not implemented, it should be set to NULL.
- */
-typedef struct {
-    /** The driver-specific size of the entropy context */
-    const size_t                context_size;
-    /** Function that performs initialization for the entropy source */
-    psa_drv_entropy_init_t      p_init;
-    /** Function that performs the get_bits operation for the entropy source */
-    psa_drv_entropy_get_bits_t  p_get_bits;
-} psa_drv_entropy_t;
-/**@}*/
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* PSA_CRYPTO_ENTROPY_DRIVER_H */
diff --git a/third_party/mbedtls/repo/include/psa/crypto_extra.h b/third_party/mbedtls/repo/include/psa/crypto_extra.h
index f793a6c..3ee0482 100644
--- a/third_party/mbedtls/repo/include/psa/crypto_extra.h
+++ b/third_party/mbedtls/repo/include/psa/crypto_extra.h
@@ -30,6 +30,7 @@
 
 #include "mbedtls/platform_util.h"
 
+#include "crypto_types.h"
 #include "crypto_compat.h"
 
 #ifdef __cplusplus
@@ -39,6 +40,10 @@
 /* UID for secure storage seed */
 #define PSA_CRYPTO_ITS_RANDOM_SEED_UID 0xFFFFFF52
 
+/* See config.h for definition */
+#if !defined(MBEDTLS_PSA_KEY_SLOT_COUNT)
+#define MBEDTLS_PSA_KEY_SLOT_COUNT 32
+#endif
 
 /** \addtogroup attributes
  * @{
@@ -175,6 +180,9 @@
  *         The secure element driver for the specified lifetime does not
  *         support registering a key.
  * \retval #PSA_ERROR_INVALID_ARGUMENT
+ *         The identifier in \p attributes is invalid, namely the identifier is
+ *         not in the user range.
+ * \retval #PSA_ERROR_INVALID_ARGUMENT
  *         \p attributes specifies a lifetime which is not located
  *         in a secure element.
  * \retval #PSA_ERROR_INVALID_ARGUMENT
@@ -183,8 +191,10 @@
  * \retval #PSA_ERROR_NOT_PERMITTED
  *         The caller is not authorized to register the specified key slot.
  * \retval #PSA_ERROR_INSUFFICIENT_MEMORY
+ * \retval #PSA_ERROR_INSUFFICIENT_STORAGE
  * \retval #PSA_ERROR_COMMUNICATION_FAILURE
- * \retval #PSA_ERROR_HARDWARE_FAILURE
+ * \retval #PSA_ERROR_DATA_INVALID
+ * \retval #PSA_ERROR_DATA_CORRUPT
  * \retval #PSA_ERROR_CORRUPTION_DETECTED
  * \retval #PSA_ERROR_BAD_STATE
  *         The library has not been previously initialized by psa_crypto_init().
@@ -401,10 +411,9 @@
 
 /* We need to expand the sample definition of this macro from
  * the API definition. */
-#undef PSA_ALG_IS_HASH_AND_SIGN
-#define PSA_ALG_IS_HASH_AND_SIGN(alg)                                   \
-    (PSA_ALG_IS_RSA_PSS(alg) || PSA_ALG_IS_RSA_PKCS1V15_SIGN(alg) ||    \
-     PSA_ALG_IS_DSA(alg) || PSA_ALG_IS_ECDSA(alg))
+#undef PSA_ALG_IS_VENDOR_HASH_AND_SIGN
+#define PSA_ALG_IS_VENDOR_HASH_AND_SIGN(alg)    \
+    PSA_ALG_IS_DSA(alg)
 
 /**@}*/
 
@@ -635,20 +644,174 @@
  *
  * \param curve         A PSA elliptic curve identifier
  *                      (`PSA_ECC_FAMILY_xxx`).
- * \param byte_length   The byte-length of a private key on \p curve.
+ * \param bits          The bit-length of a private key on \p curve.
+ * \param bits_is_sloppy If true, \p bits may be the bit-length rounded up
+ *                      to the nearest multiple of 8. This allows the caller
+ *                      to infer the exact curve from the length of a key
+ *                      which is supplied as a byte string.
  *
  * \return              The corresponding Mbed TLS elliptic curve identifier
  *                      (`MBEDTLS_ECP_DP_xxx`).
  * \return              #MBEDTLS_ECP_DP_NONE if \c curve is not recognized.
- * \return              #MBEDTLS_ECP_DP_NONE if \p byte_length is not
+ * \return              #MBEDTLS_ECP_DP_NONE if \p bits is not
  *                      correct for \p curve.
  */
 mbedtls_ecp_group_id mbedtls_ecc_group_of_psa( psa_ecc_family_t curve,
-                                               size_t byte_length );
+                                               size_t bits,
+                                               int bits_is_sloppy );
 #endif /* MBEDTLS_ECP_C */
 
 /**@}*/
 
+/** \defgroup psa_external_rng External random generator
+ * @{
+ */
+
+#if defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG)
+/** External random generator function, implemented by the platform.
+ *
+ * When the compile-time option #MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG is enabled,
+ * this function replaces Mbed TLS's entropy and DRBG modules for all
+ * random generation triggered via PSA crypto interfaces.
+ *
+ * \note This random generator must deliver random numbers with cryptographic
+ *       quality and high performance. It must supply unpredictable numbers
+ *       with a uniform distribution. The implementation of this function
+ *       is responsible for ensuring that the random generator is seeded
+ *       with sufficient entropy. If you have a hardware TRNG which is slow
+ *       or delivers non-uniform output, declare it as an entropy source
+ *       with mbedtls_entropy_add_source() instead of enabling this option.
+ *
+ * \param[in,out] context       Pointer to the random generator context.
+ *                              This is all-bits-zero on the first call
+ *                              and preserved between successive calls.
+ * \param[out] output           Output buffer. On success, this buffer
+ *                              contains random data with a uniform
+ *                              distribution.
+ * \param output_size           The size of the \p output buffer in bytes.
+ * \param[out] output_length    On success, set this value to \p output_size.
+ *
+ * \retval #PSA_SUCCESS
+ *         Success. The output buffer contains \p output_size bytes of
+ *         cryptographic-quality random data, and \c *output_length is
+ *         set to \p output_size.
+ * \retval #PSA_ERROR_INSUFFICIENT_ENTROPY
+ *         The random generator requires extra entropy and there is no
+ *         way to obtain entropy under current environment conditions.
+ *         This error should not happen under normal circumstances since
+ *         this function is responsible for obtaining as much entropy as
+ *         it needs. However implementations of this function may return
+ *         #PSA_ERROR_INSUFFICIENT_ENTROPY if there is no way to obtain
+ *         entropy without blocking indefinitely.
+ * \retval #PSA_ERROR_HARDWARE_FAILURE
+ *         A failure of the random generator hardware that isn't covered
+ *         by #PSA_ERROR_INSUFFICIENT_ENTROPY.
+ */
+psa_status_t mbedtls_psa_external_get_random(
+    mbedtls_psa_external_random_context_t *context,
+    uint8_t *output, size_t output_size, size_t *output_length );
+#endif /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */
+
+/**@}*/
+
+/** \defgroup psa_builtin_keys Built-in keys
+ * @{
+ */
+
+/** The minimum value for a key identifier that is built into the
+ * implementation.
+ *
+ * The range of key identifiers from #MBEDTLS_PSA_KEY_ID_BUILTIN_MIN
+ * to #MBEDTLS_PSA_KEY_ID_BUILTIN_MAX within the range from
+ * #PSA_KEY_ID_VENDOR_MIN and #PSA_KEY_ID_VENDOR_MAX and must not intersect
+ * with any other set of implementation-chosen key identifiers.
+ *
+ * This value is part of the library's ABI since changing it would invalidate
+ * the values of built-in key identifiers in applications.
+ */
+#define MBEDTLS_PSA_KEY_ID_BUILTIN_MIN          ((psa_key_id_t)0x7fff0000)
+
+/** The maximum value for a key identifier that is built into the
+ * implementation.
+ *
+ * See #MBEDTLS_PSA_KEY_ID_BUILTIN_MIN for more information.
+ */
+#define MBEDTLS_PSA_KEY_ID_BUILTIN_MAX          ((psa_key_id_t)0x7fffefff)
+
+/** A slot number identifying a key in a driver.
+ *
+ * Values of this type are used to identify built-in keys.
+ */
+typedef uint64_t psa_drv_slot_number_t;
+
+#if defined(MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS)
+/** Test whether a key identifier belongs to the builtin key range.
+ *
+ * \param key_id  Key identifier to test.
+ *
+ * \retval 1
+ *         The key identifier is a builtin key identifier.
+ * \retval 0
+ *         The key identifier is not a builtin key identifier.
+ */
+static inline int psa_key_id_is_builtin( psa_key_id_t key_id )
+{
+    return( ( key_id >= MBEDTLS_PSA_KEY_ID_BUILTIN_MIN ) &&
+            ( key_id <= MBEDTLS_PSA_KEY_ID_BUILTIN_MAX ) );
+}
+
+/** Platform function to obtain the location and slot number of a built-in key.
+ *
+ * An application-specific implementation of this function must be provided if
+ * #MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS is enabled. This would typically be provided
+ * as part of a platform's system image.
+ *
+ * #MBEDTLS_SVC_KEY_ID_GET_KEY_ID(\p key_id) needs to be in the range from
+ * #MBEDTLS_PSA_KEY_ID_BUILTIN_MIN to #MBEDTLS_PSA_KEY_ID_BUILTIN_MAX.
+ *
+ * In a multi-application configuration
+ * (\c MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER is defined),
+ * this function should check that #MBEDTLS_SVC_KEY_ID_GET_OWNER_ID(\p key_id)
+ * is allowed to use the given key.
+ *
+ * \param key_id                The key ID for which to retrieve the
+ *                              location and slot attributes.
+ * \param[out] lifetime         On success, the lifetime associated with the key
+ *                              corresponding to \p key_id. Lifetime is a
+ *                              combination of which driver contains the key,
+ *                              and with what persistence level the key is
+ *                              intended to be used. If the platform
+ *                              implementation does not contain specific
+ *                              information about the intended key persistence
+ *                              level, the persistence level may be reported as
+ *                              #PSA_KEY_PERSISTENCE_DEFAULT.
+ * \param[out] slot_number      On success, the slot number known to the driver
+ *                              registered at the lifetime location reported
+ *                              through \p lifetime which corresponds to the
+ *                              requested built-in key.
+ *
+ * \retval #PSA_SUCCESS
+ *         The requested key identifier designates a built-in key.
+ *         In a multi-application configuration, the requested owner
+ *         is allowed to access it.
+ * \retval #PSA_ERROR_DOES_NOT_EXIST
+ *         The requested key identifier is not a built-in key which is known
+ *         to this function. If a key exists in the key storage with this
+ *         identifier, the data from the storage will be used.
+ * \return (any other error)
+ *         Any other error is propagated to the function that requested the key.
+ *         Common errors include:
+ *         - #PSA_ERROR_NOT_PERMITTED: the key exists but the requested owner
+ *           is not allowed to access it.
+ */
+psa_status_t mbedtls_psa_platform_get_builtin_key(
+    mbedtls_svc_key_id_t key_id,
+    psa_key_lifetime_t *lifetime,
+    psa_drv_slot_number_t *slot_number );
+#endif /* MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS */
+
+/** @} */
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/third_party/mbedtls/repo/include/psa/crypto_platform.h b/third_party/mbedtls/repo/include/psa/crypto_platform.h
index 567398d..66f4687 100644
--- a/third_party/mbedtls/repo/include/psa/crypto_platform.h
+++ b/third_party/mbedtls/repo/include/psa/crypto_platform.h
@@ -81,4 +81,31 @@
 
 #endif /* MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER */
 
+/*
+ * When MBEDTLS_PSA_CRYPTO_SPM is defined, the code is being built for SPM
+ * (Secure Partition Manager) integration which separates the code into two
+ * parts: NSPE (Non-Secure Processing Environment) and SPE (Secure Processing
+ * Environment). When building for the SPE, an additional header file should be
+ * included.
+ */
+#if defined(MBEDTLS_PSA_CRYPTO_SPM)
+#define PSA_CRYPTO_SECURE 1
+#include "crypto_spe.h"
+#endif // MBEDTLS_PSA_CRYPTO_SPM
+
+#if defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG)
+/** The type of the context passed to mbedtls_psa_external_get_random().
+ *
+ * Mbed TLS initializes the context to all-bits-zero before calling
+ * mbedtls_psa_external_get_random() for the first time.
+ *
+ * The definition of this type in the Mbed TLS source code is for
+ * demonstration purposes. Implementers of mbedtls_psa_external_get_random()
+ * are expected to replace it with a custom definition.
+ */
+typedef struct {
+    uintptr_t opaque[2];
+} mbedtls_psa_external_random_context_t;
+#endif /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */
+
 #endif /* PSA_CRYPTO_PLATFORM_H */
diff --git a/third_party/mbedtls/repo/include/psa/crypto_se_driver.h b/third_party/mbedtls/repo/include/psa/crypto_se_driver.h
index 1fae575..1dc8f9b 100644
--- a/third_party/mbedtls/repo/include/psa/crypto_se_driver.h
+++ b/third_party/mbedtls/repo/include/psa/crypto_se_driver.h
@@ -1061,7 +1061,8 @@
  * \brief A function that generates a symmetric or asymmetric key on a secure
  * element
  *
- * If \p type is asymmetric (#PSA_KEY_TYPE_IS_ASYMMETRIC(\p type) = 1),
+ * If the key type \c type recorded in \p attributes
+ * is asymmetric (#PSA_KEY_TYPE_IS_ASYMMETRIC(\c type) = 1),
  * the driver may export the public key at the time of generation,
  * in the format documented for psa_export_public_key() by writing it
  * to the \p pubkey buffer.
@@ -1158,7 +1159,7 @@
  * can be problemmatic to manage on embedded platforms, the inputs are passed
  * to the driver via a function, `psa_drv_se_key_derivation_collateral`, that
  * is called multiple times with different `collateral_id`s. Thus, for a key
- * derivation algorithm that required 3 paramter inputs, the flow would look
+ * derivation algorithm that required 3 parameter inputs, the flow would look
  * something like:
  * ~~~~~~~~~~~~~{.c}
  * psa_drv_se_key_derivation_setup(kdf_algorithm, source_key, dest_key_size_bytes);
@@ -1206,7 +1207,7 @@
  * element key derivation or key agreement operation
  *
  * Since many key derivation algorithms require multiple parameters, it is
- * expeced that this function may be called multiple times for the same
+ * expected that this function may be called multiple times for the same
  * operation, each with a different algorithm-specific `collateral_id`
  *
  * \param[in,out] op_context    A hardware-specific structure containing any
@@ -1364,20 +1365,22 @@
  *
  * \return #PSA_SUCCESS
  *         The driver was successfully registered. Applications can now
- *         use \p lifetime to access keys through the methods passed to
+ *         use \p location to access keys through the methods passed to
  *         this function.
  * \return #PSA_ERROR_BAD_STATE
  *         This function was called after the initialization of the
  *         cryptography module, and this implementation does not support
  *         driver registration at this stage.
  * \return #PSA_ERROR_ALREADY_EXISTS
- *         There is already a registered driver for this value of \p lifetime.
+ *         There is already a registered driver for this value of \p location.
  * \return #PSA_ERROR_INVALID_ARGUMENT
- *         \p lifetime is a reserved value.
+ *         \p location is a reserved value.
  * \return #PSA_ERROR_NOT_SUPPORTED
  *         `methods->hal_version` is not supported by this implementation.
  * \return #PSA_ERROR_INSUFFICIENT_MEMORY
  * \return #PSA_ERROR_NOT_PERMITTED
+ * \return #PSA_ERROR_STORAGE_FAILURE
+ * \return #PSA_ERROR_DATA_CORRUPT
  */
 psa_status_t psa_register_se_driver(
     psa_key_location_t location,
diff --git a/third_party/mbedtls/repo/include/psa/crypto_sizes.h b/third_party/mbedtls/repo/include/psa/crypto_sizes.h
index 3df01b2..e2ae596 100644
--- a/third_party/mbedtls/repo/include/psa/crypto_sizes.h
+++ b/third_party/mbedtls/repo/include/psa/crypto_sizes.h
@@ -65,11 +65,9 @@
  *
  * \return The hash size for the specified hash algorithm.
  *         If the hash algorithm is not recognized, return 0.
- *         An implementation may return either 0 or the correct size
- *         for a hash algorithm that it recognizes, but does not support.
  */
-#define PSA_HASH_SIZE(alg)                                      \
-    (                                                           \
+#define PSA_HASH_LENGTH(alg)                                        \
+    (                                                               \
         PSA_ALG_HMAC_GET_HASH(alg) == PSA_ALG_MD2 ? 16 :            \
         PSA_ALG_HMAC_GET_HASH(alg) == PSA_ALG_MD4 ? 16 :            \
         PSA_ALG_HMAC_GET_HASH(alg) == PSA_ALG_MD5 ? 16 :            \
@@ -87,18 +85,51 @@
         PSA_ALG_HMAC_GET_HASH(alg) == PSA_ALG_SHA3_512 ? 64 :       \
         0)
 
+/** The input block size of a hash algorithm, in bytes.
+ *
+ * Hash algorithms process their input data in blocks. Hash operations will
+ * retain any partial blocks until they have enough input to fill the block or
+ * until the operation is finished.
+ * This affects the output from psa_hash_suspend().
+ *
+ * \param alg   A hash algorithm (\c PSA_ALG_XXX value such that
+ *              PSA_ALG_IS_HASH(\p alg) is true).
+ *
+ * \return      The block size in bytes for the specified hash algorithm.
+ *              If the hash algorithm is not recognized, return 0.
+ *              An implementation can return either 0 or the correct size for a
+ *              hash algorithm that it recognizes, but does not support.
+ */
+#define PSA_HASH_BLOCK_LENGTH(alg)                                  \
+    (                                                               \
+        PSA_ALG_HMAC_GET_HASH(alg) == PSA_ALG_MD2 ? 16 :            \
+        PSA_ALG_HMAC_GET_HASH(alg) == PSA_ALG_MD4 ? 64 :            \
+        PSA_ALG_HMAC_GET_HASH(alg) == PSA_ALG_MD5 ? 64 :            \
+        PSA_ALG_HMAC_GET_HASH(alg) == PSA_ALG_RIPEMD160 ? 64 :      \
+        PSA_ALG_HMAC_GET_HASH(alg) == PSA_ALG_SHA_1 ? 64 :          \
+        PSA_ALG_HMAC_GET_HASH(alg) == PSA_ALG_SHA_224 ? 64 :        \
+        PSA_ALG_HMAC_GET_HASH(alg) == PSA_ALG_SHA_256 ? 64 :        \
+        PSA_ALG_HMAC_GET_HASH(alg) == PSA_ALG_SHA_384 ? 128 :       \
+        PSA_ALG_HMAC_GET_HASH(alg) == PSA_ALG_SHA_512 ? 128 :       \
+        PSA_ALG_HMAC_GET_HASH(alg) == PSA_ALG_SHA_512_224 ? 128 :   \
+        PSA_ALG_HMAC_GET_HASH(alg) == PSA_ALG_SHA_512_256 ? 128 :   \
+        PSA_ALG_HMAC_GET_HASH(alg) == PSA_ALG_SHA3_224 ? 144 :      \
+        PSA_ALG_HMAC_GET_HASH(alg) == PSA_ALG_SHA3_256 ? 136 :      \
+        PSA_ALG_HMAC_GET_HASH(alg) == PSA_ALG_SHA3_384 ? 104 :      \
+        PSA_ALG_HMAC_GET_HASH(alg) == PSA_ALG_SHA3_512 ? 72 :       \
+        0)
+
 /** \def PSA_HASH_MAX_SIZE
  *
  * Maximum size of a hash.
  *
- * This macro must expand to a compile-time constant integer. This value
- * should be the maximum size of a hash supported by the implementation,
- * in bytes, and must be no smaller than this maximum.
+ * This macro expands to a compile-time constant integer. This value
+ * is the maximum size of a hash in bytes.
  */
 /* Note: for HMAC-SHA-3, the block size is 144 bytes for HMAC-SHA3-226,
  * 136 bytes for HMAC-SHA3-256, 104 bytes for SHA3-384, 72 bytes for
  * HMAC-SHA3-512. */
-#if defined(MBEDTLS_SHA512_C)
+#if defined(PSA_WANT_ALG_SHA_512) || defined(PSA_WANT_ALG_SHA_384)
 #define PSA_HASH_MAX_SIZE 64
 #define PSA_HMAC_MAX_HASH_BLOCK_SIZE 128
 #else
@@ -110,9 +141,8 @@
  *
  * Maximum size of a MAC.
  *
- * This macro must expand to a compile-time constant integer. This value
- * should be the maximum size of a MAC supported by the implementation,
- * in bytes, and must be no smaller than this maximum.
+ * This macro expands to a compile-time constant integer. This value
+ * is the maximum size of a MAC in bytes.
  */
 /* All non-HMAC MACs have a maximum size that's smaller than the
  * minimum possible value of PSA_HASH_MAX_SIZE in this implementation. */
@@ -121,25 +151,37 @@
  */
 #define PSA_MAC_MAX_SIZE PSA_HASH_MAX_SIZE
 
-/** The tag size for an AEAD algorithm, in bytes.
+/** The length of a tag for an AEAD algorithm, in bytes.
  *
+ * This macro can be used to allocate a buffer of sufficient size to store the
+ * tag output from psa_aead_finish().
+ *
+ * See also #PSA_AEAD_TAG_MAX_SIZE.
+ *
+ * \param key_type            The type of the AEAD key.
+ * \param key_bits            The size of the AEAD key in bits.
  * \param alg                 An AEAD algorithm
  *                            (\c PSA_ALG_XXX value such that
  *                            #PSA_ALG_IS_AEAD(\p alg) is true).
  *
- * \return                    The tag size for the specified algorithm.
+ * \return                    The tag length for the specified algorithm and key.
  *                            If the AEAD algorithm does not have an identified
  *                            tag that can be distinguished from the rest of
  *                            the ciphertext, return 0.
- *                            If the AEAD algorithm is not recognized, return 0.
- *                            An implementation may return either 0 or a
- *                            correct size for an AEAD algorithm that it
- *                            recognizes, but does not support.
+ *                            If the key type or AEAD algorithm is not
+ *                            recognized, or the parameters are incompatible,
+ *                            return 0.
  */
-#define PSA_AEAD_TAG_LENGTH(alg)                                        \
-    (PSA_ALG_IS_AEAD(alg) ?                                             \
-     (((alg) & PSA_ALG_AEAD_TAG_LENGTH_MASK) >> PSA_AEAD_TAG_LENGTH_OFFSET) : \
-     0)
+#define PSA_AEAD_TAG_LENGTH(key_type, key_bits, alg)                        \
+    (PSA_AEAD_NONCE_LENGTH(key_type, alg) != 0 ?                            \
+     PSA_ALG_AEAD_GET_TAG_LENGTH(alg) :                                     \
+     ((void) (key_bits), 0))
+
+/** The maximum tag size for all supported AEAD algorithms, in bytes.
+ *
+ * See also #PSA_AEAD_TAG_LENGTH(\p key_type, \p key_bits, \p alg).
+ */
+#define PSA_AEAD_TAG_MAX_SIZE       16
 
 /* The maximum size of an RSA key on this implementation, in bits.
  * This is a vendor-specific macro.
@@ -188,10 +230,11 @@
 #define PSA_VENDOR_ECC_MAX_CURVE_BITS 0
 #endif
 
-/** \def PSA_ALG_TLS12_PSK_TO_MS_MAX_PSK_LEN
+/** This macro returns the maximum supported length of the PSK for the
+ * TLS-1.2 PSK-to-MS key derivation
+ * (#PSA_ALG_TLS12_PSK_TO_MS(\c hash_alg)).
  *
- * This macro returns the maximum length of the PSK supported
- * by the TLS-1.2 PSK-to-MS key derivation.
+ * The maximum supported length does not depend on the chosen hash algorithm.
  *
  * Quoting RFC 4279, Sect 5.3:
  * TLS implementations supporting these ciphersuites MUST support
@@ -200,17 +243,21 @@
  * keys is RECOMMENDED.
  *
  * Therefore, no implementation should define a value smaller than 64
- * for #PSA_ALG_TLS12_PSK_TO_MS_MAX_PSK_LEN.
+ * for #PSA_TLS12_PSK_TO_MS_PSK_MAX_SIZE.
  */
-#define PSA_ALG_TLS12_PSK_TO_MS_MAX_PSK_LEN 128
+#define PSA_TLS12_PSK_TO_MS_PSK_MAX_SIZE 128
 
-/** The maximum size of a block cipher supported by the implementation. */
-#define PSA_MAX_BLOCK_CIPHER_BLOCK_SIZE 16
+/** The maximum size of a block cipher. */
+#define PSA_BLOCK_CIPHER_BLOCK_MAX_SIZE 16
 
 /** The size of the output of psa_mac_sign_finish(), in bytes.
  *
  * This is also the MAC size that psa_mac_verify_finish() expects.
  *
+ * \warning This macro may evaluate its arguments multiple times or
+ *          zero times, so you should not pass arguments that contain
+ *          side effects.
+ *
  * \param key_type      The type of the MAC key.
  * \param key_bits      The size of the MAC key in bits.
  * \param alg           A MAC algorithm (\c PSA_ALG_XXX value such that
@@ -224,10 +271,10 @@
  * \return              Unspecified if the key parameters are not consistent
  *                      with the algorithm.
  */
-#define PSA_MAC_FINAL_SIZE(key_type, key_bits, alg)                     \
-    ((alg) & PSA_ALG_MAC_TRUNCATION_MASK ? PSA_MAC_TRUNCATED_LENGTH(alg) : \
-     PSA_ALG_IS_HMAC(alg) ? PSA_HASH_SIZE(PSA_ALG_HMAC_GET_HASH(alg)) : \
-     PSA_ALG_IS_BLOCK_CIPHER_MAC(alg) ? PSA_BLOCK_CIPHER_BLOCK_SIZE(key_type) : \
+#define PSA_MAC_LENGTH(key_type, key_bits, alg)                                   \
+    ((alg) & PSA_ALG_MAC_TRUNCATION_MASK ? PSA_MAC_TRUNCATED_LENGTH(alg) :        \
+     PSA_ALG_IS_HMAC(alg) ? PSA_HASH_LENGTH(PSA_ALG_HMAC_GET_HASH(alg)) :         \
+     PSA_ALG_IS_BLOCK_CIPHER_MAC(alg) ? PSA_BLOCK_CIPHER_BLOCK_LENGTH(key_type) : \
      ((void)(key_type), (void)(key_bits), 0))
 
 /** The maximum size of the output of psa_aead_encrypt(), in bytes.
@@ -237,6 +284,14 @@
  * insufficient buffer size. Depending on the algorithm, the actual size of
  * the ciphertext may be smaller.
  *
+ * See also #PSA_AEAD_ENCRYPT_OUTPUT_MAX_SIZE(\p plaintext_length).
+ *
+ * \warning This macro may evaluate its arguments multiple times or
+ *          zero times, so you should not pass arguments that contain
+ *          side effects.
+ *
+ * \param key_type            A symmetric key type that is
+ *                            compatible with algorithm \p alg.
  * \param alg                 An AEAD algorithm
  *                            (\c PSA_ALG_XXX value such that
  *                            #PSA_ALG_IS_AEAD(\p alg) is true).
@@ -244,16 +299,37 @@
  *
  * \return                    The AEAD ciphertext size for the specified
  *                            algorithm.
- *                            If the AEAD algorithm is not recognized, return 0.
- *                            An implementation may return either 0 or a
- *                            correct size for an AEAD algorithm that it
- *                            recognizes, but does not support.
+ *                            If the key type or AEAD algorithm is not
+ *                            recognized, or the parameters are incompatible,
+ *                            return 0.
  */
-#define PSA_AEAD_ENCRYPT_OUTPUT_SIZE(alg, plaintext_length)       \
-    (PSA_AEAD_TAG_LENGTH(alg) != 0 ?                              \
-     (plaintext_length) + PSA_AEAD_TAG_LENGTH(alg) :              \
+#define PSA_AEAD_ENCRYPT_OUTPUT_SIZE(key_type, alg, plaintext_length) \
+    (PSA_AEAD_NONCE_LENGTH(key_type, alg) != 0 ?                      \
+     (plaintext_length) + PSA_ALG_AEAD_GET_TAG_LENGTH(alg) :          \
      0)
 
+/** A sufficient output buffer size for psa_aead_encrypt(), for any of the
+ *  supported key types and AEAD algorithms.
+ *
+ * If the size of the ciphertext buffer is at least this large, it is guaranteed
+ * that psa_aead_encrypt() will not fail due to an insufficient buffer size.
+ *
+ * \note This macro returns a compile-time constant if its arguments are
+ *       compile-time constants.
+ *
+ * See also #PSA_AEAD_ENCRYPT_OUTPUT_SIZE(\p key_type, \p alg,
+ * \p plaintext_length).
+ *
+ * \param plaintext_length    Size of the plaintext in bytes.
+ *
+ * \return                    A sufficient output buffer size for any of the
+ *                            supported key types and AEAD algorithms.
+ *
+ */
+#define PSA_AEAD_ENCRYPT_OUTPUT_MAX_SIZE(plaintext_length)          \
+    ((plaintext_length) + PSA_AEAD_TAG_MAX_SIZE)
+
+
 /** The maximum size of the output of psa_aead_decrypt(), in bytes.
  *
  * If the size of the plaintext buffer is at least this large, it is
@@ -261,6 +337,14 @@
  * insufficient buffer size. Depending on the algorithm, the actual size of
  * the plaintext may be smaller.
  *
+ * See also #PSA_AEAD_DECRYPT_OUTPUT_MAX_SIZE(\p ciphertext_length).
+ *
+ * \warning This macro may evaluate its arguments multiple times or
+ *          zero times, so you should not pass arguments that contain
+ *          side effects.
+ *
+ * \param key_type            A symmetric key type that is
+ *                            compatible with algorithm \p alg.
  * \param alg                 An AEAD algorithm
  *                            (\c PSA_ALG_XXX value such that
  *                            #PSA_ALG_IS_AEAD(\p alg) is true).
@@ -268,16 +352,84 @@
  *
  * \return                    The AEAD ciphertext size for the specified
  *                            algorithm.
- *                            If the AEAD algorithm is not recognized, return 0.
- *                            An implementation may return either 0 or a
- *                            correct size for an AEAD algorithm that it
- *                            recognizes, but does not support.
+ *                            If the key type or AEAD algorithm is not
+ *                            recognized, or the parameters are incompatible,
+ *                            return 0.
  */
-#define PSA_AEAD_DECRYPT_OUTPUT_SIZE(alg, ciphertext_length)      \
-    (PSA_AEAD_TAG_LENGTH(alg) != 0 ?                              \
-     (ciphertext_length) - PSA_AEAD_TAG_LENGTH(alg) :             \
+#define PSA_AEAD_DECRYPT_OUTPUT_SIZE(key_type, alg, ciphertext_length) \
+    (PSA_AEAD_NONCE_LENGTH(key_type, alg) != 0 &&                      \
+         (ciphertext_length) > PSA_ALG_AEAD_GET_TAG_LENGTH(alg) ?      \
+         (ciphertext_length) - PSA_ALG_AEAD_GET_TAG_LENGTH(alg) :      \
      0)
 
+/** A sufficient output buffer size for psa_aead_decrypt(), for any of the
+ *  supported key types and AEAD algorithms.
+ *
+ * If the size of the plaintext buffer is at least this large, it is guaranteed
+ * that psa_aead_decrypt() will not fail due to an insufficient buffer size.
+ *
+ * \note This macro returns a compile-time constant if its arguments are
+ *       compile-time constants.
+ *
+ * See also #PSA_AEAD_DECRYPT_OUTPUT_SIZE(\p key_type, \p alg,
+ * \p ciphertext_length).
+ *
+ * \param ciphertext_length   Size of the ciphertext in bytes.
+ *
+ * \return                    A sufficient output buffer size for any of the
+ *                            supported key types and AEAD algorithms.
+ *
+ */
+#define PSA_AEAD_DECRYPT_OUTPUT_MAX_SIZE(ciphertext_length)     \
+     (ciphertext_length)
+
+/** The default nonce size for an AEAD algorithm, in bytes.
+ *
+ * This macro can be used to allocate a buffer of sufficient size to
+ * store the nonce output from #psa_aead_generate_nonce().
+ *
+ * See also #PSA_AEAD_NONCE_MAX_SIZE.
+ *
+ * \note This is not the maximum size of nonce supported as input to
+ *       #psa_aead_set_nonce(), #psa_aead_encrypt() or #psa_aead_decrypt(),
+ *       just the default size that is generated by #psa_aead_generate_nonce().
+ *
+ * \warning This macro may evaluate its arguments multiple times or
+ *          zero times, so you should not pass arguments that contain
+ *          side effects.
+ *
+ * \param key_type  A symmetric key type that is compatible with
+ *                  algorithm \p alg.
+ *
+ * \param alg       An AEAD algorithm (\c PSA_ALG_XXX value such that
+ *                  #PSA_ALG_IS_AEAD(\p alg) is true).
+ *
+ * \return The default nonce size for the specified key type and algorithm.
+ *         If the key type or AEAD algorithm is not recognized,
+ *         or the parameters are incompatible, return 0.
+ */
+#define PSA_AEAD_NONCE_LENGTH(key_type, alg) \
+    (PSA_BLOCK_CIPHER_BLOCK_LENGTH(key_type) == 16 ? \
+          MBEDTLS_PSA_ALG_AEAD_EQUAL(alg, PSA_ALG_CCM) ? 13 : \
+          MBEDTLS_PSA_ALG_AEAD_EQUAL(alg, PSA_ALG_GCM) ? 12 : \
+          0 : \
+     (key_type) == PSA_KEY_TYPE_CHACHA20 && \
+          MBEDTLS_PSA_ALG_AEAD_EQUAL(alg, PSA_ALG_CHACHA20_POLY1305) ? 12 : \
+     0)
+
+/** The maximum default nonce size among all supported pairs of key types and
+ *  AEAD algorithms, in bytes.
+ *
+ * This is equal to or greater than any value that #PSA_AEAD_NONCE_LENGTH()
+ * may return.
+ *
+ * \note This is not the maximum size of nonce supported as input to
+ *       #psa_aead_set_nonce(), #psa_aead_encrypt() or #psa_aead_decrypt(),
+ *       just the largest size that may be generated by
+ *       #psa_aead_generate_nonce().
+ */
+#define PSA_AEAD_NONCE_MAX_SIZE 13
+
 /** A sufficient output buffer size for psa_aead_update().
  *
  * If the size of the output buffer is at least this large, it is
@@ -285,6 +437,14 @@
  * insufficient buffer size. The actual size of the output may be smaller
  * in any given call.
  *
+ * See also #PSA_AEAD_UPDATE_OUTPUT_MAX_SIZE(\p input_length).
+ *
+ * \warning This macro may evaluate its arguments multiple times or
+ *          zero times, so you should not pass arguments that contain
+ *          side effects.
+ *
+ * \param key_type            A symmetric key type that is
+ *                            compatible with algorithm \p alg.
  * \param alg                 An AEAD algorithm
  *                            (\c PSA_ALG_XXX value such that
  *                            #PSA_ALG_IS_AEAD(\p alg) is true).
@@ -292,19 +452,33 @@
  *
  * \return                    A sufficient output buffer size for the specified
  *                            algorithm.
- *                            If the AEAD algorithm is not recognized, return 0.
- *                            An implementation may return either 0 or a
- *                            correct size for an AEAD algorithm that it
- *                            recognizes, but does not support.
+ *                            If the key type or AEAD algorithm is not
+ *                            recognized, or the parameters are incompatible,
+ *                            return 0.
  */
 /* For all the AEAD modes defined in this specification, it is possible
  * to emit output without delay. However, hardware may not always be
  * capable of this. So for modes based on a block cipher, allow the
  * implementation to delay the output until it has a full block. */
-#define PSA_AEAD_UPDATE_OUTPUT_SIZE(alg, input_length)                  \
-    (PSA_ALG_IS_AEAD_ON_BLOCK_CIPHER(alg) ?                             \
-     PSA_ROUND_UP_TO_MULTIPLE(PSA_MAX_BLOCK_CIPHER_BLOCK_SIZE, (input_length)) : \
-     (input_length))
+#define PSA_AEAD_UPDATE_OUTPUT_SIZE(key_type, alg, input_length)                             \
+    (PSA_AEAD_NONCE_LENGTH(key_type, alg) != 0 ?                                             \
+         PSA_ALG_IS_AEAD_ON_BLOCK_CIPHER(alg) ?                                              \
+         PSA_ROUND_UP_TO_MULTIPLE(PSA_BLOCK_CIPHER_BLOCK_LENGTH(key_type), (input_length)) : \
+         (input_length) : \
+     0)
+
+/** A sufficient output buffer size for psa_aead_update(), for any of the
+ *  supported key types and AEAD algorithms.
+ *
+ * If the size of the output buffer is at least this large, it is guaranteed
+ * that psa_aead_update() will not fail due to an insufficient buffer size.
+ *
+ * See also #PSA_AEAD_UPDATE_OUTPUT_SIZE(\p key_type, \p alg, \p input_length).
+ *
+ * \param input_length      Size of the input in bytes.
+ */
+#define PSA_AEAD_UPDATE_OUTPUT_MAX_SIZE(input_length)                           \
+    (PSA_ROUND_UP_TO_MULTIPLE(PSA_BLOCK_CIPHER_BLOCK_MAX_SIZE, (input_length)))
 
 /** A sufficient ciphertext buffer size for psa_aead_finish().
  *
@@ -313,22 +487,33 @@
  * insufficient ciphertext buffer size. The actual size of the output may
  * be smaller in any given call.
  *
+ * See also #PSA_AEAD_FINISH_OUTPUT_MAX_SIZE.
+ *
+ * \param key_type            A symmetric key type that is
+                              compatible with algorithm \p alg.
  * \param alg                 An AEAD algorithm
  *                            (\c PSA_ALG_XXX value such that
  *                            #PSA_ALG_IS_AEAD(\p alg) is true).
  *
  * \return                    A sufficient ciphertext buffer size for the
  *                            specified algorithm.
- *                            If the AEAD algorithm is not recognized, return 0.
- *                            An implementation may return either 0 or a
- *                            correct size for an AEAD algorithm that it
- *                            recognizes, but does not support.
+ *                            If the key type or AEAD algorithm is not
+ *                            recognized, or the parameters are incompatible,
+ *                            return 0.
  */
-#define PSA_AEAD_FINISH_OUTPUT_SIZE(alg)                                \
-    (PSA_ALG_IS_AEAD_ON_BLOCK_CIPHER(alg) ?                             \
-     PSA_MAX_BLOCK_CIPHER_BLOCK_SIZE :                                  \
+#define PSA_AEAD_FINISH_OUTPUT_SIZE(key_type, alg) \
+    (PSA_AEAD_NONCE_LENGTH(key_type, alg) != 0 &&  \
+         PSA_ALG_IS_AEAD_ON_BLOCK_CIPHER(alg) ?    \
+         PSA_BLOCK_CIPHER_BLOCK_LENGTH(key_type) : \
      0)
 
+/** A sufficient ciphertext buffer size for psa_aead_finish(), for any of the
+ *  supported key types and AEAD algorithms.
+ *
+ * See also #PSA_AEAD_FINISH_OUTPUT_SIZE(\p key_type, \p alg).
+ */
+#define PSA_AEAD_FINISH_OUTPUT_MAX_SIZE     (PSA_BLOCK_CIPHER_BLOCK_MAX_SIZE)
+
 /** A sufficient plaintext buffer size for psa_aead_verify().
  *
  * If the size of the plaintext buffer is at least this large, it is
@@ -336,25 +521,36 @@
  * insufficient plaintext buffer size. The actual size of the output may
  * be smaller in any given call.
  *
+ * See also #PSA_AEAD_VERIFY_OUTPUT_MAX_SIZE.
+ *
+ * \param key_type            A symmetric key type that is
+ *                            compatible with algorithm \p alg.
  * \param alg                 An AEAD algorithm
  *                            (\c PSA_ALG_XXX value such that
  *                            #PSA_ALG_IS_AEAD(\p alg) is true).
  *
  * \return                    A sufficient plaintext buffer size for the
  *                            specified algorithm.
- *                            If the AEAD algorithm is not recognized, return 0.
- *                            An implementation may return either 0 or a
- *                            correct size for an AEAD algorithm that it
- *                            recognizes, but does not support.
+ *                            If the key type or AEAD algorithm is not
+ *                            recognized, or the parameters are incompatible,
+ *                            return 0.
  */
-#define PSA_AEAD_VERIFY_OUTPUT_SIZE(alg)                                \
-    (PSA_ALG_IS_AEAD_ON_BLOCK_CIPHER(alg) ?                             \
-     PSA_MAX_BLOCK_CIPHER_BLOCK_SIZE :                                  \
+#define PSA_AEAD_VERIFY_OUTPUT_SIZE(key_type, alg) \
+    (PSA_AEAD_NONCE_LENGTH(key_type, alg) != 0 &&  \
+         PSA_ALG_IS_AEAD_ON_BLOCK_CIPHER(alg) ?    \
+         PSA_BLOCK_CIPHER_BLOCK_LENGTH(key_type) : \
      0)
 
+/** A sufficient plaintext buffer size for psa_aead_verify(), for any of the
+ *  supported key types and AEAD algorithms.
+ *
+ * See also #PSA_AEAD_VERIFY_OUTPUT_SIZE(\p key_type, \p alg).
+ */
+#define PSA_AEAD_VERIFY_OUTPUT_MAX_SIZE     (PSA_BLOCK_CIPHER_BLOCK_MAX_SIZE)
+
 #define PSA_RSA_MINIMUM_PADDING_SIZE(alg)                         \
     (PSA_ALG_IS_RSA_OAEP(alg) ?                                   \
-     2 * PSA_HASH_SIZE(PSA_ALG_RSA_OAEP_GET_HASH(alg)) + 1 :      \
+     2 * PSA_HASH_LENGTH(PSA_ALG_RSA_OAEP_GET_HASH(alg)) + 1 :    \
      11 /*PKCS#1v1.5*/)
 
 /**
@@ -388,9 +584,8 @@
  *         a buffer size in bytes that guarantees that
  *         psa_sign_hash() will not fail with
  *         #PSA_ERROR_BUFFER_TOO_SMALL.
- *         If the parameters are a valid combination that is not supported
- *         by the implementation, this macro shall return either a
- *         sensible size or 0.
+ *         If the parameters are a valid combination that is not supported,
+ *         return either a sensible size or 0.
  *         If the parameters are not valid, the
  *         return value is unspecified.
  */
@@ -406,9 +601,8 @@
  *
  * Maximum size of an asymmetric signature.
  *
- * This macro must expand to a compile-time constant integer. This value
- * should be the maximum size of a signature supported by the implementation,
- * in bytes, and must be no smaller than this maximum.
+ * This macro expands to a compile-time constant integer. This value
+ * is the maximum size of a signature in bytes.
  */
 #define PSA_SIGNATURE_MAX_SIZE                               \
     (PSA_BITS_TO_BYTES(PSA_VENDOR_RSA_MAX_KEY_BITS) > PSA_VENDOR_ECDSA_SIGNATURE_MAX_SIZE ? \
@@ -435,9 +629,8 @@
  *         a buffer size in bytes that guarantees that
  *         psa_asymmetric_encrypt() will not fail with
  *         #PSA_ERROR_BUFFER_TOO_SMALL.
- *         If the parameters are a valid combination that is not supported
- *         by the implementation, this macro shall return either a
- *         sensible size or 0.
+ *         If the parameters are a valid combination that is not supported,
+ *         return either a sensible size or 0.
  *         If the parameters are not valid, the
  *         return value is unspecified.
  */
@@ -446,6 +639,15 @@
      ((void)alg, PSA_BITS_TO_BYTES(key_bits)) :                         \
      0)
 
+/** A sufficient output buffer size for psa_asymmetric_encrypt(), for any
+ *  supported asymmetric encryption.
+ *
+ * See also #PSA_ASYMMETRIC_ENCRYPT_OUTPUT_SIZE(\p key_type, \p key_bits, \p alg).
+ */
+/* This macro assumes that RSA is the only supported asymmetric encryption. */
+#define PSA_ASYMMETRIC_ENCRYPT_OUTPUT_MAX_SIZE          \
+    (PSA_BITS_TO_BYTES(PSA_VENDOR_RSA_MAX_KEY_BITS))
+
 /** Sufficient output buffer size for psa_asymmetric_decrypt().
  *
  * This macro returns a sufficient buffer size for a plaintext produced using
@@ -466,9 +668,8 @@
  *         a buffer size in bytes that guarantees that
  *         psa_asymmetric_decrypt() will not fail with
  *         #PSA_ERROR_BUFFER_TOO_SMALL.
- *         If the parameters are a valid combination that is not supported
- *         by the implementation, this macro shall return either a
- *         sensible size or 0.
+ *         If the parameters are a valid combination that is not supported,
+ *         return either a sensible size or 0.
  *         If the parameters are not valid, the
  *         return value is unspecified.
  */
@@ -477,6 +678,16 @@
      PSA_BITS_TO_BYTES(key_bits) - PSA_RSA_MINIMUM_PADDING_SIZE(alg) :  \
      0)
 
+/** A sufficient output buffer size for psa_asymmetric_decrypt(), for any
+ *  supported asymmetric decryption.
+ *
+ * This macro assumes that RSA is the only supported asymmetric encryption.
+ *
+ * See also #PSA_ASYMMETRIC_DECRYPT_OUTPUT_SIZE(\p key_type, \p key_bits, \p alg).
+ */
+#define PSA_ASYMMETRIC_DECRYPT_OUTPUT_MAX_SIZE          \
+    (PSA_BITS_TO_BYTES(PSA_VENDOR_RSA_MAX_KEY_BITS))
+
 /* Maximum size of the ASN.1 encoding of an INTEGER with the specified
  * number of bits.
  *
@@ -587,12 +798,13 @@
 #define PSA_KEY_EXPORT_ECC_KEY_PAIR_MAX_SIZE(key_bits)   \
     (PSA_BITS_TO_BYTES(key_bits))
 
-/** Sufficient output buffer size for psa_export_key() or psa_export_public_key().
+/** Sufficient output buffer size for psa_export_key() or
+ * psa_export_public_key().
  *
  * This macro returns a compile-time constant if its arguments are
  * compile-time constants.
  *
- * \warning This function may call its arguments multiple times or
+ * \warning This macro may evaluate its arguments multiple times or
  *          zero times, so you should not pass arguments that contain
  *          side effects.
  *
@@ -605,7 +817,7 @@
  * if (status != PSA_SUCCESS) handle_error(...);
  * psa_key_type_t key_type = psa_get_key_type(&attributes);
  * size_t key_bits = psa_get_key_bits(&attributes);
- * size_t buffer_size = PSA_KEY_EXPORT_MAX_SIZE(key_type, key_bits);
+ * size_t buffer_size = PSA_EXPORT_KEY_OUTPUT_SIZE(key_type, key_bits);
  * psa_reset_key_attributes(&attributes);
  * uint8_t *buffer = malloc(buffer_size);
  * if (buffer == NULL) handle_error(...);
@@ -614,18 +826,46 @@
  * if (status != PSA_SUCCESS) handle_error(...);
  * \endcode
  *
- * For psa_export_public_key(), calculate the buffer size from the
- * public key type. You can use the macro #PSA_KEY_TYPE_PUBLIC_KEY_OF_KEY_PAIR
- * to convert a key pair type to the corresponding public key type.
+ * \param key_type  A supported key type.
+ * \param key_bits  The size of the key in bits.
+ *
+ * \return If the parameters are valid and supported, return
+ *         a buffer size in bytes that guarantees that
+ *         psa_export_key() or psa_export_public_key() will not fail with
+ *         #PSA_ERROR_BUFFER_TOO_SMALL.
+ *         If the parameters are a valid combination that is not supported,
+ *         return either a sensible size or 0.
+ *         If the parameters are not valid, the return value is unspecified.
+ */
+#define PSA_EXPORT_KEY_OUTPUT_SIZE(key_type, key_bits)                                              \
+    (PSA_KEY_TYPE_IS_UNSTRUCTURED(key_type) ? PSA_BITS_TO_BYTES(key_bits) :                         \
+     (key_type) == PSA_KEY_TYPE_RSA_KEY_PAIR ? PSA_KEY_EXPORT_RSA_KEY_PAIR_MAX_SIZE(key_bits) :     \
+     (key_type) == PSA_KEY_TYPE_RSA_PUBLIC_KEY ? PSA_KEY_EXPORT_RSA_PUBLIC_KEY_MAX_SIZE(key_bits) : \
+     (key_type) == PSA_KEY_TYPE_DSA_KEY_PAIR ? PSA_KEY_EXPORT_DSA_KEY_PAIR_MAX_SIZE(key_bits) :     \
+     (key_type) == PSA_KEY_TYPE_DSA_PUBLIC_KEY ? PSA_KEY_EXPORT_DSA_PUBLIC_KEY_MAX_SIZE(key_bits) : \
+     PSA_KEY_TYPE_IS_ECC_KEY_PAIR(key_type) ? PSA_KEY_EXPORT_ECC_KEY_PAIR_MAX_SIZE(key_bits) :      \
+     PSA_KEY_TYPE_IS_ECC_PUBLIC_KEY(key_type) ? PSA_KEY_EXPORT_ECC_PUBLIC_KEY_MAX_SIZE(key_bits) :  \
+     0)
+
+/** Sufficient output buffer size for psa_export_public_key().
+ *
+ * This macro returns a compile-time constant if its arguments are
+ * compile-time constants.
+ *
+ * \warning This macro may evaluate its arguments multiple times or
+ *          zero times, so you should not pass arguments that contain
+ *          side effects.
+ *
+ * The following code illustrates how to allocate enough memory to export
+ * a public key by querying the key type and size at runtime.
  * \code{c}
  * psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
  * psa_status_t status;
  * status = psa_get_key_attributes(key, &attributes);
  * if (status != PSA_SUCCESS) handle_error(...);
  * psa_key_type_t key_type = psa_get_key_type(&attributes);
- * psa_key_type_t public_key_type = PSA_KEY_TYPE_PUBLIC_KEY_OF_KEY_PAIR(key_type);
  * size_t key_bits = psa_get_key_bits(&attributes);
- * size_t buffer_size = PSA_KEY_EXPORT_MAX_SIZE(public_key_type, key_bits);
+ * size_t buffer_size = PSA_EXPORT_PUBLIC_KEY_OUTPUT_SIZE(key_type, key_bits);
  * psa_reset_key_attributes(&attributes);
  * uint8_t *buffer = malloc(buffer_size);
  * if (buffer == NULL) handle_error(...);
@@ -634,71 +874,96 @@
  * if (status != PSA_SUCCESS) handle_error(...);
  * \endcode
  *
- * \param key_type  A supported key type.
- * \param key_bits  The size of the key in bits.
+ * \param key_type      A public key or key pair key type.
+ * \param key_bits      The size of the key in bits.
  *
- * \return If the parameters are valid and supported, return
- *         a buffer size in bytes that guarantees that
- *         psa_sign_hash() will not fail with
- *         #PSA_ERROR_BUFFER_TOO_SMALL.
- *         If the parameters are a valid combination that is not supported
- *         by the implementation, this macro shall return either a
- *         sensible size or 0.
- *         If the parameters are not valid, the
- *         return value is unspecified.
+ * \return              If the parameters are valid and supported, return
+ *                      a buffer size in bytes that guarantees that
+ *                      psa_export_public_key() will not fail with
+ *                      #PSA_ERROR_BUFFER_TOO_SMALL.
+ *                      If the parameters are a valid combination that is not
+ *                      supported, return either a sensible size or 0.
+ *                      If the parameters are not valid,
+ *                      the return value is unspecified.
+ *
+ *                      If the parameters are valid and supported,
+ *                      return the same result as
+ *                      #PSA_EXPORT_KEY_OUTPUT_SIZE(
+ *                          \p #PSA_KEY_TYPE_PUBLIC_KEY_OF_KEY_PAIR(\p key_type),
+ *                          \p key_bits).
  */
-#define PSA_KEY_EXPORT_MAX_SIZE(key_type, key_bits)                     \
-    (PSA_KEY_TYPE_IS_UNSTRUCTURED(key_type) ? PSA_BITS_TO_BYTES(key_bits) : \
-     (key_type) == PSA_KEY_TYPE_RSA_KEY_PAIR ? PSA_KEY_EXPORT_RSA_KEY_PAIR_MAX_SIZE(key_bits) : \
-     (key_type) == PSA_KEY_TYPE_RSA_PUBLIC_KEY ? PSA_KEY_EXPORT_RSA_PUBLIC_KEY_MAX_SIZE(key_bits) : \
-     (key_type) == PSA_KEY_TYPE_DSA_KEY_PAIR ? PSA_KEY_EXPORT_DSA_KEY_PAIR_MAX_SIZE(key_bits) : \
-     (key_type) == PSA_KEY_TYPE_DSA_PUBLIC_KEY ? PSA_KEY_EXPORT_DSA_PUBLIC_KEY_MAX_SIZE(key_bits) : \
-     PSA_KEY_TYPE_IS_ECC_KEY_PAIR(key_type) ? PSA_KEY_EXPORT_ECC_KEY_PAIR_MAX_SIZE(key_bits) : \
-     PSA_KEY_TYPE_IS_ECC_PUBLIC_KEY(key_type) ? PSA_KEY_EXPORT_ECC_PUBLIC_KEY_MAX_SIZE(key_bits) : \
+#define PSA_EXPORT_PUBLIC_KEY_OUTPUT_SIZE(key_type, key_bits)                           \
+    (PSA_KEY_TYPE_IS_RSA(key_type) ? PSA_KEY_EXPORT_RSA_PUBLIC_KEY_MAX_SIZE(key_bits) : \
+     PSA_KEY_TYPE_IS_ECC(key_type) ? PSA_KEY_EXPORT_ECC_PUBLIC_KEY_MAX_SIZE(key_bits) : \
      0)
 
-/** The default nonce size for an AEAD algorithm, in bytes.
+/** Sufficient buffer size for exporting any asymmetric key pair.
  *
- * This macro can be used to allocate a buffer of sufficient size to
- * store the nonce output from #psa_aead_generate_nonce().
+ * This macro expands to a compile-time constant integer. This value is
+ * a sufficient buffer size when calling psa_export_key() to export any
+ * asymmetric key pair, regardless of the exact key type and key size.
  *
- * See also #PSA_AEAD_NONCE_MAX_SIZE.
+ * See also #PSA_EXPORT_KEY_OUTPUT_SIZE(\p key_type, \p key_bits).
+ */
+#define PSA_EXPORT_KEY_PAIR_MAX_SIZE                                            \
+    (PSA_KEY_EXPORT_RSA_KEY_PAIR_MAX_SIZE(PSA_VENDOR_RSA_MAX_KEY_BITS) >        \
+     PSA_KEY_EXPORT_ECC_KEY_PAIR_MAX_SIZE(PSA_VENDOR_ECC_MAX_CURVE_BITS) ?      \
+     PSA_KEY_EXPORT_RSA_KEY_PAIR_MAX_SIZE(PSA_VENDOR_RSA_MAX_KEY_BITS) :        \
+     PSA_KEY_EXPORT_ECC_KEY_PAIR_MAX_SIZE(PSA_VENDOR_ECC_MAX_CURVE_BITS))
+
+/** Sufficient buffer size for exporting any asymmetric public key.
  *
- * \note This is not the maximum size of nonce supported as input to #psa_aead_set_nonce(),
- *       #psa_aead_encrypt() or #psa_aead_decrypt(), just the default size that is generated by
- *       #psa_aead_generate_nonce().
+ * This macro expands to a compile-time constant integer. This value is
+ * a sufficient buffer size when calling psa_export_key() or
+ * psa_export_public_key() to export any asymmetric public key,
+ * regardless of the exact key type and key size.
+ *
+ * See also #PSA_EXPORT_PUBLIC_KEY_OUTPUT_SIZE(\p key_type, \p key_bits).
+ */
+#define PSA_EXPORT_PUBLIC_KEY_MAX_SIZE                                          \
+    (PSA_KEY_EXPORT_RSA_PUBLIC_KEY_MAX_SIZE(PSA_VENDOR_RSA_MAX_KEY_BITS) >      \
+     PSA_KEY_EXPORT_ECC_PUBLIC_KEY_MAX_SIZE(PSA_VENDOR_ECC_MAX_CURVE_BITS) ?    \
+     PSA_KEY_EXPORT_RSA_PUBLIC_KEY_MAX_SIZE(PSA_VENDOR_RSA_MAX_KEY_BITS) :      \
+     PSA_KEY_EXPORT_ECC_PUBLIC_KEY_MAX_SIZE(PSA_VENDOR_ECC_MAX_CURVE_BITS))
+
+/** Sufficient output buffer size for psa_raw_key_agreement().
+ *
+ * This macro returns a compile-time constant if its arguments are
+ * compile-time constants.
  *
  * \warning This macro may evaluate its arguments multiple times or
  *          zero times, so you should not pass arguments that contain
  *          side effects.
  *
- * \param key_type  A symmetric key type that is compatible with algorithm \p alg.
+ * See also #PSA_RAW_KEY_AGREEMENT_OUTPUT_MAX_SIZE.
  *
- * \param alg       An AEAD algorithm (\c PSA_ALG_XXX value such that #PSA_ALG_IS_AEAD(\p alg) is true).
+ * \param key_type      A supported key type.
+ * \param key_bits      The size of the key in bits.
  *
- * \return The default nonce size for the specified key type and algorithm.
- *         If the key type or AEAD algorithm is not recognized,
- *         or the parameters are incompatible, return 0.
- *         An implementation can return either 0 or a correct size for a key type
- *         and AEAD algorithm that it recognizes, but does not support.
+ * \return              If the parameters are valid and supported, return
+ *                      a buffer size in bytes that guarantees that
+ *                      psa_raw_key_agreement() will not fail with
+ *                      #PSA_ERROR_BUFFER_TOO_SMALL.
+ *                      If the parameters are a valid combination that
+ *                      is not supported, return either a sensible size or 0.
+ *                      If the parameters are not valid,
+ *                      the return value is unspecified.
  */
-#define PSA_AEAD_NONCE_LENGTH(key_type, alg) \
-    (PSA_BLOCK_CIPHER_BLOCK_SIZE(key_type) == 16 && \
-         (PSA_ALG_AEAD_WITH_DEFAULT_TAG_LENGTH(alg) == PSA_ALG_CCM || \
-          PSA_ALG_AEAD_WITH_DEFAULT_TAG_LENGTH(alg) == PSA_ALG_GCM) ? 12 : \
-     (key_type) == PSA_KEY_TYPE_CHACHA20 && \
-          PSA_ALG_AEAD_WITH_DEFAULT_TAG_LENGTH(alg) == PSA_ALG_CHACHA20_POLY1305 ? 12 : \
+/* FFDH is not yet supported in PSA. */
+#define PSA_RAW_KEY_AGREEMENT_OUTPUT_SIZE(key_type, key_bits)   \
+    (PSA_KEY_TYPE_IS_ECC_KEY_PAIR(key_type) ?                   \
+     PSA_BITS_TO_BYTES(key_bits) :                              \
      0)
 
-/** The maximum default nonce size among all supported pairs of key types and AEAD algorithms, in bytes.
+/** Maximum size of the output from psa_raw_key_agreement().
  *
- * This is equal to or greater than any value that #PSA_AEAD_NONCE_LENGTH() may return.
+ * This macro expands to a compile-time constant integer. This value is the
+ * maximum size of the output any raw key agreement algorithm, in bytes.
  *
- * \note This is not the maximum size of nonce supported as input to #psa_aead_set_nonce(),
- *       #psa_aead_encrypt() or #psa_aead_decrypt(), just the largest size that may be generated by
- *       #psa_aead_generate_nonce().
+ * See also #PSA_RAW_KEY_AGREEMENT_OUTPUT_SIZE(\p key_type, \p key_bits).
  */
-#define PSA_AEAD_NONCE_MAX_SIZE 12
+#define PSA_RAW_KEY_AGREEMENT_OUTPUT_MAX_SIZE   \
+    (PSA_BITS_TO_BYTES(PSA_VENDOR_ECC_MAX_CURVE_BITS))
 
 /** The default IV size for a cipher algorithm, in bytes.
  *
@@ -723,17 +988,15 @@
  *         If the algorithm does not use an IV, return 0.
  *         If the key type or cipher algorithm is not recognized,
  *         or the parameters are incompatible, return 0.
- *         An implementation can return either 0 or a correct size for a key type
- *         and cipher algorithm that it recognizes, but does not support.
  */
 #define PSA_CIPHER_IV_LENGTH(key_type, alg) \
-    (PSA_BLOCK_CIPHER_BLOCK_SIZE(key_type) > 1 && \
+    (PSA_BLOCK_CIPHER_BLOCK_LENGTH(key_type) > 1 && \
         ((alg) == PSA_ALG_CTR || \
          (alg) == PSA_ALG_CFB || \
          (alg) == PSA_ALG_OFB || \
          (alg) == PSA_ALG_XTS || \
          (alg) == PSA_ALG_CBC_NO_PADDING || \
-         (alg) == PSA_ALG_CBC_PKCS7) ? PSA_BLOCK_CIPHER_BLOCK_SIZE(key_type) : \
+         (alg) == PSA_ALG_CBC_PKCS7) ? PSA_BLOCK_CIPHER_BLOCK_LENGTH(key_type) : \
      (key_type) == PSA_KEY_TYPE_CHACHA20 && \
          (alg) == PSA_ALG_STREAM_CIPHER ? 12 : \
      0)
@@ -744,4 +1007,165 @@
  */
 #define PSA_CIPHER_IV_MAX_SIZE 16
 
+/** The maximum size of the output of psa_cipher_encrypt(), in bytes.
+ *
+ * If the size of the output buffer is at least this large, it is guaranteed
+ * that psa_cipher_encrypt() will not fail due to an insufficient buffer size.
+ * Depending on the algorithm, the actual size of the output might be smaller.
+ *
+ * See also #PSA_CIPHER_ENCRYPT_OUTPUT_MAX_SIZE(\p input_length).
+ *
+ * \warning This macro may evaluate its arguments multiple times or
+ *          zero times, so you should not pass arguments that contain
+ *          side effects.
+ *
+ * \param key_type      A symmetric key type that is compatible with algorithm
+ *                      alg.
+ * \param alg           A cipher algorithm (\c PSA_ALG_XXX value such that
+ *                      #PSA_ALG_IS_CIPHER(\p alg) is true).
+ * \param input_length  Size of the input in bytes.
+ *
+ * \return              A sufficient output size for the specified key type and
+ *                      algorithm. If the key type or cipher algorithm is not
+ *                      recognized, or the parameters are incompatible,
+ *                      return 0.
+ */
+#define PSA_CIPHER_ENCRYPT_OUTPUT_SIZE(key_type, alg, input_length)             \
+    (alg == PSA_ALG_CBC_PKCS7 ?                                                 \
+     (PSA_BLOCK_CIPHER_BLOCK_LENGTH(key_type) != 0 ?                            \
+     PSA_ROUND_UP_TO_MULTIPLE(PSA_BLOCK_CIPHER_BLOCK_LENGTH(key_type),          \
+                              (input_length) + 1) +                             \
+     PSA_CIPHER_IV_LENGTH((key_type), (alg)) : 0) :                             \
+     (PSA_ALG_IS_CIPHER(alg) ?                                                  \
+      (input_length) + PSA_CIPHER_IV_LENGTH((key_type), (alg)) :                \
+     0))
+
+/** A sufficient output buffer size for psa_cipher_encrypt(), for any of the
+ *  supported key types and cipher algorithms.
+ *
+ * If the size of the output buffer is at least this large, it is guaranteed
+ * that psa_cipher_encrypt() will not fail due to an insufficient buffer size.
+ *
+ * See also #PSA_CIPHER_ENCRYPT_OUTPUT_SIZE(\p key_type, \p alg, \p input_length).
+ *
+ * \param input_length  Size of the input in bytes.
+ *
+ */
+#define PSA_CIPHER_ENCRYPT_OUTPUT_MAX_SIZE(input_length)                        \
+    (PSA_ROUND_UP_TO_MULTIPLE(PSA_BLOCK_CIPHER_BLOCK_MAX_SIZE,                  \
+                              (input_length) + 1) +                             \
+     PSA_CIPHER_IV_MAX_SIZE)
+
+/** The maximum size of the output of psa_cipher_decrypt(), in bytes.
+ *
+ * If the size of the output buffer is at least this large, it is guaranteed
+ * that psa_cipher_decrypt() will not fail due to an insufficient buffer size.
+ * Depending on the algorithm, the actual size of the output might be smaller.
+ *
+ * See also #PSA_CIPHER_DECRYPT_OUTPUT_MAX_SIZE(\p input_length).
+ *
+ * \param key_type      A symmetric key type that is compatible with algorithm
+ *                      alg.
+ * \param alg           A cipher algorithm (\c PSA_ALG_XXX value such that
+ *                      #PSA_ALG_IS_CIPHER(\p alg) is true).
+ * \param input_length  Size of the input in bytes.
+ *
+ * \return              A sufficient output size for the specified key type and
+ *                      algorithm. If the key type or cipher algorithm is not
+ *                      recognized, or the parameters are incompatible,
+ *                      return 0.
+ */
+#define PSA_CIPHER_DECRYPT_OUTPUT_SIZE(key_type, alg, input_length)                 \
+    (PSA_ALG_IS_CIPHER(alg) &&                                                      \
+     ((key_type) & PSA_KEY_TYPE_CATEGORY_MASK) == PSA_KEY_TYPE_CATEGORY_SYMMETRIC ? \
+     (input_length) :                                                               \
+     0)
+
+/** A sufficient output buffer size for psa_cipher_decrypt(), for any of the
+ *  supported key types and cipher algorithms.
+ *
+ * If the size of the output buffer is at least this large, it is guaranteed
+ * that psa_cipher_decrypt() will not fail due to an insufficient buffer size.
+ *
+ * See also #PSA_CIPHER_DECRYPT_OUTPUT_SIZE(\p key_type, \p alg, \p input_length).
+ *
+ * \param input_length  Size of the input in bytes.
+ */
+#define PSA_CIPHER_DECRYPT_OUTPUT_MAX_SIZE(input_length)    \
+    (input_length)
+
+/** A sufficient output buffer size for psa_cipher_update().
+ *
+ * If the size of the output buffer is at least this large, it is guaranteed
+ * that psa_cipher_update() will not fail due to an insufficient buffer size.
+ * The actual size of the output might be smaller in any given call.
+ *
+ * See also #PSA_CIPHER_UPDATE_OUTPUT_MAX_SIZE(\p input_length).
+ *
+ * \param key_type      A symmetric key type that is compatible with algorithm
+ *                      alg.
+ * \param alg           A cipher algorithm (PSA_ALG_XXX value such that
+ *                      #PSA_ALG_IS_CIPHER(\p alg) is true).
+ * \param input_length  Size of the input in bytes.
+ *
+ * \return              A sufficient output size for the specified key type and
+ *                      algorithm. If the key type or cipher algorithm is not
+ *                      recognized, or the parameters are incompatible, return 0.
+ */
+#define PSA_CIPHER_UPDATE_OUTPUT_SIZE(key_type, alg, input_length)              \
+    (PSA_ALG_IS_CIPHER(alg) ?                                                   \
+    (PSA_BLOCK_CIPHER_BLOCK_LENGTH(key_type) != 0 ?                             \
+     (((alg) == PSA_ALG_CBC_PKCS7      ||                                       \
+       (alg) == PSA_ALG_CBC_NO_PADDING ||                                       \
+       (alg) == PSA_ALG_ECB_NO_PADDING) ?                                       \
+      PSA_ROUND_UP_TO_MULTIPLE(PSA_BLOCK_CIPHER_BLOCK_LENGTH(key_type),         \
+                                input_length) :                                 \
+      (input_length)) : 0) :                                                    \
+     0)
+
+/** A sufficient output buffer size for psa_cipher_update(), for any of the
+ *  supported key types and cipher algorithms.
+ *
+ * If the size of the output buffer is at least this large, it is guaranteed
+ * that psa_cipher_update() will not fail due to an insufficient buffer size.
+ *
+ * See also #PSA_CIPHER_UPDATE_OUTPUT_SIZE(\p key_type, \p alg, \p input_length).
+ *
+ * \param input_length  Size of the input in bytes.
+ */
+#define PSA_CIPHER_UPDATE_OUTPUT_MAX_SIZE(input_length)     \
+    (PSA_ROUND_UP_TO_MULTIPLE(PSA_BLOCK_CIPHER_BLOCK_MAX_SIZE, input_length))
+
+/** A sufficient ciphertext buffer size for psa_cipher_finish().
+ *
+ * If the size of the ciphertext buffer is at least this large, it is
+ * guaranteed that psa_cipher_finish() will not fail due to an insufficient
+ * ciphertext buffer size. The actual size of the output might be smaller in
+ * any given call.
+ *
+ * See also #PSA_CIPHER_FINISH_OUTPUT_MAX_SIZE().
+ *
+ * \param key_type      A symmetric key type that is compatible with algorithm
+ *                      alg.
+ * \param alg           A cipher algorithm (PSA_ALG_XXX value such that
+ *                      #PSA_ALG_IS_CIPHER(\p alg) is true).
+ * \return              A sufficient output size for the specified key type and
+ *                      algorithm. If the key type or cipher algorithm is not
+ *                      recognized, or the parameters are incompatible, return 0.
+ */
+#define PSA_CIPHER_FINISH_OUTPUT_SIZE(key_type, alg)    \
+    (PSA_ALG_IS_CIPHER(alg) ?                           \
+     (alg == PSA_ALG_CBC_PKCS7 ?                        \
+      PSA_BLOCK_CIPHER_BLOCK_LENGTH(key_type) :         \
+      0) :                                              \
+     0)
+
+/** A sufficient ciphertext buffer size for psa_cipher_finish(), for any of the
+ *  supported key types and cipher algorithms.
+ *
+ * See also #PSA_CIPHER_FINISH_OUTPUT_SIZE(\p key_type, \p alg).
+ */
+#define PSA_CIPHER_FINISH_OUTPUT_MAX_SIZE           \
+    (PSA_BLOCK_CIPHER_BLOCK_MAX_SIZE)
+
 #endif /* PSA_CRYPTO_SIZES_H */
diff --git a/third_party/mbedtls/repo/include/psa/crypto_struct.h b/third_party/mbedtls/repo/include/psa/crypto_struct.h
index 6a018e1..23a02a5 100644
--- a/third_party/mbedtls/repo/include/psa/crypto_struct.h
+++ b/third_party/mbedtls/repo/include/psa/crypto_struct.h
@@ -15,12 +15,20 @@
  *
  * <h3>Design notes about multipart operation structures</h3>
  *
- * Each multipart operation structure contains a `psa_algorithm_t alg`
- * field which indicates which specific algorithm the structure is for.
- * When the structure is not in use, `alg` is 0. Most of the structure
- * consists of a union which is discriminated by `alg`.
+ * For multipart operations without driver delegation support, each multipart
+ * operation structure contains a `psa_algorithm_t alg` field which indicates
+ * which specific algorithm the structure is for. When the structure is not in
+ * use, `alg` is 0. Most of the structure consists of a union which is
+ * discriminated by `alg`.
  *
- * Note that when `alg` is 0, the content of other fields is undefined.
+ * For multipart operations with driver delegation support, each multipart
+ * operation structure contains an `unsigned int id` field indicating which
+ * driver got assigned to do the operation. When the structure is not in use,
+ * 'id' is 0. The structure contains also a driver context which is the union
+ * of the contexts of all drivers able to handle the type of multipart
+ * operation.
+ *
+ * Note that when `alg` or `id` is 0, the content of other fields is undefined.
  * In particular, it is not guaranteed that a freshly-initialized structure
  * is all-zero: we initialize structures to something like `{0, 0}`, which
  * is only guaranteed to initializes the first member of the union;
@@ -65,56 +73,23 @@
 #include MBEDTLS_CONFIG_FILE
 #endif
 
-#include "mbedtls/cipher.h"
 #include "mbedtls/cmac.h"
 #include "mbedtls/gcm.h"
-#include "mbedtls/md.h"
-#include "mbedtls/md2.h"
-#include "mbedtls/md4.h"
-#include "mbedtls/md5.h"
-#include "mbedtls/ripemd160.h"
-#include "mbedtls/sha1.h"
-#include "mbedtls/sha256.h"
-#include "mbedtls/sha512.h"
 
-typedef struct {
-    /** Unique ID indicating which driver got assigned to do the
-     * operation. Since driver contexts are driver-specific, swapping
-     * drivers halfway through the operation is not supported.
-     * ID values are auto-generated in psa_driver_wrappers.h */
-    unsigned int id;
-    /** Context structure for the assigned driver, when id is not zero. */
-    void* ctx;
-} psa_operation_driver_context_t;
+/* Include the context definition for the compiled-in drivers for the primitive
+ * algorithms. */
+#include "psa/crypto_driver_contexts_primitives.h"
 
 struct psa_hash_operation_s
 {
-    psa_algorithm_t alg;
-    union
-    {
-        unsigned dummy; /* Make the union non-empty even with no supported algorithms. */
-#if defined(MBEDTLS_MD2_C)
-        mbedtls_md2_context md2;
-#endif
-#if defined(MBEDTLS_MD4_C)
-        mbedtls_md4_context md4;
-#endif
-#if defined(MBEDTLS_MD5_C)
-        mbedtls_md5_context md5;
-#endif
-#if defined(MBEDTLS_RIPEMD160_C)
-        mbedtls_ripemd160_context ripemd160;
-#endif
-#if defined(MBEDTLS_SHA1_C)
-        mbedtls_sha1_context sha1;
-#endif
-#if defined(MBEDTLS_SHA256_C)
-        mbedtls_sha256_context sha256;
-#endif
-#if defined(MBEDTLS_SHA512_C)
-        mbedtls_sha512_context sha512;
-#endif
-    } ctx;
+    /** Unique ID indicating which driver got assigned to do the
+     * operation. Since driver contexts are driver-specific, swapping
+     * drivers halfway through the operation is not supported.
+     * ID values are auto-generated in psa_driver_wrappers.h.
+     * ID value zero means the context is not valid or not assigned to
+     * any driver (i.e. the driver context is not active, in use). */
+    unsigned int id;
+    psa_driver_hash_context_t ctx;
 };
 
 #define PSA_HASH_OPERATION_INIT {0, {0}}
@@ -124,68 +99,56 @@
     return( v );
 }
 
-#if defined(MBEDTLS_MD_C)
-typedef struct
-{
-        /** The hash context. */
-        struct psa_hash_operation_s hash_ctx;
-        /** The HMAC part of the context. */
-        uint8_t opad[PSA_HMAC_MAX_HASH_BLOCK_SIZE];
-} psa_hmac_internal_data;
-#endif /* MBEDTLS_MD_C */
-
-struct psa_mac_operation_s
-{
-    psa_algorithm_t alg;
-    unsigned int key_set : 1;
-    unsigned int iv_required : 1;
-    unsigned int iv_set : 1;
-    unsigned int has_input : 1;
-    unsigned int is_sign : 1;
-    uint8_t mac_size;
-    union
-    {
-        unsigned dummy; /* Make the union non-empty even with no supported algorithms. */
-#if defined(MBEDTLS_MD_C)
-        psa_hmac_internal_data hmac;
-#endif
-#if defined(MBEDTLS_CMAC_C)
-        mbedtls_cipher_context_t cmac;
-#endif
-    } ctx;
-};
-
-#define PSA_MAC_OPERATION_INIT {0, 0, 0, 0, 0, 0, 0, {0}}
-static inline struct psa_mac_operation_s psa_mac_operation_init( void )
-{
-    const struct psa_mac_operation_s v = PSA_MAC_OPERATION_INIT;
-    return( v );
-}
-
 struct psa_cipher_operation_s
 {
-    psa_algorithm_t alg;
-    unsigned int key_set : 1;
+    /** Unique ID indicating which driver got assigned to do the
+     * operation. Since driver contexts are driver-specific, swapping
+     * drivers halfway through the operation is not supported.
+     * ID values are auto-generated in psa_crypto_driver_wrappers.h
+     * ID value zero means the context is not valid or not assigned to
+     * any driver (i.e. none of the driver contexts are active). */
+    unsigned int id;
+
     unsigned int iv_required : 1;
     unsigned int iv_set : 1;
-    unsigned int mbedtls_in_use : 1; /* Indicates mbed TLS is handling the operation. */
-    uint8_t iv_size;
-    uint8_t block_size;
-    union
-    {
-        unsigned dummy; /* Enable easier initializing of the union. */
-        mbedtls_cipher_context_t cipher;
-        psa_operation_driver_context_t driver;
-    } ctx;
+
+    uint8_t default_iv_length;
+
+    psa_driver_cipher_context_t ctx;
 };
 
-#define PSA_CIPHER_OPERATION_INIT {0, 0, 0, 0, 0, 0, 0, {0}}
+#define PSA_CIPHER_OPERATION_INIT {0, 0, 0, 0, {0}}
 static inline struct psa_cipher_operation_s psa_cipher_operation_init( void )
 {
     const struct psa_cipher_operation_s v = PSA_CIPHER_OPERATION_INIT;
     return( v );
 }
 
+/* Include the context definition for the compiled-in drivers for the composite
+ * algorithms. */
+#include "psa/crypto_driver_contexts_composites.h"
+
+struct psa_mac_operation_s
+{
+    /** Unique ID indicating which driver got assigned to do the
+     * operation. Since driver contexts are driver-specific, swapping
+     * drivers halfway through the operation is not supported.
+     * ID values are auto-generated in psa_driver_wrappers.h
+     * ID value zero means the context is not valid or not assigned to
+     * any driver (i.e. none of the driver contexts are active). */
+    unsigned int id;
+    uint8_t mac_size;
+    unsigned int is_sign : 1;
+    psa_driver_mac_context_t ctx;
+};
+
+#define PSA_MAC_OPERATION_INIT {0, 0, 0, {0}}
+static inline struct psa_mac_operation_s psa_mac_operation_init( void )
+{
+    const struct psa_mac_operation_s v = PSA_MAC_OPERATION_INIT;
+    return( v );
+}
+
 struct psa_aead_operation_s
 {
     psa_algorithm_t alg;
@@ -207,14 +170,11 @@
     return( v );
 }
 
-#if defined(MBEDTLS_MD_C)
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF)
 typedef struct
 {
     uint8_t *info;
     size_t info_length;
-    psa_hmac_internal_data hmac;
-    uint8_t prk[PSA_HASH_MAX_SIZE];
-    uint8_t output_block[PSA_HASH_MAX_SIZE];
 #if PSA_HASH_MAX_SIZE > 0xff
 #error "PSA_HASH_MAX_SIZE does not fit in uint8_t"
 #endif
@@ -222,17 +182,21 @@
     uint8_t block_number;
     unsigned int state : 2;
     unsigned int info_set : 1;
+    uint8_t output_block[PSA_HASH_MAX_SIZE];
+    uint8_t prk[PSA_HASH_MAX_SIZE];
+    struct psa_mac_operation_s hmac;
 } psa_hkdf_key_derivation_t;
-#endif /* MBEDTLS_MD_C */
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_HKDF */
 
-#if defined(MBEDTLS_MD_C)
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS)
 typedef enum
 {
-    TLS12_PRF_STATE_INIT,       /* no input provided */
-    TLS12_PRF_STATE_SEED_SET,   /* seed has been set */
-    TLS12_PRF_STATE_KEY_SET,    /* key has been set */
-    TLS12_PRF_STATE_LABEL_SET,  /* label has been set */
-    TLS12_PRF_STATE_OUTPUT      /* output has been started */
+    PSA_TLS12_PRF_STATE_INIT,       /* no input provided */
+    PSA_TLS12_PRF_STATE_SEED_SET,   /* seed has been set */
+    PSA_TLS12_PRF_STATE_KEY_SET,    /* key has been set */
+    PSA_TLS12_PRF_STATE_LABEL_SET,  /* label has been set */
+    PSA_TLS12_PRF_STATE_OUTPUT      /* output has been started */
 } psa_tls12_prf_key_derivation_state_t;
 
 typedef struct psa_tls12_prf_key_derivation_s
@@ -250,17 +214,20 @@
 
     psa_tls12_prf_key_derivation_state_t state;
 
+    uint8_t *secret;
+    size_t secret_length;
     uint8_t *seed;
     size_t seed_length;
     uint8_t *label;
     size_t label_length;
-    psa_hmac_internal_data hmac;
+
     uint8_t Ai[PSA_HASH_MAX_SIZE];
 
     /* `HMAC_hash( prk, A(i) + seed )` in the notation of RFC 5246, Sect. 5. */
     uint8_t output_block[PSA_HASH_MAX_SIZE];
 } psa_tls12_prf_key_derivation_t;
-#endif /* MBEDTLS_MD_C */
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF) ||
+        * MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS */
 
 struct psa_key_derivation_s
 {
@@ -271,8 +238,11 @@
     {
         /* Make the union non-empty even with no supported algorithms. */
         uint8_t dummy;
-#if defined(MBEDTLS_MD_C)
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF)
         psa_hkdf_key_derivation_t hkdf;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF) || \
+    defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS)
         psa_tls12_prf_key_derivation_t tls12_prf;
 #endif
     } ctx;
@@ -421,9 +391,19 @@
     return( attributes->core.lifetime );
 }
 
+static inline void psa_extend_key_usage_flags( psa_key_usage_t *usage_flags )
+{
+    if( *usage_flags & PSA_KEY_USAGE_SIGN_HASH )
+        *usage_flags |= PSA_KEY_USAGE_SIGN_MESSAGE;
+
+    if( *usage_flags & PSA_KEY_USAGE_VERIFY_HASH )
+        *usage_flags |= PSA_KEY_USAGE_VERIFY_MESSAGE;
+}
+
 static inline void psa_set_key_usage_flags(psa_key_attributes_t *attributes,
                                            psa_key_usage_t usage_flags)
 {
+    psa_extend_key_usage_flags( &usage_flags );
     attributes->core.policy.usage = usage_flags;
 }
 
diff --git a/third_party/mbedtls/repo/include/psa/crypto_types.h b/third_party/mbedtls/repo/include/psa/crypto_types.h
index 0a2ae54..386c7d7 100644
--- a/third_party/mbedtls/repo/include/psa/crypto_types.h
+++ b/third_party/mbedtls/repo/include/psa/crypto_types.h
@@ -35,6 +35,13 @@
 
 #include "crypto_platform.h"
 
+/* If MBEDTLS_PSA_CRYPTO_C is defined, make sure MBEDTLS_PSA_CRYPTO_CLIENT
+ * is defined as well to include all PSA code.
+ */
+#if defined(MBEDTLS_PSA_CRYPTO_C)
+#define MBEDTLS_PSA_CRYPTO_CLIENT
+#endif /* MBEDTLS_PSA_CRYPTO_C */
+
 #include <stdint.h>
 
 /** \defgroup error Error codes
@@ -115,14 +122,14 @@
  *   whether the key is _volatile_ or _persistent_.
  *   See ::psa_key_persistence_t for more information.
  * - Bits 8-31 (#PSA_KEY_LIFETIME_GET_LOCATION(\c lifetime)):
- *   location indicator. This value indicates where the key is stored
- *   and where operations on the key are performed.
+ *   location indicator. This value indicates which part of the system
+ *   has access to the key material and can perform operations using the key.
  *   See ::psa_key_location_t for more information.
  *
  * Volatile keys are automatically destroyed when the application instance
  * terminates or on a power reset of the device. Persistent keys are
  * preserved until the application explicitly destroys them or until an
- * implementation-specific device management event occurs (for example,
+ * integration-specific device management event occurs (for example,
  * a factory reset).
  *
  * Persistent keys have a key identifier of type #mbedtls_svc_key_id_t.
@@ -131,12 +138,10 @@
  * The application can call psa_open_key() to open a persistent key that
  * it created previously.
  *
- * This specification defines two basic lifetime values:
- * - Keys with the lifetime #PSA_KEY_LIFETIME_VOLATILE are volatile.
- *   All implementations should support this lifetime.
- * - Keys with the lifetime #PSA_KEY_LIFETIME_PERSISTENT are persistent.
- *   All implementations that have access to persistent storage with
- *   appropriate security guarantees should support this lifetime.
+ * The default lifetime of a key is #PSA_KEY_LIFETIME_VOLATILE. The lifetime
+ * #PSA_KEY_LIFETIME_PERSISTENT is supported if persistent storage is
+ * available. Other lifetime values may be supported depending on the
+ * library configuration.
  */
 typedef uint32_t psa_key_lifetime_t;
 
@@ -149,35 +154,21 @@
  * actually affect persistent keys at different levels is outside the
  * scope of the PSA Cryptography specification.
  *
- * This specification defines the following values of persistence levels:
+ * The PSA Cryptography specification defines the following values of
+ * persistence levels:
  * - \c 0 = #PSA_KEY_PERSISTENCE_VOLATILE: volatile key.
  *   A volatile key is automatically destroyed by the implementation when
  *   the application instance terminates. In particular, a volatile key
  *   is automatically destroyed on a power reset of the device.
  * - \c 1 = #PSA_KEY_PERSISTENCE_DEFAULT:
  *   persistent key with a default lifetime.
- *   Implementations should support this value if they support persistent
- *   keys at all.
- *   Applications should use this value if they have no specific needs that
- *   are only met by implementation-specific features.
- * - \c 2-127: persistent key with a PSA-specified lifetime.
- *   The PSA Cryptography specification does not define the meaning of these
- *   values, but other PSA specifications may do so.
- * - \c 128-254: persistent key with a vendor-specified lifetime.
- *   No PSA specification will define the meaning of these values, so
- *   implementations may choose the meaning freely.
- *   As a guideline, higher persistence levels should cause a key to survive
- *   more management events than lower levels.
+ * - \c 2-254: currently not supported by Mbed TLS.
  * - \c 255 = #PSA_KEY_PERSISTENCE_READ_ONLY:
  *   read-only or write-once key.
  *   A key with this persistence level cannot be destroyed.
- *   Implementations that support such keys may either allow their creation
- *   through the PSA Cryptography API, preferably only to applications with
- *   the appropriate privilege, or only expose keys created through
- *   implementation-specific means such as a factory ROM engraving process.
- *   Note that keys that are read-only due to policy restrictions
- *   rather than due to physical limitations should not have this
- *   persistence levels.
+ *   Mbed TLS does not currently offer a way to create such keys, but
+ *   integrations of Mbed TLS can use it for built-in keys that the
+ *   application cannot modify (for example, a hardware unique key (HUK)).
  *
  * \note Key persistence levels are 8-bit values. Key management
  *       interfaces operate on lifetimes (type ::psa_key_lifetime_t) which
@@ -187,28 +178,30 @@
 
 /** Encoding of key location indicators.
  *
- * If an implementation of this API can make calls to external
+ * If an integration of Mbed TLS can make calls to external
  * cryptoprocessors such as secure elements, the location of a key
  * indicates which secure element performs the operations on the key.
- * If an implementation offers multiple physical locations for persistent
- * storage, the location indicator reflects at which physical location
- * the key is stored.
+ * Depending on the design of the secure element, the key
+ * material may be stored either in the secure element, or
+ * in wrapped (encrypted) form alongside the key metadata in the
+ * primary local storage.
  *
- * This specification defines the following values of location indicators:
+ * The PSA Cryptography API specification defines the following values of
+ * location indicators:
  * - \c 0: primary local storage.
- *   All implementations should support this value.
+ *   This location is always available.
  *   The primary local storage is typically the same storage area that
  *   contains the key metadata.
  * - \c 1: primary secure element.
- *   Implementations should support this value if there is a secure element
- *   attached to the operating environment.
+ *   Integrations of Mbed TLS should support this value if there is a secure
+ *   element attached to the operating environment.
  *   As a guideline, secure elements may provide higher resistance against
  *   side channel and physical attacks than the primary local storage, but may
  *   have restrictions on supported key types, sizes, policies and operations
  *   and may have different performance characteristics.
  * - \c 2-0x7fffff: other locations defined by a PSA specification.
  *   The PSA Cryptography API does not currently assign any meaning to these
- *   locations, but future versions of this specification or other PSA
+ *   locations, but future versions of that specification or other PSA
  *   specifications may do so.
  * - \c 0x800000-0xffffff: vendor-defined locations.
  *   No PSA specification will assign a meaning to locations in this range.
@@ -223,7 +216,7 @@
  *
  * - Applications may freely choose key identifiers in the range
  *   #PSA_KEY_ID_USER_MIN to #PSA_KEY_ID_USER_MAX.
- * - Implementations may define additional key identifiers in the range
+ * - The implementation may define additional key identifiers in the range
  *   #PSA_KEY_ID_VENDOR_MIN to #PSA_KEY_ID_VENDOR_MAX.
  * - 0 is reserved as an invalid key identifier.
  * - Key identifiers outside these ranges are reserved for future use.
@@ -271,23 +264,18 @@
  * - The key's policy, comprising usage flags and a specification of
  *   the permitted algorithm(s).
  * - Information about the key itself: the key type and its size.
- * - Implementations may define additional attributes.
+ * - Additional implementation-defined attributes.
  *
  * The actual key material is not considered an attribute of a key.
  * Key attributes do not contain information that is generally considered
  * highly confidential.
  *
- * An attribute structure can be a simple data structure where each function
+ * An attribute structure works like a simple data structure where each function
  * `psa_set_key_xxx` sets a field and the corresponding function
  * `psa_get_key_xxx` retrieves the value of the corresponding field.
- * However, implementations may report values that are equivalent to the
- * original one, but have a different encoding. For example, an
- * implementation may use a more compact representation for types where
- * many bit-patterns are invalid or not supported, and store all values
- * that it does not support as a special marker value. In such an
- * implementation, after setting an invalid value, the corresponding
- * get function returns an invalid value which may not be the one that
- * was originally stored.
+ * However, a future version of the library  may report values that are
+ * equivalent to the original one, but have a different encoding. Invalid
+ * values may be mapped to different, also invalid values.
  *
  * An attribute structure may contain references to auxiliary resources,
  * for example pointers to allocated memory or indirect references to
diff --git a/third_party/mbedtls/repo/include/psa/crypto_values.h b/third_party/mbedtls/repo/include/psa/crypto_values.h
index f1b5c53..fafe93c 100644
--- a/third_party/mbedtls/repo/include/psa/crypto_values.h
+++ b/third_party/mbedtls/repo/include/psa/crypto_values.h
@@ -270,6 +270,46 @@
  */
 #define PSA_ERROR_INVALID_HANDLE        ((psa_status_t)-136)
 
+/** Stored data has been corrupted.
+ *
+ * This error indicates that some persistent storage has suffered corruption.
+ * It does not indicate the following situations, which have specific error
+ * codes:
+ *
+ * - A corruption of volatile memory - use #PSA_ERROR_CORRUPTION_DETECTED.
+ * - A communication error between the cryptoprocessor and its external
+ *   storage - use #PSA_ERROR_COMMUNICATION_FAILURE.
+ * - When the storage is in a valid state but is full - use
+ *   #PSA_ERROR_INSUFFICIENT_STORAGE.
+ * - When the storage fails for other reasons - use
+ *   #PSA_ERROR_STORAGE_FAILURE.
+ * - When the stored data is not valid - use #PSA_ERROR_DATA_INVALID.
+ *
+ * \note A storage corruption does not indicate that any data that was
+ * previously read is invalid. However this previously read data might no
+ * longer be readable from storage.
+ *
+ * When a storage failure occurs, it is no longer possible to ensure the
+ * global integrity of the keystore.
+ */
+#define PSA_ERROR_DATA_CORRUPT          ((psa_status_t)-152)
+
+/** Data read from storage is not valid for the implementation.
+ *
+ * This error indicates that some data read from storage does not have a valid
+ * format. It does not indicate the following situations, which have specific
+ * error codes:
+ *
+ * - When the storage or stored data is corrupted - use #PSA_ERROR_DATA_CORRUPT
+ * - When the storage fails for other reasons - use #PSA_ERROR_STORAGE_FAILURE
+ * - An invalid argument to the API - use #PSA_ERROR_INVALID_ARGUMENT
+ *
+ * This error is typically a result of either storage corruption on a
+ * cleartext storage backend, or an attempt to read data that was
+ * written by an incompatible version of the library.
+ */
+#define PSA_ERROR_DATA_INVALID          ((psa_status_t)-153)
+
 /**@}*/
 
 /** \defgroup crypto_types Key and algorithm types
@@ -363,7 +403,7 @@
  * used for.
  *
  * HMAC keys should generally have the same size as the underlying hash.
- * This size can be calculated with #PSA_HASH_SIZE(\c alg) where
+ * This size can be calculated with #PSA_HASH_LENGTH(\c alg) where
  * \c alg is the HMAC algorithm or the underlying hash algorithm. */
 #define PSA_KEY_TYPE_HMAC                           ((psa_key_type_t)0x1100)
 
@@ -381,10 +421,14 @@
  */
 #define PSA_KEY_TYPE_AES                            ((psa_key_type_t)0x2400)
 
+/** Key for a cipher, AEAD or MAC algorithm based on the
+ * ARIA block cipher. */
+#define PSA_KEY_TYPE_ARIA                           ((psa_key_type_t)0x2406)
+
 /** Key for a cipher or MAC algorithm based on DES or 3DES (Triple-DES).
  *
- * The size of the key can be 8 bytes (single DES), 16 bytes (2-key 3DES) or
- * 24 bytes (3-key 3DES).
+ * The size of the key can be 64 bits (single DES), 128 bits (2-key 3DES) or
+ * 192 bits (3-key 3DES).
  *
  * Note that single DES and 2-key 3DES are weak and strongly
  * deprecated and should only be used to decrypt legacy data. 3-key 3DES
@@ -411,9 +455,15 @@
  */
 #define PSA_KEY_TYPE_CHACHA20                       ((psa_key_type_t)0x2004)
 
-/** RSA public key. */
+/** RSA public key.
+ *
+ * The size of an RSA key is the bit size of the modulus.
+ */
 #define PSA_KEY_TYPE_RSA_PUBLIC_KEY                 ((psa_key_type_t)0x4001)
-/** RSA key pair (private and public key). */
+/** RSA key pair (private and public key).
+ *
+ * The size of an RSA key is the bit size of the modulus.
+ */
 #define PSA_KEY_TYPE_RSA_KEY_PAIR                   ((psa_key_type_t)0x7001)
 /** Whether a key type is an RSA key (pair or public-only). */
 #define PSA_KEY_TYPE_IS_RSA(type)                                       \
@@ -424,6 +474,10 @@
 #define PSA_KEY_TYPE_ECC_CURVE_MASK                 ((psa_key_type_t)0x00ff)
 /** Elliptic curve key pair.
  *
+ * The size of an elliptic curve key is the bit size associated with the curve,
+ * i.e. the bit size of *q* for a curve over a field *F<sub>q</sub>*.
+ * See the documentation of `PSA_ECC_FAMILY_xxx` curve families for details.
+ *
  * \param curve     A value of type ::psa_ecc_family_t that
  *                  identifies the ECC curve to be used.
  */
@@ -431,6 +485,10 @@
     (PSA_KEY_TYPE_ECC_KEY_PAIR_BASE | (curve))
 /** Elliptic curve public key.
  *
+ * The size of an elliptic curve public key is the same as the corresponding
+ * private key (see #PSA_KEY_TYPE_ECC_KEY_PAIR and the documentation of
+ * `PSA_ECC_FAMILY_xxx` curve families).
+ *
  * \param curve     A value of type ::psa_ecc_family_t that
  *                  identifies the ECC curve to be used.
  */
@@ -529,6 +587,22 @@
  */
 #define PSA_ECC_FAMILY_MONTGOMERY        ((psa_ecc_family_t) 0x41)
 
+/** The twisted Edwards curves Ed25519 and Ed448.
+ *
+ * These curves are suitable for EdDSA (#PSA_ALG_PURE_EDDSA for both curves,
+ * #PSA_ALG_ED25519PH for the 255-bit curve,
+ * #PSA_ALG_ED448PH for the 448-bit curve).
+ *
+ * This family comprises the following twisted Edwards curves:
+ * - 255-bit: Edwards25519, the twisted Edwards curve birationally equivalent
+ *   to Curve25519.
+ *   Bernstein et al., _Twisted Edwards curves_, Africacrypt 2008.
+ * - 448-bit: Edwards448, the twisted Edwards curve birationally equivalent
+ *   to Curve448.
+ *   Hamburg, _Ed448-Goldilocks, a new elliptic curve_, NIST ECC Workshop, 2015.
+ */
+#define PSA_ECC_FAMILY_TWISTED_EDWARDS   ((psa_ecc_family_t) 0x42)
+
 #define PSA_KEY_TYPE_DH_PUBLIC_KEY_BASE             ((psa_key_type_t)0x4200)
 #define PSA_KEY_TYPE_DH_KEY_PAIR_BASE               ((psa_key_type_t)0x7200)
 #define PSA_KEY_TYPE_DH_GROUP_MASK                  ((psa_key_type_t)0x00ff)
@@ -594,9 +668,9 @@
  *
  * \warning This macro may evaluate its argument multiple times.
  */
-#define PSA_BLOCK_CIPHER_BLOCK_SIZE(type)            \
+#define PSA_BLOCK_CIPHER_BLOCK_LENGTH(type)                                     \
     (((type) & PSA_KEY_TYPE_CATEGORY_MASK) == PSA_KEY_TYPE_CATEGORY_SYMMETRIC ? \
-     1u << PSA_GET_KEY_TYPE_BLOCK_SIZE_EXPONENT(type) :                 \
+     1u << PSA_GET_KEY_TYPE_BLOCK_SIZE_EXPONENT(type) :                         \
      0u)
 
 /** Vendor-defined algorithm flag.
@@ -716,6 +790,9 @@
 #define PSA_ALG_IS_KEY_DERIVATION(alg)                                  \
     (((alg) & PSA_ALG_CATEGORY_MASK) == PSA_ALG_CATEGORY_KEY_DERIVATION)
 
+/** An invalid algorithm identifier value. */
+#define PSA_ALG_NONE                            ((psa_algorithm_t)0)
+
 #define PSA_ALG_HASH_MASK                       ((psa_algorithm_t)0x000000ff)
 /** MD2 */
 #define PSA_ALG_MD2                             ((psa_algorithm_t)0x02000001)
@@ -747,6 +824,13 @@
 #define PSA_ALG_SHA3_384                        ((psa_algorithm_t)0x02000012)
 /** SHA3-512 */
 #define PSA_ALG_SHA3_512                        ((psa_algorithm_t)0x02000013)
+/** The first 512 bits (64 bytes) of the SHAKE256 output.
+ *
+ * This is the prehashing for Ed448ph (see #PSA_ALG_ED448PH). For other
+ * scenarios where a hash function based on SHA3/SHAKE is desired, SHA3-512
+ * has the same output size and a (theoretically) higher security strength.
+ */
+#define PSA_ALG_SHAKE256_512                    ((psa_algorithm_t)0x02000015)
 
 /** In a hash-and-sign algorithm policy, allow any hash algorithm.
  *
@@ -756,7 +840,7 @@
  * algorithm parametrized with any supported hash.
  *
  * That is, suppose that `PSA_xxx_SIGNATURE` is one of the following macros:
- * - #PSA_ALG_RSA_PKCS1V15_SIGN, #PSA_ALG_RSA_PSS,
+ * - #PSA_ALG_RSA_PKCS1V15_SIGN, #PSA_ALG_RSA_PSS, #PSA_ALG_RSA_PSS_ANY_SALT,
  * - #PSA_ALG_ECDSA, #PSA_ALG_DETERMINISTIC_ECDSA.
  * Then you may create and use a key as follows:
  * - Set the key usage field using #PSA_ALG_ANY_HASH, for example:
@@ -826,6 +910,14 @@
 #define PSA_ALG_MAC_TRUNCATION_MASK             ((psa_algorithm_t)0x003f0000)
 #define PSA_MAC_TRUNCATION_OFFSET 16
 
+/* In the encoding of a MAC algorithm, the bit corresponding to
+ * #PSA_ALG_MAC_AT_LEAST_THIS_LENGTH_FLAG encodes the fact that the algorithm
+ * is a wildcard algorithm. A key with such wildcard algorithm as permitted
+ * algorithm policy can be used with any algorithm corresponding to the
+ * same base class and having a (potentially truncated) MAC length greater or
+ * equal than the one encoded in #PSA_ALG_MAC_TRUNCATION_MASK. */
+#define PSA_ALG_MAC_AT_LEAST_THIS_LENGTH_FLAG   ((psa_algorithm_t)0x00008000)
+
 /** Macro to build a truncated MAC algorithm.
  *
  * A truncated MAC algorithm is identical to the corresponding MAC
@@ -844,7 +936,7 @@
  *          for policy comparison purposes.
  *
  * \param mac_alg       A MAC algorithm identifier (value of type
- *                      #psa_algorithm_t such that #PSA_ALG_IS_MAC(\p alg)
+ *                      #psa_algorithm_t such that #PSA_ALG_IS_MAC(\p mac_alg)
  *                      is true). This may be a truncated or untruncated
  *                      MAC algorithm.
  * \param mac_length    Desired length of the truncated MAC in bytes.
@@ -855,43 +947,73 @@
  *
  * \return              The corresponding MAC algorithm with the specified
  *                      length.
- * \return              Unspecified if \p alg is not a supported
+ * \return              Unspecified if \p mac_alg is not a supported
  *                      MAC algorithm or if \p mac_length is too small or
  *                      too large for the specified MAC algorithm.
  */
-#define PSA_ALG_TRUNCATED_MAC(mac_alg, mac_length)                      \
-    (((mac_alg) & ~PSA_ALG_MAC_TRUNCATION_MASK) |                       \
+#define PSA_ALG_TRUNCATED_MAC(mac_alg, mac_length)              \
+    (((mac_alg) & ~(PSA_ALG_MAC_TRUNCATION_MASK |               \
+                    PSA_ALG_MAC_AT_LEAST_THIS_LENGTH_FLAG)) |   \
      ((mac_length) << PSA_MAC_TRUNCATION_OFFSET & PSA_ALG_MAC_TRUNCATION_MASK))
 
 /** Macro to build the base MAC algorithm corresponding to a truncated
  * MAC algorithm.
  *
  * \param mac_alg       A MAC algorithm identifier (value of type
- *                      #psa_algorithm_t such that #PSA_ALG_IS_MAC(\p alg)
+ *                      #psa_algorithm_t such that #PSA_ALG_IS_MAC(\p mac_alg)
  *                      is true). This may be a truncated or untruncated
  *                      MAC algorithm.
  *
  * \return              The corresponding base MAC algorithm.
- * \return              Unspecified if \p alg is not a supported
+ * \return              Unspecified if \p mac_alg is not a supported
  *                      MAC algorithm.
  */
-#define PSA_ALG_FULL_LENGTH_MAC(mac_alg)        \
-    ((mac_alg) & ~PSA_ALG_MAC_TRUNCATION_MASK)
+#define PSA_ALG_FULL_LENGTH_MAC(mac_alg)                        \
+    ((mac_alg) & ~(PSA_ALG_MAC_TRUNCATION_MASK |                \
+                   PSA_ALG_MAC_AT_LEAST_THIS_LENGTH_FLAG))
 
 /** Length to which a MAC algorithm is truncated.
  *
  * \param mac_alg       A MAC algorithm identifier (value of type
- *                      #psa_algorithm_t such that #PSA_ALG_IS_MAC(\p alg)
+ *                      #psa_algorithm_t such that #PSA_ALG_IS_MAC(\p mac_alg)
  *                      is true).
  *
  * \return              Length of the truncated MAC in bytes.
- * \return              0 if \p alg is a non-truncated MAC algorithm.
- * \return              Unspecified if \p alg is not a supported
+ * \return              0 if \p mac_alg is a non-truncated MAC algorithm.
+ * \return              Unspecified if \p mac_alg is not a supported
  *                      MAC algorithm.
  */
 #define PSA_MAC_TRUNCATED_LENGTH(mac_alg)                               \
     (((mac_alg) & PSA_ALG_MAC_TRUNCATION_MASK) >> PSA_MAC_TRUNCATION_OFFSET)
 
+/** Macro to build a MAC minimum-MAC-length wildcard algorithm.
+ *
+ * A minimum-MAC-length MAC wildcard algorithm permits all MAC algorithms
+ * sharing the same base algorithm, and where the (potentially truncated) MAC
+ * length of the specific algorithm is equal to or larger then the wildcard
+ * algorithm's minimum MAC length.
+ *
+ * \note    When setting the minimum required MAC length to less than the
+ *          smallest MAC length allowed by the base algorithm, this effectively
+ *          becomes an 'any-MAC-length-allowed' policy for that base algorithm.
+ *
+ * \param mac_alg         A MAC algorithm identifier (value of type
+ *                        #psa_algorithm_t such that #PSA_ALG_IS_MAC(\p mac_alg)
+ *                        is true).
+ * \param min_mac_length  Desired minimum length of the message authentication
+ *                        code in bytes. This must be at most the untruncated
+ *                        length of the MAC and must be at least 1.
+ *
+ * \return                The corresponding MAC wildcard algorithm with the
+ *                        specified minimum length.
+ * \return                Unspecified if \p mac_alg is not a supported MAC
+ *                        algorithm or if \p min_mac_length is less than 1 or
+ *                        too large for the specified MAC algorithm.
+ */
+#define PSA_ALG_AT_LEAST_THIS_LENGTH_MAC(mac_alg, min_mac_length)   \
+    ( PSA_ALG_TRUNCATED_MAC(mac_alg, min_mac_length) |              \
+      PSA_ALG_MAC_AT_LEAST_THIS_LENGTH_FLAG )
+
 #define PSA_ALG_CIPHER_MAC_BASE                 ((psa_algorithm_t)0x03c00000)
 /** The CBC-MAC construction over a block cipher
  *
@@ -1052,6 +1174,14 @@
 #define PSA_ALG_AEAD_TAG_LENGTH_MASK            ((psa_algorithm_t)0x003f0000)
 #define PSA_AEAD_TAG_LENGTH_OFFSET 16
 
+/* In the encoding of an AEAD algorithm, the bit corresponding to
+ * #PSA_ALG_AEAD_AT_LEAST_THIS_LENGTH_FLAG encodes the fact that the algorithm
+ * is a wildcard algorithm. A key with such wildcard algorithm as permitted
+ * algorithm policy can be used with any algorithm corresponding to the
+ * same base class and having a tag length greater than or equal to the one
+ * encoded in #PSA_ALG_AEAD_TAG_LENGTH_MASK. */
+#define PSA_ALG_AEAD_AT_LEAST_THIS_LENGTH_FLAG  ((psa_algorithm_t)0x00008000)
+
 /** Macro to build a shortened AEAD algorithm.
  *
  * A shortened AEAD algorithm is similar to the corresponding AEAD
@@ -1060,40 +1190,83 @@
  * of the ciphertext.
  *
  * \param aead_alg      An AEAD algorithm identifier (value of type
- *                      #psa_algorithm_t such that #PSA_ALG_IS_AEAD(\p alg)
+ *                      #psa_algorithm_t such that #PSA_ALG_IS_AEAD(\p aead_alg)
  *                      is true).
  * \param tag_length    Desired length of the authentication tag in bytes.
  *
  * \return              The corresponding AEAD algorithm with the specified
  *                      length.
- * \return              Unspecified if \p alg is not a supported
+ * \return              Unspecified if \p aead_alg is not a supported
  *                      AEAD algorithm or if \p tag_length is not valid
  *                      for the specified AEAD algorithm.
  */
-#define PSA_ALG_AEAD_WITH_TAG_LENGTH(aead_alg, tag_length)              \
-    (((aead_alg) & ~PSA_ALG_AEAD_TAG_LENGTH_MASK) |                     \
+#define PSA_ALG_AEAD_WITH_SHORTENED_TAG(aead_alg, tag_length)           \
+    (((aead_alg) & ~(PSA_ALG_AEAD_TAG_LENGTH_MASK |                     \
+                     PSA_ALG_AEAD_AT_LEAST_THIS_LENGTH_FLAG)) |         \
      ((tag_length) << PSA_AEAD_TAG_LENGTH_OFFSET &                      \
       PSA_ALG_AEAD_TAG_LENGTH_MASK))
 
+/** Retrieve the tag length of a specified AEAD algorithm
+ *
+ * \param aead_alg      An AEAD algorithm identifier (value of type
+ *                      #psa_algorithm_t such that #PSA_ALG_IS_AEAD(\p aead_alg)
+ *                      is true).
+ *
+ * \return              The tag length specified by the input algorithm.
+ * \return              Unspecified if \p aead_alg is not a supported
+ *                      AEAD algorithm.
+ */
+#define PSA_ALG_AEAD_GET_TAG_LENGTH(aead_alg)                           \
+    (((aead_alg) & PSA_ALG_AEAD_TAG_LENGTH_MASK) >>                     \
+      PSA_AEAD_TAG_LENGTH_OFFSET )
+
 /** Calculate the corresponding AEAD algorithm with the default tag length.
  *
  * \param aead_alg      An AEAD algorithm (\c PSA_ALG_XXX value such that
- *                      #PSA_ALG_IS_AEAD(\p alg) is true).
+ *                      #PSA_ALG_IS_AEAD(\p aead_alg) is true).
  *
  * \return              The corresponding AEAD algorithm with the default
  *                      tag length for that algorithm.
  */
-#define PSA_ALG_AEAD_WITH_DEFAULT_TAG_LENGTH(aead_alg)                   \
+#define PSA_ALG_AEAD_WITH_DEFAULT_LENGTH_TAG(aead_alg)                   \
     (                                                                    \
-        PSA_ALG_AEAD_WITH_DEFAULT_TAG_LENGTH_CASE(aead_alg, PSA_ALG_CCM) \
-        PSA_ALG_AEAD_WITH_DEFAULT_TAG_LENGTH_CASE(aead_alg, PSA_ALG_GCM) \
-        PSA_ALG_AEAD_WITH_DEFAULT_TAG_LENGTH_CASE(aead_alg, PSA_ALG_CHACHA20_POLY1305) \
+        PSA_ALG_AEAD_WITH_DEFAULT_LENGTH_TAG_CASE(aead_alg, PSA_ALG_CCM) \
+        PSA_ALG_AEAD_WITH_DEFAULT_LENGTH_TAG_CASE(aead_alg, PSA_ALG_GCM) \
+        PSA_ALG_AEAD_WITH_DEFAULT_LENGTH_TAG_CASE(aead_alg, PSA_ALG_CHACHA20_POLY1305) \
         0)
-#define PSA_ALG_AEAD_WITH_DEFAULT_TAG_LENGTH_CASE(aead_alg, ref)         \
-    PSA_ALG_AEAD_WITH_TAG_LENGTH(aead_alg, 0) ==                         \
-    PSA_ALG_AEAD_WITH_TAG_LENGTH(ref, 0) ?                               \
+#define PSA_ALG_AEAD_WITH_DEFAULT_LENGTH_TAG_CASE(aead_alg, ref)         \
+    PSA_ALG_AEAD_WITH_SHORTENED_TAG(aead_alg, 0) ==                      \
+    PSA_ALG_AEAD_WITH_SHORTENED_TAG(ref, 0) ?                            \
     ref :
 
+/** Macro to build an AEAD minimum-tag-length wildcard algorithm.
+ *
+ * A minimum-tag-length AEAD wildcard algorithm permits all AEAD algorithms
+ * sharing the same base algorithm, and where the tag length of the specific
+ * algorithm is equal to or larger then the minimum tag length specified by the
+ * wildcard algorithm.
+ *
+ * \note    When setting the minimum required tag length to less than the
+ *          smallest tag length allowed by the base algorithm, this effectively
+ *          becomes an 'any-tag-length-allowed' policy for that base algorithm.
+ *
+ * \param aead_alg        An AEAD algorithm identifier (value of type
+ *                        #psa_algorithm_t such that
+ *                        #PSA_ALG_IS_AEAD(\p aead_alg) is true).
+ * \param min_tag_length  Desired minimum length of the authentication tag in
+ *                        bytes. This must be at least 1 and at most the largest
+ *                        allowed tag length of the algorithm.
+ *
+ * \return                The corresponding AEAD wildcard algorithm with the
+ *                        specified minimum length.
+ * \return                Unspecified if \p aead_alg is not a supported
+ *                        AEAD algorithm or if \p min_tag_length is less than 1
+ *                        or too large for the specified AEAD algorithm.
+ */
+#define PSA_ALG_AEAD_WITH_AT_LEAST_THIS_LENGTH_TAG(aead_alg, min_tag_length) \
+    ( PSA_ALG_AEAD_WITH_SHORTENED_TAG(aead_alg, min_tag_length) |            \
+      PSA_ALG_AEAD_AT_LEAST_THIS_LENGTH_FLAG )
+
 #define PSA_ALG_RSA_PKCS1V15_SIGN_BASE          ((psa_algorithm_t)0x06000200)
 /** RSA PKCS#1 v1.5 signature with hashing.
  *
@@ -1123,6 +1296,7 @@
     (((alg) & ~PSA_ALG_HASH_MASK) == PSA_ALG_RSA_PKCS1V15_SIGN_BASE)
 
 #define PSA_ALG_RSA_PSS_BASE               ((psa_algorithm_t)0x06000300)
+#define PSA_ALG_RSA_PSS_ANY_SALT_BASE      ((psa_algorithm_t)0x06001300)
 /** RSA PSS signature with hashing.
  *
  * This is the signature scheme defined by RFC 8017
@@ -1143,9 +1317,72 @@
  */
 #define PSA_ALG_RSA_PSS(hash_alg)                               \
     (PSA_ALG_RSA_PSS_BASE | ((hash_alg) & PSA_ALG_HASH_MASK))
-#define PSA_ALG_IS_RSA_PSS(alg)                                 \
+
+/** RSA PSS signature with hashing with relaxed verification.
+ *
+ * This algorithm has the same behavior as #PSA_ALG_RSA_PSS when signing,
+ * but allows an arbitrary salt length (including \c 0) when verifying a
+ * signature.
+ *
+ * \param hash_alg      A hash algorithm (\c PSA_ALG_XXX value such that
+ *                      #PSA_ALG_IS_HASH(\p hash_alg) is true).
+ *                      This includes #PSA_ALG_ANY_HASH
+ *                      when specifying the algorithm in a usage policy.
+ *
+ * \return              The corresponding RSA PSS signature algorithm.
+ * \return              Unspecified if \p hash_alg is not a supported
+ *                      hash algorithm.
+ */
+#define PSA_ALG_RSA_PSS_ANY_SALT(hash_alg)                      \
+    (PSA_ALG_RSA_PSS_ANY_SALT_BASE | ((hash_alg) & PSA_ALG_HASH_MASK))
+
+/** Whether the specified algorithm is RSA PSS with standard salt.
+ *
+ * \param alg           An algorithm value or an algorithm policy wildcard.
+ *
+ * \return              1 if \p alg is of the form
+ *                      #PSA_ALG_RSA_PSS(\c hash_alg),
+ *                      where \c hash_alg is a hash algorithm or
+ *                      #PSA_ALG_ANY_HASH. 0 otherwise.
+ *                      This macro may return either 0 or 1 if \p alg is not
+ *                      a supported algorithm identifier or policy.
+ */
+#define PSA_ALG_IS_RSA_PSS_STANDARD_SALT(alg)                   \
     (((alg) & ~PSA_ALG_HASH_MASK) == PSA_ALG_RSA_PSS_BASE)
 
+/** Whether the specified algorithm is RSA PSS with any salt.
+ *
+ * \param alg           An algorithm value or an algorithm policy wildcard.
+ *
+ * \return              1 if \p alg is of the form
+ *                      #PSA_ALG_RSA_PSS_ANY_SALT_BASE(\c hash_alg),
+ *                      where \c hash_alg is a hash algorithm or
+ *                      #PSA_ALG_ANY_HASH. 0 otherwise.
+ *                      This macro may return either 0 or 1 if \p alg is not
+ *                      a supported algorithm identifier or policy.
+ */
+#define PSA_ALG_IS_RSA_PSS_ANY_SALT(alg)                                \
+    (((alg) & ~PSA_ALG_HASH_MASK) == PSA_ALG_RSA_PSS_ANY_SALT_BASE)
+
+/** Whether the specified algorithm is RSA PSS.
+ *
+ * This includes any of the RSA PSS algorithm variants, regardless of the
+ * constraints on salt length.
+ *
+ * \param alg           An algorithm value or an algorithm policy wildcard.
+ *
+ * \return              1 if \p alg is of the form
+ *                      #PSA_ALG_RSA_PSS(\c hash_alg) or
+ *                      #PSA_ALG_RSA_PSS_ANY_SALT_BASE(\c hash_alg),
+ *                      where \c hash_alg is a hash algorithm or
+ *                      #PSA_ALG_ANY_HASH. 0 otherwise.
+ *                      This macro may return either 0 or 1 if \p alg is not
+ *                      a supported algorithm identifier or policy.
+ */
+#define PSA_ALG_IS_RSA_PSS(alg)                                 \
+    (PSA_ALG_IS_RSA_PSS_STANDARD_SALT(alg) ||                   \
+     PSA_ALG_IS_RSA_PSS_ANY_SALT(alg))
+
 #define PSA_ALG_ECDSA_BASE                      ((psa_algorithm_t)0x06000600)
 /** ECDSA signature with hashing.
  *
@@ -1215,12 +1452,149 @@
 #define PSA_ALG_IS_RANDOMIZED_ECDSA(alg)                                \
     (PSA_ALG_IS_ECDSA(alg) && !PSA_ALG_ECDSA_IS_DETERMINISTIC(alg))
 
+/** Edwards-curve digital signature algorithm without prehashing (PureEdDSA),
+ * using standard parameters.
+ *
+ * Contexts are not supported in the current version of this specification
+ * because there is no suitable signature interface that can take the
+ * context as a parameter. A future version of this specification may add
+ * suitable functions and extend this algorithm to support contexts.
+ *
+ * PureEdDSA requires an elliptic curve key on a twisted Edwards curve.
+ * In this specification, the following curves are supported:
+ * - #PSA_ECC_FAMILY_TWISTED_EDWARDS, 255-bit: Ed25519 as specified
+ *   in RFC 8032.
+ *   The curve is Edwards25519.
+ *   The hash function used internally is SHA-512.
+ * - #PSA_ECC_FAMILY_TWISTED_EDWARDS, 448-bit: Ed448 as specified
+ *   in RFC 8032.
+ *   The curve is Edwards448.
+ *   The hash function used internally is the first 114 bytes of the
+ *   SHAKE256 output.
+ *
+ * This algorithm can be used with psa_sign_message() and
+ * psa_verify_message(). Since there is no prehashing, it cannot be used
+ * with psa_sign_hash() or psa_verify_hash().
+ *
+ * The signature format is the concatenation of R and S as defined by
+ * RFC 8032 §5.1.6 and §5.2.6 (a 64-byte string for Ed25519, a 114-byte
+ * string for Ed448).
+ */
+#define PSA_ALG_PURE_EDDSA                      ((psa_algorithm_t)0x06000800)
+
+#define PSA_ALG_HASH_EDDSA_BASE                 ((psa_algorithm_t)0x06000900)
+#define PSA_ALG_IS_HASH_EDDSA(alg)              \
+    (((alg) & ~PSA_ALG_HASH_MASK) == PSA_ALG_HASH_EDDSA_BASE)
+
+/** Edwards-curve digital signature algorithm with prehashing (HashEdDSA),
+ * using SHA-512 and the Edwards25519 curve.
+ *
+ * See #PSA_ALG_PURE_EDDSA regarding context support and the signature format.
+ *
+ * This algorithm is Ed25519 as specified in RFC 8032.
+ * The curve is Edwards25519.
+ * The prehash is SHA-512.
+ * The hash function used internally is SHA-512.
+ *
+ * This is a hash-and-sign algorithm: to calculate a signature,
+ * you can either:
+ * - call psa_sign_message() on the message;
+ * - or calculate the SHA-512 hash of the message
+ *   with psa_hash_compute()
+ *   or with a multi-part hash operation started with psa_hash_setup(),
+ *   using the hash algorithm #PSA_ALG_SHA_512,
+ *   then sign the calculated hash with psa_sign_hash().
+ * Verifying a signature is similar, using psa_verify_message() or
+ * psa_verify_hash() instead of the signature function.
+ */
+#define PSA_ALG_ED25519PH                               \
+    (PSA_ALG_HASH_EDDSA_BASE | (PSA_ALG_SHA_512 & PSA_ALG_HASH_MASK))
+
+/** Edwards-curve digital signature algorithm with prehashing (HashEdDSA),
+ * using SHAKE256 and the Edwards448 curve.
+ *
+ * See #PSA_ALG_PURE_EDDSA regarding context support and the signature format.
+ *
+ * This algorithm is Ed448 as specified in RFC 8032.
+ * The curve is Edwards448.
+ * The prehash is the first 64 bytes of the SHAKE256 output.
+ * The hash function used internally is the first 114 bytes of the
+ * SHAKE256 output.
+ *
+ * This is a hash-and-sign algorithm: to calculate a signature,
+ * you can either:
+ * - call psa_sign_message() on the message;
+ * - or calculate the first 64 bytes of the SHAKE256 output of the message
+ *   with psa_hash_compute()
+ *   or with a multi-part hash operation started with psa_hash_setup(),
+ *   using the hash algorithm #PSA_ALG_SHAKE256_512,
+ *   then sign the calculated hash with psa_sign_hash().
+ * Verifying a signature is similar, using psa_verify_message() or
+ * psa_verify_hash() instead of the signature function.
+ */
+#define PSA_ALG_ED448PH                                 \
+    (PSA_ALG_HASH_EDDSA_BASE | (PSA_ALG_SHAKE256_512 & PSA_ALG_HASH_MASK))
+
+/* Default definition, to be overridden if the library is extended with
+ * more hash-and-sign algorithms that we want to keep out of this header
+ * file. */
+#define PSA_ALG_IS_VENDOR_HASH_AND_SIGN(alg) 0
+
+/** Whether the specified algorithm is a signature algorithm that can be used
+ * with psa_sign_hash() and psa_verify_hash().
+ *
+ * This encompasses all strict hash-and-sign algorithms categorized by
+ * PSA_ALG_IS_HASH_AND_SIGN(), as well as algorithms that follow the
+ * paradigm more loosely:
+ * - #PSA_ALG_RSA_PKCS1V15_SIGN_RAW (expects its input to be an encoded hash)
+ * - #PSA_ALG_ECDSA_ANY (doesn't specify what kind of hash the input is)
+ *
+ * \param alg An algorithm identifier (value of type psa_algorithm_t).
+ *
+ * \return 1 if alg is a signature algorithm that can be used to sign a
+ *         hash. 0 if alg is a signature algorithm that can only be used
+ *         to sign a message. 0 if alg is not a signature algorithm.
+ *         This macro can return either 0 or 1 if alg is not a
+ *         supported algorithm identifier.
+ */
+#define PSA_ALG_IS_SIGN_HASH(alg)                                       \
+    (PSA_ALG_IS_RSA_PSS(alg) || PSA_ALG_IS_RSA_PKCS1V15_SIGN(alg) ||    \
+     PSA_ALG_IS_ECDSA(alg) || PSA_ALG_IS_HASH_EDDSA(alg) ||             \
+     PSA_ALG_IS_VENDOR_HASH_AND_SIGN(alg))
+
+/** Whether the specified algorithm is a signature algorithm that can be used
+ * with psa_sign_message() and psa_verify_message().
+ *
+ * \param alg An algorithm identifier (value of type #psa_algorithm_t).
+ *
+ * \return 1 if alg is a signature algorithm that can be used to sign a
+ *         message. 0 if \p alg is a signature algorithm that can only be used
+ *         to sign an already-calculated hash. 0 if \p alg is not a signature
+ *         algorithm. This macro can return either 0 or 1 if \p alg is not a
+ *         supported algorithm identifier.
+ */
+#define PSA_ALG_IS_SIGN_MESSAGE(alg)                                    \
+    (PSA_ALG_IS_SIGN_HASH(alg) || (alg) == PSA_ALG_PURE_EDDSA )
+
 /** Whether the specified algorithm is a hash-and-sign algorithm.
  *
  * Hash-and-sign algorithms are asymmetric (public-key) signature algorithms
  * structured in two parts: first the calculation of a hash in a way that
  * does not depend on the key, then the calculation of a signature from the
- * hash value and the key.
+ * hash value and the key. Hash-and-sign algorithms encode the hash
+ * used for the hashing step, and you can call #PSA_ALG_SIGN_GET_HASH
+ * to extract this algorithm.
+ *
+ * Thus, for a hash-and-sign algorithm,
+ * `psa_sign_message(key, alg, input, ...)` is equivalent to
+ * ```
+ * psa_hash_compute(PSA_ALG_SIGN_GET_HASH(alg), input, ..., hash, ...);
+ * psa_sign_hash(key, alg, hash, ..., signature, ...);
+ * ```
+ * Most usefully, separating the hash from the signature allows the hash
+ * to be calculated in multiple steps with psa_hash_setup(), psa_hash_update()
+ * and psa_hash_finish(). Likewise psa_verify_message() is equivalent to
+ * calculating the hash and then calling psa_verify_hash().
  *
  * \param alg An algorithm identifier (value of type #psa_algorithm_t).
  *
@@ -1229,8 +1603,8 @@
  *         algorithm identifier.
  */
 #define PSA_ALG_IS_HASH_AND_SIGN(alg)                                   \
-    (PSA_ALG_IS_RSA_PSS(alg) || PSA_ALG_IS_RSA_PKCS1V15_SIGN(alg) ||    \
-     PSA_ALG_IS_ECDSA(alg))
+    (PSA_ALG_IS_SIGN_HASH(alg) &&                                       \
+     ((alg) & PSA_ALG_HASH_MASK) != 0)
 
 /** Get the hash used by a hash-and-sign signature algorithm.
  *
@@ -1252,7 +1626,6 @@
  */
 #define PSA_ALG_SIGN_GET_HASH(alg)                                     \
     (PSA_ALG_IS_HASH_AND_SIGN(alg) ?                                   \
-     ((alg) & PSA_ALG_HASH_MASK) == 0 ? /*"raw" algorithm*/ 0 :        \
      ((alg) & PSA_ALG_HASH_MASK) | PSA_ALG_CATEGORY_HASH :             \
      0)
 
@@ -1540,9 +1913,13 @@
  * \return This macro may return either 0 or 1 if \c alg is not a supported
  *         algorithm identifier.
  */
-#define PSA_ALG_IS_WILDCARD(alg)                        \
-    (PSA_ALG_IS_HASH_AND_SIGN(alg) ?                    \
-     PSA_ALG_SIGN_GET_HASH(alg) == PSA_ALG_ANY_HASH :   \
+#define PSA_ALG_IS_WILDCARD(alg)                            \
+    (PSA_ALG_IS_HASH_AND_SIGN(alg) ?                        \
+     PSA_ALG_SIGN_GET_HASH(alg) == PSA_ALG_ANY_HASH :       \
+     PSA_ALG_IS_MAC(alg) ?                                  \
+     (alg & PSA_ALG_MAC_AT_LEAST_THIS_LENGTH_FLAG) != 0 :   \
+     PSA_ALG_IS_AEAD(alg) ?                                 \
+     (alg & PSA_ALG_AEAD_AT_LEAST_THIS_LENGTH_FLAG) != 0 :  \
      (alg) == PSA_ALG_ANY_HASH)
 
 /**@}*/
@@ -1568,13 +1945,12 @@
  *
  * A persistent key remains in storage until it is explicitly destroyed or
  * until the corresponding storage area is wiped. This specification does
- * not define any mechanism to wipe a storage area, but implementations may
+ * not define any mechanism to wipe a storage area, but integrations may
  * provide their own mechanism (for example to perform a factory reset,
  * to prepare for device refurbishment, or to uninstall an application).
  *
  * This lifetime value is the default storage area for the calling
- * application. Implementations may offer other storage areas designated
- * by other lifetime values as implementation-specific extensions.
+ * application. Integrations of Mbed TLS may support other persistent lifetimes.
  * See ::psa_key_lifetime_t for more information.
  */
 #define PSA_KEY_LIFETIME_PERSISTENT             ((psa_key_lifetime_t)0x00000001)
@@ -1623,6 +1999,27 @@
     (PSA_KEY_LIFETIME_GET_PERSISTENCE(lifetime) == \
      PSA_KEY_PERSISTENCE_VOLATILE)
 
+/** Whether a key lifetime indicates that the key is read-only.
+ *
+ * Read-only keys cannot be created or destroyed through the PSA Crypto API.
+ * They must be created through platform-specific means that bypass the API.
+ *
+ * Some platforms may offer ways to destroy read-only keys. For example,
+ * consider a platform with multiple levels of privilege, where a
+ * low-privilege application can use a key but is not allowed to destroy
+ * it, and the platform exposes the key to the application with a read-only
+ * lifetime. High-privilege code can destroy the key even though the
+ * application sees the key as read-only.
+ *
+ * \param lifetime      The lifetime value to query (value of type
+ *                      ::psa_key_lifetime_t).
+ *
+ * \return \c 1 if the key is read-only, otherwise \c 0.
+ */
+#define PSA_KEY_LIFETIME_IS_READ_ONLY(lifetime)  \
+    (PSA_KEY_LIFETIME_GET_PERSISTENCE(lifetime) == \
+     PSA_KEY_PERSISTENCE_READ_ONLY)
+
 /** Construct a lifetime from a persistence level and a location.
  *
  * \param persistence   The persistence level
@@ -1646,6 +2043,9 @@
 
 #define PSA_KEY_LOCATION_VENDOR_FLAG            ((psa_key_location_t)0x800000)
 
+/** The null key identifier.
+ */
+#define PSA_KEY_ID_NULL                         ((psa_key_id_t)0)
 /** The minimum value for a key identifier chosen by the application.
  */
 #define PSA_KEY_ID_USER_MIN                     ((psa_key_id_t)0x00000001)
@@ -1743,7 +2143,7 @@
  */
 static inline int mbedtls_svc_key_id_is_null( mbedtls_svc_key_id_t key )
 {
-    return( ( key.key_id == 0 ) && ( key.owner == 0 ) );
+    return( key.key_id == 0 );
 }
 
 #endif /* !MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER */
@@ -1807,6 +2207,26 @@
 
 /** Whether the key may be used to sign a message.
  *
+ * This flag allows the key to be used for a MAC calculation operation or for
+ * an asymmetric message signature operation, if otherwise permitted by the
+ * key’s type and policy.
+ *
+ * For a key pair, this concerns the private key.
+ */
+#define PSA_KEY_USAGE_SIGN_MESSAGE              ((psa_key_usage_t)0x00000400)
+
+/** Whether the key may be used to verify a message.
+ *
+ * This flag allows the key to be used for a MAC verification operation or for
+ * an asymmetric message signature verification operation, if otherwise
+ * permitted by the key’s type and policy.
+ *
+ * For a key pair, this concerns the public key.
+ */
+#define PSA_KEY_USAGE_VERIFY_MESSAGE            ((psa_key_usage_t)0x00000800)
+
+/** Whether the key may be used to sign a message.
+ *
  * This flag allows the key to be used for a MAC calculation operation
  * or for an asymmetric signature operation,
  * if otherwise permitted by the key's type and policy.
@@ -1879,4 +2299,27 @@
 
 /**@}*/
 
+/** \defgroup helper_macros Helper macros
+ * @{
+ */
+
+/* Helper macros */
+
+/** Check if two AEAD algorithm identifiers refer to the same AEAD algorithm
+ *  regardless of the tag length they encode.
+ *
+ * \param aead_alg_1 An AEAD algorithm identifier.
+ * \param aead_alg_2 An AEAD algorithm identifier.
+ *
+ * \return           1 if both identifiers refer to the same AEAD algorithm,
+ *                   0 otherwise.
+ *                   Unspecified if neither \p aead_alg_1 nor \p aead_alg_2 are
+ *                   a supported AEAD algorithm.
+ */
+#define MBEDTLS_PSA_ALG_AEAD_EQUAL(aead_alg_1, aead_alg_2) \
+    (!(((aead_alg_1) ^ (aead_alg_2)) & \
+       ~(PSA_ALG_AEAD_TAG_LENGTH_MASK | PSA_ALG_AEAD_AT_LEAST_THIS_LENGTH_FLAG)))
+
+/**@}*/
+
 #endif /* PSA_CRYPTO_VALUES_H */
diff --git a/third_party/mbedtls/repo/library/CMakeLists.txt b/third_party/mbedtls/repo/library/CMakeLists.txt
index b309b6e..0a60067 100644
--- a/third_party/mbedtls/repo/library/CMakeLists.txt
+++ b/third_party/mbedtls/repo/library/CMakeLists.txt
@@ -26,6 +26,7 @@
     chachapoly.c
     cipher.c
     cipher_wrap.c
+    constant_time.c
     cmac.c
     ctr_drbg.c
     des.c
@@ -47,6 +48,8 @@
     md4.c
     md5.c
     memory_buffer_alloc.c
+    mps_reader.c
+    mps_trace.c
     nist_kw.c
     oid.c
     padlock.c
@@ -61,7 +64,14 @@
     platform_util.c
     poly1305.c
     psa_crypto.c
+    psa_crypto_aead.c
+    psa_crypto_cipher.c
+    psa_crypto_client.c
     psa_crypto_driver_wrappers.c
+    psa_crypto_ecp.c
+    psa_crypto_hash.c
+    psa_crypto_mac.c
+    psa_crypto_rsa.c
     psa_crypto_se.c
     psa_crypto_slot_management.c
     psa_crypto_storage.c
@@ -193,15 +203,15 @@
 
 if(USE_SHARED_MBEDTLS_LIBRARY)
     add_library(${mbedcrypto_target} SHARED ${src_crypto})
-    set_target_properties(${mbedcrypto_target} PROPERTIES VERSION 2.25.0 SOVERSION 6)
+    set_target_properties(${mbedcrypto_target} PROPERTIES VERSION 2.28.0 SOVERSION 7)
     target_link_libraries(${mbedcrypto_target} PUBLIC ${libs})
 
     add_library(${mbedx509_target} SHARED ${src_x509})
-    set_target_properties(${mbedx509_target} PROPERTIES VERSION 2.25.0 SOVERSION 1)
+    set_target_properties(${mbedx509_target} PROPERTIES VERSION 2.28.0 SOVERSION 1)
     target_link_libraries(${mbedx509_target} PUBLIC ${libs} ${mbedcrypto_target})
 
     add_library(${mbedtls_target} SHARED ${src_tls})
-    set_target_properties(${mbedtls_target} PROPERTIES VERSION 2.25.0 SOVERSION 13)
+    set_target_properties(${mbedtls_target} PROPERTIES VERSION 2.28.0 SOVERSION 14)
     target_link_libraries(${mbedtls_target} PUBLIC ${libs} ${mbedx509_target})
 endif(USE_SHARED_MBEDTLS_LIBRARY)
 
diff --git a/third_party/mbedtls/repo/library/Makefile b/third_party/mbedtls/repo/library/Makefile
index ae33bf2..54b0651 100644
--- a/third_party/mbedtls/repo/library/Makefile
+++ b/third_party/mbedtls/repo/library/Makefile
@@ -2,7 +2,7 @@
 # Also see "include/mbedtls/config.h"
 
 CFLAGS	?= -O2
-WARNING_CFLAGS ?=  -Wall -Wextra
+WARNING_CFLAGS ?=  -Wall -Wextra -Wformat=2 -Wno-format-nonliteral
 LDFLAGS ?=
 
 # Include ../include for public headers and . for private headers.
@@ -39,9 +39,9 @@
 endif
 endif
 
-SOEXT_TLS=so.13
+SOEXT_TLS=so.14
 SOEXT_X509=so.1
-SOEXT_CRYPTO=so.6
+SOEXT_CRYPTO=so.7
 
 # Set AR_DASH= (empty string) to use an ar implementation that does not accept
 # the - prefix for command line options (e.g. llvm-ar)
@@ -84,6 +84,7 @@
 	     cipher.o \
 	     cipher_wrap.o \
 	     cmac.o \
+	     constant_time.o \
 	     ctr_drbg.o \
 	     des.o \
 	     dhm.o \
@@ -104,6 +105,8 @@
 	     md4.o \
 	     md5.o \
 	     memory_buffer_alloc.o \
+	     mps_reader.o \
+	     mps_trace.o \
 	     nist_kw.o \
 	     oid.o \
 	     padlock.o \
@@ -118,7 +121,14 @@
 	     platform_util.o \
 	     poly1305.o \
 	     psa_crypto.o \
+	     psa_crypto_aead.o \
+	     psa_crypto_cipher.o \
+	     psa_crypto_client.o \
 	     psa_crypto_driver_wrappers.o \
+	     psa_crypto_ecp.o \
+	     psa_crypto_hash.o \
+	     psa_crypto_mac.o \
+	     psa_crypto_rsa.o \
 	     psa_crypto_se.o \
 	     psa_crypto_slot_management.o \
 	     psa_crypto_storage.o \
@@ -180,6 +190,14 @@
 
 shared: libmbedcrypto.$(DLEXT) libmbedx509.$(DLEXT) libmbedtls.$(DLEXT)
 
+# Windows builds under Mingw can fail if make tries to create archives in the same
+# directory at the same time - see https://bugs.launchpad.net/gcc-arm-embedded/+bug/1848002.
+# This forces builds of the .a files to be serialised.
+ifdef WINDOWS
+libmbedtls.a: | libmbedx509.a
+libmbedx509.a: | libmbedcrypto.a
+endif
+
 # tls
 libmbedtls.a: $(OBJS_TLS)
 	echo "  AR    $@"
@@ -193,7 +211,7 @@
 
 libmbedtls.$(SOEXT_TLS): $(OBJS_TLS) libmbedx509.so
 	echo "  LD    $@"
-	$(CC) -shared -Wl,-soname,$@ -L. -lmbedcrypto -lmbedx509 $(LOCAL_LDFLAGS) $(LDFLAGS) -o $@ $(OBJS_TLS)
+	$(CC) -shared -Wl,-soname,$@ -o $@ $(OBJS_TLS) -L. -lmbedx509 -lmbedcrypto $(LOCAL_LDFLAGS) $(LDFLAGS)
 
 libmbedtls.so: libmbedtls.$(SOEXT_TLS)
 	echo "  LN    $@ -> $<"
@@ -201,11 +219,11 @@
 
 libmbedtls.dylib: $(OBJS_TLS) libmbedx509.dylib
 	echo "  LD    $@"
-	$(CC) -dynamiclib -L. -lmbedcrypto -lmbedx509 $(LOCAL_LDFLAGS) $(LDFLAGS) -o $@ $(OBJS_TLS)
+	$(CC) -dynamiclib -o $@ $(OBJS_TLS) -L. -lmbedx509 -lmbedcrypto $(LOCAL_LDFLAGS) $(LDFLAGS)
 
 libmbedtls.dll: $(OBJS_TLS) libmbedx509.dll
 	echo "  LD    $@"
-	$(CC) -shared -Wl,-soname,$@ -Wl,--out-implib,$@.a -o $@ $(OBJS_TLS) -lws2_32 -lwinmm -lgdi32 -L. -lmbedcrypto -lmbedx509 -static-libgcc $(LOCAL_LDFLAGS) $(LDFLAGS)
+	$(CC) -shared -Wl,-soname,$@ -Wl,--out-implib,$@.a -o $@ $(OBJS_TLS) -lws2_32 -lwinmm -lgdi32 -L. -lmbedx509 -lmbedcrypto -static-libgcc $(LOCAL_LDFLAGS) $(LDFLAGS)
 
 # x509
 libmbedx509.a: $(OBJS_X509)
@@ -220,7 +238,7 @@
 
 libmbedx509.$(SOEXT_X509): $(OBJS_X509) libmbedcrypto.so
 	echo "  LD    $@"
-	$(CC) -shared -Wl,-soname,$@ -L. -lmbedcrypto $(LOCAL_LDFLAGS) $(LDFLAGS) -o $@ $(OBJS_X509)
+	$(CC) -shared -Wl,-soname,$@ -o $@ $(OBJS_X509) -L. -lmbedcrypto $(LOCAL_LDFLAGS) $(LDFLAGS)
 
 libmbedx509.so: libmbedx509.$(SOEXT_X509)
 	echo "  LN    $@ -> $<"
@@ -228,7 +246,7 @@
 
 libmbedx509.dylib: $(OBJS_X509) libmbedcrypto.dylib
 	echo "  LD    $@"
-	$(CC) -dynamiclib -L. -lmbedcrypto  $(LOCAL_LDFLAGS) $(LDFLAGS) -o $@ $(OBJS_X509)
+	$(CC) -dynamiclib -o $@ $(OBJS_X509) -L. -lmbedcrypto  $(LOCAL_LDFLAGS) $(LDFLAGS)
 
 libmbedx509.dll: $(OBJS_X509) libmbedcrypto.dll
 	echo "  LD    $@"
@@ -247,7 +265,7 @@
 
 libmbedcrypto.$(SOEXT_CRYPTO): $(OBJS_CRYPTO)
 	echo "  LD    $@"
-	$(CC) -shared -Wl,-soname,$@ $(LOCAL_LDFLAGS) $(LDFLAGS) -o $@ $(OBJS_CRYPTO)
+	$(CC) -shared -Wl,-soname,$@ -o $@ $(OBJS_CRYPTO) $(LOCAL_LDFLAGS) $(LDFLAGS)
 
 libmbedcrypto.so: libmbedcrypto.$(SOEXT_CRYPTO)
 	echo "  LN    $@ -> $<"
@@ -255,7 +273,7 @@
 
 libmbedcrypto.dylib: $(OBJS_CRYPTO)
 	echo "  LD    $@"
-	$(CC) -dynamiclib $(LOCAL_LDFLAGS) $(LDFLAGS) -o $@ $(OBJS_CRYPTO)
+	$(CC) -dynamiclib -o $@ $(OBJS_CRYPTO) $(LOCAL_LDFLAGS) $(LDFLAGS)
 
 libmbedcrypto.dll: $(OBJS_CRYPTO)
 	echo "  LD    $@"
diff --git a/third_party/mbedtls/repo/library/aes.c b/third_party/mbedtls/repo/library/aes.c
index 3f61642..31824e7 100644
--- a/third_party/mbedtls/repo/library/aes.c
+++ b/third_party/mbedtls/repo/library/aes.c
@@ -57,29 +57,6 @@
 #define AES_VALIDATE( cond )        \
     MBEDTLS_INTERNAL_VALIDATE( cond )
 
-/*
- * 32-bit integer manipulation macros (little endian)
- */
-#ifndef GET_UINT32_LE
-#define GET_UINT32_LE(n,b,i)                            \
-{                                                       \
-    (n) = ( (uint32_t) (b)[(i)    ]       )             \
-        | ( (uint32_t) (b)[(i) + 1] <<  8 )             \
-        | ( (uint32_t) (b)[(i) + 2] << 16 )             \
-        | ( (uint32_t) (b)[(i) + 3] << 24 );            \
-}
-#endif
-
-#ifndef PUT_UINT32_LE
-#define PUT_UINT32_LE(n,b,i)                                    \
-{                                                               \
-    (b)[(i)    ] = (unsigned char) ( ( (n)       ) & 0xFF );    \
-    (b)[(i) + 1] = (unsigned char) ( ( (n) >>  8 ) & 0xFF );    \
-    (b)[(i) + 2] = (unsigned char) ( ( (n) >> 16 ) & 0xFF );    \
-    (b)[(i) + 3] = (unsigned char) ( ( (n) >> 24 ) & 0xFF );    \
-}
-#endif
-
 #if defined(MBEDTLS_PADLOCK_C) &&                      \
     ( defined(MBEDTLS_HAVE_X86) || defined(MBEDTLS_PADLOCK_ALIGN16) )
 static int aes_padlock_ace = -1;
@@ -409,7 +386,7 @@
     {
         pow[i] = x;
         log[x] = i;
-        x = ( x ^ XTIME( x ) ) & 0xFF;
+        x = MBEDTLS_BYTE_0( x ^ XTIME( x ) );
     }
 
     /*
@@ -418,7 +395,7 @@
     for( i = 0, x = 1; i < 10; i++ )
     {
         RCON[i] = (uint32_t) x;
-        x = XTIME( x ) & 0xFF;
+        x = MBEDTLS_BYTE_0( XTIME( x ) );
     }
 
     /*
@@ -431,10 +408,10 @@
     {
         x = pow[255 - log[i]];
 
-        y  = x; y = ( ( y << 1 ) | ( y >> 7 ) ) & 0xFF;
-        x ^= y; y = ( ( y << 1 ) | ( y >> 7 ) ) & 0xFF;
-        x ^= y; y = ( ( y << 1 ) | ( y >> 7 ) ) & 0xFF;
-        x ^= y; y = ( ( y << 1 ) | ( y >> 7 ) ) & 0xFF;
+        y  = x; y = MBEDTLS_BYTE_0( ( y << 1 ) | ( y >> 7 ) );
+        x ^= y; y = MBEDTLS_BYTE_0( ( y << 1 ) | ( y >> 7 ) );
+        x ^= y; y = MBEDTLS_BYTE_0( ( y << 1 ) | ( y >> 7 ) );
+        x ^= y; y = MBEDTLS_BYTE_0( ( y << 1 ) | ( y >> 7 ) );
         x ^= y ^ 0x63;
 
         FSb[i] = (unsigned char) x;
@@ -447,8 +424,8 @@
     for( i = 0; i < 256; i++ )
     {
         x = FSb[i];
-        y = XTIME( x ) & 0xFF;
-        z =  ( y ^ x ) & 0xFF;
+        y = MBEDTLS_BYTE_0( XTIME( x ) );
+        z = MBEDTLS_BYTE_0( y ^ x );
 
         FT0[i] = ( (uint32_t) y       ) ^
                  ( (uint32_t) x <<  8 ) ^
@@ -590,7 +567,7 @@
 
     for( i = 0; i < ( keybits >> 5 ); i++ )
     {
-        GET_UINT32_LE( RK[i], key, i << 2 );
+        RK[i] = MBEDTLS_GET_UINT32_LE( key, i << 2 );
     }
 
     switch( ctx->nr )
@@ -600,10 +577,10 @@
             for( i = 0; i < 10; i++, RK += 4 )
             {
                 RK[4]  = RK[0] ^ RCON[i] ^
-                ( (uint32_t) FSb[ ( RK[3] >>  8 ) & 0xFF ]       ) ^
-                ( (uint32_t) FSb[ ( RK[3] >> 16 ) & 0xFF ] <<  8 ) ^
-                ( (uint32_t) FSb[ ( RK[3] >> 24 ) & 0xFF ] << 16 ) ^
-                ( (uint32_t) FSb[ ( RK[3]       ) & 0xFF ] << 24 );
+                ( (uint32_t) FSb[ MBEDTLS_BYTE_1( RK[3] ) ]       ) ^
+                ( (uint32_t) FSb[ MBEDTLS_BYTE_2( RK[3] ) ] <<  8 ) ^
+                ( (uint32_t) FSb[ MBEDTLS_BYTE_3( RK[3] ) ] << 16 ) ^
+                ( (uint32_t) FSb[ MBEDTLS_BYTE_0( RK[3] ) ] << 24 );
 
                 RK[5]  = RK[1] ^ RK[4];
                 RK[6]  = RK[2] ^ RK[5];
@@ -616,10 +593,10 @@
             for( i = 0; i < 8; i++, RK += 6 )
             {
                 RK[6]  = RK[0] ^ RCON[i] ^
-                ( (uint32_t) FSb[ ( RK[5] >>  8 ) & 0xFF ]       ) ^
-                ( (uint32_t) FSb[ ( RK[5] >> 16 ) & 0xFF ] <<  8 ) ^
-                ( (uint32_t) FSb[ ( RK[5] >> 24 ) & 0xFF ] << 16 ) ^
-                ( (uint32_t) FSb[ ( RK[5]       ) & 0xFF ] << 24 );
+                ( (uint32_t) FSb[ MBEDTLS_BYTE_1( RK[5] ) ]       ) ^
+                ( (uint32_t) FSb[ MBEDTLS_BYTE_2( RK[5] ) ] <<  8 ) ^
+                ( (uint32_t) FSb[ MBEDTLS_BYTE_3( RK[5] ) ] << 16 ) ^
+                ( (uint32_t) FSb[ MBEDTLS_BYTE_0( RK[5] ) ] << 24 );
 
                 RK[7]  = RK[1] ^ RK[6];
                 RK[8]  = RK[2] ^ RK[7];
@@ -634,20 +611,20 @@
             for( i = 0; i < 7; i++, RK += 8 )
             {
                 RK[8]  = RK[0] ^ RCON[i] ^
-                ( (uint32_t) FSb[ ( RK[7] >>  8 ) & 0xFF ]       ) ^
-                ( (uint32_t) FSb[ ( RK[7] >> 16 ) & 0xFF ] <<  8 ) ^
-                ( (uint32_t) FSb[ ( RK[7] >> 24 ) & 0xFF ] << 16 ) ^
-                ( (uint32_t) FSb[ ( RK[7]       ) & 0xFF ] << 24 );
+                ( (uint32_t) FSb[ MBEDTLS_BYTE_1( RK[7] ) ]       ) ^
+                ( (uint32_t) FSb[ MBEDTLS_BYTE_2( RK[7] ) ] <<  8 ) ^
+                ( (uint32_t) FSb[ MBEDTLS_BYTE_3( RK[7] ) ] << 16 ) ^
+                ( (uint32_t) FSb[ MBEDTLS_BYTE_0( RK[7] ) ] << 24 );
 
                 RK[9]  = RK[1] ^ RK[8];
                 RK[10] = RK[2] ^ RK[9];
                 RK[11] = RK[3] ^ RK[10];
 
                 RK[12] = RK[4] ^
-                ( (uint32_t) FSb[ ( RK[11]       ) & 0xFF ]       ) ^
-                ( (uint32_t) FSb[ ( RK[11] >>  8 ) & 0xFF ] <<  8 ) ^
-                ( (uint32_t) FSb[ ( RK[11] >> 16 ) & 0xFF ] << 16 ) ^
-                ( (uint32_t) FSb[ ( RK[11] >> 24 ) & 0xFF ] << 24 );
+                ( (uint32_t) FSb[ MBEDTLS_BYTE_0( RK[11] ) ]       ) ^
+                ( (uint32_t) FSb[ MBEDTLS_BYTE_1( RK[11] ) ] <<  8 ) ^
+                ( (uint32_t) FSb[ MBEDTLS_BYTE_2( RK[11] ) ] << 16 ) ^
+                ( (uint32_t) FSb[ MBEDTLS_BYTE_3( RK[11] ) ] << 24 );
 
                 RK[13] = RK[5] ^ RK[12];
                 RK[14] = RK[6] ^ RK[13];
@@ -713,10 +690,10 @@
     {
         for( j = 0; j < 4; j++, SK++ )
         {
-            *RK++ = AES_RT0( FSb[ ( *SK       ) & 0xFF ] ) ^
-                    AES_RT1( FSb[ ( *SK >>  8 ) & 0xFF ] ) ^
-                    AES_RT2( FSb[ ( *SK >> 16 ) & 0xFF ] ) ^
-                    AES_RT3( FSb[ ( *SK >> 24 ) & 0xFF ] );
+            *RK++ = AES_RT0( FSb[ MBEDTLS_BYTE_0( *SK ) ] ) ^
+                    AES_RT1( FSb[ MBEDTLS_BYTE_1( *SK ) ] ) ^
+                    AES_RT2( FSb[ MBEDTLS_BYTE_2( *SK ) ] ) ^
+                    AES_RT3( FSb[ MBEDTLS_BYTE_3( *SK ) ] );
         }
     }
 
@@ -809,52 +786,52 @@
 }
 #endif /* MBEDTLS_CIPHER_MODE_XTS */
 
-#define AES_FROUND(X0,X1,X2,X3,Y0,Y1,Y2,Y3)                     \
-    do                                                          \
-    {                                                           \
-        (X0) = *RK++ ^ AES_FT0( ( (Y0)       ) & 0xFF ) ^       \
-                       AES_FT1( ( (Y1) >>  8 ) & 0xFF ) ^       \
-                       AES_FT2( ( (Y2) >> 16 ) & 0xFF ) ^       \
-                       AES_FT3( ( (Y3) >> 24 ) & 0xFF );        \
-                                                                \
-        (X1) = *RK++ ^ AES_FT0( ( (Y1)       ) & 0xFF ) ^       \
-                       AES_FT1( ( (Y2) >>  8 ) & 0xFF ) ^       \
-                       AES_FT2( ( (Y3) >> 16 ) & 0xFF ) ^       \
-                       AES_FT3( ( (Y0) >> 24 ) & 0xFF );        \
-                                                                \
-        (X2) = *RK++ ^ AES_FT0( ( (Y2)       ) & 0xFF ) ^       \
-                       AES_FT1( ( (Y3) >>  8 ) & 0xFF ) ^       \
-                       AES_FT2( ( (Y0) >> 16 ) & 0xFF ) ^       \
-                       AES_FT3( ( (Y1) >> 24 ) & 0xFF );        \
-                                                                \
-        (X3) = *RK++ ^ AES_FT0( ( (Y3)       ) & 0xFF ) ^       \
-                       AES_FT1( ( (Y0) >>  8 ) & 0xFF ) ^       \
-                       AES_FT2( ( (Y1) >> 16 ) & 0xFF ) ^       \
-                       AES_FT3( ( (Y2) >> 24 ) & 0xFF );        \
+#define AES_FROUND(X0,X1,X2,X3,Y0,Y1,Y2,Y3)                 \
+    do                                                      \
+    {                                                       \
+        (X0) = *RK++ ^ AES_FT0( MBEDTLS_BYTE_0( Y0 ) ) ^    \
+                       AES_FT1( MBEDTLS_BYTE_1( Y1 ) ) ^    \
+                       AES_FT2( MBEDTLS_BYTE_2( Y2 ) ) ^    \
+                       AES_FT3( MBEDTLS_BYTE_3( Y3 ) );     \
+                                                            \
+        (X1) = *RK++ ^ AES_FT0( MBEDTLS_BYTE_0( Y1 ) ) ^    \
+                       AES_FT1( MBEDTLS_BYTE_1( Y2 ) ) ^    \
+                       AES_FT2( MBEDTLS_BYTE_2( Y3 ) ) ^    \
+                       AES_FT3( MBEDTLS_BYTE_3( Y0 ) );     \
+                                                            \
+        (X2) = *RK++ ^ AES_FT0( MBEDTLS_BYTE_0( Y2 ) ) ^    \
+                       AES_FT1( MBEDTLS_BYTE_1( Y3 ) ) ^    \
+                       AES_FT2( MBEDTLS_BYTE_2( Y0 ) ) ^    \
+                       AES_FT3( MBEDTLS_BYTE_3( Y1 ) );     \
+                                                            \
+        (X3) = *RK++ ^ AES_FT0( MBEDTLS_BYTE_0( Y3 ) ) ^    \
+                       AES_FT1( MBEDTLS_BYTE_1( Y0 ) ) ^    \
+                       AES_FT2( MBEDTLS_BYTE_2( Y1 ) ) ^    \
+                       AES_FT3( MBEDTLS_BYTE_3( Y2 ) );     \
     } while( 0 )
 
 #define AES_RROUND(X0,X1,X2,X3,Y0,Y1,Y2,Y3)                 \
     do                                                      \
     {                                                       \
-        (X0) = *RK++ ^ AES_RT0( ( (Y0)       ) & 0xFF ) ^   \
-                       AES_RT1( ( (Y3) >>  8 ) & 0xFF ) ^   \
-                       AES_RT2( ( (Y2) >> 16 ) & 0xFF ) ^   \
-                       AES_RT3( ( (Y1) >> 24 ) & 0xFF );    \
+        (X0) = *RK++ ^ AES_RT0( MBEDTLS_BYTE_0( Y0 ) ) ^    \
+                       AES_RT1( MBEDTLS_BYTE_1( Y3 ) ) ^    \
+                       AES_RT2( MBEDTLS_BYTE_2( Y2 ) ) ^    \
+                       AES_RT3( MBEDTLS_BYTE_3( Y1 ) );     \
                                                             \
-        (X1) = *RK++ ^ AES_RT0( ( (Y1)       ) & 0xFF ) ^   \
-                       AES_RT1( ( (Y0) >>  8 ) & 0xFF ) ^   \
-                       AES_RT2( ( (Y3) >> 16 ) & 0xFF ) ^   \
-                       AES_RT3( ( (Y2) >> 24 ) & 0xFF );    \
+        (X1) = *RK++ ^ AES_RT0( MBEDTLS_BYTE_0( Y1 ) ) ^    \
+                       AES_RT1( MBEDTLS_BYTE_1( Y0 ) ) ^    \
+                       AES_RT2( MBEDTLS_BYTE_2( Y3 ) ) ^    \
+                       AES_RT3( MBEDTLS_BYTE_3( Y2 ) );     \
                                                             \
-        (X2) = *RK++ ^ AES_RT0( ( (Y2)       ) & 0xFF ) ^   \
-                       AES_RT1( ( (Y1) >>  8 ) & 0xFF ) ^   \
-                       AES_RT2( ( (Y0) >> 16 ) & 0xFF ) ^   \
-                       AES_RT3( ( (Y3) >> 24 ) & 0xFF );    \
+        (X2) = *RK++ ^ AES_RT0( MBEDTLS_BYTE_0( Y2 ) ) ^    \
+                       AES_RT1( MBEDTLS_BYTE_1( Y1 ) ) ^    \
+                       AES_RT2( MBEDTLS_BYTE_2( Y0 ) ) ^    \
+                       AES_RT3( MBEDTLS_BYTE_3( Y3 ) );     \
                                                             \
-        (X3) = *RK++ ^ AES_RT0( ( (Y3)       ) & 0xFF ) ^   \
-                       AES_RT1( ( (Y2) >>  8 ) & 0xFF ) ^   \
-                       AES_RT2( ( (Y1) >> 16 ) & 0xFF ) ^   \
-                       AES_RT3( ( (Y0) >> 24 ) & 0xFF );    \
+        (X3) = *RK++ ^ AES_RT0( MBEDTLS_BYTE_0( Y3 ) ) ^    \
+                       AES_RT1( MBEDTLS_BYTE_1( Y2 ) ) ^    \
+                       AES_RT2( MBEDTLS_BYTE_2( Y1 ) ) ^    \
+                       AES_RT3( MBEDTLS_BYTE_3( Y0 ) );     \
     } while( 0 )
 
 /*
@@ -873,10 +850,10 @@
         uint32_t Y[4];
     } t;
 
-    GET_UINT32_LE( t.X[0], input,  0 ); t.X[0] ^= *RK++;
-    GET_UINT32_LE( t.X[1], input,  4 ); t.X[1] ^= *RK++;
-    GET_UINT32_LE( t.X[2], input,  8 ); t.X[2] ^= *RK++;
-    GET_UINT32_LE( t.X[3], input, 12 ); t.X[3] ^= *RK++;
+    t.X[0] = MBEDTLS_GET_UINT32_LE( input,  0 ); t.X[0] ^= *RK++;
+    t.X[1] = MBEDTLS_GET_UINT32_LE( input,  4 ); t.X[1] ^= *RK++;
+    t.X[2] = MBEDTLS_GET_UINT32_LE( input,  8 ); t.X[2] ^= *RK++;
+    t.X[3] = MBEDTLS_GET_UINT32_LE( input, 12 ); t.X[3] ^= *RK++;
 
     for( i = ( ctx->nr >> 1 ) - 1; i > 0; i-- )
     {
@@ -887,33 +864,33 @@
     AES_FROUND( t.Y[0], t.Y[1], t.Y[2], t.Y[3], t.X[0], t.X[1], t.X[2], t.X[3] );
 
     t.X[0] = *RK++ ^ \
-            ( (uint32_t) FSb[ ( t.Y[0]       ) & 0xFF ]       ) ^
-            ( (uint32_t) FSb[ ( t.Y[1] >>  8 ) & 0xFF ] <<  8 ) ^
-            ( (uint32_t) FSb[ ( t.Y[2] >> 16 ) & 0xFF ] << 16 ) ^
-            ( (uint32_t) FSb[ ( t.Y[3] >> 24 ) & 0xFF ] << 24 );
+            ( (uint32_t) FSb[ MBEDTLS_BYTE_0( t.Y[0] ) ]       ) ^
+            ( (uint32_t) FSb[ MBEDTLS_BYTE_1( t.Y[1] ) ] <<  8 ) ^
+            ( (uint32_t) FSb[ MBEDTLS_BYTE_2( t.Y[2] ) ] << 16 ) ^
+            ( (uint32_t) FSb[ MBEDTLS_BYTE_3( t.Y[3] ) ] << 24 );
 
     t.X[1] = *RK++ ^ \
-            ( (uint32_t) FSb[ ( t.Y[1]       ) & 0xFF ]       ) ^
-            ( (uint32_t) FSb[ ( t.Y[2] >>  8 ) & 0xFF ] <<  8 ) ^
-            ( (uint32_t) FSb[ ( t.Y[3] >> 16 ) & 0xFF ] << 16 ) ^
-            ( (uint32_t) FSb[ ( t.Y[0] >> 24 ) & 0xFF ] << 24 );
+            ( (uint32_t) FSb[ MBEDTLS_BYTE_0( t.Y[1] ) ]       ) ^
+            ( (uint32_t) FSb[ MBEDTLS_BYTE_1( t.Y[2] ) ] <<  8 ) ^
+            ( (uint32_t) FSb[ MBEDTLS_BYTE_2( t.Y[3] ) ] << 16 ) ^
+            ( (uint32_t) FSb[ MBEDTLS_BYTE_3( t.Y[0] ) ] << 24 );
 
     t.X[2] = *RK++ ^ \
-            ( (uint32_t) FSb[ ( t.Y[2]       ) & 0xFF ]       ) ^
-            ( (uint32_t) FSb[ ( t.Y[3] >>  8 ) & 0xFF ] <<  8 ) ^
-            ( (uint32_t) FSb[ ( t.Y[0] >> 16 ) & 0xFF ] << 16 ) ^
-            ( (uint32_t) FSb[ ( t.Y[1] >> 24 ) & 0xFF ] << 24 );
+            ( (uint32_t) FSb[ MBEDTLS_BYTE_0( t.Y[2] ) ]       ) ^
+            ( (uint32_t) FSb[ MBEDTLS_BYTE_1( t.Y[3] ) ] <<  8 ) ^
+            ( (uint32_t) FSb[ MBEDTLS_BYTE_2( t.Y[0] ) ] << 16 ) ^
+            ( (uint32_t) FSb[ MBEDTLS_BYTE_3( t.Y[1] ) ] << 24 );
 
     t.X[3] = *RK++ ^ \
-            ( (uint32_t) FSb[ ( t.Y[3]       ) & 0xFF ]       ) ^
-            ( (uint32_t) FSb[ ( t.Y[0] >>  8 ) & 0xFF ] <<  8 ) ^
-            ( (uint32_t) FSb[ ( t.Y[1] >> 16 ) & 0xFF ] << 16 ) ^
-            ( (uint32_t) FSb[ ( t.Y[2] >> 24 ) & 0xFF ] << 24 );
+            ( (uint32_t) FSb[ MBEDTLS_BYTE_0( t.Y[3] ) ]       ) ^
+            ( (uint32_t) FSb[ MBEDTLS_BYTE_1( t.Y[0] ) ] <<  8 ) ^
+            ( (uint32_t) FSb[ MBEDTLS_BYTE_2( t.Y[1] ) ] << 16 ) ^
+            ( (uint32_t) FSb[ MBEDTLS_BYTE_3( t.Y[2] ) ] << 24 );
 
-    PUT_UINT32_LE( t.X[0], output,  0 );
-    PUT_UINT32_LE( t.X[1], output,  4 );
-    PUT_UINT32_LE( t.X[2], output,  8 );
-    PUT_UINT32_LE( t.X[3], output, 12 );
+    MBEDTLS_PUT_UINT32_LE( t.X[0], output,  0 );
+    MBEDTLS_PUT_UINT32_LE( t.X[1], output,  4 );
+    MBEDTLS_PUT_UINT32_LE( t.X[2], output,  8 );
+    MBEDTLS_PUT_UINT32_LE( t.X[3], output, 12 );
 
     mbedtls_platform_zeroize( &t, sizeof( t ) );
 
@@ -926,7 +903,7 @@
                           const unsigned char input[16],
                           unsigned char output[16] )
 {
-    mbedtls_internal_aes_encrypt( ctx, input, output );
+    MBEDTLS_IGNORE_RETURN( mbedtls_internal_aes_encrypt( ctx, input, output ) );
 }
 #endif /* !MBEDTLS_DEPRECATED_REMOVED */
 
@@ -946,10 +923,10 @@
         uint32_t Y[4];
     } t;
 
-    GET_UINT32_LE( t.X[0], input,  0 ); t.X[0] ^= *RK++;
-    GET_UINT32_LE( t.X[1], input,  4 ); t.X[1] ^= *RK++;
-    GET_UINT32_LE( t.X[2], input,  8 ); t.X[2] ^= *RK++;
-    GET_UINT32_LE( t.X[3], input, 12 ); t.X[3] ^= *RK++;
+    t.X[0] = MBEDTLS_GET_UINT32_LE( input,  0 ); t.X[0] ^= *RK++;
+    t.X[1] = MBEDTLS_GET_UINT32_LE( input,  4 ); t.X[1] ^= *RK++;
+    t.X[2] = MBEDTLS_GET_UINT32_LE( input,  8 ); t.X[2] ^= *RK++;
+    t.X[3] = MBEDTLS_GET_UINT32_LE( input, 12 ); t.X[3] ^= *RK++;
 
     for( i = ( ctx->nr >> 1 ) - 1; i > 0; i-- )
     {
@@ -960,33 +937,33 @@
     AES_RROUND( t.Y[0], t.Y[1], t.Y[2], t.Y[3], t.X[0], t.X[1], t.X[2], t.X[3] );
 
     t.X[0] = *RK++ ^ \
-            ( (uint32_t) RSb[ ( t.Y[0]       ) & 0xFF ]       ) ^
-            ( (uint32_t) RSb[ ( t.Y[3] >>  8 ) & 0xFF ] <<  8 ) ^
-            ( (uint32_t) RSb[ ( t.Y[2] >> 16 ) & 0xFF ] << 16 ) ^
-            ( (uint32_t) RSb[ ( t.Y[1] >> 24 ) & 0xFF ] << 24 );
+            ( (uint32_t) RSb[ MBEDTLS_BYTE_0( t.Y[0] ) ]       ) ^
+            ( (uint32_t) RSb[ MBEDTLS_BYTE_1( t.Y[3] ) ] <<  8 ) ^
+            ( (uint32_t) RSb[ MBEDTLS_BYTE_2( t.Y[2] ) ] << 16 ) ^
+            ( (uint32_t) RSb[ MBEDTLS_BYTE_3( t.Y[1] ) ] << 24 );
 
     t.X[1] = *RK++ ^ \
-            ( (uint32_t) RSb[ ( t.Y[1]       ) & 0xFF ]       ) ^
-            ( (uint32_t) RSb[ ( t.Y[0] >>  8 ) & 0xFF ] <<  8 ) ^
-            ( (uint32_t) RSb[ ( t.Y[3] >> 16 ) & 0xFF ] << 16 ) ^
-            ( (uint32_t) RSb[ ( t.Y[2] >> 24 ) & 0xFF ] << 24 );
+            ( (uint32_t) RSb[ MBEDTLS_BYTE_0( t.Y[1] ) ]       ) ^
+            ( (uint32_t) RSb[ MBEDTLS_BYTE_1( t.Y[0] ) ] <<  8 ) ^
+            ( (uint32_t) RSb[ MBEDTLS_BYTE_2( t.Y[3] ) ] << 16 ) ^
+            ( (uint32_t) RSb[ MBEDTLS_BYTE_3( t.Y[2] ) ] << 24 );
 
     t.X[2] = *RK++ ^ \
-            ( (uint32_t) RSb[ ( t.Y[2]       ) & 0xFF ]       ) ^
-            ( (uint32_t) RSb[ ( t.Y[1] >>  8 ) & 0xFF ] <<  8 ) ^
-            ( (uint32_t) RSb[ ( t.Y[0] >> 16 ) & 0xFF ] << 16 ) ^
-            ( (uint32_t) RSb[ ( t.Y[3] >> 24 ) & 0xFF ] << 24 );
+            ( (uint32_t) RSb[ MBEDTLS_BYTE_0( t.Y[2] ) ]       ) ^
+            ( (uint32_t) RSb[ MBEDTLS_BYTE_1( t.Y[1] ) ] <<  8 ) ^
+            ( (uint32_t) RSb[ MBEDTLS_BYTE_2( t.Y[0] ) ] << 16 ) ^
+            ( (uint32_t) RSb[ MBEDTLS_BYTE_3( t.Y[3] ) ] << 24 );
 
     t.X[3] = *RK++ ^ \
-            ( (uint32_t) RSb[ ( t.Y[3]       ) & 0xFF ]       ) ^
-            ( (uint32_t) RSb[ ( t.Y[2] >>  8 ) & 0xFF ] <<  8 ) ^
-            ( (uint32_t) RSb[ ( t.Y[1] >> 16 ) & 0xFF ] << 16 ) ^
-            ( (uint32_t) RSb[ ( t.Y[0] >> 24 ) & 0xFF ] << 24 );
+            ( (uint32_t) RSb[ MBEDTLS_BYTE_0( t.Y[3] ) ]       ) ^
+            ( (uint32_t) RSb[ MBEDTLS_BYTE_1( t.Y[2] ) ] <<  8 ) ^
+            ( (uint32_t) RSb[ MBEDTLS_BYTE_2( t.Y[1] ) ] << 16 ) ^
+            ( (uint32_t) RSb[ MBEDTLS_BYTE_3( t.Y[0] ) ] << 24 );
 
-    PUT_UINT32_LE( t.X[0], output,  0 );
-    PUT_UINT32_LE( t.X[1], output,  4 );
-    PUT_UINT32_LE( t.X[2], output,  8 );
-    PUT_UINT32_LE( t.X[3], output, 12 );
+    MBEDTLS_PUT_UINT32_LE( t.X[0], output,  0 );
+    MBEDTLS_PUT_UINT32_LE( t.X[1], output,  4 );
+    MBEDTLS_PUT_UINT32_LE( t.X[2], output,  8 );
+    MBEDTLS_PUT_UINT32_LE( t.X[3], output, 12 );
 
     mbedtls_platform_zeroize( &t, sizeof( t ) );
 
@@ -999,7 +976,7 @@
                           const unsigned char input[16],
                           unsigned char output[16] )
 {
-    mbedtls_internal_aes_decrypt( ctx, input, output );
+    MBEDTLS_IGNORE_RETURN( mbedtls_internal_aes_decrypt( ctx, input, output ) );
 }
 #endif /* !MBEDTLS_DEPRECATED_REMOVED */
 
@@ -1052,6 +1029,7 @@
                     unsigned char *output )
 {
     int i;
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
     unsigned char temp[16];
 
     AES_VALIDATE_RET( ctx != NULL );
@@ -1081,7 +1059,9 @@
         while( length > 0 )
         {
             memcpy( temp, input, 16 );
-            mbedtls_aes_crypt_ecb( ctx, mode, input, output );
+            ret = mbedtls_aes_crypt_ecb( ctx, mode, input, output );
+            if( ret != 0 )
+                goto exit;
 
             for( i = 0; i < 16; i++ )
                 output[i] = (unsigned char)( output[i] ^ iv[i] );
@@ -1100,7 +1080,9 @@
             for( i = 0; i < 16; i++ )
                 output[i] = (unsigned char)( input[i] ^ iv[i] );
 
-            mbedtls_aes_crypt_ecb( ctx, mode, output, output );
+            ret = mbedtls_aes_crypt_ecb( ctx, mode, output, output );
+            if( ret != 0 )
+                goto exit;
             memcpy( iv, output, 16 );
 
             input  += 16;
@@ -1108,42 +1090,15 @@
             length -= 16;
         }
     }
+    ret = 0;
 
-    return( 0 );
+exit:
+    return( ret );
 }
 #endif /* MBEDTLS_CIPHER_MODE_CBC */
 
 #if defined(MBEDTLS_CIPHER_MODE_XTS)
 
-/* Endianess with 64 bits values */
-#ifndef GET_UINT64_LE
-#define GET_UINT64_LE(n,b,i)                            \
-{                                                       \
-    (n) = ( (uint64_t) (b)[(i) + 7] << 56 )             \
-        | ( (uint64_t) (b)[(i) + 6] << 48 )             \
-        | ( (uint64_t) (b)[(i) + 5] << 40 )             \
-        | ( (uint64_t) (b)[(i) + 4] << 32 )             \
-        | ( (uint64_t) (b)[(i) + 3] << 24 )             \
-        | ( (uint64_t) (b)[(i) + 2] << 16 )             \
-        | ( (uint64_t) (b)[(i) + 1] <<  8 )             \
-        | ( (uint64_t) (b)[(i)    ]       );            \
-}
-#endif
-
-#ifndef PUT_UINT64_LE
-#define PUT_UINT64_LE(n,b,i)                            \
-{                                                       \
-    (b)[(i) + 7] = (unsigned char) ( (n) >> 56 );       \
-    (b)[(i) + 6] = (unsigned char) ( (n) >> 48 );       \
-    (b)[(i) + 5] = (unsigned char) ( (n) >> 40 );       \
-    (b)[(i) + 4] = (unsigned char) ( (n) >> 32 );       \
-    (b)[(i) + 3] = (unsigned char) ( (n) >> 24 );       \
-    (b)[(i) + 2] = (unsigned char) ( (n) >> 16 );       \
-    (b)[(i) + 1] = (unsigned char) ( (n) >>  8 );       \
-    (b)[(i)    ] = (unsigned char) ( (n)       );       \
-}
-#endif
-
 typedef unsigned char mbedtls_be128[16];
 
 /*
@@ -1159,14 +1114,14 @@
 {
     uint64_t a, b, ra, rb;
 
-    GET_UINT64_LE( a, x, 0 );
-    GET_UINT64_LE( b, x, 8 );
+    a = MBEDTLS_GET_UINT64_LE( x, 0 );
+    b = MBEDTLS_GET_UINT64_LE( x, 8 );
 
     ra = ( a << 1 )  ^ 0x0087 >> ( 8 - ( ( b >> 63 ) << 3 ) );
     rb = ( a >> 63 ) | ( b << 1 );
 
-    PUT_UINT64_LE( ra, r, 0 );
-    PUT_UINT64_LE( rb, r, 8 );
+    MBEDTLS_PUT_UINT64_LE( ra, r, 0 );
+    MBEDTLS_PUT_UINT64_LE( rb, r, 8 );
 }
 
 /*
@@ -1292,6 +1247,7 @@
                        unsigned char *output )
 {
     int c;
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
     size_t n;
 
     AES_VALIDATE_RET( ctx != NULL );
@@ -1312,7 +1268,11 @@
         while( length-- )
         {
             if( n == 0 )
-                mbedtls_aes_crypt_ecb( ctx, MBEDTLS_AES_ENCRYPT, iv, iv );
+            {
+                ret = mbedtls_aes_crypt_ecb( ctx, MBEDTLS_AES_ENCRYPT, iv, iv );
+                if( ret != 0 )
+                    goto exit;
+            }
 
             c = *input++;
             *output++ = (unsigned char)( c ^ iv[n] );
@@ -1326,7 +1286,11 @@
         while( length-- )
         {
             if( n == 0 )
-                mbedtls_aes_crypt_ecb( ctx, MBEDTLS_AES_ENCRYPT, iv, iv );
+            {
+                ret = mbedtls_aes_crypt_ecb( ctx, MBEDTLS_AES_ENCRYPT, iv, iv );
+                if( ret != 0 )
+                    goto exit;
+            }
 
             iv[n] = *output++ = (unsigned char)( iv[n] ^ *input++ );
 
@@ -1335,8 +1299,10 @@
     }
 
     *iv_off = n;
+    ret = 0;
 
-    return( 0 );
+exit:
+    return( ret );
 }
 
 /*
@@ -1349,6 +1315,7 @@
                             const unsigned char *input,
                             unsigned char *output )
 {
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
     unsigned char c;
     unsigned char ov[17];
 
@@ -1361,7 +1328,9 @@
     while( length-- )
     {
         memcpy( ov, iv, 16 );
-        mbedtls_aes_crypt_ecb( ctx, MBEDTLS_AES_ENCRYPT, iv, iv );
+        ret = mbedtls_aes_crypt_ecb( ctx, MBEDTLS_AES_ENCRYPT, iv, iv );
+        if( ret != 0 )
+            goto exit;
 
         if( mode == MBEDTLS_AES_DECRYPT )
             ov[16] = *input;
@@ -1373,8 +1342,10 @@
 
         memcpy( iv, ov + 1, 16 );
     }
+    ret = 0;
 
-    return( 0 );
+exit:
+    return( ret );
 }
 #endif /* MBEDTLS_CIPHER_MODE_CFB */
 
@@ -1436,6 +1407,7 @@
                        unsigned char *output )
 {
     int c, i;
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
     size_t n;
 
     AES_VALIDATE_RET( ctx != NULL );
@@ -1453,7 +1425,9 @@
     while( length-- )
     {
         if( n == 0 ) {
-            mbedtls_aes_crypt_ecb( ctx, MBEDTLS_AES_ENCRYPT, nonce_counter, stream_block );
+            ret = mbedtls_aes_crypt_ecb( ctx, MBEDTLS_AES_ENCRYPT, nonce_counter, stream_block );
+            if( ret != 0 )
+                goto exit;
 
             for( i = 16; i > 0; i-- )
                 if( ++nonce_counter[i - 1] != 0 )
@@ -1466,8 +1440,10 @@
     }
 
     *nc_off = n;
+    ret = 0;
 
-    return( 0 );
+exit:
+    return( ret );
 }
 #endif /* MBEDTLS_CIPHER_MODE_CTR */
 
diff --git a/third_party/mbedtls/repo/library/aria.c b/third_party/mbedtls/repo/library/aria.c
index 1875635..bc05c4a 100644
--- a/third_party/mbedtls/repo/library/aria.c
+++ b/third_party/mbedtls/repo/library/aria.c
@@ -56,29 +56,6 @@
     MBEDTLS_INTERNAL_VALIDATE( cond )
 
 /*
- * 32-bit integer manipulation macros (little endian)
- */
-#ifndef GET_UINT32_LE
-#define GET_UINT32_LE( n, b, i )                \
-{                                               \
-    (n) = ( (uint32_t) (b)[(i)    ]       )     \
-        | ( (uint32_t) (b)[(i) + 1] <<  8 )     \
-        | ( (uint32_t) (b)[(i) + 2] << 16 )     \
-        | ( (uint32_t) (b)[(i) + 3] << 24 );    \
-}
-#endif
-
-#ifndef PUT_UINT32_LE
-#define PUT_UINT32_LE( n, b, i )                                \
-{                                                               \
-    (b)[(i)    ] = (unsigned char) ( ( (n)       ) & 0xFF );    \
-    (b)[(i) + 1] = (unsigned char) ( ( (n) >>  8 ) & 0xFF );    \
-    (b)[(i) + 2] = (unsigned char) ( ( (n) >> 16 ) & 0xFF );    \
-    (b)[(i) + 3] = (unsigned char) ( ( (n) >> 24 ) & 0xFF );    \
-}
-#endif
-
-/*
  * modify byte order: ( A B C D ) -> ( B A D C ), i.e. swap pairs of bytes
  *
  * This is submatrix P1 in [1] Appendix B.1
@@ -235,22 +212,22 @@
                             const uint8_t sa[256], const uint8_t sb[256],
                             const uint8_t sc[256], const uint8_t sd[256] )
 {
-    *a = ( (uint32_t) sa[ *a        & 0xFF]       ) ^
-         (((uint32_t) sb[(*a >>  8) & 0xFF]) <<  8) ^
-         (((uint32_t) sc[(*a >> 16) & 0xFF]) << 16) ^
-         (((uint32_t) sd[ *a >> 24        ]) << 24);
-    *b = ( (uint32_t) sa[ *b        & 0xFF]       ) ^
-         (((uint32_t) sb[(*b >>  8) & 0xFF]) <<  8) ^
-         (((uint32_t) sc[(*b >> 16) & 0xFF]) << 16) ^
-         (((uint32_t) sd[ *b >> 24        ]) << 24);
-    *c = ( (uint32_t) sa[ *c        & 0xFF]       ) ^
-         (((uint32_t) sb[(*c >>  8) & 0xFF]) <<  8) ^
-         (((uint32_t) sc[(*c >> 16) & 0xFF]) << 16) ^
-         (((uint32_t) sd[ *c >> 24        ]) << 24);
-    *d = ( (uint32_t) sa[ *d        & 0xFF]       ) ^
-         (((uint32_t) sb[(*d >>  8) & 0xFF]) <<  8) ^
-         (((uint32_t) sc[(*d >> 16) & 0xFF]) << 16) ^
-         (((uint32_t) sd[ *d >> 24        ]) << 24);
+    *a = ( (uint32_t) sa[ MBEDTLS_BYTE_0( *a ) ]       ) ^
+         (((uint32_t) sb[ MBEDTLS_BYTE_1( *a ) ]) <<  8) ^
+         (((uint32_t) sc[ MBEDTLS_BYTE_2( *a ) ]) << 16) ^
+         (((uint32_t) sd[ MBEDTLS_BYTE_3( *a ) ]) << 24);
+    *b = ( (uint32_t) sa[ MBEDTLS_BYTE_0( *b ) ]       ) ^
+         (((uint32_t) sb[ MBEDTLS_BYTE_1( *b ) ]) <<  8) ^
+         (((uint32_t) sc[ MBEDTLS_BYTE_2( *b ) ]) << 16) ^
+         (((uint32_t) sd[ MBEDTLS_BYTE_3( *b ) ]) << 24);
+    *c = ( (uint32_t) sa[ MBEDTLS_BYTE_0( *c ) ]       ) ^
+         (((uint32_t) sb[ MBEDTLS_BYTE_1( *c ) ]) <<  8) ^
+         (((uint32_t) sc[ MBEDTLS_BYTE_2( *c ) ]) << 16) ^
+         (((uint32_t) sd[ MBEDTLS_BYTE_3( *c ) ]) << 24);
+    *d = ( (uint32_t) sa[ MBEDTLS_BYTE_0( *d ) ]       ) ^
+         (((uint32_t) sb[ MBEDTLS_BYTE_1( *d ) ]) <<  8) ^
+         (((uint32_t) sc[ MBEDTLS_BYTE_2( *d ) ]) << 16) ^
+         (((uint32_t) sd[ MBEDTLS_BYTE_3( *d ) ]) << 24);
 }
 
 /*
@@ -408,7 +385,8 @@
  * Big endian 128-bit rotation: r = a ^ (b <<< n), used only in key setup.
  *
  * We chose to store bytes into 32-bit words in little-endian format (see
- * GET/PUT_UINT32_LE) so we need to reverse bytes here.
+ * MBEDTLS_GET_UINT32_LE / MBEDTLS_PUT_UINT32_LE ) so we need to reverse
+ * bytes here.
  */
 static void aria_rot128( uint32_t r[4], const uint32_t a[4],
                          const uint32_t b[4], uint8_t n )
@@ -456,21 +434,21 @@
         return( MBEDTLS_ERR_ARIA_BAD_INPUT_DATA );
 
     /* Copy key to W0 (and potential remainder to W1) */
-    GET_UINT32_LE( w[0][0], key,  0 );
-    GET_UINT32_LE( w[0][1], key,  4 );
-    GET_UINT32_LE( w[0][2], key,  8 );
-    GET_UINT32_LE( w[0][3], key, 12 );
+    w[0][0] = MBEDTLS_GET_UINT32_LE( key,  0 );
+    w[0][1] = MBEDTLS_GET_UINT32_LE( key,  4 );
+    w[0][2] = MBEDTLS_GET_UINT32_LE( key,  8 );
+    w[0][3] = MBEDTLS_GET_UINT32_LE( key, 12 );
 
     memset( w[1], 0, 16 );
     if( keybits >= 192 )
     {
-        GET_UINT32_LE( w[1][0], key, 16 );  // 192 bit key
-        GET_UINT32_LE( w[1][1], key, 20 );
+        w[1][0] = MBEDTLS_GET_UINT32_LE( key, 16 );  // 192 bit key
+        w[1][1] = MBEDTLS_GET_UINT32_LE( key, 20 );
     }
     if( keybits == 256 )
     {
-        GET_UINT32_LE( w[1][2], key, 24 );  // 256 bit key
-        GET_UINT32_LE( w[1][3], key, 28 );
+        w[1][2] = MBEDTLS_GET_UINT32_LE( key, 24 );  // 256 bit key
+        w[1][3] = MBEDTLS_GET_UINT32_LE( key, 28 );
     }
 
     i = ( keybits - 128 ) >> 6;             // index: 0, 1, 2
@@ -547,10 +525,10 @@
     ARIA_VALIDATE_RET( input != NULL );
     ARIA_VALIDATE_RET( output != NULL );
 
-    GET_UINT32_LE( a, input,  0 );
-    GET_UINT32_LE( b, input,  4 );
-    GET_UINT32_LE( c, input,  8 );
-    GET_UINT32_LE( d, input, 12 );
+    a = MBEDTLS_GET_UINT32_LE( input,  0 );
+    b = MBEDTLS_GET_UINT32_LE( input,  4 );
+    c = MBEDTLS_GET_UINT32_LE( input,  8 );
+    d = MBEDTLS_GET_UINT32_LE( input, 12 );
 
     i = 0;
     while( 1 )
@@ -582,10 +560,10 @@
     c ^= ctx->rk[i][2];
     d ^= ctx->rk[i][3];
 
-    PUT_UINT32_LE( a, output,  0 );
-    PUT_UINT32_LE( b, output,  4 );
-    PUT_UINT32_LE( c, output,  8 );
-    PUT_UINT32_LE( d, output, 12 );
+    MBEDTLS_PUT_UINT32_LE( a, output,  0 );
+    MBEDTLS_PUT_UINT32_LE( b, output,  4 );
+    MBEDTLS_PUT_UINT32_LE( c, output,  8 );
+    MBEDTLS_PUT_UINT32_LE( d, output, 12 );
 
     return( 0 );
 }
@@ -921,7 +899,7 @@
         {                                   \
             if( verbose )                   \
                 mbedtls_printf( "failed\n" );       \
-            return( 1 );                    \
+            goto exit;                              \
         } else {                            \
             if( verbose )                   \
                 mbedtls_printf( "passed\n" );       \
@@ -935,6 +913,7 @@
     int i;
     uint8_t blk[MBEDTLS_ARIA_BLOCKSIZE];
     mbedtls_aria_context ctx;
+    int ret = 1;
 
 #if (defined(MBEDTLS_CIPHER_MODE_CFB) || defined(MBEDTLS_CIPHER_MODE_CTR))
     size_t j;
@@ -946,6 +925,8 @@
     uint8_t buf[48], iv[MBEDTLS_ARIA_BLOCKSIZE];
 #endif
 
+    mbedtls_aria_init( &ctx );
+
     /*
      * Test set 1
      */
@@ -1065,7 +1046,11 @@
         mbedtls_printf( "\n" );
 #endif /* MBEDTLS_CIPHER_MODE_CTR */
 
-    return( 0 );
+    ret = 0;
+
+exit:
+    mbedtls_aria_free( &ctx );
+    return( ret );
 }
 
 #endif /* MBEDTLS_SELF_TEST */
diff --git a/third_party/mbedtls/repo/library/asn1write.c b/third_party/mbedtls/repo/library/asn1write.c
index deb1a2f..3811ef2 100644
--- a/third_party/mbedtls/repo/library/asn1write.c
+++ b/third_party/mbedtls/repo/library/asn1write.c
@@ -60,8 +60,8 @@
         if( *p - start < 3 )
             return( MBEDTLS_ERR_ASN1_BUF_TOO_SMALL );
 
-        *--(*p) = ( len       ) & 0xFF;
-        *--(*p) = ( len >>  8 ) & 0xFF;
+        *--(*p) = MBEDTLS_BYTE_0( len );
+        *--(*p) = MBEDTLS_BYTE_1( len );
         *--(*p) = 0x82;
         return( 3 );
     }
@@ -71,9 +71,9 @@
         if( *p - start < 4 )
             return( MBEDTLS_ERR_ASN1_BUF_TOO_SMALL );
 
-        *--(*p) = ( len       ) & 0xFF;
-        *--(*p) = ( len >>  8 ) & 0xFF;
-        *--(*p) = ( len >> 16 ) & 0xFF;
+        *--(*p) = MBEDTLS_BYTE_0( len );
+        *--(*p) = MBEDTLS_BYTE_1( len );
+        *--(*p) = MBEDTLS_BYTE_2( len );
         *--(*p) = 0x83;
         return( 4 );
     }
@@ -85,10 +85,10 @@
         if( *p - start < 5 )
             return( MBEDTLS_ERR_ASN1_BUF_TOO_SMALL );
 
-        *--(*p) = ( len       ) & 0xFF;
-        *--(*p) = ( len >>  8 ) & 0xFF;
-        *--(*p) = ( len >> 16 ) & 0xFF;
-        *--(*p) = ( len >> 24 ) & 0xFF;
+        *--(*p) = MBEDTLS_BYTE_0( len );
+        *--(*p) = MBEDTLS_BYTE_1( len );
+        *--(*p) = MBEDTLS_BYTE_2( len );
+        *--(*p) = MBEDTLS_BYTE_3( len );
         *--(*p) = 0x84;
         return( 5 );
     }
diff --git a/third_party/mbedtls/repo/library/base64.c b/third_party/mbedtls/repo/library/base64.c
index d39474a..83daa0b 100644
--- a/third_party/mbedtls/repo/library/base64.c
+++ b/third_party/mbedtls/repo/library/base64.c
@@ -22,6 +22,7 @@
 #if defined(MBEDTLS_BASE64_C)
 
 #include "mbedtls/base64.h"
+#include "constant_time_internal.h"
 
 #include <stdint.h>
 
@@ -35,34 +36,6 @@
 #endif /* MBEDTLS_PLATFORM_C */
 #endif /* MBEDTLS_SELF_TEST */
 
-static const unsigned char base64_enc_map[64] =
-{
-    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
-    'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
-    'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
-    'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
-    'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
-    'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
-    '8', '9', '+', '/'
-};
-
-static const unsigned char base64_dec_map[128] =
-{
-    127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
-    127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
-    127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
-    127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
-    127, 127, 127,  62, 127, 127, 127,  63,  52,  53,
-     54,  55,  56,  57,  58,  59,  60,  61, 127, 127,
-    127,  64, 127, 127, 127,   0,   1,   2,   3,   4,
-      5,   6,   7,   8,   9,  10,  11,  12,  13,  14,
-     15,  16,  17,  18,  19,  20,  21,  22,  23,  24,
-     25, 127, 127, 127, 127, 127, 127,  26,  27,  28,
-     29,  30,  31,  32,  33,  34,  35,  36,  37,  38,
-     39,  40,  41,  42,  43,  44,  45,  46,  47,  48,
-     49,  50,  51, 127, 127, 127, 127, 127
-};
-
 #define BASE64_SIZE_T_MAX   ( (size_t) -1 ) /* SIZE_T_MAX is not standard */
 
 /*
@@ -105,10 +78,12 @@
         C2 = *src++;
         C3 = *src++;
 
-        *p++ = base64_enc_map[(C1 >> 2) & 0x3F];
-        *p++ = base64_enc_map[(((C1 &  3) << 4) + (C2 >> 4)) & 0x3F];
-        *p++ = base64_enc_map[(((C2 & 15) << 2) + (C3 >> 6)) & 0x3F];
-        *p++ = base64_enc_map[C3 & 0x3F];
+        *p++ = mbedtls_ct_base64_enc_char( ( C1 >> 2 ) & 0x3F );
+        *p++ = mbedtls_ct_base64_enc_char( ( ( ( C1 &  3 ) << 4 ) + ( C2 >> 4 ) )
+                                        & 0x3F );
+        *p++ = mbedtls_ct_base64_enc_char( ( ( ( C2 & 15 ) << 2 ) + ( C3 >> 6 ) )
+                                        & 0x3F );
+        *p++ = mbedtls_ct_base64_enc_char( C3 & 0x3F );
     }
 
     if( i < slen )
@@ -116,11 +91,12 @@
         C1 = *src++;
         C2 = ( ( i + 1 ) < slen ) ? *src++ : 0;
 
-        *p++ = base64_enc_map[(C1 >> 2) & 0x3F];
-        *p++ = base64_enc_map[(((C1 & 3) << 4) + (C2 >> 4)) & 0x3F];
+        *p++ = mbedtls_ct_base64_enc_char( ( C1 >> 2 ) & 0x3F );
+        *p++ = mbedtls_ct_base64_enc_char( ( ( ( C1 & 3 ) << 4 ) + ( C2 >> 4 ) )
+                                        & 0x3F );
 
         if( ( i + 1 ) < slen )
-             *p++ = base64_enc_map[((C2 & 15) << 2) & 0x3F];
+             *p++ = mbedtls_ct_base64_enc_char( ( ( C2 & 15 ) << 2 ) & 0x3F );
         else *p++ = '=';
 
         *p++ = '=';
@@ -138,19 +114,23 @@
 int mbedtls_base64_decode( unsigned char *dst, size_t dlen, size_t *olen,
                    const unsigned char *src, size_t slen )
 {
-    size_t i, n;
-    uint32_t j, x;
+    size_t i; /* index in source */
+    size_t n; /* number of digits or trailing = in source */
+    uint32_t x; /* value accumulator */
+    unsigned accumulated_digits = 0;
+    unsigned equals = 0;
+    int spaces_present = 0;
     unsigned char *p;
 
     /* First pass: check for validity and get output length */
-    for( i = n = j = 0; i < slen; i++ )
+    for( i = n = 0; i < slen; i++ )
     {
         /* Skip spaces before checking for EOL */
-        x = 0;
+        spaces_present = 0;
         while( i < slen && src[i] == ' ' )
         {
             ++i;
-            ++x;
+            spaces_present = 1;
         }
 
         /* Spaces at end of buffer are OK */
@@ -165,18 +145,24 @@
             continue;
 
         /* Space inside a line is an error */
-        if( x != 0 )
+        if( spaces_present )
             return( MBEDTLS_ERR_BASE64_INVALID_CHARACTER );
 
-        if( src[i] == '=' && ++j > 2 )
+        if( src[i] > 127 )
             return( MBEDTLS_ERR_BASE64_INVALID_CHARACTER );
 
-        if( src[i] > 127 || base64_dec_map[src[i]] == 127 )
-            return( MBEDTLS_ERR_BASE64_INVALID_CHARACTER );
-
-        if( base64_dec_map[src[i]] < 64 && j != 0 )
-            return( MBEDTLS_ERR_BASE64_INVALID_CHARACTER );
-
+        if( src[i] == '=' )
+        {
+            if( ++equals > 2 )
+                return( MBEDTLS_ERR_BASE64_INVALID_CHARACTER );
+        }
+        else
+        {
+            if( equals != 0 )
+                return( MBEDTLS_ERR_BASE64_INVALID_CHARACTER );
+            if( mbedtls_ct_base64_dec_value( src[i] ) < 0 )
+                return( MBEDTLS_ERR_BASE64_INVALID_CHARACTER );
+        }
         n++;
     }
 
@@ -191,7 +177,7 @@
      *     n = ( ( n * 6 ) + 7 ) >> 3;
      */
     n = ( 6 * ( n >> 3 ) ) + ( ( 6 * ( n & 0x7 ) + 7 ) >> 3 );
-    n -= j;
+    n -= equals;
 
     if( dst == NULL || dlen < n )
     {
@@ -199,20 +185,24 @@
         return( MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL );
     }
 
-   for( j = 3, n = x = 0, p = dst; i > 0; i--, src++ )
-   {
+    equals = 0;
+    for( x = 0, p = dst; i > 0; i--, src++ )
+    {
         if( *src == '\r' || *src == '\n' || *src == ' ' )
             continue;
 
-        j -= ( base64_dec_map[*src] == 64 );
-        x  = ( x << 6 ) | ( base64_dec_map[*src] & 0x3F );
+        x = x << 6;
+        if( *src == '=' )
+            ++equals;
+        else
+            x |= mbedtls_ct_base64_dec_value( *src );
 
-        if( ++n == 4 )
+        if( ++accumulated_digits == 4 )
         {
-            n = 0;
-            if( j > 0 ) *p++ = (unsigned char)( x >> 16 );
-            if( j > 1 ) *p++ = (unsigned char)( x >>  8 );
-            if( j > 2 ) *p++ = (unsigned char)( x       );
+            accumulated_digits = 0;
+            *p++ = MBEDTLS_BYTE_2( x );
+            if( equals <= 1 ) *p++ = MBEDTLS_BYTE_1( x );
+            if( equals <= 0 ) *p++ = MBEDTLS_BYTE_0( x );
         }
     }
 
diff --git a/third_party/mbedtls/repo/library/bignum.c b/third_party/mbedtls/repo/library/bignum.c
index 9cc5d66..62e7f76 100644
--- a/third_party/mbedtls/repo/library/bignum.c
+++ b/third_party/mbedtls/repo/library/bignum.c
@@ -41,7 +41,9 @@
 #include "mbedtls/bn_mul.h"
 #include "mbedtls/platform_util.h"
 #include "mbedtls/error.h"
+#include "constant_time_internal.h"
 
+#include <limits.h>
 #include <string.h>
 
 #if defined(MBEDTLS_PLATFORM_C)
@@ -181,8 +183,35 @@
     return( 0 );
 }
 
+/* Resize X to have exactly n limbs and set it to 0. */
+static int mbedtls_mpi_resize_clear( mbedtls_mpi *X, size_t limbs )
+{
+    if( limbs == 0 )
+    {
+        mbedtls_mpi_free( X );
+        return( 0 );
+    }
+    else if( X->n == limbs )
+    {
+        memset( X->p, 0, limbs * ciL );
+        X->s = 1;
+        return( 0 );
+    }
+    else
+    {
+        mbedtls_mpi_free( X );
+        return( mbedtls_mpi_grow( X, limbs ) );
+    }
+}
+
 /*
- * Copy the contents of Y into X
+ * Copy the contents of Y into X.
+ *
+ * This function is not constant-time. Leading zeros in Y may be removed.
+ *
+ * Ensure that X does not shrink. This is not guaranteed by the public API,
+ * but some code in the bignum module relies on this property, for example
+ * in mbedtls_mpi_exp_mod().
  */
 int mbedtls_mpi_copy( mbedtls_mpi *X, const mbedtls_mpi *Y )
 {
@@ -196,7 +225,11 @@
 
     if( Y->n == 0 )
     {
-        mbedtls_mpi_free( X );
+        if( X->n != 0 )
+        {
+            X->s = 1;
+            memset( X->p, 0, X->n * ciL );
+        }
         return( 0 );
     }
 
@@ -238,89 +271,6 @@
 }
 
 /*
- * Conditionally assign dest = src, without leaking information
- * about whether the assignment was made or not.
- * dest and src must be arrays of limbs of size n.
- * assign must be 0 or 1.
- */
-static void mpi_safe_cond_assign( size_t n,
-                                  mbedtls_mpi_uint *dest,
-                                  const mbedtls_mpi_uint *src,
-                                  unsigned char assign )
-{
-    size_t i;
-    for( i = 0; i < n; i++ )
-        dest[i] = dest[i] * ( 1 - assign ) + src[i] * assign;
-}
-
-/*
- * Conditionally assign X = Y, without leaking information
- * about whether the assignment was made or not.
- * (Leaking information about the respective sizes of X and Y is ok however.)
- */
-int mbedtls_mpi_safe_cond_assign( mbedtls_mpi *X, const mbedtls_mpi *Y, unsigned char assign )
-{
-    int ret = 0;
-    size_t i;
-    MPI_VALIDATE_RET( X != NULL );
-    MPI_VALIDATE_RET( Y != NULL );
-
-    /* make sure assign is 0 or 1 in a time-constant manner */
-    assign = (assign | (unsigned char)-assign) >> 7;
-
-    MBEDTLS_MPI_CHK( mbedtls_mpi_grow( X, Y->n ) );
-
-    X->s = X->s * ( 1 - assign ) + Y->s * assign;
-
-    mpi_safe_cond_assign( Y->n, X->p, Y->p, assign );
-
-    for( i = Y->n; i < X->n; i++ )
-        X->p[i] *= ( 1 - assign );
-
-cleanup:
-    return( ret );
-}
-
-/*
- * Conditionally swap X and Y, without leaking information
- * about whether the swap was made or not.
- * Here it is not ok to simply swap the pointers, which whould lead to
- * different memory access patterns when X and Y are used afterwards.
- */
-int mbedtls_mpi_safe_cond_swap( mbedtls_mpi *X, mbedtls_mpi *Y, unsigned char swap )
-{
-    int ret, s;
-    size_t i;
-    mbedtls_mpi_uint tmp;
-    MPI_VALIDATE_RET( X != NULL );
-    MPI_VALIDATE_RET( Y != NULL );
-
-    if( X == Y )
-        return( 0 );
-
-    /* make sure swap is 0 or 1 in a time-constant manner */
-    swap = (swap | (unsigned char)-swap) >> 7;
-
-    MBEDTLS_MPI_CHK( mbedtls_mpi_grow( X, Y->n ) );
-    MBEDTLS_MPI_CHK( mbedtls_mpi_grow( Y, X->n ) );
-
-    s = X->s;
-    X->s = X->s * ( 1 - swap ) + Y->s * swap;
-    Y->s = Y->s * ( 1 - swap ) +    s * swap;
-
-
-    for( i = 0; i < X->n; i++ )
-    {
-        tmp = X->p[i];
-        X->p[i] = X->p[i] * ( 1 - swap ) + Y->p[i] * swap;
-        Y->p[i] = Y->p[i] * ( 1 - swap ) +     tmp * swap;
-    }
-
-cleanup:
-    return( ret );
-}
-
-/*
  * Set value from integer
  */
 int mbedtls_mpi_lset( mbedtls_mpi *X, mbedtls_mpi_sint z )
@@ -470,6 +420,7 @@
 {
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
     size_t i, j, slen, n;
+    int sign = 1;
     mbedtls_mpi_uint d;
     mbedtls_mpi T;
     MPI_VALIDATE_RET( X != NULL );
@@ -480,6 +431,18 @@
 
     mbedtls_mpi_init( &T );
 
+    if( s[0] == 0 )
+    {
+        mbedtls_mpi_free( X );
+        return( 0 );
+    }
+
+    if( s[0] == '-' )
+    {
+        ++s;
+        sign = -1;
+    }
+
     slen = strlen( s );
 
     if( radix == 16 )
@@ -494,12 +457,6 @@
 
         for( i = slen, j = 0; i > 0; i--, j++ )
         {
-            if( i == 1 && s[i - 1] == '-' )
-            {
-                X->s = -1;
-                break;
-            }
-
             MBEDTLS_MPI_CHK( mpi_get_digit( &d, radix, s[i - 1] ) );
             X->p[j / ( 2 * ciL )] |= d << ( ( j % ( 2 * ciL ) ) << 2 );
         }
@@ -510,26 +467,15 @@
 
         for( i = 0; i < slen; i++ )
         {
-            if( i == 0 && s[i] == '-' )
-            {
-                X->s = -1;
-                continue;
-            }
-
             MBEDTLS_MPI_CHK( mpi_get_digit( &d, radix, s[i] ) );
             MBEDTLS_MPI_CHK( mbedtls_mpi_mul_int( &T, X, radix ) );
-
-            if( X->s == 1 )
-            {
-                MBEDTLS_MPI_CHK( mbedtls_mpi_add_int( X, &T, d ) );
-            }
-            else
-            {
-                MBEDTLS_MPI_CHK( mbedtls_mpi_sub_int( X, &T, d ) );
-            }
+            MBEDTLS_MPI_CHK( mbedtls_mpi_add_int( X, &T, d ) );
         }
     }
 
+    if( sign < 0 && mbedtls_mpi_bitlen( X ) != 0 )
+        X->s = -1;
+
 cleanup:
 
     mbedtls_mpi_free( &T );
@@ -848,14 +794,7 @@
     size_t const limbs = CHARS_TO_LIMBS( buflen );
 
     /* Ensure that target MPI has exactly the necessary number of limbs */
-    if( X->n != limbs )
-    {
-        mbedtls_mpi_free( X );
-        mbedtls_mpi_init( X );
-        MBEDTLS_MPI_CHK( mbedtls_mpi_grow( X, limbs ) );
-    }
-
-    MBEDTLS_MPI_CHK( mbedtls_mpi_lset( X, 0 ) );
+    MBEDTLS_MPI_CHK( mbedtls_mpi_resize_clear( X, limbs ) );
 
     for( i = 0; i < buflen; i++ )
         X->p[i / ciL] |= ((mbedtls_mpi_uint) buf[i]) << ((i % ciL) << 3);
@@ -884,17 +823,11 @@
     MPI_VALIDATE_RET( buflen == 0 || buf != NULL );
 
     /* Ensure that target MPI has exactly the necessary number of limbs */
-    if( X->n != limbs )
-    {
-        mbedtls_mpi_free( X );
-        mbedtls_mpi_init( X );
-        MBEDTLS_MPI_CHK( mbedtls_mpi_grow( X, limbs ) );
-    }
-    MBEDTLS_MPI_CHK( mbedtls_mpi_lset( X, 0 ) );
+    MBEDTLS_MPI_CHK( mbedtls_mpi_resize_clear( X, limbs ) );
 
-    /* Avoid calling `memcpy` with NULL source argument,
+    /* Avoid calling `memcpy` with NULL source or destination argument,
      * even if buflen is 0. */
-    if( buf != NULL )
+    if( buflen != 0 )
     {
         Xp = (unsigned char*) X->p;
         memcpy( Xp + overhead, buf, buflen );
@@ -1159,107 +1092,6 @@
     return( 0 );
 }
 
-/** Decide if an integer is less than the other, without branches.
- *
- * \param x         First integer.
- * \param y         Second integer.
- *
- * \return          1 if \p x is less than \p y, 0 otherwise
- */
-static unsigned ct_lt_mpi_uint( const mbedtls_mpi_uint x,
-        const mbedtls_mpi_uint y )
-{
-    mbedtls_mpi_uint ret;
-    mbedtls_mpi_uint cond;
-
-    /*
-     * Check if the most significant bits (MSB) of the operands are different.
-     */
-    cond = ( x ^ y );
-    /*
-     * If the MSB are the same then the difference x-y will be negative (and
-     * have its MSB set to 1 during conversion to unsigned) if and only if x<y.
-     */
-    ret = ( x - y ) & ~cond;
-    /*
-     * If the MSB are different, then the operand with the MSB of 1 is the
-     * bigger. (That is if y has MSB of 1, then x<y is true and it is false if
-     * the MSB of y is 0.)
-     */
-    ret |= y & cond;
-
-
-    ret = ret >> ( biL - 1 );
-
-    return (unsigned) ret;
-}
-
-/*
- * Compare signed values in constant time
- */
-int mbedtls_mpi_lt_mpi_ct( const mbedtls_mpi *X, const mbedtls_mpi *Y,
-        unsigned *ret )
-{
-    size_t i;
-    /* The value of any of these variables is either 0 or 1 at all times. */
-    unsigned cond, done, X_is_negative, Y_is_negative;
-
-    MPI_VALIDATE_RET( X != NULL );
-    MPI_VALIDATE_RET( Y != NULL );
-    MPI_VALIDATE_RET( ret != NULL );
-
-    if( X->n != Y->n )
-        return MBEDTLS_ERR_MPI_BAD_INPUT_DATA;
-
-    /*
-     * Set sign_N to 1 if N >= 0, 0 if N < 0.
-     * We know that N->s == 1 if N >= 0 and N->s == -1 if N < 0.
-     */
-    X_is_negative = ( X->s & 2 ) >> 1;
-    Y_is_negative = ( Y->s & 2 ) >> 1;
-
-    /*
-     * If the signs are different, then the positive operand is the bigger.
-     * That is if X is negative (X_is_negative == 1), then X < Y is true and it
-     * is false if X is positive (X_is_negative == 0).
-     */
-    cond = ( X_is_negative ^ Y_is_negative );
-    *ret = cond & X_is_negative;
-
-    /*
-     * This is a constant-time function. We might have the result, but we still
-     * need to go through the loop. Record if we have the result already.
-     */
-    done = cond;
-
-    for( i = X->n; i > 0; i-- )
-    {
-        /*
-         * If Y->p[i - 1] < X->p[i - 1] then X < Y is true if and only if both
-         * X and Y are negative.
-         *
-         * Again even if we can make a decision, we just mark the result and
-         * the fact that we are done and continue looping.
-         */
-        cond = ct_lt_mpi_uint( Y->p[i - 1], X->p[i - 1] );
-        *ret |= cond & ( 1 - done ) & X_is_negative;
-        done |= cond;
-
-        /*
-         * If X->p[i - 1] < Y->p[i - 1] then X < Y is true if and only if both
-         * X and Y are positive.
-         *
-         * Again even if we can make a decision, we just mark the result and
-         * the fact that we are done and continue looping.
-         */
-        cond = ct_lt_mpi_uint( X->p[i - 1], Y->p[i - 1] );
-        *ret |= cond & ( 1 - done ) & ( 1 - X_is_negative );
-        done |= cond;
-    }
-
-    return( 0 );
-}
-
 /*
  * Compare signed values
  */
@@ -1339,29 +1171,32 @@
 /**
  * Helper for mbedtls_mpi subtraction.
  *
- * Calculate d - s where d and s have the same size.
+ * Calculate l - r where l and r have the same size.
  * This function operates modulo (2^ciL)^n and returns the carry
- * (1 if there was a wraparound, i.e. if `d < s`, and 0 otherwise).
+ * (1 if there was a wraparound, i.e. if `l < r`, and 0 otherwise).
  *
- * \param n             Number of limbs of \p d and \p s.
- * \param[in,out] d     On input, the left operand.
- *                      On output, the result of the subtraction:
- * \param[in] s         The right operand.
+ * d may be aliased to l or r.
  *
- * \return              1 if `d < s`.
- *                      0 if `d >= s`.
+ * \param n             Number of limbs of \p d, \p l and \p r.
+ * \param[out] d        The result of the subtraction.
+ * \param[in] l         The left operand.
+ * \param[in] r         The right operand.
+ *
+ * \return              1 if `l < r`.
+ *                      0 if `l >= r`.
  */
 static mbedtls_mpi_uint mpi_sub_hlp( size_t n,
                                      mbedtls_mpi_uint *d,
-                                     const mbedtls_mpi_uint *s )
+                                     const mbedtls_mpi_uint *l,
+                                     const mbedtls_mpi_uint *r )
 {
     size_t i;
-    mbedtls_mpi_uint c, z;
+    mbedtls_mpi_uint c = 0, t, z;
 
-    for( i = c = 0; i < n; i++, s++, d++ )
+    for( i = 0; i < n; i++ )
     {
-        z = ( *d <  c );     *d -=  c;
-        c = ( *d < *s ) + z; *d -= *s;
+        z = ( l[i] <  c );    t = l[i] - c;
+        c = ( t < r[i] ) + z; d[i] = t - r[i];
     }
 
     return( c );
@@ -1372,7 +1207,6 @@
  */
 int mbedtls_mpi_sub_abs( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B )
 {
-    mbedtls_mpi TB;
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
     size_t n;
     mbedtls_mpi_uint carry;
@@ -1380,29 +1214,27 @@
     MPI_VALIDATE_RET( A != NULL );
     MPI_VALIDATE_RET( B != NULL );
 
-    mbedtls_mpi_init( &TB );
-
-    if( X == B )
-    {
-        MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &TB, B ) );
-        B = &TB;
-    }
-
-    if( X != A )
-        MBEDTLS_MPI_CHK( mbedtls_mpi_copy( X, A ) );
-
-    /*
-     * X should always be positive as a result of unsigned subtractions.
-     */
-    X->s = 1;
-
-    ret = 0;
-
     for( n = B->n; n > 0; n-- )
         if( B->p[n - 1] != 0 )
             break;
+    if( n > A->n )
+    {
+        /* B >= (2^ciL)^n > A */
+        ret = MBEDTLS_ERR_MPI_NEGATIVE_VALUE;
+        goto cleanup;
+    }
 
-    carry = mpi_sub_hlp( n, X->p, B->p );
+    MBEDTLS_MPI_CHK( mbedtls_mpi_grow( X, A->n ) );
+
+    /* Set the high limbs of X to match A. Don't touch the lower limbs
+     * because X might be aliased to B, and we must not overwrite the
+     * significant digits of B. */
+    if( A->n > n )
+        memcpy( X->p + n, A->p + n, ( A->n - n ) * ciL );
+    if( X->n > A->n )
+        memset( X->p + A->n, 0, ( X->n - A->n ) * ciL );
+
+    carry = mpi_sub_hlp( n, X->p, A->p, B->p );
     if( carry != 0 )
     {
         /* Propagate the carry to the first nonzero limb of X. */
@@ -1418,10 +1250,10 @@
         --X->p[n];
     }
 
+    /* X should always be positive as a result of unsigned subtractions. */
+    X->s = 1;
+
 cleanup:
-
-    mbedtls_mpi_free( &TB );
-
     return( ret );
 }
 
@@ -1500,17 +1332,17 @@
  */
 int mbedtls_mpi_add_int( mbedtls_mpi *X, const mbedtls_mpi *A, mbedtls_mpi_sint b )
 {
-    mbedtls_mpi _B;
+    mbedtls_mpi B;
     mbedtls_mpi_uint p[1];
     MPI_VALIDATE_RET( X != NULL );
     MPI_VALIDATE_RET( A != NULL );
 
     p[0] = ( b < 0 ) ? -b : b;
-    _B.s = ( b < 0 ) ? -1 : 1;
-    _B.n = 1;
-    _B.p = p;
+    B.s = ( b < 0 ) ? -1 : 1;
+    B.n = 1;
+    B.p = p;
 
-    return( mbedtls_mpi_add_mpi( X, A, &_B ) );
+    return( mbedtls_mpi_add_mpi( X, A, &B ) );
 }
 
 /*
@@ -1518,21 +1350,34 @@
  */
 int mbedtls_mpi_sub_int( mbedtls_mpi *X, const mbedtls_mpi *A, mbedtls_mpi_sint b )
 {
-    mbedtls_mpi _B;
+    mbedtls_mpi B;
     mbedtls_mpi_uint p[1];
     MPI_VALIDATE_RET( X != NULL );
     MPI_VALIDATE_RET( A != NULL );
 
     p[0] = ( b < 0 ) ? -b : b;
-    _B.s = ( b < 0 ) ? -1 : 1;
-    _B.n = 1;
-    _B.p = p;
+    B.s = ( b < 0 ) ? -1 : 1;
+    B.n = 1;
+    B.p = p;
 
-    return( mbedtls_mpi_sub_mpi( X, A, &_B ) );
+    return( mbedtls_mpi_sub_mpi( X, A, &B ) );
 }
 
-/*
- * Helper for mbedtls_mpi multiplication
+/** Helper for mbedtls_mpi multiplication.
+ *
+ * Add \p b * \p s to \p d.
+ *
+ * \param i             The number of limbs of \p s.
+ * \param[in] s         A bignum to multiply, of size \p i.
+ *                      It may overlap with \p d, but only if
+ *                      \p d <= \p s.
+ *                      Its leading limb must not be \c 0.
+ * \param[in,out] d     The bignum to add to.
+ *                      It must be sufficiently large to store the
+ *                      result of the multiplication. This means
+ *                      \p i + 1 limbs if \p d[\p i - 1] started as 0 and \p b
+ *                      is not known a priori.
+ * \param b             A scalar to multiply.
  */
 static
 #if defined(__APPLE__) && defined(__arm__)
@@ -1542,7 +1387,10 @@
  */
 __attribute__ ((noinline))
 #endif
-void mpi_mul_hlp( size_t i, mbedtls_mpi_uint *s, mbedtls_mpi_uint *d, mbedtls_mpi_uint b )
+void mpi_mul_hlp( size_t i,
+                  const mbedtls_mpi_uint *s,
+                  mbedtls_mpi_uint *d,
+                  mbedtls_mpi_uint b )
 {
     mbedtls_mpi_uint c = 0, t = 0;
 
@@ -1597,10 +1445,10 @@
 
     t++;
 
-    do {
+    while( c != 0 )
+    {
         *d += c; c = ( *d < c ); d++;
     }
-    while( c != 0 );
 }
 
 /*
@@ -1611,6 +1459,7 @@
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
     size_t i, j;
     mbedtls_mpi TA, TB;
+    int result_is_zero = 0;
     MPI_VALIDATE_RET( X != NULL );
     MPI_VALIDATE_RET( A != NULL );
     MPI_VALIDATE_RET( B != NULL );
@@ -1623,10 +1472,14 @@
     for( i = A->n; i > 0; i-- )
         if( A->p[i - 1] != 0 )
             break;
+    if( i == 0 )
+        result_is_zero = 1;
 
     for( j = B->n; j > 0; j-- )
         if( B->p[j - 1] != 0 )
             break;
+    if( j == 0 )
+        result_is_zero = 1;
 
     MBEDTLS_MPI_CHK( mbedtls_mpi_grow( X, i + j ) );
     MBEDTLS_MPI_CHK( mbedtls_mpi_lset( X, 0 ) );
@@ -1634,7 +1487,14 @@
     for( ; j > 0; j-- )
         mpi_mul_hlp( i, A->p, X->p + j - 1, B->p[j - 1] );
 
-    X->s = A->s * B->s;
+    /* If the result is 0, we don't shortcut the operation, which reduces
+     * but does not eliminate side channels leaking the zero-ness. We do
+     * need to take care to set the sign bit properly since the library does
+     * not fully support an MPI object with a value of 0 and s == -1. */
+    if( result_is_zero )
+        X->s = 1;
+    else
+        X->s = A->s * B->s;
 
 cleanup:
 
@@ -1648,17 +1508,37 @@
  */
 int mbedtls_mpi_mul_int( mbedtls_mpi *X, const mbedtls_mpi *A, mbedtls_mpi_uint b )
 {
-    mbedtls_mpi _B;
-    mbedtls_mpi_uint p[1];
     MPI_VALIDATE_RET( X != NULL );
     MPI_VALIDATE_RET( A != NULL );
 
-    _B.s = 1;
-    _B.n = 1;
-    _B.p = p;
-    p[0] = b;
+    /* mpi_mul_hlp can't deal with a leading 0. */
+    size_t n = A->n;
+    while( n > 0 && A->p[n - 1] == 0 )
+        --n;
 
-    return( mbedtls_mpi_mul_mpi( X, A, &_B ) );
+    /* The general method below doesn't work if n==0 or b==0. By chance
+     * calculating the result is trivial in those cases. */
+    if( b == 0 || n == 0 )
+    {
+        return( mbedtls_mpi_lset( X, 0 ) );
+    }
+
+    /* Calculate A*b as A + A*(b-1) to take advantage of mpi_mul_hlp */
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+    /* In general, A * b requires 1 limb more than b. If
+     * A->p[n - 1] * b / b == A->p[n - 1], then A * b fits in the same
+     * number of limbs as A and the call to grow() is not required since
+     * copy() will take care of the growth if needed. However, experimentally,
+     * making the call to grow() unconditional causes slightly fewer
+     * calls to calloc() in ECP code, presumably because it reuses the
+     * same mpi for a while and this way the mpi is more likely to directly
+     * grow to its final size. */
+    MBEDTLS_MPI_CHK( mbedtls_mpi_grow( X, n + 1 ) );
+    MBEDTLS_MPI_CHK( mbedtls_mpi_copy( X, A ) );
+    mpi_mul_hlp( n, A->p, X->p, b - 1 );
+
+cleanup:
+    return( ret );
 }
 
 /*
@@ -1799,7 +1679,7 @@
 
     MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &Z, A->n + 2 ) );
     MBEDTLS_MPI_CHK( mbedtls_mpi_lset( &Z,  0 ) );
-    MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &T1, 2 ) );
+    MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &T1, A->n + 2 ) );
 
     k = mbedtls_mpi_bitlen( &Y ) % biL;
     if( k < biL - 1 )
@@ -1892,16 +1772,16 @@
                          const mbedtls_mpi *A,
                          mbedtls_mpi_sint b )
 {
-    mbedtls_mpi _B;
+    mbedtls_mpi B;
     mbedtls_mpi_uint p[1];
     MPI_VALIDATE_RET( A != NULL );
 
     p[0] = ( b < 0 ) ? -b : b;
-    _B.s = ( b < 0 ) ? -1 : 1;
-    _B.n = 1;
-    _B.p = p;
+    B.s = ( b < 0 ) ? -1 : 1;
+    B.n = 1;
+    B.p = p;
 
-    return( mbedtls_mpi_div_mpi( Q, R, A, &_B ) );
+    return( mbedtls_mpi_div_mpi( Q, R, A, &B ) );
 }
 
 /*
@@ -2065,14 +1945,14 @@
      * do the calculation without using conditional tests. */
     /* Set d to d0 + (2^biL)^n - N where d0 is the current value of d. */
     d[n] += 1;
-    d[n] -= mpi_sub_hlp( n, d, N->p );
+    d[n] -= mpi_sub_hlp( n, d, d, N->p );
     /* If d0 < N then d < (2^biL)^n
      * so d[n] == 0 and we want to keep A as it is.
      * If d0 >= N then d >= (2^biL)^n, and d <= (2^biL)^n + N < 2 * (2^biL)^n
      * so d[n] == 1 and we want to set A to the result of the subtraction
      * which is d - (2^biL)^n, i.e. the n least significant limbs of d.
      * This exactly corresponds to a conditional assignment. */
-    mpi_safe_cond_assign( n, A->p, d, (unsigned char) d[n] );
+    mbedtls_ct_mpi_uint_cond_assign( n, A->p, d, (unsigned char) d[n] );
 }
 
 /*
@@ -2092,19 +1972,48 @@
     mpi_montmul( A, &U, N, mm, T );
 }
 
+/**
+ * Select an MPI from a table without leaking the index.
+ *
+ * This is functionally equivalent to mbedtls_mpi_copy(R, T[idx]) except it
+ * reads the entire table in order to avoid leaking the value of idx to an
+ * attacker able to observe memory access patterns.
+ *
+ * \param[out] R        Where to write the selected MPI.
+ * \param[in] T         The table to read from.
+ * \param[in] T_size    The number of elements in the table.
+ * \param[in] idx       The index of the element to select;
+ *                      this must satisfy 0 <= idx < T_size.
+ *
+ * \return \c 0 on success, or a negative error code.
+ */
+static int mpi_select( mbedtls_mpi *R, const mbedtls_mpi *T, size_t T_size, size_t idx )
+{
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+
+    for( size_t i = 0; i < T_size; i++ )
+    {
+        MBEDTLS_MPI_CHK( mbedtls_mpi_safe_cond_assign( R, &T[i],
+                        (unsigned char) mbedtls_ct_size_bool_eq( i, idx ) ) );
+    }
+
+cleanup:
+    return( ret );
+}
+
 /*
  * Sliding-window exponentiation: X = A^E mod N  (HAC 14.85)
  */
 int mbedtls_mpi_exp_mod( mbedtls_mpi *X, const mbedtls_mpi *A,
                          const mbedtls_mpi *E, const mbedtls_mpi *N,
-                         mbedtls_mpi *_RR )
+                         mbedtls_mpi *prec_RR )
 {
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
     size_t wbits, wsize, one = 1;
     size_t i, j, nblimbs;
     size_t bufsize, nbits;
     mbedtls_mpi_uint ei, mm, state;
-    mbedtls_mpi RR, T, W[ 1 << MBEDTLS_MPI_WINDOW_SIZE ], Apos;
+    mbedtls_mpi RR, T, W[ 1 << MBEDTLS_MPI_WINDOW_SIZE ], WW, Apos;
     int neg;
 
     MPI_VALIDATE_RET( X != NULL );
@@ -2128,6 +2037,7 @@
     mpi_montg_init( &mm, N );
     mbedtls_mpi_init( &RR ); mbedtls_mpi_init( &T );
     mbedtls_mpi_init( &Apos );
+    mbedtls_mpi_init( &WW );
     memset( W, 0, sizeof( W ) );
 
     i = mbedtls_mpi_bitlen( E );
@@ -2141,6 +2051,11 @@
 #endif
 
     j = N->n + 1;
+    /* All W[i] and X must have at least N->n limbs for the mpi_montmul()
+     * and mpi_montred() calls later. Here we ensure that W[1] and X are
+     * large enough, and later we'll grow other W[i] to the same length.
+     * They must not be shrunk midway through this function!
+     */
     MBEDTLS_MPI_CHK( mbedtls_mpi_grow( X, j ) );
     MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &W[1],  j ) );
     MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &T, j * 2 ) );
@@ -2159,26 +2074,34 @@
     /*
      * If 1st call, pre-compute R^2 mod N
      */
-    if( _RR == NULL || _RR->p == NULL )
+    if( prec_RR == NULL || prec_RR->p == NULL )
     {
         MBEDTLS_MPI_CHK( mbedtls_mpi_lset( &RR, 1 ) );
         MBEDTLS_MPI_CHK( mbedtls_mpi_shift_l( &RR, N->n * 2 * biL ) );
         MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi( &RR, &RR, N ) );
 
-        if( _RR != NULL )
-            memcpy( _RR, &RR, sizeof( mbedtls_mpi ) );
+        if( prec_RR != NULL )
+            memcpy( prec_RR, &RR, sizeof( mbedtls_mpi ) );
     }
     else
-        memcpy( &RR, _RR, sizeof( mbedtls_mpi ) );
+        memcpy( &RR, prec_RR, sizeof( mbedtls_mpi ) );
 
     /*
      * W[1] = A * R^2 * R^-1 mod N = A * R mod N
      */
     if( mbedtls_mpi_cmp_mpi( A, N ) >= 0 )
+    {
         MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi( &W[1], A, N ) );
+        /* This should be a no-op because W[1] is already that large before
+         * mbedtls_mpi_mod_mpi(), but it's necessary to avoid an overflow
+         * in mpi_montmul() below, so let's make sure. */
+        MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &W[1], N->n + 1 ) );
+    }
     else
         MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &W[1], A ) );
 
+    /* Note that this is safe because W[1] always has at least N->n limbs
+     * (it grew above and was preserved by mbedtls_mpi_copy()). */
     mpi_montmul( &W[1], &RR, N, mm, &T );
 
     /*
@@ -2268,7 +2191,8 @@
             /*
              * X = X * W[wbits] R^-1 mod N
              */
-            mpi_montmul( X, &W[wbits], N, mm, &T );
+            MBEDTLS_MPI_CHK( mpi_select( &WW, W, (size_t) 1 << wsize, wbits ) );
+            mpi_montmul( X, &WW, N, mm, &T );
 
             state--;
             nbits = 0;
@@ -2306,8 +2230,9 @@
         mbedtls_mpi_free( &W[i] );
 
     mbedtls_mpi_free( &W[1] ); mbedtls_mpi_free( &T ); mbedtls_mpi_free( &Apos );
+    mbedtls_mpi_free( &WW );
 
-    if( _RR == NULL || _RR->p == NULL )
+    if( prec_RR == NULL || prec_RR->p == NULL )
         mbedtls_mpi_free( &RR );
 
     return( ret );
@@ -2334,19 +2259,67 @@
     lz = mbedtls_mpi_lsb( &TA );
     lzt = mbedtls_mpi_lsb( &TB );
 
+    /* The loop below gives the correct result when A==0 but not when B==0.
+     * So have a special case for B==0. Leverage the fact that we just
+     * calculated the lsb and lsb(B)==0 iff B is odd or 0 to make the test
+     * slightly more efficient than cmp_int(). */
+    if( lzt == 0 && mbedtls_mpi_get_bit( &TB, 0 ) == 0 )
+    {
+        ret = mbedtls_mpi_copy( G, A );
+        goto cleanup;
+    }
+
     if( lzt < lz )
         lz = lzt;
 
-    MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( &TA, lz ) );
-    MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( &TB, lz ) );
-
     TA.s = TB.s = 1;
 
+    /* We mostly follow the procedure described in HAC 14.54, but with some
+     * minor differences:
+     * - Sequences of multiplications or divisions by 2 are grouped into a
+     *   single shift operation.
+     * - The procedure in HAC assumes that 0 < TB <= TA.
+     *     - The condition TB <= TA is not actually necessary for correctness.
+     *       TA and TB have symmetric roles except for the loop termination
+     *       condition, and the shifts at the beginning of the loop body
+     *       remove any significance from the ordering of TA vs TB before
+     *       the shifts.
+     *     - If TA = 0, the loop goes through 0 iterations and the result is
+     *       correctly TB.
+     *     - The case TB = 0 was short-circuited above.
+     *
+     * For the correctness proof below, decompose the original values of
+     * A and B as
+     *   A = sa * 2^a * A' with A'=0 or A' odd, and sa = +-1
+     *   B = sb * 2^b * B' with B'=0 or B' odd, and sb = +-1
+     * Then gcd(A, B) = 2^{min(a,b)} * gcd(A',B'),
+     * and gcd(A',B') is odd or 0.
+     *
+     * At the beginning, we have TA = |A| and TB = |B| so gcd(A,B) = gcd(TA,TB).
+     * The code maintains the following invariant:
+     *     gcd(A,B) = 2^k * gcd(TA,TB) for some k   (I)
+     */
+
+    /* Proof that the loop terminates:
+     * At each iteration, either the right-shift by 1 is made on a nonzero
+     * value and the nonnegative integer bitlen(TA) + bitlen(TB) decreases
+     * by at least 1, or the right-shift by 1 is made on zero and then
+     * TA becomes 0 which ends the loop (TB cannot be 0 if it is right-shifted
+     * since in that case TB is calculated from TB-TA with the condition TB>TA).
+     */
     while( mbedtls_mpi_cmp_int( &TA, 0 ) != 0 )
     {
+        /* Divisions by 2 preserve the invariant (I). */
         MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( &TA, mbedtls_mpi_lsb( &TA ) ) );
         MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( &TB, mbedtls_mpi_lsb( &TB ) ) );
 
+        /* Set either TA or TB to |TA-TB|/2. Since TA and TB are both odd,
+         * TA-TB is even so the division by 2 has an integer result.
+         * Invariant (I) is preserved since any odd divisor of both TA and TB
+         * also divides |TA-TB|/2, and any odd divisor of both TA and |TA-TB|/2
+         * also divides TB, and any odd divisior of both TB and |TA-TB|/2 also
+         * divides TA.
+         */
         if( mbedtls_mpi_cmp_mpi( &TA, &TB ) >= 0 )
         {
             MBEDTLS_MPI_CHK( mbedtls_mpi_sub_abs( &TA, &TA, &TB ) );
@@ -2357,8 +2330,18 @@
             MBEDTLS_MPI_CHK( mbedtls_mpi_sub_abs( &TB, &TB, &TA ) );
             MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( &TB, 1 ) );
         }
+        /* Note that one of TA or TB is still odd. */
     }
 
+    /* By invariant (I), gcd(A,B) = 2^k * gcd(TA,TB) for some k.
+     * At the loop exit, TA = 0, so gcd(TA,TB) = TB.
+     * - If there was at least one loop iteration, then one of TA or TB is odd,
+     *   and TA = 0, so TB is odd and gcd(TA,TB) = gcd(A',B'). In this case,
+     *   lz = min(a,b) so gcd(A,B) = 2^lz * TB.
+     * - If there was no loop iteration, then A was 0, and gcd(A,B) = B.
+     *   In this case, lz = 0 and B = TB so gcd(A,B) = B = 2^lz * TB as well.
+     */
+
     MBEDTLS_MPI_CHK( mbedtls_mpi_shift_l( &TB, lz ) );
     MBEDTLS_MPI_CHK( mbedtls_mpi_copy( G, &TB ) );
 
@@ -2369,6 +2352,33 @@
     return( ret );
 }
 
+/* Fill X with n_bytes random bytes.
+ * X must already have room for those bytes.
+ * The ordering of the bytes returned from the RNG is suitable for
+ * deterministic ECDSA (see RFC 6979 §3.3 and mbedtls_mpi_random()).
+ * The size and sign of X are unchanged.
+ * n_bytes must not be 0.
+ */
+static int mpi_fill_random_internal(
+    mbedtls_mpi *X, size_t n_bytes,
+    int (*f_rng)(void *, unsigned char *, size_t), void *p_rng )
+{
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+    const size_t limbs = CHARS_TO_LIMBS( n_bytes );
+    const size_t overhead = ( limbs * ciL ) - n_bytes;
+
+    if( X->n < limbs )
+        return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA );
+
+    memset( X->p, 0, overhead );
+    memset( (unsigned char *) X->p + limbs * ciL, 0, ( X->n - limbs ) * ciL );
+    MBEDTLS_MPI_CHK( f_rng( p_rng, (unsigned char *) X->p + overhead, n_bytes ) );
+    mpi_bigendian_to_host( X->p, limbs );
+
+cleanup:
+    return( ret );
+}
+
 /*
  * Fill X with size bytes of random.
  *
@@ -2382,30 +2392,96 @@
 {
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
     size_t const limbs = CHARS_TO_LIMBS( size );
-    size_t const overhead = ( limbs * ciL ) - size;
-    unsigned char *Xp;
 
     MPI_VALIDATE_RET( X     != NULL );
     MPI_VALIDATE_RET( f_rng != NULL );
 
     /* Ensure that target MPI has exactly the necessary number of limbs */
-    if( X->n != limbs )
-    {
-        mbedtls_mpi_free( X );
-        mbedtls_mpi_init( X );
-        MBEDTLS_MPI_CHK( mbedtls_mpi_grow( X, limbs ) );
-    }
-    MBEDTLS_MPI_CHK( mbedtls_mpi_lset( X, 0 ) );
+    MBEDTLS_MPI_CHK( mbedtls_mpi_resize_clear( X, limbs ) );
+    if( size == 0 )
+        return( 0 );
 
-    Xp = (unsigned char*) X->p;
-    MBEDTLS_MPI_CHK( f_rng( p_rng, Xp + overhead, size ) );
-
-    mpi_bigendian_to_host( X->p, limbs );
+    ret = mpi_fill_random_internal( X, size, f_rng, p_rng );
 
 cleanup:
     return( ret );
 }
 
+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 ret = MBEDTLS_ERR_MPI_BAD_INPUT_DATA;
+    int count;
+    unsigned lt_lower = 1, lt_upper = 0;
+    size_t n_bits = mbedtls_mpi_bitlen( N );
+    size_t n_bytes = ( n_bits + 7 ) / 8;
+    mbedtls_mpi lower_bound;
+
+    if( min < 0 )
+        return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA );
+    if( mbedtls_mpi_cmp_int( N, min ) <= 0 )
+        return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA );
+
+    /*
+     * When min == 0, each try has at worst a probability 1/2 of failing
+     * (the msb has a probability 1/2 of being 0, and then the result will
+     * be < N), so after 30 tries failure probability is a most 2**(-30).
+     *
+     * When N is just below a power of 2, as is the case when generating
+     * a random scalar on most elliptic curves, 1 try is enough with
+     * overwhelming probability. When N is just above a power of 2,
+     * as when generating a random scalar on secp224k1, each try has
+     * a probability of failing that is almost 1/2.
+     *
+     * The probabilities are almost the same if min is nonzero but negligible
+     * compared to N. This is always the case when N is crypto-sized, but
+     * it's convenient to support small N for testing purposes. When N
+     * is small, use a higher repeat count, otherwise the probability of
+     * failure is macroscopic.
+     */
+    count = ( n_bytes > 4 ? 30 : 250 );
+
+    mbedtls_mpi_init( &lower_bound );
+
+    /* Ensure that target MPI has exactly the same number of limbs
+     * as the upper bound, even if the upper bound has leading zeros.
+     * This is necessary for the mbedtls_mpi_lt_mpi_ct() check. */
+    MBEDTLS_MPI_CHK( mbedtls_mpi_resize_clear( X, N->n ) );
+    MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &lower_bound, N->n ) );
+    MBEDTLS_MPI_CHK( mbedtls_mpi_lset( &lower_bound, min ) );
+
+    /*
+     * Match the procedure given in RFC 6979 §3.3 (deterministic ECDSA)
+     * when f_rng is a suitably parametrized instance of HMAC_DRBG:
+     * - use the same byte ordering;
+     * - keep the leftmost n_bits bits of the generated octet string;
+     * - try until result is in the desired range.
+     * This also avoids any bias, which is especially important for ECDSA.
+     */
+    do
+    {
+        MBEDTLS_MPI_CHK( mpi_fill_random_internal( X, n_bytes, f_rng, p_rng ) );
+        MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( X, 8 * n_bytes - n_bits ) );
+
+        if( --count == 0 )
+        {
+            ret = MBEDTLS_ERR_MPI_NOT_ACCEPTABLE;
+            goto cleanup;
+        }
+
+        MBEDTLS_MPI_CHK( mbedtls_mpi_lt_mpi_ct( X, &lower_bound, &lt_lower ) );
+        MBEDTLS_MPI_CHK( mbedtls_mpi_lt_mpi_ct( X, N, &lt_upper ) );
+    }
+    while( lt_lower != 0 || lt_upper == 0 );
+
+cleanup:
+    mbedtls_mpi_free( &lower_bound );
+    return( ret );
+}
+
 /*
  * Modular inverse: X = A^-1 mod N  (HAC 14.61 / 14.64)
  */
diff --git a/third_party/mbedtls/repo/library/blowfish.c b/third_party/mbedtls/repo/library/blowfish.c
index 76da448..621e9f7 100644
--- a/third_party/mbedtls/repo/library/blowfish.c
+++ b/third_party/mbedtls/repo/library/blowfish.c
@@ -40,29 +40,6 @@
 #define BLOWFISH_VALIDATE( cond )                                           \
     MBEDTLS_INTERNAL_VALIDATE( cond )
 
-/*
- * 32-bit integer manipulation macros (big endian)
- */
-#ifndef GET_UINT32_BE
-#define GET_UINT32_BE(n,b,i)                            \
-{                                                       \
-    (n) = ( (uint32_t) (b)[(i)    ] << 24 )             \
-        | ( (uint32_t) (b)[(i) + 1] << 16 )             \
-        | ( (uint32_t) (b)[(i) + 2] <<  8 )             \
-        | ( (uint32_t) (b)[(i) + 3]       );            \
-}
-#endif
-
-#ifndef PUT_UINT32_BE
-#define PUT_UINT32_BE(n,b,i)                            \
-{                                                       \
-    (b)[(i)    ] = (unsigned char) ( (n) >> 24 );       \
-    (b)[(i) + 1] = (unsigned char) ( (n) >> 16 );       \
-    (b)[(i) + 2] = (unsigned char) ( (n) >>  8 );       \
-    (b)[(i) + 3] = (unsigned char) ( (n)       );       \
-}
-#endif
-
 static const uint32_t P[MBEDTLS_BLOWFISH_ROUNDS + 2] = {
         0x243F6A88L, 0x85A308D3L, 0x13198A2EL, 0x03707344L,
         0xA4093822L, 0x299F31D0L, 0x082EFA98L, 0xEC4E6C89L,
@@ -79,13 +56,13 @@
    unsigned short a, b, c, d;
    uint32_t  y;
 
-   d = (unsigned short)(x & 0xFF);
+   d = MBEDTLS_BYTE_0( x );
    x >>= 8;
-   c = (unsigned short)(x & 0xFF);
+   c = MBEDTLS_BYTE_0( x );
    x >>= 8;
-   b = (unsigned short)(x & 0xFF);
+   b = MBEDTLS_BYTE_0( x );
    x >>= 8;
-   a = (unsigned short)(x & 0xFF);
+   a = MBEDTLS_BYTE_0( x );
    y = ctx->S[0][a] + ctx->S[1][b];
    y = y ^ ctx->S[2][c];
    y = y + ctx->S[3][d];
@@ -242,8 +219,8 @@
     BLOWFISH_VALIDATE_RET( input  != NULL );
     BLOWFISH_VALIDATE_RET( output != NULL );
 
-    GET_UINT32_BE( X0, input,  0 );
-    GET_UINT32_BE( X1, input,  4 );
+    X0 = MBEDTLS_GET_UINT32_BE( input,  0 );
+    X1 = MBEDTLS_GET_UINT32_BE( input,  4 );
 
     if( mode == MBEDTLS_BLOWFISH_DECRYPT )
     {
@@ -254,8 +231,8 @@
         blowfish_enc( ctx, &X0, &X1 );
     }
 
-    PUT_UINT32_BE( X0, output,  0 );
-    PUT_UINT32_BE( X1, output,  4 );
+    MBEDTLS_PUT_UINT32_BE( X0, output,  0 );
+    MBEDTLS_PUT_UINT32_BE( X1, output,  4 );
 
     return( 0 );
 }
diff --git a/third_party/mbedtls/repo/library/camellia.c b/third_party/mbedtls/repo/library/camellia.c
index d60f931..29d730a 100644
--- a/third_party/mbedtls/repo/library/camellia.c
+++ b/third_party/mbedtls/repo/library/camellia.c
@@ -49,29 +49,6 @@
 #define CAMELLIA_VALIDATE( cond )                                           \
     MBEDTLS_INTERNAL_VALIDATE( cond )
 
-/*
- * 32-bit integer manipulation macros (big endian)
- */
-#ifndef GET_UINT32_BE
-#define GET_UINT32_BE(n,b,i)                            \
-{                                                       \
-    (n) = ( (uint32_t) (b)[(i)    ] << 24 )             \
-        | ( (uint32_t) (b)[(i) + 1] << 16 )             \
-        | ( (uint32_t) (b)[(i) + 2] <<  8 )             \
-        | ( (uint32_t) (b)[(i) + 3]       );            \
-}
-#endif
-
-#ifndef PUT_UINT32_BE
-#define PUT_UINT32_BE(n,b,i)                            \
-{                                                       \
-    (b)[(i)    ] = (unsigned char) ( (n) >> 24 );       \
-    (b)[(i) + 1] = (unsigned char) ( (n) >> 16 );       \
-    (b)[(i) + 2] = (unsigned char) ( (n) >>  8 );       \
-    (b)[(i) + 3] = (unsigned char) ( (n)       );       \
-}
-#endif
-
 static const unsigned char SIGMA_CHARS[6][8] =
 {
     { 0xa0, 0x9e, 0x66, 0x7f, 0x3b, 0xcc, 0x90, 0x8b },
@@ -301,14 +278,14 @@
     I0 = x[0] ^ k[0];
     I1 = x[1] ^ k[1];
 
-    I0 = ((uint32_t) SBOX1((I0 >> 24) & 0xFF) << 24) |
-         ((uint32_t) SBOX2((I0 >> 16) & 0xFF) << 16) |
-         ((uint32_t) SBOX3((I0 >>  8) & 0xFF) <<  8) |
-         ((uint32_t) SBOX4((I0      ) & 0xFF)      );
-    I1 = ((uint32_t) SBOX2((I1 >> 24) & 0xFF) << 24) |
-         ((uint32_t) SBOX3((I1 >> 16) & 0xFF) << 16) |
-         ((uint32_t) SBOX4((I1 >>  8) & 0xFF) <<  8) |
-         ((uint32_t) SBOX1((I1      ) & 0xFF)      );
+    I0 = ((uint32_t) SBOX1( MBEDTLS_BYTE_3( I0 )) << 24) |
+         ((uint32_t) SBOX2( MBEDTLS_BYTE_2( I0 )) << 16) |
+         ((uint32_t) SBOX3( MBEDTLS_BYTE_1( I0 )) <<  8) |
+         ((uint32_t) SBOX4( MBEDTLS_BYTE_0( I0 ))      );
+    I1 = ((uint32_t) SBOX2( MBEDTLS_BYTE_3( I1 )) << 24) |
+         ((uint32_t) SBOX3( MBEDTLS_BYTE_2( I1 )) << 16) |
+         ((uint32_t) SBOX4( MBEDTLS_BYTE_1( I1 )) <<  8) |
+         ((uint32_t) SBOX1( MBEDTLS_BYTE_0( I1 ))      );
 
     I0 ^= (I1 << 8) | (I1 >> 24);
     I1 ^= (I0 << 16) | (I0 >> 16);
@@ -376,8 +353,8 @@
      * Prepare SIGMA values
      */
     for( i = 0; i < 6; i++ ) {
-        GET_UINT32_BE( SIGMA[i][0], SIGMA_CHARS[i], 0 );
-        GET_UINT32_BE( SIGMA[i][1], SIGMA_CHARS[i], 4 );
+        SIGMA[i][0] = MBEDTLS_GET_UINT32_BE( SIGMA_CHARS[i], 0 );
+        SIGMA[i][1] = MBEDTLS_GET_UINT32_BE( SIGMA_CHARS[i], 4 );
     }
 
     /*
@@ -388,7 +365,7 @@
 
     /* Store KL, KR */
     for( i = 0; i < 8; i++ )
-        GET_UINT32_BE( KC[i], t, i * 4 );
+        KC[i] = MBEDTLS_GET_UINT32_BE( t, i * 4 );
 
     /* Generate KA */
     for( i = 0; i < 4; ++i )
@@ -514,10 +491,10 @@
     NR = ctx->nr;
     RK = ctx->rk;
 
-    GET_UINT32_BE( X[0], input,  0 );
-    GET_UINT32_BE( X[1], input,  4 );
-    GET_UINT32_BE( X[2], input,  8 );
-    GET_UINT32_BE( X[3], input, 12 );
+    X[0] = MBEDTLS_GET_UINT32_BE( input,  0 );
+    X[1] = MBEDTLS_GET_UINT32_BE( input,  4 );
+    X[2] = MBEDTLS_GET_UINT32_BE( input,  8 );
+    X[3] = MBEDTLS_GET_UINT32_BE( input, 12 );
 
     X[0] ^= *RK++;
     X[1] ^= *RK++;
@@ -552,10 +529,10 @@
     X[0] ^= *RK++;
     X[1] ^= *RK++;
 
-    PUT_UINT32_BE( X[2], output,  0 );
-    PUT_UINT32_BE( X[3], output,  4 );
-    PUT_UINT32_BE( X[0], output,  8 );
-    PUT_UINT32_BE( X[1], output, 12 );
+    MBEDTLS_PUT_UINT32_BE( X[2], output,  0 );
+    MBEDTLS_PUT_UINT32_BE( X[3], output,  4 );
+    MBEDTLS_PUT_UINT32_BE( X[0], output,  8 );
+    MBEDTLS_PUT_UINT32_BE( X[1], output, 12 );
 
     return( 0 );
 }
@@ -942,9 +919,11 @@
     unsigned char nonce_counter[16];
     unsigned char stream_block[16];
 #endif
+    int ret = 1;
 
     mbedtls_camellia_context ctx;
 
+    mbedtls_camellia_init( &ctx );
     memset( key, 0, 32 );
 
     for( j = 0; j < 6; j++ ) {
@@ -974,8 +953,7 @@
         {
             if( verbose != 0 )
                 mbedtls_printf( "failed\n" );
-
-            return( 1 );
+            goto exit;
         }
     }
 
@@ -1027,8 +1005,7 @@
             {
                 if( verbose != 0 )
                     mbedtls_printf( "failed\n" );
-
-                return( 1 );
+                goto exit;
             }
         }
 
@@ -1071,8 +1048,7 @@
             {
                 if( verbose != 0 )
                     mbedtls_printf( "failed\n" );
-
-                return( 1 );
+                goto exit;
             }
         }
         else
@@ -1087,8 +1063,7 @@
             {
                 if( verbose != 0 )
                     mbedtls_printf( "failed\n" );
-
-                return( 1 );
+                goto exit;
             }
         }
 
@@ -1100,7 +1075,11 @@
         mbedtls_printf( "\n" );
 #endif /* MBEDTLS_CIPHER_MODE_CTR */
 
-    return( 0 );
+    ret = 0;
+
+exit:
+    mbedtls_camellia_free( &ctx );
+    return( ret );
 }
 
 #endif /* MBEDTLS_SELF_TEST */
diff --git a/third_party/mbedtls/repo/library/ccm.c b/third_party/mbedtls/repo/library/ccm.c
index 424ee77..a21a37f 100644
--- a/third_party/mbedtls/repo/library/ccm.c
+++ b/third_party/mbedtls/repo/library/ccm.c
@@ -200,7 +200,7 @@
     memcpy( b + 1, iv, iv_len );
 
     for( i = 0, len_left = length; i < q; i++, len_left >>= 8 )
-        b[15-i] = (unsigned char)( len_left & 0xFF );
+        b[15-i] = MBEDTLS_BYTE_0( len_left );
 
     if( len_left > 0 )
         return( MBEDTLS_ERR_CCM_BAD_INPUT );
@@ -221,8 +221,7 @@
         src = add;
 
         memset( b, 0, 16 );
-        b[0] = (unsigned char)( ( add_len >> 8 ) & 0xFF );
-        b[1] = (unsigned char)( ( add_len      ) & 0xFF );
+        MBEDTLS_PUT_UINT16_BE( add_len, b, 0 );
 
         use_len = len_left < 16 - 2 ? len_left : 16 - 2;
         memcpy( b + 2, src, use_len );
diff --git a/third_party/mbedtls/repo/library/chacha20.c b/third_party/mbedtls/repo/library/chacha20.c
index 78467d3..658f046 100644
--- a/third_party/mbedtls/repo/library/chacha20.c
+++ b/third_party/mbedtls/repo/library/chacha20.c
@@ -54,13 +54,6 @@
 #define CHACHA20_VALIDATE( cond )                                           \
     MBEDTLS_INTERNAL_VALIDATE( cond )
 
-#define BYTES_TO_U32_LE( data, offset )                           \
-    ( (uint32_t) (data)[offset]                                   \
-      | (uint32_t) ( (uint32_t) (data)[( offset ) + 1] << 8 )     \
-      | (uint32_t) ( (uint32_t) (data)[( offset ) + 2] << 16 )    \
-      | (uint32_t) ( (uint32_t) (data)[( offset ) + 3] << 24 )    \
-    )
-
 #define ROTL32( value, amount ) \
     ( (uint32_t) ( (value) << (amount) ) | ( (value) >> ( 32 - (amount) ) ) )
 
@@ -171,10 +164,7 @@
     {
         size_t offset = i * 4U;
 
-        keystream[offset     ] = (unsigned char)( working_state[i]       );
-        keystream[offset + 1U] = (unsigned char)( working_state[i] >>  8 );
-        keystream[offset + 2U] = (unsigned char)( working_state[i] >> 16 );
-        keystream[offset + 3U] = (unsigned char)( working_state[i] >> 24 );
+        MBEDTLS_PUT_UINT32_LE(working_state[i], keystream, offset);
     }
 
     mbedtls_platform_zeroize( working_state, sizeof( working_state ) );
@@ -212,14 +202,14 @@
     ctx->state[3] = 0x6b206574;
 
     /* Set key */
-    ctx->state[4]  = BYTES_TO_U32_LE( key, 0 );
-    ctx->state[5]  = BYTES_TO_U32_LE( key, 4 );
-    ctx->state[6]  = BYTES_TO_U32_LE( key, 8 );
-    ctx->state[7]  = BYTES_TO_U32_LE( key, 12 );
-    ctx->state[8]  = BYTES_TO_U32_LE( key, 16 );
-    ctx->state[9]  = BYTES_TO_U32_LE( key, 20 );
-    ctx->state[10] = BYTES_TO_U32_LE( key, 24 );
-    ctx->state[11] = BYTES_TO_U32_LE( key, 28 );
+    ctx->state[4]  = MBEDTLS_GET_UINT32_LE( key, 0 );
+    ctx->state[5]  = MBEDTLS_GET_UINT32_LE( key, 4 );
+    ctx->state[6]  = MBEDTLS_GET_UINT32_LE( key, 8 );
+    ctx->state[7]  = MBEDTLS_GET_UINT32_LE( key, 12 );
+    ctx->state[8]  = MBEDTLS_GET_UINT32_LE( key, 16 );
+    ctx->state[9]  = MBEDTLS_GET_UINT32_LE( key, 20 );
+    ctx->state[10] = MBEDTLS_GET_UINT32_LE( key, 24 );
+    ctx->state[11] = MBEDTLS_GET_UINT32_LE( key, 28 );
 
     return( 0 );
 }
@@ -235,9 +225,9 @@
     ctx->state[12] = counter;
 
     /* Nonce */
-    ctx->state[13] = BYTES_TO_U32_LE( nonce, 0 );
-    ctx->state[14] = BYTES_TO_U32_LE( nonce, 4 );
-    ctx->state[15] = BYTES_TO_U32_LE( nonce, 8 );
+    ctx->state[13] = MBEDTLS_GET_UINT32_LE( nonce, 0 );
+    ctx->state[14] = MBEDTLS_GET_UINT32_LE( nonce, 4 );
+    ctx->state[15] = MBEDTLS_GET_UINT32_LE( nonce, 8 );
 
     mbedtls_platform_zeroize( ctx->keystream8, sizeof( ctx->keystream8 ) );
 
diff --git a/third_party/mbedtls/repo/library/chachapoly.c b/third_party/mbedtls/repo/library/chachapoly.c
index 77d5477..dc75b20 100644
--- a/third_party/mbedtls/repo/library/chachapoly.c
+++ b/third_party/mbedtls/repo/library/chachapoly.c
@@ -263,22 +263,8 @@
     /* The lengths of the AAD and ciphertext are processed by
      * Poly1305 as the final 128-bit block, encoded as little-endian integers.
      */
-    len_block[ 0] = (unsigned char)( ctx->aad_len       );
-    len_block[ 1] = (unsigned char)( ctx->aad_len >>  8 );
-    len_block[ 2] = (unsigned char)( ctx->aad_len >> 16 );
-    len_block[ 3] = (unsigned char)( ctx->aad_len >> 24 );
-    len_block[ 4] = (unsigned char)( ctx->aad_len >> 32 );
-    len_block[ 5] = (unsigned char)( ctx->aad_len >> 40 );
-    len_block[ 6] = (unsigned char)( ctx->aad_len >> 48 );
-    len_block[ 7] = (unsigned char)( ctx->aad_len >> 56 );
-    len_block[ 8] = (unsigned char)( ctx->ciphertext_len       );
-    len_block[ 9] = (unsigned char)( ctx->ciphertext_len >>  8 );
-    len_block[10] = (unsigned char)( ctx->ciphertext_len >> 16 );
-    len_block[11] = (unsigned char)( ctx->ciphertext_len >> 24 );
-    len_block[12] = (unsigned char)( ctx->ciphertext_len >> 32 );
-    len_block[13] = (unsigned char)( ctx->ciphertext_len >> 40 );
-    len_block[14] = (unsigned char)( ctx->ciphertext_len >> 48 );
-    len_block[15] = (unsigned char)( ctx->ciphertext_len >> 56 );
+    MBEDTLS_PUT_UINT64_LE(ctx->aad_len, len_block, 0);
+    MBEDTLS_PUT_UINT64_LE(ctx->ciphertext_len, len_block, 8);
 
     ret = mbedtls_poly1305_update( &ctx->poly1305_ctx, len_block, 16U );
     if( ret != 0 )
diff --git a/third_party/mbedtls/repo/library/check_crypto_config.h b/third_party/mbedtls/repo/library/check_crypto_config.h
index cac90a0..d7ad16a 100644
--- a/third_party/mbedtls/repo/library/check_crypto_config.h
+++ b/third_party/mbedtls/repo/library/check_crypto_config.h
@@ -28,6 +28,19 @@
 #ifndef MBEDTLS_CHECK_CRYPTO_CONFIG_H
 #define MBEDTLS_CHECK_CRYPTO_CONFIG_H
 
+#if defined(PSA_WANT_ALG_CCM) && \
+    !( defined(PSA_WANT_KEY_TYPE_AES) || \
+       defined(PSA_WANT_KEY_TYPE_CAMELLIA) )
+#error "PSA_WANT_ALG_CCM defined, but not all prerequisites"
+#endif
+
+#if defined(PSA_WANT_ALG_CMAC) && \
+    !( defined(PSA_WANT_KEY_TYPE_AES) || \
+       defined(PSA_WANT_KEY_TYPE_CAMELLIA) || \
+       defined(PSA_WANT_KEY_TYPE_DES) )
+#error "PSA_WANT_ALG_CMAC defined, but not all prerequisites"
+#endif
+
 #if defined(PSA_WANT_ALG_DETERMINISTIC_ECDSA) && \
     !( defined(PSA_WANT_KEY_TYPE_ECC_KEY_PAIR) || \
        defined(PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY) )
@@ -40,6 +53,12 @@
 #error "PSA_WANT_ALG_ECDSA defined, but not all prerequisites"
 #endif
 
+#if defined(PSA_WANT_ALG_GCM) && \
+    !( defined(PSA_WANT_KEY_TYPE_AES) || \
+       defined(PSA_WANT_KEY_TYPE_CAMELLIA) )
+#error "PSA_WANT_ALG_GCM defined, but not all prerequisites"
+#endif
+
 #if defined(PSA_WANT_ALG_RSA_PKCS1V15_CRYPT) && \
     !( defined(PSA_WANT_KEY_TYPE_RSA_KEY_PAIR) || \
        defined(PSA_WANT_KEY_TYPE_RSA_PUBLIC_KEY) )
diff --git a/third_party/mbedtls/repo/library/cipher.c b/third_party/mbedtls/repo/library/cipher.c
index 457f8f6..4ec40d2 100644
--- a/third_party/mbedtls/repo/library/cipher.c
+++ b/third_party/mbedtls/repo/library/cipher.c
@@ -29,6 +29,7 @@
 #include "mbedtls/cipher_internal.h"
 #include "mbedtls/platform_util.h"
 #include "mbedtls/error.h"
+#include "mbedtls/constant_time.h"
 
 #include <stdlib.h>
 #include <string.h>
@@ -74,27 +75,6 @@
 #define CIPHER_VALIDATE( cond )        \
     MBEDTLS_INTERNAL_VALIDATE( cond )
 
-#if defined(MBEDTLS_GCM_C) || defined(MBEDTLS_CHACHAPOLY_C)
-/* Compare the contents of two buffers in constant time.
- * Returns 0 if the contents are bitwise identical, otherwise returns
- * a non-zero value.
- * This is currently only used by GCM and ChaCha20+Poly1305.
- */
-static int mbedtls_constant_time_memcmp( const void *v1, const void *v2,
-                                         size_t len )
-{
-    const unsigned char *p1 = (const unsigned char*) v1;
-    const unsigned char *p2 = (const unsigned char*) v2;
-    size_t i;
-    unsigned char diff;
-
-    for( diff = 0, i = 0; i < len; i++ )
-        diff |= p1[i] ^ p2[i];
-
-    return( (int)diff );
-}
-#endif /* MBEDTLS_GCM_C || MBEDTLS_CHACHAPOLY_C */
-
 static int supported_init = 0;
 
 const int *mbedtls_cipher_list( void )
@@ -1145,6 +1125,12 @@
     }
 #endif /* MBEDTLS_USE_PSA_CRYPTO */
 
+    /* Status to return on a non-authenticated algorithm. It would make sense
+     * to return MBEDTLS_ERR_CIPHER_INVALID_CONTEXT or perhaps
+     * MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA, but at the time I write this our
+     * unit tests assume 0. */
+    ret = 0;
+
 #if defined(MBEDTLS_GCM_C)
     if( MBEDTLS_MODE_GCM == ctx->cipher_info->mode )
     {
@@ -1159,10 +1145,11 @@
         }
 
         /* Check the tag in "constant-time" */
-        if( mbedtls_constant_time_memcmp( tag, check_tag, tag_len ) != 0 )
-            return( MBEDTLS_ERR_CIPHER_AUTH_FAILED );
-
-        return( 0 );
+        if( mbedtls_ct_memcmp( tag, check_tag, tag_len ) != 0 )
+        {
+            ret = MBEDTLS_ERR_CIPHER_AUTH_FAILED;
+            goto exit;
+        }
     }
 #endif /* MBEDTLS_GCM_C */
 
@@ -1181,14 +1168,17 @@
         }
 
         /* Check the tag in "constant-time" */
-        if( mbedtls_constant_time_memcmp( tag, check_tag, tag_len ) != 0 )
-            return( MBEDTLS_ERR_CIPHER_AUTH_FAILED );
-
-        return( 0 );
+        if( mbedtls_ct_memcmp( tag, check_tag, tag_len ) != 0 )
+        {
+            ret = MBEDTLS_ERR_CIPHER_AUTH_FAILED;
+            goto exit;
+        }
     }
 #endif /* MBEDTLS_CHACHAPOLY_C */
 
-    return( 0 );
+exit:
+    mbedtls_platform_zeroize( check_tag, tag_len );
+    return( ret );
 }
 #endif /* MBEDTLS_GCM_C || MBEDTLS_CHACHAPOLY_C */
 
@@ -1246,9 +1236,12 @@
         if( status != PSA_SUCCESS )
             return( MBEDTLS_ERR_CIPHER_HW_ACCEL_FAILED );
 
-        status = psa_cipher_set_iv( &cipher_op, iv, iv_len );
-        if( status != PSA_SUCCESS )
-            return( MBEDTLS_ERR_CIPHER_HW_ACCEL_FAILED );
+        if( ctx->cipher_info->mode != MBEDTLS_MODE_ECB )
+        {
+            status = psa_cipher_set_iv( &cipher_op, iv, iv_len );
+            if( status != PSA_SUCCESS )
+                return( MBEDTLS_ERR_CIPHER_HW_ACCEL_FAILED );
+        }
 
         status = psa_cipher_update( &cipher_op,
                                     input, ilen,
diff --git a/third_party/mbedtls/repo/library/cmac.c b/third_party/mbedtls/repo/library/cmac.c
index 59ece15..3cc49d1 100644
--- a/third_party/mbedtls/repo/library/cmac.c
+++ b/third_party/mbedtls/repo/library/cmac.c
@@ -45,22 +45,10 @@
 #include "mbedtls/cmac.h"
 #include "mbedtls/platform_util.h"
 #include "mbedtls/error.h"
+#include "mbedtls/platform.h"
 
 #include <string.h>
 
-
-#if defined(MBEDTLS_PLATFORM_C)
-#include "mbedtls/platform.h"
-#else
-#include <stdlib.h>
-#define mbedtls_calloc     calloc
-#define mbedtls_free       free
-#if defined(MBEDTLS_SELF_TEST)
-#include <stdio.h>
-#define mbedtls_printf     printf
-#endif /* MBEDTLS_SELF_TEST */
-#endif /* MBEDTLS_PLATFORM_C */
-
 #if !defined(MBEDTLS_CMAC_ALT) || defined(MBEDTLS_SELF_TEST)
 
 /*
@@ -793,6 +781,18 @@
         if( ( ret = mbedtls_cipher_setkey( &ctx, key, keybits,
                                        MBEDTLS_ENCRYPT ) ) != 0 )
         {
+            /* When CMAC is implemented by an alternative implementation, or
+             * the underlying primitive itself is implemented alternatively,
+             * AES-192 may be unavailable. This should not cause the selftest
+             * function to fail. */
+            if( ( ret == MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED ||
+                  ret == MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE ) &&
+                  cipher_type == MBEDTLS_CIPHER_AES_192_ECB ) {
+                if( verbose != 0 )
+                    mbedtls_printf( "skipped\n" );
+                goto next_test;
+            }
+
             if( verbose != 0 )
                 mbedtls_printf( "test execution failed\n" );
 
@@ -820,6 +820,7 @@
         if( verbose != 0 )
             mbedtls_printf( "passed\n" );
 
+next_test:
         mbedtls_cipher_free( &ctx );
     }
 
@@ -864,6 +865,19 @@
         if( ( ret = mbedtls_cipher_cmac( cipher_info, key, keybits, messages,
                                          message_lengths[i], output ) ) != 0 )
         {
+            /* When CMAC is implemented by an alternative implementation, or
+             * the underlying primitive itself is implemented alternatively,
+             * AES-192 and/or 3DES may be unavailable. This should not cause
+             * the selftest function to fail. */
+            if( ( ret == MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED ||
+                  ret == MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE ) &&
+                ( cipher_type == MBEDTLS_CIPHER_AES_192_ECB ||
+                  cipher_type == MBEDTLS_CIPHER_DES_EDE3_ECB ) ) {
+                if( verbose != 0 )
+                    mbedtls_printf( "skipped\n" );
+                continue;
+            }
+
             if( verbose != 0 )
                 mbedtls_printf( "failed\n" );
             goto exit;
diff --git a/third_party/mbedtls/repo/library/common.h b/third_party/mbedtls/repo/library/common.h
index 5845766..c064724 100644
--- a/third_party/mbedtls/repo/library/common.h
+++ b/third_party/mbedtls/repo/library/common.h
@@ -29,6 +29,8 @@
 #include "mbedtls/config.h"
 #endif
 
+#include <stdint.h>
+
 /** Helper to define a function as static except when building invasive tests.
  *
  * If a function is only used inside its own source file and should be
@@ -50,4 +52,254 @@
 #define MBEDTLS_STATIC_TESTABLE static
 #endif
 
+/** Byte Reading Macros
+ *
+ * Given a multi-byte integer \p x, MBEDTLS_BYTE_n retrieves the n-th
+ * byte from x, where byte 0 is the least significant byte.
+ */
+#define MBEDTLS_BYTE_0( x ) ( (uint8_t) (   ( x )         & 0xff ) )
+#define MBEDTLS_BYTE_1( x ) ( (uint8_t) ( ( ( x ) >> 8  ) & 0xff ) )
+#define MBEDTLS_BYTE_2( x ) ( (uint8_t) ( ( ( x ) >> 16 ) & 0xff ) )
+#define MBEDTLS_BYTE_3( x ) ( (uint8_t) ( ( ( x ) >> 24 ) & 0xff ) )
+#define MBEDTLS_BYTE_4( x ) ( (uint8_t) ( ( ( x ) >> 32 ) & 0xff ) )
+#define MBEDTLS_BYTE_5( x ) ( (uint8_t) ( ( ( x ) >> 40 ) & 0xff ) )
+#define MBEDTLS_BYTE_6( x ) ( (uint8_t) ( ( ( x ) >> 48 ) & 0xff ) )
+#define MBEDTLS_BYTE_7( x ) ( (uint8_t) ( ( ( x ) >> 56 ) & 0xff ) )
+
+/**
+ * Get the unsigned 32 bits integer corresponding to four bytes in
+ * big-endian order (MSB first).
+ *
+ * \param   data    Base address of the memory to get the four bytes from.
+ * \param   offset  Offset from \p base of the first and most significant
+ *                  byte of the four bytes to build the 32 bits unsigned
+ *                  integer from.
+ */
+#ifndef MBEDTLS_GET_UINT32_BE
+#define MBEDTLS_GET_UINT32_BE( data , offset )                  \
+    (                                                           \
+          ( (uint32_t) ( data )[( offset )    ] << 24 )         \
+        | ( (uint32_t) ( data )[( offset ) + 1] << 16 )         \
+        | ( (uint32_t) ( data )[( offset ) + 2] <<  8 )         \
+        | ( (uint32_t) ( data )[( offset ) + 3]       )         \
+    )
+#endif
+
+/**
+ * Put in memory a 32 bits unsigned integer in big-endian order.
+ *
+ * \param   n       32 bits unsigned integer to put in memory.
+ * \param   data    Base address of the memory where to put the 32
+ *                  bits unsigned integer in.
+ * \param   offset  Offset from \p base where to put the most significant
+ *                  byte of the 32 bits unsigned integer \p n.
+ */
+#ifndef MBEDTLS_PUT_UINT32_BE
+#define MBEDTLS_PUT_UINT32_BE( n, data, offset )                \
+{                                                               \
+    ( data )[( offset )    ] = MBEDTLS_BYTE_3( n );             \
+    ( data )[( offset ) + 1] = MBEDTLS_BYTE_2( n );             \
+    ( data )[( offset ) + 2] = MBEDTLS_BYTE_1( n );             \
+    ( data )[( offset ) + 3] = MBEDTLS_BYTE_0( n );             \
+}
+#endif
+
+/**
+ * Get the unsigned 32 bits integer corresponding to four bytes in
+ * little-endian order (LSB first).
+ *
+ * \param   data    Base address of the memory to get the four bytes from.
+ * \param   offset  Offset from \p base of the first and least significant
+ *                  byte of the four bytes to build the 32 bits unsigned
+ *                  integer from.
+ */
+#ifndef MBEDTLS_GET_UINT32_LE
+#define MBEDTLS_GET_UINT32_LE( data, offset )                   \
+    (                                                           \
+          ( (uint32_t) ( data )[( offset )    ]       )         \
+        | ( (uint32_t) ( data )[( offset ) + 1] <<  8 )         \
+        | ( (uint32_t) ( data )[( offset ) + 2] << 16 )         \
+        | ( (uint32_t) ( data )[( offset ) + 3] << 24 )         \
+    )
+#endif
+
+/**
+ * Put in memory a 32 bits unsigned integer in little-endian order.
+ *
+ * \param   n       32 bits unsigned integer to put in memory.
+ * \param   data    Base address of the memory where to put the 32
+ *                  bits unsigned integer in.
+ * \param   offset  Offset from \p base where to put the least significant
+ *                  byte of the 32 bits unsigned integer \p n.
+ */
+#ifndef MBEDTLS_PUT_UINT32_LE
+#define MBEDTLS_PUT_UINT32_LE( n, data, offset )                \
+{                                                               \
+    ( data )[( offset )    ] = MBEDTLS_BYTE_0( n );             \
+    ( data )[( offset ) + 1] = MBEDTLS_BYTE_1( n );             \
+    ( data )[( offset ) + 2] = MBEDTLS_BYTE_2( n );             \
+    ( data )[( offset ) + 3] = MBEDTLS_BYTE_3( n );             \
+}
+#endif
+
+/**
+ * Get the unsigned 16 bits integer corresponding to two bytes in
+ * little-endian order (LSB first).
+ *
+ * \param   data    Base address of the memory to get the two bytes from.
+ * \param   offset  Offset from \p base of the first and least significant
+ *                  byte of the two bytes to build the 16 bits unsigned
+ *                  integer from.
+ */
+#ifndef MBEDTLS_GET_UINT16_LE
+#define MBEDTLS_GET_UINT16_LE( data, offset )                   \
+    (                                                           \
+          ( (uint16_t) ( data )[( offset )    ]       )         \
+        | ( (uint16_t) ( data )[( offset ) + 1] <<  8 )         \
+    )
+#endif
+
+/**
+ * Put in memory a 16 bits unsigned integer in little-endian order.
+ *
+ * \param   n       16 bits unsigned integer to put in memory.
+ * \param   data    Base address of the memory where to put the 16
+ *                  bits unsigned integer in.
+ * \param   offset  Offset from \p base where to put the least significant
+ *                  byte of the 16 bits unsigned integer \p n.
+ */
+#ifndef MBEDTLS_PUT_UINT16_LE
+#define MBEDTLS_PUT_UINT16_LE( n, data, offset )                \
+{                                                               \
+    ( data )[( offset )    ] = MBEDTLS_BYTE_0( n );             \
+    ( data )[( offset ) + 1] = MBEDTLS_BYTE_1( n );             \
+}
+#endif
+
+/**
+ * Get the unsigned 16 bits integer corresponding to two bytes in
+ * big-endian order (MSB first).
+ *
+ * \param   data    Base address of the memory to get the two bytes from.
+ * \param   offset  Offset from \p base of the first and most significant
+ *                  byte of the two bytes to build the 16 bits unsigned
+ *                  integer from.
+ */
+#ifndef MBEDTLS_GET_UINT16_BE
+#define MBEDTLS_GET_UINT16_BE( data, offset )                   \
+    (                                                           \
+          ( (uint16_t) ( data )[( offset )    ] << 8 )          \
+        | ( (uint16_t) ( data )[( offset ) + 1]      )          \
+    )
+#endif
+
+/**
+ * Put in memory a 16 bits unsigned integer in big-endian order.
+ *
+ * \param   n       16 bits unsigned integer to put in memory.
+ * \param   data    Base address of the memory where to put the 16
+ *                  bits unsigned integer in.
+ * \param   offset  Offset from \p base where to put the most significant
+ *                  byte of the 16 bits unsigned integer \p n.
+ */
+#ifndef MBEDTLS_PUT_UINT16_BE
+#define MBEDTLS_PUT_UINT16_BE( n, data, offset )                \
+{                                                               \
+    ( data )[( offset )    ] = MBEDTLS_BYTE_1( n );             \
+    ( data )[( offset ) + 1] = MBEDTLS_BYTE_0( n );             \
+}
+#endif
+
+/**
+ * Get the unsigned 64 bits integer corresponding to eight bytes in
+ * big-endian order (MSB first).
+ *
+ * \param   data    Base address of the memory to get the eight bytes from.
+ * \param   offset  Offset from \p base of the first and most significant
+ *                  byte of the eight bytes to build the 64 bits unsigned
+ *                  integer from.
+ */
+#ifndef MBEDTLS_GET_UINT64_BE
+#define MBEDTLS_GET_UINT64_BE( data, offset )                   \
+    (                                                           \
+          ( (uint64_t) ( data )[( offset )    ] << 56 )         \
+        | ( (uint64_t) ( data )[( offset ) + 1] << 48 )         \
+        | ( (uint64_t) ( data )[( offset ) + 2] << 40 )         \
+        | ( (uint64_t) ( data )[( offset ) + 3] << 32 )         \
+        | ( (uint64_t) ( data )[( offset ) + 4] << 24 )         \
+        | ( (uint64_t) ( data )[( offset ) + 5] << 16 )         \
+        | ( (uint64_t) ( data )[( offset ) + 6] <<  8 )         \
+        | ( (uint64_t) ( data )[( offset ) + 7]       )         \
+    )
+#endif
+
+/**
+ * Put in memory a 64 bits unsigned integer in big-endian order.
+ *
+ * \param   n       64 bits unsigned integer to put in memory.
+ * \param   data    Base address of the memory where to put the 64
+ *                  bits unsigned integer in.
+ * \param   offset  Offset from \p base where to put the most significant
+ *                  byte of the 64 bits unsigned integer \p n.
+ */
+#ifndef MBEDTLS_PUT_UINT64_BE
+#define MBEDTLS_PUT_UINT64_BE( n, data, offset )                \
+{                                                               \
+    ( data )[( offset )    ] = MBEDTLS_BYTE_7( n );             \
+    ( data )[( offset ) + 1] = MBEDTLS_BYTE_6( n );             \
+    ( data )[( offset ) + 2] = MBEDTLS_BYTE_5( n );             \
+    ( data )[( offset ) + 3] = MBEDTLS_BYTE_4( n );             \
+    ( data )[( offset ) + 4] = MBEDTLS_BYTE_3( n );             \
+    ( data )[( offset ) + 5] = MBEDTLS_BYTE_2( n );             \
+    ( data )[( offset ) + 6] = MBEDTLS_BYTE_1( n );             \
+    ( data )[( offset ) + 7] = MBEDTLS_BYTE_0( n );             \
+}
+#endif
+
+/**
+ * Get the unsigned 64 bits integer corresponding to eight bytes in
+ * little-endian order (LSB first).
+ *
+ * \param   data    Base address of the memory to get the eight bytes from.
+ * \param   offset  Offset from \p base of the first and least significant
+ *                  byte of the eight bytes to build the 64 bits unsigned
+ *                  integer from.
+ */
+#ifndef MBEDTLS_GET_UINT64_LE
+#define MBEDTLS_GET_UINT64_LE( data, offset )                   \
+    (                                                           \
+          ( (uint64_t) ( data )[( offset ) + 7] << 56 )         \
+        | ( (uint64_t) ( data )[( offset ) + 6] << 48 )         \
+        | ( (uint64_t) ( data )[( offset ) + 5] << 40 )         \
+        | ( (uint64_t) ( data )[( offset ) + 4] << 32 )         \
+        | ( (uint64_t) ( data )[( offset ) + 3] << 24 )         \
+        | ( (uint64_t) ( data )[( offset ) + 2] << 16 )         \
+        | ( (uint64_t) ( data )[( offset ) + 1] <<  8 )         \
+        | ( (uint64_t) ( data )[( offset )    ]       )         \
+    )
+#endif
+
+/**
+ * Put in memory a 64 bits unsigned integer in little-endian order.
+ *
+ * \param   n       64 bits unsigned integer to put in memory.
+ * \param   data    Base address of the memory where to put the 64
+ *                  bits unsigned integer in.
+ * \param   offset  Offset from \p base where to put the least significant
+ *                  byte of the 64 bits unsigned integer \p n.
+ */
+#ifndef MBEDTLS_PUT_UINT64_LE
+#define MBEDTLS_PUT_UINT64_LE( n, data, offset )                \
+{                                                               \
+    ( data )[( offset )    ] = MBEDTLS_BYTE_0( n );             \
+    ( data )[( offset ) + 1] = MBEDTLS_BYTE_1( n );             \
+    ( data )[( offset ) + 2] = MBEDTLS_BYTE_2( n );             \
+    ( data )[( offset ) + 3] = MBEDTLS_BYTE_3( n );             \
+    ( data )[( offset ) + 4] = MBEDTLS_BYTE_4( n );             \
+    ( data )[( offset ) + 5] = MBEDTLS_BYTE_5( n );             \
+    ( data )[( offset ) + 6] = MBEDTLS_BYTE_6( n );             \
+    ( data )[( offset ) + 7] = MBEDTLS_BYTE_7( n );             \
+}
+#endif
+
 #endif /* MBEDTLS_LIBRARY_COMMON_H */
diff --git a/third_party/mbedtls/repo/library/constant_time.c b/third_party/mbedtls/repo/library/constant_time.c
new file mode 100644
index 0000000..18f1b20
--- /dev/null
+++ b/third_party/mbedtls/repo/library/constant_time.c
@@ -0,0 +1,819 @@
+/**
+ *  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.
+ */
+
+ /*
+ * The following functions are implemented without using comparison operators, as those
+ * might be translated to branches by some compilers on some platforms.
+ */
+
+#include "common.h"
+#include "constant_time_internal.h"
+#include "mbedtls/constant_time.h"
+#include "mbedtls/error.h"
+#include "mbedtls/platform_util.h"
+
+#if defined(MBEDTLS_BIGNUM_C)
+#include "mbedtls/bignum.h"
+#endif
+
+#if defined(MBEDTLS_SSL_TLS_C)
+#include "mbedtls/ssl_internal.h"
+#endif
+
+#if defined(MBEDTLS_RSA_C)
+#include "mbedtls/rsa.h"
+#endif
+
+#if defined(MBEDTLS_BASE64_C)
+#include "constant_time_invasive.h"
+#endif
+
+#include <string.h>
+
+int mbedtls_ct_memcmp( const void *a,
+                       const void *b,
+                       size_t n )
+{
+    size_t i;
+    volatile const unsigned char *A = (volatile const unsigned char *) a;
+    volatile const unsigned char *B = (volatile const unsigned char *) b;
+    volatile unsigned char diff = 0;
+
+    for( i = 0; i < n; i++ )
+    {
+        /* Read volatile data in order before computing diff.
+         * This avoids IAR compiler warning:
+         * 'the order of volatile accesses is undefined ..' */
+        unsigned char x = A[i], y = B[i];
+        diff |= x ^ y;
+    }
+
+    return( (int)diff );
+}
+
+unsigned mbedtls_ct_uint_mask( unsigned value )
+{
+    /* MSVC has a warning about unary minus on unsigned, but this is
+     * well-defined and precisely what we want to do here */
+#if defined(_MSC_VER)
+#pragma warning( push )
+#pragma warning( disable : 4146 )
+#endif
+    return( - ( ( value | - value ) >> ( sizeof( value ) * 8 - 1 ) ) );
+#if defined(_MSC_VER)
+#pragma warning( pop )
+#endif
+}
+
+#if defined(MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC)
+
+size_t mbedtls_ct_size_mask( size_t value )
+{
+    /* MSVC has a warning about unary minus on unsigned integer types,
+     * but this is well-defined and precisely what we want to do here. */
+#if defined(_MSC_VER)
+#pragma warning( push )
+#pragma warning( disable : 4146 )
+#endif
+    return( - ( ( value | - value ) >> ( sizeof( value ) * 8 - 1 ) ) );
+#if defined(_MSC_VER)
+#pragma warning( pop )
+#endif
+}
+
+#endif /* MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC */
+
+#if defined(MBEDTLS_BIGNUM_C)
+
+mbedtls_mpi_uint mbedtls_ct_mpi_uint_mask( mbedtls_mpi_uint value )
+{
+    /* MSVC has a warning about unary minus on unsigned, but this is
+     * well-defined and precisely what we want to do here */
+#if defined(_MSC_VER)
+#pragma warning( push )
+#pragma warning( disable : 4146 )
+#endif
+    return( - ( ( value | - value ) >> ( sizeof( value ) * 8 - 1 ) ) );
+#if defined(_MSC_VER)
+#pragma warning( pop )
+#endif
+}
+
+#endif /* MBEDTLS_BIGNUM_C */
+
+#if defined(MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC)
+
+/** Constant-flow mask generation for "less than" comparison:
+ * - if \p x < \p y, return all-bits 1, that is (size_t) -1
+ * - otherwise, return all bits 0, that is 0
+ *
+ * This function can be used to write constant-time code by replacing branches
+ * with bit operations using masks.
+ *
+ * \param x     The first value to analyze.
+ * \param y     The second value to analyze.
+ *
+ * \return      All-bits-one if \p x is less than \p y, otherwise zero.
+ */
+static size_t mbedtls_ct_size_mask_lt( size_t x,
+                                       size_t y )
+{
+    /* This has the most significant bit set if and only if x < y */
+    const size_t sub = x - y;
+
+    /* sub1 = (x < y) ? 1 : 0 */
+    const size_t sub1 = sub >> ( sizeof( sub ) * 8 - 1 );
+
+    /* mask = (x < y) ? 0xff... : 0x00... */
+    const size_t mask = mbedtls_ct_size_mask( sub1 );
+
+    return( mask );
+}
+
+size_t mbedtls_ct_size_mask_ge( size_t x,
+                                size_t y )
+{
+    return( ~mbedtls_ct_size_mask_lt( x, y ) );
+}
+
+#endif /* MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC */
+
+#if defined(MBEDTLS_BASE64_C)
+
+/* Return 0xff if low <= c <= high, 0 otherwise.
+ *
+ * Constant flow with respect to c.
+ */
+MBEDTLS_STATIC_TESTABLE
+unsigned char mbedtls_ct_uchar_mask_of_range( unsigned char low,
+                                              unsigned char high,
+                                              unsigned char c )
+{
+    /* low_mask is: 0 if low <= c, 0x...ff if low > c */
+    unsigned low_mask = ( (unsigned) c - low ) >> 8;
+    /* high_mask is: 0 if c <= high, 0x...ff if c > high */
+    unsigned high_mask = ( (unsigned) high - c ) >> 8;
+    return( ~( low_mask | high_mask ) & 0xff );
+}
+
+#endif /* MBEDTLS_BASE64_C */
+
+unsigned mbedtls_ct_size_bool_eq( size_t x,
+                                  size_t y )
+{
+    /* diff = 0 if x == y, non-zero otherwise */
+    const size_t diff = x ^ y;
+
+    /* MSVC has a warning about unary minus on unsigned integer types,
+     * but this is well-defined and precisely what we want to do here. */
+#if defined(_MSC_VER)
+#pragma warning( push )
+#pragma warning( disable : 4146 )
+#endif
+
+    /* diff_msb's most significant bit is equal to x != y */
+    const size_t diff_msb = ( diff | (size_t) -diff );
+
+#if defined(_MSC_VER)
+#pragma warning( pop )
+#endif
+
+    /* diff1 = (x != y) ? 1 : 0 */
+    const unsigned diff1 = diff_msb >> ( sizeof( diff_msb ) * 8 - 1 );
+
+    return( 1 ^ diff1 );
+}
+
+#if defined(MBEDTLS_PKCS1_V15) && defined(MBEDTLS_RSA_C) && !defined(MBEDTLS_RSA_ALT)
+
+/** Constant-flow "greater than" comparison:
+ * return x > y
+ *
+ * This is equivalent to \p x > \p y, but is likely to be compiled
+ * to code using bitwise operation rather than a branch.
+ *
+ * \param x     The first value to analyze.
+ * \param y     The second value to analyze.
+ *
+ * \return      1 if \p x greater than \p y, otherwise 0.
+ */
+static unsigned mbedtls_ct_size_gt( size_t x,
+                                    size_t y )
+{
+    /* Return the sign bit (1 for negative) of (y - x). */
+    return( ( y - x ) >> ( sizeof( size_t ) * 8 - 1 ) );
+}
+
+#endif /* MBEDTLS_PKCS1_V15 && MBEDTLS_RSA_C && ! MBEDTLS_RSA_ALT */
+
+#if defined(MBEDTLS_BIGNUM_C)
+
+unsigned mbedtls_ct_mpi_uint_lt( const mbedtls_mpi_uint x,
+                                 const mbedtls_mpi_uint y )
+{
+    mbedtls_mpi_uint ret;
+    mbedtls_mpi_uint cond;
+
+    /*
+     * Check if the most significant bits (MSB) of the operands are different.
+     */
+    cond = ( x ^ y );
+    /*
+     * If the MSB are the same then the difference x-y will be negative (and
+     * have its MSB set to 1 during conversion to unsigned) if and only if x<y.
+     */
+    ret = ( x - y ) & ~cond;
+    /*
+     * If the MSB are different, then the operand with the MSB of 1 is the
+     * bigger. (That is if y has MSB of 1, then x<y is true and it is false if
+     * the MSB of y is 0.)
+     */
+    ret |= y & cond;
+
+
+    ret = ret >> ( sizeof( mbedtls_mpi_uint ) * 8 - 1 );
+
+    return (unsigned) ret;
+}
+
+#endif /* MBEDTLS_BIGNUM_C */
+
+unsigned mbedtls_ct_uint_if( unsigned condition,
+                             unsigned if1,
+                             unsigned if0 )
+{
+    unsigned mask = mbedtls_ct_uint_mask( condition );
+    return( ( mask & if1 ) | (~mask & if0 ) );
+}
+
+#if defined(MBEDTLS_BIGNUM_C)
+
+/** Select between two sign values without branches.
+ *
+ * This is functionally equivalent to `condition ? if1 : if0` but uses only bit
+ * operations in order to avoid branches.
+ *
+ * \note if1 and if0 must be either 1 or -1, otherwise the result
+ *       is undefined.
+ *
+ * \param condition     Condition to test.
+ * \param if1           The first sign; must be either +1 or -1.
+ * \param if0           The second sign; must be either +1 or -1.
+ *
+ * \return  \c if1 if \p condition is nonzero, otherwise \c if0.
+ * */
+static int mbedtls_ct_cond_select_sign( unsigned char condition,
+                                        int if1,
+                                        int if0 )
+{
+    /* In order to avoid questions about what we can reasonably assume about
+     * the representations of signed integers, move everything to unsigned
+     * by taking advantage of the fact that if1 and if0 are either +1 or -1. */
+    unsigned uif1 = if1 + 1;
+    unsigned uif0 = if0 + 1;
+
+    /* condition was 0 or 1, mask is 0 or 2 as are uif1 and uif0 */
+    const unsigned mask = condition << 1;
+
+    /* select uif1 or uif0 */
+    unsigned ur = ( uif0 & ~mask ) | ( uif1 & mask );
+
+    /* ur is now 0 or 2, convert back to -1 or +1 */
+    return( (int) ur - 1 );
+}
+
+void mbedtls_ct_mpi_uint_cond_assign( size_t n,
+                                      mbedtls_mpi_uint *dest,
+                                      const mbedtls_mpi_uint *src,
+                                      unsigned char condition )
+{
+    size_t i;
+
+    /* MSVC has a warning about unary minus on unsigned integer types,
+     * but this is well-defined and precisely what we want to do here. */
+#if defined(_MSC_VER)
+#pragma warning( push )
+#pragma warning( disable : 4146 )
+#endif
+
+    /* all-bits 1 if condition is 1, all-bits 0 if condition is 0 */
+    const mbedtls_mpi_uint mask = -condition;
+
+#if defined(_MSC_VER)
+#pragma warning( pop )
+#endif
+
+    for( i = 0; i < n; i++ )
+        dest[i] = ( src[i] & mask ) | ( dest[i] & ~mask );
+}
+
+#endif /* MBEDTLS_BIGNUM_C */
+
+#if defined(MBEDTLS_BASE64_C)
+
+unsigned char mbedtls_ct_base64_enc_char( unsigned char value )
+{
+    unsigned char digit = 0;
+    /* For each range of values, if value is in that range, mask digit with
+     * the corresponding value. Since value can only be in a single range,
+     * only at most one masking will change digit. */
+    digit |= mbedtls_ct_uchar_mask_of_range(  0, 25, value ) & ( 'A' + value );
+    digit |= mbedtls_ct_uchar_mask_of_range( 26, 51, value ) & ( 'a' + value - 26 );
+    digit |= mbedtls_ct_uchar_mask_of_range( 52, 61, value ) & ( '0' + value - 52 );
+    digit |= mbedtls_ct_uchar_mask_of_range( 62, 62, value ) & '+';
+    digit |= mbedtls_ct_uchar_mask_of_range( 63, 63, value ) & '/';
+    return( digit );
+}
+
+signed char mbedtls_ct_base64_dec_value( unsigned char c )
+{
+    unsigned char val = 0;
+    /* For each range of digits, if c is in that range, mask val with
+     * the corresponding value. Since c can only be in a single range,
+     * only at most one masking will change val. Set val to one plus
+     * the desired value so that it stays 0 if c is in none of the ranges. */
+    val |= mbedtls_ct_uchar_mask_of_range( 'A', 'Z', c ) & ( c - 'A' +  0 + 1 );
+    val |= mbedtls_ct_uchar_mask_of_range( 'a', 'z', c ) & ( c - 'a' + 26 + 1 );
+    val |= mbedtls_ct_uchar_mask_of_range( '0', '9', c ) & ( c - '0' + 52 + 1 );
+    val |= mbedtls_ct_uchar_mask_of_range( '+', '+', c ) & ( c - '+' + 62 + 1 );
+    val |= mbedtls_ct_uchar_mask_of_range( '/', '/', c ) & ( c - '/' + 63 + 1 );
+    /* At this point, val is 0 if c is an invalid digit and v+1 if c is
+     * a digit with the value v. */
+    return( val - 1 );
+}
+
+#endif /* MBEDTLS_BASE64_C */
+
+#if defined(MBEDTLS_PKCS1_V15) && defined(MBEDTLS_RSA_C) && !defined(MBEDTLS_RSA_ALT)
+
+/** Shift some data towards the left inside a buffer.
+ *
+ * `mbedtls_ct_mem_move_to_left(start, total, offset)` is functionally
+ * equivalent to
+ * ```
+ * memmove(start, start + offset, total - offset);
+ * memset(start + offset, 0, total - offset);
+ * ```
+ * but it strives to use a memory access pattern (and thus total timing)
+ * that does not depend on \p offset. This timing independence comes at
+ * the expense of performance.
+ *
+ * \param start     Pointer to the start of the buffer.
+ * \param total     Total size of the buffer.
+ * \param offset    Offset from which to copy \p total - \p offset bytes.
+ */
+static void mbedtls_ct_mem_move_to_left( void *start,
+                                         size_t total,
+                                         size_t offset )
+{
+    volatile unsigned char *buf = start;
+    size_t i, n;
+    if( total == 0 )
+        return;
+    for( i = 0; i < total; i++ )
+    {
+        unsigned no_op = mbedtls_ct_size_gt( total - offset, i );
+        /* The first `total - offset` passes are a no-op. The last
+         * `offset` passes shift the data one byte to the left and
+         * zero out the last byte. */
+        for( n = 0; n < total - 1; n++ )
+        {
+            unsigned char current = buf[n];
+            unsigned char next = buf[n+1];
+            buf[n] = mbedtls_ct_uint_if( no_op, current, next );
+        }
+        buf[total-1] = mbedtls_ct_uint_if( no_op, buf[total-1], 0 );
+    }
+}
+
+#endif /* MBEDTLS_PKCS1_V15 && MBEDTLS_RSA_C && ! MBEDTLS_RSA_ALT */
+
+#if defined(MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC)
+
+void mbedtls_ct_memcpy_if_eq( unsigned char *dest,
+                              const unsigned char *src,
+                              size_t len,
+                              size_t c1,
+                              size_t c2 )
+{
+    /* mask = c1 == c2 ? 0xff : 0x00 */
+    const size_t equal = mbedtls_ct_size_bool_eq( c1, c2 );
+    const unsigned char mask = (unsigned char) mbedtls_ct_size_mask( equal );
+
+    /* dest[i] = c1 == c2 ? src[i] : dest[i] */
+    for( size_t i = 0; i < len; i++ )
+        dest[i] = ( src[i] & mask ) | ( dest[i] & ~mask );
+}
+
+void mbedtls_ct_memcpy_offset( unsigned char *dest,
+                               const unsigned char *src,
+                               size_t offset,
+                               size_t offset_min,
+                               size_t offset_max,
+                               size_t len )
+{
+    size_t offsetval;
+
+    for( offsetval = offset_min; offsetval <= offset_max; offsetval++ )
+    {
+        mbedtls_ct_memcpy_if_eq( dest, src + offsetval, len,
+                                 offsetval, offset );
+    }
+}
+
+int mbedtls_ct_hmac( mbedtls_md_context_t *ctx,
+                     const unsigned char *add_data,
+                     size_t add_data_len,
+                     const unsigned char *data,
+                     size_t data_len_secret,
+                     size_t min_data_len,
+                     size_t max_data_len,
+                     unsigned char *output )
+{
+    /*
+     * This function breaks the HMAC abstraction and uses the md_clone()
+     * extension to the MD API in order to get constant-flow behaviour.
+     *
+     * HMAC(msg) is defined as HASH(okey + HASH(ikey + msg)) where + means
+     * concatenation, and okey/ikey are the XOR of the key with some fixed bit
+     * patterns (see RFC 2104, sec. 2), which are stored in ctx->hmac_ctx.
+     *
+     * We'll first compute inner_hash = HASH(ikey + msg) by hashing up to
+     * minlen, then cloning the context, and for each byte up to maxlen
+     * finishing up the hash computation, keeping only the correct result.
+     *
+     * Then we only need to compute HASH(okey + inner_hash) and we're done.
+     */
+    const mbedtls_md_type_t md_alg = mbedtls_md_get_type( ctx->md_info );
+    /* TLS 1.0-1.2 only support SHA-384, SHA-256, SHA-1, MD-5,
+     * all of which have the same block size except SHA-384. */
+    const size_t block_size = md_alg == MBEDTLS_MD_SHA384 ? 128 : 64;
+    const unsigned char * const ikey = ctx->hmac_ctx;
+    const unsigned char * const okey = ikey + block_size;
+    const size_t hash_size = mbedtls_md_get_size( ctx->md_info );
+
+    unsigned char aux_out[MBEDTLS_MD_MAX_SIZE];
+    mbedtls_md_context_t aux;
+    size_t offset;
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+
+    mbedtls_md_init( &aux );
+
+#define MD_CHK( func_call ) \
+    do {                    \
+        ret = (func_call);  \
+        if( ret != 0 )      \
+            goto cleanup;   \
+    } while( 0 )
+
+    MD_CHK( mbedtls_md_setup( &aux, ctx->md_info, 0 ) );
+
+    /* After hmac_start() of hmac_reset(), ikey has already been hashed,
+     * so we can start directly with the message */
+    MD_CHK( mbedtls_md_update( ctx, add_data, add_data_len ) );
+    MD_CHK( mbedtls_md_update( ctx, data, min_data_len ) );
+
+    /* For each possible length, compute the hash up to that point */
+    for( offset = min_data_len; offset <= max_data_len; offset++ )
+    {
+        MD_CHK( mbedtls_md_clone( &aux, ctx ) );
+        MD_CHK( mbedtls_md_finish( &aux, aux_out ) );
+        /* Keep only the correct inner_hash in the output buffer */
+        mbedtls_ct_memcpy_if_eq( output, aux_out, hash_size,
+                                 offset, data_len_secret );
+
+        if( offset < max_data_len )
+            MD_CHK( mbedtls_md_update( ctx, data + offset, 1 ) );
+    }
+
+    /* The context needs to finish() before it starts() again */
+    MD_CHK( mbedtls_md_finish( ctx, aux_out ) );
+
+    /* Now compute HASH(okey + inner_hash) */
+    MD_CHK( mbedtls_md_starts( ctx ) );
+    MD_CHK( mbedtls_md_update( ctx, okey, block_size ) );
+    MD_CHK( mbedtls_md_update( ctx, output, hash_size ) );
+    MD_CHK( mbedtls_md_finish( ctx, output ) );
+
+    /* Done, get ready for next time */
+    MD_CHK( mbedtls_md_hmac_reset( ctx ) );
+
+#undef MD_CHK
+
+cleanup:
+    mbedtls_md_free( &aux );
+    return( ret );
+}
+
+#endif /* MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC */
+
+#if defined(MBEDTLS_BIGNUM_C)
+
+#define MPI_VALIDATE_RET( cond )                                       \
+    MBEDTLS_INTERNAL_VALIDATE_RET( cond, MBEDTLS_ERR_MPI_BAD_INPUT_DATA )
+
+/*
+ * Conditionally assign X = Y, without leaking information
+ * about whether the assignment was made or not.
+ * (Leaking information about the respective sizes of X and Y is ok however.)
+ */
+int mbedtls_mpi_safe_cond_assign( mbedtls_mpi *X,
+                                  const mbedtls_mpi *Y,
+                                  unsigned char assign )
+{
+    int ret = 0;
+    size_t i;
+    mbedtls_mpi_uint limb_mask;
+    MPI_VALIDATE_RET( X != NULL );
+    MPI_VALIDATE_RET( Y != NULL );
+
+    /* all-bits 1 if assign is 1, all-bits 0 if assign is 0 */
+    limb_mask = mbedtls_ct_mpi_uint_mask( assign );;
+
+    MBEDTLS_MPI_CHK( mbedtls_mpi_grow( X, Y->n ) );
+
+    X->s = mbedtls_ct_cond_select_sign( assign, Y->s, X->s );
+
+    mbedtls_ct_mpi_uint_cond_assign( Y->n, X->p, Y->p, assign );
+
+    for( i = Y->n; i < X->n; i++ )
+        X->p[i] &= ~limb_mask;
+
+cleanup:
+    return( ret );
+}
+
+/*
+ * Conditionally swap X and Y, without leaking information
+ * about whether the swap was made or not.
+ * Here it is not ok to simply swap the pointers, which whould lead to
+ * different memory access patterns when X and Y are used afterwards.
+ */
+int mbedtls_mpi_safe_cond_swap( mbedtls_mpi *X,
+                                mbedtls_mpi *Y,
+                                unsigned char swap )
+{
+    int ret, s;
+    size_t i;
+    mbedtls_mpi_uint limb_mask;
+    mbedtls_mpi_uint tmp;
+    MPI_VALIDATE_RET( X != NULL );
+    MPI_VALIDATE_RET( Y != NULL );
+
+    if( X == Y )
+        return( 0 );
+
+    /* all-bits 1 if swap is 1, all-bits 0 if swap is 0 */
+    limb_mask = mbedtls_ct_mpi_uint_mask( swap );
+
+    MBEDTLS_MPI_CHK( mbedtls_mpi_grow( X, Y->n ) );
+    MBEDTLS_MPI_CHK( mbedtls_mpi_grow( Y, X->n ) );
+
+    s = X->s;
+    X->s = mbedtls_ct_cond_select_sign( swap, Y->s, X->s );
+    Y->s = mbedtls_ct_cond_select_sign( swap, s, Y->s );
+
+
+    for( i = 0; i < X->n; i++ )
+    {
+        tmp = X->p[i];
+        X->p[i] = ( X->p[i] & ~limb_mask ) | ( Y->p[i] & limb_mask );
+        Y->p[i] = ( Y->p[i] & ~limb_mask ) | (     tmp & limb_mask );
+    }
+
+cleanup:
+    return( ret );
+}
+
+/*
+ * Compare signed values in constant time
+ */
+int mbedtls_mpi_lt_mpi_ct( const mbedtls_mpi *X,
+                           const mbedtls_mpi *Y,
+                           unsigned *ret )
+{
+    size_t i;
+    /* The value of any of these variables is either 0 or 1 at all times. */
+    unsigned cond, done, X_is_negative, Y_is_negative;
+
+    MPI_VALIDATE_RET( X != NULL );
+    MPI_VALIDATE_RET( Y != NULL );
+    MPI_VALIDATE_RET( ret != NULL );
+
+    if( X->n != Y->n )
+        return MBEDTLS_ERR_MPI_BAD_INPUT_DATA;
+
+    /*
+     * Set sign_N to 1 if N >= 0, 0 if N < 0.
+     * We know that N->s == 1 if N >= 0 and N->s == -1 if N < 0.
+     */
+    X_is_negative = ( X->s & 2 ) >> 1;
+    Y_is_negative = ( Y->s & 2 ) >> 1;
+
+    /*
+     * If the signs are different, then the positive operand is the bigger.
+     * That is if X is negative (X_is_negative == 1), then X < Y is true and it
+     * is false if X is positive (X_is_negative == 0).
+     */
+    cond = ( X_is_negative ^ Y_is_negative );
+    *ret = cond & X_is_negative;
+
+    /*
+     * This is a constant-time function. We might have the result, but we still
+     * need to go through the loop. Record if we have the result already.
+     */
+    done = cond;
+
+    for( i = X->n; i > 0; i-- )
+    {
+        /*
+         * If Y->p[i - 1] < X->p[i - 1] then X < Y is true if and only if both
+         * X and Y are negative.
+         *
+         * Again even if we can make a decision, we just mark the result and
+         * the fact that we are done and continue looping.
+         */
+        cond = mbedtls_ct_mpi_uint_lt( Y->p[i - 1], X->p[i - 1] );
+        *ret |= cond & ( 1 - done ) & X_is_negative;
+        done |= cond;
+
+        /*
+         * If X->p[i - 1] < Y->p[i - 1] then X < Y is true if and only if both
+         * X and Y are positive.
+         *
+         * Again even if we can make a decision, we just mark the result and
+         * the fact that we are done and continue looping.
+         */
+        cond = mbedtls_ct_mpi_uint_lt( X->p[i - 1], Y->p[i - 1] );
+        *ret |= cond & ( 1 - done ) & ( 1 - X_is_negative );
+        done |= cond;
+    }
+
+    return( 0 );
+}
+
+#endif /* MBEDTLS_BIGNUM_C */
+
+#if defined(MBEDTLS_PKCS1_V15) && defined(MBEDTLS_RSA_C) && !defined(MBEDTLS_RSA_ALT)
+
+int mbedtls_ct_rsaes_pkcs1_v15_unpadding( int mode,
+                                          unsigned char *input,
+                                          size_t ilen,
+                                          unsigned char *output,
+                                          size_t output_max_len,
+                                          size_t *olen )
+{
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+    size_t i, plaintext_max_size;
+
+    /* The following variables take sensitive values: their value must
+     * not leak into the observable behavior of the function other than
+     * the designated outputs (output, olen, return value). Otherwise
+     * this would open the execution of the function to
+     * side-channel-based variants of the Bleichenbacher padding oracle
+     * attack. Potential side channels include overall timing, memory
+     * access patterns (especially visible to an adversary who has access
+     * to a shared memory cache), and branches (especially visible to
+     * an adversary who has access to a shared code cache or to a shared
+     * branch predictor). */
+    size_t pad_count = 0;
+    unsigned bad = 0;
+    unsigned char pad_done = 0;
+    size_t plaintext_size = 0;
+    unsigned output_too_large;
+
+    plaintext_max_size = ( output_max_len > ilen - 11 ) ? ilen - 11
+                                                        : output_max_len;
+
+    /* Check and get padding length in constant time and constant
+     * memory trace. The first byte must be 0. */
+    bad |= input[0];
+
+    if( mode == MBEDTLS_RSA_PRIVATE )
+    {
+        /* Decode EME-PKCS1-v1_5 padding: 0x00 || 0x02 || PS || 0x00
+         * where PS must be at least 8 nonzero bytes. */
+        bad |= input[1] ^ MBEDTLS_RSA_CRYPT;
+
+        /* Read the whole buffer. Set pad_done to nonzero if we find
+         * the 0x00 byte and remember the padding length in pad_count. */
+        for( i = 2; i < ilen; i++ )
+        {
+            pad_done  |= ((input[i] | (unsigned char)-input[i]) >> 7) ^ 1;
+            pad_count += ((pad_done | (unsigned char)-pad_done) >> 7) ^ 1;
+        }
+    }
+    else
+    {
+        /* Decode EMSA-PKCS1-v1_5 padding: 0x00 || 0x01 || PS || 0x00
+         * where PS must be at least 8 bytes with the value 0xFF. */
+        bad |= input[1] ^ MBEDTLS_RSA_SIGN;
+
+        /* Read the whole buffer. Set pad_done to nonzero if we find
+         * the 0x00 byte and remember the padding length in pad_count.
+         * If there's a non-0xff byte in the padding, the padding is bad. */
+        for( i = 2; i < ilen; i++ )
+        {
+            pad_done |= mbedtls_ct_uint_if( input[i], 0, 1 );
+            pad_count += mbedtls_ct_uint_if( pad_done, 0, 1 );
+            bad |= mbedtls_ct_uint_if( pad_done, 0, input[i] ^ 0xFF );
+        }
+    }
+
+    /* If pad_done is still zero, there's no data, only unfinished padding. */
+    bad |= mbedtls_ct_uint_if( pad_done, 0, 1 );
+
+    /* There must be at least 8 bytes of padding. */
+    bad |= mbedtls_ct_size_gt( 8, pad_count );
+
+    /* If the padding is valid, set plaintext_size to the number of
+     * remaining bytes after stripping the padding. If the padding
+     * is invalid, avoid leaking this fact through the size of the
+     * output: use the maximum message size that fits in the output
+     * buffer. Do it without branches to avoid leaking the padding
+     * validity through timing. RSA keys are small enough that all the
+     * size_t values involved fit in unsigned int. */
+    plaintext_size = mbedtls_ct_uint_if(
+                        bad, (unsigned) plaintext_max_size,
+                        (unsigned) ( ilen - pad_count - 3 ) );
+
+    /* Set output_too_large to 0 if the plaintext fits in the output
+     * buffer and to 1 otherwise. */
+    output_too_large = mbedtls_ct_size_gt( plaintext_size,
+                                           plaintext_max_size );
+
+    /* Set ret without branches to avoid timing attacks. Return:
+     * - INVALID_PADDING if the padding is bad (bad != 0).
+     * - OUTPUT_TOO_LARGE if the padding is good but the decrypted
+     *   plaintext does not fit in the output buffer.
+     * - 0 if the padding is correct. */
+    ret = - (int) mbedtls_ct_uint_if(
+                    bad, - MBEDTLS_ERR_RSA_INVALID_PADDING,
+                    mbedtls_ct_uint_if( output_too_large,
+                                        - MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE,
+                                        0 ) );
+
+    /* If the padding is bad or the plaintext is too large, zero the
+     * data that we're about to copy to the output buffer.
+     * We need to copy the same amount of data
+     * from the same buffer whether the padding is good or not to
+     * avoid leaking the padding validity through overall timing or
+     * through memory or cache access patterns. */
+    bad = mbedtls_ct_uint_mask( bad | output_too_large );
+    for( i = 11; i < ilen; i++ )
+        input[i] &= ~bad;
+
+    /* If the plaintext is too large, truncate it to the buffer size.
+     * Copy anyway to avoid revealing the length through timing, because
+     * revealing the length is as bad as revealing the padding validity
+     * for a Bleichenbacher attack. */
+    plaintext_size = mbedtls_ct_uint_if( output_too_large,
+                                         (unsigned) plaintext_max_size,
+                                         (unsigned) plaintext_size );
+
+    /* Move the plaintext to the leftmost position where it can start in
+     * the working buffer, i.e. make it start plaintext_max_size from
+     * the end of the buffer. Do this with a memory access trace that
+     * does not depend on the plaintext size. After this move, the
+     * starting location of the plaintext is no longer sensitive
+     * information. */
+    mbedtls_ct_mem_move_to_left( input + ilen - plaintext_max_size,
+                                 plaintext_max_size,
+                                 plaintext_max_size - plaintext_size );
+
+    /* Finally copy the decrypted plaintext plus trailing zeros into the output
+     * buffer. If output_max_len is 0, then output may be an invalid pointer
+     * and the result of memcpy() would be undefined; prevent undefined
+     * behavior making sure to depend only on output_max_len (the size of the
+     * user-provided output buffer), which is independent from plaintext
+     * length, validity of padding, success of the decryption, and other
+     * secrets. */
+    if( output_max_len != 0 )
+        memcpy( output, input + ilen - plaintext_max_size, plaintext_max_size );
+
+    /* Report the amount of data we copied to the output buffer. In case
+     * of errors (bad padding or output too large), the value of *olen
+     * when this function returns is not specified. Making it equivalent
+     * to the good case limits the risks of leaking the padding validity. */
+    *olen = plaintext_size;
+
+    return( ret );
+}
+
+#endif /* MBEDTLS_PKCS1_V15 && MBEDTLS_RSA_C && ! MBEDTLS_RSA_ALT */
diff --git a/third_party/mbedtls/repo/library/constant_time_internal.h b/third_party/mbedtls/repo/library/constant_time_internal.h
new file mode 100644
index 0000000..bbb3a90
--- /dev/null
+++ b/third_party/mbedtls/repo/library/constant_time_internal.h
@@ -0,0 +1,329 @@
+/**
+ *  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.
+ */
+
+#ifndef MBEDTLS_CONSTANT_TIME_INTERNAL_H
+#define MBEDTLS_CONSTANT_TIME_INTERNAL_H
+
+#include "common.h"
+
+#if defined(MBEDTLS_BIGNUM_C)
+#include "mbedtls/bignum.h"
+#endif
+
+#if defined(MBEDTLS_SSL_TLS_C)
+#include "mbedtls/ssl_internal.h"
+#endif
+
+#include <stddef.h>
+
+
+/** Turn a value into a mask:
+ * - if \p value == 0, return the all-bits 0 mask, aka 0
+ * - otherwise, return the all-bits 1 mask, aka (unsigned) -1
+ *
+ * This function can be used to write constant-time code by replacing branches
+ * with bit operations using masks.
+ *
+ * \param value     The value to analyze.
+ *
+ * \return          Zero if \p value is zero, otherwise all-bits-one.
+ */
+unsigned mbedtls_ct_uint_mask( unsigned value );
+
+#if defined(MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC)
+
+/** Turn a value into a mask:
+ * - if \p value == 0, return the all-bits 0 mask, aka 0
+ * - otherwise, return the all-bits 1 mask, aka (size_t) -1
+ *
+ * This function can be used to write constant-time code by replacing branches
+ * with bit operations using masks.
+ *
+ * \param value     The value to analyze.
+ *
+ * \return          Zero if \p value is zero, otherwise all-bits-one.
+ */
+size_t mbedtls_ct_size_mask( size_t value );
+
+#endif /* MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC */
+
+#if defined(MBEDTLS_BIGNUM_C)
+
+/** Turn a value into a mask:
+ * - if \p value == 0, return the all-bits 0 mask, aka 0
+ * - otherwise, return the all-bits 1 mask, aka (mbedtls_mpi_uint) -1
+ *
+ * This function can be used to write constant-time code by replacing branches
+ * with bit operations using masks.
+ *
+ * \param value     The value to analyze.
+ *
+ * \return          Zero if \p value is zero, otherwise all-bits-one.
+ */
+mbedtls_mpi_uint mbedtls_ct_mpi_uint_mask( mbedtls_mpi_uint value );
+
+#endif /* MBEDTLS_BIGNUM_C */
+
+#if defined(MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC)
+
+/** Constant-flow mask generation for "greater or equal" comparison:
+ * - if \p x >= \p y, return all-bits 1, that is (size_t) -1
+ * - otherwise, return all bits 0, that is 0
+ *
+ * This function can be used to write constant-time code by replacing branches
+ * with bit operations using masks.
+ *
+ * \param x     The first value to analyze.
+ * \param y     The second value to analyze.
+ *
+ * \return      All-bits-one if \p x is greater or equal than \p y,
+ *              otherwise zero.
+ */
+size_t mbedtls_ct_size_mask_ge( size_t x,
+                                size_t y );
+
+#endif /* MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC */
+
+/** Constant-flow boolean "equal" comparison:
+ * return x == y
+ *
+ * This is equivalent to \p x == \p y, but is likely to be compiled
+ * to code using bitwise operation rather than a branch.
+ *
+ * \param x     The first value to analyze.
+ * \param y     The second value to analyze.
+ *
+ * \return      1 if \p x equals to \p y, otherwise 0.
+ */
+unsigned mbedtls_ct_size_bool_eq( size_t x,
+                                  size_t y );
+
+#if defined(MBEDTLS_BIGNUM_C)
+
+/** Decide if an integer is less than the other, without branches.
+ *
+ * This is equivalent to \p x < \p y, but is likely to be compiled
+ * to code using bitwise operation rather than a branch.
+ *
+ * \param x     The first value to analyze.
+ * \param y     The second value to analyze.
+ *
+ * \return      1 if \p x is less than \p y, otherwise 0.
+ */
+unsigned mbedtls_ct_mpi_uint_lt( const mbedtls_mpi_uint x,
+                                 const mbedtls_mpi_uint y );
+
+#endif /* MBEDTLS_BIGNUM_C */
+
+/** Choose between two integer values without branches.
+ *
+ * This is equivalent to `condition ? if1 : if0`, but is likely to be compiled
+ * to code using bitwise operation rather than a branch.
+ *
+ * \param condition     Condition to test.
+ * \param if1           Value to use if \p condition is nonzero.
+ * \param if0           Value to use if \p condition is zero.
+ *
+ * \return  \c if1 if \p condition is nonzero, otherwise \c if0.
+ */
+unsigned mbedtls_ct_uint_if( unsigned condition,
+                             unsigned if1,
+                             unsigned if0 );
+
+#if defined(MBEDTLS_BIGNUM_C)
+
+/** Conditionally assign a value without branches.
+ *
+ * This is equivalent to `if ( condition ) dest = src`, but is likely
+ * to be compiled to code using bitwise operation rather than a branch.
+ *
+ * \param n             \p dest and \p src must be arrays of limbs of size n.
+ * \param dest          The MPI to conditionally assign to. This must point
+ *                      to an initialized MPI.
+ * \param src           The MPI to be assigned from. This must point to an
+ *                      initialized MPI.
+ * \param condition     Condition to test, must be 0 or 1.
+ */
+void mbedtls_ct_mpi_uint_cond_assign( size_t n,
+                                      mbedtls_mpi_uint *dest,
+                                      const mbedtls_mpi_uint *src,
+                                      unsigned char condition );
+
+#endif /* MBEDTLS_BIGNUM_C */
+
+#if defined(MBEDTLS_BASE64_C)
+
+/** Given a value in the range 0..63, return the corresponding Base64 digit.
+ *
+ * The implementation assumes that letters are consecutive (e.g. ASCII
+ * but not EBCDIC).
+ *
+ * \param value     A value in the range 0..63.
+ *
+ * \return          A base64 digit converted from \p value.
+ */
+unsigned char mbedtls_ct_base64_enc_char( unsigned char value );
+
+/** Given a Base64 digit, return its value.
+ *
+ * If c is not a Base64 digit ('A'..'Z', 'a'..'z', '0'..'9', '+' or '/'),
+ * return -1.
+ *
+ * The implementation assumes that letters are consecutive (e.g. ASCII
+ * but not EBCDIC).
+ *
+ * \param c     A base64 digit.
+ *
+ * \return      The value of the base64 digit \p c.
+ */
+signed char mbedtls_ct_base64_dec_value( unsigned char c );
+
+#endif /* MBEDTLS_BASE64_C */
+
+#if defined(MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC)
+
+/** Conditional memcpy without branches.
+ *
+ * This is equivalent to `if ( c1 == c2 ) memcpy(dest, src, len)`, but is likely
+ * to be compiled to code using bitwise operation rather than a branch.
+ *
+ * \param dest      The pointer to conditionally copy to.
+ * \param src       The pointer to copy from. Shouldn't overlap with \p dest.
+ * \param len       The number of bytes to copy.
+ * \param c1        The first value to analyze in the condition.
+ * \param c2        The second value to analyze in the condition.
+ */
+void mbedtls_ct_memcpy_if_eq( unsigned char *dest,
+                              const unsigned char *src,
+                              size_t len,
+                              size_t c1, size_t c2 );
+
+/** Copy data from a secret position with constant flow.
+ *
+ * This function copies \p len bytes from \p src_base + \p offset_secret to \p
+ * dst, with a code flow and memory access pattern that does not depend on \p
+ * offset_secret, but only on \p offset_min, \p offset_max and \p len.
+ * Functionally equivalent to `memcpy(dst, src + offset_secret, len)`.
+ *
+ * \param dest          The destination buffer. This must point to a writable
+ *                      buffer of at least \p len bytes.
+ * \param src           The base of the source buffer. This must point to a
+ *                      readable buffer of at least \p offset_max + \p len
+ *                      bytes. Shouldn't overlap with \p dest.
+ * \param offset        The offset in the source buffer from which to copy.
+ *                      This must be no less than \p offset_min and no greater
+ *                      than \p offset_max.
+ * \param offset_min    The minimal value of \p offset.
+ * \param offset_max    The maximal value of \p offset.
+ * \param len           The number of bytes to copy.
+ */
+void mbedtls_ct_memcpy_offset( unsigned char *dest,
+                               const unsigned char *src,
+                               size_t offset,
+                               size_t offset_min,
+                               size_t offset_max,
+                               size_t len );
+
+/** Compute the HMAC of variable-length data with constant flow.
+ *
+ * This function computes the HMAC of the concatenation of \p add_data and \p
+ * data, and does with a code flow and memory access pattern that does not
+ * depend on \p data_len_secret, but only on \p min_data_len and \p
+ * max_data_len. In particular, this function always reads exactly \p
+ * max_data_len bytes from \p data.
+ *
+ * \param ctx               The HMAC context. It must have keys configured
+ *                          with mbedtls_md_hmac_starts() and use one of the
+ *                          following hashes: SHA-384, SHA-256, SHA-1 or MD-5.
+ *                          It is reset using mbedtls_md_hmac_reset() after
+ *                          the computation is complete to prepare for the
+ *                          next computation.
+ * \param add_data          The first part of the message whose HMAC is being
+ *                          calculated. This must point to a readable buffer
+ *                          of \p add_data_len bytes.
+ * \param add_data_len      The length of \p add_data in bytes.
+ * \param data              The buffer containing the second part of the
+ *                          message. This must point to a readable buffer
+ *                          of \p max_data_len bytes.
+ * \param data_len_secret   The length of the data to process in \p data.
+ *                          This must be no less than \p min_data_len and no
+ *                          greater than \p max_data_len.
+ * \param min_data_len      The minimal length of the second part of the
+ *                          message, read from \p data.
+ * \param max_data_len      The maximal length of the second part of the
+ *                          message, read from \p data.
+ * \param output            The HMAC will be written here. This must point to
+ *                          a writable buffer of sufficient size to hold the
+ *                          HMAC value.
+ *
+ * \retval 0 on success.
+ * \retval #MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED
+ *         The hardware accelerator failed.
+ */
+int mbedtls_ct_hmac( mbedtls_md_context_t *ctx,
+                     const unsigned char *add_data,
+                     size_t add_data_len,
+                     const unsigned char *data,
+                     size_t data_len_secret,
+                     size_t min_data_len,
+                     size_t max_data_len,
+                     unsigned char *output );
+
+#endif /* MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC */
+
+#if defined(MBEDTLS_PKCS1_V15) && defined(MBEDTLS_RSA_C) && !defined(MBEDTLS_RSA_ALT)
+
+/** This function performs the unpadding part of a PKCS#1 v1.5 decryption
+ *  operation (EME-PKCS1-v1_5 decoding).
+ *
+ * \note The return value from this function is a sensitive value
+ *       (this is unusual). #MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE shouldn't happen
+ *       in a well-written application, but 0 vs #MBEDTLS_ERR_RSA_INVALID_PADDING
+ *       is often a situation that an attacker can provoke and leaking which
+ *       one is the result is precisely the information the attacker wants.
+ *
+ * \param mode           The mode of operation. This must be either
+ *                       #MBEDTLS_RSA_PRIVATE or #MBEDTLS_RSA_PUBLIC (deprecated).
+ * \param input          The input buffer which is the payload inside PKCS#1v1.5
+ *                       encryption padding, called the "encoded message EM"
+ *                       by the terminology.
+ * \param ilen           The length of the payload in the \p input buffer.
+ * \param output         The buffer for the payload, called "message M" by the
+ *                       PKCS#1 terminology. This must be a writable buffer of
+ *                       length \p output_max_len bytes.
+ * \param olen           The address at which to store the length of
+ *                       the payload. This must not be \c NULL.
+ * \param output_max_len The length in bytes of the output buffer \p output.
+ *
+ * \return      \c 0 on success.
+ * \return      #MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE
+ *              The output buffer is too small for the unpadded payload.
+ * \return      #MBEDTLS_ERR_RSA_INVALID_PADDING
+ *              The input doesn't contain properly formatted padding.
+ */
+int mbedtls_ct_rsaes_pkcs1_v15_unpadding( int mode,
+                                          unsigned char *input,
+                                          size_t ilen,
+                                          unsigned char *output,
+                                          size_t output_max_len,
+                                          size_t *olen );
+
+#endif /* MBEDTLS_PKCS1_V15 && MBEDTLS_RSA_C && ! MBEDTLS_RSA_ALT */
+
+#endif /* MBEDTLS_CONSTANT_TIME_INTERNAL_H */
diff --git a/third_party/mbedtls/repo/library/constant_time_invasive.h b/third_party/mbedtls/repo/library/constant_time_invasive.h
new file mode 100644
index 0000000..4620ca1
--- /dev/null
+++ b/third_party/mbedtls/repo/library/constant_time_invasive.h
@@ -0,0 +1,51 @@
+/**
+ * \file constant_time_invasive.h
+ *
+ * \brief Constant-time module: interfaces for invasive testing only.
+ *
+ * The interfaces in this file are intended for testing purposes only.
+ * They SHOULD NOT be made available in library integrations except when
+ * building the library for testing.
+ */
+/*
+ *  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.
+ */
+
+#ifndef MBEDTLS_CONSTANT_TIME_INVASIVE_H
+#define MBEDTLS_CONSTANT_TIME_INVASIVE_H
+
+#include "common.h"
+
+#if defined(MBEDTLS_TEST_HOOKS)
+
+/** Turn a value into a mask:
+ * - if \p low <= \p c <= \p high,
+ *   return the all-bits 1 mask, aka (unsigned) -1
+ * - otherwise, return the all-bits 0 mask, aka 0
+ *
+ * \param low   The value to analyze.
+ * \param high  The value to analyze.
+ * \param c     The value to analyze.
+ *
+ * \return      All-bits-one if \p low <= \p c <= \p high, otherwise zero.
+ */
+unsigned char mbedtls_ct_uchar_mask_of_range( unsigned char low,
+                                              unsigned char high,
+                                              unsigned char c );
+
+#endif /* MBEDTLS_TEST_HOOKS */
+
+#endif /* MBEDTLS_CONSTANT_TIME_INVASIVE_H */
diff --git a/third_party/mbedtls/repo/library/ctr_drbg.c b/third_party/mbedtls/repo/library/ctr_drbg.c
index 023aac5..a604ec0 100644
--- a/third_party/mbedtls/repo/library/ctr_drbg.c
+++ b/third_party/mbedtls/repo/library/ctr_drbg.c
@@ -56,10 +56,6 @@
     ctx->reseed_counter = -1;
 
     ctx->reseed_interval = MBEDTLS_CTR_DRBG_RESEED_INTERVAL;
-
-#if defined(MBEDTLS_THREADING_C)
-    mbedtls_mutex_init( &ctx->mutex );
-#endif
 }
 
 /*
@@ -72,15 +68,14 @@
         return;
 
 #if defined(MBEDTLS_THREADING_C)
-    mbedtls_mutex_free( &ctx->mutex );
+    /* The mutex is initialized iff f_entropy is set. */
+    if( ctx->f_entropy != NULL )
+        mbedtls_mutex_free( &ctx->mutex );
 #endif
     mbedtls_aes_free( &ctx->aes_ctx );
     mbedtls_platform_zeroize( ctx, sizeof( mbedtls_ctr_drbg_context ) );
     ctx->reseed_interval = MBEDTLS_CTR_DRBG_RESEED_INTERVAL;
     ctx->reseed_counter = -1;
-#if defined(MBEDTLS_THREADING_C)
-    mbedtls_mutex_init( &ctx->mutex );
-#endif
 }
 
 void mbedtls_ctr_drbg_set_prediction_resistance( mbedtls_ctr_drbg_context *ctx,
@@ -157,11 +152,8 @@
      *     (Total is padded to a multiple of 16-bytes with zeroes)
      */
     p = buf + MBEDTLS_CTR_DRBG_BLOCKSIZE;
-    *p++ = ( data_len >> 24 ) & 0xff;
-    *p++ = ( data_len >> 16 ) & 0xff;
-    *p++ = ( data_len >> 8  ) & 0xff;
-    *p++ = ( data_len       ) & 0xff;
-    p += 3;
+    MBEDTLS_PUT_UINT32_BE( data_len, p, 0);
+    p += 4 + 3;
     *p++ = MBEDTLS_CTR_DRBG_SEEDLEN;
     memcpy( p, data, data_len );
     p[data_len] = 0x80;
@@ -394,7 +386,7 @@
     /* Gather entropy for a nonce if requested. */
     if( nonce_len != 0 )
     {
-        if( 0 != ctx->f_entropy( ctx->p_entropy, seed, nonce_len ) )
+        if( 0 != ctx->f_entropy( ctx->p_entropy, seed + seedlen, nonce_len ) )
         {
             return( MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED );
         }
@@ -464,6 +456,11 @@
 
     memset( key, 0, MBEDTLS_CTR_DRBG_KEYSIZE );
 
+    /* The mutex is initialized iff f_entropy is set. */
+#if defined(MBEDTLS_THREADING_C)
+    mbedtls_mutex_init( &ctx->mutex );
+#endif
+
     mbedtls_aes_init( &ctx->aes_ctx );
 
     ctx->f_entropy = f_entropy;
@@ -684,54 +681,134 @@
 
 #if defined(MBEDTLS_SELF_TEST)
 
-static const unsigned char entropy_source_pr[96] =
-    { 0xc1, 0x80, 0x81, 0xa6, 0x5d, 0x44, 0x02, 0x16,
-      0x19, 0xb3, 0xf1, 0x80, 0xb1, 0xc9, 0x20, 0x02,
-      0x6a, 0x54, 0x6f, 0x0c, 0x70, 0x81, 0x49, 0x8b,
-      0x6e, 0xa6, 0x62, 0x52, 0x6d, 0x51, 0xb1, 0xcb,
-      0x58, 0x3b, 0xfa, 0xd5, 0x37, 0x5f, 0xfb, 0xc9,
-      0xff, 0x46, 0xd2, 0x19, 0xc7, 0x22, 0x3e, 0x95,
-      0x45, 0x9d, 0x82, 0xe1, 0xe7, 0x22, 0x9f, 0x63,
-      0x31, 0x69, 0xd2, 0x6b, 0x57, 0x47, 0x4f, 0xa3,
-      0x37, 0xc9, 0x98, 0x1c, 0x0b, 0xfb, 0x91, 0x31,
-      0x4d, 0x55, 0xb9, 0xe9, 0x1c, 0x5a, 0x5e, 0xe4,
-      0x93, 0x92, 0xcf, 0xc5, 0x23, 0x12, 0xd5, 0x56,
-      0x2c, 0x4a, 0x6e, 0xff, 0xdc, 0x10, 0xd0, 0x68 };
-
-static const unsigned char entropy_source_nopr[64] =
-    { 0x5a, 0x19, 0x4d, 0x5e, 0x2b, 0x31, 0x58, 0x14,
-      0x54, 0xde, 0xf6, 0x75, 0xfb, 0x79, 0x58, 0xfe,
-      0xc7, 0xdb, 0x87, 0x3e, 0x56, 0x89, 0xfc, 0x9d,
-      0x03, 0x21, 0x7c, 0x68, 0xd8, 0x03, 0x38, 0x20,
-      0xf9, 0xe6, 0x5e, 0x04, 0xd8, 0x56, 0xf3, 0xa9,
-      0xc4, 0x4a, 0x4c, 0xbd, 0xc1, 0xd0, 0x08, 0x46,
-      0xf5, 0x98, 0x3d, 0x77, 0x1c, 0x1b, 0x13, 0x7e,
-      0x4e, 0x0f, 0x9d, 0x8e, 0xf4, 0x09, 0xf9, 0x2e };
-
-static const unsigned char nonce_pers_pr[16] =
-    { 0xd2, 0x54, 0xfc, 0xff, 0x02, 0x1e, 0x69, 0xd2,
-      0x29, 0xc9, 0xcf, 0xad, 0x85, 0xfa, 0x48, 0x6c };
-
-static const unsigned char nonce_pers_nopr[16] =
-    { 0x1b, 0x54, 0xb8, 0xff, 0x06, 0x42, 0xbf, 0xf5,
-      0x21, 0xf1, 0x5c, 0x1c, 0x0b, 0x66, 0x5f, 0x3f };
+/* The CTR_DRBG NIST test vectors used here are available at
+ * https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Algorithm-Validation-Program/documents/drbg/drbgtestvectors.zip
+ *
+ * The parameters used to derive the test data are:
+ *
+ * [AES-128 use df]
+ * [PredictionResistance = True/False]
+ * [EntropyInputLen = 128]
+ * [NonceLen = 64]
+ * [PersonalizationStringLen = 128]
+ * [AdditionalInputLen = 0]
+ * [ReturnedBitsLen = 512]
+ *
+ * [AES-256 use df]
+ * [PredictionResistance = True/False]
+ * [EntropyInputLen = 256]
+ * [NonceLen = 128]
+ * [PersonalizationStringLen = 256]
+ * [AdditionalInputLen = 0]
+ * [ReturnedBitsLen = 512]
+ *
+ */
 
 #if defined(MBEDTLS_CTR_DRBG_USE_128_BIT_KEY)
-static const unsigned char result_pr[16] =
-    { 0x95, 0x3c, 0xa5, 0xbd, 0x44, 0x1, 0x34, 0xb7,
-      0x13, 0x58, 0x3e, 0x6a, 0x6c, 0x7e, 0x88, 0x8a };
+static const unsigned char entropy_source_pr[] =
+    { 0x04, 0xd9, 0x49, 0xa6, 0xdc, 0xe8, 0x6e, 0xbb,
+      0xf1, 0x08, 0x77, 0x2b, 0x9e, 0x08, 0xca, 0x92,
+      0x65, 0x16, 0xda, 0x99, 0xa2, 0x59, 0xf3, 0xe8,
+      0x38, 0x7e, 0x3f, 0x6b, 0x51, 0x70, 0x7b, 0x20,
+      0xec, 0x53, 0xd0, 0x66, 0xc3, 0x0f, 0xe3, 0xb0,
+      0xe0, 0x86, 0xa6, 0xaa, 0x5f, 0x72, 0x2f, 0xad,
+      0xf7, 0xef, 0x06, 0xb8, 0xd6, 0x9c, 0x9d, 0xe8 };
 
-static const unsigned char result_nopr[16] =
-    { 0x6c, 0x25, 0x27, 0x95, 0xa3, 0x62, 0xd6, 0xdb,
-      0x90, 0xfd, 0x69, 0xb5, 0x42, 0x9, 0x4b, 0x84 };
+static const unsigned char entropy_source_nopr[] =
+    { 0x07, 0x0d, 0x59, 0x63, 0x98, 0x73, 0xa5, 0x45,
+      0x27, 0x38, 0x22, 0x7b, 0x76, 0x85, 0xd1, 0xa9,
+      0x74, 0x18, 0x1f, 0x3c, 0x22, 0xf6, 0x49, 0x20,
+      0x4a, 0x47, 0xc2, 0xf3, 0x85, 0x16, 0xb4, 0x6f,
+      0x00, 0x2e, 0x71, 0xda, 0xed, 0x16, 0x9b, 0x5c };
+
+static const unsigned char pers_pr[] =
+    { 0xbf, 0xa4, 0x9a, 0x8f, 0x7b, 0xd8, 0xb1, 0x7a,
+      0x9d, 0xfa, 0x45, 0xed, 0x21, 0x52, 0xb3, 0xad };
+
+static const unsigned char pers_nopr[] =
+    { 0x4e, 0x61, 0x79, 0xd4, 0xc2, 0x72, 0xa1, 0x4c,
+      0xf1, 0x3d, 0xf6, 0x5e, 0xa3, 0xa6, 0xe5, 0x0f };
+
+static const unsigned char result_pr[] =
+    { 0xc9, 0x0a, 0xaf, 0x85, 0x89, 0x71, 0x44, 0x66,
+      0x4f, 0x25, 0x0b, 0x2b, 0xde, 0xd8, 0xfa, 0xff,
+      0x52, 0x5a, 0x1b, 0x32, 0x5e, 0x41, 0x7a, 0x10,
+      0x1f, 0xef, 0x1e, 0x62, 0x23, 0xe9, 0x20, 0x30,
+      0xc9, 0x0d, 0xad, 0x69, 0xb4, 0x9c, 0x5b, 0xf4,
+      0x87, 0x42, 0xd5, 0xae, 0x5e, 0x5e, 0x43, 0xcc,
+      0xd9, 0xfd, 0x0b, 0x93, 0x4a, 0xe3, 0xd4, 0x06,
+      0x37, 0x36, 0x0f, 0x3f, 0x72, 0x82, 0x0c, 0xcf };
+
+static const unsigned char result_nopr[] =
+    { 0x31, 0xc9, 0x91, 0x09, 0xf8, 0xc5, 0x10, 0x13,
+      0x3c, 0xd3, 0x96, 0xf9, 0xbc, 0x2c, 0x12, 0xc0,
+      0x7c, 0xc1, 0x61, 0x5f, 0xa3, 0x09, 0x99, 0xaf,
+      0xd7, 0xf2, 0x36, 0xfd, 0x40, 0x1a, 0x8b, 0xf2,
+      0x33, 0x38, 0xee, 0x1d, 0x03, 0x5f, 0x83, 0xb7,
+      0xa2, 0x53, 0xdc, 0xee, 0x18, 0xfc, 0xa7, 0xf2,
+      0xee, 0x96, 0xc6, 0xc2, 0xcd, 0x0c, 0xff, 0x02,
+      0x76, 0x70, 0x69, 0xaa, 0x69, 0xd1, 0x3b, 0xe8 };
 #else /* MBEDTLS_CTR_DRBG_USE_128_BIT_KEY */
-static const unsigned char result_pr[16] =
-    { 0x34, 0x01, 0x16, 0x56, 0xb4, 0x29, 0x00, 0x8f,
-      0x35, 0x63, 0xec, 0xb5, 0xf2, 0x59, 0x07, 0x23 };
 
-static const unsigned char result_nopr[16] =
-    { 0xa0, 0x54, 0x30, 0x3d, 0x8a, 0x7e, 0xa9, 0x88,
-      0x9d, 0x90, 0x3e, 0x07, 0x7c, 0x6f, 0x21, 0x8f };
+static const unsigned char entropy_source_pr[] =
+    { 0xca, 0x58, 0xfd, 0xf2, 0xb9, 0x77, 0xcb, 0x49,
+      0xd4, 0xe0, 0x5b, 0xe2, 0x39, 0x50, 0xd9, 0x8a,
+      0x6a, 0xb3, 0xc5, 0x2f, 0xdf, 0x74, 0xd5, 0x85,
+      0x8f, 0xd1, 0xba, 0x64, 0x54, 0x7b, 0xdb, 0x1e,
+      0xc5, 0xea, 0x24, 0xc0, 0xfa, 0x0c, 0x90, 0x15,
+      0x09, 0x20, 0x92, 0x42, 0x32, 0x36, 0x45, 0x45,
+      0x7d, 0x20, 0x76, 0x6b, 0xcf, 0xa2, 0x15, 0xc8,
+      0x2f, 0x9f, 0xbc, 0x88, 0x3f, 0x80, 0xd1, 0x2c,
+      0xb7, 0x16, 0xd1, 0x80, 0x9e, 0xe1, 0xc9, 0xb3,
+      0x88, 0x1b, 0x21, 0x45, 0xef, 0xa1, 0x7f, 0xce,
+      0xc8, 0x92, 0x35, 0x55, 0x2a, 0xd9, 0x1d, 0x8e,
+      0x12, 0x38, 0xac, 0x01, 0x4e, 0x38, 0x18, 0x76,
+      0x9c, 0xf2, 0xb6, 0xd4, 0x13, 0xb6, 0x2c, 0x77,
+      0xc0, 0xe7, 0xe6, 0x0c, 0x47, 0x44, 0x95, 0xbe };
+
+static const unsigned char entropy_source_nopr[] =
+    { 0x4c, 0xfb, 0x21, 0x86, 0x73, 0x34, 0x6d, 0x9d,
+      0x50, 0xc9, 0x22, 0xe4, 0x9b, 0x0d, 0xfc, 0xd0,
+      0x90, 0xad, 0xf0, 0x4f, 0x5c, 0x3b, 0xa4, 0x73,
+      0x27, 0xdf, 0xcd, 0x6f, 0xa6, 0x3a, 0x78, 0x5c,
+      0x01, 0x69, 0x62, 0xa7, 0xfd, 0x27, 0x87, 0xa2,
+      0x4b, 0xf6, 0xbe, 0x47, 0xef, 0x37, 0x83, 0xf1,
+      0xb7, 0xec, 0x46, 0x07, 0x23, 0x63, 0x83, 0x4a,
+      0x1b, 0x01, 0x33, 0xf2, 0xc2, 0x38, 0x91, 0xdb,
+      0x4f, 0x11, 0xa6, 0x86, 0x51, 0xf2, 0x3e, 0x3a,
+      0x8b, 0x1f, 0xdc, 0x03, 0xb1, 0x92, 0xc7, 0xe7 };
+
+static const unsigned char pers_pr[] =
+    { 0x5a, 0x70, 0x95, 0xe9, 0x81, 0x40, 0x52, 0x33,
+      0x91, 0x53, 0x7e, 0x75, 0xd6, 0x19, 0x9d, 0x1e,
+      0xad, 0x0d, 0xc6, 0xa7, 0xde, 0x6c, 0x1f, 0xe0,
+      0xea, 0x18, 0x33, 0xa8, 0x7e, 0x06, 0x20, 0xe9 };
+
+static const unsigned char pers_nopr[] =
+    { 0x88, 0xee, 0xb8, 0xe0, 0xe8, 0x3b, 0xf3, 0x29,
+      0x4b, 0xda, 0xcd, 0x60, 0x99, 0xeb, 0xe4, 0xbf,
+      0x55, 0xec, 0xd9, 0x11, 0x3f, 0x71, 0xe5, 0xeb,
+      0xcb, 0x45, 0x75, 0xf3, 0xd6, 0xa6, 0x8a, 0x6b };
+
+static const unsigned char result_pr[] =
+    { 0xce, 0x2f, 0xdb, 0xb6, 0xd9, 0xb7, 0x39, 0x85,
+      0x04, 0xc5, 0xc0, 0x42, 0xc2, 0x31, 0xc6, 0x1d,
+      0x9b, 0x5a, 0x59, 0xf8, 0x7e, 0x0d, 0xcc, 0x62,
+      0x7b, 0x65, 0x11, 0x55, 0x10, 0xeb, 0x9e, 0x3d,
+      0xa4, 0xfb, 0x1c, 0x6a, 0x18, 0xc0, 0x74, 0xdb,
+      0xdd, 0xe7, 0x02, 0x23, 0x63, 0x21, 0xd0, 0x39,
+      0xf9, 0xa7, 0xc4, 0x52, 0x84, 0x3b, 0x49, 0x40,
+      0x72, 0x2b, 0xb0, 0x6c, 0x9c, 0xdb, 0xc3, 0x43 };
+
+static const unsigned char result_nopr[] =
+    { 0xa5, 0x51, 0x80, 0xa1, 0x90, 0xbe, 0xf3, 0xad,
+      0xaf, 0x28, 0xf6, 0xb7, 0x95, 0xe9, 0xf1, 0xf3,
+      0xd6, 0xdf, 0xa1, 0xb2, 0x7d, 0xd0, 0x46, 0x7b,
+      0x0c, 0x75, 0xf5, 0xfa, 0x93, 0x1e, 0x97, 0x14,
+      0x75, 0xb2, 0x7c, 0xae, 0x03, 0xa2, 0x96, 0x54,
+      0xe2, 0xf4, 0x09, 0x66, 0xea, 0x33, 0x64, 0x30,
+      0x40, 0xd1, 0x40, 0x0f, 0xe6, 0x77, 0x87, 0x3a,
+      0xf8, 0x09, 0x7c, 0x1f, 0xe9, 0xf0, 0x02, 0x98 };
 #endif /* MBEDTLS_CTR_DRBG_USE_128_BIT_KEY */
 
 static size_t test_offset;
@@ -751,13 +828,15 @@
                         return( 1 );                        \
                     }
 
+#define SELF_TEST_OUPUT_DISCARD_LENGTH 64
+
 /*
  * Checkup routine
  */
 int mbedtls_ctr_drbg_self_test( int verbose )
 {
     mbedtls_ctr_drbg_context ctx;
-    unsigned char buf[16];
+    unsigned char buf[ sizeof( result_pr ) ];
 
     mbedtls_ctr_drbg_init( &ctx );
 
@@ -768,16 +847,16 @@
         mbedtls_printf( "  CTR_DRBG (PR = TRUE) : " );
 
     test_offset = 0;
-    mbedtls_ctr_drbg_set_entropy_len( &ctx, 32 );
-    mbedtls_ctr_drbg_set_nonce_len( &ctx, 0 );
+    mbedtls_ctr_drbg_set_entropy_len( &ctx, MBEDTLS_CTR_DRBG_KEYSIZE );
+    mbedtls_ctr_drbg_set_nonce_len( &ctx, MBEDTLS_CTR_DRBG_KEYSIZE / 2 );
     CHK( mbedtls_ctr_drbg_seed( &ctx,
                                 ctr_drbg_self_test_entropy,
                                 (void *) entropy_source_pr,
-                                nonce_pers_pr, 16 ) );
+                                pers_pr, MBEDTLS_CTR_DRBG_KEYSIZE ) );
     mbedtls_ctr_drbg_set_prediction_resistance( &ctx, MBEDTLS_CTR_DRBG_PR_ON );
-    CHK( mbedtls_ctr_drbg_random( &ctx, buf, MBEDTLS_CTR_DRBG_BLOCKSIZE ) );
-    CHK( mbedtls_ctr_drbg_random( &ctx, buf, MBEDTLS_CTR_DRBG_BLOCKSIZE ) );
-    CHK( memcmp( buf, result_pr, MBEDTLS_CTR_DRBG_BLOCKSIZE ) );
+    CHK( mbedtls_ctr_drbg_random( &ctx, buf, SELF_TEST_OUPUT_DISCARD_LENGTH ) );
+    CHK( mbedtls_ctr_drbg_random( &ctx, buf, sizeof( result_pr ) ) );
+    CHK( memcmp( buf, result_pr, sizeof( result_pr ) ) );
 
     mbedtls_ctr_drbg_free( &ctx );
 
@@ -793,16 +872,16 @@
     mbedtls_ctr_drbg_init( &ctx );
 
     test_offset = 0;
-    mbedtls_ctr_drbg_set_entropy_len( &ctx, 32 );
-    mbedtls_ctr_drbg_set_nonce_len( &ctx, 0 );
+    mbedtls_ctr_drbg_set_entropy_len( &ctx, MBEDTLS_CTR_DRBG_KEYSIZE);
+    mbedtls_ctr_drbg_set_nonce_len( &ctx, MBEDTLS_CTR_DRBG_KEYSIZE / 2 );
     CHK( mbedtls_ctr_drbg_seed( &ctx,
                                 ctr_drbg_self_test_entropy,
                                 (void *) entropy_source_nopr,
-                                nonce_pers_nopr, 16 ) );
-    CHK( mbedtls_ctr_drbg_random( &ctx, buf, 16 ) );
+                                pers_nopr, MBEDTLS_CTR_DRBG_KEYSIZE ) );
     CHK( mbedtls_ctr_drbg_reseed( &ctx, NULL, 0 ) );
-    CHK( mbedtls_ctr_drbg_random( &ctx, buf, 16 ) );
-    CHK( memcmp( buf, result_nopr, 16 ) );
+    CHK( mbedtls_ctr_drbg_random( &ctx, buf, SELF_TEST_OUPUT_DISCARD_LENGTH ) );
+    CHK( mbedtls_ctr_drbg_random( &ctx, buf, sizeof( result_nopr ) ) );
+    CHK( memcmp( buf, result_nopr, sizeof( result_nopr ) ) );
 
     mbedtls_ctr_drbg_free( &ctx );
 
diff --git a/third_party/mbedtls/repo/library/debug.c b/third_party/mbedtls/repo/library/debug.c
index c3384be..e108600 100644
--- a/third_party/mbedtls/repo/library/debug.c
+++ b/third_party/mbedtls/repo/library/debug.c
@@ -74,6 +74,7 @@
 #endif
 }
 
+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, ... )
@@ -219,8 +220,8 @@
                       const char *text, const mbedtls_mpi *X )
 {
     char str[DEBUG_BUF_SIZE];
-    int j, k, zeros = 1;
-    size_t i, n, idx = 0;
+    size_t bitlen;
+    size_t idx = 0;
 
     if( NULL == ssl              ||
         NULL == ssl->conf        ||
@@ -231,55 +232,43 @@
         return;
     }
 
-    for( n = X->n - 1; n > 0; n-- )
-        if( X->p[n] != 0 )
-            break;
+    bitlen = mbedtls_mpi_bitlen( X );
 
-    for( j = ( sizeof(mbedtls_mpi_uint) << 3 ) - 1; j >= 0; j-- )
-        if( ( ( X->p[n] >> j ) & 1 ) != 0 )
-            break;
-
-    mbedtls_snprintf( str + idx, sizeof( str ) - idx, "value of '%s' (%d bits) is:\n",
-              text, (int) ( ( n * ( sizeof(mbedtls_mpi_uint) << 3 ) ) + j + 1 ) );
-
+    mbedtls_snprintf( str, sizeof( str ), "value of '%s' (%u bits) is:\n",
+                      text, (unsigned) bitlen );
     debug_send_line( ssl, level, file, line, str );
 
-    idx = 0;
-    for( i = n + 1, j = 0; i > 0; i-- )
+    if( bitlen == 0 )
     {
-        if( zeros && X->p[i - 1] == 0 )
-            continue;
-
-        for( k = sizeof( mbedtls_mpi_uint ) - 1; k >= 0; k-- )
+        str[0] = ' '; str[1] = '0'; str[2] = '0';
+        idx = 3;
+    }
+    else
+    {
+        int n;
+        for( n = (int) ( ( bitlen - 1 ) / 8 ); n >= 0; n-- )
         {
-            if( zeros && ( ( X->p[i - 1] >> ( k << 3 ) ) & 0xFF ) == 0 )
-                continue;
-            else
-                zeros = 0;
-
-            if( j % 16 == 0 )
+            size_t limb_offset = n / sizeof( mbedtls_mpi_uint );
+            size_t offset_in_limb = n % sizeof( mbedtls_mpi_uint );
+            unsigned char octet =
+                ( X->p[limb_offset] >> ( offset_in_limb * 8 ) ) & 0xff;
+            mbedtls_snprintf( str + idx, sizeof( str ) - idx, " %02x", octet );
+            idx += 3;
+            /* Wrap lines after 16 octets that each take 3 columns */
+            if( idx >= 3 * 16 )
             {
-                if( j > 0 )
-                {
-                    mbedtls_snprintf( str + idx, sizeof( str ) - idx, "\n" );
-                    debug_send_line( ssl, level, file, line, str );
-                    idx = 0;
-                }
+                mbedtls_snprintf( str + idx, sizeof( str ) - idx, "\n" );
+                debug_send_line( ssl, level, file, line, str );
+                idx = 0;
             }
-
-            idx += mbedtls_snprintf( str + idx, sizeof( str ) - idx, " %02x", (unsigned int)
-                             ( X->p[i - 1] >> ( k << 3 ) ) & 0xFF );
-
-            j++;
         }
-
     }
 
-    if( zeros == 1 )
-        idx += mbedtls_snprintf( str + idx, sizeof( str ) - idx, " 00" );
-
-    mbedtls_snprintf( str + idx, sizeof( str ) - idx, "\n" );
-    debug_send_line( ssl, level, file, line, str );
+    if( idx != 0 )
+    {
+        mbedtls_snprintf( str + idx, sizeof( str ) - idx, "\n" );
+        debug_send_line( ssl, level, file, line, str );
+    }
 }
 #endif /* MBEDTLS_BIGNUM_C */
 
diff --git a/third_party/mbedtls/repo/library/des.c b/third_party/mbedtls/repo/library/des.c
index eddf55e..91d22b5 100644
--- a/third_party/mbedtls/repo/library/des.c
+++ b/third_party/mbedtls/repo/library/des.c
@@ -28,6 +28,7 @@
 #if defined(MBEDTLS_DES_C)
 
 #include "mbedtls/des.h"
+#include "mbedtls/error.h"
 #include "mbedtls/platform_util.h"
 
 #include <string.h>
@@ -44,29 +45,6 @@
 #if !defined(MBEDTLS_DES_ALT)
 
 /*
- * 32-bit integer manipulation macros (big endian)
- */
-#ifndef GET_UINT32_BE
-#define GET_UINT32_BE(n,b,i)                            \
-{                                                       \
-    (n) = ( (uint32_t) (b)[(i)    ] << 24 )             \
-        | ( (uint32_t) (b)[(i) + 1] << 16 )             \
-        | ( (uint32_t) (b)[(i) + 2] <<  8 )             \
-        | ( (uint32_t) (b)[(i) + 3]       );            \
-}
-#endif
-
-#ifndef PUT_UINT32_BE
-#define PUT_UINT32_BE(n,b,i)                            \
-{                                                       \
-    (b)[(i)    ] = (unsigned char) ( (n) >> 24 );       \
-    (b)[(i) + 1] = (unsigned char) ( (n) >> 16 );       \
-    (b)[(i) + 2] = (unsigned char) ( (n) >>  8 );       \
-    (b)[(i) + 3] = (unsigned char) ( (n)       );       \
-}
-#endif
-
-/*
  * Expanded DES S-boxes
  */
 static const uint32_t SB1[64] =
@@ -423,8 +401,8 @@
     int i;
     uint32_t X, Y, T;
 
-    GET_UINT32_BE( X, key, 0 );
-    GET_UINT32_BE( Y, key, 4 );
+    X = MBEDTLS_GET_UINT32_BE( key, 0 );
+    Y = MBEDTLS_GET_UINT32_BE( key, 4 );
 
     /*
      * Permuted Choice 1
@@ -633,8 +611,8 @@
 
     SK = ctx->sk;
 
-    GET_UINT32_BE( X, input, 0 );
-    GET_UINT32_BE( Y, input, 4 );
+    X = MBEDTLS_GET_UINT32_BE( input, 0 );
+    Y = MBEDTLS_GET_UINT32_BE( input, 4 );
 
     DES_IP( X, Y );
 
@@ -646,8 +624,8 @@
 
     DES_FP( Y, X );
 
-    PUT_UINT32_BE( Y, output, 0 );
-    PUT_UINT32_BE( X, output, 4 );
+    MBEDTLS_PUT_UINT32_BE( Y, output, 0 );
+    MBEDTLS_PUT_UINT32_BE( X, output, 4 );
 
     return( 0 );
 }
@@ -665,6 +643,7 @@
                     unsigned char *output )
 {
     int i;
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
     unsigned char temp[8];
 
     if( length % 8 )
@@ -677,7 +656,9 @@
             for( i = 0; i < 8; i++ )
                 output[i] = (unsigned char)( input[i] ^ iv[i] );
 
-            mbedtls_des_crypt_ecb( ctx, output, output );
+            ret = mbedtls_des_crypt_ecb( ctx, output, output );
+            if( ret != 0 )
+                goto exit;
             memcpy( iv, output, 8 );
 
             input  += 8;
@@ -690,7 +671,9 @@
         while( length > 0 )
         {
             memcpy( temp, input, 8 );
-            mbedtls_des_crypt_ecb( ctx, input, output );
+            ret = mbedtls_des_crypt_ecb( ctx, input, output );
+            if( ret != 0 )
+                goto exit;
 
             for( i = 0; i < 8; i++ )
                 output[i] = (unsigned char)( output[i] ^ iv[i] );
@@ -702,8 +685,10 @@
             length -= 8;
         }
     }
+    ret = 0;
 
-    return( 0 );
+exit:
+    return( ret );
 }
 #endif /* MBEDTLS_CIPHER_MODE_CBC */
 
@@ -720,8 +705,8 @@
 
     SK = ctx->sk;
 
-    GET_UINT32_BE( X, input, 0 );
-    GET_UINT32_BE( Y, input, 4 );
+    X = MBEDTLS_GET_UINT32_BE( input, 0 );
+    Y = MBEDTLS_GET_UINT32_BE( input, 4 );
 
     DES_IP( X, Y );
 
@@ -745,8 +730,8 @@
 
     DES_FP( Y, X );
 
-    PUT_UINT32_BE( Y, output, 0 );
-    PUT_UINT32_BE( X, output, 4 );
+    MBEDTLS_PUT_UINT32_BE( Y, output, 0 );
+    MBEDTLS_PUT_UINT32_BE( X, output, 4 );
 
     return( 0 );
 }
@@ -764,6 +749,7 @@
                      unsigned char *output )
 {
     int i;
+    int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
     unsigned char temp[8];
 
     if( length % 8 )
@@ -776,7 +762,9 @@
             for( i = 0; i < 8; i++ )
                 output[i] = (unsigned char)( input[i] ^ iv[i] );
 
-            mbedtls_des3_crypt_ecb( ctx, output, output );
+            ret = mbedtls_des3_crypt_ecb( ctx, output, output );
+            if( ret != 0 )
+                goto exit;
             memcpy( iv, output, 8 );
 
             input  += 8;
@@ -789,7 +777,9 @@
         while( length > 0 )
         {
             memcpy( temp, input, 8 );
-            mbedtls_des3_crypt_ecb( ctx, input, output );
+            ret = mbedtls_des3_crypt_ecb( ctx, input, output );
+            if( ret != 0 )
+                goto exit;
 
             for( i = 0; i < 8; i++ )
                 output[i] = (unsigned char)( output[i] ^ iv[i] );
@@ -801,8 +791,10 @@
             length -= 8;
         }
     }
+    ret = 0;
 
-    return( 0 );
+exit:
+    return( ret );
 }
 #endif /* MBEDTLS_CIPHER_MODE_CBC */
 
@@ -895,39 +887,43 @@
         switch( i )
         {
         case 0:
-            mbedtls_des_setkey_dec( &ctx, des3_test_keys );
+            ret = mbedtls_des_setkey_dec( &ctx, des3_test_keys );
             break;
 
         case 1:
-            mbedtls_des_setkey_enc( &ctx, des3_test_keys );
+            ret = mbedtls_des_setkey_enc( &ctx, des3_test_keys );
             break;
 
         case 2:
-            mbedtls_des3_set2key_dec( &ctx3, des3_test_keys );
+            ret = mbedtls_des3_set2key_dec( &ctx3, des3_test_keys );
             break;
 
         case 3:
-            mbedtls_des3_set2key_enc( &ctx3, des3_test_keys );
+            ret = mbedtls_des3_set2key_enc( &ctx3, des3_test_keys );
             break;
 
         case 4:
-            mbedtls_des3_set3key_dec( &ctx3, des3_test_keys );
+            ret = mbedtls_des3_set3key_dec( &ctx3, des3_test_keys );
             break;
 
         case 5:
-            mbedtls_des3_set3key_enc( &ctx3, des3_test_keys );
+            ret = mbedtls_des3_set3key_enc( &ctx3, des3_test_keys );
             break;
 
         default:
             return( 1 );
         }
+        if( ret != 0 )
+            goto exit;
 
         for( j = 0; j < 100; j++ )
         {
             if( u == 0 )
-                mbedtls_des_crypt_ecb( &ctx, buf, buf );
+                ret = mbedtls_des_crypt_ecb( &ctx, buf, buf );
             else
-                mbedtls_des3_crypt_ecb( &ctx3, buf, buf );
+                ret = mbedtls_des3_crypt_ecb( &ctx3, buf, buf );
+            if( ret != 0 )
+                goto exit;
         }
 
         if( ( v == MBEDTLS_DES_DECRYPT &&
@@ -970,41 +966,45 @@
         switch( i )
         {
         case 0:
-            mbedtls_des_setkey_dec( &ctx, des3_test_keys );
+            ret = mbedtls_des_setkey_dec( &ctx, des3_test_keys );
             break;
 
         case 1:
-            mbedtls_des_setkey_enc( &ctx, des3_test_keys );
+            ret = mbedtls_des_setkey_enc( &ctx, des3_test_keys );
             break;
 
         case 2:
-            mbedtls_des3_set2key_dec( &ctx3, des3_test_keys );
+            ret = mbedtls_des3_set2key_dec( &ctx3, des3_test_keys );
             break;
 
         case 3:
-            mbedtls_des3_set2key_enc( &ctx3, des3_test_keys );
+            ret = mbedtls_des3_set2key_enc( &ctx3, des3_test_keys );
             break;
 
         case 4:
-            mbedtls_des3_set3key_dec( &ctx3, des3_test_keys );
+            ret = mbedtls_des3_set3key_dec( &ctx3, des3_test_keys );
             break;
 
         case 5:
-            mbedtls_des3_set3key_enc( &ctx3, des3_test_keys );
+            ret = mbedtls_des3_set3key_enc( &ctx3, des3_test_keys );
             break;
 
         default:
             return( 1 );
         }
+        if( ret != 0 )
+            goto exit;
 
         if( v == MBEDTLS_DES_DECRYPT )
         {
             for( j = 0; j < 100; j++ )
             {
                 if( u == 0 )
-                    mbedtls_des_crypt_cbc( &ctx, v, 8, iv, buf, buf );
+                    ret = mbedtls_des_crypt_cbc( &ctx, v, 8, iv, buf, buf );