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

Compare commits

..

No commits in common. "cccd3287c1c5bc92ff55e6545f52814cf48564d3" and "2ed16e8f52563058c4ed100ba9c825292a511947" have entirely different histories.

14 changed files with 44 additions and 109 deletions

View file

@ -247,8 +247,7 @@ class ArmorUtils {
.add(OpenPgpFingerprint.of(publicKey).prettyPrint())
// Primary / First User ID
(primary ?: first)?.let {
headerMap
.getOrPut(HEADER_COMMENT) { mutableSetOf() }
headerMap.getOrPut(HEADER_COMMENT) { mutableSetOf() }
.add(it.replace("\n", "\\n").replace("\r", "\\r"))
}
// X-1 further identities

View file

@ -40,7 +40,7 @@ public class GenerateKeyWithAdditionalUserIdTest {
.addUserId(UserId.onlyEmail("primary@user.id"))
.addUserId(UserId.onlyEmail("additional@user.id"))
.addUserId(UserId.onlyEmail("additional2@user.id"))
.addUserId("\twithWhitespace@user.id ")
.addUserId("\ttrimThis@user.id ")
.setExpirationDate(expiration)
.build()
.getPGPSecretKeyRing();
@ -52,7 +52,7 @@ public class GenerateKeyWithAdditionalUserIdTest {
assertEquals("<primary@user.id>", userIds.next());
assertEquals("<additional@user.id>", userIds.next());
assertEquals("<additional2@user.id>", userIds.next());
assertEquals("\twithWhitespace@user.id ", userIds.next());
assertEquals("trimThis@user.id", userIds.next());
assertFalse(userIds.hasNext());
}
}

View file

@ -34,20 +34,19 @@ public class PassphraseTest {
@Test
public void testTrimming() {
Passphrase leadingSpace = Passphrase.fromPassword(" space").withTrimmedWhitespace();
Passphrase leadingSpace = Passphrase.fromPassword(" space");
assertArrayEquals("space".toCharArray(), leadingSpace.getChars());
assertFalse(leadingSpace.isEmpty());
Passphrase trailingSpace = Passphrase.fromPassword("space ").withTrimmedWhitespace();
Passphrase trailingSpace = Passphrase.fromPassword("space ");
assertArrayEquals("space".toCharArray(), trailingSpace.getChars());
assertFalse(trailingSpace.isEmpty());
Passphrase leadingTrailingWhitespace = new Passphrase("\t Such whitespace, much wow\n ".toCharArray())
.withTrimmedWhitespace();
Passphrase leadingTrailingWhitespace = new Passphrase("\t Such whitespace, much wow\n ".toCharArray());
assertArrayEquals("Such whitespace, much wow".toCharArray(), leadingTrailingWhitespace.getChars());
assertFalse(leadingTrailingWhitespace.isEmpty());
Passphrase fromEmptyChars = new Passphrase(" ".toCharArray()).withTrimmedWhitespace();
Passphrase fromEmptyChars = new Passphrase(" ".toCharArray());
assertNull(fromEmptyChars.getChars());
assertTrue(fromEmptyChars.isEmpty());
}
@ -55,7 +54,7 @@ public class PassphraseTest {
@ParameterizedTest
@ValueSource(strings = {"", " ", " ", "\t", "\t\t"})
public void testEmptyPassphrases(String empty) {
Passphrase passphrase = Passphrase.fromPassword(empty).withTrimmedWhitespace();
Passphrase passphrase = Passphrase.fromPassword(empty);
assertTrue(passphrase.isEmpty());
assertEquals(Passphrase.emptyPassphrase(), passphrase);

View file

@ -10,6 +10,7 @@ import org.bouncycastle.openpgp.api.OpenPGPKey
import org.pgpainless.PGPainless
import org.pgpainless.exception.KeyException
import org.pgpainless.util.OpenPGPCertificateUtil
import org.pgpainless.util.Passphrase
import sop.Ready
import sop.exception.SOPGPException
import sop.operation.CertifyUserId
@ -78,6 +79,6 @@ class CertifyUserIdImpl(private val api: PGPainless) : CertifyUserId {
override fun userId(userId: String): CertifyUserId = apply { this.userIds.add(userId) }
override fun withKeyPassword(password: ByteArray): CertifyUserId = apply {
PasswordHelper.addPassphrasePlusRemoveWhitespace(password, protector)
protector.addPassphrase(Passphrase.fromPassword(String(password)))
}
}

View file

@ -73,6 +73,6 @@ class ChangeKeyPasswordImpl(private val api: PGPainless) : ChangeKeyPassword {
override fun noArmor(): ChangeKeyPassword = apply { armor = false }
override fun oldKeyPassphrase(oldPassphrase: String): ChangeKeyPassword = apply {
PasswordHelper.addPassphrasePlusRemoveWhitespace(oldPassphrase, oldProtector)
oldProtector.addPassphrase(Passphrase.fromPassword(oldPassphrase))
}
}

View file

@ -16,11 +16,13 @@ import org.pgpainless.decryption_verification.ConsumerOptions
import org.pgpainless.exception.MalformedOpenPgpMessageException
import org.pgpainless.exception.MissingDecryptionMethodException
import org.pgpainless.exception.WrongPassphraseException
import org.pgpainless.util.Passphrase
import sop.DecryptionResult
import sop.ReadyWithResult
import sop.SessionKey
import sop.exception.SOPGPException
import sop.operation.Decrypt
import sop.util.UTF8Util
/** Implementation of the `decrypt` operation using PGPainless. */
class DecryptImpl(private val api: PGPainless) : Decrypt {
@ -96,11 +98,16 @@ class DecryptImpl(private val api: PGPainless) : Decrypt {
}
override fun withKeyPassword(password: ByteArray): Decrypt = apply {
PasswordHelper.addPassphrasePlusRemoveWhitespace(password, protector)
protector.addPassphrase(Passphrase.fromPassword(String(password, UTF8Util.UTF8)))
}
override fun withPassword(password: String): Decrypt = apply {
PasswordHelper.addMessagePassphrasePlusRemoveWhitespace(password, consumerOptions)
consumerOptions.addMessagePassphrase(Passphrase.fromPassword(password))
password.trimEnd().let {
if (it != password) {
consumerOptions.addMessagePassphrase(Passphrase.fromPassword(it))
}
}
}
override fun withSessionKey(sessionKey: SessionKey): Decrypt = apply {

View file

@ -19,12 +19,14 @@ import org.pgpainless.encryption_signing.SigningOptions
import org.pgpainless.exception.KeyException.MissingSecretKeyException
import org.pgpainless.exception.KeyException.UnacceptableSigningKeyException
import org.pgpainless.util.ArmoredOutputStreamFactory
import org.pgpainless.util.Passphrase
import sop.MicAlg
import sop.ReadyWithResult
import sop.SigningResult
import sop.enums.SignAs
import sop.exception.SOPGPException
import sop.operation.DetachedSign
import sop.util.UTF8Util
/** Implementation of the `sign` operation using PGPainless. */
class DetachedSignImpl(private val api: PGPainless) : DetachedSign {
@ -106,7 +108,7 @@ class DetachedSignImpl(private val api: PGPainless) : DetachedSign {
override fun noArmor(): DetachedSign = apply { armor = false }
override fun withKeyPassword(password: ByteArray): DetachedSign = apply {
PasswordHelper.addPassphrasePlusRemoveWhitespace(password, protector)
protector.addPassphrase(Passphrase.fromPassword(String(password, UTF8Util.UTF8)))
}
private fun modeToSigType(mode: SignAs): DocumentSignatureType {

View file

@ -30,6 +30,7 @@ import sop.SessionKey
import sop.enums.EncryptAs
import sop.exception.SOPGPException
import sop.operation.Encrypt
import sop.util.UTF8Util
/** Implementation of the `encrypt` operation using PGPainless. */
class EncryptImpl(private val api: PGPainless) : Encrypt {
@ -147,12 +148,11 @@ class EncryptImpl(private val api: PGPainless) : Encrypt {
}
override fun withKeyPassword(password: ByteArray): Encrypt = apply {
PasswordHelper.addPassphrasePlusRemoveWhitespace(password, protector)
protector.addPassphrase(Passphrase.fromPassword(String(password, UTF8Util.UTF8)))
}
override fun withPassword(password: String): Encrypt = apply {
encryptionOptions.addMessagePassphrase(
Passphrase.fromPassword(password).withTrimmedWhitespace())
encryptionOptions.addMessagePassphrase(Passphrase.fromPassword(password))
}
private fun modeToStreamEncoding(mode: EncryptAs): StreamEncoding {

View file

@ -99,7 +99,7 @@ class GenerateKeyImpl(private val api: PGPainless) : GenerateKey {
override fun userId(userId: String): GenerateKey = apply { userIds.add(userId) }
override fun withKeyPassword(password: String): GenerateKey = apply {
this.passphrase = Passphrase.fromPassword(password).withTrimmedWhitespace()
this.passphrase = Passphrase.fromPassword(password)
}
private fun generateKeyWithProfile(

View file

@ -18,10 +18,12 @@ import org.pgpainless.encryption_signing.ProducerOptions
import org.pgpainless.encryption_signing.SigningOptions
import org.pgpainless.exception.KeyException.MissingSecretKeyException
import org.pgpainless.exception.KeyException.UnacceptableSigningKeyException
import org.pgpainless.util.Passphrase
import sop.Ready
import sop.enums.InlineSignAs
import sop.exception.SOPGPException
import sop.operation.InlineSign
import sop.util.UTF8Util
/** Implementation of the `inline-sign` operation using PGPainless. */
class InlineSignImpl(private val api: PGPainless) : InlineSign {
@ -110,7 +112,7 @@ class InlineSignImpl(private val api: PGPainless) : InlineSign {
override fun noArmor(): InlineSign = apply { armor = false }
override fun withKeyPassword(password: ByteArray): InlineSign = apply {
PasswordHelper.addPassphrasePlusRemoveWhitespace(password, protector)
protector.addPassphrase(Passphrase.fromPassword(String(password, UTF8Util.UTF8)))
}
private fun modeToSigType(mode: InlineSignAs): DocumentSignatureType {

View file

@ -1,84 +0,0 @@
// SPDX-FileCopyrightText: 2025 Paul Schaub <info@pgpainless.org>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.sop
import org.pgpainless.decryption_verification.ConsumerOptions
import org.pgpainless.util.Passphrase
import sop.exception.SOPGPException
import sop.util.UTF8Util
class PasswordHelper {
companion object {
/**
* Add the given [password] as a message passphrase to the given [consumerOptions] instance.
* If the [password] contains trailing or leading whitespace, additionally add the
* [password] with these whitespace characters removed.
*
* @param password password
* @param consumerOptions consumer options for message decryption
*/
@JvmStatic
fun addMessagePassphrasePlusRemoveWhitespace(
password: String,
consumerOptions: ConsumerOptions
) {
Passphrase.fromPassword(password).let {
consumerOptions.addMessagePassphrase(it)
val trimmed = it.withTrimmedWhitespace()
if (!it.getChars().contentEquals(trimmed.getChars())) {
consumerOptions.addMessagePassphrase(trimmed)
}
}
}
/**
* Add the given [password] to the given [protector] instance. If the [password] contains
* trailing or leading whitespace, additionally add the [password] with these whitespace
* characters removed.
*
* @param password password
* @param protector secret key ring protector
* @throws SOPGPException.PasswordNotHumanReadable if the password is not a valid UTF-8
* string representation.
*/
@JvmStatic
fun addPassphrasePlusRemoveWhitespace(
password: ByteArray,
protector: MatchMakingSecretKeyRingProtector
) {
val string =
try {
UTF8Util.decodeUTF8(password)
} catch (e: CharacterCodingException) {
throw SOPGPException.PasswordNotHumanReadable(
"Cannot UTF8-decode password: ${e.stackTraceToString()}")
}
addPassphrasePlusRemoveWhitespace(string, protector)
}
/**
* Add the given [password] to the given [protector] instance. If the [password] contains
* trailing or leading whitespace, additionally add the [password] with these whitespace
* characters removed.
*
* @param password password
* @param protector secret key ring protector
*/
@JvmStatic
fun addPassphrasePlusRemoveWhitespace(
password: String,
protector: MatchMakingSecretKeyRingProtector
) {
Passphrase.fromPassword(password).let {
protector.addPassphrase(it)
val trimmed = it.withTrimmedWhitespace()
if (!it.getChars().contentEquals(trimmed.getChars())) {
protector.addPassphrase(trimmed)
}
}
}
}
}

View file

@ -15,9 +15,11 @@ import org.pgpainless.exception.WrongPassphraseException
import org.pgpainless.key.util.KeyRingUtils
import org.pgpainless.key.util.RevocationAttributes
import org.pgpainless.util.OpenPGPCertificateUtil
import org.pgpainless.util.Passphrase
import sop.Ready
import sop.exception.SOPGPException
import sop.operation.RevokeKey
import sop.util.UTF8Util
class RevokeKeyImpl(private val api: PGPainless) : RevokeKey {
@ -76,6 +78,14 @@ class RevokeKeyImpl(private val api: PGPainless) : RevokeKey {
override fun noArmor(): RevokeKey = apply { armor = false }
override fun withKeyPassword(password: ByteArray): RevokeKey = apply {
PasswordHelper.addPassphrasePlusRemoveWhitespace(password, protector)
val string =
try {
UTF8Util.decodeUTF8(password)
} catch (e: CharacterCodingException) {
// TODO: Add cause
throw SOPGPException.PasswordNotHumanReadable(
"Cannot UTF8-decode password: ${e.stackTraceToString()}")
}
protector.addPassphrase(Passphrase.fromPassword(string))
}
}

View file

@ -13,6 +13,7 @@ import org.bouncycastle.openpgp.api.OpenPGPCertificate
import org.pgpainless.PGPainless
import org.pgpainless.key.modification.secretkeyring.OpenPGPKeyUpdater
import org.pgpainless.util.OpenPGPCertificateUtil
import org.pgpainless.util.Passphrase
import sop.Ready
import sop.operation.UpdateKey
@ -80,6 +81,6 @@ class UpdateKeyImpl(private val api: PGPainless) : UpdateKey {
override fun signingOnly(): UpdateKey = apply { signingOnly = true }
override fun withKeyPassword(password: ByteArray): UpdateKey = apply {
PasswordHelper.addPassphrasePlusRemoveWhitespace(password, protector)
protector.addPassphrase(Passphrase.fromPassword(String(password)))
}
}

View file

@ -15,7 +15,6 @@ import java.io.IOException;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.api.OpenPGPKey;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.pgpainless.PGPainless;
@ -109,7 +108,6 @@ public class GenerateKeyTest {
.generate()
.getBytes();
OpenPGPKey key = PGPainless.getInstance().readKey().parseKey(keyBytes);
assertTrue(key.getValidUserIds().get(0).getUserId().equals("Foo\n\nBar"));
assertTrue(new String(keyBytes).contains("Foo\\n\\nBar"));
}
}