mirror of
https://github.com/mtrojnar/osslsigncode
synced 2026-06-08 16:13:39 +00:00
Compare commits
102 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bf209e0fc8 | |||
| 68a6826cd1 | |||
| ea5d15862d | |||
| bdde95635f | |||
| d0ef178a9a | |||
| e126ab3e4a | |||
| a1fb6600fb | |||
| 8227c68ceb | |||
| c988b48063 | |||
| 6cf70b4af2 | |||
| 97a9ade6ec | |||
| f2f33bb131 | |||
| 202b2c2866 | |||
| 2a5409b7c4 | |||
| 87bce8e372 | |||
| f7ace57c81 | |||
| 92f8761b47 | |||
| 09d3312fd9 | |||
| 9d02a20aec | |||
| f190ec5d87 | |||
| 4b30d6be28 | |||
| fac8164622 | |||
| cbee1e723c | |||
| f90327df09 | |||
| f3a590be69 | |||
| 6631a5f10b | |||
| 9924f0c085 | |||
| 7d85ac5f04 | |||
| feebbcd4d9 | |||
| d787541107 | |||
| 6390ae2746 | |||
| a472d7fbff | |||
| 27172a07ca | |||
| d77ddb9443 | |||
| 988f72249b | |||
| c23f92ca68 | |||
| 842bd94aaf | |||
| 1d72c3da8c | |||
| d792e8d0db | |||
| bbdfc1d98a | |||
| 5ac11e9f58 | |||
| 55541c6ace | |||
| 8329a14f8b | |||
| 343b0af1fe | |||
| d440f32780 | |||
| fb082942d2 | |||
| 025e808c01 | |||
| 23b6d7782c | |||
| 4c3a1e887c | |||
| 97ee163e31 | |||
| dfc3e46a77 | |||
| ff9a6d3593 | |||
| e81b08e02d | |||
| 0c85d54800 | |||
| 772bc22c94 | |||
| d65a2b5286 | |||
| a3fcf41e1a | |||
| e00caac3db | |||
| dd9b81281f | |||
| 6b56aef073 | |||
| 52bfff5756 | |||
| 4d52e9cc4b | |||
| 3292b02650 | |||
| 50c23daa4c | |||
| 9b7dae4572 | |||
| 62438908cb | |||
| 829e770250 | |||
| a6c7c25dae | |||
| 10ca3a06ea | |||
| 9ea7e85468 | |||
| 68e8845ef1 | |||
| 475ea95ba3 | |||
| d352dcc1a5 | |||
| 7734382436 | |||
| d425d8bf25 | |||
| 4568c890cc | |||
| 4bd167a8be | |||
| e7405fa839 | |||
| 776e2ec7b6 | |||
| 838aaaee8d | |||
| e8f19a6efe | |||
| 3a8e25e5bb | |||
| 7d1b460dfe | |||
| bc3e9e2172 | |||
| 21bce757ef | |||
| 6a43f62835 | |||
| 8780e6f8e4 | |||
| 78a23caa54 | |||
| d92927aff4 | |||
| 4f412b5989 | |||
| e6f3ff631d | |||
| 09135aabb8 | |||
| de983e680f | |||
| dc827b94e5 | |||
| 40ce811701 | |||
| db5b4c4dc0 | |||
| 4ee429792d | |||
| 27686c0b0c | |||
| 21133f9c3b | |||
| 64305d6415 | |||
| 4dd836bab1 | |||
| f57c213207 |
@@ -0,0 +1,8 @@
|
|||||||
|
<!--
|
||||||
|
Please use one of the available issue templates.
|
||||||
|
Bug reports without required information may be closed.
|
||||||
|
-->
|
||||||
|
|
||||||
|
If you are reporting a bug or crash, please use the appropriate issue template.
|
||||||
|
|
||||||
|
For questions or support, use please use [Discussions](<https://github.com/mtrojnar/osslsigncode/discussions>).
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
---
|
||||||
|
name: Crash report
|
||||||
|
about: Report a segmentation fault or other crash
|
||||||
|
labels: crash
|
||||||
|
---
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Thank you for your crash report.
|
||||||
|
Note: Please search to see if an issue already exists for the bug you encountered.
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Segmentation Fault / Crash Details
|
||||||
|
<!--
|
||||||
|
Provide exact, reproducible steps.
|
||||||
|
Include the complete command, exactly as executed.
|
||||||
|
-->
|
||||||
|
- Signal / exit code: <!-- SIGSEGV, SIGABRT -->
|
||||||
|
- Reproducibility: <!-- always / sometimes / once -->
|
||||||
|
- Affected command or operation: <!-- e.g. `osslsigncode sign`, `osslsigncode verify` -->
|
||||||
|
- First observed version:
|
||||||
|
- Last known working version (if any):
|
||||||
|
|
||||||
|
#### Backtrace
|
||||||
|
<!--
|
||||||
|
Provide a backtrace from gdb or lldb.
|
||||||
|
Build with debug symbols if possible. Use `bt full` if possible.
|
||||||
|
Crash reports without a backtrace may be closed without investigation.
|
||||||
|
-->
|
||||||
|
- `(gdb) bt`
|
||||||
|
|
||||||
|
#### Memory / Sanitizers
|
||||||
|
<!--
|
||||||
|
Attach relevant output if available.
|
||||||
|
-->
|
||||||
|
- [ ] Valgrind
|
||||||
|
- [ ] ASan / UBSan
|
||||||
|
- [ ] Other tools
|
||||||
|
|
||||||
|
#### Crash Context
|
||||||
|
<!--
|
||||||
|
Anything that may be relevant:
|
||||||
|
- OpenSSL provider / engine in use
|
||||||
|
- PKCS#11 modules
|
||||||
|
- Custom OpenSSL configuration
|
||||||
|
- Threading or concurrency
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Environment
|
||||||
|
- Operating system and version (e.g. Ubuntu 24.04):
|
||||||
|
- Architecture (x86_64, arm64, etc.):
|
||||||
|
|
||||||
|
### Versions
|
||||||
|
<!--
|
||||||
|
Please verify that the issue is reproducible with the current upstream master.
|
||||||
|
-->
|
||||||
|
- osslsigncode built from:
|
||||||
|
- [ ] upstream master
|
||||||
|
- [ ] upstream release (tag):
|
||||||
|
- [ ] distribution package (name and version):
|
||||||
|
- `openssl version -a`
|
||||||
|
- `osslsigncode --version`
|
||||||
|
|
||||||
|
### Configuration / Settings
|
||||||
|
<!--
|
||||||
|
Anything that could affect signing or verification:
|
||||||
|
- Custom OpenSSL configuration
|
||||||
|
- Engine / provider settings
|
||||||
|
- Environment variables (OPENSSL_CONF, etc.)
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Anything else
|
||||||
|
<!--
|
||||||
|
Links, references, related issues, workarounds or additional observations.
|
||||||
|
-->
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
name: Documentation
|
||||||
|
about: Report an error in (or missing) documentation
|
||||||
|
labels: documentation
|
||||||
|
---
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Thank you for taking the time to report a documentation issue.
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Documentation Location
|
||||||
|
<!--
|
||||||
|
Where is the problem located?
|
||||||
|
Provide a link, file path, or section name.
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Issue Description
|
||||||
|
<!--
|
||||||
|
Describe what is wrong or missing.
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Suggested Improvement (optional)
|
||||||
|
<!--
|
||||||
|
If you know how it should be fixed, describe it here.
|
||||||
|
Proposed wording or examples are especially helpful.
|
||||||
|
-->
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Suggest a new feature or improvement
|
||||||
|
labels: feature
|
||||||
|
---
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Thank you for your feature request.
|
||||||
|
Please describe the use case and motivation as clearly as possible.
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Use Case / Motivation
|
||||||
|
<!--
|
||||||
|
What problem are you trying to solve?
|
||||||
|
Why is this feature needed?
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Proposed Change
|
||||||
|
<!--
|
||||||
|
Describe the feature or improvement you are proposing.
|
||||||
|
High-level description is sufficient.
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Additional Notes (optional)
|
||||||
|
<!--
|
||||||
|
Anything else that may help:
|
||||||
|
- examples
|
||||||
|
- references
|
||||||
|
- related issues
|
||||||
|
-->
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
name: Questions / Support
|
||||||
|
about: Please use Q&A in Discussions instead
|
||||||
|
labels: question
|
||||||
|
---
|
||||||
|
|
||||||
|
### Questions and Support
|
||||||
|
|
||||||
|
Please do **not** use GitHub issues for general questions or support requests.
|
||||||
|
|
||||||
|
For:
|
||||||
|
- usage questions
|
||||||
|
- "how do I..." questions
|
||||||
|
|
||||||
|
please use [Q&A category in Discussions](<https://github.com/mtrojnar/osslsigncode/discussions/new?category=q-a>)
|
||||||
|
|
||||||
|
Bug reports and crashes should be reported using the appropriate issue templates.
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
---
|
||||||
|
name: Other bug report
|
||||||
|
about: Report a bug
|
||||||
|
labels: bug
|
||||||
|
---
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Thank you for your bug report.
|
||||||
|
Note: Please search to see if an issue already exists for the bug you encountered.
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Current Behavior
|
||||||
|
<!--
|
||||||
|
A concise description of what is happening.
|
||||||
|
Include error messages or incorrect results.
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Expected Behavior
|
||||||
|
<!--
|
||||||
|
A concise description of what you expected to happen instead.
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Steps To Reproduce & Observed Output
|
||||||
|
<!--
|
||||||
|
Provide exact, reproducible steps together with full stdout/stderr for each.
|
||||||
|
-->
|
||||||
|
1. Signing with osslsigncode
|
||||||
|
<!--
|
||||||
|
Full `osslsigncode sign` command and complete stdout/stderr output.
|
||||||
|
-->
|
||||||
|
|
||||||
|
2. Verification with osslsigncode
|
||||||
|
<!--
|
||||||
|
Full `osslsigncode verify` command and complete stdout/stderr output.
|
||||||
|
-->
|
||||||
|
|
||||||
|
3. Signing / verification with Windows signtool (if applicable)
|
||||||
|
<!--
|
||||||
|
Full signtool command (`signtool verify /pa /v`) and complete stdout/stderr output.
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Environment
|
||||||
|
- Operating system and version (e.g. Ubuntu 24.04):
|
||||||
|
- Architecture (x86_64, arm64, etc.):
|
||||||
|
|
||||||
|
### Versions
|
||||||
|
<!--
|
||||||
|
Please verify that the issue is reproducible with the current upstream master.
|
||||||
|
-->
|
||||||
|
- osslsigncode built from:
|
||||||
|
- [ ] upstream master
|
||||||
|
- [ ] upstream release (tag):
|
||||||
|
- [ ] distribution package (name and version):
|
||||||
|
- `openssl version -a`
|
||||||
|
- `osslsigncode --version`
|
||||||
|
|
||||||
|
### Files
|
||||||
|
<!--
|
||||||
|
Attach files if possible, or mention that you will share them privately.
|
||||||
|
-->
|
||||||
|
- [ ] unsigned file
|
||||||
|
- [ ] file signed with osslsigncode
|
||||||
|
- [ ] file signed with signtool or the other tool (for comparison)
|
||||||
|
- [ ] certificate chain used for verification (PEM format)
|
||||||
|
|
||||||
|
### Configuration / Settings
|
||||||
|
<!--
|
||||||
|
Anything that could affect signing or verification:
|
||||||
|
- Custom OpenSSL configuration
|
||||||
|
- Engine / provider settings
|
||||||
|
- Environment variables (OPENSSL_CONF, etc.)
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Anything else
|
||||||
|
<!--
|
||||||
|
Links, references, related issues, workarounds or additional observations.
|
||||||
|
-->
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
<!--
|
||||||
|
Thank you for your pull request.
|
||||||
|
Provide a concise summary of the changes in the PR title.
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Pull Request Type
|
||||||
|
<!--
|
||||||
|
Limit this PR to a single type. If necessary, split changes into multiple PRs.
|
||||||
|
-->
|
||||||
|
|
||||||
|
- [ ] Bug fix
|
||||||
|
- [ ] New feature
|
||||||
|
- [ ] Code style / formatting / renaming
|
||||||
|
- [ ] Refactoring (no functional or API changes)
|
||||||
|
- [ ] Build / CI related changes
|
||||||
|
- [ ] Documentation
|
||||||
|
- [ ] Other (please describe):
|
||||||
|
|
||||||
|
### Related Issue
|
||||||
|
<!--
|
||||||
|
If this fixes a GitHub issue, make sure to have a line saying 'Fixes #XXXX' (without quotes) in the commit message.
|
||||||
|
-->
|
||||||
|
Issue number: N/A
|
||||||
|
|
||||||
|
### Current Behavior
|
||||||
|
<!--
|
||||||
|
Describe the current behavior or limitation this PR addresses.
|
||||||
|
Include error messages or crash symptoms if relevant.
|
||||||
|
-->
|
||||||
|
|
||||||
|
### New Behavior
|
||||||
|
<!--
|
||||||
|
Describe the new or changed behavior introduced by this PR.
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Scope of Changes
|
||||||
|
<!--
|
||||||
|
Briefly describe what was changed and why.
|
||||||
|
Focus on relevant parts only.
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
<!--
|
||||||
|
Describe how the changes were tested.
|
||||||
|
Include commands, environments, or platforms if relevant.
|
||||||
|
-->
|
||||||
|
- [ ] Existing tests
|
||||||
|
- [ ] New tests added
|
||||||
|
- [ ] Manual testing
|
||||||
|
|
||||||
|
### Additional Notes
|
||||||
|
<!--
|
||||||
|
Any additional information relevant for reviewers:
|
||||||
|
- design decisions
|
||||||
|
- backward compatibility
|
||||||
|
- known limitations
|
||||||
|
-->
|
||||||
|
|
||||||
|
## License Declaration
|
||||||
|
<!--
|
||||||
|
All contributions to this project are licensed under the project's license.
|
||||||
|
By submitting this pull request, you confirm that you have the right to submit
|
||||||
|
the code and agree to license it accordingly.
|
||||||
|
-->
|
||||||
|
- [ ] I hereby agree to license my contribution under the project's license.
|
||||||
+75
-45
@@ -1,4 +1,4 @@
|
|||||||
name: CI
|
name: Continuous Integration
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -7,7 +7,7 @@ on:
|
|||||||
env:
|
env:
|
||||||
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
|
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
|
||||||
BUILD_TYPE: Release
|
BUILD_TYPE: Release
|
||||||
version: osslsigncode-2.9
|
version: osslsigncode-2.14-dev
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@@ -27,14 +27,8 @@ jobs:
|
|||||||
os: ubuntu-22.04
|
os: ubuntu-22.04
|
||||||
generator: Unix Makefiles
|
generator: Unix Makefiles
|
||||||
vcpkg_root:
|
vcpkg_root:
|
||||||
- id: ubuntu-20.04
|
|
||||||
triplet: x64-linux
|
|
||||||
compiler: gcc
|
|
||||||
os: ubuntu-20.04
|
|
||||||
generator: Unix Makefiles
|
|
||||||
vcpkg_root:
|
|
||||||
- id: macOS
|
- id: macOS
|
||||||
triplet: x64-osx
|
triplet: arm64-osx
|
||||||
compiler: clang
|
compiler: clang
|
||||||
os: macOS-latest
|
os: macOS-latest
|
||||||
generator: Unix Makefiles
|
generator: Unix Makefiles
|
||||||
@@ -107,17 +101,23 @@ jobs:
|
|||||||
if: matrix.compiler == 'mingw'
|
if: matrix.compiler == 'mingw'
|
||||||
run: echo "D:/a/_temp/msys64/mingw64/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
run: echo "D:/a/_temp/msys64/mingw64/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||||
|
|
||||||
- name: Install apt dependencies (Linux)
|
- name: Set up Python (macOS)
|
||||||
if: runner.os == 'Linux'
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get remove needrestart || echo Ignored
|
|
||||||
sudo apt-get install -y libssl-dev zlib1g-dev python3-cryptography
|
|
||||||
|
|
||||||
- name: Install brew dependencies (macOS)
|
|
||||||
if: runner.os == 'macOS'
|
if: runner.os == 'macOS'
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: '3.13'
|
||||||
|
update-environment: false
|
||||||
|
architecture: 'arm64'
|
||||||
|
|
||||||
|
- name: Set up Python virtual environment (Linux/macOS)
|
||||||
|
if: runner.os != 'Windows'
|
||||||
run: |
|
run: |
|
||||||
brew install python@3.8
|
python -m venv --system-site-packages --copies venv
|
||||||
|
|
||||||
|
- name: Set up Python virtual environment (Windows)
|
||||||
|
if: runner.os == 'Windows'
|
||||||
|
run: |
|
||||||
|
python.exe -m venv --system-site-packages --copies venv
|
||||||
|
|
||||||
- name: Install Xcode (macOS)
|
- name: Install Xcode (macOS)
|
||||||
if: runner.os == 'macOS'
|
if: runner.os == 'macOS'
|
||||||
@@ -128,46 +128,66 @@ jobs:
|
|||||||
- name: Setup the oldest supported version of cmake (macOS)
|
- name: Setup the oldest supported version of cmake (macOS)
|
||||||
if: runner.os == 'macOS'
|
if: runner.os == 'macOS'
|
||||||
uses: jwlawson/actions-setup-cmake@v2.0
|
uses: jwlawson/actions-setup-cmake@v2.0
|
||||||
with:
|
|
||||||
cmake-version: '3.17.0'
|
- name: Install python3 cryptography module (Linux)
|
||||||
|
if: runner.os == 'Linux'
|
||||||
|
run: |
|
||||||
|
source venv/bin/activate
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
python -m pip install --upgrade cryptography
|
||||||
|
python -c "import sys; print(sys.executable)"
|
||||||
|
python --version
|
||||||
|
python -c "import cryptography; print(f'Python3 cryptography version {cryptography.__version__}')"
|
||||||
|
|
||||||
- name: Install python3 cryptography module (macOS)
|
- name: Install python3 cryptography module (macOS)
|
||||||
if: runner.os == 'macOS'
|
if: runner.os == 'macOS'
|
||||||
run: |
|
run: |
|
||||||
python3.8 -m ensurepip
|
source venv/bin/activate
|
||||||
python3.8 -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
python3.8 -m pip install cryptography
|
ARCHFLAGS="-arch arm64" python -m pip install --upgrade cryptography
|
||||||
|
python -c "import sys; print(sys.executable)"
|
||||||
|
python --version
|
||||||
|
python -c "import cryptography; print(f'Python3 cryptography version {cryptography.__version__}')"
|
||||||
|
|
||||||
- name: Install python3 cryptography module (Windows)
|
- name: Install python3 cryptography module (Windows)
|
||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows'
|
||||||
run: |
|
run: |
|
||||||
C:/hostedtoolcache/windows/Python/3.12.3/x64/python3.exe -m ensurepip
|
.\venv\Scripts\Activate.ps1
|
||||||
C:/hostedtoolcache/windows/Python/3.12.3/x64/python.exe -m pip install --upgrade pip
|
python.exe -m ensurepip
|
||||||
C:/hostedtoolcache/windows/Python/3.12.3/x64/python.exe -m pip install cryptography
|
python.exe -m pip install --upgrade pip
|
||||||
|
python.exe -m pip install cryptography
|
||||||
|
python.exe -c "import sys; print(sys.executable)"
|
||||||
|
python.exe --version
|
||||||
|
python.exe -c "import cryptography; print(f'Python3 cryptography version {cryptography.__version__}')"
|
||||||
|
|
||||||
- name: Configure CMake
|
- name: Configure CMake (Linux/macOS)
|
||||||
run: cmake
|
if: runner.os != 'Windows'
|
||||||
-G "${{matrix.generator}}"
|
run: |
|
||||||
-S ${{github.workspace}}
|
source venv/bin/activate
|
||||||
-B ${{github.workspace}}/build
|
cmake \
|
||||||
-DCMAKE_OSX_ARCHITECTURES=arm64
|
-G "${{matrix.generator}}" \
|
||||||
-DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
|
-S "${{github.workspace}}" \
|
||||||
-DCMAKE_INSTALL_PREFIX=${{github.workspace}}/dist
|
-B "${{github.workspace}}/build" \
|
||||||
-DVCPKG_TARGET_TRIPLET=${{matrix.triplet}}
|
-DCMAKE_OSX_ARCHITECTURES=arm64 \
|
||||||
|
-DCMAKE_BUILD_TYPE="${{env.BUILD_TYPE}}" \
|
||||||
|
-DCMAKE_INSTALL_PREFIX="${{github.workspace}}/dist"
|
||||||
|
|
||||||
|
- name: Configure CMake (Windows)
|
||||||
|
if: runner.os == 'Windows'
|
||||||
|
run: |
|
||||||
|
.\venv\Scripts\Activate.ps1
|
||||||
|
cmake `
|
||||||
|
-G "${{matrix.generator}}" `
|
||||||
|
-S "${{github.workspace}}" `
|
||||||
|
-B "${{github.workspace}}/build" `
|
||||||
|
-DCMAKE_BUILD_TYPE="${{env.BUILD_TYPE}}" `
|
||||||
|
-DCMAKE_INSTALL_PREFIX="${{github.workspace}}/dist"
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: cmake
|
run: cmake
|
||||||
--build ${{github.workspace}}/build
|
--build ${{github.workspace}}/build
|
||||||
--config ${{env.BUILD_TYPE}}
|
--config ${{env.BUILD_TYPE}}
|
||||||
|
|
||||||
- name: Show python version (macOS)
|
|
||||||
working-directory: ${{github.workspace}}/build
|
|
||||||
if: runner.os == 'macOS'
|
|
||||||
run: |
|
|
||||||
python3.8 --version
|
|
||||||
python3.8 -c "import sys; print(sys.executable)"
|
|
||||||
python3.8 -c "import cryptography; print(f'Python3 cryptography version {cryptography.__version__}')"
|
|
||||||
|
|
||||||
- name: List files (Linux/macOS)
|
- name: List files (Linux/macOS)
|
||||||
if: runner.os != 'Windows'
|
if: runner.os != 'Windows'
|
||||||
run: find .. -ls
|
run: find .. -ls
|
||||||
@@ -176,9 +196,19 @@ jobs:
|
|||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows'
|
||||||
run: Get-ChildItem -Recurse -Name ..
|
run: Get-ChildItem -Recurse -Name ..
|
||||||
|
|
||||||
- name: Test
|
- name: Test (Linux/macOS)
|
||||||
|
if: runner.os != 'Windows'
|
||||||
working-directory: ${{github.workspace}}/build
|
working-directory: ${{github.workspace}}/build
|
||||||
run: ctest -C ${{env.BUILD_TYPE}}
|
run: |
|
||||||
|
source ../venv/bin/activate
|
||||||
|
ctest -C ${{env.BUILD_TYPE}}
|
||||||
|
|
||||||
|
- name: Test (Windows)
|
||||||
|
if: runner.os == 'Windows'
|
||||||
|
working-directory: ${{github.workspace}}/build
|
||||||
|
run: |
|
||||||
|
..\venv\Scripts\Activate.ps1
|
||||||
|
ctest -C ${{env.BUILD_TYPE}}
|
||||||
|
|
||||||
- name: Upload the errors
|
- name: Upload the errors
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
name: Codespell
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
codespell:
|
||||||
|
name: Check for spelling errors
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: codespell-project/actions-codespell@master
|
||||||
|
with:
|
||||||
|
skip: '*.pem'
|
||||||
+30
-7
@@ -10,13 +10,13 @@ set(BUILTIN_SOCKET ON CACHE BOOL "") # for static Python
|
|||||||
|
|
||||||
# configure basic project information
|
# configure basic project information
|
||||||
project(osslsigncode
|
project(osslsigncode
|
||||||
VERSION 2.9
|
VERSION 2.14
|
||||||
DESCRIPTION "OpenSSL based Authenticode signing for PE, CAB, CAT and MSI files"
|
DESCRIPTION "OpenSSL based Authenticode signing for PE, CAB, CAT, MSI, APPX and script files"
|
||||||
HOMEPAGE_URL "https://github.com/mtrojnar/osslsigncode"
|
HOMEPAGE_URL "https://github.com/mtrojnar/osslsigncode"
|
||||||
LANGUAGES C)
|
LANGUAGES C)
|
||||||
|
|
||||||
# force nonstandard version format for development packages
|
# force nonstandard version format for development packages
|
||||||
set(DEV "")
|
set(DEV "-dev")
|
||||||
set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}${DEV}")
|
set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}${DEV}")
|
||||||
|
|
||||||
# version and contact information
|
# version and contact information
|
||||||
@@ -29,6 +29,9 @@ set(CMAKE_C_STANDARD_REQUIRED ON)
|
|||||||
|
|
||||||
# load CMake library modules
|
# load CMake library modules
|
||||||
include(FindOpenSSL)
|
include(FindOpenSSL)
|
||||||
|
if(OPENSSL_VERSION VERSION_LESS "1.1.1")
|
||||||
|
message(FATAL_ERROR "OpenSSL version must be at least 1.1.1")
|
||||||
|
endif()
|
||||||
if(OPENSSL_VERSION VERSION_LESS "3.0.0")
|
if(OPENSSL_VERSION VERSION_LESS "3.0.0")
|
||||||
include(FindCURL)
|
include(FindCURL)
|
||||||
endif(OPENSSL_VERSION VERSION_LESS "3.0.0")
|
endif(OPENSSL_VERSION VERSION_LESS "3.0.0")
|
||||||
@@ -85,7 +88,7 @@ target_link_libraries(osslsigncode PRIVATE ${ZLIB_LIBRARIES})
|
|||||||
|
|
||||||
if(NOT UNIX)
|
if(NOT UNIX)
|
||||||
# https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-shutdown
|
# https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-shutdown
|
||||||
target_link_libraries(osslsigncode PRIVATE Ws2_32.lib crypt32.lib)
|
target_link_libraries(osslsigncode PRIVATE ws2_32.lib crypt32.lib)
|
||||||
endif(NOT UNIX)
|
endif(NOT UNIX)
|
||||||
|
|
||||||
# add paths to linker search and installed rpath
|
# add paths to linker search and installed rpath
|
||||||
@@ -94,14 +97,25 @@ set_target_properties(osslsigncode PROPERTIES INSTALL_RPATH_USE_LINK_PATH TRUE)
|
|||||||
# testing with CTest
|
# testing with CTest
|
||||||
include(CMakeTest)
|
include(CMakeTest)
|
||||||
|
|
||||||
|
# documentation with Pandoc
|
||||||
|
include(CMakeDoc)
|
||||||
|
|
||||||
# installation rules for a project
|
# installation rules for a project
|
||||||
set(BINDIR "${CMAKE_INSTALL_PREFIX}/bin")
|
include(GNUInstallDirs)
|
||||||
install(TARGETS osslsigncode RUNTIME DESTINATION ${BINDIR})
|
|
||||||
|
install(TARGETS osslsigncode RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||||
|
|
||||||
|
install(FILES
|
||||||
|
"${PROJECT_SOURCE_DIR}/README.md"
|
||||||
|
"${PROJECT_SOURCE_DIR}/NEWS.md"
|
||||||
|
DESTINATION "${CMAKE_INSTALL_DOCDIR}")
|
||||||
|
|
||||||
if(UNIX)
|
if(UNIX)
|
||||||
include(CMakeDist)
|
include(CMakeDist)
|
||||||
else(UNIX)
|
else(UNIX)
|
||||||
install(
|
install(
|
||||||
DIRECTORY ${PROJECT_BINARY_DIR}/ DESTINATION ${BINDIR}
|
DIRECTORY ${PROJECT_BINARY_DIR}/
|
||||||
|
DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||||
FILES_MATCHING
|
FILES_MATCHING
|
||||||
PATTERN "*.dll"
|
PATTERN "*.dll"
|
||||||
PATTERN "vcpkg_installed" EXCLUDE
|
PATTERN "vcpkg_installed" EXCLUDE
|
||||||
@@ -109,6 +123,15 @@ else(UNIX)
|
|||||||
PATTERN "Testing" EXCLUDE)
|
PATTERN "Testing" EXCLUDE)
|
||||||
endif(UNIX)
|
endif(UNIX)
|
||||||
|
|
||||||
|
# uninstall target
|
||||||
|
configure_file(
|
||||||
|
"${PROJECT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in"
|
||||||
|
"${PROJECT_BINARY_DIR}/cmake_uninstall.cmake"
|
||||||
|
IMMEDIATE @ONLY)
|
||||||
|
|
||||||
|
add_custom_target(uninstall
|
||||||
|
COMMAND ${CMAKE_COMMAND} -P "${PROJECT_BINARY_DIR}/cmake_uninstall.cmake")
|
||||||
|
|
||||||
#[[
|
#[[
|
||||||
Local Variables:
|
Local Variables:
|
||||||
c-basic-offset: 4
|
c-basic-offset: 4
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
OpenSSL based Authenticode signing for PE/MSI/Java CAB files.
|
OpenSSL based Authenticode signing for PE, CAB, CAT, MSI, APPX and script files.
|
||||||
|
|
||||||
Copyright (C) 2005-2014 Per Allansson <pallansson@gmail.com>
|
Copyright (C) 2005-2014 Per Allansson <pallansson@gmail.com>
|
||||||
Copyright (C) 2018-2022 Michał Trojnara <Michal.Trojnara@stunnel.org>
|
Copyright (C) 2018-2022 Michał Trojnara <Michal.Trojnara@stunnel.org>
|
||||||
|
|||||||
@@ -1,5 +1,62 @@
|
|||||||
# osslsigncode change log
|
# osslsigncode change log
|
||||||
|
|
||||||
|
### 2.14 (unreleased)
|
||||||
|
|
||||||
|
- attach-signature now uses digest-only verification instead of full signature
|
||||||
|
validation; output file is not kept if digest verification fails
|
||||||
|
|
||||||
|
### 2.13 (2026.02.10)
|
||||||
|
|
||||||
|
**MULTIPLE SECURITY VULNERABILITIES**
|
||||||
|
|
||||||
|
This release includes important security fixes. Users are strongly encouraged
|
||||||
|
to upgrade, as the issues below may be exploitable when processing untrusted
|
||||||
|
files.
|
||||||
|
|
||||||
|
- fixed integer overflows when processing APPX compressed data streams
|
||||||
|
(by Małgorzata Olszówka)
|
||||||
|
- fixed double-free vulnerabilities in APPX file processing
|
||||||
|
(by Małgorzata Olszówka)
|
||||||
|
- fixed multiple memory corruption issues in PE page hash computation
|
||||||
|
(by Antoni Klajn (Opera) and Małgorzata Olszówka)
|
||||||
|
|
||||||
|
### 2.12 (2026.02.02)
|
||||||
|
|
||||||
|
**CRITICAL SECURITY VULNERABILITY**
|
||||||
|
|
||||||
|
This release fixes a critical memory corruption vulnerability. A malicious
|
||||||
|
attacker could create a signed file, which, when verified with osslsigncode,
|
||||||
|
triggers arbitrary code execution. Any previous version of osslsigncode should
|
||||||
|
be immediately upgraded if the tool is used for verification of untrusted
|
||||||
|
files.
|
||||||
|
|
||||||
|
- fixed a buffer overflow while extracting message digests
|
||||||
|
(reported and fixed by Antoni Klajn, Opera)
|
||||||
|
|
||||||
|
### 2.11 (2026.01.20)
|
||||||
|
- added keyUsage validation for signer certificate
|
||||||
|
(thanks to Hanqing Zhao and Zi-Quan You for reporting the issue)
|
||||||
|
- added printing CRL details during signature verification
|
||||||
|
- implemented a workaround for CRL servers returning the HTTP Content-Type
|
||||||
|
header other than application/pkix-crl (thanks to Chris Thibodeaux)
|
||||||
|
- fixed HTTP keep-alive handling
|
||||||
|
- fixed macOS compiler and linker flags
|
||||||
|
- fixed undefined BIO_get_fp() behavior with BIO_FLAGS_UPLINK_INTERNAL
|
||||||
|
|
||||||
|
### 2.10 (2025.06.23)
|
||||||
|
|
||||||
|
- added JavaScript signing
|
||||||
|
- added PKCS#11 provider support (requires OpenSSL 3.0+)
|
||||||
|
- added support for providers without specifying "-pkcs11module" option
|
||||||
|
(OpenSSL 3.0+, e.g., for the upcoming CNG provider)
|
||||||
|
- added compatibility with the CNG engine version 1.1 or later
|
||||||
|
- added the "-engineCtrl" option to control hardware and CNG engines
|
||||||
|
- added the '-blobFile' option to specify a file containing the blob content
|
||||||
|
- improved unauthenticated blob support (thanks to Asger Hautop Drewsen)
|
||||||
|
- improved UTF-8 handling for certificate subjects and issuers
|
||||||
|
- fixed support for multiple signerInfo contentType OIDs (CTL and Authenticode)
|
||||||
|
- fixed tests for python-cryptography >= 43.0.0
|
||||||
|
|
||||||
### 2.9 (2024.06.29)
|
### 2.9 (2024.06.29)
|
||||||
|
|
||||||
- added a 64 bit long pseudo-random NONCE in the TSA request
|
- added a 64 bit long pseudo-random NONCE in the TSA request
|
||||||
|
|||||||
@@ -19,11 +19,13 @@ machine every time I need to sign a binary - I can compile and build
|
|||||||
the binaries using Wine on my Linux machine, but I can't sign them
|
the binaries using Wine on my Linux machine, but I can't sign them
|
||||||
since the signtool.exe makes good use of the CryptoAPI in Windows, and
|
since the signtool.exe makes good use of the CryptoAPI in Windows, and
|
||||||
these APIs aren't (yet?) fully implemented in Wine, so the signtool.exe
|
these APIs aren't (yet?) fully implemented in Wine, so the signtool.exe
|
||||||
tool would fail. And, so, osslsigncode was born.
|
tool would fail. And, so, osslsigncode was born.
|
||||||
|
|
||||||
## WHAT CAN IT DO?
|
## WHAT CAN IT DO?
|
||||||
|
|
||||||
It can sign and timestamp PE (EXE/SYS/DLL/etc), CAB, CAT and MSI files.
|
It can sign and timestamp PE (EXE/SYS/DLL/etc), CAB, CAT, MSI and APPX files,
|
||||||
|
as well as script files with extensions `.ps1`, `.ps1xml`, `.psc1`, `.psd1`,
|
||||||
|
`.psm1`, `.cdxml`, `.mof`, and `.js`.
|
||||||
It supports the equivalent of signtool.exe's "-j javasign.dll -jp low",
|
It supports the equivalent of signtool.exe's "-j javasign.dll -jp low",
|
||||||
i.e. add a valid signature for a CAB file containing Java files.
|
i.e. add a valid signature for a CAB file containing Java files.
|
||||||
It supports getting the timestamp through a proxy as well. It also
|
It supports getting the timestamp through a proxy as well. It also
|
||||||
@@ -122,7 +124,7 @@ You can use a certificate and key stored in a PKCS#12 container:
|
|||||||
-n "Your Application" -i http://www.yourwebsite.com/ \
|
-n "Your Application" -i http://www.yourwebsite.com/ \
|
||||||
-in yourapp.exe -out yourapp-signed.exe
|
-in yourapp.exe -out yourapp-signed.exe
|
||||||
```
|
```
|
||||||
To sign a CAB file containing java class files:
|
To sign a CAB file containing Java class files:
|
||||||
```
|
```
|
||||||
osslsigncode sign -certs <cert-file> -key <key-file> \
|
osslsigncode sign -certs <cert-file> -key <key-file> \
|
||||||
-n "Your Application" -i http://www.yourwebsite.com/ \
|
-n "Your Application" -i http://www.yourwebsite.com/ \
|
||||||
@@ -131,17 +133,68 @@ To sign a CAB file containing java class files:
|
|||||||
```
|
```
|
||||||
Only the 'low' parameter is currently supported.
|
Only the 'low' parameter is currently supported.
|
||||||
|
|
||||||
If you want to use PKCS11 token, you should indicate PKCS11 engine and module.
|
### Using the PKCS#11 Engine with osslsigncode
|
||||||
An example of using osslsigncode with SoftHSM:
|
If you want to use a PKCS#11 token, specify the PKCS#11 engine and module.
|
||||||
|
Example usage with SoftHSM:
|
||||||
```
|
```
|
||||||
osslsigncode sign \
|
osslsigncode sign \
|
||||||
-pkcs11engine /usr/lib64/engines-1.1/pkcs11.so \
|
-engine /usr/lib64/engines-1.1/pkcs11.so \
|
||||||
-pkcs11module /usr/lib64/pkcs11/libsofthsm2.so \
|
-pkcs11module /usr/lib64/pkcs11/libsofthsm2.so \
|
||||||
-pkcs11cert 'pkcs11:token=softhsm-token;object=cert' \
|
-pkcs11cert 'pkcs11:token=softhsm-token;object=cert' \
|
||||||
-key 'pkcs11:token=softhsm-token;object=key' \
|
-key 'pkcs11:token=softhsm-token;object=key' \
|
||||||
-in yourapp.exe -out yourapp-signed.exe
|
-in yourapp.exe -out yourapp-signed.exe
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Using the PKCS#11 Provider with osslsigncode (OpenSSL 3.x only)
|
||||||
|
OpenSSL 3.0 introduced a new provider-based architecture. To use a PKCS#11 token
|
||||||
|
with `osslsigncode`, specify the PKCS#11 provider and module.
|
||||||
|
Example usage with OpenSC:
|
||||||
|
```
|
||||||
|
osslsigncode sign \
|
||||||
|
-provider /usr/lib64/ossl-modules/pkcs11prov.so \
|
||||||
|
-pkcs11module /usr/lib64/opensc-pkcs11.so \
|
||||||
|
-pkcs11cert 'pkcs11:token=my-token;object=cert' \
|
||||||
|
-key 'pkcs11:token=my-token;object=key' \
|
||||||
|
-in yourapp.exe -out yourapp-signed.exe
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using the CNG Engine with osslsigncode (Windows only)
|
||||||
|
The CNG engine allows using certificates and keys stored in the Windows
|
||||||
|
Certificate Store. It requires CNG engine version 1.1 or later. For more
|
||||||
|
information, refer to
|
||||||
|
|
||||||
|
https://www.stunnel.org/cng-engine.html
|
||||||
|
|
||||||
|
A non-commercial edition of CNG engine is available for testing, personal,
|
||||||
|
educational, or research purposes.
|
||||||
|
|
||||||
|
To ensure `osslsigncode` can locate and load the CNG engine module (`cng.dll`)
|
||||||
|
even when it is not installed in the default system engine directory, you can:
|
||||||
|
|
||||||
|
- Specify the full or relative path to `cng.dll`:
|
||||||
|
```
|
||||||
|
osslsigncode sign -engine C:\my\engines\cng.dll ...
|
||||||
|
```
|
||||||
|
- Or set the `OPENSSL_ENGINES` environment variable to the directory containing
|
||||||
|
`cng.dll`, and refer to the engine by its ID:
|
||||||
|
```
|
||||||
|
set OPENSSL_ENGINES=C:\my\engines
|
||||||
|
osslsigncode sign -engine cng ...
|
||||||
|
```
|
||||||
|
|
||||||
|
Below is an example of how to use `osslsigncode` with the CNG engine on Windows:
|
||||||
|
```
|
||||||
|
set OPENSSL_ENGINES=C:\my\engines
|
||||||
|
osslsigncode sign ^
|
||||||
|
-engine cng ^
|
||||||
|
-pkcs11cert osslsigncode_cert ^
|
||||||
|
-key osslsigncode_cert ^
|
||||||
|
-engineCtrl store_flags:0 ^
|
||||||
|
-engineCtrl store_name:MY ^
|
||||||
|
-engineCtrl PIN:yourpass ^
|
||||||
|
-in yourapp.exe -out yourapp-signed.exe
|
||||||
|
```
|
||||||
|
|
||||||
You can check that the signed file is correct by right-clicking
|
You can check that the signed file is correct by right-clicking
|
||||||
on it in Windows and choose Properties --> Digital Signatures,
|
on it in Windows and choose Properties --> Digital Signatures,
|
||||||
and then choose the signature from the list, and click on
|
and then choose the signature from the list, and click on
|
||||||
@@ -179,13 +232,13 @@ osslsigncode.exe add -addUnauthenticatedBlob -in your_signed_file.exe -out out.e
|
|||||||
This feature allows for doing dumb things. Be very careful with what you put
|
This feature allows for doing dumb things. Be very careful with what you put
|
||||||
in the unauthenticated blob, as an attacker could modify this. Do NOT, under
|
in the unauthenticated blob, as an attacker could modify this. Do NOT, under
|
||||||
any circumstances, put a URL here that you will use to download an additional
|
any circumstances, put a URL here that you will use to download an additional
|
||||||
file. If you do do that, you would need to check the newly downloaded file is
|
file. If you do that, you would need to check the newly downloaded file is
|
||||||
code signed AND that it has been signed with your cert AND that it is the
|
code signed AND that it has been signed with your cert AND that it is the
|
||||||
version you expect.
|
version you expect.
|
||||||
|
|
||||||
## BUGS, QUESTIONS etc.
|
## BUGS, QUESTIONS etc.
|
||||||
|
|
||||||
Check whether your your question or suspected bug was already
|
Check whether your question or suspected bug was already
|
||||||
discussed on https://github.com/mtrojnar/osslsigncode/issues.
|
discussed on https://github.com/mtrojnar/osslsigncode/issues.
|
||||||
Otherwise, open a new issue.
|
Otherwise, open a new issue.
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
- signature extraction/removal/verificaton on MSI/CAB files
|
- signature extraction/removal/verification on MSI/CAB files
|
||||||
- clean up / untangle code
|
- clean up / untangle code
|
||||||
- separate timestamping
|
- separate timestamping
|
||||||
- remove mmap usage to increase portability
|
- remove mmap usage to increase portability
|
||||||
|
|||||||
@@ -29,11 +29,6 @@
|
|||||||
#endif /* _MSC_VER */
|
#endif /* _MSC_VER */
|
||||||
#endif /* PRIX64 */
|
#endif /* PRIX64 */
|
||||||
|
|
||||||
#if defined(_MSC_VER)
|
|
||||||
#define fseeko _fseeki64
|
|
||||||
#define ftello _ftelli64
|
|
||||||
#endif /* _MSC_VER */
|
|
||||||
|
|
||||||
#define EOCDR_SIZE 22
|
#define EOCDR_SIZE 22
|
||||||
#define ZIP64_EOCD_LOCATOR_SIZE 20
|
#define ZIP64_EOCD_LOCATOR_SIZE 20
|
||||||
#define ZIP64_HEADER 0x01
|
#define ZIP64_HEADER 0x01
|
||||||
@@ -293,7 +288,7 @@ static void zipWriteCentralDirectoryEntry(BIO *bio, uint64_t *sizeOnDisk, ZIP_CE
|
|||||||
static int zipAppendSignatureFile(BIO *bio, ZIP_FILE *zip, uint8_t *data, uint64_t dataSize);
|
static int zipAppendSignatureFile(BIO *bio, ZIP_FILE *zip, uint8_t *data, uint64_t dataSize);
|
||||||
static int zipOverrideFileData(ZIP_CENTRAL_DIRECTORY_ENTRY *entry, uint8_t *data, uint64_t dataSize);
|
static int zipOverrideFileData(ZIP_CENTRAL_DIRECTORY_ENTRY *entry, uint8_t *data, uint64_t dataSize);
|
||||||
static int zipRewriteData(ZIP_FILE *zip, ZIP_CENTRAL_DIRECTORY_ENTRY *entry, BIO *bio, uint64_t *sizeOnDisk);
|
static int zipRewriteData(ZIP_FILE *zip, ZIP_CENTRAL_DIRECTORY_ENTRY *entry, BIO *bio, uint64_t *sizeOnDisk);
|
||||||
static void zipWriteLocalHeader(BIO *bio, uint64_t *sizeonDisk, ZIP_LOCAL_HEADER *heade);
|
static void zipWriteLocalHeader(BIO *bio, uint64_t *sizeonDisk, ZIP_LOCAL_HEADER *header);
|
||||||
static int zipEntryExist(ZIP_FILE *zip, const char *name);
|
static int zipEntryExist(ZIP_FILE *zip, const char *name);
|
||||||
static u_char *zipCalcDigest(ZIP_FILE *zip, const char *fileName, const EVP_MD *md);
|
static u_char *zipCalcDigest(ZIP_FILE *zip, const char *fileName, const EVP_MD *md);
|
||||||
static size_t zipReadFileDataByName(uint8_t **pData, ZIP_FILE *zip, const char *name);
|
static size_t zipReadFileDataByName(uint8_t **pData, ZIP_FILE *zip, const char *name);
|
||||||
@@ -475,32 +470,33 @@ static int appx_hash_length_get(FILE_FORMAT_CTX *ctx)
|
|||||||
*/
|
*/
|
||||||
static int appx_verify_digests(FILE_FORMAT_CTX *ctx, PKCS7 *p7)
|
static int appx_verify_digests(FILE_FORMAT_CTX *ctx, PKCS7 *p7)
|
||||||
{
|
{
|
||||||
if (is_content_type(p7, SPC_INDIRECT_DATA_OBJID)) {
|
SpcIndirectDataContent *idc;
|
||||||
ASN1_STRING *content_val = p7->d.sign->contents->d.other->value.sequence;
|
BIO *hashes;
|
||||||
const u_char *p = content_val->data;
|
|
||||||
SpcIndirectDataContent *idc = d2i_SpcIndirectDataContent(NULL, &p, content_val->length);
|
|
||||||
|
|
||||||
if (idc) {
|
idc = pkcs7_get_indirect_data_content(p7);
|
||||||
BIO *hashes;
|
if (!idc)
|
||||||
if (!appx_extract_hashes(ctx, idc)) {
|
return 1; /* OK - no SpcIndirectDataContent */
|
||||||
fprintf(stderr, "Failed to extract hashes from the signature\n");
|
|
||||||
SpcIndirectDataContent_free(idc);
|
if (!appx_extract_hashes(ctx, idc)) {
|
||||||
return 0; /* FAILED */
|
fprintf(stderr, "Failed to extract hashes from the signature\n");
|
||||||
}
|
SpcIndirectDataContent_free(idc);
|
||||||
hashes = appx_calculate_hashes(ctx);
|
return 0; /* FAILED */
|
||||||
if (!hashes) {
|
|
||||||
SpcIndirectDataContent_free(idc);
|
|
||||||
return 0; /* FAILED */
|
|
||||||
}
|
|
||||||
BIO_free_all(hashes);
|
|
||||||
if (!appx_compare_hashes(ctx)) {
|
|
||||||
fprintf(stderr, "Signature hash verification failed\n");
|
|
||||||
SpcIndirectDataContent_free(idc);
|
|
||||||
return 0; /* FAILED */
|
|
||||||
}
|
|
||||||
SpcIndirectDataContent_free(idc);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hashes = appx_calculate_hashes(ctx);
|
||||||
|
if (!hashes) {
|
||||||
|
SpcIndirectDataContent_free(idc);
|
||||||
|
return 0; /* FAILED */
|
||||||
|
}
|
||||||
|
BIO_free_all(hashes);
|
||||||
|
|
||||||
|
if (!appx_compare_hashes(ctx)) {
|
||||||
|
fprintf(stderr, "Signature hash verification failed\n");
|
||||||
|
SpcIndirectDataContent_free(idc);
|
||||||
|
return 0; /* FAILED */
|
||||||
|
}
|
||||||
|
|
||||||
|
SpcIndirectDataContent_free(idc);
|
||||||
return 1; /* OK */
|
return 1; /* OK */
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -878,11 +874,18 @@ static uint8_t *appx_calc_zip_central_directory_hash(ZIP_FILE *zip, const EVP_MD
|
|||||||
u_char *mdbuf = NULL;
|
u_char *mdbuf = NULL;
|
||||||
BIO *bhash = BIO_new(BIO_f_md());
|
BIO *bhash = BIO_new(BIO_f_md());
|
||||||
|
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wcast-qual"
|
||||||
|
#endif
|
||||||
if (!BIO_set_md(bhash, md)) {
|
if (!BIO_set_md(bhash, md)) {
|
||||||
fprintf(stderr, "Unable to set the message digest of BIO\n");
|
fprintf(stderr, "Unable to set the message digest of BIO\n");
|
||||||
BIO_free_all(bhash);
|
BIO_free_all(bhash);
|
||||||
return NULL; /* FAILED */
|
return NULL; /* FAILED */
|
||||||
}
|
}
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
BIO_push(bhash, BIO_new(BIO_s_null()));
|
BIO_push(bhash, BIO_new(BIO_s_null()));
|
||||||
if (!appx_write_central_directory(bhash, zip, 1, cdOffset)) {
|
if (!appx_write_central_directory(bhash, zip, 1, cdOffset)) {
|
||||||
fprintf(stderr, "Unable to write central directory\n");
|
fprintf(stderr, "Unable to write central directory\n");
|
||||||
@@ -910,7 +913,7 @@ static int appx_write_central_directory(BIO *bio, ZIP_FILE *zip, int removeSigna
|
|||||||
uint16_t noEntries = 0;
|
uint16_t noEntries = 0;
|
||||||
|
|
||||||
for (entry = zip->centralDirectoryHead; entry != NULL; entry = entry->next) {
|
for (entry = zip->centralDirectoryHead; entry != NULL; entry = entry->next) {
|
||||||
/* the signature file is considered non existent for hashing purposes */
|
/* the signature file is considered nonexistent for hashing purposes */
|
||||||
uint64_t sizeOnDisk = 0;
|
uint64_t sizeOnDisk = 0;
|
||||||
if (noEntries > zip->centralDirectoryRecordCount) {
|
if (noEntries > zip->centralDirectoryRecordCount) {
|
||||||
fprintf(stderr, "Corrupted central directory structure\n");
|
fprintf(stderr, "Corrupted central directory structure\n");
|
||||||
@@ -923,7 +926,7 @@ static int appx_write_central_directory(BIO *bio, ZIP_FILE *zip, int removeSigna
|
|||||||
if (removeSignature && !strcmp(entry->fileName, APP_SIGNATURE_FILENAME)) {
|
if (removeSignature && !strcmp(entry->fileName, APP_SIGNATURE_FILENAME)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
/* APP_SIGNATURE is nt 'tainted' by offset shift after replacing the contents of [content_types] */
|
/* APP_SIGNATURE is not 'tainted' by offset shift after replacing the contents of [content_types] */
|
||||||
zipWriteCentralDirectoryEntry(bio, &sizeOnDisk, entry, strcmp(entry->fileName, APP_SIGNATURE_FILENAME) ? offsetDiff : 0);
|
zipWriteCentralDirectoryEntry(bio, &sizeOnDisk, entry, strcmp(entry->fileName, APP_SIGNATURE_FILENAME) ? offsetDiff : 0);
|
||||||
cdSize += sizeOnDisk;
|
cdSize += sizeOnDisk;
|
||||||
if (entry->overrideData) {
|
if (entry->overrideData) {
|
||||||
@@ -1005,11 +1008,18 @@ static uint8_t *appx_calc_zip_data_hash(uint64_t *cdOffset, ZIP_FILE *zip, const
|
|||||||
BIO *bhash = BIO_new(BIO_f_md());
|
BIO *bhash = BIO_new(BIO_f_md());
|
||||||
uint64_t noEntries = 0;
|
uint64_t noEntries = 0;
|
||||||
|
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wcast-qual"
|
||||||
|
#endif
|
||||||
if (!BIO_set_md(bhash, md)) {
|
if (!BIO_set_md(bhash, md)) {
|
||||||
fprintf(stderr, "Unable to set the message digest of BIO\n");
|
fprintf(stderr, "Unable to set the message digest of BIO\n");
|
||||||
BIO_free_all(bhash);
|
BIO_free_all(bhash);
|
||||||
return NULL; /* FAILED */
|
return NULL; /* FAILED */
|
||||||
}
|
}
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
BIO_push(bhash, BIO_new(BIO_s_null()));
|
BIO_push(bhash, BIO_new(BIO_s_null()));
|
||||||
*cdOffset = 0;
|
*cdOffset = 0;
|
||||||
for (entry = zip->centralDirectoryHead; entry != NULL; entry = entry->next) {
|
for (entry = zip->centralDirectoryHead; entry != NULL; entry = entry->next) {
|
||||||
@@ -1068,13 +1078,13 @@ static int appx_extract_hashes(FILE_FORMAT_CTX *ctx, SpcIndirectDataContent *con
|
|||||||
AppxSpcSipInfo_free(si);
|
AppxSpcSipInfo_free(si);
|
||||||
BIO_free_all(stdbio);
|
BIO_free_all(stdbio);
|
||||||
#endif
|
#endif
|
||||||
int length = content->messageDigest->digest->length;
|
int len = ASN1_STRING_length(content->messageDigest->digest);
|
||||||
uint8_t *data = content->messageDigest->digest->data;
|
const uint8_t *data = ASN1_STRING_get0_data(content->messageDigest->digest);
|
||||||
int mdlen = EVP_MD_size(ctx->appx_ctx->md);
|
int mdlen = EVP_MD_size(ctx->appx_ctx->md);
|
||||||
int pos = 4;
|
int pos = 4;
|
||||||
|
|
||||||
/* we are expecting at least 4 hashes + 4 byte header */
|
/* we are expecting at least 4 hashes + 4 byte header */
|
||||||
if (length < 4 * mdlen + 4) {
|
if (len < 4 * mdlen + 4) {
|
||||||
fprintf(stderr, "Hash too short\n");
|
fprintf(stderr, "Hash too short\n");
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
@@ -1082,7 +1092,7 @@ static int appx_extract_hashes(FILE_FORMAT_CTX *ctx, SpcIndirectDataContent *con
|
|||||||
fprintf(stderr, "Hash signature does not match\n");
|
fprintf(stderr, "Hash signature does not match\n");
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
while (pos + mdlen + 4 <= length) {
|
while (pos + mdlen + 4 <= len) {
|
||||||
if (!memcmp(data + pos, AXPC_SIGNATURE, 4)) {
|
if (!memcmp(data + pos, AXPC_SIGNATURE, 4)) {
|
||||||
ctx->appx_ctx->existingDataHash = OPENSSL_malloc((size_t)mdlen);
|
ctx->appx_ctx->existingDataHash = OPENSSL_malloc((size_t)mdlen);
|
||||||
memcpy(ctx->appx_ctx->existingDataHash, data + pos + 4, (size_t)mdlen);
|
memcpy(ctx->appx_ctx->existingDataHash, data + pos + 4, (size_t)mdlen);
|
||||||
@@ -1178,7 +1188,7 @@ static int appx_compare_hashes(FILE_FORMAT_CTX *ctx)
|
|||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
} else if (!ctx->appx_ctx->calculatedCIHash && !ctx->appx_ctx->existingCIHash) {
|
} else if (!ctx->appx_ctx->calculatedCIHash && !ctx->appx_ctx->existingCIHash) {
|
||||||
/* this is fine, CI file is optional -> if it is missing we expect both hashes to be non existent */
|
/* this is fine, CI file is optional -> if it is missing we expect both hashes to be nonexistent */
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "Code Integrity hash missing\n");
|
fprintf(stderr, "Code Integrity hash missing\n");
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
@@ -1421,7 +1431,7 @@ static void zipWriteCentralDirectoryEntry(BIO *bio, uint64_t *sizeOnDisk, ZIP_CE
|
|||||||
#if 0
|
#if 0
|
||||||
if (entry->extraFieldLen > 0 && entry->extraField)
|
if (entry->extraFieldLen > 0 && entry->extraField)
|
||||||
{
|
{
|
||||||
/* TODO, if override daata, need to rewrite the extra field */
|
/* TODO, if override data, need to rewrite the extra field */
|
||||||
BIO_write(bio, entry->extraField, entry->extraFieldLen);
|
BIO_write(bio, entry->extraField, entry->extraFieldLen);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -1493,6 +1503,8 @@ static int zipAppendSignatureFile(BIO *bio, ZIP_FILE *zip, uint8_t *data, uint64
|
|||||||
|
|
||||||
if (!get_current_position(bio, &offset)) {
|
if (!get_current_position(bio, &offset)) {
|
||||||
fprintf(stderr, "Unable to get offset\n");
|
fprintf(stderr, "Unable to get offset\n");
|
||||||
|
OPENSSL_free(header.fileName);
|
||||||
|
header.fileName = NULL;
|
||||||
OPENSSL_free(dataToWrite);
|
OPENSSL_free(dataToWrite);
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
@@ -1502,6 +1514,8 @@ static int zipAppendSignatureFile(BIO *bio, ZIP_FILE *zip, uint8_t *data, uint64
|
|||||||
size_t check;
|
size_t check;
|
||||||
if (!BIO_write_ex(bio, dataToWrite + written, toWrite, &check)
|
if (!BIO_write_ex(bio, dataToWrite + written, toWrite, &check)
|
||||||
|| check != toWrite) {
|
|| check != toWrite) {
|
||||||
|
OPENSSL_free(header.fileName);
|
||||||
|
header.fileName = NULL;
|
||||||
OPENSSL_free(dataToWrite);
|
OPENSSL_free(dataToWrite);
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
@@ -1596,6 +1610,7 @@ static int zipRewriteData(ZIP_FILE *zip, ZIP_CENTRAL_DIRECTORY_ENTRY *entry, BIO
|
|||||||
{
|
{
|
||||||
size_t check;
|
size_t check;
|
||||||
ZIP_LOCAL_HEADER header;
|
ZIP_LOCAL_HEADER header;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
memset(&header, 0, sizeof(header));
|
memset(&header, 0, sizeof(header));
|
||||||
if (entry->offsetOfLocalHeader >= (uint64_t)zip->fileSize) {
|
if (entry->offsetOfLocalHeader >= (uint64_t)zip->fileSize) {
|
||||||
@@ -1606,7 +1621,7 @@ static int zipRewriteData(ZIP_FILE *zip, ZIP_CENTRAL_DIRECTORY_ENTRY *entry, BIO
|
|||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
if (!zipReadLocalHeader(&header, zip, entry->compressedSize)) {
|
if (!zipReadLocalHeader(&header, zip, entry->compressedSize)) {
|
||||||
return 0; /* FAILED */
|
goto out;
|
||||||
}
|
}
|
||||||
if (entry->overrideData) {
|
if (entry->overrideData) {
|
||||||
header.compressedSize = entry->overrideData->compressedSize;
|
header.compressedSize = entry->overrideData->compressedSize;
|
||||||
@@ -1617,14 +1632,14 @@ static int zipRewriteData(ZIP_FILE *zip, ZIP_CENTRAL_DIRECTORY_ENTRY *entry, BIO
|
|||||||
if (entry->overrideData) {
|
if (entry->overrideData) {
|
||||||
if (!BIO_write_ex(bio, entry->overrideData->data, entry->overrideData->compressedSize, &check)
|
if (!BIO_write_ex(bio, entry->overrideData->data, entry->overrideData->compressedSize, &check)
|
||||||
|| check != entry->overrideData->compressedSize) {
|
|| check != entry->overrideData->compressedSize) {
|
||||||
return 0; /* FAILED */
|
goto out;
|
||||||
}
|
}
|
||||||
if (entry->compressedSize > (uint64_t)zip->fileSize - entry->offsetOfLocalHeader) {
|
if (entry->compressedSize > (uint64_t)zip->fileSize - entry->offsetOfLocalHeader) {
|
||||||
fprintf(stderr, "Corrupted compressedSize : 0x%08" PRIX64 "\n", entry->compressedSize);
|
fprintf(stderr, "Corrupted compressedSize : 0x%08" PRIX64 "\n", entry->compressedSize);
|
||||||
return 0; /* FAILED */
|
goto out;
|
||||||
}
|
}
|
||||||
if (fseeko(zip->file, (int64_t)entry->compressedSize, SEEK_CUR) < 0) {
|
if (fseeko(zip->file, (int64_t)entry->compressedSize, SEEK_CUR) < 0) {
|
||||||
return 0; /* FAILED */
|
goto out;
|
||||||
}
|
}
|
||||||
*sizeOnDisk += entry->overrideData->compressedSize;
|
*sizeOnDisk += entry->overrideData->compressedSize;
|
||||||
} else {
|
} else {
|
||||||
@@ -1635,12 +1650,12 @@ static int zipRewriteData(ZIP_FILE *zip, ZIP_CENTRAL_DIRECTORY_ENTRY *entry, BIO
|
|||||||
size_t size = fread(data, 1, toWrite, zip->file);
|
size_t size = fread(data, 1, toWrite, zip->file);
|
||||||
if (size != toWrite) {
|
if (size != toWrite) {
|
||||||
OPENSSL_free(data);
|
OPENSSL_free(data);
|
||||||
return 0; /* FAILED */
|
goto out;
|
||||||
}
|
}
|
||||||
if (!BIO_write_ex(bio, data, toWrite, &check)
|
if (!BIO_write_ex(bio, data, toWrite, &check)
|
||||||
|| check != toWrite) {
|
|| check != toWrite) {
|
||||||
OPENSSL_free(data);
|
OPENSSL_free(data);
|
||||||
return 0; /* FAILED */
|
goto out;
|
||||||
}
|
}
|
||||||
*sizeOnDisk += toWrite;
|
*sizeOnDisk += toWrite;
|
||||||
len -= toWrite;
|
len -= toWrite;
|
||||||
@@ -1659,19 +1674,23 @@ static int zipRewriteData(ZIP_FILE *zip, ZIP_CENTRAL_DIRECTORY_ENTRY *entry, BIO
|
|||||||
}
|
}
|
||||||
if (zip->isZip64) {
|
if (zip->isZip64) {
|
||||||
if (fseeko(zip->file, 24, SEEK_CUR) < 0) {
|
if (fseeko(zip->file, 24, SEEK_CUR) < 0) {
|
||||||
return 0; /* FAILED */
|
goto out;
|
||||||
}
|
}
|
||||||
*sizeOnDisk += 24;
|
*sizeOnDisk += 24;
|
||||||
} else {
|
} else {
|
||||||
if (fseeko(zip->file, 16, SEEK_CUR) < 0) {
|
if (fseeko(zip->file, 16, SEEK_CUR) < 0) {
|
||||||
return 0; /* FAILED */
|
goto out;
|
||||||
}
|
}
|
||||||
*sizeOnDisk += 16;
|
*sizeOnDisk += 16;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ret = 1; /* OK */
|
||||||
|
out:
|
||||||
OPENSSL_free(header.fileName);
|
OPENSSL_free(header.fileName);
|
||||||
OPENSSL_free(header.extraField);
|
OPENSSL_free(header.extraField);
|
||||||
return 1; /* OK */
|
header.fileName = NULL;
|
||||||
|
header.extraField = NULL;
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1758,12 +1777,19 @@ static u_char *zipCalcDigest(ZIP_FILE *zip, const char *fileName, const EVP_MD *
|
|||||||
return NULL; /* FAILED */
|
return NULL; /* FAILED */
|
||||||
}
|
}
|
||||||
bhash = BIO_new(BIO_f_md());
|
bhash = BIO_new(BIO_f_md());
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wcast-qual"
|
||||||
|
#endif
|
||||||
if (!BIO_set_md(bhash, md)) {
|
if (!BIO_set_md(bhash, md)) {
|
||||||
fprintf(stderr, "Unable to set the message digest of BIO\n");
|
fprintf(stderr, "Unable to set the message digest of BIO\n");
|
||||||
OPENSSL_free(data);
|
OPENSSL_free(data);
|
||||||
BIO_free_all(bhash);
|
BIO_free_all(bhash);
|
||||||
return NULL; /* FAILED */
|
return NULL; /* FAILED */
|
||||||
}
|
}
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
BIO_push(bhash, BIO_new(BIO_s_null()));
|
BIO_push(bhash, BIO_new(BIO_s_null()));
|
||||||
if (!bio_hash_data(bhash, (char *)data, 0, dataSize)) {
|
if (!bio_hash_data(bhash, (char *)data, 0, dataSize)) {
|
||||||
OPENSSL_free(data);
|
OPENSSL_free(data);
|
||||||
@@ -1831,6 +1857,11 @@ static size_t zipReadFileData(ZIP_FILE *zip, uint8_t **pData, ZIP_CENTRAL_DIRECT
|
|||||||
}
|
}
|
||||||
if (entry->overrideData) {
|
if (entry->overrideData) {
|
||||||
compressedSize = entry->overrideData->compressedSize;
|
compressedSize = entry->overrideData->compressedSize;
|
||||||
|
/* Validate sizes for safe allocation */
|
||||||
|
if (compressedSize > (uint64_t)(SIZE_MAX - 1)) {
|
||||||
|
fprintf(stderr, "Corrupted compressedSize : %" PRIu64"\n", compressedSize);
|
||||||
|
return 0; /* FAILED */
|
||||||
|
}
|
||||||
uncompressedSize = entry->overrideData->uncompressedSize;
|
uncompressedSize = entry->overrideData->uncompressedSize;
|
||||||
compressedData = OPENSSL_zalloc(compressedSize + 1);
|
compressedData = OPENSSL_zalloc(compressedSize + 1);
|
||||||
memcpy(compressedData, entry->overrideData->data, compressedSize);
|
memcpy(compressedData, entry->overrideData->data, compressedSize);
|
||||||
@@ -1840,6 +1871,10 @@ static size_t zipReadFileData(ZIP_FILE *zip, uint8_t **pData, ZIP_CENTRAL_DIRECT
|
|||||||
uncompressedSize = entry->uncompressedSize;
|
uncompressedSize = entry->uncompressedSize;
|
||||||
memset(&header, 0, sizeof(header));
|
memset(&header, 0, sizeof(header));
|
||||||
if (!zipReadLocalHeader(&header, zip, compressedSize)) {
|
if (!zipReadLocalHeader(&header, zip, compressedSize)) {
|
||||||
|
OPENSSL_free(header.fileName);
|
||||||
|
OPENSSL_free(header.extraField);
|
||||||
|
header.fileName = NULL;
|
||||||
|
header.extraField = NULL;
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
if (header.fileNameLen != entry->fileNameLen
|
if (header.fileNameLen != entry->fileNameLen
|
||||||
@@ -1848,14 +1883,22 @@ static size_t zipReadFileData(ZIP_FILE *zip, uint8_t **pData, ZIP_CENTRAL_DIRECT
|
|||||||
|| header.uncompressedSize != uncompressedSize
|
|| header.uncompressedSize != uncompressedSize
|
||||||
|| header.compression != entry->compression) {
|
|| header.compression != entry->compression) {
|
||||||
fprintf(stderr, "Local header does not match central directory entry\n");
|
fprintf(stderr, "Local header does not match central directory entry\n");
|
||||||
|
OPENSSL_free(header.fileName);
|
||||||
|
OPENSSL_free(header.extraField);
|
||||||
|
header.fileName = NULL;
|
||||||
|
header.extraField = NULL;
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
/* we don't really need those */
|
/* we don't really need those */
|
||||||
OPENSSL_free(header.fileName);
|
OPENSSL_free(header.fileName);
|
||||||
OPENSSL_free(header.extraField);
|
OPENSSL_free(header.extraField);
|
||||||
|
header.fileName = NULL;
|
||||||
|
header.extraField = NULL;
|
||||||
|
|
||||||
if (compressedSize > (uint64_t)zip->fileSize - entry->offsetOfLocalHeader) {
|
/* Validate sizes for safe allocation */
|
||||||
fprintf(stderr, "Corrupted compressedSize : 0x%08" PRIX64 "\n", entry->compressedSize);
|
if (compressedSize > (uint64_t)(SIZE_MAX - 1)
|
||||||
|
|| compressedSize > (uint64_t)zip->fileSize - entry->offsetOfLocalHeader) {
|
||||||
|
fprintf(stderr, "Corrupted compressedSize : %" PRIu64"\n", compressedSize);
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
compressedData = OPENSSL_zalloc(compressedSize + 1);
|
compressedData = OPENSSL_zalloc(compressedSize + 1);
|
||||||
@@ -1874,16 +1917,31 @@ static size_t zipReadFileData(ZIP_FILE *zip, uint8_t **pData, ZIP_CENTRAL_DIRECT
|
|||||||
*pData = compressedData;
|
*pData = compressedData;
|
||||||
dataSize = compressedSize;
|
dataSize = compressedSize;
|
||||||
} else if (entry->compression == COMPRESSION_DEFLATE) {
|
} else if (entry->compression == COMPRESSION_DEFLATE) {
|
||||||
uint8_t *uncompressedData = OPENSSL_zalloc(uncompressedSize + 1);
|
uint8_t *uncompressedData;
|
||||||
uint64_t destLen = uncompressedSize;
|
uint64_t destLen, sourceLen;
|
||||||
uint64_t sourceLen = compressedSize;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
/* Validate sizes for safe allocation */
|
||||||
|
if (uncompressedSize > (uint64_t)(SIZE_MAX - 1)) {
|
||||||
|
fprintf(stderr, "Corrupted uncompressedSize : %" PRIu64"\n", uncompressedSize);
|
||||||
|
OPENSSL_free(compressedData);
|
||||||
|
return 0; /* FAILED */
|
||||||
|
}
|
||||||
|
/* Detect suspicious compression ratio (zip bomb protection) */
|
||||||
|
if (uncompressedSize > 1024 * 1024 && uncompressedSize / 100 >= compressedSize) {
|
||||||
|
fprintf(stderr, "Error: suspicious compression ratio\n");
|
||||||
|
OPENSSL_free(compressedData);
|
||||||
|
return 0; /* FAILED */
|
||||||
|
}
|
||||||
|
uncompressedData = OPENSSL_zalloc(uncompressedSize + 1);
|
||||||
|
destLen = uncompressedSize;
|
||||||
|
sourceLen = compressedSize;
|
||||||
|
|
||||||
ret = zipInflate(uncompressedData, &destLen, compressedData, (uLong *)&sourceLen);
|
ret = zipInflate(uncompressedData, &destLen, compressedData, (uLong *)&sourceLen);
|
||||||
OPENSSL_free(compressedData);
|
OPENSSL_free(compressedData);
|
||||||
|
|
||||||
if (ret != Z_OK) {
|
if (ret != Z_OK) {
|
||||||
fprintf(stderr, "Data decompresssion failed, zlib error: %d\n", ret);
|
fprintf(stderr, "Data decompression failed, zlib error: %d\n", ret);
|
||||||
OPENSSL_free(uncompressedData);
|
OPENSSL_free(uncompressedData);
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
} else {
|
} else {
|
||||||
@@ -1945,6 +2003,8 @@ static int zipReadLocalHeader(ZIP_LOCAL_HEADER *header, ZIP_FILE *zip, uint64_t
|
|||||||
header->extraFieldLen = fileGetU16(file);
|
header->extraFieldLen = fileGetU16(file);
|
||||||
/* file name (variable size) */
|
/* file name (variable size) */
|
||||||
if (header->fileNameLen > 0) {
|
if (header->fileNameLen > 0) {
|
||||||
|
/* fileNameLen is uint16_t (ZIP spec, 2-byte field),
|
||||||
|
* so fileNameLen + 1 cannot overflow size_t */
|
||||||
header->fileName = OPENSSL_zalloc(header->fileNameLen + 1);
|
header->fileName = OPENSSL_zalloc(header->fileNameLen + 1);
|
||||||
size = fread(header->fileName, 1, header->fileNameLen, file);
|
size = fread(header->fileName, 1, header->fileNameLen, file);
|
||||||
if (size != header->fileNameLen) {
|
if (size != header->fileNameLen) {
|
||||||
@@ -1956,6 +2016,8 @@ static int zipReadLocalHeader(ZIP_LOCAL_HEADER *header, ZIP_FILE *zip, uint64_t
|
|||||||
}
|
}
|
||||||
/* extra field (variable size) */
|
/* extra field (variable size) */
|
||||||
if (header->extraFieldLen > 0) {
|
if (header->extraFieldLen > 0) {
|
||||||
|
/* extraFieldLen is uint16_t (ZIP spec, 2-byte field),
|
||||||
|
* so extraFieldLen + 1 cannot overflow size_t */
|
||||||
header->extraField = OPENSSL_zalloc(header->extraFieldLen + 1);
|
header->extraField = OPENSSL_zalloc(header->extraFieldLen + 1);
|
||||||
size = fread(header->extraField, 1, header->extraFieldLen, file);
|
size = fread(header->extraField, 1, header->extraFieldLen, file);
|
||||||
if (size != header->extraFieldLen) {
|
if (size != header->extraFieldLen) {
|
||||||
@@ -1986,6 +2048,8 @@ static int zipReadLocalHeader(ZIP_LOCAL_HEADER *header, ZIP_FILE *zip, uint64_t
|
|||||||
fprintf(stderr, "The input file is not a valid zip file - flags indicate data descriptor, but data descriptor signature does not match\n");
|
fprintf(stderr, "The input file is not a valid zip file - flags indicate data descriptor, but data descriptor signature does not match\n");
|
||||||
OPENSSL_free(header->fileName);
|
OPENSSL_free(header->fileName);
|
||||||
OPENSSL_free(header->extraField);
|
OPENSSL_free(header->extraField);
|
||||||
|
header->fileName = NULL;
|
||||||
|
header->extraField = NULL;
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
header->crc32 = fileGetU32(file);
|
header->crc32 = fileGetU32(file);
|
||||||
@@ -2149,6 +2213,7 @@ static int zipDeflate(uint8_t *dest, uint64_t *destLen, uint8_t *source, uLong s
|
|||||||
|
|
||||||
err = deflateInit2(&stream, 8, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY);
|
err = deflateInit2(&stream, 8, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY);
|
||||||
if (err != Z_OK) {
|
if (err != Z_OK) {
|
||||||
|
deflateEnd(&stream);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
stream.next_out = dest;
|
stream.next_out = dest;
|
||||||
@@ -2451,6 +2516,8 @@ static ZIP_CENTRAL_DIRECTORY_ENTRY *zipReadNextCentralDirectoryEntry(FILE *file)
|
|||||||
entry->offsetOfLocalHeader = fileGetU32(file);
|
entry->offsetOfLocalHeader = fileGetU32(file);
|
||||||
/* file name (variable size) */
|
/* file name (variable size) */
|
||||||
if (entry->fileNameLen > 0) {
|
if (entry->fileNameLen > 0) {
|
||||||
|
/* fileNameLen is uint16_t (ZIP spec, 2-byte field),
|
||||||
|
* so fileNameLen + 1 cannot overflow size_t */
|
||||||
entry->fileName = OPENSSL_zalloc(entry->fileNameLen + 1);
|
entry->fileName = OPENSSL_zalloc(entry->fileNameLen + 1);
|
||||||
size = fread(entry->fileName, 1, entry->fileNameLen, file);
|
size = fread(entry->fileName, 1, entry->fileNameLen, file);
|
||||||
if (size != entry->fileNameLen) {
|
if (size != entry->fileNameLen) {
|
||||||
@@ -2461,6 +2528,8 @@ static ZIP_CENTRAL_DIRECTORY_ENTRY *zipReadNextCentralDirectoryEntry(FILE *file)
|
|||||||
}
|
}
|
||||||
/* extra field (variable size) */
|
/* extra field (variable size) */
|
||||||
if (entry->extraFieldLen > 0) {
|
if (entry->extraFieldLen > 0) {
|
||||||
|
/* extraFieldLen is uint16_t (ZIP spec, 2-byte field),
|
||||||
|
* so extraFieldLen + 1 cannot overflow size_t */
|
||||||
entry->extraField = OPENSSL_zalloc(entry->extraFieldLen + 1);
|
entry->extraField = OPENSSL_zalloc(entry->extraFieldLen + 1);
|
||||||
size = fread(entry->extraField, 1, entry->extraFieldLen, file);
|
size = fread(entry->extraField, 1, entry->extraFieldLen, file);
|
||||||
if (size != entry->extraFieldLen) {
|
if (size != entry->extraFieldLen) {
|
||||||
@@ -2471,6 +2540,8 @@ static ZIP_CENTRAL_DIRECTORY_ENTRY *zipReadNextCentralDirectoryEntry(FILE *file)
|
|||||||
}
|
}
|
||||||
/* file comment (variable size) */
|
/* file comment (variable size) */
|
||||||
if (entry->fileCommentLen > 0) {
|
if (entry->fileCommentLen > 0) {
|
||||||
|
/* fileCommentLen is uint16_t (ZIP spec, 2-byte field),
|
||||||
|
* so fileCommentLen + 1 cannot overflow size_t */
|
||||||
entry->fileComment = OPENSSL_zalloc(entry->fileCommentLen + 1);
|
entry->fileComment = OPENSSL_zalloc(entry->fileCommentLen + 1);
|
||||||
size = fread(entry->fileComment, 1, entry->fileCommentLen, file);
|
size = fread(entry->fileComment, 1, entry->fileCommentLen, file);
|
||||||
if (size != entry->fileCommentLen) {
|
if (size != entry->fileCommentLen) {
|
||||||
@@ -2609,6 +2680,8 @@ static int readZipEOCDR(ZIP_EOCDR *eocdr, FILE *file)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (eocdr->commentLen > 0) {
|
if (eocdr->commentLen > 0) {
|
||||||
|
/* ZIP_EOCDR commentLen is uint16_t (ZIP spec, 2-byte field),
|
||||||
|
* so fileCommentLen + 1 cannot overflow size_t */
|
||||||
eocdr->comment = OPENSSL_zalloc(eocdr->commentLen + 1);
|
eocdr->comment = OPENSSL_zalloc(eocdr->commentLen + 1);
|
||||||
size = fread(eocdr->comment, 1, eocdr->commentLen, file);
|
size = fread(eocdr->comment, 1, eocdr->commentLen, file);
|
||||||
if (size != eocdr->commentLen) {
|
if (size != eocdr->commentLen) {
|
||||||
@@ -2718,7 +2791,10 @@ static int get_current_position(BIO *bio, uint64_t *offset)
|
|||||||
FILE *file = NULL;
|
FILE *file = NULL;
|
||||||
int64_t pos;
|
int64_t pos;
|
||||||
|
|
||||||
BIO_get_fp(bio, &file);
|
if (BIO_get_fp(bio, &file) != 1 || file == NULL) {
|
||||||
|
fprintf(stderr, "BIO_get_fp() failed\n");
|
||||||
|
return 0; /* FAILED */
|
||||||
|
}
|
||||||
pos = ftello(file);
|
pos = ftello(file);
|
||||||
if (pos < 0) {
|
if (pos < 0) {
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
|
|||||||
@@ -205,11 +205,18 @@ static u_char *cab_digest_calc(FILE_FORMAT_CTX *ctx, const EVP_MD *md)
|
|||||||
u_char *mdbuf = NULL;
|
u_char *mdbuf = NULL;
|
||||||
BIO *bhash = BIO_new(BIO_f_md());
|
BIO *bhash = BIO_new(BIO_f_md());
|
||||||
|
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wcast-qual"
|
||||||
|
#endif
|
||||||
if (!BIO_set_md(bhash, md)) {
|
if (!BIO_set_md(bhash, md)) {
|
||||||
fprintf(stderr, "Unable to set the message digest of BIO\n");
|
fprintf(stderr, "Unable to set the message digest of BIO\n");
|
||||||
BIO_free_all(bhash);
|
BIO_free_all(bhash);
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
BIO_push(bhash, BIO_new(BIO_s_null()));
|
BIO_push(bhash, BIO_new(BIO_s_null()));
|
||||||
|
|
||||||
/* u1 signature[4] 4643534D MSCF: 0-3 */
|
/* u1 signature[4] 4643534D MSCF: 0-3 */
|
||||||
@@ -330,19 +337,7 @@ static int cab_verify_digests(FILE_FORMAT_CTX *ctx, PKCS7 *p7)
|
|||||||
u_char mdbuf[EVP_MAX_MD_SIZE];
|
u_char mdbuf[EVP_MAX_MD_SIZE];
|
||||||
u_char *cmdbuf;
|
u_char *cmdbuf;
|
||||||
|
|
||||||
if (is_content_type(p7, SPC_INDIRECT_DATA_OBJID)) {
|
if (!pkcs7_get_content_digest(p7, mdbuf, &mdtype)) {
|
||||||
ASN1_STRING *content_val = p7->d.sign->contents->d.other->value.sequence;
|
|
||||||
const u_char *p = content_val->data;
|
|
||||||
SpcIndirectDataContent *idc = d2i_SpcIndirectDataContent(NULL, &p, content_val->length);
|
|
||||||
if (idc) {
|
|
||||||
if (idc->messageDigest && idc->messageDigest->digest && idc->messageDigest->digestAlgorithm) {
|
|
||||||
mdtype = OBJ_obj2nid(idc->messageDigest->digestAlgorithm->algorithm);
|
|
||||||
memcpy(mdbuf, idc->messageDigest->digest->data, (size_t)idc->messageDigest->digest->length);
|
|
||||||
}
|
|
||||||
SpcIndirectDataContent_free(idc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (mdtype == -1) {
|
|
||||||
fprintf(stderr, "Failed to extract current message digest\n\n");
|
fprintf(stderr, "Failed to extract current message digest\n\n");
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
@@ -689,7 +684,7 @@ static CAB_CTX *cab_ctx_get(char *indata, uint32_t filesize)
|
|||||||
* and consist of 4 bytes (little-endian order)
|
* and consist of 4 bytes (little-endian order)
|
||||||
* siglen - additional data size is located at offset 48 (from file beginning)
|
* siglen - additional data size is located at offset 48 (from file beginning)
|
||||||
* and consist of 4 bytes (little-endian order)
|
* and consist of 4 bytes (little-endian order)
|
||||||
* If there are additional headers, size of the CAB archive file is calcualted
|
* If there are additional headers, size of the CAB archive file is calculated
|
||||||
* as additional data offset plus additional data size.
|
* as additional data offset plus additional data size.
|
||||||
*/
|
*/
|
||||||
sigpos = GET_UINT32_LE(indata + 44);
|
sigpos = GET_UINT32_LE(indata + 44);
|
||||||
|
|||||||
@@ -55,8 +55,8 @@ FILE_FORMAT file_format_cat = {
|
|||||||
|
|
||||||
/* Prototypes */
|
/* Prototypes */
|
||||||
static CAT_CTX *cat_ctx_get(char *indata, uint32_t filesize);
|
static CAT_CTX *cat_ctx_get(char *indata, uint32_t filesize);
|
||||||
static int cat_add_ms_ctl_object(PKCS7 *p7);
|
static int cat_add_content_type(PKCS7 *p7, PKCS7 *cursig);
|
||||||
static int cat_sign_ms_ctl_content(PKCS7 *p7, PKCS7 *contents);
|
static int cat_sign_content(PKCS7 *p7, PKCS7 *contents);
|
||||||
static int cat_list_content(PKCS7 *p7);
|
static int cat_list_content(PKCS7 *p7);
|
||||||
static int cat_print_content_member_digest(ASN1_TYPE *content);
|
static int cat_print_content_member_digest(ASN1_TYPE *content);
|
||||||
static int cat_print_content_member_name(ASN1_TYPE *content);
|
static int cat_print_content_member_name(ASN1_TYPE *content);
|
||||||
@@ -161,17 +161,17 @@ static PKCS7 *cat_pkcs7_signature_new(FILE_FORMAT_CTX *ctx, BIO *hash)
|
|||||||
fprintf(stderr, "Creating a new signature failed\n");
|
fprintf(stderr, "Creating a new signature failed\n");
|
||||||
return NULL; /* FAILED */
|
return NULL; /* FAILED */
|
||||||
}
|
}
|
||||||
if (!cat_add_ms_ctl_object(p7)) {
|
|
||||||
fprintf(stderr, "Adding MS_CTL_OBJID failed\n");
|
|
||||||
PKCS7_free(p7);
|
|
||||||
return NULL; /* FAILED */
|
|
||||||
}
|
|
||||||
if (!ctx->cat_ctx->p7 || !ctx->cat_ctx->p7->d.sign || !ctx->cat_ctx->p7->d.sign->contents) {
|
if (!ctx->cat_ctx->p7 || !ctx->cat_ctx->p7->d.sign || !ctx->cat_ctx->p7->d.sign->contents) {
|
||||||
fprintf(stderr, "Failed to get content\n");
|
fprintf(stderr, "Failed to get content\n");
|
||||||
PKCS7_free(p7);
|
PKCS7_free(p7);
|
||||||
return NULL; /* FAILED */
|
return NULL; /* FAILED */
|
||||||
}
|
}
|
||||||
if (!cat_sign_ms_ctl_content(p7, ctx->cat_ctx->p7->d.sign->contents)) {
|
if (!cat_add_content_type(p7, ctx->cat_ctx->p7)) {
|
||||||
|
fprintf(stderr, "Adding content type failed\n");
|
||||||
|
PKCS7_free(p7);
|
||||||
|
return NULL; /* FAILED */
|
||||||
|
}
|
||||||
|
if (!cat_sign_content(p7, ctx->cat_ctx->p7->d.sign->contents)) {
|
||||||
fprintf(stderr, "Failed to set signed content\n");
|
fprintf(stderr, "Failed to set signed content\n");
|
||||||
PKCS7_free(p7);
|
PKCS7_free(p7);
|
||||||
return NULL; /* FAILED */
|
return NULL; /* FAILED */
|
||||||
@@ -251,15 +251,30 @@ static CAT_CTX *cat_ctx_get(char *indata, uint32_t filesize)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add "1.3.6.1.4.1.311.10.1" MS_CTL_OBJID signed attribute
|
* Add a content type OID to the PKCS#7 signature structure.
|
||||||
|
* The content type can be:
|
||||||
|
* - "1.3.6.1.4.1.311.10.1" (MS_CTL_OBJID) for Certificate Trust Lists (CTL),
|
||||||
|
* - "1.3.6.1.4.1.311.2.1.4" (SPC_INDIRECT_DATA_OBJID) for Authenticode data.
|
||||||
* [in, out] p7: new PKCS#7 signature
|
* [in, out] p7: new PKCS#7 signature
|
||||||
|
* [in] cursig: current PKCS#7 signature to determine content type
|
||||||
* [returns] 0 on error or 1 on success
|
* [returns] 0 on error or 1 on success
|
||||||
*/
|
*/
|
||||||
static int cat_add_ms_ctl_object(PKCS7 *p7)
|
static int cat_add_content_type(PKCS7 *p7, PKCS7 *cursig)
|
||||||
{
|
{
|
||||||
|
const char *content_type;
|
||||||
STACK_OF(PKCS7_SIGNER_INFO) *signer_info;
|
STACK_OF(PKCS7_SIGNER_INFO) *signer_info;
|
||||||
PKCS7_SIGNER_INFO *si;
|
PKCS7_SIGNER_INFO *si;
|
||||||
|
|
||||||
|
if (is_content_type(cursig, SPC_INDIRECT_DATA_OBJID)) {
|
||||||
|
/* Authenticode content */
|
||||||
|
content_type = SPC_INDIRECT_DATA_OBJID;
|
||||||
|
} else if (is_content_type(cursig, MS_CTL_OBJID)) {
|
||||||
|
/* Certificate Trust List (CTL) */
|
||||||
|
content_type = MS_CTL_OBJID;
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Unsupported content type\n");
|
||||||
|
return 0; /* FAILED */
|
||||||
|
}
|
||||||
signer_info = PKCS7_get_signer_info(p7);
|
signer_info = PKCS7_get_signer_info(p7);
|
||||||
if (!signer_info)
|
if (!signer_info)
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
@@ -267,7 +282,7 @@ static int cat_add_ms_ctl_object(PKCS7 *p7)
|
|||||||
if (!si)
|
if (!si)
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
if (!PKCS7_add_signed_attribute(si, NID_pkcs9_contentType,
|
if (!PKCS7_add_signed_attribute(si, NID_pkcs9_contentType,
|
||||||
V_ASN1_OBJECT, OBJ_txt2obj(MS_CTL_OBJID, 1)))
|
V_ASN1_OBJECT, OBJ_txt2obj(content_type, 1)))
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
return 1; /* OK */
|
return 1; /* OK */
|
||||||
}
|
}
|
||||||
@@ -280,29 +295,41 @@ static int cat_add_ms_ctl_object(PKCS7 *p7)
|
|||||||
* [in] contents: Certificate Trust List (CTL)
|
* [in] contents: Certificate Trust List (CTL)
|
||||||
* [returns] 0 on error or 1 on success
|
* [returns] 0 on error or 1 on success
|
||||||
*/
|
*/
|
||||||
static int cat_sign_ms_ctl_content(PKCS7 *p7, PKCS7 *contents)
|
static int cat_sign_content(PKCS7 *p7, PKCS7 *contents)
|
||||||
{
|
{
|
||||||
u_char *content;
|
const unsigned char *sequence_data;
|
||||||
int seqhdrlen, content_length;
|
const unsigned char *content;
|
||||||
|
ASN1_STRING *sequence;
|
||||||
|
int seqhdrlen, sequence_len, content_length;
|
||||||
|
|
||||||
if (!contents->d.other || !contents->d.other->value.sequence
|
if (!contents->d.other || !contents->d.other->value.sequence) {
|
||||||
|| !contents->d.other->value.sequence->data) {
|
|
||||||
fprintf(stderr, "Failed to get content value\n");
|
fprintf(stderr, "Failed to get content value\n");
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
seqhdrlen = asn1_simple_hdr_len(contents->d.other->value.sequence->data,
|
|
||||||
contents->d.other->value.sequence->length);
|
sequence = contents->d.other->value.sequence;
|
||||||
content = contents->d.other->value.sequence->data + seqhdrlen;
|
sequence_data = ASN1_STRING_get0_data(sequence);
|
||||||
content_length = contents->d.other->value.sequence->length - seqhdrlen;
|
sequence_len = ASN1_STRING_length(sequence);
|
||||||
|
|
||||||
|
if (!sequence_data) {
|
||||||
|
fprintf(stderr, "Failed to get content value\n");
|
||||||
|
return 0; /* FAILED */
|
||||||
|
}
|
||||||
|
|
||||||
|
seqhdrlen = asn1_simple_hdr_len(sequence_data, sequence_len);
|
||||||
|
content = (const unsigned char *)sequence_data + seqhdrlen;
|
||||||
|
content_length = sequence_len - seqhdrlen;
|
||||||
|
|
||||||
if (!pkcs7_sign_content(p7, content, content_length)) {
|
if (!pkcs7_sign_content(p7, content, content_length)) {
|
||||||
fprintf(stderr, "Failed to sign content\n");
|
fprintf(stderr, "Failed to sign content\n");
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!PKCS7_set_content(p7, PKCS7_dup(contents))) {
|
if (!PKCS7_set_content(p7, PKCS7_dup(contents))) {
|
||||||
fprintf(stderr, "PKCS7_set_content failed\n");
|
fprintf(stderr, "PKCS7_set_content failed\n");
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1; /* OK */
|
return 1; /* OK */
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -366,27 +393,22 @@ static int cat_print_content_member_digest(ASN1_TYPE *content)
|
|||||||
{
|
{
|
||||||
SpcIndirectDataContent *idc;
|
SpcIndirectDataContent *idc;
|
||||||
u_char mdbuf[EVP_MAX_MD_SIZE];
|
u_char mdbuf[EVP_MAX_MD_SIZE];
|
||||||
const u_char *data ;
|
|
||||||
int mdtype = -1;
|
int mdtype = -1;
|
||||||
ASN1_STRING *value;
|
|
||||||
|
|
||||||
value = content->value.sequence;
|
idc = asn1_type_get_indirect_data_content(content);
|
||||||
data = ASN1_STRING_get0_data(value);
|
|
||||||
idc = d2i_SpcIndirectDataContent(NULL, &data, ASN1_STRING_length(value));
|
|
||||||
if (!idc)
|
if (!idc)
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
if (idc->messageDigest && idc->messageDigest->digest && idc->messageDigest->digestAlgorithm) {
|
|
||||||
/* get a digest algorithm a message digest of the file from the content */
|
if (spc_indirect_data_content_get_digest(idc, mdbuf, &mdtype) < 0) {
|
||||||
mdtype = OBJ_obj2nid(idc->messageDigest->digestAlgorithm->algorithm);
|
fprintf(stderr, "Failed to extract message digest from signature\n\n");
|
||||||
memcpy(mdbuf, idc->messageDigest->digest->data, (size_t)idc->messageDigest->digest->length);
|
SpcIndirectDataContent_free(idc);
|
||||||
}
|
|
||||||
SpcIndirectDataContent_free(idc);
|
|
||||||
if (mdtype == -1) {
|
|
||||||
fprintf(stderr, "Failed to extract current message digest\n\n");
|
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
|
SpcIndirectDataContent_free(idc);
|
||||||
|
|
||||||
printf("\tHash algorithm: %s\n", OBJ_nid2sn(mdtype));
|
printf("\tHash algorithm: %s\n", OBJ_nid2sn(mdtype));
|
||||||
print_hash("\tMessage digest", "", mdbuf, EVP_MD_size(EVP_get_digestbynid(mdtype)));
|
print_hash("\tMessage digest", "", mdbuf, EVP_MD_size(EVP_get_digestbynid(mdtype)));
|
||||||
|
|
||||||
return 1; /* OK */
|
return 1; /* OK */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
set(CPACK_PACKAGE_NAME ${PROJECT_NAME})
|
set(CPACK_PACKAGE_NAME ${PROJECT_NAME})
|
||||||
set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION})
|
set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION})
|
||||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "OpenSSL based Authenticode signing for PE, CAB, CAT and MSI files")
|
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "OpenSSL based Authenticode signing for PE, CAB, CAT, MSI, APPX and script files")
|
||||||
set(CPACK_PACKAGE_INSTALL_DIRECTORY ${CPACK_PACKAGE_NAME})
|
set(CPACK_PACKAGE_INSTALL_DIRECTORY ${CPACK_PACKAGE_NAME})
|
||||||
set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/README.md")
|
set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/README.md")
|
||||||
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/COPYING.txt")
|
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/COPYING.txt")
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
# documentation with Pandoc
|
||||||
|
# cmake --build .
|
||||||
|
|
||||||
|
find_program(PANDOC pandoc)
|
||||||
|
|
||||||
|
if(NOT PANDOC)
|
||||||
|
message(WARNING "CMakeDoc: pandoc not found, documentation disabled")
|
||||||
|
return()
|
||||||
|
endif(NOT PANDOC)
|
||||||
|
|
||||||
|
set(DOC_MD "${PROJECT_SOURCE_DIR}/osslsigncode.md")
|
||||||
|
|
||||||
|
if(NOT EXISTS "${DOC_MD}")
|
||||||
|
message(WARNING "CMakeDoc: markdown source not found: ${DOC_MD}")
|
||||||
|
return()
|
||||||
|
endif(NOT EXISTS "${DOC_MD}")
|
||||||
|
|
||||||
|
set(MAN_PAGE "${PROJECT_BINARY_DIR}/osslsigncode.1")
|
||||||
|
set(HTML_PAGE "${PROJECT_BINARY_DIR}/osslsigncode.html")
|
||||||
|
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT "${MAN_PAGE}"
|
||||||
|
COMMAND "${PANDOC}" -s "${DOC_MD}" -t man -o "${MAN_PAGE}"
|
||||||
|
DEPENDS "${DOC_MD}"
|
||||||
|
COMMENT "CMakeDoc: generating man page"
|
||||||
|
VERBATIM)
|
||||||
|
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT "${HTML_PAGE}"
|
||||||
|
COMMAND "${PANDOC}" -s --toc --toc-depth=2 "${DOC_MD}" -t html -o "${HTML_PAGE}"
|
||||||
|
DEPENDS "${DOC_MD}"
|
||||||
|
COMMENT "CMakeDoc: generating HTML documentation"
|
||||||
|
VERBATIM)
|
||||||
|
|
||||||
|
add_custom_target(docs ALL DEPENDS "${MAN_PAGE}" "${HTML_PAGE}")
|
||||||
|
|
||||||
|
#[[
|
||||||
|
Local Variables:
|
||||||
|
c-basic-offset: 4
|
||||||
|
tab-width: 4
|
||||||
|
indent-tabs-mode: nil
|
||||||
|
End:
|
||||||
|
vim: set ts=4 expandtab:
|
||||||
|
]]
|
||||||
+6
-17
@@ -75,9 +75,9 @@ if(Python3_FOUND)
|
|||||||
endif(NOT client_result)
|
endif(NOT client_result)
|
||||||
endif(EXISTS "${LOGS}/url.log")
|
endif(EXISTS "${LOGS}/url.log")
|
||||||
|
|
||||||
set(extensions_all "exe" "ex_" "msi" "256appx" "512appx" "cat" "ps1" "psc1" "mof")
|
set(extensions_all "exe" "ex_" "msi" "256appx" "512appx" "cat" "ps1" "psc1" "mof" "js")
|
||||||
set(extensions_nocat "exe" "ex_" "msi" "256appx" "512appx" "ps1" "psc1" "mof")
|
set(extensions_nocat "exe" "ex_" "msi" "256appx" "512appx" "ps1" "psc1" "mof" "js")
|
||||||
set(extensions_nocatappx "exe" "ex_" "msi" "ps1" "psc1" "mof")
|
set(extensions_nocatappx "exe" "ex_" "msi" "ps1" "psc1" "mof" "js")
|
||||||
set(formats "pem" "der")
|
set(formats "pem" "der")
|
||||||
|
|
||||||
else(NOT cryptography_error)
|
else(NOT cryptography_error)
|
||||||
@@ -196,18 +196,12 @@ if(Python3_FOUND AND NOT cryptography_error)
|
|||||||
foreach(format ${formats})
|
foreach(format ${formats})
|
||||||
add_test(NAME "attached_${format}_${ext}"
|
add_test(NAME "attached_${format}_${ext}"
|
||||||
COMMAND ${Python3_EXECUTABLE} ${EXEC} ${OSSLSIGNCODE} "attach-signature"
|
COMMAND ${Python3_EXECUTABLE} ${EXEC} ${OSSLSIGNCODE} "attach-signature"
|
||||||
# sign options
|
|
||||||
"-add-msi-dse"
|
"-add-msi-dse"
|
||||||
"-h" "sha512"
|
"-h" "sha512"
|
||||||
"-nest"
|
"-nest"
|
||||||
"-sigin" "${FILES}/${ext}.${format}"
|
"-sigin" "${FILES}/${ext}.${format}"
|
||||||
"-in" "${FILES}/signed.${ext}"
|
"-in" "${FILES}/signed.${ext}"
|
||||||
"-out" "${FILES}/attached_${format}.${ext}"
|
"-out" "${FILES}/attached_${format}.${ext}")
|
||||||
# verify options
|
|
||||||
"-require-leaf-hash" "FILE ${CERTS}/leafhash.txt"
|
|
||||||
"-time" "1567296000" # Signature verification time: Sep 1 00:00:00 2019 GMT
|
|
||||||
"-CAfile" "${CERTS}/CACert.pem"
|
|
||||||
"-CRLfile" "${CERTS}/CACertCRL.pem")
|
|
||||||
set_tests_properties("attached_${format}_${ext}" PROPERTIES
|
set_tests_properties("attached_${format}_${ext}" PROPERTIES
|
||||||
DEPENDS "signed_${ext};extract_pem_${ext};extract_der_${ext}")
|
DEPENDS "signed_${ext};extract_pem_${ext};extract_der_${ext}")
|
||||||
list(APPEND ALL_TESTS "attached_${format}_${ext}")
|
list(APPEND ALL_TESTS "attached_${format}_${ext}")
|
||||||
@@ -219,6 +213,7 @@ if(Python3_FOUND AND NOT cryptography_error)
|
|||||||
add_test(NAME "added_${ext}"
|
add_test(NAME "added_${ext}"
|
||||||
COMMAND ${Python3_EXECUTABLE} ${EXEC} ${OSSLSIGNCODE} "add"
|
COMMAND ${Python3_EXECUTABLE} ${EXEC} ${OSSLSIGNCODE} "add"
|
||||||
"-addUnauthenticatedBlob"
|
"-addUnauthenticatedBlob"
|
||||||
|
"-blobFile" "${FILES}/unsigned.exe"
|
||||||
"-add-msi-dse" "-h" "sha512"
|
"-add-msi-dse" "-h" "sha512"
|
||||||
"-in" "${FILES}/signed.${ext}"
|
"-in" "${FILES}/signed.${ext}"
|
||||||
"-out" "${FILES}/added.${ext}")
|
"-out" "${FILES}/added.${ext}")
|
||||||
@@ -412,17 +407,11 @@ if(Python3_FOUND AND NOT cryptography_error)
|
|||||||
foreach(format ${formats})
|
foreach(format ${formats})
|
||||||
add_test(NAME "attached_data_${ext}_${data_format}_${format}"
|
add_test(NAME "attached_data_${ext}_${data_format}_${format}"
|
||||||
COMMAND ${Python3_EXECUTABLE} ${EXEC} ${OSSLSIGNCODE} "attach-signature"
|
COMMAND ${Python3_EXECUTABLE} ${EXEC} ${OSSLSIGNCODE} "attach-signature"
|
||||||
# sign options
|
|
||||||
"-add-msi-dse"
|
"-add-msi-dse"
|
||||||
"-h" "sha384"
|
"-h" "sha384"
|
||||||
"-sigin" "${FILES}/signed_data_${ext}_${data_format}.${format}"
|
"-sigin" "${FILES}/signed_data_${ext}_${data_format}.${format}"
|
||||||
"-in" "${FILES}/unsigned.${ext}"
|
"-in" "${FILES}/unsigned.${ext}"
|
||||||
"-out" "${FILES}/attached_data_${data_format}_${format}.${ext}"
|
"-out" "${FILES}/attached_data_${data_format}_${format}.${ext}")
|
||||||
# verify options
|
|
||||||
"-require-leaf-hash" "FILE ${CERTS}/leafhash.txt"
|
|
||||||
"-time" "1567296000" # Signature verification time: Sep 1 00:00:00 2019 GMT
|
|
||||||
"-CAfile" "${CERTS}/CACert.pem"
|
|
||||||
"-CRLfile" "${CERTS}/CACertCRL.pem")
|
|
||||||
set_tests_properties("attached_data_${ext}_${data_format}_${format}" PROPERTIES
|
set_tests_properties("attached_data_${ext}_${data_format}_${format}" PROPERTIES
|
||||||
DEPENDS "signed_data_${ext}_${data_format};signed_data_pem_${ext}_${data_format}")
|
DEPENDS "signed_data_${ext}_${data_format};signed_data_pem_${ext}_${data_format}")
|
||||||
list(APPEND ALL_TESTS "attached_data_${ext}_${data_format}_${format}")
|
list(APPEND ALL_TESTS "attached_data_${ext}_${data_format}_${format}")
|
||||||
|
|||||||
@@ -96,16 +96,11 @@ function(add_compile_flags target)
|
|||||||
message(WARNING "No stack protection supported")
|
message(WARNING "No stack protection supported")
|
||||||
endif(HAVE_STACK_PROTECTOR)
|
endif(HAVE_STACK_PROTECTOR)
|
||||||
endif(HAVE_STACK_PROTECTOR_ALL)
|
endif(HAVE_STACK_PROTECTOR_ALL)
|
||||||
# Support address space layout randomization (ASLR)
|
# Support address space layout randomization (ASLR) / PIE
|
||||||
if(NOT (MINGW OR CYGWIN OR CMAKE_C_COMPILER_ID STREQUAL "AppleClang"
|
if(UNIX AND NOT APPLE)
|
||||||
OR ((CMAKE_SYSTEM_NAME MATCHES Darwin) AND (CMAKE_C_COMPILER_ID MATCHES Clang))))
|
|
||||||
target_compile_options(${target} PRIVATE -fPIE)
|
target_compile_options(${target} PRIVATE -fPIE)
|
||||||
target_link_options(${target} PRIVATE -fPIE -pie)
|
target_link_options(${target} PRIVATE -fPIE -pie -Wl,-z,relro,-z,now,-z,noexecstack)
|
||||||
target_link_options(${target} PRIVATE -Wl,-z,relro)
|
endif(UNIX AND NOT APPLE)
|
||||||
target_link_options(${target} PRIVATE -Wl,-z,now)
|
|
||||||
target_link_options(${target} PRIVATE -Wl,-z,noexecstack)
|
|
||||||
endif(NOT (MINGW OR CYGWIN OR CMAKE_C_COMPILER_ID STREQUAL "AppleClang"
|
|
||||||
OR ((CMAKE_SYSTEM_NAME MATCHES Darwin) AND (CMAKE_C_COMPILER_ID MATCHES Clang))))
|
|
||||||
target_link_options(${target} PRIVATE -fstack-check)
|
target_link_options(${target} PRIVATE -fstack-check)
|
||||||
add_compile_flag_to_targets(${target})
|
add_compile_flag_to_targets(${target})
|
||||||
endif(MSVC)
|
endif(MSVC)
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# uninstall target
|
||||||
|
#
|
||||||
|
# CMake does not provide a built-in uninstall target.
|
||||||
|
# This target removes files listed in install_manifest.txt,
|
||||||
|
# generated by the install step.
|
||||||
|
#
|
||||||
|
# cmake --build . --target uninstall
|
||||||
|
|
||||||
|
if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
|
||||||
|
message(FATAL_ERROR "Cannot find install manifest")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files)
|
||||||
|
string(REPLACE "\n" ";" files "${files}")
|
||||||
|
|
||||||
|
foreach(file ${files})
|
||||||
|
message(STATUS "Removing ${file}")
|
||||||
|
|
||||||
|
if(EXISTS "${file}" OR IS_SYMLINK "${file}")
|
||||||
|
file(REMOVE "${file}")
|
||||||
|
else()
|
||||||
|
message(STATUS "File does not exist: ${file}")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
+1341
-638
File diff suppressed because it is too large
Load Diff
+18
-7
@@ -10,6 +10,7 @@ from csv import reader
|
|||||||
from requests import get
|
from requests import get
|
||||||
from requests.exceptions import RequestException
|
from requests.exceptions import RequestException
|
||||||
from concurrent.futures import ThreadPoolExecutor
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
|
from re import search
|
||||||
|
|
||||||
def download_cert(hash):
|
def download_cert(hash):
|
||||||
for attempt in range(10):
|
for attempt in range(10):
|
||||||
@@ -17,23 +18,33 @@ def download_cert(hash):
|
|||||||
sleep(10)
|
sleep(10)
|
||||||
try:
|
try:
|
||||||
creds = f'{attempt}{hash}:{attempt}{hash}'
|
creds = f'{attempt}{hash}:{attempt}{hash}'
|
||||||
resp = get(f'https://crt.sh/?d={hash}',
|
proxies = dict(https=f'socks5://{creds}@127.0.0.1:9050')
|
||||||
proxies=dict(https=f'socks5://{creds}@127.0.0.1:9050'))
|
|
||||||
|
url = f'https://crt.sh/?sha1={hash}&match=='
|
||||||
|
resp = get(url, proxies=proxies)
|
||||||
resp.raise_for_status()
|
resp.raise_for_status()
|
||||||
|
|
||||||
|
m = search(r'\bid=(\d+)\b', resp.content.decode('ascii', 'replace'))
|
||||||
|
id = m.group(1)
|
||||||
|
|
||||||
|
url = f'https://crt.sh/?d={id}'
|
||||||
|
resp = get(url, proxies=proxies)
|
||||||
|
resp.raise_for_status()
|
||||||
|
|
||||||
print('.', file=stderr, end='')
|
print('.', file=stderr, end='')
|
||||||
stderr.flush()
|
stderr.flush()
|
||||||
return resp.content.decode('utf-8')
|
return resp.content.decode('utf-8', 'replace')
|
||||||
except RequestException as e:
|
except Exception as e:
|
||||||
print(f'\nAttempt {attempt}: {e}', file=stderr)
|
print(f'\n{url} attempt {attempt}: {e}', file=stderr)
|
||||||
print('\nGiving up on', hash, file=stderr)
|
print('\nGiving up on', hash, file=stderr)
|
||||||
|
|
||||||
resp = get('https://ccadb-public.secure.force.com/microsoft/IncludedCACertificateReportForMSFTCSV')
|
resp = get('https://ccadb.my.salesforce-sites.com/microsoft/IncludedCACertificateReportForMSFTCSV')
|
||||||
resp.raise_for_status()
|
resp.raise_for_status()
|
||||||
lines = resp.content.decode('utf-8').splitlines()[1:]
|
lines = resp.content.decode('utf-8').splitlines()[1:]
|
||||||
hashes = [row[4] for row in reader(lines)
|
hashes = [row[4] for row in reader(lines)
|
||||||
if row[0] != 'Disabled'
|
if row[0] != 'Disabled'
|
||||||
or row[4] == 'F38406E540D7A9D90CB4A9479299640FFB6DF9E224ECC7A01C0D9558D8DAD77D']
|
or row[4] == 'F38406E540D7A9D90CB4A9479299640FFB6DF9E224ECC7A01C0D9558D8DAD77D']
|
||||||
with ThreadPoolExecutor(max_workers=20) as executor:
|
with ThreadPoolExecutor(max_workers=10) as executor:
|
||||||
certs = executor.map(download_cert, hashes)
|
certs = executor.map(download_cert, hashes)
|
||||||
for cert in certs:
|
for cert in certs:
|
||||||
if cert is not None:
|
if cert is not None:
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ static int pkcs7_signer_info_add_purpose(PKCS7_SIGNER_INFO *si, FILE_FORMAT_CTX
|
|||||||
static int pkcs7_signer_info_add_sequence_number(PKCS7_SIGNER_INFO *si, FILE_FORMAT_CTX *ctx);
|
static int pkcs7_signer_info_add_sequence_number(PKCS7_SIGNER_INFO *si, FILE_FORMAT_CTX *ctx);
|
||||||
static STACK_OF(X509) *X509_chain_get_sorted(FILE_FORMAT_CTX *ctx, int signer);
|
static STACK_OF(X509) *X509_chain_get_sorted(FILE_FORMAT_CTX *ctx, int signer);
|
||||||
static int X509_compare(const X509 *const *a, const X509 *const *b);
|
static int X509_compare(const X509 *const *a, const X509 *const *b);
|
||||||
|
static void sk_X509_remove_duplicates(STACK_OF(X509) *chain);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Common functions
|
* Common functions
|
||||||
@@ -165,73 +166,76 @@ int data_write_pkcs7(FILE_FORMAT_CTX *ctx, BIO *outdata, PKCS7 *p7)
|
|||||||
PKCS7 *pkcs7_create(FILE_FORMAT_CTX *ctx)
|
PKCS7 *pkcs7_create(FILE_FORMAT_CTX *ctx)
|
||||||
{
|
{
|
||||||
int i, signer = -1;
|
int i, signer = -1;
|
||||||
PKCS7 *p7;
|
|
||||||
PKCS7_SIGNER_INFO *si = NULL;
|
PKCS7_SIGNER_INFO *si = NULL;
|
||||||
STACK_OF(X509) *chain = NULL;
|
STACK_OF(X509) *chain = NULL;
|
||||||
|
PKCS7 *p7 = PKCS7_new();
|
||||||
|
|
||||||
|
if (!p7)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
p7 = PKCS7_new();
|
|
||||||
PKCS7_set_type(p7, NID_pkcs7_signed);
|
PKCS7_set_type(p7, NID_pkcs7_signed);
|
||||||
PKCS7_content_new(p7, NID_pkcs7_data);
|
PKCS7_content_new(p7, NID_pkcs7_data);
|
||||||
if (ctx->options->cert != NULL) {
|
|
||||||
/*
|
/* find the signer's certificate located somewhere in the whole certificate chain */
|
||||||
* the private key and corresponding certificate are parsed from the PKCS12
|
for (i=0; i<sk_X509_num(ctx->options->certs); i++) {
|
||||||
* structure or loaded from the security token, so we may omit to check
|
X509 *signcert = sk_X509_value(ctx->options->certs, i);
|
||||||
* the consistency of a private key with the public key in an X509 certificate
|
|
||||||
*/
|
if (X509_check_private_key(signcert, ctx->options->pkey)) {
|
||||||
si = PKCS7_add_signature(p7, ctx->options->cert, ctx->options->pkey,
|
si = PKCS7_add_signature(p7, signcert, ctx->options->pkey, ctx->options->md);
|
||||||
ctx->options->md);
|
signer = i;
|
||||||
if (si == NULL)
|
if (signer > 0)
|
||||||
return NULL; /* FAILED */
|
printf("Warning: For optimal performance, consider placing the signer certificate at the beginning of the certificate chain.\n");
|
||||||
} else {
|
break;
|
||||||
/* find the signer's certificate located somewhere in the whole certificate chain */
|
|
||||||
for (i=0; i<sk_X509_num(ctx->options->certs); i++) {
|
|
||||||
X509 *signcert = sk_X509_value(ctx->options->certs, i);
|
|
||||||
if (X509_check_private_key(signcert, ctx->options->pkey)) {
|
|
||||||
si = PKCS7_add_signature(p7, signcert, ctx->options->pkey, ctx->options->md);
|
|
||||||
signer = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (si == NULL) {
|
|
||||||
fprintf(stderr, "Failed to checking the consistency of a private key: %s\n",
|
|
||||||
ctx->options->keyfile);
|
|
||||||
fprintf(stderr, " with a public key in any X509 certificate: %s\n\n",
|
|
||||||
ctx->options->certfile);
|
|
||||||
return NULL; /* FAILED */
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!si) {
|
||||||
|
fprintf(stderr, "Failed to checking the consistency of a private key: %s\n",
|
||||||
|
ctx->options->keyfile);
|
||||||
|
fprintf(stderr, " with a public key in any X509 certificate: %s\n\n",
|
||||||
|
#if !defined(OPENSSL_NO_ENGINE) || OPENSSL_VERSION_NUMBER>=0x30000000L
|
||||||
|
ctx->options->certfile ? ctx->options->certfile : ctx->options->p11cert);
|
||||||
|
#else
|
||||||
|
ctx->options->certfile);
|
||||||
|
#endif /* !defined(OPENSSL_NO_ENGINE) || OPENSSL_VERSION_NUMBER>=0x30000000L */
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
if (!pkcs7_signer_info_add_signing_time(si, ctx)) {
|
if (!pkcs7_signer_info_add_signing_time(si, ctx)) {
|
||||||
return NULL; /* FAILED */
|
goto err;
|
||||||
}
|
}
|
||||||
if (!pkcs7_signer_info_add_purpose(si, ctx)) {
|
if (!pkcs7_signer_info_add_purpose(si, ctx)) {
|
||||||
return NULL; /* FAILED */
|
goto err;
|
||||||
}
|
}
|
||||||
if ((ctx->options->desc || ctx->options->url) &&
|
if ((ctx->options->desc || ctx->options->url) &&
|
||||||
!pkcs7_signer_info_add_spc_sp_opus_info(si, ctx)) {
|
!pkcs7_signer_info_add_spc_sp_opus_info(si, ctx)) {
|
||||||
fprintf(stderr, "Couldn't allocate memory for opus info\n");
|
fprintf(stderr, "Couldn't allocate memory for opus info\n");
|
||||||
return NULL; /* FAILED */
|
goto err;
|
||||||
}
|
}
|
||||||
if ((ctx->options->nested_number >= 0) &&
|
if ((ctx->options->nested_number >= 0) &&
|
||||||
!pkcs7_signer_info_add_sequence_number(si, ctx)) {
|
!pkcs7_signer_info_add_sequence_number(si, ctx)) {
|
||||||
return NULL; /* FAILED */
|
goto err;
|
||||||
}
|
}
|
||||||
/* create X509 chain sorted in ascending order by their DER encoding */
|
/* create X509 chain sorted in ascending order by their DER encoding */
|
||||||
chain = X509_chain_get_sorted(ctx, signer);
|
chain = X509_chain_get_sorted(ctx, signer);
|
||||||
if (chain == NULL) {
|
if (!chain) {
|
||||||
fprintf(stderr, "Failed to create a sorted certificate chain\n");
|
fprintf(stderr, "Failed to create a sorted certificate chain\n");
|
||||||
return NULL; /* FAILED */
|
goto err;
|
||||||
}
|
}
|
||||||
/* add sorted certificate chain */
|
/* add sorted certificate chain */
|
||||||
for (i=0; i<sk_X509_num(chain); i++) {
|
for (i=0; i<sk_X509_num(chain); i++) {
|
||||||
PKCS7_add_certificate(p7, sk_X509_value(chain, i));
|
(void)PKCS7_add_certificate(p7, sk_X509_value(chain, i));
|
||||||
}
|
}
|
||||||
/* add crls */
|
/* add crls */
|
||||||
if (ctx->options->crls) {
|
if (ctx->options->crls) {
|
||||||
for (i=0; i<sk_X509_CRL_num(ctx->options->crls); i++)
|
for (i=0; i<sk_X509_CRL_num(ctx->options->crls); i++)
|
||||||
PKCS7_add_crl(p7, sk_X509_CRL_value(ctx->options->crls, i));
|
(void)PKCS7_add_crl(p7, sk_X509_CRL_value(ctx->options->crls, i));
|
||||||
}
|
}
|
||||||
sk_X509_free(chain);
|
sk_X509_free(chain);
|
||||||
return p7; /* OK */
|
return p7; /* OK */
|
||||||
|
|
||||||
|
err:
|
||||||
|
PKCS7_free(p7);
|
||||||
|
return NULL; /* FAILED */
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -337,6 +341,91 @@ PKCS7 *pkcs7_set_content(ASN1_OCTET_STRING *content)
|
|||||||
return p7;
|
return p7;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Retrieve the message digest and digest algorithm from PKCS7
|
||||||
|
* SpcIndirectDataContent.
|
||||||
|
*
|
||||||
|
* [in] p7: PKCS7 structure containing SPC_INDIRECT_DATA_OBJID content
|
||||||
|
* [out] mdbuf: message digest buffer, at least EVP_MAX_MD_SIZE bytes
|
||||||
|
* [out] mdtype: OpenSSL NID of the digest algorithm
|
||||||
|
* [returns] 0 on error or 1 on success
|
||||||
|
*/
|
||||||
|
int pkcs7_get_content_digest(PKCS7 *p7, u_char *mdbuf, int *mdtype)
|
||||||
|
{
|
||||||
|
SpcIndirectDataContent *idc;
|
||||||
|
|
||||||
|
if (!mdbuf || !mdtype)
|
||||||
|
return 0; /* FAILED */
|
||||||
|
|
||||||
|
*mdtype = -1;
|
||||||
|
|
||||||
|
idc = pkcs7_get_indirect_data_content(p7);
|
||||||
|
if (!idc) {
|
||||||
|
fprintf(stderr, "Failed to decode SpcIndirectDataContent\n\n");
|
||||||
|
return 0; /* FAILED */
|
||||||
|
}
|
||||||
|
if (spc_indirect_data_content_get_digest(idc, mdbuf, mdtype) < 0) {
|
||||||
|
fprintf(stderr, "Failed to extract message digest from signature\n\n");
|
||||||
|
SpcIndirectDataContent_free(idc);
|
||||||
|
return 0; /* FAILED */
|
||||||
|
}
|
||||||
|
SpcIndirectDataContent_free(idc);
|
||||||
|
if (*mdtype == -1) {
|
||||||
|
fprintf(stderr, "Failed to extract current message digest\n\n");
|
||||||
|
return 0; /* FAILED */
|
||||||
|
}
|
||||||
|
return 1; /* OK */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Decode SpcIndirectDataContent from a PKCS7 signedData content.
|
||||||
|
*
|
||||||
|
* [in] p7: PKCS7 structure containing SPC_INDIRECT_DATA_OBJID content
|
||||||
|
* [returns] newly allocated SpcIndirectDataContent, or NULL on error
|
||||||
|
*
|
||||||
|
* The caller is responsible for freeing the returned object with
|
||||||
|
* SpcIndirectDataContent_free().
|
||||||
|
*/
|
||||||
|
SpcIndirectDataContent *pkcs7_get_indirect_data_content(PKCS7 *p7)
|
||||||
|
{
|
||||||
|
if (!is_content_type(p7, SPC_INDIRECT_DATA_OBJID))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (!p7->d.sign || !p7->d.sign->contents || !p7->d.sign->contents->d.other)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return asn1_type_get_indirect_data_content(p7->d.sign->contents->d.other);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Decode SpcIndirectDataContent from an ASN1_TYPE object.
|
||||||
|
* The ASN1_TYPE is expected to contain a V_ASN1_SEQUENCE value.
|
||||||
|
*
|
||||||
|
* [in] content: ASN1_TYPE containing DER-encoded SpcIndirectDataContent
|
||||||
|
* [returns] newly allocated SpcIndirectDataContent, or NULL on error
|
||||||
|
*
|
||||||
|
* The caller is responsible for freeing the returned object with
|
||||||
|
* SpcIndirectDataContent_free().
|
||||||
|
*/
|
||||||
|
SpcIndirectDataContent *asn1_type_get_indirect_data_content(ASN1_TYPE *content)
|
||||||
|
{
|
||||||
|
ASN1_STRING *value;
|
||||||
|
const unsigned char *data;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
if (!content || content->type != V_ASN1_SEQUENCE)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
value = content->value.sequence;
|
||||||
|
if (!value)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
data = ASN1_STRING_get0_data(value);
|
||||||
|
len = ASN1_STRING_length(value);
|
||||||
|
|
||||||
|
return d2i_SpcIndirectDataContent(NULL, &data, len);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Return spcIndirectDataContent.
|
* Return spcIndirectDataContent.
|
||||||
* [in] hash: message digest BIO
|
* [in] hash: message digest BIO
|
||||||
@@ -461,7 +550,7 @@ void print_hash(const char *descript1, const char *descript2, const u_char *mdbu
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* [in] p7: new PKCS#7 signature
|
* [in] p7: PKCS#7 signature
|
||||||
* [in] objid: Microsoft OID Authenticode
|
* [in] objid: Microsoft OID Authenticode
|
||||||
* [returns] 0 on error or 1 on success
|
* [returns] 0 on error or 1 on success
|
||||||
*/
|
*/
|
||||||
@@ -471,6 +560,10 @@ int is_content_type(PKCS7 *p7, const char *objid)
|
|||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
indir_objid = OBJ_txt2obj(objid, 1);
|
indir_objid = OBJ_txt2obj(objid, 1);
|
||||||
|
if (!indir_objid) {
|
||||||
|
fprintf(stderr, "Invalid object identifier: %s\n", objid);
|
||||||
|
return 0; /* FAILED */
|
||||||
|
}
|
||||||
ret = p7 && PKCS7_type_is_signed(p7) &&
|
ret = p7 && PKCS7_type_is_signed(p7) &&
|
||||||
!OBJ_cmp(p7->d.sign->contents->type, indir_objid) &&
|
!OBJ_cmp(p7->d.sign->contents->type, indir_objid) &&
|
||||||
(p7->d.sign->contents->d.other->type == V_ASN1_SEQUENCE ||
|
(p7->d.sign->contents->d.other->type == V_ASN1_SEQUENCE ||
|
||||||
@@ -555,6 +648,42 @@ int compare_digests(u_char *mdbuf, u_char *cmdbuf, int mdtype)
|
|||||||
return mdok;
|
return mdok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Safely extract digest from SpcIndirectDataContent with bounds checking.
|
||||||
|
* This function validates that the digest length from the ASN.1 structure
|
||||||
|
* does not exceed the destination buffer size, preventing buffer overflows
|
||||||
|
* from maliciously crafted signatures.
|
||||||
|
* [in] idc: parsed SpcIndirectDataContent structure
|
||||||
|
* [out] mdbuf: output buffer (must be at least EVP_MAX_MD_SIZE bytes)
|
||||||
|
* [out] mdtype: digest algorithm NID
|
||||||
|
* [returns] digest length on success, -1 on error
|
||||||
|
*/
|
||||||
|
int spc_indirect_data_content_get_digest(SpcIndirectDataContent *idc, u_char *mdbuf, int *mdtype)
|
||||||
|
{
|
||||||
|
ASN1_OCTET_STRING *digest_asn1;
|
||||||
|
const unsigned char *digest_data;
|
||||||
|
int digest_len;
|
||||||
|
|
||||||
|
if (!idc || !idc->messageDigest || !idc->messageDigest->digest ||
|
||||||
|
!idc->messageDigest->digestAlgorithm) {
|
||||||
|
return -1; /* FAILED */
|
||||||
|
}
|
||||||
|
digest_asn1 = idc->messageDigest->digest;
|
||||||
|
digest_len = ASN1_STRING_length((ASN1_STRING *)digest_asn1);
|
||||||
|
|
||||||
|
/* Validate digest length to prevent buffer overflow */
|
||||||
|
if (digest_len <= 0 || digest_len > EVP_MAX_MD_SIZE) {
|
||||||
|
fprintf(stderr, "Invalid digest length in signature: %d (expected 1-%d)\n",
|
||||||
|
digest_len, EVP_MAX_MD_SIZE);
|
||||||
|
return -1; /* FAILED */
|
||||||
|
}
|
||||||
|
|
||||||
|
digest_data = ASN1_STRING_get0_data((ASN1_STRING *)digest_asn1);
|
||||||
|
*mdtype = OBJ_obj2nid(idc->messageDigest->digestAlgorithm->algorithm);
|
||||||
|
memcpy(mdbuf, digest_data, (size_t)digest_len);
|
||||||
|
return digest_len; /* OK */
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Helper functions
|
* Helper functions
|
||||||
*/
|
*/
|
||||||
@@ -610,8 +739,16 @@ static int spc_indirect_data_content_create(u_char **blob, int *len, FILE_FORMAT
|
|||||||
idc->data->value->type = V_ASN1_SEQUENCE;
|
idc->data->value->type = V_ASN1_SEQUENCE;
|
||||||
idc->data->value->value.sequence = ASN1_STRING_new();
|
idc->data->value->value.sequence = ASN1_STRING_new();
|
||||||
idc->data->type = ctx->format->data_blob_get(&p, &l, ctx);
|
idc->data->type = ctx->format->data_blob_get(&p, &l, ctx);
|
||||||
idc->data->value->value.sequence->data = p;
|
if (!idc->data->type) {
|
||||||
idc->data->value->value.sequence->length = l;
|
SpcIndirectDataContent_free(idc);
|
||||||
|
return 0; /* FAILED */
|
||||||
|
}
|
||||||
|
if (!ASN1_STRING_set(idc->data->value->value.sequence, p, l)) {
|
||||||
|
OPENSSL_free(p);
|
||||||
|
SpcIndirectDataContent_free(idc);
|
||||||
|
return 0; /* FAILED */
|
||||||
|
}
|
||||||
|
OPENSSL_free(p);
|
||||||
idc->messageDigest->digestAlgorithm->algorithm = OBJ_nid2obj(mdtype);
|
idc->messageDigest->digestAlgorithm->algorithm = OBJ_nid2obj(mdtype);
|
||||||
idc->messageDigest->digestAlgorithm->parameters = ASN1_TYPE_new();
|
idc->messageDigest->digestAlgorithm->parameters = ASN1_TYPE_new();
|
||||||
idc->messageDigest->digestAlgorithm->parameters->type = V_ASN1_NULL;
|
idc->messageDigest->digestAlgorithm->parameters->type = V_ASN1_NULL;
|
||||||
@@ -732,11 +869,6 @@ static STACK_OF(X509) *X509_chain_get_sorted(FILE_FORMAT_CTX *ctx, int signer)
|
|||||||
int i;
|
int i;
|
||||||
STACK_OF(X509) *chain = sk_X509_new(X509_compare);
|
STACK_OF(X509) *chain = sk_X509_new(X509_compare);
|
||||||
|
|
||||||
/* add the signer's certificate */
|
|
||||||
if (ctx->options->cert != NULL && !sk_X509_push(chain, ctx->options->cert)) {
|
|
||||||
sk_X509_free(chain);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (signer != -1 && !sk_X509_push(chain, sk_X509_value(ctx->options->certs, signer))) {
|
if (signer != -1 && !sk_X509_push(chain, sk_X509_value(ctx->options->certs, signer))) {
|
||||||
sk_X509_free(chain);
|
sk_X509_free(chain);
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -761,6 +893,9 @@ static STACK_OF(X509) *X509_chain_get_sorted(FILE_FORMAT_CTX *ctx, int signer)
|
|||||||
}
|
}
|
||||||
/* sort certificate chain using the supplied comparison function */
|
/* sort certificate chain using the supplied comparison function */
|
||||||
sk_X509_sort(chain);
|
sk_X509_sort(chain);
|
||||||
|
/* remove duplicates */
|
||||||
|
sk_X509_remove_duplicates(chain);
|
||||||
|
|
||||||
return chain;
|
return chain;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -779,6 +914,15 @@ static int X509_compare(const X509 *const *a, const X509 *const *b)
|
|||||||
size_t a_len, b_len;
|
size_t a_len, b_len;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
#if OPENSSL_VERSION_NUMBER<0x30000000L
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wincompatible-pointer-types-discards-qualifiers"
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wdiscarded-qualifiers"
|
||||||
|
#endif
|
||||||
|
#endif /* OPENSSL_VERSION_NUMBER<0x30000000L */
|
||||||
a_len = (size_t)i2d_X509(*a, NULL);
|
a_len = (size_t)i2d_X509(*a, NULL);
|
||||||
a_tmp = a_data = OPENSSL_malloc(a_len);
|
a_tmp = a_data = OPENSSL_malloc(a_len);
|
||||||
i2d_X509(*a, &a_tmp);
|
i2d_X509(*a, &a_tmp);
|
||||||
@@ -786,6 +930,13 @@ static int X509_compare(const X509 *const *a, const X509 *const *b)
|
|||||||
b_len = (size_t)i2d_X509(*b, NULL);
|
b_len = (size_t)i2d_X509(*b, NULL);
|
||||||
b_tmp = b_data = OPENSSL_malloc(b_len);
|
b_tmp = b_data = OPENSSL_malloc(b_len);
|
||||||
i2d_X509(*b, &b_tmp);
|
i2d_X509(*b, &b_tmp);
|
||||||
|
#if OPENSSL_VERSION_NUMBER<0x30000000L
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
|
#endif /* OPENSSL_VERSION_NUMBER<0x30000000L */
|
||||||
|
|
||||||
ret = memcmp(a_data, b_data, MIN(a_len, b_len));
|
ret = memcmp(a_data, b_data, MIN(a_len, b_len));
|
||||||
OPENSSL_free(a_data);
|
OPENSSL_free(a_data);
|
||||||
@@ -796,6 +947,35 @@ static int X509_compare(const X509 *const *a, const X509 *const *b)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove duplicate certificates from a sorted STACK_OF(X509).
|
||||||
|
*
|
||||||
|
* This function assumes the stack is sorted according to X.690-compliant
|
||||||
|
* certificate comparison, so duplicate certificates appear consecutively.
|
||||||
|
* It iterates through the stack and removes any duplicate certificates
|
||||||
|
* by comparing each element with its immediate predecessor.
|
||||||
|
* The stack is modified in place.
|
||||||
|
*/
|
||||||
|
static void sk_X509_remove_duplicates(STACK_OF(X509) *chain)
|
||||||
|
{
|
||||||
|
int i, n = sk_X509_num(chain);
|
||||||
|
|
||||||
|
if (n < 2)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* start from the second element */
|
||||||
|
for (i = 1; i < n; ) {
|
||||||
|
if (!X509_cmp(sk_X509_value(chain, i - 1), sk_X509_value(chain, i))) {
|
||||||
|
/* duplicate found: remove the certificate at index i */
|
||||||
|
(void)sk_X509_delete(chain, i);
|
||||||
|
n--; /* reduce stack size since one element was removed */
|
||||||
|
/* do not increment i, as next element shifts into index i */
|
||||||
|
} else {
|
||||||
|
i++; /* advance only if no removal was done */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Local Variables:
|
Local Variables:
|
||||||
c-basic-offset: 4
|
c-basic-offset: 4
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ PKCS7 *pkcs7_create(FILE_FORMAT_CTX *ctx);
|
|||||||
int add_indirect_data_object(PKCS7 *p7);
|
int add_indirect_data_object(PKCS7 *p7);
|
||||||
int sign_spc_indirect_data_content(PKCS7 *p7, ASN1_OCTET_STRING *content);
|
int sign_spc_indirect_data_content(PKCS7 *p7, ASN1_OCTET_STRING *content);
|
||||||
PKCS7 *pkcs7_set_content(ASN1_OCTET_STRING *content);
|
PKCS7 *pkcs7_set_content(ASN1_OCTET_STRING *content);
|
||||||
|
int pkcs7_get_content_digest(PKCS7 *p7, u_char *mdbuf, int *mdtype);
|
||||||
|
SpcIndirectDataContent *pkcs7_get_indirect_data_content(PKCS7 *p7);
|
||||||
|
SpcIndirectDataContent *asn1_type_get_indirect_data_content(ASN1_TYPE *content);
|
||||||
ASN1_OCTET_STRING *spc_indirect_data_content_get(BIO *hash, FILE_FORMAT_CTX *ctx);
|
ASN1_OCTET_STRING *spc_indirect_data_content_get(BIO *hash, FILE_FORMAT_CTX *ctx);
|
||||||
int pkcs7_sign_content(PKCS7 *p7, const u_char *data, int len);
|
int pkcs7_sign_content(PKCS7 *p7, const u_char *data, int len);
|
||||||
int asn1_simple_hdr_len(const u_char *p, int len);
|
int asn1_simple_hdr_len(const u_char *p, int len);
|
||||||
@@ -25,6 +28,7 @@ MsCtlContent *ms_ctl_content_get(PKCS7 *p7);
|
|||||||
ASN1_TYPE *catalog_content_get(CatalogAuthAttr *attribute);
|
ASN1_TYPE *catalog_content_get(CatalogAuthAttr *attribute);
|
||||||
SpcLink *spc_link_obsolete_get(void);
|
SpcLink *spc_link_obsolete_get(void);
|
||||||
int compare_digests(u_char *mdbuf, u_char *cmdbuf, int mdtype);
|
int compare_digests(u_char *mdbuf, u_char *cmdbuf, int mdtype);
|
||||||
|
int spc_indirect_data_content_get_digest(SpcIndirectDataContent *idc, u_char *mdbuf, int *mdtype);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Local Variables:
|
Local Variables:
|
||||||
|
|||||||
@@ -373,11 +373,18 @@ static u_char *msi_digest_calc(FILE_FORMAT_CTX *ctx, const EVP_MD *md)
|
|||||||
u_char *mdbuf = NULL;
|
u_char *mdbuf = NULL;
|
||||||
BIO *bhash = BIO_new(BIO_f_md());
|
BIO *bhash = BIO_new(BIO_f_md());
|
||||||
|
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wcast-qual"
|
||||||
|
#endif
|
||||||
if (!BIO_set_md(bhash, md)) {
|
if (!BIO_set_md(bhash, md)) {
|
||||||
fprintf(stderr, "Unable to set the message digest of BIO\n");
|
fprintf(stderr, "Unable to set the message digest of BIO\n");
|
||||||
BIO_free_all(bhash);
|
BIO_free_all(bhash);
|
||||||
return NULL; /* FAILED */
|
return NULL; /* FAILED */
|
||||||
}
|
}
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
BIO_push(bhash, BIO_new(BIO_s_null()));
|
BIO_push(bhash, BIO_new(BIO_s_null()));
|
||||||
if (!bio_hash_data(bhash, ctx->options->indata, 0, ctx->msi_ctx->fileend)) {
|
if (!bio_hash_data(bhash, ctx->options->indata, 0, ctx->msi_ctx->fileend)) {
|
||||||
fprintf(stderr, "Unable to calculate digest\n");
|
fprintf(stderr, "Unable to calculate digest\n");
|
||||||
@@ -407,30 +414,25 @@ static int msi_verify_digests(FILE_FORMAT_CTX *ctx, PKCS7 *p7)
|
|||||||
const EVP_MD *md;
|
const EVP_MD *md;
|
||||||
BIO *hash;
|
BIO *hash;
|
||||||
|
|
||||||
if (is_content_type(p7, SPC_INDIRECT_DATA_OBJID)) {
|
if (!pkcs7_get_content_digest(p7, mdbuf, &mdtype)) {
|
||||||
ASN1_STRING *content_val = p7->d.sign->contents->d.other->value.sequence;
|
|
||||||
const u_char *p = content_val->data;
|
|
||||||
SpcIndirectDataContent *idc = d2i_SpcIndirectDataContent(NULL, &p, content_val->length);
|
|
||||||
if (idc) {
|
|
||||||
if (idc->messageDigest && idc->messageDigest->digest && idc->messageDigest->digestAlgorithm) {
|
|
||||||
mdtype = OBJ_obj2nid(idc->messageDigest->digestAlgorithm->algorithm);
|
|
||||||
memcpy(mdbuf, idc->messageDigest->digest->data, (size_t)idc->messageDigest->digest->length);
|
|
||||||
}
|
|
||||||
SpcIndirectDataContent_free(idc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (mdtype == -1) {
|
|
||||||
fprintf(stderr, "Failed to extract current message digest\n\n");
|
fprintf(stderr, "Failed to extract current message digest\n\n");
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
printf("Message digest algorithm : %s\n", OBJ_nid2sn(mdtype));
|
printf("Message digest algorithm : %s\n", OBJ_nid2sn(mdtype));
|
||||||
md = EVP_get_digestbynid(mdtype);
|
md = EVP_get_digestbynid(mdtype);
|
||||||
hash = BIO_new(BIO_f_md());
|
hash = BIO_new(BIO_f_md());
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wcast-qual"
|
||||||
|
#endif
|
||||||
if (!BIO_set_md(hash, md)) {
|
if (!BIO_set_md(hash, md)) {
|
||||||
fprintf(stderr, "Unable to set the message digest of BIO\n");
|
fprintf(stderr, "Unable to set the message digest of BIO\n");
|
||||||
BIO_free_all(hash);
|
BIO_free_all(hash);
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
BIO_push(hash, BIO_new(BIO_s_null()));
|
BIO_push(hash, BIO_new(BIO_s_null()));
|
||||||
if (ctx->msi_ctx->p_msiex) {
|
if (ctx->msi_ctx->p_msiex) {
|
||||||
BIO *prehash = BIO_new(BIO_f_md());
|
BIO *prehash = BIO_new(BIO_f_md());
|
||||||
@@ -440,12 +442,19 @@ static int msi_verify_digests(FILE_FORMAT_CTX *ctx, PKCS7 *p7)
|
|||||||
BIO_free_all(prehash);
|
BIO_free_all(prehash);
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wcast-qual"
|
||||||
|
#endif
|
||||||
if (!BIO_set_md(prehash, md)) {
|
if (!BIO_set_md(prehash, md)) {
|
||||||
fprintf(stderr, "Unable to set the message digest of BIO\n");
|
fprintf(stderr, "Unable to set the message digest of BIO\n");
|
||||||
BIO_free_all(hash);
|
BIO_free_all(hash);
|
||||||
BIO_free_all(prehash);
|
BIO_free_all(prehash);
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
BIO_push(prehash, BIO_new(BIO_s_null()));
|
BIO_push(prehash, BIO_new(BIO_s_null()));
|
||||||
|
|
||||||
print_hash("Current MsiDigitalSignatureEx ", "", (u_char *)ctx->msi_ctx->p_msiex,
|
print_hash("Current MsiDigitalSignatureEx ", "", (u_char *)ctx->msi_ctx->p_msiex,
|
||||||
@@ -1785,7 +1794,6 @@ static int ministream_save(MSI_DIRENT *dirent, BIO *outdata, MSI_OUT *out)
|
|||||||
dirent->entry->startSectorLocation = out->sectorNum;
|
dirent->entry->startSectorLocation = out->sectorNum;
|
||||||
/* ministream save */
|
/* ministream save */
|
||||||
BIO_write(outdata, out->ministream, (int)out->miniStreamLen);
|
BIO_write(outdata, out->ministream, (int)out->miniStreamLen);
|
||||||
OPENSSL_free(out->ministream);
|
|
||||||
/* fill to the end with known data, such as all zeroes */
|
/* fill to the end with known data, such as all zeroes */
|
||||||
if (out->miniStreamLen % out->sectorSize > 0) {
|
if (out->miniStreamLen % out->sectorSize > 0) {
|
||||||
remain = out->sectorSize - out->miniStreamLen % out->sectorSize;
|
remain = out->sectorSize - out->miniStreamLen % out->sectorSize;
|
||||||
@@ -2186,6 +2194,8 @@ static int msiout_set(MSI_FILE *msi, uint32_t len_msi, uint32_t len_msiex, MSI_O
|
|||||||
out->header = header_new(msi->m_hdr, out);
|
out->header = header_new(msi->m_hdr, out);
|
||||||
out->minifatMemallocCount = msi->m_hdr->numMiniFATSector;
|
out->minifatMemallocCount = msi->m_hdr->numMiniFATSector;
|
||||||
out->fatMemallocCount = msi->m_hdr->numFATSector;
|
out->fatMemallocCount = msi->m_hdr->numFATSector;
|
||||||
|
out->difatMemallocCount = 0;
|
||||||
|
out->difat = NULL;
|
||||||
out->ministream = NULL;
|
out->ministream = NULL;
|
||||||
out->minifat = OPENSSL_malloc((uint64_t)out->minifatMemallocCount * out->sectorSize);
|
out->minifat = OPENSSL_malloc((uint64_t)out->minifatMemallocCount * out->sectorSize);
|
||||||
out->fat = OPENSSL_malloc((uint64_t)out->fatMemallocCount * out->sectorSize);
|
out->fat = OPENSSL_malloc((uint64_t)out->fatMemallocCount * out->sectorSize);
|
||||||
@@ -2226,7 +2236,9 @@ static int msi_file_write(MSI_FILE *msi, MSI_DIRENT *dirent, u_char *p_msi, uint
|
|||||||
out:
|
out:
|
||||||
OPENSSL_free(out.header);
|
OPENSSL_free(out.header);
|
||||||
OPENSSL_free(out.fat);
|
OPENSSL_free(out.fat);
|
||||||
|
OPENSSL_free(out.difat);
|
||||||
OPENSSL_free(out.minifat);
|
OPENSSL_free(out.minifat);
|
||||||
|
OPENSSL_free(out.ministream);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2295,11 +2307,18 @@ static int msi_calc_MsiDigitalSignatureEx(FILE_FORMAT_CTX *ctx, BIO *hash)
|
|||||||
size_t written;
|
size_t written;
|
||||||
BIO *prehash = BIO_new(BIO_f_md());
|
BIO *prehash = BIO_new(BIO_f_md());
|
||||||
|
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wcast-qual"
|
||||||
|
#endif
|
||||||
if (!BIO_set_md(prehash, ctx->options->md)) {
|
if (!BIO_set_md(prehash, ctx->options->md)) {
|
||||||
fprintf(stderr, "Unable to set the message digest of BIO\n");
|
fprintf(stderr, "Unable to set the message digest of BIO\n");
|
||||||
BIO_free_all(prehash);
|
BIO_free_all(prehash);
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
BIO_push(prehash, BIO_new(BIO_s_null()));
|
BIO_push(prehash, BIO_new(BIO_s_null()));
|
||||||
|
|
||||||
if (!msi_prehash_dir(ctx->msi_ctx->dirent, prehash, 1)) {
|
if (!msi_prehash_dir(ctx->msi_ctx->dirent, prehash, 1)) {
|
||||||
@@ -2368,8 +2387,8 @@ static int msi_hash_length_get(FILE_FORMAT_CTX *ctx)
|
|||||||
/*
|
/*
|
||||||
* Get DigitalSignature and MsiDigitalSignatureEx streams
|
* Get DigitalSignature and MsiDigitalSignatureEx streams
|
||||||
* to check if the signature exists.
|
* to check if the signature exists.
|
||||||
* [in, out] ctx: structure holds input and output datafv
|
* [in, out] ctx: structure holds input and output data
|
||||||
* [returns] 0 on error or 1 on successs
|
* [returns] 0 on error or 1 on success
|
||||||
*/
|
*/
|
||||||
static int msi_check_file(FILE_FORMAT_CTX *ctx)
|
static int msi_check_file(FILE_FORMAT_CTX *ctx)
|
||||||
{
|
{
|
||||||
|
|||||||
+1045
-648
File diff suppressed because it is too large
Load Diff
+22
-4
@@ -66,7 +66,9 @@
|
|||||||
#include <openssl/rand.h>
|
#include <openssl/rand.h>
|
||||||
#include <openssl/safestack.h>
|
#include <openssl/safestack.h>
|
||||||
#include <openssl/ssl.h>
|
#include <openssl/ssl.h>
|
||||||
|
#include <openssl/store.h>
|
||||||
#include <openssl/ts.h>
|
#include <openssl/ts.h>
|
||||||
|
#include <openssl/ui.h>
|
||||||
#include <openssl/x509.h>
|
#include <openssl/x509.h>
|
||||||
#include <openssl/x509v3.h> /* X509_PURPOSE */
|
#include <openssl/x509v3.h> /* X509_PURPOSE */
|
||||||
|
|
||||||
@@ -90,7 +92,9 @@
|
|||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
/* not WIN32, because strcasecmp exists in MinGW */
|
/* not WIN32, because strcasecmp exists in MinGW */
|
||||||
#define strcasecmp _stricmp
|
#define strcasecmp _stricmp
|
||||||
#endif
|
#define fseeko _fseeki64
|
||||||
|
#define ftello _ftelli64
|
||||||
|
#endif /* _MSC_VER */
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
#define remove_file(filename) _unlink(filename)
|
#define remove_file(filename) _unlink(filename)
|
||||||
@@ -242,6 +246,16 @@ typedef enum {
|
|||||||
|
|
||||||
typedef unsigned char u_char;
|
typedef unsigned char u_char;
|
||||||
|
|
||||||
|
#ifndef OPENSSL_NO_ENGINE
|
||||||
|
typedef struct {
|
||||||
|
ASN1_OCTET_STRING *cmd;
|
||||||
|
ASN1_OCTET_STRING *param;
|
||||||
|
} EngineControl;
|
||||||
|
|
||||||
|
DECLARE_ASN1_FUNCTIONS(EngineControl)
|
||||||
|
DEFINE_STACK_OF(EngineControl)
|
||||||
|
#endif /* OPENSSL_NO_ENGINE */
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char *infile;
|
char *infile;
|
||||||
char *outfile;
|
char *outfile;
|
||||||
@@ -254,10 +268,13 @@ typedef struct {
|
|||||||
int output_pkcs7;
|
int output_pkcs7;
|
||||||
#ifndef OPENSSL_NO_ENGINE
|
#ifndef OPENSSL_NO_ENGINE
|
||||||
char *p11engine;
|
char *p11engine;
|
||||||
char *p11module;
|
STACK_OF(EngineControl) *engine_ctrls;
|
||||||
char *p11cert;
|
|
||||||
int login;
|
int login;
|
||||||
#endif /* OPENSSL_NO_ENGINE */
|
#endif /* OPENSSL_NO_ENGINE */
|
||||||
|
#if !defined(OPENSSL_NO_ENGINE) || OPENSSL_VERSION_NUMBER>=0x30000000L
|
||||||
|
char *p11module;
|
||||||
|
char *p11cert;
|
||||||
|
#endif /* !defined(OPENSSL_NO_ENGINE) || OPENSSL_VERSION_NUMBER>=0x30000000L */
|
||||||
int askpass;
|
int askpass;
|
||||||
char *readpass;
|
char *readpass;
|
||||||
char *pass;
|
char *pass;
|
||||||
@@ -274,6 +291,7 @@ typedef struct {
|
|||||||
char *proxy;
|
char *proxy;
|
||||||
int noverifypeer;
|
int noverifypeer;
|
||||||
int addBlob;
|
int addBlob;
|
||||||
|
const char *blob_file;
|
||||||
int nest;
|
int nest;
|
||||||
int index;
|
int index;
|
||||||
int ignore_timestamp;
|
int ignore_timestamp;
|
||||||
@@ -292,9 +310,9 @@ typedef struct {
|
|||||||
int jp;
|
int jp;
|
||||||
#if OPENSSL_VERSION_NUMBER>=0x30000000L
|
#if OPENSSL_VERSION_NUMBER>=0x30000000L
|
||||||
int legacy;
|
int legacy;
|
||||||
|
char *provider;
|
||||||
#endif /* OPENSSL_VERSION_NUMBER>=0x30000000L */
|
#endif /* OPENSSL_VERSION_NUMBER>=0x30000000L */
|
||||||
EVP_PKEY *pkey;
|
EVP_PKEY *pkey;
|
||||||
X509 *cert;
|
|
||||||
STACK_OF(X509) *certs;
|
STACK_OF(X509) *certs;
|
||||||
STACK_OF(X509) *xcerts;
|
STACK_OF(X509) *xcerts;
|
||||||
STACK_OF(X509_CRL) *crls;
|
STACK_OF(X509_CRL) *crls;
|
||||||
|
|||||||
+554
@@ -0,0 +1,554 @@
|
|||||||
|
---
|
||||||
|
title: osslsigncode
|
||||||
|
lang: en-US
|
||||||
|
---
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
|
||||||
|
osslsigncode - Authenticode signing, timestamping, extraction, attachment, removal, and verification tool
|
||||||
|
|
||||||
|
# SYNOPSIS
|
||||||
|
|
||||||
|
`osslsigncode` [`--help`] [`--version`]
|
||||||
|
|
||||||
|
`osslsigncode` `sign`
|
||||||
|
[`-certs` *file* | `-spc` *file* | `-pkcs12` *file*]
|
||||||
|
[`-key` *file-or-URI*]
|
||||||
|
[`-ac` *file*]
|
||||||
|
[`-pass` *password* | `-readpass` *file* | `-askpass`]
|
||||||
|
[`-pkcs11module` *module*] [`-pkcs11cert` *URI*]
|
||||||
|
[`-engine` *engine*] [`-provider` *provider*]
|
||||||
|
[`-login`] [`-engineCtrl` *command*[:*parameter*]]
|
||||||
|
[`-h` *digest*]
|
||||||
|
[`-n` *description*] [`-i` *URL*]
|
||||||
|
[`-jp` `low`] [`-comm`] [`-ph`]
|
||||||
|
[`-t` *URL* ... | `-ts` *URL* ...]
|
||||||
|
[`-TSA-certs` *file* `-TSA-key` *file-or-URI* [`-TSA-time` *unix-time*]]
|
||||||
|
[`-HTTPS-CAfile` *file*] [`-HTTPS-CRLfile` *file*]
|
||||||
|
[`-time` *unix-time*]
|
||||||
|
[`-addUnauthenticatedBlob` [`-blobFile` *file*]]
|
||||||
|
[`-nest`] [`-add-msi-dse`] [`-verbose`] [`-pem`]
|
||||||
|
`-in` *input* `-out` *output*
|
||||||
|
|
||||||
|
`osslsigncode` `extract-data`
|
||||||
|
[`-pem`] [`-h` *digest*] [`-ph`] [`-add-msi-dse`]
|
||||||
|
`-in` *input* `-out` *output*
|
||||||
|
|
||||||
|
`osslsigncode` `add`
|
||||||
|
[`-addUnauthenticatedBlob` [`-blobFile` *file*]]
|
||||||
|
[`-t` *URL* ... | `-ts` *URL* ...]
|
||||||
|
[`-TSA-certs` *file* `-TSA-key` *file-or-URI* [`-TSA-time` *unix-time*]]
|
||||||
|
[`-HTTPS-CAfile` *file*] [`-HTTPS-CRLfile` *file*]
|
||||||
|
[`-h` *digest*] [`-index` *n*] [`-verbose`] [`-add-msi-dse`]
|
||||||
|
`-in` *input* `-out` *output*
|
||||||
|
|
||||||
|
`osslsigncode` `attach-signature`
|
||||||
|
`-sigin` *signature*
|
||||||
|
[`-h` *digest*] [`-nest`] [`-add-msi-dse`]
|
||||||
|
`-in` *input* `-out` *output*
|
||||||
|
|
||||||
|
`osslsigncode` `extract-signature`
|
||||||
|
[`-pem`]
|
||||||
|
`-in` *input* `-out` *output*
|
||||||
|
|
||||||
|
`osslsigncode` `remove-signature`
|
||||||
|
`-in` *input* `-out` *output*
|
||||||
|
|
||||||
|
`osslsigncode` `verify`
|
||||||
|
`-in` *input*
|
||||||
|
[`-c` | `-catalog` *catalog-file*]
|
||||||
|
[`-CAfile` *file*] [`-CRLfile` *file*]
|
||||||
|
[`-HTTPS-CAfile` *file*] [`-HTTPS-CRLfile` *file*]
|
||||||
|
[`-TSA-CAfile` *file*] [`-TSA-CRLfile` *file*]
|
||||||
|
[`-p` *proxy*] [`-index` *n*]
|
||||||
|
[`-ignore-timestamp`] [`-ignore-cdp`] [`-ignore-crl`]
|
||||||
|
[`-time` *unix-time*]
|
||||||
|
[`-require-leaf-hash` *alg*:*hex*]
|
||||||
|
[`-verbose`]
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
`osslsigncode` signs and verifies Microsoft Authenticode signatures on
|
||||||
|
supported file formats. It can also extract data for detached signing,
|
||||||
|
attach an externally produced signature, add timestamps or unauthenticated
|
||||||
|
blobs to an existing signature, and remove an embedded signature.
|
||||||
|
|
||||||
|
Supported input formats include PE files such as EXE, DLL, and SYS, CAB,
|
||||||
|
CAT, MSI, APPX, and several script file types, including `.ps1`, `.ps1xml`,
|
||||||
|
`.psc1`, `.psd1`, `.psm1`, `.cdxml`, `.mof`, and `.js`.
|
||||||
|
|
||||||
|
The program supports these common workflows:
|
||||||
|
|
||||||
|
- direct signing of an unsigned file
|
||||||
|
- detached signing via `extract-data`, `sign`, and `attach-signature`
|
||||||
|
- post-sign timestamping with `add`
|
||||||
|
- verification of embedded signatures or catalog signatures with `verify`
|
||||||
|
|
||||||
|
If no subcommand is given, `sign` is assumed.
|
||||||
|
|
||||||
|
# FORMATS
|
||||||
|
|
||||||
|
Support is not identical across all file formats.
|
||||||
|
|
||||||
|
In particular, detached-signature workflows, nested signatures, catalog-based
|
||||||
|
verification, and signature removal are format-dependent features. A command
|
||||||
|
that is valid for one supported file type may be unsupported for another.
|
||||||
|
|
||||||
|
CAT files are a special case. They are detached catalog containers for
|
||||||
|
hashes of other files, not ordinary embedded-signature payloads. A CAT
|
||||||
|
file is itself a PKCS#7 structure containing authenticated entries for one
|
||||||
|
or more external files. In practice, the catalog signs file digests
|
||||||
|
recorded in the catalog, rather than embedding a signature into each
|
||||||
|
covered file.
|
||||||
|
|
||||||
|
Because of this, CAT files behave differently from embedded-signature
|
||||||
|
formats. They do not support `attach-signature`, `remove-signature`,
|
||||||
|
`extract-data`, or nested signatures.
|
||||||
|
|
||||||
|
MSI files are also a special case. They support an extended signature mode
|
||||||
|
controlled by `-add-msi-dse`. In this mode, the MSI signature covers file
|
||||||
|
metadata as well as file content. Detached-signing workflows and any later
|
||||||
|
re-signing or nesting operations must use a mode consistent with the MSI
|
||||||
|
file's existing signature structure.
|
||||||
|
|
||||||
|
# COMMANDS
|
||||||
|
|
||||||
|
## `sign`
|
||||||
|
|
||||||
|
Create a new Authenticode signature.
|
||||||
|
|
||||||
|
This command can sign a normal unsigned file, or it can sign PKCS#7 data
|
||||||
|
previously produced by `extract-data`.
|
||||||
|
|
||||||
|
## `extract-data`
|
||||||
|
|
||||||
|
Extract the PKCS#7 content to be signed later. This is used for detached
|
||||||
|
signing workflows.
|
||||||
|
|
||||||
|
## `add`
|
||||||
|
|
||||||
|
Add unauthenticated attributes to an existing signature, typically an
|
||||||
|
Authenticode timestamp, an RFC 3161 timestamp, or an unauthenticated blob.
|
||||||
|
|
||||||
|
With `-index`, the selected signature in a multi-signature file is updated.
|
||||||
|
|
||||||
|
## `attach-signature`
|
||||||
|
|
||||||
|
Attach a detached PKCS#7 signature to an input file.
|
||||||
|
|
||||||
|
With `-nest`, the new signature is attached as a nested signature instead of
|
||||||
|
replacing the primary one, if the file format supports nested signatures.
|
||||||
|
|
||||||
|
## `extract-signature`
|
||||||
|
|
||||||
|
Extract the embedded PKCS#7 signature from a signed file.
|
||||||
|
|
||||||
|
## `remove-signature`
|
||||||
|
|
||||||
|
Remove the embedded signature from a signed file.
|
||||||
|
|
||||||
|
## `verify`
|
||||||
|
|
||||||
|
Verify an embedded signature or a catalog signature.
|
||||||
|
|
||||||
|
Verification may include digest consistency, certificate chain validation,
|
||||||
|
certificate revocation checking, timestamp validation, and optional checking
|
||||||
|
of the signer's leaf certificate hash.
|
||||||
|
|
||||||
|
When verifying that a file is covered by a catalog, use `verify -catalog
|
||||||
|
catalog.cat -in file`. Verifying the CAT file by itself validates the
|
||||||
|
catalog signature; verifying with `-catalog` checks whether the specified
|
||||||
|
input file is covered by that catalog.
|
||||||
|
|
||||||
|
# OPTIONS
|
||||||
|
|
||||||
|
Some options are available only in particular builds or OpenSSL versions.
|
||||||
|
In particular, `-askpass` is build-dependent, `-provider` and `-nolegacy`
|
||||||
|
require OpenSSL 3, and engine-related options depend on engine support in the
|
||||||
|
build.
|
||||||
|
|
||||||
|
## General options
|
||||||
|
|
||||||
|
`--help`
|
||||||
|
: Show help text. With a subcommand, show help for that subcommand.
|
||||||
|
|
||||||
|
`-v`, `--version`
|
||||||
|
: Show version information.
|
||||||
|
|
||||||
|
`-in` *file*
|
||||||
|
: Input file.
|
||||||
|
|
||||||
|
`-out` *file*
|
||||||
|
: Output file. Required for all commands except `verify`.
|
||||||
|
|
||||||
|
`-verbose`
|
||||||
|
: Produce more detailed diagnostic output.
|
||||||
|
|
||||||
|
## Signing material
|
||||||
|
|
||||||
|
`-pkcs12` *file*
|
||||||
|
: Read the signing certificate and private key from a PKCS#12 container.
|
||||||
|
|
||||||
|
`-certs`, `-spc` *file*
|
||||||
|
: Read the signing certificate chain. The historical alias `-spc` is accepted.
|
||||||
|
|
||||||
|
`-key` *file-or-URI*
|
||||||
|
: Read the private key. This may also be a store or PKCS#11 URI.
|
||||||
|
|
||||||
|
`-ac` *file*
|
||||||
|
: Add extra certificates to the signature block.
|
||||||
|
|
||||||
|
`-pass` *password*
|
||||||
|
: Password or PIN for the key, token, or PKCS#12 container.
|
||||||
|
|
||||||
|
`-readpass` *file*
|
||||||
|
: Read the password or PIN from *file*. Use `-` to read from standard input.
|
||||||
|
|
||||||
|
`-askpass`
|
||||||
|
: Prompt for the password interactively.
|
||||||
|
|
||||||
|
## PKCS#11, engines, and providers
|
||||||
|
|
||||||
|
`-pkcs11module` *module*
|
||||||
|
: Path to a PKCS#11 module.
|
||||||
|
|
||||||
|
`-pkcs11cert` *URI*
|
||||||
|
: PKCS#11 URI identifying the certificate object.
|
||||||
|
|
||||||
|
`-provider` *provider*
|
||||||
|
: OpenSSL 3 provider to load. This is the preferred modern interface for
|
||||||
|
provider-based PKCS#11 use.
|
||||||
|
|
||||||
|
`-engine`, `-pkcs11engine` *engine*
|
||||||
|
: OpenSSL engine identifier or path to a dynamic engine module. This
|
||||||
|
interface is retained for compatibility with builds and deployments that
|
||||||
|
still support engines.
|
||||||
|
|
||||||
|
`-login`
|
||||||
|
: Force login to the token for engine-based PKCS#11 use.
|
||||||
|
|
||||||
|
`-engineCtrl` *command*[:*parameter*]
|
||||||
|
: Pass a control command to the selected engine.
|
||||||
|
|
||||||
|
`-nolegacy`
|
||||||
|
: On OpenSSL 3 builds, do not automatically load the legacy provider.
|
||||||
|
|
||||||
|
## Signature contents and digest control
|
||||||
|
|
||||||
|
`-h` `md5` | `sha1` | `sha2` | `sha256` | `sha384` | `sha512`
|
||||||
|
: Select the digest algorithm. The default is `sha256`. `sha2` and
|
||||||
|
`sha256` are equivalent.
|
||||||
|
|
||||||
|
`-n` *description*
|
||||||
|
: Description of the signed content.
|
||||||
|
|
||||||
|
`-i` *URL*
|
||||||
|
: Informational URL associated with the signed content.
|
||||||
|
|
||||||
|
`-comm`
|
||||||
|
: Use Microsoft Commercial Code Signing purpose instead of the default
|
||||||
|
individual purpose.
|
||||||
|
|
||||||
|
`-jp` `low`
|
||||||
|
: Add the Java CAB permission attribute. Only `low` is currently supported.
|
||||||
|
|
||||||
|
`-ph`
|
||||||
|
: Generate page hashes for executable files.
|
||||||
|
|
||||||
|
`-add-msi-dse`
|
||||||
|
: For MSI files, enable the `MsiDigitalSignatureEx` signing mode. In this
|
||||||
|
mode, the signature covers MSI metadata as well as file content. The
|
||||||
|
metadata portion includes stream names, sizes, and selected timestamps in
|
||||||
|
the MSI structure. This option changes the MSI signature format and should
|
||||||
|
be used consistently in any detached-signing workflow involving
|
||||||
|
`extract-data`, `sign`, `attach-signature`, or `add`.
|
||||||
|
|
||||||
|
For a newly signed MSI, this mode is generally preferred because it extends
|
||||||
|
signing coverage beyond file content alone. For an already signed MSI,
|
||||||
|
however, the chosen mode must match the file's existing signature
|
||||||
|
structure. Switching between basic MSI signing and `MsiDigitalSignatureEx`
|
||||||
|
during re-signing or nested-signature operations can invalidate the
|
||||||
|
existing signature.
|
||||||
|
|
||||||
|
`-pem`
|
||||||
|
: Write PKCS#7 output in PEM format instead of DER.
|
||||||
|
|
||||||
|
## Timestamping and network options
|
||||||
|
|
||||||
|
The following timestamping modes are **mutually exclusive** within a single
|
||||||
|
`sign` or `add` invocation:
|
||||||
|
|
||||||
|
- Authenticode timestamping with `-t`
|
||||||
|
- RFC 3161 timestamping with `-ts`
|
||||||
|
- built-in RFC 3161 timestamp generation with `-TSA-certs` and `-TSA-key`
|
||||||
|
|
||||||
|
`-t` *URL*
|
||||||
|
: Add an Authenticode timestamp from the specified URL. May be repeated.
|
||||||
|
|
||||||
|
`-ts` *URL*
|
||||||
|
: Add an RFC 3161 timestamp from the specified URL. May be repeated.
|
||||||
|
|
||||||
|
`-p` *proxy*
|
||||||
|
: Proxy used for timestamp or CRL retrieval.
|
||||||
|
|
||||||
|
`-noverifypeer`
|
||||||
|
: Do not verify the TLS certificate of the remote timestamp service.
|
||||||
|
|
||||||
|
`-HTTPS-CAfile` *file*
|
||||||
|
: PEM bundle used to verify HTTPS peers contacted by `osslsigncode`.
|
||||||
|
|
||||||
|
`-HTTPS-CRLfile` *file*
|
||||||
|
: PEM CRL file used while verifying HTTPS peers.
|
||||||
|
|
||||||
|
`-TSA-certs` *file*
|
||||||
|
: PEM certificate chain for locally generated RFC 3161 timestamps.
|
||||||
|
|
||||||
|
`-TSA-key` *file-or-URI*
|
||||||
|
: Private key for locally generated RFC 3161 timestamps.
|
||||||
|
|
||||||
|
`-TSA-time` *unix-time*
|
||||||
|
: Timestamp time for locally generated RFC 3161 responses.
|
||||||
|
|
||||||
|
## Nested signatures and indexed operations
|
||||||
|
|
||||||
|
`-nest`
|
||||||
|
: Add a nested signature instead of replacing the primary signature.
|
||||||
|
|
||||||
|
`-index` *n*
|
||||||
|
: Select a signature by index for `add` or `verify`. Index 0 is the primary
|
||||||
|
signature.
|
||||||
|
|
||||||
|
## Unauthenticated blob options
|
||||||
|
|
||||||
|
`-addUnauthenticatedBlob`
|
||||||
|
: Add an unauthenticated blob to the signature.
|
||||||
|
|
||||||
|
`-blobFile` *file*
|
||||||
|
: Read blob contents from *file*. If omitted, a placeholder blob is created.
|
||||||
|
|
||||||
|
## Verification options
|
||||||
|
|
||||||
|
`-c`, `-catalog` *file*
|
||||||
|
: Verify the input file against the specified catalog file.
|
||||||
|
|
||||||
|
`-CAfile` *file*
|
||||||
|
: PEM bundle of trusted CA certificates for signer validation.
|
||||||
|
|
||||||
|
`-CRLfile` *file*
|
||||||
|
: PEM file containing CRLs for signer validation.
|
||||||
|
|
||||||
|
`-TSA-CAfile`, `-untrusted` *file*
|
||||||
|
: PEM bundle of trusted CA certificates for timestamp validation.
|
||||||
|
|
||||||
|
`-TSA-CRLfile`, `-CRLuntrusted` *file*
|
||||||
|
: PEM file containing CRLs for timestamp validation.
|
||||||
|
|
||||||
|
`-time`, `-st` *unix-time*
|
||||||
|
: Verification time. If a valid timestamp is present and used, chain
|
||||||
|
validation is normally performed at the timestamp time.
|
||||||
|
|
||||||
|
`-ignore-timestamp`
|
||||||
|
: Skip verification of the timestamp signature.
|
||||||
|
|
||||||
|
`-ignore-cdp`
|
||||||
|
: Do not fetch CRLs from CRL Distribution Points.
|
||||||
|
|
||||||
|
`-ignore-crl`
|
||||||
|
: Disable CRL retrieval and CRL validation.
|
||||||
|
|
||||||
|
`-require-leaf-hash` *alg*:*hex*
|
||||||
|
: Require the signer's leaf certificate to hash to the specified value.
|
||||||
|
The hash is computed over the DER encoding of the leaf certificate.
|
||||||
|
|
||||||
|
# EXIT STATUS
|
||||||
|
|
||||||
|
`0`
|
||||||
|
: Success.
|
||||||
|
|
||||||
|
non-zero
|
||||||
|
: Failure.
|
||||||
|
|
||||||
|
# DIAGNOSTICS
|
||||||
|
|
||||||
|
Common causes of failure include:
|
||||||
|
|
||||||
|
missing CA trust bundle
|
||||||
|
: On Unix-like systems, `verify` expects a readable CA bundle, either from
|
||||||
|
`-CAfile` or from a detected system default.
|
||||||
|
|
||||||
|
detached-signing mismatch
|
||||||
|
: `extract-data`, `sign`, and `attach-signature` must use compatible
|
||||||
|
digest-affecting options such as `-h`, and where relevant `-ph` and
|
||||||
|
`-add-msi-dse`.
|
||||||
|
|
||||||
|
unsupported format feature
|
||||||
|
: Some file formats do not support every subcommand or every signature mode.
|
||||||
|
|
||||||
|
missing TSA trust chain
|
||||||
|
: Timestamp verification may fail unless the appropriate TSA trust anchors
|
||||||
|
are supplied with `-TSA-CAfile`, and where needed `-TSA-CRLfile`.
|
||||||
|
|
||||||
|
conflicting timestamp modes
|
||||||
|
: `-t`, `-ts`, and built-in TSA signing cannot be combined in one command.
|
||||||
|
|
||||||
|
MSI signature mode mismatch
|
||||||
|
: Re-signing or nesting an MSI signature must be consistent with whether the
|
||||||
|
file already uses `MsiDigitalSignatureEx`. Mixing modes may invalidate the
|
||||||
|
existing signature.
|
||||||
|
|
||||||
|
# ENVIRONMENT
|
||||||
|
|
||||||
|
`HTTP_PROXY`, `http_proxy`
|
||||||
|
: Default proxy for HTTP access if `-p` is not given.
|
||||||
|
|
||||||
|
`HTTPS_PROXY`, `https_proxy`
|
||||||
|
: Default proxy for HTTPS access if `-p` is not given.
|
||||||
|
|
||||||
|
`OPENSSL_ENGINES`
|
||||||
|
: May help OpenSSL find engine modules.
|
||||||
|
|
||||||
|
# FILES
|
||||||
|
|
||||||
|
On Unix-like systems, `osslsigncode` tries common CA bundle locations for
|
||||||
|
its default `-CAfile`, including:
|
||||||
|
|
||||||
|
- `/etc/ssl/certs/ca-certificates.crt`
|
||||||
|
- `/etc/pki/tls/certs/ca-bundle.crt`
|
||||||
|
- `/usr/share/ssl/certs/ca-bundle.crt`
|
||||||
|
- `/usr/local/share/certs/ca-root-nss.crt`
|
||||||
|
- `/etc/ssl/cert.pem`
|
||||||
|
|
||||||
|
If no readable CA bundle is available, `verify` may require an explicit
|
||||||
|
`-CAfile`.
|
||||||
|
|
||||||
|
# NOTES
|
||||||
|
|
||||||
|
Use `extract-data` when you need to create a new detached signature object.
|
||||||
|
Use `extract-signature` when you need to copy an existing embedded PKCS#7
|
||||||
|
signature out of a file.
|
||||||
|
|
||||||
|
For safer secret handling, prefer `-readpass` or `-askpass` over `-pass`.
|
||||||
|
|
||||||
|
Data added with `-addUnauthenticatedBlob` is not protected by the signature
|
||||||
|
and must not be treated as trusted.
|
||||||
|
|
||||||
|
For new MSI signatures, `-add-msi-dse` is generally preferred because it
|
||||||
|
extends signing coverage to MSI metadata as well as file content. However,
|
||||||
|
it is format-affecting rather than cosmetic, so existing signed MSI files
|
||||||
|
should be re-signed only in a mode consistent with their current signature
|
||||||
|
structure.
|
||||||
|
|
||||||
|
Output files are not overwritten.
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
## Sign and verify a file
|
||||||
|
|
||||||
|
```sh
|
||||||
|
osslsigncode sign \
|
||||||
|
-pkcs12 signer.p12 \
|
||||||
|
-readpass p12-pass.txt \
|
||||||
|
-n "Example Application" \
|
||||||
|
-i "https://example.com/" \
|
||||||
|
-ts "https://tsa.example.net/" \
|
||||||
|
-in app.exe \
|
||||||
|
-out app-signed.exe
|
||||||
|
|
||||||
|
osslsigncode verify \
|
||||||
|
-CAfile ca-bundle.pem \
|
||||||
|
-TSA-CAfile tsa-ca-bundle.pem \
|
||||||
|
-in app-signed.exe
|
||||||
|
```
|
||||||
|
|
||||||
|
## Detached signing workflow
|
||||||
|
|
||||||
|
```sh
|
||||||
|
osslsigncode extract-data \
|
||||||
|
-h sha384 \
|
||||||
|
-ph \
|
||||||
|
-in app.exe \
|
||||||
|
-out app-data.der
|
||||||
|
|
||||||
|
osslsigncode sign \
|
||||||
|
-pkcs12 signer.p12 \
|
||||||
|
-readpass p12-pass.txt \
|
||||||
|
-h sha384 \
|
||||||
|
-in app-data.der \
|
||||||
|
-out app-sig.der
|
||||||
|
|
||||||
|
osslsigncode attach-signature \
|
||||||
|
-h sha384 \
|
||||||
|
-sigin app-sig.der \
|
||||||
|
-in app.exe \
|
||||||
|
-out app-signed.exe
|
||||||
|
|
||||||
|
osslsigncode verify \
|
||||||
|
-CAfile ca-bundle.pem \
|
||||||
|
-in app-signed.exe
|
||||||
|
```
|
||||||
|
|
||||||
|
## Sign a new MSI with extended MSI metadata coverage
|
||||||
|
|
||||||
|
```sh
|
||||||
|
osslsigncode sign \
|
||||||
|
-pkcs12 signer.p12 \
|
||||||
|
-readpass p12-pass.txt \
|
||||||
|
-add-msi-dse \
|
||||||
|
-in installer.msi \
|
||||||
|
-out installer-signed.msi
|
||||||
|
```
|
||||||
|
|
||||||
|
## Use a PKCS#11 provider
|
||||||
|
|
||||||
|
```sh
|
||||||
|
osslsigncode sign \
|
||||||
|
-provider /path/to/pkcs11prov.so \
|
||||||
|
-pkcs11module /path/to/opensc-pkcs11.so \
|
||||||
|
-pkcs11cert 'pkcs11:token=my-token;object=cert' \
|
||||||
|
-key 'pkcs11:token=my-token;object=key' \
|
||||||
|
-readpass pin.txt \
|
||||||
|
-in app.exe \
|
||||||
|
-out app-signed.exe
|
||||||
|
```
|
||||||
|
|
||||||
|
## Add a timestamp to an already signed file
|
||||||
|
|
||||||
|
```sh
|
||||||
|
osslsigncode add \
|
||||||
|
-ts "https://tsa.example.net/" \
|
||||||
|
-in app-signed.exe \
|
||||||
|
-out app-signed-ts.exe
|
||||||
|
```
|
||||||
|
|
||||||
|
## Verify that a file is covered by a catalog
|
||||||
|
|
||||||
|
```sh
|
||||||
|
osslsigncode verify \
|
||||||
|
-catalog drivers.cat \
|
||||||
|
-CAfile ca-bundle.pem \
|
||||||
|
-CRLfile ca-crl.pem \
|
||||||
|
-in driver.sys
|
||||||
|
```
|
||||||
|
|
||||||
|
# REPORTING BUGS
|
||||||
|
|
||||||
|
Report bugs and suspected issues via the project issue tracker:
|
||||||
|
|
||||||
|
<https://github.com/mtrojnar/osslsigncode/issues>
|
||||||
|
|
||||||
|
# AUTHORS
|
||||||
|
|
||||||
|
Originally written by Per Allansson.
|
||||||
|
|
||||||
|
Maintained and extended by Michał Trojnara.
|
||||||
|
|
||||||
|
Major contributions by Małgorzata Olszówka.
|
||||||
|
|
||||||
|
Additional contributions by other project contributors.
|
||||||
|
|
||||||
|
# SEE ALSO
|
||||||
|
|
||||||
|
**OpenSSL** Library
|
||||||
|
|
||||||
|
<https://openssl-library.org/>
|
||||||
|
|
||||||
@@ -87,6 +87,7 @@ static uint32_t pe_calc_checksum(BIO *bio, uint32_t header_size);
|
|||||||
static uint32_t pe_calc_realchecksum(FILE_FORMAT_CTX *ctx);
|
static uint32_t pe_calc_realchecksum(FILE_FORMAT_CTX *ctx);
|
||||||
static int pe_modify_header(FILE_FORMAT_CTX *ctx, BIO *hash, BIO *outdata);
|
static int pe_modify_header(FILE_FORMAT_CTX *ctx, BIO *hash, BIO *outdata);
|
||||||
static BIO *pe_digest_calc_bio(FILE_FORMAT_CTX *ctx, const EVP_MD *md);
|
static BIO *pe_digest_calc_bio(FILE_FORMAT_CTX *ctx, const EVP_MD *md);
|
||||||
|
static int pkcs7_get_page_hash(PKCS7 *p7, u_char **ph, int *phlen, int *phtype);
|
||||||
static int pe_page_hash_get(u_char **ph, int *phlen, int *phtype, SpcAttributeTypeAndOptionalValue *obj);
|
static int pe_page_hash_get(u_char **ph, int *phlen, int *phtype, SpcAttributeTypeAndOptionalValue *obj);
|
||||||
static u_char *pe_page_hash_calc(int *rphlen, FILE_FORMAT_CTX *ctx, int phtype);
|
static u_char *pe_page_hash_calc(int *rphlen, FILE_FORMAT_CTX *ctx, int phtype);
|
||||||
static int pe_verify_page_hash(FILE_FORMAT_CTX *ctx, u_char *ph, int phlen, int phtype);
|
static int pe_verify_page_hash(FILE_FORMAT_CTX *ctx, u_char *ph, int phlen, int phtype);
|
||||||
@@ -163,8 +164,10 @@ static ASN1_OBJECT *pe_spc_image_data_get(u_char **p, int *plen, FILE_FORMAT_CTX
|
|||||||
if (EVP_MD_size(ctx->options->md) > EVP_MD_size(EVP_sha1()))
|
if (EVP_MD_size(ctx->options->md) > EVP_MD_size(EVP_sha1()))
|
||||||
phtype = NID_sha256;
|
phtype = NID_sha256;
|
||||||
link = pe_page_hash_link_get(ctx, phtype);
|
link = pe_page_hash_link_get(ctx, phtype);
|
||||||
if (!link)
|
if (!link) {
|
||||||
|
SpcPeImageData_free(pid);
|
||||||
return NULL; /* FAILED */
|
return NULL; /* FAILED */
|
||||||
|
}
|
||||||
pid->file = link;
|
pid->file = link;
|
||||||
} else {
|
} else {
|
||||||
pid->file = spc_link_obsolete_get();
|
pid->file = spc_link_obsolete_get();
|
||||||
@@ -245,49 +248,34 @@ static int pe_verify_digests(FILE_FORMAT_CTX *ctx, PKCS7 *p7)
|
|||||||
u_char *cmdbuf = NULL;
|
u_char *cmdbuf = NULL;
|
||||||
u_char *ph = NULL;
|
u_char *ph = NULL;
|
||||||
|
|
||||||
if (is_content_type(p7, SPC_INDIRECT_DATA_OBJID)) {
|
if (!pkcs7_get_content_digest(p7, mdbuf, &mdtype)) {
|
||||||
ASN1_STRING *content_val = p7->d.sign->contents->d.other->value.sequence;
|
|
||||||
const u_char *p = content_val->data;
|
|
||||||
SpcIndirectDataContent *idc = d2i_SpcIndirectDataContent(NULL, &p, content_val->length);
|
|
||||||
if (idc) {
|
|
||||||
if (!pe_page_hash_get(&ph, &phlen, &phtype, idc->data)) {
|
|
||||||
fprintf(stderr, "Failed to extract a page hash\n\n");
|
|
||||||
SpcIndirectDataContent_free(idc);
|
|
||||||
return 0; /* FAILED */
|
|
||||||
}
|
|
||||||
if (idc->messageDigest && idc->messageDigest->digest && idc->messageDigest->digestAlgorithm) {
|
|
||||||
mdtype = OBJ_obj2nid(idc->messageDigest->digestAlgorithm->algorithm);
|
|
||||||
memcpy(mdbuf, idc->messageDigest->digest->data, (size_t)idc->messageDigest->digest->length);
|
|
||||||
}
|
|
||||||
SpcIndirectDataContent_free(idc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (mdtype == -1) {
|
|
||||||
fprintf(stderr, "Failed to extract current message digest\n\n");
|
fprintf(stderr, "Failed to extract current message digest\n\n");
|
||||||
OPENSSL_free(ph);
|
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
md = EVP_get_digestbynid(mdtype);
|
md = EVP_get_digestbynid(mdtype);
|
||||||
cmdbuf = pe_digest_calc(ctx, md);
|
cmdbuf = pe_digest_calc(ctx, md);
|
||||||
if (!cmdbuf) {
|
if (!cmdbuf) {
|
||||||
fprintf(stderr, "Failed to calculate message digest\n\n");
|
fprintf(stderr, "Failed to calculate message digest\n\n");
|
||||||
OPENSSL_free(ph);
|
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
if (!compare_digests(mdbuf, cmdbuf, mdtype)) {
|
if (!compare_digests(mdbuf, cmdbuf, mdtype)) {
|
||||||
fprintf(stderr, "Signature verification: failed\n\n");
|
fprintf(stderr, "Signature verification: failed\n\n");
|
||||||
OPENSSL_free(ph);
|
|
||||||
OPENSSL_free(cmdbuf);
|
OPENSSL_free(cmdbuf);
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
|
OPENSSL_free(cmdbuf);
|
||||||
|
|
||||||
|
if (!pkcs7_get_page_hash(p7, &ph, &phlen, &phtype)) {
|
||||||
|
fprintf(stderr, "Failed to extract page hash\n\n");
|
||||||
|
return 0; /* FAILED */
|
||||||
|
}
|
||||||
if (!pe_verify_page_hash(ctx, ph, phlen, phtype)) {
|
if (!pe_verify_page_hash(ctx, ph, phlen, phtype)) {
|
||||||
fprintf(stderr, "Signature verification: failed\n\n");
|
fprintf(stderr, "Signature verification: failed\n\n");
|
||||||
OPENSSL_free(ph);
|
OPENSSL_free(ph);
|
||||||
OPENSSL_free(cmdbuf);
|
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
OPENSSL_free(ph);
|
OPENSSL_free(ph);
|
||||||
OPENSSL_free(cmdbuf);
|
|
||||||
return 1; /* OK */
|
return 1; /* OK */
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -402,6 +390,7 @@ static PKCS7 *pe_pkcs7_signature_new(FILE_FORMAT_CTX *ctx, BIO *hash)
|
|||||||
content = spc_indirect_data_content_get(hash, ctx);
|
content = spc_indirect_data_content_get(hash, ctx);
|
||||||
if (!content) {
|
if (!content) {
|
||||||
fprintf(stderr, "Failed to get spcIndirectDataContent\n");
|
fprintf(stderr, "Failed to get spcIndirectDataContent\n");
|
||||||
|
PKCS7_free(p7);
|
||||||
return NULL; /* FAILED */
|
return NULL; /* FAILED */
|
||||||
}
|
}
|
||||||
if (!sign_spc_indirect_data_content(p7, content)) {
|
if (!sign_spc_indirect_data_content(p7, content)) {
|
||||||
@@ -779,11 +768,18 @@ static BIO *pe_digest_calc_bio(FILE_FORMAT_CTX *ctx, const EVP_MD *md)
|
|||||||
uint32_t idx = 0, fileend;
|
uint32_t idx = 0, fileend;
|
||||||
BIO *bhash = BIO_new(BIO_f_md());
|
BIO *bhash = BIO_new(BIO_f_md());
|
||||||
|
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wcast-qual"
|
||||||
|
#endif
|
||||||
if (!BIO_set_md(bhash, md)) {
|
if (!BIO_set_md(bhash, md)) {
|
||||||
fprintf(stderr, "Unable to set the message digest of BIO\n");
|
fprintf(stderr, "Unable to set the message digest of BIO\n");
|
||||||
BIO_free_all(bhash);
|
BIO_free_all(bhash);
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
BIO_push(bhash, BIO_new(BIO_s_null()));
|
BIO_push(bhash, BIO_new(BIO_s_null()));
|
||||||
if (ctx->pe_ctx->sigpos)
|
if (ctx->pe_ctx->sigpos)
|
||||||
fileend = ctx->pe_ctx->sigpos;
|
fileend = ctx->pe_ctx->sigpos;
|
||||||
@@ -827,6 +823,36 @@ static BIO *pe_digest_calc_bio(FILE_FORMAT_CTX *ctx, const EVP_MD *md)
|
|||||||
* Page hash support
|
* Page hash support
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Retrieve a page hash from PKCS7 SPC_INDIRECT_DATA structure.
|
||||||
|
* [in] p7: PKCS7 signature
|
||||||
|
* [out] ph: page hash
|
||||||
|
* [out] phlen: page hash length
|
||||||
|
* [out] phtype: NID_sha1 or NID_sha256
|
||||||
|
* [returns] 0 on error or 1 on success
|
||||||
|
*/
|
||||||
|
static int pkcs7_get_page_hash(PKCS7 *p7, u_char **ph, int *phlen, int *phtype)
|
||||||
|
{
|
||||||
|
SpcIndirectDataContent *idc = pkcs7_get_indirect_data_content(p7);
|
||||||
|
|
||||||
|
if (!idc) {
|
||||||
|
fprintf(stderr, "Failed to decode SpcIndirectDataContent\n\n");
|
||||||
|
return 0; /* FAILED */
|
||||||
|
}
|
||||||
|
if (!idc->data) {
|
||||||
|
fprintf(stderr, "Missing SpcIndirectDataContent data\n\n");
|
||||||
|
SpcIndirectDataContent_free(idc);
|
||||||
|
return 0; /* FAILED */
|
||||||
|
}
|
||||||
|
if (!pe_page_hash_get(ph, phlen, phtype, idc->data)) {
|
||||||
|
fprintf(stderr, "Failed to extract a page hash\n\n");
|
||||||
|
SpcIndirectDataContent_free(idc);
|
||||||
|
return 0; /* FAILED */
|
||||||
|
}
|
||||||
|
SpcIndirectDataContent_free(idc);
|
||||||
|
return 1; /* OK */
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Retrieve a page hash from SPC_INDIRECT_DATA structure.
|
* Retrieve a page hash from SPC_INDIRECT_DATA structure.
|
||||||
* [out] ph: page hash
|
* [out] ph: page hash
|
||||||
@@ -835,43 +861,71 @@ static BIO *pe_digest_calc_bio(FILE_FORMAT_CTX *ctx, const EVP_MD *md)
|
|||||||
* [in] obj: SPC_INDIRECT_DATA OID: 1.3.6.1.4.1.311.2.1.4 containing page hash
|
* [in] obj: SPC_INDIRECT_DATA OID: 1.3.6.1.4.1.311.2.1.4 containing page hash
|
||||||
* [returns] 0 on error or 1 on success
|
* [returns] 0 on error or 1 on success
|
||||||
*/
|
*/
|
||||||
static int pe_page_hash_get(u_char **ph, int *phlen, int *phtype, SpcAttributeTypeAndOptionalValue *obj)
|
static int pe_page_hash_get(u_char **ph, int *phlen, int *phtype,
|
||||||
|
SpcAttributeTypeAndOptionalValue *obj)
|
||||||
{
|
{
|
||||||
const u_char *blob;
|
const unsigned char *blob;
|
||||||
|
const unsigned char *sequence_data;
|
||||||
|
const unsigned char *classid_data;
|
||||||
|
const unsigned char *serialized_data;
|
||||||
SpcPeImageData *id;
|
SpcPeImageData *id;
|
||||||
SpcSerializedObject *so;
|
SpcSerializedObject *so;
|
||||||
int l, l2;
|
int sequence_len, classid_len, serialized_len, l, l2;
|
||||||
char buf[128];
|
char buf[128];
|
||||||
|
|
||||||
|
/* Validate input object */
|
||||||
if (!obj || !obj->value)
|
if (!obj || !obj->value)
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
blob = obj->value->value.sequence->data;
|
|
||||||
id = d2i_SpcPeImageData(NULL, &blob, obj->value->value.sequence->length);
|
/* Decode SpcPeImageData from ASN.1 sequence */
|
||||||
if (!id) {
|
sequence_data = ASN1_STRING_get0_data(obj->value->value.sequence);
|
||||||
|
sequence_len = ASN1_STRING_length(obj->value->value.sequence);
|
||||||
|
|
||||||
|
/* d2i_* modifies the input pointer, so use a temporary variable */
|
||||||
|
blob = sequence_data;
|
||||||
|
id = d2i_SpcPeImageData(NULL, &blob, sequence_len);
|
||||||
|
if (!id)
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
|
||||||
|
/* Validate SpcPeImageData contents */
|
||||||
if (!id->file) {
|
if (!id->file) {
|
||||||
SpcPeImageData_free(id);
|
SpcPeImageData_free(id);
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Type 1 means SpcSerializedObject */
|
||||||
if (id->file->type != 1) {
|
if (id->file->type != 1) {
|
||||||
SpcPeImageData_free(id);
|
SpcPeImageData_free(id);
|
||||||
return 1; /* OK - This is not SpcSerializedObject structure that contains page hashes */
|
return 1; /* OK - no page hashes present */
|
||||||
}
|
}
|
||||||
|
|
||||||
so = id->file->value.moniker;
|
so = id->file->value.moniker;
|
||||||
if (so->classId->length != sizeof classid_page_hash ||
|
|
||||||
memcmp(so->classId->data, classid_page_hash, sizeof classid_page_hash)) {
|
/* Validate serialized object class ID */
|
||||||
|
classid_data = ASN1_STRING_get0_data((ASN1_STRING *)so->classId);
|
||||||
|
classid_len = ASN1_STRING_length((ASN1_STRING *)so->classId);
|
||||||
|
|
||||||
|
if (classid_len != sizeof classid_page_hash ||
|
||||||
|
memcmp(classid_data, classid_page_hash, sizeof classid_page_hash)) {
|
||||||
SpcPeImageData_free(id);
|
SpcPeImageData_free(id);
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
/* skip ASN.1 SET hdr */
|
|
||||||
l = asn1_simple_hdr_len(so->serializedData->data, so->serializedData->length);
|
/*Get serialized ASN.1 blob */
|
||||||
blob = so->serializedData->data + l;
|
serialized_data = ASN1_STRING_get0_data((ASN1_STRING *)so->serializedData);
|
||||||
obj = d2i_SpcAttributeTypeAndOptionalValue(NULL, &blob, so->serializedData->length - l);
|
serialized_len = ASN1_STRING_length((ASN1_STRING *)so->serializedData);
|
||||||
|
|
||||||
|
/* Skip ASN.1 SET header */
|
||||||
|
l = asn1_simple_hdr_len(serialized_data, serialized_len);
|
||||||
|
blob = serialized_data + l;
|
||||||
|
|
||||||
|
/* Decode nested SpcAttributeTypeAndOptionalValue */
|
||||||
|
obj = d2i_SpcAttributeTypeAndOptionalValue(NULL, &blob, serialized_len - l);
|
||||||
SpcPeImageData_free(id);
|
SpcPeImageData_free(id);
|
||||||
if (!obj)
|
if (!obj)
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
|
|
||||||
|
/* Determine page hash algorithm */
|
||||||
*phtype = 0;
|
*phtype = 0;
|
||||||
buf[0] = 0x00;
|
buf[0] = 0x00;
|
||||||
OBJ_obj2txt(buf, sizeof buf, obj->type, 1);
|
OBJ_obj2txt(buf, sizeof buf, obj->type, 1);
|
||||||
@@ -883,15 +937,30 @@ static int pe_page_hash_get(u_char **ph, int *phlen, int *phtype, SpcAttributeTy
|
|||||||
SpcAttributeTypeAndOptionalValue_free(obj);
|
SpcAttributeTypeAndOptionalValue_free(obj);
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
/* Skip ASN.1 SET hdr */
|
|
||||||
l2 = asn1_simple_hdr_len(obj->value->value.sequence->data, obj->value->value.sequence->length);
|
/* IMPORTANT:
|
||||||
/* Skip ASN.1 OCTET STRING hdr */
|
* obj now points to the newly decoded structure,
|
||||||
l = asn1_simple_hdr_len(obj->value->value.sequence->data + l2, obj->value->value.sequence->length - l2);
|
* so refresh sequence_data/sequence_len */
|
||||||
|
sequence_data = ASN1_STRING_get0_data(obj->value->value.sequence);
|
||||||
|
sequence_len = ASN1_STRING_length(obj->value->value.sequence);
|
||||||
|
|
||||||
|
/* Skip ASN.1 SET header */
|
||||||
|
l2 = asn1_simple_hdr_len(sequence_data, sequence_len);
|
||||||
|
|
||||||
|
/* Skip ASN.1 OCTET STRING header */
|
||||||
|
l = asn1_simple_hdr_len(sequence_data + l2, sequence_len - l2);
|
||||||
l += l2;
|
l += l2;
|
||||||
*phlen = obj->value->value.sequence->length - l;
|
|
||||||
|
/* Extract raw page hash blob */
|
||||||
|
*phlen = sequence_len - l;
|
||||||
*ph = OPENSSL_malloc((size_t)*phlen);
|
*ph = OPENSSL_malloc((size_t)*phlen);
|
||||||
memcpy(*ph, obj->value->value.sequence->data + l, (size_t)*phlen);
|
if (!*ph) {
|
||||||
|
SpcAttributeTypeAndOptionalValue_free(obj);
|
||||||
|
return 0; /* FAILED */
|
||||||
|
}
|
||||||
|
memcpy(*ph, sequence_data + l, (size_t)*phlen);
|
||||||
SpcAttributeTypeAndOptionalValue_free(obj);
|
SpcAttributeTypeAndOptionalValue_free(obj);
|
||||||
|
|
||||||
return 1; /* OK */
|
return 1; /* OK */
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -907,12 +976,25 @@ static u_char *pe_page_hash_calc(int *rphlen, FILE_FORMAT_CTX *ctx, int phtype)
|
|||||||
uint16_t nsections, opthdr_size;
|
uint16_t nsections, opthdr_size;
|
||||||
uint32_t alignment, pagesize, hdrsize;
|
uint32_t alignment, pagesize, hdrsize;
|
||||||
uint32_t rs, ro, l, lastpos = 0;
|
uint32_t rs, ro, l, lastpos = 0;
|
||||||
int pphlen, phlen, i, pi = 1;
|
int mdlen, pphlen, phlen, i, pi = 1;
|
||||||
size_t written;
|
size_t written, off, sect_off, sect_tbl, need;
|
||||||
u_char *res, *zeroes;
|
u_char *res = NULL, *zeroes = NULL;
|
||||||
char *sections;
|
char *sections;
|
||||||
const EVP_MD *md = EVP_get_digestbynid(phtype);
|
const EVP_MD *md = EVP_get_digestbynid(phtype);
|
||||||
BIO *bhash;
|
BIO *bhash = NULL;
|
||||||
|
uint32_t filebound;
|
||||||
|
size_t pphlen_sz, sections_factor;
|
||||||
|
|
||||||
|
if (rphlen == NULL || ctx == NULL || ctx->options == NULL || ctx->pe_ctx == NULL
|
||||||
|
|| ctx->options->indata == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (md == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
mdlen = EVP_MD_size(md);
|
||||||
|
if (mdlen <= 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
/* NumberOfSections indicates the size of the section table,
|
/* NumberOfSections indicates the size of the section table,
|
||||||
* which immediately follows the headers, can be up to 65535 under Vista and later */
|
* which immediately follows the headers, can be up to 65535 under Vista and later */
|
||||||
@@ -954,16 +1036,62 @@ static u_char *pe_page_hash_calc(int *rphlen, FILE_FORMAT_CTX *ctx, int phtype)
|
|||||||
fprintf(stderr, "Corrupted optional header size: 0x%08X\n", opthdr_size);
|
fprintf(stderr, "Corrupted optional header size: 0x%08X\n", opthdr_size);
|
||||||
return NULL; /* FAILED */
|
return NULL; /* FAILED */
|
||||||
}
|
}
|
||||||
pphlen = 4 + EVP_MD_size(md);
|
/* Validate that pagesize >= hdrsize to prevent integer underflow */
|
||||||
phlen = pphlen * (3 + (int)nsections + (int)(ctx->pe_ctx->fileend / pagesize));
|
if (pagesize < hdrsize) {
|
||||||
|
fprintf(stderr, "Page size (0x%08X) is smaller than header size (0x%08X)\n",
|
||||||
|
pagesize, hdrsize);
|
||||||
|
return NULL; /* FAILED */
|
||||||
|
}
|
||||||
|
pphlen = 4 + mdlen;
|
||||||
|
|
||||||
|
/* Compute an upper bound for result size and guard overflow */
|
||||||
|
pphlen_sz = (size_t)pphlen;
|
||||||
|
sections_factor = 3 + (size_t)nsections + ((size_t)ctx->pe_ctx->fileend / pagesize);
|
||||||
|
if (sections_factor > SIZE_MAX / pphlen_sz) {
|
||||||
|
fprintf(stderr, "Page hash allocation size would overflow\n");
|
||||||
|
return NULL; /* FAILED */
|
||||||
|
}
|
||||||
|
phlen = (int)(pphlen_sz * sections_factor);
|
||||||
|
/* Sanity limit - page hash shouldn't exceed reasonable size (16 MB) */
|
||||||
|
if (phlen < 0 || (size_t)phlen > SIZE_16M) {
|
||||||
|
fprintf(stderr, "Page hash size exceeds limit: %d\n", phlen);
|
||||||
|
return NULL; /* FAILED */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Determine the file boundary for section data validation */
|
||||||
|
filebound = ctx->pe_ctx->sigpos ? ctx->pe_ctx->sigpos : ctx->pe_ctx->fileend;
|
||||||
|
|
||||||
|
/* Validate section table bounds before reading section headers */
|
||||||
|
sect_off = (size_t)ctx->pe_ctx->header_size + 24u + (size_t)opthdr_size;
|
||||||
|
sect_tbl = (size_t)nsections * 40u;
|
||||||
|
|
||||||
|
if (sect_off > (size_t)filebound || sect_tbl > (size_t)filebound - sect_off) {
|
||||||
|
fprintf(stderr, "Section table out of bounds: off=%zu size=%zu filebound=%u\n",
|
||||||
|
sect_off, sect_tbl, filebound);
|
||||||
|
return NULL; /* FAILED */
|
||||||
|
}
|
||||||
|
sections = (char *)ctx->options->indata + sect_off;
|
||||||
|
|
||||||
bhash = BIO_new(BIO_f_md());
|
bhash = BIO_new(BIO_f_md());
|
||||||
|
if (bhash == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wcast-qual"
|
||||||
|
#endif
|
||||||
if (!BIO_set_md(bhash, md)) {
|
if (!BIO_set_md(bhash, md)) {
|
||||||
fprintf(stderr, "Unable to set the message digest of BIO\n");
|
fprintf(stderr, "Unable to set the message digest of BIO\n");
|
||||||
BIO_free_all(bhash);
|
BIO_free_all(bhash);
|
||||||
return NULL; /* FAILED */
|
return NULL; /* FAILED */
|
||||||
}
|
}
|
||||||
BIO_push(bhash, BIO_new(BIO_s_null()));
|
#if defined(__GNUC__)
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
|
if (BIO_push(bhash, BIO_new(BIO_s_null())) == NULL) {
|
||||||
|
BIO_free_all(bhash);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
if (!BIO_write_ex(bhash, ctx->options->indata, ctx->pe_ctx->header_size + 88, &written)
|
if (!BIO_write_ex(bhash, ctx->options->indata, ctx->pe_ctx->header_size + 88, &written)
|
||||||
|| written != ctx->pe_ctx->header_size + 88) {
|
|| written != ctx->pe_ctx->header_size + 88) {
|
||||||
BIO_free_all(bhash);
|
BIO_free_all(bhash);
|
||||||
@@ -975,36 +1103,87 @@ static u_char *pe_page_hash_calc(int *rphlen, FILE_FORMAT_CTX *ctx, int phtype)
|
|||||||
BIO_free_all(bhash);
|
BIO_free_all(bhash);
|
||||||
return NULL; /* FAILED */
|
return NULL; /* FAILED */
|
||||||
}
|
}
|
||||||
if (!BIO_write_ex(bhash,
|
off = ctx->pe_ctx->header_size + 160 + (size_t)ctx->pe_ctx->pe32plus * 16;
|
||||||
ctx->options->indata + ctx->pe_ctx->header_size + 160 + ctx->pe_ctx->pe32plus*16,
|
if (hdrsize < off || hdrsize > filebound) {
|
||||||
hdrsize - (ctx->pe_ctx->header_size + 160 + ctx->pe_ctx->pe32plus*16), &written)
|
BIO_free_all(bhash);
|
||||||
|| written != hdrsize - (ctx->pe_ctx->header_size + 160 + ctx->pe_ctx->pe32plus*16)) {
|
return NULL; /* FAILED: header too small */
|
||||||
|
}
|
||||||
|
if (!BIO_write_ex(bhash, ctx->options->indata + off, (size_t)hdrsize - off, &written)
|
||||||
|
|| written != hdrsize - off) {
|
||||||
BIO_free_all(bhash);
|
BIO_free_all(bhash);
|
||||||
return NULL; /* FAILED */
|
return NULL; /* FAILED */
|
||||||
}
|
}
|
||||||
|
if (pagesize < hdrsize) {
|
||||||
|
BIO_free_all(bhash);
|
||||||
|
return NULL; /* FAILED: header larger than page */
|
||||||
|
}
|
||||||
zeroes = OPENSSL_zalloc((size_t)pagesize);
|
zeroes = OPENSSL_zalloc((size_t)pagesize);
|
||||||
if (!BIO_write_ex(bhash, zeroes, pagesize - hdrsize, &written)
|
if (zeroes == NULL) {
|
||||||
|| written != pagesize - hdrsize) {
|
BIO_free_all(bhash);
|
||||||
|
return NULL; /* FAILED */
|
||||||
|
}
|
||||||
|
if (!BIO_write_ex(bhash, zeroes, (size_t)pagesize - (size_t)hdrsize, &written)
|
||||||
|
|| written != (size_t)pagesize - (size_t)hdrsize) {
|
||||||
BIO_free_all(bhash);
|
BIO_free_all(bhash);
|
||||||
OPENSSL_free(zeroes);
|
OPENSSL_free(zeroes);
|
||||||
return NULL; /* FAILED */
|
return NULL; /* FAILED */
|
||||||
}
|
}
|
||||||
res = OPENSSL_malloc((size_t)phlen);
|
res = OPENSSL_malloc((size_t)phlen);
|
||||||
|
if (res == NULL) {
|
||||||
|
BIO_free_all(bhash);
|
||||||
|
OPENSSL_free(zeroes);
|
||||||
|
return NULL; /* FAILED */
|
||||||
|
}
|
||||||
memset(res, 0, 4);
|
memset(res, 0, 4);
|
||||||
BIO_gets(bhash, (char*)res + 4, EVP_MD_size(md));
|
if (BIO_gets(bhash, (char *)res + 4, mdlen) != mdlen) {
|
||||||
|
BIO_free_all(bhash);
|
||||||
|
OPENSSL_free(zeroes);
|
||||||
|
OPENSSL_free(res);
|
||||||
|
return NULL; /* FAILED */
|
||||||
|
}
|
||||||
BIO_free_all(bhash);
|
BIO_free_all(bhash);
|
||||||
sections = ctx->options->indata + ctx->pe_ctx->header_size + 24 + opthdr_size;
|
|
||||||
for (i=0; i<nsections; i++) {
|
for (i = 0; i < (int)nsections; i++) {
|
||||||
/* Resource Table address and size */
|
/* SizeOfRawData and PointerToRawData from section header */
|
||||||
rs = GET_UINT32_LE(sections + 16);
|
rs = GET_UINT32_LE(sections + 16);
|
||||||
ro = GET_UINT32_LE(sections + 20);
|
ro = GET_UINT32_LE(sections + 20);
|
||||||
if (rs == 0 || rs >= UINT32_MAX) {
|
if (rs == 0) {
|
||||||
sections += 40;
|
sections += 40;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (l=0; l<rs; l+=pagesize, pi++) {
|
/* Validate section bounds against file size to prevent OOB read */
|
||||||
PUT_UINT32_LE(ro + l, res + pi*pphlen);
|
if (ro >= filebound || rs > filebound - ro) {
|
||||||
|
fprintf(stderr, "Section %d has invalid bounds: offset=0x%08X, size=0x%08X, fileend=0x%08X\n",
|
||||||
|
i, ro, rs, filebound);
|
||||||
|
OPENSSL_free(zeroes);
|
||||||
|
OPENSSL_free(res);
|
||||||
|
return NULL; /* FAILED */
|
||||||
|
}
|
||||||
|
for (l = 0; l < rs; l += pagesize, pi++) {
|
||||||
|
need = (size_t)(pi + 1) * (size_t)pphlen;
|
||||||
|
|
||||||
|
/* Prevent OOB write into res if pi grows beyond allocated factor */
|
||||||
|
if (need > (size_t)phlen) {
|
||||||
|
fprintf(stderr, "Page hash buffer overflow prevented: pi=%d need=%zu phlen=%d\n",
|
||||||
|
pi, need, phlen);
|
||||||
|
OPENSSL_free(zeroes);
|
||||||
|
OPENSSL_free(res);
|
||||||
|
return NULL; /* FAILED */
|
||||||
|
}
|
||||||
|
|
||||||
|
PUT_UINT32_LE(ro + l, res + (size_t)pi * (size_t)pphlen);
|
||||||
|
|
||||||
bhash = BIO_new(BIO_f_md());
|
bhash = BIO_new(BIO_f_md());
|
||||||
|
if (bhash == NULL) {
|
||||||
|
OPENSSL_free(zeroes);
|
||||||
|
OPENSSL_free(res);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wcast-qual"
|
||||||
|
#endif
|
||||||
if (!BIO_set_md(bhash, md)) {
|
if (!BIO_set_md(bhash, md)) {
|
||||||
fprintf(stderr, "Unable to set the message digest of BIO\n");
|
fprintf(stderr, "Unable to set the message digest of BIO\n");
|
||||||
BIO_free_all(bhash);
|
BIO_free_all(bhash);
|
||||||
@@ -1012,17 +1191,27 @@ static u_char *pe_page_hash_calc(int *rphlen, FILE_FORMAT_CTX *ctx, int phtype)
|
|||||||
OPENSSL_free(res);
|
OPENSSL_free(res);
|
||||||
return NULL; /* FAILED */
|
return NULL; /* FAILED */
|
||||||
}
|
}
|
||||||
BIO_push(bhash, BIO_new(BIO_s_null()));
|
#if defined(__GNUC__)
|
||||||
if (rs - l < pagesize) {
|
#pragma GCC diagnostic pop
|
||||||
if (!BIO_write_ex(bhash, ctx->options->indata + ro + l, rs - l, &written)
|
#endif
|
||||||
|| written != rs - l) {
|
if (BIO_push(bhash, BIO_new(BIO_s_null())) == NULL) {
|
||||||
|
BIO_free_all(bhash);
|
||||||
|
OPENSSL_free(zeroes);
|
||||||
|
OPENSSL_free(res);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (l < rs && rs - l < pagesize) {
|
||||||
|
size_t tail = (size_t)(rs - l);
|
||||||
|
|
||||||
|
if (!BIO_write_ex(bhash, ctx->options->indata + ro + l, tail, &written)
|
||||||
|
|| written != tail) {
|
||||||
BIO_free_all(bhash);
|
BIO_free_all(bhash);
|
||||||
OPENSSL_free(zeroes);
|
OPENSSL_free(zeroes);
|
||||||
OPENSSL_free(res);
|
OPENSSL_free(res);
|
||||||
return NULL; /* FAILED */
|
return NULL; /* FAILED */
|
||||||
}
|
}
|
||||||
if (!BIO_write_ex(bhash, zeroes, pagesize - (rs - l), &written)
|
if (!BIO_write_ex(bhash, zeroes, pagesize - tail, &written)
|
||||||
|| written != pagesize - (rs - l)) {
|
|| written != pagesize - tail) {
|
||||||
BIO_free_all(bhash);
|
BIO_free_all(bhash);
|
||||||
OPENSSL_free(zeroes);
|
OPENSSL_free(zeroes);
|
||||||
OPENSSL_free(res);
|
OPENSSL_free(res);
|
||||||
@@ -1037,17 +1226,34 @@ static u_char *pe_page_hash_calc(int *rphlen, FILE_FORMAT_CTX *ctx, int phtype)
|
|||||||
return NULL; /* FAILED */
|
return NULL; /* FAILED */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BIO_gets(bhash, (char*)res + pi*pphlen + 4, EVP_MD_size(md));
|
if (BIO_gets(bhash, (char *)res + (size_t)pi * (size_t)pphlen + 4, mdlen) != mdlen) {
|
||||||
|
BIO_free_all(bhash);
|
||||||
|
OPENSSL_free(zeroes);
|
||||||
|
OPENSSL_free(res);
|
||||||
|
return NULL; /* FAILED */
|
||||||
|
}
|
||||||
BIO_free_all(bhash);
|
BIO_free_all(bhash);
|
||||||
}
|
}
|
||||||
lastpos = ro + rs;
|
lastpos = ro + rs;
|
||||||
sections += 40;
|
sections += 40;
|
||||||
}
|
}
|
||||||
PUT_UINT32_LE(lastpos, res + pi*pphlen);
|
/* Final entry */
|
||||||
memset(res + pi*pphlen + 4, 0, (size_t)EVP_MD_size(md));
|
need = (size_t)(pi + 1) * (size_t)pphlen;
|
||||||
|
|
||||||
|
if (need > (size_t)phlen) {
|
||||||
|
fprintf(stderr, "Page hash buffer overflow prevented at final entry: pi=%d need=%zu phlen=%d\n",
|
||||||
|
pi, need, phlen);
|
||||||
|
OPENSSL_free(zeroes);
|
||||||
|
OPENSSL_free(res);
|
||||||
|
return NULL; /* FAILED */
|
||||||
|
}
|
||||||
|
|
||||||
|
PUT_UINT32_LE(lastpos, res + (size_t)pi * (size_t)pphlen);
|
||||||
|
memset(res + (size_t)pi * (size_t)pphlen + 4, 0, (size_t)mdlen);
|
||||||
pi++;
|
pi++;
|
||||||
|
|
||||||
OPENSSL_free(zeroes);
|
OPENSSL_free(zeroes);
|
||||||
*rphlen = pi*pphlen;
|
*rphlen = pi * pphlen;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1067,6 +1273,10 @@ static int pe_verify_page_hash(FILE_FORMAT_CTX *ctx, u_char *ph, int phlen, int
|
|||||||
if (!ph)
|
if (!ph)
|
||||||
return 1; /* OK */
|
return 1; /* OK */
|
||||||
cph = pe_page_hash_calc(&cphlen, ctx, phtype);
|
cph = pe_page_hash_calc(&cphlen, ctx, phtype);
|
||||||
|
if (!cph) {
|
||||||
|
fprintf(stderr, "Page hash verification failed: could not calculate page hash\n");
|
||||||
|
return 0; /* FAILED */
|
||||||
|
}
|
||||||
mdok = (phlen == cphlen) && !memcmp(ph, cph, (size_t)phlen);
|
mdok = (phlen == cphlen) && !memcmp(ph, cph, (size_t)phlen);
|
||||||
printf("Page hash algorithm : %s\n", OBJ_nid2sn(phtype));
|
printf("Page hash algorithm : %s\n", OBJ_nid2sn(phtype));
|
||||||
if (ctx->options->verbose) {
|
if (ctx->options->verbose) {
|
||||||
@@ -1169,7 +1379,8 @@ static int pe_check_file(FILE_FORMAT_CTX *ctx)
|
|||||||
{
|
{
|
||||||
uint32_t real_pe_checksum, sum = 0;
|
uint32_t real_pe_checksum, sum = 0;
|
||||||
|
|
||||||
if (!ctx) {
|
if (ctx == NULL || ctx->pe_ctx == NULL || ctx->options == NULL
|
||||||
|
|| ctx->options->indata == NULL) {
|
||||||
fprintf(stderr, "Init error\n");
|
fprintf(stderr, "Init error\n");
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
@@ -1181,25 +1392,52 @@ static int pe_check_file(FILE_FORMAT_CTX *ctx)
|
|||||||
printf("Calculated PE checksum: %08X\n", real_pe_checksum);
|
printf("Calculated PE checksum: %08X\n", real_pe_checksum);
|
||||||
printf("Warning: invalid PE checksum\n");
|
printf("Warning: invalid PE checksum\n");
|
||||||
}
|
}
|
||||||
|
/* Signature directory bounds */
|
||||||
if (ctx->pe_ctx->sigpos == 0 || ctx->pe_ctx->siglen == 0
|
if (ctx->pe_ctx->sigpos == 0 || ctx->pe_ctx->siglen == 0
|
||||||
|| ctx->pe_ctx->sigpos > ctx->pe_ctx->fileend) {
|
|| ctx->pe_ctx->sigpos > ctx->pe_ctx->fileend
|
||||||
|
|| ctx->pe_ctx->siglen > ctx->pe_ctx->fileend - ctx->pe_ctx->sigpos) {
|
||||||
fprintf(stderr, "No signature found\n");
|
fprintf(stderr, "No signature found\n");
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
|
* Validate WIN_CERTIFICATE chain.
|
||||||
* If the sum of the rounded dwLength values does not equal the Size value,
|
* If the sum of the rounded dwLength values does not equal the Size value,
|
||||||
* then either the attribute certificate table or the Size field is corrupted.
|
* then either the attribute certificate table or the Size field is corrupted.
|
||||||
*/
|
*/
|
||||||
while (sum < ctx->pe_ctx->siglen) {
|
while (sum < ctx->pe_ctx->siglen) {
|
||||||
uint32_t len = GET_UINT32_LE(ctx->options->indata + ctx->pe_ctx->sigpos + sum);
|
uint32_t len, off;
|
||||||
if (ctx->pe_ctx->siglen - len > 8) {
|
|
||||||
|
/* Prevent overflow in sigpos + sum */
|
||||||
|
if (sum > UINT32_MAX - ctx->pe_ctx->sigpos) {
|
||||||
fprintf(stderr, "Corrupted attribute certificate table\n");
|
fprintf(stderr, "Corrupted attribute certificate table\n");
|
||||||
fprintf(stderr, "Attribute certificate table size : %08X\n", ctx->pe_ctx->siglen);
|
|
||||||
fprintf(stderr, "Attribute certificate entry length: %08X\n\n", len);
|
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
/* quadword align data */
|
off = ctx->pe_ctx->sigpos + sum;
|
||||||
len += len % 8 ? 8 - len % 8 : 0;
|
|
||||||
|
/* Need at least 4 bytes to read dwLength */
|
||||||
|
if (off > ctx->pe_ctx->fileend || ctx->pe_ctx->fileend - off < 4) {
|
||||||
|
fprintf(stderr, "Corrupted attribute certificate table\n");
|
||||||
|
return 0; /* FAILED */
|
||||||
|
}
|
||||||
|
len = GET_UINT32_LE(ctx->options->indata + off);
|
||||||
|
|
||||||
|
/* dwLength must include the 8-byte WIN_CERTIFICATE header */
|
||||||
|
if (len < 8 || len > ctx->pe_ctx->siglen - sum || len > ctx->pe_ctx->fileend - off) {
|
||||||
|
fprintf(stderr, "Corrupted attribute certificate table\n");
|
||||||
|
return 0; /* FAILED */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Quadword align data */
|
||||||
|
if (len % 8) {
|
||||||
|
uint32_t pad = 8 - (len % 8);
|
||||||
|
|
||||||
|
/* Ensure quadword alignment does not overflow or exceed remaining table size */
|
||||||
|
if (pad > ctx->pe_ctx->siglen - sum - len) {
|
||||||
|
fprintf(stderr, "Corrupted attribute certificate table\n");
|
||||||
|
return 0; /* FAILED */
|
||||||
|
}
|
||||||
|
len += pad;
|
||||||
|
}
|
||||||
sum += len;
|
sum += len;
|
||||||
}
|
}
|
||||||
if (sum != ctx->pe_ctx->siglen) {
|
if (sum != ctx->pe_ctx->siglen) {
|
||||||
|
|||||||
@@ -8,11 +8,17 @@
|
|||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
#include "utf.h"
|
#include "utf.h"
|
||||||
|
|
||||||
typedef enum {comment_hash, comment_xml, comment_c, comment_not_found} comment_style;
|
typedef enum {
|
||||||
|
comment_hash,
|
||||||
|
comment_xml,
|
||||||
|
comment_c,
|
||||||
|
comment_js,
|
||||||
|
comment_not_found
|
||||||
|
} COMMENT_STYLE;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const char *extension;
|
const char *extension;
|
||||||
comment_style comment;
|
COMMENT_STYLE comment;
|
||||||
} SCRIPT_FORMAT;
|
} SCRIPT_FORMAT;
|
||||||
|
|
||||||
const SCRIPT_FORMAT supported_formats[] = {
|
const SCRIPT_FORMAT supported_formats[] = {
|
||||||
@@ -23,21 +29,24 @@ const SCRIPT_FORMAT supported_formats[] = {
|
|||||||
{".psm1", comment_hash},
|
{".psm1", comment_hash},
|
||||||
{".cdxml", comment_xml},
|
{".cdxml", comment_xml},
|
||||||
{".mof", comment_c},
|
{".mof", comment_c},
|
||||||
|
{".js", comment_js},
|
||||||
{NULL, comment_not_found},
|
{NULL, comment_not_found},
|
||||||
};
|
};
|
||||||
|
|
||||||
const char *signature_header = "SIG # Begin signature block";
|
#define header_hash "SIG # Begin signature block"
|
||||||
const char *signature_footer = "SIG # End signature block";
|
#define footer_hash "SIG # End signature block"
|
||||||
|
#define header_js "Begin signature block"
|
||||||
|
#define footer_js "End signature block"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const char *open;
|
const char *open, *close, *header, *footer;
|
||||||
const char *close;
|
|
||||||
} SCRIPT_COMMENT;
|
} SCRIPT_COMMENT;
|
||||||
|
|
||||||
const SCRIPT_COMMENT comment_text[] = {
|
const SCRIPT_COMMENT comment_text[] = {
|
||||||
[comment_hash] = {"# ", ""},
|
[comment_hash] = {"# ", "", header_hash, footer_hash},
|
||||||
[comment_xml] = {"<!-- ", " -->"},
|
[comment_xml] = {"<!-- ", " -->", header_hash, footer_hash},
|
||||||
[comment_c] = {"/* ", " */"}
|
[comment_c] = {"/* ", " */", header_hash, footer_hash},
|
||||||
|
[comment_js] = {"// SIG // ", "", header_js, footer_js}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct script_ctx_st {
|
struct script_ctx_st {
|
||||||
@@ -242,11 +251,18 @@ static u_char *script_digest_calc(FILE_FORMAT_CTX *ctx, const EVP_MD *md)
|
|||||||
u_char *mdbuf;
|
u_char *mdbuf;
|
||||||
BIO *hash = BIO_new(BIO_f_md());
|
BIO *hash = BIO_new(BIO_f_md());
|
||||||
|
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wcast-qual"
|
||||||
|
#endif
|
||||||
if (!BIO_set_md(hash, md)) {
|
if (!BIO_set_md(hash, md)) {
|
||||||
fprintf(stderr, "Unable to set the message digest of BIO\n");
|
fprintf(stderr, "Unable to set the message digest of BIO\n");
|
||||||
BIO_free_all(hash);
|
BIO_free_all(hash);
|
||||||
return NULL; /* FAILED */
|
return NULL; /* FAILED */
|
||||||
}
|
}
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
BIO_push(hash, BIO_new(BIO_s_null()));
|
BIO_push(hash, BIO_new(BIO_s_null()));
|
||||||
if (!script_write_bio(hash, ctx->options->indata, ctx->script_ctx->fileend)) {
|
if (!script_write_bio(hash, ctx->options->indata, ctx->script_ctx->fileend)) {
|
||||||
BIO_free_all(hash);
|
BIO_free_all(hash);
|
||||||
@@ -272,20 +288,7 @@ static int script_verify_digests(FILE_FORMAT_CTX *ctx, PKCS7 *p7)
|
|||||||
const EVP_MD *md;
|
const EVP_MD *md;
|
||||||
BIO *bhash;
|
BIO *bhash;
|
||||||
|
|
||||||
/* FIXME: this shared code most likely belongs in osslsigncode.c */
|
if (!pkcs7_get_content_digest(p7, mdbuf, &mdtype)) {
|
||||||
if (is_content_type(p7, SPC_INDIRECT_DATA_OBJID)) {
|
|
||||||
ASN1_STRING *content_val = p7->d.sign->contents->d.other->value.sequence;
|
|
||||||
const u_char *p = content_val->data;
|
|
||||||
SpcIndirectDataContent *idc = d2i_SpcIndirectDataContent(NULL, &p, content_val->length);
|
|
||||||
if (idc) {
|
|
||||||
if (idc->messageDigest && idc->messageDigest->digest && idc->messageDigest->digestAlgorithm) {
|
|
||||||
mdtype = OBJ_obj2nid(idc->messageDigest->digestAlgorithm->algorithm);
|
|
||||||
memcpy(mdbuf, idc->messageDigest->digest->data, (size_t)idc->messageDigest->digest->length);
|
|
||||||
}
|
|
||||||
SpcIndirectDataContent_free(idc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (mdtype == -1) {
|
|
||||||
fprintf(stderr, "Failed to extract current message digest\n\n");
|
fprintf(stderr, "Failed to extract current message digest\n\n");
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
@@ -326,8 +329,8 @@ static PKCS7 *script_pkcs7_extract(FILE_FORMAT_CTX *ctx)
|
|||||||
const char *close_tag = ctx->script_ctx->comment_text->close;
|
const char *close_tag = ctx->script_ctx->comment_text->close;
|
||||||
size_t open_tag_len = strlen(open_tag);
|
size_t open_tag_len = strlen(open_tag);
|
||||||
size_t close_tag_len = strlen(close_tag);
|
size_t close_tag_len = strlen(close_tag);
|
||||||
size_t signature_header_len = strlen(signature_header);
|
size_t header_len = strlen(ctx->script_ctx->comment_text->header);
|
||||||
size_t signature_footer_len = strlen(signature_footer);
|
size_t footer_len = strlen(ctx->script_ctx->comment_text->footer);
|
||||||
PKCS7 *retval = NULL;
|
PKCS7 *retval = NULL;
|
||||||
|
|
||||||
if (!script_check_file(ctx)) {
|
if (!script_check_file(ctx)) {
|
||||||
@@ -337,6 +340,8 @@ static PKCS7 *script_pkcs7_extract(FILE_FORMAT_CTX *ctx)
|
|||||||
if (ctx->script_ctx->utf == 8) {
|
if (ctx->script_ctx->utf == 8) {
|
||||||
base64_len = signature_len;
|
base64_len = signature_len;
|
||||||
base64_data = OPENSSL_malloc(base64_len);
|
base64_data = OPENSSL_malloc(base64_len);
|
||||||
|
if (!base64_data)
|
||||||
|
return NULL; /* memory allocation failed */
|
||||||
memcpy(base64_data, signature_data, base64_len);
|
memcpy(base64_data, signature_data, base64_len);
|
||||||
} else {
|
} else {
|
||||||
base64_len = utf16_to_utf8((const void *)signature_data,
|
base64_len = utf16_to_utf8((const void *)signature_data,
|
||||||
@@ -364,12 +369,12 @@ static PKCS7 *script_pkcs7_extract(FILE_FORMAT_CTX *ctx)
|
|||||||
}
|
}
|
||||||
ptr++;
|
ptr++;
|
||||||
}
|
}
|
||||||
/* process signature_header and signature_footer */
|
/* process header and footer */
|
||||||
if (ptr + signature_header_len < base64_data + base64_len &&
|
if (ptr + header_len < base64_data + base64_len &&
|
||||||
!memcmp(ptr, signature_header, signature_header_len))
|
!memcmp(ptr, ctx->script_ctx->comment_text->header, header_len))
|
||||||
ptr += signature_header_len;
|
ptr += header_len;
|
||||||
if (ptr + signature_footer_len <= base64_data + base64_len &&
|
if (ptr + footer_len <= base64_data + base64_len &&
|
||||||
!memcmp(ptr, signature_footer, signature_footer_len))
|
!memcmp(ptr, ctx->script_ctx->comment_text->footer, footer_len))
|
||||||
break; /* success */
|
break; /* success */
|
||||||
|
|
||||||
/* copy until the closing tag */
|
/* copy until the closing tag */
|
||||||
@@ -531,7 +536,9 @@ static int script_append_pkcs7(FILE_FORMAT_CTX *ctx, BIO *outdata, PKCS7 *p7)
|
|||||||
(void)BIO_set_close(bio, BIO_NOCLOSE);
|
(void)BIO_set_close(bio, BIO_NOCLOSE);
|
||||||
|
|
||||||
/* split to individual lines and write to outdata */
|
/* split to individual lines and write to outdata */
|
||||||
if (!write_commented(ctx, outdata, signature_header, strlen(signature_header)))
|
if (!write_commented(ctx, outdata,
|
||||||
|
ctx->script_ctx->comment_text->header,
|
||||||
|
strlen(ctx->script_ctx->comment_text->header)))
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
for (i = 0; i < buffer->length; i += 64) {
|
for (i = 0; i < buffer->length; i += 64) {
|
||||||
if (!write_commented(ctx, outdata, buffer->data + i,
|
if (!write_commented(ctx, outdata, buffer->data + i,
|
||||||
@@ -539,7 +546,9 @@ static int script_append_pkcs7(FILE_FORMAT_CTX *ctx, BIO *outdata, PKCS7 *p7)
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!write_commented(ctx, outdata, signature_footer, strlen(signature_footer)))
|
if (!write_commented(ctx, outdata,
|
||||||
|
ctx->script_ctx->comment_text->footer,
|
||||||
|
strlen(ctx->script_ctx->comment_text->footer)))
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
/* signtool expects CRLF terminator at the end of the text file */
|
/* signtool expects CRLF terminator at the end of the text file */
|
||||||
@@ -605,7 +614,7 @@ static SCRIPT_CTX *script_ctx_get(char *indata, uint32_t filesize, const SCRIPT_
|
|||||||
*ptr && commented_header_len < commented_header_size;
|
*ptr && commented_header_len < commented_header_size;
|
||||||
commented_header_len++)
|
commented_header_len++)
|
||||||
ptr = utf8DecodeRune(ptr, 1, commented_header + commented_header_len);
|
ptr = utf8DecodeRune(ptr, 1, commented_header + commented_header_len);
|
||||||
for (ptr = signature_header;
|
for (ptr = comment->header;
|
||||||
*ptr && commented_header_len < commented_header_size;
|
*ptr && commented_header_len < commented_header_size;
|
||||||
commented_header_len++)
|
commented_header_len++)
|
||||||
ptr = utf8DecodeRune(ptr, 1, commented_header + commented_header_len);
|
ptr = utf8DecodeRune(ptr, 1, commented_header + commented_header_len);
|
||||||
@@ -671,6 +680,8 @@ static int write_commented(FILE_FORMAT_CTX *ctx, BIO *outdata, const char *data,
|
|||||||
* - closing tag
|
* - closing tag
|
||||||
* - trailing NUL ("\0") */
|
* - trailing NUL ("\0") */
|
||||||
line = OPENSSL_malloc(2 + open_tag_len + length + close_tag_len + 1);
|
line = OPENSSL_malloc(2 + open_tag_len + length + close_tag_len + 1);
|
||||||
|
if (!line)
|
||||||
|
return 0; /* memory allocation failed */
|
||||||
strcpy(line, "\r\n");
|
strcpy(line, "\r\n");
|
||||||
strcat(line, open_tag);
|
strcat(line, open_tag);
|
||||||
memcpy(line + 2 + open_tag_len, data, length);
|
memcpy(line + 2 + open_tag_len, data, length);
|
||||||
@@ -782,12 +793,18 @@ static BIO *script_digest_calc_bio(FILE_FORMAT_CTX *ctx, const EVP_MD *md)
|
|||||||
fileend = ctx->script_ctx->sigpos;
|
fileend = ctx->script_ctx->sigpos;
|
||||||
else
|
else
|
||||||
fileend = ctx->script_ctx->fileend;
|
fileend = ctx->script_ctx->fileend;
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wcast-qual"
|
||||||
|
#endif
|
||||||
if (!BIO_set_md(hash, md)) {
|
if (!BIO_set_md(hash, md)) {
|
||||||
fprintf(stderr, "Unable to set the message digest of BIO\n");
|
fprintf(stderr, "Unable to set the message digest of BIO\n");
|
||||||
BIO_free_all(hash);
|
BIO_free_all(hash);
|
||||||
return NULL; /* FAILED */
|
return NULL; /* FAILED */
|
||||||
}
|
}
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
BIO_push(hash, BIO_new(BIO_s_null()));
|
BIO_push(hash, BIO_new(BIO_s_null()));
|
||||||
if (!script_digest_convert(hash, ctx, fileend)) {
|
if (!script_digest_convert(hash, ctx, fileend)) {
|
||||||
fprintf(stderr, "Unable calc a message digest value\n");
|
fprintf(stderr, "Unable calc a message digest value\n");
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/python3
|
#!/usr/bin/python3
|
||||||
"""Implementation of a HTTP client"""
|
"""Implementation of an HTTP client"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|||||||
+1
-1
@@ -29,7 +29,7 @@ def main() -> None:
|
|||||||
# all exceptions are critical
|
# all exceptions are critical
|
||||||
print(err, file=sys.stderr)
|
print(err, file=sys.stderr)
|
||||||
else:
|
else:
|
||||||
print("Usage:\n\t{} COMMAND [ARG]...'".format(sys.argv[0]), file=sys.stderr)
|
print("Usage:\n\t{} COMMAND [ARG]...".format(sys.argv[0]), file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
console.log("Hello, world!");
|
||||||
+176
-115
@@ -4,11 +4,60 @@
|
|||||||
import os
|
import os
|
||||||
import datetime
|
import datetime
|
||||||
import cryptography
|
import cryptography
|
||||||
from cryptography import x509
|
|
||||||
from cryptography.x509.oid import NameOID
|
# Explicit imports of cryptography submodules
|
||||||
from cryptography.hazmat.primitives import hashes
|
import cryptography.x509
|
||||||
from cryptography.hazmat.primitives import serialization
|
import cryptography.x509.oid
|
||||||
from cryptography.hazmat.primitives.asymmetric import rsa
|
import cryptography.hazmat.primitives.hashes
|
||||||
|
import cryptography.hazmat.primitives.asymmetric.rsa
|
||||||
|
import cryptography.hazmat.primitives.serialization
|
||||||
|
import cryptography.hazmat.primitives.serialization.pkcs12
|
||||||
|
|
||||||
|
# Import classes and functions from the cryptography module
|
||||||
|
from cryptography.x509 import (
|
||||||
|
AuthorityKeyIdentifier,
|
||||||
|
BasicConstraints,
|
||||||
|
Certificate,
|
||||||
|
CertificateBuilder,
|
||||||
|
CertificateRevocationListBuilder,
|
||||||
|
CRLDistributionPoints,
|
||||||
|
CRLNumber,
|
||||||
|
CRLReason,
|
||||||
|
DistributionPoint,
|
||||||
|
DNSName,
|
||||||
|
ExtendedKeyUsage,
|
||||||
|
KeyUsage,
|
||||||
|
Name,
|
||||||
|
NameAttribute,
|
||||||
|
NameConstraints,
|
||||||
|
random_serial_number,
|
||||||
|
RevokedCertificateBuilder,
|
||||||
|
ReasonFlags,
|
||||||
|
SubjectKeyIdentifier,
|
||||||
|
UniformResourceIdentifier
|
||||||
|
)
|
||||||
|
from cryptography.x509.oid import (
|
||||||
|
ExtendedKeyUsageOID,
|
||||||
|
NameOID
|
||||||
|
)
|
||||||
|
from cryptography.hazmat.primitives.hashes import SHA256
|
||||||
|
from cryptography.hazmat.primitives.asymmetric.rsa import (
|
||||||
|
generate_private_key,
|
||||||
|
RSAPrivateKey
|
||||||
|
)
|
||||||
|
from cryptography.hazmat.primitives.serialization import (
|
||||||
|
BestAvailableEncryption,
|
||||||
|
Encoding,
|
||||||
|
NoEncryption,
|
||||||
|
PrivateFormat
|
||||||
|
)
|
||||||
|
from cryptography.hazmat.primitives.serialization.pkcs12 import serialize_key_and_certificates
|
||||||
|
|
||||||
|
try:
|
||||||
|
if cryptography.__version__ >= '38.0.0':
|
||||||
|
from cryptography.hazmat.primitives.serialization.pkcs12 import PBES
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
RESULT_PATH = os.getcwd()
|
RESULT_PATH = os.getcwd()
|
||||||
CERTS_PATH = os.path.join(RESULT_PATH, "./Testing/certs/")
|
CERTS_PATH = os.path.join(RESULT_PATH, "./Testing/certs/")
|
||||||
@@ -28,25 +77,25 @@ class X509Extensions():
|
|||||||
self.port = cdp_port
|
self.port = cdp_port
|
||||||
self.name = cdp_name
|
self.name = cdp_name
|
||||||
|
|
||||||
def create_x509_name(self, common_name) -> x509.Name:
|
def create_x509_name(self, common_name) -> Name:
|
||||||
"""Return x509.Name"""
|
"""Return x509.Name"""
|
||||||
return x509.Name(
|
return Name(
|
||||||
[
|
[
|
||||||
x509.NameAttribute(NameOID.COUNTRY_NAME, "PL"),
|
NameAttribute(NameOID.COUNTRY_NAME, "PL"),
|
||||||
x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "Mazovia Province"),
|
NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "Mazovia Province"),
|
||||||
x509.NameAttribute(NameOID.LOCALITY_NAME, "Warsaw"),
|
NameAttribute(NameOID.LOCALITY_NAME, "Warsaw"),
|
||||||
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "osslsigncode"),
|
NameAttribute(NameOID.ORGANIZATION_NAME, "osslsigncode"),
|
||||||
x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, self.unit_name),
|
NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, self.unit_name),
|
||||||
x509.NameAttribute(NameOID.COMMON_NAME, common_name)
|
NameAttribute(NameOID.COMMON_NAME, common_name)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
def create_x509_crldp(self) -> x509.CRLDistributionPoints:
|
def create_x509_crldp(self) -> CRLDistributionPoints:
|
||||||
"""Return x509.CRLDistributionPoints"""
|
"""Return x509.CRLDistributionPoints"""
|
||||||
return x509.CRLDistributionPoints(
|
return CRLDistributionPoints(
|
||||||
[
|
[
|
||||||
x509.DistributionPoint(
|
DistributionPoint(
|
||||||
full_name=[x509.UniformResourceIdentifier(
|
full_name=[UniformResourceIdentifier(
|
||||||
"http://127.0.0.1:" + str(self.port) + "/" + str(self.name))
|
"http://127.0.0.1:" + str(self.port) + "/" + str(self.name))
|
||||||
],
|
],
|
||||||
relative_name=None,
|
relative_name=None,
|
||||||
@@ -56,10 +105,10 @@ class X509Extensions():
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
def create_x509_name_constraints(self) -> x509.NameConstraints:
|
def create_x509_name_constraints(self) -> NameConstraints:
|
||||||
"""Return x509.NameConstraints"""
|
"""Return x509.NameConstraints"""
|
||||||
return x509.NameConstraints(
|
return NameConstraints(
|
||||||
permitted_subtrees = [x509.DNSName('test.com'), x509.DNSName('test.org')],
|
permitted_subtrees = [DNSName('test.com'), DNSName('test.org')],
|
||||||
excluded_subtrees = None
|
excluded_subtrees = None
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -71,14 +120,14 @@ class IntermediateCACertificate(X509Extensions):
|
|||||||
self.issuer_key = issuer_key
|
self.issuer_key = issuer_key
|
||||||
super().__init__("Certification Authority", 0, None)
|
super().__init__("Certification Authority", 0, None)
|
||||||
|
|
||||||
def make_cert(self) -> (x509.Certificate, rsa.RSAPrivateKey):
|
def make_cert(self) -> (Certificate, RSAPrivateKey):
|
||||||
"""Generate intermediate CA certificate"""
|
"""Generate intermediate CA certificate"""
|
||||||
key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
|
key = generate_private_key(public_exponent=65537, key_size=2048)
|
||||||
key_public = key.public_key()
|
key_public = key.public_key()
|
||||||
authority_key = x509.AuthorityKeyIdentifier.from_issuer_subject_key_identifier(
|
authority_key = AuthorityKeyIdentifier.from_issuer_subject_key_identifier(
|
||||||
self.issuer_cert.extensions.get_extension_for_class(x509.SubjectKeyIdentifier).value
|
self.issuer_cert.extensions.get_extension_for_class(SubjectKeyIdentifier).value
|
||||||
)
|
)
|
||||||
key_usage = x509.KeyUsage(
|
key_usage = KeyUsage(
|
||||||
digital_signature=True,
|
digital_signature=True,
|
||||||
content_commitment=False,
|
content_commitment=False,
|
||||||
key_encipherment=False,
|
key_encipherment=False,
|
||||||
@@ -90,22 +139,22 @@ class IntermediateCACertificate(X509Extensions):
|
|||||||
decipher_only=False
|
decipher_only=False
|
||||||
)
|
)
|
||||||
cert = (
|
cert = (
|
||||||
x509.CertificateBuilder()
|
CertificateBuilder()
|
||||||
.subject_name(self.create_x509_name("Intermediate CA"))
|
.subject_name(self.create_x509_name("Intermediate CA"))
|
||||||
.issuer_name(self.issuer_cert.subject)
|
.issuer_name(self.issuer_cert.subject)
|
||||||
.public_key(key_public)
|
.public_key(key_public)
|
||||||
.serial_number(x509.random_serial_number())
|
.serial_number(random_serial_number())
|
||||||
.not_valid_before(date_20180101)
|
.not_valid_before(date_20180101)
|
||||||
.not_valid_after(date_20180101 + datetime.timedelta(days=7300))
|
.not_valid_after(date_20180101 + datetime.timedelta(days=7300))
|
||||||
.add_extension(x509.BasicConstraints(ca=True, path_length=0), critical=True)
|
.add_extension(BasicConstraints(ca=True, path_length=0), critical=True)
|
||||||
.add_extension(x509.SubjectKeyIdentifier.from_public_key(key_public), critical=False)
|
.add_extension(SubjectKeyIdentifier.from_public_key(key_public), critical=False)
|
||||||
.add_extension(authority_key, critical=False)
|
.add_extension(authority_key, critical=False)
|
||||||
.add_extension(key_usage, critical=True)
|
.add_extension(key_usage, critical=True)
|
||||||
.sign(self.issuer_key, hashes.SHA256())
|
.sign(self.issuer_key, SHA256())
|
||||||
)
|
)
|
||||||
file_path=os.path.join(CERTS_PATH, "intermediateCA.pem")
|
file_path=os.path.join(CERTS_PATH, "intermediateCA.pem")
|
||||||
with open(file_path, mode="wb") as file:
|
with open(file_path, mode="wb") as file:
|
||||||
file.write(cert.public_bytes(encoding=serialization.Encoding.PEM))
|
file.write(cert.public_bytes(encoding=Encoding.PEM))
|
||||||
|
|
||||||
return cert, key
|
return cert, key
|
||||||
|
|
||||||
@@ -114,7 +163,7 @@ class RootCACertificate(X509Extensions):
|
|||||||
"""Base class for Root CA certificate"""
|
"""Base class for Root CA certificate"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.key_usage = x509.KeyUsage(
|
self.key_usage = KeyUsage(
|
||||||
digital_signature=True,
|
digital_signature=True,
|
||||||
content_commitment=False,
|
content_commitment=False,
|
||||||
key_encipherment=False,
|
key_encipherment=False,
|
||||||
@@ -127,7 +176,7 @@ class RootCACertificate(X509Extensions):
|
|||||||
)
|
)
|
||||||
super().__init__("Certification Authority", 0, None)
|
super().__init__("Certification Authority", 0, None)
|
||||||
|
|
||||||
def make_cert(self) -> (x509.Certificate, rsa.RSAPrivateKey):
|
def make_cert(self) -> (Certificate, RSAPrivateKey):
|
||||||
"""Generate CA certificates"""
|
"""Generate CA certificates"""
|
||||||
ca_root, root_key = self.make_ca_cert("Trusted Root CA", "CAroot.pem")
|
ca_root, root_key = self.make_ca_cert("Trusted Root CA", "CAroot.pem")
|
||||||
ca_cert, ca_key = self.make_ca_cert("Root CA", "CACert.pem")
|
ca_cert, ca_key = self.make_ca_cert("Root CA", "CACert.pem")
|
||||||
@@ -136,52 +185,52 @@ class RootCACertificate(X509Extensions):
|
|||||||
|
|
||||||
def make_ca_cert(self, common_name, file_name) -> None:
|
def make_ca_cert(self, common_name, file_name) -> None:
|
||||||
"""Generate self-signed root CA certificate"""
|
"""Generate self-signed root CA certificate"""
|
||||||
ca_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
|
ca_key = generate_private_key(public_exponent=65537, key_size=2048)
|
||||||
ca_public = ca_key.public_key()
|
ca_public = ca_key.public_key()
|
||||||
authority_key = x509.AuthorityKeyIdentifier.from_issuer_public_key(ca_public)
|
authority_key = AuthorityKeyIdentifier.from_issuer_public_key(ca_public)
|
||||||
name = self.create_x509_name(common_name)
|
name = self.create_x509_name(common_name)
|
||||||
ca_cert = (
|
ca_cert = (
|
||||||
x509.CertificateBuilder()
|
CertificateBuilder()
|
||||||
.subject_name(name)
|
.subject_name(name)
|
||||||
.issuer_name(name)
|
.issuer_name(name)
|
||||||
.public_key(ca_public)
|
.public_key(ca_public)
|
||||||
.serial_number(x509.random_serial_number())
|
.serial_number(random_serial_number())
|
||||||
.not_valid_before(date_20170101)
|
.not_valid_before(date_20170101)
|
||||||
.not_valid_after(date_20170101 + datetime.timedelta(days=7300))
|
.not_valid_after(date_20170101 + datetime.timedelta(days=7300))
|
||||||
.add_extension(x509.BasicConstraints(ca=True, path_length=None), critical=True)
|
.add_extension(BasicConstraints(ca=True, path_length=None), critical=True)
|
||||||
.add_extension(x509.SubjectKeyIdentifier.from_public_key(ca_public), critical=False)
|
.add_extension(SubjectKeyIdentifier.from_public_key(ca_public), critical=False)
|
||||||
.add_extension(authority_key, critical=False)
|
.add_extension(authority_key, critical=False)
|
||||||
.add_extension(self.key_usage, critical=True)
|
.add_extension(self.key_usage, critical=True)
|
||||||
.sign(ca_key, hashes.SHA256())
|
.sign(ca_key, SHA256())
|
||||||
)
|
)
|
||||||
file_path=os.path.join(CERTS_PATH, file_name)
|
file_path=os.path.join(CERTS_PATH, file_name)
|
||||||
with open(file_path, mode="wb") as file:
|
with open(file_path, mode="wb") as file:
|
||||||
file.write(ca_cert.public_bytes(encoding=serialization.Encoding.PEM))
|
file.write(ca_cert.public_bytes(encoding=Encoding.PEM))
|
||||||
return ca_cert, ca_key
|
return ca_cert, ca_key
|
||||||
|
|
||||||
def make_cross_cert(self, ca_root, root_key, ca_cert, ca_key) -> None:
|
def make_cross_cert(self, ca_root, root_key, ca_cert, ca_key) -> None:
|
||||||
"""Generate cross-signed root CA certificate"""
|
"""Generate cross-signed root CA certificate"""
|
||||||
ca_public = ca_key.public_key()
|
ca_public = ca_key.public_key()
|
||||||
authority_key = x509.AuthorityKeyIdentifier.from_issuer_subject_key_identifier(
|
authority_key = AuthorityKeyIdentifier.from_issuer_subject_key_identifier(
|
||||||
ca_root.extensions.get_extension_for_class(x509.SubjectKeyIdentifier).value
|
ca_root.extensions.get_extension_for_class(SubjectKeyIdentifier).value
|
||||||
)
|
)
|
||||||
ca_cross = (
|
ca_cross = (
|
||||||
x509.CertificateBuilder()
|
CertificateBuilder()
|
||||||
.subject_name(ca_cert.subject)
|
.subject_name(ca_cert.subject)
|
||||||
.issuer_name(ca_root.subject)
|
.issuer_name(ca_root.subject)
|
||||||
.public_key(ca_public)
|
.public_key(ca_public)
|
||||||
.serial_number(ca_cert.serial_number)
|
.serial_number(ca_cert.serial_number)
|
||||||
.not_valid_before(date_20180101)
|
.not_valid_before(date_20180101)
|
||||||
.not_valid_after(date_20180101 + datetime.timedelta(days=7300))
|
.not_valid_after(date_20180101 + datetime.timedelta(days=7300))
|
||||||
.add_extension(x509.BasicConstraints(ca=True, path_length=None), critical=True)
|
.add_extension(BasicConstraints(ca=True, path_length=None), critical=True)
|
||||||
.add_extension(x509.SubjectKeyIdentifier.from_public_key(ca_public), critical=False)
|
.add_extension(SubjectKeyIdentifier.from_public_key(ca_public), critical=False)
|
||||||
.add_extension(authority_key, critical=False)
|
.add_extension(authority_key, critical=False)
|
||||||
.add_extension(self.key_usage, critical=True)
|
.add_extension(self.key_usage, critical=True)
|
||||||
.sign(root_key, hashes.SHA256())
|
.sign(root_key, SHA256())
|
||||||
)
|
)
|
||||||
file_path=os.path.join(CERTS_PATH, "CAcross.pem")
|
file_path=os.path.join(CERTS_PATH, "CAcross.pem")
|
||||||
with open(file_path, mode="wb") as file:
|
with open(file_path, mode="wb") as file:
|
||||||
file.write(ca_cross.public_bytes(encoding=serialization.Encoding.PEM))
|
file.write(ca_cross.public_bytes(encoding=Encoding.PEM))
|
||||||
|
|
||||||
def write_key(self, key, file_name) -> None:
|
def write_key(self, key, file_name) -> None:
|
||||||
"""Write a private RSA key"""
|
"""Write a private RSA key"""
|
||||||
@@ -194,27 +243,27 @@ class RootCACertificate(X509Extensions):
|
|||||||
file_path = os.path.join(CERTS_PATH, file_name + "p.pem")
|
file_path = os.path.join(CERTS_PATH, file_name + "p.pem")
|
||||||
with open(file_path, mode="wb") as file:
|
with open(file_path, mode="wb") as file:
|
||||||
file.write(key.private_bytes(
|
file.write(key.private_bytes(
|
||||||
encoding=serialization.Encoding.PEM,
|
encoding=Encoding.PEM,
|
||||||
format=serialization.PrivateFormat.PKCS8,
|
format=PrivateFormat.PKCS8,
|
||||||
encryption_algorithm=serialization.BestAvailableEncryption(PASSWORD.encode())
|
encryption_algorithm=BestAvailableEncryption(PASSWORD.encode())
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
# Write decrypted key in PEM format
|
# Write decrypted key in PEM format
|
||||||
file_path = os.path.join(CERTS_PATH, file_name + ".pem")
|
file_path = os.path.join(CERTS_PATH, file_name + ".pem")
|
||||||
with open(file_path, mode="wb") as file:
|
with open(file_path, mode="wb") as file:
|
||||||
file.write(key.private_bytes(
|
file.write(key.private_bytes(
|
||||||
encoding=serialization.Encoding.PEM,
|
encoding=Encoding.PEM,
|
||||||
format=serialization.PrivateFormat.PKCS8,
|
format=PrivateFormat.PKCS8,
|
||||||
encryption_algorithm=serialization.NoEncryption()
|
encryption_algorithm=NoEncryption()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
# Write the key in DER format
|
# Write the key in DER format
|
||||||
file_path = os.path.join(CERTS_PATH, file_name + ".der")
|
file_path = os.path.join(CERTS_PATH, file_name + ".der")
|
||||||
with open(file_path, mode="wb") as file:
|
with open(file_path, mode="wb") as file:
|
||||||
file.write(key.private_bytes(
|
file.write(key.private_bytes(
|
||||||
encoding=serialization.Encoding.DER,
|
encoding=Encoding.DER,
|
||||||
format=serialization.PrivateFormat.PKCS8,
|
format=PrivateFormat.PKCS8,
|
||||||
encryption_algorithm=serialization.NoEncryption()
|
encryption_algorithm=NoEncryption()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -225,13 +274,13 @@ class TSARootCACertificate(X509Extensions):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__("Timestamp Authority Root CA", 0, None)
|
super().__init__("Timestamp Authority Root CA", 0, None)
|
||||||
|
|
||||||
def make_cert(self) -> (x509.Certificate, rsa.RSAPrivateKey):
|
def make_cert(self) -> (Certificate, RSAPrivateKey):
|
||||||
"""Generate a Time Stamp Authority certificate"""
|
"""Generate a Time Stamp Authority certificate"""
|
||||||
ca_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
|
ca_key = generate_private_key(public_exponent=65537, key_size=2048)
|
||||||
ca_public = ca_key.public_key()
|
ca_public = ca_key.public_key()
|
||||||
authority_key = x509.AuthorityKeyIdentifier.from_issuer_public_key(ca_public)
|
authority_key = AuthorityKeyIdentifier.from_issuer_public_key(ca_public)
|
||||||
name = self.create_x509_name("TSA Root CA")
|
name = self.create_x509_name("TSA Root CA")
|
||||||
key_usage = x509.KeyUsage(
|
key_usage = KeyUsage(
|
||||||
digital_signature=False,
|
digital_signature=False,
|
||||||
content_commitment=False,
|
content_commitment=False,
|
||||||
key_encipherment=False,
|
key_encipherment=False,
|
||||||
@@ -243,22 +292,22 @@ class TSARootCACertificate(X509Extensions):
|
|||||||
decipher_only=False
|
decipher_only=False
|
||||||
)
|
)
|
||||||
ca_cert = (
|
ca_cert = (
|
||||||
x509.CertificateBuilder()
|
CertificateBuilder()
|
||||||
.subject_name(name)
|
.subject_name(name)
|
||||||
.issuer_name(name)
|
.issuer_name(name)
|
||||||
.public_key(ca_public)
|
.public_key(ca_public)
|
||||||
.serial_number(x509.random_serial_number())
|
.serial_number(random_serial_number())
|
||||||
.not_valid_before(date_20170101)
|
.not_valid_before(date_20170101)
|
||||||
.not_valid_after(date_20170101 + datetime.timedelta(days=7300))
|
.not_valid_after(date_20170101 + datetime.timedelta(days=7300))
|
||||||
.add_extension(x509.BasicConstraints(ca=True, path_length=None), critical=True)
|
.add_extension(BasicConstraints(ca=True, path_length=None), critical=True)
|
||||||
.add_extension(x509.SubjectKeyIdentifier.from_public_key(ca_public), critical=False)
|
.add_extension(SubjectKeyIdentifier.from_public_key(ca_public), critical=False)
|
||||||
.add_extension(authority_key, critical=False)
|
.add_extension(authority_key, critical=False)
|
||||||
.add_extension(key_usage, critical=True)
|
.add_extension(key_usage, critical=True)
|
||||||
.sign(ca_key, hashes.SHA256())
|
.sign(ca_key, SHA256())
|
||||||
)
|
)
|
||||||
file_path=os.path.join(CERTS_PATH, "TSACA.pem")
|
file_path=os.path.join(CERTS_PATH, "TSACA.pem")
|
||||||
with open(file_path, mode="wb") as file:
|
with open(file_path, mode="wb") as file:
|
||||||
file.write(ca_cert.public_bytes(encoding=serialization.Encoding.PEM))
|
file.write(ca_cert.public_bytes(encoding=Encoding.PEM))
|
||||||
|
|
||||||
return ca_cert, ca_key
|
return ca_cert, ca_key
|
||||||
|
|
||||||
@@ -267,14 +316,14 @@ class TSARootCACertificate(X509Extensions):
|
|||||||
file_path = os.path.join(CERTS_PATH, file_name + ".key")
|
file_path = os.path.join(CERTS_PATH, file_name + ".key")
|
||||||
with open(file_path, mode="wb") as file:
|
with open(file_path, mode="wb") as file:
|
||||||
file.write(key.private_bytes(
|
file.write(key.private_bytes(
|
||||||
encoding=serialization.Encoding.PEM,
|
encoding=Encoding.PEM,
|
||||||
format=serialization.PrivateFormat.PKCS8,
|
format=PrivateFormat.PKCS8,
|
||||||
encryption_algorithm=serialization.NoEncryption()
|
encryption_algorithm=NoEncryption()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class Certificate(X509Extensions):
|
class LeafCertificate(X509Extensions):
|
||||||
"""Base class for a leaf certificate"""
|
"""Base class for a leaf certificate"""
|
||||||
|
|
||||||
def __init__(self, issuer_cert, issuer_key, unit_name, common_name, cdp_port, cdp_name):
|
def __init__(self, issuer_cert, issuer_key, unit_name, common_name, cdp_port, cdp_name):
|
||||||
@@ -284,78 +333,90 @@ class Certificate(X509Extensions):
|
|||||||
self.common_name = common_name
|
self.common_name = common_name
|
||||||
super().__init__(unit_name, cdp_port, cdp_name)
|
super().__init__(unit_name, cdp_port, cdp_name)
|
||||||
|
|
||||||
def make_cert(self, public_key, not_before, days) -> x509.Certificate:
|
def make_cert(self, public_key, not_before, days) -> Certificate:
|
||||||
"""Generate a leaf certificate"""
|
"""Generate a leaf certificate"""
|
||||||
authority_key = x509.AuthorityKeyIdentifier.from_issuer_subject_key_identifier(
|
authority_key = AuthorityKeyIdentifier.from_issuer_subject_key_identifier(
|
||||||
self.issuer_cert.extensions.get_extension_for_class(x509.SubjectKeyIdentifier).value
|
self.issuer_cert.extensions.get_extension_for_class(SubjectKeyIdentifier).value
|
||||||
)
|
)
|
||||||
extended_key_usage = x509.ExtendedKeyUsage(
|
key_usage = KeyUsage(
|
||||||
[x509.oid.ExtendedKeyUsageOID.CODE_SIGNING]
|
digital_signature=True,
|
||||||
|
content_commitment=False,
|
||||||
|
key_encipherment=False,
|
||||||
|
data_encipherment=False,
|
||||||
|
key_agreement=False,
|
||||||
|
key_cert_sign=False,
|
||||||
|
crl_sign=False,
|
||||||
|
encipher_only=False,
|
||||||
|
decipher_only=False
|
||||||
|
)
|
||||||
|
extended_key_usage = ExtendedKeyUsage(
|
||||||
|
[ExtendedKeyUsageOID.CODE_SIGNING]
|
||||||
)
|
)
|
||||||
cert = (
|
cert = (
|
||||||
x509.CertificateBuilder()
|
CertificateBuilder()
|
||||||
.subject_name(self.create_x509_name(self.common_name))
|
.subject_name(self.create_x509_name(self.common_name))
|
||||||
.issuer_name(self.issuer_cert.subject)
|
.issuer_name(self.issuer_cert.subject)
|
||||||
.public_key(public_key)
|
.public_key(public_key)
|
||||||
.serial_number(x509.random_serial_number())
|
.serial_number(random_serial_number())
|
||||||
.not_valid_before(not_before)
|
.not_valid_before(not_before)
|
||||||
.not_valid_after(not_before + datetime.timedelta(days=days))
|
.not_valid_after(not_before + datetime.timedelta(days=days))
|
||||||
.add_extension(x509.BasicConstraints(ca=False, path_length=None), critical=False)
|
.add_extension(BasicConstraints(ca=False, path_length=None), critical=False)
|
||||||
.add_extension(x509.SubjectKeyIdentifier.from_public_key(public_key), critical=False)
|
.add_extension(SubjectKeyIdentifier.from_public_key(public_key), critical=False)
|
||||||
.add_extension(authority_key, critical=False)
|
.add_extension(authority_key, critical=False)
|
||||||
|
.add_extension(key_usage, critical=False)
|
||||||
.add_extension(extended_key_usage, critical=False)
|
.add_extension(extended_key_usage, critical=False)
|
||||||
.add_extension(self.create_x509_crldp(), critical=False)
|
.add_extension(self.create_x509_crldp(), critical=False)
|
||||||
.sign(self.issuer_key, hashes.SHA256())
|
.sign(self.issuer_key, SHA256())
|
||||||
)
|
)
|
||||||
# Write PEM file and attach intermediate certificate
|
# Write PEM file and attach intermediate certificate
|
||||||
file_path = os.path.join(CERTS_PATH, self.common_name + ".pem")
|
file_path = os.path.join(CERTS_PATH, self.common_name + ".pem")
|
||||||
with open(file_path, mode="wb") as file:
|
with open(file_path, mode="wb") as file:
|
||||||
file.write(cert.public_bytes(encoding=serialization.Encoding.PEM))
|
file.write(cert.public_bytes(encoding=Encoding.PEM))
|
||||||
file.write(self.issuer_cert.public_bytes(encoding=serialization.Encoding.PEM))
|
file.write(self.issuer_cert.public_bytes(encoding=Encoding.PEM))
|
||||||
|
|
||||||
return cert
|
return cert
|
||||||
|
|
||||||
def revoke_cert(self, serial_number, file_name) -> None:
|
def revoke_cert(self, serial_number, file_name) -> None:
|
||||||
"""Revoke a certificate"""
|
"""Revoke a certificate"""
|
||||||
revoked = (
|
revoked = (
|
||||||
x509.RevokedCertificateBuilder()
|
RevokedCertificateBuilder()
|
||||||
.serial_number(serial_number)
|
.serial_number(serial_number)
|
||||||
.revocation_date(date_20190101)
|
.revocation_date(date_20190101)
|
||||||
.add_extension(x509.CRLReason(x509.ReasonFlags.superseded), critical=False)
|
.add_extension(CRLReason(ReasonFlags.superseded), critical=False)
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
# Generate CRL
|
# Generate CRL
|
||||||
authority_key = x509.AuthorityKeyIdentifier.from_issuer_subject_key_identifier(
|
authority_key = AuthorityKeyIdentifier.from_issuer_subject_key_identifier(
|
||||||
self.issuer_cert.extensions.get_extension_for_class(x509.SubjectKeyIdentifier).value
|
self.issuer_cert.extensions.get_extension_for_class(SubjectKeyIdentifier).value
|
||||||
)
|
)
|
||||||
crl = (
|
crl = (
|
||||||
x509.CertificateRevocationListBuilder()
|
CertificateRevocationListBuilder()
|
||||||
.issuer_name(self.issuer_cert.subject)
|
.issuer_name(self.issuer_cert.subject)
|
||||||
.last_update(date_20190101)
|
.last_update(date_20190101)
|
||||||
.next_update(date_20190101 + datetime.timedelta(days=7300))
|
.next_update(date_20190101 + datetime.timedelta(days=7300))
|
||||||
.add_extension(authority_key, critical=False)
|
.add_extension(authority_key, critical=False)
|
||||||
.add_extension(x509.CRLNumber(4097), critical=False)
|
.add_extension(CRLNumber(4097), critical=False)
|
||||||
.add_revoked_certificate(revoked)
|
.add_revoked_certificate(revoked)
|
||||||
.sign(self.issuer_key, hashes.SHA256())
|
.sign(self.issuer_key, SHA256())
|
||||||
)
|
)
|
||||||
# Write CRL file
|
# Write CRL file
|
||||||
file_path = os.path.join(CERTS_PATH, file_name + ".pem")
|
file_path = os.path.join(CERTS_PATH, file_name + ".pem")
|
||||||
with open(file_path, mode="wb") as file:
|
with open(file_path, mode="wb") as file:
|
||||||
file.write(crl.public_bytes(encoding=serialization.Encoding.PEM))
|
file.write(crl.public_bytes(encoding=Encoding.PEM))
|
||||||
|
|
||||||
file_path = os.path.join(CERTS_PATH, file_name + ".der")
|
file_path = os.path.join(CERTS_PATH, file_name + ".der")
|
||||||
with open(file_path, mode="wb") as file:
|
with open(file_path, mode="wb") as file:
|
||||||
file.write(crl.public_bytes(encoding=serialization.Encoding.DER))
|
file.write(crl.public_bytes(encoding=Encoding.DER))
|
||||||
|
|
||||||
|
|
||||||
class LeafCACertificate(Certificate):
|
class LeafCACertificate(LeafCertificate):
|
||||||
"""Base class for a leaf certificate"""
|
"""Base class for a leaf certificate"""
|
||||||
|
|
||||||
def __init__(self, issuer_cert, issuer_key, common, cdp_port):
|
def __init__(self, issuer_cert, issuer_key, common, cdp_port):
|
||||||
super().__init__(issuer_cert, issuer_key, "CSP", common, cdp_port, "intermediateCA")
|
super().__init__(issuer_cert, issuer_key, "CSP", common, cdp_port, "intermediateCA")
|
||||||
|
|
||||||
|
|
||||||
class LeafTSACertificate(Certificate):
|
class LeafTSACertificate(LeafCertificate):
|
||||||
"""Base class for a TSA leaf certificate"""
|
"""Base class for a TSA leaf certificate"""
|
||||||
|
|
||||||
def __init__(self, issuer_cert, issuer_key, common, cdp_port):
|
def __init__(self, issuer_cert, issuer_key, common, cdp_port):
|
||||||
@@ -364,40 +425,40 @@ class LeafTSACertificate(Certificate):
|
|||||||
self.common_name = common
|
self.common_name = common
|
||||||
super().__init__(issuer_cert, issuer_key, "Timestamp Root CA", common, cdp_port, "TSACA")
|
super().__init__(issuer_cert, issuer_key, "Timestamp Root CA", common, cdp_port, "TSACA")
|
||||||
|
|
||||||
def make_cert(self, public_key, not_before, days) -> x509.Certificate:
|
def make_cert(self, public_key, not_before, days) -> Certificate:
|
||||||
"""Generate a TSA leaf certificate"""
|
"""Generate a TSA leaf certificate"""
|
||||||
|
|
||||||
authority_key = x509.AuthorityKeyIdentifier.from_issuer_subject_key_identifier(
|
authority_key = AuthorityKeyIdentifier.from_issuer_subject_key_identifier(
|
||||||
self.issuer_cert.extensions.get_extension_for_class(x509.SubjectKeyIdentifier).value
|
self.issuer_cert.extensions.get_extension_for_class(SubjectKeyIdentifier).value
|
||||||
)
|
)
|
||||||
|
|
||||||
# The TSA signing certificate must have exactly one extended key usage
|
# The TSA signing certificate must have exactly one extended key usage
|
||||||
# assigned to it: timeStamping. The extended key usage must also be critical,
|
# assigned to it: timeStamping. The extended key usage must also be critical,
|
||||||
# otherwise the certificate is going to be refused.
|
# otherwise the certificate is going to be refused.
|
||||||
extended_key_usage = x509.ExtendedKeyUsage(
|
extended_key_usage = ExtendedKeyUsage(
|
||||||
[x509.oid.ExtendedKeyUsageOID.TIME_STAMPING]
|
[ExtendedKeyUsageOID.TIME_STAMPING]
|
||||||
)
|
)
|
||||||
cert = (
|
cert = (
|
||||||
x509.CertificateBuilder()
|
CertificateBuilder()
|
||||||
.subject_name(self.create_x509_name(self.common_name))
|
.subject_name(self.create_x509_name(self.common_name))
|
||||||
.issuer_name(self.issuer_cert.subject)
|
.issuer_name(self.issuer_cert.subject)
|
||||||
.public_key(public_key)
|
.public_key(public_key)
|
||||||
.serial_number(x509.random_serial_number())
|
.serial_number(random_serial_number())
|
||||||
.not_valid_before(not_before)
|
.not_valid_before(not_before)
|
||||||
.not_valid_after(not_before + datetime.timedelta(days=days))
|
.not_valid_after(not_before + datetime.timedelta(days=days))
|
||||||
.add_extension(x509.BasicConstraints(ca=False, path_length=None), critical=True)
|
.add_extension(BasicConstraints(ca=False, path_length=None), critical=True)
|
||||||
.add_extension(x509.SubjectKeyIdentifier.from_public_key(public_key), critical=False)
|
.add_extension(SubjectKeyIdentifier.from_public_key(public_key), critical=False)
|
||||||
.add_extension(authority_key, critical=False)
|
.add_extension(authority_key, critical=False)
|
||||||
.add_extension(extended_key_usage, critical=True)
|
.add_extension(extended_key_usage, critical=True)
|
||||||
.add_extension(self.create_x509_crldp(), critical=False)
|
.add_extension(self.create_x509_crldp(), critical=False)
|
||||||
.add_extension(self.create_x509_name_constraints(), critical=False)
|
.add_extension(self.create_x509_name_constraints(), critical=False)
|
||||||
.sign(self.issuer_key, hashes.SHA256())
|
.sign(self.issuer_key, SHA256())
|
||||||
)
|
)
|
||||||
# Write PEM file and attach intermediate certificate
|
# Write PEM file and attach intermediate certificate
|
||||||
file_path = os.path.join(CERTS_PATH, self.common_name + ".pem")
|
file_path = os.path.join(CERTS_PATH, self.common_name + ".pem")
|
||||||
with open(file_path, mode="wb") as file:
|
with open(file_path, mode="wb") as file:
|
||||||
file.write(cert.public_bytes(encoding=serialization.Encoding.PEM))
|
file.write(cert.public_bytes(encoding=Encoding.PEM))
|
||||||
file.write(self.issuer_cert.public_bytes(encoding=serialization.Encoding.PEM))
|
file.write(self.issuer_cert.public_bytes(encoding=Encoding.PEM))
|
||||||
|
|
||||||
return cert
|
return cert
|
||||||
|
|
||||||
@@ -433,7 +494,7 @@ class CertificateMaker():
|
|||||||
issuer_cert, issuer_key = intermediate.make_cert()
|
issuer_cert, issuer_key = intermediate.make_cert()
|
||||||
|
|
||||||
# Generate private RSA key
|
# Generate private RSA key
|
||||||
private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
|
private_key = generate_private_key(public_exponent=65537, key_size=2048)
|
||||||
public_key = private_key.public_key()
|
public_key = private_key.public_key()
|
||||||
root.write_key(key=private_key, file_name="key")
|
root.write_key(key=private_key, file_name="key")
|
||||||
|
|
||||||
@@ -460,7 +521,7 @@ class CertificateMaker():
|
|||||||
# Write DER file and attach intermediate certificate
|
# Write DER file and attach intermediate certificate
|
||||||
file_path = os.path.join(CERTS_PATH, "cert.der")
|
file_path = os.path.join(CERTS_PATH, "cert.der")
|
||||||
with open(file_path, mode="wb") as file:
|
with open(file_path, mode="wb") as file:
|
||||||
file.write(cert.public_bytes(encoding=serialization.Encoding.DER))
|
file.write(cert.public_bytes(encoding=Encoding.DER))
|
||||||
|
|
||||||
def make_tsa_certs(self):
|
def make_tsa_certs(self):
|
||||||
"""Make test TSA certificates"""
|
"""Make test TSA certificates"""
|
||||||
@@ -470,7 +531,7 @@ class CertificateMaker():
|
|||||||
issuer_cert, issuer_key = root.make_cert()
|
issuer_cert, issuer_key = root.make_cert()
|
||||||
|
|
||||||
# Generate private RSA key
|
# Generate private RSA key
|
||||||
private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
|
private_key = generate_private_key(public_exponent=65537, key_size=2048)
|
||||||
public_key = private_key.public_key()
|
public_key = private_key.public_key()
|
||||||
root.write_key(key=private_key, file_name="TSA")
|
root.write_key(key=private_key, file_name="TSA")
|
||||||
|
|
||||||
@@ -486,8 +547,8 @@ class CertificateMaker():
|
|||||||
# Save the chain to be included in the TSA response
|
# Save the chain to be included in the TSA response
|
||||||
file_path = os.path.join(CERTS_PATH, "tsa-chain.pem")
|
file_path = os.path.join(CERTS_PATH, "tsa-chain.pem")
|
||||||
with open(file_path, mode="wb") as file:
|
with open(file_path, mode="wb") as file:
|
||||||
file.write(cert.public_bytes(encoding=serialization.Encoding.PEM))
|
file.write(cert.public_bytes(encoding=Encoding.PEM))
|
||||||
file.write(issuer_cert.public_bytes(encoding=serialization.Encoding.PEM))
|
file.write(issuer_cert.public_bytes(encoding=Encoding.PEM))
|
||||||
|
|
||||||
|
|
||||||
def write_pkcs12_container(self, cert, key, issuer) -> None:
|
def write_pkcs12_container(self, cert, key, issuer) -> None:
|
||||||
@@ -499,16 +560,16 @@ class CertificateMaker():
|
|||||||
# and private key encryption: DES-EDE3-CBC (vel 3DES_CBC)
|
# and private key encryption: DES-EDE3-CBC (vel 3DES_CBC)
|
||||||
# pylint: disable=no-member
|
# pylint: disable=no-member
|
||||||
encryption = (
|
encryption = (
|
||||||
serialization.PrivateFormat.PKCS12.encryption_builder()
|
PrivateFormat.PKCS12.encryption_builder()
|
||||||
.key_cert_algorithm(serialization.pkcs12.PBES.PBESv1SHA1And3KeyTripleDESCBC)
|
.key_cert_algorithm(PBES.PBESv1SHA1And3KeyTripleDESCBC)
|
||||||
.kdf_rounds(5000)
|
.kdf_rounds(5000)
|
||||||
.build(PASSWORD.encode())
|
.build(PASSWORD.encode())
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
encryption = serialization.BestAvailableEncryption(PASSWORD.encode())
|
encryption = BestAvailableEncryption(PASSWORD.encode())
|
||||||
|
|
||||||
# Generate PKCS#12 struct
|
# Generate PKCS#12 struct
|
||||||
pkcs12 = serialization.pkcs12.serialize_key_and_certificates(
|
pkcs12 = serialize_key_and_certificates(
|
||||||
name=b'certificate',
|
name=b'certificate',
|
||||||
key=key,
|
key=key,
|
||||||
cert=cert,
|
cert=cert,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/python3
|
#!/usr/bin/python3
|
||||||
"""Implementation of a HTTP server"""
|
"""Implementation of an HTTP server"""
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
@@ -17,7 +17,7 @@ CERTS_PATH = os.path.join(RESULT_PATH, "./Testing/certs/")
|
|||||||
CONF_PATH = os.path.join(RESULT_PATH, "./Testing/conf/")
|
CONF_PATH = os.path.join(RESULT_PATH, "./Testing/conf/")
|
||||||
LOGS_PATH = os.path.join(RESULT_PATH, "./Testing/logs/")
|
LOGS_PATH = os.path.join(RESULT_PATH, "./Testing/logs/")
|
||||||
REQUEST = os.path.join(FILES_PATH, "./jreq.tsq")
|
REQUEST = os.path.join(FILES_PATH, "./jreq.tsq")
|
||||||
RESPONS = os.path.join(FILES_PATH, "./jresp.tsr")
|
RESPONSE = os.path.join(FILES_PATH, "./jresp.tsr")
|
||||||
OPENSSL_CONF = os.path.join(CONF_PATH, "./openssl_tsa.cnf")
|
OPENSSL_CONF = os.path.join(CONF_PATH, "./openssl_tsa.cnf")
|
||||||
SERVER_LOG = os.path.join(LOGS_PATH, "./server.log")
|
SERVER_LOG = os.path.join(LOGS_PATH, "./server.log")
|
||||||
URL_LOG = os.path.join(LOGS_PATH, "./url.log")
|
URL_LOG = os.path.join(LOGS_PATH, "./url.log")
|
||||||
@@ -26,7 +26,7 @@ OPENSSL_TS = ["openssl", "ts",
|
|||||||
"-reply", "-config", OPENSSL_CONF,
|
"-reply", "-config", OPENSSL_CONF,
|
||||||
"-passin", "pass:passme",
|
"-passin", "pass:passme",
|
||||||
"-queryfile", REQUEST,
|
"-queryfile", REQUEST,
|
||||||
"-out", RESPONS]
|
"-out", RESPONSE]
|
||||||
|
|
||||||
|
|
||||||
class ThreadingHTTPServer(ThreadingMixIn, HTTPServer):
|
class ThreadingHTTPServer(ThreadingMixIn, HTTPServer):
|
||||||
@@ -86,7 +86,7 @@ class RequestHandler(SimpleHTTPRequestHandler):
|
|||||||
self.send_header("Content-type", "application/timestamp-reply")
|
self.send_header("Content-type", "application/timestamp-reply")
|
||||||
self.end_headers()
|
self.end_headers()
|
||||||
resp_data = b''
|
resp_data = b''
|
||||||
with open(RESPONS, mode="rb") as file:
|
with open(RESPONSE, mode="rb") as file:
|
||||||
resp_data = file.read()
|
resp_data = file.read()
|
||||||
self.wfile.write(resp_data)
|
self.wfile.write(resp_data)
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/python3
|
#!/usr/bin/python3
|
||||||
"""Windows: Implementation of a HTTP server"""
|
"""Windows: Implementation of an HTTP server"""
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
@@ -16,7 +16,7 @@ CERTS_PATH = os.path.join(RESULT_PATH, "./Testing/certs/")
|
|||||||
CONF_PATH = os.path.join(RESULT_PATH, "./Testing/conf/")
|
CONF_PATH = os.path.join(RESULT_PATH, "./Testing/conf/")
|
||||||
LOGS_PATH = os.path.join(RESULT_PATH, "./Testing/logs/")
|
LOGS_PATH = os.path.join(RESULT_PATH, "./Testing/logs/")
|
||||||
REQUEST = os.path.join(FILES_PATH, "./jreq.tsq")
|
REQUEST = os.path.join(FILES_PATH, "./jreq.tsq")
|
||||||
RESPONS = os.path.join(FILES_PATH, "./jresp.tsr")
|
RESPONSE = os.path.join(FILES_PATH, "./jresp.tsr")
|
||||||
OPENSSL_CONF = os.path.join(CONF_PATH, "./openssl_tsa.cnf")
|
OPENSSL_CONF = os.path.join(CONF_PATH, "./openssl_tsa.cnf")
|
||||||
SERVER_LOG = os.path.join(LOGS_PATH, "./server.log")
|
SERVER_LOG = os.path.join(LOGS_PATH, "./server.log")
|
||||||
URL_LOG = os.path.join(LOGS_PATH, "./url.log")
|
URL_LOG = os.path.join(LOGS_PATH, "./url.log")
|
||||||
@@ -26,7 +26,7 @@ OPENSSL_TS = ["openssl", "ts",
|
|||||||
"-reply", "-config", OPENSSL_CONF,
|
"-reply", "-config", OPENSSL_CONF,
|
||||||
"-passin", "pass:passme",
|
"-passin", "pass:passme",
|
||||||
"-queryfile", REQUEST,
|
"-queryfile", REQUEST,
|
||||||
"-out", RESPONS]
|
"-out", RESPONSE]
|
||||||
|
|
||||||
|
|
||||||
class RequestHandler(SimpleHTTPRequestHandler):
|
class RequestHandler(SimpleHTTPRequestHandler):
|
||||||
@@ -82,7 +82,7 @@ class RequestHandler(SimpleHTTPRequestHandler):
|
|||||||
self.send_header("Content-type", "application/timestamp-reply")
|
self.send_header("Content-type", "application/timestamp-reply")
|
||||||
self.end_headers()
|
self.end_headers()
|
||||||
resp_data = b''
|
resp_data = b''
|
||||||
with open(RESPONS, mode="rb") as file:
|
with open(RESPONSE, mode="rb") as file:
|
||||||
resp_data = file.read()
|
resp_data = file.read()
|
||||||
self.wfile.write(resp_data)
|
self.wfile.write(resp_data)
|
||||||
except Exception as err: # pylint: disable=broad-except
|
except Exception as err: # pylint: disable=broad-except
|
||||||
|
|||||||
@@ -55,3 +55,6 @@ CATATTR1=0x11010001:OSAttr:2:6.0
|
|||||||
|
|
||||||
<HASH>MOFfile=..\files\unsigned.mof
|
<HASH>MOFfile=..\files\unsigned.mof
|
||||||
<HASH>MOFfileATTR1=0x11010001:File:unsigned.mof
|
<HASH>MOFfileATTR1=0x11010001:File:unsigned.mof
|
||||||
|
|
||||||
|
<HASH>JSfile=..\files\unsigned.js
|
||||||
|
<HASH>JSfileATTR1=0x11010001:File:unsigned.js
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ def clear_catalog(certs_path) -> None:
|
|||||||
file.write(random_hex)
|
file.write(random_hex)
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
"""Wait for all tests certificate, compute leafhash"""
|
"""Wait for all test certificates and compute leaf hash"""
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
|||||||
Reference in New Issue
Block a user