COMS-1 LRIT Key Decryption
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.
- 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)
-
Key 1
- 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 85 84 72 F4 AD E3 BF 7B
00 66 00 11 22 33 44 55 66 77 85 84 72 F4 AD E3 BF 7B
00 67 00 11 22 33 44 55 66 77 85 84 72 F4 AD E3 BF 7B
00 68 00 11 22 33 44 55 66 77 85 84 72 F4 AD E3 BF 7B
...
00 D6 00 11 22 33 44 55 66 77 85 84 72 F4 AD E3 BF 7B
00 D7 00 11 22 33 44 55 66 77 85 84 72 F4 AD E3 BF 7B
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.
The 8 byte keys are saved to a new decrypted Key Message file (302 bytes) in the format:
-
Key count (2 bytes)
- Always
0x001E
- Always
-
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)
-
Key 1
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.
To see these keys put to use decrypting real-time imagery using xrit-rx head to the blog post below:
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