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

Base PGPainlessCLI on new sop-java module

* Rename pgpainless-sop -> pgpainless-cli
* Introduce sop-java (implementation-independent SOP API)
* Introduce sop-java-picocli (CLI frontend for sop-java)
* Introduce pgpainless-sop (implementation of sop-java using PGPainless)
* Rework pgpainless-cli (plugs pgpainless-sop into sop-java-picocli)
This commit is contained in:
Paul Schaub 2021-07-15 16:55:13 +02:00
parent 2ba782c451
commit 8cf5347b52
112 changed files with 6146 additions and 1303 deletions

18
sop-java/build.gradle Normal file
View file

@ -0,0 +1,18 @@
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()
}

View file

@ -0,0 +1,35 @@
/*
* Copyright 2021 Paul Schaub.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sop;
public class ByteArrayAndResult<T> {
private final byte[] bytes;
private final T result;
public ByteArrayAndResult(byte[] bytes, T result) {
this.bytes = bytes;
this.result = result;
}
public byte[] getBytes() {
return bytes;
}
public T getResult() {
return result;
}
}

View file

@ -0,0 +1,40 @@
/*
* Copyright 2021 Paul Schaub.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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);
}
}

View file

@ -0,0 +1,31 @@
/*
* Copyright 2021 Paul Schaub.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sop;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public abstract class Ready {
public abstract void writeTo(OutputStream outputStream) throws IOException;
public byte[] getBytes() throws IOException {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
writeTo(bytes);
return bytes.toByteArray();
}
}

View file

@ -0,0 +1,33 @@
/*
* Copyright 2021 Paul Schaub.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sop;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import sop.exception.SOPGPException;
public abstract class ReadyWithResult<T> {
public abstract T writeTo(OutputStream outputStream) throws IOException, SOPGPException.NoSignature;
public ByteArrayAndResult<T> toBytes() throws IOException, SOPGPException.NoSignature {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
T result = writeTo(bytes);
return new ByteArrayAndResult<>(bytes.toByteArray(), result);
}
}

View file

@ -0,0 +1,103 @@
/*
* Copyright 2021 Paul Schaub.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sop;
import sop.operation.Armor;
import sop.operation.Dearmor;
import sop.operation.Decrypt;
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();
}

View file

@ -0,0 +1,75 @@
/*
* Copyright 2021 Paul Schaub.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sop;
import java.util.Arrays;
import sop.util.HexUtil;
public class SessionKey {
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());
}
@Override
public String toString() {
return "" + (int) getAlgorithm() + ':' + HexUtil.bytesToHex(sessionKey);
}
}

View file

@ -0,0 +1,69 @@
/*
* Copyright 2021 Paul Schaub.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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();
}
}

View file

@ -0,0 +1,24 @@
/*
* Copyright 2021 Paul Schaub.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sop.enums;
public enum ArmorLabel {
Auto,
Sig,
Key,
Cert,
Message
}

View file

@ -0,0 +1,22 @@
/*
* Copyright 2021 Paul Schaub.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sop.enums;
public enum EncryptAs {
Binary,
Text,
MIME
}

View file

@ -0,0 +1,21 @@
/*
* Copyright 2021 Paul Schaub.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sop.enums;
public enum SignAs {
Binary,
Text
}

View file

@ -0,0 +1,21 @@
/*
* Copyright 2021 Paul Schaub.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Stateless OpenPGP Interface for Java.
* Enumerations.
*/
package sop.enums;

View file

@ -0,0 +1,157 @@
/*
* Copyright 2021 Paul Schaub.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sop.exception;
public class SOPGPException extends Exception {
public SOPGPException() {
super();
}
public SOPGPException(String message) {
super(message);
}
public SOPGPException(Throwable e) {
super(e);
}
public static class NoSignature extends SOPGPException {
public static final int EXIT_CODE = 3;
public int getExitCode() {
return EXIT_CODE;
}
}
public static class UnsupportedAsymmetricAlgo extends SOPGPException {
public static final int EXIT_CODE = 13;
public UnsupportedAsymmetricAlgo(Throwable e) {
super(e);
}
public int getExitCode() {
return EXIT_CODE;
}
}
public static class CertCannotEncrypt extends SOPGPException {
public static final int EXIT_CODE = 17;
public int getExitCode() {
return EXIT_CODE;
}
}
public static class CertCannotSign extends SOPGPException {
}
public static class MissingArg extends SOPGPException {
public static final int EXIT_CODE = 19;
public MissingArg(String s) {
super(s);
}
public int getExitCode() {
return EXIT_CODE;
}
}
public static class IncompleteVerification extends SOPGPException {
public static final int EXIT_CODE = 23;
public int getExitCode() {
return EXIT_CODE;
}
}
public static class CannotDecrypt extends SOPGPException {
public static final int EXIT_CODE = 29;
public int getExitCode() {
return EXIT_CODE;
}
}
public static class PasswordNotHumanReadable extends SOPGPException {
public static final int EXIT_CODE = 31;
public int getExitCode() {
return EXIT_CODE;
}
}
public static class UnsupportedOption extends SOPGPException {
public static final int EXIT_CODE = 37;
public int getExitCode() {
return EXIT_CODE;
}
}
public static class BadData extends SOPGPException {
public static final int EXIT_CODE = 41;
public BadData(Throwable e) {
super(e);
}
public int getExitCode() {
return EXIT_CODE;
}
}
public static class ExpectedText extends SOPGPException {
public static final int EXIT_CODE = 53;
public int getExitCode() {
return EXIT_CODE;
}
}
public static class OutputExists extends SOPGPException {
}
public static class KeyIsProtected extends SOPGPException {
}
public static class AmbiguousInput extends SOPGPException {
}
public static class NotImplemented extends SOPGPException {
public static final int EXIT_CODE = 69;
public int getExitCode() {
return EXIT_CODE;
}
}
}

View file

@ -0,0 +1,21 @@
/*
* Copyright 2021 Paul Schaub.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Stateless OpenPGP Interface for Java.
* Exception classes.
*/
package sop.exception;

View file

@ -0,0 +1,48 @@
/*
* Copyright 2021 Paul Schaub.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sop.operation;
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;
/**
* Allow nested Armoring.
*
* @return builder instance
*/
Armor allowNested() 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;
}

View file

@ -0,0 +1,33 @@
/*
* Copyright 2021 Paul Schaub.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sop.operation;
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;
}

View file

@ -0,0 +1,94 @@
/*
* Copyright 2021 Paul Schaub.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sop.operation;
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 the verification cert.
*
* @param cert input stream containing the cert
* @return builder instance
*/
Decrypt verifyWithCert(InputStream cert)
throws SOPGPException.BadData,
IOException;
/**
* 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 the decryption key.
*
* @param key input stream containing the key
* @return builder instance
*/
Decrypt withKey(InputStream key)
throws SOPGPException.KeyIsProtected,
SOPGPException.BadData,
SOPGPException.UnsupportedAsymmetricAlgo;
/**
* 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;
}

View file

@ -0,0 +1,83 @@
/*
* Copyright 2021 Paul Schaub.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sop.operation;
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.CertCannotSign,
SOPGPException.UnsupportedAsymmetricAlgo,
SOPGPException.BadData;
/**
* 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 the given data yielding the ciphertext.
* @param plaintext plaintext
* @return input stream containing the ciphertext
*/
Ready plaintext(InputStream plaintext)
throws IOException;
}

View file

@ -0,0 +1,40 @@
/*
* Copyright 2021 Paul Schaub.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sop.operation;
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 from the provided key.
*
* @param keyInputStream input stream containing the encoding of an OpenPGP key
* @return input stream containing the encoding of the keys cert
*/
Ready key(InputStream keyInputStream) throws IOException, SOPGPException.BadData;
}

View file

@ -0,0 +1,47 @@
/*
* Copyright 2021 Paul Schaub.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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;
}

View file

@ -0,0 +1,57 @@
/*
* Copyright 2021 Paul Schaub.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sop.operation;
import java.io.IOException;
import java.io.InputStream;
import sop.Ready;
import sop.enums.SignAs;
import sop.exception.SOPGPException;
public interface Sign {
/**
* Disable ASCII armor encoding.
*
* @return builder instance
*/
Sign noArmor();
/**
* Sets the signature mode.
*
* @param mode signature mode
* @return builder instance
*/
Sign mode(SignAs mode) throws SOPGPException.UnsupportedOption;
/**
* Adds the signer key.
*
* @param key input stream containing encoded key
* @return builder instance
*/
Sign key(InputStream key) throws SOPGPException.KeyIsProtected, SOPGPException.BadData, IOException;
/**
* Signs data.
*
* @param data input stream containing data
* @return ready
*/
Ready data(InputStream data) throws IOException, SOPGPException.ExpectedText;
}

View file

@ -0,0 +1,57 @@
/*
* Copyright 2021 Paul Schaub.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sop.operation;
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;
/**
* Adds the verification cert.
*
* @param cert input stream containing the encoded cert
* @return builder instance
*/
Verify cert(InputStream cert) throws SOPGPException.BadData;
/**
* Provides the signatures.
* @param signatures input stream containing encoded, detached signatures.
*
* @return builder instance
*/
VerifySignatures signatures(InputStream signatures) throws SOPGPException.BadData;
}

View file

@ -0,0 +1,28 @@
/*
* Copyright 2021 Paul Schaub.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sop.operation;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import sop.Verification;
import sop.exception.SOPGPException;
public interface VerifySignatures {
List<Verification> data(InputStream data) throws IOException, SOPGPException.NoSignature, SOPGPException.BadData;
}

View file

@ -0,0 +1,33 @@
/*
* Copyright 2021 Paul Schaub.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sop.operation;
public interface Version {
/**
* Return the implementations name.
*
* @return implementation name
*/
String getName();
/**
* Return the implementations version string.
*
* @return version string
*/
String getVersion();
}

View file

@ -0,0 +1,21 @@
/*
* Copyright 2021 Paul Schaub.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Stateless OpenPGP Interface for Java.
* Different cryptographic operations.
*/
package sop.operation;

View file

@ -0,0 +1,20 @@
/*
* Copyright 2021 Paul Schaub.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Stateless OpenPGP Interface for Java.
*/
package sop;

View file

@ -0,0 +1,57 @@
/*
* Copyright 2021 Paul Schaub, @maybeWeCouldStealAVan, @Dave L.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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
* @return
*/
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;
}
}

View file

@ -0,0 +1,61 @@
/*
* Copyright 2021 Paul Schaub.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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;
}
}

View file

@ -0,0 +1,91 @@
/*
* Copyright 2021 Paul Schaub.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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 where 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);
}
}
}

View file

@ -0,0 +1,66 @@
/*
* Copyright 2021 Paul Schaub.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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 SimpleDateFormat UTC_FORMATTER = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
public static 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) {
}
}
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);
}
}

View file

@ -0,0 +1,19 @@
/*
* Copyright 2021 Paul Schaub.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Utility classes.
*/
package sop.util;

View file

@ -0,0 +1,44 @@
/*
* Copyright 2021 Paul Schaub.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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());
}
}

View file

@ -0,0 +1,73 @@
/*
* Copyright 2021 Paul Schaub.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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 {
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));
}
}

View file

@ -0,0 +1,89 @@
/*
* Copyright 2021 Paul Schaub.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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());
}
}

View file

@ -0,0 +1,51 @@
/*
* Copyright 2021 Paul Schaub.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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());
}
}

View file

@ -0,0 +1,41 @@
/*
* Copyright 2021 Paul Schaub.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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());
}
}

View file

@ -0,0 +1,55 @@
/*
* Copyright 2021 Paul Schaub.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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.toBytes();
assertArrayEquals(data, bytesAndResult.getBytes());
assertEquals(result, bytesAndResult.getResult());
}
}

View file

@ -0,0 +1,52 @@
/*
* Copyright 2021 Paul Schaub.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sop.util;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import org.junit.jupiter.api.Test;
import sop.SessionKey;
public class SessionKeyTest {
@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");
}
}

View file

@ -0,0 +1,59 @@
/*
* Copyright 2021 Paul Schaub.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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);
}
}