mirror of
https://github.com/pgpainless/pgpainless.git
synced 2025-09-09 18:29:39 +02:00
Moved sop-java and sop-java-picocli to its own repositories
See https://github.com/pgpainless/sop-java See https://codeberg.org/PGPainless/sop-java
This commit is contained in:
parent
9800ca8bd4
commit
c6bc8f9774
82 changed files with 11 additions and 5226 deletions
|
@ -1,80 +1 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
|
||||
# SOP-Java
|
||||
|
||||
[](https://datatracker.ietf.org/doc/html/draft-dkg-openpgp-stateless-cli-03)
|
||||
[](https://search.maven.org/artifact/org.pgpainless/sop-java)
|
||||
[](https://pgpainless.org/releases/latest/javadoc/sop/SOP.html)
|
||||
[](https://api.reuse.software/info/github.com/pgpainless/pgpainless)
|
||||
|
||||
Stateless OpenPGP Protocol for Java.
|
||||
|
||||
This module contains interfaces that model the API described by the
|
||||
[Stateless OpenPGP Command Line Interface](https://datatracker.ietf.org/doc/html/draft-dkg-openpgp-stateless-cli-03) specification.
|
||||
|
||||
This module is not a command line application! For that, see `sop-java-picocli`.
|
||||
|
||||
## Usage Examples
|
||||
|
||||
The API defined by `sop-java` is super straight forward:
|
||||
```java
|
||||
SOP sop = ... // e.g. new org.pgpainless.sop.SOPImpl();
|
||||
|
||||
// Generate an OpenPGP key
|
||||
byte[] key = sop.generateKey()
|
||||
.userId("Alice <alice@example.org>")
|
||||
.generate()
|
||||
.getBytes();
|
||||
|
||||
// Extract the certificate (public key)
|
||||
byte[] cert = sop.extractCert()
|
||||
.key(key)
|
||||
.getBytes();
|
||||
|
||||
// Encrypt a message
|
||||
byte[] message = ...
|
||||
byte[] encrypted = sop.encrypt()
|
||||
.withCert(cert)
|
||||
.signWith(key)
|
||||
.plaintext(message)
|
||||
.getBytes();
|
||||
|
||||
// Decrypt a message
|
||||
ByteArrayAndResult<DecryptionResult> messageAndVerifications = sop.decrypt()
|
||||
.verifyWith(cert)
|
||||
.withKey(key)
|
||||
.ciphertext(encrypted)
|
||||
.toByteArrayAndResult();
|
||||
byte[] decrypted = messageAndVerifications.getBytes();
|
||||
// Signature Verifications
|
||||
DecryptionResult messageInfo = messageAndVerifications.getResult();
|
||||
List<Verification> signatureVerifications = messageInfo.getVerifications();
|
||||
```
|
||||
|
||||
Furthermore, the API is capable of signing messages and verifying unencrypted signed data, as well as adding and removing ASCII armor.
|
||||
|
||||
### Limitations
|
||||
As per the spec, sop-java does not (yet) deal with encrypted OpenPGP keys.
|
||||
|
||||
## Why should I use this?
|
||||
|
||||
If you need to use OpenPGP functionality like encrypting/decrypting messages, or creating/verifying
|
||||
signatures inside your application, you probably don't want to start from scratch and instead reuse some library.
|
||||
|
||||
Instead of locking yourselves in by depending hard on that one library, you can simply depend on the interfaces from
|
||||
`sop-java` and plug in a library (such as `pgpainless-sop`) that implements said interfaces.
|
||||
|
||||
That way you don't make yourself dependent from a single OpenPGP library and stay flexible.
|
||||
Should another library emerge, that better suits your needs (and implements `sop-java`), you can easily switch
|
||||
by swapping out the dependency with minimal changes to your code.
|
||||
|
||||
## Why should I *implement* this?
|
||||
|
||||
Did you create an [OpenPGP](https://datatracker.ietf.org/doc/html/rfc4880) implementation that can be used in the Java ecosystem?
|
||||
By implementing the `sop-java` interface, you can turn your library into a command line interface (see `sop-java-picocli`).
|
||||
This allows you to plug your library into the [OpenPGP interoperability test suite](https://tests.sequoia-pgp.org/)
|
||||
of the [Sequoia-PGP](https://sequoia-pgp.org/) project.
|
||||
# [MOVED](https://github.com/pgpainless/sop-java/tree/master/sop-java)
|
|
@ -1,22 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
plugins {
|
||||
id 'java'
|
||||
}
|
||||
|
||||
group 'org.pgpainless'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion"
|
||||
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion"
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sop;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Tuple of a byte array and associated result object.
|
||||
* @param <T> type of result
|
||||
*/
|
||||
public class ByteArrayAndResult<T> {
|
||||
|
||||
private final byte[] bytes;
|
||||
private final T result;
|
||||
|
||||
public ByteArrayAndResult(byte[] bytes, T result) {
|
||||
this.bytes = bytes;
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the byte array part.
|
||||
*
|
||||
* @return bytes
|
||||
*/
|
||||
public byte[] getBytes() {
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the result part.
|
||||
*
|
||||
* @return result
|
||||
*/
|
||||
public T getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the byte array part as an {@link InputStream}.
|
||||
*
|
||||
* @return input stream
|
||||
*/
|
||||
public InputStream getInputStream() {
|
||||
return new ByteArrayInputStream(getBytes());
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sop;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import sop.util.Optional;
|
||||
|
||||
public class DecryptionResult {
|
||||
|
||||
private final Optional<SessionKey> sessionKey;
|
||||
private final List<Verification> verifications;
|
||||
|
||||
public DecryptionResult(SessionKey sessionKey, List<Verification> verifications) {
|
||||
this.sessionKey = Optional.ofNullable(sessionKey);
|
||||
this.verifications = Collections.unmodifiableList(verifications);
|
||||
}
|
||||
|
||||
public Optional<SessionKey> getSessionKey() {
|
||||
return sessionKey;
|
||||
}
|
||||
|
||||
public List<Verification> getVerifications() {
|
||||
return new ArrayList<>(verifications);
|
||||
}
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sop;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
public class MicAlg {
|
||||
|
||||
private final String micAlg;
|
||||
|
||||
public MicAlg(String micAlg) {
|
||||
if (micAlg == null) {
|
||||
throw new IllegalArgumentException("MicAlg String cannot be null.");
|
||||
}
|
||||
this.micAlg = micAlg;
|
||||
}
|
||||
|
||||
public static MicAlg empty() {
|
||||
return new MicAlg("");
|
||||
}
|
||||
|
||||
public static MicAlg fromHashAlgorithmId(int id) {
|
||||
switch (id) {
|
||||
case 1:
|
||||
return new MicAlg("pgp-md5");
|
||||
case 2:
|
||||
return new MicAlg("pgp-sha1");
|
||||
case 3:
|
||||
return new MicAlg("pgp-ripemd160");
|
||||
case 8:
|
||||
return new MicAlg("pgp-sha256");
|
||||
case 9:
|
||||
return new MicAlg("pgp-sha384");
|
||||
case 10:
|
||||
return new MicAlg("pgp-sha512");
|
||||
case 11:
|
||||
return new MicAlg("pgp-sha224");
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported hash algorithm ID: " + id);
|
||||
}
|
||||
}
|
||||
|
||||
public String getMicAlg() {
|
||||
return micAlg;
|
||||
}
|
||||
|
||||
public void writeTo(OutputStream outputStream) {
|
||||
PrintWriter pw = new PrintWriter(outputStream);
|
||||
pw.write(getMicAlg());
|
||||
pw.close();
|
||||
}
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sop;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public abstract class Ready {
|
||||
|
||||
/**
|
||||
* Write the data to the provided output stream.
|
||||
*
|
||||
* @param outputStream output stream
|
||||
* @throws IOException in case of an IO error
|
||||
*/
|
||||
public abstract void writeTo(OutputStream outputStream) throws IOException;
|
||||
|
||||
/**
|
||||
* Return the data as a byte array by writing it to a {@link ByteArrayOutputStream} first and then returning
|
||||
* the array.
|
||||
*
|
||||
* @return data as byte array
|
||||
* @throws IOException in case of an IO error
|
||||
*/
|
||||
public byte[] getBytes() throws IOException {
|
||||
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||
writeTo(bytes);
|
||||
return bytes.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an input stream containing the data.
|
||||
*
|
||||
* @return input stream
|
||||
* @throws IOException in case of an IO error
|
||||
*/
|
||||
public InputStream getInputStream() throws IOException {
|
||||
return new ByteArrayInputStream(getBytes());
|
||||
}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sop;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import sop.exception.SOPGPException;
|
||||
|
||||
public abstract class ReadyWithResult<T> {
|
||||
|
||||
/**
|
||||
* Write the data e.g. decrypted plaintext to the provided output stream and return the result of the
|
||||
* processing operation.
|
||||
*
|
||||
* @param outputStream output stream
|
||||
* @return result, eg. signatures
|
||||
*
|
||||
* @throws IOException in case of an IO error
|
||||
* @throws SOPGPException.NoSignature if there are no valid signatures found
|
||||
*/
|
||||
public abstract T writeTo(OutputStream outputStream) throws IOException, SOPGPException.NoSignature;
|
||||
|
||||
/**
|
||||
* Return the data as a {@link ByteArrayAndResult}.
|
||||
* Calling {@link ByteArrayAndResult#getBytes()} will give you access to the data as byte array, while
|
||||
* {@link ByteArrayAndResult#getResult()} will grant access to the appended result.
|
||||
*
|
||||
* @return byte array and result
|
||||
* @throws IOException in case of an IO error
|
||||
* @throws SOPGPException.NoSignature if there are no valid signatures found
|
||||
*/
|
||||
public ByteArrayAndResult<T> toByteArrayAndResult() throws IOException, SOPGPException.NoSignature {
|
||||
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||
T result = writeTo(bytes);
|
||||
return new ByteArrayAndResult<>(bytes.toByteArray(), result);
|
||||
}
|
||||
}
|
|
@ -1,95 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sop;
|
||||
|
||||
import sop.operation.Armor;
|
||||
import sop.operation.Dearmor;
|
||||
import sop.operation.Decrypt;
|
||||
import sop.operation.DetachInbandSignatureAndMessage;
|
||||
import sop.operation.Encrypt;
|
||||
import sop.operation.ExtractCert;
|
||||
import sop.operation.GenerateKey;
|
||||
import sop.operation.Sign;
|
||||
import sop.operation.Verify;
|
||||
import sop.operation.Version;
|
||||
|
||||
/**
|
||||
* Stateless OpenPGP Interface.
|
||||
*/
|
||||
public interface SOP {
|
||||
|
||||
/**
|
||||
* Get information about the implementations name and version.
|
||||
*
|
||||
* @return version
|
||||
*/
|
||||
Version version();
|
||||
|
||||
/**
|
||||
* Generate a secret key.
|
||||
* Customize the operation using the builder {@link GenerateKey}.
|
||||
*
|
||||
* @return builder instance
|
||||
*/
|
||||
GenerateKey generateKey();
|
||||
|
||||
/**
|
||||
* Extract a certificate (public key) from a secret key.
|
||||
* Customize the operation using the builder {@link ExtractCert}.
|
||||
*
|
||||
* @return builder instance
|
||||
*/
|
||||
ExtractCert extractCert();
|
||||
|
||||
/**
|
||||
* Create detached signatures.
|
||||
* Customize the operation using the builder {@link Sign}.
|
||||
*
|
||||
* @return builder instance
|
||||
*/
|
||||
Sign sign();
|
||||
|
||||
/**
|
||||
* Verify detached signatures.
|
||||
* Customize the operation using the builder {@link Verify}.
|
||||
*
|
||||
* @return builder instance
|
||||
*/
|
||||
Verify verify();
|
||||
|
||||
/**
|
||||
* Encrypt a message.
|
||||
* Customize the operation using the builder {@link Encrypt}.
|
||||
*
|
||||
* @return builder instance
|
||||
*/
|
||||
Encrypt encrypt();
|
||||
|
||||
/**
|
||||
* Decrypt a message.
|
||||
* Customize the operation using the builder {@link Decrypt}.
|
||||
*
|
||||
* @return builder instance
|
||||
*/
|
||||
Decrypt decrypt();
|
||||
|
||||
/**
|
||||
* Convert binary OpenPGP data to ASCII.
|
||||
* Customize the operation using the builder {@link Armor}.
|
||||
*
|
||||
* @return builder instance
|
||||
*/
|
||||
Armor armor();
|
||||
|
||||
/**
|
||||
* Converts ASCII armored OpenPGP data to binary.
|
||||
* Customize the operation using the builder {@link Dearmor}.
|
||||
*
|
||||
* @return builder instance
|
||||
*/
|
||||
Dearmor dearmor();
|
||||
|
||||
DetachInbandSignatureAndMessage detachInbandSignatureAndMessage();
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sop;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import sop.util.HexUtil;
|
||||
|
||||
public class SessionKey {
|
||||
|
||||
private static final Pattern PATTERN = Pattern.compile("^(\\d):([0-9a-fA-F]+)$");
|
||||
|
||||
private final byte algorithm;
|
||||
private final byte[] sessionKey;
|
||||
|
||||
public SessionKey(byte algorithm, byte[] sessionKey) {
|
||||
this.algorithm = algorithm;
|
||||
this.sessionKey = sessionKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the symmetric algorithm octet.
|
||||
*
|
||||
* @return algorithm id
|
||||
*/
|
||||
public byte getAlgorithm() {
|
||||
return algorithm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the session key.
|
||||
*
|
||||
* @return session key
|
||||
*/
|
||||
public byte[] getKey() {
|
||||
return sessionKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getAlgorithm() * 17 + Arrays.hashCode(getKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (other == null) {
|
||||
return false;
|
||||
}
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof SessionKey)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SessionKey otherKey = (SessionKey) other;
|
||||
return getAlgorithm() == otherKey.getAlgorithm() && Arrays.equals(getKey(), otherKey.getKey());
|
||||
}
|
||||
|
||||
public static SessionKey fromString(String string) {
|
||||
Matcher matcher = PATTERN.matcher(string);
|
||||
if (!matcher.matches()) {
|
||||
throw new IllegalArgumentException("Provided session key does not match expected format.");
|
||||
}
|
||||
byte algorithm = Byte.parseByte(matcher.group(1));
|
||||
String key = matcher.group(2);
|
||||
|
||||
return new SessionKey(algorithm, HexUtil.hexToBytes(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "" + (int) getAlgorithm() + ':' + HexUtil.bytesToHex(sessionKey);
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sop;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public abstract class Signatures extends Ready {
|
||||
|
||||
/**
|
||||
* Write OpenPGP signatures to the provided output stream.
|
||||
*
|
||||
* @param signatureOutputStream output stream
|
||||
* @throws IOException in case of an IO error
|
||||
*/
|
||||
@Override
|
||||
public abstract void writeTo(OutputStream signatureOutputStream) throws IOException;
|
||||
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sop;
|
||||
|
||||
/**
|
||||
* This class contains various information about a signed message.
|
||||
*/
|
||||
public final class SigningResult {
|
||||
|
||||
private final MicAlg micAlg;
|
||||
|
||||
private SigningResult(MicAlg micAlg) {
|
||||
this.micAlg = micAlg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a string identifying the digest mechanism used to create the signed message.
|
||||
* This is useful for setting the micalg= parameter for the multipart/signed
|
||||
* content type of a PGP/MIME object as described in section 5 of [RFC3156].
|
||||
*
|
||||
* If more than one signature was generated and different digest mechanisms were used,
|
||||
* the value of the micalg object is an empty string.
|
||||
*
|
||||
* @return micalg
|
||||
*/
|
||||
public MicAlg getMicAlg() {
|
||||
return micAlg;
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private MicAlg micAlg;
|
||||
|
||||
public Builder setMicAlg(MicAlg micAlg) {
|
||||
this.micAlg = micAlg;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SigningResult build() {
|
||||
SigningResult signingResult = new SigningResult(micAlg);
|
||||
return signingResult;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sop;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import sop.util.UTCUtil;
|
||||
|
||||
public class Verification {
|
||||
|
||||
private final Date creationTime;
|
||||
private final String signingKeyFingerprint;
|
||||
private final String signingCertFingerprint;
|
||||
|
||||
public Verification(Date creationTime, String signingKeyFingerprint, String signingCertFingerprint) {
|
||||
this.creationTime = creationTime;
|
||||
this.signingKeyFingerprint = signingKeyFingerprint;
|
||||
this.signingCertFingerprint = signingCertFingerprint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the signatures' creation time.
|
||||
*
|
||||
* @return signature creation time
|
||||
*/
|
||||
public Date getCreationTime() {
|
||||
return creationTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the fingerprint of the signing (sub)key.
|
||||
*
|
||||
* @return signing key fingerprint
|
||||
*/
|
||||
public String getSigningKeyFingerprint() {
|
||||
return signingKeyFingerprint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the fingerprint fo the signing certificate.
|
||||
*
|
||||
* @return signing certificate fingerprint
|
||||
*/
|
||||
public String getSigningCertFingerprint() {
|
||||
return signingCertFingerprint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return UTCUtil.formatUTCDate(getCreationTime()) +
|
||||
' ' +
|
||||
getSigningKeyFingerprint() +
|
||||
' ' +
|
||||
getSigningCertFingerprint();
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sop.enums;
|
||||
|
||||
public enum ArmorLabel {
|
||||
Auto,
|
||||
Sig,
|
||||
Key,
|
||||
Cert,
|
||||
Message
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sop.enums;
|
||||
|
||||
public enum EncryptAs {
|
||||
Binary,
|
||||
Text,
|
||||
MIME
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sop.enums;
|
||||
|
||||
public enum SignAs {
|
||||
Binary,
|
||||
Text
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/**
|
||||
* Stateless OpenPGP Interface for Java.
|
||||
* Enumerations.
|
||||
*/
|
||||
package sop.enums;
|
|
@ -1,316 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sop.exception;
|
||||
|
||||
public abstract class SOPGPException extends RuntimeException {
|
||||
|
||||
public SOPGPException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public SOPGPException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public SOPGPException(Throwable e) {
|
||||
super(e);
|
||||
}
|
||||
|
||||
public SOPGPException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public abstract int getExitCode();
|
||||
|
||||
/**
|
||||
* No acceptable signatures found (sop verify).
|
||||
*/
|
||||
public static class NoSignature extends SOPGPException {
|
||||
|
||||
public static final int EXIT_CODE = 3;
|
||||
|
||||
public NoSignature() {
|
||||
super("No verifiable signature found.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getExitCode() {
|
||||
return EXIT_CODE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asymmetric algorithm unsupported (sop encrypt).
|
||||
*/
|
||||
public static class UnsupportedAsymmetricAlgo extends SOPGPException {
|
||||
|
||||
public static final int EXIT_CODE = 13;
|
||||
|
||||
public UnsupportedAsymmetricAlgo(String message, Throwable e) {
|
||||
super(message, e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getExitCode() {
|
||||
return EXIT_CODE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Certificate not encryption capable (e,g, expired, revoked, unacceptable usage).
|
||||
*/
|
||||
public static class CertCannotEncrypt extends SOPGPException {
|
||||
public static final int EXIT_CODE = 17;
|
||||
|
||||
public CertCannotEncrypt(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getExitCode() {
|
||||
return EXIT_CODE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Missing required argument.
|
||||
*/
|
||||
public static class MissingArg extends SOPGPException {
|
||||
|
||||
public static final int EXIT_CODE = 19;
|
||||
|
||||
public MissingArg(String s) {
|
||||
super(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getExitCode() {
|
||||
return EXIT_CODE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Incomplete verification instructions (sop decrypt).
|
||||
*/
|
||||
public static class IncompleteVerification extends SOPGPException {
|
||||
|
||||
public static final int EXIT_CODE = 23;
|
||||
|
||||
public IncompleteVerification(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getExitCode() {
|
||||
return EXIT_CODE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unable to decrypt (sop decrypt).
|
||||
*/
|
||||
public static class CannotDecrypt extends SOPGPException {
|
||||
|
||||
public static final int EXIT_CODE = 29;
|
||||
|
||||
@Override
|
||||
public int getExitCode() {
|
||||
return EXIT_CODE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Non-UTF-8 or otherwise unreliable password (sop encrypt).
|
||||
*/
|
||||
public static class PasswordNotHumanReadable extends SOPGPException {
|
||||
|
||||
public static final int EXIT_CODE = 31;
|
||||
|
||||
@Override
|
||||
public int getExitCode() {
|
||||
return EXIT_CODE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsupported option.
|
||||
*/
|
||||
public static class UnsupportedOption extends SOPGPException {
|
||||
|
||||
public static final int EXIT_CODE = 37;
|
||||
|
||||
public UnsupportedOption(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public UnsupportedOption(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getExitCode() {
|
||||
return EXIT_CODE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalid data type (no secret key where KEYS expected, etc.).
|
||||
*/
|
||||
public static class BadData extends SOPGPException {
|
||||
|
||||
public static final int EXIT_CODE = 41;
|
||||
|
||||
public BadData(Throwable e) {
|
||||
super(e);
|
||||
}
|
||||
|
||||
public BadData(String message, BadData badData) {
|
||||
super(message, badData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getExitCode() {
|
||||
return EXIT_CODE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Non-Text input where text expected.
|
||||
*/
|
||||
public static class ExpectedText extends SOPGPException {
|
||||
|
||||
public static final int EXIT_CODE = 53;
|
||||
|
||||
@Override
|
||||
public int getExitCode() {
|
||||
return EXIT_CODE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Output file already exists.
|
||||
*/
|
||||
public static class OutputExists extends SOPGPException {
|
||||
|
||||
public static final int EXIT_CODE = 59;
|
||||
|
||||
public OutputExists(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getExitCode() {
|
||||
return EXIT_CODE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Input file does not exist.
|
||||
*/
|
||||
public static class MissingInput extends SOPGPException {
|
||||
|
||||
public static final int EXIT_CODE = 61;
|
||||
|
||||
public MissingInput(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getExitCode() {
|
||||
return EXIT_CODE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A KEYS input is protected (locked) with a password, and sop cannot unlock it.
|
||||
*/
|
||||
public static class KeyIsProtected extends SOPGPException {
|
||||
|
||||
public static final int EXIT_CODE = 67;
|
||||
|
||||
public KeyIsProtected() {
|
||||
super();
|
||||
}
|
||||
|
||||
public KeyIsProtected(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getExitCode() {
|
||||
return EXIT_CODE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsupported subcommand.
|
||||
*/
|
||||
public static class UnsupportedSubcommand extends SOPGPException {
|
||||
|
||||
public static final int EXIT_CODE = 69;
|
||||
|
||||
public UnsupportedSubcommand(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getExitCode() {
|
||||
return EXIT_CODE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An indirect parameter is a special designator (it starts with @), but sop does not know how to handle the prefix.
|
||||
*/
|
||||
public static class UnsupportedSpecialPrefix extends SOPGPException {
|
||||
|
||||
public static final int EXIT_CODE = 71;
|
||||
|
||||
@Override
|
||||
public int getExitCode() {
|
||||
return EXIT_CODE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A indirect input parameter is a special designator (it starts with @),
|
||||
* and a filename matching the designator is actually present.
|
||||
*/
|
||||
public static class AmbiguousInput extends SOPGPException {
|
||||
|
||||
public static final int EXIT_CODE = 73;
|
||||
|
||||
public AmbiguousInput(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getExitCode() {
|
||||
return EXIT_CODE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Key not signature-capable (e.g. expired, revoked, unacceptable usage flags)
|
||||
* (sop sign and sop encrypt with --sign-with).
|
||||
*/
|
||||
public static class KeyCannotSign extends SOPGPException {
|
||||
|
||||
public static final int EXIT_CODE = 79;
|
||||
|
||||
public KeyCannotSign() {
|
||||
super();
|
||||
}
|
||||
|
||||
public KeyCannotSign(String s, KeyCannotSign keyCannotSign) {
|
||||
super(s, keyCannotSign);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getExitCode() {
|
||||
return EXIT_CODE;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/**
|
||||
* Stateless OpenPGP Interface for Java.
|
||||
* Exception classes.
|
||||
*/
|
||||
package sop.exception;
|
|
@ -1,41 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sop.operation;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
|
||||
import sop.Ready;
|
||||
import sop.enums.ArmorLabel;
|
||||
import sop.exception.SOPGPException;
|
||||
|
||||
public interface Armor {
|
||||
|
||||
/**
|
||||
* Overrides automatic detection of label.
|
||||
*
|
||||
* @param label armor label
|
||||
* @return builder instance
|
||||
*/
|
||||
Armor label(ArmorLabel label) throws SOPGPException.UnsupportedOption;
|
||||
|
||||
/**
|
||||
* Armor the provided data.
|
||||
*
|
||||
* @param data input stream of unarmored OpenPGP data
|
||||
* @return armored data
|
||||
*/
|
||||
Ready data(InputStream data) throws SOPGPException.BadData;
|
||||
|
||||
/**
|
||||
* Armor the provided data.
|
||||
*
|
||||
* @param data unarmored OpenPGP data
|
||||
* @return armored data
|
||||
*/
|
||||
default Ready data(byte[] data) throws SOPGPException.BadData {
|
||||
return data(new ByteArrayInputStream(data));
|
||||
}
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sop.operation;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import sop.Ready;
|
||||
import sop.exception.SOPGPException;
|
||||
|
||||
public interface Dearmor {
|
||||
|
||||
/**
|
||||
* Dearmor armored OpenPGP data.
|
||||
*
|
||||
* @param data armored OpenPGP data
|
||||
* @return input stream of unarmored data
|
||||
*/
|
||||
Ready data(InputStream data) throws SOPGPException.BadData, IOException;
|
||||
|
||||
/**
|
||||
* Dearmor armored OpenPGP data.
|
||||
*
|
||||
* @param data armored OpenPGP data
|
||||
* @return input stream of unarmored data
|
||||
*/
|
||||
default Ready data(byte[] data) throws SOPGPException.BadData, IOException {
|
||||
return data(new ByteArrayInputStream(data));
|
||||
}
|
||||
}
|
|
@ -1,118 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sop.operation;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Date;
|
||||
|
||||
import sop.DecryptionResult;
|
||||
import sop.ReadyWithResult;
|
||||
import sop.SessionKey;
|
||||
import sop.exception.SOPGPException;
|
||||
|
||||
public interface Decrypt {
|
||||
|
||||
/**
|
||||
* Makes the SOP consider signatures before this date invalid.
|
||||
*
|
||||
* @param timestamp timestamp
|
||||
* @return builder instance
|
||||
*/
|
||||
Decrypt verifyNotBefore(Date timestamp)
|
||||
throws SOPGPException.UnsupportedOption;
|
||||
|
||||
/**
|
||||
* Makes the SOP consider signatures after this date invalid.
|
||||
*
|
||||
* @param timestamp timestamp
|
||||
* @return builder instance
|
||||
*/
|
||||
Decrypt verifyNotAfter(Date timestamp)
|
||||
throws SOPGPException.UnsupportedOption;
|
||||
|
||||
/**
|
||||
* Adds one or more verification cert.
|
||||
*
|
||||
* @param cert input stream containing the cert(s)
|
||||
* @return builder instance
|
||||
*/
|
||||
Decrypt verifyWithCert(InputStream cert)
|
||||
throws SOPGPException.BadData,
|
||||
IOException;
|
||||
|
||||
/**
|
||||
* Adds one or more verification cert.
|
||||
*
|
||||
* @param cert byte array containing the cert(s)
|
||||
* @return builder instance
|
||||
*/
|
||||
default Decrypt verifyWithCert(byte[] cert)
|
||||
throws SOPGPException.BadData, IOException {
|
||||
return verifyWithCert(new ByteArrayInputStream(cert));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to decrypt with the given session key.
|
||||
*
|
||||
* @param sessionKey session key
|
||||
* @return builder instance
|
||||
*/
|
||||
Decrypt withSessionKey(SessionKey sessionKey)
|
||||
throws SOPGPException.UnsupportedOption;
|
||||
|
||||
/**
|
||||
* Tries to decrypt with the given password.
|
||||
*
|
||||
* @param password password
|
||||
* @return builder instance
|
||||
*/
|
||||
Decrypt withPassword(String password)
|
||||
throws SOPGPException.PasswordNotHumanReadable,
|
||||
SOPGPException.UnsupportedOption;
|
||||
|
||||
/**
|
||||
* Adds one or more decryption key.
|
||||
*
|
||||
* @param key input stream containing the key(s)
|
||||
* @return builder instance
|
||||
*/
|
||||
Decrypt withKey(InputStream key)
|
||||
throws SOPGPException.KeyIsProtected,
|
||||
SOPGPException.BadData,
|
||||
SOPGPException.UnsupportedAsymmetricAlgo;
|
||||
|
||||
/**
|
||||
* Adds one or more decryption key.
|
||||
*
|
||||
* @param key byte array containing the key(s)
|
||||
* @return builder instance
|
||||
*/
|
||||
default Decrypt withKey(byte[] key)
|
||||
throws SOPGPException.KeyIsProtected,
|
||||
SOPGPException.BadData,
|
||||
SOPGPException.UnsupportedAsymmetricAlgo {
|
||||
return withKey(new ByteArrayInputStream(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypts the given ciphertext, returning verification results and plaintext.
|
||||
* @param ciphertext ciphertext
|
||||
* @return ready with result
|
||||
*/
|
||||
ReadyWithResult<DecryptionResult> ciphertext(InputStream ciphertext)
|
||||
throws SOPGPException.BadData, SOPGPException.MissingArg, SOPGPException.CannotDecrypt;
|
||||
|
||||
/**
|
||||
* Decrypts the given ciphertext, returning verification results and plaintext.
|
||||
* @param ciphertext ciphertext
|
||||
* @return ready with result
|
||||
*/
|
||||
default ReadyWithResult<DecryptionResult> ciphertext(byte[] ciphertext)
|
||||
throws SOPGPException.BadData, SOPGPException.MissingArg, SOPGPException.CannotDecrypt {
|
||||
return ciphertext(new ByteArrayInputStream(ciphertext));
|
||||
}
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sop.operation;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import sop.ReadyWithResult;
|
||||
import sop.Signatures;
|
||||
|
||||
/**
|
||||
* Split cleartext signed messages up into data and signatures.
|
||||
*/
|
||||
public interface DetachInbandSignatureAndMessage {
|
||||
|
||||
/**
|
||||
* Do not wrap the signatures in ASCII armor.
|
||||
* @return builder
|
||||
*/
|
||||
DetachInbandSignatureAndMessage noArmor();
|
||||
|
||||
/**
|
||||
* Detach the provided cleartext signed message from its signatures.
|
||||
*
|
||||
* @param messageInputStream input stream containing the signed message
|
||||
* @return result containing the detached message
|
||||
* @throws IOException in case of an IO error
|
||||
*/
|
||||
ReadyWithResult<Signatures> message(InputStream messageInputStream) throws IOException;
|
||||
|
||||
/**
|
||||
* Detach the provided cleartext signed message from its signatures.
|
||||
*
|
||||
* @param message byte array containing the signed message
|
||||
* @return result containing the detached message
|
||||
* @throws IOException in case of an IO error
|
||||
*/
|
||||
default ReadyWithResult<Signatures> message(byte[] message) throws IOException {
|
||||
return message(new ByteArrayInputStream(message));
|
||||
}
|
||||
}
|
|
@ -1,109 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sop.operation;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import sop.Ready;
|
||||
import sop.enums.EncryptAs;
|
||||
import sop.exception.SOPGPException;
|
||||
|
||||
public interface Encrypt {
|
||||
|
||||
/**
|
||||
* Disable ASCII armor encoding.
|
||||
*
|
||||
* @return builder instance
|
||||
*/
|
||||
Encrypt noArmor();
|
||||
|
||||
/**
|
||||
* Sets encryption mode.
|
||||
*
|
||||
* @param mode mode
|
||||
* @return builder instance
|
||||
*/
|
||||
Encrypt mode(EncryptAs mode)
|
||||
throws SOPGPException.UnsupportedOption;
|
||||
|
||||
/**
|
||||
* Adds the signer key.
|
||||
*
|
||||
* @param key input stream containing the encoded signer key
|
||||
* @return builder instance
|
||||
*/
|
||||
Encrypt signWith(InputStream key)
|
||||
throws SOPGPException.KeyIsProtected,
|
||||
SOPGPException.KeyCannotSign,
|
||||
SOPGPException.UnsupportedAsymmetricAlgo,
|
||||
SOPGPException.BadData;
|
||||
|
||||
/**
|
||||
* Adds the signer key.
|
||||
*
|
||||
* @param key byte array containing the encoded signer key
|
||||
* @return builder instance
|
||||
*/
|
||||
default Encrypt signWith(byte[] key)
|
||||
throws SOPGPException.KeyIsProtected,
|
||||
SOPGPException.KeyCannotSign,
|
||||
SOPGPException.UnsupportedAsymmetricAlgo,
|
||||
SOPGPException.BadData {
|
||||
return signWith(new ByteArrayInputStream(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt with the given password.
|
||||
*
|
||||
* @param password password
|
||||
* @return builder instance
|
||||
*/
|
||||
Encrypt withPassword(String password)
|
||||
throws SOPGPException.PasswordNotHumanReadable,
|
||||
SOPGPException.UnsupportedOption;
|
||||
|
||||
/**
|
||||
* Encrypt with the given cert.
|
||||
*
|
||||
* @param cert input stream containing the encoded cert.
|
||||
* @return builder instance
|
||||
*/
|
||||
Encrypt withCert(InputStream cert)
|
||||
throws SOPGPException.CertCannotEncrypt,
|
||||
SOPGPException.UnsupportedAsymmetricAlgo,
|
||||
SOPGPException.BadData;
|
||||
|
||||
/**
|
||||
* Encrypt with the given cert.
|
||||
*
|
||||
* @param cert byte array containing the encoded cert.
|
||||
* @return builder instance
|
||||
*/
|
||||
default Encrypt withCert(byte[] cert)
|
||||
throws SOPGPException.CertCannotEncrypt,
|
||||
SOPGPException.UnsupportedAsymmetricAlgo,
|
||||
SOPGPException.BadData {
|
||||
return withCert(new ByteArrayInputStream(cert));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt the given data yielding the ciphertext.
|
||||
* @param plaintext plaintext
|
||||
* @return input stream containing the ciphertext
|
||||
*/
|
||||
Ready plaintext(InputStream plaintext)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* Encrypt the given data yielding the ciphertext.
|
||||
* @param plaintext plaintext
|
||||
* @return input stream containing the ciphertext
|
||||
*/
|
||||
default Ready plaintext(byte[] plaintext) throws IOException {
|
||||
return plaintext(new ByteArrayInputStream(plaintext));
|
||||
}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sop.operation;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import sop.Ready;
|
||||
import sop.exception.SOPGPException;
|
||||
|
||||
public interface ExtractCert {
|
||||
|
||||
/**
|
||||
* Disable ASCII armor encoding.
|
||||
*
|
||||
* @return builder instance
|
||||
*/
|
||||
ExtractCert noArmor();
|
||||
|
||||
/**
|
||||
* Extract the cert(s) from the provided key(s).
|
||||
*
|
||||
* @param keyInputStream input stream containing the encoding of one or more OpenPGP keys
|
||||
* @return result containing the encoding of the keys certs
|
||||
*/
|
||||
Ready key(InputStream keyInputStream) throws IOException, SOPGPException.BadData;
|
||||
|
||||
/**
|
||||
* Extract the cert(s) from the provided key(s).
|
||||
*
|
||||
* @param key byte array containing the encoding of one or more OpenPGP key
|
||||
* @return result containing the encoding of the keys certs
|
||||
*/
|
||||
default Ready key(byte[] key) throws IOException, SOPGPException.BadData {
|
||||
return key(new ByteArrayInputStream(key));
|
||||
}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sop.operation;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import sop.Ready;
|
||||
import sop.exception.SOPGPException;
|
||||
|
||||
public interface GenerateKey {
|
||||
|
||||
/**
|
||||
* Disable ASCII armor encoding.
|
||||
*
|
||||
* @return builder instance
|
||||
*/
|
||||
GenerateKey noArmor();
|
||||
|
||||
/**
|
||||
* Adds a user-id.
|
||||
*
|
||||
* @param userId user-id
|
||||
* @return builder instance
|
||||
*/
|
||||
GenerateKey userId(String userId);
|
||||
|
||||
/**
|
||||
* Generate the OpenPGP key and return it encoded as an {@link InputStream}.
|
||||
*
|
||||
* @return key
|
||||
*/
|
||||
Ready generate() throws SOPGPException.MissingArg, SOPGPException.UnsupportedAsymmetricAlgo, IOException;
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sop.operation;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import sop.ReadyWithResult;
|
||||
import sop.SigningResult;
|
||||
import sop.enums.SignAs;
|
||||
import sop.exception.SOPGPException;
|
||||
|
||||
public interface Sign {
|
||||
|
||||
/**
|
||||
* Disable ASCII armor encoding.
|
||||
*
|
||||
* @return builder instance
|
||||
*/
|
||||
Sign noArmor();
|
||||
|
||||
/**
|
||||
* Sets the signature mode.
|
||||
* Note: This method has to be called before {@link #key(InputStream)} is called.
|
||||
*
|
||||
* @param mode signature mode
|
||||
* @return builder instance
|
||||
*/
|
||||
Sign mode(SignAs mode) throws SOPGPException.UnsupportedOption;
|
||||
|
||||
/**
|
||||
* Add one or more signing keys.
|
||||
*
|
||||
* @param key input stream containing encoded keys
|
||||
* @return builder instance
|
||||
*/
|
||||
Sign key(InputStream key) throws SOPGPException.KeyIsProtected, SOPGPException.BadData, IOException;
|
||||
|
||||
/**
|
||||
* Add one or more signing keys.
|
||||
*
|
||||
* @param key byte array containing encoded keys
|
||||
* @return builder instance
|
||||
*/
|
||||
default Sign key(byte[] key) throws SOPGPException.KeyIsProtected, SOPGPException.BadData, IOException {
|
||||
return key(new ByteArrayInputStream(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Signs data.
|
||||
*
|
||||
* @param data input stream containing data
|
||||
* @return ready
|
||||
*/
|
||||
ReadyWithResult<SigningResult> data(InputStream data) throws IOException, SOPGPException.ExpectedText;
|
||||
|
||||
/**
|
||||
* Signs data.
|
||||
*
|
||||
* @param data byte array containing data
|
||||
* @return ready
|
||||
*/
|
||||
default ReadyWithResult<SigningResult> data(byte[] data) throws IOException, SOPGPException.ExpectedText {
|
||||
return data(new ByteArrayInputStream(data));
|
||||
}
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sop.operation;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.Date;
|
||||
|
||||
import sop.exception.SOPGPException;
|
||||
|
||||
public interface Verify extends VerifySignatures {
|
||||
|
||||
/**
|
||||
* Makes the SOP implementation consider signatures before this date invalid.
|
||||
*
|
||||
* @param timestamp timestamp
|
||||
* @return builder instance
|
||||
*/
|
||||
Verify notBefore(Date timestamp) throws SOPGPException.UnsupportedOption;
|
||||
|
||||
/**
|
||||
* Makes the SOP implementation consider signatures after this date invalid.
|
||||
*
|
||||
* @param timestamp timestamp
|
||||
* @return builder instance
|
||||
*/
|
||||
Verify notAfter(Date timestamp) throws SOPGPException.UnsupportedOption;
|
||||
|
||||
/**
|
||||
* Add one or more verification cert.
|
||||
*
|
||||
* @param cert input stream containing the encoded certs
|
||||
* @return builder instance
|
||||
*/
|
||||
Verify cert(InputStream cert) throws SOPGPException.BadData;
|
||||
|
||||
/**
|
||||
* Add one or more verification cert.
|
||||
*
|
||||
* @param cert byte array containing the encoded certs
|
||||
* @return builder instance
|
||||
*/
|
||||
default Verify cert(byte[] cert) throws SOPGPException.BadData {
|
||||
return cert(new ByteArrayInputStream(cert));
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the signatures.
|
||||
* @param signatures input stream containing encoded, detached signatures.
|
||||
*
|
||||
* @return builder instance
|
||||
*/
|
||||
VerifySignatures signatures(InputStream signatures) throws SOPGPException.BadData;
|
||||
|
||||
/**
|
||||
* Provides the signatures.
|
||||
* @param signatures byte array containing encoded, detached signatures.
|
||||
*
|
||||
* @return builder instance
|
||||
*/
|
||||
default VerifySignatures signatures(byte[] signatures) throws SOPGPException.BadData {
|
||||
return signatures(new ByteArrayInputStream(signatures));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sop.operation;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
||||
import sop.Verification;
|
||||
import sop.exception.SOPGPException;
|
||||
|
||||
public interface VerifySignatures {
|
||||
|
||||
/**
|
||||
* Provide the signed data (without signatures).
|
||||
*
|
||||
* @param data signed data
|
||||
* @return list of signature verifications
|
||||
* @throws IOException in case of an IO error
|
||||
* @throws SOPGPException.NoSignature when no signature is found
|
||||
* @throws SOPGPException.BadData when the data is invalid OpenPGP data
|
||||
*/
|
||||
List<Verification> data(InputStream data) throws IOException, SOPGPException.NoSignature, SOPGPException.BadData;
|
||||
|
||||
/**
|
||||
* Provide the signed data (without signatures).
|
||||
*
|
||||
* @param data signed data
|
||||
* @return list of signature verifications
|
||||
* @throws IOException in case of an IO error
|
||||
* @throws SOPGPException.NoSignature when no signature is found
|
||||
* @throws SOPGPException.BadData when the data is invalid OpenPGP data
|
||||
*/
|
||||
default List<Verification> data(byte[] data) throws IOException, SOPGPException.NoSignature, SOPGPException.BadData {
|
||||
return data(new ByteArrayInputStream(data));
|
||||
}
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sop.operation;
|
||||
|
||||
public interface Version {
|
||||
|
||||
/**
|
||||
* Return the implementations name.
|
||||
* e.g. "SOP",
|
||||
*
|
||||
* @return implementation name
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Return the implementations short version string.
|
||||
* e.g. "1.0"
|
||||
*
|
||||
* @return version string
|
||||
*/
|
||||
String getVersion();
|
||||
|
||||
/**
|
||||
* Return version information about the used OpenPGP backend.
|
||||
* e.g. "Bouncycastle 1.70"
|
||||
*
|
||||
* @return backend version string
|
||||
*/
|
||||
String getBackendVersion();
|
||||
|
||||
/**
|
||||
* Return an extended version string containing multiple lines of version information.
|
||||
* The first line MUST match the information produced by {@link #getName()} and {@link #getVersion()}, but the rest of the text
|
||||
* has no defined structure.
|
||||
* Example:
|
||||
* <pre>
|
||||
* "SOP 1.0
|
||||
* Awesome PGP!
|
||||
* Using Bouncycastle 1.70
|
||||
* LibFoo 1.2.2
|
||||
* See https://pgp.example.org/sop/ for more information"
|
||||
* </pre>
|
||||
*
|
||||
* @return extended version string
|
||||
*/
|
||||
String getExtendedVersion();
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/**
|
||||
* Stateless OpenPGP Interface for Java.
|
||||
* Different cryptographic operations.
|
||||
*/
|
||||
package sop.operation;
|
|
@ -1,8 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/**
|
||||
* Stateless OpenPGP Interface for Java.
|
||||
*/
|
||||
package sop;
|
|
@ -1,47 +0,0 @@
|
|||
// Copyright 2021 Paul Schaub, @maybeWeCouldStealAVan, @Dave L.
|
||||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sop.util;
|
||||
|
||||
public class HexUtil {
|
||||
|
||||
private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
|
||||
|
||||
/**
|
||||
* Encode a byte array to a hex string.
|
||||
*
|
||||
* @see <a href="https://stackoverflow.com/a/9855338">
|
||||
* How to convert a byte array to a hex string in Java?</a>
|
||||
* @param bytes bytes
|
||||
* @return hex encoding
|
||||
*/
|
||||
public static String bytesToHex(byte[] bytes) {
|
||||
char[] hexChars = new char[bytes.length * 2];
|
||||
for (int j = 0; j < bytes.length; j++) {
|
||||
int v = bytes[j] & 0xFF;
|
||||
hexChars[j * 2] = HEX_ARRAY[v >>> 4];
|
||||
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
|
||||
}
|
||||
return new String(hexChars);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a hex string into a byte array.
|
||||
*
|
||||
* @see <a href="https://stackoverflow.com/a/140861">
|
||||
* Convert a string representation of a hex dump to a byte array using Java?</a>
|
||||
* @param s hex string
|
||||
* @return decoded byte array
|
||||
*/
|
||||
public static byte[] hexToBytes(String s) {
|
||||
int len = s.length();
|
||||
byte[] data = new byte[len / 2];
|
||||
for (int i = 0; i < len; i += 2) {
|
||||
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
|
||||
+ Character.digit(s.charAt(i + 1), 16));
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sop.util;
|
||||
|
||||
/**
|
||||
* Backport of java.util.Optional for older Android versions.
|
||||
*
|
||||
* @param <T> item type
|
||||
*/
|
||||
public class Optional<T> {
|
||||
|
||||
private final T item;
|
||||
|
||||
public Optional() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public Optional(T item) {
|
||||
this.item = item;
|
||||
}
|
||||
|
||||
public static <T> Optional<T> of(T item) {
|
||||
if (item == null) {
|
||||
throw new NullPointerException("Item cannot be null.");
|
||||
}
|
||||
return new Optional<>(item);
|
||||
}
|
||||
|
||||
public static <T> Optional<T> ofNullable(T item) {
|
||||
return new Optional<>(item);
|
||||
}
|
||||
|
||||
public static <T> Optional<T> ofEmpty() {
|
||||
return new Optional<>(null);
|
||||
}
|
||||
|
||||
public T get() {
|
||||
return item;
|
||||
}
|
||||
|
||||
public boolean isPresent() {
|
||||
return item != null;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return item == null;
|
||||
}
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sop.util;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* {@link OutputStream} that buffers data being written into it, until its underlying output stream is being replaced.
|
||||
* At that point, first all the buffered data is being written to the underlying stream, followed by any successive
|
||||
* data that may get written to the {@link ProxyOutputStream}.
|
||||
*
|
||||
* This class is useful if we need to provide an {@link OutputStream} at one point in time when the final
|
||||
* target output stream is not yet known.
|
||||
*/
|
||||
public class ProxyOutputStream extends OutputStream {
|
||||
|
||||
private final ByteArrayOutputStream buffer;
|
||||
private OutputStream swapped;
|
||||
|
||||
public ProxyOutputStream() {
|
||||
this.buffer = new ByteArrayOutputStream();
|
||||
}
|
||||
|
||||
public synchronized void replaceOutputStream(OutputStream underlying) throws IOException {
|
||||
if (underlying == null) {
|
||||
throw new NullPointerException("Underlying OutputStream cannot be null.");
|
||||
}
|
||||
this.swapped = underlying;
|
||||
|
||||
byte[] bufferBytes = buffer.toByteArray();
|
||||
swapped.write(bufferBytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void write(byte[] b) throws IOException {
|
||||
if (swapped == null) {
|
||||
buffer.write(b);
|
||||
} else {
|
||||
swapped.write(b);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void write(byte[] b, int off, int len) throws IOException {
|
||||
if (swapped == null) {
|
||||
buffer.write(b, off, len);
|
||||
} else {
|
||||
swapped.write(b, off, len);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void flush() throws IOException {
|
||||
buffer.flush();
|
||||
if (swapped != null) {
|
||||
swapped.flush();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() throws IOException {
|
||||
buffer.close();
|
||||
if (swapped != null) {
|
||||
swapped.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void write(int i) throws IOException {
|
||||
if (swapped == null) {
|
||||
buffer.write(i);
|
||||
} else {
|
||||
swapped.write(i);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sop.util;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* Utility class to parse and format dates as ISO-8601 UTC timestamps.
|
||||
*/
|
||||
public class UTCUtil {
|
||||
|
||||
public static final SimpleDateFormat UTC_FORMATTER = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
|
||||
public static final SimpleDateFormat[] UTC_PARSERS = new SimpleDateFormat[] {
|
||||
UTC_FORMATTER,
|
||||
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX"),
|
||||
new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'"),
|
||||
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'")
|
||||
};
|
||||
|
||||
static {
|
||||
for (SimpleDateFormat f : UTC_PARSERS) {
|
||||
f.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Parse an ISO-8601 UTC timestamp from a string.
|
||||
*
|
||||
* @param dateString string
|
||||
* @return date
|
||||
*/
|
||||
public static Date parseUTCDate(String dateString) {
|
||||
for (SimpleDateFormat parser : UTC_PARSERS) {
|
||||
try {
|
||||
return parser.parse(dateString);
|
||||
} catch (ParseException e) {
|
||||
// Try next parser
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a date as ISO-8601 UTC timestamp.
|
||||
*
|
||||
* @param date date
|
||||
* @return timestamp string
|
||||
*/
|
||||
public static String formatUTCDate(Date date) {
|
||||
return UTC_FORMATTER.format(date);
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/**
|
||||
* Utility classes.
|
||||
*/
|
||||
package sop.util;
|
|
@ -1,33 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sop.util;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import sop.ByteArrayAndResult;
|
||||
import sop.Verification;
|
||||
|
||||
public class ByteArrayAndResultTest {
|
||||
|
||||
@Test
|
||||
public void testCreationAndGetters() {
|
||||
byte[] bytes = "Hello, World!\n".getBytes(StandardCharsets.UTF_8);
|
||||
List<Verification> result = Collections.singletonList(
|
||||
new Verification(UTCUtil.parseUTCDate("2019-10-24T23:48:29Z"),
|
||||
"C90E6D36200A1B922A1509E77618196529AE5FF8",
|
||||
"C4BC2DDB38CCE96485EBE9C2F20691179038E5C6")
|
||||
);
|
||||
ByteArrayAndResult<List<Verification>> bytesAndResult = new ByteArrayAndResult<>(bytes, result);
|
||||
|
||||
assertArrayEquals(bytes, bytesAndResult.getBytes());
|
||||
assertEquals(result, bytesAndResult.getResult());
|
||||
}
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sop.util;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Test using some test vectors from RFC4648.
|
||||
*
|
||||
* @see <a href="https://datatracker.ietf.org/doc/html/rfc4648#section-10">RFC-4648 §10: Test Vectors</a>
|
||||
*/
|
||||
public class HexUtilTest {
|
||||
|
||||
@SuppressWarnings("CharsetObjectCanBeUsed")
|
||||
private static final Charset ASCII = Charset.forName("US-ASCII");
|
||||
|
||||
@Test
|
||||
public void emptyHexEncodeTest() {
|
||||
assertHexEquals("", "");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeF() {
|
||||
assertHexEquals("66", "f");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeFo() {
|
||||
assertHexEquals("666F", "fo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeFoo() {
|
||||
assertHexEquals("666F6F", "foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeFoob() {
|
||||
assertHexEquals("666F6F62", "foob");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeFooba() {
|
||||
assertHexEquals("666F6F6261", "fooba");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeFoobar() {
|
||||
assertHexEquals("666F6F626172", "foobar");
|
||||
}
|
||||
|
||||
private void assertHexEquals(String hex, String ascii) {
|
||||
assertEquals(hex, HexUtil.bytesToHex(ascii.getBytes(ASCII)));
|
||||
assertArrayEquals(ascii.getBytes(ASCII), HexUtil.hexToBytes(hex));
|
||||
}
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sop.util;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import sop.MicAlg;
|
||||
|
||||
public class MicAlgTest {
|
||||
|
||||
@Test
|
||||
public void constructorNullArgThrows() {
|
||||
assertThrows(IllegalArgumentException.class, () -> new MicAlg(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void emptyMicAlgIsEmptyString() {
|
||||
MicAlg empty = MicAlg.empty();
|
||||
assertNotNull(empty.getMicAlg());
|
||||
assertTrue(empty.getMicAlg().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromInvalidAlgorithmIdThrows() {
|
||||
assertThrows(IllegalArgumentException.class, () -> MicAlg.fromHashAlgorithmId(-1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromHashAlgorithmIdsKnownAlgsMatch() {
|
||||
Map<Integer, String> knownAlgorithmMicalgs = new HashMap<>();
|
||||
knownAlgorithmMicalgs.put(1, "pgp-md5");
|
||||
knownAlgorithmMicalgs.put(2, "pgp-sha1");
|
||||
knownAlgorithmMicalgs.put(3, "pgp-ripemd160");
|
||||
knownAlgorithmMicalgs.put(8, "pgp-sha256");
|
||||
knownAlgorithmMicalgs.put(9, "pgp-sha384");
|
||||
knownAlgorithmMicalgs.put(10, "pgp-sha512");
|
||||
knownAlgorithmMicalgs.put(11, "pgp-sha224");
|
||||
|
||||
for (Integer id : knownAlgorithmMicalgs.keySet()) {
|
||||
MicAlg micAlg = MicAlg.fromHashAlgorithmId(id);
|
||||
assertEquals(knownAlgorithmMicalgs.get(id), micAlg.getMicAlg());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sop.util;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class OptionalTest {
|
||||
|
||||
@Test
|
||||
public void testEmpty() {
|
||||
Optional<String> optional = new Optional<>();
|
||||
assertEmpty(optional);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testArg() {
|
||||
String string = "foo";
|
||||
Optional<String> optional = new Optional<>(string);
|
||||
assertFalse(optional.isEmpty());
|
||||
assertTrue(optional.isPresent());
|
||||
assertEquals(string, optional.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOfEmpty() {
|
||||
Optional<String> optional = Optional.ofEmpty();
|
||||
assertEmpty(optional);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNullArg() {
|
||||
Optional<String> optional = new Optional<>(null);
|
||||
assertEmpty(optional);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOfWithNullArgThrows() {
|
||||
assertThrows(NullPointerException.class, () -> Optional.of(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOf() {
|
||||
String string = "Hello, World!";
|
||||
Optional<String> optional = Optional.of(string);
|
||||
assertFalse(optional.isEmpty());
|
||||
assertTrue(optional.isPresent());
|
||||
assertEquals(string, optional.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOfNullableWithNull() {
|
||||
Optional<String> optional = Optional.ofNullable(null);
|
||||
assertEmpty(optional);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOfNullableWithArg() {
|
||||
Optional<String> optional = Optional.ofNullable("bar");
|
||||
assertEquals("bar", optional.get());
|
||||
assertFalse(optional.isEmpty());
|
||||
assertTrue(optional.isPresent());
|
||||
}
|
||||
|
||||
private <T> void assertEmpty(Optional<T> optional) {
|
||||
assertTrue(optional.isEmpty());
|
||||
assertFalse(optional.isPresent());
|
||||
|
||||
assertNull(optional.get());
|
||||
}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sop.util;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class ProxyOutputStreamTest {
|
||||
|
||||
@Test
|
||||
public void replaceOutputStreamThrowsNPEForNull() {
|
||||
ProxyOutputStream proxy = new ProxyOutputStream();
|
||||
assertThrows(NullPointerException.class, () -> proxy.replaceOutputStream(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSwappingStreamPreservesWrittenBytes() throws IOException {
|
||||
byte[] firstSection = "Foo\nBar\n".getBytes(StandardCharsets.UTF_8);
|
||||
byte[] secondSection = "Baz\n".getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
ProxyOutputStream proxy = new ProxyOutputStream();
|
||||
proxy.write(firstSection);
|
||||
|
||||
ByteArrayOutputStream swappedStream = new ByteArrayOutputStream();
|
||||
proxy.replaceOutputStream(swappedStream);
|
||||
|
||||
proxy.write(secondSection);
|
||||
proxy.close();
|
||||
|
||||
assertEquals("Foo\nBar\nBaz\n", swappedStream.toString());
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sop.util;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import sop.Ready;
|
||||
|
||||
public class ReadyTest {
|
||||
|
||||
@Test
|
||||
public void readyTest() throws IOException {
|
||||
byte[] data = "Hello, World!\n".getBytes(StandardCharsets.UTF_8);
|
||||
Ready ready = new Ready() {
|
||||
@Override
|
||||
public void writeTo(OutputStream outputStream) throws IOException {
|
||||
outputStream.write(data);
|
||||
}
|
||||
};
|
||||
|
||||
assertArrayEquals(data, ready.getBytes());
|
||||
}
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sop.util;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import sop.ByteArrayAndResult;
|
||||
import sop.ReadyWithResult;
|
||||
import sop.Verification;
|
||||
import sop.exception.SOPGPException;
|
||||
|
||||
public class ReadyWithResultTest {
|
||||
|
||||
@Test
|
||||
public void testReadyWithResult() throws SOPGPException.NoSignature, IOException {
|
||||
byte[] data = "Hello, World!\n".getBytes(StandardCharsets.UTF_8);
|
||||
List<Verification> result = Collections.singletonList(
|
||||
new Verification(UTCUtil.parseUTCDate("2019-10-24T23:48:29Z"),
|
||||
"C90E6D36200A1B922A1509E77618196529AE5FF8",
|
||||
"C4BC2DDB38CCE96485EBE9C2F20691179038E5C6")
|
||||
);
|
||||
ReadyWithResult<List<Verification>> readyWithResult = new ReadyWithResult<List<Verification>>() {
|
||||
@Override
|
||||
public List<Verification> writeTo(OutputStream outputStream) throws IOException, SOPGPException.NoSignature {
|
||||
outputStream.write(data);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
ByteArrayAndResult<List<Verification>> bytesAndResult = readyWithResult.toByteArrayAndResult();
|
||||
assertArrayEquals(data, bytesAndResult.getBytes());
|
||||
assertEquals(result, bytesAndResult.getResult());
|
||||
}
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sop.util;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import sop.SessionKey;
|
||||
|
||||
public class SessionKeyTest {
|
||||
|
||||
@Test
|
||||
public void fromStringTest() {
|
||||
String string = "9:FCA4BEAF687F48059CACC14FB019125CD57392BAB7037C707835925CBF9F7BCD";
|
||||
SessionKey sessionKey = SessionKey.fromString(string);
|
||||
assertEquals(string, sessionKey.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toStringTest() {
|
||||
SessionKey sessionKey = new SessionKey((byte) 9, HexUtil.hexToBytes("FCA4BEAF687F48059CACC14FB019125CD57392BAB7037C707835925CBF9F7BCD"));
|
||||
assertEquals("9:FCA4BEAF687F48059CACC14FB019125CD57392BAB7037C707835925CBF9F7BCD", sessionKey.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equalsTest() {
|
||||
SessionKey s1 = new SessionKey((byte) 9, HexUtil.hexToBytes("FCA4BEAF687F48059CACC14FB019125CD57392BAB7037C707835925CBF9F7BCD"));
|
||||
SessionKey s2 = new SessionKey((byte) 9, HexUtil.hexToBytes("FCA4BEAF687F48059CACC14FB019125CD57392BAB7037C707835925CBF9F7BCD"));
|
||||
SessionKey s3 = new SessionKey((byte) 4, HexUtil.hexToBytes("FCA4BEAF687F48059CACC14FB019125CD57392BAB7037C707835925CBF9F7BCD"));
|
||||
SessionKey s4 = new SessionKey((byte) 9, HexUtil.hexToBytes("19125CD57392BAB7037C7078359FCA4BEAF687F4025CBF9F7BCD8059CACC14FB"));
|
||||
SessionKey s5 = new SessionKey((byte) 4, HexUtil.hexToBytes("19125CD57392BAB7037C7078359FCA4BEAF687F4025CBF9F7BCD8059CACC14FB"));
|
||||
|
||||
assertEquals(s1, s1);
|
||||
assertEquals(s1, s2);
|
||||
assertEquals(s1.hashCode(), s2.hashCode());
|
||||
assertNotEquals(s1, s3);
|
||||
assertNotEquals(s1.hashCode(), s3.hashCode());
|
||||
assertNotEquals(s1, s4);
|
||||
assertNotEquals(s1.hashCode(), s4.hashCode());
|
||||
assertNotEquals(s4, s5);
|
||||
assertNotEquals(s4.hashCode(), s5.hashCode());
|
||||
assertNotEquals(s1, null);
|
||||
assertNotEquals(s1, "FCA4BEAF687F48059CACC14FB019125CD57392BAB7037C707835925CBF9F7BCD");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromString_missingAlgorithmIdThrows() {
|
||||
String missingAlgorithId = "FCA4BEAF687F48059CACC14FB019125CD57392BAB7037C707835925CBF9F7BCD";
|
||||
assertThrows(IllegalArgumentException.class, () -> SessionKey.fromString(missingAlgorithId));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromString_wrongDivider() {
|
||||
String semicolonDivider = "9;FCA4BEAF687F48059CACC14FB019125CD57392BAB7037C707835925CBF9F7BCD";
|
||||
assertThrows(IllegalArgumentException.class, () -> SessionKey.fromString(semicolonDivider));
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sop.util;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import sop.MicAlg;
|
||||
import sop.SigningResult;
|
||||
|
||||
public class SigningResultTest {
|
||||
|
||||
@Test
|
||||
public void basicBuilderTest() {
|
||||
SigningResult result = SigningResult.builder()
|
||||
.setMicAlg(MicAlg.fromHashAlgorithmId(10))
|
||||
.build();
|
||||
|
||||
assertEquals("pgp-sha512", result.getMicAlg().getMicAlg());
|
||||
}
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sop.util;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Test parsing some date examples from the stateless OpenPGP CLI spec.
|
||||
*
|
||||
* @see <a href="https://datatracker.ietf.org/doc/html/draft-dkg-openpgp-stateless-cli-01#section-4.1">OpenPGP Stateless CLI §4.1. Date</a>
|
||||
*/
|
||||
public class UTCUtilTest {
|
||||
|
||||
@Test
|
||||
public void parseExample1() {
|
||||
String timestamp = "2019-10-29T12:11:04+00:00";
|
||||
Date date = UTCUtil.parseUTCDate(timestamp);
|
||||
assertEquals("2019-10-29T12:11:04Z", UTCUtil.formatUTCDate(date));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseExample2() {
|
||||
String timestamp = "2019-10-24T23:48:29Z";
|
||||
Date date = UTCUtil.parseUTCDate(timestamp);
|
||||
assertEquals("2019-10-24T23:48:29Z", UTCUtil.formatUTCDate(date));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseExample3() {
|
||||
String timestamp = "20191029T121104Z";
|
||||
Date date = UTCUtil.parseUTCDate(timestamp);
|
||||
assertEquals("2019-10-29T12:11:04Z", UTCUtil.formatUTCDate(date));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidDateReturnsNull() {
|
||||
String invalidTimestamp = "foobar";
|
||||
Date expectNull = UTCUtil.parseUTCDate(invalidTimestamp);
|
||||
assertNull(expectNull);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue