securedypkt 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- securedypkt-0.1.0/LICENSE +19 -0
- securedypkt-0.1.0/PKG-INFO +160 -0
- securedypkt-0.1.0/README.md +138 -0
- securedypkt-0.1.0/pyproject.toml +37 -0
- securedypkt-0.1.0/securedypkt/__init__.py +26 -0
- securedypkt-0.1.0/securedypkt/_repacket.py +110 -0
- securedypkt-0.1.0/securedypkt/_unpacket.py +81 -0
- securedypkt-0.1.0/securedypkt/decipher/__init__.py +0 -0
- securedypkt-0.1.0/securedypkt/decipher/cmac.py +58 -0
- securedypkt-0.1.0/securedypkt/decipher/ctr.py +33 -0
- securedypkt-0.1.0/securedypkt/decipher/eax.py +53 -0
- securedypkt-0.1.0/securedypkt/decipher/pt_crypto.py +46 -0
- securedypkt-0.1.0/securedypkt/decipher/twofish.py +393 -0
- securedypkt-0.1.0/securedypkt.egg-info/PKG-INFO +160 -0
- securedypkt-0.1.0/securedypkt.egg-info/SOURCES.txt +18 -0
- securedypkt-0.1.0/securedypkt.egg-info/dependency_links.txt +1 -0
- securedypkt-0.1.0/securedypkt.egg-info/entry_points.txt +3 -0
- securedypkt-0.1.0/securedypkt.egg-info/top_level.txt +1 -0
- securedypkt-0.1.0/setup.cfg +4 -0
- securedypkt-0.1.0/setup.py +2 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Securedy Labs Proprietary License
|
|
2
|
+
Copyright (c) 2026 Securedy Labs. All rights reserved.
|
|
3
|
+
|
|
4
|
+
This software and its source code are the property of Securedy Labs.
|
|
5
|
+
Unauthorized copying, distribution, modification, or use of this software,
|
|
6
|
+
in whole or in part, is strictly prohibited without prior written permission
|
|
7
|
+
from Securedy Labs.
|
|
8
|
+
|
|
9
|
+
--- Third-Party Attributions ---
|
|
10
|
+
|
|
11
|
+
Portions of this software (Decipher/twofish.py) are derived from an
|
|
12
|
+
implementation by Dr. Brian Gladman (gladman@seven77.demon.co.uk), used
|
|
13
|
+
with permission for free direct or derivative use, subject to acknowledgment
|
|
14
|
+
of its origin. The original Twofish algorithm was authored by Bruce Schneier
|
|
15
|
+
and colleagues.
|
|
16
|
+
|
|
17
|
+
The original open-source codebase this project was derived from was released
|
|
18
|
+
under the MIT License by Punkcake21 (2026). Securedy Labs' modifications,
|
|
19
|
+
extensions, and proprietary additions are subject to the terms above.
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: securedypkt
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Pure-Python toolkit for decrypting and re-encrypting Cisco Packet Tracer (.pkt) files
|
|
5
|
+
Author-email: Securedy Labs <nathanrampersaud@gmail.com>
|
|
6
|
+
Project-URL: Homepage, https://github.com/Nathan8044/SecuredyPKT
|
|
7
|
+
Keywords: cisco,packet-tracer,pkt,twofish,eax,crypto,network
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Topic :: Security :: Cryptography
|
|
17
|
+
Classifier: Topic :: Utilities
|
|
18
|
+
Requires-Python: >=3.8
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Dynamic: license-file
|
|
22
|
+
|
|
23
|
+
# SecuredyPKT
|
|
24
|
+
|
|
25
|
+
**SecuredyPKT** is a pure-Python research tool by **Securedy Labs** for decrypting and re-encrypting Cisco Packet Tracer (`.pkt`) files.
|
|
26
|
+
|
|
27
|
+
It fully reimplements the proprietary cryptographic and obfuscation pipeline used internally by Packet Tracer, enabling offline inspection, diffing, and version-controlling of lab topologies — without requiring the official application.
|
|
28
|
+
|
|
29
|
+
This is an active research and development project. A custom crypto model and extended feature set are planned.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install securedypkt
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
No additional dependencies required. Pure Python 3.8+.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Python API (for agents and integrations)
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
import securedypkt
|
|
47
|
+
|
|
48
|
+
# Decrypt a .pkt file → XML bytes
|
|
49
|
+
xml_bytes = securedypkt.decrypt_file("lab.pkt")
|
|
50
|
+
|
|
51
|
+
# Or work directly with bytes (e.g. from an upload handler)
|
|
52
|
+
with open("lab.pkt", "rb") as f:
|
|
53
|
+
xml_bytes = securedypkt.decrypt_pkt(f.read())
|
|
54
|
+
|
|
55
|
+
# Re-encrypt XML bytes → .pkt bytes
|
|
56
|
+
pkt_bytes = securedypkt.encrypt_pkt(xml_bytes)
|
|
57
|
+
pkt_bytes = securedypkt.encrypt_file("lab.xml")
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## CLI Usage
|
|
63
|
+
|
|
64
|
+
### Decrypt `.pkt` → XML
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
unpacket lab.pkt
|
|
68
|
+
# Output: lab.xml
|
|
69
|
+
|
|
70
|
+
unpacket lab.pkt -o decrypted.xml
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Re-encrypt XML → `.pkt`
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
repacket lab.xml
|
|
77
|
+
# Output: lab.pkt
|
|
78
|
+
|
|
79
|
+
repacket lab.xml -o rebuilt.pkt
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Options
|
|
83
|
+
|
|
84
|
+
| Tool | Option | Description |
|
|
85
|
+
|------|--------|-------------|
|
|
86
|
+
| `unpacket` | `input_file` | Path to `.pkt` file |
|
|
87
|
+
| | `-o, --output` | Output XML path |
|
|
88
|
+
| `repacket` | `input_file` | Path to `.xml` file |
|
|
89
|
+
| | `-o, --output` | Output `.pkt` path |
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## How It Works
|
|
94
|
+
|
|
95
|
+
Cisco Packet Tracer `.pkt` files are protected by a 4-layer scheme. SecuredyPKT reverses all four layers:
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
[ .pkt file on disk ]
|
|
99
|
+
|
|
|
100
|
+
v
|
|
101
|
+
[1] Stage-1 Deobfuscation
|
|
102
|
+
Byte-order reversal + positional XOR
|
|
103
|
+
result[i] = data[L-1-i] ^ (L - i*L & 0xFF)
|
|
104
|
+
|
|
|
105
|
+
v
|
|
106
|
+
[2] Twofish/EAX Decryption (authenticated)
|
|
107
|
+
128-bit Twofish block cipher in EAX mode
|
|
108
|
+
Key: 0x89 * 16 (hardcoded in Packet Tracer)
|
|
109
|
+
IV: 0x10 * 16 (hardcoded in Packet Tracer)
|
|
110
|
+
Last 16 bytes of ciphertext = AEAD authentication tag
|
|
111
|
+
|
|
|
112
|
+
v
|
|
113
|
+
[3] Stage-2 Deobfuscation
|
|
114
|
+
Positional XOR with decreasing counter
|
|
115
|
+
result[i] = b ^ (L - i & 0xFF)
|
|
116
|
+
|
|
|
117
|
+
v
|
|
118
|
+
[4] Qt Decompression
|
|
119
|
+
zlib stream with 4-byte big-endian size prefix (qCompress format)
|
|
120
|
+
|
|
|
121
|
+
v
|
|
122
|
+
[ Plain XML topology ]
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
`repacket` runs this pipeline in reverse: XML → compress → obfuscate → encrypt → obfuscate → `.pkt`.
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Crypto Stack
|
|
130
|
+
|
|
131
|
+
| Module | Purpose |
|
|
132
|
+
|--------|---------|
|
|
133
|
+
| `securedypkt/decipher/twofish.py` | Pure-Python Twofish block cipher (128-bit) |
|
|
134
|
+
| `securedypkt/decipher/cmac.py` | CMAC / OMAC message authentication code |
|
|
135
|
+
| `securedypkt/decipher/ctr.py` | CTR mode keystream engine (big-endian counter) |
|
|
136
|
+
| `securedypkt/decipher/eax.py` | EAX authenticated encryption (AEAD) |
|
|
137
|
+
| `securedypkt/decipher/pt_crypto.py` | Full Packet Tracer decryption pipeline |
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## Security Notes
|
|
142
|
+
|
|
143
|
+
- The hardcoded key and IV are **Cisco's**, reverse-engineered from the Packet Tracer binary
|
|
144
|
+
- EAX tag verification is enforced — corrupted or tampered `.pkt` files are rejected
|
|
145
|
+
- No network calls; fully offline
|
|
146
|
+
- Known research items for future versions:
|
|
147
|
+
- Input file size guard
|
|
148
|
+
- zlib decompression size cap
|
|
149
|
+
- XML parsing hardening
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## Legal
|
|
154
|
+
|
|
155
|
+
This software is provided for **security research, educational, and interoperability purposes**.
|
|
156
|
+
|
|
157
|
+
Cisco Packet Tracer and all related trademarks are property of Cisco Systems, Inc.
|
|
158
|
+
SecuredyPKT is not affiliated with or endorsed by Cisco.
|
|
159
|
+
|
|
160
|
+
See [LICENSE](LICENSE) for full terms.
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# SecuredyPKT
|
|
2
|
+
|
|
3
|
+
**SecuredyPKT** is a pure-Python research tool by **Securedy Labs** for decrypting and re-encrypting Cisco Packet Tracer (`.pkt`) files.
|
|
4
|
+
|
|
5
|
+
It fully reimplements the proprietary cryptographic and obfuscation pipeline used internally by Packet Tracer, enabling offline inspection, diffing, and version-controlling of lab topologies — without requiring the official application.
|
|
6
|
+
|
|
7
|
+
This is an active research and development project. A custom crypto model and extended feature set are planned.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install securedypkt
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
No additional dependencies required. Pure Python 3.8+.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Python API (for agents and integrations)
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
import securedypkt
|
|
25
|
+
|
|
26
|
+
# Decrypt a .pkt file → XML bytes
|
|
27
|
+
xml_bytes = securedypkt.decrypt_file("lab.pkt")
|
|
28
|
+
|
|
29
|
+
# Or work directly with bytes (e.g. from an upload handler)
|
|
30
|
+
with open("lab.pkt", "rb") as f:
|
|
31
|
+
xml_bytes = securedypkt.decrypt_pkt(f.read())
|
|
32
|
+
|
|
33
|
+
# Re-encrypt XML bytes → .pkt bytes
|
|
34
|
+
pkt_bytes = securedypkt.encrypt_pkt(xml_bytes)
|
|
35
|
+
pkt_bytes = securedypkt.encrypt_file("lab.xml")
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## CLI Usage
|
|
41
|
+
|
|
42
|
+
### Decrypt `.pkt` → XML
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
unpacket lab.pkt
|
|
46
|
+
# Output: lab.xml
|
|
47
|
+
|
|
48
|
+
unpacket lab.pkt -o decrypted.xml
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Re-encrypt XML → `.pkt`
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
repacket lab.xml
|
|
55
|
+
# Output: lab.pkt
|
|
56
|
+
|
|
57
|
+
repacket lab.xml -o rebuilt.pkt
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Options
|
|
61
|
+
|
|
62
|
+
| Tool | Option | Description |
|
|
63
|
+
|------|--------|-------------|
|
|
64
|
+
| `unpacket` | `input_file` | Path to `.pkt` file |
|
|
65
|
+
| | `-o, --output` | Output XML path |
|
|
66
|
+
| `repacket` | `input_file` | Path to `.xml` file |
|
|
67
|
+
| | `-o, --output` | Output `.pkt` path |
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## How It Works
|
|
72
|
+
|
|
73
|
+
Cisco Packet Tracer `.pkt` files are protected by a 4-layer scheme. SecuredyPKT reverses all four layers:
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
[ .pkt file on disk ]
|
|
77
|
+
|
|
|
78
|
+
v
|
|
79
|
+
[1] Stage-1 Deobfuscation
|
|
80
|
+
Byte-order reversal + positional XOR
|
|
81
|
+
result[i] = data[L-1-i] ^ (L - i*L & 0xFF)
|
|
82
|
+
|
|
|
83
|
+
v
|
|
84
|
+
[2] Twofish/EAX Decryption (authenticated)
|
|
85
|
+
128-bit Twofish block cipher in EAX mode
|
|
86
|
+
Key: 0x89 * 16 (hardcoded in Packet Tracer)
|
|
87
|
+
IV: 0x10 * 16 (hardcoded in Packet Tracer)
|
|
88
|
+
Last 16 bytes of ciphertext = AEAD authentication tag
|
|
89
|
+
|
|
|
90
|
+
v
|
|
91
|
+
[3] Stage-2 Deobfuscation
|
|
92
|
+
Positional XOR with decreasing counter
|
|
93
|
+
result[i] = b ^ (L - i & 0xFF)
|
|
94
|
+
|
|
|
95
|
+
v
|
|
96
|
+
[4] Qt Decompression
|
|
97
|
+
zlib stream with 4-byte big-endian size prefix (qCompress format)
|
|
98
|
+
|
|
|
99
|
+
v
|
|
100
|
+
[ Plain XML topology ]
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
`repacket` runs this pipeline in reverse: XML → compress → obfuscate → encrypt → obfuscate → `.pkt`.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Crypto Stack
|
|
108
|
+
|
|
109
|
+
| Module | Purpose |
|
|
110
|
+
|--------|---------|
|
|
111
|
+
| `securedypkt/decipher/twofish.py` | Pure-Python Twofish block cipher (128-bit) |
|
|
112
|
+
| `securedypkt/decipher/cmac.py` | CMAC / OMAC message authentication code |
|
|
113
|
+
| `securedypkt/decipher/ctr.py` | CTR mode keystream engine (big-endian counter) |
|
|
114
|
+
| `securedypkt/decipher/eax.py` | EAX authenticated encryption (AEAD) |
|
|
115
|
+
| `securedypkt/decipher/pt_crypto.py` | Full Packet Tracer decryption pipeline |
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Security Notes
|
|
120
|
+
|
|
121
|
+
- The hardcoded key and IV are **Cisco's**, reverse-engineered from the Packet Tracer binary
|
|
122
|
+
- EAX tag verification is enforced — corrupted or tampered `.pkt` files are rejected
|
|
123
|
+
- No network calls; fully offline
|
|
124
|
+
- Known research items for future versions:
|
|
125
|
+
- Input file size guard
|
|
126
|
+
- zlib decompression size cap
|
|
127
|
+
- XML parsing hardening
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Legal
|
|
132
|
+
|
|
133
|
+
This software is provided for **security research, educational, and interoperability purposes**.
|
|
134
|
+
|
|
135
|
+
Cisco Packet Tracer and all related trademarks are property of Cisco Systems, Inc.
|
|
136
|
+
SecuredyPKT is not affiliated with or endorsed by Cisco.
|
|
137
|
+
|
|
138
|
+
See [LICENSE](LICENSE) for full terms.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=42", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "securedypkt"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Pure-Python toolkit for decrypting and re-encrypting Cisco Packet Tracer (.pkt) files"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license-files = ["LICENSE"]
|
|
11
|
+
authors = [{ name = "Securedy Labs", email = "nathanrampersaud@gmail.com" }]
|
|
12
|
+
requires-python = ">=3.8"
|
|
13
|
+
dependencies = []
|
|
14
|
+
keywords = ["cisco", "packet-tracer", "pkt", "twofish", "eax", "crypto", "network"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"Programming Language :: Python :: 3.8",
|
|
18
|
+
"Programming Language :: Python :: 3.9",
|
|
19
|
+
"Programming Language :: Python :: 3.10",
|
|
20
|
+
"Programming Language :: Python :: 3.11",
|
|
21
|
+
"Programming Language :: Python :: 3.12",
|
|
22
|
+
"Programming Language :: Python :: 3.13",
|
|
23
|
+
"Operating System :: OS Independent",
|
|
24
|
+
"Topic :: Security :: Cryptography",
|
|
25
|
+
"Topic :: Utilities",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[project.scripts]
|
|
29
|
+
unpacket = "securedypkt._unpacket:main"
|
|
30
|
+
repacket = "securedypkt._repacket:main"
|
|
31
|
+
|
|
32
|
+
[project.urls]
|
|
33
|
+
"Homepage" = "https://github.com/Nathan8044/SecuredyPKT"
|
|
34
|
+
|
|
35
|
+
[tool.setuptools.packages.find]
|
|
36
|
+
where = ["."]
|
|
37
|
+
include = ["securedypkt*"]
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""
|
|
2
|
+
securedypkt — Cisco Packet Tracer .pkt crypto toolkit.
|
|
3
|
+
Securedy Labs, 2026.
|
|
4
|
+
|
|
5
|
+
Quick start:
|
|
6
|
+
from securedypkt import decrypt_pkt
|
|
7
|
+
xml_bytes = decrypt_pkt(open("lab.pkt", "rb").read())
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from securedypkt.decipher.pt_crypto import decrypt_pkt
|
|
11
|
+
from securedypkt._repacket import encrypt_pkt
|
|
12
|
+
|
|
13
|
+
__version__ = "0.1.0"
|
|
14
|
+
__all__ = ["decrypt_pkt", "encrypt_pkt", "decrypt_file", "encrypt_file"]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def decrypt_file(path: str) -> bytes:
|
|
18
|
+
"""Read a .pkt file and return the decrypted XML as bytes."""
|
|
19
|
+
with open(path, "rb") as fh:
|
|
20
|
+
return decrypt_pkt(fh.read())
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def encrypt_file(path: str) -> bytes:
|
|
24
|
+
"""Read an XML file and return the encrypted .pkt bytes."""
|
|
25
|
+
with open(path, "rb") as fh:
|
|
26
|
+
return encrypt_pkt(fh.read())
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import argparse
|
|
3
|
+
import sys
|
|
4
|
+
import os
|
|
5
|
+
import zlib
|
|
6
|
+
import struct
|
|
7
|
+
from securedypkt.decipher.eax import EAX
|
|
8
|
+
from securedypkt.decipher.twofish import Twofish
|
|
9
|
+
|
|
10
|
+
def banner():
|
|
11
|
+
print("==============================================")
|
|
12
|
+
print(" SecuredyPKT - Cisco Packet Tracer Encryptor ")
|
|
13
|
+
print(" Securedy Labs ")
|
|
14
|
+
print("==============================================")
|
|
15
|
+
|
|
16
|
+
def compress_qt(xml_data: bytes) -> bytes:
|
|
17
|
+
size = len(xml_data)
|
|
18
|
+
header = struct.pack(">I", size)
|
|
19
|
+
compressed = zlib.compress(xml_data)
|
|
20
|
+
return header + compressed
|
|
21
|
+
|
|
22
|
+
def obf_stage2(data: bytes) -> bytes:
|
|
23
|
+
L = len(data)
|
|
24
|
+
return bytes(b ^ (L - i & 0xFF) for i, b in enumerate(data))
|
|
25
|
+
|
|
26
|
+
def obf_stage1(data: bytes) -> bytes:
|
|
27
|
+
L = len(data)
|
|
28
|
+
output = bytearray(L)
|
|
29
|
+
for i in range(L):
|
|
30
|
+
key_byte = (L - i*L) & 0xFF
|
|
31
|
+
val = data[i] ^ key_byte
|
|
32
|
+
output[L-1-i] = val
|
|
33
|
+
return bytes(output)
|
|
34
|
+
|
|
35
|
+
def encrypt_pkt(data: bytes) -> bytes:
|
|
36
|
+
key = bytes([137])*16
|
|
37
|
+
iv = bytes([16])*16
|
|
38
|
+
tf = Twofish(key)
|
|
39
|
+
eax = EAX(tf.encrypt)
|
|
40
|
+
ciphertext, tag = eax.encrypt(nonce=iv, plaintext=data)
|
|
41
|
+
return ciphertext + tag
|
|
42
|
+
|
|
43
|
+
def main():
|
|
44
|
+
banner()
|
|
45
|
+
|
|
46
|
+
parser = argparse.ArgumentParser(
|
|
47
|
+
description="Encrypts XML files back to Cisco Packet Tracer (.pkt) format."
|
|
48
|
+
)
|
|
49
|
+
parser.add_argument(
|
|
50
|
+
"input_file",
|
|
51
|
+
help="Path to the .xml file to encrypt."
|
|
52
|
+
)
|
|
53
|
+
parser.add_argument(
|
|
54
|
+
"-o", "--output",
|
|
55
|
+
help="Path to the output .pkt file. Defaults to <input_file>.pkt",
|
|
56
|
+
default=None
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
args = parser.parse_args()
|
|
60
|
+
input_path = args.input_file
|
|
61
|
+
|
|
62
|
+
if not os.path.exists(input_path):
|
|
63
|
+
print(f"[-] Error: Input file '{input_path}' not found.")
|
|
64
|
+
sys.exit(1)
|
|
65
|
+
|
|
66
|
+
if args.output:
|
|
67
|
+
output_path = args.output
|
|
68
|
+
else:
|
|
69
|
+
if input_path.lower().endswith(".xml"):
|
|
70
|
+
output_path = input_path[:-4] + ".pkt"
|
|
71
|
+
else:
|
|
72
|
+
output_path = input_path + ".pkt"
|
|
73
|
+
|
|
74
|
+
print(f"[*] Reading XML '{input_path}'...")
|
|
75
|
+
try:
|
|
76
|
+
with open(input_path, "rb") as f:
|
|
77
|
+
xml_data = f.read()
|
|
78
|
+
except IOError as e:
|
|
79
|
+
print(f"[-] Error reading file: {e}")
|
|
80
|
+
sys.exit(1)
|
|
81
|
+
|
|
82
|
+
try:
|
|
83
|
+
print("[*] Compressing data (zlib)...")
|
|
84
|
+
stage2_input = compress_qt(xml_data)
|
|
85
|
+
|
|
86
|
+
print("[*] Applying Stage 2 Obfuscation...")
|
|
87
|
+
decrypted_blob = obf_stage2(stage2_input)
|
|
88
|
+
|
|
89
|
+
print("[*] Encrypting (Twofish/EAX)...")
|
|
90
|
+
stage1_input = encrypt_pkt(decrypted_blob)
|
|
91
|
+
|
|
92
|
+
print("[*] Applying Stage 1 Obfuscation...")
|
|
93
|
+
final_pkt_data = obf_stage1(stage1_input)
|
|
94
|
+
|
|
95
|
+
except Exception as e:
|
|
96
|
+
print(f"[-] Encryption/Obfuscation failed: {e}")
|
|
97
|
+
sys.exit(1)
|
|
98
|
+
|
|
99
|
+
print(f"[*] Writing encrypted data to '{output_path}'...")
|
|
100
|
+
try:
|
|
101
|
+
with open(output_path, "wb") as f:
|
|
102
|
+
f.write(final_pkt_data)
|
|
103
|
+
except IOError as e:
|
|
104
|
+
print(f"[-] Error writing output file: {e}")
|
|
105
|
+
sys.exit(1)
|
|
106
|
+
|
|
107
|
+
print("[+] Success! File repacketed.")
|
|
108
|
+
|
|
109
|
+
if __name__ == "__main__":
|
|
110
|
+
main()
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import argparse
|
|
3
|
+
import sys
|
|
4
|
+
import os
|
|
5
|
+
import xml.etree.ElementTree as ET
|
|
6
|
+
from securedypkt.decipher.pt_crypto import decrypt_pkt
|
|
7
|
+
|
|
8
|
+
def banner():
|
|
9
|
+
print("==============================================")
|
|
10
|
+
print(" SecuredyPKT - Cisco Packet Tracer Decryptor ")
|
|
11
|
+
print(" Securedy Labs ")
|
|
12
|
+
print("==============================================")
|
|
13
|
+
|
|
14
|
+
def main():
|
|
15
|
+
banner()
|
|
16
|
+
|
|
17
|
+
parser = argparse.ArgumentParser(
|
|
18
|
+
description="Decrypts Cisco Packet Tracer (.pkt) files to XML format."
|
|
19
|
+
)
|
|
20
|
+
parser.add_argument(
|
|
21
|
+
"input_file",
|
|
22
|
+
help="Path to the .pkt file to decrypt."
|
|
23
|
+
)
|
|
24
|
+
parser.add_argument(
|
|
25
|
+
"-o", "--output",
|
|
26
|
+
help="Path to the output XML file. Defaults to <input_file>.xml",
|
|
27
|
+
default=None
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
args = parser.parse_args()
|
|
31
|
+
|
|
32
|
+
input_path = args.input_file
|
|
33
|
+
|
|
34
|
+
if not os.path.exists(input_path):
|
|
35
|
+
print(f"[-] Error: Input file '{input_path}' not found.")
|
|
36
|
+
sys.exit(1)
|
|
37
|
+
|
|
38
|
+
if args.output:
|
|
39
|
+
output_path = args.output
|
|
40
|
+
else:
|
|
41
|
+
if input_path.lower().endswith(".pkt"):
|
|
42
|
+
output_path = input_path[:-4] + ".xml"
|
|
43
|
+
else:
|
|
44
|
+
output_path = input_path + ".xml"
|
|
45
|
+
|
|
46
|
+
print(f"[*] Reading '{input_path}'...")
|
|
47
|
+
try:
|
|
48
|
+
with open(input_path, "rb") as f:
|
|
49
|
+
data = f.read()
|
|
50
|
+
except IOError as e:
|
|
51
|
+
print(f"[-] Error reading file: {e}")
|
|
52
|
+
sys.exit(1)
|
|
53
|
+
|
|
54
|
+
print("[*] Decrypting...")
|
|
55
|
+
try:
|
|
56
|
+
xml_data = decrypt_pkt(data)
|
|
57
|
+
except Exception as e:
|
|
58
|
+
print(f"[-] Decryption failed: {e}")
|
|
59
|
+
sys.exit(1)
|
|
60
|
+
|
|
61
|
+
print("[*] Parsing XML...")
|
|
62
|
+
try:
|
|
63
|
+
root = ET.fromstring(xml_data)
|
|
64
|
+
tree = ET.ElementTree(root)
|
|
65
|
+
if hasattr(ET, "indent"):
|
|
66
|
+
ET.indent(tree, space=" ")
|
|
67
|
+
except ET.ParseError as e:
|
|
68
|
+
print(f"[-] XML Parsing failed: {e}")
|
|
69
|
+
sys.exit(1)
|
|
70
|
+
|
|
71
|
+
print(f"[*] Writing decrypted data to '{output_path}'...")
|
|
72
|
+
try:
|
|
73
|
+
tree.write(output_path, encoding="utf-8", xml_declaration=True)
|
|
74
|
+
except IOError as e:
|
|
75
|
+
print(f"[-] Error writing output file: {e}")
|
|
76
|
+
sys.exit(1)
|
|
77
|
+
|
|
78
|
+
print("[+] Success! Decryption complete.")
|
|
79
|
+
|
|
80
|
+
if __name__ == "__main__":
|
|
81
|
+
main()
|
|
File without changes
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from typing import Callable
|
|
2
|
+
|
|
3
|
+
BLOCK_SIZE = 16 # 128 bit for Twofish
|
|
4
|
+
|
|
5
|
+
def xor_bytes(a: bytes, b: bytes) -> bytes:
|
|
6
|
+
return bytes(x ^ y for x, y in zip(a, b))
|
|
7
|
+
|
|
8
|
+
def left_shift_one(bitstring: bytes) -> bytes:
|
|
9
|
+
out = bytearray(len(bitstring))
|
|
10
|
+
carry = 0
|
|
11
|
+
for i in reversed(range(len(bitstring))):
|
|
12
|
+
new = (bitstring[i] << 1) & 0xFF
|
|
13
|
+
out[i] = new | carry
|
|
14
|
+
carry = (bitstring[i] & 0x80) >> 7
|
|
15
|
+
return bytes(out)
|
|
16
|
+
|
|
17
|
+
def generate_subkeys(encrypt_block: Callable[[bytes], bytes]):
|
|
18
|
+
const_rb = 0x87
|
|
19
|
+
zero = bytes(BLOCK_SIZE)
|
|
20
|
+
L = encrypt_block(zero)
|
|
21
|
+
|
|
22
|
+
K1 = left_shift_one(L)
|
|
23
|
+
if L[0] & 0x80:
|
|
24
|
+
K1 = xor_bytes(K1, b'\x00' * 15 + bytes([const_rb]))
|
|
25
|
+
|
|
26
|
+
K2 = left_shift_one(K1)
|
|
27
|
+
if K1[0] & 0x80:
|
|
28
|
+
K2 = xor_bytes(K2, b'\x00' * 15 + bytes([const_rb]))
|
|
29
|
+
|
|
30
|
+
return K1, K2
|
|
31
|
+
|
|
32
|
+
def pad(block: bytes) -> bytes:
|
|
33
|
+
padded = block + b'\x80'
|
|
34
|
+
return padded + b'\x00' * (BLOCK_SIZE - len(padded))
|
|
35
|
+
|
|
36
|
+
class CMAC:
|
|
37
|
+
def __init__(self, encrypt_block: Callable[[bytes], bytes]):
|
|
38
|
+
self.encrypt_block = encrypt_block
|
|
39
|
+
self.K1, self.K2 = generate_subkeys(encrypt_block)
|
|
40
|
+
|
|
41
|
+
def digest(self, data: bytes) -> bytes:
|
|
42
|
+
if len(data) == 0:
|
|
43
|
+
last = xor_bytes(pad(b''), self.K2)
|
|
44
|
+
blocks = []
|
|
45
|
+
else:
|
|
46
|
+
blocks = [data[i:i+BLOCK_SIZE] for i in range(0, len(data), BLOCK_SIZE)]
|
|
47
|
+
if len(blocks[-1]) == BLOCK_SIZE:
|
|
48
|
+
last = xor_bytes(blocks[-1], self.K1)
|
|
49
|
+
blocks = blocks[:-1]
|
|
50
|
+
else:
|
|
51
|
+
last = xor_bytes(pad(blocks[-1]), self.K2)
|
|
52
|
+
blocks = blocks[:-1]
|
|
53
|
+
|
|
54
|
+
X = bytes(BLOCK_SIZE)
|
|
55
|
+
for block in blocks:
|
|
56
|
+
X = self.encrypt_block(xor_bytes(X, block))
|
|
57
|
+
|
|
58
|
+
return self.encrypt_block(xor_bytes(X, last))
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from typing import Callable
|
|
2
|
+
|
|
3
|
+
BLOCK_SIZE = 16 # 128 bit
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def inc_counter_be(counter: bytearray):
|
|
7
|
+
"""Incrementa un contatore big-endian a 128 bit (come Crypto++)."""
|
|
8
|
+
for i in range(BLOCK_SIZE - 1, -1, -1):
|
|
9
|
+
counter[i] = (counter[i] + 1) & 0xFF
|
|
10
|
+
if counter[i] != 0:
|
|
11
|
+
break
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class CTR:
|
|
15
|
+
def __init__(self, encrypt_block: Callable[[bytes], bytes], initial_counter: bytes):
|
|
16
|
+
assert len(initial_counter) == BLOCK_SIZE
|
|
17
|
+
self.encrypt_block = encrypt_block
|
|
18
|
+
self.counter = bytearray(initial_counter)
|
|
19
|
+
|
|
20
|
+
def process(self, data: bytes) -> bytes:
|
|
21
|
+
out = bytearray()
|
|
22
|
+
offset = 0
|
|
23
|
+
|
|
24
|
+
while offset < len(data):
|
|
25
|
+
keystream = self.encrypt_block(bytes(self.counter))
|
|
26
|
+
inc_counter_be(self.counter)
|
|
27
|
+
|
|
28
|
+
block = data[offset:offset + BLOCK_SIZE]
|
|
29
|
+
ks = keystream[:len(block)]
|
|
30
|
+
out.extend(b ^ k for b, k in zip(block, ks))
|
|
31
|
+
offset += BLOCK_SIZE
|
|
32
|
+
|
|
33
|
+
return bytes(out)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from typing import Callable
|
|
2
|
+
from .cmac import CMAC, xor_bytes, BLOCK_SIZE
|
|
3
|
+
from .ctr import CTR
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def _omac_with_prefix(cmac: CMAC, prefix: int, data: bytes) -> bytes:
|
|
7
|
+
# Prefisso di 16 byte: [0, 0, ..., prefix]
|
|
8
|
+
P = b'\x00' * (BLOCK_SIZE - 1) + bytes([prefix])
|
|
9
|
+
return cmac.digest(P + data)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class EAX:
|
|
13
|
+
def __init__(self, encrypt_block: Callable[[bytes], bytes]):
|
|
14
|
+
self.encrypt_block = encrypt_block
|
|
15
|
+
self.cmac = CMAC(encrypt_block)
|
|
16
|
+
|
|
17
|
+
def encrypt(self, nonce: bytes, plaintext: bytes, aad: bytes = b''):
|
|
18
|
+
# OMAC_0 = CMAC(0x00 || nonce)
|
|
19
|
+
n_tag = _omac_with_prefix(self.cmac, 0x00, nonce)
|
|
20
|
+
|
|
21
|
+
# OMAC_1 = CMAC(0x01 || aad)
|
|
22
|
+
h_tag = _omac_with_prefix(self.cmac, 0x01, aad)
|
|
23
|
+
|
|
24
|
+
# CTR parte da n_tag
|
|
25
|
+
ctr = CTR(self.encrypt_block, n_tag)
|
|
26
|
+
ciphertext = ctr.process(plaintext)
|
|
27
|
+
|
|
28
|
+
# OMAC_2 = CMAC(0x02 || ciphertext)
|
|
29
|
+
c_tag = _omac_with_prefix(self.cmac, 0x02, ciphertext)
|
|
30
|
+
|
|
31
|
+
# TAG finale = n_tag ⊕ h_tag ⊕ c_tag
|
|
32
|
+
tag = xor_bytes(xor_bytes(n_tag, h_tag), c_tag)
|
|
33
|
+
|
|
34
|
+
return ciphertext, tag
|
|
35
|
+
|
|
36
|
+
def decrypt(self, nonce: bytes, ciphertext: bytes, tag: bytes, aad: bytes = b''):
|
|
37
|
+
# Ricalcolo OMAC_0
|
|
38
|
+
n_tag = _omac_with_prefix(self.cmac, 0x00, nonce)
|
|
39
|
+
|
|
40
|
+
# CTR
|
|
41
|
+
ctr = CTR(self.encrypt_block, n_tag)
|
|
42
|
+
plaintext = ctr.process(ciphertext)
|
|
43
|
+
|
|
44
|
+
# Ricalcolo OMAC_1 e OMAC_2
|
|
45
|
+
h_tag = _omac_with_prefix(self.cmac, 0x01, aad)
|
|
46
|
+
c_tag = _omac_with_prefix(self.cmac, 0x02, ciphertext)
|
|
47
|
+
|
|
48
|
+
# Verifica TAG
|
|
49
|
+
expected_tag = xor_bytes(xor_bytes(n_tag, h_tag), c_tag)
|
|
50
|
+
if expected_tag != tag:
|
|
51
|
+
raise ValueError("EAX authentication failed")
|
|
52
|
+
|
|
53
|
+
return plaintext
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from .eax import EAX
|
|
2
|
+
from .twofish import Twofish
|
|
3
|
+
import zlib
|
|
4
|
+
import struct
|
|
5
|
+
|
|
6
|
+
def deobf_stage1(data: bytes) -> bytes:
|
|
7
|
+
L = len(data)
|
|
8
|
+
return bytes(data[L-1-i] ^ (L - i*L & 0xFF) for i in range(L))
|
|
9
|
+
|
|
10
|
+
def deobf_stage2(data: bytes) -> bytes:
|
|
11
|
+
L = len(data)
|
|
12
|
+
return bytes(b ^ (L - i & 0xFF) for i, b in enumerate(data))
|
|
13
|
+
|
|
14
|
+
def uncompress_qt(blob: bytes) -> bytes:
|
|
15
|
+
size = struct.unpack(">I", blob[:4])[0]
|
|
16
|
+
return zlib.decompress(blob[4:])[:size]
|
|
17
|
+
|
|
18
|
+
def decrypt_pkt(pkt: bytes) -> bytes:
|
|
19
|
+
# Stage 1 deobfuscation
|
|
20
|
+
stage1 = deobf_stage1(pkt)
|
|
21
|
+
|
|
22
|
+
# Chiave e IV per i file .pkt
|
|
23
|
+
key = bytes([137])*16
|
|
24
|
+
iv = bytes([16])*16
|
|
25
|
+
|
|
26
|
+
# Twofish block cipher
|
|
27
|
+
tf = Twofish(key)
|
|
28
|
+
encrypt_block = tf.encrypt
|
|
29
|
+
|
|
30
|
+
# EAX con nonce = iv
|
|
31
|
+
eax = EAX(encrypt_block)
|
|
32
|
+
|
|
33
|
+
# Supponiamo che negli .pkt il tag sia alla fine
|
|
34
|
+
ciphertext = stage1[:-16]
|
|
35
|
+
tag = stage1[-16:]
|
|
36
|
+
|
|
37
|
+
# Decrypt usando nonce fisso
|
|
38
|
+
decrypted = eax.decrypt(nonce=iv, ciphertext=ciphertext, tag=tag)
|
|
39
|
+
|
|
40
|
+
# Stage 2 deobfuscation
|
|
41
|
+
stage2 = deobf_stage2(decrypted)
|
|
42
|
+
|
|
43
|
+
# Decompressione
|
|
44
|
+
xml = uncompress_qt(stage2)
|
|
45
|
+
|
|
46
|
+
return xml
|
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
## twofish.py - pure Python implementation of the Twofish algorithm.
|
|
2
|
+
## Bjorn Edstrom <be@bjrn.se> 13 december 2007.
|
|
3
|
+
##
|
|
4
|
+
## Copyrights
|
|
5
|
+
## ==========
|
|
6
|
+
##
|
|
7
|
+
## This code is a derived from an implementation by Dr Brian Gladman
|
|
8
|
+
## (gladman@seven77.demon.co.uk) which is subject to the following license.
|
|
9
|
+
## This Python implementation is not subject to any other license.
|
|
10
|
+
##
|
|
11
|
+
##/* This is an independent implementation of the encryption algorithm: */
|
|
12
|
+
##/* */
|
|
13
|
+
##/* Twofish by Bruce Schneier and colleagues */
|
|
14
|
+
##/* */
|
|
15
|
+
##/* which is a candidate algorithm in the Advanced Encryption Standard */
|
|
16
|
+
##/* programme of the US National Institute of Standards and Technology. */
|
|
17
|
+
##/* */
|
|
18
|
+
##/* Copyright in this implementation is held by Dr B R Gladman but I */
|
|
19
|
+
##/* hereby give permission for its free direct or derivative use subject */
|
|
20
|
+
##/* to acknowledgment of its origin and compliance with any conditions */
|
|
21
|
+
##/* that the originators of t he algorithm place on its exploitation. */
|
|
22
|
+
##/* */
|
|
23
|
+
##/* My thanks to Doug Whiting and Niels Ferguson for comments that led */
|
|
24
|
+
##/* to improvements in this implementation. */
|
|
25
|
+
##/* */
|
|
26
|
+
##/* Dr Brian Gladman (gladman@seven77.demon.co.uk) 14th January 1999 */
|
|
27
|
+
##
|
|
28
|
+
## The above copyright notice must not be removed.
|
|
29
|
+
##
|
|
30
|
+
## Information
|
|
31
|
+
## ===========
|
|
32
|
+
##
|
|
33
|
+
## Anyone thinking of using this code should reconsider. It's slow.
|
|
34
|
+
## Try python-mcrypt instead. In case a faster library is not installed
|
|
35
|
+
## on the target system, this code can be used as a portable fallback.
|
|
36
|
+
|
|
37
|
+
# pylint: disable-all
|
|
38
|
+
|
|
39
|
+
block_size = 16
|
|
40
|
+
key_size = 32
|
|
41
|
+
|
|
42
|
+
class Twofish:
|
|
43
|
+
|
|
44
|
+
def __init__(self, key=None):
|
|
45
|
+
"""Twofish."""
|
|
46
|
+
|
|
47
|
+
if key:
|
|
48
|
+
self.set_key(key)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def set_key(self, key):
|
|
52
|
+
"""Init."""
|
|
53
|
+
|
|
54
|
+
key_len = len(key)
|
|
55
|
+
if key_len not in [16, 24, 32]:
|
|
56
|
+
# XXX: add padding?
|
|
57
|
+
raise KeyError("key must be 16, 24 or 32 bytes")
|
|
58
|
+
if key_len % 4:
|
|
59
|
+
# XXX: add padding?
|
|
60
|
+
raise KeyError("key not a multiple of 4")
|
|
61
|
+
if key_len > 32:
|
|
62
|
+
# XXX: prune?
|
|
63
|
+
raise KeyError("key_len > 32")
|
|
64
|
+
|
|
65
|
+
self.context = TWI()
|
|
66
|
+
|
|
67
|
+
key_word32 = [0] * 32
|
|
68
|
+
i = 0
|
|
69
|
+
while key:
|
|
70
|
+
key_word32[i] = struct.unpack("<L", key[0:4])[0]
|
|
71
|
+
key = key[4:]
|
|
72
|
+
i += 1
|
|
73
|
+
|
|
74
|
+
set_key(self.context, key_word32, key_len)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def decrypt(self, block):
|
|
78
|
+
"""Decrypt blocks."""
|
|
79
|
+
|
|
80
|
+
if len(block) % 16:
|
|
81
|
+
raise ValueError("block size must be a multiple of 16")
|
|
82
|
+
|
|
83
|
+
plaintext = b''
|
|
84
|
+
|
|
85
|
+
while block:
|
|
86
|
+
a, b, c, d = struct.unpack("<4L", block[:16])
|
|
87
|
+
temp = [a, b, c, d]
|
|
88
|
+
decrypt(self.context, temp)
|
|
89
|
+
plaintext += struct.pack("<4L", *temp)
|
|
90
|
+
block = block[16:]
|
|
91
|
+
|
|
92
|
+
return plaintext
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def encrypt(self, block):
|
|
96
|
+
"""Encrypt blocks."""
|
|
97
|
+
|
|
98
|
+
if len(block) % 16:
|
|
99
|
+
raise ValueError("block size must be a multiple of 16")
|
|
100
|
+
|
|
101
|
+
ciphertext = b''
|
|
102
|
+
|
|
103
|
+
while block:
|
|
104
|
+
a, b, c, d = struct.unpack("<4L", block[0:16])
|
|
105
|
+
temp = [a, b, c, d]
|
|
106
|
+
encrypt(self.context, temp)
|
|
107
|
+
ciphertext += struct.pack("<4L", *temp)
|
|
108
|
+
block = block[16:]
|
|
109
|
+
|
|
110
|
+
return ciphertext
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def get_name(self):
|
|
114
|
+
"""Return the name of the cipher."""
|
|
115
|
+
|
|
116
|
+
return "Twofish"
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def get_block_size(self):
|
|
120
|
+
"""Get cipher block size in bytes."""
|
|
121
|
+
|
|
122
|
+
return 16
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def get_key_size(self):
|
|
126
|
+
"""Get cipher key size in bytes."""
|
|
127
|
+
|
|
128
|
+
return 32
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
#
|
|
132
|
+
# Private.
|
|
133
|
+
#
|
|
134
|
+
|
|
135
|
+
import struct
|
|
136
|
+
import sys
|
|
137
|
+
|
|
138
|
+
WORD_BIGENDIAN = 0
|
|
139
|
+
if sys.byteorder == 'big':
|
|
140
|
+
WORD_BIGENDIAN = 1
|
|
141
|
+
|
|
142
|
+
def rotr32(x, n):
|
|
143
|
+
return (x >> n) | ((x << (32 - n)) & 0xFFFFFFFF)
|
|
144
|
+
|
|
145
|
+
def rotl32(x, n):
|
|
146
|
+
return ((x << n) & 0xFFFFFFFF) | (x >> (32 - n))
|
|
147
|
+
|
|
148
|
+
def byteswap32(x):
|
|
149
|
+
return ((x & 0xff) << 24) | (((x >> 8) & 0xff) << 16) | \
|
|
150
|
+
(((x >> 16) & 0xff) << 8) | ((x >> 24) & 0xff)
|
|
151
|
+
|
|
152
|
+
class TWI:
|
|
153
|
+
def __init__(self):
|
|
154
|
+
self.k_len = 0 # word32
|
|
155
|
+
self.l_key = [0]*40 # word32
|
|
156
|
+
self.s_key = [0]*4 # word32
|
|
157
|
+
self.qt_gen = 0 # word32
|
|
158
|
+
self.q_tab = [[0]*256, [0]*256] # byte
|
|
159
|
+
self.mt_gen = 0 # word32
|
|
160
|
+
self.m_tab = [[0]*256, [0]*256, [0]*256, [0]*256] # word32
|
|
161
|
+
self.mk_tab = [[0]*256, [0]*256, [0]*256, [0]*256] # word32
|
|
162
|
+
|
|
163
|
+
def byte(x, n):
|
|
164
|
+
return (x >> (8 * n)) & 0xff
|
|
165
|
+
|
|
166
|
+
tab_5b = [0, 90, 180, 238]
|
|
167
|
+
tab_ef = [0, 238, 180, 90]
|
|
168
|
+
ror4 = [0, 8, 1, 9, 2, 10, 3, 11, 4, 12, 5, 13, 6, 14, 7, 15]
|
|
169
|
+
ashx = [0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12, 5, 14, 7]
|
|
170
|
+
qt0 = [[8, 1, 7, 13, 6, 15, 3, 2, 0, 11, 5, 9, 14, 12, 10, 4],
|
|
171
|
+
[2, 8, 11, 13, 15, 7, 6, 14, 3, 1, 9, 4, 0, 10, 12, 5]]
|
|
172
|
+
qt1 = [[14, 12, 11, 8, 1, 2, 3, 5, 15, 4, 10, 6, 7, 0, 9, 13],
|
|
173
|
+
[1, 14, 2, 11, 4, 12, 3, 7, 6, 13, 10, 5, 15, 9, 0, 8]]
|
|
174
|
+
qt2 = [[11, 10, 5, 14, 6, 13, 9, 0, 12, 8, 15, 3, 2, 4, 7, 1],
|
|
175
|
+
[4, 12, 7, 5, 1, 6, 9, 10, 0, 14, 13, 8, 2, 11, 3, 15]]
|
|
176
|
+
qt3 = [[13, 7, 15, 4, 1, 2, 6, 14, 9, 11, 3, 0, 8, 5, 12, 10],
|
|
177
|
+
[11, 9, 5, 1, 12, 3, 13, 14, 6, 4, 7, 15, 2, 0, 8, 10]]
|
|
178
|
+
|
|
179
|
+
def qp(n, x): # word32, byte
|
|
180
|
+
n %= 0x100000000
|
|
181
|
+
x %= 0x100
|
|
182
|
+
a0 = x >> 4;
|
|
183
|
+
b0 = x & 15;
|
|
184
|
+
a1 = a0 ^ b0;
|
|
185
|
+
b1 = ror4[b0] ^ ashx[a0];
|
|
186
|
+
a2 = qt0[n][a1];
|
|
187
|
+
b2 = qt1[n][b1];
|
|
188
|
+
a3 = a2 ^ b2;
|
|
189
|
+
b3 = ror4[b2] ^ ashx[a2];
|
|
190
|
+
a4 = qt2[n][a3];
|
|
191
|
+
b4 = qt3[n][b3];
|
|
192
|
+
return (b4 << 4) | a4;
|
|
193
|
+
|
|
194
|
+
def gen_qtab(pkey):
|
|
195
|
+
for i in range(256):
|
|
196
|
+
pkey.q_tab[0][i] = qp(0, i)
|
|
197
|
+
pkey.q_tab[1][i] = qp(1, i)
|
|
198
|
+
|
|
199
|
+
def gen_mtab(pkey):
|
|
200
|
+
for i in range(256):
|
|
201
|
+
f01 = pkey.q_tab[1][i]
|
|
202
|
+
f01 = pkey.q_tab[1][i];
|
|
203
|
+
f5b = ((f01) ^ ((f01) >> 2) ^ tab_5b[(f01) & 3]);
|
|
204
|
+
fef = ((f01) ^ ((f01) >> 1) ^ ((f01) >> 2) ^ tab_ef[(f01) & 3]);
|
|
205
|
+
pkey.m_tab[0][i] = f01 + (f5b << 8) + (fef << 16) + (fef << 24);
|
|
206
|
+
pkey.m_tab[2][i] = f5b + (fef << 8) + (f01 << 16) + (fef << 24);
|
|
207
|
+
|
|
208
|
+
f01 = pkey.q_tab[0][i];
|
|
209
|
+
f5b = ((f01) ^ ((f01) >> 2) ^ tab_5b[(f01) & 3]);
|
|
210
|
+
fef = ((f01) ^ ((f01) >> 1) ^ ((f01) >> 2) ^ tab_ef[(f01) & 3]);
|
|
211
|
+
pkey.m_tab[1][i] = fef + (fef << 8) + (f5b << 16) + (f01 << 24);
|
|
212
|
+
pkey.m_tab[3][i] = f5b + (f01 << 8) + (fef << 16) + (f5b << 24);
|
|
213
|
+
|
|
214
|
+
def gen_mk_tab(pkey, key):
|
|
215
|
+
if pkey.k_len == 2:
|
|
216
|
+
for i in range(256):
|
|
217
|
+
by = i % 0x100
|
|
218
|
+
pkey.mk_tab[0][i] = pkey.m_tab[0][pkey.q_tab[0][pkey.q_tab[0][by] ^ byte(key[1],0)] ^ byte(key[0],0)];
|
|
219
|
+
pkey.mk_tab[1][i] = pkey.m_tab[1][pkey.q_tab[0][pkey.q_tab[1][by] ^ byte(key[1],1)] ^ byte(key[0],1)];
|
|
220
|
+
pkey.mk_tab[2][i] = pkey.m_tab[2][pkey.q_tab[1][pkey.q_tab[0][by] ^ byte(key[1],2)] ^ byte(key[0],2)];
|
|
221
|
+
pkey.mk_tab[3][i] = pkey.m_tab[3][pkey.q_tab[1][pkey.q_tab[1][by] ^ byte(key[1],3)] ^ byte(key[0],3)];
|
|
222
|
+
if pkey.k_len == 3:
|
|
223
|
+
for i in range(256):
|
|
224
|
+
by = i % 0x100
|
|
225
|
+
pkey.mk_tab[0][i] = pkey.m_tab[0][pkey.q_tab[0][pkey.q_tab[0][pkey.q_tab[1][by] ^ byte(key[2], 0)] ^ byte(key[1], 0)] ^ byte(key[0], 0)];
|
|
226
|
+
pkey.mk_tab[1][i] = pkey.m_tab[1][pkey.q_tab[0][pkey.q_tab[1][pkey.q_tab[1][by] ^ byte(key[2], 1)] ^ byte(key[1], 1)] ^ byte(key[0], 1)];
|
|
227
|
+
pkey.mk_tab[2][i] = pkey.m_tab[2][pkey.q_tab[1][pkey.q_tab[0][pkey.q_tab[0][by] ^ byte(key[2], 2)] ^ byte(key[1], 2)] ^ byte(key[0], 2)];
|
|
228
|
+
pkey.mk_tab[3][i] = pkey.m_tab[3][pkey.q_tab[1][pkey.q_tab[1][pkey.q_tab[0][by] ^ byte(key[2], 3)] ^ byte(key[1], 3)] ^ byte(key[0], 3)];
|
|
229
|
+
if pkey.k_len == 4:
|
|
230
|
+
for i in range(256):
|
|
231
|
+
by = i % 0x100
|
|
232
|
+
pkey.mk_tab[0][i] = pkey.m_tab[0][pkey.q_tab[0][pkey.q_tab[0][pkey.q_tab[1][pkey.q_tab[1][by] ^ byte(key[3], 0)] ^ byte(key[2], 0)] ^ byte(key[1], 0)] ^ byte(key[0], 0)];
|
|
233
|
+
pkey.mk_tab[1][i] = pkey.m_tab[1][pkey.q_tab[0][pkey.q_tab[1][pkey.q_tab[1][pkey.q_tab[0][by] ^ byte(key[3], 1)] ^ byte(key[2], 1)] ^ byte(key[1], 1)] ^ byte(key[0], 1)];
|
|
234
|
+
pkey.mk_tab[2][i] = pkey.m_tab[2][pkey.q_tab[1][pkey.q_tab[0][pkey.q_tab[0][pkey.q_tab[0][by] ^ byte(key[3], 2)] ^ byte(key[2], 2)] ^ byte(key[1], 2)] ^ byte(key[0], 2)];
|
|
235
|
+
pkey.mk_tab[3][i] = pkey.m_tab[3][pkey.q_tab[1][pkey.q_tab[1][pkey.q_tab[0][pkey.q_tab[1][by] ^ byte(key[3], 3)] ^ byte(key[2], 3)] ^ byte(key[1], 3)] ^ byte(key[0], 3)];
|
|
236
|
+
|
|
237
|
+
def h_fun(pkey, x, key):
|
|
238
|
+
b0 = byte(x, 0);
|
|
239
|
+
b1 = byte(x, 1);
|
|
240
|
+
b2 = byte(x, 2);
|
|
241
|
+
b3 = byte(x, 3);
|
|
242
|
+
if pkey.k_len >= 4:
|
|
243
|
+
b0 = pkey.q_tab[1][b0] ^ byte(key[3], 0);
|
|
244
|
+
b1 = pkey.q_tab[0][b1] ^ byte(key[3], 1);
|
|
245
|
+
b2 = pkey.q_tab[0][b2] ^ byte(key[3], 2);
|
|
246
|
+
b3 = pkey.q_tab[1][b3] ^ byte(key[3], 3);
|
|
247
|
+
if pkey.k_len >= 3:
|
|
248
|
+
b0 = pkey.q_tab[1][b0] ^ byte(key[2], 0);
|
|
249
|
+
b1 = pkey.q_tab[1][b1] ^ byte(key[2], 1);
|
|
250
|
+
b2 = pkey.q_tab[0][b2] ^ byte(key[2], 2);
|
|
251
|
+
b3 = pkey.q_tab[0][b3] ^ byte(key[2], 3);
|
|
252
|
+
if pkey.k_len >= 2:
|
|
253
|
+
b0 = pkey.q_tab[0][pkey.q_tab[0][b0] ^ byte(key[1], 0)] ^ byte(key[0], 0);
|
|
254
|
+
b1 = pkey.q_tab[0][pkey.q_tab[1][b1] ^ byte(key[1], 1)] ^ byte(key[0], 1);
|
|
255
|
+
b2 = pkey.q_tab[1][pkey.q_tab[0][b2] ^ byte(key[1], 2)] ^ byte(key[0], 2);
|
|
256
|
+
b3 = pkey.q_tab[1][pkey.q_tab[1][b3] ^ byte(key[1], 3)] ^ byte(key[0], 3);
|
|
257
|
+
return pkey.m_tab[0][b0] ^ pkey.m_tab[1][b1] ^ pkey.m_tab[2][b2] ^ pkey.m_tab[3][b3];
|
|
258
|
+
|
|
259
|
+
def mds_rem(p0, p1):
|
|
260
|
+
i, t, u = 0, 0, 0
|
|
261
|
+
for i in range(8):
|
|
262
|
+
t = p1 >> 24
|
|
263
|
+
p1 = ((p1 << 8) & 0xffffffff) | (p0 >> 24)
|
|
264
|
+
p0 = (p0 << 8) & 0xffffffff
|
|
265
|
+
u = (t << 1) & 0xffffffff
|
|
266
|
+
if t & 0x80:
|
|
267
|
+
u ^= 0x0000014d
|
|
268
|
+
p1 ^= t ^ ((u << 16) & 0xffffffff)
|
|
269
|
+
u ^= (t >> 1)
|
|
270
|
+
if t & 0x01:
|
|
271
|
+
u ^= 0x0000014d >> 1
|
|
272
|
+
p1 ^= ((u << 24) & 0xffffffff) | ((u << 8) & 0xffffffff)
|
|
273
|
+
return p1
|
|
274
|
+
|
|
275
|
+
def set_key(pkey, in_key, key_len):
|
|
276
|
+
pkey.qt_gen = 0
|
|
277
|
+
if not pkey.qt_gen:
|
|
278
|
+
gen_qtab(pkey)
|
|
279
|
+
pkey.qt_gen = 1
|
|
280
|
+
pkey.mt_gen = 0
|
|
281
|
+
if not pkey.mt_gen:
|
|
282
|
+
gen_mtab(pkey)
|
|
283
|
+
pkey.mt_gen = 1
|
|
284
|
+
pkey.k_len = (key_len * 8) // 64
|
|
285
|
+
|
|
286
|
+
a = 0
|
|
287
|
+
b = 0
|
|
288
|
+
me_key = [0,0,0,0]
|
|
289
|
+
mo_key = [0,0,0,0]
|
|
290
|
+
for i in range(pkey.k_len):
|
|
291
|
+
if WORD_BIGENDIAN:
|
|
292
|
+
a = byteswap32(in_key[i + 1])
|
|
293
|
+
me_key[i] = a
|
|
294
|
+
b = byteswap32(in_key[i + i + 1])
|
|
295
|
+
else:
|
|
296
|
+
a = in_key[i + i]
|
|
297
|
+
me_key[i] = a
|
|
298
|
+
b = in_key[i + i + 1]
|
|
299
|
+
mo_key[i] = b
|
|
300
|
+
pkey.s_key[pkey.k_len - i - 1] = mds_rem(a, b);
|
|
301
|
+
for i in range(0, 40, 2):
|
|
302
|
+
a = (0x01010101 * i) % 0x100000000;
|
|
303
|
+
b = (a + 0x01010101) % 0x100000000;
|
|
304
|
+
a = h_fun(pkey, a, me_key);
|
|
305
|
+
b = rotl32(h_fun(pkey, b, mo_key), 8);
|
|
306
|
+
pkey.l_key[i] = (a + b) % 0x100000000;
|
|
307
|
+
pkey.l_key[i + 1] = rotl32((a + 2 * b) % 0x100000000, 9);
|
|
308
|
+
gen_mk_tab(pkey, pkey.s_key)
|
|
309
|
+
|
|
310
|
+
def encrypt(pkey, in_blk):
|
|
311
|
+
blk = [0, 0, 0, 0]
|
|
312
|
+
|
|
313
|
+
if WORD_BIGENDIAN:
|
|
314
|
+
blk[0] = byteswap32(in_blk[0]) ^ pkey.l_key[0];
|
|
315
|
+
blk[1] = byteswap32(in_blk[1]) ^ pkey.l_key[1];
|
|
316
|
+
blk[2] = byteswap32(in_blk[2]) ^ pkey.l_key[2];
|
|
317
|
+
blk[3] = byteswap32(in_blk[3]) ^ pkey.l_key[3];
|
|
318
|
+
else:
|
|
319
|
+
blk[0] = in_blk[0] ^ pkey.l_key[0];
|
|
320
|
+
blk[1] = in_blk[1] ^ pkey.l_key[1];
|
|
321
|
+
blk[2] = in_blk[2] ^ pkey.l_key[2];
|
|
322
|
+
blk[3] = in_blk[3] ^ pkey.l_key[3];
|
|
323
|
+
|
|
324
|
+
for i in range(8):
|
|
325
|
+
t1 = ( pkey.mk_tab[0][byte(blk[1],3)] ^ pkey.mk_tab[1][byte(blk[1],0)] ^ pkey.mk_tab[2][byte(blk[1],1)] ^ pkey.mk_tab[3][byte(blk[1],2)] );
|
|
326
|
+
t0 = ( pkey.mk_tab[0][byte(blk[0],0)] ^ pkey.mk_tab[1][byte(blk[0],1)] ^ pkey.mk_tab[2][byte(blk[0],2)] ^ pkey.mk_tab[3][byte(blk[0],3)] );
|
|
327
|
+
|
|
328
|
+
blk[2] = rotr32(blk[2] ^ ((t0 + t1 + pkey.l_key[4 * (i) + 8]) % 0x100000000), 1);
|
|
329
|
+
blk[3] = rotl32(blk[3], 1) ^ ((t0 + 2 * t1 + pkey.l_key[4 * (i) + 9]) % 0x100000000);
|
|
330
|
+
|
|
331
|
+
t1 = ( pkey.mk_tab[0][byte(blk[3],3)] ^ pkey.mk_tab[1][byte(blk[3],0)] ^ pkey.mk_tab[2][byte(blk[3],1)] ^ pkey.mk_tab[3][byte(blk[3],2)] );
|
|
332
|
+
t0 = ( pkey.mk_tab[0][byte(blk[2],0)] ^ pkey.mk_tab[1][byte(blk[2],1)] ^ pkey.mk_tab[2][byte(blk[2],2)] ^ pkey.mk_tab[3][byte(blk[2],3)] );
|
|
333
|
+
|
|
334
|
+
blk[0] = rotr32(blk[0] ^ ((t0 + t1 + pkey.l_key[4 * (i) + 10]) % 0x100000000), 1);
|
|
335
|
+
blk[1] = rotl32(blk[1], 1) ^ ((t0 + 2 * t1 + pkey.l_key[4 * (i) + 11]) % 0x100000000);
|
|
336
|
+
|
|
337
|
+
if WORD_BIGENDIAN:
|
|
338
|
+
in_blk[0] = byteswap32(blk[2] ^ pkey.l_key[4]);
|
|
339
|
+
in_blk[1] = byteswap32(blk[3] ^ pkey.l_key[5]);
|
|
340
|
+
in_blk[2] = byteswap32(blk[0] ^ pkey.l_key[6]);
|
|
341
|
+
in_blk[3] = byteswap32(blk[1] ^ pkey.l_key[7]);
|
|
342
|
+
else:
|
|
343
|
+
in_blk[0] = blk[2] ^ pkey.l_key[4];
|
|
344
|
+
in_blk[1] = blk[3] ^ pkey.l_key[5];
|
|
345
|
+
in_blk[2] = blk[0] ^ pkey.l_key[6];
|
|
346
|
+
in_blk[3] = blk[1] ^ pkey.l_key[7];
|
|
347
|
+
|
|
348
|
+
return
|
|
349
|
+
|
|
350
|
+
def decrypt(pkey, in_blk):
|
|
351
|
+
blk = [0, 0, 0, 0]
|
|
352
|
+
|
|
353
|
+
if WORD_BIGENDIAN:
|
|
354
|
+
blk[0] = byteswap32(in_blk[0]) ^ pkey.l_key[4];
|
|
355
|
+
blk[1] = byteswap32(in_blk[1]) ^ pkey.l_key[5];
|
|
356
|
+
blk[2] = byteswap32(in_blk[2]) ^ pkey.l_key[6];
|
|
357
|
+
blk[3] = byteswap32(in_blk[3]) ^ pkey.l_key[7];
|
|
358
|
+
else:
|
|
359
|
+
blk[0] = in_blk[0] ^ pkey.l_key[4];
|
|
360
|
+
blk[1] = in_blk[1] ^ pkey.l_key[5];
|
|
361
|
+
blk[2] = in_blk[2] ^ pkey.l_key[6];
|
|
362
|
+
blk[3] = in_blk[3] ^ pkey.l_key[7];
|
|
363
|
+
|
|
364
|
+
for i in range(7, -1, -1):
|
|
365
|
+
t1 = ( pkey.mk_tab[0][byte(blk[1],3)] ^ pkey.mk_tab[1][byte(blk[1],0)] ^ pkey.mk_tab[2][byte(blk[1],1)] ^ pkey.mk_tab[3][byte(blk[1],2)] )
|
|
366
|
+
t0 = ( pkey.mk_tab[0][byte(blk[0],0)] ^ pkey.mk_tab[1][byte(blk[0],1)] ^ pkey.mk_tab[2][byte(blk[0],2)] ^ pkey.mk_tab[3][byte(blk[0],3)] )
|
|
367
|
+
|
|
368
|
+
blk[2] = rotl32(blk[2], 1) ^ ((t0 + t1 + pkey.l_key[4 * (i) + 10]) % 0x100000000)
|
|
369
|
+
blk[3] = rotr32(blk[3] ^ ((t0 + 2 * t1 + pkey.l_key[4 * (i) + 11]) % 0x100000000), 1)
|
|
370
|
+
|
|
371
|
+
t1 = ( pkey.mk_tab[0][byte(blk[3],3)] ^ pkey.mk_tab[1][byte(blk[3],0)] ^ pkey.mk_tab[2][byte(blk[3],1)] ^ pkey.mk_tab[3][byte(blk[3],2)] )
|
|
372
|
+
t0 = ( pkey.mk_tab[0][byte(blk[2],0)] ^ pkey.mk_tab[1][byte(blk[2],1)] ^ pkey.mk_tab[2][byte(blk[2],2)] ^ pkey.mk_tab[3][byte(blk[2],3)] )
|
|
373
|
+
|
|
374
|
+
blk[0] = rotl32(blk[0], 1) ^ ((t0 + t1 + pkey.l_key[4 * (i) + 8]) % 0x100000000)
|
|
375
|
+
blk[1] = rotr32(blk[1] ^ ((t0 + 2 * t1 + pkey.l_key[4 * (i) + 9]) % 0x100000000), 1)
|
|
376
|
+
|
|
377
|
+
if WORD_BIGENDIAN:
|
|
378
|
+
in_blk[0] = byteswap32(blk[2] ^ pkey.l_key[0]);
|
|
379
|
+
in_blk[1] = byteswap32(blk[3] ^ pkey.l_key[1]);
|
|
380
|
+
in_blk[2] = byteswap32(blk[0] ^ pkey.l_key[2]);
|
|
381
|
+
in_blk[3] = byteswap32(blk[1] ^ pkey.l_key[3]);
|
|
382
|
+
else:
|
|
383
|
+
in_blk[0] = blk[2] ^ pkey.l_key[0];
|
|
384
|
+
in_blk[1] = blk[3] ^ pkey.l_key[1];
|
|
385
|
+
in_blk[2] = blk[0] ^ pkey.l_key[2];
|
|
386
|
+
in_blk[3] = blk[1] ^ pkey.l_key[3];
|
|
387
|
+
return
|
|
388
|
+
|
|
389
|
+
__testkey = b'\xD4\x3B\xB7\x55\x6E\xA3\x2E\x46\xF2\xA2\x82\xB7\xD4\x5B\x4E\x0D\x57\xFF\x73\x9D\x4D\xC9\x2C\x1B\xD7\xFC\x01\x70\x0C\xC8\x21\x6F'
|
|
390
|
+
__testdat = b'\x90\xAF\xE9\x1B\xB2\x88\x54\x4F\x2C\x32\xDC\x23\x9B\x26\x35\xE6'
|
|
391
|
+
assert b'l\xb4V\x1c@\xbf\n\x97\x05\x93\x1c\xb6\xd4\x08\xe7\xfa' == Twofish(__testkey).encrypt(__testdat)
|
|
392
|
+
assert __testdat == Twofish(__testkey).decrypt(b'l\xb4V\x1c@\xbf\n\x97\x05\x93\x1c\xb6\xd4\x08\xe7\xfa')
|
|
393
|
+
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: securedypkt
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Pure-Python toolkit for decrypting and re-encrypting Cisco Packet Tracer (.pkt) files
|
|
5
|
+
Author-email: Securedy Labs <nathanrampersaud@gmail.com>
|
|
6
|
+
Project-URL: Homepage, https://github.com/Nathan8044/SecuredyPKT
|
|
7
|
+
Keywords: cisco,packet-tracer,pkt,twofish,eax,crypto,network
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Topic :: Security :: Cryptography
|
|
17
|
+
Classifier: Topic :: Utilities
|
|
18
|
+
Requires-Python: >=3.8
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Dynamic: license-file
|
|
22
|
+
|
|
23
|
+
# SecuredyPKT
|
|
24
|
+
|
|
25
|
+
**SecuredyPKT** is a pure-Python research tool by **Securedy Labs** for decrypting and re-encrypting Cisco Packet Tracer (`.pkt`) files.
|
|
26
|
+
|
|
27
|
+
It fully reimplements the proprietary cryptographic and obfuscation pipeline used internally by Packet Tracer, enabling offline inspection, diffing, and version-controlling of lab topologies — without requiring the official application.
|
|
28
|
+
|
|
29
|
+
This is an active research and development project. A custom crypto model and extended feature set are planned.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install securedypkt
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
No additional dependencies required. Pure Python 3.8+.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Python API (for agents and integrations)
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
import securedypkt
|
|
47
|
+
|
|
48
|
+
# Decrypt a .pkt file → XML bytes
|
|
49
|
+
xml_bytes = securedypkt.decrypt_file("lab.pkt")
|
|
50
|
+
|
|
51
|
+
# Or work directly with bytes (e.g. from an upload handler)
|
|
52
|
+
with open("lab.pkt", "rb") as f:
|
|
53
|
+
xml_bytes = securedypkt.decrypt_pkt(f.read())
|
|
54
|
+
|
|
55
|
+
# Re-encrypt XML bytes → .pkt bytes
|
|
56
|
+
pkt_bytes = securedypkt.encrypt_pkt(xml_bytes)
|
|
57
|
+
pkt_bytes = securedypkt.encrypt_file("lab.xml")
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## CLI Usage
|
|
63
|
+
|
|
64
|
+
### Decrypt `.pkt` → XML
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
unpacket lab.pkt
|
|
68
|
+
# Output: lab.xml
|
|
69
|
+
|
|
70
|
+
unpacket lab.pkt -o decrypted.xml
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Re-encrypt XML → `.pkt`
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
repacket lab.xml
|
|
77
|
+
# Output: lab.pkt
|
|
78
|
+
|
|
79
|
+
repacket lab.xml -o rebuilt.pkt
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Options
|
|
83
|
+
|
|
84
|
+
| Tool | Option | Description |
|
|
85
|
+
|------|--------|-------------|
|
|
86
|
+
| `unpacket` | `input_file` | Path to `.pkt` file |
|
|
87
|
+
| | `-o, --output` | Output XML path |
|
|
88
|
+
| `repacket` | `input_file` | Path to `.xml` file |
|
|
89
|
+
| | `-o, --output` | Output `.pkt` path |
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## How It Works
|
|
94
|
+
|
|
95
|
+
Cisco Packet Tracer `.pkt` files are protected by a 4-layer scheme. SecuredyPKT reverses all four layers:
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
[ .pkt file on disk ]
|
|
99
|
+
|
|
|
100
|
+
v
|
|
101
|
+
[1] Stage-1 Deobfuscation
|
|
102
|
+
Byte-order reversal + positional XOR
|
|
103
|
+
result[i] = data[L-1-i] ^ (L - i*L & 0xFF)
|
|
104
|
+
|
|
|
105
|
+
v
|
|
106
|
+
[2] Twofish/EAX Decryption (authenticated)
|
|
107
|
+
128-bit Twofish block cipher in EAX mode
|
|
108
|
+
Key: 0x89 * 16 (hardcoded in Packet Tracer)
|
|
109
|
+
IV: 0x10 * 16 (hardcoded in Packet Tracer)
|
|
110
|
+
Last 16 bytes of ciphertext = AEAD authentication tag
|
|
111
|
+
|
|
|
112
|
+
v
|
|
113
|
+
[3] Stage-2 Deobfuscation
|
|
114
|
+
Positional XOR with decreasing counter
|
|
115
|
+
result[i] = b ^ (L - i & 0xFF)
|
|
116
|
+
|
|
|
117
|
+
v
|
|
118
|
+
[4] Qt Decompression
|
|
119
|
+
zlib stream with 4-byte big-endian size prefix (qCompress format)
|
|
120
|
+
|
|
|
121
|
+
v
|
|
122
|
+
[ Plain XML topology ]
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
`repacket` runs this pipeline in reverse: XML → compress → obfuscate → encrypt → obfuscate → `.pkt`.
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Crypto Stack
|
|
130
|
+
|
|
131
|
+
| Module | Purpose |
|
|
132
|
+
|--------|---------|
|
|
133
|
+
| `securedypkt/decipher/twofish.py` | Pure-Python Twofish block cipher (128-bit) |
|
|
134
|
+
| `securedypkt/decipher/cmac.py` | CMAC / OMAC message authentication code |
|
|
135
|
+
| `securedypkt/decipher/ctr.py` | CTR mode keystream engine (big-endian counter) |
|
|
136
|
+
| `securedypkt/decipher/eax.py` | EAX authenticated encryption (AEAD) |
|
|
137
|
+
| `securedypkt/decipher/pt_crypto.py` | Full Packet Tracer decryption pipeline |
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## Security Notes
|
|
142
|
+
|
|
143
|
+
- The hardcoded key and IV are **Cisco's**, reverse-engineered from the Packet Tracer binary
|
|
144
|
+
- EAX tag verification is enforced — corrupted or tampered `.pkt` files are rejected
|
|
145
|
+
- No network calls; fully offline
|
|
146
|
+
- Known research items for future versions:
|
|
147
|
+
- Input file size guard
|
|
148
|
+
- zlib decompression size cap
|
|
149
|
+
- XML parsing hardening
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## Legal
|
|
154
|
+
|
|
155
|
+
This software is provided for **security research, educational, and interoperability purposes**.
|
|
156
|
+
|
|
157
|
+
Cisco Packet Tracer and all related trademarks are property of Cisco Systems, Inc.
|
|
158
|
+
SecuredyPKT is not affiliated with or endorsed by Cisco.
|
|
159
|
+
|
|
160
|
+
See [LICENSE](LICENSE) for full terms.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
setup.py
|
|
5
|
+
securedypkt/__init__.py
|
|
6
|
+
securedypkt/_repacket.py
|
|
7
|
+
securedypkt/_unpacket.py
|
|
8
|
+
securedypkt.egg-info/PKG-INFO
|
|
9
|
+
securedypkt.egg-info/SOURCES.txt
|
|
10
|
+
securedypkt.egg-info/dependency_links.txt
|
|
11
|
+
securedypkt.egg-info/entry_points.txt
|
|
12
|
+
securedypkt.egg-info/top_level.txt
|
|
13
|
+
securedypkt/decipher/__init__.py
|
|
14
|
+
securedypkt/decipher/cmac.py
|
|
15
|
+
securedypkt/decipher/ctr.py
|
|
16
|
+
securedypkt/decipher/eax.py
|
|
17
|
+
securedypkt/decipher/pt_crypto.py
|
|
18
|
+
securedypkt/decipher/twofish.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
securedypkt
|