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.
cccd3287c1
...
2ed16e8f52
14 changed files with 44 additions and 109 deletions
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue