spsdk-el2go 0.2.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.
- spsdk_el2go-0.2.0/LICENSE +32 -0
- spsdk_el2go-0.2.0/MANIFEST.in +7 -0
- spsdk_el2go-0.2.0/PKG-INFO +113 -0
- spsdk_el2go-0.2.0/README.md +87 -0
- spsdk_el2go-0.2.0/pyproject.toml +125 -0
- spsdk_el2go-0.2.0/setup.cfg +4 -0
- spsdk_el2go-0.2.0/spsdk_el2go/__init__.py +16 -0
- spsdk_el2go-0.2.0/spsdk_el2go/cli.py +117 -0
- spsdk_el2go-0.2.0/spsdk_el2go/client.py +263 -0
- spsdk_el2go-0.2.0/spsdk_el2go/key_derivation_provider.py +33 -0
- spsdk_el2go-0.2.0/spsdk_el2go/signature_provider.py +47 -0
- spsdk_el2go-0.2.0/spsdk_el2go/utils.py +57 -0
- spsdk_el2go-0.2.0/spsdk_el2go.egg-info/PKG-INFO +113 -0
- spsdk_el2go-0.2.0/spsdk_el2go.egg-info/SOURCES.txt +18 -0
- spsdk_el2go-0.2.0/spsdk_el2go.egg-info/dependency_links.txt +1 -0
- spsdk_el2go-0.2.0/spsdk_el2go.egg-info/entry_points.txt +11 -0
- spsdk_el2go-0.2.0/spsdk_el2go.egg-info/requires.txt +2 -0
- spsdk_el2go-0.2.0/spsdk_el2go.egg-info/top_level.txt +1 -0
- spsdk_el2go-0.2.0/tests/__init__.py +9 -0
- spsdk_el2go-0.2.0/tests/test_integration.py +22 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
BSD 3-Clause "New" or "Revised" License
|
|
4
|
+
|
|
5
|
+
Copyright (c) 2026, NXP
|
|
6
|
+
All rights reserved.
|
|
7
|
+
|
|
8
|
+
Redistribution and use in source and binary forms, with or without modification,
|
|
9
|
+
are permitted provided that the following conditions are met:
|
|
10
|
+
|
|
11
|
+
* Redistributions of source code must retain the above copyright notice, this
|
|
12
|
+
list of conditions and the following disclaimer.
|
|
13
|
+
|
|
14
|
+
* Redistributions in binary form must reproduce the above copyright notice, this
|
|
15
|
+
list of conditions and the following disclaimer in the documentation and/or
|
|
16
|
+
other materials provided with the distribution.
|
|
17
|
+
|
|
18
|
+
* Neither the name of the copyright holder nor the names of its
|
|
19
|
+
contributors may be used to endorse or promote products derived from this
|
|
20
|
+
software without specific prior written permission.
|
|
21
|
+
|
|
22
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
23
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
24
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
25
|
+
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
|
26
|
+
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
27
|
+
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
28
|
+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
|
29
|
+
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
|
30
|
+
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
|
31
|
+
OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
32
|
+
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: spsdk-el2go
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: SPSDK Service providers powered by EdgeLock2GO
|
|
5
|
+
Author: NXP
|
|
6
|
+
Maintainer-email: NXP <spsdk@nxp.com>
|
|
7
|
+
License-Expression: BSD-3-Clause
|
|
8
|
+
Project-URL: Homepage, https://github.com/nxp-mcuxpresso/spsdk_plugins/tree/master/el2go
|
|
9
|
+
Project-URL: Issues, https://github.com/nxp-mcuxpresso/spsdk_plugins/issues
|
|
10
|
+
Keywords: NXP,SPSDK,el2go
|
|
11
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Natural Language :: English
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: spsdk>2.1
|
|
24
|
+
Requires-Dist: platformdirs
|
|
25
|
+
Dynamic: license-file
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# EdgeLock 2 GO service providers for SPSDK
|
|
29
|
+
|
|
30
|
+
Package contains multiple SPSDK service providers powered by EL2GO
|
|
31
|
+
|
|
32
|
+
* Free software: BSD-3-Clause
|
|
33
|
+
* Documentation: https://spsdk-el2go.readthedocs.io.
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
Installation
|
|
37
|
+
------------
|
|
38
|
+
|
|
39
|
+
* Activate virtual env, where you have SPSDK
|
|
40
|
+
- if SPSDK is not installed, the latest one will be installed automatically from [PyPI](https://pypi.org/project/spsdk/)
|
|
41
|
+
* `pip install spsdk-el2go`
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
## Signature Provider
|
|
45
|
+
|
|
46
|
+
Everywhere in SPSDK where a signature (private key) is required, a signature provider can be used instead.
|
|
47
|
+
Using this plugin, all signing operations can be delegated to EL2GO. Private keys stay securely stored in EL2GO servers.
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
### Configuration:
|
|
51
|
+
|
|
52
|
+
SPSDK Service providers are configured via a Service configuration string.
|
|
53
|
+
Format: `type=<service_id>[;param1=value1][;param2=value2]`
|
|
54
|
+
In this case the service identifier is `el2go` thus the config string starts with `type=el2go`
|
|
55
|
+
|
|
56
|
+
EL2GO signature provider uses the following configuration options:
|
|
57
|
+
* `url`: Base EL2GO API URL (default: https://api.edgelock2go.com)
|
|
58
|
+
* `api_key`: EL2GO API key. Can be placed in env variable (staring with $) or a file.
|
|
59
|
+
* E.g.: `~/.config/el2go/token.txt`, `$EL2GO_TOKEN`, `$PATH_TO_FILE_WITH_API_KEY`
|
|
60
|
+
* make sure your API token has Code Signing permissions set (both View and Manage are required)
|
|
61
|
+
* `key_set_id` (number): ID of the Key Set in EL2GO server
|
|
62
|
+
* `key_id` (number): ID of the key used for signing operation
|
|
63
|
+
* `config_path`: path to a YAML file containing some or all the config options above
|
|
64
|
+
|
|
65
|
+
These configuration options can be specified in service configuration string
|
|
66
|
+
|
|
67
|
+
type=el2go;url=https://api.edgelock2go.com;api_key=~/.config/el2go/token.txt;key_set_id=4;key_id=1
|
|
68
|
+
|
|
69
|
+
or can be passed via a config file
|
|
70
|
+
|
|
71
|
+
type=el2go;config_path=el2go_config.yaml
|
|
72
|
+
|
|
73
|
+
or a combination of both, overriding/adding options from config file in config string:
|
|
74
|
+
|
|
75
|
+
type=el2go;config_path=el2go_config.yaml;key_id=2
|
|
76
|
+
|
|
77
|
+
To help with setting up the config file, you may use `el2go-helper` application which is bundled with this python package.
|
|
78
|
+
Run `el2go-helper --help` to see more info.
|
|
79
|
+
|
|
80
|
+
### Usage
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
* Use the configuration string in any SPSDK YAML config file, where a private key is normally provided
|
|
84
|
+
* e.g.: in MBI config file: `signer: type=el2go;config_path=el2go_config.yaml`
|
|
85
|
+
|
|
86
|
+
* Use SPSDK application as you would normally do
|
|
87
|
+
* e.g.: `nxpimage mbi export -c mbi_config.yaml`
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
## SB Key derivation provider
|
|
91
|
+
|
|
92
|
+
Derive encryption key used for SB3 and SB4 files from key stored in EdgeLock 2 GO server
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
### Configuration
|
|
96
|
+
|
|
97
|
+
Exactly the same as for Signature Provider
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
### Usage
|
|
101
|
+
|
|
102
|
+
* Use the configuration string in SB3 or SB4 YAML config file, where the CUST_MK_SK/SBKEK is provided
|
|
103
|
+
* e.g.: `containerKeyBlobEncryptionKey: type=el2go;config_path=el2go_config.yaml;key_id=20`
|
|
104
|
+
|
|
105
|
+
* Use SPSDK's nxpimage application as you would normally do
|
|
106
|
+
* e.g.: `nxpimage sb31 export -c sb_config.yaml`
|
|
107
|
+
|
|
108
|
+
## Public key provider
|
|
109
|
+
|
|
110
|
+
Allow to specify any public key in YAML config file as a reference to public key stored in EdgeLock 2 GO
|
|
111
|
+
|
|
112
|
+
**Coming soon**
|
|
113
|
+
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
|
|
2
|
+
# EdgeLock 2 GO service providers for SPSDK
|
|
3
|
+
|
|
4
|
+
Package contains multiple SPSDK service providers powered by EL2GO
|
|
5
|
+
|
|
6
|
+
* Free software: BSD-3-Clause
|
|
7
|
+
* Documentation: https://spsdk-el2go.readthedocs.io.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
Installation
|
|
11
|
+
------------
|
|
12
|
+
|
|
13
|
+
* Activate virtual env, where you have SPSDK
|
|
14
|
+
- if SPSDK is not installed, the latest one will be installed automatically from [PyPI](https://pypi.org/project/spsdk/)
|
|
15
|
+
* `pip install spsdk-el2go`
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
## Signature Provider
|
|
19
|
+
|
|
20
|
+
Everywhere in SPSDK where a signature (private key) is required, a signature provider can be used instead.
|
|
21
|
+
Using this plugin, all signing operations can be delegated to EL2GO. Private keys stay securely stored in EL2GO servers.
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
### Configuration:
|
|
25
|
+
|
|
26
|
+
SPSDK Service providers are configured via a Service configuration string.
|
|
27
|
+
Format: `type=<service_id>[;param1=value1][;param2=value2]`
|
|
28
|
+
In this case the service identifier is `el2go` thus the config string starts with `type=el2go`
|
|
29
|
+
|
|
30
|
+
EL2GO signature provider uses the following configuration options:
|
|
31
|
+
* `url`: Base EL2GO API URL (default: https://api.edgelock2go.com)
|
|
32
|
+
* `api_key`: EL2GO API key. Can be placed in env variable (staring with $) or a file.
|
|
33
|
+
* E.g.: `~/.config/el2go/token.txt`, `$EL2GO_TOKEN`, `$PATH_TO_FILE_WITH_API_KEY`
|
|
34
|
+
* make sure your API token has Code Signing permissions set (both View and Manage are required)
|
|
35
|
+
* `key_set_id` (number): ID of the Key Set in EL2GO server
|
|
36
|
+
* `key_id` (number): ID of the key used for signing operation
|
|
37
|
+
* `config_path`: path to a YAML file containing some or all the config options above
|
|
38
|
+
|
|
39
|
+
These configuration options can be specified in service configuration string
|
|
40
|
+
|
|
41
|
+
type=el2go;url=https://api.edgelock2go.com;api_key=~/.config/el2go/token.txt;key_set_id=4;key_id=1
|
|
42
|
+
|
|
43
|
+
or can be passed via a config file
|
|
44
|
+
|
|
45
|
+
type=el2go;config_path=el2go_config.yaml
|
|
46
|
+
|
|
47
|
+
or a combination of both, overriding/adding options from config file in config string:
|
|
48
|
+
|
|
49
|
+
type=el2go;config_path=el2go_config.yaml;key_id=2
|
|
50
|
+
|
|
51
|
+
To help with setting up the config file, you may use `el2go-helper` application which is bundled with this python package.
|
|
52
|
+
Run `el2go-helper --help` to see more info.
|
|
53
|
+
|
|
54
|
+
### Usage
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
* Use the configuration string in any SPSDK YAML config file, where a private key is normally provided
|
|
58
|
+
* e.g.: in MBI config file: `signer: type=el2go;config_path=el2go_config.yaml`
|
|
59
|
+
|
|
60
|
+
* Use SPSDK application as you would normally do
|
|
61
|
+
* e.g.: `nxpimage mbi export -c mbi_config.yaml`
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
## SB Key derivation provider
|
|
65
|
+
|
|
66
|
+
Derive encryption key used for SB3 and SB4 files from key stored in EdgeLock 2 GO server
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
### Configuration
|
|
70
|
+
|
|
71
|
+
Exactly the same as for Signature Provider
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
### Usage
|
|
75
|
+
|
|
76
|
+
* Use the configuration string in SB3 or SB4 YAML config file, where the CUST_MK_SK/SBKEK is provided
|
|
77
|
+
* e.g.: `containerKeyBlobEncryptionKey: type=el2go;config_path=el2go_config.yaml;key_id=20`
|
|
78
|
+
|
|
79
|
+
* Use SPSDK's nxpimage application as you would normally do
|
|
80
|
+
* e.g.: `nxpimage sb31 export -c sb_config.yaml`
|
|
81
|
+
|
|
82
|
+
## Public key provider
|
|
83
|
+
|
|
84
|
+
Allow to specify any public key in YAML config file as a reference to public key stored in EdgeLock 2 GO
|
|
85
|
+
|
|
86
|
+
**Coming soon**
|
|
87
|
+
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools >= 61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[tool.setuptools]
|
|
6
|
+
packages = ["spsdk_el2go"]
|
|
7
|
+
|
|
8
|
+
[tool.setuptools.dynamic]
|
|
9
|
+
version = { attr = "spsdk_el2go.__version__" }
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
[project]
|
|
13
|
+
dynamic = ["version"]
|
|
14
|
+
name = "spsdk-el2go"
|
|
15
|
+
description = "SPSDK Service providers powered by EdgeLock2GO"
|
|
16
|
+
|
|
17
|
+
requires-python = ">= 3.10"
|
|
18
|
+
dependencies = ["spsdk>2.1", "platformdirs"]
|
|
19
|
+
authors = [{ name = "NXP" }]
|
|
20
|
+
maintainers = [{ name = "NXP", email = "spsdk@nxp.com" }]
|
|
21
|
+
readme = { file = "README.md", content-type = "text/markdown" }
|
|
22
|
+
license = "BSD-3-Clause"
|
|
23
|
+
license-files = ["LICENSE"]
|
|
24
|
+
|
|
25
|
+
keywords = ["NXP", "SPSDK", "el2go"]
|
|
26
|
+
classifiers = [
|
|
27
|
+
"Development Status :: 2 - Pre-Alpha",
|
|
28
|
+
"Intended Audience :: Developers",
|
|
29
|
+
"Natural Language :: English",
|
|
30
|
+
"Programming Language :: Python :: 3",
|
|
31
|
+
"Programming Language :: Python :: 3.10",
|
|
32
|
+
"Programming Language :: Python :: 3.11",
|
|
33
|
+
"Programming Language :: Python :: 3.12",
|
|
34
|
+
"Programming Language :: Python :: 3.13",
|
|
35
|
+
"Programming Language :: Python :: 3.14",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
[project.scripts]
|
|
39
|
+
"el2go-helper" = "spsdk_el2go.cli:main"
|
|
40
|
+
|
|
41
|
+
[project.entry-points]
|
|
42
|
+
"spsdk.sp" = { el2go = "spsdk_el2go.signature_provider" }
|
|
43
|
+
"spsdk.sb31kdp" = { el2go = "spsdk_el2go.key_derivation_provider" }
|
|
44
|
+
pyinstaller40 = { hook-dirs = "spsdk_el2go.__pyinstaller:get_hook_dirs" }
|
|
45
|
+
|
|
46
|
+
[project.urls]
|
|
47
|
+
Homepage = "https://github.com/nxp-mcuxpresso/spsdk_plugins/tree/master/el2go"
|
|
48
|
+
Issues = "https://github.com/nxp-mcuxpresso/spsdk_plugins/issues"
|
|
49
|
+
|
|
50
|
+
[tool.bumpversion]
|
|
51
|
+
current_version = "0.2.0"
|
|
52
|
+
|
|
53
|
+
[[tool.bumpversion.files]]
|
|
54
|
+
filename = "spsdk_el2go/__init__.py"
|
|
55
|
+
search = '__version__ = "{current_version}"'
|
|
56
|
+
replace = '__version__ = "{new_version}"'
|
|
57
|
+
|
|
58
|
+
[tool.pytest.ini_options]
|
|
59
|
+
testpaths = ["tests"]
|
|
60
|
+
junit_family = "xunit1"
|
|
61
|
+
|
|
62
|
+
[tool.black]
|
|
63
|
+
line-length = 100
|
|
64
|
+
target-version = ["py310", "py311", "py312", "py313", "py314"]
|
|
65
|
+
include = '\.pyi?$'
|
|
66
|
+
|
|
67
|
+
[tool.isort]
|
|
68
|
+
multi_line_output = 3
|
|
69
|
+
include_trailing_comma = true
|
|
70
|
+
force_grid_wrap = 0
|
|
71
|
+
use_parentheses = true
|
|
72
|
+
ensure_newline_before_comments = true
|
|
73
|
+
line_length = 100
|
|
74
|
+
|
|
75
|
+
[tool.mypy]
|
|
76
|
+
disallow_untyped_defs = true
|
|
77
|
+
ignore_missing_imports = true
|
|
78
|
+
warn_unused_ignores = true
|
|
79
|
+
|
|
80
|
+
[tool.pydocstyle]
|
|
81
|
+
convention = "google"
|
|
82
|
+
add_ignore = "D105,D301"
|
|
83
|
+
match = '(?!test_|__version__).*\.py'
|
|
84
|
+
|
|
85
|
+
[tool.pylint]
|
|
86
|
+
format = { max-line-length = 120 }
|
|
87
|
+
"messages control" = { disable = ["logging-fstring-interpolation"] }
|
|
88
|
+
|
|
89
|
+
[tool.bandit]
|
|
90
|
+
skips = ["B101"]
|
|
91
|
+
|
|
92
|
+
[tool.ruff]
|
|
93
|
+
line-length = 100
|
|
94
|
+
|
|
95
|
+
[tool.ruff.lint]
|
|
96
|
+
ignore = ["EXE001", "I001", "N999", "UP009"]
|
|
97
|
+
|
|
98
|
+
[tool.nxp_codecheck]
|
|
99
|
+
git_parent_branch = "origin/master"
|
|
100
|
+
output_directory = "reports"
|
|
101
|
+
default_check_paths = ["spsdk_el2go"]
|
|
102
|
+
checkers = [
|
|
103
|
+
{ "pytest" = { kwargs = { disable_xdist = true } } },
|
|
104
|
+
{ "gitcov" = { info_only = true } },
|
|
105
|
+
"pylint",
|
|
106
|
+
"mypy",
|
|
107
|
+
# "dependencies",
|
|
108
|
+
"pydocstyle",
|
|
109
|
+
"ruff",
|
|
110
|
+
{ "radon_c" = { info_only = true } },
|
|
111
|
+
"radon_d",
|
|
112
|
+
"black",
|
|
113
|
+
"isort",
|
|
114
|
+
"copyright",
|
|
115
|
+
"py_headers",
|
|
116
|
+
{ "bandit" = { info_only = true } },
|
|
117
|
+
]
|
|
118
|
+
|
|
119
|
+
[tool.release-tools]
|
|
120
|
+
disabled = ['docs', 'scr']
|
|
121
|
+
|
|
122
|
+
[tool.release-tools.blackduck_sca]
|
|
123
|
+
project_name = "spsdk_el2go"
|
|
124
|
+
include_detector_types = ["pip"]
|
|
125
|
+
sbom_location = "SBOM-spsdk_el2go.spdx.json"
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: UTF-8 -*-
|
|
3
|
+
#
|
|
4
|
+
# Copyright 2026 NXP
|
|
5
|
+
#
|
|
6
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
|
7
|
+
"""Top-level package for el2go_signature_provider."""
|
|
8
|
+
|
|
9
|
+
__author__ = """NXP"""
|
|
10
|
+
__email__ = "spsdk@nxp.com"
|
|
11
|
+
__version__ = "0.2.0"
|
|
12
|
+
|
|
13
|
+
from .key_derivation_provider import EL2GoKDP
|
|
14
|
+
from .signature_provider import EL2GoSP
|
|
15
|
+
|
|
16
|
+
__all__ = ["EL2GoKDP", "EL2GoSP"]
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: UTF-8 -*-
|
|
3
|
+
#
|
|
4
|
+
# Copyright 2026 NXP
|
|
5
|
+
#
|
|
6
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
|
7
|
+
"""Helper application for EL2GO Service Providers."""
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
import sys
|
|
11
|
+
|
|
12
|
+
import click
|
|
13
|
+
|
|
14
|
+
from spsdk_el2go.client import EL2GOServiceClient, KeyInfo
|
|
15
|
+
from spsdk_el2go.utils import load_config
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@click.group()
|
|
19
|
+
def main() -> None:
|
|
20
|
+
"""EL2GO CLI tool."""
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@main.command(name="download-public-key", no_args_is_help=True)
|
|
24
|
+
@click.option(
|
|
25
|
+
"-c",
|
|
26
|
+
"--config",
|
|
27
|
+
type=click.Path(exists=True),
|
|
28
|
+
required=True,
|
|
29
|
+
help="Path to the configuration file.",
|
|
30
|
+
)
|
|
31
|
+
@click.option(
|
|
32
|
+
"-ks",
|
|
33
|
+
"--key-set-id",
|
|
34
|
+
type=int,
|
|
35
|
+
help="Key set ID for the public key to download. (override config file value)",
|
|
36
|
+
)
|
|
37
|
+
@click.option(
|
|
38
|
+
"-k",
|
|
39
|
+
"--key-id",
|
|
40
|
+
type=int,
|
|
41
|
+
help="Key ID for the public key to download. (override config file value)",
|
|
42
|
+
)
|
|
43
|
+
@click.option(
|
|
44
|
+
"-o",
|
|
45
|
+
"--output",
|
|
46
|
+
type=click.Path(),
|
|
47
|
+
required=True,
|
|
48
|
+
help="Path to save the downloaded public key.",
|
|
49
|
+
)
|
|
50
|
+
@click.option(
|
|
51
|
+
"-fd",
|
|
52
|
+
"--force-download",
|
|
53
|
+
is_flag=True,
|
|
54
|
+
default=False,
|
|
55
|
+
help="Force download even if cached key exists.",
|
|
56
|
+
)
|
|
57
|
+
def download_public_key(
|
|
58
|
+
config: str, output: str, force_download: bool, key_id: int | None, key_set_id: int | None
|
|
59
|
+
) -> None:
|
|
60
|
+
"""Download public key from EL2GO service."""
|
|
61
|
+
click.echo("Downloading public key from EL2GO service...")
|
|
62
|
+
|
|
63
|
+
config_data = load_config(config)
|
|
64
|
+
client = EL2GOServiceClient.from_config(config_data)
|
|
65
|
+
key_set_id = key_set_id or config_data.get_int("key_set_id")
|
|
66
|
+
key_id = key_id or config_data.get_int("key_id")
|
|
67
|
+
key_info = KeyInfo(key_set_id=key_set_id, key_id=key_id)
|
|
68
|
+
public_key = client.download_public_key(key_info=key_info, force_download=force_download)
|
|
69
|
+
|
|
70
|
+
os.makedirs(os.path.dirname(output) or ".", exist_ok=True)
|
|
71
|
+
|
|
72
|
+
with open(output, "wb") as f:
|
|
73
|
+
f.write(public_key)
|
|
74
|
+
click.echo(f"Public key downloaded: {output}")
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@main.command(name="get-template", no_args_is_help=True)
|
|
78
|
+
@click.option(
|
|
79
|
+
"-o",
|
|
80
|
+
"--output",
|
|
81
|
+
type=click.Path(),
|
|
82
|
+
required=True,
|
|
83
|
+
help="Path to save the configuration file template.",
|
|
84
|
+
)
|
|
85
|
+
def get_template(output: str) -> None:
|
|
86
|
+
"""Generate a configuration file template for EL2GO service."""
|
|
87
|
+
click.echo("Generating configuration file template...")
|
|
88
|
+
|
|
89
|
+
template = """
|
|
90
|
+
# Configuration file for EL2GO Code Signing service
|
|
91
|
+
|
|
92
|
+
# Base URL of the EL2GO service
|
|
93
|
+
url: https://api.edgelock2go.com
|
|
94
|
+
|
|
95
|
+
# API key for authenticating with the EL2GO service
|
|
96
|
+
# Options how to provide the API key:
|
|
97
|
+
# 1. Directly in the configuration file (not recommended for security reasons)
|
|
98
|
+
# 2. Environment variable (starting with $ e.g.: $MY_EL2GO_API_KEY)
|
|
99
|
+
# 3. Path to a file containing the API key (e.g.: ~/.el2go/api_key)
|
|
100
|
+
# 4. Environment variable pointing to a file containing the API key (e.g.: $MY_EL2GO_API_KEY_FILE)
|
|
101
|
+
api_key: ~/.el2go/api_key
|
|
102
|
+
|
|
103
|
+
# Key set ID and key ID for sign/encryption operations
|
|
104
|
+
# IDs are shown in the EL2GO web interface under "Code Signing" section
|
|
105
|
+
key_set_id: 12
|
|
106
|
+
key_id: 123
|
|
107
|
+
"""
|
|
108
|
+
|
|
109
|
+
os.makedirs(os.path.dirname(output) or ".", exist_ok=True)
|
|
110
|
+
|
|
111
|
+
with open(output, "w", encoding="utf-8") as f:
|
|
112
|
+
f.write(template)
|
|
113
|
+
click.echo(f"Configuration file template generated: {output}")
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
if __name__ == "__main__":
|
|
117
|
+
sys.exit(main())
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
#
|
|
4
|
+
# Copyright 2026 NXP
|
|
5
|
+
#
|
|
6
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
|
7
|
+
"""Module for EL2GO Service Client."""
|
|
8
|
+
|
|
9
|
+
import base64
|
|
10
|
+
import logging
|
|
11
|
+
import uuid
|
|
12
|
+
|
|
13
|
+
from spsdk.crypto.hash import EnumHashAlgorithm, get_hash
|
|
14
|
+
from spsdk.crypto.keys import PublicKey
|
|
15
|
+
from spsdk.el2go.client import EL2GOClient
|
|
16
|
+
from spsdk.utils.config import Config
|
|
17
|
+
|
|
18
|
+
from .utils import KeyInfo, get_public_key_cache_path, load_config
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger("spsdk.el2go_service_client")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
KEY_TYPES = ["ecc_p256", "ecc_p384", "ecc_p512", "rsa_2048", "rsa_3072", "rsa_4096"]
|
|
24
|
+
KEY_TO_HASH_ALGORITHM = {
|
|
25
|
+
"ecc_p256": EnumHashAlgorithm.SHA256,
|
|
26
|
+
"ecc_p384": EnumHashAlgorithm.SHA384,
|
|
27
|
+
"ecc_p512": EnumHashAlgorithm.SHA512,
|
|
28
|
+
"rsa_2048": EnumHashAlgorithm.SHA256,
|
|
29
|
+
"rsa_3072": EnumHashAlgorithm.SHA384,
|
|
30
|
+
"rsa_4096": EnumHashAlgorithm.SHA512,
|
|
31
|
+
}
|
|
32
|
+
KEY_TO_EL2GO_SIGNATURE_ALGORITHM = {
|
|
33
|
+
"ecc_p256": "ECDSA",
|
|
34
|
+
"ecc_p384": "ECDSA",
|
|
35
|
+
"ecc_p512": "ECDSA",
|
|
36
|
+
"rsa_2048": "RSASSA_PKCS_v1_5",
|
|
37
|
+
"rsa_3072": "RSASSA_PKCS_v1_5",
|
|
38
|
+
"rsa_4096": "RSASSA_PKCS_v1_5",
|
|
39
|
+
}
|
|
40
|
+
KEY_TO_SIGNATURE_LENGTH = {
|
|
41
|
+
"ecc_p256": 64,
|
|
42
|
+
"ecc_p384": 96,
|
|
43
|
+
"ecc_p512": 132,
|
|
44
|
+
"rsa_2048": 256,
|
|
45
|
+
"rsa_3072": 384,
|
|
46
|
+
"rsa_4096": 512,
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class EL2GOServiceClient(EL2GOClient):
|
|
51
|
+
"""Extended Client for interacting with the EL2GO services."""
|
|
52
|
+
|
|
53
|
+
def __init__(
|
|
54
|
+
self,
|
|
55
|
+
api_key: str,
|
|
56
|
+
url: str = "https://api.edgelock2go.com",
|
|
57
|
+
timeout: int = 60,
|
|
58
|
+
) -> None:
|
|
59
|
+
"""Initialize EL2GO Service Client.
|
|
60
|
+
|
|
61
|
+
:param api_key: API key for authentication with the EL2GO service.
|
|
62
|
+
:param url: Base URL for the EL2GO API endpoints.
|
|
63
|
+
:param timeout: Timeout in seconds for HTTP requests to the EL2GO service.
|
|
64
|
+
"""
|
|
65
|
+
super().__init__(api_key=api_key, url=url, timeout=timeout)
|
|
66
|
+
|
|
67
|
+
@classmethod
|
|
68
|
+
def from_config(cls, config: Config) -> "EL2GOServiceClient":
|
|
69
|
+
"""Create an instance of EL2GOServiceClient from a configuration object.
|
|
70
|
+
|
|
71
|
+
:param config: Configuration object containing API credentials and settings.
|
|
72
|
+
:return: An instance of EL2GOServiceClient initialized with the provided configuration.
|
|
73
|
+
"""
|
|
74
|
+
api_key = config.load_secret("api_key")
|
|
75
|
+
url = config.get_str("url", "https://api.edgelock2go.com")
|
|
76
|
+
timeout = config.get_int("timeout", 60)
|
|
77
|
+
return cls(
|
|
78
|
+
api_key=api_key,
|
|
79
|
+
url=url,
|
|
80
|
+
timeout=timeout,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
def sign(self, data: bytes, key_info: KeyInfo) -> bytes:
|
|
84
|
+
"""Sign the provided data using the EL2GO Service Client.
|
|
85
|
+
|
|
86
|
+
:param data: Data to be signed, provided as bytes.
|
|
87
|
+
:param key_info: KeyInfo object containing key details for signing.
|
|
88
|
+
|
|
89
|
+
:return: The signature as bytes.
|
|
90
|
+
"""
|
|
91
|
+
logger.info(
|
|
92
|
+
f"Signing data using key_set_id={key_info.key_set_id}, key_id={key_info.key_id}"
|
|
93
|
+
)
|
|
94
|
+
endpoint = f"/api/v1/keysets/{key_info.key_set_id}/keys/{key_info.key_id}/sign-hash"
|
|
95
|
+
logger.debug(f"Signing endpoint: {endpoint}")
|
|
96
|
+
|
|
97
|
+
# at this point, hash should be set already
|
|
98
|
+
assert key_info.hash_algorithm is not None, "Hash algorithm must be specified in KeyInfo."
|
|
99
|
+
|
|
100
|
+
hashed_data = get_hash(data, key_info.hash_algorithm)
|
|
101
|
+
|
|
102
|
+
logger.debug(f"Using hash algorithm: {key_info.hash_algorithm.name}")
|
|
103
|
+
logger.debug(f"Length of pre-hashed data: {len(hashed_data)} bytes")
|
|
104
|
+
logger.debug(f"Data to be signed (pre-hashed): {hashed_data.hex()}")
|
|
105
|
+
|
|
106
|
+
data_to_sign = base64.b64encode(hashed_data).decode()
|
|
107
|
+
json_data = {
|
|
108
|
+
"payload": data_to_sign,
|
|
109
|
+
"signatureAlgorithm": key_info.signature_algorithm,
|
|
110
|
+
}
|
|
111
|
+
logger.debug(f"JSON data to be sent for signing: {json_data}")
|
|
112
|
+
response = self._handle_el2go_request(
|
|
113
|
+
method=self.Method.POST,
|
|
114
|
+
url=endpoint,
|
|
115
|
+
json_data=json_data,
|
|
116
|
+
)
|
|
117
|
+
logger.debug(f"Received response from signing request: {response.json_body}")
|
|
118
|
+
if response.status_code != 200:
|
|
119
|
+
logger.error(f"Failed to sign data: {response.json_body}")
|
|
120
|
+
raise RuntimeError(
|
|
121
|
+
f"Failed to sign data: {response.status_code} - {response.json_body}"
|
|
122
|
+
)
|
|
123
|
+
signature = base64.b64decode(response.json_body["signature"])
|
|
124
|
+
logger.debug(f"Received signature (raw): {signature.hex()}")
|
|
125
|
+
return signature
|
|
126
|
+
|
|
127
|
+
def download_public_key(self, key_info: KeyInfo, force_download: bool = False) -> bytes:
|
|
128
|
+
"""Download the public key associated with the specified key set and key ID.
|
|
129
|
+
|
|
130
|
+
:param key_info: KeyInfo object containing key details for downloading the public key.
|
|
131
|
+
|
|
132
|
+
:param force_download: If True, force download even if cached key exists.
|
|
133
|
+
:return: The public key as bytes.
|
|
134
|
+
"""
|
|
135
|
+
if not force_download:
|
|
136
|
+
key_file_path = get_public_key_cache_path(key_info=key_info)
|
|
137
|
+
if key_file_path.exists():
|
|
138
|
+
logger.info(f"Using cached public key from: {key_file_path}")
|
|
139
|
+
return key_file_path.read_bytes()
|
|
140
|
+
|
|
141
|
+
endpoint = (
|
|
142
|
+
f"/api/v1/keysets/{key_info.key_set_id}/keys/{key_info.key_id}/download-public-key"
|
|
143
|
+
)
|
|
144
|
+
logger.info(
|
|
145
|
+
f"Downloading public key: key_set_id={key_info.key_set_id}, key_id={key_info.key_id}"
|
|
146
|
+
)
|
|
147
|
+
logger.debug(f"Public key download endpoint: {endpoint}")
|
|
148
|
+
|
|
149
|
+
accept_header_backup = self.headers.get("Accept")
|
|
150
|
+
try:
|
|
151
|
+
self.headers["Accept"] = "application/octet-stream"
|
|
152
|
+
|
|
153
|
+
self.correlation_id = str(uuid.uuid4())
|
|
154
|
+
logger.info(f"EL2G-Correlation-ID: {self.correlation_id}")
|
|
155
|
+
self.headers["EL2G-Correlation-ID"] = self.correlation_id
|
|
156
|
+
api_response = super()._handle_request(
|
|
157
|
+
method=self.Method.GET, url=endpoint, param_data=None, json_data=None
|
|
158
|
+
)
|
|
159
|
+
api_response.raise_for_status()
|
|
160
|
+
|
|
161
|
+
# Assuming the public key is returned as a string
|
|
162
|
+
public_key_str = api_response.text
|
|
163
|
+
public_key_bytes = public_key_str.encode()
|
|
164
|
+
|
|
165
|
+
logger.debug(f"Received response from public key request:\n{api_response.text}")
|
|
166
|
+
|
|
167
|
+
key_file_path = get_public_key_cache_path(key_info=key_info)
|
|
168
|
+
key_file_path.write_bytes(public_key_bytes)
|
|
169
|
+
logger.info(f"Public key saved to: {key_file_path}")
|
|
170
|
+
|
|
171
|
+
return public_key_bytes
|
|
172
|
+
|
|
173
|
+
finally:
|
|
174
|
+
if accept_header_backup is not None:
|
|
175
|
+
self.headers["Accept"] = accept_header_backup
|
|
176
|
+
else:
|
|
177
|
+
del self.headers["Accept"]
|
|
178
|
+
|
|
179
|
+
def cmac(self, data: bytes, key_info: KeyInfo) -> bytes:
|
|
180
|
+
"""Compute CMAC for the provided data using the EL2GO Service Client.
|
|
181
|
+
|
|
182
|
+
:param data: Data for which to compute the CMAC, provided as bytes.
|
|
183
|
+
:param key_info: KeyInfo object containing key details for CMAC computation.
|
|
184
|
+
|
|
185
|
+
:return: The computed CMAC as bytes.
|
|
186
|
+
"""
|
|
187
|
+
logger.info(
|
|
188
|
+
f"Computing CMAC for data using key_set_id={key_info.key_set_id}, key_id={key_info.key_id}"
|
|
189
|
+
)
|
|
190
|
+
endpoint = f"/api/v1/keysets/{key_info.key_set_id}/keys/{key_info.key_id}/cmac"
|
|
191
|
+
logger.debug(f"CMAC endpoint: {endpoint}")
|
|
192
|
+
|
|
193
|
+
json_data = {"payload": base64.b64encode(data).decode()}
|
|
194
|
+
logger.debug(f"JSON data to be sent for CMAC computation: {json_data}")
|
|
195
|
+
response = self._handle_el2go_request(
|
|
196
|
+
method=self.Method.POST,
|
|
197
|
+
url=endpoint,
|
|
198
|
+
json_data=json_data,
|
|
199
|
+
)
|
|
200
|
+
logger.debug(f"Received response from CMAC request: {response.json_body}")
|
|
201
|
+
if response.status_code != 200:
|
|
202
|
+
logger.error(f"Failed to compute CMAC: {response.json_body}")
|
|
203
|
+
raise RuntimeError(
|
|
204
|
+
f"Failed to compute CMAC: {response.status_code} - {response.json_body}"
|
|
205
|
+
)
|
|
206
|
+
cmac_value = base64.b64decode(response.json_body["cmacPayload"])
|
|
207
|
+
logger.debug(f"Received CMAC (raw): {cmac_value.hex()}")
|
|
208
|
+
return cmac_value
|
|
209
|
+
|
|
210
|
+
def get_key_info(self, key_set_id: int, key_id: int) -> KeyInfo:
|
|
211
|
+
"""Retrieve the public key type from the EL2GO service.
|
|
212
|
+
|
|
213
|
+
:param key_set_id: ID of the key set.
|
|
214
|
+
:param key_id: ID of the key.
|
|
215
|
+
:return: The public key type as a string.
|
|
216
|
+
"""
|
|
217
|
+
logger.info(f"Retrieving public key type for key_set_id={key_set_id}, key_id={key_id}")
|
|
218
|
+
key_info = KeyInfo(key_set_id=key_set_id, key_id=key_id)
|
|
219
|
+
key_data = self.download_public_key(key_info=key_info)
|
|
220
|
+
public_key = PublicKey.parse(key_data)
|
|
221
|
+
key_type = ""
|
|
222
|
+
|
|
223
|
+
# the mypy issues below shall be fixed in SPSDK
|
|
224
|
+
if "RSA" in repr(public_key):
|
|
225
|
+
key_type = f"rsa_{public_key.key_size}" # type: ignore[attr-defined]
|
|
226
|
+
elif "EC" in repr(public_key):
|
|
227
|
+
key_type = f"ecc_p{public_key.key_size}" # type: ignore[attr-defined]
|
|
228
|
+
if key_type not in KEY_TYPES:
|
|
229
|
+
raise ValueError(f"Invalid key type retrieved: {key_type}. Must be one of {KEY_TYPES}.")
|
|
230
|
+
|
|
231
|
+
hash_algorithm = KEY_TO_HASH_ALGORITHM[key_type]
|
|
232
|
+
signature_algorithm = KEY_TO_EL2GO_SIGNATURE_ALGORITHM[key_type]
|
|
233
|
+
signature_length = KEY_TO_SIGNATURE_LENGTH[key_type]
|
|
234
|
+
|
|
235
|
+
return KeyInfo(
|
|
236
|
+
key_set_id=key_set_id,
|
|
237
|
+
key_id=key_id,
|
|
238
|
+
key_type=key_type,
|
|
239
|
+
hash_algorithm=hash_algorithm,
|
|
240
|
+
signature_algorithm=signature_algorithm,
|
|
241
|
+
signature_length=signature_length,
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def get_client_key_info(
|
|
246
|
+
config_path: str | None = None, full_key_info: bool = True, **kwargs: str
|
|
247
|
+
) -> tuple[EL2GOServiceClient, KeyInfo]:
|
|
248
|
+
"""Get the key information from the EL2GO service based on the provided configuration.
|
|
249
|
+
|
|
250
|
+
:param config_path: Path to the configuration file. If None, an empty configuration is created.
|
|
251
|
+
:param full_key_info: Whether to retrieve full key information.
|
|
252
|
+
:param kwargs: Additional configuration parameters to update the loaded configuration.
|
|
253
|
+
:return: A tuple containing the EL2GOServiceClient and a KeyInfo object.
|
|
254
|
+
"""
|
|
255
|
+
config = load_config(config_path, **kwargs)
|
|
256
|
+
client = EL2GOServiceClient.from_config(config)
|
|
257
|
+
key_set_id = config.get_int("key_set_id")
|
|
258
|
+
key_id = config.get_int("key_id")
|
|
259
|
+
if full_key_info:
|
|
260
|
+
key_info = client.get_key_info(key_set_id=key_set_id, key_id=key_id)
|
|
261
|
+
else:
|
|
262
|
+
key_info = KeyInfo(key_set_id=key_set_id, key_id=key_id)
|
|
263
|
+
return client, key_info
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: UTF-8 -*-
|
|
3
|
+
#
|
|
4
|
+
# Copyright 2026 NXP
|
|
5
|
+
#
|
|
6
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
|
7
|
+
"""Main module for EL2GO SB Key Derivation Provider."""
|
|
8
|
+
|
|
9
|
+
from spsdk.sbfile.utils.key_derivator import SB31KeyDerivator
|
|
10
|
+
|
|
11
|
+
from .client import get_client_key_info
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class EL2GoKDP(SB31KeyDerivator):
|
|
15
|
+
"""Key Derivation Provider based on a remote signing service."""
|
|
16
|
+
|
|
17
|
+
# identifier of this key derivation provider; used in yaml configuration file
|
|
18
|
+
identifier = "el2go"
|
|
19
|
+
|
|
20
|
+
def __init__(self, config_path: str | None = None, **kwargs: str) -> None:
|
|
21
|
+
"""Initialize the EL2GoSP."""
|
|
22
|
+
super().__init__()
|
|
23
|
+
self.client, self.key_info = get_client_key_info(
|
|
24
|
+
config_path=config_path, full_key_info=False, **kwargs
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
def remote_cmac(self, data: bytes) -> bytes:
|
|
28
|
+
"""Calculate CMAC using the implementation-specific method."""
|
|
29
|
+
return self.client.cmac(data=data, key_info=self.key_info)
|
|
30
|
+
|
|
31
|
+
def info(self) -> str:
|
|
32
|
+
"""Provide information about the Key Derivation provider."""
|
|
33
|
+
return f"EL2GO SB key derivator: {self.client.base_url}, {self.key_info}"
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: UTF-8 -*-
|
|
3
|
+
#
|
|
4
|
+
# Copyright 2026 NXP
|
|
5
|
+
#
|
|
6
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
|
7
|
+
"""Main module for EL2GO Signature Provider."""
|
|
8
|
+
|
|
9
|
+
from spsdk.crypto.keys import PublicKey
|
|
10
|
+
from spsdk.crypto.signature_provider import SignatureProvider
|
|
11
|
+
|
|
12
|
+
from .client import get_client_key_info
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class EL2GoSP(SignatureProvider):
|
|
16
|
+
"""Signature Provider based on a remote signing service."""
|
|
17
|
+
|
|
18
|
+
# identifier of this signature provider; used in yaml configuration file
|
|
19
|
+
identifier = "el2go"
|
|
20
|
+
|
|
21
|
+
def __init__(self, config_path: str | None = None, **kwargs: str) -> None:
|
|
22
|
+
"""Initialize the EL2GoSP."""
|
|
23
|
+
super().__init__()
|
|
24
|
+
self.client, self.key_info = get_client_key_info(
|
|
25
|
+
config_path=config_path, full_key_info=True, **kwargs
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
def sign(self, data: bytes) -> bytes:
|
|
29
|
+
"""Return signature for data."""
|
|
30
|
+
signature = self.client.sign(data=data, key_info=self.key_info)
|
|
31
|
+
return signature
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def signature_length(self) -> int:
|
|
35
|
+
"""Return length of the signature."""
|
|
36
|
+
# at this point, signature_length should be set already
|
|
37
|
+
assert self.key_info.signature_length is not None
|
|
38
|
+
return self.key_info.signature_length
|
|
39
|
+
|
|
40
|
+
def verify_public_key(self, public_key: PublicKey) -> bool:
|
|
41
|
+
"""Verify if given public key matches private key."""
|
|
42
|
+
puk = PublicKey.parse(self.client.download_public_key(key_info=self.key_info))
|
|
43
|
+
return puk == public_key
|
|
44
|
+
|
|
45
|
+
def info(self) -> str:
|
|
46
|
+
"""Provide information about the Signature provider."""
|
|
47
|
+
return f"EL2GO SP: {self.client.base_url}, {self.key_info}"
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
#
|
|
4
|
+
# Copyright 2026 NXP
|
|
5
|
+
#
|
|
6
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
|
7
|
+
"""Module for EL2GO Service Client utilities."""
|
|
8
|
+
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
from platformdirs import PlatformDirs
|
|
13
|
+
from spsdk.crypto.hash import EnumHashAlgorithm
|
|
14
|
+
from spsdk.utils.config import Config
|
|
15
|
+
|
|
16
|
+
PLATFORM_DIRS = PlatformDirs("spsdk-el2go", "nxp", ensure_exists=True)
|
|
17
|
+
PUBLIC_KEY_CACHE_DIR = PLATFORM_DIRS.user_cache_path
|
|
18
|
+
PUBLIC_KEY_FILE_TEMPLATE = "public_key_{key_set_id}_{key_id}.pem"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def load_config(config_path: str | None = None, **kwargs: int | str) -> Config:
|
|
22
|
+
"""Load configuration from a file and update it with additional keyword arguments.
|
|
23
|
+
|
|
24
|
+
:param config_path: Path to the configuration file. If None, an empty configuration is created.
|
|
25
|
+
:param kwargs: Additional configuration parameters to update the loaded configuration.
|
|
26
|
+
:return: A Config object containing the loaded and updated configuration.
|
|
27
|
+
"""
|
|
28
|
+
if config_path is None:
|
|
29
|
+
config = Config({})
|
|
30
|
+
else:
|
|
31
|
+
config = Config.create_from_file(config_path)
|
|
32
|
+
config.update(kwargs)
|
|
33
|
+
return config
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass
|
|
37
|
+
class KeyInfo:
|
|
38
|
+
"""Data class to hold key information."""
|
|
39
|
+
|
|
40
|
+
key_set_id: int
|
|
41
|
+
key_id: int
|
|
42
|
+
key_type: str | None = None
|
|
43
|
+
hash_algorithm: EnumHashAlgorithm | None = None
|
|
44
|
+
signature_algorithm: str | None = None
|
|
45
|
+
signature_length: int | None = None
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def get_public_key_cache_path(key_info: KeyInfo) -> Path:
|
|
49
|
+
"""Get the path to the cached public key file.
|
|
50
|
+
|
|
51
|
+
:param key_info: KeyInfo object containing key details.
|
|
52
|
+
:return: Path to the cached public key file as a Path object.
|
|
53
|
+
"""
|
|
54
|
+
key_file_name = PUBLIC_KEY_FILE_TEMPLATE.format(
|
|
55
|
+
key_set_id=key_info.key_set_id, key_id=key_info.key_id
|
|
56
|
+
)
|
|
57
|
+
return PUBLIC_KEY_CACHE_DIR / key_file_name
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: spsdk-el2go
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: SPSDK Service providers powered by EdgeLock2GO
|
|
5
|
+
Author: NXP
|
|
6
|
+
Maintainer-email: NXP <spsdk@nxp.com>
|
|
7
|
+
License-Expression: BSD-3-Clause
|
|
8
|
+
Project-URL: Homepage, https://github.com/nxp-mcuxpresso/spsdk_plugins/tree/master/el2go
|
|
9
|
+
Project-URL: Issues, https://github.com/nxp-mcuxpresso/spsdk_plugins/issues
|
|
10
|
+
Keywords: NXP,SPSDK,el2go
|
|
11
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Natural Language :: English
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: spsdk>2.1
|
|
24
|
+
Requires-Dist: platformdirs
|
|
25
|
+
Dynamic: license-file
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# EdgeLock 2 GO service providers for SPSDK
|
|
29
|
+
|
|
30
|
+
Package contains multiple SPSDK service providers powered by EL2GO
|
|
31
|
+
|
|
32
|
+
* Free software: BSD-3-Clause
|
|
33
|
+
* Documentation: https://spsdk-el2go.readthedocs.io.
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
Installation
|
|
37
|
+
------------
|
|
38
|
+
|
|
39
|
+
* Activate virtual env, where you have SPSDK
|
|
40
|
+
- if SPSDK is not installed, the latest one will be installed automatically from [PyPI](https://pypi.org/project/spsdk/)
|
|
41
|
+
* `pip install spsdk-el2go`
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
## Signature Provider
|
|
45
|
+
|
|
46
|
+
Everywhere in SPSDK where a signature (private key) is required, a signature provider can be used instead.
|
|
47
|
+
Using this plugin, all signing operations can be delegated to EL2GO. Private keys stay securely stored in EL2GO servers.
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
### Configuration:
|
|
51
|
+
|
|
52
|
+
SPSDK Service providers are configured via a Service configuration string.
|
|
53
|
+
Format: `type=<service_id>[;param1=value1][;param2=value2]`
|
|
54
|
+
In this case the service identifier is `el2go` thus the config string starts with `type=el2go`
|
|
55
|
+
|
|
56
|
+
EL2GO signature provider uses the following configuration options:
|
|
57
|
+
* `url`: Base EL2GO API URL (default: https://api.edgelock2go.com)
|
|
58
|
+
* `api_key`: EL2GO API key. Can be placed in env variable (staring with $) or a file.
|
|
59
|
+
* E.g.: `~/.config/el2go/token.txt`, `$EL2GO_TOKEN`, `$PATH_TO_FILE_WITH_API_KEY`
|
|
60
|
+
* make sure your API token has Code Signing permissions set (both View and Manage are required)
|
|
61
|
+
* `key_set_id` (number): ID of the Key Set in EL2GO server
|
|
62
|
+
* `key_id` (number): ID of the key used for signing operation
|
|
63
|
+
* `config_path`: path to a YAML file containing some or all the config options above
|
|
64
|
+
|
|
65
|
+
These configuration options can be specified in service configuration string
|
|
66
|
+
|
|
67
|
+
type=el2go;url=https://api.edgelock2go.com;api_key=~/.config/el2go/token.txt;key_set_id=4;key_id=1
|
|
68
|
+
|
|
69
|
+
or can be passed via a config file
|
|
70
|
+
|
|
71
|
+
type=el2go;config_path=el2go_config.yaml
|
|
72
|
+
|
|
73
|
+
or a combination of both, overriding/adding options from config file in config string:
|
|
74
|
+
|
|
75
|
+
type=el2go;config_path=el2go_config.yaml;key_id=2
|
|
76
|
+
|
|
77
|
+
To help with setting up the config file, you may use `el2go-helper` application which is bundled with this python package.
|
|
78
|
+
Run `el2go-helper --help` to see more info.
|
|
79
|
+
|
|
80
|
+
### Usage
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
* Use the configuration string in any SPSDK YAML config file, where a private key is normally provided
|
|
84
|
+
* e.g.: in MBI config file: `signer: type=el2go;config_path=el2go_config.yaml`
|
|
85
|
+
|
|
86
|
+
* Use SPSDK application as you would normally do
|
|
87
|
+
* e.g.: `nxpimage mbi export -c mbi_config.yaml`
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
## SB Key derivation provider
|
|
91
|
+
|
|
92
|
+
Derive encryption key used for SB3 and SB4 files from key stored in EdgeLock 2 GO server
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
### Configuration
|
|
96
|
+
|
|
97
|
+
Exactly the same as for Signature Provider
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
### Usage
|
|
101
|
+
|
|
102
|
+
* Use the configuration string in SB3 or SB4 YAML config file, where the CUST_MK_SK/SBKEK is provided
|
|
103
|
+
* e.g.: `containerKeyBlobEncryptionKey: type=el2go;config_path=el2go_config.yaml;key_id=20`
|
|
104
|
+
|
|
105
|
+
* Use SPSDK's nxpimage application as you would normally do
|
|
106
|
+
* e.g.: `nxpimage sb31 export -c sb_config.yaml`
|
|
107
|
+
|
|
108
|
+
## Public key provider
|
|
109
|
+
|
|
110
|
+
Allow to specify any public key in YAML config file as a reference to public key stored in EdgeLock 2 GO
|
|
111
|
+
|
|
112
|
+
**Coming soon**
|
|
113
|
+
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
MANIFEST.in
|
|
3
|
+
README.md
|
|
4
|
+
pyproject.toml
|
|
5
|
+
spsdk_el2go/__init__.py
|
|
6
|
+
spsdk_el2go/cli.py
|
|
7
|
+
spsdk_el2go/client.py
|
|
8
|
+
spsdk_el2go/key_derivation_provider.py
|
|
9
|
+
spsdk_el2go/signature_provider.py
|
|
10
|
+
spsdk_el2go/utils.py
|
|
11
|
+
spsdk_el2go.egg-info/PKG-INFO
|
|
12
|
+
spsdk_el2go.egg-info/SOURCES.txt
|
|
13
|
+
spsdk_el2go.egg-info/dependency_links.txt
|
|
14
|
+
spsdk_el2go.egg-info/entry_points.txt
|
|
15
|
+
spsdk_el2go.egg-info/requires.txt
|
|
16
|
+
spsdk_el2go.egg-info/top_level.txt
|
|
17
|
+
tests/__init__.py
|
|
18
|
+
tests/test_integration.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
spsdk_el2go
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: UTF-8 -*-
|
|
3
|
+
#
|
|
4
|
+
# Copyright 2026 NXP
|
|
5
|
+
#
|
|
6
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
|
7
|
+
"""Tests for `spsdk_el2go` package."""
|
|
8
|
+
|
|
9
|
+
from spsdk.crypto.signature_provider import SignatureProvider
|
|
10
|
+
from spsdk.sbfile.utils.key_derivator import SB31KeyDerivator
|
|
11
|
+
|
|
12
|
+
from spsdk_el2go import EL2GoKDP, EL2GoSP
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_sp_registration() -> None:
|
|
16
|
+
"""Test whether EL2GoSP got picked up by SPSDK."""
|
|
17
|
+
assert EL2GoSP.identifier in SignatureProvider.get_types()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_kdp_registration() -> None:
|
|
21
|
+
"""Test whether EL2GoKDP got picked up by SPSDK."""
|
|
22
|
+
assert EL2GoKDP.identifier in SB31KeyDerivator.get_types()
|