Skip to content

Instantly share code, notes, and snippets.

@heathhenley
Created February 3, 2024 01:45
Show Gist options
  • Save heathhenley/7ecf13aec81eb09399d204e240306e00 to your computer and use it in GitHub Desktop.
Save heathhenley/7ecf13aec81eb09399d204e240306e00 to your computer and use it in GitHub Desktop.
S63.ipynb
Display the source blob
Display the rendered blob
Raw
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"provenance": [],
"authorship_tag": "ABX9TyP+ZAEr/o7RunkYh6KNKxzx",
"include_colab_link": true
},
"kernelspec": {
"name": "python3",
"display_name": "Python 3"
},
"language_info": {
"name": "python"
}
},
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "view-in-github",
"colab_type": "text"
},
"source": [
"<a href=\"https://colab.research.google.com/gist/heathhenley/7ecf13aec81eb09399d204e240306e00/s63.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "D_hb2MDSd4aG",
"outputId": "f1fd02d5-e303-4a21-d28b-b0712533a5df"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Collecting pycryptodome\n",
" Downloading pycryptodome-3.20.0-cp35-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.1 MB)\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m2.1/2.1 MB\u001b[0m \u001b[31m10.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[?25hInstalling collected packages: pycryptodome\n",
"Successfully installed pycryptodome-3.20.0\n"
]
}
],
"source": [
"%pip install pycryptodome"
]
},
{
"cell_type": "code",
"source": [
"# This example is the one in 5.2.1 in the linked doc:\n",
"# https://iho.int/uploads/user/Services%20and%20Standards/ENC_ECDIS/data_protection/S-63_Test_Data_Implementation_Guide_v1.1.pdf\n",
"from Crypto.Cipher import Blowfish\n",
"import binascii\n",
"\n",
"# Hardware ID / encrypted HW_ID\n",
"\n",
"# Assigned to manufacturer (OEM) by IHO:\n",
"m_id = \"10\"\n",
"m_key = \"10121\".encode()\n",
"\n",
"# Generated by manufacturer (OEM) for each installation:\n",
"# - padded to 8 bytes\n",
"hw_id = \"12345\".encode() + b\"\\x03\" * 3\n",
"\n",
"# Encrypt hardware id with blowfish and m_key:\n",
"cipher = Blowfish.new(m_key, Blowfish.MODE_ECB)\n",
"encrypted_hw_id = cipher.encrypt(hw_id).hex().upper()\n",
"print(f\"Encrypted HW_ID: {encrypted_hw_id}\")\n",
"\n",
"# When it's time to decrypt:\n",
"print(f\"Decrypted: {cipher.decrypt(bytes.fromhex(encrypted_hw_id))[:5].decode()}\")"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "1GEJPczsd9Hq",
"outputId": "7fb6f47d-5e86-4eca-dc85-ccd844613033"
},
"execution_count": 52,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Encrypted HW_ID: 66B5CBFDF7E4139D\n",
"Decrypted: 12345\n"
]
}
]
},
{
"cell_type": "code",
"source": [
"import binascii\n",
"\n",
"# Compute the CRC32 checksum of the encrypted hw_id:\n",
"crc = binascii.crc32(encrypted_hw_id.encode())\n",
"crc = f\"{crc:x}\".upper().zfill(8)\n",
"print(f\"CRC32: {crc}\")"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "vepO6fu3tWCA",
"outputId": "bc60eef1-4c8c-4eb4-e2ff-90f457892e15"
},
"execution_count": 111,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"CRC32: 5B6086C2\n"
]
}
]
},
{
"cell_type": "code",
"source": [
"# Now we have everything to compute the UPN:\n",
"upn = encrypted_hw_id + crc + m_id.encode().hex().upper()\n",
"\n",
"print(f\"UPN: {upn}\")"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "abSWi31WvXcB",
"outputId": "415ec9ba-ddae-4e4f-9821-58e40f608a2b"
},
"execution_count": 112,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"UPN: 66B5CBFDF7E4139D5B6086C23130\n"
]
}
]
},
{
"cell_type": "code",
"source": [
"import os\n",
"\n",
"# An example of how you might make a 5 byte key:\n",
"\n",
"# random 5 byte key - using cryptographically secure option\n",
"os.urandom(5).hex()"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 35
},
"id": "JIavBR1SY5Sd",
"outputId": "2a02cd7a-8604-4c8e-80d4-df067cfcfa21"
},
"execution_count": 3,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"'20b459214d'"
],
"application/vnd.google.colaboratory.intrinsic+json": {
"type": "string"
}
},
"metadata": {},
"execution_count": 3
}
]
},
{
"cell_type": "code",
"source": [
"import binascii\n",
"from Crypto.Cipher import Blowfish\n",
"\n",
"# Check the checksum, and then decrypt hardware_id:\n",
"\n",
"upn = \"66B5CBFDF7E4139D5B6086C23130\"\n",
"# We looked up the MKEY using the MID at the end of the UPN (3130)\n",
"MKEY = \"10121\"\n",
"\n",
"# the encrypted hw id is the first 16 chars\n",
"encrypted_hw_id = upn[:16]\n",
"\n",
"# the crc check is the next 8:\n",
"crc = upn[16:24]\n",
"\n",
"# we can make sure it matches to ensure there was no error / data\n",
"# integrity issues:\n",
"assert crc == f\"{binascii.crc32(encrypted_hw_id.encode()):x}\".upper()\n",
"\n",
"# The crc checks out, so lets decrypt the hw_id using the m_key:\n",
"cipher = Blowfish.new(MKEY.encode(), Blowfish.MODE_ECB)\n",
"\n",
"decrypted_hw_id = cipher.decrypt(bytes.fromhex(encrypted_hw_id))\n",
"# only the first 5, the rest is padding\n",
"print(f\"HW_ID: {decrypted_hw_id[:5].decode()}\")"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "Z8yAnDArY64M",
"outputId": "2aed110c-4c27-415b-c6c7-2a91195d3c28"
},
"execution_count": 64,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"HW_ID: 12345\n"
]
}
]
},
{
"cell_type": "code",
"source": [
"# Encypt the cell keys:\n",
"\n",
"hw_id = \"12345\" # --> got this from the UPN as above\n",
"# append first byte of hw_id to end of hw_id (in 9.6.2 of standard)\n",
"hw_id = hw_id + hw_id[0]\n",
"\n",
"# padding to 8 bytes\n",
"padding = b\"\\x03\" * 3\n",
"\n",
"# the cell keys that were randomly generated, and used to encrypt the cell\n",
"cell_key1, cell_key2 = \"9C467D359D\", \"27737811B4\"\n",
"\n",
"cipher = Blowfish.new(hw_id.encode(), Blowfish.MODE_ECB)\n",
"eck1 = cipher.encrypt(bytes.fromhex(cell_key1) + padding).hex().upper()\n",
"eck2 = cipher.encrypt(bytes.fromhex(cell_key2) + padding).hex().upper()\n",
"\n",
"print(f\"Encrypted key 1: {eck1}\\nEncrypted key 2: {eck2}\")"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "dm7wkIjpjVqU",
"outputId": "bc0a4fbb-e82c-4143-d4c9-8abb72c21813"
},
"execution_count": 191,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Encrypted key 1: F7B3814E59C84805\n",
"Encrypted key 2: D150D571B9BE53A6\n"
]
}
]
},
{
"cell_type": "code",
"source": [
"# Make a permit:\n",
"cell_name = \"NO4D0512\"\n",
"expiration_date = \"20040826\"\n",
"permit = f\"{cell_name}{expiration_date}{eck1}{eck2}\".upper()\n",
"hex_crc = f\"{binascii.crc32(permit.encode()):x}\".upper()\n",
"bytes_crc = bytes.fromhex(hex_crc)\n",
"encrypted_crc = cipher.encrypt(bytes_crc + b\"\\x04\" * 4).hex().upper()\n",
"permit += encrypted_crc\n",
"print(f\"Full cell permit: {permit}\")"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "QLRIDH50kaz_",
"outputId": "12a9d74b-f49e-400b-ffad-1497627e0761"
},
"execution_count": 177,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Full cell permit: NO4D051220040826F7B3814E59C84805D150D571B9BE53A642E5B05951975E9C\n"
]
}
]
},
{
"cell_type": "code",
"source": [
"# Decrypt the cell keys, use them to decrypt the charts, and then extract!\n",
"import zipfile\n",
"\n",
"full_permit = \"NO4D051220040826F7B3814E59C84805D150D571B9BE53A642E5B05951975E9C\"\n",
"\n",
"# extract encrypted cell key\n",
"eck1 = full_permit[16:32]\n",
"\n",
"# go backwards to get the cell key from the permit\n",
"hw_id = \"12345\"\n",
"hw_id6 = hw_id + hw_id[0]\n",
"\n",
"# decrypt the cell key\n",
"cipher = Blowfish.new(hw_id6.encode(), Blowfish.MODE_ECB)\n",
"ck1 = cipher.decrypt(bytes.fromhex(eck1))[:5]\n",
"\n",
"# encrypted s57\n",
"with open(\"NO4D0512.000\", \"rb\") as f:\n",
" encrypted_s57 = f.read()\n",
"\n",
"# decrypt the s57 data with the cell key\n",
"cipher = Blowfish.new(ck1, Blowfish.MODE_ECB)\n",
"decrypted_s57_zip = cipher.decrypt(encrypted_s57)\n",
"\n",
"# check the header (the zip file header starts with PK)\n",
"assert decrypted_s57_zip[0:2] == b\"PK\", \"invalid zip\"\n",
"\n",
"# save a copy of the decrypted chart\n",
"with open(\"NO4D0512_decrypted.000\", \"wb\") as f:\n",
" f.write(decrypted_s57_zip)\n",
"\n",
"# extract\n",
"with zipfile.ZipFile(\"NO4D0512_decrypted.000\") as z:\n",
" z.extractall(\"NO4D0512_decrypted_unzipped.000\")\n"
],
"metadata": {
"id": "YN1-uaM1yk5z"
},
"execution_count": 205,
"outputs": []
},
{
"cell_type": "code",
"source": [],
"metadata": {
"id": "p2vMnUHSJDvT"
},
"execution_count": 162,
"outputs": []
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment