1
0
Fork 0
mirror of https://github.com/pgpainless/pgpainless.git synced 2025-09-09 10:19:39 +02:00

Compare commits

...

76 commits
1.7.0 ... main

Author SHA1 Message Date
0fe3a7abf6
Fix ArmorUtils spotless complaint 2025-08-30 10:36:40 +02:00
0d807cb6b8
Fix typo in error message 2025-07-23 11:26:40 +02:00
9b0a3cd4c7
Do not trim passphrases automatically 2025-07-23 11:26:28 +02:00
0ee31b232a
Allow UserIDs with trailing/leading whitespace and escape newlines in ASCII armor 2025-07-23 11:26:17 +02:00
f2cbde43be
Update codeql action to v3 2025-07-01 10:54:06 +02:00
4cf6c6b16a
Update CHANGELOG 2025-06-17 10:42:50 +02:00
0f54cc615c
Bump BC to 1.81, update native-image reflect-config, resource-config 2025-06-17 10:30:42 +02:00
a74db2d26d
Merge pull request #475 from felhag/fix/typo-readme
Fixed typo in sop readme
2025-06-04 16:37:00 +02:00
Felix Hagemans
5f30df6d16 Fixed typo in sop readme 2025-06-04 16:02:23 +02:00
7953ade136
Bump checkstyle to 10.25.0
Fixes https://github.com/pgpainless/pgpainless/security/dependabot/24
2025-06-03 12:37:04 +02:00
0649c041cd
gradle: migrate to new shadow plugin namespace 2025-04-21 19:12:10 +02:00
5a413f53a4
Specify license information for native-image metadata 2025-04-21 19:11:52 +02:00
3b92ccc59d
PGPainless 1.7.7-SNAPSHOT 2025-04-14 16:05:05 +02:00
83613250ef
PGPainless 1.7.6 2025-04-14 15:55:29 +02:00
05c84835e6
Bump SOP-Java to 10.1.1 2025-04-14 15:31:49 +02:00
d20a3b7556
Add config files for nativeimage
Those were generated by running the following commands in order:

gradle -Pagent test
gradle metadataCopy --task test --dir src/main/resources/META-INF/native-image

gradle nativeCompile

The resulting nativeimage can resolve method calls that use reflection. Yay
2025-04-14 15:31:49 +02:00
2d0608cf0f
Re-add shadow plugin 2025-04-13 19:45:12 +02:00
143c9777d6
Implement graal nativeimage compilation
Requires sop-java 10.1.1-SNAPSHOT for now, as that version includes picocli configurations files
2025-04-03 15:27:49 +02:00
9ac928fcf1
Update changelog 2025-03-26 15:04:22 +01:00
811f72ffef
Fix RevocationSignatureBuilder properly calculating 3rd-party delegation revocations 2025-03-26 15:02:52 +01:00
a6f8058fb4
PGPainless 1.7.6-SNAPSHOT 2025-03-25 12:09:43 +01:00
de5ef4de00
PGPainless 1.7.5 2025-03-25 12:05:33 +01:00
2b9c5ea272
Update CHANGELOG 2025-03-25 12:01:29 +01:00
f054c30460
Upgrade gradle-wrapper to 8.8 2025-03-25 11:58:55 +01:00
65113a6d82
Rework gradle, making use of toolchain feature 2025-03-25 11:22:16 +01:00
a0254f47fb
Simplify mavenCentralChecksums task 2025-03-25 11:03:22 +01:00
eebd02e309
Perform coveralls task after jacocoRootReport 2025-03-20 18:18:40 +01:00
7dc4329c52
PGPainless 1.7.5-SNAPSHOT 2025-03-20 18:05:37 +01:00
f22cada0ce
PGPainless 1.7.4 2025-03-20 18:02:07 +01:00
cb51bb64f3
Update changelog 2025-03-20 17:35:08 +01:00
c861a5eb73
Set Java release version to 8 to fix Kotlin desugaring
Fixes https://github.com/pgpainless/pgpainless/issues/471
2025-03-20 17:31:30 +01:00
62b0d0a560
Remove YourKit profiler usage 2025-03-14 13:44:24 +01:00
effc9e747a
Remove -werror flag from javadoc task 2025-03-11 22:55:43 +01:00
bb9393d948
PGPainless 1.7.4-SNAPSHOT 2025-03-11 22:43:46 +01:00
3b1dbf4102
PGPainless 1.7.3 2025-03-11 22:40:44 +01:00
d7e08186ac
Update CHANGELOG 2025-03-11 22:20:00 +01:00
883eb80a63
Bump bcpg, bcprov to 1.80, add bcutil dependency
Adding bcutil as a dependency is apparently required now.
See https://github.com/bcgit/bc-java/issues/1977
2025-03-11 22:04:03 +01:00
9a1a01fe05
pgpainless-cli version: Fix repository URL 2025-03-11 21:39:11 +01:00
13c3295e64
Bump sop-java to 10.1.0 2025-03-11 21:39:11 +01:00
8854429205
Revert "SOP: encrypt: Apply CRLF encoding if text encoding is used"
This reverts commit cb6dde4e39.
2025-03-11 20:53:24 +01:00
d5845d94a0
Merge pull request #470 from bjansen/bugfix/gh-469
Fix for #469
2025-03-11 19:51:08 +01:00
Bastien JANSEN
4185bf0326 Fix #469 2025-03-11 17:58:42 +01:00
74b28afd4a
SOP: inline-sign, detached-sign: Do not apply compression 2025-03-05 14:30:13 +01:00
bfdbac0f2d
Fix test by comparing to CRLF'd plaintext 2025-02-22 17:26:21 +01:00
cb6dde4e39
SOP: encrypt: Apply CRLF encoding if text encoding is used
Fixes #466
2025-02-22 15:46:36 +01:00
25c720b033
SOP inline-sign: Wrap data in LiteralData(text) packet if signing mode is text
Fixes #465
2025-02-22 13:37:51 +01:00
8d03810bf3
Fix typo in test 2025-02-18 14:26:01 +01:00
2d18d05bd7
Merge pull request #464 from elduffy/sig-bounds-typo
Fix a typo on signature creation bounds check message
2025-02-17 13:18:39 +01:00
Eric Duffy
c5bae9d50d Fix a typo on signature creation bounds check message 2025-02-14 10:35:15 -06:00
588b9d7469
Add REUSE.toml file to REUSE.toml file 2025-01-28 12:21:46 +01:00
3af506fedb
Migrate reuse from dep5 to REUSE.toml 2025-01-10 10:41:11 +01:00
1db59acf0d
Add reuse license header to properties file 2025-01-10 10:40:56 +01:00
02330a5aa1
Reproducible file order 2024-12-21 14:32:42 +01:00
4dbc633d7d
version --extended: Include sop-java version
Fixes #454
2024-12-21 13:41:37 +01:00
5018386318
Extract pgpainless-sop-version property via resource filtering 2024-12-21 13:24:05 +01:00
a43ae43722
Bump logback to 1.5.13
I hope I mitigated logback spam by modifying the logback.xml file and by
setting 'slf4j.internal.verbosity' to 'WARN'.
See https://github.com/pgpainless/pgpainless/issues/426 for reference
2024-12-21 12:43:05 +01:00
3e96af5450
Remove dependency on deprecated com.ginsberg:junit5-system-exit library 2024-12-21 11:14:37 +01:00
391549a7d6
Revert "Ensure proper compatibility with keys with missing direct-key or certification self-sigs"
This reverts commit 620c1fc96a.
2024-12-21 10:27:06 +01:00
fdf49cfafb
Improve error message when no acceptable certificate signature is found
Relates to #457
2024-11-19 13:59:41 +01:00
b99822f405
Ignore certificate signatures of unknown type 2024-11-19 13:58:54 +01:00
89790a0a94
Fix decryption example in README
Fixes #456
2024-11-11 11:52:56 +01:00
e46cbae6b8
PGPainless 1.7.3-SNAPSHOT 2024-10-31 14:54:34 +01:00
df5297a661
PGPainless 1.7.2 2024-10-31 14:52:45 +01:00
f6c4ddd288
Update changelog 2024-10-31 14:44:46 +01:00
34e9748d0f
Bump sop-java to 10.0.3 2024-10-31 14:39:56 +01:00
de4a113528
Update changelog 2024-10-24 19:06:56 +02:00
f1610f6425
Fix returning proper value for KeyRingInfo.lastModified
While porting to kotlin the code was accidentally changed to return the key ring creation
time instead of the latest self-sig creation time
2024-10-24 17:41:18 +02:00
d966349032
Add test to verify proper functionality of hash algorithm policy overrides for SOP 2024-10-15 14:45:28 +02:00
54569b3b02
Update changelog 2024-10-14 16:46:22 +02:00
dabafe538f
PGPainless 1.7.2-SNAPSHOT 2024-10-14 16:37:56 +02:00
fac23745b4
PGPainless 1.7.1 2024-10-14 16:35:52 +02:00
60c963fca5
Document logback spam 2024-10-14 16:33:47 +02:00
ab6cde3ec6
Bump sop-java to 10.0.2 2024-10-14 16:28:50 +02:00
e5a0617621
Fix CLI being spammed by logback by downgrading to logback-core 1.2.13 2024-10-14 15:47:00 +02:00
52b6d5c3f7
Fix some minor code issues 2024-10-14 15:18:49 +02:00
fb71ef2193
PGPainless 1.7.1-SNAPSHOT 2024-10-14 14:54:26 +02:00
59 changed files with 1664 additions and 579 deletions

View file

@ -36,7 +36,7 @@ jobs:
strategy:
fail-fast: false
matrix:
language: [ 'java' ]
language: [ 'java-kotlin' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Learn more about CodeQL language support at https://git.io/codeql-language-support
@ -46,7 +46,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@ -57,7 +57,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
uses: github/codeql-action/autobuild@v3
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@ -71,4 +71,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
uses: github/codeql-action/analyze@v3

View file

@ -28,9 +28,13 @@ jobs:
with:
java-version: '11'
distribution: 'temurin'
- name: Build, Check and Coverage
- name: Build and Check
uses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1
with:
arguments: check jacocoRootReport
- name: Coveralls
uses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1
env:
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}
with:
arguments: check jacocoRootReport coveralls
arguments: coveralls

View file

@ -1,86 +0,0 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: PGPainless
Upstream-Contact: Paul Schaub <info@pgpainless.org>
Source: https://pgpainless.org
# Sample paragraph, commented out:
#
# Files: src/*
# Copyright: $YEAR $NAME <$CONTACT>
# License: ...
# GitBlameIgnore
Files: .git-blame-ignore-revs
Copyright: 2023 Paul Schaub <info@pgpainless.org>
License: CC0-1.0
# Documentation
Files: docs/*
Copyright: 2022 Paul Schaub <info@pgpainless.org>
License: CC-BY-3.0
Files: .readthedocs.yaml
Copyright: 2022 Paul Schaub <info@pgpainless.org>
License: CC0-1.0
# Gradle build tool
Files: gradle*
Copyright: 2015 the original author or authors.
License: Apache-2.0
# Editorconfig
Files: .editorconfig
Copyright: Facebook
License: Apache-2.0
# PGPainless Logo
Files: assets/repository-open-graph.png
Copyright: 2021 Paul Schaub <info@pgpainless.org>
License: CC-BY-3.0
Files: assets/pgpainless.svg
Copyright: 2021 Paul Schaub <info@pgpainless.org>
License: CC-BY-3.0
Files: assets/logo.png
Copyright: 2022 Paul Schaub <info@pgpainless.org>
License: CC-BY-3.0
Files: assets/test_vectors/*
Copyright: 2018 Paul Schaub <info@pgpainless.org>
License: CC0-1.0
Files: pgpainless-core/src/test/resources/*
Copyright: 2020 Paul Schaub <info@pgpainless.org>
License: CC0-1.0
Files: audit/*
Copyright: 2021 Paul Schaub <info@pgpainless.org>
License: CC0-1.0
# GH Pages
Files: CNAME
Copyright: 2022 Paul Schaub <info@pgpainless.org>
License: CC0-1.0
Files: _config.yml
Copyright: 2022 Paul Schaub <info@pgpainless.org>
License: CC0-1.0
Files: _layouts/*
Copyright: 2022 Paul Schaub <info@pgpainless.org>, 2017 Steve Smith
License: CC-BY-SA-3.0
# Man Pages
Files: pgpainless-cli/rewriteManPages.sh
Copyright: 2022 Paul Schaub <info@pgpainless.org>
License: Apache-2.0
Files: pgpainless-cli/packaging/man/*
Copyright: 2022 Paul Schaub <info@pgpainless.org>
License: Apache-2.0
# Github Issue Templates
Files: .github/ISSUE_TEMPLATE/*
Copyright: 2024 Paul Schaub <info@pgpainless.org>
License: CC0-1.0

View file

@ -5,8 +5,44 @@ SPDX-License-Identifier: CC0-1.0
# PGPainless Changelog
## 1.7.7-SNAPSHOT
- Bump `bcpg-jdk8on` to `1.81`
- Bump `bcprov-jdk18on` to `1.81`
## 1.7.0-SNAPSHOT
## 1.7.6
- Fix `RevocationSignatureBuilder` properly calculating third-party signatures of type `KeyRevocation` (delegation revocations)
- Enable support for native images
- Re-enable shadow plugin and build fat-jar
## 1.7.5
- Actually attempt to fix Kotlin desugaring.
- Bump javaSourceCompatibility and javaTargetCompatibility to 11
- Bump gradle-wrapper to 8.8
## 1.7.4
- Fix proper Kotlin desugaring for Java 8
## 1.7.3
- Bump `bcpg-jdk8on` to `1.80`
- Bump `bcprov-jdk18on` to `1.80`
- Add dependency on `bcutil-jdk18on` as a workaround
- Ignore unknown type signatures on certificates
- Fix typo on signature creation bounds check (thanks @elduffy)
- Fix superfluous newline added in CRLF encoding (thanks @bjansen)
- Bump `sop-java` to `1.10.0`
- SOP inline-sign: Do not apply compression
## 1.7.2
- Fix bug in `KeyRingInfo.lastModified` (thanks to @Jerbell, @sosnovsky for reporting)
- Bump `sop-java` to `10.0.3`
- allow multiple arguments `--with-key-password` in `revoke-key` command
- Properly pass `--old-key-password` and `--new-key-password` options as indirect arguments in `change-key-password` command
## 1.7.1
- Bump `sop-java` to `10.0.2`
- Downgrade `logback-core` and `logback-classic` to `1.2.13` (fix CLI spam)
## 1.7.0
- Bump `bcpg-jdk8on` to `1.78.1`
- Bump `bcprov-jdk18on` to `1.78.1`
- Bump `logback-core` and `logback-classic` to `1.4.14`
@ -27,7 +63,7 @@ SPDX-License-Identifier: CC0-1.0
- Do not choke on LibrePGP OED packets
- Supersede `addPassphrase()`/`addDecryptionPassphrase()` methods with more clear `addMessagePassphrase()`
- `pgpainless-sop`, `pgpainless-cli`
- Bump `sop-java` to `10.0.0`, implementing [SOP Spec Revision 10](https://www.ietf.org/archive/id/draft-dkg-openpgp-stateless-cli-10.html)
- Bump `sop-java` to `10.0.1`, implementing [SOP Spec Revision 10](https://www.ietf.org/archive/id/draft-dkg-openpgp-stateless-cli-10.html)
- Change API of `sop.encrypt` to return a `ReadyWithResult<EncryptionResult>` to expose the session key
- `decrypt --verify-with`: Fix to not throw `NoSignature` exception (exit code 3) if `VERIFICATIONS` is empty
- Separate signature verification operations into `SOPV` interface
@ -35,6 +71,11 @@ SPDX-License-Identifier: CC0-1.0
- Throw `BadData` error when passing KEYS where CERTS are expected.
- `armor`: Remove `--label` option
## 1.6.8
- Bump `sop-java` to `7.0.2`
- SOP `change-key-password`: Fix reading password from indirect parameter instead of erroneously passing filename (fixes #453)
- SOP `revoke-key`: Allow for multiple `--with-key-password` options
## 1.6.7
- SOP: Fix OOM error when detached-signing large amounts of data (fix #432)
- Move `CachingBcPublicKeyDataDecryptorFactory` from `org.bouncycastle` packet to `org.pgpainless.decryption_verification` to avoid package split (partially addresses #428)

View file

@ -164,7 +164,7 @@ This behaviour can be modified though using the `Policy` class.
DecryptionStream decryptionStream = PGPainless.decryptAndOrVerify()
.onInputStream(encryptedInputStream)
.withOptions(new ConsumerOptions()
.addMessagePassphrase(bobSecKeys, secretKeyProtector)
.addDecryptionKey(bobSecKeys, secretKeyProtector)
.addVerificationCert(alicePubKeys)
);
@ -191,7 +191,7 @@ repositories {
}
dependencies {
implementation 'org.pgpainless:pgpainless-core:1.7.0'
implementation 'org.pgpainless:pgpainless-core:1.7.6'
}
```
@ -222,9 +222,6 @@ Parts of PGPainless development ([project page](https://nlnet.nl/project/PGPainl
NGI Assure is made possible with financial support from the [European Commission](https://ec.europa.eu/)'s [Next Generation Internet](https://ngi.eu/) programme, under the aegis of [DG Communications Networks, Content and Technology](https://ec.europa.eu/info/departments/communications-networks-content-and-technology_en).
[![NGI Assure Logo](https://blog.jabberhead.tk/wp-content/uploads/2022/05/NGIAssure_tag.svg)](https://nlnet.nl/assure/)
Thanks to [YourKit](https://www.yourkit.com/) for providing a free license of the [YourKit Java Profiler](https://www.yourkit.com/java/profiler/) to support PGPainless Development!
[![YourKit Logo](https://www.yourkit.com/images/yklogo.png)](https://www.yourkit.com/)
Big thank you also to those who decided to support the work by donating!
Notably @msfjarvis

118
REUSE.toml Normal file
View file

@ -0,0 +1,118 @@
version = 1
SPDX-PackageName = "PGPainless"
SPDX-PackageSupplier = "Paul Schaub <info@pgpainless.org>"
SPDX-PackageDownloadLocation = "https://pgpainless.org"
[[annotations]]
path = "REUSE.toml"
precedence = "aggregate"
SPDX-FileCopyrightText = "2025 Paul Schaub <info@pgpainless.org>"
SPDX-License-Identifier = "CC0-1.0"
[[annotations]]
path = ".git-blame-ignore-revs"
precedence = "aggregate"
SPDX-FileCopyrightText = "2023 Paul Schaub <info@pgpainless.org>"
SPDX-License-Identifier = "CC0-1.0"
[[annotations]]
path = "docs/**"
precedence = "aggregate"
SPDX-FileCopyrightText = "2022 Paul Schaub <info@pgpainless.org>"
SPDX-License-Identifier = "CC-BY-3.0"
[[annotations]]
path = ".readthedocs.yaml"
precedence = "aggregate"
SPDX-FileCopyrightText = "2022 Paul Schaub <info@pgpainless.org>"
SPDX-License-Identifier = "CC0-1.0"
[[annotations]]
path = "gradle**"
precedence = "aggregate"
SPDX-FileCopyrightText = "2015 the original author or authors."
SPDX-License-Identifier = "Apache-2.0"
[[annotations]]
path = ".editorconfig"
precedence = "aggregate"
SPDX-FileCopyrightText = "Facebook"
SPDX-License-Identifier = "Apache-2.0"
[[annotations]]
path = "assets/repository-open-graph.png"
precedence = "aggregate"
SPDX-FileCopyrightText = "2021 Paul Schaub <info@pgpainless.org>"
SPDX-License-Identifier = "CC-BY-3.0"
[[annotations]]
path = "assets/pgpainless.svg"
precedence = "aggregate"
SPDX-FileCopyrightText = "2021 Paul Schaub <info@pgpainless.org>"
SPDX-License-Identifier = "CC-BY-3.0"
[[annotations]]
path = "assets/logo.png"
precedence = "aggregate"
SPDX-FileCopyrightText = "2022 Paul Schaub <info@pgpainless.org>"
SPDX-License-Identifier = "CC-BY-3.0"
[[annotations]]
path = "assets/test_vectors/**"
precedence = "aggregate"
SPDX-FileCopyrightText = "2018 Paul Schaub <info@pgpainless.org>"
SPDX-License-Identifier = "CC0-1.0"
[[annotations]]
path = "pgpainless-core/src/test/resources/**"
precedence = "aggregate"
SPDX-FileCopyrightText = "2020 Paul Schaub <info@pgpainless.org>"
SPDX-License-Identifier = "CC0-1.0"
[[annotations]]
path = "audit/**"
precedence = "aggregate"
SPDX-FileCopyrightText = "2021 Paul Schaub <info@pgpainless.org>"
SPDX-License-Identifier = "CC0-1.0"
[[annotations]]
path = "CNAME"
precedence = "aggregate"
SPDX-FileCopyrightText = "2022 Paul Schaub <info@pgpainless.org>"
SPDX-License-Identifier = "CC0-1.0"
[[annotations]]
path = "_config.yml"
precedence = "aggregate"
SPDX-FileCopyrightText = "2022 Paul Schaub <info@pgpainless.org>"
SPDX-License-Identifier = "CC0-1.0"
[[annotations]]
path = "_layouts/**"
precedence = "aggregate"
SPDX-FileCopyrightText = "2022 Paul Schaub <info@pgpainless.org>, 2017 Steve Smith"
SPDX-License-Identifier = "CC-BY-SA-3.0"
[[annotations]]
path = "pgpainless-cli/src/main/resources/META-INF/native-image/**"
precedence = "aggregate"
SPDX-FileCopyrightText = "2025 Paul Schaub <info@pgpainless.org>"
SPDX-License-Identifier = "Apache-2.0"
[[annotations]]
path = "pgpainless-cli/rewriteManPages.sh"
precedence = "aggregate"
SPDX-FileCopyrightText = "2022 Paul Schaub <info@pgpainless.org>"
SPDX-License-Identifier = "Apache-2.0"
[[annotations]]
path = "pgpainless-cli/packaging/man/**"
precedence = "aggregate"
SPDX-FileCopyrightText = "2022 Paul Schaub <info@pgpainless.org>"
SPDX-License-Identifier = "Apache-2.0"
[[annotations]]
path = ".github/ISSUE_TEMPLATE/**"
precedence = "aggregate"
SPDX-FileCopyrightText = "2024 Paul Schaub <info@pgpainless.org>"
SPDX-License-Identifier = "CC0-1.0"

View file

@ -33,24 +33,17 @@ allprojects {
apply plugin: 'kotlin'
apply plugin: 'com.diffplug.spotless'
java {
targetCompatibility = JavaVersion.VERSION_1_8
}
compileJava {
options.release = 8
}
// Only generate jar for submodules
// without this we would generate an empty pgpainless.jar for the project root
// https://stackoverflow.com/a/25445035
jar {
reproducibleFileOrder = true
onlyIf { !sourceSets.main.allSource.files.isEmpty() }
}
// checkstyle
checkstyle {
toolVersion = '10.12.1'
toolVersion = '10.25.0'
}
spotless {
@ -63,8 +56,6 @@ allprojects {
description = "Simple to use OpenPGP API for Java based on Bouncycastle"
version = shortVersion
sourceCompatibility = javaSourceCompatibility
repositories {
mavenCentral()
mavenLocal()
@ -79,6 +70,10 @@ allprojects {
fileMode = 0644
}
kotlin {
jvmToolchain(javaSourceCompatibility)
}
// Compatibility of default implementations in kotlin interfaces with Java implementations.
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach {
kotlinOptions {
@ -119,7 +114,7 @@ allprojects {
sourceDirectories.setFrom(project.files(sourceSets.main.allSource.srcDirs))
classDirectories.setFrom(project.files(sourceSets.main.output))
reports {
xml.enabled true
xml.required = true
}
}
@ -137,15 +132,15 @@ subprojects {
apply plugin: 'signing'
task sourcesJar(type: Jar, dependsOn: classes) {
classifier = 'sources'
archiveClassifier = 'sources'
from sourceSets.main.allSource
}
task javadocJar(type: Jar, dependsOn: javadoc) {
classifier = 'javadoc'
archiveClassifier = 'javadoc'
from javadoc.destinationDir
}
task testsJar(type: Jar, dependsOn: testClasses) {
classifier = 'tests'
archiveClassifier = 'tests'
from sourceSets.test.output
}
@ -242,7 +237,7 @@ task jacocoRootReport(type: JacocoReport) {
classDirectories.setFrom(files(subprojects.sourceSets.main.output))
executionData.setFrom(files(subprojects.jacocoTestReport.executionData))
reports {
xml.enabled true
xml.required = true
xml.destination file("${buildDir}/reports/jacoco/test/jacocoTestReport.xml")
}
// We could remove the following setOnlyIf line, but then
@ -253,10 +248,6 @@ task jacocoRootReport(type: JacocoReport) {
}
task javadocAll(type: Javadoc) {
def currentJavaVersion = JavaVersion.current()
if (currentJavaVersion.compareTo(JavaVersion.VERSION_1_9) >= 0) {
options.addStringOption("-release", "8");
}
source subprojects.collect {project ->
project.sourceSets.main.allJava }
destinationDir = new File(buildDir, 'javadoc')
@ -270,18 +261,6 @@ task javadocAll(type: Javadoc) {
] as String[]
}
if (JavaVersion.current().isJava8Compatible()) {
tasks.withType(Javadoc) {
// The '-quiet' as second argument is actually a hack,
// since the one paramater addStringOption doesn't seem to
// work, we extra add '-quiet', which is added anyway by
// gradle. See https://github.com/gradle/gradle/issues/2354
// See JDK-8200363 (https://bugs.openjdk.java.net/browse/JDK-8200363)
// for information about the -Xwerror option.
options.addStringOption('Xwerror', '-quiet')
}
}
/**
* Fetch sha256 checksums of artifacts published to maven central.
*
@ -291,34 +270,13 @@ task mavenCentralChecksums() {
description 'Fetch and display checksums for artifacts published to Maven Central'
String ver = project.hasProperty('release') ? release : shortVersion
doLast {
Process p = "curl -f https://repo1.maven.org/maven2/org/pgpainless/pgpainless-core/${ver}/pgpainless-core-${ver}.jar.sha256".execute()
if (p.waitFor() == 0) {
print p.text.trim()
println " pgpainless-core/build/libs/pgpainless-core-${ver}.jar"
}
p = "curl -f https://repo1.maven.org/maven2/org/pgpainless/pgpainless-sop/${ver}/pgpainless-sop-${ver}.jar.sha256".execute()
if (p.waitFor() == 0) {
print p.text.trim()
println " pgpainless-sop/build/libs/pgpainless-sop-${ver}.jar"
}
p = "curl -f https://repo1.maven.org/maven2/org/pgpainless/pgpainless-cli/${ver}/pgpainless-cli-${ver}-all.jar.sha256".execute()
if (p.waitFor() == 0) {
print p.text.trim()
println " pgpainless-cli/build/libs/pgpainless-cli-${ver}-all.jar"
}
p = "curl -f https://repo1.maven.org/maven2/org/pgpainless/pgpainless-cli/${ver}/pgpainless-cli-${ver}.jar.sha256".execute()
if (p.waitFor() == 0) {
print p.text.trim()
println " pgpainless-cli/build/libs/pgpainless-cli-${ver}.jar"
}
p = "curl -f https://repo1.maven.org/maven2/org/pgpainless/hsregex/${ver}/hsregex-${ver}.jar.sha256".execute()
if (p.waitFor() == 0) {
print p.text.trim()
println " hsregex/build/libs/hsregex-${ver}.jar"
for (Project p : rootProject.subprojects) {
String url = "https://repo1.maven.org/maven2/org/pgpainless/${p.name}/${ver}/${p.name}-${ver}.jar.sha256"
Process fetch = "curl -f $url".execute()
if (fetch.waitFor() == 0) {
print fetch.text.trim()
println " ${p.name}/build/libs/${p.name}-${ver}.jar"
}
}
}
}

View file

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View file

@ -4,27 +4,13 @@
plugins {
id 'application'
id "com.github.johnrengelman.shadow" version "6.1.0"
}
def generatedVersionDir = "${buildDir}/generated-version"
sourceSets {
main {
output.dir(generatedVersionDir, builtBy: 'generateVersionProperties')
}
id 'org.graalvm.buildtools.native' version '0.10.6'
id 'com.gradleup.shadow' version '8.3.6'
}
task generateVersionProperties {
doLast {
def propertiesFile = file "$generatedVersionDir/version.properties"
propertiesFile.parentFile.mkdirs()
propertiesFile.createNewFile()
// Instead of using a Properties object here, we directly write to the file
// since Properties adds a timestamp, ruining reproducibility
propertiesFile.write("version="+rootProject.version.toString())
}
graalvmNative {
toolchainDetection = true
}
processResources.dependsOn generateVersionProperties
dependencies {
@ -32,13 +18,12 @@ dependencies {
testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion"
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion"
// https://todd.ginsberg.com/post/testing-system-exit/
testImplementation 'com.ginsberg:junit5-system-exit:1.1.2'
// implementation "ch.qos.logback:logback-core:1.2.6"
// We want logback logging in tests and in the app
testImplementation "ch.qos.logback:logback-classic:$logbackVersion"
implementation "ch.qos.logback:logback-classic:$logbackVersion"
// implementation "ch.qos.logback:logback-classic:$logbackVersion"
implementation "org.slf4j:slf4j-nop:$slf4jVersion"
implementation(project(":pgpainless-sop"))
implementation "org.pgpainless:sop-java-picocli:$sopJavaVersion"
@ -52,22 +37,6 @@ mainClassName = 'org.pgpainless.cli.PGPainlessCLI'
application {
mainClass = mainClassName
}
/**
jar {
duplicatesStrategy(DuplicatesStrategy.EXCLUDE)
manifest {
attributes 'Main-Class': "$mainClassName"
}
from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
} {
exclude "META-INF/*.SF"
exclude "META-INF/*.DSA"
exclude "META-INF/*.RSA"
}
}
*/
run {
// https://stackoverflow.com/questions/59445306/pipe-into-gradle-run
@ -77,5 +46,3 @@ run {
args Eval.me(appArgs)
}
}
// tasks."jar".dependsOn(":pgpainless-core:assemble", ":pgpainless-sop:assemble")

View file

@ -14,6 +14,10 @@ import sop.cli.picocli.SopCLI;
public class PGPainlessCLI {
static {
// Prevent slf4j initialization logging
// https://github.com/qos-ch/slf4j/issues/422#issuecomment-2277280185
System.setProperty("slf4j.internal.verbosity", "WARN");
SopCLI.EXECUTABLE_NAME = "pgpainless-cli";
SopCLI.setSopInstance(new SOPImpl());
}

View file

@ -0,0 +1,7 @@
[
{
"type":"agent-extracted",
"classes":[
]
}
]

View file

@ -0,0 +1,891 @@
[
{
"name":"[Ljava.lang.Object;"
},
{
"name":"ch.qos.logback.classic.encoder.PatternLayoutEncoder",
"queryAllPublicMethods":true,
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"ch.qos.logback.classic.joran.SerializedModelConfigurator",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"ch.qos.logback.classic.util.DefaultJoranConfigurator",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"ch.qos.logback.core.ConsoleAppender",
"queryAllPublicMethods":true,
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setTarget","parameterTypes":["java.lang.String"] }]
},
{
"name":"ch.qos.logback.core.OutputStreamAppender",
"methods":[{"name":"setEncoder","parameterTypes":["ch.qos.logback.core.encoder.Encoder"] }]
},
{
"name":"ch.qos.logback.core.encoder.Encoder",
"methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }]
},
{
"name":"ch.qos.logback.core.encoder.LayoutWrappingEncoder",
"methods":[{"name":"setParent","parameterTypes":["ch.qos.logback.core.spi.ContextAware"] }]
},
{
"name":"ch.qos.logback.core.pattern.PatternLayoutEncoderBase",
"methods":[{"name":"setPattern","parameterTypes":["java.lang.String"] }]
},
{
"name":"ch.qos.logback.core.spi.ContextAware",
"methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }]
},
{
"name":"com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"groovy.lang.Closure"
},
{
"name":"java.io.FilePermission"
},
{
"name":"java.lang.Enum"
},
{
"name":"java.lang.Object",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true
},
{
"name":"java.lang.RuntimePermission"
},
{
"name":"java.lang.System",
"methods":[{"name":"console","parameterTypes":[] }]
},
{
"name":"java.lang.invoke.MethodHandle"
},
{
"name":"java.net.NetPermission"
},
{
"name":"java.net.SocketPermission"
},
{
"name":"java.net.URLPermission",
"methods":[{"name":"<init>","parameterTypes":["java.lang.String","java.lang.String"] }]
},
{
"name":"java.nio.channels.SelectionKey",
"fields":[{"name":"attachment"}]
},
{
"name":"java.nio.file.Path"
},
{
"name":"java.nio.file.Paths",
"methods":[{"name":"get","parameterTypes":["java.lang.String","java.lang.String[]"] }]
},
{
"name":"java.security.AllPermission"
},
{
"name":"java.security.MessageDigestSpi"
},
{
"name":"java.security.SecureRandomParameters"
},
{
"name":"java.security.SecurityPermission"
},
{
"name":"java.security.cert.PKIXRevocationChecker"
},
{
"name":"java.sql.Connection"
},
{
"name":"java.sql.Driver"
},
{
"name":"java.sql.DriverManager",
"methods":[{"name":"getConnection","parameterTypes":["java.lang.String"] }, {"name":"getDriver","parameterTypes":["java.lang.String"] }]
},
{
"name":"java.sql.Time",
"methods":[{"name":"<init>","parameterTypes":["long"] }]
},
{
"name":"java.sql.Timestamp",
"methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }]
},
{
"name":"java.time.Duration",
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
},
{
"name":"java.time.Instant",
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
},
{
"name":"java.time.LocalDate",
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
},
{
"name":"java.time.LocalDateTime",
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
},
{
"name":"java.time.LocalTime",
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
},
{
"name":"java.time.MonthDay",
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
},
{
"name":"java.time.OffsetDateTime",
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
},
{
"name":"java.time.OffsetTime",
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
},
{
"name":"java.time.Period",
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
},
{
"name":"java.time.Year",
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
},
{
"name":"java.time.YearMonth",
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
},
{
"name":"java.time.ZoneId",
"methods":[{"name":"of","parameterTypes":["java.lang.String"] }]
},
{
"name":"java.time.ZoneOffset",
"methods":[{"name":"of","parameterTypes":["java.lang.String"] }]
},
{
"name":"java.time.ZonedDateTime",
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
},
{
"name":"java.util.HashSet"
},
{
"name":"java.util.LinkedHashSet"
},
{
"name":"java.util.PropertyPermission"
},
{
"name":"java.util.concurrent.ArrayBlockingQueue"
},
{
"name":"java.util.concurrent.atomic.AtomicReference",
"fields":[{"name":"value"}]
},
{
"name":"java.util.concurrent.locks.AbstractOwnableSynchronizer"
},
{
"name":"java.util.concurrent.locks.AbstractQueuedSynchronizer"
},
{
"name":"java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject"
},
{
"name":"java.util.concurrent.locks.ReentrantLock"
},
{
"name":"java.util.concurrent.locks.ReentrantLock$NonfairSync"
},
{
"name":"java.util.concurrent.locks.ReentrantLock$Sync"
},
{
"name":"javax.smartcardio.CardPermission"
},
{
"name":"org.bouncycastle.jcajce.provider.asymmetric.COMPOSITE$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.asymmetric.CONTEXT$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.asymmetric.CompositeSignatures$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.asymmetric.DH$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.asymmetric.DSA$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.asymmetric.DSTU4145$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.asymmetric.Dilithium$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.asymmetric.EC$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.asymmetric.ECGOST$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.asymmetric.EXTERNAL$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.asymmetric.EdEC$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.asymmetric.ElGamal$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.asymmetric.Falcon$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.asymmetric.GM$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.asymmetric.GOST$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.asymmetric.IES$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.asymmetric.LMS$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.asymmetric.MLDSA$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.asymmetric.MLKEM$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.asymmetric.NTRU$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.asymmetric.NoSig$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.asymmetric.RSA$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.asymmetric.SLHDSA$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.asymmetric.SPHINCSPlus$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.asymmetric.X509$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.asymmetric.edec.KeyPairGeneratorSpi$EdDSA",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.asymmetric.edec.KeyPairGeneratorSpi$XDH",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.asymmetric.rsa.KeyPairGeneratorSpi",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.digest.Blake2b$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.digest.Blake2s$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.digest.Blake3$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.digest.DSTU7564$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.digest.GOST3411$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.digest.Haraka$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.digest.Keccak$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.digest.MD2$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.digest.MD4$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.digest.MD5$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.digest.RIPEMD128$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.digest.RIPEMD160$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.digest.RIPEMD256$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.digest.RIPEMD320$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.digest.SHA1$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.digest.SHA224$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.digest.SHA256$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.digest.SHA3$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.digest.SHA384$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.digest.SHA512$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.digest.SM3$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.digest.Skein$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.digest.Tiger$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.digest.Whirlpool$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.drbg.DRBG$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.keystore.BC$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.keystore.BCFKS$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.keystore.PKCS12$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.AES$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.ARC4$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.ARIA$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.Blowfish$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.CAST5$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.CAST6$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.Camellia$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.ChaCha$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.DES$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.DESede$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.DSTU7624$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.GOST28147$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.GOST3412_2015$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.Grain128$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.Grainv1$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.HC128$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.HC256$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.IDEA$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.Noekeon$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.OpenSSLPBKDF$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.PBEPBKDF1$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.PBEPBKDF2$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.PBEPKCS12$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.Poly1305$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.RC2$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.RC5$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.RC6$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.Rijndael$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.SCRYPT$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.SEED$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.SM4$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.Salsa20$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.Serpent$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.Shacal2$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.SipHash$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.SipHash128$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.Skipjack$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.TEA$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.TLSKDF$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.Threefish$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.Twofish$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.VMPC$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.VMPCKSA3$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.XSalsa20$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.XTEA$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.bouncycastle.jcajce.provider.symmetric.Zuc$Mappings",
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"org.pgpainless.cli.ExitCodeTest",
"allDeclaredFields":true,
"allDeclaredClasses":true,
"queryAllDeclaredMethods":true,
"queryAllPublicMethods":true,
"queryAllDeclaredConstructors":true,
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"successfulExecutionDoesNotTerminateJVM","parameterTypes":[] }, {"name":"testCommandWithUnknownOption_37","parameterTypes":[] }, {"name":"testUnknownCommand_69","parameterTypes":[] }]
},
{
"name":"org.pgpainless.cli.TestUtils",
"allDeclaredClasses":true,
"queryAllDeclaredMethods":true,
"queryAllPublicMethods":true
},
{
"name":"org.pgpainless.cli.commands.ArmorCmdTest",
"allDeclaredFields":true,
"allDeclaredClasses":true,
"queryAllDeclaredMethods":true,
"queryAllPublicMethods":true,
"queryAllDeclaredConstructors":true,
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"armorAlreadyArmoredDataIsIdempotent","parameterTypes":[] }, {"name":"armorMessage","parameterTypes":[] }, {"name":"armorPublicKey","parameterTypes":[] }, {"name":"armorSecretKey","parameterTypes":[] }]
},
{
"name":"org.pgpainless.cli.commands.CLITest",
"allDeclaredFields":true,
"allDeclaredClasses":true,
"queryAllDeclaredMethods":true,
"queryAllPublicMethods":true,
"methods":[{"name":"cleanup","parameterTypes":[] }, {"name":"setup","parameterTypes":[] }]
},
{
"name":"org.pgpainless.cli.commands.DearmorCmdTest",
"allDeclaredFields":true,
"allDeclaredClasses":true,
"queryAllDeclaredMethods":true,
"queryAllPublicMethods":true,
"queryAllDeclaredConstructors":true,
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"dearmorBrokenArmoredKeyFails","parameterTypes":[] }, {"name":"dearmorCertificate","parameterTypes":[] }, {"name":"dearmorGarbageEmitsEmpty","parameterTypes":[] }, {"name":"dearmorMessage","parameterTypes":[] }, {"name":"dearmorSecretKey","parameterTypes":[] }]
},
{
"name":"org.pgpainless.cli.commands.ExtractCertCmdTest",
"allDeclaredFields":true,
"allDeclaredClasses":true,
"queryAllDeclaredMethods":true,
"queryAllPublicMethods":true,
"queryAllDeclaredConstructors":true,
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"extractCertFromGarbageFails","parameterTypes":[] }, {"name":"testExtractCert","parameterTypes":[] }, {"name":"testExtractCertFromCertFails","parameterTypes":[] }, {"name":"testExtractCertUnarmored","parameterTypes":[] }]
},
{
"name":"org.pgpainless.cli.commands.GenerateKeyCmdTest",
"allDeclaredFields":true,
"allDeclaredClasses":true,
"queryAllDeclaredMethods":true,
"queryAllPublicMethods":true,
"queryAllDeclaredConstructors":true,
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"testGenerateBinaryKey","parameterTypes":[] }, {"name":"testGenerateKey","parameterTypes":[] }, {"name":"testGenerateKeyWithMultipleUserIds","parameterTypes":[] }, {"name":"testGeneratePasswordProtectedKey_missingPasswordFile","parameterTypes":[] }, {"name":"testPasswordProtectedKey","parameterTypes":[] }]
},
{
"name":"org.pgpainless.cli.commands.InlineDetachCmdTest",
"allDeclaredFields":true,
"allDeclaredClasses":true,
"queryAllDeclaredMethods":true,
"queryAllPublicMethods":true,
"queryAllDeclaredConstructors":true,
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"detachInbandSignatureAndMessage","parameterTypes":[] }, {"name":"detachInbandSignatureAndMessageNoArmor","parameterTypes":[] }, {"name":"detachMissingSignaturesFromCleartextSignedMessageFails","parameterTypes":[] }, {"name":"detachNonOpenPgpDataFails","parameterTypes":[] }, {"name":"existingSignatureOutCausesException","parameterTypes":[] }]
},
{
"name":"org.pgpainless.cli.commands.ListProfilesCmdTest",
"allDeclaredFields":true,
"allDeclaredClasses":true,
"queryAllDeclaredMethods":true,
"queryAllPublicMethods":true,
"queryAllDeclaredConstructors":true,
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"listProfileOfGenerateKey","parameterTypes":[] }, {"name":"listProfilesOfEncrypt","parameterTypes":[] }, {"name":"listProfilesWithoutCommand","parameterTypes":[] }]
},
{
"name":"org.pgpainless.cli.commands.RoundTripEncryptDecryptCmdTest",
"allDeclaredFields":true,
"allDeclaredClasses":true,
"queryAllDeclaredMethods":true,
"queryAllPublicMethods":true,
"queryAllDeclaredConstructors":true,
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"decryptGarbageFails","parameterTypes":[] }, {"name":"decryptMalformedMessageYieldsBadData","parameterTypes":[] }, {"name":"decryptMessageWithSessionKey","parameterTypes":[] }, {"name":"decryptMessageWithWrongKeyFails","parameterTypes":[] }, {"name":"decryptWithPasswordWithPendingWhitespaceWorks","parameterTypes":[] }, {"name":"decryptWithWhitespacePasswordWorks","parameterTypes":[] }, {"name":"decrypt_verifyWithGarbageCertFails","parameterTypes":[] }, {"name":"decrypt_withGarbageKeyFails","parameterTypes":[] }, {"name":"encryptAndDecryptAMessage","parameterTypes":[] }, {"name":"encryptAndDecryptMessageWithPassphrase","parameterTypes":[] }, {"name":"encryptWithGarbageCertFails","parameterTypes":[] }, {"name":"encryptWithPasswordADecryptWithPasswordBFails","parameterTypes":[] }, {"name":"encryptWithProtectedKey_wrongPassphraseFails","parameterTypes":[] }, {"name":"encryptWithTrailingWhitespaceDecryptWithoutWorks","parameterTypes":[] }, {"name":"encrypt_signWithGarbageKeyFails","parameterTypes":[] }, {"name":"testDecryptVerifyOut_withoutVerifyWithFails","parameterTypes":[] }, {"name":"testDecryptWithSessionKeyVerifyWithYieldsExpectedVerifications","parameterTypes":[] }, {"name":"testDecryptWithoutDecryptionOptionFails","parameterTypes":[] }, {"name":"testEncryptDecryptRoundTripWithPasswordProtectedKey","parameterTypes":[] }, {"name":"testEncryptDecryptWithFreshRSAKey","parameterTypes":[] }, {"name":"testEncryptWithIncapableCert","parameterTypes":[] }, {"name":"testEncrypt_SignWithCertFails","parameterTypes":[] }, {"name":"testMissingArgumentsIfNoArgsSupplied","parameterTypes":[] }, {"name":"testSessionKeyOutWritesSessionKeyOut","parameterTypes":[] }, {"name":"testSignWithIncapableKey","parameterTypes":[] }, {"name":"testVerificationsOutAlreadyExistFails","parameterTypes":[] }]
},
{
"name":"org.pgpainless.cli.commands.RoundTripInlineSignInlineVerifyCmdTest",
"allDeclaredFields":true,
"allDeclaredClasses":true,
"queryAllDeclaredMethods":true,
"queryAllPublicMethods":true,
"queryAllDeclaredConstructors":true,
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"cannotVerifyEncryptedMessage","parameterTypes":[] }, {"name":"cannotVerifyMalformedMessage","parameterTypes":[] }, {"name":"createAndVerifyCleartextSignedMessage","parameterTypes":[] }, {"name":"createAndVerifyMultiKeyBinarySignedMessage","parameterTypes":[] }, {"name":"createAndVerifyTextSignedMessage","parameterTypes":[] }, {"name":"createCleartextSignedMessage","parameterTypes":[] }, {"name":"createMalformedMessage","parameterTypes":[] }, {"name":"createSignedMessageWithKeyAAndVerifyWithKeyBFails","parameterTypes":[] }, {"name":"createTextSignedMessageInlineDetachAndDetachedVerify","parameterTypes":[] }, {"name":"signWithProtectedKeyWithWrongPassphraseFails","parameterTypes":[] }, {"name":"testInlineSignWithMissingSecretKeysFails","parameterTypes":[] }, {"name":"testUnlockKeyWithOneOfMultiplePasswords","parameterTypes":[] }, {"name":"verifyPrependedSignedMessage","parameterTypes":[] }]
},
{
"name":"org.pgpainless.cli.commands.RoundTripInlineSignVerifyCmdTest",
"allDeclaredFields":true,
"allDeclaredClasses":true,
"queryAllDeclaredMethods":true,
"queryAllPublicMethods":true,
"queryAllDeclaredConstructors":true,
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"encryptAndDecryptAMessage","parameterTypes":[] }]
},
{
"name":"org.pgpainless.cli.commands.RoundTripSignVerifyCmdTest",
"allDeclaredFields":true,
"allDeclaredClasses":true,
"queryAllDeclaredMethods":true,
"queryAllPublicMethods":true,
"queryAllDeclaredConstructors":true,
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"createArmoredSignature","parameterTypes":[] }, {"name":"createUnarmoredSignature","parameterTypes":[] }, {"name":"signWithProtectedKey","parameterTypes":[] }, {"name":"signWithProtectedKey_missingPassphraseFails","parameterTypes":[] }, {"name":"signWithProtectedKey_wrongPassphraseFails","parameterTypes":[] }, {"name":"testNotAfter","parameterTypes":[] }, {"name":"testNotBefore","parameterTypes":[] }, {"name":"testSignWithIncapableKey","parameterTypes":[] }, {"name":"testSignatureCreationAndVerification","parameterTypes":[] }, {"name":"unarmorArmoredSigAndVerify","parameterTypes":[] }]
},
{
"name":"org.pgpainless.cli.commands.VersionCmdTest",
"allDeclaredFields":true,
"allDeclaredClasses":true,
"queryAllDeclaredMethods":true,
"queryAllPublicMethods":true,
"queryAllDeclaredConstructors":true,
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"testExtendedVersion","parameterTypes":[] }, {"name":"testGetBackendVersion","parameterTypes":[] }, {"name":"testSopSpecVersion","parameterTypes":[] }, {"name":"testVersion","parameterTypes":[] }]
},
{
"name":"org.pgpainless.cli.misc.SignUsingPublicKeyBehaviorTest",
"allDeclaredFields":true,
"allDeclaredClasses":true,
"queryAllDeclaredMethods":true,
"queryAllPublicMethods":true,
"queryAllDeclaredConstructors":true,
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"testSignatureCreationAndVerification","parameterTypes":[] }]
},
{
"name":"picocli.AutoComplete$GenerateCompletion",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true,
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"picocli.CommandLine$AutoHelpMixin",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true
},
{
"name":"picocli.CommandLine$HelpCommand",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true
},
{
"name":"sop.cli.picocli.SopCLI",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true,
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"sop.cli.picocli.SopCLI$InitLocale",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true
},
{
"name":"sop.cli.picocli.commands.AbstractSopCmd",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true
},
{
"name":"sop.cli.picocli.commands.ArmorCmd",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true,
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"sop.cli.picocli.commands.ChangeKeyPasswordCmd",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true
},
{
"name":"sop.cli.picocli.commands.DearmorCmd",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true,
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"sop.cli.picocli.commands.DecryptCmd",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true,
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"sop.cli.picocli.commands.EncryptCmd",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true,
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"sop.cli.picocli.commands.ExtractCertCmd",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true,
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"sop.cli.picocli.commands.GenerateKeyCmd",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true,
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"sop.cli.picocli.commands.InlineDetachCmd",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true,
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"sop.cli.picocli.commands.InlineSignCmd",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true,
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"sop.cli.picocli.commands.InlineVerifyCmd",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true,
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"sop.cli.picocli.commands.ListProfilesCmd",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true,
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"sop.cli.picocli.commands.RevokeKeyCmd",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true
},
{
"name":"sop.cli.picocli.commands.SignCmd",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true,
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"sop.cli.picocli.commands.VerifyCmd",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true,
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"sop.cli.picocli.commands.VersionCmd",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true,
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"sop.cli.picocli.commands.VersionCmd$Exclusive",
"allDeclaredFields":true,
"queryAllDeclaredMethods":true,
"methods":[{"name":"<init>","parameterTypes":[] }]
},
{
"name":"sun.security.provider.NativePRNG",
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"<init>","parameterTypes":["java.security.SecureRandomParameters"] }]
},
{
"name":"sun.security.provider.SHA",
"methods":[{"name":"<init>","parameterTypes":[] }]
}
]

View file

@ -0,0 +1,93 @@
{
"resources":{
"includes":[{
"pattern":"\\QMETA-INF/services/ch.qos.logback.classic.spi.Configurator\\E"
}, {
"pattern":"\\QMETA-INF/services/java.lang.System$LoggerFinder\\E"
}, {
"pattern":"\\QMETA-INF/services/java.nio.channels.spi.SelectorProvider\\E"
}, {
"pattern":"\\QMETA-INF/services/java.time.zone.ZoneRulesProvider\\E"
}, {
"pattern":"\\QMETA-INF/services/java.util.spi.ResourceBundleControlProvider\\E"
}, {
"pattern":"\\QMETA-INF/services/javax.xml.parsers.SAXParserFactory\\E"
}, {
"pattern":"\\QMETA-INF/services/org.junit.platform.engine.TestEngine\\E"
}, {
"pattern":"\\QMETA-INF/services/org.junit.platform.launcher.LauncherDiscoveryListener\\E"
}, {
"pattern":"\\QMETA-INF/services/org.junit.platform.launcher.LauncherSessionListener\\E"
}, {
"pattern":"\\QMETA-INF/services/org.junit.platform.launcher.PostDiscoveryFilter\\E"
}, {
"pattern":"\\QMETA-INF/services/org.junit.platform.launcher.TestExecutionListener\\E"
}, {
"pattern":"\\QMETA-INF/services/org.slf4j.spi.SLF4JServiceProvider\\E"
}, {
"pattern":"\\Qjunit-platform.properties\\E"
}, {
"pattern":"\\Qlogback-test.scmo\\E"
}, {
"pattern":"\\Qlogback-test.xml\\E"
}, {
"pattern":"\\Qlogback.scmo\\E"
}, {
"pattern":"\\Qorg/slf4j/impl/StaticLoggerBinder.class\\E"
}, {
"pattern":"\\Qpgpainless-sop.properties\\E"
}, {
"pattern":"\\Qsop-java-version.properties\\E"
}, {
"pattern":"java.base:\\Qsun/text/resources/LineBreakIteratorData\\E"
}]},
"bundles":[{
"name":"msg_armor",
"locales":["de", "und"]
}, {
"name":"msg_change-key-password",
"locales":["de", "und"]
}, {
"name":"msg_dearmor",
"locales":["de", "und"]
}, {
"name":"msg_decrypt",
"locales":["de", "und"]
}, {
"name":"msg_detached-sign",
"locales":["de", "und"]
}, {
"name":"msg_detached-verify",
"locales":["de", "und"]
}, {
"name":"msg_encrypt",
"locales":["de", "und"]
}, {
"name":"msg_extract-cert",
"locales":["de", "und"]
}, {
"name":"msg_generate-key",
"locales":["de", "und"]
}, {
"name":"msg_inline-detach",
"locales":["de", "und"]
}, {
"name":"msg_inline-sign",
"locales":["de", "und"]
}, {
"name":"msg_inline-verify",
"locales":["de", "und"]
}, {
"name":"msg_list-profiles",
"locales":["de", "und"]
}, {
"name":"msg_revoke-key",
"locales":["de", "und"]
}, {
"name":"msg_sop",
"locales":["de", "und"]
}, {
"name":"msg_version",
"locales":["de", "und"]
}]
}

View file

@ -0,0 +1,41 @@
{
"types":[
{
"name":"java.lang.Enum"
},
{
"name":"java.lang.Object[]"
},
{
"name":"java.util.HashSet"
},
{
"name":"java.util.LinkedHashSet"
},
{
"name":"java.util.concurrent.ArrayBlockingQueue"
},
{
"name":"java.util.concurrent.locks.AbstractOwnableSynchronizer"
},
{
"name":"java.util.concurrent.locks.AbstractQueuedSynchronizer"
},
{
"name":"java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject"
},
{
"name":"java.util.concurrent.locks.ReentrantLock"
},
{
"name":"java.util.concurrent.locks.ReentrantLock$NonfairSync"
},
{
"name":"java.util.concurrent.locks.ReentrantLock$Sync"
}
],
"lambdaCapturingTypes":[
],
"proxies":[
]
}

View file

@ -5,22 +5,5 @@ SPDX-License-Identifier: Apache-2.0
-->
<configuration debug="false">
<appender name="STDERR" class="ch.qos.logback.core.ConsoleAppender">
<target>System.err</target>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<target>System.out</target>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="error">
<appender-ref ref="STDERR" />
</root>
<statusListener class="ch.qos.logback.core.status.NopStatusListener" />
</configuration>

View file

@ -4,28 +4,35 @@
package org.pgpainless.cli;
import com.ginsberg.junit.exit.ExpectSystemExitWithStatus;
import com.ginsberg.junit.exit.FailOnSystemExit;
import org.junit.jupiter.api.Test;
import org.pgpainless.cli.commands.CLITest;
import org.slf4j.LoggerFactory;
import sop.exception.SOPGPException;
public class ExitCodeTest {
import java.io.IOException;
@Test
@ExpectSystemExitWithStatus(SOPGPException.UnsupportedSubcommand.EXIT_CODE)
public void testUnknownCommand_69() {
PGPainlessCLI.main(new String[] {"generate-kex"});
import static org.junit.jupiter.api.Assertions.assertEquals;
public class ExitCodeTest extends CLITest {
public ExitCodeTest() {
super(LoggerFactory.getLogger(ExitCodeTest.class));
}
@Test
@ExpectSystemExitWithStatus(SOPGPException.UnsupportedOption.EXIT_CODE)
public void testCommandWithUnknownOption_37() {
PGPainlessCLI.main(new String[] {"generate-key", "-k", "\"k is unknown\""});
public void testUnknownCommand_69() throws IOException {
assertEquals(SOPGPException.UnsupportedSubcommand.EXIT_CODE,
executeCommand("unsupported-subcommand"));
}
@Test
@FailOnSystemExit
public void successfulExecutionDoesNotTerminateJVM() {
PGPainlessCLI.main(new String[] {"version"});
public void testCommandWithUnknownOption_37() throws IOException {
assertEquals(SOPGPException.UnsupportedOption.EXIT_CODE,
executeCommand("generate-key", "-k", "\"k is unknown\""));
}
@Test
public void successfulExecutionDoesNotTerminateJVM() throws IOException {
assertSuccess(executeCommand("version"));
}
}

View file

@ -14,7 +14,6 @@ import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import com.ginsberg.junit.exit.FailOnSystemExit;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
@ -83,7 +82,6 @@ public class RoundTripEncryptDecryptCmdTest extends CLITest {
"-----END PGP PUBLIC KEY BLOCK-----";
@Test
@FailOnSystemExit
public void encryptAndDecryptAMessage() throws IOException {
// Juliets key and cert
File julietKeyFile = pipeStdoutToFile("juliet.key");

View file

@ -138,6 +138,10 @@ public class RoundTripInlineSignInlineVerifyCmdTest extends CLITest {
"\n" +
"There is only one Lord of the Keys, only one who can bend them to his will. And he does not share power.";
private static final String MESSAGE_CRLF = "One does not simply use OpenPGP!\r\n" +
"\r\n" +
"There is only one Lord of the Keys, only one who can bend them to his will. And he does not share power.";
@Test
public void createCleartextSignedMessage() throws IOException {
File key = writeFile("key.asc", KEY_1);
@ -153,7 +157,7 @@ public class RoundTripInlineSignInlineVerifyCmdTest extends CLITest {
String cleartextSigned = ciphertextOut.toString();
assertTrue(cleartextSigned.startsWith("-----BEGIN PGP SIGNED MESSAGE-----\n" +
"Hash: "));
assertTrue(cleartextSigned.contains(MESSAGE));
assertTrue(cleartextSigned.contains(MESSAGE_CRLF));
assertTrue(cleartextSigned.contains("\n-----BEGIN PGP SIGNATURE-----\n"));
assertTrue(cleartextSigned.endsWith("-----END PGP SIGNATURE-----\n"));
}
@ -203,7 +207,7 @@ public class RoundTripInlineSignInlineVerifyCmdTest extends CLITest {
"--verifications-out", verifications.getAbsolutePath(),
cert.getAbsolutePath()));
assertEquals(MESSAGE, plaintextOut.toString());
assertEquals(MESSAGE_CRLF, plaintextOut.toString());
String verificationString = readStringFromFile(verifications);
assertTrue(verificationString.contains(CERT_1_SIGNING_KEY));
}

View file

@ -4,104 +4,49 @@
package org.pgpainless.cli.commands;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.assertFalse;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import com.ginsberg.junit.exit.FailOnSystemExit;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.pgpainless.cli.PGPainlessCLI;
import org.pgpainless.cli.TestUtils;
import org.slf4j.LoggerFactory;
public class RoundTripInlineSignVerifyCmdTest {
private static File tempDir;
private static PrintStream originalSout;
public class RoundTripInlineSignVerifyCmdTest extends CLITest {
@BeforeAll
public static void prepare() throws IOException {
tempDir = TestUtils.createTempDirectory();
public RoundTripInlineSignVerifyCmdTest() {
super(LoggerFactory.getLogger(RoundTripInlineSignVerifyCmdTest.class));
}
@Test
@FailOnSystemExit
public void encryptAndDecryptAMessage() throws IOException {
originalSout = System.out;
File sigmundKeyFile = new File(tempDir, "sigmund.key");
assertTrue(sigmundKeyFile.createNewFile());
File sigmundCertFile = new File(tempDir, "sigmund.cert");
assertTrue(sigmundCertFile.createNewFile());
File msgFile = new File(tempDir, "signed.asc");
assertTrue(msgFile.createNewFile());
File passwordFile = new File(tempDir, "password");
assertTrue(passwordFile.createNewFile());
// write password file
FileOutputStream passwordOut = new FileOutputStream(passwordFile);
passwordOut.write("sw0rdf1sh".getBytes(StandardCharsets.UTF_8));
passwordOut.close();
File password = writeFile("password", "sw0rdf1sh");
// generate key
OutputStream sigmundKeyOut = new FileOutputStream(sigmundKeyFile);
System.setOut(new PrintStream(sigmundKeyOut));
PGPainlessCLI.execute("generate-key",
"--with-key-password=" + passwordFile.getAbsolutePath(),
"Sigmund Freud <sigmund@pgpainless.org>");
sigmundKeyOut.close();
File sigmundKey = pipeStdoutToFile("sigmund.key");
assertSuccess(executeCommand("generate-key", "--with-key-password=" + password.getAbsolutePath(),
"Sigmund Freud <sigmund@pgpainless.org>"));
// extract cert
FileInputStream sigmundKeyIn = new FileInputStream(sigmundKeyFile);
System.setIn(sigmundKeyIn);
OutputStream sigmundCertOut = new FileOutputStream(sigmundCertFile);
System.setOut(new PrintStream(sigmundCertOut));
PGPainlessCLI.execute("extract-cert");
sigmundKeyIn.close();
sigmundCertOut.close();
File sigmundCert = pipeStdoutToFile("sigmund.cert");
pipeFileToStdin(sigmundKey);
assertSuccess(executeCommand("extract-cert"));
// sign message
String msg = "Hello World!\n";
ByteArrayInputStream msgIn = new ByteArrayInputStream(msg.getBytes(StandardCharsets.UTF_8));
System.setIn(msgIn);
OutputStream msgAscOut = new FileOutputStream(msgFile);
System.setOut(new PrintStream(msgAscOut));
PGPainlessCLI.execute("inline-sign",
"--with-key-password=" + passwordFile.getAbsolutePath(),
sigmundKeyFile.getAbsolutePath());
msgAscOut.close();
pipeBytesToStdin("Hello, World!\n".getBytes(StandardCharsets.UTF_8));
File signedMsg = pipeStdoutToFile("signed.asc");
assertSuccess(executeCommand("inline-sign", "--with-key-password=" + password.getAbsolutePath(),
sigmundKey.getAbsolutePath()));
File verifyFile = new File(tempDir, "verify.txt");
// verify message
File verifyFile = nonExistentFile("verify.txt");
pipeFileToStdin(signedMsg);
assertSuccess(executeCommand("inline-verify", "--verifications-out", verifyFile.getAbsolutePath(),
sigmundCert.getAbsolutePath()));
FileInputStream msgAscIn = new FileInputStream(msgFile);
System.setIn(msgAscIn);
ByteArrayOutputStream out = new ByteArrayOutputStream();
PrintStream pOut = new PrintStream(out);
System.setOut(pOut);
PGPainlessCLI.execute("inline-verify",
"--verifications-out", verifyFile.getAbsolutePath(),
sigmundCertFile.getAbsolutePath());
msgAscIn.close();
assertEquals(msg, out.toString());
}
@AfterAll
public static void after() {
System.setOut(originalSout);
// CHECKSTYLE:OFF
System.out.println(tempDir.getAbsolutePath());
// CHECKSTYLE:ON
String verifications = readStringFromFile(verifyFile);
assertFalse(verifications.trim().isEmpty());
}
}

View file

@ -4,28 +4,18 @@
package org.pgpainless.cli.misc;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import com.ginsberg.junit.exit.ExpectSystemExitWithStatus;
import org.bouncycastle.util.io.Streams;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.pgpainless.cli.PGPainlessCLI;
import org.pgpainless.cli.TestUtils;
import org.pgpainless.cli.commands.CLITest;
import org.slf4j.LoggerFactory;
import sop.exception.SOPGPException;
public class SignUsingPublicKeyBehaviorTest {
public class SignUsingPublicKeyBehaviorTest extends CLITest {
public static final String KEY_THAT_IS_A_CERT = "" +
"-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
@ -89,61 +79,24 @@ public class SignUsingPublicKeyBehaviorTest {
"=oJQ2\n" +
"-----END PGP PUBLIC KEY BLOCK-----";
private static File tempDir;
private static PrintStream originalSout;
@BeforeAll
public static void prepare() throws IOException {
tempDir = TestUtils.createTempDirectory();
public SignUsingPublicKeyBehaviorTest() {
super(LoggerFactory.getLogger(SignUsingPublicKeyBehaviorTest.class));
}
@Test
@ExpectSystemExitWithStatus(SOPGPException.KeyCannotSign.EXIT_CODE)
public void testSignatureCreationAndVerification() throws IOException {
originalSout = System.out;
InputStream originalIn = System.in;
// Write alice key to disc
File aliceKeyFile = new File(tempDir, "alice.key");
assertTrue(aliceKeyFile.createNewFile());
OutputStream aliceKeyOut = new FileOutputStream(aliceKeyFile);
Streams.pipeAll(new ByteArrayInputStream(KEY_THAT_IS_A_CERT.getBytes(StandardCharsets.UTF_8)), aliceKeyOut);
aliceKeyOut.close();
// Write alice pub key to disc
File aliceCertFile = new File(tempDir, "alice.pub");
assertTrue(aliceCertFile.createNewFile());
OutputStream aliceCertOut = new FileOutputStream(aliceCertFile);
Streams.pipeAll(new ByteArrayInputStream(KEY_THAT_IS_A_CERT.getBytes(StandardCharsets.UTF_8)), aliceCertOut);
aliceCertOut.close();
File aliceKeyFile = writeFile("alice.key", KEY_THAT_IS_A_CERT);
// Write test data to disc
String data = "If privacy is outlawed, only outlaws will have privacy.\n";
File dataFile = new File(tempDir, "data");
assertTrue(dataFile.createNewFile());
FileOutputStream dataOut = new FileOutputStream(dataFile);
Streams.pipeAll(new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)), dataOut);
dataOut.close();
File dataFile = writeFile("data", "If privacy is outlawed, only outlaws will have privacy.\n");
// Sign test data
FileInputStream dataIn = new FileInputStream(dataFile);
System.setIn(dataIn);
File sigFile = new File(tempDir, "sig.asc");
assertTrue(sigFile.createNewFile());
FileOutputStream sigOut = new FileOutputStream(sigFile);
System.setOut(new PrintStream(sigOut));
PGPainlessCLI.main(new String[] {"sign", "--armor", aliceKeyFile.getAbsolutePath()});
File sigFile = pipeStdoutToFile("sig.asc");
pipeFileToStdin(dataFile);
assertEquals(SOPGPException.KeyCannotSign.EXIT_CODE,
executeCommand("sign", "--armor", aliceKeyFile.getAbsolutePath()));
System.setIn(originalIn);
}
@AfterAll
public static void after() {
System.setOut(originalSout);
// CHECKSTYLE:OFF
System.out.println(tempDir.getAbsolutePath());
// CHECKSTYLE:ON
assertTrue(readStringFromFile(sigFile).trim().isEmpty());
}
}

View file

@ -22,6 +22,7 @@ dependencies {
// Bouncy Castle
api "org.bouncycastle:bcprov-jdk18on:$bouncyCastleVersion"
api "org.bouncycastle:bcpg-jdk18on:$bouncyPgVersion"
api "org.bouncycastle:bcutil-jdk18on:$bouncyCastleVersion"
// api(files("../libs/bcpg-jdk18on-1.70.jar"))
// @Nullable, @Nonnull annotations

View file

@ -32,6 +32,7 @@ import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.NoSuchElementException;
import org.bouncycastle.bcpg.BCPGInputStream;
import org.bouncycastle.openpgp.PGPCompressedData;
@ -208,8 +209,8 @@ public class OpenPgpInputStream extends BufferedInputStream {
}
try {
SignatureType.valueOf(sigType);
} catch (IllegalArgumentException e) {
SignatureType.requireFromCode(sigType);
} catch (NoSuchElementException e) {
return;
}
@ -236,8 +237,8 @@ public class OpenPgpInputStream extends BufferedInputStream {
if (opsVersion == 3) {
int opsSigType = bcpgIn.read();
try {
SignatureType.valueOf(opsSigType);
} catch (IllegalArgumentException e) {
SignatureType.requireFromCode(opsSigType);
} catch (NoSuchElementException e) {
return;
}
int opsHashAlg = bcpgIn.read();

View file

@ -28,7 +28,14 @@ public class SignatureValidationException extends PGPException {
StringBuilder sb = new StringBuilder();
sb.append(rejections.size()).append(" rejected signatures:\n");
for (PGPSignature signature : rejections.keySet()) {
sb.append(SignatureType.valueOf(signature.getSignatureType())).append(' ')
String typeString;
SignatureType type = SignatureType.fromCode(signature.getSignatureType());
if (type == null) {
typeString = "0x" + Long.toHexString(signature.getSignatureType());
} else {
typeString = type.toString();
}
sb.append(typeString).append(' ')
.append(signature.getCreationTime()).append(": ")
.append(rejections.get(signature).getMessage()).append('\n');
}

View file

@ -34,7 +34,11 @@ public final class OpenPgpKeyAttributeUtil {
continue;
}
SignatureType signatureType = SignatureType.valueOf(signature.getSignatureType());
SignatureType signatureType = SignatureType.fromCode(signature.getSignatureType());
if (signatureType == null) {
// unknown signature type
continue;
}
if (signatureType == SignatureType.POSITIVE_CERTIFICATION
|| signatureType == SignatureType.GENERIC_CERTIFICATION) {
int[] hashAlgos = signature.getHashedSubPackets().getPreferredHashAlgorithms();
@ -71,8 +75,8 @@ public final class OpenPgpKeyAttributeUtil {
continue;
}
SignatureType signatureType = SignatureType.valueOf(signature.getSignatureType());
if (signatureType != SignatureType.POSITIVE_CERTIFICATION
SignatureType signatureType = SignatureType.fromCode(signature.getSignatureType());
if (signatureType == null || signatureType != SignatureType.POSITIVE_CERTIFICATION
&& signatureType != SignatureType.GENERIC_CERTIFICATION) {
continue;
}

View file

@ -170,7 +170,8 @@ enum class SignatureType(val code: Int) {
@JvmStatic
fun isRevocationSignature(signatureType: Int): Boolean {
return isRevocationSignature(valueOf(signatureType))
val sigType = fromCode(signatureType)
return sigType?.let { isRevocationSignature(it) } ?: false
}
@JvmStatic

View file

@ -77,7 +77,8 @@ fun PGPSignature.wasIssuedBy(key: PGPPublicKey): Boolean = wasIssuedBy(OpenPgpFi
/** Return true, if this signature is a hard revocation. */
val PGPSignature.isHardRevocation
get() =
when (SignatureType.requireFromCode(signatureType)) {
when (SignatureType.fromCode(signatureType)) {
null -> false
SignatureType.KEY_REVOCATION,
SignatureType.SUBKEY_REVOCATION,
SignatureType.CERTIFICATION_REVOCATION -> {
@ -104,4 +105,4 @@ val PGPSignature.signatureHashAlgorithm: HashAlgorithm
get() = HashAlgorithm.requireFromId(hashAlgorithm)
fun PGPSignature.isOfType(type: SignatureType): Boolean =
SignatureType.requireFromCode(signatureType) == type
SignatureType.fromCode(signatureType) == type

View file

@ -4,7 +4,11 @@
package org.pgpainless.decryption_verification
import org.bouncycastle.bcpg.AEADEncDataPacket
import org.bouncycastle.bcpg.SymmetricEncIntegrityPacket
import org.bouncycastle.openpgp.PGPPrivateKey
import org.bouncycastle.openpgp.PGPSessionKey
import org.bouncycastle.openpgp.operator.PGPDataDecryptor
import org.bouncycastle.openpgp.operator.bc.BcPublicKeyDataDecryptorFactory
import org.bouncycastle.util.encoders.Base64
import org.pgpainless.key.SubkeyIdentifier
@ -21,16 +25,34 @@ import org.pgpainless.key.SubkeyIdentifier
class CachingBcPublicKeyDataDecryptorFactory(
privateKey: PGPPrivateKey,
override val subkeyIdentifier: SubkeyIdentifier
) : BcPublicKeyDataDecryptorFactory(privateKey), CustomPublicKeyDataDecryptorFactory {
) : CustomPublicKeyDataDecryptorFactory() {
private val decryptorFactory: BcPublicKeyDataDecryptorFactory =
BcPublicKeyDataDecryptorFactory(privateKey)
private val cachedSessions: MutableMap<String, ByteArray> = mutableMapOf()
override fun createDataDecryptor(p0: Boolean, p1: Int, p2: ByteArray?): PGPDataDecryptor {
return decryptorFactory.createDataDecryptor(p0, p1, p2)
}
override fun createDataDecryptor(p0: AEADEncDataPacket?, p1: PGPSessionKey?): PGPDataDecryptor {
return decryptorFactory.createDataDecryptor(p0, p1)
}
override fun createDataDecryptor(
p0: SymmetricEncIntegrityPacket?,
p1: PGPSessionKey?
): PGPDataDecryptor {
return decryptorFactory.createDataDecryptor(p0, p1)
}
override fun recoverSessionData(
keyAlgorithm: Int,
secKeyData: Array<out ByteArray>
secKeyData: Array<out ByteArray>,
pkeskVersion: Int
): ByteArray =
lookupSessionKeyData(secKeyData)
?: costlyRecoverSessionData(keyAlgorithm, secKeyData).also {
?: costlyRecoverSessionData(keyAlgorithm, secKeyData, pkeskVersion).also {
cacheSessionKeyData(secKeyData, it)
}
@ -39,8 +61,9 @@ class CachingBcPublicKeyDataDecryptorFactory(
private fun costlyRecoverSessionData(
keyAlgorithm: Int,
secKeyData: Array<out ByteArray>
): ByteArray = super.recoverSessionData(keyAlgorithm, secKeyData)
secKeyData: Array<out ByteArray>,
pkeskVersion: Int
): ByteArray = decryptorFactory.recoverSessionData(keyAlgorithm, secKeyData, pkeskVersion)
private fun cacheSessionKeyData(secKeyData: Array<out ByteArray>, sessionKey: ByteArray) {
cachedSessions[toKey(secKeyData)] = sessionKey.clone()

View file

@ -4,6 +4,7 @@
package org.pgpainless.decryption_verification
import org.bouncycastle.openpgp.operator.AbstractPublicKeyDataDecryptorFactory
import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory
import org.pgpainless.key.SubkeyIdentifier
@ -14,7 +15,7 @@ import org.pgpainless.key.SubkeyIdentifier
*
* @see [ConsumerOptions.addCustomDecryptorFactory]
*/
interface CustomPublicKeyDataDecryptorFactory : PublicKeyDataDecryptorFactory {
abstract class CustomPublicKeyDataDecryptorFactory : AbstractPublicKeyDataDecryptorFactory() {
/**
* Identifier for the subkey for which this particular [CustomPublicKeyDataDecryptorFactory] is
@ -22,5 +23,5 @@ interface CustomPublicKeyDataDecryptorFactory : PublicKeyDataDecryptorFactory {
*
* @return subkey identifier
*/
val subkeyIdentifier: SubkeyIdentifier
abstract val subkeyIdentifier: SubkeyIdentifier
}

View file

@ -29,11 +29,17 @@ class HardwareSecurity {
* @param keyId id of the key
* @param keyAlgorithm algorithm
* @param sessionKeyData encrypted session key
* @param pkeskVersion version of the Public-Key-Encrypted-Session-Key packet (3 or 6)
* @return decrypted session key
* @throws HardwareSecurityException exception
*/
@Throws(HardwareSecurityException::class)
fun decryptSessionKey(keyId: Long, keyAlgorithm: Int, sessionKeyData: ByteArray): ByteArray
fun decryptSessionKey(
keyId: Long,
keyAlgorithm: Int,
sessionKeyData: ByteArray,
pkeskVersion: Int
): ByteArray
}
/**
@ -44,7 +50,7 @@ class HardwareSecurity {
class HardwareDataDecryptorFactory(
override val subkeyIdentifier: SubkeyIdentifier,
private val callback: DecryptionCallback,
) : CustomPublicKeyDataDecryptorFactory {
) : CustomPublicKeyDataDecryptorFactory() {
// luckily we can instantiate the BcPublicKeyDataDecryptorFactory with null as argument.
private val factory: PublicKeyDataDecryptorFactory = BcPublicKeyDataDecryptorFactory(null)
@ -73,10 +79,12 @@ class HardwareSecurity {
override fun recoverSessionData(
keyAlgorithm: Int,
secKeyData: Array<out ByteArray>
secKeyData: Array<out ByteArray>,
pkeskVersion: Int
): ByteArray {
return try {
callback.decryptSessionKey(subkeyIdentifier.subkeyId, keyAlgorithm, secKeyData[0])
callback.decryptSessionKey(
subkeyIdentifier.subkeyId, keyAlgorithm, secKeyData[0], pkeskVersion)
} catch (e: HardwareSecurityException) {
throw PGPException("Hardware-backed decryption failed.", e)
}

View file

@ -37,7 +37,7 @@ class CRLFGeneratorStream(private val crlfOut: OutputStream, encoding: StreamEnc
}
override fun close() {
if (!isBinary && lastB == 'r'.code) {
if (!isBinary && lastB == '\r'.code) {
crlfOut.write('\n'.code)
}
crlfOut.close()

View file

@ -45,7 +45,7 @@ class KeyRingBuilder : KeyRingBuilderInterface<KeyRingBuilder> {
}
override fun addUserId(userId: CharSequence): KeyRingBuilder = apply {
userIds[userId.toString().trim()] = null
userIds[userId.toString()] = null
}
override fun addUserId(userId: ByteArray): KeyRingBuilder =

View file

@ -67,11 +67,9 @@ abstract class KeyAccessor(protected val info: KeyRingInfo, protected val key: S
info.getLatestUserIdCertification(userId).let { if (it != null) return it }
}
if (info.latestDirectKeySelfSignature != null) {
return info.latestDirectKeySelfSignature
}
return info.getCurrentSubkeyBindingSignature(key.subkeyId)!!
return info.getCurrentSubkeyBindingSignature(key.subkeyId)
?: throw NoSuchElementException(
"Key does not carry acceptable self-signature signature.")
}
}

View file

@ -172,11 +172,8 @@ class KeyRingInfo(
primaryUserIdCertification?.let { getKeyExpirationTimeAsDate(it, publicKey) }
if (latestDirectKeySelfSignature == null && primaryUserIdCertification == null) {
/*
throw NoSuchElementException(
"No direct-key signature and no user-id signature found.")
*/
return null
}
if (directKeyExpirationDate != null && userIdExpirationDate == null) {
return directKeyExpirationDate
@ -405,7 +402,7 @@ class KeyRingInfo(
.plus(signatures.userIdRevocations.values)
.plus(signatures.subkeyBindings.values)
.plus(signatures.subkeyRevocations.values)
.maxByOrNull { creationDate }
.maxByOrNull { it.creationTime }
/**
* Return the creation time of the latest added subkey.
*

View file

@ -478,7 +478,7 @@ class SecretKeyRingEditor(
val prevBinding =
inspectKeyRing(secretKeyRing).getCurrentSubkeyBindingSignature(keyId)
?: throw NoSuchElementException(
"Previous subkey binding signaure for ${keyId.openPgpKeyId()} MUST NOT be null.")
"Previous subkey binding signature for ${keyId.openPgpKeyId()} MUST NOT be null.")
val bindingSig = reissueSubkeyBindingSignature(subkey, expiration, protector, prevBinding)
secretKeyRing = injectCertification(secretKeyRing, subkey, bindingSig)
}
@ -569,9 +569,10 @@ class SecretKeyRingEditor(
}
private fun sanitizeUserId(userId: CharSequence): CharSequence =
// TODO: Further research how to sanitize user IDs.
// e.g. what about newlines?
userId.toString().trim()
// I'm not sure, what kind of sanitization is needed.
// Newlines are allowed, they just need to be escaped when emitted in an ASCII armor header
// Trailing/Leading whitespace is also fine.
userId.toString()
private fun callbackFromRevocationAttributes(attributes: RevocationAttributes?) =
object : RevocationSignatureSubpackets.Callback {

View file

@ -52,7 +52,7 @@ class RevocationSignatureBuilder : AbstractSignatureBuilder<RevocationSignatureB
require(revokeeKey.isMasterKey) {
"Signature type is KEY_REVOCATION, but provided revokee does not appear to be a primary key."
}
it.generateCertification(publicSigningKey)
it.generateCertification(revokeeKey)
} else {
it.generateCertification(publicSigningKey, revokeeKey)
}

View file

@ -235,7 +235,8 @@ abstract class SignatureValidator {
signature: PGPSignature,
policy: Policy
): Policy.HashAlgorithmPolicy {
return when (SignatureType.requireFromCode(signature.signatureType)) {
return when (SignatureType.fromCode(signature.signatureType)) {
null -> policy.certificationSignatureHashAlgorithmPolicy
SignatureType.CERTIFICATION_REVOCATION,
SignatureType.KEY_REVOCATION,
SignatureType.SUBKEY_REVOCATION -> policy.revocationSignatureHashAlgorithmPolicy
@ -598,7 +599,8 @@ abstract class SignatureValidator {
if (signatureType.none { signature.isOfType(it) }) {
throw SignatureValidationException(
"Signature is of type" +
" ${SignatureType.requireFromCode(signature.signatureType)}, " +
" ${SignatureType.fromCode(signature.signatureType) ?:
("0x" + signature.signatureType.toString(16))}, " +
"while only ${signatureType.contentToString()} are allowed here.")
}
}
@ -688,7 +690,7 @@ abstract class SignatureValidator {
}
if (notAfter != null && timestamp > notAfter) {
throw SignatureValidationException(
"Signature was made before the latest allowed signature creation time." +
"Signature was made after the latest allowed signature creation time." +
" Created: ${timestamp.formatUTC()}," +
" latest allowed: ${notAfter.formatUTC()}")
}

View file

@ -59,12 +59,13 @@ class SignatureVerifier {
policy: Policy,
referenceTime: Date
): Boolean {
val type = SignatureType.requireFromCode(signature.signatureType)
val type = SignatureType.fromCode(signature.signatureType)
return when (type) {
SignatureType.GENERIC_CERTIFICATION,
SignatureType.NO_CERTIFICATION,
SignatureType.CASUAL_CERTIFICATION,
SignatureType.POSITIVE_CERTIFICATION ->
SignatureType.POSITIVE_CERTIFICATION,
null ->
verifyUserIdCertification(
userId, signature, signingKey, keyWithUserId, policy, referenceTime)
SignatureType.CERTIFICATION_REVOCATION ->

View file

@ -247,7 +247,9 @@ class ArmorUtils {
.add(OpenPgpFingerprint.of(publicKey).prettyPrint())
// Primary / First User ID
(primary ?: first)?.let {
headerMap.getOrPut(HEADER_COMMENT) { mutableSetOf() }.add(it)
headerMap
.getOrPut(HEADER_COMMENT) { mutableSetOf() }
.add(it.replace("\n", "\\n").replace("\r", "\\r"))
}
// X-1 further identities
when (userIds.size) {

View file

@ -11,14 +11,9 @@ import org.bouncycastle.util.Arrays
*
* @param chars may be null for empty passwords.
*/
class Passphrase(chars: CharArray?) {
class Passphrase(private val chars: CharArray?) {
private val lock = Any()
private var valid = true
private val chars: CharArray?
init {
this.chars = trimWhitespace(chars)
}
/**
* Return a copy of the underlying char array. A return value of null represents an empty
@ -67,6 +62,13 @@ class Passphrase(chars: CharArray?) {
override fun hashCode(): Int = getChars()?.let { String(it) }.hashCode()
/**
* Return a copy of this [Passphrase], but with whitespace characters trimmed off.
*
* @return copy with trimmed whitespace
*/
fun withTrimmedWhitespace(): Passphrase = Passphrase(trimWhitespace(chars))
companion object {
/**

View file

@ -5,7 +5,6 @@
package org.pgpainless.algorithm;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
@ -31,6 +30,6 @@ public class SignatureTypeTest {
assertFalse(SignatureType.isRevocationSignature(SignatureType.STANDALONE.getCode()));
assertFalse(SignatureType.isRevocationSignature(SignatureType.TIMESTAMP.getCode()));
assertThrows(IllegalArgumentException.class, () -> SignatureType.isRevocationSignature(-3));
assertFalse(SignatureType.isRevocationSignature(-3));
}
}

View file

@ -13,6 +13,7 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.stream.Stream;
import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.bcpg.CompressionAlgorithmTags;
@ -30,6 +31,9 @@ import org.bouncycastle.openpgp.operator.bc.BcPGPContentSignerBuilder;
import org.bouncycastle.util.io.Streams;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.pgpainless.PGPainless;
import org.pgpainless.algorithm.DocumentSignatureType;
import org.pgpainless.algorithm.HashAlgorithm;
@ -288,11 +292,9 @@ public class CanonicalizedDataEncryptionTest {
}
}
@Test
public void resultOfDecryptionIsCRLFEncoded() throws PGPException, IOException {
String before = "Foo\nBar!\n";
String after = "Foo\r\nBar!\r\n";
@ParameterizedTest
@MethodSource("resultOfDecryptionIsCRLFEncodedArguments")
public void resultOfDecryptionIsCRLFEncoded(String before, String after) throws PGPException, IOException {
String encrypted = encryptAndSign(before, DocumentSignatureType.BINARY_DOCUMENT, StreamEncoding.TEXT, true);
ByteArrayInputStream in = new ByteArrayInputStream(encrypted.getBytes(StandardCharsets.UTF_8));
@ -309,6 +311,16 @@ public class CanonicalizedDataEncryptionTest {
assertArrayEquals(after.getBytes(StandardCharsets.UTF_8), decrypted.toByteArray());
}
private static Stream<Arguments> resultOfDecryptionIsCRLFEncodedArguments() {
return Stream.of(
Arguments.of("foo", "foo"),
Arguments.of("rrr", "rrr"),
Arguments.of("Foo\nBar!\n", "Foo\r\nBar!\r\n"),
Arguments.of("Foo\rBar!\r", "Foo\r\nBar!\r\n"),
Arguments.of("Foo\r\nBar!\r\n", "Foo\r\nBar!\r\n")
);
}
@Test
public void resultOfDecryptionIsNotCRLFEncoded() throws PGPException, IOException {
String beforeAndAfter = "Foo\nBar!\n";

View file

@ -55,14 +55,14 @@ public class CustomPublicKeyDataDecryptorFactoryTest {
HardwareSecurity.DecryptionCallback hardwareDecryptionCallback = new HardwareSecurity.DecryptionCallback() {
@Override
public byte[] decryptSessionKey(long keyId, int keyAlgorithm, byte[] sessionKeyData)
public byte[] decryptSessionKey(long keyId, int keyAlgorithm, byte[] sessionKeyData, int pkeskVersion)
throws HardwareSecurity.HardwareSecurityException {
// Emulate hardware decryption.
try {
PGPSecretKey decryptionKey = secretKey.getSecretKey(encryptionKey.getKeyID());
PGPPrivateKey privateKey = UnlockSecretKey.unlockSecretKey(decryptionKey, Passphrase.emptyPassphrase());
PublicKeyDataDecryptorFactory internal = new BcPublicKeyDataDecryptorFactory(privateKey);
return internal.recoverSessionData(keyAlgorithm, new byte[][] {sessionKeyData});
return internal.recoverSessionData(keyAlgorithm, new byte[][] {sessionKeyData}, pkeskVersion);
} catch (PGPException e) {
throw new HardwareSecurity.HardwareSecurityException();
}

View file

@ -24,7 +24,7 @@ public class MessageMetadataTest {
@Test
public void processTestMessage_COMP_ENC_ENC_LIT() {
// Note: COMP of ENC does not make sense, since ENC is indistinguishable from randomness
// and randomness cannot be encrypted.
// and randomness cannot be compressed.
// For the sake of testing though, this is okay.
MessageMetadata.Message message = new MessageMetadata.Message();

View file

@ -1,111 +0,0 @@
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.key
import java.io.ByteArrayOutputStream
import org.bouncycastle.openpgp.PGPPublicKey
import org.bouncycastle.openpgp.PGPPublicKeyRing
import org.bouncycastle.openpgp.PGPSecretKeyRing
import org.bouncycastle.util.io.Streams
import org.junit.jupiter.api.Test
import org.pgpainless.PGPainless
import org.pgpainless.algorithm.KeyFlag
import org.pgpainless.decryption_verification.ConsumerOptions
import org.pgpainless.encryption_signing.EncryptionOptions
import org.pgpainless.encryption_signing.ProducerOptions
import org.pgpainless.encryption_signing.SigningOptions
import org.pgpainless.key.generation.KeySpec
import org.pgpainless.key.generation.type.KeyType
import org.pgpainless.key.generation.type.eddsa_legacy.EdDSALegacyCurve
import org.pgpainless.key.generation.type.xdh_legacy.XDHLegacySpec
import org.pgpainless.key.protection.SecretKeyRingProtector
class KeyWithoutSelfSigsTest {
@Test
fun signAndVerify() {
val key = PGPainless.readKeyRing().secretKeyRing(KEY)
val cert = PGPainless.extractCertificate(key!!)
val ciphertextOut = ByteArrayOutputStream()
val encryptionStream =
PGPainless.encryptAndOrSign()
.onOutputStream(ciphertextOut)
.withOptions(
ProducerOptions.signAndEncrypt(
EncryptionOptions.encryptCommunications().addRecipient(cert),
SigningOptions.get()
.addSignature(SecretKeyRingProtector.unprotectedKeys(), key)))
encryptionStream.write("Hello, World!\n".toByteArray())
encryptionStream.close()
val plaintextOut = ByteArrayOutputStream()
val decryptionStream =
PGPainless.decryptAndOrVerify()
.onInputStream(ciphertextOut.toByteArray().inputStream())
.withOptions(
ConsumerOptions.get()
.addVerificationCert(cert)
.addDecryptionKey(key, SecretKeyRingProtector.unprotectedKeys()))
Streams.pipeAll(decryptionStream, plaintextOut)
decryptionStream.close()
}
fun generateKey() {
val key =
PGPainless.buildKeyRing()
.setPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA_LEGACY(EdDSALegacyCurve._Ed25519)))
.addSubkey(
KeySpec.getBuilder(
KeyType.EDDSA_LEGACY(EdDSALegacyCurve._Ed25519), KeyFlag.SIGN_DATA))
.addSubkey(
KeySpec.getBuilder(
KeyType.XDH_LEGACY(XDHLegacySpec._X25519),
KeyFlag.ENCRYPT_STORAGE,
KeyFlag.ENCRYPT_COMMS))
.build()
.let {
var cert = PGPainless.extractCertificate(it)
cert =
PGPPublicKeyRing(
buildList {
val iterator = cert.publicKeys
val primaryKey = iterator.next()
add(
PGPPublicKey.removeCertification(
primaryKey, primaryKey.signatures.next()))
while (iterator.hasNext()) {
add(iterator.next())
}
})
PGPSecretKeyRing.replacePublicKeys(it, cert)
}
println(PGPainless.asciiArmor(key))
}
companion object {
const val KEY =
"-----BEGIN PGP PRIVATE KEY BLOCK-----\n" +
"Version: PGPainless\n" +
"Comment: DA3E CC77 1CD6 46F0 C6C4 4FDA 86A3 7B22 7802 2FC7\n" +
"\n" +
"lFgEZUuWuhYJKwYBBAHaRw8BAQdAuXfarON/+UG1qwhVy4/VCYuEb9iLFLb8KGQt\n" +
"KfX4Se0AAQDgqGHsb2M43F+6wK5Hla+oZzFkTUsBx8HMpRx2yeQT6hFAnFgEZUuW\n" +
"uhYJKwYBBAHaRw8BAQdAx0OHISLtekltdUVGGrG/Gs3asc/jG/nqCkBEZ5uyELwA\n" +
"AP0faf8bprP3fj248/NacfynKEVnjzc1gocfhGiWrnVgAxC1iNUEGBYKAH0FAmVL\n" +
"lroCngECmwIFFgIDAQAECwkIBwUVCgkIC18gBBkWCgAGBQJlS5a6AAoJED9gFx9r\n" +
"B25syqoA/0JR3Zcs6fHQ0jW7+u6330SD5h8WvG78IKsE6AfChBLXAP4hlXGidztq\n" +
"5sOHEQvXD2KPCHEJ6MuQ+rbNSSf0fQhgDwAKCRCGo3sieAIvxzmIAP9+9vRoevUM\n" +
"luQhZzQ7DgYqTCyNkeq2cpVgOfa0lyVDgwEApwrd5DlU3GorGHAQHFS6jhw1IOoG\n" +
"FGQ3zpWaOXd7XwKcXQRlS5a6EgorBgEEAZdVAQUBAQdAZIY7ISyNzp0oMoK0dgb8\n" +
"dX6t/i4Uh+l0jnxM0Z1dEB8DAQgHAAD/fhL5dzdJQ7hFhr78AmDEZKFE4txZFPvd\n" +
"ZVFvIWTthFgQ5Ih1BBgWCgAdBQJlS5a6Ap4BApsMBRYCAwEABAsJCAcFFQoJCAsA\n" +
"CgkQhqN7IngCL8cIGgEAzydjTfKvdrTvzXXu97j8TAoOxk89QnLqsM6BU0VsVmkA\n" +
"/1IzH+PXgPPW9ff+elxTi2NWmK+P033P6i5b5Jdf41YD\n" +
"=GBVS\n" +
"-----END PGP PRIVATE KEY BLOCK-----"
}
}

View file

@ -23,7 +23,7 @@ To start using pgpainless-sop in your code, include the following lines in your
...
dependencies {
...
implementation "org.pgpainless:pgpainless-sop:1.7.0"
implementation "org.pgpainless:pgpainless-sop:1.7.6"
...
}
@ -34,7 +34,7 @@ dependencies {
<dependency>
<groupId>org.pgpainless</groupId>
<artifactId>pgpainless-sop</artifactId>
<version>1.7.0</version>
<version>1.7.6</version>
</dependency>
...
</dependencies>
@ -67,7 +67,7 @@ byte[] encrypted = sop.encrypt()
// Decrypt a message
ByteArrayAndResult<DecryptionResult> messageAndVerifications = sop.decrypt()
.verifyWith(cert)
.verifyWithCert(cert)
.withKey(key)
.ciphertext(encrypted)
.toByteArrayAndResult();

View file

@ -1,7 +1,7 @@
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
import org.apache.tools.ant.filters.*
plugins {
id 'java-library'
}
@ -22,7 +22,7 @@ dependencies {
testImplementation "ch.qos.logback:logback-classic:$logbackVersion"
// Depend on "shared" sop-java test suite (fixtures are turned into tests by inheritance inside test sources)
testImplementation(testFixtures("org.pgpainless:sop-java:$sopJavaVersion"))
testImplementation "org.pgpainless:sop-java-testfixtures:$sopJavaVersion"
implementation(project(":pgpainless-core"))
api "org.pgpainless:sop-java:$sopJavaVersion"
@ -30,6 +30,12 @@ dependencies {
implementation "com.google.code.findbugs:jsr305:3.0.2"
}
processResources {
filter ReplaceTokens, tokens: [
"project.version": project.version.toString()
]
}
test {
useJUnitPlatform()
environment("test.implementation", "sop.testsuite.pgpainless.PGPainlessSopInstanceFactory")

View file

@ -12,7 +12,6 @@ import org.bouncycastle.util.io.Streams
import org.pgpainless.decryption_verification.OpenPgpInputStream
import org.pgpainless.util.ArmoredOutputStreamFactory
import sop.Ready
import sop.enums.ArmorLabel
import sop.exception.SOPGPException
import sop.operation.Armor
@ -46,9 +45,4 @@ class ArmorImpl : Armor {
}
}
}
@Deprecated("Setting custom labels is not supported.")
override fun label(label: ArmorLabel): Armor {
throw SOPGPException.UnsupportedOption("Setting custom Armor labels not supported.")
}
}

View file

@ -92,7 +92,7 @@ class DecryptImpl : Decrypt {
}
override fun verifyWithCert(cert: InputStream): Decrypt = apply {
KeyReader.readPublicKeys(cert, true)?.let { consumerOptions.addVerificationCerts(it) }
KeyReader.readPublicKeys(cert, true).let { consumerOptions.addVerificationCerts(it) }
}
override fun withKey(key: InputStream): Decrypt = apply {
@ -107,10 +107,10 @@ class DecryptImpl : Decrypt {
}
override fun withPassword(password: String): Decrypt = apply {
consumerOptions.addDecryptionPassphrase(Passphrase.fromPassword(password))
consumerOptions.addMessagePassphrase(Passphrase.fromPassword(password))
password.trimEnd().let {
if (it != password) {
consumerOptions.addDecryptionPassphrase(Passphrase.fromPassword(it))
consumerOptions.addMessagePassphrase(Passphrase.fromPassword(it))
}
}
}

View file

@ -11,6 +11,7 @@ import org.bouncycastle.openpgp.PGPSecretKeyRing
import org.bouncycastle.openpgp.PGPSignature
import org.bouncycastle.util.io.Streams
import org.pgpainless.PGPainless
import org.pgpainless.algorithm.CompressionAlgorithm
import org.pgpainless.algorithm.DocumentSignatureType
import org.pgpainless.algorithm.HashAlgorithm
import org.pgpainless.bouncycastle.extensions.openPgpFingerprint
@ -57,7 +58,10 @@ class DetachedSignImpl : DetachedSign {
val signingStream =
PGPainless.encryptAndOrSign()
.discardOutput()
.withOptions(ProducerOptions.sign(signingOptions).setAsciiArmor(armor))
.withOptions(
ProducerOptions.sign(signingOptions)
.setAsciiArmor(armor)
.overrideCompressionAlgorithm(CompressionAlgorithm.UNCOMPRESSED))
return object : ReadyWithResult<SigningResult>() {
override fun writeTo(outputStream: OutputStream): SigningResult {

View file

@ -136,7 +136,7 @@ class EncryptImpl : Encrypt {
}
override fun withPassword(password: String): Encrypt = apply {
encryptionOptions.addPassphrase(Passphrase.fromPassword(password))
encryptionOptions.addMessagePassphrase(Passphrase.fromPassword(password))
}
private fun modeToStreamEncoding(mode: EncryptAs): StreamEncoding {

View file

@ -34,7 +34,7 @@ class InlineDetachImpl : InlineDetach {
private val sigOut = ByteArrayOutputStream()
override fun writeTo(messageOutputStream: OutputStream): Signatures {
override fun writeTo(outputStream: OutputStream): Signatures {
var pgpIn = OpenPgpInputStream(messageInputStream)
if (pgpIn.isNonOpenPgp) {
throw SOPGPException.BadData("Data appears to be non-OpenPGP.")
@ -50,7 +50,7 @@ class InlineDetachImpl : InlineDetach {
try {
signatures =
ClearsignedMessageUtil.detachSignaturesFromInbandClearsignedMessage(
armorIn, messageOutputStream)
armorIn, outputStream)
if (signatures.isEmpty) {
throw SOPGPException.BadData(
"Data did not contain OpenPGP signatures.")
@ -86,7 +86,7 @@ class InlineDetachImpl : InlineDetach {
if (next is PGPLiteralData) {
// Write out contents of Literal Data packet
val literalIn = (next as PGPLiteralData).dataStream
Streams.pipeAll(literalIn, messageOutputStream)
Streams.pipeAll(literalIn, outputStream)
literalIn.close()
continue
}

View file

@ -11,7 +11,9 @@ import org.bouncycastle.openpgp.PGPException
import org.bouncycastle.openpgp.PGPSecretKeyRing
import org.bouncycastle.util.io.Streams
import org.pgpainless.PGPainless
import org.pgpainless.algorithm.CompressionAlgorithm
import org.pgpainless.algorithm.DocumentSignatureType
import org.pgpainless.algorithm.StreamEncoding
import org.pgpainless.bouncycastle.extensions.openPgpFingerprint
import org.pgpainless.encryption_signing.ProducerOptions
import org.pgpainless.encryption_signing.SigningOptions
@ -56,12 +58,22 @@ class InlineSignImpl : InlineSign {
val producerOptions =
ProducerOptions.sign(signingOptions).apply {
if (mode == InlineSignAs.clearsigned) {
setCleartextSigned()
setAsciiArmor(true) // CSF is always armored
} else {
setAsciiArmor(armor)
when (mode) {
InlineSignAs.clearsigned -> {
setCleartextSigned()
setAsciiArmor(true) // CSF is always armored
setEncoding(StreamEncoding.TEXT)
applyCRLFEncoding()
}
InlineSignAs.text -> {
setEncoding(StreamEncoding.TEXT)
applyCRLFEncoding()
}
else -> {
setAsciiArmor(armor)
}
}
overrideCompressionAlgorithm(CompressionAlgorithm.UNCOMPRESSED)
}
return object : Ready() {

View file

@ -8,6 +8,7 @@ import java.io.IOException
import java.io.InputStream
import java.util.*
import org.bouncycastle.jce.provider.BouncyCastleProvider
import sop.SOP
import sop.operation.Version
/** Implementation of the `version` operation using PGPainless. */
@ -25,14 +26,14 @@ class VersionImpl : Version {
String.format(Locale.US, "Bouncy Castle %.2f", BouncyCastleProvider().version)
val specVersion = String.format("%02d", SOP_VERSION)
return """${getName()} ${getVersion()}
https://codeberg.org/PGPainless/pgpainless/src/branch/master/pgpainless-sop
https://codeberg.org/PGPainless/pgpainless/src/branch/main/pgpainless-sop
Implementation of the Stateless OpenPGP Protocol Version $specVersion
https://datatracker.ietf.org/doc/html/draft-dkg-openpgp-stateless-cli-$specVersion
Based on pgpainless-core ${getVersion()}
https://pgpainless.org
${formatSopJavaVersion()}
Using $bcVersion
https://www.bouncycastle.org/java.html"""
}
@ -49,15 +50,27 @@ https://www.bouncycastle.org/java.html"""
// See https://stackoverflow.com/a/50119235
return try {
val resourceIn: InputStream =
javaClass.getResourceAsStream("/version.properties")
?: throw IOException("File version.properties not found.")
SOP::class.java.getResourceAsStream("/pgpainless-sop.properties")
?: throw IOException("File pgpainless-sop.properties not found.")
val properties = Properties().apply { load(resourceIn) }
properties.getProperty("version")
properties.getProperty("pgpainless-sop-version")
} catch (e: IOException) {
"DEVELOPMENT"
}
}
private fun formatSopJavaVersion(): String {
return getSopJavaVersion()?.let {
"""
sop-java $it
"""
.trimIndent()
}
?: ""
}
override fun isSopSpecImplementationIncomplete(): Boolean = false
}

View file

@ -0,0 +1,4 @@
# SPDX-FileCopyrightText: 2025 Paul Schaub <info@pgpainless.org>
#
# SPDX-License-Identifier: Apache-2.0
pgpainless-sop-version=@project.version@

View file

@ -100,4 +100,14 @@ public class GenerateKeyTest {
assertThrows(SOPGPException.UnsupportedProfile.class, () ->
sop.generateKey().profile("invalid"));
}
@Test
public void generateKeyWithNewlinesInUserId() throws IOException {
byte[] keyBytes = sop.generateKey()
.userId("Foo\n\nBar")
.generate()
.getBytes();
assertTrue(new String(keyBytes).contains("Foo\\n\\nBar"));
}
}

View file

@ -0,0 +1,164 @@
// SPDX-FileCopyrightText: 2024 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.sop;
import org.junit.jupiter.api.Test;
import org.pgpainless.PGPainless;
import org.pgpainless.policy.Policy;
import sop.ByteArrayAndResult;
import sop.Verification;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertFalse;
public class VerifyLegacySignatureTest {
@Test
public void verifyLegacySignature() throws IOException {
// Key generated in 2012 using SHA1 for self sigs
String KEY = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" +
"Version: PGPainless\n" +
"Comment: 21FA 6E60 D6C9 B7D1 0EAC 56A2 984B 91CF D303 214C\n" +
"Comment: Legacy <legacy@example.com>\n" +
"\n" +
"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\n" +
"SV9g8CiU0cX7hiBYYpktcDVU3uRCR4Fkvw==\n" +
"=n8qw\n" +
"-----END PGP PRIVATE KEY BLOCK-----";
// Sig generated in 2012 using SHA1
String oldSig = "-----BEGIN PGP MESSAGE-----\n" +
"Version: PGPainless\n" +
"\n" +
"owEB2gEl/pANAwACAZhLkc/TAyFMAcsTYgAAAAAASGVsbG8sIFdvcmxkIYkBswQA\n" +
"AQIAJwkQmEuRz9MDIUwWIQQh+m5g1sm30Q6sVqKYS5HP0wMhTAWCUNRWwAAALxEL\n" +
"/2uhYsTLM8nUnYm2GJB6pkapX1kbQrqfAhK46IjxcPpRdl6CW4cFrG6iFegx4YLE\n" +
"fu44VKG+XGy/RTZXIEJubi9zVyOGGJM9Bwwdcp/eekO16/kJ7BsbkaO+5AG/fNeg\n" +
"bL5C8D2m6jV1seAt/+tRyM9jLkRi9odq8BsGA6ZcthAxh3MUoo1yw3QwwEcFFHg/\n" +
"gBw4ZtL8KIQN1PKDz3sSV4GXPQAiz+/uADZ2lL6mbDEK/gXAK1KevIO3U8ZU9B6l\n" +
"cOF9fJww31SCqFGDq50Lzwz7eySJB1TZ0IoehGDXoQ8JF88uTVfACkBATE0Zx7zg\n" +
"TAYIgPSjWY4TEDZ9YjdxJ0hKTMncxVfZPB+J/mYCpVADYSEhLbUJ1ntjc0s35xJD\n" +
"udLSwUWuboedVdEcaqnfgHoaaV+nKk+6F9y8NO56RK3Bfx5FmKmNZHbhfXO/qRt9\n" +
"H43UktMUD6xWxxJv7mutThOp2aizBeboa5YSJ1mxtkPW0/lyK1jr438ETHUnCeu6\n" +
"Vw==\n" +
"=TtKx\n" +
"-----END PGP MESSAGE-----";
SOPImpl sop = new SOPImpl();
byte[] cert = sop.extractCert().key(KEY.getBytes(StandardCharsets.UTF_8))
.getBytes();
ByteArrayAndResult<List<Verification>> result = sop.inlineVerify()
.cert(cert)
.data(oldSig.getBytes(StandardCharsets.UTF_8))
.toByteArrayAndResult();
assertFalse(result.getResult().isEmpty());
// Adjust data signature hash policy to accept new SHA-1 sigs
PGPainless.getPolicy().setDataSignatureHashAlgorithmPolicy(
Policy.HashAlgorithmPolicy.static2022RevocationSignatureHashAlgorithmPolicy());
// Sig generated in 2024 using SHA1
String newSig = "-----BEGIN PGP MESSAGE-----\n" +
"Version: PGPainless\n" +
"\n" +
"owEB2gEl/pANAwACAZhLkc/TAyFMAcsTYgAAAAAASGVsbG8sIFdvcmxkIYkBswQA\n" +
"AQIAJwWCZw5i2AkQmEuRz9MDIUwWIQQh+m5g1sm30Q6sVqKYS5HP0wMhTAAAhVML\n" +
"+QGH+O2fEJoAY8ZxKz/mosg4it9IeSzMhBvDgZJE8Jc+VGk7EuXL0M8pfHL+Jgmv\n" +
"FMzF3chzzLS7QA4K6hbxO31/M8TNSU12geuzQiBV7Kb1hjpvIObBgEqYsX50ZV8r\n" +
"5DHcr7huABUOH6tCKmCA2OxOvr1QV8X39h856bz3WqqP9HW8kZ6H1Z6d7XWlRMtW\n" +
"mAnSevvOJbb0Z3D97obYqytSLzi2Jyv+w2R9kYzMQff2Rl6Cv4F7zsRrF9JRC0m6\n" +
"X/s+VSNuT2yG0/4F5y8vrxvNkfd8YfM8DM6irJV4yJyVuwIoZnM913XCA4F7Uo4t\n" +
"Z8ER17SY4WOYvdja/7qPcOUjX5n1dDU0W7q2muqnZXREw2JXTULiDl0MET3K4kFu\n" +
"a6FyyMGGQwFpAnZ4gDZKzw06abd95AgHx4QlkD89J7MnUBBV+AGHNAQlCPPEVPQq\n" +
"dWTInYndt4GKCUxVkJeHD6ZPLdxEEvICmEap4FQzhqM8U7weoEsSinoVoc4JmSY9\n" +
"dQ==\n" +
"=XrzP\n" +
"-----END PGP MESSAGE-----";
result = sop.inlineVerify()
.cert(cert)
.data(newSig.getBytes(StandardCharsets.UTF_8))
.toByteArrayAndResult();
assertFalse(result.getResult().isEmpty());
}
}

View file

@ -4,16 +4,15 @@
allprojects {
ext {
shortVersion = '1.7.0'
isSnapshot = false
pgpainlessMinAndroidSdk = 10
javaSourceCompatibility = 1.8
bouncyCastleVersion = '1.78.1'
shortVersion = '1.7.7'
isSnapshot = true
javaSourceCompatibility = 11
bouncyCastleVersion = '1.81'
bouncyPgVersion = bouncyCastleVersion
junitVersion = '5.8.2'
logbackVersion = '1.4.14'
logbackVersion = '1.5.13'
mockitoVersion = '4.5.1'
slf4jVersion = '1.7.36'
sopJavaVersion = '10.0.1'
sopJavaVersion = '10.1.1'
}
}