upk-tool 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.
@@ -0,0 +1,14 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *$py.class
4
+ *.egg-info/
5
+ *.egg
6
+ dist/
7
+ build/
8
+ .eggs/
9
+ *.upk
10
+ *.pem
11
+ .env
12
+ .venv/
13
+ venv/
14
+ .idea/
upk_tool-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 UGREEN UPK Tool Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,215 @@
1
+ Metadata-Version: 2.4
2
+ Name: upk-tool
3
+ Version: 0.1.0
4
+ Summary: Inspect, extract, and create UGREEN NAS .upk package files
5
+ Project-URL: Homepage, https://github.com/fuho/upk-tool
6
+ Project-URL: Issues, https://github.com/fuho/upk-tool/issues
7
+ Author: UGREEN UPK Tool Contributors
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: firmware,nas,package,ugreen,upk
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.8
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Topic :: System :: Archiving :: Packaging
23
+ Classifier: Topic :: Utilities
24
+ Requires-Python: >=3.8
25
+ Provides-Extra: dev
26
+ Requires-Dist: build>=1.0; extra == 'dev'
27
+ Requires-Dist: pytest>=7.0; extra == 'dev'
28
+ Requires-Dist: twine>=4.0; extra == 'dev'
29
+ Provides-Extra: signing
30
+ Requires-Dist: cryptography>=3.0; extra == 'signing'
31
+ Description-Content-Type: text/markdown
32
+
33
+ # upk-tool
34
+
35
+ Inspect, extract, and create **UGREEN NAS `.upk` package files** (UGREEN-PKG-V2-FORMAT).
36
+
37
+ ## What is a .upk file?
38
+
39
+ `.upk` files are application packages used by UGREEN NAS devices. They contain:
40
+
41
+ - **Cryptographic signatures** (RSA-2048) for file integrity and authenticity
42
+ - **Public keys** for signer and manufacturer verification
43
+ - **App icon** (PNG)
44
+ - **App payload** (gzip → tar → XZ → tar with app binaries, configs, web UI)
45
+ - **Hash chain** for additional integrity verification
46
+
47
+ ### Format structure
48
+
49
+ ```
50
+ UGREEN-PKG-V2-FORMAT
51
+ filesig:344:<base64 RSA signature>
52
+ userpub:392:<base64 DER public key>
53
+ usersig:344:<base64 RSA signature>
54
+ midpub:392:<base64 DER public key>
55
+ midsig:344:<base64 RSA signature>
56
+ ico:31350:<raw PNG binary>
57
+ ugb:15608334:<gzip compressed tar>
58
+ obj2:10204:<hex hash chain>
59
+ ```
60
+
61
+ Each field follows the pattern `key:length:value` where `length` is the byte count of `value`.
62
+
63
+ ## Installation
64
+
65
+ ```bash
66
+ pip install upk-tool
67
+ ```
68
+
69
+ For signing support:
70
+ ```bash
71
+ pip install upk-tool[signing]
72
+ ```
73
+
74
+ ## Usage
75
+
76
+ ### Inspect a .upk file
77
+
78
+ ```bash
79
+ upk-tool verify firmware.upk
80
+ ```
81
+
82
+ Output:
83
+ ```
84
+ File: firmware.upk
85
+ Fields: 8
86
+ filesig: 344 bytes
87
+ userpub: 392 bytes
88
+ usersig: 344 bytes
89
+ midpub: 392 bytes
90
+ midsig: 344 bytes
91
+ ico: 31350 bytes
92
+ ugb: 15608334 bytes
93
+ obj2: 10204 bytes
94
+
95
+ ugb contents (gzip tar): 2 entries
96
+ uninstall.sh (657 bytes)
97
+ com.ugreen.comic.ugb (15602856 bytes)
98
+ -> com.ugreen.comic.ugb: XZ -> tar with 64 files
99
+
100
+ Signature verification:
101
+ filesig: 256 bytes (RSA-2048)
102
+ usersig: 256 bytes (RSA-2048)
103
+ midsig: 256 bytes (RSA-2048)
104
+ userpub: 294 bytes DER
105
+ midpub: 294 bytes DER
106
+ ```
107
+
108
+ ### Extract a .upk file
109
+
110
+ ```bash
111
+ upk-tool extract firmware.upk -o extracted/
112
+ ```
113
+
114
+ This will:
115
+ 1. Save all raw fields to the output directory
116
+ 2. Create `metadata.json` with field metadata
117
+ 3. Decompress the `ugb` payload (gzip → tar)
118
+ 4. Decompress inner `.ugb` files (XZ → tar)
119
+ 5. Extract the final app contents
120
+
121
+ Directory structure:
122
+ ```
123
+ extracted/
124
+ ├── metadata.json
125
+ ├── ico.bin
126
+ ├── ugb.bin
127
+ └── ugb_contents/
128
+ ├── uninstall.sh
129
+ ├── com.ugreen.app.ugb
130
+ ├── com.ugreen.app_files.tar
131
+ └── com.ugreen.app_app/
132
+ ├── sbin/
133
+ │ └── my_service
134
+ ├── config.json
135
+ ├── www/
136
+ │ └── assets/
137
+ └── ...
138
+ ```
139
+
140
+ ### Create a .upk file
141
+
142
+ ```bash
143
+ upk-tool create ./my_app/ -o my_app.upk --icon icon.png --name myapp
144
+ ```
145
+
146
+ Options:
147
+ - `--icon` - PNG icon file (optional, uses 1x1 placeholder if omitted)
148
+ - `--name` - App name used in the package (default: `app`)
149
+ - `--uninstall` - Custom uninstall script (optional)
150
+ - `--user-key` - RSA private key for user signing (PEM format)
151
+ - `--mfg-key` - RSA private key for manufacturer signing (PEM format)
152
+
153
+ ### Create a signed .upk file
154
+
155
+ ```bash
156
+ # Generate keys (for testing)
157
+ openssl genrsa -out user_key.pem 2048
158
+ openssl genrsa -out mfg_key.pem 2048
159
+
160
+ # Create signed package
161
+ upk-tool create ./my_app/ \
162
+ -o my_app.upk \
163
+ --icon icon.png \
164
+ --name myapp \
165
+ --user-key user_key.pem \
166
+ --mfg-key mfg_key.pem
167
+ ```
168
+
169
+ > **Note**: Without real UGREEN private keys, the package will have valid structure but signatures won't verify on actual hardware.
170
+
171
+ ## Python API
172
+
173
+ ```python
174
+ from upk_tool import parse_upk, extract_upk, create_upk, verify_upk
175
+
176
+ # Parse and inspect
177
+ fields = parse_upk("firmware.upk")
178
+ print(fields.keys()) # ['filesig', 'userpub', 'usersig', ...]
179
+
180
+ # Extract
181
+ extract_upk("firmware.upk", "output_dir/")
182
+
183
+ # Create
184
+ create_upk(
185
+ app_dir="./my_app/",
186
+ output_path="output.upk",
187
+ icon_path="icon.png",
188
+ app_name="myapp",
189
+ )
190
+
191
+ # Verify/inspect
192
+ verify_upk("firmware.upk")
193
+ ```
194
+
195
+ ## Development
196
+
197
+ ```bash
198
+ # Clone and install in development mode
199
+ git clone https://github.com/anomalyco/oc_analyze_ugreen_upk_package_file.git
200
+ cd oc_analyze_ugreen_upk_package_file
201
+ pip install -e ".[dev]"
202
+
203
+ # Run tests
204
+ pytest
205
+
206
+ # Build package
207
+ python -m build
208
+
209
+ # Upload to PyPI
210
+ twine upload dist/*
211
+ ```
212
+
213
+ ## License
214
+
215
+ MIT
@@ -0,0 +1,183 @@
1
+ # upk-tool
2
+
3
+ Inspect, extract, and create **UGREEN NAS `.upk` package files** (UGREEN-PKG-V2-FORMAT).
4
+
5
+ ## What is a .upk file?
6
+
7
+ `.upk` files are application packages used by UGREEN NAS devices. They contain:
8
+
9
+ - **Cryptographic signatures** (RSA-2048) for file integrity and authenticity
10
+ - **Public keys** for signer and manufacturer verification
11
+ - **App icon** (PNG)
12
+ - **App payload** (gzip → tar → XZ → tar with app binaries, configs, web UI)
13
+ - **Hash chain** for additional integrity verification
14
+
15
+ ### Format structure
16
+
17
+ ```
18
+ UGREEN-PKG-V2-FORMAT
19
+ filesig:344:<base64 RSA signature>
20
+ userpub:392:<base64 DER public key>
21
+ usersig:344:<base64 RSA signature>
22
+ midpub:392:<base64 DER public key>
23
+ midsig:344:<base64 RSA signature>
24
+ ico:31350:<raw PNG binary>
25
+ ugb:15608334:<gzip compressed tar>
26
+ obj2:10204:<hex hash chain>
27
+ ```
28
+
29
+ Each field follows the pattern `key:length:value` where `length` is the byte count of `value`.
30
+
31
+ ## Installation
32
+
33
+ ```bash
34
+ pip install upk-tool
35
+ ```
36
+
37
+ For signing support:
38
+ ```bash
39
+ pip install upk-tool[signing]
40
+ ```
41
+
42
+ ## Usage
43
+
44
+ ### Inspect a .upk file
45
+
46
+ ```bash
47
+ upk-tool verify firmware.upk
48
+ ```
49
+
50
+ Output:
51
+ ```
52
+ File: firmware.upk
53
+ Fields: 8
54
+ filesig: 344 bytes
55
+ userpub: 392 bytes
56
+ usersig: 344 bytes
57
+ midpub: 392 bytes
58
+ midsig: 344 bytes
59
+ ico: 31350 bytes
60
+ ugb: 15608334 bytes
61
+ obj2: 10204 bytes
62
+
63
+ ugb contents (gzip tar): 2 entries
64
+ uninstall.sh (657 bytes)
65
+ com.ugreen.comic.ugb (15602856 bytes)
66
+ -> com.ugreen.comic.ugb: XZ -> tar with 64 files
67
+
68
+ Signature verification:
69
+ filesig: 256 bytes (RSA-2048)
70
+ usersig: 256 bytes (RSA-2048)
71
+ midsig: 256 bytes (RSA-2048)
72
+ userpub: 294 bytes DER
73
+ midpub: 294 bytes DER
74
+ ```
75
+
76
+ ### Extract a .upk file
77
+
78
+ ```bash
79
+ upk-tool extract firmware.upk -o extracted/
80
+ ```
81
+
82
+ This will:
83
+ 1. Save all raw fields to the output directory
84
+ 2. Create `metadata.json` with field metadata
85
+ 3. Decompress the `ugb` payload (gzip → tar)
86
+ 4. Decompress inner `.ugb` files (XZ → tar)
87
+ 5. Extract the final app contents
88
+
89
+ Directory structure:
90
+ ```
91
+ extracted/
92
+ ├── metadata.json
93
+ ├── ico.bin
94
+ ├── ugb.bin
95
+ └── ugb_contents/
96
+ ├── uninstall.sh
97
+ ├── com.ugreen.app.ugb
98
+ ├── com.ugreen.app_files.tar
99
+ └── com.ugreen.app_app/
100
+ ├── sbin/
101
+ │ └── my_service
102
+ ├── config.json
103
+ ├── www/
104
+ │ └── assets/
105
+ └── ...
106
+ ```
107
+
108
+ ### Create a .upk file
109
+
110
+ ```bash
111
+ upk-tool create ./my_app/ -o my_app.upk --icon icon.png --name myapp
112
+ ```
113
+
114
+ Options:
115
+ - `--icon` - PNG icon file (optional, uses 1x1 placeholder if omitted)
116
+ - `--name` - App name used in the package (default: `app`)
117
+ - `--uninstall` - Custom uninstall script (optional)
118
+ - `--user-key` - RSA private key for user signing (PEM format)
119
+ - `--mfg-key` - RSA private key for manufacturer signing (PEM format)
120
+
121
+ ### Create a signed .upk file
122
+
123
+ ```bash
124
+ # Generate keys (for testing)
125
+ openssl genrsa -out user_key.pem 2048
126
+ openssl genrsa -out mfg_key.pem 2048
127
+
128
+ # Create signed package
129
+ upk-tool create ./my_app/ \
130
+ -o my_app.upk \
131
+ --icon icon.png \
132
+ --name myapp \
133
+ --user-key user_key.pem \
134
+ --mfg-key mfg_key.pem
135
+ ```
136
+
137
+ > **Note**: Without real UGREEN private keys, the package will have valid structure but signatures won't verify on actual hardware.
138
+
139
+ ## Python API
140
+
141
+ ```python
142
+ from upk_tool import parse_upk, extract_upk, create_upk, verify_upk
143
+
144
+ # Parse and inspect
145
+ fields = parse_upk("firmware.upk")
146
+ print(fields.keys()) # ['filesig', 'userpub', 'usersig', ...]
147
+
148
+ # Extract
149
+ extract_upk("firmware.upk", "output_dir/")
150
+
151
+ # Create
152
+ create_upk(
153
+ app_dir="./my_app/",
154
+ output_path="output.upk",
155
+ icon_path="icon.png",
156
+ app_name="myapp",
157
+ )
158
+
159
+ # Verify/inspect
160
+ verify_upk("firmware.upk")
161
+ ```
162
+
163
+ ## Development
164
+
165
+ ```bash
166
+ # Clone and install in development mode
167
+ git clone https://github.com/anomalyco/oc_analyze_ugreen_upk_package_file.git
168
+ cd oc_analyze_ugreen_upk_package_file
169
+ pip install -e ".[dev]"
170
+
171
+ # Run tests
172
+ pytest
173
+
174
+ # Build package
175
+ python -m build
176
+
177
+ # Upload to PyPI
178
+ twine upload dist/*
179
+ ```
180
+
181
+ ## License
182
+
183
+ MIT
@@ -0,0 +1,50 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "upk-tool"
7
+ version = "0.1.0"
8
+ description = "Inspect, extract, and create UGREEN NAS .upk package files"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ requires-python = ">=3.8"
12
+ authors = [
13
+ { name = "UGREEN UPK Tool Contributors" },
14
+ ]
15
+ keywords = ["ugreen", "nas", "upk", "package", "firmware"]
16
+ classifiers = [
17
+ "Development Status :: 4 - Beta",
18
+ "Environment :: Console",
19
+ "Intended Audience :: Developers",
20
+ "License :: OSI Approved :: MIT License",
21
+ "Operating System :: OS Independent",
22
+ "Programming Language :: Python :: 3",
23
+ "Programming Language :: Python :: 3.8",
24
+ "Programming Language :: Python :: 3.9",
25
+ "Programming Language :: Python :: 3.10",
26
+ "Programming Language :: Python :: 3.11",
27
+ "Programming Language :: Python :: 3.12",
28
+ "Topic :: System :: Archiving :: Packaging",
29
+ "Topic :: Utilities",
30
+ ]
31
+ dependencies = []
32
+
33
+ [project.optional-dependencies]
34
+ signing = ["cryptography>=3.0"]
35
+ dev = [
36
+ "pytest>=7.0",
37
+ "build>=1.0",
38
+ "twine>=4.0",
39
+ ]
40
+
41
+ [project.scripts]
42
+ upk-tool = "upk_tool.cli:main"
43
+ upk_tool = "upk_tool.cli:main"
44
+
45
+ [project.urls]
46
+ Homepage = "https://github.com/fuho/upk-tool"
47
+ Issues = "https://github.com/fuho/upk-tool/issues"
48
+
49
+ [tool.hatch.build.targets.wheel]
50
+ packages = ["upk_tool"]
@@ -0,0 +1,27 @@
1
+ """
2
+ upk_tool - UGREEN-PKG-V2-FORMAT (.upk) packer/unpacker
3
+
4
+ A tool for inspecting, extracting, and creating UGREEN NAS
5
+ application package files (.upk).
6
+ """
7
+
8
+ __version__ = "0.1.0"
9
+ __author__ = "UGREEN UPK Tool Contributors"
10
+
11
+ from .core import (
12
+ MAGIC,
13
+ parse_upk,
14
+ serialize,
15
+ extract_upk,
16
+ create_upk,
17
+ verify_upk,
18
+ )
19
+
20
+ __all__ = [
21
+ "MAGIC",
22
+ "parse_upk",
23
+ "serialize",
24
+ "extract_upk",
25
+ "create_upk",
26
+ "verify_upk",
27
+ ]
@@ -0,0 +1,3 @@
1
+ from .cli import main
2
+
3
+ main()
@@ -0,0 +1,63 @@
1
+ import argparse
2
+ from .core import extract_upk, create_upk, verify_upk
3
+
4
+
5
+ def main():
6
+ parser = argparse.ArgumentParser(
7
+ prog="upk_tool",
8
+ description="UGREEN-PKG-V2-FORMAT (.upk) packer/unpacker",
9
+ formatter_class=argparse.RawDescriptionHelpFormatter,
10
+ )
11
+ parser.add_argument(
12
+ "--version",
13
+ action="version",
14
+ version=f"%(prog)s {_get_version()}",
15
+ )
16
+ sub = parser.add_subparsers(dest="command")
17
+
18
+ p_extract = sub.add_parser("extract", help="Extract a .upk file")
19
+ p_extract.add_argument("input", help="Input .upk file")
20
+ p_extract.add_argument(
21
+ "-o", "--output", default="extracted", help="Output directory (default: extracted)"
22
+ )
23
+
24
+ p_create = sub.add_parser("create", help="Create a .upk file")
25
+ p_create.add_argument("app_dir", help="Directory with app files to package")
26
+ p_create.add_argument(
27
+ "-o", "--output", default="output.upk", help="Output .upk file (default: output.upk)"
28
+ )
29
+ p_create.add_argument("--icon", help="PNG icon file")
30
+ p_create.add_argument("--user-key", help="User RSA private key (PEM)")
31
+ p_create.add_argument("--mfg-key", help="Manufacturer RSA private key (PEM)")
32
+ p_create.add_argument("--uninstall", help="Uninstall script")
33
+ p_create.add_argument("--name", default="app", help="App name (default: app)")
34
+
35
+ p_verify = sub.add_parser("verify", help="Verify/inspect a .upk file")
36
+ p_verify.add_argument("input", help="Input .upk file")
37
+
38
+ args = parser.parse_args()
39
+
40
+ if args.command == "extract":
41
+ extract_upk(args.input, args.output)
42
+ elif args.command == "create":
43
+ create_upk(
44
+ app_dir=args.app_dir,
45
+ icon_path=args.icon,
46
+ user_key_path=args.user_key,
47
+ mfg_key_path=args.mfg_key,
48
+ uninstall_script=args.uninstall,
49
+ app_name=args.name,
50
+ output_path=args.output,
51
+ )
52
+ elif args.command == "verify":
53
+ verify_upk(args.input)
54
+ else:
55
+ parser.print_help()
56
+
57
+
58
+ def _get_version():
59
+ try:
60
+ from . import __version__
61
+ return __version__
62
+ except ImportError:
63
+ return "unknown"