Cover Image

COMS-1 LRIT Key Decryption

June 26, 2018 - sam210723

Like NOAA's series of GOES meteorological satellites, COMS-1 (128.2°E) operated by the Korea Meteorological Administration (KMA) has both LRIT and HRIT downlinks for disseminating real-time meteorological data. Unlike GOES, these downlinks are encrypted using single-layer DES and decryption keys are controlled by KMA through an application approval process.

This post will concentrate on the first step in decrypting these downlinks, decrypting the Key Message file. If you're interested in the steps involved in LRIT/HRIT reception prior to decryption, check out Lucas Teske's GOES Satellite Hunt series and the Open Satellite Project.

Key Message File

KMA seems to only approve applications from governments, research institutes, and large organisations so I'll be working with example decryption keys. These keys were provided by KMA in their decryption sample code (linked below). Once KMA approves a ground station application, an encrypted "Key Message" file containing a list of 30 keys is emailed to the user.

The structure of an encrypted Key Message file (550 bytes) is:

  •  Application Time Header  (8 bytes)
  • Key List (540 bytes)
    • Key 1
      •  Index  (2 bytes)
      •  Encrypted Key  (16 bytes)
    • Key 2
      •  Index  (2 bytes)
      •  Encrypted Key  (16 bytes)
        ...
    • Key 30
      •  Index  (2 bytes)
      •  Encrypted Key  (16 bytes)
  •  CRC  (2 bytes)


This is an example encrypted Key Message file:

 20 11  02 09 07 30 00 00 
 00 65  00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 
 00 66  00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 
 00 67  00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 
 00 68  00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 
...
 00 D6  00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 
 00 D7  00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 
 00 11 


Application Time Header

Top of the file is a timestamp in the format "YY YY MM DD HH mm ss ??". This refers to either when the application was approved or when the Key Message file was generated. The purpose of the last byte in this header is unknown (maybe just padding header out to 8 bytes). This header is not relevant to the decryption process.

Key List

This is a list of 30 index/key pairs split up into two groups containing 15 pairs each. Keys 0x65 to 0x73 in group 0, 0xC9 to 0xD7 in group 1. Each key is 16 bytes long with the last 8 bytes being encrypted padding (0x858472F4ADE3BF7B). This padding is later discarded once the key has been decrypted.

CRC

The file ends with a 16 bit checksum calculated using CRC-16/CCITT-FALSE algorithm, which uses the polynomials x16 + x12 + x5 + 1 (or 0x1021 in hex). For efficiency, a look-up table of 256 checksums is calculated before calculating the CRC of the Key Message file.

Key Decryption

During the ground station application process the user must provide a unique MAC address to KMA. Two null bytes are appended to the MAC address to give a total of 8 bytes. These 8 bytes are the encryption/decryption key used in a ground stations Key Message file. The MAC address for the example Key Message File is provided by KMA.

Each 16 byte key is individually decrypted into an 8 byte key followed by 8 bytes of padding (0x0808080808080808). This padding is discarded, leaving the 8 byte key used for LRIT/HRIT decryption.

Key decryption method

Key decryption method

The 8 byte keys are saved to a new decrypted Key Message file (302 bytes) in the format:

  •  Key count  (2 bytes)
    • Always 0x001E
  • Key List (300 bytes)
    • Key 1
      •  Index  (2 bytes)
      •  Decrypted Key  (8 bytes)
    • Key 2
      •  Index  (2 bytes)
      •  Decrypted Key  (8 bytes) ...
    • Key 30
      •  Index  (2 bytes)
      •  Decrypted Key  (8 bytes)


This is an example decrypted Key Message file:

 00 1E 
 00 65  00 11 22 33 44 55 66 77 
 00 66  00 11 22 33 44 55 66 77 
 00 67  00 11 22 33 44 55 66 77 
 00 68  00 11 22 33 44 55 66 77 
...
 00 D6  00 11 22 33 44 55 66 77 
 00 D7  00 11 22 33 44 55 66 77 


KMA provides code examples and some documentation for decrypting the Key Message file (linked below). The following code is a simplified implementation of the key decryption process in Python 3:

# Define field lengths
headerLen = 8
dataLen = 540
crcLen = 2

# Open encrypted Key Message file in binary mode
kmFile = open(args.PATH, mode="rb")
kmBytes = kmFile.read()

# Split file into fields
kmHeader = kmBytes[:headerLen]
kmData = kmBytes[headerLen: headerLen + dataLen]
kmCRC = kmBytes[-crcLen:]

# Parse Application Time header
kmHeaderHex = kmHeader.hex()
appYear = kmHeaderHex[0:4]
appMonth = kmHeaderHex[4:6]
appDay = kmHeaderHex[6:8]
appHour = kmHeaderHex[8:10]
appMin = kmHeaderHex[10:12]
appSec = str(round(int(kmHeaderHex[12:16])/1000))

# Generate CRC-16/CCITT-FALSE lookup table
crcTable = []
poly = 0x1021
initial = 0xFFFF
for i in range(256):
    crc = 0
    c = i << 8

    for j in range(8):
        if (crc ^ c) & 0x8000:
            crc = (crc << 1) ^ poly
        else:
            crc = crc << 1

        c = c << 1
        crc = crc & 0xFFFF

    crcTable.append(crc)

# Calculate CRC-16/CCITT-FALSE from encrypted Key Message file
crcData = kmHeader + kmData
crc = initial

for i in range(len(crcData)):
    lutPos = ((crc >> 8) ^ crcData[i]) & 0xFFFF
    crc = ((crc << 8) ^ crcTable[lutPos]) & 0xFFFF

# Compare CRC from file and calculated CRC
if int(crc) != int.from_bytes(kmCRC, byteorder='big'):
    exit(0)

# Add encrypted keys to list
indexes = []
encKeys = []
for i in range(30):     # 30 keys total
    offset = i*18       # 18 bytes per index/key pair
    indexes.append(kmData[offset: offset+2])        # Bytes 0-1: Key index
    encKeys.append(kmData[offset+2:offset+18])      # Bytes 2-17: Encrypted key

# Decrypt keys and add to list
macBin = binascii.unhexlify(args.MAC) + b'\x00\x00'     # MAC String to binary + two byte padding
decKeys = []
for i in range(30):
    decKey = pyDes.des(macBin).decrypt(encKeys[i])
    decKeys.append(decKey[:8])
    print("[{0}   ]: {1}".format(indexes[i][-1:].hex().upper(), decKeys[i].hex().upper()))

# Write decrypted Key Message file to disk
decKmFileName = "" + args.PATH + ".dec"
decKmFile = open(decKmFileName, mode="wb")

decKmFile.write(b'\x00\x1E')  # Number of keys (30/0x1E, 2 bytes)
for i in range(30):
    decKmFile.write(indexes[i])
    decKmFile.write(decKeys[i])

decKmFile.close()

The complete code can be found in the GitHub repo.


LRIT Decryption

Each xRIT file downlinked from COMS-1 contains an Encryption Key Header (header type 7) that specifies the index of the encryption key being used. Below is the output of xrit-header.py showing key 0xD7 is in use on an Enhanced Northern Hemisphere image.

LRIT headers LRIT headers

The next post will cover using Python to decrypt LRIT data from the real downlink using GNURadio for demodulation and Open Satellite Project as the decoder.


Resources

KMA NMSC Introduction to COMS
COMS-1 WMO OSCAR
COMS-1 eoPortal
LRIT Mission Specific Implementation
HRIT Mission Specific Implementation
LRIT Sample Data
HRIT Sample Data
Decryption Sample Code (C)
Decryption Sample Code (Java)
Open Satellite Project - GitHub
GOES Satellite Hunt - Lucas Teske