first commit
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
setup.*
|
||||
135
README.md
Normal file
135
README.md
Normal file
@@ -0,0 +1,135 @@
|
||||
# Rigol setup.stp Crypter
|
||||
|
||||
A utility for encrypting, decrypting, and parsing Rigol DG[89]00 Pro Arbitrary Waveform Generator `setup.stp`.
|
||||
|
||||
## Command-Line Options
|
||||
|
||||
```sh
|
||||
usage: rigol_setup_crypter.py [-h] (-e | -d | -p) (-t INPUT_TEXT | -f INPUT_FILE) [-k CPU_SERIAL] [-o FILEPATH]
|
||||
|
||||
Encrypt or Decrypt Rigol DG[89]00 Pro setup.stp using the CPU SN.
|
||||
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
-e, --encrypt Encrypt plain text data
|
||||
-d, --decrypt Decrypt hex string data
|
||||
-p, --parse Parse Rigol CSV data string
|
||||
-t, --text INPUT_TEXT
|
||||
Input data as text (Plain for Encrypt/Parse, Hex for Decrypt)
|
||||
-f, --file INPUT_FILE
|
||||
Path to read input data from file
|
||||
-k, --key CPU_SERIAL CPU Serial Number (Required for -e/-d modes)
|
||||
-o, --output FILEPATH
|
||||
Optional path to save output. Console if omitted
|
||||
```
|
||||
|
||||
## Setup and Workflow
|
||||
|
||||
### Prerequisites
|
||||
|
||||
1. Install ADB (Android Debug Bridge) on your system:
|
||||
|
||||
- **Linux**: `sudo apt install adb` (or equivalent for your distribution)
|
||||
- **Windows**: Download and install [Android Platform Tools](https://developer.android.com/studio/releases/platform-tools)
|
||||
|
||||
2. Connect your Rigol AFG to your local network and note its IP address
|
||||
|
||||
### Extracting the Setup File
|
||||
|
||||
1. Connect to your device via ADB:
|
||||
|
||||
```
|
||||
adb connect IP:55555
|
||||
```
|
||||
|
||||
Replace `IP` with your device's IP address
|
||||
|
||||
2. Pull the setup file from the device:
|
||||
|
||||
```
|
||||
adb pull /rigol/data/setup.stp
|
||||
```
|
||||
|
||||
3. Read your CPU serial:
|
||||
|
||||
```
|
||||
adb shell -- /rigol/shell/get_cpu_serial_num.sh
|
||||
```
|
||||
|
||||
4. **IMPORTANT**: Create a backup of the original file:
|
||||
```
|
||||
cp setup.stp setup.stp.backup
|
||||
```
|
||||
|
||||
### Modifying the Setup File
|
||||
|
||||
1. Decrypt the setup file:
|
||||
|
||||
```
|
||||
python rigol_setup_crypter.py -d -f setup.stp -k YOUR_CPU_SERIAL -o setup.csv
|
||||
```
|
||||
|
||||
Replace `YOUR_CPU_SERIAL` with your device's CPU serial number
|
||||
|
||||
2. Parse the file to better understand its structure:
|
||||
|
||||
```
|
||||
python rigol_setup_crypter.py -p -f setup.csv
|
||||
```
|
||||
|
||||
3. Edit the CSV file using a text editor of your choice
|
||||
|
||||
4. Verify your changes by parsing again:
|
||||
|
||||
```
|
||||
python rigol_setup_crypter.py -p -f setup.csv
|
||||
```
|
||||
|
||||
5. Encrypt the modified file:
|
||||
```
|
||||
python rigol_setup_crypter.py -e -f setup.csv -k YOUR_CPU_SERIAL -o setup.stp.new
|
||||
```
|
||||
|
||||
### Uploading the Modified File
|
||||
|
||||
1. Push the modified file back to the device:
|
||||
|
||||
```
|
||||
adb push setup.stp.new /rigol/data/setup.stp
|
||||
```
|
||||
|
||||
2. Restart your device to apply the changes
|
||||
|
||||
```
|
||||
adb shell -- reboot
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Decrypt a setup file
|
||||
|
||||
```
|
||||
python rigol_setup_crypter.py -d -f setup.stp -k d95ebe35672e20c2fc08 -o decrypted.csv
|
||||
```
|
||||
|
||||
### Decrypt a text string and output to console
|
||||
|
||||
```
|
||||
python rigol_setup_crypter.py -d -t "FBC6BDDB0E..." -k d95ebe35672e20c2fc08
|
||||
```
|
||||
|
||||
### Parse a decrypted CSV
|
||||
|
||||
```
|
||||
python rigol_setup_crypter.py -p -f decrypted.csv
|
||||
```
|
||||
|
||||
### Encrypt a modified CSV
|
||||
|
||||
```
|
||||
python rigol_setup_crypter.py -e -f modified.csv -k d95ebe35672e20c2fc08 -o new_setup.stp
|
||||
```
|
||||
|
||||
## Warning
|
||||
|
||||
Keep a backup of the original `setup.csv`.
|
||||
491
rigol_setup_crypter.py
Normal file
491
rigol_setup_crypter.py
Normal file
@@ -0,0 +1,491 @@
|
||||
#!/usr/bin/env python3
|
||||
# author: kuwoyuki <kuwoyuki.eee@cock.li>
|
||||
# Rigol DG[89]00 Pro AWG setup.stp crypter
|
||||
import argparse
|
||||
import binascii
|
||||
import csv
|
||||
import json
|
||||
import io
|
||||
import sys
|
||||
|
||||
# Standard DES Tables (FIPS 46-3, 1-based indexing)
|
||||
# fmt: off
|
||||
IP_1based = [58,50,42,34,26,18,10,2,60,52,44,36,28,20,12,4,62,54,46,38,30,22,14,6,64,56,48,40,32,24,16,8,57,49,41,33,25,17,9,1,59,51,43,35,27,19,11,3,61,53,45,37,29,21,13,5,63,55,47,39,31,23,15,7]
|
||||
FP_1based = [40,8,48,16,56,24,64,32,39,7,47,15,55,23,63,31,38,6,46,14,54,22,62,30,37,5,45,13,53,21,61,29,36,4,44,12,52,20,60,28,35,3,43,11,51,19,59,27,34,2,42,10,50,18,58,26,33,1,41,9,49,17,57,25]
|
||||
E_1based = [32,1,2,3,4,5,4,5,6,7,8,9,8,9,10,11,12,13,12,13,14,15,16,17,16,17,18,19,20,21,20,21,22,23,24,25,24,25,26,27,28,29,28,29,30,31,32,1]
|
||||
P_1based = [16,7,20,21,29,12,28,17,1,15,23,26,5,18,31,10,2,8,24,14,32,27,3,9,19,13,30,6,22,11,4,25]
|
||||
PC1_1based = [57,49,41,33,25,17,9,1,58,50,42,34,26,18,10,2,59,51,43,35,27,19,11,3,60,52,44,36,63,55,47,39,31,23,15,7,62,54,46,38,30,22,14,6,61,53,45,37,29,21,13,5,28,20,12,4]
|
||||
PC2_1based = [14,17,11,24,1,5,3,28,15,6,21,10,23,19,12,4,26,8,16,7,27,20,13,2,41,52,31,37,47,55,30,40,51,45,33,48,44,49,39,56,34,53,46,42,50,36,29,32]
|
||||
S = [[14,4,13,1,2,15,11,8,3,10,6,12,5,9,0,7,0,15,7,4,14,2,13,1,10,6,12,11,9,5,3,8,4,1,14,8,13,6,2,11,15,12,9,7,3,10,5,0,15,12,8,2,4,9,1,7,5,11,3,14,10,0,6,13],[15,1,8,14,6,11,3,4,9,7,2,13,12,0,5,10,3,13,4,7,15,2,8,14,12,0,1,10,6,9,11,5,0,14,7,11,10,4,13,1,5,8,12,6,9,3,2,15,13,8,10,1,3,15,4,2,11,6,7,12,0,5,14,9],[10,0,9,14,6,3,15,5,1,13,12,7,11,4,2,8,13,7,0,9,3,4,6,10,2,8,5,14,12,11,15,1,13,6,4,9,8,15,3,0,11,1,2,12,5,10,14,7,1,10,13,0,6,9,8,7,4,15,14,3,11,5,2,12],[7,13,14,3,0,6,9,10,1,2,8,5,11,12,4,15,13,8,11,5,6,15,0,3,4,7,2,12,1,10,14,9,10,6,9,0,12,11,7,13,15,1,3,14,5,2,8,4,3,15,0,6,10,1,13,8,9,4,5,11,12,7,2,14],[2,12,4,1,7,10,11,6,8,5,3,15,13,0,14,9,14,11,2,12,4,7,13,1,5,0,15,10,3,9,8,6,4,2,1,11,10,13,7,8,15,9,12,5,6,3,0,14,11,8,12,7,1,14,2,13,6,15,0,9,10,4,5,3],[12,1,10,15,9,2,6,8,0,13,3,4,14,7,5,11,10,15,4,2,7,12,9,5,6,1,13,14,0,11,3,8,9,14,15,5,2,8,12,3,7,0,4,10,1,13,11,6,4,3,2,12,9,5,15,10,11,14,1,7,6,0,8,13],[4,11,2,14,15,0,8,13,3,12,9,7,5,10,6,1,13,0,11,7,4,9,1,10,14,3,5,12,2,15,8,6,1,4,11,13,12,3,7,14,10,15,6,8,0,5,9,2,6,11,13,8,1,4,10,7,9,5,0,15,14,2,3,12],[13,2,8,4,6,15,11,1,10,9,3,14,5,0,12,7,1,15,13,8,10,3,7,4,12,5,6,11,0,14,9,2,7,11,4,1,9,12,14,2,0,6,10,13,15,3,5,8,2,1,14,7,4,10,8,13,15,12,9,0,3,5,6,11]]
|
||||
LEFT_SHIFTS = [1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1]
|
||||
# fmt: on
|
||||
|
||||
|
||||
def _bytes_to_bits(byte_data):
|
||||
return [(byte >> i) & 1 for byte in byte_data for i in range(7, -1, -1)]
|
||||
|
||||
|
||||
def _bits_to_bytes(bits):
|
||||
byte_list = bytearray()
|
||||
padded_len = (len(bits) + 7) // 8 * 8
|
||||
bits = bits + [0] * (padded_len - len(bits))
|
||||
for i in range(0, padded_len, 8):
|
||||
byte_val = sum(bits[i + j] << (7 - j) for j in range(8))
|
||||
byte_list.append(byte_val)
|
||||
return bytes(byte_list)
|
||||
|
||||
|
||||
def _permute(block, table):
|
||||
return [block[i - 1] for i in table]
|
||||
|
||||
|
||||
def _xor(bits_a, bits_b):
|
||||
return [a ^ b for a, b in zip(bits_a, bits_b)]
|
||||
|
||||
|
||||
def _rotate_left(bits, n):
|
||||
n = n % len(bits)
|
||||
return bits[n:] + bits[:n]
|
||||
|
||||
|
||||
# key & IV derivation
|
||||
def _generate_iv_bits():
|
||||
"""Generate the 64-bit IV from 'Rigoler'"""
|
||||
iv_str = "Rigoler" # 7 chars
|
||||
iv_bits_49 = []
|
||||
for char in iv_str:
|
||||
char_val = ord(char)
|
||||
char_7_bits_lsb = [(char_val >> i) & 1 for i in range(7)]
|
||||
iv_bits_49.extend(char_7_bits_lsb[::-1]) # reverse for MSB-of-7 first
|
||||
return iv_bits_49 + [0] * 15 # pad with 15 zeros to 64 bits
|
||||
|
||||
|
||||
def _generate_round_keys(cpu_serial):
|
||||
# key material: 48 zeros | 8 bits of char 0 | 8 zeros
|
||||
effective_64_bits = [0] * 64
|
||||
effective_64_bits[48:56] = [(ord(cpu_serial[0]) >> i) & 1 for i in range(7, -1, -1)]
|
||||
|
||||
key_56 = _permute(effective_64_bits, PC1_1based)
|
||||
c, d = key_56[:28], key_56[28:]
|
||||
round_keys = []
|
||||
for i in range(16):
|
||||
c, d = _rotate_left(c, LEFT_SHIFTS[i]), _rotate_left(d, LEFT_SHIFTS[i])
|
||||
round_keys.append(_permute(c + d, PC2_1based))
|
||||
return round_keys
|
||||
|
||||
|
||||
# DES stuff
|
||||
def _des_f_function(r_32, k_48):
|
||||
"""DES Feistel function F(R, K)."""
|
||||
xored = _xor(_permute(r_32, E_1based), k_48)
|
||||
s_out_32 = []
|
||||
for i in range(8):
|
||||
sub = xored[i * 6 : (i + 1) * 6]
|
||||
row = (sub[0] << 1) | sub[5]
|
||||
col = (sub[1] << 3) | (sub[2] << 2) | (sub[3] << 1) | sub[4]
|
||||
val = S[i][row * 16 + col]
|
||||
s_out_32.extend([(val >> k) & 1 for k in range(3, -1, -1)])
|
||||
return _permute(s_out_32, P_1based)
|
||||
|
||||
|
||||
def _des_crypt_block(block_64bits, round_keys, is_encrypt):
|
||||
"""DES encrypt/decrypt single 64-bit block"""
|
||||
l, r = (
|
||||
_permute(block_64bits, IP_1based)[:32],
|
||||
_permute(block_64bits, IP_1based)[32:],
|
||||
)
|
||||
keys = round_keys if is_encrypt else round_keys[::-1]
|
||||
for k in keys:
|
||||
l, r = r, _xor(l, _des_f_function(r, k))
|
||||
return _permute(r + l, FP_1based) # final swap included
|
||||
|
||||
|
||||
# CBC mode and padding
|
||||
def _pad(data_bytes):
|
||||
"""Pad data with space chars (0x20) to multiple of 8 bytes"""
|
||||
rem = len(data_bytes) % 8
|
||||
if rem == 0:
|
||||
# len already a multiple of 8, NO padding
|
||||
return data_bytes
|
||||
else:
|
||||
# add padding to reach the next multiple of 8
|
||||
pad_len = 8 - rem
|
||||
return data_bytes + bytes([0x20] * pad_len)
|
||||
|
||||
|
||||
def _unpad(padded_bytes):
|
||||
"""Remove trailing space padding marked by first ' ' ???"""
|
||||
marker_index = padded_bytes.find(b" ")
|
||||
return padded_bytes[:marker_index] if marker_index != -1 else padded_bytes
|
||||
|
||||
|
||||
def _des_cbc_crypt(data_bytes, iv_64bits, round_keys, is_encrypt):
|
||||
"""DES-CBC encrypt/decrypt with padding"""
|
||||
if is_encrypt:
|
||||
data_bytes = _pad(data_bytes)
|
||||
result_bytes = bytearray()
|
||||
prev_block_bits = iv_64bits
|
||||
|
||||
for i in range(0, len(data_bytes), 8):
|
||||
current_block_bits = _bytes_to_bits(data_bytes[i : i + 8])
|
||||
if is_encrypt:
|
||||
processed_bits = _des_crypt_block(
|
||||
_xor(current_block_bits, prev_block_bits), round_keys, True
|
||||
)
|
||||
result_bytes.extend(_bits_to_bytes(processed_bits))
|
||||
prev_block_bits = processed_bits
|
||||
else: # decrypt
|
||||
decrypted_bits = _des_crypt_block(current_block_bits, round_keys, False)
|
||||
result_bytes.extend(_bits_to_bytes(_xor(decrypted_bits, prev_block_bits)))
|
||||
prev_block_bits = current_block_bits
|
||||
return _unpad(bytes(result_bytes)) if not is_encrypt else bytes(result_bytes)
|
||||
|
||||
|
||||
def DataDecryption(strEncryptedDataHex, cpu_serial):
|
||||
"""Decrypts hex string using DES-CBC"""
|
||||
try:
|
||||
encrypted_bytes = binascii.unhexlify(strEncryptedDataHex)
|
||||
except binascii.Error as e:
|
||||
raise ValueError(f"Invalid hex string provided: {e}")
|
||||
round_keys, iv_bits = _generate_round_keys(cpu_serial), _generate_iv_bits()
|
||||
decrypted_bytes = _des_cbc_crypt(encrypted_bytes, iv_bits, round_keys, False)
|
||||
try:
|
||||
return decrypted_bytes.decode("utf-8")
|
||||
except UnicodeDecodeError:
|
||||
return decrypted_bytes # rawdog bytes
|
||||
|
||||
|
||||
def DataEncryption(strPlainData, cpu_serial):
|
||||
"""Encrypts string using DES-CBC. Returns uppercase hex"""
|
||||
plaintext_bytes = strPlainData.encode("utf-8")
|
||||
round_keys, iv_bits = _generate_round_keys(cpu_serial), _generate_iv_bits()
|
||||
encrypted_bytes = _des_cbc_crypt(plaintext_bytes, iv_bits, round_keys, True)
|
||||
return binascii.hexlify(encrypted_bytes).decode("ascii").upper()
|
||||
|
||||
|
||||
def write_output(data, output_file=None, is_binary=False):
|
||||
# w to file
|
||||
if output_file:
|
||||
mode = "wb" if is_binary else "w"
|
||||
encoding = None if is_binary else "utf-8"
|
||||
with open(output_file, mode, encoding=encoding) as f:
|
||||
f.write(data)
|
||||
print(f"Output successfully written to: {output_file}")
|
||||
return
|
||||
|
||||
# w to stdout
|
||||
if is_binary:
|
||||
print(data.decode("utf-8"))
|
||||
else:
|
||||
print(data)
|
||||
|
||||
|
||||
def parse_rigol_csv(csv_string):
|
||||
FIELD_NAMES = [
|
||||
"Manufacturer",
|
||||
"InstrModel",
|
||||
"InstrSN",
|
||||
"CalibrationDate",
|
||||
"SineMaxFreq",
|
||||
"SquareMaxFreq",
|
||||
"RampMaxFreq",
|
||||
"PulseMaxFreq",
|
||||
"ArbMaxFreq",
|
||||
"HarmonicMaxFreq",
|
||||
"MinFreq",
|
||||
"HarmonicMinFreq",
|
||||
"ARMSerial",
|
||||
"MaxChannels",
|
||||
"ArbWaveLenLicense",
|
||||
"ArbWaveLenValidTime",
|
||||
"DuoChanChannelValidTime",
|
||||
]
|
||||
FREQ_FIELDS = [
|
||||
"SineMaxFreq",
|
||||
"SquareMaxFreq",
|
||||
"RampMaxFreq",
|
||||
"PulseMaxFreq",
|
||||
"ArbMaxFreq",
|
||||
"HarmonicMaxFreq",
|
||||
"MinFreq",
|
||||
"HarmonicMinFreq",
|
||||
]
|
||||
UHZ_TO_MHZ_FACTOR = 1e-12 # 1 / 1_000_000_000_000
|
||||
ARB_LICENSE_MAP = {
|
||||
"DEF": "Default",
|
||||
"MEM": "Memory Depth License",
|
||||
"CHD": "Dual Channel License",
|
||||
}
|
||||
TIME_STATUS_MAP = {"Forever": "Active (No Expiration)", "Inactive": "Not Active"}
|
||||
TIME_FIELDS = ["ArbWaveLenValidTime", "DuoChanChannelValidTime"]
|
||||
|
||||
if not csv_string.strip():
|
||||
return None
|
||||
|
||||
print("\nCSV header:")
|
||||
print(",".join(FIELD_NAMES))
|
||||
print(csv_string)
|
||||
|
||||
# parse CSV row
|
||||
reader = csv.reader(io.StringIO(csv_string))
|
||||
row = next(reader, None)
|
||||
if not row:
|
||||
return None
|
||||
|
||||
data = dict(zip(FIELD_NAMES, row))
|
||||
|
||||
# convert frequency fields
|
||||
for field in FREQ_FIELDS:
|
||||
if field in data and data[field].isdigit():
|
||||
uhz_value = int(data[field])
|
||||
data[f"{field}_MHz"] = uhz_value * UHZ_TO_MHZ_FACTOR # pyright: ignore [reportArgumentType]
|
||||
|
||||
# map license code
|
||||
if "ArbWaveLenLicense" in data and data["ArbWaveLenLicense"] in ARB_LICENSE_MAP:
|
||||
data["ArbWaveLenLicense_Description"] = ARB_LICENSE_MAP[
|
||||
data["ArbWaveLenLicense"]
|
||||
]
|
||||
|
||||
# map time fields
|
||||
for field in TIME_FIELDS:
|
||||
if field in data and data[field] in TIME_STATUS_MAP:
|
||||
data[f"{field}_Status"] = TIME_STATUS_MAP[data[field]]
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def run_parse_mode(args, input_data):
|
||||
parsed_result = parse_rigol_csv(input_data.strip())
|
||||
if not parsed_result:
|
||||
print("Error: Failed to parse CSV data.")
|
||||
sys.exit(1)
|
||||
|
||||
if args.output_file:
|
||||
json_output = json.dumps(parsed_result, indent=2)
|
||||
with open(args.output_file, "w") as f:
|
||||
f.write(json_output)
|
||||
print(f"Output written to: {args.output_file}")
|
||||
return
|
||||
|
||||
sections = [
|
||||
(
|
||||
"Instrument Information",
|
||||
[
|
||||
("Manufacturer", "Manufacturer"),
|
||||
("Model", "InstrModel"),
|
||||
("Serial Number", "InstrSN"),
|
||||
("Calibration Date", "CalibrationDate"),
|
||||
("ARM CPU Serial", "ARMSerial"),
|
||||
("Max Channels", "MaxChannels"),
|
||||
],
|
||||
),
|
||||
(
|
||||
"Frequency Specifications",
|
||||
[
|
||||
("Sine Max Frequency", "SineMaxFreq"),
|
||||
("Square Max Frequency", "SquareMaxFreq"),
|
||||
("Ramp Max Frequency", "RampMaxFreq"),
|
||||
("Pulse Max Frequency", "PulseMaxFreq"),
|
||||
("Arb Max Frequency", "ArbMaxFreq"),
|
||||
("Harmonic Max Frequency", "HarmonicMaxFreq"),
|
||||
("Min Frequency", "MinFreq"),
|
||||
("Harmonic Min Frequency", "HarmonicMinFreq"),
|
||||
],
|
||||
),
|
||||
(
|
||||
"Licensing Information",
|
||||
[
|
||||
("Arb Wavelength License", "ArbWaveLenLicense"),
|
||||
("Arb Wavelength Valid Time", "ArbWaveLenValidTime"),
|
||||
("Duo Channel Valid Time", "DuoChanChannelValidTime"),
|
||||
],
|
||||
),
|
||||
]
|
||||
|
||||
for section_title, fields in sections:
|
||||
print(f"\n{section_title}:")
|
||||
section_has_data = False
|
||||
|
||||
for label, key in fields:
|
||||
if key not in parsed_result:
|
||||
continue
|
||||
|
||||
section_has_data = True
|
||||
value = parsed_result[key]
|
||||
output_line = f" {label}: {value}"
|
||||
|
||||
# MHz conv if available
|
||||
mhz_key = f"{key}_MHz"
|
||||
if mhz_key in parsed_result:
|
||||
mhz_value = parsed_result[mhz_key]
|
||||
if isinstance(mhz_value, (int, float)):
|
||||
mhz_str = (
|
||||
f"{mhz_value:.6f}"
|
||||
if abs(mhz_value) > 1e-3 or mhz_value == 0
|
||||
else f"{mhz_value:.6e}"
|
||||
)
|
||||
output_line += f" ({mhz_str} MHz)"
|
||||
|
||||
desc_key = f"{key}_Description"
|
||||
status_key = f"{key}_Status"
|
||||
if desc_key in parsed_result:
|
||||
output_line += f" ({parsed_result[desc_key]})"
|
||||
elif status_key in parsed_result:
|
||||
output_line += f" ({parsed_result[status_key]})"
|
||||
|
||||
print(output_line)
|
||||
|
||||
if not section_has_data:
|
||||
print(" (No data)")
|
||||
|
||||
|
||||
def write_output(data, output_file=None, is_binary=False):
|
||||
# w to file
|
||||
if output_file:
|
||||
mode = "wb" if is_binary else "w"
|
||||
encoding = None if is_binary else "utf-8"
|
||||
with open(output_file, mode, encoding=encoding) as f:
|
||||
f.write(data)
|
||||
print(f"Output successfully written to: {output_file}")
|
||||
return
|
||||
|
||||
# w to stdout
|
||||
if is_binary:
|
||||
print(data.decode("utf-8"))
|
||||
else:
|
||||
print(data)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Encrypt or Decrypt Rigol DG[89]00 Pro setup.stp using the CPU SN.",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="""Examples:
|
||||
Encrypt text to console:
|
||||
python %(prog)s -e -t "RIGOL TECHNOLOGIES,DG821,..." "d95ebe35672e20c2fc08"
|
||||
|
||||
Decrypt text to console:
|
||||
python %(prog)s -d -t "FBC6BDDB0E..." "d95ebe35672e20c2fc08"
|
||||
|
||||
Parse CSV (decrypted setup.stp) to console:
|
||||
python %(prog)s -p -t "RIGOL TECHNOLOGIES,DG821,..."
|
||||
|
||||
Encrypt from file to output file:
|
||||
python %(prog)s -e -f input.txt "d95ebe35672e20c2fc08" -o setup.stp
|
||||
|
||||
Decrypt from file to output file:
|
||||
python %(prog)s -d -f encrypted.hex "d95ebe35672e20c2fc08" -o setup.stp.hex
|
||||
""",
|
||||
)
|
||||
|
||||
mode_group = parser.add_mutually_exclusive_group(required=True)
|
||||
mode_group.add_argument(
|
||||
"-e",
|
||||
"--encrypt",
|
||||
action="store_const",
|
||||
dest="mode",
|
||||
const="e",
|
||||
help="Encrypt plain text data",
|
||||
)
|
||||
mode_group.add_argument(
|
||||
"-d",
|
||||
"--decrypt",
|
||||
action="store_const",
|
||||
dest="mode",
|
||||
const="d",
|
||||
help="Decrypt hex string data",
|
||||
)
|
||||
mode_group.add_argument(
|
||||
"-p",
|
||||
"--parse",
|
||||
action="store_const",
|
||||
dest="mode",
|
||||
const="p",
|
||||
help="Parse Rigol CSV data string",
|
||||
)
|
||||
|
||||
input_group = parser.add_mutually_exclusive_group(required=True)
|
||||
input_group.add_argument(
|
||||
"-t",
|
||||
"--text",
|
||||
dest="input_text",
|
||||
help="Input data as text (Plain for Encrypt/Parse, Hex for Decrypt)",
|
||||
)
|
||||
input_group.add_argument(
|
||||
"-f", "--file", dest="input_file", help="Path to read input data from file"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-k",
|
||||
"--key",
|
||||
dest="cpu_serial",
|
||||
help="CPU Serial Number (Required for -e/-d modes)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-o",
|
||||
"--output",
|
||||
metavar="FILEPATH",
|
||||
dest="output_file",
|
||||
help="Optional path to save output. Console if omitted",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.mode in ["e", "d"] and not args.cpu_serial:
|
||||
parser.error(
|
||||
"-k/--key (CPU serial) is required for encryption and decryption modes"
|
||||
)
|
||||
|
||||
# read input data from arg or file
|
||||
if args.input_file:
|
||||
with open(args.input_file, "r", encoding="utf-8") as f:
|
||||
input_data = f.read()
|
||||
else:
|
||||
input_data = args.input_text
|
||||
|
||||
if args.mode == "e":
|
||||
# encrypt
|
||||
print("Mode: Encrypt")
|
||||
|
||||
if args.input_file:
|
||||
print(f"Reading input from file: {args.input_file}")
|
||||
|
||||
print(f"Input Data: '{input_data[:50]}{'...' if len(input_data) > 50 else ''}'")
|
||||
print(f"CPU Serial: {args.cpu_serial}")
|
||||
print("-" * 20)
|
||||
|
||||
encrypted_result_hex = DataEncryption(input_data, args.cpu_serial)
|
||||
|
||||
print("Encryption Complete.")
|
||||
if not args.output_file:
|
||||
print("\nEncrypted Hex Output:")
|
||||
# always a hex str
|
||||
write_output(encrypted_result_hex, args.output_file, is_binary=False)
|
||||
|
||||
elif args.mode == "d":
|
||||
# decrypt
|
||||
print("Mode: Decrypt")
|
||||
|
||||
if args.input_file:
|
||||
print(f"Reading input from file: {args.input_file}")
|
||||
|
||||
print(
|
||||
f"Input Hex Data: '{input_data[:50]}{'...' if len(input_data) > 50 else ''}'"
|
||||
)
|
||||
print(f"CPU Serial: {args.cpu_serial}")
|
||||
print("-" * 20)
|
||||
|
||||
decrypted_result = DataDecryption(input_data, args.cpu_serial)
|
||||
|
||||
print("Decryption Complete.")
|
||||
if not args.output_file:
|
||||
print("\nDecrypted Data Output:")
|
||||
|
||||
is_binary_output = isinstance(decrypted_result, bytes)
|
||||
write_output(decrypted_result, args.output_file, is_binary=is_binary_output)
|
||||
elif args.mode == "p":
|
||||
run_parse_mode(args, input_data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user