Compare commits

..

No commits in common. "main" and "0.1.0" have entirely different histories.
main ... 0.1.0

25 changed files with 49 additions and 166 deletions

View file

@ -13,8 +13,3 @@ Source: https://pgpainless.org
Files: gradle* Files: gradle*
Copyright: 2015 the original author or authors. Copyright: 2015 the original author or authors.
License: Apache-2.0 License: Apache-2.0
# Woodpecker build files
Files: .woodpecker/*
Copyright: 2022 the original author or authors.
License: Apache-2.0

View file

@ -1,12 +0,0 @@
pipeline:
run:
image: gradle:7.5-jdk8
commands:
- git checkout $CI_COMMIT_BRANCH
# Code works
- gradle test
# Code is clean
- gradle check javadocAll
# Code has coverage
- gradle jacocoRootReport coveralls
secrets: [COVERALLS_REPO_TOKEN]

View file

@ -1,7 +0,0 @@
# Code is licensed properly
# See https://reuse.software/
pipeline:
reuse:
image: fsfe/reuse:latest
commands:
- reuse lint

View file

@ -6,21 +6,5 @@ SPDX-License-Identifier: Apache-2.0
# Changelog # Changelog
## 0.1.2
- Bump `pgpainless-core` to `1.5.6`
- Bump `cert-d-pgpainless` to `0.2.2`
- Bump `cert-d-java` to `0.2.2`
## 0.1.1
- Bump `pgpainless-core` to `1.2.1`
- Bump `cert-d-pgpainless` to `0.1.2`
- Bump `pgp-certificate-store` to `0.1.1`
- Bump `slf4j` to `1.7.36`
- Bump `logback` to `1.2.11`
- Bump `mockito` to `4.5.1`
- Bump `jackson-data-bind` to `2.13.2.2`
- Bump `jackson-data-format-xml` to `2.13.2`
- Rename `fetch` command to `get`
## 0.1.0 ## 0.1.0
- Initial Release - Initial Release

View file

@ -6,14 +6,6 @@ SPDX-License-Identifier: Apache-2.0
# Web Key Directory for Java # Web Key Directory for Java
[![status-badge](https://ci.codeberg.org/api/badges/PGPainless/wkd-java/status.svg)](https://ci.codeberg.org/PGPainless/wkd-java)
[![Coverage Status](https://coveralls.io/repos/github/pgpainless/wkd-java/badge.svg?branch=main)](https://coveralls.io/github/pgpainless/wkd-java?branch=main)
[![REUSE status](https://api.reuse.software/badge/github.com/pgpainless/wkd-java)](https://api.reuse.software/info/github.com/pgpainless/wkd-java)
Client-side API for fetching OpenPGP certificates via the [Web Key Directory](https://www.ietf.org/archive/id/draft-koch-openpgp-webkey-service-13.html) protocol.
## Modules
This repository consists of the following modules: This repository consists of the following modules:
* [wkd-java](/wkd-java): An implementation of Certificate Discovery * [wkd-java](/wkd-java): An implementation of Certificate Discovery

View file

@ -9,6 +9,7 @@ buildscript {
maven { maven {
url "https://plugins.gradle.org/m2/" url "https://plugins.gradle.org/m2/"
} }
mavenLocal()
mavenCentral() mavenCentral()
} }
dependencies { dependencies {
@ -60,7 +61,6 @@ allprojects {
repositories { repositories {
mavenCentral() mavenCentral()
mavenLocal()
} }
// Reproducible Builds // Reproducible Builds
@ -70,6 +70,10 @@ allprojects {
} }
project.ext { project.ext {
junitVersion = '5.8.2'
slf4jVersion = '1.7.32'
logbackVersion = '1.2.10'
pgpainlessVersion = '1.1.1'
rootConfigDir = new File(rootDir, 'config') rootConfigDir = new File(rootDir, 'config')
gitCommit = getGitCommit() gitCommit = getGitCommit()
isContinuousIntegrationEnvironment = Boolean.parseBoolean(System.getenv('CI')) isContinuousIntegrationEnvironment = Boolean.parseBoolean(System.getenv('CI'))

View file

@ -4,22 +4,9 @@
allprojects { allprojects {
ext { ext {
shortVersion = '0.1.3' shortVersion = '0.1.0'
isSnapshot = true isSnapshot = false
minAndroidSdk = 10 minAndroidSdk = 10
javaSourceCompatibility = 1.8 javaSourceCompatibility = 1.8
jacksonDataBindVersion = '2.15.2'
jacksonDataFormatXmlVersion = '2.15.2'
junitVersion = '5.8.2'
junitSysExitVersion = '1.1.2'
jsrVersion = '3.0.2'
slf4jVersion = '1.7.36'
logbackVersion = '1.2.11'
mockitoVersion = '4.5.1'
pgpainlessVersion = '1.5.6'
pgpainlessCertDVersion = '0.2.2'
picocliVersion = '4.6.3'
certDJavaVersion = '0.2.2'
zbase32Version = '1.0.0'
} }
} }

View file

@ -14,31 +14,21 @@ repositories {
} }
dependencies { dependencies {
// JUnit
testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion" testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion"
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion" testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion"
// Test CLI Exit Codes
// https://todd.ginsberg.com/post/testing-system-exit/ // https://todd.ginsberg.com/post/testing-system-exit/
testImplementation "com.ginsberg:junit5-system-exit:$junitSysExitVersion" testImplementation 'com.ginsberg:junit5-system-exit:1.1.2'
testImplementation 'org.mockito:mockito-core:4.3.1'
// Test using mocked components implementation("org.pgpainless:pgpainless-cert-d:0.1.1")
testImplementation "org.mockito:mockito-core:$mockitoVersion"
// Certificates
implementation("org.pgpainless:pgpainless-cert-d:$pgpainlessCertDVersion")
// WKD
implementation project(':wkd-java') implementation project(':wkd-java')
implementation "info.picocli:picocli:4.6.3"
// CLI
implementation "info.picocli:picocli:$picocliVersion"
// Logging // Logging
testImplementation "ch.qos.logback:logback-classic:$logbackVersion" testImplementation "ch.qos.logback:logback-classic:$logbackVersion"
implementation "org.slf4j:slf4j-nop:$slf4jVersion" implementation 'org.slf4j:slf4j-nop:1.7.36'
// Test Suite
testImplementation project(":wkd-test-suite") testImplementation project(":wkd-test-suite")
} }

View file

@ -6,11 +6,11 @@ package pgp.wkd.cli;
import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
import org.pgpainless.PGPainless; import org.pgpainless.PGPainless;
import org.pgpainless.certificate_store.CertificateFactory; import org.pgpainless.certificate_store.CertificateFactory;
import org.pgpainless.key.collection.PGPKeyRingCollection;
import org.pgpainless.key.info.KeyRingInfo; import org.pgpainless.key.info.KeyRingInfo;
import pgp.certificate_store.certificate.Certificate; import pgp.certificate_store.Certificate;
import pgp.wkd.CertificateAndUserIds; import pgp.wkd.CertificateAndUserIds;
import pgp.wkd.discovery.CertificateParser; import pgp.wkd.discovery.CertificateParser;
@ -24,13 +24,10 @@ public class PGPainlessCertificateParser implements CertificateParser {
public List<CertificateAndUserIds> read(InputStream inputStream) throws IOException { public List<CertificateAndUserIds> read(InputStream inputStream) throws IOException {
List<CertificateAndUserIds> certificatesAndUserIds = new ArrayList<>(); List<CertificateAndUserIds> certificatesAndUserIds = new ArrayList<>();
try { try {
PGPKeyRingCollection keyMaterial = PGPainless.readKeyRing().keyRingCollection(inputStream, true); PGPPublicKeyRingCollection certificates = PGPainless.readKeyRing().publicKeyRingCollection(inputStream);
if (keyMaterial.getPGPSecretKeyRingCollection().size() != 0) { for (PGPPublicKeyRing certificate : certificates) {
throw new PGPException("Secret key material encountered!");
}
for (PGPPublicKeyRing certificate : keyMaterial.getPgpPublicKeyRingCollection()) {
KeyRingInfo info = PGPainless.inspectKeyRing(certificate); KeyRingInfo info = PGPainless.inspectKeyRing(certificate);
Certificate parsedCert = CertificateFactory.certificateFromPublicKeyRing(certificate, 0L); Certificate parsedCert = CertificateFactory.certificateFromPublicKeyRing(certificate);
List<String> userIds = info.getValidAndExpiredUserIds(); List<String> userIds = info.getValidAndExpiredUserIds();
certificatesAndUserIds.add(new CertificateAndUserIds(parsedCert, userIds)); certificatesAndUserIds.add(new CertificateAndUserIds(parsedCert, userIds));
} }

View file

@ -6,15 +6,13 @@ package pgp.wkd.cli;
import pgp.wkd.exception.CertNotFetchableException; import pgp.wkd.exception.CertNotFetchableException;
import pgp.wkd.exception.RejectedCertificateException; import pgp.wkd.exception.RejectedCertificateException;
import pgp.wkd.cli.command.GetCmd; import pgp.wkd.cli.command.Fetch;
import picocli.CommandLine; import picocli.CommandLine;
@CommandLine.Command( @CommandLine.Command(
name = "wkd",
resourceBundle = "msg_wkd",
subcommands = { subcommands = {
CommandLine.HelpCommand.class, CommandLine.HelpCommand.class,
GetCmd.class Fetch.class
} }
) )
public class WKDCLI { public class WKDCLI {

View file

@ -20,19 +20,23 @@ import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
@CommandLine.Command( @CommandLine.Command(
name = "get", name = "fetch",
resourceBundle = "msg_get" description = "Fetch an OpenPGP Certificate via the Web Key Directory"
) )
public class GetCmd implements Runnable { public class Fetch implements Runnable {
@CommandLine.Parameters( @CommandLine.Parameters(
index = "0", index = "0",
arity = "1", arity = "1",
paramLabel = "USERID") paramLabel = "USERID",
description = "Certificate User-ID"
)
String userId; String userId;
@CommandLine.Option( @CommandLine.Option(
names = {"-a", "--armor"}) names = {"-a", "--armor"},
description = "ASCII Armor the output"
)
boolean armor = false; boolean armor = false;
public static final CertificateDiscoverer DEFAULT_DISCOVERER = new ValidatingCertificateDiscoverer( public static final CertificateDiscoverer DEFAULT_DISCOVERER = new ValidatingCertificateDiscoverer(
@ -63,7 +67,7 @@ public class GetCmd implements Runnable {
throw new NullPointerException("CertificateDiscoverer cannot be null."); throw new NullPointerException("CertificateDiscoverer cannot be null.");
} }
GetCmd.discoverer = discoverer; Fetch.discoverer = discoverer;
} }
private WKDAddress addressFromUserId(String userId) { private WKDAddress addressFromUserId(String userId) {

View file

@ -1,12 +0,0 @@
# SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
#
# SPDX-License-Identifier: Apache-2.0
usage.header=Get an OpenPGP Certificate via the Web Key Directory
USERID[0]=Certificate User-ID
armor=ASCII Armor the output
# Generic TODO: Remove when bumping picocli to 4.7.0
usage.synopsisHeading=Usage:\u0020
usage.commandListHeading = %nCommands:%n
usage.optionListHeading = %nOptions:%n
usage.footerHeading=Powered by picocli%n

View file

@ -1,12 +0,0 @@
# SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
#
# SPDX-License-Identifier: Apache-2.0
usage.header=Empfange ein OpenPGP Zertifikat mithilfe des Web Key Directory
USERID[0]=Nutzeridentität des Zertifikats
armor=Schütze die Ausgabe mit ASCII Armor
# Generic TODO: Remove when bumping picocli to 4.7.0
usage.synopsisHeading=Aufruf:\u0020
usage.commandListHeading=%nBefehle:%n
usage.optionListHeading = %nOptionen:%n
usage.footerHeading=Powered by Picocli%n

View file

@ -1,10 +0,0 @@
# SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
#
# SPDX-License-Identifier: Apache-2.0
usage.header=Interact with the Web Key Directory
# Generic TODO: Remove when bumping picocli to 4.7.0
usage.synopsisHeading=Usage:\u0020
usage.commandListHeading = %nCommands:%n
usage.optionListHeading = %nOptions:%n
usage.footerHeading=Powered by picocli%n

View file

@ -1,10 +0,0 @@
# SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
#
# SPDX-License-Identifier: Apache-2.0
usage.header=Interagiere mit dem Web Key Directory
# Generic TODO: Remove when bumping picocli to 4.7.0
usage.synopsisHeading=Aufruf:\u0020
usage.commandListHeading=%nBefehle:%n
usage.optionListHeading = %nOptionen:%n
usage.footerHeading=Powered by Picocli%n

View file

@ -12,11 +12,11 @@ import pgp.wkd.cli.WKDCLI;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
@Disabled("For privacy reasons") @Disabled("For privacy reasons")
public class TestGetKeysFromGithubPages extends RedirectSystemStreamsTest { public class TestFetchKeysFromGithubPages extends RedirectSystemStreamsTest {
// Valid WKD publication. // Valid WKD publication.
// Cert is available at https://pgpainless.github.io/.well-known/openpgpkey/hu/eprjcbeppbna3f6xabhtpddzpn41nknw // Cert is available at https://pgpainless.github.io/.well-known/openpgpkey/hu/eprjcbeppbna3f6xabhtpddzpn41nknw
private static final String USERID_BASE = "WKD Test (Base Case - Valid User-ID) <wkd-test-base@pgpainless.github.io>"; private static final String USERID_BASE = "WKD Test <wkd-test-base@pgpainless.github.io> [Base Case - Valid User-ID]";
private static final String MAIL_BASE = "wkd-test-base@pgpainless.github.io"; private static final String MAIL_BASE = "wkd-test-base@pgpainless.github.io";
@Test @Test

View file

@ -10,7 +10,7 @@ import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.TestFactory; import org.junit.jupiter.api.TestFactory;
import pgp.wkd.cli.PGPainlessCertificateParser; import pgp.wkd.cli.PGPainlessCertificateParser;
import pgp.wkd.cli.WKDCLI; import pgp.wkd.cli.WKDCLI;
import pgp.wkd.cli.command.GetCmd; import pgp.wkd.cli.command.Fetch;
import pgp.wkd.discovery.CertificateDiscoverer; import pgp.wkd.discovery.CertificateDiscoverer;
import pgp.wkd.discovery.ValidatingCertificateDiscoverer; import pgp.wkd.discovery.ValidatingCertificateDiscoverer;
import pgp.wkd.discovery.DiscoveryMethod; import pgp.wkd.discovery.DiscoveryMethod;
@ -45,7 +45,7 @@ public class TestSuiteTestRunner {
// Fetch certificates from a local directory instead of the internetzzz. // Fetch certificates from a local directory instead of the internetzzz.
CertificateDiscoverer discoverer = new ValidatingCertificateDiscoverer( CertificateDiscoverer discoverer = new ValidatingCertificateDiscoverer(
new PGPainlessCertificateParser(), new DirectoryBasedCertificateFetcher(tempPath)); new PGPainlessCertificateParser(), new DirectoryBasedCertificateFetcher(tempPath));
GetCmd.setCertificateDiscoverer(discoverer); Fetch.setCertificateDiscoverer(discoverer);
} }
@TestFactory @TestFactory
@ -61,7 +61,7 @@ public class TestSuiteTestRunner {
String mail = testCase.getLookupMailAddress(); String mail = testCase.getLookupMailAddress();
int exitCode = WKDCLI.execute(new String[] { int exitCode = WKDCLI.execute(new String[] {
"get", "--armor", mail "fetch", "--armor", mail
}); });
if (testCase.isExpectSuccess()) { if (testCase.isExpectSuccess()) {
@ -74,6 +74,6 @@ public class TestSuiteTestRunner {
@AfterAll @AfterAll
public static void reset() { public static void reset() {
GetCmd.setCertificateDiscoverer(GetCmd.DEFAULT_DISCOVERER); Fetch.setCertificateDiscoverer(Fetch.DEFAULT_DISCOVERER);
} }
} }

View file

@ -13,22 +13,19 @@ repositories {
} }
dependencies { dependencies {
// JUnit
testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion" testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion"
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion" testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion"
// Logging // Logging
api "org.slf4j:slf4j-api:$slf4jVersion" api "org.slf4j:slf4j-api:$slf4jVersion"
testImplementation "ch.qos.logback:logback-classic:$logbackVersion" testImplementation "ch.qos.logback:logback-classic:$logbackVersion"
api "org.pgpainless:pgp-certificate-store:0.1.0"
// Certificate store
api "org.pgpainless:pgp-certificate-store:$certDJavaVersion"
// Z-Base32 // Z-Base32
implementation "com.sandinh:zbase32-commons-codec:$zbase32Version" implementation 'com.sandinh:zbase32-commons-codec:1.0.0'
// @Nullable etc. // @Nullable etc.
implementation "com.google.code.findbugs:jsr305:$jsrVersion" implementation "com.google.code.findbugs:jsr305:3.0.2"
} }
test { test {

View file

@ -4,7 +4,7 @@
package pgp.wkd; package pgp.wkd;
import pgp.certificate_store.certificate.Certificate; import pgp.certificate_store.Certificate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;

View file

@ -4,8 +4,7 @@
package pgp.wkd; package pgp.wkd;
import pgp.certificate_store.Certificate;
import pgp.certificate_store.certificate.Certificate;
/** /**
* A rejected OpenPGP certificate. * A rejected OpenPGP certificate.

View file

@ -4,7 +4,7 @@
package pgp.wkd.discovery; package pgp.wkd.discovery;
import pgp.certificate_store.certificate.Certificate; import pgp.certificate_store.Certificate;
import pgp.wkd.RejectedCertificate; import pgp.wkd.RejectedCertificate;
import pgp.wkd.WKDAddress; import pgp.wkd.WKDAddress;
import pgp.wkd.exception.MissingPolicyFileException; import pgp.wkd.exception.MissingPolicyFileException;
@ -12,7 +12,6 @@ import pgp.wkd.exception.MissingPolicyFileException;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.net.URI; import java.net.URI;
import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
@ -171,8 +170,8 @@ public final class DiscoveryResponse {
private DiscoveryMethod discoveryMethod; private DiscoveryMethod discoveryMethod;
private WKDAddress address; private WKDAddress address;
private List<Certificate> acceptableCertificates = new ArrayList<>(); private List<Certificate> acceptableCertificates;
private List<RejectedCertificate> rejectedCertificates = new ArrayList<>(); private List<RejectedCertificate> rejectedCertificates;
private Throwable fetchingFailure; private Throwable fetchingFailure;
private WKDPolicy policy; private WKDPolicy policy;
private MissingPolicyFileException missingPolicyFileException; private MissingPolicyFileException missingPolicyFileException;

View file

@ -4,7 +4,7 @@
package pgp.wkd.discovery; package pgp.wkd.discovery;
import pgp.certificate_store.certificate.Certificate; import pgp.certificate_store.Certificate;
import pgp.wkd.exception.CertNotFetchableException; import pgp.wkd.exception.CertNotFetchableException;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;

View file

@ -4,7 +4,7 @@
package pgp.wkd.discovery; package pgp.wkd.discovery;
import pgp.certificate_store.certificate.Certificate; import pgp.certificate_store.Certificate;
import pgp.wkd.CertificateAndUserIds; import pgp.wkd.CertificateAndUserIds;
import pgp.wkd.exception.MissingPolicyFileException; import pgp.wkd.exception.MissingPolicyFileException;
import pgp.wkd.exception.RejectedCertificateException; import pgp.wkd.exception.RejectedCertificateException;

View file

@ -26,11 +26,11 @@ dependencies {
implementation "org.pgpainless:pgpainless-core:$pgpainlessVersion" implementation "org.pgpainless:pgpainless-core:$pgpainlessVersion"
// CLI // CLI
implementation "info.picocli:picocli:$picocliVersion" implementation "info.picocli:picocli:4.6.3"
// Object Mapping // Object Mapping
implementation "com.fasterxml.jackson.dataformat:jackson-dataformat-xml:$jacksonDataFormatXmlVersion" implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.13.1'
implementation "com.fasterxml.jackson.core:jackson-databind:$jacksonDataBindVersion" implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.1'
} }
application { application {

View file

@ -38,7 +38,7 @@ public class AbstractTestSuiteGenerator {
} }
protected PGPSecretKeyRing secretKey(String userId) throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException { protected PGPSecretKeyRing secretKey(String userId) throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException {
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing().modernKeyRing(userId); PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing().modernKeyRing(userId, null);
return secretKeys; return secretKeys;
} }