{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "ZigBee encryption with AES-128-CCM*.ipynb",
      "provenance": [],
      "collapsed_sections": []
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "iVuVad_Z97B6"
      },
      "source": [
        "# ZigBee frame encryption with AES-128-CCM*\n",
        "\n",
        "This notebook details how to encrypt ZigBee frames with AES-128-CCM*. Zigbee encryption is performed in three stages:\n",
        "* Input transformation (prepare data)\n",
        "* Authentication Transformation (generate MIC)\n",
        "* Encryption Transformation (returns encrypted frame)\n",
        "\n",
        "The frame used in this example is a frame listen on a real ZigBee network detailed on this page: [Autopsy of a ZigBee frame](https://lucidar.me/en/zigbee/autopsy-of-a-zigbee-frame/).\n",
        "\n",
        "The algorithm is detailed in the [section 4.3.1.1](https://lucidar.me/en/zigbee/files/docs-05-3474-21-0csg-zigbee-specification.pdf#page=404&zoom=100,93,0) and [annex A of the ZigBee specification](https://lucidar.me/en/zigbee/files/docs-05-3474-21-0csg-zigbee-specification.pdf#page=479&zoom=100,93,0).\n",
        "\n",
        "\n",
        "More details on [Philippe Lucidarme's blog](lucidar.me/en/zigbee/zigbee-frame-encryption-with-aes-128-ccm/)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "dAwg_r7OfwXf"
      },
      "source": [
        "## Libraries and functions\n",
        "\n",
        "Here are the libraries and functions used in the following:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "07Stlv649y06",
        "outputId": "585121f5-633b-4d79-f3f7-dd9bba7210bb"
      },
      "source": [
        "!pip install pycrypto\n",
        "\n",
        "# Python Cryptography Toolkit for AES encryption\n",
        "from Crypto.Cipher import AES\n",
        "\n",
        "# Print aray of bytes in hexadecimal\n",
        "def printhex(x, sep = ' '):\n",
        "  str = ''\n",
        "  for b in x:  \n",
        "    byte = hex(b)[2:]\n",
        "    if (len(byte)<2)  :\n",
        "      str += '0' + byte + sep\n",
        "    else:\n",
        "      str += hex(b)[2:] + sep \n",
        "  print (str[:-1].upper())\n",
        "\n",
        "\n",
        "# 16 bits padding (with 0x00)\n",
        "def pad(x):\n",
        "  n=(16-len(x)%16)%16\n",
        "  return x + bytes([0x00]*n)"
      ],
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Requirement already satisfied: pycrypto in /usr/local/lib/python3.6/dist-packages (2.6.1)\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "TrEwN896JtBI"
      },
      "source": [
        "## Network key\n",
        "\n",
        "According to [annex A.2 of the ZigBee specification](https://lucidar.me/en/zigbee/files/docs-05-3474-21-0csg-zigbee-specification.pdf#page=479&zoom=100,93,650):\n",
        "\n",
        "> *A bit string Key of length keylen bits to be used as the key. Each entity shall have evidence that access to this key is restricted to the entity itself and its intended key-sharing group member(s).*\n",
        "\n",
        "In this example, the network key has been previously listen on the network thank this [ZigBee sniffer](https://lucidar.me/en/zigbee/zigbee-sniffer/):\n",
        "\n",
        "**Network key:** ```AD:8E:BB:C4:F9:6A:E7:00:05:06:D3:FC:D1:62:7F:B8```"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "037cI7roJvqD",
        "outputId": "ecf22a99-75b6-4623-84cc-67bad33892ad"
      },
      "source": [
        "key =   bytes([0xAD, 0x8E, 0xBB, 0xC4, 0xF9, 0x6A, 0xE7, 0x00, 0x05, 0x06, 0xD3, 0xFC, 0xD1, 0x62, 0x7F, 0xB8])\n",
        "printhex (key)"
      ],
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "AD 8E BB C4 F9 6A E7 00 05 06 D3 FC D1 62 7F B8\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Oawa0nEyp0DH"
      },
      "source": [
        "## L of the message length field\n",
        "\n",
        "According to [annex A of the ZigBee specification](https://lucidar.me/en/zigbee/files/docs-05-3474-21-0csg-zigbee-specification.pdf#page=479&zoom=100,93,350):\n",
        "\n",
        "> *The length L of the message length field, in octets, shall have been chosen. Valid values for L are the integers 2, 3,..., 8 (the value L=1 is reserved).*\n",
        "\n",
        "In the present case, L=2."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "FElHJD7vqZPf"
      },
      "source": [
        "L = 2"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "HQJroF33A-xV"
      },
      "source": [
        "## M (length of frame integrity)\n",
        "\n",
        "According to [annex A of the ZigBee specification](https://lucidar.me/en/zigbee/files/docs-05-3474-21-0csg-zigbee-specification.pdf#page=479&zoom=100,93,350):\n",
        "\n",
        "> *The length M of the authentication field, in octets, shall have been chosen. Valid values for M are the integers 0, 4, 6, 8, 10, 12, 14, and 16. (The value M=0 corresponds to disabling authenticity, since then the authentication field contains an empty string.)*\n",
        "\n",
        "M is the length of the [frame integrity (MIC)](https://lucidar.me/en/zigbee/autopsy-of-a-zigbee-frame/#nwk-mic) in number of bytes.  In the present case, and from [section 4.5.1.1.1 of the ZigBee specification](https://lucidar.me/en/zigbee/files/docs-05-3474-21-0csg-zigbee-specification.pdf#page=448&zoom=100,0,240), we can deduce the length of M is 4 bytes: ```AC:4C:76:AF```."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "x5lq5ChIBpyZ"
      },
      "source": [
        "M=4"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "D7uzZFN5Chuy"
      },
      "source": [
        "## NWK header\n",
        "\n",
        "The NWK header (network header) is extracted from the [raw frame](https://lucidar.me/en/zigbee/autopsy-of-a-zigbee-frame/#nwk-header):\n",
        "\n",
        "**NWK header:** ```48:02:00:00:8A:5C:1E:5D```\n"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "f0Vu7nWUCj33",
        "outputId": "909c7ca6-0a35-4447-f4a4-d86870d42ad6"
      },
      "source": [
        "NwkHeader = bytes([0x48, 0x02, 0x00, 0x00, 0x8A, 0x5C, 0x1E, 0x5D])\n",
        "printhex (NwkHeader)"
      ],
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "48 02 00 00 8A 5C 1E 5D\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Ns3UniEz-Cj3"
      },
      "source": [
        "## NWK AUX Header\n",
        "\n",
        "According to [section 4.5.1 of the ZigBee specification](https://lucidar.me/en/zigbee/files/docs-05-3474-21-0csg-zigbee-specification.pdf#page=447&zoom=100,93,598)\n",
        "\n",
        "> The auxiliary frame header, as illustrated by Figure 4.18, shall include a security control field and a frame counter field, and may include a sender address field and key sequence number field. \n",
        "\n",
        "| Octets: 1 | 4 | 0/8 | 0/1 |\n",
        "| --- | --- | --- | --- |\n",
        "| Security control | Frame counter | Source address | Key sequence number |\n",
        "\n",
        "* **Security control:** ```2D``` => (ENC-MIC-32) See [section 4.5.1.1.1 of the ZigBee specification](https://lucidar.me/en/zigbee/files/docs-05-3474-21-0csg-zigbee-specification.pdf#page=448&zoom=100,0,240) for more details)\n",
        "* **Frame counter:** ```E1``` \n",
        "* **Source address:** ```00:15:8D:00:01:E8:3C:01```\n",
        "* **Key sequence:** ```01```\n",
        "\n",
        "More details on the [section about NWK auxiliary header](https://lucidar.me/en/zigbee/autopsy-of-a-zigbee-frame/#nwk-aux-header)."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "RVOB40W3-I21",
        "outputId": "c1c3b6ce-0cfe-45f5-faa9-08803d62608c"
      },
      "source": [
        "AuxiliaryHeader = bytes([0x2D, 0xE1, 0x00, 0x00, 0x00, 0x01, 0x3C, 0xE8, 0x01, 0x00, 0x8D, 0x15, 0x00, 0x01])\n",
        "printhex (AuxiliaryHeader)"
      ],
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "2D E1 00 00 00 01 3C E8 01 00 8D 15 00 01\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "7ulYLuch_o1e"
      },
      "source": [
        "## Nonce\n",
        "\n",
        "According to [section 4.5.2.2 of the ZigBee specification](https://lucidar.me/en/zigbee/files/docs-05-3474-21-0csg-zigbee-specification.pdf#page=450&zoom=100,93,96):\n",
        "\n",
        "> *The nonce input used for the CCM encryption and authentication transformation and for the CCM decryption and authentication checking transformation consists of data explicitly included in the frame and data that both devices can independently obtain.*\n",
        "\n",
        "| Octets: 8 | 4 | 1 |\n",
        "| --- | --- | --- |\n",
        "|Source address | Frame counter | Security control |\n",
        "\n",
        "* **Source address:** ```00:15:8D:00:01:E8:3C:01```\n",
        "* **Frame counter:** ```E1```\n",
        "* **Security control:** ```2D``` => (ENC-MIC-32) See [section 4.5.1.1.1 of the ZigBee specification](https://lucidar.me/en/zigbee/files/docs-05-3474-21-0csg-zigbee-specification.pdf#page=448&zoom=100,0,240) for more details)"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "-iftruab-VLT",
        "outputId": "388d175b-186e-42cb-9c01-89f8d334b3af"
      },
      "source": [
        "nonce = bytes([0x01, 0x3C, 0xE8, 0x01, 0x00, 0x8D, 0x15, 0x00  ,  0xE1, 0x00, 0x00, 0x00  ,  0x2D])\n",
        "print (len(nonce), 'bytes')\n",
        "printhex (nonce)"
      ],
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "13 bytes\n",
            "01 3C E8 01 00 8D 15 00 E1 00 00 00 2D\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "NqLPHMEPB1kS"
      },
      "source": [
        "## a and m\n",
        "\n",
        "From [section 4.3.1.1 from the ZigBee specification](https://lucidar.me/en/zigbee/files/docs-05-3474-21-0csg-zigbee-specification.pdf#page=404&zoom=100,93,540):\n",
        "\n",
        "> *If the security level requires encryption, the octet string **a shall be the string NwkHeader || AuxiliaryHeader** and the octet string **m shall be the string Payload**. *\n",
        "\n",
        "Since in our case, security level requires encryption:\n",
        "\n",
        "a = [NwkHeader](https://lucidar.me/en/zigbee/autopsy-of-a-zigbee-frame/#nwk-header) || [AuxiliaryHeader](https://lucidar.me/en/zigbee/autopsy-of-a-zigbee-frame/#nwk-aux-header)\n",
        "\n",
        "m = payload (The payload contains the data sent by the switch, see [section NWK payload](https://lucidar.me/en/zigbee/autopsy-of-a-zigbee-frame/#nwk-payload) for more details)"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "hJzOBerlCYHI",
        "outputId": "cb2a72a3-e79a-4e63-8de5-8be96c0e900a"
      },
      "source": [
        "# Octet string a\n",
        "a = NwkHeader + AuxiliaryHeader\n",
        "print ('a:')\n",
        "printhex (a)\n",
        "\n",
        "# Octet string m\n",
        "print ('\\nm:')\n",
        "m = bytes([0x00, 0x01, 0x12, 0x00, 0x04, 0x01, 0x01, 0x62, 0x18, 0xC3, 0x0A, 0x55, 0x00, 0x21, 0x01, 0x00])\n",
        "printhex(m)"
      ],
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "a:\n",
            "48 02 00 00 8A 5C 1E 5D 2D E1 00 00 00 01 3C E8 01 00 8D 15 00 01\n",
            "\n",
            "m:\n",
            "00 01 12 00 04 01 01 62 18 C3 0A 55 00 21 01 00\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "4bJTHfStFgGo"
      },
      "source": [
        "## AddAuthData\n",
        "\n",
        "From [annex A.2.1 of the ZigBee specification](https://lucidar.me/en/zigbee/files/docs-05-3474-21-0csg-zigbee-specification.pdf#page=480&zoom=100,93,300):\n",
        "\n",
        "> 1. *Form the octet string representation L(a) of the length l(a) of the octet \n",
        "string a.* [...]\n",
        "> 2. *Right-concatenate the octet string L(a) with the octet string a itself. Note that the resulting string contains and a encoded in a reversible manner.*\n",
        "> 3. *Form the padded message AddAuthData by right-concatenating the resulting string with the smallest non-negative number of all-zero octets such that the octet string AddAuthData has length divisible by 16.*\n",
        "\n",
        "In the present case (case b in the ZigBee specification),  L(a) is the 2-octets encoding of l(a)."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "FpK97wDRE8XC",
        "outputId": "ba29fb62-b91e-4595-f9b6-47814f1db84c"
      },
      "source": [
        "# Right-concatenate the octet string L(a) with the octet string a itself.\n",
        "AddAuthData = len(a).to_bytes(2, byteorder = 'big') + a\n",
        "# Form the padded message AddAuthData\n",
        "AddAuthData = pad(AddAuthData)\n",
        "\n",
        "print(len(AddAuthData), 'bytes')\n",
        "printhex(AddAuthData)"
      ],
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "32 bytes\n",
            "00 16 48 02 00 00 8A 5C 1E 5D 2D E1 00 00 00 01 3C E8 01 00 8D 15 00 01 00 00 00 00 00 00 00 00\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "xMGlGx7ZGDz2"
      },
      "source": [
        "## PlaintextData\n",
        "\n",
        "From [annex A.2.1 of the ZigBee specification](https://lucidar.me/en/zigbee/files/docs-05-3474-21-0csg-zigbee-specification.pdf#page=480&zoom=100,93,300):\n",
        "\n",
        "> *Form the padded message PlaintextData by right-concatenating the octet string m with the smallest non-negative number of all-zero octets such that the octet string PlaintextData has length divisible by 16.*"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "dRlyOL__GHZM",
        "outputId": "6155499c-4623-4e71-82eb-bb5d36237068"
      },
      "source": [
        "PlaintextData = m\n",
        "# Padding (not necessary here, because length(m)=16)\n",
        "PlaintextData = pad(PlaintextData)\n",
        "\n",
        "print (len(PlaintextData), 'bytes')\n",
        "printhex (PlaintextData)"
      ],
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "16 bytes\n",
            "00 01 12 00 04 01 01 62 18 C3 0A 55 00 21 01 00\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "wKvSaZfFGXrs"
      },
      "source": [
        "## AuthData\n",
        "\n",
        "From [annex A.2.1 of the ZigBee specification](https://lucidar.me/en/zigbee/files/docs-05-3474-21-0csg-zigbee-specification.pdf#page=480&zoom=100,93,300):\n",
        "\n",
        "> *Form the message AuthData consisting of the octet strings AddAuthData and PlaintextData:* \n",
        ">\n",
        "> **AuthData = AddAuthData || PlaintextData**\n",
        "\n",
        "Note that ```AuthData``` is part of the input string of the EAS-CBC algorithm and can be truncated into 16 bytes segments for AES:\n",
        "\n",
        "AuthData = B1 || B2 || B3 || B4"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "CTf37Qq2GaGA",
        "outputId": "e2af9dd4-dadb-4b33-f828-ce42f44fd1c4"
      },
      "source": [
        "AuthData = AddAuthData + PlaintextData\n",
        "print (len(AuthData), 'bytes')\n",
        "printhex (AuthData)"
      ],
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "48 bytes\n",
            "00 16 48 02 00 00 8A 5C 1E 5D 2D E1 00 00 00 01 3C E8 01 00 8D 15 00 01 00 00 00 00 00 00 00 00 00 01 12 00 04 01 01 62 18 C3 0A 55 00 21 01 00\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "QuLoIHBqPOni"
      },
      "source": [
        "## Flags\n",
        "\n",
        "The 1-octet flags is created according to [section A.2.2 of the ZigBee specification](https://lucidar.me/en/zigbee/files/docs-05-3474-21-0csg-zigbee-specification.pdf#page=480&zoom=100,93,610):\n",
        "\n",
        "\n",
        "> _Form the 1-octet Flags field consisting of the 1-bit Reserved field, the 1-bit Adata field, and the 3-bit representations of the integers M and L, as follows:_\n",
        ">\n",
        "> _Flags = Reserved || Adata || M || L_\n",
        ">\n",
        "> _Here, the 1-bit Reserved field is reserved for future expansions and shall be set to ‘0’._\n",
        "\n",
        "**Reserved = 0b0**\n",
        "\n",
        "> _The 1-bit Adata field is set to ‘0’ if l(a)=0, and set to ‘1’ if l(a)>0. _\n",
        "\n",
        "**Adata = 0b1** since l(a)=22\n",
        "\n",
        "\n",
        "> _The L field is the 3-bit representation of the integer L-1, in most-significant-bit-first order._\n",
        "\n",
        "Since L=2, L-1=1 => **L field = 001** \n",
        "\n",
        ">_The M field is the 3-bit representation of the integer (M-2)/2 if M>0 and of the integer 0 if M=0, in most-significant-bit-first order_\n",
        "\n",
        "Since M=4, M-2/2=1 => **M field = 001**\n",
        "\n",
        "In conclusion :\n",
        "\n",
        "**Flags = 0b01001001 = 0x49**\n"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "3BRCePfkRYy1",
        "outputId": "5c3fda05-bf83-4598-f7a6-b6f8199391ce"
      },
      "source": [
        "Flags = bytes([0x49]) # = ([0b01001001])\n",
        "printhex (Flags)"
      ],
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "49\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "0FvkFdgpOjCn"
      },
      "source": [
        "## B0\n",
        "\n",
        "B0 is the first 16 bytes input of the AES algorithm. According to [section A.2.2 of the ZigBee specification](https://lucidar.me/en/zigbee/files/docs-05-3474-21-0csg-zigbee-specification.pdf#page=480&zoom=100,93,800):\n",
        "\n",
        "> *Form the 16-octet B0 field consisting of the 1-octet Flags field defined above, the 15-L octet nonce field N, and the L-octet representation of the length field l(m), as follows:*\n",
        ">\n",
        "> _B0 = Flags || Nonce N || l(m)_\n"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "KktLQp8-HiIJ",
        "outputId": "6e7e671a-4eab-4ab7-a819-0a13b4ca3986"
      },
      "source": [
        "B0 = Flags + nonce + len(m).to_bytes(2, byteorder = 'big')\n",
        "print ('Should be 16 bytes =>', len(B0), 'bytes')\n",
        "printhex (B0)"
      ],
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Should be 16 bytes => 16 bytes\n",
            "49 01 3C E8 01 00 8D 15 00 E1 00 00 00 2D 00 10\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "m9Z6aZ09Sh39"
      },
      "source": [
        "## X0\n",
        "\n",
        "According to [section A.2.2 of the ZigBee specification](https://lucidar.me/en/zigbee/files/docs-05-3474-21-0csg-zigbee-specification.pdf#page=480&zoom=100,93,855):\n",
        "\n",
        "> _Parse the message AuthData as B1 || B2 || ... ||Bt, where each message block Bi is a 16-octet string. The CBC-MAC value Xt+1 is defined by:_\n",
        ">\n",
        "> _X0: = 0b0 (x128); Xi+1:= E(Key, Xi ⊕ Bi) for i=0, ... , t._\n",
        "\n",
        "\n",
        "X0 is initialized with:\n",
        "\n",
        "**X0 = 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00**\n"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "7coSq7BoJSxp",
        "outputId": "55251766-d48d-4379-8366-c339dcd48cd2"
      },
      "source": [
        "X0 = bytes([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])\n",
        "printhex(X0)"
      ],
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "DPAbSCEzTZBn"
      },
      "source": [
        "## Authentication tag T (MIC)\n",
        "\n",
        "In this section, the message integrity code (MIC) is generated. \n",
        "According to [section A.2.2 of the ZigBee specification](https://lucidar.me/en/zigbee/files/docs-05-3474-21-0csg-zigbee-specification.pdf#page=480&zoom=100,93,855), the algorithm used for encryption is AES-128-CBC. Instead of processing groups of 16 bytes (B0, B1, B2 ...), we use here the [Python Cryptography Toolkit](https://pycryptodome.readthedocs.io/en/latest/src/cipher/aes.html) for encryption with the following parameters:\n",
        "\n",
        "* mode (encryption algorithm) = AES-CBC\n",
        "* IV = X0\n",
        "* Raw data = B0 || AuthData\n",
        "\n",
        "The MIC generated is ```AC 4C 76 AF``` which is the expected [NWK MIC](https://lucidar.me/fr/zigbee/autopsy-of-a-zigbee-frame/#nwk-mic).\n"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "a2KtZzDGJkZV",
        "outputId": "9b8eb85e-e3e1-404d-8bcb-9600cdf6e76c"
      },
      "source": [
        "cipher = AES.new(key, AES.MODE_CBC, X0)\n",
        "X1 = cipher.encrypt(B0 + AuthData)\n",
        "T = X1[-16:-12]\n",
        "print ('MIC = ')\n",
        "printhex (T)"
      ],
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "MIC = \n",
            "AC 4C 76 AF\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "5ht2kP9kVDmo"
      },
      "source": [
        "## Encryption transformation\n",
        "\n",
        "The last part of the process is the encryption transformation based on [Section A.2.3 of the Zigbee Specification](https://lucidar.me/en/zigbee/files/docs-05-3474-21-0csg-zigbee-specification.pdf#page=481&zoom=100,93,130).\n",
        "\n",
        "The first step is to build the 1-octet Flags:\n",
        "\n",
        "> _Form the 1-octet Flags field consisting of two 1-bit Reserved fields, and the 3- bit representations of the integers 0 and L, as follows:_\n",
        ">\n",
        "> _Flags = Reserved || Reserved || 0 || L_\n",
        ">\n",
        "> _Here, the two 1-bit Reserved fields are reserved for future expansions and shall be set to ‘0’. The L field is the 3-bit representation of the integer L-1, in most-significant- bit-first order. The ‘0’ field is the 3-bit representation of the integer 0, in most-significant-bit-first order._\n",
        "\n",
        "In our case, L=2 => L-1 = 1"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "aRYN7IVTWSvk",
        "outputId": "5673dbfe-8ec3-4e7e-8b34-dfe7e3eee45d"
      },
      "source": [
        "Flags = bytes ([0b00000001])\n",
        "printhex (Flags)"
      ],
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "01\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "A7D86TIRVnY5"
      },
      "source": [
        "## Ai\n",
        "\n",
        "According to [section A.2.3 of the ZigBee specification](https://lucidar.me/en/zigbee/files/docs-05-3474-21-0csg-zigbee-specification.pdf#page=481&zoom=100,93,317), the 16-octet Ai are built with the following method:\n",
        "\n",
        "> _Define the 16-octet Ai field consisting of the 1-octet Flags field defined above, the 15-L octet nonce field N, and the L-octet representation of the integer i, as follows:_\n",
        ">\n",
        "> _Ai = Flags || Nonce N || Counter i, for i=0, 1, 2, …_\n",
        ">\n",
        "> _Note that this definition ensures that all the Ai fields are distinct from the B0 fields that are actually used, as those have a Flags field with a non-zero encoding of M in the positions where all Ai fields have an all-zero encoding of the integer 0 (see section A.2.2, step 1)._\n",
        "\n",
        "Since ```PlaintextData``` is a 16-bytes length, we only need A0 and A1.\n",
        "* A0 is for MIC encryption (tag U)\n",
        "* A1 is for generating Ciphertext"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "ic4_1pCcWd4f",
        "outputId": "61acf08e-710b-44ef-945c-5a62521e55ef"
      },
      "source": [
        "A0 = Flags + nonce + bytes([0x00, 0x00])\n",
        "A1 = Flags + nonce + bytes([0x00, 0x01])\n",
        "printhex (A0)\n",
        "printhex (A1)"
      ],
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "01 01 3C E8 01 00 8D 15 00 E1 00 00 00 2D 00 00\n",
            "01 01 3C E8 01 00 8D 15 00 E1 00 00 00 2D 00 01\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "x3K1-e9Yfg7I"
      },
      "source": [
        "## AES CTR\n",
        "\n",
        "The algorithm used for encrypting the payload is AES-CTR. Here, we use the [Python Cryptography Toolkit](https://pycryptodome.readthedocs.io/en/latest/src/cipher/aes.html) that supports AES-CTR. Since `PlaintextData` is only 16 bytes, we cheat the CTR counter by always returning A1.\n",
        "\n",
        "According to the [Raw encrypted frame](https://lucidar.me/en/zigbee/autopsy-of-a-zigbee-frame/#raw-frames) listened on the network, the expected encrypted data is:\n",
        "\n",
        "**Expected encrypted payload:** ```EA 59 DE 1F 96 0E EA 8A EE 18 5A 11 89 30 96 41```\n"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "y7W67A4EY8Tf",
        "outputId": "32ee9d89-3a45-43d9-ab9e-1277f9d50fc3"
      },
      "source": [
        "def counter():  \n",
        "  return A1\n",
        "cipher = AES.new(key, AES.MODE_CTR, counter = counter)\n",
        "Ciphertext = cipher.encrypt(PlaintextData)\n",
        "printhex (Ciphertext)"
      ],
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "EA 59 DE 1F 96 0E EA 8A EE 18 5A 11 89 30 96 41\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "iwnFLNb4mcO3"
      },
      "source": [
        "## Encrypted authentication tag U\n",
        "\n",
        "The last part of the encryption is the tag U (encrypted message integrity code):\n",
        "\n",
        "According to [section A.2.3 of the ZigBee specification](https://lucidar.me/en/zigbee/files/docs-05-3474-21-0csg-zigbee-specification.pdf#page=481&zoom=100,93,500), the authentification tag is built according to the following:\n",
        "\n",
        "> _Define the 16-octet encryption block S0 by:_\n",
        ">\n",
        "> _S0:= E(Key, A0)_\n",
        ">\n",
        "> _The encrypted authentication tag U is the result of XOR-ing the string consisting of the leftmost M octets of S0 and the authentication tag T._\n",
        "\n",
        "According to the [raw encrypted frame](https://lucidar.me/en/zigbee/autopsy-of-a-zigbee-frame/#raw-frames) listened on the network, the expected encrypted data is:\n",
        "\n",
        "**Expected encrypted tag U:** ```4E 05 A2 43```\n"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "_3zC21pQaaIC",
        "outputId": "0c3a48e1-12a9-43a6-d092-22d76ac99003"
      },
      "source": [
        "# Encryption: S0:= E(Key, A0)\n",
        "cipher = AES.new(key)\n",
        "S0 = cipher.encrypt(A0)\n",
        "\n",
        "# Perform S0[0:4] XOR T\n",
        "U = bytes(a ^ b for (a, b) in zip(S0[0:4], T))\n",
        "printhex (U)"
      ],
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "4E 05 A2 43\n"
          ],
          "name": "stdout"
        }
      ]
    }
  ]
}