Skip to content

Commit

Permalink
Now using org.pgpainless for signing
Browse files Browse the repository at this point in the history
Signed-off-by: Phillip Kruger <phillip.kruger@gmail.com>
  • Loading branch information
phillip-kruger committed Mar 13, 2024
1 parent 184705e commit 7b1db5b
Show file tree
Hide file tree
Showing 8 changed files with 164 additions and 72 deletions.
4 changes: 2 additions & 2 deletions .locker/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>io.mvnpm</groupId>
<version>3.0.28-SNAPSHOT</version>
<version>3.0.29-SNAPSHOT</version>
<artifactId>mvnpm-locker</artifactId>
<packaging>pom</packaging>

Expand Down Expand Up @@ -142,7 +142,7 @@
<dependency>
<groupId>org.mvnpm</groupId>
<artifactId>marked</artifactId>
<version>12.0.0</version>
<version>12.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
Expand Down
47 changes: 35 additions & 12 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>io.mvnpm</groupId>
<artifactId>mvnpm</artifactId>
<version>3.0.28-SNAPSHOT</version>
<version>3.0.29-SNAPSHOT</version>
<name>mvnpm</name>
<description>Maven on NPM</description>
<url>https://mvnpm.org/</url>
Expand Down Expand Up @@ -53,15 +53,27 @@
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
<quarkus.platform.version>3.8.1</quarkus.platform.version>
<quarkus.platform.version>3.8.2</quarkus.platform.version>
<skipITs>true</skipITs>
<surefire-plugin.version>3.2.5</surefire-plugin.version>
<formatter.plugin.version>2.23.0</formatter.plugin.version>
<impsort.plugin.version>1.9.0</impsort.plugin.version>
<quarkus.ide-config.version>${quarkus.platform.version}</quarkus.ide-config.version>

<pgpainless.version>1.5.5</pgpainless.version>
<!-- UI Libs -->
<quarkus-web-bundler.version>1.3.1</quarkus-web-bundler.version>
<importmap.version>1.0.10</importmap.version>
<vaadin.version>24.3.7</vaadin.version>
<vaadin-router.version>1.7.5</vaadin-router.version>
<codeblock.version>1.0.6</codeblock.version>
<compare-versions.version>6.1.0</compare-versions.version>
<marked.version>12.0.1</marked.version>
<card.version>1.0.0</card.version>
<badge.version>1.0.0</badge.version>
<!-- Testing-->
<playwright.version>0.0.1</playwright.version>
<esbuild-java.version>1.3.0</esbuild-java.version>
</properties>
<dependencyManagement>
<dependencies>
Expand Down Expand Up @@ -147,6 +159,17 @@
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-postgresql</artifactId>
</dependency>
<!-- To sign the files (PGP) -->
<dependency>
<groupId>org.pgpainless</groupId>
<artifactId>pgpainless-core</artifactId>
<version>${pgpainless.version}</version>
</dependency>
<dependency>
<groupId>org.pgpainless</groupId>
<artifactId>pgpainless-sop</artifactId>
<version>${pgpainless.version}</version>
</dependency>
<!-- To create the importmap -->
<dependency>
<groupId>io.mvnpm</groupId>
Expand All @@ -168,49 +191,49 @@
<dependency>
<groupId>org.mvnpm.at.mvnpm</groupId>
<artifactId>vaadin-webcomponents</artifactId>
<version>24.3.7</version>
<version>${vaadin.version}</version>
<scope>provided</scope>
</dependency>
<!-- Router -->
<dependency>
<groupId>org.mvnpm.at.vaadin</groupId>
<artifactId>router</artifactId>
<version>1.7.5</version>
<version>${vaadin-router.version}</version>
<scope>provided</scope>
</dependency>
<!-- Codemirror -->
<!-- Codeblock -->
<dependency>
<groupId>org.mvnpm.at.mvnpm</groupId>
<artifactId>codeblock</artifactId>
<version>1.0.6</version>
<version>${codeblock.version}</version>
<scope>provided</scope>
</dependency>
<!-- To compare versions -->
<dependency>
<groupId>org.mvnpm</groupId>
<artifactId>compare-versions</artifactId>
<version>6.1.0</version>
<version>${compare-versions.version}</version>
<scope>provided</scope>
</dependency>
<!-- To render Markdown -->
<dependency>
<groupId>org.mvnpm</groupId>
<artifactId>marked</artifactId>
<version>12.0.0</version>
<version>${marked.version}</version>
<scope>provided</scope>
</dependency>
<!-- Cards for display -->
<dependency>
<groupId>org.mvnpm.at.quarkus-webcomponents</groupId>
<artifactId>card</artifactId>
<version>1.0.0</version>
<version>${card.version}</version>
<scope>provided</scope>
</dependency>
<!-- Badges for display -->
<dependency>
<groupId>org.mvnpm.at.quarkus-webcomponents</groupId>
<artifactId>badge</artifactId>
<version>1.0.0</version>
<version>${badge.version}</version>
<scope>provided</scope>
</dependency>
<!-- Testing -->
Expand All @@ -227,13 +250,13 @@
<dependency>
<groupId>io.quarkiverse.playwright</groupId>
<artifactId>quarkus-playwright</artifactId>
<version>0.0.1</version>
<version>${playwright.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.mvnpm</groupId>
<artifactId>esbuild-java</artifactId>
<version>1.2.0</version>
<version>${esbuild-java.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
Expand Down
6 changes: 5 additions & 1 deletion src/main/java/io/mvnpm/composite/CompositeCreator.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import io.mvnpm.file.FileStore;
import io.mvnpm.file.FileType;
import io.mvnpm.file.FileUtil;
import io.mvnpm.file.KeyHolder;
import io.mvnpm.importmap.Aggregator;
import io.mvnpm.importmap.ImportsDataBinding;
import io.mvnpm.maven.MavenRepositoryService;
Expand Down Expand Up @@ -75,6 +76,9 @@ public class CompositeCreator {
@Inject
NpmRegistryFacade npmRegistryFacade;

@Inject
KeyHolder keyHolder;

private final MavenXpp3Reader mavenXpp3Writer = new MavenXpp3Reader();

public void buildAllComposites() {
Expand Down Expand Up @@ -353,7 +357,7 @@ private void mergeSource(Model pom, List<Dependency> dependencies) throws IOExce
}
// Also create the Sha1
FileUtil.createSha1(outputJar);
FileUtil.createAsc(outputJar);
FileUtil.createAsc(keyHolder.getSecretKeyRing(), outputJar);
FileUtil.createMd5(outputJar);
}
}
Expand Down
55 changes: 30 additions & 25 deletions src/main/java/io/mvnpm/file/FileUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,15 @@

import jakarta.ws.rs.core.StreamingOutput;

import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.pgpainless.sop.SOPImpl;

import io.mvnpm.Constants;
import io.quarkus.logging.Log;
import sop.ByteArrayAndResult;
import sop.ReadyWithResult;
import sop.SOP;
import sop.SigningResult;

public class FileUtil {

Expand Down Expand Up @@ -151,34 +158,34 @@ private static String getMd5(java.io.InputStream inputStream) {
}
}

public static boolean createAsc(Path localFilePath) {
return FileUtil.createAsc(localFilePath, false);
}
public static boolean createAsc(PGPSecretKeyRing secretKeyRing, Path localFilePath) {
if (secretKeyRing != null) {
String outputFile = localFilePath.toString() + Constants.DOT_ASC;
Path ascFileOutput = Paths.get(outputFile);
synchronized (ascFileOutput) {
if (!Files.exists(ascFileOutput)) {

try {
byte[] jarFileBytes = Files.readAllBytes(localFilePath);

SOP sop = new SOPImpl();
ReadyWithResult<SigningResult> readyWithResult = sop.detachedSign()
.key(secretKeyRing.getSecretKey().getEncoded())
.data(jarFileBytes);

public static boolean createAsc(Path localFilePath, boolean force) {
String outputFile = localFilePath.toString() + Constants.DOT_ASC;
Path f = Paths.get(outputFile);
synchronized (f) {
if (!Files.exists(f) || force) {
try {
Process process = Runtime.getRuntime().exec(GPG_COMMAND + localFilePath.toString());
// Set a timeout of 10 seconds
long timeout = 20;
boolean processFinished = process.waitFor(timeout, TimeUnit.SECONDS);

if (!processFinished) {
process.destroy(); // If the process doesn't finish, we can destroy it
ByteArrayAndResult<SigningResult> bytesAndResult = readyWithResult.toByteArrayAndResult();

byte[] detachedSignature = bytesAndResult.getBytes();

Files.write(ascFileOutput, detachedSignature);
} catch (IOException e) {
e.printStackTrace();
return false;
} else {
// Process finished within the timeout
int exitCode = process.exitValue();
process.destroy();
return true;
}
} catch (InterruptedException | IOException ex) {
throw new IllegalStateException(ex);
}
}
} else {
return false;
}
return true;
}
Expand All @@ -194,6 +201,4 @@ private static byte[] getMessageDigest(InputStream inputStream, String algorithm

return md.digest();
}

private static final String GPG_COMMAND = "gpg -ab ";
}
51 changes: 51 additions & 0 deletions src/main/java/io/mvnpm/file/KeyHolder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package io.mvnpm.file;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Optional;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Observes;

import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.pgpainless.PGPainless;

import io.quarkus.runtime.StartupEvent;

/**
* Holds the key for signing the files
*
* @author Phillip Kruger (phillip.kruger@gmail.com)
*/
@ApplicationScoped
public class KeyHolder {

private PGPSecretKeyRing secretKeyRing = null;

@ConfigProperty(name = "mvnpm.asckey.path")
Optional<String> asckeyPath;

void onStart(@Observes StartupEvent ev) {
if (asckeyPath.isPresent()) {
try {
Path keyFilePath = Paths.get(asckeyPath.get());
byte[] keyBytes = Files.readAllBytes(keyFilePath);
this.secretKeyRing = PGPainless.readKeyRing().secretKeyRing(keyBytes);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
}

public PGPSecretKeyRing getSecretKeyRing() {
return this.secretKeyRing;
}

public boolean hasSecretKeyRing() {
return this.secretKeyRing != null;
}

}
56 changes: 30 additions & 26 deletions src/main/java/io/mvnpm/file/type/JarClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
import org.apache.commons.compress.utils.IOUtils;
import org.apache.commons.io.IOUtils;

import io.mvnpm.Constants;
import io.mvnpm.file.FileStore;
Expand Down Expand Up @@ -114,10 +114,10 @@ private void tgzToJar(io.mvnpm.npm.model.Package p, Path tgzPath, JarArchiveOutp

try (InputStream tgzInputStream = Files.newInputStream(tgzPath);
GzipCompressorInputStream gzipInputStream = new GzipCompressorInputStream(tgzInputStream);
TarArchiveInputStream tarArchiveInputStream = new TarArchiveInputStream(gzipInputStream);) {
TarArchiveInputStream tarArchiveInputStream = new TarArchiveInputStream(gzipInputStream)) {
final Map<String, byte[]> toTgz = new LinkedHashMap<>();
for (TarArchiveEntry entry = tarArchiveInputStream.getNextTarEntry(); entry != null; entry = tarArchiveInputStream
.getNextTarEntry()) {
for (TarArchiveEntry entry = tarArchiveInputStream.getNextEntry(); entry != null; entry = tarArchiveInputStream
.getNextEntry()) {
tgzEntryToJarEntry(p, entry, tarArchiveInputStream, toTgz, jarOutput);
}
if (!toTgz.isEmpty()) {
Expand All @@ -135,33 +135,37 @@ private void tgzEntryToJarEntry(io.mvnpm.npm.model.Package p, ArchiveEntry entry
String name = entry.getName();
final boolean shouldAdd = !matches(FILES_TO_EXCLUDE, name);
final boolean shouldTgz = matches(FILES_TO_TGZ, name);
if (shouldAdd || shouldTgz) {
name = name.replaceFirst(NPM_ROOT, Constants.EMPTY);
// do not add entries that will result in invalid zip file systems that will not be able to be opened
// by quarkus because it uses the ZipFileSystem implementation.
final String jarEntryPath = MVN_ROOT + importMapRoot + name;
final String tarEntryPath = importMapRoot + name;
// paths that include "/./" or "/../" as path element are invalid
if (jarEntryPath.startsWith("./") || jarEntryPath.contains("/./")
|| (shouldTgz && (tarEntryPath.startsWith(".") || tarEntryPath.contains("/./")))) {
return;
}

try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
BufferedOutputStream bos = new BufferedOutputStream(baos, bufferSize)) {
IOUtils.copy(tar, bos, bufferSize);
bos.flush();
baos.flush();
if (shouldAdd) {
writeJarEntry(jarOutput, jarEntryPath, baos.toByteArray());
} else {
// We don't add the META-INF because the tgz is already in META-INF
toTgz.put("resources" + importMapRoot + name, baos.toByteArray());
}
name = name.replaceFirst(NPM_ROOT, Constants.EMPTY);
// do not add entries that will result in invalid zip file systems that will not be able to be opened
// by quarkus because it uses the ZipFileSystem implementation.
final String jarEntryPath = MVN_ROOT + importMapRoot + name;
final String tarEntryPath = importMapRoot + name;
final boolean isRelativeLink = isRelativeLink(jarEntryPath, tarEntryPath, shouldTgz);

try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
BufferedOutputStream bos = new BufferedOutputStream(baos, bufferSize)) {
IOUtils.copy(tar, bos, bufferSize);
bos.flush();
baos.flush();
if (shouldAdd && !isRelativeLink) {
writeJarEntry(jarOutput, jarEntryPath, baos.toByteArray());
} else if (shouldTgz && !isRelativeLink) {
// We don't add the META-INF because the tgz is already in META-INF
toTgz.put("resources" + importMapRoot + name, baos.toByteArray());
}
}
}

private boolean isRelativeLink(final String jarEntryPath, final String tarEntryPath, final boolean shouldTgz) {
// paths that include "/./" or "/../" as path element are invalid
if (jarEntryPath.startsWith("./") || jarEntryPath.contains("/./")
|| (shouldTgz && (tarEntryPath.startsWith(".") || tarEntryPath.contains("/./")))) {
return true;
}
return false;
}

private byte[] tarGz(Map<String, byte[]> toCompress) throws IOException {
// Step 1, 2 and 3: Create tar archive from map
ByteArrayOutputStream tarOutput = new ByteArrayOutputStream();
Expand Down
Loading

0 comments on commit 7b1db5b

Please sign in to comment.