Skip to content

Commit

Permalink
Add pgpainless_signer module containing implementations of SignatureP…
Browse files Browse the repository at this point in the history
…rocessors and SignerCreators
  • Loading branch information
vanitasvitae committed Apr 3, 2024
1 parent 77d1ef0 commit d034c72
Show file tree
Hide file tree
Showing 9 changed files with 715 additions and 0 deletions.
83 changes: 83 additions & 0 deletions pgpainless_signer/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.eclipse.packager</groupId>
<artifactId>packager</artifactId>
<version>0.20.1-SNAPSHOT</version>
</parent>

<artifactId>packager-pgpainless_signer</artifactId>
<name>Eclipse Packager :: PGPainless Signer</name>

<dependencies>
<dependency>
<groupId>org.eclipse.packager</groupId>
<artifactId>packager-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.packager</groupId>
<artifactId>packager-rpm</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.pgpainless</groupId>
<artifactId>pgpainless-core</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>org.tukaani</groupId>
<artifactId>xz</artifactId>
</dependency>
<!-- enable to have Zstd support on local runs
<dependency>
<groupId>com.github.luben</groupId>
<artifactId>zstd-jni</artifactId>
<version>1.5.2-5</version>
<optional>true</optional>
</dependency>
-->

<!-- testing -->

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<scope>test</scope>
</dependency>
<!-- show log output during tests using logback as the slf4j backend. -->
<!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright (c) 2024 Paul Schaub
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/

package org.eclipse.packager.rpm.signature.pgpainless;

import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSignature;
import org.eclipse.packager.rpm.RpmSignatureTag;
import org.eclipse.packager.rpm.header.Header;
import org.pgpainless.encryption_signing.EncryptionResult;
import org.pgpainless.key.protection.SecretKeyRingProtector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.nio.ByteBuffer;

public class PGPainlessHeaderSignatureProcessor extends PGPainlessSignatureProcessor {

private final Logger logger = LoggerFactory.getLogger(PGPainlessHeaderSignatureProcessor.class);

public PGPainlessHeaderSignatureProcessor(PGPSecretKeyRing key, SecretKeyRingProtector keyProtector, int hashAlgorithm) {
super(key, keyProtector, hashAlgorithm);
}

@Override
public Logger getLogger() {
return logger;
}

@Override
public void feedPayloadData(ByteBuffer data) {
// We only work on header data
}

@Override
public void finish(Header<RpmSignatureTag> signature) {
try {
signingStream.close();
EncryptionResult result = signingStream.getResult();
PGPSignature pgpSignature = result.getDetachedSignatures().flatten().iterator().next();
byte[] value = pgpSignature.getEncoded();
switch (pgpSignature.getKeyAlgorithm()) {
// RSA
case PublicKeyAlgorithmTags.RSA_GENERAL: // 1
getLogger().info("RSA HEADER: {}", value);
signature.putBlob(RpmSignatureTag.RSAHEADER, value);
break;

// DSA
case PublicKeyAlgorithmTags.DSA: // 17
case PublicKeyAlgorithmTags.EDDSA_LEGACY: // 22
getLogger().info("DSA HEADER: {}", value);
signature.putBlob(RpmSignatureTag.DSAHEADER, value);
break;

default:
throw new RuntimeException("Unsupported public key algorithm id: " + pgpSignature.getKeyAlgorithm());
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
* Copyright (c) 2024 Paul Schaub
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/

package org.eclipse.packager.rpm.signature.pgpainless;

import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSignature;
import org.eclipse.packager.rpm.RpmSignatureTag;
import org.eclipse.packager.rpm.header.Header;
import org.eclipse.packager.rpm.signature.SignatureProcessor;
import org.pgpainless.PGPainless;
import org.pgpainless.algorithm.CompressionAlgorithm;
import org.pgpainless.algorithm.HashAlgorithm;
import org.pgpainless.encryption_signing.EncryptionResult;
import org.pgpainless.encryption_signing.EncryptionStream;
import org.pgpainless.encryption_signing.ProducerOptions;
import org.pgpainless.encryption_signing.SigningOptions;
import org.pgpainless.key.protection.SecretKeyRingProtector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;

public class PGPainlessSignatureProcessor implements SignatureProcessor {

protected final EncryptionStream signingStream;
private final Logger logger = LoggerFactory.getLogger(PGPainlessSignatureProcessor.class);

public PGPainlessSignatureProcessor(PGPSecretKeyRing key, SecretKeyRingProtector keyProtector, int hashAlgorithm) {
OutputStream sink = new OutputStream() {
@Override
public void write(int i) {
// Discard plaintext
}
};
try {
signingStream = PGPainless.encryptAndOrSign()
.onOutputStream(sink)
.withOptions(
ProducerOptions.sign(
SigningOptions.get()
.overrideHashAlgorithm(HashAlgorithm.requireFromId(hashAlgorithm))
.addDetachedSignature(keyProtector, key)
).setAsciiArmor(false)
.overrideCompressionAlgorithm(CompressionAlgorithm.UNCOMPRESSED)
);
} catch (PGPException | IOException e) {
throw new RuntimeException(e);
}
}

@Override
public void feedHeader(ByteBuffer header) {
feedData(header);
}

@Override
public void feedPayloadData(ByteBuffer data) {
feedData(data);
}

private void feedData(ByteBuffer data) {
try {
if (data.hasArray()) {
signingStream.write(data.array(), data.position(), data.remaining());
} else {
final byte[] buffer = new byte[data.remaining()];
data.get(buffer);
signingStream.write(buffer);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}

protected Logger getLogger() {
return logger;
}

@Override
public void finish(Header<RpmSignatureTag> signature) {
try {
signingStream.close();
EncryptionResult result = signingStream.getResult();
PGPSignature pgpSignature = result.getDetachedSignatures().flatten().iterator().next();
byte[] value = pgpSignature.getEncoded();
switch (pgpSignature.getKeyAlgorithm()) {
// RSA
case PublicKeyAlgorithmTags.RSA_GENERAL: // 1
getLogger().info("RSA: {}", value);
signature.putBlob(RpmSignatureTag.PGP, value);
break;

// DSA
case PublicKeyAlgorithmTags.DSA: // 17
case PublicKeyAlgorithmTags.EDDSA_LEGACY: // 22
getLogger().info("DSA: {}", value);
signature.putBlob(RpmSignatureTag.GPG, value);
break;

default:
throw new RuntimeException("Unsupported public key algorithm id: " + pgpSignature.getKeyAlgorithm());
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright (c) 2024 Paul Schaub
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/

package org.eclipse.packager.rpm.signature.pgpainless;

import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.eclipse.packager.rpm.signature.PgpSignatureProcessorFactory;
import org.eclipse.packager.rpm.signature.SignatureProcessor;
import org.pgpainless.key.protection.SecretKeyRingProtector;
import org.pgpainless.util.Passphrase;

public class PGPainlessSignatureProcessorFactory extends PgpSignatureProcessorFactory {

private final PGPSecretKeyRing key;
private final SecretKeyRingProtector keyProtector;
private final int hashAlgorithm;

public PGPainlessSignatureProcessorFactory(PGPSecretKeyRing key, char[] passphrase, int hashAlgorithm) {
this.key = key;
this.keyProtector = SecretKeyRingProtector.unlockAnyKeyWith(new Passphrase(passphrase));
this.hashAlgorithm = hashAlgorithm;
}

@Override
public SignatureProcessor createHeaderSignatureProcessor() {
return new PGPainlessHeaderSignatureProcessor(key, keyProtector, hashAlgorithm);
}

@Override
public SignatureProcessor createSignatureProcessor() {
return new PGPainlessSignatureProcessor(key, keyProtector, hashAlgorithm);
}
}
Loading

0 comments on commit d034c72

Please sign in to comment.