diff --git a/pgpainless-core/src/main/kotlin/openpgp/DateExtensions.kt b/pgpainless-core/src/main/kotlin/openpgp/DateExtensions.kt index a1c80710..2763cb55 100644 --- a/pgpainless-core/src/main/kotlin/openpgp/DateExtensions.kt +++ b/pgpainless-core/src/main/kotlin/openpgp/DateExtensions.kt @@ -11,40 +11,38 @@ import java.util.* /** * Return a new date which represents this date plus the given amount of seconds added. * - * Since '0' is a special date value in the OpenPGP specification - * (e.g. '0' means no expiration for expiration dates), this method will return 'null' if seconds is 0. + * Since '0' is a special date value in the OpenPGP specification (e.g. '0' means no expiration for + * expiration dates), this method will return 'null' if seconds is 0. * * @param date date * @param seconds number of seconds to be added * @return date plus seconds or null if seconds is '0' */ fun Date.plusSeconds(seconds: Long): Date? { - require(Long.MAX_VALUE - time > seconds) { "Adding $seconds seconds to this date would cause time to overflow." } - return if (seconds == 0L) null - else Date(this.time + 1000 * seconds) + require(Long.MAX_VALUE - time > seconds) { + "Adding $seconds seconds to this date would cause time to overflow." + } + return if (seconds == 0L) null else Date(this.time + 1000 * seconds) } val Date.asSeconds: Long get() = time / 1000 fun Date.secondsTill(later: Date): Long { - require(this <= later) { - "Timestamp MUST be before the later timestamp." - } + require(this <= later) { "Timestamp MUST be before the later timestamp." } return later.asSeconds - this.asSeconds } -/** - * Return a new [Date] instance with this instance's time floored down to seconds precision. - */ +/** Return a new [Date] instance with this instance's time floored down to seconds precision. */ fun Date.toSecondsPrecision(): Date { return Date(asSeconds * 1000) } internal val parser: SimpleDateFormat - // Java's SimpleDateFormat is not thread-safe, therefore we return a new instance on every invocation. - get() = SimpleDateFormat("yyyy-MM-dd HH:mm:ss z") - .apply { timeZone = TimeZone.getTimeZone("UTC") } + // Java's SimpleDateFormat is not thread-safe, therefore we return a new instance on every + // invocation. + get() = + SimpleDateFormat("yyyy-MM-dd HH:mm:ss z").apply { timeZone = TimeZone.getTimeZone("UTC") } /** * Format a date as UTC timestamp. @@ -55,12 +53,13 @@ fun Date.formatUTC(): String = parser.format(this) /** * Parse a UTC timestamp into a date. + * * @return date */ fun String.parseUTC(): Date { return try { parser.parse(this) - } catch (e : ParseException) { + } catch (e: ParseException) { throw IllegalArgumentException("Malformed UTC timestamp: $this", e) } } diff --git a/pgpainless-core/src/main/kotlin/openpgp/LongExtensions.kt b/pgpainless-core/src/main/kotlin/openpgp/LongExtensions.kt index 13f94943..c6c318b3 100644 --- a/pgpainless-core/src/main/kotlin/openpgp/LongExtensions.kt +++ b/pgpainless-core/src/main/kotlin/openpgp/LongExtensions.kt @@ -4,22 +4,18 @@ package openpgp -/** - * Format this Long as an OpenPGP key-ID (16 digit uppercase hex number). - */ +/** Format this Long as an OpenPGP key-ID (16 digit uppercase hex number). */ fun Long.openPgpKeyId(): String { return String.format("%016X", this).uppercase() } -/** - * Parse a Long form a 16 digit hex encoded OpenPgp key-ID. - */ +/** Parse a Long form a 16 digit hex encoded OpenPgp key-ID. */ fun Long.Companion.fromOpenPgpKeyId(hexKeyId: String): Long { require("^[0-9A-Fa-f]{16}$".toRegex().matches(hexKeyId)) { "Provided long key-id does not match expected format. " + - "A long key-id consists of 16 hexadecimal characters." + "A long key-id consists of 16 hexadecimal characters." } // Calling toLong() only fails with a NumberFormatException. // Therefore, we call toULong(16).toLong(), which seems to work. return hexKeyId.toULong(16).toLong() -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/bouncycastle/CachingBcPublicKeyDataDecryptorFactory.kt b/pgpainless-core/src/main/kotlin/org/bouncycastle/CachingBcPublicKeyDataDecryptorFactory.kt index 60b860f2..3a6f5351 100644 --- a/pgpainless-core/src/main/kotlin/org/bouncycastle/CachingBcPublicKeyDataDecryptorFactory.kt +++ b/pgpainless-core/src/main/kotlin/org/bouncycastle/CachingBcPublicKeyDataDecryptorFactory.kt @@ -11,40 +11,46 @@ import org.pgpainless.decryption_verification.CustomPublicKeyDataDecryptorFactor import org.pgpainless.key.SubkeyIdentifier /** - * Implementation of the [PublicKeyDataDecryptorFactory] which caches decrypted session keys. - * That way, if a message needs to be decrypted multiple times, expensive private key operations can be omitted. + * Implementation of the [PublicKeyDataDecryptorFactory] which caches decrypted session keys. That + * way, if a message needs to be decrypted multiple times, expensive private key operations can be + * omitted. * - * This implementation changes the behavior or [recoverSessionData] to first return any - * cache hits. - * If no hit is found, the method call is delegated to the underlying [PublicKeyDataDecryptorFactory]. - * The result of that is then placed in the cache and returned. + * This implementation changes the behavior or [recoverSessionData] to first return any cache hits. + * If no hit is found, the method call is delegated to the underlying + * [PublicKeyDataDecryptorFactory]. The result of that is then placed in the cache and returned. */ class CachingBcPublicKeyDataDecryptorFactory( - privateKey: PGPPrivateKey, - override val subkeyIdentifier: SubkeyIdentifier + privateKey: PGPPrivateKey, + override val subkeyIdentifier: SubkeyIdentifier ) : BcPublicKeyDataDecryptorFactory(privateKey), CustomPublicKeyDataDecryptorFactory { private val cachedSessions: MutableMap = mutableMapOf() - override fun recoverSessionData(keyAlgorithm: Int, secKeyData: Array): ByteArray = - lookupSessionKeyData(secKeyData) ?: - costlyRecoverSessionData(keyAlgorithm, secKeyData) - .also { cacheSessionKeyData(secKeyData, it) } + override fun recoverSessionData( + keyAlgorithm: Int, + secKeyData: Array + ): ByteArray = + lookupSessionKeyData(secKeyData) + ?: costlyRecoverSessionData(keyAlgorithm, secKeyData).also { + cacheSessionKeyData(secKeyData, it) + } private fun lookupSessionKeyData(secKeyData: Array): ByteArray? = - cachedSessions[toKey(secKeyData)]?.clone() + cachedSessions[toKey(secKeyData)]?.clone() - private fun costlyRecoverSessionData(keyAlgorithm: Int, secKeyData: Array): ByteArray = - super.recoverSessionData(keyAlgorithm, secKeyData) + private fun costlyRecoverSessionData( + keyAlgorithm: Int, + secKeyData: Array + ): ByteArray = super.recoverSessionData(keyAlgorithm, secKeyData) private fun cacheSessionKeyData(secKeyData: Array, sessionKey: ByteArray) { cachedSessions[toKey(secKeyData)] = sessionKey.clone() } private fun toKey(secKeyData: Array): String = - Base64.toBase64String(secKeyData[0]) + Base64.toBase64String(secKeyData[0]) fun clear() { cachedSessions.clear() } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/bouncycastle/extensions/PGPKeyRingExtensions.kt b/pgpainless-core/src/main/kotlin/org/bouncycastle/extensions/PGPKeyRingExtensions.kt index afa38aa6..611fa591 100644 --- a/pgpainless-core/src/main/kotlin/org/bouncycastle/extensions/PGPKeyRingExtensions.kt +++ b/pgpainless-core/src/main/kotlin/org/bouncycastle/extensions/PGPKeyRingExtensions.kt @@ -13,12 +13,10 @@ import org.pgpainless.PGPainless import org.pgpainless.key.OpenPgpFingerprint import org.pgpainless.key.SubkeyIdentifier -/** - * Return true, if this [PGPKeyRing] contains the subkey identified by the [SubkeyIdentifier]. - */ +/** Return true, if this [PGPKeyRing] contains the subkey identified by the [SubkeyIdentifier]. */ fun PGPKeyRing.matches(subkeyIdentifier: SubkeyIdentifier): Boolean = - this.publicKey.keyID == subkeyIdentifier.primaryKeyId && - this.getPublicKey(subkeyIdentifier.subkeyId) != null + this.publicKey.keyID == subkeyIdentifier.primaryKeyId && + this.getPublicKey(subkeyIdentifier.subkeyId) != null /** * Return true, if the [PGPKeyRing] contains a public key with the given key-ID. @@ -26,8 +24,7 @@ fun PGPKeyRing.matches(subkeyIdentifier: SubkeyIdentifier): Boolean = * @param keyId keyId * @return true if key with the given key-ID is present, false otherwise */ -fun PGPKeyRing.hasPublicKey(keyId: Long): Boolean = - this.getPublicKey(keyId) != null +fun PGPKeyRing.hasPublicKey(keyId: Long): Boolean = this.getPublicKey(keyId) != null /** * Return true, if the [PGPKeyRing] contains a public key with the given fingerprint. @@ -36,7 +33,7 @@ fun PGPKeyRing.hasPublicKey(keyId: Long): Boolean = * @return true if key with the given fingerprint is present, false otherwise */ fun PGPKeyRing.hasPublicKey(fingerprint: OpenPgpFingerprint): Boolean = - this.getPublicKey(fingerprint) != null + this.getPublicKey(fingerprint) != null /** * Return the [PGPPublicKey] with the given [OpenPgpFingerprint] or null, if no such key is present. @@ -45,36 +42,33 @@ fun PGPKeyRing.hasPublicKey(fingerprint: OpenPgpFingerprint): Boolean = * @return public key */ fun PGPKeyRing.getPublicKey(fingerprint: OpenPgpFingerprint): PGPPublicKey? = - this.getPublicKey(fingerprint.bytes) + this.getPublicKey(fingerprint.bytes) fun PGPKeyRing.requirePublicKey(keyId: Long): PGPPublicKey = - getPublicKey(keyId) ?: throw NoSuchElementException("OpenPGP key does not contain key with id ${keyId.openPgpKeyId()}.") + getPublicKey(keyId) + ?: throw NoSuchElementException( + "OpenPGP key does not contain key with id ${keyId.openPgpKeyId()}.") fun PGPKeyRing.requirePublicKey(fingerprint: OpenPgpFingerprint): PGPPublicKey = - getPublicKey(fingerprint) ?: throw NoSuchElementException("OpenPGP key does not contain key with fingerprint $fingerprint.") + getPublicKey(fingerprint) + ?: throw NoSuchElementException( + "OpenPGP key does not contain key with fingerprint $fingerprint.") /** - * Return the [PGPPublicKey] that matches the [OpenPgpFingerprint] of the given [PGPSignature]. - * If the [PGPSignature] does not carry an issuer-fingerprint subpacket, fall back to the issuer-keyID subpacket to - * identify the [PGPPublicKey] via its key-ID. + * Return the [PGPPublicKey] that matches the [OpenPgpFingerprint] of the given [PGPSignature]. If + * the [PGPSignature] does not carry an issuer-fingerprint subpacket, fall back to the issuer-keyID + * subpacket to identify the [PGPPublicKey] via its key-ID. */ fun PGPKeyRing.getPublicKeyFor(signature: PGPSignature): PGPPublicKey? = - signature.fingerprint?.let { this.getPublicKey(it) } ?: - this.getPublicKey(signature.keyID) + signature.fingerprint?.let { this.getPublicKey(it) } ?: this.getPublicKey(signature.keyID) -/** - * Return the [PGPPublicKey] that matches the key-ID of the given [PGPOnePassSignature] packet. - */ +/** Return the [PGPPublicKey] that matches the key-ID of the given [PGPOnePassSignature] packet. */ fun PGPKeyRing.getPublicKeyFor(onePassSignature: PGPOnePassSignature): PGPPublicKey? = - this.getPublicKey(onePassSignature.keyID) + this.getPublicKey(onePassSignature.keyID) -/** - * Return the [OpenPgpFingerprint] of this OpenPGP key. - */ +/** Return the [OpenPgpFingerprint] of this OpenPGP key. */ val PGPKeyRing.openPgpFingerprint: OpenPgpFingerprint get() = OpenPgpFingerprint.of(this) -/** - * Return this OpenPGP key as an ASCII armored String. - */ -fun PGPKeyRing.toAsciiArmor(): String = PGPainless.asciiArmor(this) \ No newline at end of file +/** Return this OpenPGP key as an ASCII armored String. */ +fun PGPKeyRing.toAsciiArmor(): String = PGPainless.asciiArmor(this) diff --git a/pgpainless-core/src/main/kotlin/org/bouncycastle/extensions/PGPPublicKeyExtensions.kt b/pgpainless-core/src/main/kotlin/org/bouncycastle/extensions/PGPPublicKeyExtensions.kt index ad51c6f4..847f1cf1 100644 --- a/pgpainless-core/src/main/kotlin/org/bouncycastle/extensions/PGPPublicKeyExtensions.kt +++ b/pgpainless-core/src/main/kotlin/org/bouncycastle/extensions/PGPPublicKeyExtensions.kt @@ -15,8 +15,8 @@ import org.pgpainless.key.OpenPgpFingerprint import org.pgpainless.key.generation.type.eddsa.EdDSACurve /** - * For secret keys of types [PublicKeyAlgorithm.ECDSA], [PublicKeyAlgorithm.ECDH] and [PublicKeyAlgorithm.EDDSA], - * this method returns the name of the underlying elliptic curve. + * For secret keys of types [PublicKeyAlgorithm.ECDSA], [PublicKeyAlgorithm.ECDH] and + * [PublicKeyAlgorithm.EDDSA], this method returns the name of the underlying elliptic curve. * * For other key types or unknown curves, this method throws an [IllegalArgumentException]. * @@ -24,27 +24,29 @@ import org.pgpainless.key.generation.type.eddsa.EdDSACurve */ fun PGPPublicKey.getCurveName(): String { PublicKeyAlgorithm.requireFromId(algorithm) - .let { - when (it) { - PublicKeyAlgorithm.ECDSA -> publicKeyPacket.key as ECDSAPublicBCPGKey - PublicKeyAlgorithm.ECDH -> publicKeyPacket.key as ECDHPublicBCPGKey - PublicKeyAlgorithm.EDDSA -> publicKeyPacket.key as EdDSAPublicBCPGKey - else -> throw IllegalArgumentException("No an elliptic curve public key ($it).") - } + .let { + when (it) { + PublicKeyAlgorithm.ECDSA -> publicKeyPacket.key as ECDSAPublicBCPGKey + PublicKeyAlgorithm.ECDH -> publicKeyPacket.key as ECDHPublicBCPGKey + PublicKeyAlgorithm.EDDSA -> publicKeyPacket.key as EdDSAPublicBCPGKey + else -> throw IllegalArgumentException("No an elliptic curve public key ($it).") } - .let { if (it.curveOID == GNUObjectIdentifiers.Ed25519) return EdDSACurve._Ed25519.curveName else it.curveOID} - .let { it to ECUtil.getCurveName(it) } - .let { if (it.second != null) return it.second else throw IllegalArgumentException("Unknown curve: ${it.first}") } + } + .let { + if (it.curveOID == GNUObjectIdentifiers.Ed25519) return EdDSACurve._Ed25519.curveName + else it.curveOID + } + .let { it to ECUtil.getCurveName(it) } + .let { + if (it.second != null) return it.second + else throw IllegalArgumentException("Unknown curve: ${it.first}") + } } -/** - * Return the [PublicKeyAlgorithm] of this key. - */ +/** Return the [PublicKeyAlgorithm] of this key. */ val PGPPublicKey.publicKeyAlgorithm: PublicKeyAlgorithm get() = PublicKeyAlgorithm.requireFromId(algorithm) -/** - * Return the [OpenPgpFingerprint] of this key. - */ +/** Return the [OpenPgpFingerprint] of this key. */ val PGPPublicKey.openPgpFingerprint: OpenPgpFingerprint get() = OpenPgpFingerprint.of(this) diff --git a/pgpainless-core/src/main/kotlin/org/bouncycastle/extensions/PGPSecretKeyExtensions.kt b/pgpainless-core/src/main/kotlin/org/bouncycastle/extensions/PGPSecretKeyExtensions.kt index 3d759d1a..6049742c 100644 --- a/pgpainless-core/src/main/kotlin/org/bouncycastle/extensions/PGPSecretKeyExtensions.kt +++ b/pgpainless-core/src/main/kotlin/org/bouncycastle/extensions/PGPSecretKeyExtensions.kt @@ -7,7 +7,6 @@ package org.bouncycastle.extensions import org.bouncycastle.bcpg.S2K import org.bouncycastle.openpgp.PGPException import org.bouncycastle.openpgp.PGPPrivateKey -import org.bouncycastle.openpgp.PGPPublicKey import org.bouncycastle.openpgp.PGPSecretKey import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor import org.pgpainless.algorithm.PublicKeyAlgorithm @@ -28,7 +27,7 @@ import org.pgpainless.util.Passphrase */ @Throws(PGPException::class, KeyIntegrityException::class) fun PGPSecretKey.unlock(passphrase: Passphrase): PGPPrivateKey = - UnlockSecretKey.unlockSecretKey(this, passphrase) + UnlockSecretKey.unlockSecretKey(this, passphrase) /** * Unlock the secret key to get its [PGPPrivateKey]. @@ -39,8 +38,9 @@ fun PGPSecretKey.unlock(passphrase: Passphrase): PGPPrivateKey = */ @Throws(PGPException::class, KeyIntegrityException::class) @JvmOverloads -fun PGPSecretKey.unlock(protector: SecretKeyRingProtector = SecretKeyRingProtector.unprotectedKeys()): PGPPrivateKey = - UnlockSecretKey.unlockSecretKey(this, protector) +fun PGPSecretKey.unlock( + protector: SecretKeyRingProtector = SecretKeyRingProtector.unprotectedKeys() +): PGPPrivateKey = UnlockSecretKey.unlockSecretKey(this, protector) /** * Unlock the secret key to get its [PGPPrivateKey]. @@ -74,14 +74,10 @@ fun PGPSecretKey?.isDecrypted(): Boolean = (this == null) || (s2KUsage == 0) */ fun PGPSecretKey?.hasDummyS2K(): Boolean = (this != null) && (s2K?.type == S2K.GNU_DUMMY_S2K) -/** - * Return the [PublicKeyAlgorithm] of this key. - */ +/** Return the [PublicKeyAlgorithm] of this key. */ val PGPSecretKey.publicKeyAlgorithm: PublicKeyAlgorithm get() = publicKey.publicKeyAlgorithm -/** - * Return the [OpenPgpFingerprint] of this key. - */ +/** Return the [OpenPgpFingerprint] of this key. */ val PGPSecretKey.openPgpFingerprint: OpenPgpFingerprint get() = OpenPgpFingerprint.of(this) diff --git a/pgpainless-core/src/main/kotlin/org/bouncycastle/extensions/PGPSecretKeyRingExtensions.kt b/pgpainless-core/src/main/kotlin/org/bouncycastle/extensions/PGPSecretKeyRingExtensions.kt index d0529d51..2116c748 100644 --- a/pgpainless-core/src/main/kotlin/org/bouncycastle/extensions/PGPSecretKeyRingExtensions.kt +++ b/pgpainless-core/src/main/kotlin/org/bouncycastle/extensions/PGPSecretKeyRingExtensions.kt @@ -8,9 +8,7 @@ import openpgp.openPgpKeyId import org.bouncycastle.openpgp.* import org.pgpainless.key.OpenPgpFingerprint -/** - * OpenPGP certificate containing the public keys of this OpenPGP key. - */ +/** OpenPGP certificate containing the public keys of this OpenPGP key. */ val PGPSecretKeyRing.certificate: PGPPublicKeyRing get() = PGPPublicKeyRing(this.publicKeys.asSequence().toList()) @@ -20,8 +18,7 @@ val PGPSecretKeyRing.certificate: PGPPublicKeyRing * @param keyId keyId of the secret key * @return true, if the [PGPSecretKeyRing] has a matching [PGPSecretKey], false otherwise */ -fun PGPSecretKeyRing.hasSecretKey(keyId: Long): Boolean = - this.getSecretKey(keyId) != null +fun PGPSecretKeyRing.hasSecretKey(keyId: Long): Boolean = this.getSecretKey(keyId) != null /** * Return true, if the [PGPSecretKeyRing] contains a [PGPSecretKey] with the given fingerprint. @@ -30,7 +27,7 @@ fun PGPSecretKeyRing.hasSecretKey(keyId: Long): Boolean = * @return true, if the [PGPSecretKeyRing] has a matching [PGPSecretKey], false otherwise */ fun PGPSecretKeyRing.hasSecretKey(fingerprint: OpenPgpFingerprint): Boolean = - this.getSecretKey(fingerprint) != null + this.getSecretKey(fingerprint) != null /** * Return the [PGPSecretKey] with the given [OpenPgpFingerprint]. @@ -39,41 +36,44 @@ fun PGPSecretKeyRing.hasSecretKey(fingerprint: OpenPgpFingerprint): Boolean = * @return the secret key or null */ fun PGPSecretKeyRing.getSecretKey(fingerprint: OpenPgpFingerprint): PGPSecretKey? = - this.getSecretKey(fingerprint.bytes) + this.getSecretKey(fingerprint.bytes) /** * Return the [PGPSecretKey] with the given key-ID. * - * @throws NoSuchElementException if the OpenPGP key doesn't contain a secret key with the given key-ID + * @throws NoSuchElementException if the OpenPGP key doesn't contain a secret key with the given + * key-ID */ fun PGPSecretKeyRing.requireSecretKey(keyId: Long): PGPSecretKey = - getSecretKey(keyId) ?: throw NoSuchElementException("OpenPGP key does not contain key with id ${keyId.openPgpKeyId()}.") + getSecretKey(keyId) + ?: throw NoSuchElementException( + "OpenPGP key does not contain key with id ${keyId.openPgpKeyId()}.") /** * Return the [PGPSecretKey] with the given fingerprint. * - * @throws NoSuchElementException of the OpenPGP key doesn't contain a secret key with the given fingerprint + * @throws NoSuchElementException of the OpenPGP key doesn't contain a secret key with the given + * fingerprint */ fun PGPSecretKeyRing.requireSecretKey(fingerprint: OpenPgpFingerprint): PGPSecretKey = - getSecretKey(fingerprint) ?: throw NoSuchElementException("OpenPGP key does not contain key with fingerprint $fingerprint.") + getSecretKey(fingerprint) + ?: throw NoSuchElementException( + "OpenPGP key does not contain key with fingerprint $fingerprint.") /** - * Return the [PGPSecretKey] that matches the [OpenPgpFingerprint] of the given [PGPSignature]. - * If the [PGPSignature] does not carry an issuer-fingerprint subpacket, fall back to the issuer-keyID subpacket to - * identify the [PGPSecretKey] via its key-ID. + * Return the [PGPSecretKey] that matches the [OpenPgpFingerprint] of the given [PGPSignature]. If + * the [PGPSignature] does not carry an issuer-fingerprint subpacket, fall back to the issuer-keyID + * subpacket to identify the [PGPSecretKey] via its key-ID. */ fun PGPSecretKeyRing.getSecretKeyFor(signature: PGPSignature): PGPSecretKey? = - signature.fingerprint?.let { this.getSecretKey(it) } ?: - this.getSecretKey(signature.keyID) + signature.fingerprint?.let { this.getSecretKey(it) } ?: this.getSecretKey(signature.keyID) -/** - * Return the [PGPSecretKey] that matches the key-ID of the given [PGPOnePassSignature] packet. - */ +/** Return the [PGPSecretKey] that matches the key-ID of the given [PGPOnePassSignature] packet. */ fun PGPSecretKeyRing.getSecretKeyFor(onePassSignature: PGPOnePassSignature): PGPSecretKey? = - this.getSecretKey(onePassSignature.keyID) + this.getSecretKey(onePassSignature.keyID) fun PGPSecretKeyRing.getSecretKeyFor(pkesk: PGPPublicKeyEncryptedData): PGPSecretKey? = - when(pkesk.version) { - 3 -> this.getSecretKey(pkesk.keyID) - else -> throw NotImplementedError("Version 6 PKESKs are not yet supported.") - } \ No newline at end of file + when (pkesk.version) { + 3 -> this.getSecretKey(pkesk.keyID) + else -> throw NotImplementedError("Version 6 PKESKs are not yet supported.") + } diff --git a/pgpainless-core/src/main/kotlin/org/bouncycastle/extensions/PGPSignatureExtensions.kt b/pgpainless-core/src/main/kotlin/org/bouncycastle/extensions/PGPSignatureExtensions.kt index 4fe97bc7..2be011bd 100644 --- a/pgpainless-core/src/main/kotlin/org/bouncycastle/extensions/PGPSignatureExtensions.kt +++ b/pgpainless-core/src/main/kotlin/org/bouncycastle/extensions/PGPSignatureExtensions.kt @@ -4,6 +4,7 @@ package org.bouncycastle.extensions +import java.util.* import openpgp.plusSeconds import org.bouncycastle.openpgp.PGPPublicKey import org.bouncycastle.openpgp.PGPSignature @@ -12,84 +13,84 @@ import org.pgpainless.algorithm.SignatureType import org.pgpainless.key.OpenPgpFingerprint import org.pgpainless.key.util.RevocationAttributes.Reason import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil -import java.util.* /** * Return the value of the KeyExpirationDate subpacket, or null, if the signature does not carry * such a subpacket. */ fun PGPSignature.getKeyExpirationDate(keyCreationDate: Date): Date? = - SignatureSubpacketsUtil.getKeyExpirationTime(this) - ?.let { keyCreationDate.plusSeconds(it.time) } + SignatureSubpacketsUtil.getKeyExpirationTime(this)?.let { keyCreationDate.plusSeconds(it.time) } /** - * Return the value of the signature ExpirationTime subpacket, or null, if the signature - * does not carry such a subpacket. + * Return the value of the signature ExpirationTime subpacket, or null, if the signature does not + * carry such a subpacket. */ val PGPSignature.signatureExpirationDate: Date? - get() = SignatureSubpacketsUtil.getSignatureExpirationTime(this) - ?.let { this.creationTime.plusSeconds(it.time) } + get() = + SignatureSubpacketsUtil.getSignatureExpirationTime(this)?.let { + this.creationTime.plusSeconds(it.time) + } -/** - * Return true, if the signature is expired at the given reference time. - */ +/** Return true, if the signature is expired at the given reference time. */ fun PGPSignature.isExpired(referenceTime: Date = Date()) = - signatureExpirationDate?.let { referenceTime >= it } ?: false + signatureExpirationDate?.let { referenceTime >= it } ?: false /** * Return the key-ID of the issuer, determined by examining the IssuerKeyId and IssuerFingerprint * subpackets of the signature. */ val PGPSignature.issuerKeyId: Long - get() = when (version) { - 2, 3 -> keyID - else -> { - SignatureSubpacketsUtil.getIssuerKeyIdAsLong(this) - ?.let { if (it != 0L) it else null } - ?: fingerprint?.keyId - ?: 0L + get() = + when (version) { + 2, + 3 -> keyID + else -> { + SignatureSubpacketsUtil.getIssuerKeyIdAsLong(this)?.let { + if (it != 0L) it else null + } + ?: fingerprint?.keyId ?: 0L + } } - } -/** - * Return true, if the signature was likely issued by a key with the given fingerprint. - */ +/** Return true, if the signature was likely issued by a key with the given fingerprint. */ fun PGPSignature.wasIssuedBy(fingerprint: OpenPgpFingerprint): Boolean = - this.fingerprint?.let { it.keyId == fingerprint.keyId } ?: (keyID == fingerprint.keyId) + this.fingerprint?.let { it.keyId == fingerprint.keyId } ?: (keyID == fingerprint.keyId) /** * Return true, if the signature was likely issued by a key with the given fingerprint. + * * @param fingerprint fingerprint bytes */ @Deprecated("Discouraged in favor of method taking an OpenPgpFingerprint.") fun PGPSignature.wasIssuedBy(fingerprint: ByteArray): Boolean = - try { - wasIssuedBy(OpenPgpFingerprint.parseFromBinary(fingerprint)) - } catch (e : IllegalArgumentException) { - // Unknown fingerprint length / format - false - } - -fun PGPSignature.wasIssuedBy(key: PGPPublicKey): Boolean = - wasIssuedBy(OpenPgpFingerprint.of(key)) - -/** - * Return true, if this signature is a hard revocation. - */ -val PGPSignature.isHardRevocation - get() = when (SignatureType.requireFromCode(signatureType)) { - SignatureType.KEY_REVOCATION, SignatureType.SUBKEY_REVOCATION, SignatureType.CERTIFICATION_REVOCATION -> { - SignatureSubpacketsUtil.getRevocationReason(this) - ?.let { Reason.isHardRevocation(it.revocationReason) } - ?: true // no reason -> hard revocation - } - else -> false // Not a revocation + try { + wasIssuedBy(OpenPgpFingerprint.parseFromBinary(fingerprint)) + } catch (e: IllegalArgumentException) { + // Unknown fingerprint length / format + false } +fun PGPSignature.wasIssuedBy(key: PGPPublicKey): Boolean = wasIssuedBy(OpenPgpFingerprint.of(key)) + +/** Return true, if this signature is a hard revocation. */ +val PGPSignature.isHardRevocation + get() = + when (SignatureType.requireFromCode(signatureType)) { + SignatureType.KEY_REVOCATION, + SignatureType.SUBKEY_REVOCATION, + SignatureType.CERTIFICATION_REVOCATION -> { + SignatureSubpacketsUtil.getRevocationReason(this)?.let { + Reason.isHardRevocation(it.revocationReason) + } + ?: true // no reason -> hard revocation + } + else -> false // Not a revocation + } + fun PGPSignature?.toRevocationState() = - if (this == null) RevocationState.notRevoked() - else if (isHardRevocation) RevocationState.hardRevoked() - else RevocationState.softRevoked(creationTime) + if (this == null) RevocationState.notRevoked() + else if (isHardRevocation) RevocationState.hardRevoked() + else RevocationState.softRevoked(creationTime) val PGPSignature.fingerprint: OpenPgpFingerprint? - get() = SignatureSubpacketsUtil.getIssuerFingerprintAsOpenPgpFingerprint(this) \ No newline at end of file + get() = SignatureSubpacketsUtil.getIssuerFingerprintAsOpenPgpFingerprint(this) diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/PGPainless.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/PGPainless.kt index 6aa9799e..d866ac93 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/PGPainless.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/PGPainless.kt @@ -4,6 +4,8 @@ package org.pgpainless +import java.io.OutputStream +import java.util.* import org.bouncycastle.openpgp.PGPKeyRing import org.bouncycastle.openpgp.PGPPublicKeyRing import org.bouncycastle.openpgp.PGPSecretKeyRing @@ -19,8 +21,6 @@ import org.pgpainless.key.parsing.KeyRingReader import org.pgpainless.key.util.KeyRingUtils import org.pgpainless.policy.Policy import org.pgpainless.util.ArmorUtils -import java.io.OutputStream -import java.util.* class PGPainless private constructor() { @@ -28,25 +28,24 @@ class PGPainless private constructor() { /** * Generate a fresh OpenPGP key ring from predefined templates. + * * @return templates */ - @JvmStatic - fun generateKeyRing() = KeyRingTemplates() + @JvmStatic fun generateKeyRing() = KeyRingTemplates() /** * Build a custom OpenPGP key ring. * * @return builder */ - @JvmStatic - fun buildKeyRing() = KeyRingBuilder() + @JvmStatic fun buildKeyRing() = KeyRingBuilder() /** * Read an existing OpenPGP key ring. + * * @return builder */ - @JvmStatic - fun readKeyRing() = KeyRingReader() + @JvmStatic fun readKeyRing() = KeyRingReader() /** * Extract a public key certificate from a secret key. @@ -56,10 +55,11 @@ class PGPainless private constructor() { */ @JvmStatic fun extractCertificate(secretKey: PGPSecretKeyRing) = - KeyRingUtils.publicKeyRingFrom(secretKey) + KeyRingUtils.publicKeyRingFrom(secretKey) /** - * Merge two copies of the same certificate (e.g. an old copy, and one retrieved from a key server) together. + * Merge two copies of the same certificate (e.g. an old copy, and one retrieved from a key + * server) together. * * @param originalCopy local, older copy of the cert * @param updatedCopy updated, newer copy of the cert @@ -67,31 +67,27 @@ class PGPainless private constructor() { * @throws PGPException in case of an error */ @JvmStatic - fun mergeCertificate(originalCopy: PGPPublicKeyRing, - updatedCopy: PGPPublicKeyRing) = - PGPPublicKeyRing.join(originalCopy, updatedCopy) + fun mergeCertificate(originalCopy: PGPPublicKeyRing, updatedCopy: PGPPublicKeyRing) = + PGPPublicKeyRing.join(originalCopy, updatedCopy) /** * Wrap a key or certificate in ASCII armor. * * @param key key or certificate * @return ascii armored string - * * @throws IOException in case of an error during the armoring process */ @JvmStatic fun asciiArmor(key: PGPKeyRing) = - if (key is PGPSecretKeyRing) - ArmorUtils.toAsciiArmoredString(key) - else - ArmorUtils.toAsciiArmoredString(key as PGPPublicKeyRing) + if (key is PGPSecretKeyRing) ArmorUtils.toAsciiArmoredString(key) + else ArmorUtils.toAsciiArmoredString(key as PGPPublicKeyRing) /** - * Wrap a key of certificate in ASCII armor and write the result into the given [OutputStream]. + * Wrap a key of certificate in ASCII armor and write the result into the given + * [OutputStream]. * * @param key key or certificate * @param outputStream output stream - * * @throws IOException in case of an error during the armoring process */ @JvmStatic @@ -106,33 +102,34 @@ class PGPainless private constructor() { * * @param signature detached signature * @return ascii armored string - * * @throws IOException in case of an error during the armoring process */ @JvmStatic fun asciiArmor(signature: PGPSignature) = ArmorUtils.toAsciiArmoredString(signature) /** - * Create an [EncryptionBuilder], which can be used to encrypt and/or sign data using OpenPGP. + * Create an [EncryptionBuilder], which can be used to encrypt and/or sign data using + * OpenPGP. * * @return builder */ - @JvmStatic - fun encryptAndOrSign() = EncryptionBuilder() + @JvmStatic fun encryptAndOrSign() = EncryptionBuilder() /** - * Create a [DecryptionBuilder], which can be used to decrypt and/or verify data using OpenPGP. + * Create a [DecryptionBuilder], which can be used to decrypt and/or verify data using + * OpenPGP. * * @return builder */ - @JvmStatic - fun decryptAndOrVerify() = DecryptionBuilder() + @JvmStatic fun decryptAndOrVerify() = DecryptionBuilder() /** - * Make changes to a secret key at the given reference time. - * This method can be used to change key expiration dates and passphrases, or add/revoke user-ids and subkeys. + * Make changes to a secret key at the given reference time. This method can be used to + * change key expiration dates and passphrases, or add/revoke user-ids and subkeys. + * *

- * After making the desired changes in the builder, the modified key can be extracted using {@link SecretKeyRingEditorInterface#done()}. + * After making the desired changes in the builder, the modified key can be extracted using + * {@link SecretKeyRingEditorInterface#done()}. * * @param secretKeys secret key ring * @param referenceTime reference time used as signature creation date @@ -141,11 +138,12 @@ class PGPainless private constructor() { @JvmStatic @JvmOverloads fun modifyKeyRing(secretKey: PGPSecretKeyRing, referenceTime: Date = Date()) = - SecretKeyRingEditor(secretKey, referenceTime) + SecretKeyRingEditor(secretKey, referenceTime) /** - * Quickly access information about a [org.bouncycastle.openpgp.PGPPublicKeyRing] / [PGPSecretKeyRing]. - * This method can be used to determine expiration dates, key flags and other information about a key at a specific time. + * Quickly access information about a [org.bouncycastle.openpgp.PGPPublicKeyRing] / + * [PGPSecretKeyRing]. This method can be used to determine expiration dates, key flags and + * other information about a key at a specific time. * * @param keyRing key ring * @param referenceTime date of inspection @@ -154,22 +152,20 @@ class PGPainless private constructor() { @JvmStatic @JvmOverloads fun inspectKeyRing(key: PGPKeyRing, referenceTime: Date = Date()) = - KeyRingInfo(key, referenceTime) + KeyRingInfo(key, referenceTime) /** * Access, and make changes to PGPainless policy on acceptable/default algorithms etc. * * @return policy */ - @JvmStatic - fun getPolicy() = Policy.getInstance() + @JvmStatic fun getPolicy() = Policy.getInstance() /** * Create different kinds of signatures on other keys. * * @return builder */ - @JvmStatic - fun certify() = CertifyCertificate() + @JvmStatic fun certify() = CertifyCertificate() } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/AEADAlgorithm.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/AEADAlgorithm.kt index 61672122..253b37dd 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/AEADAlgorithm.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/AEADAlgorithm.kt @@ -4,10 +4,7 @@ package org.pgpainless.algorithm -enum class AEADAlgorithm( - val algorithmId: Int, - val ivLength: Int, - val tagLength: Int) { +enum class AEADAlgorithm(val algorithmId: Int, val ivLength: Int, val tagLength: Int) { EAX(1, 16, 16), OCB(2, 15, 16), GCM(3, 12, 16), @@ -16,15 +13,12 @@ enum class AEADAlgorithm( companion object { @JvmStatic fun fromId(id: Int): AEADAlgorithm? { - return values().firstOrNull { - algorithm -> algorithm.algorithmId == id - } + return values().firstOrNull { algorithm -> algorithm.algorithmId == id } } @JvmStatic fun requireFromId(id: Int): AEADAlgorithm { - return fromId(id) ?: - throw NoSuchElementException("No AEADAlgorithm found for id $id") + return fromId(id) ?: throw NoSuchElementException("No AEADAlgorithm found for id $id") } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/AlgorithmSuite.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/AlgorithmSuite.kt index 0e4997fc..867bf1b8 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/AlgorithmSuite.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/AlgorithmSuite.kt @@ -5,9 +5,10 @@ package org.pgpainless.algorithm class AlgorithmSuite( - symmetricKeyAlgorithms: List, - hashAlgorithms: List, - compressionAlgorithms: List) { + symmetricKeyAlgorithms: List, + hashAlgorithms: List, + compressionAlgorithms: List +) { val symmetricKeyAlgorithms: Set = symmetricKeyAlgorithms.toSet() val hashAlgorithms: Set = hashAlgorithms.toSet() @@ -16,30 +17,31 @@ class AlgorithmSuite( companion object { @JvmStatic - val defaultSymmetricKeyAlgorithms = listOf( + val defaultSymmetricKeyAlgorithms = + listOf( SymmetricKeyAlgorithm.AES_256, SymmetricKeyAlgorithm.AES_192, SymmetricKeyAlgorithm.AES_128) @JvmStatic - val defaultHashAlgorithms = listOf( + val defaultHashAlgorithms = + listOf( HashAlgorithm.SHA512, HashAlgorithm.SHA384, HashAlgorithm.SHA256, HashAlgorithm.SHA224) @JvmStatic - val defaultCompressionAlgorithms = listOf( + val defaultCompressionAlgorithms = + listOf( CompressionAlgorithm.ZLIB, CompressionAlgorithm.BZIP2, CompressionAlgorithm.ZIP, CompressionAlgorithm.UNCOMPRESSED) @JvmStatic - val defaultAlgorithmSuite = AlgorithmSuite( - defaultSymmetricKeyAlgorithms, - defaultHashAlgorithms, - defaultCompressionAlgorithms) + val defaultAlgorithmSuite = + AlgorithmSuite( + defaultSymmetricKeyAlgorithms, defaultHashAlgorithms, defaultCompressionAlgorithms) } - -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/CertificationType.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/CertificationType.kt index 33025ad6..5617109c 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/CertificationType.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/CertificationType.kt @@ -4,18 +4,17 @@ package org.pgpainless.algorithm -enum class CertificationType( - val signatureType: SignatureType -) { +enum class CertificationType(val signatureType: SignatureType) { /** - * The issuer of this certification does not make any particular assertion as to how well the certifier has - * checked that the owner of the key is in fact the person described by the User ID. + * The issuer of this certification does not make any particular assertion as to how well the + * certifier has checked that the owner of the key is in fact the person described by the User + * ID. */ GENERIC(SignatureType.GENERIC_CERTIFICATION), /** - * The issuer of this certification has not done any verification of the claim that the owner of this key is - * the User ID specified. + * The issuer of this certification has not done any verification of the claim that the owner of + * this key is the User ID specified. */ NONE(SignatureType.NO_CERTIFICATION), @@ -31,4 +30,4 @@ enum class CertificationType( ; fun asSignatureType() = signatureType -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/CompressionAlgorithm.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/CompressionAlgorithm.kt index 73179722..da4085bd 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/CompressionAlgorithm.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/CompressionAlgorithm.kt @@ -20,22 +20,20 @@ enum class CompressionAlgorithm(val algorithmId: Int) { companion object { /** - * Return the [CompressionAlgorithm] value that corresponds to the provided numerical id. - * If an invalid id is provided, null is returned. + * Return the [CompressionAlgorithm] value that corresponds to the provided numerical id. If + * an invalid id is provided, null is returned. * * @param id id * @return compression algorithm */ @JvmStatic fun fromId(id: Int): CompressionAlgorithm? { - return values().firstOrNull { - c -> c.algorithmId == id - } + return values().firstOrNull { c -> c.algorithmId == id } } /** - * Return the [CompressionAlgorithm] value that corresponds to the provided numerical id. - * If an invalid id is provided, throw an [NoSuchElementException]. + * Return the [CompressionAlgorithm] value that corresponds to the provided numerical id. If + * an invalid id is provided, throw an [NoSuchElementException]. * * @param id id * @return compression algorithm @@ -43,8 +41,8 @@ enum class CompressionAlgorithm(val algorithmId: Int) { */ @JvmStatic fun requireFromId(id: Int): CompressionAlgorithm { - return fromId(id) ?: - throw NoSuchElementException("No CompressionAlgorithm found for id $id") + return fromId(id) + ?: throw NoSuchElementException("No CompressionAlgorithm found for id $id") } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/DocumentSignatureType.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/DocumentSignatureType.kt index b3cd17ac..e41a4605 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/DocumentSignatureType.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/DocumentSignatureType.kt @@ -6,15 +6,11 @@ package org.pgpainless.algorithm enum class DocumentSignatureType(val signatureType: SignatureType) { - /** - * Signature is calculated over the unchanged binary data. - */ + /** Signature is calculated over the unchanged binary data. */ BINARY_DOCUMENT(SignatureType.BINARY_DOCUMENT), /** - * The signature is calculated over the text data with its line endings - * converted to ``. + * The signature is calculated over the text data with its line endings converted to ``. */ CANONICAL_TEXT_DOCUMENT(SignatureType.CANONICAL_TEXT_DOCUMENT), - ; -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/EncryptionPurpose.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/EncryptionPurpose.kt index f7e5ce2d..1b4bbe6e 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/EncryptionPurpose.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/EncryptionPurpose.kt @@ -5,18 +5,10 @@ package org.pgpainless.algorithm enum class EncryptionPurpose { - /** - * The stream will encrypt communication that goes over the wire. - * E.g. EMail, Chat... - */ + /** The stream will encrypt communication that goes over the wire. E.g. EMail, Chat... */ COMMUNICATIONS, - /** - * The stream will encrypt data at rest. - * E.g. Encrypted backup... - */ + /** The stream will encrypt data at rest. E.g. Encrypted backup... */ STORAGE, - /** - * The stream will use keys with either flags to encrypt the data. - */ + /** The stream will use keys with either flags to encrypt the data. */ ANY -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/Feature.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/Feature.kt index 2e0058b5..3f9be1f5 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/Feature.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/Feature.kt @@ -12,42 +12,44 @@ package org.pgpainless.algorithm enum class Feature(val featureId: Byte) { /** - * Support for Symmetrically Encrypted Integrity Protected Data Packets (version 1) using Modification - * Detection Code Packets. + * Support for Symmetrically Encrypted Integrity Protected Data Packets (version 1) using + * Modification Detection Code Packets. * - * See [RFC-4880 §5.14: Modification Detection Code Packet](https://tools.ietf.org/html/rfc4880#section-5.14) + * See + * [RFC-4880 §5.14: Modification Detection Code Packet](https://tools.ietf.org/html/rfc4880#section-5.14) */ MODIFICATION_DETECTION(0x01), /** - * Support for Authenticated Encryption with Additional Data (AEAD). - * If a key announces this feature, it signals support for consuming AEAD Encrypted Data Packets. + * Support for Authenticated Encryption with Additional Data (AEAD). If a key announces this + * feature, it signals support for consuming AEAD Encrypted Data Packets. * - * NOTE: PGPAINLESS DOES NOT YET SUPPORT THIS FEATURE!!! - * NOTE: This value is currently RESERVED. + * NOTE: PGPAINLESS DOES NOT YET SUPPORT THIS FEATURE!!! NOTE: This value is currently RESERVED. * - * See [AEAD Encrypted Data Packet](https://openpgp-wg.gitlab.io/rfc4880bis/#name-aead-encrypted-data-packet-) + * See + * [AEAD Encrypted Data Packet](https://openpgp-wg.gitlab.io/rfc4880bis/#name-aead-encrypted-data-packet-) */ GNUPG_AEAD_ENCRYPTED_DATA(0x02), /** - * If a key announces this feature, it is a version 5 public key. - * The version 5 format is similar to the version 4 format except for the addition of a count for the key material. - * This count helps to parse secret key packets (which are an extension of the public key packet format) in the case - * of an unknown algorithm. - * In addition, fingerprints of version 5 keys are calculated differently from version 4 keys. + * If a key announces this feature, it is a version 5 public key. The version 5 format is + * similar to the version 4 format except for the addition of a count for the key material. This + * count helps to parse secret key packets (which are an extension of the public key packet + * format) in the case of an unknown algorithm. In addition, fingerprints of version 5 keys are + * calculated differently from version 4 keys. * - * NOTE: PGPAINLESS DOES NOT YET SUPPORT THIS FEATURE!!! - * NOTE: This value is currently RESERVED. + * NOTE: PGPAINLESS DOES NOT YET SUPPORT THIS FEATURE!!! NOTE: This value is currently RESERVED. * - * See [Public-Key Packet Formats](https://openpgp-wg.gitlab.io/rfc4880bis/#name-public-key-packet-formats) + * See + * [Public-Key Packet Formats](https://openpgp-wg.gitlab.io/rfc4880bis/#name-public-key-packet-formats) */ GNUPG_VERSION_5_PUBLIC_KEY(0x04), /** * Support for Symmetrically Encrypted Integrity Protected Data packet version 2. * - * See [crypto-refresh-06 §5.13.2. Version 2 Sym. Encrypted Integrity Protected Data Packet Format](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-06.html#version-two-seipd) + * See + * [crypto-refresh-06 §5.13.2. Version 2 Sym. Encrypted Integrity Protected Data Packet Format](https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-06.html#version-two-seipd) */ MODIFICATION_DETECTION_2(0x08), ; @@ -55,29 +57,26 @@ enum class Feature(val featureId: Byte) { companion object { @JvmStatic fun fromId(id: Byte): Feature? { - return values().firstOrNull { - f -> f.featureId == id - } + return values().firstOrNull { f -> f.featureId == id } } @JvmStatic fun requireFromId(id: Byte): Feature { - return fromId(id) ?: - throw NoSuchElementException("Unknown feature id encountered: $id") + return fromId(id) ?: throw NoSuchElementException("Unknown feature id encountered: $id") } @JvmStatic fun fromBitmask(bitmask: Int): List { - return values().filter { - it.featureId.toInt() and bitmask != 0 - } + return values().filter { it.featureId.toInt() and bitmask != 0 } } @JvmStatic fun toBitmask(vararg features: Feature): Byte { - return features.map { it.featureId.toInt() } - .reduceOrNull { mask, f -> mask or f }?.toByte() - ?: 0 + return features + .map { it.featureId.toInt() } + .reduceOrNull { mask, f -> mask or f } + ?.toByte() + ?: 0 } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/HashAlgorithm.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/HashAlgorithm.kt index 9c433efe..3360e7fe 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/HashAlgorithm.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/HashAlgorithm.kt @@ -11,36 +11,33 @@ package org.pgpainless.algorithm */ enum class HashAlgorithm(val algorithmId: Int, val algorithmName: String) { - @Deprecated("MD5 is deprecated") - MD5 (1, "MD5"), - SHA1 (2, "SHA1"), - RIPEMD160 (3, "RIPEMD160"), - SHA256 (8, "SHA256"), - SHA384 (9, "SHA384"), - SHA512 (10, "SHA512"), - SHA224 (11, "SHA224"), - SHA3_256 (12, "SHA3-256"), - SHA3_512 (14, "SHA3-512"), + @Deprecated("MD5 is deprecated") MD5(1, "MD5"), + SHA1(2, "SHA1"), + RIPEMD160(3, "RIPEMD160"), + SHA256(8, "SHA256"), + SHA384(9, "SHA384"), + SHA512(10, "SHA512"), + SHA224(11, "SHA224"), + SHA3_256(12, "SHA3-256"), + SHA3_512(14, "SHA3-512"), ; companion object { /** - * Return the [HashAlgorithm] value that corresponds to the provided algorithm id. - * If an invalid algorithm id was provided, null is returned. + * Return the [HashAlgorithm] value that corresponds to the provided algorithm id. If an + * invalid algorithm id was provided, null is returned. * * @param id numeric id * @return enum value */ @JvmStatic fun fromId(id: Int): HashAlgorithm? { - return values().firstOrNull { - h -> h.algorithmId == id - } + return values().firstOrNull { h -> h.algorithmId == id } } /** - * Return the [HashAlgorithm] value that corresponds to the provided algorithm id. - * If an invalid algorithm id was provided, throw a [NoSuchElementException]. + * Return the [HashAlgorithm] value that corresponds to the provided algorithm id. If an + * invalid algorithm id was provided, throw a [NoSuchElementException]. * * @param id algorithm id * @return enum value @@ -48,15 +45,15 @@ enum class HashAlgorithm(val algorithmId: Int, val algorithmName: String) { */ @JvmStatic fun requireFromId(id: Int): HashAlgorithm { - return fromId(id) ?: - throw NoSuchElementException("No HashAlgorithm found for id $id") + return fromId(id) ?: throw NoSuchElementException("No HashAlgorithm found for id $id") } /** - * Return the [HashAlgorithm] value that corresponds to the provided name. - * If an invalid algorithm name was provided, null is returned. + * Return the [HashAlgorithm] value that corresponds to the provided name. If an invalid + * algorithm name was provided, null is returned. * - * See [RFC4880: §9.4 Hash Algorithms](https://datatracker.ietf.org/doc/html/rfc4880#section-9.4) + * See + * [RFC4880: §9.4 Hash Algorithms](https://datatracker.ietf.org/doc/html/rfc4880#section-9.4) * for a list of algorithms and names. * * @param name text name @@ -65,12 +62,9 @@ enum class HashAlgorithm(val algorithmId: Int, val algorithmName: String) { @JvmStatic fun fromName(name: String): HashAlgorithm? { return name.uppercase().let { algoName -> - values().firstOrNull { - it.algorithmName == algoName - } ?: values().firstOrNull { - it.algorithmName == algoName.replace("-", "") - } + values().firstOrNull { it.algorithmName == algoName } + ?: values().firstOrNull { it.algorithmName == algoName.replace("-", "") } } } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/KeyFlag.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/KeyFlag.kt index d8686cd6..b8bc2d96 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/KeyFlag.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/KeyFlag.kt @@ -6,40 +6,26 @@ package org.pgpainless.algorithm enum class KeyFlag(val flag: Int) { - /** - * This key may be used to certify third-party keys. - */ - CERTIFY_OTHER (1), + /** This key may be used to certify third-party keys. */ + CERTIFY_OTHER(1), - /** - * This key may be used to sign data. - */ - SIGN_DATA (2), + /** This key may be used to sign data. */ + SIGN_DATA(2), - /** - * This key may be used to encrypt communications. - */ - ENCRYPT_COMMS (4), + /** This key may be used to encrypt communications. */ + ENCRYPT_COMMS(4), - /** - * This key may be used to encrypt storage. - */ + /** This key may be used to encrypt storage. */ ENCRYPT_STORAGE(8), - /** - * The private component of this key may have been split by a secret-sharing mechanism. - */ - SPLIT (16), + /** The private component of this key may have been split by a secret-sharing mechanism. */ + SPLIT(16), - /** - * This key may be used for authentication. - */ - AUTHENTICATION (32), + /** This key may be used for authentication. */ + AUTHENTICATION(32), - /** - * The private component of this key may be in the possession of more than one person. - */ - SHARED (128), + /** The private component of this key may be in the possession of more than one person. */ + SHARED(128), ; companion object { @@ -52,9 +38,7 @@ enum class KeyFlag(val flag: Int) { */ @JvmStatic fun fromBitmask(bitmask: Int): List { - return values().filter { - it.flag and bitmask != 0 - } + return values().filter { it.flag and bitmask != 0 } } /** @@ -65,13 +49,12 @@ enum class KeyFlag(val flag: Int) { */ @JvmStatic fun toBitmask(vararg flags: KeyFlag): Int { - return flags.map { it.flag }.reduceOrNull { mask, f -> mask or f } - ?: 0 + return flags.map { it.flag }.reduceOrNull { mask, f -> mask or f } ?: 0 } /** - * Return true if the provided bitmask has the bit for the provided flag set. - * Return false if the mask does not contain the flag. + * Return true if the provided bitmask has the bit for the provided flag set. Return false + * if the mask does not contain the flag. * * @param mask bitmask * @param flag flag to be tested for @@ -84,9 +67,7 @@ enum class KeyFlag(val flag: Int) { @JvmStatic fun containsAny(mask: Int, vararg flags: KeyFlag): Boolean { - return flags.any { - hasKeyFlag(mask, it) - } + return flags.any { hasKeyFlag(mask, it) } } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/OpenPgpPacket.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/OpenPgpPacket.kt index f05599bd..ecb8db35 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/OpenPgpPacket.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/OpenPgpPacket.kt @@ -22,7 +22,6 @@ enum class OpenPgpPacket(val tag: Int) { UATTR(17), SEIPD(18), MDC(19), - EXP_1(60), EXP_2(61), EXP_3(62), @@ -32,15 +31,13 @@ enum class OpenPgpPacket(val tag: Int) { companion object { @JvmStatic fun fromTag(tag: Int): OpenPgpPacket? { - return values().firstOrNull { - it.tag == tag - } + return values().firstOrNull { it.tag == tag } } @JvmStatic fun requireFromTag(tag: Int): OpenPgpPacket { - return fromTag(tag) ?: - throw NoSuchElementException("No OpenPGP packet known for tag $tag") + return fromTag(tag) + ?: throw NoSuchElementException("No OpenPGP packet known for tag $tag") } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/PublicKeyAlgorithm.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/PublicKeyAlgorithm.kt index 9fad8e7d..4a218673 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/PublicKeyAlgorithm.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/PublicKeyAlgorithm.kt @@ -10,87 +10,73 @@ package org.pgpainless.algorithm * See [RFC4880: Public-Key Algorithms](https://tools.ietf.org/html/rfc4880#section-9.1) */ enum class PublicKeyAlgorithm( - val algorithmId: Int, - val signingCapable: Boolean, - val encryptionCapable: Boolean) { + val algorithmId: Int, + val signingCapable: Boolean, + val encryptionCapable: Boolean +) { - /** - * RSA capable of encryption and signatures. - */ - RSA_GENERAL (1, true, true), + /** RSA capable of encryption and signatures. */ + RSA_GENERAL(1, true, true), /** * RSA with usage encryption. * - * @deprecated see Deprecation notice + * @deprecated see Deprecation + * notice */ - @Deprecated("RSA_ENCRYPT is deprecated in favor of RSA_GENERAL", - ReplaceWith("RSA_GENERAL")) - RSA_ENCRYPT (2, false, true), + @Deprecated("RSA_ENCRYPT is deprecated in favor of RSA_GENERAL", ReplaceWith("RSA_GENERAL")) + RSA_ENCRYPT(2, false, true), /** * RSA with usage of creating signatures. * - * @deprecated see Deprecation notice + * @deprecated see Deprecation + * notice */ - @Deprecated("RSA_SIGN is deprecated in favor of RSA_GENERAL", - ReplaceWith("RSA_GENERAL")) - RSA_SIGN (3, true, false), + @Deprecated("RSA_SIGN is deprecated in favor of RSA_GENERAL", ReplaceWith("RSA_GENERAL")) + RSA_SIGN(3, true, false), - /** - * ElGamal with usage encryption. - */ - ELGAMAL_ENCRYPT (16, false, true), + /** ElGamal with usage encryption. */ + ELGAMAL_ENCRYPT(16, false, true), - /** - * Digital Signature Algorithm. - */ - DSA (17, true, false), + /** Digital Signature Algorithm. */ + DSA(17, true, false), - /** - * Elliptic Curve Diffie-Hellman. - */ - ECDH (18, false, true), + /** Elliptic Curve Diffie-Hellman. */ + ECDH(18, false, true), - /** - * Elliptic Curve Digital Signature Algorithm. - */ - ECDSA (19, true, false), + /** Elliptic Curve Digital Signature Algorithm. */ + ECDSA(19, true, false), /** * ElGamal General. * - * @deprecated see Deprecation notice + * @deprecated see Deprecation + * notice */ - @Deprecated("ElGamal is deprecated") - ELGAMAL_GENERAL (20, true, true), + @Deprecated("ElGamal is deprecated") ELGAMAL_GENERAL(20, true, true), - /** - * Diffie-Hellman key exchange algorithm. - */ - DIFFIE_HELLMAN (21, false, true), + /** Diffie-Hellman key exchange algorithm. */ + DIFFIE_HELLMAN(21, false, true), - /** - * Digital Signature Algorithm based on twisted Edwards Curves. - */ - EDDSA (22, true, false), + /** Digital Signature Algorithm based on twisted Edwards Curves. */ + EDDSA(22, true, false), ; fun isSigningCapable(): Boolean = signingCapable + fun isEncryptionCapable(): Boolean = encryptionCapable companion object { @JvmStatic fun fromId(id: Int): PublicKeyAlgorithm? { - return values().firstOrNull { - it.algorithmId == id - } + return values().firstOrNull { it.algorithmId == id } } @JvmStatic fun requireFromId(id: Int): PublicKeyAlgorithm { - return fromId(id) ?: - throw NoSuchElementException("No PublicKeyAlgorithm found for id $id") + return fromId(id) + ?: throw NoSuchElementException("No PublicKeyAlgorithm found for id $id") } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/RevocationState.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/RevocationState.kt index d5d95d45..ba288469 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/RevocationState.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/RevocationState.kt @@ -4,53 +4,48 @@ package org.pgpainless.algorithm -import org.pgpainless.util.DateUtil import java.lang.AssertionError import java.util.* import kotlin.NoSuchElementException +import org.pgpainless.util.DateUtil -class RevocationState private constructor( - val type: RevocationStateType, - private val _date: Date?): Comparable { +class RevocationState private constructor(val type: RevocationStateType, private val _date: Date?) : + Comparable { val date: Date get() { if (!isSoftRevocation()) { - throw NoSuchElementException("RevocationStateType is not equal to 'softRevoked'. Cannot extract date.") + throw NoSuchElementException( + "RevocationStateType is not equal to 'softRevoked'. Cannot extract date.") } return _date!! } - private constructor(type: RevocationStateType): this(type, null) + private constructor(type: RevocationStateType) : this(type, null) fun isSoftRevocation() = type == RevocationStateType.softRevoked + fun isHardRevocation() = type == RevocationStateType.hardRevoked + fun isNotRevoked() = type == RevocationStateType.notRevoked companion object { - @JvmStatic - fun notRevoked() = RevocationState(RevocationStateType.notRevoked) + @JvmStatic fun notRevoked() = RevocationState(RevocationStateType.notRevoked) @JvmStatic fun softRevoked(date: Date) = RevocationState(RevocationStateType.softRevoked, date) - @JvmStatic - fun hardRevoked() = RevocationState(RevocationStateType.hardRevoked) + @JvmStatic fun hardRevoked() = RevocationState(RevocationStateType.hardRevoked) } override fun compareTo(other: RevocationState): Int { - return when(type) { - RevocationStateType.notRevoked -> - if (other.isNotRevoked()) 0 - else -1 + return when (type) { + RevocationStateType.notRevoked -> if (other.isNotRevoked()) 0 else -1 RevocationStateType.softRevoked -> if (other.isNotRevoked()) 1 // Compare soft dates in reverse - else if (other.isSoftRevocation()) other.date.compareTo(date) - else -1 - RevocationStateType.hardRevoked -> - if (other.isHardRevocation()) 0 - else 1 + else if (other.isSoftRevocation()) other.date.compareTo(date) else -1 + RevocationStateType.hardRevoked -> if (other.isHardRevocation()) 0 else 1 else -> throw AssertionError("Unknown type: $type") } } @@ -80,8 +75,9 @@ class RevocationState private constructor( return false } if (isSoftRevocation()) { - return DateUtil.toSecondsPrecision(date).time == DateUtil.toSecondsPrecision(other.date).time + return DateUtil.toSecondsPrecision(date).time == + DateUtil.toSecondsPrecision(other.date).time } return true } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/RevocationStateType.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/RevocationStateType.kt index bf18e27f..1f95ad7b 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/RevocationStateType.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/RevocationStateType.kt @@ -5,18 +5,12 @@ package org.pgpainless.algorithm enum class RevocationStateType { - /** - * Certificate is not revoked. - */ + /** Certificate is not revoked. */ notRevoked, - /** - * Certificate is revoked with a soft revocation. - */ + /** Certificate is revoked with a soft revocation. */ softRevoked, - /** - * Certificate is revoked with a hard revocation. - */ + /** Certificate is revoked with a hard revocation. */ hardRevoked -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/SignatureSubpacket.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/SignatureSubpacket.kt index fcb0c90a..a0efd618 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/SignatureSubpacket.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/SignatureSubpacket.kt @@ -7,23 +7,24 @@ package org.pgpainless.algorithm import org.bouncycastle.bcpg.SignatureSubpacketTags.* /** - * Enumeration of possible subpackets that might be found in the hashed and unhashed area of an OpenPGP signature. + * Enumeration of possible subpackets that might be found in the hashed and unhashed area of an + * OpenPGP signature. * - * See [RFC4880: Signature Subpacket Specification](https://tools.ietf.org/html/rfc4880#section-5.2.3.1) + * See + * [RFC4880: Signature Subpacket Specification](https://tools.ietf.org/html/rfc4880#section-5.2.3.1) */ enum class SignatureSubpacket(val code: Int) { /** - * The time the signature was made. - * MUST be present in the hashed area of the signature. + * The time the signature was made. MUST be present in the hashed area of the signature. * * See [Signature Creation Time](https://tools.ietf.org/html/rfc4880#section-5.2.3.4) */ signatureCreationTime(2), /** - * The validity period of the signature. This is the number of seconds - * after the signature creation time that the signature expires. If - * this is not present or has a value of zero, it never expires. + * The validity period of the signature. This is the number of seconds after the signature + * creation time that the signature expires. If this is not present or has a value of zero, it + * never expires. * * See [Signature Expiration Time](https://tools.ietf.org/html/rfc4880#section-5.2.3.10) */ @@ -37,90 +38,76 @@ enum class SignatureSubpacket(val code: Int) { exportableCertification(4), /** - * Signer asserts that the key is not only valid but also trustworthy at - * the specified level. Level 0 has the same meaning as an ordinary - * validity signature. Level 1 means that the signed key is asserted to - * be a valid, trusted introducer, with the 2nd octet of the body - * specifying the degree of trust. Level 2 means that the signed key is - * asserted to be trusted to issue level 1 trust signatures, i.e., that - * it is a "meta introducer". Generally, a level n trust signature - * asserts that a key is trusted to issue level n-1 trust signatures. - * The trust amount is in a range from 0-255, interpreted such that - * values less than 120 indicate partial trust and values of 120 or - * greater indicate complete trust. Implementations SHOULD emit values - * of 60 for partial trust and 120 for complete trust. + * Signer asserts that the key is not only valid but also trustworthy at the specified level. + * Level 0 has the same meaning as an ordinary validity signature. Level 1 means that the signed + * key is asserted to be a valid, trusted introducer, with the 2nd octet of the body specifying + * the degree of trust. Level 2 means that the signed key is asserted to be trusted to issue + * level 1 trust signatures, i.e., that it is a "meta introducer". Generally, a level n trust + * signature asserts that a key is trusted to issue level n-1 trust signatures. The trust amount + * is in a range from 0-255, interpreted such that values less than 120 indicate partial trust + * and values of 120 or greater indicate complete trust. Implementations SHOULD emit values of + * 60 for partial trust and 120 for complete trust. * * See [Trust Signature](https://tools.ietf.org/html/rfc4880#section-5.2.3.13) */ trustSignature(5), /** - * Used in conjunction with trust Signature packets (of level greater 0) to - * limit the scope of trust that is extended. Only signatures by the - * target key on User IDs that match the regular expression in the body - * of this packet have trust extended by the trust Signature subpacket. - * The regular expression uses the same syntax as the Henry Spencer's - * "almost public domain" regular expression [REGEX] package. A - * description of the syntax is found in Section 8 below. + * Used in conjunction with trust Signature packets (of level greater 0) to limit the scope of + * trust that is extended. Only signatures by the target key on User IDs that match the regular + * expression in the body of this packet have trust extended by the trust Signature subpacket. + * The regular expression uses the same syntax as the Henry Spencer's "almost public domain" + * regular expression [REGEX] package. A description of the syntax is found in Section 8 below. * * See [Regular Expression](https://tools.ietf.org/html/rfc4880#section-5.2.3.14) */ regularExpression(6), /** - * Signature's revocability status. The packet body contains a Boolean - * flag indicating whether the signature is revocable. Signatures that - * are not revocable have any later revocation signatures ignored. They - * represent a commitment by the signer that he cannot revoke his - * signature for the life of his key. If this packet is not present, - * the signature is revocable. + * Signature's revocability status. The packet body contains a Boolean flag indicating whether + * the signature is revocable. Signatures that are not revocable have any later revocation + * signatures ignored. They represent a commitment by the signer that he cannot revoke his + * signature for the life of his key. If this packet is not present, the signature is revocable. * * See [Revocable](https://tools.ietf.org/html/rfc4880#section-5.2.3.12) */ revocable(7), /** - * The validity period of the key. This is the number of seconds after - * the key creation time that the key expires. If this is not present - * or has a value of zero, the key never expires. This is found only on - * a self-signature. + * The validity period of the key. This is the number of seconds after the key creation time + * that the key expires. If this is not present or has a value of zero, the key never expires. + * This is found only on a self-signature. * * See [Key Expiration Time](https://tools.ietf.org/html/rfc4880#section-5.2.3.6) */ keyExpirationTime(9), - /** - * Placeholder for backwards compatibility. - */ + /** Placeholder for backwards compatibility. */ placeholder(10), /** - * Symmetric algorithm numbers that indicate which algorithms the keyholder - * prefers to use. The subpackets body is an ordered list of - * octets with the most preferred listed first. It is assumed that only - * algorithms listed are supported by the recipient's software. - * This is only found on a self-signature. + * Symmetric algorithm numbers that indicate which algorithms the keyholder prefers to use. The + * subpackets body is an ordered list of octets with the most preferred listed first. It is + * assumed that only algorithms listed are supported by the recipient's software. This is only + * found on a self-signature. * * See [Preferred Symmetric Algorithms](https://tools.ietf.org/html/rfc4880#section-5.2.3.7) */ preferredSymmetricAlgorithms(11), /** - * Authorizes the specified key to issue revocation signatures for this - * key. Class octet must have bit 0x80 set. If the bit 0x40 is set, - * then this means that the revocation information is sensitive. Other - * bits are for future expansion to other kinds of authorizations. This - * is found on a self-signature. + * Authorizes the specified key to issue revocation signatures for this key. Class octet must + * have bit 0x80 set. If the bit 0x40 is set, then this means that the revocation information is + * sensitive. Other bits are for future expansion to other kinds of authorizations. This is + * found on a self-signature. * - * If the "sensitive" flag is set, the keyholder feels this subpacket - * contains private trust information that describes a real-world - * sensitive relationship. If this flag is set, implementations SHOULD - * NOT export this signature to other users except in cases where the - * data needs to be available: when the signature is being sent to the - * designated revoker, or when it is accompanied by a revocation - * signature from that revoker. Note that it may be appropriate to - * isolate this subpacket within a separate signature so that it is not - * combined with other subpackets that need to be exported. + * If the "sensitive" flag is set, the keyholder feels this subpacket contains private trust + * information that describes a real-world sensitive relationship. If this flag is set, + * implementations SHOULD NOT export this signature to other users except in cases where the + * data needs to be available: when the signature is being sent to the designated revoker, or + * when it is accompanied by a revocation signature from that revoker. Note that it may be + * appropriate to isolate this subpacket within a separate signature so that it is not combined + * with other subpackets that need to be exported. * * See [Revocation Key](https://tools.ietf.org/html/rfc4880#section-5.2.3.15) */ @@ -134,54 +121,48 @@ enum class SignatureSubpacket(val code: Int) { issuerKeyId(16), /** - * This subpacket describes a "notation" on the signature that the - * issuer wishes to make. The notation has a name and a value, each of - * which are strings of octets. There may be more than one notation in - * a signature. Notations can be used for any extension the issuer of - * the signature cares to make. The "flags" field holds four octets of - * flags. + * This subpacket describes a "notation" on the signature that the issuer wishes to make. The + * notation has a name and a value, each of which are strings of octets. There may be more than + * one notation in a signature. Notations can be used for any extension the issuer of the + * signature cares to make. The "flags" field holds four octets of flags. * * See [Notation Data](https://tools.ietf.org/html/rfc4880#section-5.2.3.16) */ notationData(20), /** - * Message digest algorithm numbers that indicate which algorithms the - * keyholder prefers to receive. Like the preferred symmetric - * algorithms, the list is ordered. - * This is only found on a self-signature. + * Message digest algorithm numbers that indicate which algorithms the keyholder prefers to + * receive. Like the preferred symmetric algorithms, the list is ordered. This is only found on + * a self-signature. * * See [Preferred Hash Algorithms](https://tools.ietf.org/html/rfc4880#section-5.2.3.8) */ preferredHashAlgorithms(21), /** - * Compression algorithm numbers that indicate which algorithms the - * keyholder prefers to use. Like the preferred symmetric algorithms, the - * list is ordered. If this subpacket is not included, ZIP is preferred. - * A zero denotes that uncompressed data is preferred; the keyholder's - * software might have no compression software in that implementation. - * This is only found on a self-signature. + * Compression algorithm numbers that indicate which algorithms the keyholder prefers to use. + * Like the preferred symmetric algorithms, the list is ordered. If this subpacket is not + * included, ZIP is preferred. A zero denotes that uncompressed data is preferred; the + * keyholder's software might have no compression software in that implementation. This is only + * found on a self-signature. * * See [Preferred Compressio Algorithms](https://tools.ietf.org/html/rfc4880#section-5.2.3.9) */ preferredCompressionAlgorithms(22), /** - * This is a list of one-bit flags that indicate preferences that the - * keyholder has about how the key is handled on a key server. All - * undefined flags MUST be zero. - * This is found only on a self-signature. + * This is a list of one-bit flags that indicate preferences that the keyholder has about how + * the key is handled on a key server. All undefined flags MUST be zero. This is found only on a + * self-signature. * * See [Key Server Preferences](https://tools.ietf.org/html/rfc4880#section-5.2.3.17) */ keyServerPreferences(23), /** - * This is a URI of a key server that the keyholder prefers be used for - * updates. Note that keys with multiple User IDs can have a preferred - * key server for each User ID. Note also that since this is a URI, the - * key server can actually be a copy of the key retrieved by ftp, http, + * This is a URI of a key server that the keyholder prefers be used for updates. Note that keys + * with multiple User IDs can have a preferred key server for each User ID. Note also that since + * this is a URI, the key server can actually be a copy of the key retrieved by ftp, http, * finger, etc. * * See [Preferred Key Server](https://tools.ietf.org/html/rfc4880#section-5.2.3.18) @@ -189,63 +170,55 @@ enum class SignatureSubpacket(val code: Int) { preferredKeyServers(24), /** - * This is a flag in a User ID's self-signature that states whether this - * User ID is the main User ID for this key. It is reasonable for an - * implementation to resolve ambiguities in preferences, etc. by - * referring to the primary User ID. If this flag is absent, its value - * is zero. If more than one User ID in a key is marked as primary, the - * implementation may resolve the ambiguity in any way it sees fit, but - * it is RECOMMENDED that priority be given to the User ID with the most - * recent self-signature. + * This is a flag in a User ID's self-signature that states whether this User ID is the main + * User ID for this key. It is reasonable for an implementation to resolve ambiguities in + * preferences, etc. by referring to the primary User ID. If this flag is absent, its value is + * zero. If more than one User ID in a key is marked as primary, the implementation may resolve + * the ambiguity in any way it sees fit, but it is RECOMMENDED that priority be given to the + * User ID with the most recent self-signature. * - * When appearing on a self-signature on a User ID packet, this - * subpacket applies only to User ID packets. When appearing on a - * self-signature on a User Attribute packet, this subpacket applies - * only to User Attribute packets. That is to say, there are two - * different and independent "primaries" -- one for User IDs, and one - * for User Attributes. + * When appearing on a self-signature on a User ID packet, this subpacket applies only to User + * ID packets. When appearing on a self-signature on a User Attribute packet, this subpacket + * applies only to User Attribute packets. That is to say, there are two different and + * independent "primaries" -- one for User IDs, and one for User Attributes. * * See [Primary User-ID](https://tools.ietf.org/html/rfc4880#section-5.2.3.19) */ primaryUserId(25), /** - * This subpacket contains a URI of a document that describes the policy - * under which the signature was issued. + * This subpacket contains a URI of a document that describes the policy under which the + * signature was issued. * * See [Policy URL](https://tools.ietf.org/html/rfc4880#section-5.2.3.20) */ policyUrl(26), /** - * This subpacket contains a list of binary flags that hold information - * about a key. It is a string of octets, and an implementation MUST - * NOT assume a fixed size. This is so it can grow over time. If a - * list is shorter than an implementation expects, the unstated flags - * are considered to be zero. + * This subpacket contains a list of binary flags that hold information about a key. It is a + * string of octets, and an implementation MUST NOT assume a fixed size. This is so it can grow + * over time. If a list is shorter than an implementation expects, the unstated flags are + * considered to be zero. * * See [Key Flags](https://tools.ietf.org/html/rfc4880#section-5.2.3.21) */ keyFlags(27), /** - * This subpacket allows a keyholder to state which User ID is - * responsible for the signing. Many keyholders use a single key for - * different purposes, such as business communications as well as - * personal communications. This subpacket allows such a keyholder to - * state which of their roles is making a signature. + * This subpacket allows a keyholder to state which User ID is responsible for the signing. Many + * keyholders use a single key for different purposes, such as business communications as well + * as personal communications. This subpacket allows such a keyholder to state which of their + * roles is making a signature. * * See [Signer's User ID](https://tools.ietf.org/html/rfc4880#section-5.2.3.22) */ signerUserId(28), /** - * This subpacket is used only in key revocation and certification - * revocation signatures. It describes the reason why the key or - * certificate was revoked. + * This subpacket is used only in key revocation and certification revocation signatures. It + * describes the reason why the key or certificate was revoked. * - * The first octet contains a machine-readable code that denotes the - * reason for the revocation: + * The first octet contains a machine-readable code that denotes the reason for the revocation: * * 0 - No reason specified (key revocations or cert revocations) * 1 - Key is superseded (key revocations) @@ -259,117 +232,105 @@ enum class SignatureSubpacket(val code: Int) { revocationReason(29), /** - * The Features subpacket denotes which advanced OpenPGP features a - * user's implementation supports. This is so that as features are - * added to OpenPGP that cannot be backwards-compatible, a user can - * state that they can use that feature. The flags are single bits that - * indicate that a given feature is supported. + * The Features subpacket denotes which advanced OpenPGP features a user's implementation + * supports. This is so that as features are added to OpenPGP that cannot be + * backwards-compatible, a user can state that they can use that feature. The flags are single + * bits that indicate that a given feature is supported. * - * This subpacket is similar to a preferences subpacket, and only - * appears in a self-signature. + * This subpacket is similar to a preferences subpacket, and only appears in a self-signature. * * See [Features](https://tools.ietf.org/html/rfc4880#section-5.2.3.24) */ features(30), /** - * This subpacket identifies a specific target signature to which a - * signature refers. For revocation signatures, this subpacket - * provides explicit designation of which signature is being revoked. - * For a third-party or timestamp signature, this designates what - * signature is signed. All arguments are an identifier of that target - * signature. + * This subpacket identifies a specific target signature to which a signature refers. For + * revocation signatures, this subpacket provides explicit designation of which signature is + * being revoked. For a third-party or timestamp signature, this designates what signature is + * signed. All arguments are an identifier of that target signature. * - * The N octets of hash data MUST be the size of the hash of the - * signature. For example, a target signature with a SHA-1 hash MUST - * have 20 octets of hash data. + * The N octets of hash data MUST be the size of the hash of the signature. For example, a + * target signature with a SHA-1 hash MUST have 20 octets of hash data. * * See [Signature Target](https://tools.ietf.org/html/rfc4880#section-5.2.3.25) */ signatureTarget(31), /** - * This subpacket contains a complete Signature packet body as - * specified in Section 5.2 above. It is useful when one signature - * needs to refer to, or be incorporated in, another signature. + * This subpacket contains a complete Signature packet body as specified in Section 5.2 above. + * It is useful when one signature needs to refer to, or be incorporated in, another signature. * * See [Embedded Signature](https://tools.ietf.org/html/rfc4880#section-5.2.3.26) */ embeddedSignature(32), /** - * The OpenPGP Key fingerprint of the key issuing the signature. This - * subpacket SHOULD be included in all signatures. If the version of - * the issuing key is 4 and an Issuer subpacket is also included in the - * signature, the key ID of the Issuer subpacket MUST match the low 64 - * bits of the fingerprint. + * The OpenPGP Key fingerprint of the key issuing the signature. This subpacket SHOULD be + * included in all signatures. If the version of the issuing key is 4 and an Issuer subpacket is + * also included in the signature, the key ID of the Issuer subpacket MUST match the low 64 bits + * of the fingerprint. * - * Note that the length N of the fingerprint for a version 4 key is 20 - * octets; for a version 5 key N is 32. + * Note that the length N of the fingerprint for a version 4 key is 20 octets; for a version 5 + * key N is 32. * - * See [Issuer Fingerprint](https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-10#section-5.2.3.28) + * See + * [Issuer Fingerprint](https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-10#section-5.2.3.28) */ issuerFingerprint(33), /** - * AEAD algorithm numbers that indicate which AEAD algorithms the - * keyholder prefers to use. The subpackets body is an ordered list of - * octets with the most preferred listed first. It is assumed that only - * algorithms listed are supported by the recipient's software. - * This is only found on a self-signature. - * Note that support for the AEAD Encrypted Data packet in the general - * is indicated by a Feature Flag. + * AEAD algorithm numbers that indicate which AEAD algorithms the keyholder prefers to use. The + * subpackets body is an ordered list of octets with the most preferred listed first. It is + * assumed that only algorithms listed are supported by the recipient's software. This is only + * found on a self-signature. Note that support for the AEAD Encrypted Data packet in the + * general is indicated by a Feature Flag. * - * See [Preferred AEAD Algorithms](https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-10#section-5.2.3.8) + * See + * [Preferred AEAD Algorithms](https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-10#section-5.2.3.8) */ preferredAEADAlgorithms(39), /** - * The OpenPGP Key fingerprint of the intended recipient primary key. - * If one or more subpackets of this type are included in a signature, - * it SHOULD be considered valid only in an encrypted context, where the - * key it was encrypted to is one of the indicated primary keys, or one - * of their subkeys. This can be used to prevent forwarding a signature - * outside its intended, encrypted context. + * The OpenPGP Key fingerprint of the intended recipient primary key. If one or more subpackets + * of this type are included in a signature, it SHOULD be considered valid only in an encrypted + * context, where the key it was encrypted to is one of the indicated primary keys, or one of + * their subkeys. This can be used to prevent forwarding a signature outside its intended, + * encrypted context. * - * Note that the length N of the fingerprint for a version 4 key is 20 - * octets; for a version 5 key N is 32. + * Note that the length N of the fingerprint for a version 4 key is 20 octets; for a version 5 + * key N is 32. * - * See [Intended Recipient Fingerprint](https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-10#section-5.2.3.29) + * See + * [Intended Recipient Fingerprint](https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-10#section-5.2.3.29) */ intendedRecipientFingerprint(35), /** - * This subpacket MUST only appear as a hashed subpacket of an - * Attestation Key Signature. It has no meaning in any other signature - * type. It is used by the primary key to attest to a set of third- - * party certifications over the associated User ID or User Attribute. - * This enables the holder of an OpenPGP primary key to mark specific - * third-party certifications as re-distributable with the rest of the - * Transferable Public Key (see the "No-modify" flag in "Key Server - * Preferences", above). Implementations MUST include exactly one - * Attested Certification subpacket in any generated Attestation Key - * Signature. + * This subpacket MUST only appear as a hashed subpacket of an Attestation Key Signature. It has + * no meaning in any other signature type. It is used by the primary key to attest to a set of + * third- party certifications over the associated User ID or User Attribute. This enables the + * holder of an OpenPGP primary key to mark specific third-party certifications as + * re-distributable with the rest of the Transferable Public Key (see the "No-modify" flag in + * "Key Server Preferences", above). Implementations MUST include exactly one Attested + * Certification subpacket in any generated Attestation Key Signature. * - * See [Attested Certification](https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-10#section-5.2.3.30) + * See + * [Attested Certification](https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-10#section-5.2.3.30) */ - attestedCertification(37) - ; - + attestedCertification(37); + companion object { - + /** - * Return the [SignatureSubpacket] that corresponds to the provided id. - * If an unmatched code is presented, return null. + * Return the [SignatureSubpacket] that corresponds to the provided id. If an unmatched code + * is presented, return null. * * @param code id * @return signature subpacket */ @JvmStatic fun fromCode(code: Int): SignatureSubpacket? { - return values().firstOrNull { - it.code == code - } + return values().firstOrNull { it.code == code } } /** @@ -381,21 +342,20 @@ enum class SignatureSubpacket(val code: Int) { */ @JvmStatic fun requireFromCode(code: Int): SignatureSubpacket { - return fromCode(code) ?: - throw NoSuchElementException("No SignatureSubpacket tag found with code $code") + return fromCode(code) + ?: throw NoSuchElementException("No SignatureSubpacket tag found with code $code") } /** - * Convert an array of signature subpacket tags into a list of [SignatureSubpacket SignatureSubpackets]. + * Convert an array of signature subpacket tags into a list of + * [SignatureSubpacket SignatureSubpackets]. * * @param codes array of codes * @return list of subpackets */ @JvmStatic fun fromCodes(vararg codes: Int): List { - return codes.toList().mapNotNull { - fromCode(it) - } + return codes.toList().mapNotNull { fromCode(it) } } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/SignatureType.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/SignatureType.kt index 1b708a03..865549b8 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/SignatureType.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/SignatureType.kt @@ -7,150 +7,120 @@ package org.pgpainless.algorithm import org.bouncycastle.openpgp.PGPSignature /** - * Enum that enlists all the Signature Types defined in rfc4880 section 5.2.1 - * See [PGPSignature] for comparison. + * Enum that enlists all the Signature Types defined in rfc4880 section 5.2.1 See [PGPSignature] for + * comparison. * * See [rfc4880 §5.2.1. Signature Types](https://tools.ietf.org/html/rfc4880#section-5.11) */ enum class SignatureType(val code: Int) { /** - * Signature of a binary document. - * This means the signer owns it, created it, or certifies that it - * has not been modified. + * Signature of a binary document. This means the signer owns it, created it, or certifies that + * it has not been modified. */ BINARY_DOCUMENT(0x00), /** - * Signature of a canonical text document. - * This means the signer owns it, created it, or certifies that it - * has not been modified. The signature is calculated over the text - * data with its line endings converted to {@code }. + * Signature of a canonical text document. This means the signer owns it, created it, or + * certifies that it has not been modified. The signature is calculated over the text data with + * its line endings converted to {@code }. */ CANONICAL_TEXT_DOCUMENT(0x01), /** - * Standalone signature. - * This signature is a signature of only its own subpacket contents. - * It is calculated identically to a signature over a zero-length - * binary document. Note that it doesn't make sense to have a V3 - * standalone signature. + * Standalone signature. This signature is a signature of only its own subpacket contents. It is + * calculated identically to a signature over a zero-length binary document. Note that it + * doesn't make sense to have a V3 standalone signature. */ STANDALONE(0x02), /** - * Generic certification of a User ID and Public-Key packet. - * The issuer of this certification does not make any particular - * assertion as to how well the certifier has checked that the owner - * of the key is in fact the person described by the User ID. + * Generic certification of a User ID and Public-Key packet. The issuer of this certification + * does not make any particular assertion as to how well the certifier has checked that the + * owner of the key is in fact the person described by the User ID. */ GENERIC_CERTIFICATION(0x10), /** - * Persona certification of a User ID and Public-Key packet. - * The issuer of this certification has not done any verification of - * the claim that the owner of this key is the User ID specified. + * Persona certification of a User ID and Public-Key packet. The issuer of this certification + * has not done any verification of the claim that the owner of this key is the User ID + * specified. */ NO_CERTIFICATION(0x11), /** - * Casual certification of a User ID and Public-Key packet. - * The issuer of this certification has done some casual - * verification of the claim of identity. + * Casual certification of a User ID and Public-Key packet. The issuer of this certification has + * done some casual verification of the claim of identity. */ CASUAL_CERTIFICATION(0x12), /** - * Positive certification of a User ID and Public-Key packet. - * The issuer of this certification has done substantial - * verification of the claim of identity. + * Positive certification of a User ID and Public-Key packet. The issuer of this certification + * has done substantial verification of the claim of identity. */ POSITIVE_CERTIFICATION(0x13), /** - * Subkey Binding Signature. - * This signature is a statement by the top-level signing key that - * indicates that it owns the subkey. This signature is calculated - * directly on the primary key and subkey, and not on any User ID or - * other packets. A signature that binds a signing subkey MUST have - * an Embedded Signature subpacket in this binding signature that - * contains a [#PRIMARYKEY_BINDING] signature made by the - * signing subkey on the primary key and subkey. + * Subkey Binding Signature. This signature is a statement by the top-level signing key that + * indicates that it owns the subkey. This signature is calculated directly on the primary key + * and subkey, and not on any User ID or other packets. A signature that binds a signing subkey + * MUST have an Embedded Signature subpacket in this binding signature that contains a + * [#PRIMARYKEY_BINDING] signature made by the signing subkey on the primary key and subkey. */ SUBKEY_BINDING(0x18), /** - * Primary Key Binding Signature - * This signature is a statement by a signing subkey, indicating - * that it is owned by the primary key and subkey. This signature - * is calculated the same way as a [#SUBKEY_BINDING] signature: - * directly on the primary key and subkey, and not on any User ID or - * other packets. + * Primary Key Binding Signature This signature is a statement by a signing subkey, indicating + * that it is owned by the primary key and subkey. This signature is calculated the same way as + * a [#SUBKEY_BINDING] signature: directly on the primary key and subkey, and not on any User ID + * or other packets. */ PRIMARYKEY_BINDING(0x19), /** - * Signature directly on a key - * This signature is calculated directly on a key. It binds the - * information in the Signature subpackets to the key, and is - * appropriate to be used for subpackets that provide information - * about the key, such as the Revocation Key subpacket. It is also - * appropriate for statements that non-self certifiers want to make - * about the key itself, rather than the binding between a key and a - * name. + * Signature directly on a key This signature is calculated directly on a key. It binds the + * information in the Signature subpackets to the key, and is appropriate to be used for + * subpackets that provide information about the key, such as the Revocation Key subpacket. It + * is also appropriate for statements that non-self certifiers want to make about the key + * itself, rather than the binding between a key and a name. */ DIRECT_KEY(0x1f), /** - * Key revocation signature - * The signature is calculated directly on the key being revoked. A - * revoked key is not to be used. Only revocation signatures by the - * key being revoked, or by an authorized revocation key, should be - * considered valid revocation signatures. + * Key revocation signature The signature is calculated directly on the key being revoked. A + * revoked key is not to be used. Only revocation signatures by the key being revoked, or by an + * authorized revocation key, should be considered valid revocation signatures. */ KEY_REVOCATION(0x20), /** - * Subkey revocation signature - * The signature is calculated directly on the subkey being revoked. - * A revoked subkey is not to be used. Only revocation signatures - * by the top-level signature key that is bound to this subkey, or - * by an authorized revocation key, should be considered valid + * Subkey revocation signature The signature is calculated directly on the subkey being revoked. + * A revoked subkey is not to be used. Only revocation signatures by the top-level signature key + * that is bound to this subkey, or by an authorized revocation key, should be considered valid * revocation signatures. */ SUBKEY_REVOCATION(0x28), /** - * Certification revocation signature - * This signature revokes an earlier User ID certification signature - * (signature class 0x10 through 0x13) or signature [#DIRECT_KEY]. - * It should be issued by the same key that issued the - * revoked signature or an authorized revocation key. The signature - * is computed over the same data as the certificate that it - * revokes, and should have a later creation date than that - * certificate. + * Certification revocation signature This signature revokes an earlier User ID certification + * signature (signature class 0x10 through 0x13) or signature [#DIRECT_KEY]. It should be issued + * by the same key that issued the revoked signature or an authorized revocation key. The + * signature is computed over the same data as the certificate that it revokes, and should have + * a later creation date than that certificate. */ CERTIFICATION_REVOCATION(0x30), - /** - * Timestamp signature. - * This signature is only meaningful for the timestamp contained in - * it. - */ + /** Timestamp signature. This signature is only meaningful for the timestamp contained in it. */ TIMESTAMP(0x40), /** - * Third-Party Confirmation signature. - * This signature is a signature over some other OpenPGP Signature - * packet(s). It is analogous to a notary seal on the signed data. - * A third-party signature SHOULD include Signature Target - * subpacket(s) to give easy identification. Note that we really do - * mean SHOULD. There are plausible uses for this (such as a blind - * party that only sees the signature, not the key or source - * document) that cannot include a target subpacket. + * Third-Party Confirmation signature. This signature is a signature over some other OpenPGP + * Signature packet(s). It is analogous to a notary seal on the signed data. A third-party + * signature SHOULD include Signature Target subpacket(s) to give easy identification. Note that + * we really do mean SHOULD. There are plausible uses for this (such as a blind party that only + * sees the signature, not the key or source document) that cannot include a target subpacket. */ - THIRD_PARTY_CONFIRMATION(0x50) - ; + THIRD_PARTY_CONFIRMATION(0x50); companion object { @@ -162,9 +132,7 @@ enum class SignatureType(val code: Int) { */ @JvmStatic fun fromCode(code: Int): SignatureType? { - return values().firstOrNull { - it.code == code - } + return values().firstOrNull { it.code == code } } /** @@ -176,8 +144,9 @@ enum class SignatureType(val code: Int) { */ @JvmStatic fun requireFromCode(code: Int): SignatureType { - return fromCode(code) ?: - throw NoSuchElementException("Signature type 0x${Integer.toHexString(code)} appears to be invalid.") + return fromCode(code) + ?: throw NoSuchElementException( + "Signature type 0x${Integer.toHexString(code)} appears to be invalid.") } /** @@ -188,8 +157,7 @@ enum class SignatureType(val code: Int) { * @throws IllegalArgumentException in case of an unmatched signature type code */ @JvmStatic - @Deprecated("Deprecated in favor of requireFromCode", - ReplaceWith("requireFromCode")) + @Deprecated("Deprecated in favor of requireFromCode", ReplaceWith("requireFromCode")) fun valueOf(code: Int): SignatureType { try { return requireFromCode(code) @@ -197,12 +165,12 @@ enum class SignatureType(val code: Int) { throw IllegalArgumentException(e.message) } } - + @JvmStatic fun isRevocationSignature(signatureType: Int): Boolean { return isRevocationSignature(valueOf(signatureType)) } - + @JvmStatic fun isRevocationSignature(signatureType: SignatureType): Boolean { return when (signatureType) { @@ -218,11 +186,11 @@ enum class SignatureType(val code: Int) { DIRECT_KEY, TIMESTAMP, THIRD_PARTY_CONFIRMATION -> false - KEY_REVOCATION, - SUBKEY_REVOCATION, + KEY_REVOCATION, + SUBKEY_REVOCATION, CERTIFICATION_REVOCATION -> true else -> throw IllegalArgumentException("Unknown signature type: $signatureType") } } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/StreamEncoding.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/StreamEncoding.kt index 391797b1..74b2a56b 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/StreamEncoding.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/StreamEncoding.kt @@ -11,61 +11,52 @@ package org.pgpainless.algorithm */ enum class StreamEncoding(val code: Char) { - /** - * The Literal packet contains binary data. - */ + /** The Literal packet contains binary data. */ BINARY('b'), /** - * The Literal packet contains text data, and thus may need line ends converted to local form, or other - * text-mode changes. + * The Literal packet contains text data, and thus may need line ends converted to local form, + * or other text-mode changes. */ TEXT('t'), - /** - * Indication that the implementation believes that the literal data contains UTF-8 text. - */ + /** Indication that the implementation believes that the literal data contains UTF-8 text. */ UTF8('u'), /** - * Early versions of PGP also defined a value of 'l' as a 'local' mode for machine-local conversions. - * RFC 1991 [RFC1991] incorrectly stated this local mode flag as '1' (ASCII numeral one). - * Both of these local modes are deprecated. + * Early versions of PGP also defined a value of 'l' as a 'local' mode for machine-local + * conversions. RFC 1991 [RFC1991] incorrectly stated this local mode flag as '1' (ASCII numeral + * one). Both of these local modes are deprecated. */ - @Deprecated("LOCAL is deprecated.") - LOCAL('l'), + @Deprecated("LOCAL is deprecated.") LOCAL('l'), ; - companion object { /** - * Return the [StreamEncoding] corresponding to the provided code identifier. - * If no matching encoding is found, return null. + * Return the [StreamEncoding] corresponding to the provided code identifier. If no matching + * encoding is found, return null. * * @param code identifier * @return encoding enum */ @JvmStatic fun fromCode(code: Int): StreamEncoding? { - return values().firstOrNull { - it.code == code.toChar() - } ?: if (code == 1) return LOCAL else null + return values().firstOrNull { it.code == code.toChar() } + ?: if (code == 1) return LOCAL else null } /** - * Return the [StreamEncoding] corresponding to the provided code identifier. - * If no matching encoding is found, throw a [NoSuchElementException]. + * Return the [StreamEncoding] corresponding to the provided code identifier. If no matching + * encoding is found, throw a [NoSuchElementException]. * * @param code identifier * @return encoding enum - * * @throws NoSuchElementException in case of an unmatched identifier */ @JvmStatic fun requireFromCode(code: Int): StreamEncoding { - return fromCode(code) ?: - throw NoSuchElementException("No StreamEncoding found for code $code") + return fromCode(code) + ?: throw NoSuchElementException("No StreamEncoding found for code $code") } } - -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/SymmetricKeyAlgorithm.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/SymmetricKeyAlgorithm.kt index ae72ff05..bfd32343 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/SymmetricKeyAlgorithm.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/SymmetricKeyAlgorithm.kt @@ -11,110 +11,79 @@ package org.pgpainless.algorithm */ enum class SymmetricKeyAlgorithm(val algorithmId: Int) { - /** - * Plaintext or unencrypted data. - */ - NULL (0), + /** Plaintext or unencrypted data. */ + NULL(0), /** * IDEA is deprecated. + * * @deprecated use a different algorithm. */ - @Deprecated("IDEA is deprecated.") - IDEA (1), + @Deprecated("IDEA is deprecated.") IDEA(1), - /** - * TripleDES (DES-EDE - 168 bit key derived from 192). - */ - TRIPLE_DES (2), + /** TripleDES (DES-EDE - 168 bit key derived from 192). */ + TRIPLE_DES(2), - /** - * CAST5 (128-bit key, as per RFC2144). - */ - CAST5 (3), + /** CAST5 (128-bit key, as per RFC2144). */ + CAST5(3), - /** - * Blowfish (128-bit key, 16 rounds). - */ - BLOWFISH (4), + /** Blowfish (128-bit key, 16 rounds). */ + BLOWFISH(4), - /** - * Reserved in RFC4880. - * SAFER-SK128 (13 rounds) - */ - SAFER (5), + /** Reserved in RFC4880. SAFER-SK128 (13 rounds) */ + SAFER(5), - /** - * Reserved in RFC4880. - * Reserved for DES/SK - */ - DES (6), + /** Reserved in RFC4880. Reserved for DES/SK */ + DES(6), - /** - * AES with 128-bit key. - */ - AES_128 (7), + /** AES with 128-bit key. */ + AES_128(7), - /** - * AES with 192-bit key. - */ - AES_192 (8), + /** AES with 192-bit key. */ + AES_192(8), - /** - * AES with 256-bit key. - */ - AES_256 (9), + /** AES with 256-bit key. */ + AES_256(9), - /** - * Twofish with 256-bit key. - */ - TWOFISH (10), + /** Twofish with 256-bit key. */ + TWOFISH(10), - /** - * Reserved for Camellia with 128-bit key. - */ - CAMELLIA_128 (11), + /** Reserved for Camellia with 128-bit key. */ + CAMELLIA_128(11), - /** - * Reserved for Camellia with 192-bit key. - */ - CAMELLIA_192 (12), + /** Reserved for Camellia with 192-bit key. */ + CAMELLIA_192(12), - /** - * Reserved for Camellia with 256-bit key. - */ - CAMELLIA_256 (13), + /** Reserved for Camellia with 256-bit key. */ + CAMELLIA_256(13), ; companion object { /** - * Return the [SymmetricKeyAlgorithm] enum that corresponds to the provided numeric id. - * If an invalid id is provided, null is returned. + * Return the [SymmetricKeyAlgorithm] enum that corresponds to the provided numeric id. If + * an invalid id is provided, null is returned. * * @param id numeric algorithm id * @return symmetric key algorithm enum */ @JvmStatic fun fromId(id: Int): SymmetricKeyAlgorithm? { - return values().firstOrNull { - it.algorithmId == id - } + return values().firstOrNull { it.algorithmId == id } } /** - * Return the [SymmetricKeyAlgorithm] enum that corresponds to the provided numeric id. - * If an invalid id is provided, throw a [NoSuchElementException]. + * Return the [SymmetricKeyAlgorithm] enum that corresponds to the provided numeric id. If + * an invalid id is provided, throw a [NoSuchElementException]. * * @param id numeric algorithm id * @return symmetric key algorithm enum - * * @throws NoSuchElementException if an unmatched id is provided */ @JvmStatic fun requireFromId(id: Int): SymmetricKeyAlgorithm { - return fromId(id) ?: - throw NoSuchElementException("No SymmetricKeyAlgorithm found for id $id") + return fromId(id) + ?: throw NoSuchElementException("No SymmetricKeyAlgorithm found for id $id") } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/Trustworthiness.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/Trustworthiness.kt index 695632a2..5b9f4c40 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/Trustworthiness.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/Trustworthiness.kt @@ -5,26 +5,25 @@ package org.pgpainless.algorithm /** - * Facade class for [org.bouncycastle.bcpg.sig.TrustSignature]. - * A trust signature subpacket marks the trustworthiness of a certificate and defines its capabilities to act - * as a trusted introducer. + * Facade class for [org.bouncycastle.bcpg.sig.TrustSignature]. A trust signature subpacket marks + * the trustworthiness of a certificate and defines its capabilities to act as a trusted introducer. */ class Trustworthiness(amount: Int, depth: Int) { val depth = capDepth(depth) val amount = capAmount(amount) /** - * Returns true, if the trust amount is equal to 0. - * This means the key is not trusted. + * Returns true, if the trust amount is equal to 0. This means the key is not trusted. * * Otherwise return false + * * @return true if untrusted */ fun isNotTrusted() = amount == NOT_TRUSTED /** - * Return true if the certificate is at least marginally trusted. - * That is the case, if the trust amount is greater than 0. + * Return true if the certificate is at least marginally trusted. That is the case, if the trust + * amount is greater than 0. * * @return true if the cert is at least marginally trusted */ @@ -46,7 +45,8 @@ class Trustworthiness(amount: Int, depth: Int) { fun isIntroducer() = depth >= 1 /** - * Return true, if the certified cert can introduce certificates with trust depth of

otherDepth
. + * Return true, if the certified cert can introduce certificates with trust depth of + *
otherDepth
. * * @param otherDepth other certifications trust depth * @return true if the cert can introduce the other @@ -54,7 +54,8 @@ class Trustworthiness(amount: Int, depth: Int) { fun canIntroduce(otherDepth: Int) = depth > otherDepth /** - * Return true, if the certified cert can introduce certificates with the given
other
trust depth. + * Return true, if the certified cert can introduce certificates with the given
other
+ * trust depth. * * @param other other certificates trust depth * @return true if the cert can introduce the other @@ -66,33 +67,29 @@ class Trustworthiness(amount: Int, depth: Int) { const val MARGINALLY_CONVINCED = 60 // default value for marginally convinced const val NOT_TRUSTED = 0 // 0 is not trusted - @JvmStatic - private val validRange = 0..255 + @JvmStatic private val validRange = 0..255 /** * This means that we are fully convinced of the trustworthiness of the key. * * @return builder */ - @JvmStatic - fun fullyTrusted() = Builder(THRESHOLD_FULLY_CONVINCED) + @JvmStatic fun fullyTrusted() = Builder(THRESHOLD_FULLY_CONVINCED) /** - * This means that we are marginally (partially) convinced of the trustworthiness of the key. + * This means that we are marginally (partially) convinced of the trustworthiness of the + * key. * * @return builder */ - @JvmStatic - fun marginallyTrusted() = Builder(MARGINALLY_CONVINCED) + @JvmStatic fun marginallyTrusted() = Builder(MARGINALLY_CONVINCED) /** - * This means that we do not trust the key. - * Can be used to overwrite previous trust. + * This means that we do not trust the key. Can be used to overwrite previous trust. * * @return builder */ - @JvmStatic - fun untrusted() = Builder(NOT_TRUSTED) + @JvmStatic fun untrusted() = Builder(NOT_TRUSTED) @JvmStatic private fun capAmount(amount: Int): Int { @@ -114,29 +111,28 @@ class Trustworthiness(amount: Int, depth: Int) { class Builder(val amount: Int) { /** - * The key is a trusted introducer (depth 1). - * Certifications made by this key are considered trustworthy. + * The key is a trusted introducer (depth 1). Certifications made by this key are considered + * trustworthy. * * @return trust */ fun introducer() = Trustworthiness(amount, 1) /** - * The key is a meta introducer (depth 2). - * This key can introduce trusted introducers of depth 1. + * The key is a meta introducer (depth 2). This key can introduce trusted introducers of + * depth 1. * * @return trust */ fun metaIntroducer() = Trustworthiness(amount, 2) /** - * The key is a meta introducer of depth
n
. - * This key can introduce meta introducers of depth
n - 1
. + * The key is a meta introducer of depth
n
. This key can introduce meta + * introducers of depth
n - 1
. * * @param n depth * @return trust */ fun metaIntroducerOfDepth(d: Int) = Trustworthiness(amount, d) } - -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/negotiation/HashAlgorithmNegotiator.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/negotiation/HashAlgorithmNegotiator.kt index 98cbe522..31d6b118 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/negotiation/HashAlgorithmNegotiator.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/negotiation/HashAlgorithmNegotiator.kt @@ -26,8 +26,8 @@ interface HashAlgorithmNegotiator { companion object { /** - * Return an instance that negotiates [HashAlgorithms][HashAlgorithm] used for non-revocation signatures - * based on the given [Policy]. + * Return an instance that negotiates [HashAlgorithms][HashAlgorithm] used for + * non-revocation signatures based on the given [Policy]. * * @param policy algorithm policy * @return negotiator @@ -38,8 +38,8 @@ interface HashAlgorithmNegotiator { } /** - * Return an instance that negotiates [HashAlgorithms][HashAlgorithm] used for revocation signatures - * based on the given [Policy]. + * Return an instance that negotiates [HashAlgorithms][HashAlgorithm] used for revocation + * signatures based on the given [Policy]. * * @param policy algorithm policy * @return negotiator @@ -57,15 +57,17 @@ interface HashAlgorithmNegotiator { * @return negotiator */ @JvmStatic - fun negotiateByPolicy(hashAlgorithmPolicy: Policy.HashAlgorithmPolicy): HashAlgorithmNegotiator { - return object: HashAlgorithmNegotiator { - override fun negotiateHashAlgorithm(orderedPrefs: Set): HashAlgorithm { - return orderedPrefs.firstOrNull { - hashAlgorithmPolicy.isAcceptable(it) - } ?: hashAlgorithmPolicy.defaultHashAlgorithm() + fun negotiateByPolicy( + hashAlgorithmPolicy: Policy.HashAlgorithmPolicy + ): HashAlgorithmNegotiator { + return object : HashAlgorithmNegotiator { + override fun negotiateHashAlgorithm( + orderedPrefs: Set + ): HashAlgorithm { + return orderedPrefs.firstOrNull { hashAlgorithmPolicy.isAcceptable(it) } + ?: hashAlgorithmPolicy.defaultHashAlgorithm() } - } } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/negotiation/SymmetricKeyAlgorithmNegotiator.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/negotiation/SymmetricKeyAlgorithmNegotiator.kt index 1a6dfe7f..d11f2c03 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/negotiation/SymmetricKeyAlgorithmNegotiator.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/algorithm/negotiation/SymmetricKeyAlgorithmNegotiator.kt @@ -4,38 +4,41 @@ package org.pgpainless.algorithm.negotiation +import java.lang.IllegalArgumentException import org.pgpainless.algorithm.SymmetricKeyAlgorithm import org.pgpainless.policy.Policy -import java.lang.IllegalArgumentException interface SymmetricKeyAlgorithmNegotiator { /** - * Negotiate a symmetric encryption algorithm. - * If the override is non-null, it will be returned instead of performing an actual negotiation. - * Otherwise, the list of ordered sets containing the preferences of different recipient keys will be - * used to determine a suitable symmetric encryption algorithm. + * Negotiate a symmetric encryption algorithm. If the override is non-null, it will be returned + * instead of performing an actual negotiation. Otherwise, the list of ordered sets containing + * the preferences of different recipient keys will be used to determine a suitable symmetric + * encryption algorithm. * * @param policy algorithm policy * @param override algorithm override (if not null, return this) * @param keyPreferences list of preferences per key * @return negotiated algorithm */ - fun negotiate(policy: Policy.SymmetricKeyAlgorithmPolicy, - override: SymmetricKeyAlgorithm?, - keyPreferences: List>): SymmetricKeyAlgorithm + fun negotiate( + policy: Policy.SymmetricKeyAlgorithmPolicy, + override: SymmetricKeyAlgorithm?, + keyPreferences: List> + ): SymmetricKeyAlgorithm companion object { @JvmStatic fun byPopularity(): SymmetricKeyAlgorithmNegotiator { - return object: SymmetricKeyAlgorithmNegotiator { + return object : SymmetricKeyAlgorithmNegotiator { override fun negotiate( - policy: Policy.SymmetricKeyAlgorithmPolicy, - override: SymmetricKeyAlgorithm?, - keyPreferences: List>): - SymmetricKeyAlgorithm { + policy: Policy.SymmetricKeyAlgorithmPolicy, + override: SymmetricKeyAlgorithm?, + keyPreferences: List> + ): SymmetricKeyAlgorithm { if (override == SymmetricKeyAlgorithm.NULL) { - throw IllegalArgumentException("Algorithm override cannot be NULL (plaintext).") + throw IllegalArgumentException( + "Algorithm override cannot be NULL (plaintext).") } if (override != null) { @@ -53,7 +56,9 @@ interface SymmetricKeyAlgorithmNegotiator { // Pivot map and sort by popularity ascending // score to list(algo) - val byScore = supportWeight.toList() + val byScore = + supportWeight + .toList() .map { e -> e.second to e.first } .groupBy { e -> e.first } .map { e -> e.key to e.value.map { it.second }.toList() } @@ -70,8 +75,7 @@ interface SymmetricKeyAlgorithmNegotiator { return policy.defaultSymmetricKeyAlgorithm } - } } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/authentication/CertificateAuthenticity.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/authentication/CertificateAuthenticity.kt index 2024c710..f3d60bf6 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/authentication/CertificateAuthenticity.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/authentication/CertificateAuthenticity.kt @@ -6,19 +6,19 @@ package org.pgpainless.authentication import org.bouncycastle.openpgp.PGPPublicKeyRing -class CertificateAuthenticity(val userId: String, - val certificate: PGPPublicKeyRing, - val certificationChains: Map, - val targetAmount: Int) { +class CertificateAuthenticity( + val userId: String, + val certificate: PGPPublicKeyRing, + val certificationChains: Map, + val targetAmount: Int +) { val totalTrustAmount: Int get() = certificationChains.values.sum() - /** - * Return the degree of authentication of the binding in percent. - * 100% means full authentication. - * Values smaller than 100% mean partial authentication. + * Return the degree of authentication of the binding in percent. 100% means full + * authentication. Values smaller than 100% mean partial authentication. * * @return authenticity in percent */ @@ -42,16 +42,7 @@ class CertificateAuthenticity(val userId: String, * @param trustAmount actual trust amount of the chain * @param chainLinks links of the chain, starting at the trust-root, ending at the target. */ -class CertificationChain( - val trustAmount: Int, - val chainLinks: List) { +class CertificationChain(val trustAmount: Int, val chainLinks: List) {} -} - -/** - * A chain link contains a node in the trust chain. - */ -class ChainLink( - val certificate: PGPPublicKeyRing) { - -} \ No newline at end of file +/** A chain link contains a node in the trust chain. */ +class ChainLink(val certificate: PGPPublicKeyRing) {} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/authentication/CertificateAuthority.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/authentication/CertificateAuthority.kt index e510f48a..093c2325 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/authentication/CertificateAuthority.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/authentication/CertificateAuthority.kt @@ -2,14 +2,14 @@ // // SPDX-License-Identifier: Apache-2.0 -package org.pgpainless.authentication; +package org.pgpainless.authentication -import org.pgpainless.key.OpenPgpFingerprint import java.util.* +import org.pgpainless.key.OpenPgpFingerprint /** - * Interface for a CA that can authenticate trust-worthy certificates. - * Such a CA might be a fixed list of trustworthy certificates, or a dynamic implementation like the Web-of-Trust. + * Interface for a CA that can authenticate trust-worthy certificates. Such a CA might be a fixed + * list of trustworthy certificates, or a dynamic implementation like the Web-of-Trust. * * @see PGPainless-WOT * @see OpenPGP Web of Trust @@ -17,52 +17,58 @@ import java.util.* interface CertificateAuthority { /** - * Determine the authenticity of the binding between the given fingerprint and the userId. - * In other words, determine, how much evidence can be gathered, that the certificate with the given - * fingerprint really belongs to the user with the given userId. + * Determine the authenticity of the binding between the given fingerprint and the userId. In + * other words, determine, how much evidence can be gathered, that the certificate with the + * given fingerprint really belongs to the user with the given userId. * * @param fingerprint fingerprint of the certificate * @param userId userId - * @param email if true, the userId will be treated as an email address and all user-IDs containing - * the email address will be matched. + * @param email if true, the userId will be treated as an email address and all user-IDs + * containing the email address will be matched. * @param referenceTime reference time at which the binding shall be evaluated - * @param targetAmount target trust amount (120 = fully authenticated, 240 = doubly authenticated, - * 60 = partially authenticated...) + * @param targetAmount target trust amount (120 = fully authenticated, 240 = doubly + * authenticated, 60 = partially authenticated...) * @return information about the authenticity of the binding */ - fun authenticateBinding(fingerprint: OpenPgpFingerprint, - userId: String, - email: Boolean, - referenceTime: Date, - targetAmount: Int): CertificateAuthenticity; + fun authenticateBinding( + fingerprint: OpenPgpFingerprint, + userId: String, + email: Boolean, + referenceTime: Date, + targetAmount: Int + ): CertificateAuthenticity /** * Lookup certificates, which carry a trustworthy binding to the given userId. * * @param userId userId - * @param email if true, the user-ID will be treated as an email address and all user-IDs containing - * the email address will be matched. + * @param email if true, the user-ID will be treated as an email address and all user-IDs + * containing the email address will be matched. * @param referenceTime reference time at which the binding shall be evaluated - * @param targetAmount target trust amount (120 = fully authenticated, 240 = doubly authenticated, - * 60 = partially authenticated...) + * @param targetAmount target trust amount (120 = fully authenticated, 240 = doubly + * authenticated, 60 = partially authenticated...) * @return list of identified bindings */ - fun lookupByUserId(userId: String, - email: Boolean, - referenceTime: Date, - targetAmount: Int): List + fun lookupByUserId( + userId: String, + email: Boolean, + referenceTime: Date, + targetAmount: Int + ): List /** - * Identify trustworthy bindings for a certificate. - * The result is a list of authenticatable userIds on the certificate. + * Identify trustworthy bindings for a certificate. The result is a list of authenticatable + * userIds on the certificate. * * @param fingerprint fingerprint of the certificate * @param referenceTime reference time for trust calculations - * @param targetAmount target trust amount (120 = fully authenticated, 240 = doubly authenticated, - * 60 = partially authenticated...) + * @param targetAmount target trust amount (120 = fully authenticated, 240 = doubly + * authenticated, 60 = partially authenticated...) * @return list of identified bindings */ - fun identifyByFingerprint(fingerprint: OpenPgpFingerprint, - referenceTime: Date, - targetAmount: Int): List + fun identifyByFingerprint( + fingerprint: OpenPgpFingerprint, + referenceTime: Date, + targetAmount: Int + ): List } diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/ConsumerOptions.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/ConsumerOptions.kt index e0ec1fd5..dff33b2d 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/ConsumerOptions.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/ConsumerOptions.kt @@ -4,6 +4,9 @@ package org.pgpainless.decryption_verification +import java.io.IOException +import java.io.InputStream +import java.util.* import org.bouncycastle.extensions.getPublicKeyFor import org.bouncycastle.openpgp.* import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory @@ -14,13 +17,8 @@ import org.pgpainless.key.protection.SecretKeyRingProtector import org.pgpainless.signature.SignatureUtils import org.pgpainless.util.Passphrase import org.pgpainless.util.SessionKey -import java.io.IOException -import java.io.InputStream -import java.util.* -/** - * Options for decryption and signature verification. - */ +/** Options for decryption and signature verification. */ class ConsumerOptions { private var ignoreMDCErrors = false @@ -34,15 +32,16 @@ class ConsumerOptions { private var missingCertificateCallback: MissingPublicKeyCallback? = null private var sessionKey: SessionKey? = null - private val customDecryptorFactories = mutableMapOf() + private val customDecryptorFactories = + mutableMapOf() private val decryptionKeys = mutableMapOf() private val decryptionPassphrases = mutableSetOf() private var missingKeyPassphraseStrategy = MissingKeyPassphraseStrategy.INTERACTIVE private var multiPassStrategy: MultiPassStrategy = InMemoryMultiPassStrategy() /** - * Consider signatures on the message made before the given timestamp invalid. - * Null means no limitation. + * Consider signatures on the message made before the given timestamp invalid. Null means no + * limitation. * * @param timestamp timestamp * @return options @@ -54,8 +53,8 @@ class ConsumerOptions { fun getVerifyNotBefore() = verifyNotBefore /** - * Consider signatures on the message made after the given timestamp invalid. - * Null means no limitation. + * Consider signatures on the message made after the given timestamp invalid. Null means no + * limitation. * * @param timestamp timestamp * @return options @@ -82,26 +81,27 @@ class ConsumerOptions { * @param verificationCerts certificates for signature verification * @return options */ - fun addVerificationCerts(verificationCerts: PGPPublicKeyRingCollection): ConsumerOptions = apply { - for (cert in verificationCerts) { - addVerificationCert(cert) + fun addVerificationCerts(verificationCerts: PGPPublicKeyRingCollection): ConsumerOptions = + apply { + for (cert in verificationCerts) { + addVerificationCert(cert) + } } - } /** * Add some detached signatures from the given [InputStream] for verification. * * @param signatureInputStream input stream of detached signatures * @return options - * * @throws IOException in case of an IO error * @throws PGPException in case of an OpenPGP error */ @Throws(IOException::class, PGPException::class) - fun addVerificationOfDetachedSignatures(signatureInputStream: InputStream): ConsumerOptions = apply { - val signatures = SignatureUtils.readSignatures(signatureInputStream) - addVerificationOfDetachedSignatures(signatures) - } + fun addVerificationOfDetachedSignatures(signatureInputStream: InputStream): ConsumerOptions = + apply { + val signatures = SignatureUtils.readSignatures(signatureInputStream) + addVerificationOfDetachedSignatures(signatures) + } /** * Add some detached signatures for verification. @@ -109,7 +109,9 @@ class ConsumerOptions { * @param detachedSignatures detached signatures * @return options */ - fun addVerificationOfDetachedSignatures(detachedSignatures: List): ConsumerOptions = apply { + fun addVerificationOfDetachedSignatures( + detachedSignatures: List + ): ConsumerOptions = apply { for (signature in detachedSignatures) { addVerificationOfDetachedSignature(signature) } @@ -121,14 +123,16 @@ class ConsumerOptions { * @param detachedSignature detached signature * @return options */ - fun addVerificationOfDetachedSignature(detachedSignature: PGPSignature): ConsumerOptions = apply { - detachedSignatures.add(detachedSignature) - } + fun addVerificationOfDetachedSignature(detachedSignature: PGPSignature): ConsumerOptions = + apply { + detachedSignatures.add(detachedSignature) + } fun getDetachedSignatures() = detachedSignatures.toList() /** - * Set a callback that's used when a certificate (public key) is missing for signature verification. + * Set a callback that's used when a certificate (public key) is missing for signature + * verification. * * @param callback callback * @return options @@ -152,18 +156,18 @@ class ConsumerOptions { fun getSessionKey() = sessionKey /** - * Add a key for message decryption. If the key is encrypted, the [SecretKeyRingProtector] - * is used to decrypt it when needed. + * Add a key for message decryption. If the key is encrypted, the [SecretKeyRingProtector] is + * used to decrypt it when needed. * * @param key key * @param keyRingProtector protector for the secret key * @return options */ @JvmOverloads - fun addDecryptionKey(key: PGPSecretKeyRing, - protector: SecretKeyRingProtector = SecretKeyRingProtector.unprotectedKeys()) = apply { - decryptionKeys[key] = protector - } + fun addDecryptionKey( + key: PGPSecretKeyRing, + protector: SecretKeyRingProtector = SecretKeyRingProtector.unprotectedKeys() + ) = apply { decryptionKeys[key] = protector } /** * Add the keys in the provided key collection for message decryption. @@ -173,18 +177,21 @@ class ConsumerOptions { * @return options */ @JvmOverloads - fun addDecryptionKeys(keys: PGPSecretKeyRingCollection, - protector: SecretKeyRingProtector = SecretKeyRingProtector.unprotectedKeys()) = apply { + fun addDecryptionKeys( + keys: PGPSecretKeyRingCollection, + protector: SecretKeyRingProtector = SecretKeyRingProtector.unprotectedKeys() + ) = apply { for (key in keys) { addDecryptionKey(key, protector) } } /** - * Add a passphrase for message decryption. - * This passphrase will be used to try to decrypt messages which were symmetrically encrypted for a passphrase. + * Add a passphrase for message decryption. This passphrase will be used to try to decrypt + * messages which were symmetrically encrypted for a passphrase. * - * See [Symmetrically Encrypted Data Packet](https://datatracker.ietf.org/doc/html/rfc4880#section-5.7) + * See + * [Symmetrically Encrypted Data Packet](https://datatracker.ietf.org/doc/html/rfc4880#section-5.7) * * @param passphrase passphrase * @return options @@ -195,8 +202,8 @@ class ConsumerOptions { /** * Add a custom [PublicKeyDataDecryptorFactory] which enable decryption of messages, e.g. using - * hardware-backed secret keys. - * (See e.g. [org.pgpainless.decryption_verification.HardwareSecurity.HardwareDataDecryptorFactory]). + * hardware-backed secret keys. (See e.g. + * [org.pgpainless.decryption_verification.HardwareSecurity.HardwareDataDecryptorFactory]). * * @param factory decryptor factory * @return options @@ -206,9 +213,8 @@ class ConsumerOptions { } /** - * Return the custom [PublicKeyDataDecryptorFactory] that were - * set by the user. - * These factories can be used to decrypt session keys using a custom logic. + * Return the custom [PublicKeyDataDecryptorFactory] that were set by the user. These factories + * can be used to decrypt session keys using a custom logic. * * @return custom decryptor factories */ @@ -236,8 +242,8 @@ class ConsumerOptions { fun getCertificateSource() = certificates /** - * Return the callback that gets called when a certificate for signature verification is missing. - * This method might return `null` if the users hasn't set a callback. + * Return the callback that gets called when a certificate for signature verification is + * missing. This method might return `null` if the users hasn't set a callback. * * @return missing public key callback */ @@ -255,45 +261,46 @@ class ConsumerOptions { /** * By default, PGPainless will require encrypted messages to make use of SEIP data packets. - * Those are Symmetrically Encrypted Integrity Protected Data packets. - * Symmetrically Encrypted Data Packets without integrity protection are rejected by default. - * Furthermore, PGPainless will throw an exception if verification of the MDC error detection - * code of the SEIP packet fails. + * Those are Symmetrically Encrypted Integrity Protected Data packets. Symmetrically Encrypted + * Data Packets without integrity protection are rejected by default. Furthermore, PGPainless + * will throw an exception if verification of the MDC error detection code of the SEIP packet + * fails. * * Failure of MDC verification indicates a tampered ciphertext, which might be the cause of an * attack or data corruption. * * This method can be used to ignore MDC errors and allow PGPainless to consume encrypted data - * without integrity protection. - * If the flag
ignoreMDCErrors
is set to true, PGPainless will + * without integrity protection. If the flag
ignoreMDCErrors
is set to true, + * PGPainless will + * * not throw exceptions for SEIP packets with tampered ciphertext + * * not throw exceptions for SEIP packets with tampered MDC + * * not throw exceptions for MDCs with bad CTB + * * not throw exceptions for MDCs with bad length * - * * not throw exceptions for SEIP packets with tampered ciphertext - * * not throw exceptions for SEIP packets with tampered MDC - * * not throw exceptions for MDCs with bad CTB - * * not throw exceptions for MDCs with bad length + * It will however still throw an exception if it encounters a SEIP packet with missing or + * truncated MDC * - * - * It will however still throw an exception if it encounters a SEIP packet with missing or truncated MDC - * - * See [Sym. Encrypted Integrity Protected Data Packet](https://datatracker.ietf.org/doc/html/rfc4880.section-5.13) + * See + * [Sym. Encrypted Integrity Protected Data Packet](https://datatracker.ietf.org/doc/html/rfc4880.section-5.13) * * @param ignoreMDCErrors true if MDC errors or missing MDCs shall be ignored, false otherwise. * @return options */ @Deprecated("Ignoring non-integrity-protected packets is discouraged.") - fun setIgnoreMDCErrors(ignoreMDCErrors: Boolean): ConsumerOptions = apply { this.ignoreMDCErrors = ignoreMDCErrors } + fun setIgnoreMDCErrors(ignoreMDCErrors: Boolean): ConsumerOptions = apply { + this.ignoreMDCErrors = ignoreMDCErrors + } fun isIgnoreMDCErrors() = ignoreMDCErrors /** - * Force PGPainless to handle the data provided by the [InputStream] as non-OpenPGP data. - * This workaround might come in handy if PGPainless accidentally mistakes the data for binary OpenPGP data. + * Force PGPainless to handle the data provided by the [InputStream] as non-OpenPGP data. This + * workaround might come in handy if PGPainless accidentally mistakes the data for binary + * OpenPGP data. * * @return options */ - fun forceNonOpenPgpData(): ConsumerOptions = apply { - this.forceNonOpenPgpData = true - } + fun forceNonOpenPgpData(): ConsumerOptions = apply { this.forceNonOpenPgpData = true } /** * Return true, if the ciphertext should be handled as binary non-OpenPGP data. @@ -303,15 +310,15 @@ class ConsumerOptions { fun isForceNonOpenPgpData() = forceNonOpenPgpData /** - * Specify the [MissingKeyPassphraseStrategy]. - * This strategy defines, how missing passphrases for unlocking secret keys are handled. - * In interactive mode ([MissingKeyPassphraseStrategy.INTERACTIVE]) PGPainless will try to obtain missing + * Specify the [MissingKeyPassphraseStrategy]. This strategy defines, how missing passphrases + * for unlocking secret keys are handled. In interactive mode + * ([MissingKeyPassphraseStrategy.INTERACTIVE]) PGPainless will try to obtain missing * passphrases for secret keys via the [SecretKeyRingProtector] * [org.pgpainless.key.protection.passphrase_provider.SecretKeyPassphraseProvider] callback. * - * In non-interactice mode ([MissingKeyPassphraseStrategy.THROW_EXCEPTION]), PGPainless will instead - * throw a [org.pgpainless.exception.MissingPassphraseException] containing the ids of all keys for which - * there are missing passphrases. + * In non-interactice mode ([MissingKeyPassphraseStrategy.THROW_EXCEPTION]), PGPainless will + * instead throw a [org.pgpainless.exception.MissingPassphraseException] containing the ids of + * all keys for which there are missing passphrases. * * @param strategy strategy * @return options @@ -331,8 +338,8 @@ class ConsumerOptions { } /** - * Set a custom multi-pass strategy for processing cleartext-signed messages. - * Uses [InMemoryMultiPassStrategy] by default. + * Set a custom multi-pass strategy for processing cleartext-signed messages. Uses + * [InMemoryMultiPassStrategy] by default. * * @param multiPassStrategy multi-pass caching strategy * @return builder @@ -343,8 +350,7 @@ class ConsumerOptions { } /** - * Return the currently configured [MultiPassStrategy]. - * Defaults to [InMemoryMultiPassStrategy]. + * Return the currently configured [MultiPassStrategy]. Defaults to [InMemoryMultiPassStrategy]. * * @return multi-pass strategy */ @@ -353,8 +359,8 @@ class ConsumerOptions { } /** - * Source for OpenPGP certificates. - * When verifying signatures on a message, this object holds available signer certificates. + * Source for OpenPGP certificates. When verifying signatures on a message, this object holds + * available signer certificates. */ class CertificateSource { private val explicitCertificates: MutableSet = mutableSetOf() @@ -370,6 +376,7 @@ class ConsumerOptions { /** * Return the set of explicitly set verification certificates. + * * @return explicitly set verification certs */ fun getExplicitCertificates(): Set { @@ -377,9 +384,9 @@ class ConsumerOptions { } /** - * Return a certificate which contains a subkey with the given keyId. - * This method first checks all explicitly set verification certs and if no cert is found it consults - * the certificate stores. + * Return a certificate which contains a subkey with the given keyId. This method first + * checks all explicitly set verification certs and if no cert is found it consults the + * certificate stores. * * @param keyId key id * @return certificate @@ -389,13 +396,10 @@ class ConsumerOptions { } fun getCertificate(signature: PGPSignature): PGPPublicKeyRing? = - explicitCertificates.firstOrNull { - it.getPublicKeyFor(signature) != null - } + explicitCertificates.firstOrNull { it.getPublicKeyFor(signature) != null } } companion object { - @JvmStatic - fun get() = ConsumerOptions() + @JvmStatic fun get() = ConsumerOptions() } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/CustomPublicKeyDataDecryptorFactory.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/CustomPublicKeyDataDecryptorFactory.kt index b7f57da3..4a0dbeba 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/CustomPublicKeyDataDecryptorFactory.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/CustomPublicKeyDataDecryptorFactory.kt @@ -8,19 +8,19 @@ import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory import org.pgpainless.key.SubkeyIdentifier /** - * Custom [PublicKeyDataDecryptorFactory] which can enable customized implementations of message decryption - * using public keys. - * This class can for example be used to implement message encryption using hardware tokens like smartcards or - * TPMs. + * Custom [PublicKeyDataDecryptorFactory] which can enable customized implementations of message + * decryption using public keys. This class can for example be used to implement message encryption + * using hardware tokens like smartcards or TPMs. + * * @see [ConsumerOptions.addCustomDecryptorFactory] */ interface CustomPublicKeyDataDecryptorFactory : PublicKeyDataDecryptorFactory { /** - * Identifier for the subkey for which this particular [CustomPublicKeyDataDecryptorFactory] - * is intended. + * Identifier for the subkey for which this particular [CustomPublicKeyDataDecryptorFactory] is + * intended. * * @return subkey identifier */ val subkeyIdentifier: SubkeyIdentifier -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/DecryptionBuilder.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/DecryptionBuilder.kt index 4934f5de..d1d4f8b2 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/DecryptionBuilder.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/DecryptionBuilder.kt @@ -7,20 +7,20 @@ package org.pgpainless.decryption_verification import java.io.InputStream /** - * Builder class that takes an [InputStream] of ciphertext (or plaintext signed data) - * and combines it with a configured [ConsumerOptions] object to form a [DecryptionStream] which - * can be used to decrypt an OpenPGP message or verify signatures. + * Builder class that takes an [InputStream] of ciphertext (or plaintext signed data) and combines + * it with a configured [ConsumerOptions] object to form a [DecryptionStream] which can be used to + * decrypt an OpenPGP message or verify signatures. */ -class DecryptionBuilder: DecryptionBuilderInterface { +class DecryptionBuilder : DecryptionBuilderInterface { override fun onInputStream(inputStream: InputStream): DecryptionBuilderInterface.DecryptWith { return DecryptWithImpl(inputStream) } - class DecryptWithImpl(val inputStream: InputStream): DecryptionBuilderInterface.DecryptWith { + class DecryptWithImpl(val inputStream: InputStream) : DecryptionBuilderInterface.DecryptWith { override fun withOptions(consumerOptions: ConsumerOptions): DecryptionStream { return OpenPgpMessageInputStream.create(inputStream, consumerOptions) } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/DecryptionBuilderInterface.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/DecryptionBuilderInterface.kt index c15f301e..18fd4179 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/DecryptionBuilderInterface.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/DecryptionBuilderInterface.kt @@ -4,14 +4,15 @@ package org.pgpainless.decryption_verification -import org.bouncycastle.openpgp.PGPException import java.io.IOException import java.io.InputStream +import org.bouncycastle.openpgp.PGPException interface DecryptionBuilderInterface { /** - * Create a [DecryptionStream] on an [InputStream] which contains the encrypted and/or signed data. + * Create a [DecryptionStream] on an [InputStream] which contains the encrypted and/or signed + * data. * * @param inputStream encrypted and/or signed data. * @return api handle @@ -31,4 +32,4 @@ interface DecryptionBuilderInterface { @Throws(PGPException::class, IOException::class) fun withOptions(consumerOptions: ConsumerOptions): DecryptionStream } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/DecryptionStream.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/DecryptionStream.kt index b9499784..86bd490a 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/DecryptionStream.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/DecryptionStream.kt @@ -9,13 +9,13 @@ import java.io.InputStream /** * Abstract definition of an [InputStream] which can be used to decrypt / verify OpenPGP messages. */ -abstract class DecryptionStream: InputStream() { +abstract class DecryptionStream : InputStream() { /** - * Return [MessageMetadata] about the decrypted / verified message. - * The [DecryptionStream] MUST be closed via [close] before the metadata object can be accessed. + * Return [MessageMetadata] about the decrypted / verified message. The [DecryptionStream] MUST + * be closed via [close] before the metadata object can be accessed. * * @return message metadata */ abstract val metadata: MessageMetadata -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/HardwareSecurity.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/HardwareSecurity.kt index 7d0d243a..1974e290 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/HardwareSecurity.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/HardwareSecurity.kt @@ -4,6 +4,7 @@ package org.pgpainless.decryption_verification +import kotlin.jvm.Throws import org.bouncycastle.bcpg.AEADEncDataPacket import org.bouncycastle.bcpg.SymmetricEncIntegrityPacket import org.bouncycastle.openpgp.PGPException @@ -12,25 +13,22 @@ import org.bouncycastle.openpgp.operator.PGPDataDecryptor import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory import org.bouncycastle.openpgp.operator.bc.BcPublicKeyDataDecryptorFactory import org.pgpainless.key.SubkeyIdentifier -import kotlin.jvm.Throws -/** - * Enable integration of hardware-backed OpenPGP keys. - */ +/** Enable integration of hardware-backed OpenPGP keys. */ class HardwareSecurity { interface DecryptionCallback { /** - * Delegate decryption of a Public-Key-Encrypted-Session-Key (PKESK) to an external API for dealing with - * hardware security modules such as smartcards or TPMs. + * Delegate decryption of a Public-Key-Encrypted-Session-Key (PKESK) to an external API for + * dealing with hardware security modules such as smartcards or TPMs. * - * If decryption fails for some reason, a subclass of the [HardwareSecurityException] is thrown. + * If decryption fails for some reason, a subclass of the [HardwareSecurityException] is + * thrown. * * @param keyId id of the key * @param keyAlgorithm algorithm * @param sessionKeyData encrypted session key - * * @return decrypted session key * @throws HardwareSecurityException exception */ @@ -39,38 +37,51 @@ class HardwareSecurity { } /** - * Implementation of [PublicKeyDataDecryptorFactory] which delegates decryption of encrypted session keys - * to a [DecryptionCallback]. - * Users can provide such a callback to delegate decryption of messages to hardware security SDKs. + * Implementation of [PublicKeyDataDecryptorFactory] which delegates decryption of encrypted + * session keys to a [DecryptionCallback]. Users can provide such a callback to delegate + * decryption of messages to hardware security SDKs. */ class HardwareDataDecryptorFactory( - override val subkeyIdentifier: SubkeyIdentifier, - private val callback: DecryptionCallback, + override val subkeyIdentifier: SubkeyIdentifier, + private val callback: DecryptionCallback, ) : CustomPublicKeyDataDecryptorFactory { // luckily we can instantiate the BcPublicKeyDataDecryptorFactory with null as argument. private val factory: PublicKeyDataDecryptorFactory = BcPublicKeyDataDecryptorFactory(null) - override fun createDataDecryptor(withIntegrityPacket: Boolean, encAlgorithm: Int, key: ByteArray?): PGPDataDecryptor { + override fun createDataDecryptor( + withIntegrityPacket: Boolean, + encAlgorithm: Int, + key: ByteArray? + ): PGPDataDecryptor { return factory.createDataDecryptor(withIntegrityPacket, encAlgorithm, key) } - override fun createDataDecryptor(aeadEncDataPacket: AEADEncDataPacket?, sessionKey: PGPSessionKey?): PGPDataDecryptor { + override fun createDataDecryptor( + aeadEncDataPacket: AEADEncDataPacket?, + sessionKey: PGPSessionKey? + ): PGPDataDecryptor { return factory.createDataDecryptor(aeadEncDataPacket, sessionKey) } - override fun createDataDecryptor(seipd: SymmetricEncIntegrityPacket?, sessionKey: PGPSessionKey?): PGPDataDecryptor { + override fun createDataDecryptor( + seipd: SymmetricEncIntegrityPacket?, + sessionKey: PGPSessionKey? + ): PGPDataDecryptor { return factory.createDataDecryptor(seipd, sessionKey) } - override fun recoverSessionData(keyAlgorithm: Int, secKeyData: Array): ByteArray { + override fun recoverSessionData( + keyAlgorithm: Int, + secKeyData: Array + ): ByteArray { return try { callback.decryptSessionKey(subkeyIdentifier.subkeyId, keyAlgorithm, secKeyData[0]) - } catch (e : HardwareSecurityException) { + } catch (e: HardwareSecurityException) { throw PGPException("Hardware-backed decryption failed.", e) } } } class HardwareSecurityException : Exception() -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/IntegrityProtectedInputStream.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/IntegrityProtectedInputStream.kt index a1e095f8..4618882c 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/IntegrityProtectedInputStream.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/IntegrityProtectedInputStream.kt @@ -4,23 +4,25 @@ package org.pgpainless.decryption_verification +import java.io.IOException +import java.io.InputStream import org.bouncycastle.openpgp.PGPEncryptedData import org.bouncycastle.openpgp.PGPException import org.pgpainless.exception.ModificationDetectionException import org.slf4j.Logger import org.slf4j.LoggerFactory -import java.io.IOException -import java.io.InputStream class IntegrityProtectedInputStream( - private val inputStream: InputStream, - private val encryptedData: PGPEncryptedData, - private val options: ConsumerOptions + private val inputStream: InputStream, + private val encryptedData: PGPEncryptedData, + private val options: ConsumerOptions ) : InputStream() { private var closed: Boolean = false override fun read() = inputStream.read() + override fun read(b: ByteArray, off: Int, len: Int) = inputStream.read(b, off, len) + override fun close() { if (closed) return @@ -29,7 +31,7 @@ class IntegrityProtectedInputStream( try { if (!encryptedData.verify()) throw ModificationDetectionException() LOGGER.debug("Integrity Protection check passed.") - } catch (e : PGPException) { + } catch (e: PGPException) { throw IOException("Data appears to not be integrity protected.", e) } } @@ -39,4 +41,4 @@ class IntegrityProtectedInputStream( @JvmStatic val LOGGER: Logger = LoggerFactory.getLogger(IntegrityProtectedInputStream::class.java) } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/MessageInspector.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/MessageInspector.kt index 64b2a5f3..acfcba51 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/MessageInspector.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/MessageInspector.kt @@ -4,14 +4,15 @@ package org.pgpainless.decryption_verification +import java.io.IOException +import java.io.InputStream import org.bouncycastle.openpgp.* import org.pgpainless.implementation.ImplementationFactory import org.pgpainless.util.ArmorUtils -import java.io.IOException -import java.io.InputStream /** - * Inspect an OpenPGP message to determine IDs of its encryption keys or whether it is passphrase protected. + * Inspect an OpenPGP message to determine IDs of its encryption keys or whether it is passphrase + * protected. */ class MessageInspector { @@ -23,9 +24,10 @@ class MessageInspector { * @param isSignedOnly true, if the message is not encrypted, but signed using OnePassSignatures */ data class EncryptionInfo( - val keyIds: List, - val isPassphraseEncrypted: Boolean, - val isSignedOnly: Boolean) { + val keyIds: List, + val isPassphraseEncrypted: Boolean, + val isSignedOnly: Boolean + ) { val isEncrypted: Boolean get() = isPassphraseEncrypted || keyIds.isNotEmpty() @@ -34,25 +36,26 @@ class MessageInspector { companion object { /** - * Parses parts of the provided OpenPGP message in order to determine which keys were used to encrypt it. + * Parses parts of the provided OpenPGP message in order to determine which keys were used + * to encrypt it. * * @param message OpenPGP message * @return encryption info - * * @throws PGPException in case the message is broken * @throws IOException in case of an IO error */ @JvmStatic @Throws(PGPException::class, IOException::class) - fun determineEncryptionInfoForMessage(message: String): EncryptionInfo = determineEncryptionInfoForMessage(message.byteInputStream()) + fun determineEncryptionInfoForMessage(message: String): EncryptionInfo = + determineEncryptionInfoForMessage(message.byteInputStream()) /** - * Parses parts of the provided OpenPGP message in order to determine which keys were used to encrypt it. - * Note: This method does not rewind the passed in Stream, so you might need to take care of that yourselves. + * Parses parts of the provided OpenPGP message in order to determine which keys were used + * to encrypt it. Note: This method does not rewind the passed in Stream, so you might need + * to take care of that yourselves. * * @param inputStream openpgp message * @return encryption information - * * @throws IOException in case of an IO error * @throws PGPException if the message is broken */ @@ -70,13 +73,12 @@ class MessageInspector { var n: Any? while (objectFactory.nextObject().also { n = it } != null) { when (val next = n!!) { - is PGPOnePassSignatureList -> { if (!next.isEmpty) { - return EncryptionInfo(listOf(), isPassphraseEncrypted = false, isSignedOnly = true) + return EncryptionInfo( + listOf(), isPassphraseEncrypted = false, isSignedOnly = true) } } - is PGPEncryptedDataList -> { var isPassphraseEncrypted = false val keyIds = mutableListOf() @@ -90,13 +92,12 @@ class MessageInspector { // Data is encrypted, we cannot go deeper return EncryptionInfo(keyIds, isPassphraseEncrypted, false) } - is PGPCompressedData -> { - objectFactory = ImplementationFactory.getInstance().getPGPObjectFactory( - PGPUtil.getDecoderStream(next.dataStream)) + objectFactory = + ImplementationFactory.getInstance() + .getPGPObjectFactory(PGPUtil.getDecoderStream(next.dataStream)) continue } - is PGPLiteralData -> { break } @@ -105,4 +106,4 @@ class MessageInspector { return EncryptionInfo(listOf(), isPassphraseEncrypted = false, isSignedOnly = false) } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/MessageMetadata.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/MessageMetadata.kt index 3eb49e5d..ecdeadf0 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/MessageMetadata.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/MessageMetadata.kt @@ -4,6 +4,8 @@ package org.pgpainless.decryption_verification +import java.util.* +import javax.annotation.Nonnull import org.bouncycastle.extensions.matches import org.bouncycastle.openpgp.PGPKeyRing import org.bouncycastle.openpgp.PGPLiteralData @@ -15,116 +17,118 @@ import org.pgpainless.exception.MalformedOpenPgpMessageException import org.pgpainless.key.OpenPgpFingerprint import org.pgpainless.key.SubkeyIdentifier import org.pgpainless.util.SessionKey -import java.util.* -import javax.annotation.Nonnull -/** - * View for extracting metadata about a [Message]. - */ -class MessageMetadata( - val message: Message -) { +/** View for extracting metadata about a [Message]. */ +class MessageMetadata(val message: Message) { // ################################################################################################################ - // ### Encryption ### + // ### Encryption + // ### // ################################################################################################################ /** - * The [SymmetricKeyAlgorithm] of the outermost encrypted data packet, or null if message is unencrypted. + * The [SymmetricKeyAlgorithm] of the outermost encrypted data packet, or null if message is + * unencrypted. */ val encryptionAlgorithm: SymmetricKeyAlgorithm? - get() = encryptionAlgorithms.let { - if (it.hasNext()) it.next() else null - } + get() = encryptionAlgorithms.let { if (it.hasNext()) it.next() else null } /** - * [Iterator] of each [SymmetricKeyAlgorithm] encountered in the message. - * The first item returned by the iterator is the algorithm of the outermost encrypted data packet, the next item - * that of the next nested encrypted data packet and so on. - * The iterator might also be empty, in case of an unencrypted message. + * [Iterator] of each [SymmetricKeyAlgorithm] encountered in the message. The first item + * returned by the iterator is the algorithm of the outermost encrypted data packet, the next + * item that of the next nested encrypted data packet and so on. The iterator might also be + * empty, in case of an unencrypted message. */ val encryptionAlgorithms: Iterator get() = encryptionLayers.asSequence().map { it.algorithm }.iterator() val isEncrypted: Boolean - get() = if (encryptionAlgorithm == null) false else encryptionAlgorithm != SymmetricKeyAlgorithm.NULL + get() = + if (encryptionAlgorithm == null) false + else encryptionAlgorithm != SymmetricKeyAlgorithm.NULL fun isEncryptedFor(keys: PGPKeyRing): Boolean { return encryptionLayers.asSequence().any { - it.recipients.any { keyId -> - keys.getPublicKey(keyId) != null - } + it.recipients.any { keyId -> keys.getPublicKey(keyId) != null } } } /** - * [SessionKey] of the outermost encrypted data packet. - * If the message was unencrypted, this method returns `null`. + * [SessionKey] of the outermost encrypted data packet. If the message was unencrypted, this + * method returns `null`. */ val sessionKey: SessionKey? get() = sessionKeys.asSequence().firstOrNull() /** - * [Iterator] of each [SessionKey] for all encrypted data packets in the message. - * The first item returned by the iterator is the session key of the outermost encrypted data packet, - * the next item that of the next nested encrypted data packet and so on. - * The iterator might also be empty, in case of an unencrypted message. + * [Iterator] of each [SessionKey] for all encrypted data packets in the message. The first item + * returned by the iterator is the session key of the outermost encrypted data packet, the next + * item that of the next nested encrypted data packet and so on. The iterator might also be + * empty, in case of an unencrypted message. */ val sessionKeys: Iterator get() = encryptionLayers.asSequence().mapNotNull { it.sessionKey }.iterator() /** * [SubkeyIdentifier] of the decryption key that was used to decrypt the outermost encryption - * layer. - * If the message was unencrypted or was decrypted using a passphrase, this field might be `null`. + * layer. If the message was unencrypted or was decrypted using a passphrase, this field might + * be `null`. */ val decryptionKey: SubkeyIdentifier? - get() = encryptionLayers.asSequence() - .mapNotNull { it.decryptionKey } - .firstOrNull() + get() = encryptionLayers.asSequence().mapNotNull { it.decryptionKey }.firstOrNull() - /** - * List containing all recipient keyIDs. - */ + /** List containing all recipient keyIDs. */ val recipientKeyIds: List - get() = encryptionLayers.asSequence() + get() = + encryptionLayers + .asSequence() .map { it.recipients.toMutableList() } - .reduce { all, keyIds -> all.addAll(keyIds); all } + .reduce { all, keyIds -> + all.addAll(keyIds) + all + } .toList() val encryptionLayers: Iterator - get() = object : LayerIterator(message) { - override fun matches(layer: Packet) = layer is EncryptedData - override fun getProperty(last: Layer) = last as EncryptedData - } + get() = + object : LayerIterator(message) { + override fun matches(layer: Packet) = layer is EncryptedData + + override fun getProperty(last: Layer) = last as EncryptedData + } // ################################################################################################################ - // ### Compression ### + // ### Compression + // ### // ################################################################################################################ /** - * [CompressionAlgorithm] of the outermost compressed data packet, or null, if the message - * does not contain any compressed data packets. + * [CompressionAlgorithm] of the outermost compressed data packet, or null, if the message does + * not contain any compressed data packets. */ - val compressionAlgorithm: CompressionAlgorithm? = compressionAlgorithms.asSequence().firstOrNull() + val compressionAlgorithm: CompressionAlgorithm? = + compressionAlgorithms.asSequence().firstOrNull() /** - * [Iterator] of each [CompressionAlgorithm] encountered in the message. - * The first item returned by the iterator is the algorithm of the outermost compressed data packet, the next - * item that of the next nested compressed data packet and so on. - * The iterator might also be empty, in case of a message without any compressed data packets. + * [Iterator] of each [CompressionAlgorithm] encountered in the message. The first item returned + * by the iterator is the algorithm of the outermost compressed data packet, the next item that + * of the next nested compressed data packet and so on. The iterator might also be empty, in + * case of a message without any compressed data packets. */ val compressionAlgorithms: Iterator get() = compressionLayers.asSequence().map { it.algorithm }.iterator() val compressionLayers: Iterator - get() = object : LayerIterator(message) { - override fun matches(layer: Packet) = layer is CompressedData - override fun getProperty(last: Layer) = last as CompressedData - } + get() = + object : LayerIterator(message) { + override fun matches(layer: Packet) = layer is CompressedData + + override fun getProperty(last: Layer) = last as CompressedData + } // ################################################################################################################ - // ### Signatures ### + // ### Signatures + // ### // ################################################################################################################ val isUsingCleartextSignatureFramework: Boolean @@ -133,81 +137,87 @@ class MessageMetadata( val verifiedSignatures: List get() = verifiedInlineSignatures.plus(verifiedDetachedSignatures) - /** - * List of all rejected signatures. - */ + /** List of all rejected signatures. */ val rejectedSignatures: List - get() = mutableListOf() + get() = + mutableListOf() .plus(rejectedInlineSignatures) .plus(rejectedDetachedSignatures) .toList() /** - * List of all verified inline-signatures. - * This list contains all acceptable, correct signatures that were part of the message itself. + * List of all verified inline-signatures. This list contains all acceptable, correct signatures + * that were part of the message itself. */ - val verifiedInlineSignatures: List = verifiedInlineSignaturesByLayer + val verifiedInlineSignatures: List = + verifiedInlineSignaturesByLayer .asSequence() .map { it.toMutableList() } - .reduce { acc, signatureVerifications -> acc.addAll(signatureVerifications); acc } + .reduce { acc, signatureVerifications -> + acc.addAll(signatureVerifications) + acc + } .toList() /** * [Iterator] of each [List] of verified inline-signatures of the message, separated by layer. - * Since signatures might occur in different layers within a message, this method can be used to gain more detailed - * insights into what signatures were encountered at what layers of the message structure. - * Each item of the [Iterator] represents a layer of the message and contains only signatures from - * this layer. - * An empty list means no (or no acceptable) signatures were encountered in that layer. + * Since signatures might occur in different layers within a message, this method can be used to + * gain more detailed insights into what signatures were encountered at what layers of the + * message structure. Each item of the [Iterator] represents a layer of the message and contains + * only signatures from this layer. An empty list means no (or no acceptable) signatures were + * encountered in that layer. */ val verifiedInlineSignaturesByLayer: Iterator> - get() = object : LayerIterator>(message) { - override fun matches(layer: Packet) = layer is Layer + get() = + object : LayerIterator>(message) { + override fun matches(layer: Packet) = layer is Layer - override fun getProperty(last: Layer): List { - return listOf() + override fun getProperty(last: Layer): List { + return listOf() .plus(last.verifiedOnePassSignatures) .plus(last.verifiedPrependedSignatures) + } } - } - - /** - * List of all rejected inline-signatures of the message. - */ - val rejectedInlineSignatures: List = rejectedInlineSignaturesByLayer + /** List of all rejected inline-signatures of the message. */ + val rejectedInlineSignatures: List = + rejectedInlineSignaturesByLayer .asSequence() .map { it.toMutableList() } - .reduce { acc, failures -> acc.addAll(failures); acc} + .reduce { acc, failures -> + acc.addAll(failures) + acc + } .toList() /** - * Similar to [verifiedInlineSignaturesByLayer], this field contains all rejected inline-signatures - * of the message, but organized by layer. + * Similar to [verifiedInlineSignaturesByLayer], this field contains all rejected + * inline-signatures of the message, but organized by layer. */ val rejectedInlineSignaturesByLayer: Iterator> - get() = object : LayerIterator>(message) { - override fun matches(layer: Packet) = layer is Layer + get() = + object : LayerIterator>(message) { + override fun matches(layer: Packet) = layer is Layer - override fun getProperty(last: Layer): List = + override fun getProperty(last: Layer): List = mutableListOf() - .plus(last.rejectedOnePassSignatures) - .plus(last.rejectedPrependedSignatures) - } + .plus(last.rejectedOnePassSignatures) + .plus(last.rejectedPrependedSignatures) + } /** - * List of all verified detached signatures. - * This list contains all acceptable, correct detached signatures. + * List of all verified detached signatures. This list contains all acceptable, correct detached + * signatures. */ val verifiedDetachedSignatures: List = message.verifiedDetachedSignatures - /** - * List of all rejected detached signatures. - */ - val rejectedDetachedSignatures: List = message.rejectedDetachedSignatures + /** List of all rejected detached signatures. */ + val rejectedDetachedSignatures: List = + message.rejectedDetachedSignatures /** - * True, if the message contains any (verified or rejected) signature, false if no signatures are present. + * True, if the message contains any (verified or rejected) signature, false if no signatures + * are present. */ val hasSignature: Boolean get() = isVerifiedSigned() || hasRejectedSignatures() @@ -217,110 +227,131 @@ class MessageMetadata( fun hasRejectedSignatures(): Boolean = rejectedSignatures.isNotEmpty() /** - * Return true, if the message was signed by a certificate for which we can authenticate a binding to the given userId. + * Return true, if the message was signed by a certificate for which we can authenticate a + * binding to the given userId. * * @param userId userId - * @param email if true, treat the user-id as an email address and match all userIDs containing this address + * @param email if true, treat the user-id as an email address and match all userIDs containing + * this address * @param certificateAuthority certificate authority - * @param targetAmount targeted trust amount that needs to be reached by the binding to qualify as authenticated. - * defaults to 120. + * @param targetAmount targeted trust amount that needs to be reached by the binding to qualify + * as authenticated. defaults to 120. * @return true, if we can authenticate a binding for a signing key with sufficient evidence */ @JvmOverloads - fun isAuthenticatablySignedBy(userId: String, email: Boolean, certificateAuthority: CertificateAuthority, targetAmount: Int = 120): Boolean { - return verifiedSignatures.any { certificateAuthority - .authenticateBinding(it.signingKey.fingerprint, userId, email, it.signature.creationTime, targetAmount) + fun isAuthenticatablySignedBy( + userId: String, + email: Boolean, + certificateAuthority: CertificateAuthority, + targetAmount: Int = 120 + ): Boolean { + return verifiedSignatures.any { + certificateAuthority + .authenticateBinding( + it.signingKey.fingerprint, + userId, + email, + it.signature.creationTime, + targetAmount) .authenticated } } /** - * Return rue, if the message was verifiable signed by a certificate that either has the given fingerprint - * as primary key, or as the signing subkey. + * Return rue, if the message was verifiable signed by a certificate that either has the given + * fingerprint as primary key, or as the signing subkey. * * @param fingerprint fingerprint * @return true if message was signed by a cert identified by the given fingerprint */ fun isVerifiedSignedBy(fingerprint: OpenPgpFingerprint) = - verifiedSignatures.any { it.signingKey.matches(fingerprint) } + verifiedSignatures.any { it.signingKey.matches(fingerprint) } fun isVerifiedSignedBy(keys: PGPKeyRing) = - verifiedSignatures.any { keys.matches(it.signingKey) } + verifiedSignatures.any { keys.matches(it.signingKey) } fun isVerifiedDetachedSignedBy(fingerprint: OpenPgpFingerprint) = - verifiedDetachedSignatures.any { it.signingKey.matches(fingerprint) } + verifiedDetachedSignatures.any { it.signingKey.matches(fingerprint) } fun isVerifiedDetachedSignedBy(keys: PGPKeyRing) = - verifiedDetachedSignatures.any { keys.matches(it.signingKey) } + verifiedDetachedSignatures.any { keys.matches(it.signingKey) } fun isVerifiedInlineSignedBy(fingerprint: OpenPgpFingerprint) = - verifiedInlineSignatures.any { it.signingKey.matches(fingerprint) } + verifiedInlineSignatures.any { it.signingKey.matches(fingerprint) } fun isVerifiedInlineSignedBy(keys: PGPKeyRing) = - verifiedInlineSignatures.any { keys.matches(it.signingKey) } + verifiedInlineSignatures.any { keys.matches(it.signingKey) } // ################################################################################################################ - // ### Literal Data ### + // ### Literal Data + // ### // ################################################################################################################ /** - * Value of the literal data packet's filename field. - * This value can be used to store a decrypted file under its original filename, - * but since this field is not necessarily part of the signed data of a message, usage of this field is - * discouraged. + * Value of the literal data packet's filename field. This value can be used to store a + * decrypted file under its original filename, but since this field is not necessarily part of + * the signed data of a message, usage of this field is discouraged. * - * @see RFC4880 §5.9. Literal Data Packet + * @see RFC4880 §5.9. Literal Data + * Packet */ val filename: String? = findLiteralData()?.fileName /** - * True, if the sender signals an increased degree of confidentiality by setting the filename of the literal - * data packet to a special value that indicates that the data is intended for your eyes only. + * True, if the sender signals an increased degree of confidentiality by setting the filename of + * the literal data packet to a special value that indicates that the data is intended for your + * eyes only. */ @Deprecated("Reliance on this signaling mechanism is discouraged.") val isForYourEyesOnly: Boolean = PGPLiteralData.CONSOLE == filename /** - * Value of the literal data packets modification date field. - * This value can be used to restore the modification date of a decrypted file, - * but since this field is not necessarily part of the signed data, its use is discouraged. + * Value of the literal data packets modification date field. This value can be used to restore + * the modification date of a decrypted file, but since this field is not necessarily part of + * the signed data, its use is discouraged. * - * @see RFC4880 §5.9. Literal Data Packet + * @see RFC4880 §5.9. Literal Data + * Packet */ val modificationDate: Date? = findLiteralData()?.modificationDate /** - * Value of the format field of the literal data packet. - * This value indicates what format (text, binary data, ...) the data has. - * Since this field is not necessarily part of the signed data of a message, its usage is discouraged. + * Value of the format field of the literal data packet. This value indicates what format (text, + * binary data, ...) the data has. Since this field is not necessarily part of the signed data + * of a message, its usage is discouraged. * - * @see RFC4880 §5.9. Literal Data Packet + * @see RFC4880 §5.9. Literal Data + * Packet */ val literalDataEncoding: StreamEncoding? = findLiteralData()?.format /** - * Find the [LiteralData] layer of an OpenPGP message. - * This method might return null, for example for a cleartext signed message without OpenPGP packets. + * Find the [LiteralData] layer of an OpenPGP message. This method might return null, for + * example for a cleartext signed message without OpenPGP packets. * * @return literal data */ private fun findLiteralData(): LiteralData? { - // If the message is a non-OpenPGP message with a detached signature, or a Cleartext Signed message, + // If the message is a non-OpenPGP message with a detached signature, or a Cleartext Signed + // message, // we might not have a Literal Data packet. var nested = message.child ?: return null while (nested.hasNestedChild()) { val layer = nested as Layer - nested = checkNotNull(layer.child) { - // Otherwise, we MUST find a Literal Data packet, or else the message is malformed - "Malformed OpenPGP message. Cannot find Literal Data Packet" - } + nested = + checkNotNull(layer.child) { + // Otherwise, we MUST find a Literal Data packet, or else the message is + // malformed + "Malformed OpenPGP message. Cannot find Literal Data Packet" + } } return nested as LiteralData } // ################################################################################################################ - // ### Message Structure ### + // ### Message Structure + // ### // ################################################################################################################ interface Packet @@ -329,13 +360,12 @@ class MessageMetadata( fun hasNestedChild(): Boolean } - abstract class Layer( - val depth: Int - ) : Packet { + abstract class Layer(val depth: Int) : Packet { init { if (depth > MAX_LAYER_DEPTH) { - throw MalformedOpenPgpMessageException("Maximum packet nesting depth ($MAX_LAYER_DEPTH) exceeded.") + throw MalformedOpenPgpMessageException( + "Maximum packet nesting depth ($MAX_LAYER_DEPTH) exceeded.") } } @@ -347,9 +377,8 @@ class MessageMetadata( val rejectedPrependedSignatures: List = mutableListOf() /** - * Nested child element of this layer. - * Might be `null`, if this layer does not have a child element - * (e.g. if this is a [LiteralData] packet). + * Nested child element of this layer. Might be `null`, if this layer does not have a child + * element (e.g. if this is a [LiteralData] packet). */ var child: Nested? = null @@ -386,8 +415,8 @@ class MessageMetadata( * Outermost OpenPGP Message structure. * * @param cleartextSigned whether the message is using the Cleartext Signature Framework - * - * @see RFC4880 §7. Cleartext Signature Framework + * @see RFC4880 §7. Cleartext + * Signature Framework */ class Message(var cleartextSigned: Boolean = false) : Layer(0) { fun setCleartextSigned() = apply { cleartextSigned = true } @@ -397,14 +426,14 @@ class MessageMetadata( * Literal Data Packet. * * @param fileName value of the filename field. An empty String represents no filename. - * @param modificationDate value of the modification date field. The special value `Date(0)` indicates no - * modification date. + * @param modificationDate value of the modification date field. The special value `Date(0)` + * indicates no modification date. * @param format value of the format field. */ class LiteralData( - val fileName: String = "", - val modificationDate: Date = Date(0L), - val format: StreamEncoding = StreamEncoding.BINARY + val fileName: String = "", + val modificationDate: Date = Date(0L), + val format: StreamEncoding = StreamEncoding.BINARY ) : Nested { // A literal data packet MUST NOT have a child element, as its content is the plaintext @@ -417,9 +446,7 @@ class MessageMetadata( * @param algorithm [CompressionAlgorithm] used to compress the packet. * @param depth nesting depth at which this packet was encountered. */ - class CompressedData( - val algorithm: CompressionAlgorithm, - depth: Int) : Layer(depth), Nested { + class CompressedData(val algorithm: CompressionAlgorithm, depth: Int) : Layer(depth), Nested { // A compressed data packet MUST have a child element override fun hasNestedChild() = true @@ -431,38 +458,30 @@ class MessageMetadata( * @param algorithm symmetric key algorithm used to encrypt the packet. * @param depth nesting depth at which this packet was encountered. */ - class EncryptedData( - val algorithm: SymmetricKeyAlgorithm, - depth: Int - ) : Layer(depth), Nested { + class EncryptedData(val algorithm: SymmetricKeyAlgorithm, depth: Int) : Layer(depth), Nested { - /** - * [SessionKey] used to decrypt the packet. - */ + /** [SessionKey] used to decrypt the packet. */ var sessionKey: SessionKey? = null - /** - * List of all recipient key ids to which the packet was encrypted for. - */ + /** List of all recipient key ids to which the packet was encrypted for. */ val recipients: List = mutableListOf() - fun addRecipients(keyIds: List) = apply { - (recipients as MutableList).addAll(keyIds) - } + fun addRecipients(keyIds: List) = apply { (recipients as MutableList).addAll(keyIds) } /** - * Identifier of the subkey that was used to decrypt the packet (in case of a public key encrypted packet). + * Identifier of the subkey that was used to decrypt the packet (in case of a public key + * encrypted packet). */ var decryptionKey: SubkeyIdentifier? = null // An encrypted data packet MUST have a child element override fun hasNestedChild() = true - } /** - * Iterator that iterates the packet structure from outermost to innermost packet, emitting the results of - * a transformation ([getProperty]) on those packets that match ([matches]) a given criterion. + * Iterator that iterates the packet structure from outermost to innermost packet, emitting the + * results of a transformation ([getProperty]) on those packets that match ([matches]) a given + * criterion. * * @param message outermost structure object */ @@ -519,6 +538,7 @@ class MessageMetadata( } abstract fun matches(layer: Packet): Boolean + abstract fun getProperty(last: Layer): O } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/MissingKeyPassphraseStrategy.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/MissingKeyPassphraseStrategy.kt index f8bb448b..c5443ba8 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/MissingKeyPassphraseStrategy.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/MissingKeyPassphraseStrategy.kt @@ -4,19 +4,18 @@ package org.pgpainless.decryption_verification -/** - * Strategy defining how missing secret key passphrases are handled. - */ +/** Strategy defining how missing secret key passphrases are handled. */ enum class MissingKeyPassphraseStrategy { /** - * Try to interactively obtain key passphrases one-by-one via callbacks, - * eg [org.pgpainless.key.protection.passphrase_provider.SecretKeyPassphraseProvider]. + * Try to interactively obtain key passphrases one-by-one via callbacks, eg + * [org.pgpainless.key.protection.passphrase_provider.SecretKeyPassphraseProvider]. */ INTERACTIVE, /** * Do not try to obtain passphrases interactively and instead throw a - * [org.pgpainless.exception.MissingPassphraseException] listing all keys with missing passphrases. + * [org.pgpainless.exception.MissingPassphraseException] listing all keys with missing + * passphrases. */ THROW_EXCEPTION -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/MissingPublicKeyCallback.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/MissingPublicKeyCallback.kt index 723530b7..eb81847f 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/MissingPublicKeyCallback.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/MissingPublicKeyCallback.kt @@ -9,20 +9,19 @@ import org.bouncycastle.openpgp.PGPPublicKeyRing fun interface MissingPublicKeyCallback { /** - * This method gets called if we encounter a signature made by a key which was not provided for signature verification. - * If you cannot provide the requested key, it is safe to return null here. - * PGPainless will then continue verification with the next signature. + * This method gets called if we encounter a signature made by a key which was not provided for + * signature verification. If you cannot provide the requested key, it is safe to return null + * here. PGPainless will then continue verification with the next signature. * - * Note: The key-id might belong to a subkey, so be aware that when looking up the [PGPPublicKeyRing], - * you may not only search for the key-id on the key rings primary key! + * Note: The key-id might belong to a subkey, so be aware that when looking up the + * [PGPPublicKeyRing], you may not only search for the key-id on the key rings primary key! * - * It would be super cool to provide the OpenPgp fingerprint here, but unfortunately one-pass-signatures - * only contain the key id. + * It would be super cool to provide the OpenPgp fingerprint here, but unfortunately + * one-pass-signatures only contain the key id. * * @param keyId ID of the missing signing (sub)key * @return keyring containing the key or null - * * @see RFC */ fun onMissingPublicKeyEncountered(keyId: Long): PGPPublicKeyRing? -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/OpenPgpMessageInputStream.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/OpenPgpMessageInputStream.kt index b823fdaf..5e8b68f2 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/OpenPgpMessageInputStream.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/OpenPgpMessageInputStream.kt @@ -4,6 +4,9 @@ package org.pgpainless.decryption_verification +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream import openpgp.openPgpKeyId import org.bouncycastle.bcpg.BCPGInputStream import org.bouncycastle.bcpg.UnsupportedPacketVersionException @@ -34,16 +37,14 @@ import org.pgpainless.signature.consumer.SignatureValidator import org.pgpainless.util.ArmoredInputStreamFactory import org.pgpainless.util.SessionKey import org.slf4j.LoggerFactory -import java.io.IOException -import java.io.InputStream -import java.io.OutputStream class OpenPgpMessageInputStream( - type: Type, - inputStream: InputStream, - private val options: ConsumerOptions, - private val layerMetadata: Layer, - private val policy: Policy) : DecryptionStream() { + type: Type, + inputStream: InputStream, + private val options: ConsumerOptions, + private val layerMetadata: Layer, + private val policy: Policy +) : DecryptionStream() { private val signatures: Signatures = Signatures(options) private var packetInputStream: TeeBCPGInputStream? = null @@ -58,19 +59,20 @@ class OpenPgpMessageInputStream( signatures.addDetachedSignatures(options.getDetachedSignatures()) } - when(type) { + when (type) { Type.standard -> { // tee out packet bytes for signature verification - packetInputStream = TeeBCPGInputStream(BCPGInputStream.wrap(inputStream), signatures) + packetInputStream = + TeeBCPGInputStream(BCPGInputStream.wrap(inputStream), signatures) // *omnomnom* consumePackets() } - Type.cleartext_signed -> { val multiPassStrategy = options.getMultiPassStrategy() - val detachedSignatures = ClearsignedMessageUtil.detachSignaturesFromInbandClearsignedMessage( + val detachedSignatures = + ClearsignedMessageUtil.detachSignaturesFromInbandClearsignedMessage( inputStream, multiPassStrategy.messageOutputStream) for (signature in detachedSignatures) { @@ -78,9 +80,9 @@ class OpenPgpMessageInputStream( } options.isForceNonOpenPgpData() - nestedInputStream = TeeInputStream(multiPassStrategy.messageInputStream, this.signatures) + nestedInputStream = + TeeInputStream(multiPassStrategy.messageInputStream, this.signatures) } - Type.non_openpgp -> { packetInputStream = null nestedInputStream = TeeInputStream(inputStream, this.signatures) @@ -89,11 +91,17 @@ class OpenPgpMessageInputStream( } enum class Type { - standard, cleartext_signed, non_openpgp + standard, + cleartext_signed, + non_openpgp } - constructor(inputStream: InputStream, options: ConsumerOptions, metadata: Layer, policy: Policy): - this(Type.standard, inputStream, options, metadata, policy) + constructor( + inputStream: InputStream, + options: ConsumerOptions, + metadata: Layer, + policy: Policy + ) : this(Type.standard, inputStream, options, metadata, policy) private fun consumePackets() { val pIn = packetInputStream ?: return @@ -102,50 +110,53 @@ class OpenPgpMessageInputStream( // Comsume packets, potentially stepping into nested layers layer@ while (run { - packet = pIn.nextPacketTag() - packet - } != null) { + packet = pIn.nextPacketTag() + packet + } != null) { signatures.nextPacket(packet!!) // Consume packets in a layer - when(packet) { - + when (packet) { OpenPgpPacket.LIT -> { processLiteralData() break@layer // nest down } - OpenPgpPacket.COMP -> { processCompressedData() break@layer // nest down } - OpenPgpPacket.OPS -> { processOnePassSignature() // OPS is on the same layer, no nest down } - OpenPgpPacket.SIG -> { processSignature() // SIG is on the same layer, no nest down } - - OpenPgpPacket.PKESK, OpenPgpPacket.SKESK, OpenPgpPacket.SED, OpenPgpPacket.SEIPD -> { + OpenPgpPacket.PKESK, + OpenPgpPacket.SKESK, + OpenPgpPacket.SED, + OpenPgpPacket.SEIPD -> { if (processEncryptedData()) { break@layer } throw MissingDecryptionMethodException("No working decryption method found.") } - OpenPgpPacket.MARKER -> { LOGGER.debug("Skipping Marker Packet") pIn.readMarker() } - - OpenPgpPacket.SK, OpenPgpPacket.PK, OpenPgpPacket.SSK, OpenPgpPacket.PSK, OpenPgpPacket.TRUST, OpenPgpPacket.UID, OpenPgpPacket.UATTR -> + OpenPgpPacket.SK, + OpenPgpPacket.PK, + OpenPgpPacket.SSK, + OpenPgpPacket.PSK, + OpenPgpPacket.TRUST, + OpenPgpPacket.UID, + OpenPgpPacket.UATTR -> throw MalformedOpenPgpMessageException("Illegal Packet in Stream: $packet") - - OpenPgpPacket.EXP_1, OpenPgpPacket.EXP_2, OpenPgpPacket.EXP_3, OpenPgpPacket.EXP_4 -> + OpenPgpPacket.EXP_1, + OpenPgpPacket.EXP_2, + OpenPgpPacket.EXP_3, + OpenPgpPacket.EXP_4 -> throw MalformedOpenPgpMessageException("Unsupported Packet in Stream: $packet") - else -> throw MalformedOpenPgpMessageException("Unexpected Packet in Stream: $packet") } @@ -158,8 +169,10 @@ class OpenPgpMessageInputStream( val literalData = packetInputStream!!.readLiteralData() // Extract Metadata - layerMetadata.child = LiteralData( - literalData.fileName, literalData.modificationTime, + layerMetadata.child = + LiteralData( + literalData.fileName, + literalData.modificationTime, StreamEncoding.requireFromCode(literalData.format)) nestedInputStream = literalData.inputStream @@ -171,18 +184,22 @@ class OpenPgpMessageInputStream( val compressedData = packetInputStream!!.readCompressedData() // Extract Metadata - val compressionLayer = CompressedData( + val compressionLayer = + CompressedData( CompressionAlgorithm.requireFromId(compressedData.algorithm), layerMetadata.depth + 1) - LOGGER.debug("Compressed Data Packet (${compressionLayer.algorithm}) at depth ${layerMetadata.depth} encountered.") - nestedInputStream = OpenPgpMessageInputStream(compressedData.dataStream, options, compressionLayer, policy) + LOGGER.debug( + "Compressed Data Packet (${compressionLayer.algorithm}) at depth ${layerMetadata.depth} encountered.") + nestedInputStream = + OpenPgpMessageInputStream(compressedData.dataStream, options, compressionLayer, policy) } private fun processOnePassSignature() { syntaxVerifier.next(InputSymbol.ONE_PASS_SIGNATURE) val ops = packetInputStream!!.readOnePassSignature() - LOGGER.debug("One-Pass-Signature Packet by key ${ops.keyID.openPgpKeyId()} at depth ${layerMetadata.depth} encountered.") + LOGGER.debug( + "One-Pass-Signature Packet by key ${ops.keyID.openPgpKeyId()} at depth ${layerMetadata.depth} encountered.") signatures.addOnePassSignature(ops) } @@ -190,26 +207,33 @@ class OpenPgpMessageInputStream( // true if signature corresponds to OPS val isSigForOps = syntaxVerifier.peekStack() == StackSymbol.OPS syntaxVerifier.next(InputSymbol.SIGNATURE) - val signature = try { - packetInputStream!!.readSignature() - } catch (e : UnsupportedPacketVersionException) { - LOGGER.debug("Unsupported Signature at depth ${layerMetadata.depth} encountered.", e) - return - } + val signature = + try { + packetInputStream!!.readSignature() + } catch (e: UnsupportedPacketVersionException) { + LOGGER.debug( + "Unsupported Signature at depth ${layerMetadata.depth} encountered.", e) + return + } val keyId = signature.issuerKeyId if (isSigForOps) { - LOGGER.debug("Signature Packet corresponding to One-Pass-Signature by key ${keyId.openPgpKeyId()} at depth ${layerMetadata.depth} encountered.") - signatures.leaveNesting() // TODO: Only leave nesting if all OPSs of the nesting layer are dealt with + LOGGER.debug( + "Signature Packet corresponding to One-Pass-Signature by key ${keyId.openPgpKeyId()} at depth ${layerMetadata.depth} encountered.") + signatures + .leaveNesting() // TODO: Only leave nesting if all OPSs of the nesting layer are + // dealt with signatures.addCorrespondingOnePassSignature(signature, layerMetadata, policy) } else { - LOGGER.debug("Prepended Signature Packet by key ${keyId.openPgpKeyId()} at depth ${layerMetadata.depth} encountered.") + LOGGER.debug( + "Prepended Signature Packet by key ${keyId.openPgpKeyId()} at depth ${layerMetadata.depth} encountered.") signatures.addPrependedSignature(signature) } } private fun processEncryptedData(): Boolean { - LOGGER.debug("Symmetrically Encrypted Data Packet at depth ${layerMetadata.depth} encountered.") + LOGGER.debug( + "Symmetrically Encrypted Data Packet at depth ${layerMetadata.depth} encountered.") syntaxVerifier.next(InputSymbol.ENCRYPTED_DATA) val encDataList = packetInputStream!!.readEncryptedDataList() if (!encDataList.isIntegrityProtected) { @@ -220,22 +244,25 @@ class OpenPgpMessageInputStream( } val esks = SortedESKs(encDataList) - LOGGER.debug("Symmetrically Encrypted Integrity-Protected Data has ${esks.skesks.size} SKESK(s) and" + + LOGGER.debug( + "Symmetrically Encrypted Integrity-Protected Data has ${esks.skesks.size} SKESK(s) and" + " ${esks.pkesks.size + esks.anonPkesks.size} PKESK(s) from which ${esks.anonPkesks.size} PKESK(s)" + " have an anonymous recipient.") // try custom decryptor factories for ((key, decryptorFactory) in options.getCustomDecryptorFactories()) { LOGGER.debug("Attempt decryption with custom decryptor factory with key $key.") - esks.pkesks.filter { - // find matching PKESK - it.keyID == key.subkeyId - }.forEach { - // attempt decryption - if (decryptPKESKAndStream(esks, key, decryptorFactory, it)) { - return true + esks.pkesks + .filter { + // find matching PKESK + it.keyID == key.subkeyId + } + .forEach { + // attempt decryption + if (decryptPKESKAndStream(esks, key, decryptorFactory, it)) { + return true + } } - } } // try provided session key @@ -244,20 +271,24 @@ class OpenPgpMessageInputStream( LOGGER.debug("Attempt decryption with provided session key.") throwIfUnacceptable(sk.algorithm) - val decryptorFactory = ImplementationFactory.getInstance() - .getSessionKeyDataDecryptorFactory(sk) + val decryptorFactory = + ImplementationFactory.getInstance().getSessionKeyDataDecryptorFactory(sk) val layer = EncryptedData(sk.algorithm, layerMetadata.depth + 1) val skEncData = encDataList.extractSessionKeyEncryptedData() try { val decrypted = skEncData.getDataStream(decryptorFactory) layer.sessionKey = sk - val integrityProtected = IntegrityProtectedInputStream(decrypted, skEncData, options) - nestedInputStream = OpenPgpMessageInputStream(integrityProtected, options, layer, policy) + val integrityProtected = + IntegrityProtectedInputStream(decrypted, skEncData, options) + nestedInputStream = + OpenPgpMessageInputStream(integrityProtected, options, layer, policy) LOGGER.debug("Successfully decrypted data using provided session key") return true - } catch (e : PGPException) { + } catch (e: PGPException) { // Session key mismatch? - LOGGER.debug("Decryption using provided session key failed. Mismatched session key and message?", e) + LOGGER.debug( + "Decryption using provided session key failed. Mismatched session key and message?", + e) } } @@ -267,19 +298,21 @@ class OpenPgpMessageInputStream( LOGGER.debug("Attempt decryption with provided passphrase") val algorithm = SymmetricKeyAlgorithm.requireFromId(skesk.algorithm) if (!isAcceptable(algorithm)) { - LOGGER.debug("Skipping SKESK with unacceptable encapsulation algorithm $algorithm") + LOGGER.debug( + "Skipping SKESK with unacceptable encapsulation algorithm $algorithm") continue } - val decryptorFactory = ImplementationFactory.getInstance() - .getPBEDataDecryptorFactory(passphrase) + val decryptorFactory = + ImplementationFactory.getInstance().getPBEDataDecryptorFactory(passphrase) if (decryptSKESKAndStream(esks, skesk, decryptorFactory)) { return true } } } - val postponedDueToMissingPassphrase = mutableListOf>() + val postponedDueToMissingPassphrase = + mutableListOf>() // try (known) secret keys esks.pkesks.forEach { pkesk -> @@ -295,7 +328,8 @@ class OpenPgpMessageInputStream( LOGGER.debug("Attempt decryption using secret key $decryptionKeyId") val protector = options.getSecretKeyProtector(decryptionKeys) ?: continue if (!protector.hasPassphraseFor(secretKey.keyID)) { - LOGGER.debug("Missing passphrase for key $decryptionKeyId. Postponing decryption until all other keys were tried.") + LOGGER.debug( + "Missing passphrase for key $decryptionKeyId. Postponing decryption until all other keys were tried.") postponedDueToMissingPassphrase.add(secretKey to pkesk) continue } @@ -319,7 +353,8 @@ class OpenPgpMessageInputStream( val protector = options.getSecretKeyProtector(decryptionKeys) ?: continue if (!protector.hasPassphraseFor(secretKey.keyID)) { - LOGGER.debug("Missing passphrase for key $decryptionKeyId. Postponing decryption until all other keys were tried.") + LOGGER.debug( + "Missing passphrase for key $decryptionKeyId. Postponing decryption until all other keys were tried.") postponedDueToMissingPassphrase.add(secretKey to pkesk) continue } @@ -331,15 +366,14 @@ class OpenPgpMessageInputStream( } } - if (options.getMissingKeyPassphraseStrategy() == MissingKeyPassphraseStrategy.THROW_EXCEPTION) { + if (options.getMissingKeyPassphraseStrategy() == + MissingKeyPassphraseStrategy.THROW_EXCEPTION) { // Non-interactive mode: Throw an exception with all locked decryption keys - postponedDueToMissingPassphrase.map { - SubkeyIdentifier(getDecryptionKey(it.first.keyID)!!, it.first.keyID) - }.also { - if (it.isNotEmpty()) - throw MissingPassphraseException(it.toSet()) - } - } else if (options.getMissingKeyPassphraseStrategy() == MissingKeyPassphraseStrategy.INTERACTIVE) { + postponedDueToMissingPassphrase + .map { SubkeyIdentifier(getDecryptionKey(it.first.keyID)!!, it.first.keyID) } + .also { if (it.isNotEmpty()) throw MissingPassphraseException(it.toSet()) } + } else if (options.getMissingKeyPassphraseStrategy() == + MissingKeyPassphraseStrategy.INTERACTIVE) { for ((secretKey, pkesk) in postponedDueToMissingPassphrase) { val keyId = secretKey.keyID val decryptionKeys = getDecryptionKey(pkesk)!! @@ -348,7 +382,8 @@ class OpenPgpMessageInputStream( continue } - LOGGER.debug("Attempt decryption with key $decryptionKeyId while interactively requesting its passphrase.") + LOGGER.debug( + "Attempt decryption with key $decryptionKeyId while interactively requesting its passphrase.") val protector = options.getSecretKeyProtector(decryptionKeys) ?: continue val privateKey = secretKey.unlock(protector) if (decryptWithPrivateKey(esks, privateKey, decryptionKeyId, pkesk)) { @@ -364,29 +399,37 @@ class OpenPgpMessageInputStream( return false } - private fun decryptWithPrivateKey(esks: SortedESKs, - privateKey: PGPPrivateKey, - decryptionKeyId: SubkeyIdentifier, - pkesk: PGPPublicKeyEncryptedData): Boolean { - val decryptorFactory = ImplementationFactory.getInstance() - .getPublicKeyDataDecryptorFactory(privateKey) + private fun decryptWithPrivateKey( + esks: SortedESKs, + privateKey: PGPPrivateKey, + decryptionKeyId: SubkeyIdentifier, + pkesk: PGPPublicKeyEncryptedData + ): Boolean { + val decryptorFactory = + ImplementationFactory.getInstance().getPublicKeyDataDecryptorFactory(privateKey) return decryptPKESKAndStream(esks, decryptionKeyId, decryptorFactory, pkesk) } - private fun hasUnsupportedS2KSpecifier(secretKey: PGPSecretKey, decryptionKeyId: SubkeyIdentifier): Boolean { + private fun hasUnsupportedS2KSpecifier( + secretKey: PGPSecretKey, + decryptionKeyId: SubkeyIdentifier + ): Boolean { val s2k = secretKey.s2K if (s2k != null) { if (s2k.type in 100..110) { - LOGGER.debug("Skipping PKESK because key $decryptionKeyId has unsupported private S2K specifier ${s2k.type}") + LOGGER.debug( + "Skipping PKESK because key $decryptionKeyId has unsupported private S2K specifier ${s2k.type}") return true } } return false } - private fun decryptSKESKAndStream(esks: SortedESKs, - skesk: PGPPBEEncryptedData, - decryptorFactory: PBEDataDecryptorFactory): Boolean { + private fun decryptSKESKAndStream( + esks: SortedESKs, + skesk: PGPPBEEncryptedData, + decryptorFactory: PBEDataDecryptorFactory + ): Boolean { try { val decrypted = skesk.getDataStream(decryptorFactory) val sessionKey = SessionKey(skesk.getSessionKey(decryptorFactory)) @@ -396,38 +439,45 @@ class OpenPgpMessageInputStream( encryptedData.addRecipients(esks.pkesks.map { it.keyID }) LOGGER.debug("Successfully decrypted data with passphrase") val integrityProtected = IntegrityProtectedInputStream(decrypted, skesk, options) - nestedInputStream = OpenPgpMessageInputStream(integrityProtected, options, encryptedData, policy) + nestedInputStream = + OpenPgpMessageInputStream(integrityProtected, options, encryptedData, policy) return true - } catch (e : UnacceptableAlgorithmException) { + } catch (e: UnacceptableAlgorithmException) { throw e - } catch (e : PGPException) { - LOGGER.debug("Decryption of encrypted data packet using password failed. Password mismatch?", e) + } catch (e: PGPException) { + LOGGER.debug( + "Decryption of encrypted data packet using password failed. Password mismatch?", e) } return false } - private fun decryptPKESKAndStream(esks: SortedESKs, - decryptionKeyId: SubkeyIdentifier, - decryptorFactory: PublicKeyDataDecryptorFactory, - pkesk: PGPPublicKeyEncryptedData): Boolean { + private fun decryptPKESKAndStream( + esks: SortedESKs, + decryptionKeyId: SubkeyIdentifier, + decryptorFactory: PublicKeyDataDecryptorFactory, + pkesk: PGPPublicKeyEncryptedData + ): Boolean { try { val decrypted = pkesk.getDataStream(decryptorFactory) val sessionKey = SessionKey(pkesk.getSessionKey(decryptorFactory)) throwIfUnacceptable(sessionKey.algorithm) - val encryptedData = EncryptedData( - SymmetricKeyAlgorithm.requireFromId(pkesk.getSymmetricAlgorithm(decryptorFactory)), + val encryptedData = + EncryptedData( + SymmetricKeyAlgorithm.requireFromId( + pkesk.getSymmetricAlgorithm(decryptorFactory)), layerMetadata.depth + 1) encryptedData.decryptionKey = decryptionKeyId encryptedData.sessionKey = sessionKey encryptedData.addRecipients(esks.pkesks.plus(esks.anonPkesks).map { it.keyID }) LOGGER.debug("Successfully decrypted data with key $decryptionKeyId") val integrityProtected = IntegrityProtectedInputStream(decrypted, pkesk, options) - nestedInputStream = OpenPgpMessageInputStream(integrityProtected, options, encryptedData, policy) + nestedInputStream = + OpenPgpMessageInputStream(integrityProtected, options, encryptedData, policy) return true - } catch (e : UnacceptableAlgorithmException) { + } catch (e: UnacceptableAlgorithmException) { throw e - } catch (e : PGPException) { + } catch (e: PGPException) { LOGGER.debug("Decryption of encrypted data packet using secret key failed.", e) } return false @@ -441,11 +491,12 @@ class OpenPgpMessageInputStream( return -1 } - val r: Int = try { - nestedInputStream!!.read() - } catch (e: IOException) { - -1 - } + val r: Int = + try { + nestedInputStream!!.read() + } catch (e: IOException) { + -1 + } if (r != -1) { signatures.updateLiteral(r.toByte()) } else { @@ -532,34 +583,37 @@ class OpenPgpMessageInputStream( return MessageMetadata((layerMetadata as Message)) } - private fun getDecryptionKey(keyId: Long): PGPSecretKeyRing? = options.getDecryptionKeys().firstOrNull { - it.any { - k -> k.keyID == keyId - }.and(PGPainless.inspectKeyRing(it).decryptionSubkeys.any { - k -> k.keyID == keyId - }) - } - - private fun getDecryptionKey(pkesk: PGPPublicKeyEncryptedData): PGPSecretKeyRing? = options.getDecryptionKeys().firstOrNull { - it.getSecretKeyFor(pkesk) != null && PGPainless.inspectKeyRing(it).decryptionSubkeys.any { subkey -> - when (pkesk.version) { - 3 -> pkesk.keyID == subkey.keyID - else -> throw NotImplementedError("Version 6 PKESK not yet supported.") - } + private fun getDecryptionKey(keyId: Long): PGPSecretKeyRing? = + options.getDecryptionKeys().firstOrNull { + it.any { k -> k.keyID == keyId } + .and(PGPainless.inspectKeyRing(it).decryptionSubkeys.any { k -> k.keyID == keyId }) } - } - private fun getDecryptionKeys(pkesk: PGPPublicKeyEncryptedData): List = - options.getDecryptionKeys().filter { - it.getSecretKeyFor(pkesk) != null && PGPainless.inspectKeyRing(it).decryptionSubkeys.any { subkey -> + private fun getDecryptionKey(pkesk: PGPPublicKeyEncryptedData): PGPSecretKeyRing? = + options.getDecryptionKeys().firstOrNull { + it.getSecretKeyFor(pkesk) != null && + PGPainless.inspectKeyRing(it).decryptionSubkeys.any { subkey -> when (pkesk.version) { 3 -> pkesk.keyID == subkey.keyID else -> throw NotImplementedError("Version 6 PKESK not yet supported.") } } - } + } - private fun findPotentialDecryptionKeys(pkesk: PGPPublicKeyEncryptedData): List> { + private fun getDecryptionKeys(pkesk: PGPPublicKeyEncryptedData): List = + options.getDecryptionKeys().filter { + it.getSecretKeyFor(pkesk) != null && + PGPainless.inspectKeyRing(it).decryptionSubkeys.any { subkey -> + when (pkesk.version) { + 3 -> pkesk.keyID == subkey.keyID + else -> throw NotImplementedError("Version 6 PKESK not yet supported.") + } + } + } + + private fun findPotentialDecryptionKeys( + pkesk: PGPPublicKeyEncryptedData + ): List> { val algorithm = pkesk.algorithm val candidates = mutableListOf>() options.getDecryptionKeys().forEach { @@ -574,11 +628,12 @@ class OpenPgpMessageInputStream( } private fun isAcceptable(algorithm: SymmetricKeyAlgorithm): Boolean = - policy.symmetricKeyDecryptionAlgorithmPolicy.isAcceptable(algorithm) + policy.symmetricKeyDecryptionAlgorithmPolicy.isAcceptable(algorithm) private fun throwIfUnacceptable(algorithm: SymmetricKeyAlgorithm) { if (!isAcceptable(algorithm)) { - throw UnacceptableAlgorithmException("Symmetric-Key algorithm $algorithm is not acceptable for message decryption.") + throw UnacceptableAlgorithmException( + "Symmetric-Key algorithm $algorithm is not acceptable for message decryption.") } } @@ -610,9 +665,7 @@ class OpenPgpMessageInputStream( get() = skesks.plus(pkesks).plus(anonPkesks) } - private class Signatures( - val options: ConsumerOptions - ) : OutputStream() { + private class Signatures(val options: ConsumerOptions) : OutputStream() { val detachedSignatures = mutableListOf() val prependedSignatures = mutableListOf() val onePassSignatures = mutableListOf() @@ -636,8 +689,10 @@ class OpenPgpMessageInputStream( if (check != null) { detachedSignatures.add(check) } else { - LOGGER.debug("No suitable certificate for verification of signature by key ${keyId.openPgpKeyId()} found.") - detachedSignaturesWithMissingCert.add(SignatureVerification.Failure( + LOGGER.debug( + "No suitable certificate for verification of signature by key ${keyId.openPgpKeyId()} found.") + detachedSignaturesWithMissingCert.add( + SignatureVerification.Failure( signature, null, SignatureValidationException("Missing verification key."))) } } @@ -648,10 +703,11 @@ class OpenPgpMessageInputStream( if (check != null) { prependedSignatures.add(check) } else { - LOGGER.debug("No suitable certificate for verification of signature by key ${keyId.openPgpKeyId()} found.") - prependedSignaturesWithMissingCert.add(SignatureVerification.Failure( - signature, null, SignatureValidationException("Missing verification key") - )) + LOGGER.debug( + "No suitable certificate for verification of signature by key ${keyId.openPgpKeyId()} found.") + prependedSignaturesWithMissingCert.add( + SignatureVerification.Failure( + signature, null, SignatureValidationException("Missing verification key"))) } } @@ -680,7 +736,11 @@ class OpenPgpMessageInputStream( } } - fun addCorrespondingOnePassSignature(signature: PGPSignature, layer: Layer, policy: Policy) { + fun addCorrespondingOnePassSignature( + signature: PGPSignature, + layer: Layer, + policy: Policy + ) { var found = false val keyId = signature.issuerKeyId for ((i, check) in onePassSignatures.withIndex().reversed()) { @@ -694,27 +754,32 @@ class OpenPgpMessageInputStream( } check.signature = signature - val verification = SignatureVerification(signature, + val verification = + SignatureVerification( + signature, SubkeyIdentifier(check.verificationKeys, check.onePassSignature.keyID)) try { - SignatureValidator.signatureWasCreatedInBounds(options.getVerifyNotBefore(), options.getVerifyNotAfter()) - .verify(signature) + SignatureValidator.signatureWasCreatedInBounds( + options.getVerifyNotBefore(), options.getVerifyNotAfter()) + .verify(signature) CertificateValidator.validateCertificateAndVerifyOnePassSignature(check, policy) LOGGER.debug("Acceptable signature by key ${verification.signingKey}") layer.addVerifiedOnePassSignature(verification) } catch (e: SignatureValidationException) { LOGGER.debug("Rejected signature by key ${verification.signingKey}", e) - layer.addRejectedOnePassSignature(SignatureVerification.Failure(verification, e)) + layer.addRejectedOnePassSignature( + SignatureVerification.Failure(verification, e)) } break } if (!found) { - LOGGER.debug("No suitable certificate for verification of signature by key ${keyId.openPgpKeyId()} found.") - inbandSignaturesWithMissingCert.add(SignatureVerification.Failure( - signature, null, SignatureValidationException("Missing verification key.") - )) + LOGGER.debug( + "No suitable certificate for verification of signature by key ${keyId.openPgpKeyId()} found.") + inbandSignaturesWithMissingCert.add( + SignatureVerification.Failure( + signature, null, SignatureValidationException("Missing verification key."))) } } @@ -737,7 +802,9 @@ class OpenPgpMessageInputStream( } if (options.getMissingCertificateCallback() != null) { - return options.getMissingCertificateCallback()!!.onMissingPublicKeyEncountered(signature.keyID) + return options + .getMissingCertificateCallback()!! + .onMissingPublicKeyEncountered(signature.keyID) } return null // TODO: Missing cert for sig } @@ -749,7 +816,9 @@ class OpenPgpMessageInputStream( } if (options.getMissingCertificateCallback() != null) { - return options.getMissingCertificateCallback()!!.onMissingPublicKeyEncountered(signature.keyID) + return options + .getMissingCertificateCallback()!! + .onMissingPublicKeyEncountered(signature.keyID) } return null // TODO: Missing cert for sig } @@ -800,32 +869,42 @@ class OpenPgpMessageInputStream( fun finish(layer: Layer, policy: Policy) { for (detached in detachedSignatures) { - val verification = SignatureVerification(detached.signature, detached.signingKeyIdentifier) + val verification = + SignatureVerification(detached.signature, detached.signingKeyIdentifier) try { - SignatureValidator.signatureWasCreatedInBounds(options.getVerifyNotBefore(), options.getVerifyNotAfter()) - .verify(detached.signature) + SignatureValidator.signatureWasCreatedInBounds( + options.getVerifyNotBefore(), options.getVerifyNotAfter()) + .verify(detached.signature) CertificateValidator.validateCertificateAndVerifyInitializedSignature( - detached.signature, KeyRingUtils.publicKeys(detached.signingKeyRing), policy) + detached.signature, + KeyRingUtils.publicKeys(detached.signingKeyRing), + policy) LOGGER.debug("Acceptable signature by key ${verification.signingKey}") layer.addVerifiedDetachedSignature(verification) - } catch (e : SignatureValidationException) { + } catch (e: SignatureValidationException) { LOGGER.debug("Rejected signature by key ${verification.signingKey}", e) - layer.addRejectedDetachedSignature(SignatureVerification.Failure(verification, e)) + layer.addRejectedDetachedSignature( + SignatureVerification.Failure(verification, e)) } } for (prepended in prependedSignatures) { - val verification = SignatureVerification(prepended.signature, prepended.signingKeyIdentifier) + val verification = + SignatureVerification(prepended.signature, prepended.signingKeyIdentifier) try { - SignatureValidator.signatureWasCreatedInBounds(options.getVerifyNotBefore(), options.getVerifyNotAfter()) - .verify(prepended.signature) + SignatureValidator.signatureWasCreatedInBounds( + options.getVerifyNotBefore(), options.getVerifyNotAfter()) + .verify(prepended.signature) CertificateValidator.validateCertificateAndVerifyInitializedSignature( - prepended.signature, KeyRingUtils.publicKeys(prepended.signingKeyRing), policy) + prepended.signature, + KeyRingUtils.publicKeys(prepended.signingKeyRing), + policy) LOGGER.debug("Acceptable signature by key ${verification.signingKey}") layer.addVerifiedPrependedSignature(verification) - } catch (e : SignatureValidationException) { + } catch (e: SignatureValidationException) { LOGGER.debug("Rejected signature by key ${verification.signingKey}", e) - layer.addRejectedPrependedSignature(SignatureVerification.Failure(verification, e)) + layer.addRejectedPrependedSignature( + SignatureVerification.Failure(verification, e)) } } @@ -864,22 +943,22 @@ class OpenPgpMessageInputStream( companion object { @JvmStatic private fun initialize(signature: PGPSignature, publicKey: PGPPublicKey) { - val verifierProvider = ImplementationFactory.getInstance() - .pgpContentVerifierBuilderProvider + val verifierProvider = + ImplementationFactory.getInstance().pgpContentVerifierBuilderProvider try { signature.init(verifierProvider, publicKey) - } catch (e : PGPException) { + } catch (e: PGPException) { throw RuntimeException(e) } } @JvmStatic private fun initialize(ops: PGPOnePassSignature, publicKey: PGPPublicKey) { - val verifierProvider = ImplementationFactory.getInstance() - .pgpContentVerifierBuilderProvider + val verifierProvider = + ImplementationFactory.getInstance().pgpContentVerifierBuilderProvider try { ops.init(verifierProvider, publicKey) - } catch (e : PGPException) { + } catch (e: PGPException) { throw RuntimeException(e) } } @@ -891,36 +970,40 @@ class OpenPgpMessageInputStream( private val LOGGER = LoggerFactory.getLogger(OpenPgpMessageInputStream::class.java) @JvmStatic - fun create(inputStream: InputStream, - options: ConsumerOptions) = create(inputStream, options, PGPainless.getPolicy()) + fun create(inputStream: InputStream, options: ConsumerOptions) = + create(inputStream, options, PGPainless.getPolicy()) @JvmStatic - fun create(inputStream: InputStream, - options: ConsumerOptions, - policy: Policy) = create(inputStream, options, Message(), policy) + fun create(inputStream: InputStream, options: ConsumerOptions, policy: Policy) = + create(inputStream, options, Message(), policy) @JvmStatic - internal fun create(inputStream: InputStream, - options: ConsumerOptions, - metadata: Layer, - policy: Policy): OpenPgpMessageInputStream { + internal fun create( + inputStream: InputStream, + options: ConsumerOptions, + metadata: Layer, + policy: Policy + ): OpenPgpMessageInputStream { val openPgpIn = OpenPgpInputStream(inputStream) openPgpIn.reset() if (openPgpIn.isNonOpenPgp || options.isForceNonOpenPgpData()) { - return OpenPgpMessageInputStream(Type.non_openpgp, openPgpIn, options, metadata, policy) + return OpenPgpMessageInputStream( + Type.non_openpgp, openPgpIn, options, metadata, policy) } if (openPgpIn.isBinaryOpenPgp) { // Simply consume OpenPGP message - return OpenPgpMessageInputStream(Type.standard, openPgpIn, options, metadata, policy) + return OpenPgpMessageInputStream( + Type.standard, openPgpIn, options, metadata, policy) } return if (openPgpIn.isAsciiArmored) { val armorIn = ArmoredInputStreamFactory.get(openPgpIn) if (armorIn.isClearText) { (metadata as Message).setCleartextSigned() - OpenPgpMessageInputStream(Type.cleartext_signed, armorIn, options, metadata, policy) + OpenPgpMessageInputStream( + Type.cleartext_signed, armorIn, options, metadata, policy) } else { // Simply consume dearmored OpenPGP message OpenPgpMessageInputStream(Type.standard, armorIn, options, metadata, policy) @@ -930,4 +1013,4 @@ class OpenPgpMessageInputStream( } } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/SignatureVerification.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/SignatureVerification.kt index 8d229fb2..3e00fbb2 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/SignatureVerification.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/SignatureVerification.kt @@ -11,43 +11,43 @@ import org.pgpainless.key.SubkeyIdentifier import org.pgpainless.signature.SignatureUtils /** - * Tuple of a signature and an identifier of its corresponding verification key. - * Semantic meaning of the signature verification (success, failure) is merely given by context. - * E.g. [MessageMetadata.getVerifiedInlineSignatures] contains verified verifications, - * while the class [Failure] contains failed verifications. + * Tuple of a signature and an identifier of its corresponding verification key. Semantic meaning of + * the signature verification (success, failure) is merely given by context. E.g. + * [MessageMetadata.getVerifiedInlineSignatures] contains verified verifications, while the class + * [Failure] contains failed verifications. * * @param signature PGPSignature object * @param signingKey [SubkeyIdentifier] of the (sub-) key that is used for signature verification. - * Note, that this might be null, e.g. in case of a [Failure] due to missing verification key. + * Note, that this might be null, e.g. in case of a [Failure] due to missing verification key. */ -data class SignatureVerification( - val signature: PGPSignature, - val signingKey: SubkeyIdentifier -) { +data class SignatureVerification(val signature: PGPSignature, val signingKey: SubkeyIdentifier) { override fun toString(): String { return "Signature: ${SignatureUtils.getSignatureDigestPrefix(signature)};" + - " Key: $signingKey;" + " Key: $signingKey;" } /** - * Tuple object of a [SignatureVerification] and the corresponding [SignatureValidationException] - * that caused the verification to fail. + * Tuple object of a [SignatureVerification] and the corresponding + * [SignatureValidationException] that caused the verification to fail. * - * @param signatureVerification verification (tuple of [PGPSignature] and corresponding [SubkeyIdentifier]) + * @param signatureVerification verification (tuple of [PGPSignature] and corresponding + * [SubkeyIdentifier]) * @param validationException exception that caused the verification to fail */ data class Failure( - val signature: PGPSignature, - val signingKey: SubkeyIdentifier?, - val validationException: SignatureValidationException + val signature: PGPSignature, + val signingKey: SubkeyIdentifier?, + val validationException: SignatureValidationException ) { - constructor(verification: SignatureVerification, validationException: SignatureValidationException): - this(verification.signature, verification.signingKey, validationException) + constructor( + verification: SignatureVerification, + validationException: SignatureValidationException + ) : this(verification.signature, verification.signingKey, validationException) override fun toString(): String { return "Signature: ${SignatureUtils.getSignatureDigestPrefix(signature)}; Key: ${signingKey?.toString() ?: "null"}; Failure: ${validationException.message}" } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/TeeBCPGInputStream.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/TeeBCPGInputStream.kt index f6c5a454..73c10e8a 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/TeeBCPGInputStream.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/TeeBCPGInputStream.kt @@ -4,6 +4,9 @@ package org.pgpainless.decryption_verification +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream import org.bouncycastle.bcpg.BCPGInputStream import org.bouncycastle.bcpg.MarkerPacket import org.bouncycastle.bcpg.Packet @@ -13,25 +16,21 @@ import org.bouncycastle.openpgp.PGPLiteralData import org.bouncycastle.openpgp.PGPOnePassSignature import org.bouncycastle.openpgp.PGPSignature import org.pgpainless.algorithm.OpenPgpPacket -import java.io.IOException -import java.io.InputStream -import java.io.OutputStream /** - * Since we need to update signatures with data from the underlying stream, this class is used to tee out the data. - * Unfortunately we cannot simply override [BCPGInputStream.read] to tee the data out though, since - * [BCPGInputStream.readPacket] inconsistently calls a mix of [BCPGInputStream.read] and - * [InputStream.read] of the underlying stream. This would cause the second length byte to get swallowed up. + * Since we need to update signatures with data from the underlying stream, this class is used to + * tee out the data. Unfortunately we cannot simply override [BCPGInputStream.read] to tee the data + * out though, since [BCPGInputStream.readPacket] inconsistently calls a mix of + * [BCPGInputStream.read] and [InputStream.read] of the underlying stream. This would cause the + * second length byte to get swallowed up. * - * Therefore, this class delegates the teeing to an [DelayedTeeInputStream] which wraps the underlying - * stream. Since calling [BCPGInputStream.nextPacketTag] reads up to and including the next packets tag, - * we need to delay teeing out that byte to signature verifiers. - * Hence, the reading methods of the [TeeBCPGInputStream] handle pushing this byte to the output stream using + * Therefore, this class delegates the teeing to an [DelayedTeeInputStream] which wraps the + * underlying stream. Since calling [BCPGInputStream.nextPacketTag] reads up to and including the + * next packets tag, we need to delay teeing out that byte to signature verifiers. Hence, the + * reading methods of the [TeeBCPGInputStream] handle pushing this byte to the output stream using * [DelayedTeeInputStream.squeeze]. */ -class TeeBCPGInputStream( - inputStream: BCPGInputStream, - outputStream: OutputStream) { +class TeeBCPGInputStream(inputStream: BCPGInputStream, outputStream: OutputStream) { private val delayedTee: DelayedTeeInputStream private val packetInputStream: BCPGInputStream @@ -43,8 +42,7 @@ class TeeBCPGInputStream( fun nextPacketTag(): OpenPgpPacket? { return packetInputStream.nextPacketTag().let { - if (it == -1) null - else OpenPgpPacket.requireFromTag(it) + if (it == -1) null else OpenPgpPacket.requireFromTag(it) } } @@ -82,8 +80,8 @@ class TeeBCPGInputStream( } class DelayedTeeInputStream( - private val inputStream: InputStream, - private val outputStream: OutputStream + private val inputStream: InputStream, + private val outputStream: OutputStream ) : InputStream() { private var last: Int = -1 @@ -94,7 +92,7 @@ class TeeBCPGInputStream( return try { last = inputStream.read() last - } catch (e : IOException) { + } catch (e: IOException) { if (e.message?.contains("crc check failed in armored message") == true) { throw e } @@ -108,19 +106,18 @@ class TeeBCPGInputStream( } inputStream.read(b, off, len).let { r -> - last = if (r > 0) { - outputStream.write(b, off, r - 1) - b[off + r - 1].toInt() - } else { - -1 - } + last = + if (r > 0) { + outputStream.write(b, off, r - 1) + b[off + r - 1].toInt() + } else { + -1 + } return r } } - /** - * Squeeze the last byte out and update the output stream. - */ + /** Squeeze the last byte out and update the output stream. */ fun squeeze() { if (last != -1) { outputStream.write(last) @@ -133,4 +130,4 @@ class TeeBCPGInputStream( outputStream.close() } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/cleartext_signatures/ClearsignedMessageUtil.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/cleartext_signatures/ClearsignedMessageUtil.kt index 7a3b93ee..78614a96 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/cleartext_signatures/ClearsignedMessageUtil.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/cleartext_signatures/ClearsignedMessageUtil.kt @@ -4,47 +4,49 @@ package org.pgpainless.decryption_verification.cleartext_signatures +import java.io.* +import kotlin.jvm.Throws import org.bouncycastle.bcpg.ArmoredInputStream import org.bouncycastle.openpgp.PGPSignatureList import org.bouncycastle.util.Strings import org.pgpainless.exception.WrongConsumingMethodException import org.pgpainless.implementation.ImplementationFactory import org.pgpainless.util.ArmoredInputStreamFactory -import java.io.* -import kotlin.jvm.Throws /** - * Utility class to deal with cleartext-signed messages. - * Based on Bouncycastle's [org.bouncycastle.openpgp.examples.ClearSignedFileProcessor]. + * Utility class to deal with cleartext-signed messages. Based on Bouncycastle's + * [org.bouncycastle.openpgp.examples.ClearSignedFileProcessor]. */ class ClearsignedMessageUtil { companion object { /** - * Dearmor a clearsigned message, detach the inband signatures and write the plaintext message to the provided - * messageOutputStream. + * Dearmor a clearsigned message, detach the inband signatures and write the plaintext + * message to the provided messageOutputStream. * * @param clearsignedInputStream input stream containing a clearsigned message * @param messageOutputStream output stream to which the dearmored message shall be written * @return signatures - * * @throws IOException if the message is not clearsigned or some other IO error happens * @throws WrongConsumingMethodException in case the armored message is not cleartext signed */ @JvmStatic @Throws(WrongConsumingMethodException::class, IOException::class) fun detachSignaturesFromInbandClearsignedMessage( - clearsignedInputStream: InputStream, - messageOutputStream: OutputStream): PGPSignatureList { - val input: ArmoredInputStream = if (clearsignedInputStream is ArmoredInputStream) { - clearsignedInputStream - } else { - ArmoredInputStreamFactory.get(clearsignedInputStream) - } + clearsignedInputStream: InputStream, + messageOutputStream: OutputStream + ): PGPSignatureList { + val input: ArmoredInputStream = + if (clearsignedInputStream is ArmoredInputStream) { + clearsignedInputStream + } else { + ArmoredInputStreamFactory.get(clearsignedInputStream) + } if (!input.isClearText) { - throw WrongConsumingMethodException("Message isn't using the Cleartext Signature Framework.") + throw WrongConsumingMethodException( + "Message isn't using the Cleartext Signature Framework.") } BufferedOutputStream(messageOutputStream).use { output -> @@ -94,7 +96,11 @@ class ClearsignedMessageUtil { } @JvmStatic - private fun readInputLine(bOut: ByteArrayOutputStream, lookAhead: Int, fIn: InputStream): Int { + private fun readInputLine( + bOut: ByteArrayOutputStream, + lookAhead: Int, + fIn: InputStream + ): Int { var mLookAhead = lookAhead bOut.reset() var ch = mLookAhead @@ -150,4 +156,4 @@ class ClearsignedMessageUtil { return isLineEnding(b) || b == '\t'.code.toByte() || b == ' '.code.toByte() } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/cleartext_signatures/InMemoryMultiPassStrategy.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/cleartext_signatures/InMemoryMultiPassStrategy.kt index eed6438f..da7c7cec 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/cleartext_signatures/InMemoryMultiPassStrategy.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/cleartext_signatures/InMemoryMultiPassStrategy.kt @@ -8,12 +8,12 @@ import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream /** - * Implementation of the [MultiPassStrategy]. - * This class keeps the read data in memory by caching the data inside a [ByteArrayOutputStream]. + * Implementation of the [MultiPassStrategy]. This class keeps the read data in memory by caching + * the data inside a [ByteArrayOutputStream]. * - * Note, that this class is suitable and efficient for processing small amounts of data. - * For larger data like encrypted files, use of the [WriteToFileMultiPassStrategy] is recommended to - * prevent [OutOfMemoryError] and other issues. + * Note, that this class is suitable and efficient for processing small amounts of data. For larger + * data like encrypted files, use of the [WriteToFileMultiPassStrategy] is recommended to prevent + * [OutOfMemoryError] and other issues. */ class InMemoryMultiPassStrategy : MultiPassStrategy { @@ -26,4 +26,4 @@ class InMemoryMultiPassStrategy : MultiPassStrategy { get() = ByteArrayInputStream(getBytes()) fun getBytes(): ByteArray = messageOutputStream.toByteArray() -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/cleartext_signatures/MultiPassStrategy.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/cleartext_signatures/MultiPassStrategy.kt index ae69b9c5..4ef7ef3a 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/cleartext_signatures/MultiPassStrategy.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/cleartext_signatures/MultiPassStrategy.kt @@ -7,16 +7,16 @@ package org.pgpainless.decryption_verification.cleartext_signatures import java.io.* /** - * Since for verification of cleartext signed messages, we need to read the whole data twice in order to verify signatures, - * a strategy for how to cache the read data is required. - * Otherwise, large data kept in memory could cause an [OutOfMemoryError] or other issues. + * Since for verification of cleartext signed messages, we need to read the whole data twice in + * order to verify signatures, a strategy for how to cache the read data is required. Otherwise, + * large data kept in memory could cause an [OutOfMemoryError] or other issues. * - * This is an Interface that describes a strategy to deal with the fact that detached signatures require multiple passes - * to do verification. + * This is an Interface that describes a strategy to deal with the fact that detached signatures + * require multiple passes to do verification. * - * This interface can be used to write the signed data stream out via [messageOutputStream] and later - * get access to the data again via [messageInputStream]. - * Thereby the detail where the data is being stored (memory, file, etc.) can be abstracted away. + * This interface can be used to write the signed data stream out via [messageOutputStream] and + * later get access to the data again via [messageInputStream]. Thereby the detail where the data is + * being stored (memory, file, etc.) can be abstracted away. */ interface MultiPassStrategy { @@ -32,8 +32,8 @@ interface MultiPassStrategy { * Provide an [InputStream] which contains the data that was previously written away in * [messageOutputStream]. * - * As there may be multiple signatures that need to be processed, each call of this method MUST return - * a new [InputStream]. + * As there may be multiple signatures that need to be processed, each call of this method MUST + * return a new [InputStream]. * * @return input stream * @throws IOException io error @@ -43,9 +43,10 @@ interface MultiPassStrategy { companion object { /** - * Write the message content out to a file and re-read it to verify signatures. - * This strategy is best suited for larger messages (e.g. plaintext signed files) which might not fit into memory. - * After the message has been processed completely, the messages content are available at the provided file. + * Write the message content out to a file and re-read it to verify signatures. This + * strategy is best suited for larger messages (e.g. plaintext signed files) which might not + * fit into memory. After the message has been processed completely, the messages content + * are available at the provided file. * * @param file target file * @return strategy @@ -56,10 +57,10 @@ interface MultiPassStrategy { } /** - * Read the message content into memory. - * This strategy is best suited for small messages which fit into memory. - * After the message has been processed completely, the message content can be accessed by calling - * [ByteArrayOutputStream.toByteArray] on [messageOutputStream]. + * Read the message content into memory. This strategy is best suited for small messages + * which fit into memory. After the message has been processed completely, the message + * content can be accessed by calling [ByteArrayOutputStream.toByteArray] on + * [messageOutputStream]. * * @return strategy */ @@ -68,4 +69,4 @@ interface MultiPassStrategy { return InMemoryMultiPassStrategy() } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/cleartext_signatures/WriteToFileMultiPassStrategy.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/cleartext_signatures/WriteToFileMultiPassStrategy.kt index 5d6567a4..88fcf6f1 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/cleartext_signatures/WriteToFileMultiPassStrategy.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/cleartext_signatures/WriteToFileMultiPassStrategy.kt @@ -7,18 +7,15 @@ package org.pgpainless.decryption_verification.cleartext_signatures import java.io.* /** - * Implementation of the [MultiPassStrategy]. - * When processing signed data the first time, the data is being written out into a file. - * For the second pass, that file is being read again. + * Implementation of the [MultiPassStrategy]. When processing signed data the first time, the data + * is being written out into a file. For the second pass, that file is being read again. * - * This strategy is recommended when larger amounts of data need to be processed. - * For smaller files, [InMemoryMultiPassStrategy] yields higher efficiency. + * This strategy is recommended when larger amounts of data need to be processed. For smaller files, + * [InMemoryMultiPassStrategy] yields higher efficiency. * * @param file file to write the data to and read from */ -class WriteToFileMultiPassStrategy( - private val file: File -) : MultiPassStrategy { +class WriteToFileMultiPassStrategy(private val file: File) : MultiPassStrategy { override val messageOutputStream: OutputStream @Throws(IOException::class) @@ -39,5 +36,4 @@ class WriteToFileMultiPassStrategy( } return FileInputStream(file) } - -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/syntax_check/InputSymbol.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/syntax_check/InputSymbol.kt index 133bfcb3..2bea3356 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/syntax_check/InputSymbol.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/syntax_check/InputSymbol.kt @@ -5,35 +5,26 @@ package org.pgpainless.decryption_verification.syntax_check enum class InputSymbol { - /** - * A [PGPLiteralData] packet. - */ + /** A [PGPLiteralData] packet. */ LITERAL_DATA, - /** - * A [PGPSignatureList] object. - */ + /** A [PGPSignatureList] object. */ SIGNATURE, - /** - * A [PGPOnePassSignatureList] object. - */ + /** A [PGPOnePassSignatureList] object. */ ONE_PASS_SIGNATURE, /** - * A [PGPCompressedData] packet. - * The contents of this packet MUST form a valid OpenPGP message, so a nested PDA is opened to verify - * its nested packet sequence. + * A [PGPCompressedData] packet. The contents of this packet MUST form a valid OpenPGP message, + * so a nested PDA is opened to verify its nested packet sequence. */ COMPRESSED_DATA, /** - * A [PGPEncryptedDataList] object. - * This object combines multiple ESKs and the corresponding Symmetrically Encrypted - * (possibly Integrity Protected) Data packet. + * A [PGPEncryptedDataList] object. This object combines multiple ESKs and the corresponding + * Symmetrically Encrypted (possibly Integrity Protected) Data packet. */ ENCRYPTED_DATA, /** - * Marks the end of a (sub-) sequence. - * This input is given if the end of an OpenPGP message is reached. - * This might be the case for the end of the whole ciphertext, or the end of a packet with nested contents - * (e.g. the end of a Compressed Data packet). + * Marks the end of a (sub-) sequence. This input is given if the end of an OpenPGP message is + * reached. This might be the case for the end of the whole ciphertext, or the end of a packet + * with nested contents (e.g. the end of a Compressed Data packet). */ END_OF_SEQUENCE -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/syntax_check/OpenPgpMessageSyntax.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/syntax_check/OpenPgpMessageSyntax.kt index 56bd9a77..127d10f8 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/syntax_check/OpenPgpMessageSyntax.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/syntax_check/OpenPgpMessageSyntax.kt @@ -9,9 +9,10 @@ import org.pgpainless.exception.MalformedOpenPgpMessageException /** * This class describes the syntax for OpenPGP messages as specified by rfc4880. * - * See [rfc4880 - §11.3. OpenPGP Messages](https://www.rfc-editor.org/rfc/rfc4880#section-11.3) - * See [Blog post about theoretic background and translation of grammar to PDA syntax](https://blog.jabberhead.tk/2022/09/14/using-pushdown-automata-to-verify-packet-sequences/) - * See [Blog post about practically implementing the PDA for packet syntax validation](https://blog.jabberhead.tk/2022/10/26/implementing-packet-sequence-validation-using-pushdown-automata/) + * See [rfc4880 - §11.3. OpenPGP Messages](https://www.rfc-editor.org/rfc/rfc4880#section-11.3) See + * [Blog post about theoretic background and translation of grammar to PDA syntax](https://blog.jabberhead.tk/2022/09/14/using-pushdown-automata-to-verify-packet-sequences/) + * See + * [Blog post about practically implementing the PDA for packet syntax validation](https://blog.jabberhead.tk/2022/10/26/implementing-packet-sequence-validation-using-pushdown-automata/) */ class OpenPgpMessageSyntax : Syntax { @@ -33,10 +34,12 @@ class OpenPgpMessageSyntax : Syntax { return when (input) { InputSymbol.LITERAL_DATA -> Transition(State.LITERAL_MESSAGE) InputSymbol.SIGNATURE -> Transition(State.OPENPGP_MESSAGE, StackSymbol.MSG) - InputSymbol.ONE_PASS_SIGNATURE -> Transition(State.OPENPGP_MESSAGE, StackSymbol.OPS, StackSymbol.MSG) + InputSymbol.ONE_PASS_SIGNATURE -> + Transition(State.OPENPGP_MESSAGE, StackSymbol.OPS, StackSymbol.MSG) InputSymbol.COMPRESSED_DATA -> Transition(State.COMPRESSED_MESSAGE) InputSymbol.ENCRYPTED_DATA -> Transition(State.ENCRYPTED_MESSAGE) - InputSymbol.END_OF_SEQUENCE -> throw MalformedOpenPgpMessageException(State.OPENPGP_MESSAGE, input, stackItem) + InputSymbol.END_OF_SEQUENCE -> + throw MalformedOpenPgpMessageException(State.OPENPGP_MESSAGE, input, stackItem) else -> throw MalformedOpenPgpMessageException(State.OPENPGP_MESSAGE, input, stackItem) } } @@ -85,4 +88,4 @@ class OpenPgpMessageSyntax : Syntax { } throw MalformedOpenPgpMessageException(State.VALID, input, stackItem) } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/syntax_check/PDA.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/syntax_check/PDA.kt index b1949917..7dff6ba2 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/syntax_check/PDA.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/syntax_check/PDA.kt @@ -8,37 +8,41 @@ import org.pgpainless.exception.MalformedOpenPgpMessageException import org.slf4j.LoggerFactory /** - * Pushdown Automaton for validating context-free languages. - * In PGPainless, this class is used to validate OpenPGP message packet sequences against the allowed syntax. + * Pushdown Automaton for validating context-free languages. In PGPainless, this class is used to + * validate OpenPGP message packet sequences against the allowed syntax. * * See [OpenPGP Message Syntax](https://www.rfc-editor.org/rfc/rfc4880#section-11.3) */ -class PDA constructor( - private val syntax: Syntax, - private val stack: ArrayDeque, - private val inputs: MutableList, - private var state: State +class PDA +constructor( + private val syntax: Syntax, + private val stack: ArrayDeque, + private val inputs: MutableList, + private var state: State ) { /** - * Construct a PDA with a custom [Syntax], initial [State] and initial [StackSymbols][StackSymbol]. + * Construct a PDA with a custom [Syntax], initial [State] and initial + * [StackSymbols][StackSymbol]. * * @param syntax syntax * @param initialState initial state - * @param initialStack zero or more initial stack items (get pushed onto the stack in order of appearance) + * @param initialStack zero or more initial stack items (get pushed onto the stack in order of + * appearance) */ - constructor(syntax: Syntax, initialState: State, vararg initialStack: StackSymbol): this ( - syntax, ArrayDeque(initialStack.toList().reversed()), mutableListOf(), initialState) + constructor( + syntax: Syntax, + initialState: State, + vararg initialStack: StackSymbol + ) : this(syntax, ArrayDeque(initialStack.toList().reversed()), mutableListOf(), initialState) + + /** Default constructor which initializes the PDA to work with the [OpenPgpMessageSyntax]. */ + constructor() : + this(OpenPgpMessageSyntax(), State.OPENPGP_MESSAGE, StackSymbol.TERMINUS, StackSymbol.MSG) /** - * Default constructor which initializes the PDA to work with the [OpenPgpMessageSyntax]. - */ - constructor(): this(OpenPgpMessageSyntax(), State.OPENPGP_MESSAGE, StackSymbol.TERMINUS, StackSymbol.MSG) - - /** - * Process the next [InputSymbol]. - * This will either leave the PDA in the next state, or throw a [MalformedOpenPgpMessageException] if the - * input symbol is rejected. + * Process the next [InputSymbol]. This will either leave the PDA in the next state, or throw a + * [MalformedOpenPgpMessageException] if the input symbol is rejected. * * @param input input symbol * @throws MalformedOpenPgpMessageException if the input symbol is rejected @@ -53,14 +57,17 @@ class PDA constructor( } inputs.add(input) } catch (e: MalformedOpenPgpMessageException) { - val stackFormat = if (stackSymbol != null) { - "${stack.joinToString()}||$stackSymbol" - } else { - stack.joinToString() - } - val wrapped = MalformedOpenPgpMessageException( + val stackFormat = + if (stackSymbol != null) { + "${stack.joinToString()}||$stackSymbol" + } else { + stack.joinToString() + } + val wrapped = + MalformedOpenPgpMessageException( "Malformed message: After reading packet sequence ${inputs.joinToString()}, token '$input' is not allowed.\n" + - "No transition from state '$state' with stack $stackFormat", e) + "No transition from state '$state' with stack $stackFormat", + e) LOGGER.debug("Invalid input '$input'", wrapped) throw wrapped } @@ -87,7 +94,8 @@ class PDA constructor( */ fun assertValid() { if (!isValid()) { - throw MalformedOpenPgpMessageException("Pushdown Automaton is not in an acceptable state: ${toString()}") + throw MalformedOpenPgpMessageException( + "Pushdown Automaton is not in an acceptable state: ${toString()}") } } @@ -114,7 +122,6 @@ class PDA constructor( } companion object { - @JvmStatic - private val LOGGER = LoggerFactory.getLogger(PDA::class.java) + @JvmStatic private val LOGGER = LoggerFactory.getLogger(PDA::class.java) } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/syntax_check/StackSymbol.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/syntax_check/StackSymbol.kt index 960e5eba..8f927864 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/syntax_check/StackSymbol.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/syntax_check/StackSymbol.kt @@ -5,16 +5,10 @@ package org.pgpainless.decryption_verification.syntax_check enum class StackSymbol { - /** - * OpenPGP Message. - */ + /** OpenPGP Message. */ MSG, - /** - * OnePassSignature (in case of BC this represents a OnePassSignatureList). - */ + /** OnePassSignature (in case of BC this represents a OnePassSignatureList). */ OPS, - /** - * Special symbol representing the end of the message. - */ + /** Special symbol representing the end of the message. */ TERMINUS -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/syntax_check/State.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/syntax_check/State.kt index 8e1f682c..8fad2fb7 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/syntax_check/State.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/syntax_check/State.kt @@ -4,13 +4,11 @@ package org.pgpainless.decryption_verification.syntax_check -/** - * Set of states of the automaton. - */ +/** Set of states of the automaton. */ enum class State { OPENPGP_MESSAGE, LITERAL_MESSAGE, COMPRESSED_MESSAGE, ENCRYPTED_MESSAGE, VALID -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/syntax_check/Syntax.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/syntax_check/Syntax.kt index 16f0445b..a90e981e 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/syntax_check/Syntax.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/syntax_check/Syntax.kt @@ -6,25 +6,24 @@ package org.pgpainless.decryption_verification.syntax_check import org.pgpainless.exception.MalformedOpenPgpMessageException -/** - * This interface can be used to define a custom syntax for the [PDA]. - */ +/** This interface can be used to define a custom syntax for the [PDA]. */ interface Syntax { /** * Describe a transition rule from [State]
from
for [InputSymbol]
input
- * with [StackSymbol]
stackItem
from the top of the [PDAs][PDA] stack. - * The resulting [Transition] contains the new [State], as well as a list of - * [StackSymbols][StackSymbol] that get pushed onto the stack by the transition rule. - * If there is no applicable rule, a [MalformedOpenPgpMessageException] is thrown, since in this case - * the [InputSymbol] must be considered illegal. + * with [StackSymbol]
stackItem
from the top of the [PDAs][PDA] stack. The resulting + * [Transition] contains the new [State], as well as a list of [StackSymbols][StackSymbol] that + * get pushed onto the stack by the transition rule. If there is no applicable rule, a + * [MalformedOpenPgpMessageException] is thrown, since in this case the [InputSymbol] must be + * considered illegal. * * @param from current state of the PDA * @param input input symbol * @param stackItem item that got popped from the top of the stack * @return applicable transition rule containing the new state and pushed stack symbols - * @throws MalformedOpenPgpMessageException if there is no applicable transition rule (the input symbol is illegal) + * @throws MalformedOpenPgpMessageException if there is no applicable transition rule (the input + * symbol is illegal) */ @Throws(MalformedOpenPgpMessageException::class) fun transition(from: State, input: InputSymbol, stackItem: StackSymbol?): Transition -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/syntax_check/Transition.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/syntax_check/Transition.kt index 5bc6dfc6..d0a09992 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/syntax_check/Transition.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/syntax_check/Transition.kt @@ -5,18 +5,18 @@ package org.pgpainless.decryption_verification.syntax_check /** - * Result of applying a transition rule. - * Transition rules can be described by implementing the [Syntax] interface. + * Result of applying a transition rule. Transition rules can be described by implementing the + * [Syntax] interface. * * @param newState new [State] that is reached by applying the transition. - * @param pushedItems list of [StackSymbol] that are pushed onto the stack by applying the transition. - * The list contains items in the order in which they are pushed onto the stack. - * The list may be empty. + * @param pushedItems list of [StackSymbol] that are pushed onto the stack by applying the + * transition. The list contains items in the order in which they are pushed onto the stack. The + * list may be empty. */ -class Transition private constructor( - val pushedItems: List, - val newState: State -) { +class Transition private constructor(val pushedItems: List, val newState: State) { - constructor(newState: State, vararg pushedItems: StackSymbol): this(pushedItems.toList(), newState) -} \ No newline at end of file + constructor( + newState: State, + vararg pushedItems: StackSymbol + ) : this(pushedItems.toList(), newState) +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/BcHashContextSigner.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/BcHashContextSigner.kt index 90f275a4..68e41091 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/BcHashContextSigner.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/BcHashContextSigner.kt @@ -4,6 +4,7 @@ package org.pgpainless.encryption_signing +import java.security.MessageDigest import org.bouncycastle.extensions.unlock import org.bouncycastle.openpgp.PGPException import org.bouncycastle.openpgp.PGPPrivateKey @@ -13,20 +14,23 @@ import org.bouncycastle.openpgp.PGPSignatureGenerator import org.pgpainless.PGPainless import org.pgpainless.algorithm.SignatureType import org.pgpainless.key.protection.SecretKeyRingProtector -import java.security.MessageDigest class BcHashContextSigner { companion object { @JvmStatic - fun signHashContext(hashContext: MessageDigest, - signatureType: SignatureType, - secretKey: PGPSecretKeyRing, - protector: SecretKeyRingProtector): PGPSignature { + fun signHashContext( + hashContext: MessageDigest, + signatureType: SignatureType, + secretKey: PGPSecretKeyRing, + protector: SecretKeyRingProtector + ): PGPSignature { val info = PGPainless.inspectKeyRing(secretKey) - return info.signingSubkeys.mapNotNull { info.getSecretKey(it.keyID) }.firstOrNull() - ?.let { signHashContext(hashContext, signatureType, it.unlock(protector)) } - ?: throw PGPException("Key does not contain suitable signing subkey.") + return info.signingSubkeys + .mapNotNull { info.getSecretKey(it.keyID) } + .firstOrNull() + ?.let { signHashContext(hashContext, signatureType, it.unlock(protector)) } + ?: throw PGPException("Key does not contain suitable signing subkey.") } /** @@ -38,12 +42,14 @@ class BcHashContextSigner { * @throws PGPException in case of an OpenPGP error */ @JvmStatic - internal fun signHashContext(hashContext: MessageDigest, - signatureType: SignatureType, - privateKey: PGPPrivateKey): PGPSignature { + internal fun signHashContext( + hashContext: MessageDigest, + signatureType: SignatureType, + privateKey: PGPPrivateKey + ): PGPSignature { return PGPSignatureGenerator(BcPGPHashContextContentSignerBuilder(hashContext)) - .apply { init(signatureType.code, privateKey) } - .generate() + .apply { init(signatureType.code, privateKey) } + .generate() } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/BcPGPHashContextContentSignerBuilder.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/BcPGPHashContextContentSignerBuilder.kt index 381c4716..bf66b6dc 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/BcPGPHashContextContentSignerBuilder.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/BcPGPHashContextContentSignerBuilder.kt @@ -4,6 +4,8 @@ package org.pgpainless.encryption_signing +import java.io.OutputStream +import java.security.MessageDigest import org.bouncycastle.bcpg.PublicKeyAlgorithmTags import org.bouncycastle.crypto.CipherParameters import org.bouncycastle.crypto.CryptoException @@ -23,17 +25,14 @@ import org.bouncycastle.openpgp.operator.PGPContentSigner import org.bouncycastle.openpgp.operator.bc.BcPGPKeyConverter import org.pgpainless.algorithm.HashAlgorithm import org.pgpainless.algorithm.PublicKeyAlgorithm -import java.io.OutputStream -import java.security.MessageDigest /** - * Implementation of [PGPContentSignerBuilder] using the BC API, which can be used to sign hash contexts. - * This can come in handy to sign data, which was already processed to calculate the hash context, without the - * need to process it again to calculate the OpenPGP signature. + * Implementation of [PGPContentSignerBuilder] using the BC API, which can be used to sign hash + * contexts. This can come in handy to sign data, which was already processed to calculate the hash + * context, without the need to process it again to calculate the OpenPGP signature. */ -class BcPGPHashContextContentSignerBuilder( - private val messageDigest: MessageDigest -) : PGPHashContextContentSignerBuilder() { +class BcPGPHashContextContentSignerBuilder(private val messageDigest: MessageDigest) : + PGPHashContextContentSignerBuilder() { private val keyConverter = BcPGPKeyConverter() private val _hashAlgorithm: HashAlgorithm @@ -50,15 +49,22 @@ class BcPGPHashContextContentSignerBuilder( return object : PGPContentSigner { override fun getOutputStream(): OutputStream = SignerOutputStream(signer) - override fun getSignature(): ByteArray = try { - signer.generateSignature() - } catch (e : CryptoException) { - throw IllegalStateException("unable to create signature.", e) - } + + override fun getSignature(): ByteArray = + try { + signer.generateSignature() + } catch (e: CryptoException) { + throw IllegalStateException("unable to create signature.", e) + } + override fun getDigest(): ByteArray = messageDigest.digest() + override fun getType(): Int = signatureType + override fun getHashAlgorithm(): Int = _hashAlgorithm.algorithmId + override fun getKeyAlgorithm(): Int = keyAlgorithm.algorithmId + override fun getKeyID(): Long = privateKey.keyID } } @@ -67,21 +73,25 @@ class BcPGPHashContextContentSignerBuilder( @JvmStatic private fun requireFromName(digestName: String): HashAlgorithm { val algorithm = HashAlgorithm.fromName(digestName) - require(algorithm != null) { "Cannot recognize OpenPGP Hash Algorithm: $digestName"} + require(algorithm != null) { "Cannot recognize OpenPGP Hash Algorithm: $digestName" } return algorithm } @JvmStatic - private fun createSigner(keyAlgorithm: PublicKeyAlgorithm, - messageDigest: MessageDigest, - keyParam: CipherParameters): Signer { + private fun createSigner( + keyAlgorithm: PublicKeyAlgorithm, + messageDigest: MessageDigest, + keyParam: CipherParameters + ): Signer { val staticDigest = ExistingMessageDigest(messageDigest) return when (keyAlgorithm.algorithmId) { - PublicKeyAlgorithmTags.RSA_GENERAL, PublicKeyAlgorithmTags.RSA_SIGN -> RSADigestSigner(staticDigest) + PublicKeyAlgorithmTags.RSA_GENERAL, + PublicKeyAlgorithmTags.RSA_SIGN -> RSADigestSigner(staticDigest) PublicKeyAlgorithmTags.DSA -> DSADigestSigner(DSASigner(), staticDigest) PublicKeyAlgorithmTags.ECDSA -> DSADigestSigner(ECDSASigner(), staticDigest) PublicKeyAlgorithmTags.EDDSA_LEGACY -> { - if (keyParam is Ed25519PrivateKeyParameters || keyParam is Ed25519PublicKeyParameters) + if (keyParam is Ed25519PrivateKeyParameters || + keyParam is Ed25519PublicKeyParameters) EdDsaSigner(Ed25519Signer(), staticDigest) else EdDsaSigner(Ed448Signer(byteArrayOf()), staticDigest) } @@ -91,10 +101,7 @@ class BcPGPHashContextContentSignerBuilder( } // Copied from BCs BcImplProvider - required since BCs class is package visible only :/ - internal class EdDsaSigner( - private val signer: Signer, - private val digest: Digest - ) : Signer { + internal class EdDsaSigner(private val signer: Signer, private val digest: Digest) : Signer { private val digBuf: ByteArray = ByteArray(digest.digestSize) override fun init(forSigning: Boolean, param: CipherParameters) { @@ -128,4 +135,4 @@ class BcPGPHashContextContentSignerBuilder( digest.reset() } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/CRLFGeneratorStream.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/CRLFGeneratorStream.kt index 8735d7b1..badc8b48 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/CRLFGeneratorStream.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/CRLFGeneratorStream.kt @@ -5,17 +5,15 @@ package org.pgpainless.encryption_signing -import org.pgpainless.algorithm.StreamEncoding import java.io.OutputStream +import org.pgpainless.algorithm.StreamEncoding /** - * [OutputStream] which applies CR-LF encoding of its input data, based on the desired [StreamEncoding]. - * This implementation originates from the Bouncy Castle library. + * [OutputStream] which applies CR-LF encoding of its input data, based on the desired + * [StreamEncoding]. This implementation originates from the Bouncy Castle library. */ -class CRLFGeneratorStream( - private val crlfOut: OutputStream, - encoding: StreamEncoding -) : OutputStream() { +class CRLFGeneratorStream(private val crlfOut: OutputStream, encoding: StreamEncoding) : + OutputStream() { private val isBinary: Boolean private var lastB = 0 @@ -26,9 +24,9 @@ class CRLFGeneratorStream( override fun write(b: Int) { if (!isBinary) { - if (b == '\n'.code && lastB != '\r'.code) { // Unix + if (b == '\n'.code && lastB != '\r'.code) { // Unix crlfOut.write('\r'.code) - } else if (lastB == '\r'.code) { // MAC + } else if (lastB == '\r'.code) { // MAC if (b != '\n'.code) { crlfOut.write('\n'.code) } @@ -49,4 +47,4 @@ class CRLFGeneratorStream( super.flush() crlfOut.flush() } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/EncryptionBuilder.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/EncryptionBuilder.kt index 324fcf3b..13785df4 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/EncryptionBuilder.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/EncryptionBuilder.kt @@ -4,16 +4,18 @@ package org.pgpainless.encryption_signing +import java.io.OutputStream import org.pgpainless.PGPainless.Companion.getPolicy import org.pgpainless.algorithm.CompressionAlgorithm import org.pgpainless.algorithm.SymmetricKeyAlgorithm import org.pgpainless.algorithm.negotiation.SymmetricKeyAlgorithmNegotiator.Companion.byPopularity import org.slf4j.Logger import org.slf4j.LoggerFactory -import java.io.OutputStream class EncryptionBuilder : EncryptionBuilderInterface { - override fun onOutputStream(outputStream: OutputStream): EncryptionBuilderInterface.WithOptions { + override fun onOutputStream( + outputStream: OutputStream + ): EncryptionBuilderInterface.WithOptions { return WithOptionsImpl(outputStream) } @@ -26,8 +28,7 @@ class EncryptionBuilder : EncryptionBuilderInterface { companion object { - @JvmStatic - val LOGGER: Logger = LoggerFactory.getLogger(EncryptionBuilder::class.java) + @JvmStatic val LOGGER: Logger = LoggerFactory.getLogger(EncryptionBuilder::class.java) /** * Negotiate the [SymmetricKeyAlgorithm] used for message encryption. @@ -36,24 +37,32 @@ class EncryptionBuilder : EncryptionBuilderInterface { * @return negotiated symmetric key algorithm */ @JvmStatic - fun negotiateSymmetricEncryptionAlgorithm(encryptionOptions: EncryptionOptions): SymmetricKeyAlgorithm { - val preferences = encryptionOptions.keyViews.values + fun negotiateSymmetricEncryptionAlgorithm( + encryptionOptions: EncryptionOptions + ): SymmetricKeyAlgorithm { + val preferences = + encryptionOptions.keyViews.values .map { it.preferredSymmetricKeyAlgorithms } .toList() - val algorithm = byPopularity().negotiate( - getPolicy().symmetricKeyEncryptionAlgorithmPolicy, - encryptionOptions.encryptionAlgorithmOverride, - preferences) - LOGGER.debug("Negotiation resulted in {} being the symmetric encryption algorithm of choice.", algorithm) + val algorithm = + byPopularity() + .negotiate( + getPolicy().symmetricKeyEncryptionAlgorithmPolicy, + encryptionOptions.encryptionAlgorithmOverride, + preferences) + LOGGER.debug( + "Negotiation resulted in {} being the symmetric encryption algorithm of choice.", + algorithm) return algorithm } @JvmStatic fun negotiateCompressionAlgorithm(producerOptions: ProducerOptions): CompressionAlgorithm { val compressionAlgorithmOverride = producerOptions.compressionAlgorithmOverride - return compressionAlgorithmOverride ?: getPolicy().compressionAlgorithmPolicy.defaultCompressionAlgorithm() + return compressionAlgorithmOverride + ?: getPolicy().compressionAlgorithmPolicy.defaultCompressionAlgorithm() // TODO: Negotiation } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/EncryptionBuilderInterface.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/EncryptionBuilderInterface.kt index 2d42c68a..586fbc6e 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/EncryptionBuilderInterface.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/EncryptionBuilderInterface.kt @@ -4,9 +4,9 @@ package org.pgpainless.encryption_signing -import org.bouncycastle.openpgp.PGPException import java.io.IOException import java.io.OutputStream +import org.bouncycastle.openpgp.PGPException fun interface EncryptionBuilderInterface { @@ -26,11 +26,11 @@ fun interface EncryptionBuilderInterface { * * @param options options * @return encryption stream - * * @throws PGPException if something goes wrong during encryption stream preparation - * @throws IOException if something goes wrong during encryption stream preparation (writing headers) + * @throws IOException if something goes wrong during encryption stream preparation (writing + * headers) */ @Throws(PGPException::class, IOException::class) fun withOptions(options: ProducerOptions): EncryptionStream } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/EncryptionOptions.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/EncryptionOptions.kt index 00b1359a..3e232654 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/EncryptionOptions.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/EncryptionOptions.kt @@ -4,6 +4,7 @@ package org.pgpainless.encryption_signing +import java.util.* import org.bouncycastle.openpgp.PGPPublicKey import org.bouncycastle.openpgp.PGPPublicKeyRing import org.bouncycastle.openpgp.operator.PGPKeyEncryptionMethodGenerator @@ -19,12 +20,8 @@ import org.pgpainless.key.SubkeyIdentifier import org.pgpainless.key.info.KeyAccessor import org.pgpainless.key.info.KeyRingInfo import org.pgpainless.util.Passphrase -import java.util.* - -class EncryptionOptions( - private val purpose: EncryptionPurpose -) { +class EncryptionOptions(private val purpose: EncryptionPurpose) { private val _encryptionMethods: MutableSet = mutableSetOf() private val _encryptionKeyIdentifiers: MutableSet = mutableSetOf() private val _keyRingInfo: MutableMap = mutableMapOf() @@ -37,16 +34,20 @@ class EncryptionOptions( val encryptionMethods get() = _encryptionMethods.toSet() + val encryptionKeyIdentifiers get() = _encryptionKeyIdentifiers.toSet() + val keyRingInfo get() = _keyRingInfo.toMap() + val keyViews get() = _keyViews.toMap() + val encryptionAlgorithmOverride get() = _encryptionAlgorithmOverride - constructor(): this(EncryptionPurpose.ANY) + constructor() : this(EncryptionPurpose.ANY) /** * Factory method to create an {@link EncryptionOptions} object which will encrypt for keys @@ -54,54 +55,56 @@ class EncryptionOptions( * * @return encryption options */ - fun setEvaluationDate(evaluationDate: Date) = apply { - this.evaluationDate = evaluationDate - } + fun setEvaluationDate(evaluationDate: Date) = apply { this.evaluationDate = evaluationDate } /** - * Identify authenticatable certificates for the given user-ID by querying the {@link CertificateAuthority} for - * identifiable bindings. - * Add all acceptable bindings, whose trust amount is larger or equal to the target amount to the list of recipients. + * Identify authenticatable certificates for the given user-ID by querying the {@link + * CertificateAuthority} for identifiable bindings. Add all acceptable bindings, whose trust + * amount is larger or equal to the target amount to the list of recipients. + * * @param userId userId - * @param email if true, treat the user-ID as an email address and match all user-IDs containing the mail address + * @param email if true, treat the user-ID as an email address and match all user-IDs containing + * the mail address * @param authority certificate authority - * @param targetAmount target amount (120 = fully authenticated, 240 = doubly authenticated, - * 60 = partially authenticated...) + * @param targetAmount target amount (120 = fully authenticated, 240 = doubly authenticated, 60 + * = partially authenticated...) * @return encryption options */ @JvmOverloads - fun addAuthenticatableRecipients(userId: String, email: Boolean, authority: CertificateAuthority, targetAmount: Int = 120) = apply { + fun addAuthenticatableRecipients( + userId: String, + email: Boolean, + authority: CertificateAuthority, + targetAmount: Int = 120 + ) = apply { var foundAcceptable = false - authority.lookupByUserId(userId, email, evaluationDate, targetAmount) - .filter { it.isAuthenticated() } - .forEach { addRecipient(it.certificate) - .also { - foundAcceptable = true - } - } + authority + .lookupByUserId(userId, email, evaluationDate, targetAmount) + .filter { it.isAuthenticated() } + .forEach { addRecipient(it.certificate).also { foundAcceptable = true } } require(foundAcceptable) { "Could not identify any trust-worthy certificates for '$userId' and target trust amount $targetAmount." } } /** - * Add all key rings in the provided {@link Iterable} (e.g. {@link PGPPublicKeyRingCollection}) as recipients. + * Add all key rings in the provided {@link Iterable} (e.g. {@link PGPPublicKeyRingCollection}) + * as recipients. * * @param keys keys * @return this */ fun addRecipients(keys: Iterable) = apply { keys.toList().let { - require(it.isNotEmpty()) { - "Set of recipient keys cannot be empty." - } + require(it.isNotEmpty()) { "Set of recipient keys cannot be empty." } it.forEach { key -> addRecipient(key) } } } /** - * Add all key rings in the provided {@link Iterable} (e.g. {@link PGPPublicKeyRingCollection}) as recipients. - * Per key ring, the selector is applied to select one or more encryption subkeys. + * Add all key rings in the provided {@link Iterable} (e.g. {@link PGPPublicKeyRingCollection}) + * as recipients. Per key ring, the selector is applied to select one or more encryption + * subkeys. * * @param keys keys * @param selector encryption key selector @@ -109,9 +112,7 @@ class EncryptionOptions( */ fun addRecipients(keys: Iterable, selector: EncryptionKeySelector) = apply { keys.toList().let { - require(it.isNotEmpty()) { - "Set of recipient keys cannot be empty." - } + require(it.isNotEmpty()) { "Set of recipient keys cannot be empty." } it.forEach { key -> addRecipient(key, selector) } } } @@ -125,19 +126,25 @@ class EncryptionOptions( fun addRecipient(key: PGPPublicKeyRing) = addRecipient(key, encryptionKeySelector) /** - * Add a recipient by providing a key and recipient user-id. - * The user-id is used to determine the recipients preferences (algorithms etc.). + * Add a recipient by providing a key and recipient user-id. The user-id is used to determine + * the recipients preferences (algorithms etc.). * * @param key key ring * @param userId user id * @return this */ fun addRecipient(key: PGPPublicKeyRing, userId: CharSequence) = - addRecipient(key, userId, encryptionKeySelector) + addRecipient(key, userId, encryptionKeySelector) - fun addRecipient(key: PGPPublicKeyRing, userId: CharSequence, encryptionKeySelector: EncryptionKeySelector) = apply { + fun addRecipient( + key: PGPPublicKeyRing, + userId: CharSequence, + encryptionKeySelector: EncryptionKeySelector + ) = apply { val info = KeyRingInfo(key, evaluationDate) - val subkeys = encryptionKeySelector.selectEncryptionSubkeys(info.getEncryptionSubkeys(userId, purpose)) + val subkeys = + encryptionKeySelector.selectEncryptionSubkeys( + info.getEncryptionSubkeys(userId, purpose)) if (subkeys.isEmpty()) { throw KeyException.UnacceptableEncryptionKeyException(OpenPgpFingerprint.of(key)) } @@ -155,17 +162,23 @@ class EncryptionOptions( } @JvmOverloads - fun addHiddenRecipient(key: PGPPublicKeyRing, selector: EncryptionKeySelector = encryptionKeySelector) = apply { - addAsRecipient(key, selector, true) - } + fun addHiddenRecipient( + key: PGPPublicKeyRing, + selector: EncryptionKeySelector = encryptionKeySelector + ) = apply { addAsRecipient(key, selector, true) } - private fun addAsRecipient(key: PGPPublicKeyRing, selector: EncryptionKeySelector, wildcardKeyId: Boolean) = apply { + private fun addAsRecipient( + key: PGPPublicKeyRing, + selector: EncryptionKeySelector, + wildcardKeyId: Boolean + ) = apply { val info = KeyRingInfo(key, evaluationDate) - val primaryKeyExpiration = try { - info.primaryKeyExpirationDate - } catch (e: NoSuchElementException) { - throw UnacceptableSelfSignatureException(OpenPgpFingerprint.of(key)) - } + val primaryKeyExpiration = + try { + info.primaryKeyExpirationDate + } catch (e: NoSuchElementException) { + throw UnacceptableSelfSignatureException(OpenPgpFingerprint.of(key)) + } if (primaryKeyExpiration != null && primaryKeyExpiration < evaluationDate) { throw ExpiredKeyException(OpenPgpFingerprint.of(key), primaryKeyExpiration) @@ -174,10 +187,12 @@ class EncryptionOptions( var encryptionSubkeys = selector.selectEncryptionSubkeys(info.getEncryptionSubkeys(purpose)) // There are some legacy keys around without key flags. - // If we allow encryption for those keys, we add valid keys without any key flags, if they are + // If we allow encryption for those keys, we add valid keys without any key flags, if they + // are // capable of encryption by means of their algorithm if (encryptionSubkeys.isEmpty() && allowEncryptionWithMissingKeyFlags) { - encryptionSubkeys = info.validSubkeys + encryptionSubkeys = + info.validSubkeys .filter { it.isEncryptionKey } .filter { info.getKeyFlagsOf(it.keyID).isEmpty() } } @@ -194,13 +209,16 @@ class EncryptionOptions( } } - private fun addRecipientKey(certificate: PGPPublicKeyRing, - key: PGPPublicKey, - wildcardKeyId: Boolean) { + private fun addRecipientKey( + certificate: PGPPublicKeyRing, + key: PGPPublicKey, + wildcardKeyId: Boolean + ) { _encryptionKeyIdentifiers.add(SubkeyIdentifier(certificate, key.keyID)) - addEncryptionMethod(ImplementationFactory.getInstance() - .getPublicKeyKeyEncryptionMethodGenerator(key) - .also { it.setUseWildcardKeyID(wildcardKeyId) }) + addEncryptionMethod( + ImplementationFactory.getInstance().getPublicKeyKeyEncryptionMethodGenerator(key).also { + it.setUseWildcardKeyID(wildcardKeyId) + }) } /** @@ -210,19 +228,19 @@ class EncryptionOptions( * @return this */ fun addPassphrase(passphrase: Passphrase) = apply { - require(!passphrase.isEmpty) { - "Passphrase MUST NOT be empty." - } - addEncryptionMethod(ImplementationFactory.getInstance().getPBEKeyEncryptionMethodGenerator(passphrase)) + require(!passphrase.isEmpty) { "Passphrase MUST NOT be empty." } + addEncryptionMethod( + ImplementationFactory.getInstance().getPBEKeyEncryptionMethodGenerator(passphrase)) } /** * Add an {@link PGPKeyEncryptionMethodGenerator} which will be used to encrypt the message. - * Method generators are either {@link PBEKeyEncryptionMethodGenerator} (passphrase) - * or {@link PGPKeyEncryptionMethodGenerator} (public key). + * Method generators are either {@link PBEKeyEncryptionMethodGenerator} (passphrase) or {@link + * PGPKeyEncryptionMethodGenerator} (public key). * - * This method is intended for advanced users to allow encryption for specific subkeys. - * This can come in handy for example if data needs to be encrypted to a subkey that's ignored by PGPainless. + * This method is intended for advanced users to allow encryption for specific subkeys. This can + * come in handy for example if data needs to be encrypted to a subkey that's ignored by + * PGPainless. * * @param encryptionMethod encryption method * @return this @@ -232,10 +250,9 @@ class EncryptionOptions( } /** - * Override the used symmetric encryption algorithm. - * The symmetric encryption algorithm is used to encrypt the message itself, - * while the used symmetric key will be encrypted to all recipients using public key - * cryptography. + * Override the used symmetric encryption algorithm. The symmetric encryption algorithm is used + * to encrypt the message itself, while the used symmetric key will be encrypted to all + * recipients using public key cryptography. * * If the algorithm is not overridden, a suitable algorithm will be negotiated. * @@ -250,10 +267,10 @@ class EncryptionOptions( } /** - * If this method is called, subsequent calls to {@link #addRecipient(PGPPublicKeyRing)} will allow encryption - * for subkeys that do not carry any {@link org.pgpainless.algorithm.KeyFlag} subpacket. - * This is a workaround for dealing with legacy keys that have no key flags subpacket but rely on the key algorithm - * type to convey the subkeys use. + * If this method is called, subsequent calls to {@link #addRecipient(PGPPublicKeyRing)} will + * allow encryption for subkeys that do not carry any {@link org.pgpainless.algorithm.KeyFlag} + * subpacket. This is a workaround for dealing with legacy keys that have no key flags subpacket + * but rely on the key algorithm type to convey the subkeys use. * * @return this */ @@ -263,20 +280,16 @@ class EncryptionOptions( fun hasEncryptionMethod() = _encryptionMethods.isNotEmpty() - fun interface EncryptionKeySelector { fun selectEncryptionSubkeys(encryptionCapableKeys: List): List } companion object { - @JvmStatic - fun get() = EncryptionOptions() + @JvmStatic fun get() = EncryptionOptions() - @JvmStatic - fun encryptCommunications() = EncryptionOptions(EncryptionPurpose.COMMUNICATIONS) + @JvmStatic fun encryptCommunications() = EncryptionOptions(EncryptionPurpose.COMMUNICATIONS) - @JvmStatic - fun encryptDataAtRest() = EncryptionOptions(EncryptionPurpose.STORAGE) + @JvmStatic fun encryptDataAtRest() = EncryptionOptions(EncryptionPurpose.STORAGE) /** * Only encrypt to the first valid encryption capable subkey we stumble upon. @@ -285,7 +298,8 @@ class EncryptionOptions( */ @JvmStatic fun encryptToFirstSubkey() = EncryptionKeySelector { encryptionCapableKeys -> - encryptionCapableKeys.firstOrNull()?.let { listOf(it) } ?: listOf() } + encryptionCapableKeys.firstOrNull()?.let { listOf(it) } ?: listOf() + } /** * Encrypt to any valid, encryption capable subkey on the key ring. @@ -293,6 +307,8 @@ class EncryptionOptions( * @return encryption key selector */ @JvmStatic - fun encryptToAllCapableSubkeys() = EncryptionKeySelector { encryptionCapableKeys -> encryptionCapableKeys } + fun encryptToAllCapableSubkeys() = EncryptionKeySelector { encryptionCapableKeys -> + encryptionCapableKeys + } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/EncryptionResult.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/EncryptionResult.kt index 0e9a40c9..24d89191 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/EncryptionResult.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/EncryptionResult.kt @@ -4,6 +4,7 @@ package org.pgpainless.encryption_signing +import java.util.* import org.bouncycastle.extensions.matches import org.bouncycastle.openpgp.PGPLiteralData import org.bouncycastle.openpgp.PGPPublicKeyRing @@ -13,21 +14,20 @@ import org.pgpainless.algorithm.StreamEncoding import org.pgpainless.algorithm.SymmetricKeyAlgorithm import org.pgpainless.key.SubkeyIdentifier import org.pgpainless.util.MultiMap -import java.util.* data class EncryptionResult( - val encryptionAlgorithm: SymmetricKeyAlgorithm, - val compressionAlgorithm: CompressionAlgorithm, - val detachedSignatures: MultiMap, - val recipients: Set, - val fileName: String, - val modificationDate: Date, - val fileEncoding: StreamEncoding + val encryptionAlgorithm: SymmetricKeyAlgorithm, + val compressionAlgorithm: CompressionAlgorithm, + val detachedSignatures: MultiMap, + val recipients: Set, + val fileName: String, + val modificationDate: Date, + val fileEncoding: StreamEncoding ) { /** - * Return true, if the message is marked as for-your-eyes-only. - * This is typically done by setting the filename "_CONSOLE". + * Return true, if the message is marked as for-your-eyes-only. This is typically done by + * setting the filename "_CONSOLE". * * @return is message for your eyes only? */ @@ -48,8 +48,7 @@ data class EncryptionResult( * * @return builder */ - @JvmStatic - fun builder() = Builder() + @JvmStatic fun builder() = Builder() } class Builder { @@ -70,32 +69,35 @@ data class EncryptionResult( _compressionAlgorithm = compressionAlgorithm } - fun setFileName(fileName: String) = apply { - _fileName = fileName - } + fun setFileName(fileName: String) = apply { _fileName = fileName } fun setModificationDate(modificationDate: Date) = apply { _modificationDate = modificationDate } - fun setFileEncoding(encoding: StreamEncoding) = apply { - _encoding = encoding - } + fun setFileEncoding(encoding: StreamEncoding) = apply { _encoding = encoding } fun addRecipient(recipient: SubkeyIdentifier) = apply { (recipients as MutableSet).add(recipient) } - fun addDetachedSignature(signingSubkeyIdentifier: SubkeyIdentifier, detachedSignature: PGPSignature) = apply { - detachedSignatures.put(signingSubkeyIdentifier, detachedSignature) - } + fun addDetachedSignature( + signingSubkeyIdentifier: SubkeyIdentifier, + detachedSignature: PGPSignature + ) = apply { detachedSignatures.put(signingSubkeyIdentifier, detachedSignature) } fun build(): EncryptionResult { checkNotNull(_encryptionAlgorithm) { "Encryption algorithm not set." } checkNotNull(_compressionAlgorithm) { "Compression algorithm not set." } - return EncryptionResult(_encryptionAlgorithm!!, _compressionAlgorithm!!, detachedSignatures, recipients, - _fileName, _modificationDate, _encoding) + return EncryptionResult( + _encryptionAlgorithm!!, + _compressionAlgorithm!!, + detachedSignatures, + recipients, + _fileName, + _modificationDate, + _encoding) } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/EncryptionStream.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/EncryptionStream.kt index 50397905..f2617c34 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/EncryptionStream.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/EncryptionStream.kt @@ -4,6 +4,9 @@ package org.pgpainless.encryption_signing +import java.io.BufferedOutputStream +import java.io.IOException +import java.io.OutputStream import org.bouncycastle.bcpg.ArmoredOutputStream import org.bouncycastle.bcpg.BCPGOutputStream import org.bouncycastle.openpgp.PGPCompressedDataGenerator @@ -16,9 +19,6 @@ import org.pgpainless.algorithm.SymmetricKeyAlgorithm import org.pgpainless.implementation.ImplementationFactory import org.pgpainless.util.ArmoredOutputStreamFactory import org.slf4j.LoggerFactory -import java.io.BufferedOutputStream -import java.io.IOException -import java.io.OutputStream // 1 << 8 causes wrong partial body length encoding // 1 << 9 fixes this. @@ -30,11 +30,13 @@ const val BUFFER_SIZE = 1 shl 9 * depending on its configuration. * * This class is based upon Jens Neuhalfen's Bouncy-GPG PGPEncryptingStream. - * @see Source + * + * @see Source */ class EncryptionStream( - private var outermostStream: OutputStream, - private val options: ProducerOptions, + private var outermostStream: OutputStream, + private val options: ProducerOptions, ) : OutputStream() { private val resultBuilder: EncryptionResult.Builder = EncryptionResult.builder() @@ -66,8 +68,8 @@ class EncryptionStream( outermostStream = BufferedOutputStream(outermostStream) LOGGER.debug("Wrap encryption output in ASCII armor.") - armorOutputStream = ArmoredOutputStreamFactory.get(outermostStream, options) - .also { outermostStream = it } + armorOutputStream = + ArmoredOutputStreamFactory.get(outermostStream, options).also { outermostStream = it } } @Throws(IOException::class, PGPException::class) @@ -84,9 +86,11 @@ class EncryptionStream( EncryptionBuilder.negotiateSymmetricEncryptionAlgorithm(options.encryptionOptions).let { resultBuilder.setEncryptionAlgorithm(it) LOGGER.debug("Encrypt message using symmetric algorithm $it.") - val encryptedDataGenerator = PGPEncryptedDataGenerator( - ImplementationFactory.getInstance().getPGPDataEncryptorBuilder(it) - .apply { setWithIntegrityPacket(true) }) + val encryptedDataGenerator = + PGPEncryptedDataGenerator( + ImplementationFactory.getInstance().getPGPDataEncryptorBuilder(it).apply { + setWithIntegrityPacket(true) + }) options.encryptionOptions.encryptionMethods.forEach { m -> encryptedDataGenerator.addMethod(m) } @@ -94,8 +98,11 @@ class EncryptionStream( resultBuilder.addRecipient(r) } - publicKeyEncryptedStream = encryptedDataGenerator.open(outermostStream, ByteArray(BUFFER_SIZE)) - .also { stream -> outermostStream = stream } + publicKeyEncryptedStream = + encryptedDataGenerator.open(outermostStream, ByteArray(BUFFER_SIZE)).also { stream + -> + outermostStream = stream + } } } @@ -107,8 +114,10 @@ class EncryptionStream( if (it == CompressionAlgorithm.UNCOMPRESSED) return LOGGER.debug("Compress using $it.") - basicCompressionStream = BCPGOutputStream(compressedDataGenerator!!.open(outermostStream)) - .also { stream -> outermostStream = stream } + basicCompressionStream = + BCPGOutputStream(compressedDataGenerator!!.open(outermostStream)).also { stream -> + outermostStream = stream + } } } @@ -138,12 +147,17 @@ class EncryptionStream( return } - literalDataGenerator = PGPLiteralDataGenerator().also { gen -> - literalDataStream = gen.open(outermostStream, options.encoding.code, options.fileName, - options.modificationDate, ByteArray(BUFFER_SIZE)).also { stream -> - outermostStream = stream + literalDataGenerator = + PGPLiteralDataGenerator().also { gen -> + literalDataStream = + gen.open( + outermostStream, + options.encoding.code, + options.fileName, + options.modificationDate, + ByteArray(BUFFER_SIZE)) + .also { stream -> outermostStream = stream } } - } resultBuilder.apply { setFileName(options.fileName) setModificationDate(options.modificationDate) @@ -156,39 +170,47 @@ class EncryptionStream( } private fun prepareInputEncoding() { - outermostStream = CRLFGeneratorStream( + outermostStream = + CRLFGeneratorStream( // By buffering here, we drastically improve performance - // Reason is that CRLFGeneratorStream only implements write(int), so we need BufferedOutputStream to + // Reason is that CRLFGeneratorStream only implements write(int), so we need + // BufferedOutputStream to // "convert" to write(buf) calls again BufferedOutputStream(outermostStream), if (options.isApplyCRLFEncoding) StreamEncoding.UTF8 else StreamEncoding.BINARY) } private fun collectHashAlgorithmsForCleartextSigning(): Array { - return options.signingOptions?.signingMethods?.values - ?.map { it.hashAlgorithm }?.toSet() - ?.map { it.algorithmId }?.toTypedArray() - ?: arrayOf() + return options.signingOptions + ?.signingMethods + ?.values + ?.map { it.hashAlgorithm } + ?.toSet() + ?.map { it.algorithmId } + ?.toTypedArray() + ?: arrayOf() } - @Throws(IOException::class) - override fun write(data: Int) = outermostStream.write(data) + @Throws(IOException::class) override fun write(data: Int) = outermostStream.write(data) @Throws(IOException::class) override fun write(buffer: ByteArray) = write(buffer, 0, buffer.size) @Throws(IOException::class) - override fun write(buffer: ByteArray, off: Int, len: Int) = outermostStream.write(buffer, off, len) + override fun write(buffer: ByteArray, off: Int, len: Int) = + outermostStream.write(buffer, off, len) - @Throws(IOException::class) - override fun flush() = outermostStream.flush() + @Throws(IOException::class) override fun flush() = outermostStream.flush() @Throws(IOException::class) override fun close() { if (closed) return outermostStream.close() - literalDataStream?.apply { flush(); close() } + literalDataStream?.apply { + flush() + close() + } literalDataGenerator?.close() if (options.isCleartextSigned) { @@ -201,7 +223,7 @@ class EncryptionStream( try { writeSignatures() - } catch (e : PGPException) { + } catch (e: PGPException) { throw IOException("Exception while writing signatures.", e) } @@ -238,14 +260,14 @@ class EncryptionStream( } val result: EncryptionResult - get() = check(closed) { "EncryptionStream must be closed before accessing the result." } + get() = + check(closed) { "EncryptionStream must be closed before accessing the result." } .let { resultBuilder.build() } val isClosed get() = closed companion object { - @JvmStatic - private val LOGGER = LoggerFactory.getLogger(EncryptionStream::class.java) + @JvmStatic private val LOGGER = LoggerFactory.getLogger(EncryptionStream::class.java) } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/PGPHashContextContentSignerBuilder.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/PGPHashContextContentSignerBuilder.kt index aa3dd58c..ef8641a7 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/PGPHashContextContentSignerBuilder.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/PGPHashContextContentSignerBuilder.kt @@ -4,39 +4,43 @@ package org.pgpainless.encryption_signing +import java.io.OutputStream +import java.security.MessageDigest import org.bouncycastle.crypto.Digest import org.bouncycastle.crypto.Signer import org.bouncycastle.openpgp.operator.PGPContentSignerBuilder -import java.io.OutputStream -import java.security.MessageDigest abstract class PGPHashContextContentSignerBuilder : PGPContentSignerBuilder { // Copied from BC, required since BCs class is package visible only - internal class SignerOutputStream( - private val signer: Signer - ) : OutputStream() { + internal class SignerOutputStream(private val signer: Signer) : OutputStream() { override fun write(p0: Int) = signer.update(p0.toByte()) + override fun write(b: ByteArray) = signer.update(b, 0, b.size) + override fun write(b: ByteArray, off: Int, len: Int) = signer.update(b, off, len) } - internal class ExistingMessageDigest( - private val digest: MessageDigest - ) : Digest { + internal class ExistingMessageDigest(private val digest: MessageDigest) : Digest { override fun getAlgorithmName(): String = digest.algorithm + override fun getDigestSize(): Int = digest.digestLength + override fun update(b: Byte) = digest.update(b) + override fun update(buf: ByteArray, inOff: Int, len: Int) = digest.update(buf) + override fun doFinal(out: ByteArray, outOff: Int): Int { digest.digest().copyInto(out, outOff) return digestSize } + override fun reset() { // Nope! - // We cannot reset, since BCs signer classes are resetting in their init() methods, which would also reset + // We cannot reset, since BCs signer classes are resetting in their init() methods, + // which would also reset // the messageDigest, losing its state. This would shatter our intention. } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/ProducerOptions.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/ProducerOptions.kt index 88345fd9..871f8950 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/ProducerOptions.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/ProducerOptions.kt @@ -4,15 +4,17 @@ package org.pgpainless.encryption_signing +import java.util.* import org.bouncycastle.openpgp.PGPLiteralData import org.pgpainless.PGPainless import org.pgpainless.algorithm.CompressionAlgorithm import org.pgpainless.algorithm.StreamEncoding -import java.util.* -class ProducerOptions private constructor( - val encryptionOptions: EncryptionOptions?, - val signingOptions: SigningOptions?) { +class ProducerOptions +private constructor( + val encryptionOptions: EncryptionOptions?, + val signingOptions: SigningOptions? +) { private var _fileName: String = "" private var _modificationDate: Date = PGPLiteralData.NOW @@ -22,14 +24,15 @@ class ProducerOptions private constructor( private var _hideArmorHeaders = false var isDisableAsciiArmorCRC = false - private var _compressionAlgorithmOverride: CompressionAlgorithm = PGPainless.getPolicy().compressionAlgorithmPolicy.defaultCompressionAlgorithm + private var _compressionAlgorithmOverride: CompressionAlgorithm = + PGPainless.getPolicy().compressionAlgorithmPolicy.defaultCompressionAlgorithm private var asciiArmor = true private var _comment: String? = null private var _version: String? = null /** - * Specify, whether the result of the encryption/signing operation shall be ascii armored. - * The default value is true. + * Specify, whether the result of the encryption/signing operation shall be ascii armored. The + * default value is true. * * @param asciiArmor ascii armor * @return builder @@ -50,19 +53,15 @@ class ProducerOptions private constructor( get() = asciiArmor /** - * Set the comment header in ASCII armored output. - * The default value is null, which means no comment header is added. - * Multiline comments are possible using '\\n'. - *
- * Note: If a default header comment is set using [org.pgpainless.util.ArmoredOutputStreamFactory.setComment], - * then both comments will be written to the produced ASCII armor. + * Set the comment header in ASCII armored output. The default value is null, which means no + * comment header is added. Multiline comments are possible using '\\n'.
Note: If a default + * header comment is set using [org.pgpainless.util.ArmoredOutputStreamFactory.setComment], then + * both comments will be written to the produced ASCII armor. * * @param comment comment header text * @return builder */ - fun setComment(comment: String?) = apply { - _comment = comment - } + fun setComment(comment: String?) = apply { _comment = comment } /** * Return comment set for header in ascii armored output. @@ -80,18 +79,15 @@ class ProducerOptions private constructor( fun hasComment() = _comment != null /** - * Set the version header in ASCII armored output. - * The default value is null, which means no version header is added. - *
- * Note: If the value is non-null, then this method overrides the default version header set using + * Set the version header in ASCII armored output. The default value is null, which means no + * version header is added.
Note: If the value is non-null, then this method overrides the + * default version header set using * [org.pgpainless.util.ArmoredOutputStreamFactory.setVersionInfo]. * * @param version version header, or null for no version info. * @return builder */ - fun setVersion(version: String?) = apply { - _version = version - } + fun setVersion(version: String?) = apply { _version = version } /** * Return the version info header in ascii armored output. @@ -128,15 +124,13 @@ class ProducerOptions private constructor( get() = cleartextSigned /** - * Set the name of the encrypted file. - * Note: This option cannot be used simultaneously with [setForYourEyesOnly]. + * Set the name of the encrypted file. Note: This option cannot be used simultaneously with + * [setForYourEyesOnly]. * * @param fileName name of the encrypted file * @return this */ - fun setFileName(fileName: String) = apply { - _fileName = fileName - } + fun setFileName(fileName: String) = apply { _fileName = fileName } /** * Return the encrypted files name. @@ -147,17 +141,15 @@ class ProducerOptions private constructor( get() = _fileName /** - * Mark the encrypted message as for-your-eyes-only by setting a special file name. - * Note: Therefore this method cannot be used simultaneously with [setFileName]. + * Mark the encrypted message as for-your-eyes-only by setting a special file name. Note: + * Therefore this method cannot be used simultaneously with [setFileName]. * * @return this - * @deprecated deprecated since at least crypto-refresh-05. It is not recommended using this special filename in - * newly generated literal data packets + * @deprecated deprecated since at least crypto-refresh-05. It is not recommended using this + * special filename in newly generated literal data packets */ @Deprecated("Signaling using special file name is discouraged.") - fun setForYourEyesOnly() = apply { - _fileName = PGPLiteralData.CONSOLE - } + fun setForYourEyesOnly() = apply { _fileName = PGPLiteralData.CONSOLE } /** * Set the modification date of the encrypted file. @@ -165,9 +157,7 @@ class ProducerOptions private constructor( * @param modificationDate Modification date of the encrypted file. * @return this */ - fun setModificationDate(modificationDate: Date) = apply { - _modificationDate = modificationDate - } + fun setModificationDate(modificationDate: Date) = apply { _modificationDate = modificationDate } /** * Return the modification date of the encrypted file. @@ -178,40 +168,32 @@ class ProducerOptions private constructor( get() = _modificationDate /** - * Set format metadata field of the literal data packet. - * Defaults to [StreamEncoding.BINARY]. - *
- * This does not change the encoding of the wrapped data itself. - * To apply CR/LF encoding to your input data before processing, use [applyCRLFEncoding] instead. - * - * @see RFC4880 §5.9. Literal Data Packet + * Set format metadata field of the literal data packet. Defaults to [StreamEncoding.BINARY]. + *
This does not change the encoding of the wrapped data itself. To apply CR/LF encoding to + * your input data before processing, use [applyCRLFEncoding] instead. * * @param encoding encoding * @return this - * - * @deprecated options other than the default value of {@link StreamEncoding#BINARY} are discouraged. + * @see RFC4880 §5.9. + * Literal Data Packet + * @deprecated options other than the default value of {@link StreamEncoding#BINARY} are + * discouraged. */ @Deprecated("Options other than BINARY are discouraged.") - fun setEncoding(encoding: StreamEncoding) = apply { - encodingField = encoding - } + fun setEncoding(encoding: StreamEncoding) = apply { encodingField = encoding } val encoding: StreamEncoding get() = encodingField /** - * Apply special encoding of line endings to the input data. - * By default, this is disabled, which means that the data is not altered. - *
- * Enabling it will change the line endings to CR/LF. - * Note: The encoding will not be reversed when decrypting, so applying CR/LF encoding will result in - * the identity "decrypt(encrypt(data)) == data == verify(sign(data))". + * Apply special encoding of line endings to the input data. By default, this is disabled, which + * means that the data is not altered.
Enabling it will change the line endings to CR/LF. + * Note: The encoding will not be reversed when decrypting, so applying CR/LF encoding will + * result in the identity "decrypt(encrypt(data)) == data == verify(sign(data))". * * @return this */ - fun applyCRLFEncoding() = apply { - applyCRLFEncoding = true - } + fun applyCRLFEncoding() = apply { applyCRLFEncoding = true } /** * Return the input encoding that will be applied before signing / encryption. @@ -239,9 +221,8 @@ class ProducerOptions private constructor( /** * If set to `true`, armor headers like version or comments will be omitted from armored output. - * By default, armor headers are not hidden. - * Note: If comments are added via [setComment], those are not omitted, even if - * [hideArmorHeaders] is set to `true`. + * By default, armor headers are not hidden. Note: If comments are added via [setComment], those + * are not omitted, even if [hideArmorHeaders] is set to `true`. * * @param hideArmorHeaders true or false * @return this @@ -260,7 +241,7 @@ class ProducerOptions private constructor( */ @JvmStatic fun signAndEncrypt(encryptionOptions: EncryptionOptions, signingOptions: SigningOptions) = - ProducerOptions(encryptionOptions, signingOptions) + ProducerOptions(encryptionOptions, signingOptions) /** * Sign some data without encryption. @@ -268,8 +249,7 @@ class ProducerOptions private constructor( * @param signingOptions signing options * @return builder */ - @JvmStatic - fun sign(signingOptions: SigningOptions) = ProducerOptions(null, signingOptions) + @JvmStatic fun sign(signingOptions: SigningOptions) = ProducerOptions(null, signingOptions) /** * Encrypt some data without signing. @@ -281,12 +261,10 @@ class ProducerOptions private constructor( fun encrypt(encryptionOptions: EncryptionOptions) = ProducerOptions(encryptionOptions, null) /** - * Only wrap the data in an OpenPGP packet. - * No encryption or signing will be applied. + * Only wrap the data in an OpenPGP packet. No encryption or signing will be applied. * * @return builder */ - @JvmStatic - fun noEncryptionNoSigning() = ProducerOptions(null, null) + @JvmStatic fun noEncryptionNoSigning() = ProducerOptions(null, null) } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/SignatureGenerationStream.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/SignatureGenerationStream.kt index d0f04851..b2d53dcd 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/SignatureGenerationStream.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/SignatureGenerationStream.kt @@ -6,23 +6,20 @@ package org.pgpainless.encryption_signing import java.io.OutputStream -/** - * OutputStream which has the task of updating signature generators for written data. - */ +/** OutputStream which has the task of updating signature generators for written data. */ class SignatureGenerationStream( - private val wrapped: OutputStream, - private val options: SigningOptions? + private val wrapped: OutputStream, + private val options: SigningOptions? ) : OutputStream() { override fun close() = wrapped.close() + override fun flush() = wrapped.flush() override fun write(b: Int) { wrapped.write(b) options?.run { - signingMethods.values.forEach { - it.signatureGenerator.update((b and 0xff).toByte()) - } + signingMethods.values.forEach { it.signatureGenerator.update((b and 0xff).toByte()) } } } @@ -30,10 +27,6 @@ class SignatureGenerationStream( override fun write(b: ByteArray, off: Int, len: Int) { wrapped.write(b, off, len) - options?.run { - signingMethods.values.forEach { - it.signatureGenerator.update(b, off, len) - } - } + options?.run { signingMethods.values.forEach { it.signatureGenerator.update(b, off, len) } } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/SigningOptions.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/SigningOptions.kt index 512e074b..089b2d28 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/SigningOptions.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/SigningOptions.kt @@ -4,6 +4,7 @@ package org.pgpainless.encryption_signing +import java.util.* import org.bouncycastle.extensions.unlock import org.bouncycastle.openpgp.* import org.pgpainless.PGPainless.Companion.getPolicy @@ -22,7 +23,6 @@ import org.pgpainless.policy.Policy import org.pgpainless.signature.subpackets.BaseSignatureSubpackets.Callback import org.pgpainless.signature.subpackets.SignatureSubpackets import org.pgpainless.signature.subpackets.SignatureSubpacketsHelper -import java.util.* class SigningOptions { @@ -34,10 +34,10 @@ class SigningOptions { get() = _hashAlgorithmOverride /** - * Override hash algorithm negotiation by dictating which hash algorithm needs to be used. - * If no override has been set, an acceptable algorithm will be negotiated instead. - * Note: To override the hash algorithm for signing, call this method *before* calling - * [addInlineSignature] or [addDetachedSignature]. + * Override hash algorithm negotiation by dictating which hash algorithm needs to be used. If no + * override has been set, an acceptable algorithm will be negotiated instead. Note: To override + * the hash algorithm for signing, call this method *before* calling [addInlineSignature] or + * [addDetachedSignature]. * * @param hashAlgorithmOverride override hash algorithm * @return this @@ -55,9 +55,7 @@ class SigningOptions { * @param evaluationDate new evaluation date * @return this */ - fun setEvaluationDate(evaluationDate: Date) = apply { - _evaluationDate = evaluationDate - } + fun setEvaluationDate(evaluationDate: Date) = apply { _evaluationDate = evaluationDate } /** * Sign the message using an inline signature made by the provided signing key. @@ -65,14 +63,15 @@ class SigningOptions { * @param signingKeyProtector protector to unlock the signing key * @param signingKey key ring containing the signing key * @return this - * * @throws KeyException if something is wrong with the key * @throws PGPException if the key cannot be unlocked or a signing method cannot be created */ @Throws(KeyException::class, PGPException::class) - fun addSignature(signingKeyProtector: SecretKeyRingProtector, signingKey: PGPSecretKeyRing) = apply { - addInlineSignature(signingKeyProtector, signingKey, null, DocumentSignatureType.BINARY_DOCUMENT) - } + fun addSignature(signingKeyProtector: SecretKeyRingProtector, signingKey: PGPSecretKeyRing) = + apply { + addInlineSignature( + signingKeyProtector, signingKey, null, DocumentSignatureType.BINARY_DOCUMENT) + } /** * Add inline signatures with all secret key rings in the provided secret key ring collection. @@ -81,44 +80,41 @@ class SigningOptions { * @param signingKeys collection of signing keys * @param signatureType type of signature (binary, canonical text) * @return this - * * @throws KeyException if something is wrong with any of the keys - * @throws PGPException if any of the keys cannot be unlocked or a signing method cannot be created + * @throws PGPException if any of the keys cannot be unlocked or a signing method cannot be + * created */ @Throws(KeyException::class, PGPException::class) - fun addInlineSignatures(signingKeyProtector: SecretKeyRingProtector, - signingKeys: Iterable, - signatureType: DocumentSignatureType) = apply { - signingKeys.forEach { - addInlineSignature(signingKeyProtector, it, null, signatureType) - } + fun addInlineSignatures( + signingKeyProtector: SecretKeyRingProtector, + signingKeys: Iterable, + signatureType: DocumentSignatureType + ) = apply { + signingKeys.forEach { addInlineSignature(signingKeyProtector, it, null, signatureType) } } - /** - * Add an inline-signature. - * Inline signatures are being embedded into the message itself and can be processed in one pass, thanks to the use - * of one-pass-signature packets. + * Add an inline-signature. Inline signatures are being embedded into the message itself and can + * be processed in one pass, thanks to the use of one-pass-signature packets. * * @param signingKeyProtector decryptor to unlock the signing secret key * @param signingKey signing key * @param signatureType type of signature (binary, canonical text) * @return this - * * @throws KeyException if something is wrong with the key * @throws PGPException if the key cannot be unlocked or the signing method cannot be created */ @Throws(KeyException::class, PGPException::class) - fun addInlineSignature(signingKeyProtector: SecretKeyRingProtector, - signingKey: PGPSecretKeyRing, - signatureType: DocumentSignatureType) = apply { - addInlineSignature(signingKeyProtector, signingKey, null, signatureType) - } + fun addInlineSignature( + signingKeyProtector: SecretKeyRingProtector, + signingKey: PGPSecretKeyRing, + signatureType: DocumentSignatureType + ) = apply { addInlineSignature(signingKeyProtector, signingKey, null, signatureType) } /** - * Add an inline-signature. - * Inline signatures are being embedded into the message itself and can be processed in one pass, thanks to the use - * of one-pass-signature packets. + * Add an inline-signature. Inline signatures are being embedded into the message itself and can + * be processed in one pass, thanks to the use of one-pass-signature packets. + * *

* This method uses the passed in user-id to select user-specific hash algorithms. * @@ -126,27 +122,28 @@ class SigningOptions { * @param signingKey signing key * @param userId user-id of the signer * @param signatureType signature type (binary, canonical text) - * @param subpacketsCallback callback to modify the hashed and unhashed subpackets of the signature + * @param subpacketsCallback callback to modify the hashed and unhashed subpackets of the + * signature * @return this - * * @throws KeyException if the key is invalid * @throws PGPException if the key cannot be unlocked or the signing method cannot be created */ @Throws(KeyException::class, PGPException::class) @JvmOverloads - fun addInlineSignature(signingKeyProtector: SecretKeyRingProtector, - signingKey: PGPSecretKeyRing, - userId: CharSequence? = null, - signatureType: DocumentSignatureType = DocumentSignatureType.BINARY_DOCUMENT, - subpacketsCallback: Callback? = null) = apply { + fun addInlineSignature( + signingKeyProtector: SecretKeyRingProtector, + signingKey: PGPSecretKeyRing, + userId: CharSequence? = null, + signatureType: DocumentSignatureType = DocumentSignatureType.BINARY_DOCUMENT, + subpacketsCallback: Callback? = null + ) = apply { val keyRingInfo = inspectKeyRing(signingKey, evaluationDate) if (userId != null && !keyRingInfo.isUserIdValid(userId)) { throw UnboundUserIdException( - of(signingKey), - userId.toString(), - keyRingInfo.getLatestUserIdCertification(userId), - keyRingInfo.getUserIdRevocation(userId) - ) + of(signingKey), + userId.toString(), + keyRingInfo.getLatestUserIdCertification(userId), + keyRingInfo.getUserIdRevocation(userId)) } val signingPubKeys = keyRingInfo.signingSubkeys @@ -155,14 +152,16 @@ class SigningOptions { } for (signingPubKey in signingPubKeys) { - val signingSecKey: PGPSecretKey = signingKey.getSecretKey(signingPubKey.keyID) + val signingSecKey: PGPSecretKey = + signingKey.getSecretKey(signingPubKey.keyID) ?: throw MissingSecretKeyException(of(signingKey), signingPubKey.keyID) val signingSubkey: PGPPrivateKey = signingSecKey.unlock(signingKeyProtector) val hashAlgorithms = - if (userId != null) keyRingInfo.getPreferredHashAlgorithms(userId) - else keyRingInfo.getPreferredHashAlgorithms(signingPubKey.keyID) + if (userId != null) keyRingInfo.getPreferredHashAlgorithms(userId) + else keyRingInfo.getPreferredHashAlgorithms(signingPubKey.keyID) val hashAlgorithm: HashAlgorithm = negotiateHashAlgorithm(hashAlgorithms, getPolicy()) - addSigningMethod(signingKey, signingSubkey, hashAlgorithm, signatureType, false, subpacketsCallback) + addSigningMethod( + signingKey, signingSubkey, hashAlgorithm, signatureType, false, subpacketsCallback) } } @@ -175,17 +174,22 @@ class SigningOptions { * @param signatureType signature type * @param subpacketsCallback callback to modify the signatures subpackets * @return builder - * @throws PGPException if the secret key cannot be unlocked or if no signing method can be created. - * @throws KeyException.UnacceptableSigningKeyException if the key ring does not carry any signing-capable subkeys - * @throws KeyException.MissingSecretKeyException if the key ring does not contain the identified secret key + * @throws PGPException if the secret key cannot be unlocked or if no signing method can be + * created. + * @throws KeyException.UnacceptableSigningKeyException if the key ring does not carry any + * signing-capable subkeys + * @throws KeyException.MissingSecretKeyException if the key ring does not contain the + * identified secret key */ @Throws(KeyException::class, PGPException::class) @JvmOverloads - fun addInlineSignature(signingKeyProtector: SecretKeyRingProtector, - signingKey: PGPSecretKeyRing, - keyId: Long, - signatureType: DocumentSignatureType = DocumentSignatureType.BINARY_DOCUMENT, - subpacketsCallback: Callback? = null) = apply { + fun addInlineSignature( + signingKeyProtector: SecretKeyRingProtector, + signingKey: PGPSecretKeyRing, + keyId: Long, + signatureType: DocumentSignatureType = DocumentSignatureType.BINARY_DOCUMENT, + subpacketsCallback: Callback? = null + ) = apply { val keyRingInfo = inspectKeyRing(signingKey, evaluationDate) val signingPubKeys = keyRingInfo.signingSubkeys if (signingPubKeys.isEmpty()) { @@ -197,12 +201,14 @@ class SigningOptions { continue } - val signingSecKey = signingKey.getSecretKey(signingPubKey.keyID) + val signingSecKey = + signingKey.getSecretKey(signingPubKey.keyID) ?: throw MissingSecretKeyException(of(signingKey), signingPubKey.keyID) val signingSubkey = signingSecKey.unlock(signingKeyProtector) val hashAlgorithms = keyRingInfo.getPreferredHashAlgorithms(signingPubKey.keyID) val hashAlgorithm: HashAlgorithm = negotiateHashAlgorithm(hashAlgorithms, getPolicy()) - addSigningMethod(signingKey, signingSubkey, hashAlgorithm, signatureType, false, subpacketsCallback) + addSigningMethod( + signingKey, signingSubkey, hashAlgorithm, signatureType, false, subpacketsCallback) return this } throw MissingSecretKeyException(of(signingKey), keyId) @@ -215,45 +221,44 @@ class SigningOptions { * @param signingKeys collection of signing key rings * @param signatureType type of the signature (binary, canonical text) * @return this - * * @throws KeyException if something is wrong with any of the keys - * @throws PGPException if any of the keys cannot be validated or unlocked, or if any signing method cannot be created + * @throws PGPException if any of the keys cannot be validated or unlocked, or if any signing + * method cannot be created */ @Throws(KeyException::class, PGPException::class) - fun addDetachedSignatures(signingKeyProtector: SecretKeyRingProtector, - signingKeys: Iterable, - signatureType: DocumentSignatureType) = apply { - signingKeys.forEach { - addDetachedSignature(signingKeyProtector, it, null, signatureType) - } + fun addDetachedSignatures( + signingKeyProtector: SecretKeyRingProtector, + signingKeys: Iterable, + signatureType: DocumentSignatureType + ) = apply { + signingKeys.forEach { addDetachedSignature(signingKeyProtector, it, null, signatureType) } } /** - * Create a detached signature. - * Detached signatures are not being added into the PGP message itself. - * Instead, they can be distributed separately to the message. - * Detached signatures are useful if the data that is being signed shall not be modified (e.g. when signing a file). + * Create a detached signature. Detached signatures are not being added into the PGP message + * itself. Instead, they can be distributed separately to the message. Detached signatures are + * useful if the data that is being signed shall not be modified (e.g. when signing a file). * * @param signingKeyProtector decryptor to unlock the secret signing key * @param signingKey signing key * @param signatureType type of data that is signed (binary, canonical text) * @return this - * * @throws KeyException if something is wrong with the key - * @throws PGPException if the key cannot be validated or unlocked, or if no signature method can be created + * @throws PGPException if the key cannot be validated or unlocked, or if no signature method + * can be created */ @Throws(KeyException::class, PGPException::class) - fun addDetachedSignature(signingKeyProtector: SecretKeyRingProtector, - signingKey: PGPSecretKeyRing, - signatureType: DocumentSignatureType) = apply { - addDetachedSignature(signingKeyProtector, signingKey, null, signatureType) - } + fun addDetachedSignature( + signingKeyProtector: SecretKeyRingProtector, + signingKey: PGPSecretKeyRing, + signatureType: DocumentSignatureType + ) = apply { addDetachedSignature(signingKeyProtector, signingKey, null, signatureType) } /** - * Create a detached signature. - * Detached signatures are not being added into the PGP message itself. - * Instead, they can be distributed separately to the message. - * Detached signatures are useful if the data that is being signed shall not be modified (e.g. when signing a file). + * Create a detached signature. Detached signatures are not being added into the PGP message + * itself. Instead, they can be distributed separately to the message. Detached signatures are + * useful if the data that is being signed shall not be modified (e.g. when signing a file). + * *

* This method uses the passed in user-id to select user-specific hash algorithms. * @@ -263,25 +268,26 @@ class SigningOptions { * @param signatureType type of data that is signed (binary, canonical text) * @param subpacketCallback callback to modify hashed and unhashed subpackets of the signature * @return this - * * @throws KeyException if something is wrong with the key - * @throws PGPException if the key cannot be validated or unlocked, or if no signature method can be created + * @throws PGPException if the key cannot be validated or unlocked, or if no signature method + * can be created */ @JvmOverloads @Throws(KeyException::class, PGPException::class) - fun addDetachedSignature(signingKeyProtector: SecretKeyRingProtector, - signingKey: PGPSecretKeyRing, - userId: String? = null, - signatureType: DocumentSignatureType = DocumentSignatureType.BINARY_DOCUMENT, - subpacketCallback: Callback? = null) = apply { + fun addDetachedSignature( + signingKeyProtector: SecretKeyRingProtector, + signingKey: PGPSecretKeyRing, + userId: String? = null, + signatureType: DocumentSignatureType = DocumentSignatureType.BINARY_DOCUMENT, + subpacketCallback: Callback? = null + ) = apply { val keyRingInfo = inspectKeyRing(signingKey, evaluationDate) if (userId != null && !keyRingInfo.isUserIdValid(userId)) { throw UnboundUserIdException( - of(signingKey), - userId.toString(), - keyRingInfo.getLatestUserIdCertification(userId), - keyRingInfo.getUserIdRevocation(userId) - ) + of(signingKey), + userId.toString(), + keyRingInfo.getLatestUserIdCertification(userId), + keyRingInfo.getUserIdRevocation(userId)) } val signingPubKeys = keyRingInfo.signingSubkeys @@ -290,14 +296,16 @@ class SigningOptions { } for (signingPubKey in signingPubKeys) { - val signingSecKey: PGPSecretKey = signingKey.getSecretKey(signingPubKey.keyID) + val signingSecKey: PGPSecretKey = + signingKey.getSecretKey(signingPubKey.keyID) ?: throw MissingSecretKeyException(of(signingKey), signingPubKey.keyID) val signingSubkey: PGPPrivateKey = signingSecKey.unlock(signingKeyProtector) val hashAlgorithms = - if (userId != null) keyRingInfo.getPreferredHashAlgorithms(userId) - else keyRingInfo.getPreferredHashAlgorithms(signingPubKey.keyID) + if (userId != null) keyRingInfo.getPreferredHashAlgorithms(userId) + else keyRingInfo.getPreferredHashAlgorithms(signingPubKey.keyID) val hashAlgorithm: HashAlgorithm = negotiateHashAlgorithm(hashAlgorithms, getPolicy()) - addSigningMethod(signingKey, signingSubkey, hashAlgorithm, signatureType, true, subpacketCallback) + addSigningMethod( + signingKey, signingSubkey, hashAlgorithm, signatureType, true, subpacketCallback) } } @@ -310,17 +318,22 @@ class SigningOptions { * @param signatureType signature type * @param subpacketsCallback callback to modify the signatures subpackets * @return builder - * @throws PGPException if the secret key cannot be unlocked or if no signing method can be created. - * @throws KeyException.UnacceptableSigningKeyException if the key ring does not carry any signing-capable subkeys - * @throws KeyException.MissingSecretKeyException if the key ring does not contain the identified secret key + * @throws PGPException if the secret key cannot be unlocked or if no signing method can be + * created. + * @throws KeyException.UnacceptableSigningKeyException if the key ring does not carry any + * signing-capable subkeys + * @throws KeyException.MissingSecretKeyException if the key ring does not contain the + * identified secret key */ @Throws(KeyException::class, PGPException::class) @JvmOverloads - fun addDetachedSignature(signingKeyProtector: SecretKeyRingProtector, - signingKey: PGPSecretKeyRing, - keyId: Long, - signatureType: DocumentSignatureType = DocumentSignatureType.BINARY_DOCUMENT, - subpacketsCallback: Callback? = null) = apply { + fun addDetachedSignature( + signingKeyProtector: SecretKeyRingProtector, + signingKey: PGPSecretKeyRing, + keyId: Long, + signatureType: DocumentSignatureType = DocumentSignatureType.BINARY_DOCUMENT, + subpacketsCallback: Callback? = null + ) = apply { val keyRingInfo = inspectKeyRing(signingKey, evaluationDate) val signingPubKeys = keyRingInfo.signingSubkeys @@ -330,12 +343,20 @@ class SigningOptions { for (signingPubKey in signingPubKeys) { if (signingPubKey.keyID == keyId) { - val signingSecKey: PGPSecretKey = signingKey.getSecretKey(signingPubKey.keyID) + val signingSecKey: PGPSecretKey = + signingKey.getSecretKey(signingPubKey.keyID) ?: throw MissingSecretKeyException(of(signingKey), signingPubKey.keyID) val signingSubkey: PGPPrivateKey = signingSecKey.unlock(signingKeyProtector) val hashAlgorithms = keyRingInfo.getPreferredHashAlgorithms(signingPubKey.keyID) - val hashAlgorithm: HashAlgorithm = negotiateHashAlgorithm(hashAlgorithms, getPolicy()) - addSigningMethod(signingKey, signingSubkey, hashAlgorithm, signatureType, true, subpacketsCallback) + val hashAlgorithm: HashAlgorithm = + negotiateHashAlgorithm(hashAlgorithms, getPolicy()) + addSigningMethod( + signingKey, + signingSubkey, + hashAlgorithm, + signatureType, + true, + subpacketsCallback) return this } } @@ -343,26 +364,30 @@ class SigningOptions { throw MissingSecretKeyException(of(signingKey), keyId) } - private fun addSigningMethod(signingKey: PGPSecretKeyRing, - signingSubkey: PGPPrivateKey, - hashAlgorithm: HashAlgorithm, - signatureType: DocumentSignatureType, - detached: Boolean, - subpacketCallback: Callback? = null) { + private fun addSigningMethod( + signingKey: PGPSecretKeyRing, + signingSubkey: PGPPrivateKey, + hashAlgorithm: HashAlgorithm, + signatureType: DocumentSignatureType, + detached: Boolean, + subpacketCallback: Callback? = null + ) { val signingKeyIdentifier = SubkeyIdentifier(signingKey, signingSubkey.keyID) val signingSecretKey: PGPSecretKey = signingKey.getSecretKey(signingSubkey.keyID) val publicKeyAlgorithm = requireFromId(signingSecretKey.publicKey.algorithm) val bitStrength = signingSecretKey.publicKey.bitStrength if (!getPolicy().publicKeyAlgorithmPolicy.isAcceptable(publicKeyAlgorithm, bitStrength)) { throw UnacceptableSigningKeyException( - PublicKeyAlgorithmPolicyException( - of(signingKey), signingSecretKey.keyID, publicKeyAlgorithm, bitStrength)) + PublicKeyAlgorithmPolicyException( + of(signingKey), signingSecretKey.keyID, publicKeyAlgorithm, bitStrength)) } - val generator: PGPSignatureGenerator = createSignatureGenerator(signingSubkey, hashAlgorithm, signatureType) + val generator: PGPSignatureGenerator = + createSignatureGenerator(signingSubkey, hashAlgorithm, signatureType) // Subpackets - val hashedSubpackets = SignatureSubpackets.createHashedSubpackets(signingSecretKey.publicKey) + val hashedSubpackets = + SignatureSubpackets.createHashedSubpackets(signingSecretKey.publicKey) val unhashedSubpackets = SignatureSubpackets.createEmptySubpackets() if (subpacketCallback != null) { subpacketCallback.modifyHashedSubpackets(hashedSubpackets) @@ -372,80 +397,87 @@ class SigningOptions { generator.setUnhashedSubpackets(SignatureSubpacketsHelper.toVector(unhashedSubpackets)) val signingMethod = - if (detached) SigningMethod.detachedSignature(generator, hashAlgorithm) - else SigningMethod.inlineSignature(generator, hashAlgorithm) + if (detached) SigningMethod.detachedSignature(generator, hashAlgorithm) + else SigningMethod.inlineSignature(generator, hashAlgorithm) (signingMethods as MutableMap)[signingKeyIdentifier] = signingMethod } /** * Negotiate, which hash algorithm to use. * - * - * This method gives the highest priority to the algorithm override, which can be set via [.overrideHashAlgorithm]. - * After that, the signing keys hash algorithm preferences are iterated to find the first acceptable algorithm. - * Lastly, should no acceptable algorithm be found, the [Policies][Policy] default signature hash algorithm is - * used as a fallback. + * This method gives the highest priority to the algorithm override, which can be set via + * [.overrideHashAlgorithm]. After that, the signing keys hash algorithm preferences are + * iterated to find the first acceptable algorithm. Lastly, should no acceptable algorithm be + * found, the [Policies][Policy] default signature hash algorithm is used as a fallback. * * @param preferences preferences * @param policy policy * @return selected hash algorithm */ - private fun negotiateHashAlgorithm(preferences: Set, - policy: Policy): HashAlgorithm { - return _hashAlgorithmOverride ?: negotiateSignatureHashAlgorithm(policy).negotiateHashAlgorithm(preferences) + private fun negotiateHashAlgorithm( + preferences: Set, + policy: Policy + ): HashAlgorithm { + return _hashAlgorithmOverride + ?: negotiateSignatureHashAlgorithm(policy).negotiateHashAlgorithm(preferences) } @Throws(PGPException::class) - private fun createSignatureGenerator(privateKey: PGPPrivateKey, - hashAlgorithm: HashAlgorithm, - signatureType: DocumentSignatureType): PGPSignatureGenerator { + private fun createSignatureGenerator( + privateKey: PGPPrivateKey, + hashAlgorithm: HashAlgorithm, + signatureType: DocumentSignatureType + ): PGPSignatureGenerator { return ImplementationFactory.getInstance() - .getPGPContentSignerBuilder(privateKey.publicKeyPacket.algorithm, hashAlgorithm.algorithmId) - .let { csb -> - PGPSignatureGenerator(csb).also { it.init(signatureType.signatureType.code, privateKey) } + .getPGPContentSignerBuilder( + privateKey.publicKeyPacket.algorithm, hashAlgorithm.algorithmId) + .let { csb -> + PGPSignatureGenerator(csb).also { + it.init(signatureType.signatureType.code, privateKey) } + } } - companion object { - @JvmStatic - fun get() = SigningOptions() + @JvmStatic fun get() = SigningOptions() } - /** - * A method of signing. - */ - class SigningMethod private constructor( - val signatureGenerator: PGPSignatureGenerator, - val isDetached: Boolean, - val hashAlgorithm: HashAlgorithm + /** A method of signing. */ + class SigningMethod + private constructor( + val signatureGenerator: PGPSignatureGenerator, + val isDetached: Boolean, + val hashAlgorithm: HashAlgorithm ) { companion object { /** - * Inline-signature method. - * The resulting signature will be written into the message itself, together with a one-pass-signature packet. + * Inline-signature method. The resulting signature will be written into the message + * itself, together with a one-pass-signature packet. * * @param signatureGenerator signature generator * @param hashAlgorithm hash algorithm used to generate the signature * @return inline signing method */ @JvmStatic - fun inlineSignature(signatureGenerator: PGPSignatureGenerator, hashAlgorithm: HashAlgorithm) = - SigningMethod(signatureGenerator, false, hashAlgorithm) + fun inlineSignature( + signatureGenerator: PGPSignatureGenerator, + hashAlgorithm: HashAlgorithm + ) = SigningMethod(signatureGenerator, false, hashAlgorithm) /** - * Detached signing method. - * The resulting signature will not be added to the message, and instead can be distributed separately - * to the signed message. + * Detached signing method. The resulting signature will not be added to the message, + * and instead can be distributed separately to the signed message. * * @param signatureGenerator signature generator * @param hashAlgorithm hash algorithm used to generate the signature * @return detached signing method */ @JvmStatic - fun detachedSignature(signatureGenerator: PGPSignatureGenerator, hashAlgorithm: HashAlgorithm) = - SigningMethod(signatureGenerator, true, hashAlgorithm) + fun detachedSignature( + signatureGenerator: PGPSignatureGenerator, + hashAlgorithm: HashAlgorithm + ) = SigningMethod(signatureGenerator, true, hashAlgorithm) } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/implementation/BcImplementationFactory.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/implementation/BcImplementationFactory.kt index fec3a550..dcf594ea 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/implementation/BcImplementationFactory.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/implementation/BcImplementationFactory.kt @@ -4,6 +4,9 @@ package org.pgpainless.implementation +import java.io.InputStream +import java.security.KeyPair +import java.util.* import org.bouncycastle.crypto.AsymmetricCipherKeyPair import org.bouncycastle.openpgp.* import org.bouncycastle.openpgp.bc.BcPGPObjectFactory @@ -27,70 +30,85 @@ import org.pgpainless.algorithm.HashAlgorithm import org.pgpainless.algorithm.PublicKeyAlgorithm import org.pgpainless.algorithm.SymmetricKeyAlgorithm import org.pgpainless.util.Passphrase -import java.io.InputStream -import java.security.KeyPair -import java.util.* class BcImplementationFactory : ImplementationFactory() { - override val pgpDigestCalculatorProvider: BcPGPDigestCalculatorProvider = BcPGPDigestCalculatorProvider() - override val pgpContentVerifierBuilderProvider: BcPGPContentVerifierBuilderProvider = BcPGPContentVerifierBuilderProvider() + override val pgpDigestCalculatorProvider: BcPGPDigestCalculatorProvider = + BcPGPDigestCalculatorProvider() + override val pgpContentVerifierBuilderProvider: BcPGPContentVerifierBuilderProvider = + BcPGPContentVerifierBuilderProvider() override val keyFingerprintCalculator: BcKeyFingerprintCalculator = BcKeyFingerprintCalculator() - override fun getPBESecretKeyEncryptor(symmetricKeyAlgorithm: SymmetricKeyAlgorithm, - digestCalculator: PGPDigestCalculator, - passphrase: Passphrase): PBESecretKeyEncryptor = - BcPBESecretKeyEncryptorBuilder(symmetricKeyAlgorithm.algorithmId, digestCalculator) - .build(passphrase.getChars()) + override fun getPBESecretKeyEncryptor( + symmetricKeyAlgorithm: SymmetricKeyAlgorithm, + digestCalculator: PGPDigestCalculator, + passphrase: Passphrase + ): PBESecretKeyEncryptor = + BcPBESecretKeyEncryptorBuilder(symmetricKeyAlgorithm.algorithmId, digestCalculator) + .build(passphrase.getChars()) - override fun getPBESecretKeyEncryptor(encryptionAlgorithm: SymmetricKeyAlgorithm, - hashAlgorithm: HashAlgorithm, - s2kCount: Int, - passphrase: Passphrase): PBESecretKeyEncryptor = - BcPBESecretKeyEncryptorBuilder( - encryptionAlgorithm.algorithmId, - getPGPDigestCalculator(hashAlgorithm), - s2kCount) - .build(passphrase.getChars()) + override fun getPBESecretKeyEncryptor( + encryptionAlgorithm: SymmetricKeyAlgorithm, + hashAlgorithm: HashAlgorithm, + s2kCount: Int, + passphrase: Passphrase + ): PBESecretKeyEncryptor = + BcPBESecretKeyEncryptorBuilder( + encryptionAlgorithm.algorithmId, getPGPDigestCalculator(hashAlgorithm), s2kCount) + .build(passphrase.getChars()) override fun getPBESecretKeyDecryptor(passphrase: Passphrase): PBESecretKeyDecryptor = - BcPBESecretKeyDecryptorBuilder(pgpDigestCalculatorProvider) - .build(passphrase.getChars()) + BcPBESecretKeyDecryptorBuilder(pgpDigestCalculatorProvider).build(passphrase.getChars()) - override fun getPGPContentSignerBuilder(keyAlgorithm: Int, hashAlgorithm: Int): PGPContentSignerBuilder = - BcPGPContentSignerBuilder(keyAlgorithm, hashAlgorithm) + override fun getPGPContentSignerBuilder( + keyAlgorithm: Int, + hashAlgorithm: Int + ): PGPContentSignerBuilder = BcPGPContentSignerBuilder(keyAlgorithm, hashAlgorithm) override fun getPBEDataDecryptorFactory(passphrase: Passphrase): PBEDataDecryptorFactory = - BcPBEDataDecryptorFactory(passphrase.getChars(), pgpDigestCalculatorProvider) + BcPBEDataDecryptorFactory(passphrase.getChars(), pgpDigestCalculatorProvider) - override fun getPublicKeyDataDecryptorFactory(privateKey: PGPPrivateKey): PublicKeyDataDecryptorFactory = - BcPublicKeyDataDecryptorFactory(privateKey) + override fun getPublicKeyDataDecryptorFactory( + privateKey: PGPPrivateKey + ): PublicKeyDataDecryptorFactory = BcPublicKeyDataDecryptorFactory(privateKey) - override fun getSessionKeyDataDecryptorFactory(sessionKey: PGPSessionKey): SessionKeyDataDecryptorFactory = - BcSessionKeyDataDecryptorFactory(sessionKey) + override fun getSessionKeyDataDecryptorFactory( + sessionKey: PGPSessionKey + ): SessionKeyDataDecryptorFactory = BcSessionKeyDataDecryptorFactory(sessionKey) - override fun getPublicKeyKeyEncryptionMethodGenerator(key: PGPPublicKey): PublicKeyKeyEncryptionMethodGenerator = - BcPublicKeyKeyEncryptionMethodGenerator(key) + override fun getPublicKeyKeyEncryptionMethodGenerator( + key: PGPPublicKey + ): PublicKeyKeyEncryptionMethodGenerator = BcPublicKeyKeyEncryptionMethodGenerator(key) - override fun getPBEKeyEncryptionMethodGenerator(passphrase: Passphrase): PBEKeyEncryptionMethodGenerator = - BcPBEKeyEncryptionMethodGenerator(passphrase.getChars()) + override fun getPBEKeyEncryptionMethodGenerator( + passphrase: Passphrase + ): PBEKeyEncryptionMethodGenerator = BcPBEKeyEncryptionMethodGenerator(passphrase.getChars()) override fun getPGPDataEncryptorBuilder(symmetricKeyAlgorithm: Int): PGPDataEncryptorBuilder = - BcPGPDataEncryptorBuilder(symmetricKeyAlgorithm) + BcPGPDataEncryptorBuilder(symmetricKeyAlgorithm) - override fun getPGPKeyPair(publicKeyAlgorithm: PublicKeyAlgorithm, keyPair: KeyPair, creationDate: Date): PGPKeyPair = - BcPGPKeyPair( - publicKeyAlgorithm.algorithmId, - jceToBcKeyPair(publicKeyAlgorithm, keyPair, creationDate), - creationDate) + override fun getPGPKeyPair( + publicKeyAlgorithm: PublicKeyAlgorithm, + keyPair: KeyPair, + creationDate: Date + ): PGPKeyPair = + BcPGPKeyPair( + publicKeyAlgorithm.algorithmId, + jceToBcKeyPair(publicKeyAlgorithm, keyPair, creationDate), + creationDate) - override fun getPGPObjectFactory(inputStream: InputStream): PGPObjectFactory = BcPGPObjectFactory(inputStream) + override fun getPGPObjectFactory(inputStream: InputStream): PGPObjectFactory = + BcPGPObjectFactory(inputStream) - private fun jceToBcKeyPair(publicKeyAlgorithm: PublicKeyAlgorithm, - keyPair: KeyPair, - creationDate: Date): AsymmetricCipherKeyPair = - BcPGPKeyConverter().let { converter -> - JcaPGPKeyPair(publicKeyAlgorithm.algorithmId, keyPair, creationDate).let { pair -> - AsymmetricCipherKeyPair(converter.getPublicKey(pair.publicKey), converter.getPrivateKey(pair.privateKey)) - } + private fun jceToBcKeyPair( + publicKeyAlgorithm: PublicKeyAlgorithm, + keyPair: KeyPair, + creationDate: Date + ): AsymmetricCipherKeyPair = + BcPGPKeyConverter().let { converter -> + JcaPGPKeyPair(publicKeyAlgorithm.algorithmId, keyPair, creationDate).let { pair -> + AsymmetricCipherKeyPair( + converter.getPublicKey(pair.publicKey), + converter.getPrivateKey(pair.privateKey)) } -} \ No newline at end of file + } +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/implementation/ImplementationFactory.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/implementation/ImplementationFactory.kt index 5ae653b4..58478379 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/implementation/ImplementationFactory.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/implementation/ImplementationFactory.kt @@ -4,6 +4,9 @@ package org.pgpainless.implementation +import java.io.InputStream +import java.security.KeyPair +import java.util.* import org.bouncycastle.openpgp.* import org.bouncycastle.openpgp.operator.* import org.pgpainless.algorithm.HashAlgorithm @@ -11,18 +14,13 @@ import org.pgpainless.algorithm.PublicKeyAlgorithm import org.pgpainless.algorithm.SymmetricKeyAlgorithm import org.pgpainless.util.Passphrase import org.pgpainless.util.SessionKey -import java.io.InputStream -import java.security.KeyPair -import java.util.* abstract class ImplementationFactory { companion object { - @JvmStatic - private var instance: ImplementationFactory = BcImplementationFactory() + @JvmStatic private var instance: ImplementationFactory = BcImplementationFactory() - @JvmStatic - fun getInstance() = instance + @JvmStatic fun getInstance() = instance @JvmStatic fun setFactoryImplementation(implementation: ImplementationFactory) = apply { @@ -38,56 +36,82 @@ abstract class ImplementationFactory { get() = getPGPDigestCalculator(HashAlgorithm.SHA1) @Throws(PGPException::class) - abstract fun getPBESecretKeyEncryptor(symmetricKeyAlgorithm: SymmetricKeyAlgorithm, - digestCalculator: PGPDigestCalculator, - passphrase: Passphrase): PBESecretKeyEncryptor + abstract fun getPBESecretKeyEncryptor( + symmetricKeyAlgorithm: SymmetricKeyAlgorithm, + digestCalculator: PGPDigestCalculator, + passphrase: Passphrase + ): PBESecretKeyEncryptor @Throws(PGPException::class) abstract fun getPBESecretKeyDecryptor(passphrase: Passphrase): PBESecretKeyDecryptor @Throws(PGPException::class) - abstract fun getPBESecretKeyEncryptor(encryptionAlgorithm: SymmetricKeyAlgorithm, hashAlgorithm: HashAlgorithm, - s2kCount: Int, passphrase: Passphrase): PBESecretKeyEncryptor + abstract fun getPBESecretKeyEncryptor( + encryptionAlgorithm: SymmetricKeyAlgorithm, + hashAlgorithm: HashAlgorithm, + s2kCount: Int, + passphrase: Passphrase + ): PBESecretKeyEncryptor fun getPGPDigestCalculator(hashAlgorithm: HashAlgorithm): PGPDigestCalculator = - getPGPDigestCalculator(hashAlgorithm.algorithmId) + getPGPDigestCalculator(hashAlgorithm.algorithmId) fun getPGPDigestCalculator(hashAlgorithm: Int): PGPDigestCalculator = - pgpDigestCalculatorProvider.get(hashAlgorithm) + pgpDigestCalculatorProvider.get(hashAlgorithm) - fun getPGPContentSignerBuilder(keyAlgorithm: PublicKeyAlgorithm, hashAlgorithm: HashAlgorithm): PGPContentSignerBuilder = - getPGPContentSignerBuilder(keyAlgorithm.algorithmId, hashAlgorithm.algorithmId) + fun getPGPContentSignerBuilder( + keyAlgorithm: PublicKeyAlgorithm, + hashAlgorithm: HashAlgorithm + ): PGPContentSignerBuilder = + getPGPContentSignerBuilder(keyAlgorithm.algorithmId, hashAlgorithm.algorithmId) - abstract fun getPGPContentSignerBuilder(keyAlgorithm: Int, hashAlgorithm: Int): PGPContentSignerBuilder + abstract fun getPGPContentSignerBuilder( + keyAlgorithm: Int, + hashAlgorithm: Int + ): PGPContentSignerBuilder @Throws(PGPException::class) abstract fun getPBEDataDecryptorFactory(passphrase: Passphrase): PBEDataDecryptorFactory - abstract fun getPublicKeyDataDecryptorFactory(privateKey: PGPPrivateKey): PublicKeyDataDecryptorFactory + abstract fun getPublicKeyDataDecryptorFactory( + privateKey: PGPPrivateKey + ): PublicKeyDataDecryptorFactory fun getSessionKeyDataDecryptorFactory(sessionKey: SessionKey): SessionKeyDataDecryptorFactory = - getSessionKeyDataDecryptorFactory(PGPSessionKey(sessionKey.algorithm.algorithmId, sessionKey.key)) + getSessionKeyDataDecryptorFactory( + PGPSessionKey(sessionKey.algorithm.algorithmId, sessionKey.key)) - abstract fun getSessionKeyDataDecryptorFactory(sessionKey: PGPSessionKey): SessionKeyDataDecryptorFactory + abstract fun getSessionKeyDataDecryptorFactory( + sessionKey: PGPSessionKey + ): SessionKeyDataDecryptorFactory - abstract fun getPublicKeyKeyEncryptionMethodGenerator(key: PGPPublicKey): PublicKeyKeyEncryptionMethodGenerator + abstract fun getPublicKeyKeyEncryptionMethodGenerator( + key: PGPPublicKey + ): PublicKeyKeyEncryptionMethodGenerator - abstract fun getPBEKeyEncryptionMethodGenerator(passphrase: Passphrase): PBEKeyEncryptionMethodGenerator + abstract fun getPBEKeyEncryptionMethodGenerator( + passphrase: Passphrase + ): PBEKeyEncryptionMethodGenerator - fun getPGPDataEncryptorBuilder(symmetricKeyAlgorithm: SymmetricKeyAlgorithm): PGPDataEncryptorBuilder = - getPGPDataEncryptorBuilder(symmetricKeyAlgorithm.algorithmId) + fun getPGPDataEncryptorBuilder( + symmetricKeyAlgorithm: SymmetricKeyAlgorithm + ): PGPDataEncryptorBuilder = getPGPDataEncryptorBuilder(symmetricKeyAlgorithm.algorithmId) abstract fun getPGPDataEncryptorBuilder(symmetricKeyAlgorithm: Int): PGPDataEncryptorBuilder @Throws(PGPException::class) - abstract fun getPGPKeyPair(publicKeyAlgorithm: PublicKeyAlgorithm, keyPair: KeyPair, creationDate: Date): PGPKeyPair + abstract fun getPGPKeyPair( + publicKeyAlgorithm: PublicKeyAlgorithm, + keyPair: KeyPair, + creationDate: Date + ): PGPKeyPair fun getPGPObjectFactory(bytes: ByteArray): PGPObjectFactory = - getPGPObjectFactory(bytes.inputStream()) + getPGPObjectFactory(bytes.inputStream()) abstract fun getPGPObjectFactory(inputStream: InputStream): PGPObjectFactory override fun toString(): String { return javaClass.simpleName } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/implementation/JceImplementationFactory.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/implementation/JceImplementationFactory.kt index 9a4d8e41..865f1e0d 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/implementation/JceImplementationFactory.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/implementation/JceImplementationFactory.kt @@ -4,6 +4,9 @@ package org.pgpainless.implementation +import java.io.InputStream +import java.security.KeyPair +import java.util.* import org.bouncycastle.openpgp.* import org.bouncycastle.openpgp.operator.* import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator @@ -24,79 +27,86 @@ import org.pgpainless.algorithm.PublicKeyAlgorithm import org.pgpainless.algorithm.SymmetricKeyAlgorithm import org.pgpainless.provider.ProviderFactory import org.pgpainless.util.Passphrase -import java.io.InputStream -import java.security.KeyPair -import java.util.* class JceImplementationFactory : ImplementationFactory() { override val pgpDigestCalculatorProvider: PGPDigestCalculatorProvider = - JcaPGPDigestCalculatorProviderBuilder() - .setProvider(ProviderFactory.provider) - .build() + JcaPGPDigestCalculatorProviderBuilder().setProvider(ProviderFactory.provider).build() override val pgpContentVerifierBuilderProvider: PGPContentVerifierBuilderProvider = - JcaPGPContentVerifierBuilderProvider() - .setProvider(ProviderFactory.provider) + JcaPGPContentVerifierBuilderProvider().setProvider(ProviderFactory.provider) override val keyFingerprintCalculator: KeyFingerPrintCalculator = - JcaKeyFingerprintCalculator() - .setProvider(ProviderFactory.provider) + JcaKeyFingerprintCalculator().setProvider(ProviderFactory.provider) - override fun getPBESecretKeyEncryptor(symmetricKeyAlgorithm: SymmetricKeyAlgorithm, - digestCalculator: PGPDigestCalculator, - passphrase: Passphrase): PBESecretKeyEncryptor = - JcePBESecretKeyEncryptorBuilder(symmetricKeyAlgorithm.algorithmId, digestCalculator) - .setProvider(ProviderFactory.provider) - .build(passphrase.getChars()) + override fun getPBESecretKeyEncryptor( + symmetricKeyAlgorithm: SymmetricKeyAlgorithm, + digestCalculator: PGPDigestCalculator, + passphrase: Passphrase + ): PBESecretKeyEncryptor = + JcePBESecretKeyEncryptorBuilder(symmetricKeyAlgorithm.algorithmId, digestCalculator) + .setProvider(ProviderFactory.provider) + .build(passphrase.getChars()) - override fun getPBESecretKeyEncryptor(encryptionAlgorithm: SymmetricKeyAlgorithm, - hashAlgorithm: HashAlgorithm, - s2kCount: Int, - passphrase: Passphrase): PBESecretKeyEncryptor = - JcePBESecretKeyEncryptorBuilder( - encryptionAlgorithm.algorithmId, - getPGPDigestCalculator(hashAlgorithm), - s2kCount) - .setProvider(ProviderFactory.provider) - .build(passphrase.getChars()) + override fun getPBESecretKeyEncryptor( + encryptionAlgorithm: SymmetricKeyAlgorithm, + hashAlgorithm: HashAlgorithm, + s2kCount: Int, + passphrase: Passphrase + ): PBESecretKeyEncryptor = + JcePBESecretKeyEncryptorBuilder( + encryptionAlgorithm.algorithmId, getPGPDigestCalculator(hashAlgorithm), s2kCount) + .setProvider(ProviderFactory.provider) + .build(passphrase.getChars()) override fun getPBESecretKeyDecryptor(passphrase: Passphrase): PBESecretKeyDecryptor = - JcePBESecretKeyDecryptorBuilder(pgpDigestCalculatorProvider) - .setProvider(ProviderFactory.provider) - .build(passphrase.getChars()) + JcePBESecretKeyDecryptorBuilder(pgpDigestCalculatorProvider) + .setProvider(ProviderFactory.provider) + .build(passphrase.getChars()) - override fun getPGPContentSignerBuilder(keyAlgorithm: Int, hashAlgorithm: Int): PGPContentSignerBuilder = - JcaPGPContentSignerBuilder(keyAlgorithm, hashAlgorithm) - .setProvider(ProviderFactory.provider) + override fun getPGPContentSignerBuilder( + keyAlgorithm: Int, + hashAlgorithm: Int + ): PGPContentSignerBuilder = + JcaPGPContentSignerBuilder(keyAlgorithm, hashAlgorithm) + .setProvider(ProviderFactory.provider) override fun getPBEDataDecryptorFactory(passphrase: Passphrase): PBEDataDecryptorFactory = - JcePBEDataDecryptorFactoryBuilder(pgpDigestCalculatorProvider) - .setProvider(ProviderFactory.provider) - .build(passphrase.getChars()) + JcePBEDataDecryptorFactoryBuilder(pgpDigestCalculatorProvider) + .setProvider(ProviderFactory.provider) + .build(passphrase.getChars()) - override fun getPublicKeyDataDecryptorFactory(privateKey: PGPPrivateKey): PublicKeyDataDecryptorFactory = - JcePublicKeyDataDecryptorFactoryBuilder() - .setProvider(ProviderFactory.provider) - .build(privateKey) + override fun getPublicKeyDataDecryptorFactory( + privateKey: PGPPrivateKey + ): PublicKeyDataDecryptorFactory = + JcePublicKeyDataDecryptorFactoryBuilder() + .setProvider(ProviderFactory.provider) + .build(privateKey) - override fun getSessionKeyDataDecryptorFactory(sessionKey: PGPSessionKey): SessionKeyDataDecryptorFactory = - JceSessionKeyDataDecryptorFactoryBuilder() - .setProvider(ProviderFactory.provider) - .build(sessionKey) + override fun getSessionKeyDataDecryptorFactory( + sessionKey: PGPSessionKey + ): SessionKeyDataDecryptorFactory = + JceSessionKeyDataDecryptorFactoryBuilder() + .setProvider(ProviderFactory.provider) + .build(sessionKey) - override fun getPublicKeyKeyEncryptionMethodGenerator(key: PGPPublicKey): PublicKeyKeyEncryptionMethodGenerator = - JcePublicKeyKeyEncryptionMethodGenerator(key) - .setProvider(ProviderFactory.provider) + override fun getPublicKeyKeyEncryptionMethodGenerator( + key: PGPPublicKey + ): PublicKeyKeyEncryptionMethodGenerator = + JcePublicKeyKeyEncryptionMethodGenerator(key).setProvider(ProviderFactory.provider) - override fun getPBEKeyEncryptionMethodGenerator(passphrase: Passphrase): PBEKeyEncryptionMethodGenerator = - JcePBEKeyEncryptionMethodGenerator(passphrase.getChars()) - .setProvider(ProviderFactory.provider) + override fun getPBEKeyEncryptionMethodGenerator( + passphrase: Passphrase + ): PBEKeyEncryptionMethodGenerator = + JcePBEKeyEncryptionMethodGenerator(passphrase.getChars()) + .setProvider(ProviderFactory.provider) override fun getPGPDataEncryptorBuilder(symmetricKeyAlgorithm: Int): PGPDataEncryptorBuilder = - JcePGPDataEncryptorBuilder(symmetricKeyAlgorithm) - .setProvider(ProviderFactory.provider) + JcePGPDataEncryptorBuilder(symmetricKeyAlgorithm).setProvider(ProviderFactory.provider) - override fun getPGPKeyPair(publicKeyAlgorithm: PublicKeyAlgorithm, keyPair: KeyPair, creationDate: Date): PGPKeyPair = - JcaPGPKeyPair(publicKeyAlgorithm.algorithmId, keyPair, creationDate) + override fun getPGPKeyPair( + publicKeyAlgorithm: PublicKeyAlgorithm, + keyPair: KeyPair, + creationDate: Date + ): PGPKeyPair = JcaPGPKeyPair(publicKeyAlgorithm.algorithmId, keyPair, creationDate) override fun getPGPObjectFactory(inputStream: InputStream): PGPObjectFactory = - PGPObjectFactory(inputStream, keyFingerprintCalculator) -} \ No newline at end of file + PGPObjectFactory(inputStream, keyFingerprintCalculator) +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/OpenPgpFingerprint.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/OpenPgpFingerprint.kt index fdcad306..c67c3983 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/OpenPgpFingerprint.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/OpenPgpFingerprint.kt @@ -4,16 +4,13 @@ package org.pgpainless.key +import java.nio.charset.Charset import org.bouncycastle.openpgp.PGPKeyRing import org.bouncycastle.openpgp.PGPPublicKey import org.bouncycastle.openpgp.PGPSecretKey import org.bouncycastle.util.encoders.Hex -import java.nio.charset.Charset -/** - * Abstract super class of different version OpenPGP fingerprints. - * - */ +/** Abstract super class of different version OpenPGP fingerprints. */ abstract class OpenPgpFingerprint : CharSequence, Comparable { val fingerprint: String val bytes: ByteArray @@ -26,39 +23,41 @@ abstract class OpenPgpFingerprint : CharSequence, Comparable abstract fun getVersion(): Int /** - * Return the key id of the OpenPGP public key this {@link OpenPgpFingerprint} belongs to. - * This method can be implemented for V4 and V5 fingerprints. - * V3 key-IDs cannot be derived from the fingerprint, but we don't care, since V3 is deprecated. + * Return the key id of the OpenPGP public key this {@link OpenPgpFingerprint} belongs to. This + * method can be implemented for V4 and V5 fingerprints. V3 key-IDs cannot be derived from the + * fingerprint, but we don't care, since V3 is deprecated. * - * @see - * RFC-4880 §12.2: Key IDs and Fingerprints * @return key id + * @see RFC-4880 §12.2: Key IDs and + * Fingerprints */ abstract val keyId: Long constructor(fingerprint: String) { val prep = fingerprint.replace(" ", "").trim().uppercase() if (!isValid(prep)) { - throw IllegalArgumentException("Fingerprint '$fingerprint' does not appear to be a valid OpenPGP V${getVersion()} fingerprint.") + throw IllegalArgumentException( + "Fingerprint '$fingerprint' does not appear to be a valid OpenPGP V${getVersion()} fingerprint.") } this.fingerprint = prep this.bytes = Hex.decode(prep) } - constructor(bytes: ByteArray): this(Hex.toHexString(bytes)) + constructor(bytes: ByteArray) : this(Hex.toHexString(bytes)) - constructor(key: PGPPublicKey): this(key.fingerprint) { + constructor(key: PGPPublicKey) : this(key.fingerprint) { if (key.version != getVersion()) { throw IllegalArgumentException("Key is not a v${getVersion()} OpenPgp key.") } } - constructor(key: PGPSecretKey): this(key.publicKey) + constructor(key: PGPSecretKey) : this(key.publicKey) - constructor(keys: PGPKeyRing): this(keys.publicKey) + constructor(keys: PGPKeyRing) : this(keys.publicKey) /** * Check, whether the fingerprint consists of 40 valid hexadecimal characters. + * * @param fp fingerprint to check. * @return true if fingerprint is valid. */ @@ -69,7 +68,9 @@ abstract class OpenPgpFingerprint : CharSequence, Comparable override fun get(index: Int) = fingerprint.get(index) - override fun subSequence(startIndex: Int, endIndex: Int) = fingerprint.subSequence(startIndex, endIndex) + override fun subSequence(startIndex: Int, endIndex: Int) = + fingerprint.subSequence(startIndex, endIndex) + override fun compareTo(other: OpenPgpFingerprint): Int { return fingerprint.compareTo(other.fingerprint) } @@ -87,49 +88,49 @@ abstract class OpenPgpFingerprint : CharSequence, Comparable abstract fun prettyPrint(): String companion object { - @JvmStatic - val utf8: Charset = Charset.forName("UTF-8") + @JvmStatic val utf8: Charset = Charset.forName("UTF-8") /** - * Return the fingerprint of the given key. - * This method automatically matches key versions to fingerprint implementations. + * Return the fingerprint of the given key. This method automatically matches key versions + * to fingerprint implementations. + * + * @param key key + * @return fingerprint + */ + @JvmStatic fun of(key: PGPSecretKey): OpenPgpFingerprint = of(key.publicKey) + + /** + * Return the fingerprint of the given key. This method automatically matches key versions + * to fingerprint implementations. * * @param key key * @return fingerprint */ @JvmStatic - fun of(key: PGPSecretKey): OpenPgpFingerprint = of(key.publicKey) + fun of(key: PGPPublicKey): OpenPgpFingerprint = + when (key.version) { + 4 -> OpenPgpV4Fingerprint(key) + 5 -> OpenPgpV5Fingerprint(key) + 6 -> OpenPgpV6Fingerprint(key) + else -> + throw IllegalArgumentException( + "OpenPGP keys of version ${key.version} are not supported.") + } /** - * Return the fingerprint of the given key. - * This method automatically matches key versions to fingerprint implementations. - * - * @param key key - * @return fingerprint - */ - @JvmStatic - fun of (key: PGPPublicKey): OpenPgpFingerprint = when(key.version) { - 4 -> OpenPgpV4Fingerprint(key) - 5 -> OpenPgpV5Fingerprint(key) - 6 -> OpenPgpV6Fingerprint(key) - else -> throw IllegalArgumentException("OpenPGP keys of version ${key.version} are not supported.") - } - - /** - * Return the fingerprint of the primary key of the given key ring. - * This method automatically matches key versions to fingerprint implementations. + * Return the fingerprint of the primary key of the given key ring. This method + * automatically matches key versions to fingerprint implementations. * * @param ring key ring * @return fingerprint */ - @JvmStatic - fun of (keys: PGPKeyRing): OpenPgpFingerprint = of(keys.publicKey) + @JvmStatic fun of(keys: PGPKeyRing): OpenPgpFingerprint = of(keys.publicKey) /** - * Try to parse an {@link OpenPgpFingerprint} from the given fingerprint string. - * If the trimmed fingerprint without whitespace is 64 characters long, it is either a v5 or v6 fingerprint. - * In this case, we return a {@link _64DigitFingerprint}. Since this is ambiguous, it is generally recommended - * to know the version of the key beforehand. + * Try to parse an {@link OpenPgpFingerprint} from the given fingerprint string. If the + * trimmed fingerprint without whitespace is 64 characters long, it is either a v5 or v6 + * fingerprint. In this case, we return a {@link _64DigitFingerprint}. Since this is + * ambiguous, it is generally recommended to know the version of the key beforehand. * * @param fingerprint fingerprint * @return parsed fingerprint @@ -146,7 +147,8 @@ abstract class OpenPgpFingerprint : CharSequence, Comparable // Might be v5 or v6 :/ return _64DigitFingerprint(prep) } - throw IllegalArgumentException("Fingerprint does not appear to match any known fingerprint pattern.") + throw IllegalArgumentException( + "Fingerprint does not appear to match any known fingerprint pattern.") } /** @@ -159,6 +161,6 @@ abstract class OpenPgpFingerprint : CharSequence, Comparable @JvmStatic @Deprecated("use the parse() methods of the versioned fingerprint subclasses instead.") fun parseFromBinary(binaryFingerprint: ByteArray): OpenPgpFingerprint = - parse(Hex.toHexString(binaryFingerprint)) + parse(Hex.toHexString(binaryFingerprint)) } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/OpenPgpV4Fingerprint.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/OpenPgpV4Fingerprint.kt index fe0c4364..e02f0ae7 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/OpenPgpV4Fingerprint.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/OpenPgpV4Fingerprint.kt @@ -4,22 +4,26 @@ package org.pgpainless.key -import org.bouncycastle.openpgp.PGPKeyRing -import org.bouncycastle.openpgp.PGPPublicKey -import org.bouncycastle.openpgp.PGPSecretKey -import org.bouncycastle.util.encoders.Hex import java.net.URI import java.nio.Buffer import java.nio.ByteBuffer import java.nio.charset.Charset +import org.bouncycastle.openpgp.PGPKeyRing +import org.bouncycastle.openpgp.PGPPublicKey +import org.bouncycastle.openpgp.PGPSecretKey +import org.bouncycastle.util.encoders.Hex -class OpenPgpV4Fingerprint: OpenPgpFingerprint { +class OpenPgpV4Fingerprint : OpenPgpFingerprint { - constructor(fingerprint: String): super(fingerprint) - constructor(bytes: ByteArray): super(bytes) - constructor(key: PGPPublicKey): super(key) - constructor(key: PGPSecretKey): super(key) - constructor(keys: PGPKeyRing): super(keys) + constructor(fingerprint: String) : super(fingerprint) + + constructor(bytes: ByteArray) : super(bytes) + + constructor(key: PGPPublicKey) : super(key) + + constructor(key: PGPSecretKey) : super(key) + + constructor(keys: PGPKeyRing) : super(keys) override fun getVersion() = 4 @@ -47,7 +51,7 @@ class OpenPgpV4Fingerprint: OpenPgpFingerprint { append(fingerprint, i * 4, (i + 1) * 4).append(' ') } append(' ') - for (i in 5 .. 8) { + for (i in 5..8) { append(fingerprint, i * 4, (i + 1) * 4).append(' ') } append(fingerprint, 36, 40) @@ -55,8 +59,7 @@ class OpenPgpV4Fingerprint: OpenPgpFingerprint { } companion object { - @JvmStatic - val SCHEME = "openpgp4fpr" + @JvmStatic val SCHEME = "openpgp4fpr" @JvmStatic fun fromUri(uri: URI): OpenPgpV4Fingerprint { @@ -66,4 +69,4 @@ class OpenPgpV4Fingerprint: OpenPgpFingerprint { return OpenPgpV4Fingerprint(uri.schemeSpecificPart) } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/OpenPgpV5Fingerprint.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/OpenPgpV5Fingerprint.kt index b82a3482..7bc36cc9 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/OpenPgpV5Fingerprint.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/OpenPgpV5Fingerprint.kt @@ -8,18 +8,20 @@ import org.bouncycastle.openpgp.PGPKeyRing import org.bouncycastle.openpgp.PGPPublicKey import org.bouncycastle.openpgp.PGPSecretKey -/** - * This class represents a hex encoded uppercase OpenPGP v5 fingerprint. - */ -class OpenPgpV5Fingerprint: _64DigitFingerprint { +/** This class represents a hex encoded uppercase OpenPGP v5 fingerprint. */ +class OpenPgpV5Fingerprint : _64DigitFingerprint { - constructor(fingerprint: String): super(fingerprint) - constructor(key: PGPPublicKey): super(key) - constructor(key: PGPSecretKey): super(key) - constructor(keys: PGPKeyRing): super(keys) - constructor(bytes: ByteArray): super(bytes) + constructor(fingerprint: String) : super(fingerprint) + + constructor(key: PGPPublicKey) : super(key) + + constructor(key: PGPSecretKey) : super(key) + + constructor(keys: PGPKeyRing) : super(keys) + + constructor(bytes: ByteArray) : super(bytes) override fun getVersion(): Int { return 5 } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/OpenPgpV6Fingerprint.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/OpenPgpV6Fingerprint.kt index 11cf05b0..0b843ed2 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/OpenPgpV6Fingerprint.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/OpenPgpV6Fingerprint.kt @@ -8,18 +8,20 @@ import org.bouncycastle.openpgp.PGPKeyRing import org.bouncycastle.openpgp.PGPPublicKey import org.bouncycastle.openpgp.PGPSecretKey -/** - * This class represents a hex encoded, uppercase OpenPGP v6 fingerprint. - */ -class OpenPgpV6Fingerprint: _64DigitFingerprint { +/** This class represents a hex encoded, uppercase OpenPGP v6 fingerprint. */ +class OpenPgpV6Fingerprint : _64DigitFingerprint { - constructor(fingerprint: String): super(fingerprint) - constructor(key: PGPPublicKey): super(key) - constructor(key: PGPSecretKey): super(key) - constructor(keys: PGPKeyRing): super(keys) - constructor(bytes: ByteArray): super(bytes) + constructor(fingerprint: String) : super(fingerprint) + + constructor(key: PGPPublicKey) : super(key) + + constructor(key: PGPSecretKey) : super(key) + + constructor(keys: PGPKeyRing) : super(keys) + + constructor(bytes: ByteArray) : super(bytes) override fun getVersion(): Int { return 6 } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/SubkeyIdentifier.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/SubkeyIdentifier.kt index a793fc99..2aec7976 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/SubkeyIdentifier.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/SubkeyIdentifier.kt @@ -13,17 +13,30 @@ import org.bouncycastle.openpgp.PGPPublicKey * as well as the subkeys fingerprint. */ class SubkeyIdentifier( - val primaryKeyFingerprint: OpenPgpFingerprint, - val subkeyFingerprint: OpenPgpFingerprint) { + val primaryKeyFingerprint: OpenPgpFingerprint, + val subkeyFingerprint: OpenPgpFingerprint +) { - constructor(fingerprint: OpenPgpFingerprint): this(fingerprint, fingerprint) - constructor(keys: PGPKeyRing): this(keys.publicKey) - constructor(key: PGPPublicKey): this(OpenPgpFingerprint.of(key)) - constructor(keys: PGPKeyRing, keyId: Long): this( - OpenPgpFingerprint.of(keys.publicKey), - OpenPgpFingerprint.of(keys.getPublicKey(keyId) ?: - throw NoSuchElementException("OpenPGP key does not contain subkey ${keyId.openPgpKeyId()}"))) - constructor(keys: PGPKeyRing, subkeyFingerprint: OpenPgpFingerprint): this(OpenPgpFingerprint.of(keys), subkeyFingerprint) + constructor(fingerprint: OpenPgpFingerprint) : this(fingerprint, fingerprint) + + constructor(keys: PGPKeyRing) : this(keys.publicKey) + + constructor(key: PGPPublicKey) : this(OpenPgpFingerprint.of(key)) + + constructor( + keys: PGPKeyRing, + keyId: Long + ) : this( + OpenPgpFingerprint.of(keys.publicKey), + OpenPgpFingerprint.of( + keys.getPublicKey(keyId) + ?: throw NoSuchElementException( + "OpenPGP key does not contain subkey ${keyId.openPgpKeyId()}"))) + + constructor( + keys: PGPKeyRing, + subkeyFingerprint: OpenPgpFingerprint + ) : this(OpenPgpFingerprint.of(keys), subkeyFingerprint) val keyId = subkeyFingerprint.keyId val fingerprint = subkeyFingerprint @@ -34,7 +47,7 @@ class SubkeyIdentifier( val isPrimaryKey = primaryKeyId == subkeyId fun matches(fingerprint: OpenPgpFingerprint) = - primaryKeyFingerprint == fingerprint || subkeyFingerprint == fingerprint + primaryKeyFingerprint == fingerprint || subkeyFingerprint == fingerprint override fun equals(other: Any?): Boolean { if (other == null) { @@ -47,7 +60,8 @@ class SubkeyIdentifier( return false } - return primaryKeyFingerprint == other.primaryKeyFingerprint && subkeyFingerprint == other.subkeyFingerprint + return primaryKeyFingerprint == other.primaryKeyFingerprint && + subkeyFingerprint == other.subkeyFingerprint } override fun hashCode(): Int { @@ -55,4 +69,4 @@ class SubkeyIdentifier( } override fun toString(): String = "$subkeyFingerprint $primaryKeyFingerprint" -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/_64DigitFingerprint.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/_64DigitFingerprint.kt index 0f7dd705..279815bd 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/_64DigitFingerprint.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/_64DigitFingerprint.kt @@ -2,47 +2,47 @@ // // SPDX-License-Identifier: Apache-2.0 -package org.pgpainless.key; +package org.pgpainless.key -import java.nio.Buffer; -import java.nio.ByteBuffer; -import java.nio.charset.Charset; -import javax.annotation.Nonnull; - -import org.bouncycastle.openpgp.PGPKeyRing; -import org.bouncycastle.openpgp.PGPPublicKey; -import org.bouncycastle.openpgp.PGPPublicKeyRing; -import org.bouncycastle.openpgp.PGPSecretKey; -import org.bouncycastle.openpgp.PGPSecretKeyRing; -import org.bouncycastle.util.encoders.Hex; +import java.nio.Buffer +import java.nio.ByteBuffer +import java.nio.charset.Charset +import org.bouncycastle.openpgp.PGPKeyRing +import org.bouncycastle.openpgp.PGPPublicKey +import org.bouncycastle.openpgp.PGPSecretKey +import org.bouncycastle.util.encoders.Hex /** - * This class represents a hex encoded, upper case OpenPGP v5 or v6 fingerprint. - * Since both fingerprints use the same format, this class is used when parsing the fingerprint without knowing the - * key version. + * This class represents a hex encoded, upper case OpenPGP v5 or v6 fingerprint. Since both + * fingerprints use the same format, this class is used when parsing the fingerprint without knowing + * the key version. */ -open class _64DigitFingerprint: OpenPgpFingerprint { +open class _64DigitFingerprint : OpenPgpFingerprint { /** * Create an {@link _64DigitFingerprint}. * * @param fingerprint uppercase hexadecimal fingerprint of length 64 */ - constructor(fingerprint: String): super(fingerprint) - constructor(bytes: ByteArray): super(bytes) - constructor(key: PGPPublicKey): super(key) - constructor(key: PGPSecretKey): super(key) - constructor(keys: PGPKeyRing): super(keys) + constructor(fingerprint: String) : super(fingerprint) + + constructor(bytes: ByteArray) : super(bytes) + + constructor(key: PGPPublicKey) : super(key) + + constructor(key: PGPSecretKey) : super(key) + + constructor(keys: PGPKeyRing) : super(keys) override val keyId: Long get() { val bytes = Hex.decode(fingerprint.toByteArray(Charset.forName("UTF-8"))) - val buf = ByteBuffer.wrap(bytes); + val buf = ByteBuffer.wrap(bytes) // The key id is the left-most 8 bytes (conveniently a long). // We have to cast here in order to be compatible with java 8 // https://github.com/eclipse/jetty.project/issues/3244 - (buf as Buffer).position(0); + (buf as Buffer).position(0) return buf.getLong() } @@ -62,13 +62,13 @@ open class _64DigitFingerprint: OpenPgpFingerprint { override fun prettyPrint(): String { return buildString { for (i in 0 until 4) { - append(fingerprint, i * 8, (i + 1) * 8).append(' '); + append(fingerprint, i * 8, (i + 1) * 8).append(' ') } - append(' '); + append(' ') for (i in 4 until 7) { - append(fingerprint, i * 8, (i + 1) * 8).append(' '); + append(fingerprint, i * 8, (i + 1) * 8).append(' ') } - append(fingerprint, 56, 64); + append(fingerprint, 56, 64) } } } diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/certification/CertifyCertificate.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/certification/CertifyCertificate.kt index a924e0f3..9499355c 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/certification/CertifyCertificate.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/certification/CertifyCertificate.kt @@ -4,6 +4,7 @@ package org.pgpainless.key.certification +import java.util.* import org.bouncycastle.openpgp.PGPException import org.bouncycastle.openpgp.PGPPublicKeyRing import org.bouncycastle.openpgp.PGPSecretKey @@ -22,28 +23,27 @@ import org.pgpainless.key.util.KeyRingUtils import org.pgpainless.signature.builder.ThirdPartyCertificationSignatureBuilder import org.pgpainless.signature.builder.ThirdPartyDirectKeySignatureBuilder import org.pgpainless.signature.subpackets.CertificationSubpackets -import java.util.* /** - * API for creating certifications and delegations (Signatures) on keys. - * This API can be used to sign another persons OpenPGP key. + * API for creating certifications and delegations (Signatures) on keys. This API can be used to + * sign another persons OpenPGP key. * - * A certification over a user-id is thereby used to attest, that the user believes that the user-id really belongs - * to the owner of the certificate. - * A delegation over a key can be used to delegate trust by marking the certificate as a trusted introducer. + * A certification over a user-id is thereby used to attest, that the user believes that the user-id + * really belongs to the owner of the certificate. A delegation over a key can be used to delegate + * trust by marking the certificate as a trusted introducer. */ class CertifyCertificate { /** - * Create a certification over a User-Id. - * By default, this method will use [CertificationType.GENERIC] to create the signature. + * Create a certification over a User-Id. By default, this method will use + * [CertificationType.GENERIC] to create the signature. * * @param userId user-id to certify * @param certificate certificate * @return API */ fun userIdOnCertificate(userId: String, certificate: PGPPublicKeyRing): CertificationOnUserId = - userIdOnCertificate(userId, certificate, CertificationType.GENERIC) + userIdOnCertificate(userId, certificate, CertificationType.GENERIC) /** * Create a certification of the given [CertificationType] over a User-Id. @@ -53,36 +53,40 @@ class CertifyCertificate { * @param certificationType type of signature * @return API */ - fun userIdOnCertificate(userId: String, certificate: PGPPublicKeyRing, certificationType: CertificationType) = - CertificationOnUserId(userId, certificate, certificationType) + fun userIdOnCertificate( + userId: String, + certificate: PGPPublicKeyRing, + certificationType: CertificationType + ) = CertificationOnUserId(userId, certificate, certificationType) /** - * Create a delegation (direct key signature) over a certificate. - * This can be used to mark a certificate as a trusted introducer - * (see [certificate] method with [Trustworthiness] argument). + * Create a delegation (direct key signature) over a certificate. This can be used to mark a + * certificate as a trusted introducer (see [certificate] method with [Trustworthiness] + * argument). * * @param certificate certificate * @return API */ fun certificate(certificate: PGPPublicKeyRing): DelegationOnCertificate = - certificate(certificate, null) + certificate(certificate, null) /** - * Create a delegation (direct key signature) containing a [org.bouncycastle.bcpg.sig.TrustSignature] packet - * over a certificate. - * This can be used to mark a certificate as a trusted introducer. + * Create a delegation (direct key signature) containing a + * [org.bouncycastle.bcpg.sig.TrustSignature] packet over a certificate. This can be used to + * mark a certificate as a trusted introducer. * * @param certificate certificate * @param trustworthiness trustworthiness of the certificate * @return API */ fun certificate(certificate: PGPPublicKeyRing, trustworthiness: Trustworthiness?) = - DelegationOnCertificate(certificate, trustworthiness) + DelegationOnCertificate(certificate, trustworthiness) class CertificationOnUserId( - val userId: String, - val certificate: PGPPublicKeyRing, - val certificationType: CertificationType) { + val userId: String, + val certificate: PGPPublicKeyRing, + val certificationType: CertificationType + ) { /** * Create the certification using the given key. @@ -92,11 +96,14 @@ class CertifyCertificate { * @return API * @throws PGPException in case of an OpenPGP related error */ - fun withKey(certificationKey: PGPSecretKeyRing, - protector: SecretKeyRingProtector): CertificationOnUserIdWithSubpackets { + fun withKey( + certificationKey: PGPSecretKeyRing, + protector: SecretKeyRingProtector + ): CertificationOnUserIdWithSubpackets { val secretKey = getCertifyingSecretKey(certificationKey) - val sigBuilder = ThirdPartyCertificationSignatureBuilder( + val sigBuilder = + ThirdPartyCertificationSignatureBuilder( certificationType.asSignatureType(), secretKey, protector) return CertificationOnUserIdWithSubpackets(certificate, userId, sigBuilder) @@ -104,9 +111,9 @@ class CertifyCertificate { } class CertificationOnUserIdWithSubpackets( - val certificate: PGPPublicKeyRing, - val userId: String, - val sigBuilder: ThirdPartyCertificationSignatureBuilder + val certificate: PGPPublicKeyRing, + val userId: String, + val sigBuilder: ThirdPartyCertificationSignatureBuilder ) { /** @@ -116,7 +123,9 @@ class CertifyCertificate { * @return result * @throws PGPException in case of an OpenPGP related error */ - fun buildWithSubpackets(subpacketCallback: CertificationSubpackets.Callback): CertificationResult { + fun buildWithSubpackets( + subpacketCallback: CertificationSubpackets.Callback + ): CertificationResult { sigBuilder.applyCallback(subpacketCallback) return build() } @@ -129,14 +138,16 @@ class CertifyCertificate { */ fun build(): CertificationResult { val signature = sigBuilder.build(certificate, userId) - val certifiedCertificate = KeyRingUtils.injectCertification(certificate, userId, signature) + val certifiedCertificate = + KeyRingUtils.injectCertification(certificate, userId, signature) return CertificationResult(certifiedCertificate, signature) } } class DelegationOnCertificate( - val certificate: PGPPublicKeyRing, - val trustworthiness: Trustworthiness?) { + val certificate: PGPPublicKeyRing, + val trustworthiness: Trustworthiness? + ) { /** * Build the delegation using the given certification key. @@ -146,20 +157,24 @@ class CertifyCertificate { * @return API * @throws PGPException in case of an OpenPGP related error */ - fun withKey(certificationKey: PGPSecretKeyRing, - protector: SecretKeyRingProtector): DelegationOnCertificateWithSubpackets { + fun withKey( + certificationKey: PGPSecretKeyRing, + protector: SecretKeyRingProtector + ): DelegationOnCertificateWithSubpackets { val secretKey = getCertifyingSecretKey(certificationKey) val sigBuilder = ThirdPartyDirectKeySignatureBuilder(secretKey, protector) if (trustworthiness != null) { - sigBuilder.hashedSubpackets.setTrust(true, trustworthiness.depth, trustworthiness.amount) + sigBuilder.hashedSubpackets.setTrust( + true, trustworthiness.depth, trustworthiness.amount) } return DelegationOnCertificateWithSubpackets(certificate, sigBuilder) } } class DelegationOnCertificateWithSubpackets( - val certificate: PGPPublicKeyRing, - val sigBuilder: ThirdPartyDirectKeySignatureBuilder) { + val certificate: PGPPublicKeyRing, + val sigBuilder: ThirdPartyDirectKeySignatureBuilder + ) { /** * Apply the given signature subpackets and build the delegation signature. @@ -168,7 +183,9 @@ class CertifyCertificate { * @return result * @throws PGPException in case of an OpenPGP related error */ - fun buildWithSubpackets(subpacketsCallback: CertificationSubpackets.Callback): CertificationResult { + fun buildWithSubpackets( + subpacketsCallback: CertificationSubpackets.Callback + ): CertificationResult { sigBuilder.applyCallback(subpacketsCallback) return build() } @@ -182,7 +199,8 @@ class CertifyCertificate { fun build(): CertificationResult { val delegatedKey = certificate.publicKey val delegation = sigBuilder.build(delegatedKey) - val delegatedCertificate = KeyRingUtils.injectCertification(certificate, delegatedKey, delegation) + val delegatedCertificate = + KeyRingUtils.injectCertification(certificate, delegatedKey, delegation) return CertificationResult(delegatedCertificate, delegation) } } @@ -194,8 +212,9 @@ class CertifyCertificate { * @param certification the newly created signature */ data class CertificationResult( - val certifiedCertificate: PGPPublicKeyRing, - val certification: PGPSignature) + val certifiedCertificate: PGPPublicKeyRing, + val certification: PGPSignature + ) companion object { @JvmStatic @@ -205,9 +224,7 @@ class CertifyCertificate { val fingerprint = info.fingerprint val certificationPubKey = info.getPublicKey(fingerprint) - requireNotNull(certificationPubKey) { - "Primary key cannot be null." - } + requireNotNull(certificationPubKey) { "Primary key cannot be null." } if (!info.isKeyValidlyBound(certificationPubKey.keyID)) { throw RevokedKeyException(fingerprint) } @@ -222,7 +239,7 @@ class CertifyCertificate { } return certificationKey.getSecretKey(certificationPubKey.keyID) - ?: throw MissingSecretKeyException(fingerprint, certificationPubKey.keyID) + ?: throw MissingSecretKeyException(fingerprint, certificationPubKey.keyID) } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/collection/PGPKeyRingCollection.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/collection/PGPKeyRingCollection.kt index 63151a4c..f69d4a08 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/collection/PGPKeyRingCollection.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/collection/PGPKeyRingCollection.kt @@ -4,32 +4,38 @@ package org.pgpainless.key.collection +import java.io.InputStream import org.bouncycastle.openpgp.* import org.pgpainless.implementation.ImplementationFactory import org.pgpainless.util.ArmorUtils -import java.io.InputStream /** - * This class describes a logic of handling a collection of different [PGPKeyRing]. The logic was inspired by - * [PGPSecretKeyRingCollection] and [PGPPublicKeyRingCollection]. + * This class describes a logic of handling a collection of different [PGPKeyRing]. The logic was + * inspired by [PGPSecretKeyRingCollection] and [PGPPublicKeyRingCollection]. */ class PGPKeyRingCollection( - val pgpSecretKeyRingCollection: PGPSecretKeyRingCollection, - val pgpPublicKeyRingCollection: PGPPublicKeyRingCollection + val pgpSecretKeyRingCollection: PGPSecretKeyRingCollection, + val pgpPublicKeyRingCollection: PGPPublicKeyRingCollection ) { - constructor(encoding: ByteArray, isSilent: Boolean): this(encoding.inputStream(), isSilent) + constructor(encoding: ByteArray, isSilent: Boolean) : this(encoding.inputStream(), isSilent) - constructor(inputStream: InputStream, isSilent: Boolean): this(parse(inputStream, isSilent)) + constructor(inputStream: InputStream, isSilent: Boolean) : this(parse(inputStream, isSilent)) - constructor(collection: Collection, isSilent: Boolean): this(segment(collection, isSilent)) + constructor( + collection: Collection, + isSilent: Boolean + ) : this(segment(collection, isSilent)) - private constructor(arguments: Pair): this(arguments.first, arguments.second) + private constructor( + arguments: Pair + ) : this(arguments.first, arguments.second) /** * The number of rings in this collection. * - * @return total size of [PGPSecretKeyRingCollection] plus [PGPPublicKeyRingCollection] in this collection + * @return total size of [PGPSecretKeyRingCollection] plus [PGPPublicKeyRingCollection] in this + * collection */ val size: Int get() = pgpSecretKeyRingCollection.size() + pgpPublicKeyRingCollection.size() @@ -41,11 +47,16 @@ class PGPKeyRingCollection( companion object { @JvmStatic - private fun parse(inputStream: InputStream, isSilent: Boolean): Pair { + private fun parse( + inputStream: InputStream, + isSilent: Boolean + ): Pair { val secretKeyRings = mutableListOf() val certificates = mutableListOf() // Double getDecoderStream because of #96 - val objectFactory = ImplementationFactory.getInstance().getPGPObjectFactory(ArmorUtils.getDecoderStream(inputStream)) + val objectFactory = + ImplementationFactory.getInstance() + .getPGPObjectFactory(ArmorUtils.getDecoderStream(inputStream)) for (obj in objectFactory) { if (obj == null) { @@ -68,16 +79,21 @@ class PGPKeyRingCollection( } if (!isSilent) { - throw PGPException("${obj.javaClass.name} found where ${PGPSecretKeyRing::class.java.simpleName}" + + throw PGPException( + "${obj.javaClass.name} found where ${PGPSecretKeyRing::class.java.simpleName}" + " or ${PGPPublicKeyRing::class.java.simpleName} expected") } } - return PGPSecretKeyRingCollection(secretKeyRings) to PGPPublicKeyRingCollection(certificates) + return PGPSecretKeyRingCollection(secretKeyRings) to + PGPPublicKeyRingCollection(certificates) } @JvmStatic - private fun segment(collection: Collection, isSilent: Boolean): Pair { + private fun segment( + collection: Collection, + isSilent: Boolean + ): Pair { val secretKeyRings = mutableListOf() val certificates = mutableListOf() @@ -87,12 +103,14 @@ class PGPKeyRingCollection( } else if (keyRing is PGPPublicKeyRing) { certificates.add(keyRing) } else if (!isSilent) { - throw PGPException("${keyRing.javaClass.name} found where ${PGPSecretKeyRing::class.java.simpleName}" + + throw PGPException( + "${keyRing.javaClass.name} found where ${PGPSecretKeyRing::class.java.simpleName}" + " or ${PGPPublicKeyRing::class.java.simpleName} expected") } } - return PGPSecretKeyRingCollection(secretKeyRings) to PGPPublicKeyRingCollection(certificates) + return PGPSecretKeyRingCollection(secretKeyRings) to + PGPPublicKeyRingCollection(certificates) } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/KeyRingBuilder.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/KeyRingBuilder.kt index f194638b..8404b652 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/KeyRingBuilder.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/KeyRingBuilder.kt @@ -4,6 +4,9 @@ package org.pgpainless.key.generation +import java.io.IOException +import java.security.KeyPairGenerator +import java.util.* import org.bouncycastle.extensions.unlock import org.bouncycastle.openpgp.* import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor @@ -21,10 +24,6 @@ import org.pgpainless.signature.subpackets.SelfSignatureSubpackets import org.pgpainless.signature.subpackets.SignatureSubpackets import org.pgpainless.signature.subpackets.SignatureSubpacketsHelper import org.pgpainless.util.Passphrase -import java.io.IOException -import java.security.KeyPairGenerator -import java.util.* - class KeyRingBuilder : KeyRingBuilderInterface { @@ -49,19 +48,19 @@ class KeyRingBuilder : KeyRingBuilderInterface { userIds[userId.toString().trim()] = null } - override fun addUserId(userId: ByteArray): KeyRingBuilder = addUserId(Strings.fromUTF8ByteArray(userId)) + override fun addUserId(userId: ByteArray): KeyRingBuilder = + addUserId(Strings.fromUTF8ByteArray(userId)) override fun setExpirationDate(expirationDate: Date?): KeyRingBuilder = apply { if (expirationDate == null) { this.expirationDate = null return@apply } - this.expirationDate = expirationDate.let { - require(Date() < expirationDate) { - "Expiration date must be in the future." + this.expirationDate = + expirationDate.let { + require(Date() < expirationDate) { "Expiration date must be in the future." } + expirationDate } - expirationDate - } } override fun setPassphrase(passphrase: Passphrase): KeyRingBuilder = apply { @@ -85,17 +84,14 @@ class KeyRingBuilder : KeyRingBuilderInterface { private fun keyIsCertificationCapable(keySpec: KeySpec) = keySpec.keyType.canCertify override fun build(): PGPSecretKeyRing { - val keyFingerprintCalculator = ImplementationFactory.getInstance() - .v4FingerprintCalculator + val keyFingerprintCalculator = ImplementationFactory.getInstance().v4FingerprintCalculator val secretKeyEncryptor = buildSecretKeyEncryptor(keyFingerprintCalculator) val secretKeyDecryptor = buildSecretKeyDecryptor() passphrase.clear() // Passphrase was used above, so we can get rid of it // generate primary key - requireNotNull(primaryKeySpec) { - "Primary Key spec required." - } + requireNotNull(primaryKeySpec) { "Primary Key spec required." } val certKey = generateKeyPair(primaryKeySpec!!) val signer = buildContentSigner(certKey) val signatureGenerator = PGPSignatureGenerator(signer) @@ -110,16 +106,28 @@ class KeyRingBuilder : KeyRingBuilderInterface { val generator = PGPSignatureSubpacketGenerator() SignatureSubpacketsHelper.applyTo(hashedSubPacketGenerator, generator) val hashedSubPackets = generator.generate() - val ringGenerator = if (userIds.isEmpty()) { - PGPKeyRingGenerator(certKey, keyFingerprintCalculator, hashedSubPackets, null, signer, + val ringGenerator = + if (userIds.isEmpty()) { + PGPKeyRingGenerator( + certKey, + keyFingerprintCalculator, + hashedSubPackets, + null, + signer, secretKeyEncryptor) - } else { - userIds.keys.first().let { primaryUserId -> - PGPKeyRingGenerator(SignatureType.POSITIVE_CERTIFICATION.code, certKey, primaryUserId, - keyFingerprintCalculator, hashedSubPackets, null, signer, + } else { + userIds.keys.first().let { primaryUserId -> + PGPKeyRingGenerator( + SignatureType.POSITIVE_CERTIFICATION.code, + certKey, + primaryUserId, + keyFingerprintCalculator, + hashedSubPackets, + null, + signer, secretKeyEncryptor) + } } - } addSubKeys(certKey, ringGenerator) @@ -138,20 +146,26 @@ class KeyRingBuilder : KeyRingBuilderInterface { val additionalUserId = userIdIterator.next() val userIdString = additionalUserId.key val callback = additionalUserId.value - val subpackets = if (callback == null) { - hashedSubPacketGenerator.also { it.setPrimaryUserId(null) } - } else { - SignatureSubpackets.createHashedSubpackets(primaryPubKey).also { callback.modifyHashedSubpackets(it) } - } + val subpackets = + if (callback == null) { + hashedSubPacketGenerator.also { it.setPrimaryUserId(null) } + } else { + SignatureSubpackets.createHashedSubpackets(primaryPubKey).also { + callback.modifyHashedSubpackets(it) + } + } signatureGenerator.init(SignatureType.POSITIVE_CERTIFICATION.code, privateKey) - signatureGenerator.setHashedSubpackets( - SignatureSubpacketsHelper.toVector(subpackets)) - val additionalUserIdSignature = signatureGenerator.generateCertification(userIdString, primaryPubKey) - primaryPubKey = PGPPublicKey.addCertification(primaryPubKey, userIdString, additionalUserIdSignature) + signatureGenerator.setHashedSubpackets(SignatureSubpacketsHelper.toVector(subpackets)) + val additionalUserIdSignature = + signatureGenerator.generateCertification(userIdString, primaryPubKey) + primaryPubKey = + PGPPublicKey.addCertification( + primaryPubKey, userIdString, additionalUserIdSignature) } // Reassemble secret key ring with modified primary key - val primarySecretKey = PGPSecretKey( + val primarySecretKey = + PGPSecretKey( privateKey, primaryPubKey, keyFingerprintCalculator, true, secretKeyEncryptor) val secretKeyList = mutableListOf(primarySecretKey) while (secretKeys.hasNext()) { @@ -168,25 +182,34 @@ class KeyRingBuilder : KeyRingBuilderInterface { } else { var hashedSubpackets = subKeySpec.subpackets try { - hashedSubpackets = addPrimaryKeyBindingSignatureIfNecessary(primaryKey, subKey, hashedSubpackets) + hashedSubpackets = + addPrimaryKeyBindingSignatureIfNecessary( + primaryKey, subKey, hashedSubpackets) } catch (e: IOException) { - throw PGPException("Exception while adding primary key binding signature to signing subkey.", e) + throw PGPException( + "Exception while adding primary key binding signature to signing subkey.", + e) } ringGenerator.addSubKey(subKey, hashedSubpackets, null) } } } - private fun addPrimaryKeyBindingSignatureIfNecessary(primaryKey: PGPKeyPair, subKey: PGPKeyPair, hashedSubpackets: PGPSignatureSubpacketVector): PGPSignatureSubpacketVector { + private fun addPrimaryKeyBindingSignatureIfNecessary( + primaryKey: PGPKeyPair, + subKey: PGPKeyPair, + hashedSubpackets: PGPSignatureSubpacketVector + ): PGPSignatureSubpacketVector { val keyFlagMask = hashedSubpackets.keyFlags if (!KeyFlag.hasKeyFlag(keyFlagMask, KeyFlag.SIGN_DATA) && - !KeyFlag.hasKeyFlag(keyFlagMask, KeyFlag.CERTIFY_OTHER)) { + !KeyFlag.hasKeyFlag(keyFlagMask, KeyFlag.CERTIFY_OTHER)) { return hashedSubpackets } val bindingSignatureGenerator = PGPSignatureGenerator(buildContentSigner(subKey)) bindingSignatureGenerator.init(SignatureType.PRIMARYKEY_BINDING.code, subKey.privateKey) - val primaryKeyBindingSig = bindingSignatureGenerator.generateCertification(primaryKey.publicKey, subKey.publicKey) + val primaryKeyBindingSig = + bindingSignatureGenerator.generateCertification(primaryKey.publicKey, subKey.publicKey) val subpacketGenerator = PGPSignatureSubpacketGenerator(hashedSubpackets) subpacketGenerator.addEmbeddedSignature(false, primaryKeyBindingSig) return subpacketGenerator.generate() @@ -194,25 +217,29 @@ class KeyRingBuilder : KeyRingBuilderInterface { private fun buildContentSigner(certKey: PGPKeyPair): PGPContentSignerBuilder { val hashAlgorithm = PGPainless.getPolicy().signatureHashAlgorithmPolicy.defaultHashAlgorithm - return ImplementationFactory.getInstance().getPGPContentSignerBuilder( - certKey.publicKey.algorithm, hashAlgorithm.algorithmId) + return ImplementationFactory.getInstance() + .getPGPContentSignerBuilder(certKey.publicKey.algorithm, hashAlgorithm.algorithmId) } - private fun buildSecretKeyEncryptor(keyFingerprintCalculator: PGPDigestCalculator): PBESecretKeyEncryptor? { - val keyEncryptionAlgorithm = PGPainless.getPolicy().symmetricKeyEncryptionAlgorithmPolicy.defaultSymmetricKeyAlgorithm - check(passphrase.isValid) { - "Passphrase was cleared." - } - return if (passphrase.isEmpty) null else ImplementationFactory.getInstance() - .getPBESecretKeyEncryptor(keyEncryptionAlgorithm, keyFingerprintCalculator, passphrase) + private fun buildSecretKeyEncryptor( + keyFingerprintCalculator: PGPDigestCalculator + ): PBESecretKeyEncryptor? { + val keyEncryptionAlgorithm = + PGPainless.getPolicy() + .symmetricKeyEncryptionAlgorithmPolicy + .defaultSymmetricKeyAlgorithm + check(passphrase.isValid) { "Passphrase was cleared." } + return if (passphrase.isEmpty) null + else + ImplementationFactory.getInstance() + .getPBESecretKeyEncryptor( + keyEncryptionAlgorithm, keyFingerprintCalculator, passphrase) } private fun buildSecretKeyDecryptor(): PBESecretKeyDecryptor? { - check(passphrase.isValid) { - "Passphrase was cleared." - } - return if (passphrase.isEmpty) null else ImplementationFactory.getInstance() - .getPBESecretKeyDecryptor(passphrase) + check(passphrase.isValid) { "Passphrase was cleared." } + return if (passphrase.isEmpty) null + else ImplementationFactory.getInstance().getPBESecretKeyDecryptor(passphrase) } companion object { @@ -222,16 +249,16 @@ class KeyRingBuilder : KeyRingBuilderInterface { fun generateKeyPair(spec: KeySpec): PGPKeyPair { spec.keyType.let { type -> // Create raw Key Pair - val keyPair = KeyPairGenerator.getInstance(type.name, ProviderFactory.provider) - .also { it.initialize(type.algorithmSpec) } - .generateKeyPair() + val keyPair = + KeyPairGenerator.getInstance(type.name, ProviderFactory.provider) + .also { it.initialize(type.algorithmSpec) } + .generateKeyPair() val keyCreationDate = spec.keyCreationDate ?: Date() // Form PGP Key Pair return ImplementationFactory.getInstance() - .getPGPKeyPair(type.algorithm, keyPair, keyCreationDate) + .getPGPKeyPair(type.algorithm, keyPair, keyCreationDate) } } } - -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/KeyRingBuilderInterface.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/KeyRingBuilderInterface.kt index 0ed301fe..ecc818b6 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/KeyRingBuilderInterface.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/KeyRingBuilderInterface.kt @@ -4,12 +4,12 @@ package org.pgpainless.key.generation -import org.bouncycastle.openpgp.PGPException -import org.bouncycastle.openpgp.PGPSecretKeyRing -import org.pgpainless.util.Passphrase import java.security.InvalidAlgorithmParameterException import java.security.NoSuchAlgorithmException import java.util.* +import org.bouncycastle.openpgp.PGPException +import org.bouncycastle.openpgp.PGPSecretKeyRing +import org.pgpainless.util.Passphrase interface KeyRingBuilderInterface> { @@ -29,6 +29,9 @@ interface KeyRingBuilderInterface> { fun setPassphrase(passphrase: Passphrase): B - @Throws(NoSuchAlgorithmException::class, PGPException::class, InvalidAlgorithmParameterException::class) + @Throws( + NoSuchAlgorithmException::class, + PGPException::class, + InvalidAlgorithmParameterException::class) fun build(): PGPSecretKeyRing -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/KeyRingTemplates.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/KeyRingTemplates.kt index 82215971..5e9fa7fd 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/KeyRingTemplates.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/KeyRingTemplates.kt @@ -17,8 +17,8 @@ import org.pgpainless.util.Passphrase class KeyRingTemplates { /** - * Generate an RSA OpenPGP key consisting of an RSA primary key used for certification, - * a dedicated RSA subkey used for signing and a third RSA subkey used for encryption. + * Generate an RSA OpenPGP key consisting of an RSA primary key used for certification, a + * dedicated RSA subkey used for signing and a third RSA subkey used for encryption. * * @param userId userId or null * @param length length of the RSA keys @@ -26,160 +26,187 @@ class KeyRingTemplates { * @return key */ @JvmOverloads - fun rsaKeyRing(userId: CharSequence?, - length: RsaLength, - passphrase: Passphrase = Passphrase.emptyPassphrase() - ): PGPSecretKeyRing = buildKeyRing().apply { - setPrimaryKey(getBuilder(KeyType.RSA(length), KeyFlag.CERTIFY_OTHER)) - addSubkey(getBuilder(KeyType.RSA(length), KeyFlag.SIGN_DATA)) - addSubkey(getBuilder(KeyType.RSA(length), KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE)) - setPassphrase(passphrase) - if (userId != null) { - addUserId(userId) - } - }.build() + fun rsaKeyRing( + userId: CharSequence?, + length: RsaLength, + passphrase: Passphrase = Passphrase.emptyPassphrase() + ): PGPSecretKeyRing = + buildKeyRing() + .apply { + setPrimaryKey(getBuilder(KeyType.RSA(length), KeyFlag.CERTIFY_OTHER)) + addSubkey(getBuilder(KeyType.RSA(length), KeyFlag.SIGN_DATA)) + addSubkey( + getBuilder(KeyType.RSA(length), KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE)) + setPassphrase(passphrase) + if (userId != null) { + addUserId(userId) + } + } + .build() /** - * Generate an RSA OpenPGP key consisting of an RSA primary key used for certification, - * a dedicated RSA subkey used for signing and a third RSA subkey used for encryption. + * Generate an RSA OpenPGP key consisting of an RSA primary key used for certification, a + * dedicated RSA subkey used for signing and a third RSA subkey used for encryption. * * @param userId userId or null * @param length length of the RSA keys - * @param password passphrase to encrypt the key with. Can be null or blank for unencrypted keys. + * @param password passphrase to encrypt the key with. Can be null or blank for unencrypted + * keys. * @return key */ - fun rsaKeyRing(userId: CharSequence?, - length: RsaLength, - password: String? - ): PGPSecretKeyRing = password.let { - if (it.isNullOrBlank()) { - rsaKeyRing(userId, length, Passphrase.emptyPassphrase()) - } else { - rsaKeyRing(userId, length, Passphrase.fromPassword(it)) + fun rsaKeyRing(userId: CharSequence?, length: RsaLength, password: String?): PGPSecretKeyRing = + password.let { + if (it.isNullOrBlank()) { + rsaKeyRing(userId, length, Passphrase.emptyPassphrase()) + } else { + rsaKeyRing(userId, length, Passphrase.fromPassword(it)) + } } - } /** - * Creates a simple RSA KeyPair of length {@code length} with user-id {@code userId}. - * The KeyPair consists of a single RSA master key which is used for signing, encryption and certification. + * Creates a simple RSA KeyPair of length {@code length} with user-id {@code userId}. The + * KeyPair consists of a single RSA master key which is used for signing, encryption and + * certification. * * @param userId user id. * @param length length in bits. * @param password Password of the key. Can be empty for unencrypted keys. - * * @return {@link PGPSecretKeyRing} containing the KeyPair. */ @JvmOverloads - fun simpleRsaKeyRing(userId: CharSequence?, - length: RsaLength, - passphrase: Passphrase = Passphrase.emptyPassphrase() - ): PGPSecretKeyRing = buildKeyRing().apply { - setPrimaryKey(getBuilder(KeyType.RSA(length), KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA, KeyFlag.ENCRYPT_COMMS)) - setPassphrase(passphrase) - if (userId != null) { - addUserId(userId.toString()) - } - }.build() + fun simpleRsaKeyRing( + userId: CharSequence?, + length: RsaLength, + passphrase: Passphrase = Passphrase.emptyPassphrase() + ): PGPSecretKeyRing = + buildKeyRing() + .apply { + setPrimaryKey( + getBuilder( + KeyType.RSA(length), + KeyFlag.CERTIFY_OTHER, + KeyFlag.SIGN_DATA, + KeyFlag.ENCRYPT_COMMS)) + setPassphrase(passphrase) + if (userId != null) { + addUserId(userId.toString()) + } + } + .build() /** - * Creates a simple RSA KeyPair of length {@code length} with user-id {@code userId}. - * The KeyPair consists of a single RSA master key which is used for signing, encryption and certification. + * Creates a simple RSA KeyPair of length {@code length} with user-id {@code userId}. The + * KeyPair consists of a single RSA master key which is used for signing, encryption and + * certification. * * @param userId user id. * @param length length in bits. * @param password Password of the key. Can be null or blank for unencrypted keys. - * * @return {@link PGPSecretKeyRing} containing the KeyPair. */ - fun simpleRsaKeyRing(userId: CharSequence?, - length: RsaLength, - password: String? - ) = password.let { - if (it.isNullOrBlank()) { - simpleRsaKeyRing(userId, length, Passphrase.emptyPassphrase()) - } else { - simpleRsaKeyRing(userId, length, Passphrase.fromPassword(it)) + fun simpleRsaKeyRing(userId: CharSequence?, length: RsaLength, password: String?) = + password.let { + if (it.isNullOrBlank()) { + simpleRsaKeyRing(userId, length, Passphrase.emptyPassphrase()) + } else { + simpleRsaKeyRing(userId, length, Passphrase.fromPassword(it)) + } } - } /** - * Creates a key ring consisting of an ed25519 EdDSA primary key and a X25519 XDH subkey. - * The EdDSA primary key is used for signing messages and certifying the sub key. - * The XDH subkey is used for encryption and decryption of messages. + * Creates a key ring consisting of an ed25519 EdDSA primary key and a X25519 XDH subkey. The + * EdDSA primary key is used for signing messages and certifying the sub key. The XDH subkey is + * used for encryption and decryption of messages. * * @param userId user-id * @param passphrase Password of the private key. Can be empty for an unencrypted key. - * * @return {@link PGPSecretKeyRing} containing the key pairs. */ @JvmOverloads - fun simpleEcKeyRing(userId: CharSequence?, - passphrase: Passphrase = Passphrase.emptyPassphrase() - ): PGPSecretKeyRing = buildKeyRing().apply { - setPrimaryKey(getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA)) - addSubkey(getBuilder(KeyType.XDH(XDHSpec._X25519), KeyFlag.ENCRYPT_STORAGE, KeyFlag.ENCRYPT_COMMS)) - setPassphrase(passphrase) - if (userId != null) { - addUserId(userId.toString()) - } - }.build() + fun simpleEcKeyRing( + userId: CharSequence?, + passphrase: Passphrase = Passphrase.emptyPassphrase() + ): PGPSecretKeyRing = + buildKeyRing() + .apply { + setPrimaryKey( + getBuilder( + KeyType.EDDSA(EdDSACurve._Ed25519), + KeyFlag.CERTIFY_OTHER, + KeyFlag.SIGN_DATA)) + addSubkey( + getBuilder( + KeyType.XDH(XDHSpec._X25519), + KeyFlag.ENCRYPT_STORAGE, + KeyFlag.ENCRYPT_COMMS)) + setPassphrase(passphrase) + if (userId != null) { + addUserId(userId.toString()) + } + } + .build() /** - * Creates a key ring consisting of an ed25519 EdDSA primary key and a X25519 XDH subkey. - * The EdDSA primary key is used for signing messages and certifying the sub key. - * The XDH subkey is used for encryption and decryption of messages. + * Creates a key ring consisting of an ed25519 EdDSA primary key and a X25519 XDH subkey. The + * EdDSA primary key is used for signing messages and certifying the sub key. The XDH subkey is + * used for encryption and decryption of messages. * * @param userId user-id * @param passphrase Password of the private key. Can be null or blank for an unencrypted key. - * * @return {@link PGPSecretKeyRing} containing the key pairs. */ - fun simpleEcKeyRing(userId: CharSequence?, - password: String? - ): PGPSecretKeyRing = password.let { - if (it.isNullOrBlank()) { - simpleEcKeyRing(userId, Passphrase.emptyPassphrase()) - } else { - simpleEcKeyRing(userId, Passphrase.fromPassword(it)) + fun simpleEcKeyRing(userId: CharSequence?, password: String?): PGPSecretKeyRing = + password.let { + if (it.isNullOrBlank()) { + simpleEcKeyRing(userId, Passphrase.emptyPassphrase()) + } else { + simpleEcKeyRing(userId, Passphrase.fromPassword(it)) + } } - } /** - * Generate a modern PGP key ring consisting of an ed25519 EdDSA primary key which is used to certify - * an X25519 XDH encryption subkey and an ed25519 EdDSA signing key. + * Generate a modern PGP key ring consisting of an ed25519 EdDSA primary key which is used to + * certify an X25519 XDH encryption subkey and an ed25519 EdDSA signing key. * * @param userId primary user id * @param passphrase passphrase for the private key. Can be empty for an unencrypted key. * @return key ring */ @JvmOverloads - fun modernKeyRing(userId: CharSequence?, - passphrase: Passphrase = Passphrase.emptyPassphrase() - ): PGPSecretKeyRing = buildKeyRing().apply { - setPrimaryKey(getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.CERTIFY_OTHER)) - addSubkey(getBuilder(KeyType.XDH(XDHSpec._X25519), KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE)) - addSubkey(getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.SIGN_DATA)) - setPassphrase(passphrase) - if (userId != null) { - addUserId(userId) - } - }.build() + fun modernKeyRing( + userId: CharSequence?, + passphrase: Passphrase = Passphrase.emptyPassphrase() + ): PGPSecretKeyRing = + buildKeyRing() + .apply { + setPrimaryKey(getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.CERTIFY_OTHER)) + addSubkey( + getBuilder( + KeyType.XDH(XDHSpec._X25519), + KeyFlag.ENCRYPT_COMMS, + KeyFlag.ENCRYPT_STORAGE)) + addSubkey(getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.SIGN_DATA)) + setPassphrase(passphrase) + if (userId != null) { + addUserId(userId) + } + } + .build() /** - * Generate a modern PGP key ring consisting of an ed25519 EdDSA primary key which is used to certify - * an X25519 XDH encryption subkey and an ed25519 EdDSA signing key. + * Generate a modern PGP key ring consisting of an ed25519 EdDSA primary key which is used to + * certify an X25519 XDH encryption subkey and an ed25519 EdDSA signing key. * * @param userId primary user id * @param password passphrase for the private key. Can be null or blank for an unencrypted key. * @return key ring */ - fun modernKeyRing(userId: CharSequence?, - password: String? - ): PGPSecretKeyRing = password.let { - if (it.isNullOrBlank()) { - modernKeyRing(userId, Passphrase.emptyPassphrase()) - } else { - modernKeyRing(userId, Passphrase.fromPassword(it)) + fun modernKeyRing(userId: CharSequence?, password: String?): PGPSecretKeyRing = + password.let { + if (it.isNullOrBlank()) { + modernKeyRing(userId, Passphrase.emptyPassphrase()) + } else { + modernKeyRing(userId, Passphrase.fromPassword(it)) + } } - } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/KeySpec.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/KeySpec.kt index fd0b8635..bc8d5599 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/KeySpec.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/KeySpec.kt @@ -4,18 +4,18 @@ package org.pgpainless.key.generation +import java.util.* import org.bouncycastle.openpgp.PGPSignatureSubpacketVector import org.pgpainless.algorithm.KeyFlag import org.pgpainless.key.generation.type.KeyType import org.pgpainless.signature.subpackets.SignatureSubpackets import org.pgpainless.signature.subpackets.SignatureSubpacketsHelper -import java.util.* data class KeySpec( - val keyType: KeyType, - val subpacketGenerator: SignatureSubpackets, - val isInheritedSubPackets: Boolean, - val keyCreationDate: Date + val keyType: KeyType, + val subpacketGenerator: SignatureSubpackets, + val isInheritedSubPackets: Boolean, + val keyCreationDate: Date ) { val subpackets: PGPSignatureSubpacketVector @@ -25,4 +25,4 @@ data class KeySpec( @JvmStatic fun getBuilder(type: KeyType, vararg flags: KeyFlag) = KeySpecBuilder(type, *flags) } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/KeySpecBuilder.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/KeySpecBuilder.kt index a430e796..03291f2d 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/KeySpecBuilder.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/KeySpecBuilder.kt @@ -4,41 +4,47 @@ package org.pgpainless.key.generation +import java.util.* import org.pgpainless.PGPainless import org.pgpainless.algorithm.* import org.pgpainless.key.generation.type.KeyType import org.pgpainless.signature.subpackets.SelfSignatureSubpackets import org.pgpainless.signature.subpackets.SignatureSubpackets import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil -import java.util.* -class KeySpecBuilder constructor( - private val type: KeyType, - private val keyFlags: List, +class KeySpecBuilder +constructor( + private val type: KeyType, + private val keyFlags: List, ) : KeySpecBuilderInterface { private val hashedSubpackets: SelfSignatureSubpackets = SignatureSubpackets() private val algorithmSuite: AlgorithmSuite = PGPainless.getPolicy().keyGenerationAlgorithmSuite - private var preferredCompressionAlgorithms: Set = algorithmSuite.compressionAlgorithms + private var preferredCompressionAlgorithms: Set = + algorithmSuite.compressionAlgorithms private var preferredHashAlgorithms: Set = algorithmSuite.hashAlgorithms - private var preferredSymmetricAlgorithms: Set = algorithmSuite.symmetricKeyAlgorithms + private var preferredSymmetricAlgorithms: Set = + algorithmSuite.symmetricKeyAlgorithms private var keyCreationDate = Date() - constructor(type: KeyType, vararg keyFlags: KeyFlag): this(type, listOf(*keyFlags)) + constructor(type: KeyType, vararg keyFlags: KeyFlag) : this(type, listOf(*keyFlags)) init { SignatureSubpacketsUtil.assureKeyCanCarryFlags(type, *keyFlags.toTypedArray()) } - override fun overridePreferredCompressionAlgorithms(vararg algorithms: CompressionAlgorithm): KeySpecBuilder = apply { - this.preferredCompressionAlgorithms = algorithms.toSet() - } + override fun overridePreferredCompressionAlgorithms( + vararg algorithms: CompressionAlgorithm + ): KeySpecBuilder = apply { this.preferredCompressionAlgorithms = algorithms.toSet() } - override fun overridePreferredHashAlgorithms(vararg algorithms: HashAlgorithm): KeySpecBuilder = apply { - this.preferredHashAlgorithms = algorithms.toSet() - } + override fun overridePreferredHashAlgorithms(vararg algorithms: HashAlgorithm): KeySpecBuilder = + apply { + this.preferredHashAlgorithms = algorithms.toSet() + } - override fun overridePreferredSymmetricKeyAlgorithms(vararg algorithms: SymmetricKeyAlgorithm): KeySpecBuilder = apply { + override fun overridePreferredSymmetricKeyAlgorithms( + vararg algorithms: SymmetricKeyAlgorithm + ): KeySpecBuilder = apply { require(!algorithms.contains(SymmetricKeyAlgorithm.NULL)) { "NULL (unencrypted) is an invalid symmetric key algorithm preference." } @@ -50,14 +56,14 @@ class KeySpecBuilder constructor( } override fun build(): KeySpec { - return hashedSubpackets.apply { - setKeyFlags(keyFlags) - setPreferredCompressionAlgorithms(preferredCompressionAlgorithms) - setPreferredHashAlgorithms(preferredHashAlgorithms) - setPreferredSymmetricKeyAlgorithms(preferredSymmetricAlgorithms) - setFeatures(Feature.MODIFICATION_DETECTION) - }.let { - KeySpec(type, hashedSubpackets as SignatureSubpackets, false, keyCreationDate) - } + return hashedSubpackets + .apply { + setKeyFlags(keyFlags) + setPreferredCompressionAlgorithms(preferredCompressionAlgorithms) + setPreferredHashAlgorithms(preferredHashAlgorithms) + setPreferredSymmetricKeyAlgorithms(preferredSymmetricAlgorithms) + setFeatures(Feature.MODIFICATION_DETECTION) + } + .let { KeySpec(type, hashedSubpackets as SignatureSubpackets, false, keyCreationDate) } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/KeySpecBuilderInterface.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/KeySpecBuilderInterface.kt index fcafb9c1..7fb767e4 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/KeySpecBuilderInterface.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/KeySpecBuilderInterface.kt @@ -4,20 +4,24 @@ package org.pgpainless.key.generation +import java.util.* import org.pgpainless.algorithm.CompressionAlgorithm import org.pgpainless.algorithm.HashAlgorithm import org.pgpainless.algorithm.SymmetricKeyAlgorithm -import java.util.* interface KeySpecBuilderInterface { - fun overridePreferredCompressionAlgorithms(vararg algorithms: CompressionAlgorithm): KeySpecBuilder + fun overridePreferredCompressionAlgorithms( + vararg algorithms: CompressionAlgorithm + ): KeySpecBuilder fun overridePreferredHashAlgorithms(vararg algorithms: HashAlgorithm): KeySpecBuilder - fun overridePreferredSymmetricKeyAlgorithms(vararg algorithms: SymmetricKeyAlgorithm): KeySpecBuilder + fun overridePreferredSymmetricKeyAlgorithms( + vararg algorithms: SymmetricKeyAlgorithm + ): KeySpecBuilder fun setKeyCreationDate(creationDate: Date): KeySpecBuilder fun build(): KeySpec -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/KeyLength.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/KeyLength.kt index 377fbb94..1ff63604 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/KeyLength.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/KeyLength.kt @@ -7,4 +7,4 @@ package org.pgpainless.key.generation.type interface KeyLength { val length: Int -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/KeyType.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/KeyType.kt index 105962b9..bc1497f9 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/KeyType.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/KeyType.kt @@ -4,6 +4,7 @@ package org.pgpainless.key.generation.type +import java.security.spec.AlgorithmParameterSpec import org.pgpainless.algorithm.PublicKeyAlgorithm import org.pgpainless.key.generation.type.ecc.EllipticCurve import org.pgpainless.key.generation.type.ecc.ecdh.ECDH @@ -14,7 +15,6 @@ import org.pgpainless.key.generation.type.rsa.RSA import org.pgpainless.key.generation.type.rsa.RsaLength import org.pgpainless.key.generation.type.xdh.XDH import org.pgpainless.key.generation.type.xdh.XDHSpec -import java.security.spec.AlgorithmParameterSpec @Suppress("INAPPLICABLE_JVM_NAME") // https://youtrack.jetbrains.com/issue/KT-31420 interface KeyType { @@ -35,20 +35,22 @@ interface KeyType { /** * Return the strength of the key in bits. + * * @return strength of the key in bits */ val bitStrength: Int /** - * Return an implementation of {@link AlgorithmParameterSpec} that can be used to generate the key. + * Return an implementation of {@link AlgorithmParameterSpec} that can be used to generate the + * key. * * @return algorithm parameter spec */ val algorithmSpec: AlgorithmParameterSpec /** - * Return true if the key that is generated from this type is able to carry the SIGN_DATA key flag. - * See {@link org.pgpainless.algorithm.KeyFlag#SIGN_DATA}. + * Return true if the key that is generated from this type is able to carry the SIGN_DATA key + * flag. See {@link org.pgpainless.algorithm.KeyFlag#SIGN_DATA}. * * @return true if the key can sign. */ @@ -56,8 +58,8 @@ interface KeyType { @JvmName("canSign") get() = algorithm.signingCapable /** - * Return true if the key that is generated from this type is able to carry the CERTIFY_OTHER key flag. - * See {@link org.pgpainless.algorithm.KeyFlag#CERTIFY_OTHER}. + * Return true if the key that is generated from this type is able to carry the CERTIFY_OTHER + * key flag. See {@link org.pgpainless.algorithm.KeyFlag#CERTIFY_OTHER}. * * @return true if the key is able to certify other keys */ @@ -65,8 +67,8 @@ interface KeyType { @JvmName("canCertify") get() = canSign /** - * Return true if the key that is generated from this type is able to carry the AUTHENTICATION key flag. - * See {@link org.pgpainless.algorithm.KeyFlag#AUTHENTICATION}. + * Return true if the key that is generated from this type is able to carry the AUTHENTICATION + * key flag. See {@link org.pgpainless.algorithm.KeyFlag#AUTHENTICATION}. * * @return true if the key can be used for authentication purposes. */ @@ -74,8 +76,8 @@ interface KeyType { @JvmName("canAuthenticate") get() = canSign /** - * Return true if the key that is generated from this type is able to carry the ENCRYPT_COMMS key flag. - * See {@link org.pgpainless.algorithm.KeyFlag#ENCRYPT_COMMS}. + * Return true if the key that is generated from this type is able to carry the ENCRYPT_COMMS + * key flag. See {@link org.pgpainless.algorithm.KeyFlag#ENCRYPT_COMMS}. * * @return true if the key can encrypt communication */ @@ -83,8 +85,8 @@ interface KeyType { @JvmName("canEncryptCommunication") get() = algorithm.encryptionCapable /** - * Return true if the key that is generated from this type is able to carry the ENCRYPT_STORAGE key flag. - * See {@link org.pgpainless.algorithm.KeyFlag#ENCRYPT_STORAGE}. + * Return true if the key that is generated from this type is able to carry the ENCRYPT_STORAGE + * key flag. See {@link org.pgpainless.algorithm.KeyFlag#ENCRYPT_STORAGE}. * * @return true if the key can encrypt for storage */ @@ -92,19 +94,14 @@ interface KeyType { @JvmName("canEncryptStorage") get() = algorithm.encryptionCapable companion object { - @JvmStatic - fun RSA(length: RsaLength): RSA = RSA.withLength(length) + @JvmStatic fun RSA(length: RsaLength): RSA = RSA.withLength(length) - @JvmStatic - fun ECDH(curve: EllipticCurve): ECDH = ECDH.fromCurve(curve) + @JvmStatic fun ECDH(curve: EllipticCurve): ECDH = ECDH.fromCurve(curve) - @JvmStatic - fun ECDSA(curve: EllipticCurve): ECDSA = ECDSA.fromCurve(curve) + @JvmStatic fun ECDSA(curve: EllipticCurve): ECDSA = ECDSA.fromCurve(curve) - @JvmStatic - fun EDDSA(curve: EdDSACurve): EdDSA = EdDSA.fromCurve(curve) + @JvmStatic fun EDDSA(curve: EdDSACurve): EdDSA = EdDSA.fromCurve(curve) - @JvmStatic - fun XDH(curve: XDHSpec): XDH = XDH.fromSpec(curve) + @JvmStatic fun XDH(curve: XDHSpec): XDH = XDH.fromSpec(curve) } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/ecc/EllipticCurve.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/ecc/EllipticCurve.kt index 287df67f..d9b51cb3 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/ecc/EllipticCurve.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/ecc/EllipticCurve.kt @@ -4,19 +4,20 @@ package org.pgpainless.key.generation.type.ecc - /** * Elliptic curves for use with [org.pgpainless.key.generation.type.ecc.ecdh.ECDH] and - * [org.pgpainless.key.generation.type.ecc.ecdsa.ECDSA]. - * For curve25519 related curve definitions see [XDHSpec] and [org.pgpainless.key.generation.type.eddsa.EdDSACurve]. + * [org.pgpainless.key.generation.type.ecc.ecdsa.ECDSA]. For curve25519 related curve definitions + * see [XDHSpec] and [org.pgpainless.key.generation.type.eddsa.EdDSACurve]. */ -enum class EllipticCurve( - val curveName: String, - val bitStrength: Int -) { - _P256("prime256v1", 256), // prime256v1 is equivalent to P-256, see https://tools.ietf.org/search/rfc4492#page-32 - _P384("secp384r1", 384), // secp384r1 is equivalent to P-384, see https://tools.ietf.org/search/rfc4492#page-32 - _P521("secp521r1", 521), // secp521r1 is equivalent to P-521, see https://tools.ietf.org/search/rfc4492#page-32 +enum class EllipticCurve(val curveName: String, val bitStrength: Int) { + _P256("prime256v1", 256), // prime256v1 is equivalent to P-256, see + // https://tools.ietf.org/search/rfc4492#page-32 + _P384( + "secp384r1", + 384), // secp384r1 is equivalent to P-384, see https://tools.ietf.org/search/rfc4492#page-32 + _P521( + "secp521r1", + 521), // secp521r1 is equivalent to P-521, see https://tools.ietf.org/search/rfc4492#page-32 _SECP256K1("secp256k1", 256), _BRAINPOOLP256R1("brainpoolP256r1", 256), _BRAINPOOLP384R1("brainpoolP384r1", 384), @@ -24,4 +25,4 @@ enum class EllipticCurve( ; fun getName(): String = curveName -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/ecc/ecdh/ECDH.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/ecc/ecdh/ECDH.kt index 6bab2fcc..04e196e0 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/ecc/ecdh/ECDH.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/ecc/ecdh/ECDH.kt @@ -16,7 +16,6 @@ class ECDH private constructor(val curve: EllipticCurve) : KeyType { override val algorithmSpec = ECNamedCurveGenParameterSpec(curve.curveName) companion object { - @JvmStatic - fun fromCurve(curve: EllipticCurve) = ECDH(curve) + @JvmStatic fun fromCurve(curve: EllipticCurve) = ECDH(curve) } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/ecc/ecdsa/ECDSA.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/ecc/ecdsa/ECDSA.kt index b7a0b94f..1784b49d 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/ecc/ecdsa/ECDSA.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/ecc/ecdsa/ECDSA.kt @@ -16,7 +16,6 @@ class ECDSA private constructor(val curve: EllipticCurve) : KeyType { override val algorithmSpec = ECNamedCurveGenParameterSpec(curve.curveName) companion object { - @JvmStatic - fun fromCurve(curve: EllipticCurve) = ECDSA(curve) + @JvmStatic fun fromCurve(curve: EllipticCurve) = ECDSA(curve) } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/eddsa/EdDSA.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/eddsa/EdDSA.kt index d1e51a8e..6130328a 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/eddsa/EdDSA.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/eddsa/EdDSA.kt @@ -15,7 +15,6 @@ class EdDSA private constructor(val curve: EdDSACurve) : KeyType { override val algorithmSpec = ECNamedCurveGenParameterSpec(curve.curveName) companion object { - @JvmStatic - fun fromCurve(curve: EdDSACurve) = EdDSA(curve) + @JvmStatic fun fromCurve(curve: EdDSACurve) = EdDSA(curve) } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/eddsa/EdDSACurve.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/eddsa/EdDSACurve.kt index 52b6949b..943c8237 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/eddsa/EdDSACurve.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/eddsa/EdDSACurve.kt @@ -4,11 +4,9 @@ package org.pgpainless.key.generation.type.eddsa -enum class EdDSACurve( - val curveName: String, - val bitStrength: Int) { +enum class EdDSACurve(val curveName: String, val bitStrength: Int) { _Ed25519("ed25519", 256), ; fun getName() = curveName -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/elgamal/ElGamal.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/elgamal/ElGamal.kt index 6cfbc8a7..d925fc3d 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/elgamal/ElGamal.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/elgamal/ElGamal.kt @@ -22,7 +22,6 @@ class ElGamal private constructor(length: ElGamalLength) : KeyType { override val algorithmSpec = ElGamalParameterSpec(length.p, length.g) companion object { - @JvmStatic - fun withLength(length: ElGamalLength) = ElGamal(length) + @JvmStatic fun withLength(length: ElGamalLength) = ElGamal(length) } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/elgamal/ElGamalLength.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/elgamal/ElGamalLength.kt index 9eae195c..2d29b88d 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/elgamal/ElGamalLength.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/elgamal/ElGamalLength.kt @@ -4,61 +4,54 @@ package org.pgpainless.key.generation.type.elgamal -import org.pgpainless.key.generation.type.KeyLength import java.math.BigInteger +import org.pgpainless.key.generation.type.KeyLength /** * The following primes are taken from RFC-3526. * - * @see - * RFC-3526: More Modular Exponential (MODP) Diffie-Hellman groups for Internet Key Exchange (IKE) - * + * @see RFC-3526: More Modular Exponential (MODP) + * Diffie-Hellman groups for Internet Key Exchange (IKE) * @deprecated the use of ElGamal keys is no longer recommended. */ - @Deprecated("The use of ElGamal keys is no longer recommended.") -enum class ElGamalLength( - override val length: Int, - p: String, - g: String -) : KeyLength { +enum class ElGamalLength(override val length: Int, p: String, g: String) : KeyLength { - /** - * prime: 2^1536 - 2^1472 - 1 + 2^64 * { [2^1406 pi] + 741804 }. - * generator: 2 - */ - _1536(1536, "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF", "2"), + /** prime: 2^1536 - 2^1472 - 1 + 2^64 * { [2^1406 pi] + 741804 }. generator: 2 */ + _1536( + 1536, + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF", + "2"), - /** - * prime: 2^2048 - 2^1984 - 1 + 2^64 * { [2^1918 pi] + 124476 }. - * generator: 2 - */ - _2048(2048, "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", "2"), + /** prime: 2^2048 - 2^1984 - 1 + 2^64 * { [2^1918 pi] + 124476 }. generator: 2 */ + _2048( + 2048, + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", + "2"), - /** - * prime: 2^3072 - 2^3008 - 1 + 2^64 * { [2^2942 pi] + 1690314 }. - * generator: 2 - */ - _3072(3072, "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF", "2"), + /** prime: 2^3072 - 2^3008 - 1 + 2^64 * { [2^2942 pi] + 1690314 }. generator: 2 */ + _3072( + 3072, + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF", + "2"), - /** - * prime: 2^4096 - 2^4032 - 1 + 2^64 * { [2^3966 pi] + 240904 }. - * generator: 2 - */ - _4096(4096, "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF", "2"), + /** prime: 2^4096 - 2^4032 - 1 + 2^64 * { [2^3966 pi] + 240904 }. generator: 2 */ + _4096( + 4096, + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF", + "2"), - /** - * prime: 2^6144 - 2^6080 - 1 + 2^64 * { [2^6014 pi] + 929484 }. - * generator: 2 - */ - _6144(6144, "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DCC4024FFFFFFFFFFFFFFFF", "2"), + /** prime: 2^6144 - 2^6080 - 1 + 2^64 * { [2^6014 pi] + 929484 }. generator: 2 */ + _6144( + 6144, + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DCC4024FFFFFFFFFFFFFFFF", + "2"), - /** - * prime: 2^8192 - 2^8128 - 1 + 2^64 * { [2^8062 pi] + 4743158 }. - * generator: 2 - */ - _8192(8192, "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD922222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC50846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E7160C980DD98EDD3DFFFFFFFFFFFFFFFFF", "2") - ; + /** prime: 2^8192 - 2^8128 - 1 + 2^64 * { [2^8062 pi] + 4743158 }. generator: 2 */ + _8192( + 8192, + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD922222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC50846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E7160C980DD98EDD3DFFFFFFFFFFFFFFFFF", + "2"); val p: BigInteger val g: BigInteger @@ -67,6 +60,4 @@ enum class ElGamalLength( this.p = BigInteger(p, 16) this.g = BigInteger(g, 16) } - - -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/rsa/RSA.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/rsa/RSA.kt index 1f8c0509..39ddbbbb 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/rsa/RSA.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/rsa/RSA.kt @@ -4,14 +4,12 @@ package org.pgpainless.key.generation.type.rsa +import java.security.spec.RSAKeyGenParameterSpec import org.pgpainless.algorithm.PublicKeyAlgorithm import org.pgpainless.key.generation.type.KeyType -import java.security.spec.RSAKeyGenParameterSpec -/** - * Key type that specifies the RSA_GENERAL algorithm. - */ -class RSA private constructor(length: RsaLength): KeyType { +/** Key type that specifies the RSA_GENERAL algorithm. */ +class RSA private constructor(length: RsaLength) : KeyType { override val name = "RSA" override val algorithm = PublicKeyAlgorithm.RSA_GENERAL @@ -19,7 +17,6 @@ class RSA private constructor(length: RsaLength): KeyType { override val algorithmSpec = RSAKeyGenParameterSpec(length.length, RSAKeyGenParameterSpec.F4) companion object { - @JvmStatic - fun withLength(length: RsaLength) = RSA(length) + @JvmStatic fun withLength(length: RsaLength) = RSA(length) } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/rsa/RsaLength.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/rsa/RsaLength.kt index ae8bb804..7837a1f5 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/rsa/RsaLength.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/rsa/RsaLength.kt @@ -14,4 +14,4 @@ enum class RsaLength(override val length: Int) : KeyLength { _3072(3072), _4096(4096), _8192(8192) -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/xdh/XDH.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/xdh/XDH.kt index 06888237..8a95fc3b 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/xdh/XDH.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/xdh/XDH.kt @@ -8,14 +8,13 @@ import org.bouncycastle.jce.spec.ECNamedCurveGenParameterSpec import org.pgpainless.algorithm.PublicKeyAlgorithm import org.pgpainless.key.generation.type.KeyType -class XDH private constructor(spec: XDHSpec): KeyType { +class XDH private constructor(spec: XDHSpec) : KeyType { override val name = "XDH" override val algorithm = PublicKeyAlgorithm.ECDH override val bitStrength = spec.bitStrength override val algorithmSpec = ECNamedCurveGenParameterSpec(spec.algorithmName) companion object { - @JvmStatic - fun fromSpec(spec: XDHSpec) = XDH(spec) + @JvmStatic fun fromSpec(spec: XDHSpec) = XDH(spec) } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/xdh/XDHSpec.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/xdh/XDHSpec.kt index 9486365f..36fcfcaa 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/xdh/XDHSpec.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/type/xdh/XDHSpec.kt @@ -4,12 +4,9 @@ package org.pgpainless.key.generation.type.xdh -enum class XDHSpec( - val algorithmName: String, - val curveName: String, - val bitStrength: Int) { +enum class XDHSpec(val algorithmName: String, val curveName: String, val bitStrength: Int) { _X25519("X25519", "curve25519", 256), ; fun getName() = algorithmName -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/info/KeyAccessor.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/info/KeyAccessor.kt index f49a6ca4..a6891f0f 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/info/KeyAccessor.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/info/KeyAccessor.kt @@ -11,78 +11,79 @@ import org.pgpainless.algorithm.SymmetricKeyAlgorithm import org.pgpainless.key.SubkeyIdentifier import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil -abstract class KeyAccessor( - protected val info: KeyRingInfo, - protected val key: SubkeyIdentifier -) { +abstract class KeyAccessor(protected val info: KeyRingInfo, protected val key: SubkeyIdentifier) { /** - * Depending on the way we address the key (key-id or user-id), return the respective [PGPSignature] - * which contains the algorithm preferences we are going to use. + * Depending on the way we address the key (key-id or user-id), return the respective + * [PGPSignature] which contains the algorithm preferences we are going to use. + * *

- * If we address a key via its user-id, we want to rely on the algorithm preferences in the user-id certification, - * while we would instead rely on those in the direct-key signature if we'd address the key by key-id. + * If we address a key via its user-id, we want to rely on the algorithm preferences in the + * user-id certification, while we would instead rely on those in the direct-key signature if + * we'd address the key by key-id. * * @return signature */ abstract val signatureWithPreferences: PGPSignature - /** - * Preferred symmetric key encryption algorithms. - */ + /** Preferred symmetric key encryption algorithms. */ val preferredSymmetricKeyAlgorithms: Set - get() = SignatureSubpacketsUtil.parsePreferredSymmetricKeyAlgorithms(signatureWithPreferences) + get() = + SignatureSubpacketsUtil.parsePreferredSymmetricKeyAlgorithms(signatureWithPreferences) - /** - * Preferred hash algorithms. - */ + /** Preferred hash algorithms. */ val preferredHashAlgorithms: Set get() = SignatureSubpacketsUtil.parsePreferredHashAlgorithms(signatureWithPreferences) - /** - * Preferred compression algorithms. - */ + /** Preferred compression algorithms. */ val preferredCompressionAlgorithms: Set - get() = SignatureSubpacketsUtil.parsePreferredCompressionAlgorithms(signatureWithPreferences) + get() = + SignatureSubpacketsUtil.parsePreferredCompressionAlgorithms(signatureWithPreferences) /** - * Address the key via a user-id (e.g. `Alice `). - * In this case we are sourcing preferred algorithms from the user-id certification first. + * Address the key via a user-id (e.g. `Alice `). In this case we are + * sourcing preferred algorithms from the user-id certification first. */ - class ViaUserId(info: KeyRingInfo, key: SubkeyIdentifier, private val userId: CharSequence): KeyAccessor(info, key) { + class ViaUserId(info: KeyRingInfo, key: SubkeyIdentifier, private val userId: CharSequence) : + KeyAccessor(info, key) { override val signatureWithPreferences: PGPSignature - get() = checkNotNull(info.getLatestUserIdCertification(userId.toString())) { - "No valid user-id certification signature found for '$userId'." - } + get() = + checkNotNull(info.getLatestUserIdCertification(userId.toString())) { + "No valid user-id certification signature found for '$userId'." + } } /** - * Address the key via key-id. - * In this case we are sourcing preferred algorithms from the keys direct-key signature first. + * Address the key via key-id. In this case we are sourcing preferred algorithms from the keys + * direct-key signature first. */ class ViaKeyId(info: KeyRingInfo, key: SubkeyIdentifier) : KeyAccessor(info, key) { override val signatureWithPreferences: PGPSignature get() { - // If the key is located by Key ID, the algorithm of the primary User ID of the key provides the + // If the key is located by Key ID, the algorithm of the primary User ID of the key + // provides the // preferred symmetric algorithm. - info.primaryUserId?.let { - userId -> info.getLatestUserIdCertification(userId).let { if (it != null) return it } + info.primaryUserId?.let { userId -> + info.getLatestUserIdCertification(userId).let { if (it != null) return it } } - return checkNotNull(info.latestDirectKeySelfSignature) { "No valid signature found." } + return checkNotNull(info.latestDirectKeySelfSignature) { + "No valid signature found." + } } } - class SubKey(info: KeyRingInfo, key: SubkeyIdentifier): KeyAccessor(info, key) { + class SubKey(info: KeyRingInfo, key: SubkeyIdentifier) : KeyAccessor(info, key) { override val signatureWithPreferences: PGPSignature - get() = checkNotNull( + get() = + checkNotNull( if (key.isPrimaryKey) { - info.latestDirectKeySelfSignature ?: - info.primaryUserId?.let { info.getLatestUserIdCertification(it) } + info.latestDirectKeySelfSignature + ?: info.primaryUserId?.let { info.getLatestUserIdCertification(it) } } else { info.getCurrentSubkeyBindingSignature(key.subkeyId) + }) { + "No valid signature found." } - ) { "No valid signature found." } - } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/info/KeyInfo.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/info/KeyInfo.kt index 652cf22d..f510af3e 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/info/KeyInfo.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/info/KeyInfo.kt @@ -12,64 +12,70 @@ import org.bouncycastle.openpgp.PGPPublicKey import org.bouncycastle.openpgp.PGPSecretKey @Deprecated("Deprecated in favor of extension functions to PGPSecretKey and PGPPublicKey.") -class KeyInfo private constructor( - val secretKey: PGPSecretKey?, - val publicKey: PGPPublicKey) { +class KeyInfo private constructor(val secretKey: PGPSecretKey?, val publicKey: PGPPublicKey) { - constructor(secretKey: PGPSecretKey): this(secretKey, secretKey.publicKey) - constructor(publicKey: PGPPublicKey): this(null, publicKey) + constructor(secretKey: PGPSecretKey) : this(secretKey, secretKey.publicKey) + + constructor(publicKey: PGPPublicKey) : this(null, publicKey) /** - * Return the name of the elliptic curve used by this key, or throw an [IllegalArgumentException] if the key - * is not based on elliptic curves, or on an unknown curve. + * Return the name of the elliptic curve used by this key, or throw an + * [IllegalArgumentException] if the key is not based on elliptic curves, or on an unknown + * curve. */ - @Deprecated("Deprecated in favor of calling getCurveName() on the PGPPublicKey itself.", - ReplaceWith("publicKey.getCurveName()")) + @Deprecated( + "Deprecated in favor of calling getCurveName() on the PGPPublicKey itself.", + ReplaceWith("publicKey.getCurveName()")) val curveName: String get() = publicKey.getCurveName() /** - * Return true, if the secret key is encrypted. - * This method returns false, if the secret key is null. + * Return true, if the secret key is encrypted. This method returns false, if the secret key is + * null. */ - @Deprecated("Deprecated in favor of calling isEncrypted() on the PGPSecretKey itself.", - ReplaceWith("secretKey.isEncrypted()")) + @Deprecated( + "Deprecated in favor of calling isEncrypted() on the PGPSecretKey itself.", + ReplaceWith("secretKey.isEncrypted()")) val isEncrypted: Boolean get() = secretKey?.isEncrypted() ?: false /** - * Return true, if the secret key is decrypted. - * This method returns true, if the secret key is null. + * Return true, if the secret key is decrypted. This method returns true, if the secret key is + * null. */ - @Deprecated("Deprecated in favor of calling isDecrypted() on the PGPSecretKey itself.", - ReplaceWith("secretKey.isDecrypted()")) + @Deprecated( + "Deprecated in favor of calling isDecrypted() on the PGPSecretKey itself.", + ReplaceWith("secretKey.isDecrypted()")) val isDecrypted: Boolean get() = secretKey?.isDecrypted() ?: true /** - * Return true, if the secret key is using the GNU_DUMMY_S2K s2k type. - * This method returns false, if the secret key is null. + * Return true, if the secret key is using the GNU_DUMMY_S2K s2k type. This method returns + * false, if the secret key is null. */ - @Deprecated("Deprecated in favor of calling hasDummyS2K() on the PGPSecretKey itself.", - ReplaceWith("secretKey.hasDummyS2K()")) + @Deprecated( + "Deprecated in favor of calling hasDummyS2K() on the PGPSecretKey itself.", + ReplaceWith("secretKey.hasDummyS2K()")) val hasDummyS2K: Boolean - @JvmName("hasDummyS2K") - get() = secretKey?.hasDummyS2K() ?: false + @JvmName("hasDummyS2K") get() = secretKey?.hasDummyS2K() ?: false companion object { @JvmStatic - @Deprecated("Deprecated in favor of calling isEncrypted() on the PGPSecretKey itself.", - ReplaceWith("secretKey.isEncrypted()")) + @Deprecated( + "Deprecated in favor of calling isEncrypted() on the PGPSecretKey itself.", + ReplaceWith("secretKey.isEncrypted()")) fun isEncrypted(secretKey: PGPSecretKey?) = secretKey.isEncrypted() @JvmStatic - @Deprecated("Deprecated in favor of calling isDecrypted() on the PGPSecretKey itself.", - ReplaceWith("secretKey.isDecrypted()")) + @Deprecated( + "Deprecated in favor of calling isDecrypted() on the PGPSecretKey itself.", + ReplaceWith("secretKey.isDecrypted()")) fun isDecrypted(secretKey: PGPSecretKey?) = secretKey.isDecrypted() @JvmStatic - @Deprecated("Deprecated in favor of calling hasDummyS2K() on the PGPSecretKey itself.", - ReplaceWith("secretKey.hasDummyS2K()")) + @Deprecated( + "Deprecated in favor of calling hasDummyS2K() on the PGPSecretKey itself.", + ReplaceWith("secretKey.hasDummyS2K()")) fun hasDummyS2K(secretKey: PGPSecretKey?) = secretKey.hasDummyS2K() } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/info/KeyRingInfo.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/info/KeyRingInfo.kt index 208d8060..9f0bbc87 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/info/KeyRingInfo.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/info/KeyRingInfo.kt @@ -4,6 +4,7 @@ package org.pgpainless.key.info +import java.util.* import openpgp.openPgpKeyId import org.bouncycastle.extensions.* import org.bouncycastle.openpgp.* @@ -19,240 +20,210 @@ import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil.Companion.getKeyExpirationTimeAsDate import org.pgpainless.util.DateUtil import org.slf4j.LoggerFactory -import java.util.* class KeyRingInfo( - val keys: PGPKeyRing, - val policy: Policy = PGPainless.getPolicy(), - val referenceDate: Date = Date()) { + val keys: PGPKeyRing, + val policy: Policy = PGPainless.getPolicy(), + val referenceDate: Date = Date() +) { @JvmOverloads - constructor(keys: PGPKeyRing, referenceDate: Date = Date()): this(keys, PGPainless.getPolicy(), referenceDate) + constructor( + keys: PGPKeyRing, + referenceDate: Date = Date() + ) : this(keys, PGPainless.getPolicy(), referenceDate) private val signatures: Signatures = Signatures(keys, referenceDate, policy) - /** - * Primary {@link PGPPublicKey}.´ - */ + /** Primary {@link PGPPublicKey}.´ */ val publicKey: PGPPublicKey = KeyRingUtils.requirePrimaryPublicKeyFrom(keys) - /** - * Primary key ID. - */ + /** Primary key ID. */ val keyId: Long = publicKey.keyID - /** - * Primary key fingerprint. - */ + /** Primary key fingerprint. */ val fingerprint: OpenPgpFingerprint = OpenPgpFingerprint.of(keys) - /** - * All User-IDs (valid, expired, revoked). - */ + /** All User-IDs (valid, expired, revoked). */ val userIds: List = KeyRingUtils.getUserIdsIgnoringInvalidUTF8(publicKey) - /** - * Primary User-ID. - */ + /** Primary User-ID. */ val primaryUserId = findPrimaryUserId() - /** - * Revocation State. - */ + /** Revocation State. */ val revocationState = signatures.primaryKeyRevocation.toRevocationState() /** * Return the date on which the primary key was revoked, or null if it has not yet been revoked. * * @return revocation date or null */ - val revocationDate: Date? = if (revocationState.isSoftRevocation()) revocationState.date else null + val revocationDate: Date? = + if (revocationState.isSoftRevocation()) revocationState.date else null /** * Primary [PGPSecretKey] of this key ring or null if the key ring is not a [PGPSecretKeyRing]. */ - val secretKey: PGPSecretKey? = when(keys) { - is PGPSecretKeyRing -> keys.secretKey!! - else -> null - } + val secretKey: PGPSecretKey? = + when (keys) { + is PGPSecretKeyRing -> keys.secretKey!! + else -> null + } - /** - * OpenPGP key version. - */ + /** OpenPGP key version. */ val version: Int = publicKey.version /** - * Return all {@link PGPPublicKey PGPPublicKeys} of this key ring. - * The first key in the list being the primary key. - * Note that the list is unmodifiable. + * Return all {@link PGPPublicKey PGPPublicKeys} of this key ring. The first key in the list + * being the primary key. Note that the list is unmodifiable. * * @return list of public keys */ val publicKeys: List = keys.publicKeys.asSequence().toList() - /** - * All secret keys. - * If the key ring is a [PGPPublicKeyRing], then return an empty list. - */ - val secretKeys: List = when(keys) { - is PGPSecretKeyRing -> keys.secretKeys.asSequence().toList() - else -> listOf() - } + /** All secret keys. If the key ring is a [PGPPublicKeyRing], then return an empty list. */ + val secretKeys: List = + when (keys) { + is PGPSecretKeyRing -> keys.secretKeys.asSequence().toList() + else -> listOf() + } - /** - * List of valid public subkeys. - */ - val validSubkeys: List = keys.publicKeys.asSequence() - .filter { isKeyValidlyBound(it.keyID) } - .toList() + /** List of valid public subkeys. */ + val validSubkeys: List = + keys.publicKeys.asSequence().filter { isKeyValidlyBound(it.keyID) }.toList() - /** - * List of valid user-IDs. - */ + /** List of valid user-IDs. */ val validUserIds: List = userIds.filter { isUserIdBound(it) } - /** - * List of valid and expired user-IDs. - */ - val validAndExpiredUserIds: List = userIds.filter { - val certification = signatures.userIdCertifications[it] ?: return@filter false - val revocation = signatures.userIdRevocations[it] ?: return@filter true - return@filter !revocation.isHardRevocation && certification.creationTime > revocation.creationTime - } + /** List of valid and expired user-IDs. */ + val validAndExpiredUserIds: List = + userIds.filter { + val certification = signatures.userIdCertifications[it] ?: return@filter false + val revocation = signatures.userIdRevocations[it] ?: return@filter true + return@filter !revocation.isHardRevocation && + certification.creationTime > revocation.creationTime + } - /** - * List of email addresses that can be extracted from the user-IDs. - */ - val emailAddresses: List = userIds.mapNotNull { - PATTERN_EMAIL_FROM_USERID.matcher(it).let { m1 -> - if (m1.find()) m1.group(1) - else PATTERN_EMAIL_EXPLICIT.matcher(it).let { m2 -> - if(m2.find()) m2.group(1) else null + /** List of email addresses that can be extracted from the user-IDs. */ + val emailAddresses: List = + userIds.mapNotNull { + PATTERN_EMAIL_FROM_USERID.matcher(it).let { m1 -> + if (m1.find()) m1.group(1) + else + PATTERN_EMAIL_EXPLICIT.matcher(it).let { m2 -> + if (m2.find()) m2.group(1) else null + } } } - } - /** - * Newest direct-key self-signature on the primary key. - */ + /** Newest direct-key self-signature on the primary key. */ val latestDirectKeySelfSignature: PGPSignature? = signatures.primaryKeySelfSignature - /** - * Newest primary-key revocation self-signature. - */ + /** Newest primary-key revocation self-signature. */ val revocationSelfSignature: PGPSignature? = signatures.primaryKeyRevocation - /** - * Public-key encryption-algorithm of the primary key. - */ + /** Public-key encryption-algorithm of the primary key. */ val algorithm: PublicKeyAlgorithm = PublicKeyAlgorithm.requireFromId(publicKey.algorithm) - /** - * Creation date of the primary key. - */ + /** Creation date of the primary key. */ val creationDate: Date = publicKey.creationTime!! - /** - * Latest date at which the key was modified (either by adding a subkey or self-signature). - */ + /** Latest date at which the key was modified (either by adding a subkey or self-signature). */ val lastModified: Date = getMostRecentSignature()?.creationTime ?: getLatestKeyCreationDate() - /** - * True, if the underlying keyring is a [PGPSecretKeyRing]. - */ + /** True, if the underlying keyring is a [PGPSecretKeyRing]. */ val isSecretKey: Boolean = keys is PGPSecretKeyRing - /** - * True, if there are no encrypted secret keys. - */ - val isFullyDecrypted: Boolean = !isSecretKey || secretKeys.all { it.hasDummyS2K() || it.isDecrypted() } + /** True, if there are no encrypted secret keys. */ + val isFullyDecrypted: Boolean = + !isSecretKey || secretKeys.all { it.hasDummyS2K() || it.isDecrypted() } - /** - * True, if there are only encrypted secret keys. - */ - val isFullyEncrypted: Boolean = isSecretKey && secretKeys.none { !it.hasDummyS2K() && it.isDecrypted() } + /** True, if there are only encrypted secret keys. */ + val isFullyEncrypted: Boolean = + isSecretKey && secretKeys.none { !it.hasDummyS2K() && it.isDecrypted() } - /** - * List of public keys, whose secret key counterparts can be used to decrypt messages. - */ - val decryptionSubkeys: List = keys.publicKeys.asSequence().filter { - if (it.keyID != keyId) { - if (signatures.subkeyBindings[it.keyID] == null) { - LOGGER.debug("Subkey ${it.keyID.openPgpKeyId()} has no binding signature.") - return@filter false + /** List of public keys, whose secret key counterparts can be used to decrypt messages. */ + val decryptionSubkeys: List = + keys.publicKeys + .asSequence() + .filter { + if (it.keyID != keyId) { + if (signatures.subkeyBindings[it.keyID] == null) { + LOGGER.debug("Subkey ${it.keyID.openPgpKeyId()} has no binding signature.") + return@filter false + } + } + if (!it.isEncryptionKey) { + LOGGER.debug("(Sub-?)Key ${it.keyID.openPgpKeyId()} is not encryption-capable.") + return@filter false + } + return@filter true } - } - if (!it.isEncryptionKey) { - LOGGER.debug("(Sub-?)Key ${it.keyID.openPgpKeyId()} is not encryption-capable.") - return@filter false - } - return@filter true - }.toList() + .toList() - /** - * Expiration date of the primary key. - */ + /** Expiration date of the primary key. */ val primaryKeyExpirationDate: Date? get() { - val directKeyExpirationDate: Date? = latestDirectKeySelfSignature?.let { getKeyExpirationTimeAsDate(it, publicKey) } - val possiblyExpiredPrimaryUserId = getPossiblyExpiredPrimaryUserId() - val primaryUserIdCertification = possiblyExpiredPrimaryUserId?.let { getLatestUserIdCertification(it) } - val userIdExpirationDate: Date? = primaryUserIdCertification?.let { getKeyExpirationTimeAsDate(it, publicKey) } + val directKeyExpirationDate: Date? = + latestDirectKeySelfSignature?.let { getKeyExpirationTimeAsDate(it, publicKey) } + val possiblyExpiredPrimaryUserId = getPossiblyExpiredPrimaryUserId() + val primaryUserIdCertification = + possiblyExpiredPrimaryUserId?.let { getLatestUserIdCertification(it) } + val userIdExpirationDate: Date? = + primaryUserIdCertification?.let { getKeyExpirationTimeAsDate(it, publicKey) } - if (latestDirectKeySelfSignature == null && primaryUserIdCertification == null) { - throw NoSuchElementException("No direct-key signature and no user-id signature found.") + if (latestDirectKeySelfSignature == null && primaryUserIdCertification == null) { + throw NoSuchElementException( + "No direct-key signature and no user-id signature found.") + } + if (directKeyExpirationDate != null && userIdExpirationDate == null) { + return directKeyExpirationDate + } + if (directKeyExpirationDate == null) { + return userIdExpirationDate + } + return if (directKeyExpirationDate < userIdExpirationDate) directKeyExpirationDate + else userIdExpirationDate } - if (directKeyExpirationDate != null && userIdExpirationDate == null) { - return directKeyExpirationDate - } - if (directKeyExpirationDate == null) { - return userIdExpirationDate - } - return if (directKeyExpirationDate < userIdExpirationDate) - directKeyExpirationDate - else userIdExpirationDate - } - /** - * List of all subkeys that can be used to sign a message. - */ - val signingSubkeys: List = validSubkeys.filter { getKeyFlagsOf(it.keyID).contains(KeyFlag.SIGN_DATA) } + /** List of all subkeys that can be used to sign a message. */ + val signingSubkeys: List = + validSubkeys.filter { getKeyFlagsOf(it.keyID).contains(KeyFlag.SIGN_DATA) } - /** - * Whether the key is usable for encryption. - */ + /** Whether the key is usable for encryption. */ val isUsableForEncryption: Boolean = isUsableForEncryption(EncryptionPurpose.ANY) /** - * Whether the key is capable of signing messages. - * This field is also true, if the key contains a subkey that is capable of signing messages, but where the secret - * key is unavailable, e.g. because it was moved to a smart-card. + * Whether the key is capable of signing messages. This field is also true, if the key contains + * a subkey that is capable of signing messages, but where the secret key is unavailable, e.g. + * because it was moved to a smart-card. * * To check for keys that are actually usable to sign messages, use [isUsableForSigning]. */ val isSigningCapable: Boolean = isKeyValidlyBound(keyId) && signingSubkeys.isNotEmpty() - /** - * Whether the key is actually usable to sign messages. - */ - val isUsableForSigning: Boolean = isSigningCapable && signingSubkeys.any { isSecretKeyAvailable(it.keyID) } + /** Whether the key is actually usable to sign messages. */ + val isUsableForSigning: Boolean = + isSigningCapable && signingSubkeys.any { isSecretKeyAvailable(it.keyID) } - /** - * [HashAlgorithm] preferences of the primary user-ID or if absent, of the primary key. - */ + /** [HashAlgorithm] preferences of the primary user-ID or if absent, of the primary key. */ val preferredHashAlgorithms: Set - get() = primaryUserId?.let { getPreferredHashAlgorithms(it) } ?: getPreferredHashAlgorithms(keyId) + get() = + primaryUserId?.let { getPreferredHashAlgorithms(it) } + ?: getPreferredHashAlgorithms(keyId) /** * [SymmetricKeyAlgorithm] preferences of the primary user-ID or if absent of the primary key. */ val preferredSymmetricKeyAlgorithms: Set - get() = primaryUserId?.let { getPreferredSymmetricKeyAlgorithms(it) } ?: getPreferredSymmetricKeyAlgorithms(keyId) + get() = + primaryUserId?.let { getPreferredSymmetricKeyAlgorithms(it) } + ?: getPreferredSymmetricKeyAlgorithms(keyId) - /** - * [CompressionAlgorithm] preferences of the primary user-ID or if absent, the primary key. - */ + /** [CompressionAlgorithm] preferences of the primary user-ID or if absent, the primary key. */ val preferredCompressionAlgorithms: Set - get() = primaryUserId?.let { getPreferredCompressionAlgorithms(it) } ?: getPreferredCompressionAlgorithms(keyId) + get() = + primaryUserId?.let { getPreferredCompressionAlgorithms(it) } + ?: getPreferredCompressionAlgorithms(keyId) /** * Return the expiration date of the subkey with the provided fingerprint. @@ -272,13 +243,19 @@ class KeyRingInfo( */ fun getSubkeyExpirationDate(keyId: Long): Date? { if (publicKey.keyID == keyId) return primaryKeyExpirationDate - val subkey = getPublicKey(keyId) ?: throw NoSuchElementException("No subkey with key-ID ${keyId.openPgpKeyId()} found.") - val bindingSig = getCurrentSubkeyBindingSignature(keyId) ?: throw AssertionError("Subkey has no valid binding signature.") + val subkey = + getPublicKey(keyId) + ?: throw NoSuchElementException( + "No subkey with key-ID ${keyId.openPgpKeyId()} found.") + val bindingSig = + getCurrentSubkeyBindingSignature(keyId) + ?: throw AssertionError("Subkey has no valid binding signature.") return bindingSig.getKeyExpirationDate(subkey.creationTime) } /** - * Return the date after which the key can no longer be used to perform the given use-case, caused by expiration. + * Return the date after which the key can no longer be used to perform the given use-case, + * caused by expiration. * * @return expiration date for the given use-case */ @@ -289,17 +266,21 @@ class KeyRingInfo( val primaryKeyExpiration = primaryKeyExpirationDate val keysWithFlag: List = getKeysWithKeyFlag(use) - if (keysWithFlag.isEmpty()) throw NoSuchElementException("No key with the required key flag found.") + if (keysWithFlag.isEmpty()) + throw NoSuchElementException("No key with the required key flag found.") var nonExpiring = false - val latestSubkeyExpiration = keysWithFlag.map { key -> - getSubkeyExpirationDate(key.keyID).also { if (it == null) nonExpiring = true } - }.filterNotNull().maxByOrNull { it } + val latestSubkeyExpiration = + keysWithFlag + .map { key -> + getSubkeyExpirationDate(key.keyID).also { if (it == null) nonExpiring = true } + } + .filterNotNull() + .maxByOrNull { it } if (nonExpiring) return primaryKeyExpiration return if (primaryKeyExpiration == null) latestSubkeyExpiration - else if (latestSubkeyExpiration == null) - primaryKeyExpiration + else if (latestSubkeyExpiration == null) primaryKeyExpiration else minOf(primaryKeyExpiration, latestSubkeyExpiration) } @@ -318,17 +299,24 @@ class KeyRingInfo( * @param flag flag * @return keys with flag */ - fun getKeysWithKeyFlag(flag: KeyFlag): List = publicKeys.filter { getKeyFlagsOf(it.keyID).contains(flag) } + fun getKeysWithKeyFlag(flag: KeyFlag): List = + publicKeys.filter { getKeyFlagsOf(it.keyID).contains(flag) } /** * Return a list of all subkeys which can be used to encrypt a message for the given user-ID. * * @return encryption subkeys */ - fun getEncryptionSubkeys(userId: CharSequence?, purpose: EncryptionPurpose): List { + fun getEncryptionSubkeys( + userId: CharSequence?, + purpose: EncryptionPurpose + ): List { if (userId != null && !isUserIdValid(userId)) { - throw UnboundUserIdException(OpenPgpFingerprint.of(keys), userId.toString(), - getLatestUserIdCertification(userId), getUserIdRevocation(userId)) + throw UnboundUserIdException( + OpenPgpFingerprint.of(keys), + userId.toString(), + getLatestUserIdCertification(userId), + getUserIdRevocation(userId)) } return getEncryptionSubkeys(purpose) } @@ -341,42 +329,53 @@ class KeyRingInfo( fun getEncryptionSubkeys(purpose: EncryptionPurpose): List { primaryKeyExpirationDate?.let { if (it < referenceDate) { - LOGGER.debug("Certificate is expired: Primary key is expired on ${DateUtil.formatUTCDate(it)}") + LOGGER.debug( + "Certificate is expired: Primary key is expired on ${DateUtil.formatUTCDate(it)}") return listOf() } } - return keys.publicKeys.asSequence().filter { - if (!isKeyValidlyBound(it.keyID)) { - LOGGER.debug("(Sub?)-Key ${it.keyID.openPgpKeyId()} is not validly bound.") - return@filter false - } - - getSubkeyExpirationDate(it.keyID)?.let { exp -> - if (exp < referenceDate) { - LOGGER.debug("(Sub?)-Key ${it.keyID.openPgpKeyId()} is expired on ${DateUtil.formatUTCDate(exp)}.") + return keys.publicKeys + .asSequence() + .filter { + if (!isKeyValidlyBound(it.keyID)) { + LOGGER.debug("(Sub?)-Key ${it.keyID.openPgpKeyId()} is not validly bound.") return@filter false } - } - if (!it.isEncryptionKey) { - LOGGER.debug("(Sub?)-Key ${it.keyID.openPgpKeyId()} algorithm is not capable of encryption.") - return@filter false - } + getSubkeyExpirationDate(it.keyID)?.let { exp -> + if (exp < referenceDate) { + LOGGER.debug( + "(Sub?)-Key ${it.keyID.openPgpKeyId()} is expired on ${DateUtil.formatUTCDate(exp)}.") + return@filter false + } + } - val keyFlags = getKeyFlagsOf(it.keyID) - when (purpose) { - EncryptionPurpose.COMMUNICATIONS -> return@filter keyFlags.contains(KeyFlag.ENCRYPT_COMMS) - EncryptionPurpose.STORAGE -> return@filter keyFlags.contains(KeyFlag.ENCRYPT_STORAGE) - EncryptionPurpose.ANY -> return@filter keyFlags.contains(KeyFlag.ENCRYPT_COMMS) || keyFlags.contains(KeyFlag.ENCRYPT_STORAGE) + if (!it.isEncryptionKey) { + LOGGER.debug( + "(Sub?)-Key ${it.keyID.openPgpKeyId()} algorithm is not capable of encryption.") + return@filter false + } + + val keyFlags = getKeyFlagsOf(it.keyID) + when (purpose) { + EncryptionPurpose.COMMUNICATIONS -> + return@filter keyFlags.contains(KeyFlag.ENCRYPT_COMMS) + EncryptionPurpose.STORAGE -> + return@filter keyFlags.contains(KeyFlag.ENCRYPT_STORAGE) + EncryptionPurpose.ANY -> + return@filter keyFlags.contains(KeyFlag.ENCRYPT_COMMS) || + keyFlags.contains(KeyFlag.ENCRYPT_STORAGE) + } } - }.toList() + .toList() } /** * Return, whether the key is usable for encryption, given the purpose. * - * @return true, if the key can be used to encrypt a message according to the encryption-purpose. + * @return true, if the key can be used to encrypt a message according to the + * encryption-purpose. */ fun isUsableForEncryption(purpose: EncryptionPurpose): Boolean { return isKeyValidlyBound(keyId) && getEncryptionSubkeys(purpose).isNotEmpty() @@ -387,29 +386,30 @@ class KeyRingInfo( * * @return possibly expired primary user-ID */ - fun getPossiblyExpiredPrimaryUserId(): String? = primaryUserId ?: userIds - .mapNotNull { userId -> - getLatestUserIdCertification(userId)?.let { userId to it } - } - .sortedByDescending { it.second.creationTime } - .maxByOrNull { it.second.hashedSubPackets.isPrimaryUserID }?.first + fun getPossiblyExpiredPrimaryUserId(): String? = + primaryUserId + ?: userIds + .mapNotNull { userId -> getLatestUserIdCertification(userId)?.let { userId to it } } + .sortedByDescending { it.second.creationTime } + .maxByOrNull { it.second.hashedSubPackets.isPrimaryUserID } + ?.first - /** - * Return the most-recently created self-signature on the key. - */ + /** Return the most-recently created self-signature on the key. */ private fun getMostRecentSignature(): PGPSignature? = - setOfNotNull(latestDirectKeySelfSignature, revocationSelfSignature).asSequence() - .plus(signatures.userIdCertifications.values) - .plus(signatures.userIdRevocations.values) - .plus(signatures.subkeyBindings.values) - .plus(signatures.subkeyRevocations.values) - .maxByOrNull { creationDate } + setOfNotNull(latestDirectKeySelfSignature, revocationSelfSignature) + .asSequence() + .plus(signatures.userIdCertifications.values) + .plus(signatures.userIdRevocations.values) + .plus(signatures.subkeyBindings.values) + .plus(signatures.subkeyRevocations.values) + .maxByOrNull { creationDate } /** * Return the creation time of the latest added subkey. * * @return latest key creation time */ - fun getLatestKeyCreationDate(): Date = validSubkeys.maxByOrNull { creationDate }?.creationTime + fun getLatestKeyCreationDate(): Date = + validSubkeys.maxByOrNull { creationDate }?.creationTime ?: throw AssertionError("Apparently there is no validly bound key in this key ring.") /** @@ -417,57 +417,63 @@ class KeyRingInfo( * * @return latest self-certification for the given user-ID. */ - fun getLatestUserIdCertification(userId: CharSequence): PGPSignature? = signatures.userIdCertifications[userId] + fun getLatestUserIdCertification(userId: CharSequence): PGPSignature? = + signatures.userIdCertifications[userId] /** * Return the latest revocation self-signature for the given user-ID * * @return latest user-ID revocation for the given user-ID */ - fun getUserIdRevocation(userId: CharSequence): PGPSignature? = signatures.userIdRevocations[userId] + fun getUserIdRevocation(userId: CharSequence): PGPSignature? = + signatures.userIdRevocations[userId] /** * Return the current binding signature for the subkey with the given key-ID. * * @return current subkey binding signature */ - fun getCurrentSubkeyBindingSignature(keyId: Long): PGPSignature? = signatures.subkeyBindings[keyId] + fun getCurrentSubkeyBindingSignature(keyId: Long): PGPSignature? = + signatures.subkeyBindings[keyId] /** * Return the current revocation signature for the subkey with the given key-ID. * * @return current subkey revocation signature */ - fun getSubkeyRevocationSignature(keyId: Long): PGPSignature? = signatures.subkeyRevocations[keyId] + fun getSubkeyRevocationSignature(keyId: Long): PGPSignature? = + signatures.subkeyRevocations[keyId] /** * Return a list of {@link KeyFlag KeyFlags} that apply to the subkey with the provided key id. + * * @param keyId key-id * @return list of key flags */ fun getKeyFlagsOf(keyId: Long): List = - if (keyId == publicKey.keyID) { - latestDirectKeySelfSignature?.let { sig -> - SignatureSubpacketsUtil.parseKeyFlags(sig)?.let { flags -> - return flags - } + if (keyId == publicKey.keyID) { + latestDirectKeySelfSignature?.let { sig -> + SignatureSubpacketsUtil.parseKeyFlags(sig)?.let { flags -> + return flags } - - primaryUserId?.let { - SignatureSubpacketsUtil.parseKeyFlags(getLatestUserIdCertification(it))?.let { flags -> - return flags - } - } - listOf() - } else { - getCurrentSubkeyBindingSignature(keyId)?.let { - SignatureSubpacketsUtil.parseKeyFlags(it)?.let { flags -> - return flags - } - } - listOf() } + primaryUserId?.let { + SignatureSubpacketsUtil.parseKeyFlags(getLatestUserIdCertification(it))?.let { flags + -> + return flags + } + } + listOf() + } else { + getCurrentSubkeyBindingSignature(keyId)?.let { + SignatureSubpacketsUtil.parseKeyFlags(it)?.let { flags -> + return flags + } + } + listOf() + } + /** * Return a list of {@link KeyFlag KeyFlags} that apply to the given user-id. * @@ -475,13 +481,15 @@ class KeyRingInfo( * @return key flags */ fun getKeyFlagsOf(userId: CharSequence): List = - if (!isUserIdValid(userId)) { - listOf() - } else { - getLatestUserIdCertification(userId)?.let { - SignatureSubpacketsUtil.parseKeyFlags(it) ?: listOf() - } ?: throw AssertionError("While user-id '$userId' was reported as valid, there appears to be no certification for it.") + if (!isUserIdValid(userId)) { + listOf() + } else { + getLatestUserIdCertification(userId)?.let { + SignatureSubpacketsUtil.parseKeyFlags(it) ?: listOf() } + ?: throw AssertionError( + "While user-id '$userId' was reported as valid, there appears to be no certification for it.") + } /** * Return the public key with the given key id from the provided key ring. @@ -497,13 +505,15 @@ class KeyRingInfo( * @param keyId key id * @return secret key or null */ - fun getSecretKey(keyId: Long): PGPSecretKey? = when(keys) { - is PGPSecretKeyRing -> keys.getSecretKey(keyId) - else -> null - } + fun getSecretKey(keyId: Long): PGPSecretKey? = + when (keys) { + is PGPSecretKeyRing -> keys.getSecretKey(keyId) + else -> null + } /** - * Return true, if the secret-key with the given key-ID is available (i.e. not moved to a smart-card). + * Return true, if the secret-key with the given key-ID is available (i.e. not moved to a + * smart-card). * * @return availability of the secret key */ @@ -511,7 +521,8 @@ class KeyRingInfo( return getSecretKey(keyId)?.let { return if (it.s2K == null) true // Unencrypted key else it.s2K.type !in 100..110 // Secret key on smart-card - } ?: false // Missing secret key + } + ?: false // Missing secret key } /** @@ -520,7 +531,8 @@ class KeyRingInfo( * @param fingerprint fingerprint * @return public key or null */ - fun getPublicKey(fingerprint: OpenPgpFingerprint): PGPPublicKey? = keys.getPublicKey(fingerprint.keyId) + fun getPublicKey(fingerprint: OpenPgpFingerprint): PGPPublicKey? = + keys.getPublicKey(fingerprint.keyId) /** * Return the secret key with the given fingerprint. @@ -528,21 +540,21 @@ class KeyRingInfo( * @param fingerprint fingerprint * @return secret key or null */ - fun getSecretKey(fingerprint: OpenPgpFingerprint): PGPSecretKey? = when(keys) { - is PGPSecretKeyRing -> keys.getSecretKey(fingerprint.keyId) - else -> null - } + fun getSecretKey(fingerprint: OpenPgpFingerprint): PGPSecretKey? = + when (keys) { + is PGPSecretKeyRing -> keys.getSecretKey(fingerprint.keyId) + else -> null + } /** * Return the public key matching the given [SubkeyIdentifier]. * * @return public key - * @throws IllegalArgumentException if the identifier's primary key does not match the primary key of the key. + * @throws IllegalArgumentException if the identifier's primary key does not match the primary + * key of the key. */ fun getPublicKey(identifier: SubkeyIdentifier): PGPPublicKey? { - require(identifier.primaryKeyId == publicKey.keyID) { - "Mismatching primary key ID." - } + require(identifier.primaryKeyId == publicKey.keyID) { "Mismatching primary key ID." } return getPublicKey(identifier.subkeyId) } @@ -550,17 +562,19 @@ class KeyRingInfo( * Return the secret key matching the given [SubkeyIdentifier]. * * @return secret key - * @throws IllegalArgumentException if the identifier's primary key does not match the primary key of the key. + * @throws IllegalArgumentException if the identifier's primary key does not match the primary + * key of the key. */ - fun getSecretKey(identifier: SubkeyIdentifier): PGPSecretKey? = when(keys) { - is PGPSecretKeyRing -> { - require(identifier.primaryKeyId == publicKey.keyID) { - "Mismatching primary key ID." + fun getSecretKey(identifier: SubkeyIdentifier): PGPSecretKey? = + when (keys) { + is PGPSecretKeyRing -> { + require(identifier.primaryKeyId == publicKey.keyID) { + "Mismatching primary key ID." + } + keys.getSecretKey(identifier.subkeyId) } - keys.getSecretKey(identifier.subkeyId) + else -> null } - else -> null - } /** * Return true if the public key with the given key id is bound to the key ring properly. @@ -573,7 +587,8 @@ class KeyRingInfo( // Primary key -> Check Primary Key Revocation if (publicKey.keyID == this.publicKey.keyID) { - return if (signatures.primaryKeyRevocation != null && signatures.primaryKeyRevocation.isHardRevocation) { + return if (signatures.primaryKeyRevocation != null && + signatures.primaryKeyRevocation.isHardRevocation) { false } else signatures.primaryKeyRevocation == null } @@ -594,16 +609,18 @@ class KeyRingInfo( false } else { // Key is soft-revoked, not yet re-bound - (revocation.isExpired(referenceDate) || !revocation.creationTime.after(binding.creationTime)) + (revocation.isExpired(referenceDate) || + !revocation.creationTime.after(binding.creationTime)) } } else true } /** * Return the current primary user-id of the key ring. + * *

- * Note: If no user-id is marked as primary key using a {@link PrimaryUserID} packet, - * this method returns the first user-id on the key, otherwise null. + * Note: If no user-id is marked as primary key using a {@link PrimaryUserID} packet, this + * method returns the first user-id on the key, otherwise null. * * @return primary user-id or null */ @@ -612,94 +629,85 @@ class KeyRingInfo( return null } - return signatures.userIdCertifications.filter { (_, certification) -> - certification.hashedSubPackets.isPrimaryUserID - }.entries.maxByOrNull { (_, certification) -> - certification.creationTime - }?.key ?: signatures.userIdCertifications.keys.firstOrNull() + return signatures.userIdCertifications + .filter { (_, certification) -> certification.hashedSubPackets.isPrimaryUserID } + .entries + .maxByOrNull { (_, certification) -> certification.creationTime } + ?.key + ?: signatures.userIdCertifications.keys.firstOrNull() } - /** - * Return true, if the primary user-ID, as well as the given user-ID are valid and bound. - */ + /** Return true, if the primary user-ID, as well as the given user-ID are valid and bound. */ fun isUserIdValid(userId: CharSequence) = - if (primaryUserId == null) { - false - } else { - isUserIdBound(primaryUserId) && (if (userId == primaryUserId) true else isUserIdBound(userId)) - } + if (primaryUserId == null) { + false + } else { + isUserIdBound(primaryUserId) && + (if (userId == primaryUserId) true else isUserIdBound(userId)) + } - /** - * Return true, if the given user-ID is validly bound. - */ + /** Return true, if the given user-ID is validly bound. */ fun isUserIdBound(userId: CharSequence) = - signatures.userIdCertifications[userId]?.let { sig -> - if (sig.isExpired(referenceDate)) { - // certification expired - return false + signatures.userIdCertifications[userId]?.let { sig -> + if (sig.isExpired(referenceDate)) { + // certification expired + return false + } + if (sig.hashedSubPackets.isPrimaryUserID) { + getKeyExpirationTimeAsDate(sig, publicKey)?.let { expirationDate -> + // key expired? + if (expirationDate < referenceDate) return false } - if (sig.hashedSubPackets.isPrimaryUserID) { - getKeyExpirationTimeAsDate(sig, publicKey)?.let { expirationDate -> - // key expired? - if (expirationDate < referenceDate) return false - } + } + signatures.userIdRevocations[userId]?.let { rev -> + if (rev.isHardRevocation) { + return false // hard revoked -> invalid } - signatures.userIdRevocations[userId]?.let { rev -> - if (rev.isHardRevocation) { - return false // hard revoked -> invalid - } - sig.creationTime > rev.creationTime// re-certification after soft revocation? - } ?: true // certification, but no revocation - } ?: false // no certification + sig.creationTime > rev.creationTime // re-certification after soft revocation? + } + ?: true // certification, but no revocation + } + ?: false // no certification - /** - * [HashAlgorithm] preferences of the given user-ID. - */ + /** [HashAlgorithm] preferences of the given user-ID. */ fun getPreferredHashAlgorithms(userId: CharSequence): Set { return getKeyAccessor(userId, keyId).preferredHashAlgorithms } - /** - * [HashAlgorithm] preferences of the given key. - */ + /** [HashAlgorithm] preferences of the given key. */ fun getPreferredHashAlgorithms(keyId: Long): Set { return KeyAccessor.SubKey(this, SubkeyIdentifier(keys, keyId)).preferredHashAlgorithms } - /** - * [SymmetricKeyAlgorithm] preferences of the given user-ID. - */ + /** [SymmetricKeyAlgorithm] preferences of the given user-ID. */ fun getPreferredSymmetricKeyAlgorithms(userId: CharSequence): Set { return getKeyAccessor(userId, keyId).preferredSymmetricKeyAlgorithms } - /** - * [SymmetricKeyAlgorithm] preferences of the given key. - */ + /** [SymmetricKeyAlgorithm] preferences of the given key. */ fun getPreferredSymmetricKeyAlgorithms(keyId: Long): Set { - return KeyAccessor.SubKey(this, SubkeyIdentifier(keys, keyId)).preferredSymmetricKeyAlgorithms + return KeyAccessor.SubKey(this, SubkeyIdentifier(keys, keyId)) + .preferredSymmetricKeyAlgorithms } - /** - * [CompressionAlgorithm] preferences of the given user-ID. - */ + /** [CompressionAlgorithm] preferences of the given user-ID. */ fun getPreferredCompressionAlgorithms(userId: CharSequence): Set { return getKeyAccessor(userId, keyId).preferredCompressionAlgorithms } - /** - * [CompressionAlgorithm] preferences of the given key. - */ + /** [CompressionAlgorithm] preferences of the given key. */ fun getPreferredCompressionAlgorithms(keyId: Long): Set { - return KeyAccessor.SubKey(this, SubkeyIdentifier(keys, keyId)).preferredCompressionAlgorithms + return KeyAccessor.SubKey(this, SubkeyIdentifier(keys, keyId)) + .preferredCompressionAlgorithms } val isUsableForThirdPartyCertification: Boolean = - isKeyValidlyBound(keyId) && getKeyFlagsOf(keyId).contains(KeyFlag.CERTIFY_OTHER) + isKeyValidlyBound(keyId) && getKeyFlagsOf(keyId).contains(KeyFlag.CERTIFY_OTHER) private fun getKeyAccessor(userId: CharSequence?, keyId: Long): KeyAccessor { if (getPublicKey(keyId) == null) { - throw NoSuchElementException("No subkey with key-id ${keyId.openPgpKeyId()} found on this key.") + throw NoSuchElementException( + "No subkey with key-id ${keyId.openPgpKeyId()} found on this key.") } if (userId != null && !userIds.contains(userId)) { throw NoSuchElementException("No user-id '$userId' found on this key.") @@ -713,26 +721,24 @@ class KeyRingInfo( companion object { - /** - * Evaluate the key for the given signature. - */ + /** Evaluate the key for the given signature. */ @JvmStatic - fun evaluateForSignature(keys: PGPKeyRing, signature: PGPSignature) = KeyRingInfo(keys, signature.creationTime!!) + fun evaluateForSignature(keys: PGPKeyRing, signature: PGPSignature) = + KeyRingInfo(keys, signature.creationTime!!) - private val PATTERN_EMAIL_FROM_USERID = "<([a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+@[a-zA-Z0-9.-]+)>".toPattern() - private val PATTERN_EMAIL_EXPLICIT = "^([a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+@[a-zA-Z0-9.-]+)$".toPattern() - - @JvmStatic - private val LOGGER = LoggerFactory.getLogger(KeyRingInfo::class.java) + private val PATTERN_EMAIL_FROM_USERID = + "<([a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+@[a-zA-Z0-9.-]+)>".toPattern() + private val PATTERN_EMAIL_EXPLICIT = + "^([a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+@[a-zA-Z0-9.-]+)$".toPattern() + @JvmStatic private val LOGGER = LoggerFactory.getLogger(KeyRingInfo::class.java) } - private class Signatures( - val keys: PGPKeyRing, - val referenceDate: Date, - val policy: Policy) { - val primaryKeyRevocation: PGPSignature? = SignaturePicker.pickCurrentRevocationSelfSignature(keys, policy, referenceDate) - val primaryKeySelfSignature: PGPSignature? = SignaturePicker.pickLatestDirectKeySignature(keys, policy, referenceDate) + private class Signatures(val keys: PGPKeyRing, val referenceDate: Date, val policy: Policy) { + val primaryKeyRevocation: PGPSignature? = + SignaturePicker.pickCurrentRevocationSelfSignature(keys, policy, referenceDate) + val primaryKeySelfSignature: PGPSignature? = + SignaturePicker.pickLatestDirectKeySignature(keys, policy, referenceDate) val userIdRevocations = mutableMapOf() val userIdCertifications = mutableMapOf() val subkeyRevocations = mutableMapOf() @@ -740,21 +746,21 @@ class KeyRingInfo( init { KeyRingUtils.getUserIdsIgnoringInvalidUTF8(keys.publicKey).forEach { userId -> - SignaturePicker.pickCurrentUserIdRevocationSignature(keys, userId, policy, referenceDate)?.let { - userIdRevocations[userId] = it - } - SignaturePicker.pickLatestUserIdCertificationSignature(keys, userId, policy, referenceDate)?.let { - userIdCertifications[userId] = it - } + SignaturePicker.pickCurrentUserIdRevocationSignature( + keys, userId, policy, referenceDate) + ?.let { userIdRevocations[userId] = it } + SignaturePicker.pickLatestUserIdCertificationSignature( + keys, userId, policy, referenceDate) + ?.let { userIdCertifications[userId] = it } } keys.publicKeys.asSequence().drop(1).forEach { subkey -> - SignaturePicker.pickCurrentSubkeyBindingRevocationSignature(keys, subkey, policy, referenceDate)?.let { - subkeyRevocations[subkey.keyID] = it - } - SignaturePicker.pickLatestSubkeyBindingSignature(keys, subkey, policy, referenceDate)?.let { - subkeyBindings[subkey.keyID] = it - } + SignaturePicker.pickCurrentSubkeyBindingRevocationSignature( + keys, subkey, policy, referenceDate) + ?.let { subkeyRevocations[subkey.keyID] = it } + SignaturePicker.pickLatestSubkeyBindingSignature( + keys, subkey, policy, referenceDate) + ?.let { subkeyBindings[subkey.keyID] = it } } } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.kt index 3425145b..891d64e7 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.kt @@ -4,6 +4,10 @@ package org.pgpainless.key.modification.secretkeyring +import java.util.* +import java.util.function.Predicate +import javax.annotation.Nonnull +import kotlin.NoSuchElementException import org.bouncycastle.bcpg.sig.KeyExpirationTime import org.bouncycastle.extensions.getKeyExpirationDate import org.bouncycastle.extensions.publicKeyAlgorithm @@ -30,19 +34,17 @@ import org.pgpainless.signature.builder.* import org.pgpainless.signature.subpackets.* import org.pgpainless.util.Passphrase import org.pgpainless.util.selection.userid.SelectUserId -import java.util.* -import java.util.function.Predicate -import javax.annotation.Nonnull -import kotlin.NoSuchElementException class SecretKeyRingEditor( - var secretKeyRing: PGPSecretKeyRing, - override val referenceTime: Date = Date() + var secretKeyRing: PGPSecretKeyRing, + override val referenceTime: Date = Date() ) : SecretKeyRingEditorInterface { - override fun addUserId(userId: CharSequence, - callback: SelfSignatureSubpackets.Callback?, - protector: SecretKeyRingProtector): SecretKeyRingEditorInterface { + override fun addUserId( + userId: CharSequence, + callback: SelfSignatureSubpackets.Callback?, + protector: SecretKeyRingProtector + ): SecretKeyRingEditorInterface { val sanitizedUserId = sanitizeUserId(userId).toString() val primaryKey = secretKeyRing.secretKey @@ -51,17 +53,28 @@ class SecretKeyRingEditor( "User-ID $userId is hard revoked and cannot be re-certified." } - val (hashAlgorithmPreferences, symmetricKeyAlgorithmPreferences, compressionAlgorithmPreferences) = try { - Triple(info.preferredHashAlgorithms, info.preferredSymmetricKeyAlgorithms, info.preferredCompressionAlgorithms) - } catch (e : IllegalStateException) { // missing user-id sig - val algorithmSuite = AlgorithmSuite.defaultAlgorithmSuite - Triple(algorithmSuite.hashAlgorithms, algorithmSuite.symmetricKeyAlgorithms, algorithmSuite.compressionAlgorithms) - } + val ( + hashAlgorithmPreferences, + symmetricKeyAlgorithmPreferences, + compressionAlgorithmPreferences) = + try { + Triple( + info.preferredHashAlgorithms, + info.preferredSymmetricKeyAlgorithms, + info.preferredCompressionAlgorithms) + } catch (e: IllegalStateException) { // missing user-id sig + val algorithmSuite = AlgorithmSuite.defaultAlgorithmSuite + Triple( + algorithmSuite.hashAlgorithms, + algorithmSuite.symmetricKeyAlgorithms, + algorithmSuite.compressionAlgorithms) + } - val builder = SelfSignatureBuilder(primaryKey, protector).apply { - hashedSubpackets.setSignatureCreationTime(referenceTime) - setSignatureType(SignatureType.POSITIVE_CERTIFICATION) - } + val builder = + SelfSignatureBuilder(primaryKey, protector).apply { + hashedSubpackets.setSignatureCreationTime(referenceTime) + setSignatureType(SignatureType.POSITIVE_CERTIFICATION) + } builder.hashedSubpackets.apply { setKeyFlags(info.getKeyFlagsOf(primaryKey.keyID)) setPreferredHashAlgorithms(hashAlgorithmPreferences) @@ -70,66 +83,109 @@ class SecretKeyRingEditor( setFeatures(Feature.MODIFICATION_DETECTION) } builder.applyCallback(callback) - secretKeyRing = injectCertification(secretKeyRing, sanitizedUserId, builder.build(primaryKey.publicKey, sanitizedUserId)) + secretKeyRing = + injectCertification( + secretKeyRing, + sanitizedUserId, + builder.build(primaryKey.publicKey, sanitizedUserId)) return this } - override fun addPrimaryUserId(userId: CharSequence, protector: SecretKeyRingProtector): SecretKeyRingEditorInterface { + override fun addPrimaryUserId( + userId: CharSequence, + protector: SecretKeyRingProtector + ): SecretKeyRingEditorInterface { val uid = sanitizeUserId(userId) val primaryKey = secretKeyRing.publicKey var info = inspectKeyRing(secretKeyRing, referenceTime) val primaryUserId = info.primaryUserId - val signature = if (primaryUserId == null) info.latestDirectKeySelfSignature else info.getLatestUserIdCertification(primaryUserId) + val signature = + if (primaryUserId == null) info.latestDirectKeySelfSignature + else info.getLatestUserIdCertification(primaryUserId) val previousKeyExpiration = signature?.getKeyExpirationDate(primaryKey.creationTime) // Add new primary user-id signature - addUserId(uid, object : SelfSignatureSubpackets.Callback { - override fun modifyHashedSubpackets(hashedSubpackets: SelfSignatureSubpackets) { - hashedSubpackets.apply { - setPrimaryUserId() - if (previousKeyExpiration != null) setKeyExpirationTime(primaryKey, previousKeyExpiration) - else setKeyExpirationTime(null) + addUserId( + uid, + object : SelfSignatureSubpackets.Callback { + override fun modifyHashedSubpackets(hashedSubpackets: SelfSignatureSubpackets) { + hashedSubpackets.apply { + setPrimaryUserId() + if (previousKeyExpiration != null) + setKeyExpirationTime(primaryKey, previousKeyExpiration) + else setKeyExpirationTime(null) + } } - } - }, protector) + }, + protector) // unmark previous primary user-ids to be non-primary info = inspectKeyRing(secretKeyRing, referenceTime) - info.validAndExpiredUserIds.filterNot { it == uid }.forEach { otherUserId -> - if (info.getLatestUserIdCertification(otherUserId)!!.hashedSubPackets.isPrimaryUserID) { - // We need to unmark this user-id as primary - addUserId(otherUserId, object : SelfSignatureSubpackets.Callback { - override fun modifyHashedSubpackets(hashedSubpackets: SelfSignatureSubpackets) { - hashedSubpackets.apply { - setPrimaryUserId(null) - setKeyExpirationTime(null) // non-primary - } - } - }, protector) + info.validAndExpiredUserIds + .filterNot { it == uid } + .forEach { otherUserId -> + if (info + .getLatestUserIdCertification(otherUserId)!! + .hashedSubPackets + .isPrimaryUserID) { + // We need to unmark this user-id as primary + addUserId( + otherUserId, + object : SelfSignatureSubpackets.Callback { + override fun modifyHashedSubpackets( + hashedSubpackets: SelfSignatureSubpackets + ) { + hashedSubpackets.apply { + setPrimaryUserId(null) + setKeyExpirationTime(null) // non-primary + } + } + }, + protector) + } } - } return this } - @Deprecated("Use of SelectUserId class is deprecated.", replaceWith = ReplaceWith("removeUserId(protector, predicate)")) - override fun removeUserId(selector: SelectUserId, protector: SecretKeyRingProtector): SecretKeyRingEditorInterface { - return revokeUserIds(selector, protector, RevocationAttributes.createCertificateRevocation() + @Deprecated( + "Use of SelectUserId class is deprecated.", + replaceWith = ReplaceWith("removeUserId(protector, predicate)")) + override fun removeUserId( + selector: SelectUserId, + protector: SecretKeyRingProtector + ): SecretKeyRingEditorInterface { + return revokeUserIds( + selector, + protector, + RevocationAttributes.createCertificateRevocation() .withReason(RevocationAttributes.Reason.USER_ID_NO_LONGER_VALID) .withoutDescription()) } - override fun removeUserId(protector: SecretKeyRingProtector, predicate: (String) -> Boolean): SecretKeyRingEditorInterface { - return revokeUserIds(protector, RevocationAttributes.createCertificateRevocation() + override fun removeUserId( + protector: SecretKeyRingProtector, + predicate: (String) -> Boolean + ): SecretKeyRingEditorInterface { + return revokeUserIds( + protector, + RevocationAttributes.createCertificateRevocation() .withReason(RevocationAttributes.Reason.USER_ID_NO_LONGER_VALID) .withoutDescription(), - predicate) + predicate) } - override fun removeUserId(userId: CharSequence, protector: SecretKeyRingProtector): SecretKeyRingEditorInterface { + override fun removeUserId( + userId: CharSequence, + protector: SecretKeyRingProtector + ): SecretKeyRingEditorInterface { return removeUserId(protector) { uid -> userId == uid } } - override fun replaceUserId(oldUserId: CharSequence, newUserId: CharSequence, protector: SecretKeyRingProtector): SecretKeyRingEditorInterface { + override fun replaceUserId( + oldUserId: CharSequence, + newUserId: CharSequence, + protector: SecretKeyRingProtector + ): SecretKeyRingEditorInterface { val oldUID = sanitizeUserId(oldUserId) val newUID = sanitizeUserId(newUserId) require(oldUID.isNotBlank()) { "Old user-ID cannot be empty." } @@ -137,148 +193,230 @@ class SecretKeyRingEditor( val info = inspectKeyRing(secretKeyRing, referenceTime) if (!info.isUserIdValid(oldUID)) { - throw NoSuchElementException("Key does not carry user-ID '$oldUID', or it is not valid.") + throw NoSuchElementException( + "Key does not carry user-ID '$oldUID', or it is not valid.") } - val oldCertification = info.getLatestUserIdCertification(oldUID) + val oldCertification = + info.getLatestUserIdCertification(oldUID) ?: throw AssertionError("Certification for old user-ID MUST NOT be null.") - addUserId(newUID, object : SelfSignatureSubpackets.Callback { - override fun modifyHashedSubpackets(hashedSubpackets: SelfSignatureSubpackets) { - SignatureSubpacketsHelper.applyFrom(oldCertification.hashedSubPackets, hashedSubpackets as SignatureSubpackets) - if (oldUID == info.primaryUserId && !oldCertification.hashedSubPackets.isPrimaryUserID) { - hashedSubpackets.setPrimaryUserId() + addUserId( + newUID, + object : SelfSignatureSubpackets.Callback { + override fun modifyHashedSubpackets(hashedSubpackets: SelfSignatureSubpackets) { + SignatureSubpacketsHelper.applyFrom( + oldCertification.hashedSubPackets, hashedSubpackets as SignatureSubpackets) + if (oldUID == info.primaryUserId && + !oldCertification.hashedSubPackets.isPrimaryUserID) { + hashedSubpackets.setPrimaryUserId() + } } - } - override fun modifyUnhashedSubpackets(unhashedSubpackets: SelfSignatureSubpackets) { - SignatureSubpacketsHelper.applyFrom(oldCertification.unhashedSubPackets, unhashedSubpackets as SignatureSubpackets) - } - }, protector) + override fun modifyUnhashedSubpackets(unhashedSubpackets: SelfSignatureSubpackets) { + SignatureSubpacketsHelper.applyFrom( + oldCertification.unhashedSubPackets, + unhashedSubpackets as SignatureSubpackets) + } + }, + protector) return revokeUserId(oldUID, protector) } - override fun addSubKey(keySpec: KeySpec, - subkeyPassphrase: Passphrase, - protector: SecretKeyRingProtector): SecretKeyRingEditorInterface { - val callback = object : SelfSignatureSubpackets.Callback { - override fun modifyHashedSubpackets(hashedSubpackets: SelfSignatureSubpackets) { - SignatureSubpacketsHelper.applyFrom(keySpec.subpackets, hashedSubpackets as SignatureSubpackets) + override fun addSubKey( + keySpec: KeySpec, + subkeyPassphrase: Passphrase, + protector: SecretKeyRingProtector + ): SecretKeyRingEditorInterface { + val callback = + object : SelfSignatureSubpackets.Callback { + override fun modifyHashedSubpackets(hashedSubpackets: SelfSignatureSubpackets) { + SignatureSubpacketsHelper.applyFrom( + keySpec.subpackets, hashedSubpackets as SignatureSubpackets) + } } - } return addSubKey(keySpec, subkeyPassphrase, callback, protector) } - override fun addSubKey(keySpec: KeySpec, - subkeyPassphrase: Passphrase, - callback: SelfSignatureSubpackets.Callback?, - protector: SecretKeyRingProtector): SecretKeyRingEditorInterface { + override fun addSubKey( + keySpec: KeySpec, + subkeyPassphrase: Passphrase, + callback: SelfSignatureSubpackets.Callback?, + protector: SecretKeyRingProtector + ): SecretKeyRingEditorInterface { val keyPair = KeyRingBuilder.generateKeyPair(keySpec) - val subkeyProtector = PasswordBasedSecretKeyRingProtector.forKeyId(keyPair.keyID, subkeyPassphrase) + val subkeyProtector = + PasswordBasedSecretKeyRingProtector.forKeyId(keyPair.keyID, subkeyPassphrase) val keyFlags = KeyFlag.fromBitmask(keySpec.subpackets.keyFlags).toMutableList() - return addSubKey(keyPair, callback, subkeyProtector, protector, keyFlags.removeFirst(), *keyFlags.toTypedArray()) + return addSubKey( + keyPair, + callback, + subkeyProtector, + protector, + keyFlags.removeFirst(), + *keyFlags.toTypedArray()) } - override fun addSubKey(subkey: PGPKeyPair, - callback: SelfSignatureSubpackets.Callback?, - subkeyProtector: SecretKeyRingProtector, - primaryKeyProtector: SecretKeyRingProtector, - keyFlag: KeyFlag, - vararg keyFlags: KeyFlag): SecretKeyRingEditorInterface { + override fun addSubKey( + subkey: PGPKeyPair, + callback: SelfSignatureSubpackets.Callback?, + subkeyProtector: SecretKeyRingProtector, + primaryKeyProtector: SecretKeyRingProtector, + keyFlag: KeyFlag, + vararg keyFlags: KeyFlag + ): SecretKeyRingEditorInterface { val flags = listOf(keyFlag).plus(keyFlags) val subkeyAlgorithm = subkey.publicKey.publicKeyAlgorithm SignatureSubpacketsUtil.assureKeyCanCarryFlags(subkeyAlgorithm) val bitStrength = subkey.publicKey.bitStrength - require(PGPainless.getPolicy().publicKeyAlgorithmPolicy.isAcceptable(subkeyAlgorithm, bitStrength)) { - "Public key algorithm policy violation: $subkeyAlgorithm with bit strength $bitStrength is not acceptable." - } + require( + PGPainless.getPolicy() + .publicKeyAlgorithmPolicy + .isAcceptable(subkeyAlgorithm, bitStrength)) { + "Public key algorithm policy violation: $subkeyAlgorithm with bit strength $bitStrength is not acceptable." + } val primaryKey = secretKeyRing.secretKey val info = inspectKeyRing(secretKeyRing, referenceTime) - val hashAlgorithm = HashAlgorithmNegotiator.negotiateSignatureHashAlgorithm(PGPainless.getPolicy()) + val hashAlgorithm = + HashAlgorithmNegotiator.negotiateSignatureHashAlgorithm(PGPainless.getPolicy()) .negotiateHashAlgorithm(info.preferredHashAlgorithms) - var secretSubkey = PGPSecretKey(subkey.privateKey, subkey.publicKey, + var secretSubkey = + PGPSecretKey( + subkey.privateKey, + subkey.publicKey, ImplementationFactory.getInstance().v4FingerprintCalculator, - false, subkeyProtector.getEncryptor(subkey.keyID)) - val skBindingBuilder = SubkeyBindingSignatureBuilder(primaryKey, primaryKeyProtector, hashAlgorithm) + false, + subkeyProtector.getEncryptor(subkey.keyID)) + val skBindingBuilder = + SubkeyBindingSignatureBuilder(primaryKey, primaryKeyProtector, hashAlgorithm) skBindingBuilder.apply { hashedSubpackets.setSignatureCreationTime(referenceTime) hashedSubpackets.setKeyFlags(flags) if (subkeyAlgorithm.isSigningCapable()) { - val pkBindingBuilder = PrimaryKeyBindingSignatureBuilder(secretSubkey, subkeyProtector, hashAlgorithm) + val pkBindingBuilder = + PrimaryKeyBindingSignatureBuilder(secretSubkey, subkeyProtector, hashAlgorithm) pkBindingBuilder.hashedSubpackets.setSignatureCreationTime(referenceTime) hashedSubpackets.addEmbeddedSignature(pkBindingBuilder.build(primaryKey.publicKey)) } applyCallback(callback) } - secretSubkey = KeyRingUtils.secretKeyPlusSignature(secretSubkey, skBindingBuilder.build(secretSubkey.publicKey)) + secretSubkey = + KeyRingUtils.secretKeyPlusSignature( + secretSubkey, skBindingBuilder.build(secretSubkey.publicKey)) secretKeyRing = KeyRingUtils.keysPlusSecretKey(secretKeyRing, secretSubkey) return this } - override fun revoke(protector: SecretKeyRingProtector, revocationAttributes: RevocationAttributes?): SecretKeyRingEditorInterface { + override fun revoke( + protector: SecretKeyRingProtector, + revocationAttributes: RevocationAttributes? + ): SecretKeyRingEditorInterface { return revoke(protector, callbackFromRevocationAttributes(revocationAttributes)) } - override fun revoke(protector: SecretKeyRingProtector, callback: RevocationSignatureSubpackets.Callback?): SecretKeyRingEditorInterface { + override fun revoke( + protector: SecretKeyRingProtector, + callback: RevocationSignatureSubpackets.Callback? + ): SecretKeyRingEditorInterface { return revokeSubKey(secretKeyRing.secretKey.keyID, protector, callback) } - override fun revokeSubKey(subkeyId: Long, protector: SecretKeyRingProtector, revocationAttributes: RevocationAttributes?): SecretKeyRingEditorInterface { - return revokeSubKey(subkeyId, protector, callbackFromRevocationAttributes(revocationAttributes)) + override fun revokeSubKey( + subkeyId: Long, + protector: SecretKeyRingProtector, + revocationAttributes: RevocationAttributes? + ): SecretKeyRingEditorInterface { + return revokeSubKey( + subkeyId, protector, callbackFromRevocationAttributes(revocationAttributes)) } - override fun revokeSubKey(subkeyId: Long, protector: SecretKeyRingProtector, callback: RevocationSignatureSubpackets.Callback?): SecretKeyRingEditorInterface { + override fun revokeSubKey( + subkeyId: Long, + protector: SecretKeyRingProtector, + callback: RevocationSignatureSubpackets.Callback? + ): SecretKeyRingEditorInterface { val revokeeSubKey = secretKeyRing.requirePublicKey(subkeyId) val subkeyRevocation = generateRevocation(protector, revokeeSubKey, callback) secretKeyRing = injectCertification(secretKeyRing, revokeeSubKey, subkeyRevocation) return this } - override fun revokeUserId(userId: CharSequence, protector: SecretKeyRingProtector, revocationAttributes: RevocationAttributes?): SecretKeyRingEditorInterface { + override fun revokeUserId( + userId: CharSequence, + protector: SecretKeyRingProtector, + revocationAttributes: RevocationAttributes? + ): SecretKeyRingEditorInterface { if (revocationAttributes != null) { - require(revocationAttributes.reason == RevocationAttributes.Reason.NO_REASON || - revocationAttributes.reason == RevocationAttributes.Reason.USER_ID_NO_LONGER_VALID) { - "Revocation reason must either be NO_REASON or USER_ID_NO_LONGER_VALID" - } + require( + revocationAttributes.reason == RevocationAttributes.Reason.NO_REASON || + revocationAttributes.reason == + RevocationAttributes.Reason.USER_ID_NO_LONGER_VALID) { + "Revocation reason must either be NO_REASON or USER_ID_NO_LONGER_VALID" + } } - return revokeUserId(userId, protector, object : RevocationSignatureSubpackets.Callback { - override fun modifyHashedSubpackets(hashedSubpackets: RevocationSignatureSubpackets) { - if (revocationAttributes != null) { - hashedSubpackets.setRevocationReason(false, revocationAttributes) + return revokeUserId( + userId, + protector, + object : RevocationSignatureSubpackets.Callback { + override fun modifyHashedSubpackets( + hashedSubpackets: RevocationSignatureSubpackets + ) { + if (revocationAttributes != null) { + hashedSubpackets.setRevocationReason(false, revocationAttributes) + } } - } - }) + }) } - override fun revokeUserId(userId: CharSequence, protector: SecretKeyRingProtector, callback: RevocationSignatureSubpackets.Callback?): SecretKeyRingEditorInterface { + override fun revokeUserId( + userId: CharSequence, + protector: SecretKeyRingProtector, + callback: RevocationSignatureSubpackets.Callback? + ): SecretKeyRingEditorInterface { return revokeUserIds(protector, callback, SelectUserId.exactMatch(sanitizeUserId(userId))) } - override fun revokeUserIds(protector: SecretKeyRingProtector, - revocationAttributes: RevocationAttributes?, - predicate: (String) -> Boolean): SecretKeyRingEditorInterface { - return revokeUserIds(protector, object : RevocationSignatureSubpackets.Callback { - override fun modifyHashedSubpackets(hashedSubpackets: RevocationSignatureSubpackets) { - if (revocationAttributes != null) hashedSubpackets.setRevocationReason(revocationAttributes) - } - }, predicate) + override fun revokeUserIds( + protector: SecretKeyRingProtector, + revocationAttributes: RevocationAttributes?, + predicate: (String) -> Boolean + ): SecretKeyRingEditorInterface { + return revokeUserIds( + protector, + object : RevocationSignatureSubpackets.Callback { + override fun modifyHashedSubpackets( + hashedSubpackets: RevocationSignatureSubpackets + ) { + if (revocationAttributes != null) + hashedSubpackets.setRevocationReason(revocationAttributes) + } + }, + predicate) } - override fun revokeUserIds(protector: SecretKeyRingProtector, - callback: RevocationSignatureSubpackets.Callback?, - predicate: (String) -> Boolean): SecretKeyRingEditorInterface { - selectUserIds(predicate).also { - if (it.isEmpty()) throw NoSuchElementException("No matching user-ids found on the key.") - }.forEach { userId -> doRevokeUserId(userId, protector, callback) } + override fun revokeUserIds( + protector: SecretKeyRingProtector, + callback: RevocationSignatureSubpackets.Callback?, + predicate: (String) -> Boolean + ): SecretKeyRingEditorInterface { + selectUserIds(predicate) + .also { + if (it.isEmpty()) + throw NoSuchElementException("No matching user-ids found on the key.") + } + .forEach { userId -> doRevokeUserId(userId, protector, callback) } return this } - override fun setExpirationDate(expiration: Date?, protector: SecretKeyRingProtector): SecretKeyRingEditorInterface { + override fun setExpirationDate( + expiration: Date?, + protector: SecretKeyRingProtector + ): SecretKeyRingEditorInterface { require(secretKeyRing.secretKey.isMasterKey) { "OpenPGP key does not appear to contain a primary secret key." } @@ -286,14 +424,19 @@ class SecretKeyRingEditor( val prevDirectKeySig = getPreviousDirectKeySignature() // reissue direct key sig if (prevDirectKeySig != null) { - secretKeyRing = injectCertification(secretKeyRing, secretKeyRing.publicKey, + secretKeyRing = + injectCertification( + secretKeyRing, + secretKeyRing.publicKey, reissueDirectKeySignature(expiration, protector, prevDirectKeySig)) } - val primaryUserId = inspectKeyRing(secretKeyRing, referenceTime).getPossiblyExpiredPrimaryUserId() + val primaryUserId = + inspectKeyRing(secretKeyRing, referenceTime).getPossiblyExpiredPrimaryUserId() if (primaryUserId != null) { val prevUserIdSig = getPreviousUserIdSignatures(primaryUserId) - val userIdSig = reissuePrimaryUserIdSig(expiration, protector, primaryUserId, prevUserIdSig!!) + val userIdSig = + reissuePrimaryUserIdSig(expiration, protector, primaryUserId, prevUserIdSig!!) secretKeyRing = injectCertification(secretKeyRing, primaryUserId, userIdSig) } @@ -303,9 +446,15 @@ class SecretKeyRingEditor( continue } - val prevUserIdSig = info.getLatestUserIdCertification(userId) ?: throw AssertionError("A valid user-id shall never have no user-id signature.") + val prevUserIdSig = + info.getLatestUserIdCertification(userId) + ?: throw AssertionError( + "A valid user-id shall never have no user-id signature.") if (prevUserIdSig.hashedSubPackets.isPrimaryUserID) { - secretKeyRing = injectCertification(secretKeyRing, primaryUserId!!, + secretKeyRing = + injectCertification( + secretKeyRing, + primaryUserId!!, reissueNonPrimaryUserId(protector, userId, prevUserIdSig)) } } @@ -313,7 +462,10 @@ class SecretKeyRingEditor( return this } - override fun createMinimalRevocationCertificate(protector: SecretKeyRingProtector, revocationAttributes: RevocationAttributes?): PGPPublicKeyRing { + override fun createMinimalRevocationCertificate( + protector: SecretKeyRingProtector, + revocationAttributes: RevocationAttributes? + ): PGPPublicKeyRing { // Check reason if (revocationAttributes != null) { require(RevocationAttributes.Reason.isKeyRevocation(revocationAttributes.reason)) { @@ -328,30 +480,67 @@ class SecretKeyRingEditor( return PGPPublicKeyRing(listOf(primaryKey)) } - override fun createRevocation(protector: SecretKeyRingProtector, revocationAttributes: RevocationAttributes?): PGPSignature { - return generateRevocation(protector, secretKeyRing.publicKey, callbackFromRevocationAttributes(revocationAttributes)) + override fun createRevocation( + protector: SecretKeyRingProtector, + revocationAttributes: RevocationAttributes? + ): PGPSignature { + return generateRevocation( + protector, + secretKeyRing.publicKey, + callbackFromRevocationAttributes(revocationAttributes)) } - override fun createRevocation(subkeyId: Long, protector: SecretKeyRingProtector, revocationAttributes: RevocationAttributes?): PGPSignature { - return generateRevocation(protector, secretKeyRing.requirePublicKey(subkeyId), callbackFromRevocationAttributes(revocationAttributes)) + override fun createRevocation( + subkeyId: Long, + protector: SecretKeyRingProtector, + revocationAttributes: RevocationAttributes? + ): PGPSignature { + return generateRevocation( + protector, + secretKeyRing.requirePublicKey(subkeyId), + callbackFromRevocationAttributes(revocationAttributes)) } - override fun createRevocation(subkeyId: Long, protector: SecretKeyRingProtector, callback: RevocationSignatureSubpackets.Callback?): PGPSignature { + override fun createRevocation( + subkeyId: Long, + protector: SecretKeyRingProtector, + callback: RevocationSignatureSubpackets.Callback? + ): PGPSignature { return generateRevocation(protector, secretKeyRing.requirePublicKey(subkeyId), callback) } - override fun createRevocation(subkeyFingerprint: OpenPgpFingerprint, protector: SecretKeyRingProtector, revocationAttributes: RevocationAttributes?): PGPSignature { - return generateRevocation(protector, secretKeyRing.requirePublicKey(subkeyFingerprint), callbackFromRevocationAttributes(revocationAttributes)) + override fun createRevocation( + subkeyFingerprint: OpenPgpFingerprint, + protector: SecretKeyRingProtector, + revocationAttributes: RevocationAttributes? + ): PGPSignature { + return generateRevocation( + protector, + secretKeyRing.requirePublicKey(subkeyFingerprint), + callbackFromRevocationAttributes(revocationAttributes)) } - override fun changePassphraseFromOldPassphrase(oldPassphrase: Passphrase, oldProtectionSettings: KeyRingProtectionSettings): SecretKeyRingEditorInterface.WithKeyRingEncryptionSettings { - return WithKeyRingEncryptionSettingsImpl(this, null, - PasswordBasedSecretKeyRingProtector(oldProtectionSettings, SolitaryPassphraseProvider(oldPassphrase))) + override fun changePassphraseFromOldPassphrase( + oldPassphrase: Passphrase, + oldProtectionSettings: KeyRingProtectionSettings + ): SecretKeyRingEditorInterface.WithKeyRingEncryptionSettings { + return WithKeyRingEncryptionSettingsImpl( + this, + null, + PasswordBasedSecretKeyRingProtector( + oldProtectionSettings, SolitaryPassphraseProvider(oldPassphrase))) } - override fun changeSubKeyPassphraseFromOldPassphrase(keyId: Long, oldPassphrase: Passphrase, oldProtectionSettings: KeyRingProtectionSettings): SecretKeyRingEditorInterface.WithKeyRingEncryptionSettings { - return WithKeyRingEncryptionSettingsImpl(this, keyId, - CachingSecretKeyRingProtector(mapOf(keyId to oldPassphrase), oldProtectionSettings, null)) + override fun changeSubKeyPassphraseFromOldPassphrase( + keyId: Long, + oldPassphrase: Passphrase, + oldProtectionSettings: KeyRingProtectionSettings + ): SecretKeyRingEditorInterface.WithKeyRingEncryptionSettings { + return WithKeyRingEncryptionSettingsImpl( + this, + keyId, + CachingSecretKeyRingProtector( + mapOf(keyId to oldPassphrase), oldProtectionSettings, null)) } override fun done(): PGPSecretKeyRing { @@ -359,45 +548,52 @@ class SecretKeyRingEditor( } private fun sanitizeUserId(userId: CharSequence): CharSequence = - // TODO: Further research how to sanitize user IDs. - // e.g. what about newlines? - userId.toString().trim() + // TODO: Further research how to sanitize user IDs. + // e.g. what about newlines? + userId.toString().trim() private fun callbackFromRevocationAttributes(attributes: RevocationAttributes?) = - object : RevocationSignatureSubpackets.Callback { - override fun modifyHashedSubpackets(hashedSubpackets: RevocationSignatureSubpackets) { - if (attributes != null) { - hashedSubpackets.setRevocationReason(attributes) - } + object : RevocationSignatureSubpackets.Callback { + override fun modifyHashedSubpackets(hashedSubpackets: RevocationSignatureSubpackets) { + if (attributes != null) { + hashedSubpackets.setRevocationReason(attributes) } } + } - private fun generateRevocation(protector: SecretKeyRingProtector, - revokeeSubkey: PGPPublicKey, - callback: RevocationSignatureSubpackets.Callback?): PGPSignature { + private fun generateRevocation( + protector: SecretKeyRingProtector, + revokeeSubkey: PGPPublicKey, + callback: RevocationSignatureSubpackets.Callback? + ): PGPSignature { val primaryKey = secretKeyRing.secretKey val signatureType = - if (revokeeSubkey.isMasterKey) SignatureType.KEY_REVOCATION - else SignatureType.SUBKEY_REVOCATION + if (revokeeSubkey.isMasterKey) SignatureType.KEY_REVOCATION + else SignatureType.SUBKEY_REVOCATION return RevocationSignatureBuilder(signatureType, primaryKey, protector) - .apply { applyCallback(callback) } - .build(revokeeSubkey) + .apply { applyCallback(callback) } + .build(revokeeSubkey) } - private fun doRevokeUserId(userId: CharSequence, - protector: SecretKeyRingProtector, - callback: RevocationSignatureSubpackets.Callback?): SecretKeyRingEditorInterface { - RevocationSignatureBuilder(SignatureType.CERTIFICATION_REVOCATION, secretKeyRing.secretKey, protector).apply { - hashedSubpackets.setSignatureCreationTime(referenceTime) - applyCallback(callback) - }.let { - secretKeyRing = injectCertification(secretKeyRing, userId, it.build(userId.toString())) - } + private fun doRevokeUserId( + userId: CharSequence, + protector: SecretKeyRingProtector, + callback: RevocationSignatureSubpackets.Callback? + ): SecretKeyRingEditorInterface { + RevocationSignatureBuilder( + SignatureType.CERTIFICATION_REVOCATION, secretKeyRing.secretKey, protector) + .apply { + hashedSubpackets.setSignatureCreationTime(referenceTime) + applyCallback(callback) + } + .let { + secretKeyRing = + injectCertification(secretKeyRing, userId, it.build(userId.toString())) + } return this } - private fun getPreviousDirectKeySignature(): PGPSignature? { val info = inspectKeyRing(secretKeyRing, referenceTime) return info.latestDirectKeySelfSignature @@ -410,88 +606,109 @@ class SecretKeyRingEditor( @Throws(PGPException::class) private fun reissueNonPrimaryUserId( - secretKeyRingProtector: SecretKeyRingProtector, - userId: String, - prevUserIdSig: PGPSignature): PGPSignature { - val builder = SelfSignatureBuilder(secretKeyRing.secretKey, secretKeyRingProtector, prevUserIdSig) + secretKeyRingProtector: SecretKeyRingProtector, + userId: String, + prevUserIdSig: PGPSignature + ): PGPSignature { + val builder = + SelfSignatureBuilder(secretKeyRing.secretKey, secretKeyRingProtector, prevUserIdSig) builder.hashedSubpackets.setSignatureCreationTime(referenceTime) - builder.applyCallback(object : SelfSignatureSubpackets.Callback { - override fun modifyHashedSubpackets(hashedSubpackets: SelfSignatureSubpackets) { - // unmark as primary - hashedSubpackets.setPrimaryUserId(null) - } - }) + builder.applyCallback( + object : SelfSignatureSubpackets.Callback { + override fun modifyHashedSubpackets(hashedSubpackets: SelfSignatureSubpackets) { + // unmark as primary + hashedSubpackets.setPrimaryUserId(null) + } + }) return builder.build(secretKeyRing.publicKey, userId) } @Throws(PGPException::class) private fun reissuePrimaryUserIdSig( - expiration: Date?, - @Nonnull secretKeyRingProtector: SecretKeyRingProtector, - @Nonnull primaryUserId: String, - @Nonnull prevUserIdSig: PGPSignature): PGPSignature { + expiration: Date?, + @Nonnull secretKeyRingProtector: SecretKeyRingProtector, + @Nonnull primaryUserId: String, + @Nonnull prevUserIdSig: PGPSignature + ): PGPSignature { return SelfSignatureBuilder(secretKeyRing.secretKey, secretKeyRingProtector, prevUserIdSig) - .apply { - hashedSubpackets.setSignatureCreationTime(referenceTime) - applyCallback(object : SelfSignatureSubpackets.Callback { - override fun modifyHashedSubpackets(hashedSubpackets: SelfSignatureSubpackets) { + .apply { + hashedSubpackets.setSignatureCreationTime(referenceTime) + applyCallback( + object : SelfSignatureSubpackets.Callback { + override fun modifyHashedSubpackets( + hashedSubpackets: SelfSignatureSubpackets + ) { if (expiration != null) { - hashedSubpackets.setKeyExpirationTime(true, secretKeyRing.publicKey.creationTime, expiration) + hashedSubpackets.setKeyExpirationTime( + true, secretKeyRing.publicKey.creationTime, expiration) } else { hashedSubpackets.setKeyExpirationTime(KeyExpirationTime(true, 0)) } hashedSubpackets.setPrimaryUserId() } }) - }.build(secretKeyRing.publicKey, primaryUserId) + } + .build(secretKeyRing.publicKey, primaryUserId) } @Throws(PGPException::class) private fun reissueDirectKeySignature( - expiration: Date?, - secretKeyRingProtector: SecretKeyRingProtector, - prevDirectKeySig: PGPSignature): PGPSignature { - return DirectKeySelfSignatureBuilder(secretKeyRing.secretKey, secretKeyRingProtector, prevDirectKeySig) - .apply { - hashedSubpackets.setSignatureCreationTime(referenceTime) - applyCallback(object : SelfSignatureSubpackets.Callback { - override fun modifyHashedSubpackets(hashedSubpackets: SelfSignatureSubpackets) { + expiration: Date?, + secretKeyRingProtector: SecretKeyRingProtector, + prevDirectKeySig: PGPSignature + ): PGPSignature { + return DirectKeySelfSignatureBuilder( + secretKeyRing.secretKey, secretKeyRingProtector, prevDirectKeySig) + .apply { + hashedSubpackets.setSignatureCreationTime(referenceTime) + applyCallback( + object : SelfSignatureSubpackets.Callback { + override fun modifyHashedSubpackets( + hashedSubpackets: SelfSignatureSubpackets + ) { if (expiration != null) { - hashedSubpackets.setKeyExpirationTime(secretKeyRing.publicKey.creationTime, expiration) + hashedSubpackets.setKeyExpirationTime( + secretKeyRing.publicKey.creationTime, expiration) } else { hashedSubpackets.setKeyExpirationTime(null) } } }) - }.build(secretKeyRing.publicKey) + } + .build(secretKeyRing.publicKey) } private fun selectUserIds(predicate: Predicate): List = - inspectKeyRing(secretKeyRing).validUserIds.filter { predicate.test(it) } + inspectKeyRing(secretKeyRing).validUserIds.filter { predicate.test(it) } private class WithKeyRingEncryptionSettingsImpl( - private val editor: SecretKeyRingEditor, - private val keyId: Long?, - private val oldProtector: SecretKeyRingProtector) : SecretKeyRingEditorInterface.WithKeyRingEncryptionSettings { + private val editor: SecretKeyRingEditor, + private val keyId: Long?, + private val oldProtector: SecretKeyRingProtector + ) : SecretKeyRingEditorInterface.WithKeyRingEncryptionSettings { override fun withSecureDefaultSettings(): SecretKeyRingEditorInterface.WithPassphrase { return withCustomSettings(KeyRingProtectionSettings.secureDefaultSettings()) } - override fun withCustomSettings(settings: KeyRingProtectionSettings): SecretKeyRingEditorInterface.WithPassphrase { + override fun withCustomSettings( + settings: KeyRingProtectionSettings + ): SecretKeyRingEditorInterface.WithPassphrase { return WithPassphraseImpl(editor, keyId, oldProtector, settings) } } private class WithPassphraseImpl( - private val editor: SecretKeyRingEditor, - private val keyId: Long?, - private val oldProtector: SecretKeyRingProtector, - private val newProtectionSettings: KeyRingProtectionSettings + private val editor: SecretKeyRingEditor, + private val keyId: Long?, + private val oldProtector: SecretKeyRingProtector, + private val newProtectionSettings: KeyRingProtectionSettings ) : SecretKeyRingEditorInterface.WithPassphrase { override fun toNewPassphrase(passphrase: Passphrase): SecretKeyRingEditorInterface { - val protector = PasswordBasedSecretKeyRingProtector(newProtectionSettings, SolitaryPassphraseProvider(passphrase)) + val protector = + PasswordBasedSecretKeyRingProtector( + newProtectionSettings, SolitaryPassphraseProvider(passphrase)) val secretKeys = changePassphrase(keyId, editor.secretKeyRing, oldProtector, protector) editor.secretKeyRing = secretKeys return editor @@ -504,5 +721,4 @@ class SecretKeyRingEditor( return editor } } - -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditorInterface.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditorInterface.kt index 8a26161b..b8fb993c 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditorInterface.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditorInterface.kt @@ -4,6 +4,10 @@ package org.pgpainless.key.modification.secretkeyring +import java.io.IOException +import java.security.InvalidAlgorithmParameterException +import java.security.NoSuchAlgorithmException +import java.util.* import org.bouncycastle.openpgp.* import org.pgpainless.algorithm.KeyFlag import org.pgpainless.key.OpenPgpFingerprint @@ -15,18 +19,12 @@ import org.pgpainless.signature.subpackets.RevocationSignatureSubpackets import org.pgpainless.signature.subpackets.SelfSignatureSubpackets import org.pgpainless.util.Passphrase import org.pgpainless.util.selection.userid.SelectUserId -import java.io.IOException -import java.security.InvalidAlgorithmParameterException -import java.security.NoSuchAlgorithmException -import java.util.* -import java.util.function.Predicate interface SecretKeyRingEditorInterface { /** - * Editors reference time. - * This time is used as creation date for new signatures, or as reference when evaluating expiration of - * existing signatures. + * Editors reference time. This time is used as creation date for new signatures, or as + * reference when evaluating expiration of existing signatures. */ val referenceTime: Date @@ -36,11 +34,11 @@ interface SecretKeyRingEditorInterface { * @param userId user-id * @param protector protector to unlock the secret key * @return the builder - * * @throws PGPException in case we cannot generate a signature for the user-id */ @Throws(PGPException::class) - fun addUserId(userId: CharSequence, protector: SecretKeyRingProtector) = addUserId(userId, null, protector) + fun addUserId(userId: CharSequence, protector: SecretKeyRingProtector) = + addUserId(userId, null, protector) /** * Add a user-id to the key ring. @@ -49,105 +47,126 @@ interface SecretKeyRingEditorInterface { * @param callback callback to modify the self-signature subpackets * @param protector protector to unlock the secret key * @return the builder - * * @throws PGPException in case we cannot generate a signature for the user-id */ @Throws(PGPException::class) - fun addUserId(userId: CharSequence, callback: SelfSignatureSubpackets.Callback? = null, protector: SecretKeyRingProtector): SecretKeyRingEditorInterface + fun addUserId( + userId: CharSequence, + callback: SelfSignatureSubpackets.Callback? = null, + protector: SecretKeyRingProtector + ): SecretKeyRingEditorInterface /** - * Add a user-id to the key ring and mark it as primary. - * If the user-id is already present, a new certification signature will be created. + * Add a user-id to the key ring and mark it as primary. If the user-id is already present, a + * new certification signature will be created. * * @param userId user id * @param protector protector to unlock the secret key * @return the builder - * * @throws PGPException in case we cannot generate a signature for the user-id */ @Throws(PGPException::class) - fun addPrimaryUserId(userId: CharSequence, protector: SecretKeyRingProtector): SecretKeyRingEditorInterface + fun addPrimaryUserId( + userId: CharSequence, + protector: SecretKeyRingProtector + ): SecretKeyRingEditorInterface /** - * Convenience method to revoke selected user-ids using soft revocation signatures. - * The revocation will use [RevocationAttributes.Reason.USER_ID_NO_LONGER_VALID], so that the user-id - * can be re-certified at a later point. + * Convenience method to revoke selected user-ids using soft revocation signatures. The + * revocation will use [RevocationAttributes.Reason.USER_ID_NO_LONGER_VALID], so that the + * user-id can be re-certified at a later point. * * @param selector selector to select user-ids * @param protector protector to unlock the primary key * @return the builder - * * @throws PGPException in case we cannot generate a revocation signature for the user-id */ - @Deprecated("Use of SelectUserId class is deprecated.", - ReplaceWith("removeUserId(protector, predicate)")) + @Deprecated( + "Use of SelectUserId class is deprecated.", + ReplaceWith("removeUserId(protector, predicate)")) @Throws(PGPException::class) fun removeUserId(selector: SelectUserId, protector: SecretKeyRingProtector) = - removeUserId(protector, selector) + removeUserId(protector, selector) /** - * Convenience method to revoke selected user-ids using soft revocation signatures. - * The revocation will use [RevocationAttributes.Reason.USER_ID_NO_LONGER_VALID], so that the user-id - * can be re-certified at a later point. + * Convenience method to revoke selected user-ids using soft revocation signatures. The + * revocation will use [RevocationAttributes.Reason.USER_ID_NO_LONGER_VALID], so that the + * user-id can be re-certified at a later point. * * @param protector protector to unlock the primary key * @param predicate predicate to select user-ids for revocation * @return the builder - * * @throws PGPException in case we cannot generate a revocation signature for the user-id */ @Throws(PGPException::class) - fun removeUserId(protector: SecretKeyRingProtector, predicate: (String) -> Boolean): SecretKeyRingEditorInterface + fun removeUserId( + protector: SecretKeyRingProtector, + predicate: (String) -> Boolean + ): SecretKeyRingEditorInterface /** - * Convenience method to revoke a single user-id using a soft revocation signature. - * The revocation will use [RevocationAttributes.Reason.USER_ID_NO_LONGER_VALID] so that the user-id + * Convenience method to revoke a single user-id using a soft revocation signature. The + * revocation will use [RevocationAttributes.Reason.USER_ID_NO_LONGER_VALID] so that the user-id * can be re-certified at a later point. * * @param userId user-id to revoke * @param protector protector to unlock the primary key * @return the builder - * * @throws PGPException in case we cannot generate a revocation signature for the user-id */ @Throws(PGPException::class) - fun removeUserId(userId: CharSequence, protector: SecretKeyRingProtector): SecretKeyRingEditorInterface + fun removeUserId( + userId: CharSequence, + protector: SecretKeyRingProtector + ): SecretKeyRingEditorInterface /** - * Replace a user-id on the key with a new one. - * The old user-id gets soft revoked and the new user-id gets bound with the same signature subpackets as the - * old one, with one exception: - * If the old user-id was implicitly primary (did not carry a [org.bouncycastle.bcpg.sig.PrimaryUserID] packet, - * but effectively was primary), then the new user-id will be explicitly marked as primary. + * Replace a user-id on the key with a new one. The old user-id gets soft revoked and the new + * user-id gets bound with the same signature subpackets as the old one, with one exception: If + * the old user-id was implicitly primary (did not carry a + * [org.bouncycastle.bcpg.sig.PrimaryUserID] packet, but effectively was primary), then the new + * user-id will be explicitly marked as primary. * * @param oldUserId old user-id * @param newUserId new user-id * @param protector protector to unlock the secret key * @return the builder * @throws PGPException in case we cannot generate a revocation and certification signature - * @throws java.util.NoSuchElementException if the old user-id was not found on the key; or if the oldUserId - * was already invalid + * @throws java.util.NoSuchElementException if the old user-id was not found on the key; or if + * the oldUserId was already invalid */ @Throws(PGPException::class) - fun replaceUserId(oldUserId: CharSequence, newUserId: CharSequence, protector: SecretKeyRingProtector): SecretKeyRingEditorInterface + fun replaceUserId( + oldUserId: CharSequence, + newUserId: CharSequence, + protector: SecretKeyRingProtector + ): SecretKeyRingEditorInterface /** - * Add a subkey to the key ring. - * The subkey will be generated from the provided [KeySpec]. + * Add a subkey to the key ring. The subkey will be generated from the provided [KeySpec]. * * @param keySpec key specification * @param subkeyPassphrase passphrase to encrypt the sub key * @param callback callback to modify the subpackets of the subkey binding signature * @param protector protector to unlock the secret key of the key ring * @return the builder - * - * @throws InvalidAlgorithmParameterException in case the user wants to use invalid parameters for the key + * @throws InvalidAlgorithmParameterException in case the user wants to use invalid parameters + * for the key * @throws NoSuchAlgorithmException in case of missing algorithm support in the crypto backend * @throws PGPException in case we cannot generate a binding signature for the subkey * @throws IOException in case of an IO error */ - @Throws(PGPException::class, IOException::class, InvalidAlgorithmParameterException::class, NoSuchAlgorithmException::class) - fun addSubKey(keySpec: KeySpec, subkeyPassphrase: Passphrase, callback: SelfSignatureSubpackets.Callback? = null, protector: SecretKeyRingProtector): SecretKeyRingEditorInterface + @Throws( + PGPException::class, + IOException::class, + InvalidAlgorithmParameterException::class, + NoSuchAlgorithmException::class) + fun addSubKey( + keySpec: KeySpec, + subkeyPassphrase: Passphrase, + callback: SelfSignatureSubpackets.Callback? = null, + protector: SecretKeyRingProtector + ): SecretKeyRingEditorInterface /** * Add a subkey to the key ring. @@ -159,140 +178,140 @@ interface SecretKeyRingEditorInterface { * @param keyFlag first mandatory key flag for the subkey * @param keyFlags optional additional key flags * @return builder - * * @throws PGPException in case we cannot generate a binding signature for the subkey * @throws IOException in case of an IO error */ @Throws(PGPException::class, IOException::class) - fun addSubKey(subkey: PGPKeyPair, - callback: SelfSignatureSubpackets.Callback?, - subkeyProtector: SecretKeyRingProtector, - primaryKeyProtector: SecretKeyRingProtector, - keyFlag: KeyFlag, - vararg keyFlags: KeyFlag): SecretKeyRingEditorInterface + fun addSubKey( + subkey: PGPKeyPair, + callback: SelfSignatureSubpackets.Callback?, + subkeyProtector: SecretKeyRingProtector, + primaryKeyProtector: SecretKeyRingProtector, + keyFlag: KeyFlag, + vararg keyFlags: KeyFlag + ): SecretKeyRingEditorInterface /** * Revoke the key ring using a hard revocation. * * @param protector protector of the primary key * @return the builder - * * @throws PGPException in case we cannot generate a revocation signature */ @Throws(PGPException::class) fun revoke(protector: SecretKeyRingProtector) = revoke(protector, null as RevocationAttributes?) /** - * Revoke the key ring using the provided revocation attributes. - * The attributes define, whether the revocation was a hard revocation or not. + * Revoke the key ring using the provided revocation attributes. The attributes define, whether + * the revocation was a hard revocation or not. * * @param protector protector of the primary key * @param revocationAttributes reason for the revocation * @return the builder - * * @throws PGPException in case we cannot generate a revocation signature */ @Throws(PGPException::class) - fun revoke(protector: SecretKeyRingProtector, revocationAttributes: RevocationAttributes? = null): SecretKeyRingEditorInterface + fun revoke( + protector: SecretKeyRingProtector, + revocationAttributes: RevocationAttributes? = null + ): SecretKeyRingEditorInterface /** - * Revoke the key ring. - * You can use the [RevocationSignatureSubpackets.Callback] to modify the revocation signatures - * subpackets, e.g. in order to define whether this is a hard or soft revocation. + * Revoke the key ring. You can use the [RevocationSignatureSubpackets.Callback] to modify the + * revocation signatures subpackets, e.g. in order to define whether this is a hard or soft + * revocation. * * @param protector protector to unlock the primary secret key * @param callback callback to modify the revocations subpackets * @return builder - * * @throws PGPException in case we cannot generate a revocation signature */ @Throws(PGPException::class) - fun revoke(protector: SecretKeyRingProtector, callback: RevocationSignatureSubpackets.Callback?): SecretKeyRingEditorInterface + fun revoke( + protector: SecretKeyRingProtector, + callback: RevocationSignatureSubpackets.Callback? + ): SecretKeyRingEditorInterface /** - * Revoke the subkey binding signature of a subkey. - * The subkey with the provided fingerprint will be revoked. - * If no suitable subkey is found, a [NoSuchElementException] will be thrown. + * Revoke the subkey binding signature of a subkey. The subkey with the provided fingerprint + * will be revoked. If no suitable subkey is found, a [NoSuchElementException] will be thrown. * * @param fingerprint fingerprint of the subkey to be revoked * @param protector protector to unlock the primary key * @return the builder - * * @throws PGPException in case we cannot generate a revocation signature for the subkey */ @Throws(PGPException::class) - fun revokeSubKey(fingerprint: OpenPgpFingerprint, - protector: SecretKeyRingProtector) = revokeSubKey(fingerprint, protector, null) + fun revokeSubKey(fingerprint: OpenPgpFingerprint, protector: SecretKeyRingProtector) = + revokeSubKey(fingerprint, protector, null) /** - * Revoke the subkey binding signature of a subkey. - * The subkey with the provided fingerprint will be revoked. - * If no suitable subkey is found, a [NoSuchElementException] will be thrown. + * Revoke the subkey binding signature of a subkey. The subkey with the provided fingerprint + * will be revoked. If no suitable subkey is found, a [NoSuchElementException] will be thrown. * * @param fingerprint fingerprint of the subkey to be revoked * @param protector protector to unlock the primary key * @param revocationAttributes reason for the revocation * @return the builder - * * @throws PGPException in case we cannot generate a revocation signature for the subkey */ @Throws(PGPException::class) - fun revokeSubKey(fingerprint: OpenPgpFingerprint, - protector: SecretKeyRingProtector, - revocationAttributes: RevocationAttributes? = null): SecretKeyRingEditorInterface = - revokeSubKey(fingerprint.keyId, protector, revocationAttributes) + fun revokeSubKey( + fingerprint: OpenPgpFingerprint, + protector: SecretKeyRingProtector, + revocationAttributes: RevocationAttributes? = null + ): SecretKeyRingEditorInterface = + revokeSubKey(fingerprint.keyId, protector, revocationAttributes) /** - * Revoke the subkey binding signature of a subkey. - * The subkey with the provided key-id will be revoked. - * If no suitable subkey is found, a [NoSuchElementException] will be thrown. + * Revoke the subkey binding signature of a subkey. The subkey with the provided key-id will be + * revoked. If no suitable subkey is found, a [NoSuchElementException] will be thrown. * * @param subkeyId id of the subkey * @param protector protector to unlock the primary key * @return the builder - * * @throws PGPException in case we cannot generate a revocation signature for the subkey */ @Throws(PGPException::class) fun revokeSubKey(subkeyId: Long, protector: SecretKeyRingProtector) = - revokeSubKey(subkeyId, protector, null as RevocationAttributes?) + revokeSubKey(subkeyId, protector, null as RevocationAttributes?) /** - * Revoke the subkey binding signature of a subkey. - * The subkey with the provided key-id will be revoked. - * If no suitable subkey is found, a [NoSuchElementException] will be thrown. + * Revoke the subkey binding signature of a subkey. The subkey with the provided key-id will be + * revoked. If no suitable subkey is found, a [NoSuchElementException] will be thrown. * * @param subkeyId id of the subkey * @param protector protector to unlock the primary key * @param revocationAttributes reason for the revocation * @return the builder - * * @throws PGPException in case we cannot generate a revocation signature for the subkey */ @Throws(PGPException::class) - fun revokeSubKey(subkeyId: Long, - protector: SecretKeyRingProtector, - revocationAttributes: RevocationAttributes? = null): SecretKeyRingEditorInterface + fun revokeSubKey( + subkeyId: Long, + protector: SecretKeyRingProtector, + revocationAttributes: RevocationAttributes? = null + ): SecretKeyRingEditorInterface /** - * Revoke the subkey binding signature of a subkey. - * The subkey with the provided key-id will be revoked. - * If no suitable subkey is found, a [NoSuchElementException] will be thrown. + * Revoke the subkey binding signature of a subkey. The subkey with the provided key-id will be + * revoked. If no suitable subkey is found, a [NoSuchElementException] will be thrown. * * The provided subpackets callback is used to modify the revocation signatures subpackets. * * @param subkeyId id of the subkey * @param protector protector to unlock the secret key ring * @param callback callback which can be used to modify the subpackets of the revocation - * signature + * signature * @return the builder - * * @throws PGPException in case we cannot generate a revocation signature for the subkey */ @Throws(PGPException::class) - fun revokeSubKey(subkeyId: Long, - protector: SecretKeyRingProtector, - callback: RevocationSignatureSubpackets.Callback?): SecretKeyRingEditorInterface + fun revokeSubKey( + subkeyId: Long, + protector: SecretKeyRingProtector, + callback: RevocationSignatureSubpackets.Callback? + ): SecretKeyRingEditorInterface /** * Hard-revoke the given userID. @@ -300,11 +319,11 @@ interface SecretKeyRingEditorInterface { * @param userId userId to revoke * @param protector protector to unlock the primary key * @return the builder - * * @throws PGPException in case we cannot generate a revocation signature for the user-id */ @Throws(PGPException::class) - fun revokeUserId(userId: CharSequence, protector: SecretKeyRingProtector) = revokeUserId(userId, protector, null as RevocationAttributes?) + fun revokeUserId(userId: CharSequence, protector: SecretKeyRingProtector) = + revokeUserId(userId, protector, null as RevocationAttributes?) /** * Revoke the given userID using the provided revocation attributes. @@ -313,83 +332,85 @@ interface SecretKeyRingEditorInterface { * @param protector protector to unlock the primary key * @param revocationAttributes reason for the revocation * @return the builder - * * @throws PGPException in case we cannot generate a revocation signature for the user-id */ @Throws(PGPException::class) - fun revokeUserId(userId: CharSequence, - protector: SecretKeyRingProtector, - revocationAttributes: RevocationAttributes? = null): SecretKeyRingEditorInterface + fun revokeUserId( + userId: CharSequence, + protector: SecretKeyRingProtector, + revocationAttributes: RevocationAttributes? = null + ): SecretKeyRingEditorInterface /** - * Revoke the provided user-id. - * Note: If you don't provide a [RevocationSignatureSubpackets.Callback] which - * sets a revocation reason ([RevocationAttributes]), the revocation will be considered hard. - * So if you intend to re-certify the user-id at a later point to make it valid again, - * make sure to set a soft revocation reason in the signatures hashed area using the subpacket callback. + * Revoke the provided user-id. Note: If you don't provide a + * [RevocationSignatureSubpackets.Callback] which sets a revocation reason + * ([RevocationAttributes]), the revocation will be considered hard. So if you intend to + * re-certify the user-id at a later point to make it valid again, make sure to set a soft + * revocation reason in the signatures hashed area using the subpacket callback. * * @param userId userid to be revoked * @param protector protector to unlock the primary secret key * @param callback callback to modify the revocations subpackets * @return builder - * * @throws PGPException in case we cannot generate a revocation signature for the user-id */ @Throws(PGPException::class) - fun revokeUserId(userId: CharSequence, - protector: SecretKeyRingProtector, - callback: RevocationSignatureSubpackets.Callback?): SecretKeyRingEditorInterface + fun revokeUserId( + userId: CharSequence, + protector: SecretKeyRingProtector, + callback: RevocationSignatureSubpackets.Callback? + ): SecretKeyRingEditorInterface /** - * Revoke all user-ids that match the provided [SelectUserId] filter. - * The provided [RevocationAttributes] will be set as reason for revocation in each - * revocation signature. + * Revoke all user-ids that match the provided [SelectUserId] filter. The provided + * [RevocationAttributes] will be set as reason for revocation in each revocation signature. * - * Note: If you intend to re-certify these user-ids at a later point, make sure to choose - * a soft revocation reason. See [RevocationAttributes.Reason] for more information. + * Note: If you intend to re-certify these user-ids at a later point, make sure to choose a soft + * revocation reason. See [RevocationAttributes.Reason] for more information. * * @param selector user-id selector * @param protector protector to unlock the primary secret key * @param revocationAttributes revocation attributes * @return builder - * * @throws PGPException in case we cannot generate a revocation signature for the user-id */ @Throws(PGPException::class) - @Deprecated("Use of SelectUserId class is deprecated.", - ReplaceWith("revokeUserIds(protector, revocationAttributes, predicate)")) - fun revokeUserIds(selector: SelectUserId, - protector: SecretKeyRingProtector, - revocationAttributes: RevocationAttributes?) = - revokeUserIds(protector, revocationAttributes, selector) + @Deprecated( + "Use of SelectUserId class is deprecated.", + ReplaceWith("revokeUserIds(protector, revocationAttributes, predicate)")) + fun revokeUserIds( + selector: SelectUserId, + protector: SecretKeyRingProtector, + revocationAttributes: RevocationAttributes? + ) = revokeUserIds(protector, revocationAttributes, selector) /** - * Revoke all user-ids that match the provided [SelectUserId] filter. - * The provided [RevocationAttributes] will be set as reason for revocation in each - * revocation signature. + * Revoke all user-ids that match the provided [SelectUserId] filter. The provided + * [RevocationAttributes] will be set as reason for revocation in each revocation signature. * - * Note: If you intend to re-certify these user-ids at a later point, make sure to choose - * a soft revocation reason. See [RevocationAttributes.Reason] for more information. + * Note: If you intend to re-certify these user-ids at a later point, make sure to choose a soft + * revocation reason. See [RevocationAttributes.Reason] for more information. * * @param protector protector to unlock the primary secret key * @param revocationAttributes revocation attributes * @param predicate to select user-ids for revocation * @return builder - * * @throws PGPException in case we cannot generate a revocation signature for the user-id */ @Throws(PGPException::class) - fun revokeUserIds(protector: SecretKeyRingProtector, - revocationAttributes: RevocationAttributes?, - predicate: (String) -> Boolean): SecretKeyRingEditorInterface + fun revokeUserIds( + protector: SecretKeyRingProtector, + revocationAttributes: RevocationAttributes?, + predicate: (String) -> Boolean + ): SecretKeyRingEditorInterface /** - * Revoke all user-ids that match the provided [SelectUserId] filter. - * The provided [RevocationSignatureSubpackets.Callback] will be used to modify the - * revocation signatures subpackets. + * Revoke all user-ids that match the provided [SelectUserId] filter. The provided + * [RevocationSignatureSubpackets.Callback] will be used to modify the revocation signatures + * subpackets. * - * Note: If you intend to re-certify these user-ids at a later point, make sure to set - * a soft revocation reason in the revocation signatures hashed subpacket area using the callback. + * Note: If you intend to re-certify these user-ids at a later point, make sure to set a soft + * revocation reason in the revocation signatures hashed subpacket area using the callback. * * See [RevocationAttributes.Reason] for more information. * @@ -397,24 +418,25 @@ interface SecretKeyRingEditorInterface { * @param protector protector to unlock the primary secret key * @param callback callback to modify the revocations subpackets * @return builder - * * @throws PGPException in case we cannot generate a revocation signature for the user-id */ @Throws(PGPException::class) - @Deprecated("Use of SelectUserId class is deprecated.", - ReplaceWith("revokeUserIds(protector, callback, predicate)")) - fun revokeUserIds(selector: SelectUserId, - protector: SecretKeyRingProtector, - callback: RevocationSignatureSubpackets.Callback?) = - revokeUserIds(protector, callback, selector) + @Deprecated( + "Use of SelectUserId class is deprecated.", + ReplaceWith("revokeUserIds(protector, callback, predicate)")) + fun revokeUserIds( + selector: SelectUserId, + protector: SecretKeyRingProtector, + callback: RevocationSignatureSubpackets.Callback? + ) = revokeUserIds(protector, callback, selector) /** - * Revoke all user-ids that match the provided [SelectUserId] filter. - * The provided [RevocationSignatureSubpackets.Callback] will be used to modify the - * revocation signatures subpackets. + * Revoke all user-ids that match the provided [SelectUserId] filter. The provided + * [RevocationSignatureSubpackets.Callback] will be used to modify the revocation signatures + * subpackets. * - * Note: If you intend to re-certify these user-ids at a later point, make sure to set - * a soft revocation reason in the revocation signatures hashed subpacket area using the callback. + * Note: If you intend to re-certify these user-ids at a later point, make sure to set a soft + * revocation reason in the revocation signatures hashed subpacket area using the callback. * * See [RevocationAttributes.Reason] for more information. * @@ -422,56 +444,61 @@ interface SecretKeyRingEditorInterface { * @param callback callback to modify the revocations subpackets * @param predicate to select user-ids for revocation * @return builder - * * @throws PGPException in case we cannot generate a revocation signature for the user-id */ @Throws(PGPException::class) - fun revokeUserIds(protector: SecretKeyRingProtector, - callback: RevocationSignatureSubpackets.Callback?, - predicate: (String) -> Boolean): SecretKeyRingEditorInterface + fun revokeUserIds( + protector: SecretKeyRingProtector, + callback: RevocationSignatureSubpackets.Callback?, + predicate: (String) -> Boolean + ): SecretKeyRingEditorInterface /** - * Set the expiration date for the primary key of the key ring. - * If the key is supposed to never expire, then an expiration date of null is expected. + * Set the expiration date for the primary key of the key ring. If the key is supposed to never + * expire, then an expiration date of null is expected. * * @param expiration new expiration date or null * @param protector to unlock the secret key * @return the builder - * - * @throws PGPException in case we cannot generate a new self-signature with the changed expiration date + * @throws PGPException in case we cannot generate a new self-signature with the changed + * expiration date */ @Throws(PGPException::class) - fun setExpirationDate(expiration: Date?, protector: SecretKeyRingProtector): SecretKeyRingEditorInterface + fun setExpirationDate( + expiration: Date?, + protector: SecretKeyRingProtector + ): SecretKeyRingEditorInterface /** * Create a minimal, self-authorizing revocation certificate, containing only the primary key - * and a revocation signature. - * This type of revocation certificates was introduced in OpenPGP v6. - * This method has no side effects on the original key and will leave it intact. + * and a revocation signature. This type of revocation certificates was introduced in OpenPGP + * v6. This method has no side effects on the original key and will leave it intact. * * @param protector protector to unlock the primary key. * @param revocationAttributes reason for the revocation (key revocation) * @return minimal revocation certificate - * * @throws PGPException in case we cannot generate a revocation signature */ @Throws(PGPException::class) - fun createMinimalRevocationCertificate(protector: SecretKeyRingProtector, - revocationAttributes: RevocationAttributes?): PGPPublicKeyRing + fun createMinimalRevocationCertificate( + protector: SecretKeyRingProtector, + revocationAttributes: RevocationAttributes? + ): PGPPublicKeyRing /** - * Create a detached revocation certificate, which can be used to revoke the whole key. - * The original key will not be modified by this method. + * Create a detached revocation certificate, which can be used to revoke the whole key. The + * original key will not be modified by this method. * * @param protector protector to unlock the primary key. * @param revocationAttributes reason for the revocation * @return revocation certificate - * * @throws PGPException in case we cannot generate a revocation certificate */ @Throws(PGPException::class) - fun createRevocation(protector: SecretKeyRingProtector, - revocationAttributes: RevocationAttributes?): PGPSignature + fun createRevocation( + protector: SecretKeyRingProtector, + revocationAttributes: RevocationAttributes? + ): PGPSignature /** * Create a detached revocation certificate, which can be used to revoke the specified subkey. @@ -481,13 +508,14 @@ interface SecretKeyRingEditorInterface { * @param protector protector to unlock the primary key. * @param revocationAttributes reason for the revocation * @return revocation certificate - * * @throws PGPException in case we cannot generate a revocation certificate */ @Throws(PGPException::class) - fun createRevocation(subkeyId: Long, - protector: SecretKeyRingProtector, - revocationAttributes: RevocationAttributes?): PGPSignature + fun createRevocation( + subkeyId: Long, + protector: SecretKeyRingProtector, + revocationAttributes: RevocationAttributes? + ): PGPSignature /** * Create a detached revocation certificate, which can be used to revoke the specified subkey. @@ -497,13 +525,14 @@ interface SecretKeyRingEditorInterface { * @param protector protector to unlock the primary key. * @param callback callback to modify the subpackets of the revocation certificate. * @return revocation certificate - * * @throws PGPException in case we cannot generate a revocation certificate */ @Throws(PGPException::class) - fun createRevocation(subkeyId: Long, - protector: SecretKeyRingProtector, - callback: RevocationSignatureSubpackets.Callback?): PGPSignature + fun createRevocation( + subkeyId: Long, + protector: SecretKeyRingProtector, + callback: RevocationSignatureSubpackets.Callback? + ): PGPSignature /** * Create a detached revocation certificate, which can be used to revoke the specified subkey. @@ -513,13 +542,14 @@ interface SecretKeyRingEditorInterface { * @param protector protector to unlock the primary key. * @param revocationAttributes reason for the revocation * @return revocation certificate - * * @throws PGPException in case we cannot generate a revocation certificate */ @Throws(PGPException::class) - fun createRevocation(subkeyFingerprint: OpenPgpFingerprint, - protector: SecretKeyRingProtector, - revocationAttributes: RevocationAttributes?): PGPSignature + fun createRevocation( + subkeyFingerprint: OpenPgpFingerprint, + protector: SecretKeyRingProtector, + revocationAttributes: RevocationAttributes? + ): PGPSignature /** * Change the passphrase of the whole key ring. @@ -527,8 +557,9 @@ interface SecretKeyRingEditorInterface { * @param oldPassphrase old passphrase (empty, if the key was unprotected) * @return next builder step */ - fun changePassphraseFromOldPassphrase( - oldPassphrase: Passphrase) = changePassphraseFromOldPassphrase(oldPassphrase, KeyRingProtectionSettings.secureDefaultSettings()) + fun changePassphraseFromOldPassphrase(oldPassphrase: Passphrase) = + changePassphraseFromOldPassphrase( + oldPassphrase, KeyRingProtectionSettings.secureDefaultSettings()) /** * Change the passphrase of the whole key ring. @@ -538,27 +569,30 @@ interface SecretKeyRingEditorInterface { * @return next builder step */ fun changePassphraseFromOldPassphrase( - oldPassphrase: Passphrase, - oldProtectionSettings: KeyRingProtectionSettings = KeyRingProtectionSettings.secureDefaultSettings()): WithKeyRingEncryptionSettings + oldPassphrase: Passphrase, + oldProtectionSettings: KeyRingProtectionSettings = + KeyRingProtectionSettings.secureDefaultSettings() + ): WithKeyRingEncryptionSettings /** * Change the passphrase of a single subkey in the key ring. * - * Note: While it is a valid use-case to have different passphrases per subKey, - * this is one of the reasons why OpenPGP sucks in practice. + * Note: While it is a valid use-case to have different passphrases per subKey, this is one of + * the reasons why OpenPGP sucks in practice. * * @param keyId id of the subkey * @param oldPassphrase old passphrase (empty if the key was unprotected) * @return next builder step */ fun changeSubKeyPassphraseFromOldPassphrase(keyId: Long, oldPassphrase: Passphrase) = - changeSubKeyPassphraseFromOldPassphrase(keyId, oldPassphrase, KeyRingProtectionSettings.secureDefaultSettings()) + changeSubKeyPassphraseFromOldPassphrase( + keyId, oldPassphrase, KeyRingProtectionSettings.secureDefaultSettings()) /** * Change the passphrase of a single subkey in the key ring. * - * Note: While it is a valid use-case to have different passphrases per subKey, - * this is one of the reasons why OpenPGP sucks in practice. + * Note: While it is a valid use-case to have different passphrases per subKey, this is one of + * the reasons why OpenPGP sucks in practice. * * @param keyId id of the subkey * @param oldPassphrase old passphrase (empty if the key was unprotected) @@ -566,15 +600,16 @@ interface SecretKeyRingEditorInterface { * @return next builder step */ fun changeSubKeyPassphraseFromOldPassphrase( - keyId: Long, - oldPassphrase: Passphrase, - oldProtectionSettings: KeyRingProtectionSettings): WithKeyRingEncryptionSettings + keyId: Long, + oldPassphrase: Passphrase, + oldProtectionSettings: KeyRingProtectionSettings + ): WithKeyRingEncryptionSettings interface WithKeyRingEncryptionSettings { /** - * Set secure default settings for the symmetric passphrase encryption. - * Note that this obviously has no effect if you decide to set [WithPassphrase.toNoPassphrase]. + * Set secure default settings for the symmetric passphrase encryption. Note that this + * obviously has no effect if you decide to set [WithPassphrase.toNoPassphrase]. * * @return next builder step */ @@ -596,7 +631,6 @@ interface SecretKeyRingEditorInterface { * * @param passphrase passphrase * @return editor builder - * * @throws PGPException in case the passphrase cannot be changed */ @Throws(PGPException::class) @@ -606,17 +640,21 @@ interface SecretKeyRingEditorInterface { * Leave the key unprotected. * * @return editor builder - * * @throws PGPException in case the passphrase cannot be changed */ - @Throws(PGPException::class) - fun toNoPassphrase(): SecretKeyRingEditorInterface + @Throws(PGPException::class) fun toNoPassphrase(): SecretKeyRingEditorInterface } /** * Return the [PGPSecretKeyRing]. + * * @return the key */ fun done(): PGPSecretKeyRing - fun addSubKey(keySpec: KeySpec, subkeyPassphrase: Passphrase, protector: SecretKeyRingProtector): SecretKeyRingEditorInterface -} \ No newline at end of file + + fun addSubKey( + keySpec: KeySpec, + subkeyPassphrase: Passphrase, + protector: SecretKeyRingProtector + ): SecretKeyRingEditorInterface +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/parsing/KeyRingReader.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/parsing/KeyRingReader.kt index 388294d5..6f6bde61 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/parsing/KeyRingReader.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/parsing/KeyRingReader.kt @@ -4,124 +4,118 @@ package org.pgpainless.key.parsing +import java.io.IOException +import java.io.InputStream +import java.nio.charset.Charset +import kotlin.jvm.Throws import org.bouncycastle.openpgp.* import org.bouncycastle.util.io.Streams import org.pgpainless.PGPainless import org.pgpainless.implementation.ImplementationFactory import org.pgpainless.key.collection.PGPKeyRingCollection import org.pgpainless.util.ArmorUtils -import java.io.IOException -import java.io.InputStream -import java.nio.charset.Charset -import kotlin.jvm.Throws class KeyRingReader { /** - * Read a [PGPKeyRing] (either [PGPSecretKeyRing] or [PGPPublicKeyRing]) from the given [InputStream]. + * Read a [PGPKeyRing] (either [PGPSecretKeyRing] or [PGPPublicKeyRing]) from the given + * [InputStream]. * * @param inputStream inputStream containing the OpenPGP key or certificate * @return key ring * @throws IOException in case of an IO error */ @Throws(IOException::class) - fun keyRing(inputStream: InputStream): PGPKeyRing? = - readKeyRing(inputStream) + fun keyRing(inputStream: InputStream): PGPKeyRing? = readKeyRing(inputStream) /** - * Read a [PGPKeyRing] (either [PGPSecretKeyRing] or [PGPPublicKeyRing]) from the given byte array. + * Read a [PGPKeyRing] (either [PGPSecretKeyRing] or [PGPPublicKeyRing]) from the given byte + * array. * * @param bytes byte array containing the OpenPGP key or certificate * @return key ring * @throws IOException in case of an IO error */ @Throws(IOException::class) - fun keyRing(bytes: ByteArray): PGPKeyRing? = - keyRing(bytes.inputStream()) + fun keyRing(bytes: ByteArray): PGPKeyRing? = keyRing(bytes.inputStream()) /** - * Read a [PGPKeyRing] (either [PGPSecretKeyRing] or [PGPPublicKeyRing]) from the given - * ASCII armored string. + * Read a [PGPKeyRing] (either [PGPSecretKeyRing] or [PGPPublicKeyRing]) from the given ASCII + * armored string. * * @param asciiArmored ASCII armored OpenPGP key or certificate * @return key ring * @throws IOException in case of an IO error */ @Throws(IOException::class) - fun keyRing(asciiArmored: String): PGPKeyRing? = - keyRing(asciiArmored.toByteArray(UTF8)) + fun keyRing(asciiArmored: String): PGPKeyRing? = keyRing(asciiArmored.toByteArray(UTF8)) @Throws(IOException::class) - fun publicKeyRing(inputStream: InputStream): PGPPublicKeyRing? = - readPublicKeyRing(inputStream) + fun publicKeyRing(inputStream: InputStream): PGPPublicKeyRing? = readPublicKeyRing(inputStream) @Throws(IOException::class) - fun publicKeyRing(bytes: ByteArray): PGPPublicKeyRing? = - publicKeyRing(bytes.inputStream()) + fun publicKeyRing(bytes: ByteArray): PGPPublicKeyRing? = publicKeyRing(bytes.inputStream()) @Throws(IOException::class) fun publicKeyRing(asciiArmored: String): PGPPublicKeyRing? = - publicKeyRing(asciiArmored.toByteArray(UTF8)) + publicKeyRing(asciiArmored.toByteArray(UTF8)) @Throws(IOException::class) fun publicKeyRingCollection(inputStream: InputStream): PGPPublicKeyRingCollection = - readPublicKeyRingCollection(inputStream) + readPublicKeyRingCollection(inputStream) @Throws(IOException::class) fun publicKeyRingCollection(bytes: ByteArray): PGPPublicKeyRingCollection = - publicKeyRingCollection(bytes.inputStream()) + publicKeyRingCollection(bytes.inputStream()) @Throws(IOException::class) fun publicKeyRingCollection(asciiArmored: String): PGPPublicKeyRingCollection = - publicKeyRingCollection(asciiArmored.toByteArray(UTF8)) + publicKeyRingCollection(asciiArmored.toByteArray(UTF8)) @Throws(IOException::class) - fun secretKeyRing(inputStream: InputStream): PGPSecretKeyRing? = - readSecretKeyRing(inputStream) + fun secretKeyRing(inputStream: InputStream): PGPSecretKeyRing? = readSecretKeyRing(inputStream) @Throws(IOException::class) - fun secretKeyRing(bytes: ByteArray): PGPSecretKeyRing? = - secretKeyRing(bytes.inputStream()) + fun secretKeyRing(bytes: ByteArray): PGPSecretKeyRing? = secretKeyRing(bytes.inputStream()) @Throws(IOException::class) fun secretKeyRing(asciiArmored: String): PGPSecretKeyRing? = - secretKeyRing(asciiArmored.toByteArray(UTF8)) + secretKeyRing(asciiArmored.toByteArray(UTF8)) @Throws(IOException::class) fun secretKeyRingCollection(inputStream: InputStream): PGPSecretKeyRingCollection = - readSecretKeyRingCollection(inputStream) + readSecretKeyRingCollection(inputStream) @Throws(IOException::class) fun secretKeyRingCollection(bytes: ByteArray): PGPSecretKeyRingCollection = - secretKeyRingCollection(bytes.inputStream()) + secretKeyRingCollection(bytes.inputStream()) @Throws(IOException::class) fun secretKeyRingCollection(asciiArmored: String): PGPSecretKeyRingCollection = - secretKeyRingCollection(asciiArmored.toByteArray(UTF8)) + secretKeyRingCollection(asciiArmored.toByteArray(UTF8)) @Throws(IOException::class) fun keyRingCollection(inptStream: InputStream, isSilent: Boolean): PGPKeyRingCollection = - readKeyRingCollection(inptStream, isSilent) + readKeyRingCollection(inptStream, isSilent) @Throws(IOException::class) fun keyRingCollection(bytes: ByteArray, isSilent: Boolean): PGPKeyRingCollection = - keyRingCollection(bytes.inputStream(), isSilent) + keyRingCollection(bytes.inputStream(), isSilent) @Throws(IOException::class) fun keyRingCollection(asciiArmored: String, isSilent: Boolean): PGPKeyRingCollection = - keyRingCollection(asciiArmored.toByteArray(UTF8), isSilent) + keyRingCollection(asciiArmored.toByteArray(UTF8), isSilent) companion object { private const val MAX_ITERATIONS = 10000 - @JvmStatic - val UTF8: Charset = charset("UTF8") - + @JvmStatic val UTF8: Charset = charset("UTF8") /** - * Read a [PGPKeyRing] (either [PGPSecretKeyRing] or [PGPPublicKeyRing]) from the given [InputStream]. - * This method will attempt to read at most

maxIterations
objects from the stream before aborting. - * The first [PGPPublicKeyRing] or [PGPSecretKeyRing] will be returned. + * Read a [PGPKeyRing] (either [PGPSecretKeyRing] or [PGPPublicKeyRing]) from the given + * [InputStream]. This method will attempt to read at most
maxIterations
objects + * from the stream before aborting. The first [PGPPublicKeyRing] or [PGPSecretKeyRing] will + * be returned. * * @param inputStream inputStream containing the OpenPGP key or certificate * @param maxIterations maximum number of objects that are read before the method will abort @@ -131,10 +125,13 @@ class KeyRingReader { @JvmStatic @JvmOverloads @Throws(IOException::class) - fun readKeyRing(inputStream: InputStream, - maxIterations: Int = MAX_ITERATIONS): PGPKeyRing? { - val objectFactory = ImplementationFactory.getInstance().getPGPObjectFactory( - ArmorUtils.getDecoderStream(inputStream)) + fun readKeyRing( + inputStream: InputStream, + maxIterations: Int = MAX_ITERATIONS + ): PGPKeyRing? { + val objectFactory = + ImplementationFactory.getInstance() + .getPGPObjectFactory(ArmorUtils.getDecoderStream(inputStream)) try { for ((i, next) in objectFactory.withIndex()) { @@ -152,30 +149,31 @@ class KeyRingReader { } continue } - } catch (e : PGPRuntimeOperationException) { + } catch (e: PGPRuntimeOperationException) { throw e.cause!! } return null } /** - * Read a public key ring from the provided [InputStream]. - * If more than maxIterations PGP packets are encountered before a [PGPPublicKeyRing] is read, - * an [IOException] is thrown. + * Read a public key ring from the provided [InputStream]. If more than maxIterations PGP + * packets are encountered before a [PGPPublicKeyRing] is read, an [IOException] is thrown. * * @param inputStream input stream * @param maxIterations max iterations before abort * @return public key ring - * * @throws IOException in case of an IO error or exceeding of max iterations */ @JvmStatic @JvmOverloads @Throws(IOException::class) - fun readPublicKeyRing(inputStream: InputStream, - maxIterations: Int = MAX_ITERATIONS): PGPPublicKeyRing? { - val objectFactory = ImplementationFactory.getInstance().getPGPObjectFactory( - ArmorUtils.getDecoderStream(inputStream)) + fun readPublicKeyRing( + inputStream: InputStream, + maxIterations: Int = MAX_ITERATIONS + ): PGPPublicKeyRing? { + val objectFactory = + ImplementationFactory.getInstance() + .getPGPObjectFactory(ArmorUtils.getDecoderStream(inputStream)) try { for ((i, next) in objectFactory.withIndex()) { @@ -190,31 +188,33 @@ class KeyRingReader { } continue } - } catch (e : PGPRuntimeOperationException) { + } catch (e: PGPRuntimeOperationException) { throw e.cause!! } return null } /** - * Read a public key ring collection from the provided [InputStream]. - * If more than maxIterations PGP packets are encountered before the stream is exhausted, - * an [IOException] is thrown. - * If the stream contain secret key packets, their public key parts are extracted and returned. + * Read a public key ring collection from the provided [InputStream]. If more than + * maxIterations PGP packets are encountered before the stream is exhausted, an + * [IOException] is thrown. If the stream contain secret key packets, their public key parts + * are extracted and returned. * * @param inputStream input stream * @param maxIterations max iterations before abort * @return public key ring collection - * * @throws IOException in case of an IO error or exceeding of max iterations */ @JvmStatic @JvmOverloads @Throws(IOException::class) - fun readPublicKeyRingCollection(inputStream: InputStream, - maxIterations: Int = MAX_ITERATIONS): PGPPublicKeyRingCollection { - val objectFactory = ImplementationFactory.getInstance().getPGPObjectFactory( - ArmorUtils.getDecoderStream(inputStream)) + fun readPublicKeyRingCollection( + inputStream: InputStream, + maxIterations: Int = MAX_ITERATIONS + ): PGPPublicKeyRingCollection { + val objectFactory = + ImplementationFactory.getInstance() + .getPGPObjectFactory(ArmorUtils.getDecoderStream(inputStream)) val certificates = mutableListOf() try { for ((i, next) in objectFactory.withIndex()) { @@ -237,30 +237,31 @@ class KeyRingReader { continue } } - } catch (e : PGPRuntimeOperationException) { + } catch (e: PGPRuntimeOperationException) { throw e.cause!! } return PGPPublicKeyRingCollection(certificates) } /** - * Read a secret key ring from the provided [InputStream]. - * If more than maxIterations PGP packets are encountered before a [PGPSecretKeyRing] is read, - * an [IOException] is thrown. + * Read a secret key ring from the provided [InputStream]. If more than maxIterations PGP + * packets are encountered before a [PGPSecretKeyRing] is read, an [IOException] is thrown. * * @param inputStream input stream * @param maxIterations max iterations before abort * @return public key ring - * * @throws IOException in case of an IO error or exceeding of max iterations */ @JvmStatic @JvmOverloads @Throws(IOException::class) - fun readSecretKeyRing(inputStream: InputStream, - maxIterations: Int = MAX_ITERATIONS): PGPSecretKeyRing? { + fun readSecretKeyRing( + inputStream: InputStream, + maxIterations: Int = MAX_ITERATIONS + ): PGPSecretKeyRing? { val decoderStream = ArmorUtils.getDecoderStream(inputStream) - val objectFactory = ImplementationFactory.getInstance().getPGPObjectFactory(decoderStream) + val objectFactory = + ImplementationFactory.getInstance().getPGPObjectFactory(decoderStream) try { for ((i, next) in objectFactory.withIndex()) { @@ -275,30 +276,32 @@ class KeyRingReader { return next } } - } catch (e : PGPRuntimeOperationException) { + } catch (e: PGPRuntimeOperationException) { throw e.cause!! } return null } /** - * Read a secret key ring collection from the provided [InputStream]. - * If more than maxIterations PGP packets are encountered before the stream is exhausted, - * an [IOException] is thrown. + * Read a secret key ring collection from the provided [InputStream]. If more than + * maxIterations PGP packets are encountered before the stream is exhausted, an + * [IOException] is thrown. * * @param inputStream input stream * @param maxIterations max iterations before abort * @return secret key ring collection - * * @throws IOException in case of an IO error or exceeding of max iterations */ @JvmStatic @JvmOverloads @Throws(IOException::class) - fun readSecretKeyRingCollection(inputStream: InputStream, - maxIterations: Int = MAX_ITERATIONS): PGPSecretKeyRingCollection { - val objectFactory = ImplementationFactory.getInstance().getPGPObjectFactory( - ArmorUtils.getDecoderStream(inputStream)) + fun readSecretKeyRingCollection( + inputStream: InputStream, + maxIterations: Int = MAX_ITERATIONS + ): PGPSecretKeyRingCollection { + val objectFactory = + ImplementationFactory.getInstance() + .getPGPObjectFactory(ArmorUtils.getDecoderStream(inputStream)) val secretKeys = mutableListOf() try { @@ -318,7 +321,7 @@ class KeyRingReader { continue } } - } catch (e : PGPRuntimeOperationException) { + } catch (e: PGPRuntimeOperationException) { throw e.cause!! } return PGPSecretKeyRingCollection(secretKeys) @@ -326,8 +329,9 @@ class KeyRingReader { @JvmStatic @Throws(IOException::class) - fun readKeyRingCollection(inputStream: InputStream, isSilent: Boolean): PGPKeyRingCollection = - PGPKeyRingCollection(inputStream, isSilent) + fun readKeyRingCollection( + inputStream: InputStream, + isSilent: Boolean + ): PGPKeyRingCollection = PGPKeyRingCollection(inputStream, isSilent) } - -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/BaseSecretKeyRingProtector.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/BaseSecretKeyRingProtector.kt index 450ca7f5..c5db2086 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/BaseSecretKeyRingProtector.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/BaseSecretKeyRingProtector.kt @@ -10,31 +10,35 @@ import org.pgpainless.implementation.ImplementationFactory import org.pgpainless.key.protection.passphrase_provider.SecretKeyPassphraseProvider /** - * Basic [SecretKeyRingProtector] implementation that respects the users [KeyRingProtectionSettings] when encrypting keys. + * Basic [SecretKeyRingProtector] implementation that respects the users [KeyRingProtectionSettings] + * when encrypting keys. */ open class BaseSecretKeyRingProtector( - private val passphraseProvider: SecretKeyPassphraseProvider, - private val protectionSettings: KeyRingProtectionSettings + private val passphraseProvider: SecretKeyPassphraseProvider, + private val protectionSettings: KeyRingProtectionSettings ) : SecretKeyRingProtector { - constructor(passphraseProvider: SecretKeyPassphraseProvider): - this(passphraseProvider, KeyRingProtectionSettings.secureDefaultSettings()) + constructor( + passphraseProvider: SecretKeyPassphraseProvider + ) : this(passphraseProvider, KeyRingProtectionSettings.secureDefaultSettings()) override fun hasPassphraseFor(keyId: Long): Boolean = passphraseProvider.hasPassphrase(keyId) override fun getDecryptor(keyId: Long): PBESecretKeyDecryptor? = - passphraseProvider.getPassphraseFor(keyId)?.let { - if (it.isEmpty) null - else ImplementationFactory.getInstance().getPBESecretKeyDecryptor(it) - } + passphraseProvider.getPassphraseFor(keyId)?.let { + if (it.isEmpty) null + else ImplementationFactory.getInstance().getPBESecretKeyDecryptor(it) + } override fun getEncryptor(keyId: Long): PBESecretKeyEncryptor? = - passphraseProvider.getPassphraseFor(keyId)?.let { - if (it.isEmpty) null - else ImplementationFactory.getInstance().getPBESecretKeyEncryptor( + passphraseProvider.getPassphraseFor(keyId)?.let { + if (it.isEmpty) null + else + ImplementationFactory.getInstance() + .getPBESecretKeyEncryptor( protectionSettings.encryptionAlgorithm, protectionSettings.hashAlgorithm, protectionSettings.s2kCount, it) - } -} \ No newline at end of file + } +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/CachingSecretKeyRingProtector.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/CachingSecretKeyRingProtector.kt index 32f69f70..5a3ff47f 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/CachingSecretKeyRingProtector.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/CachingSecretKeyRingProtector.kt @@ -13,8 +13,8 @@ import org.pgpainless.util.Passphrase /** * Implementation of the [SecretKeyRingProtector] which holds a map of key ids and their passwords. - * In case the needed passphrase is not contained in the map, the `missingPassphraseCallback` will be consulted, - * and the passphrase is added to the map. + * In case the needed passphrase is not contained in the map, the `missingPassphraseCallback` will + * be consulted, and the passphrase is added to the map. * * If you need to unlock multiple [PGPKeyRing] instances, it is advised to use a separate * [CachingSecretKeyRingProtector] instance for each ring. @@ -25,29 +25,33 @@ class CachingSecretKeyRingProtector : SecretKeyRingProtector, SecretKeyPassphras private val protector: SecretKeyRingProtector private val provider: SecretKeyPassphraseProvider? - constructor(): this(null) + constructor() : this(null) - constructor(missingPassphraseCallback: SecretKeyPassphraseProvider?): this( - mapOf(), - KeyRingProtectionSettings.secureDefaultSettings(), - missingPassphraseCallback) + constructor( + missingPassphraseCallback: SecretKeyPassphraseProvider? + ) : this( + mapOf(), + KeyRingProtectionSettings.secureDefaultSettings(), + missingPassphraseCallback) - constructor(passphrases: Map, - protectionSettings: KeyRingProtectionSettings, - missingPassphraseCallback: SecretKeyPassphraseProvider?) { + constructor( + passphrases: Map, + protectionSettings: KeyRingProtectionSettings, + missingPassphraseCallback: SecretKeyPassphraseProvider? + ) { this.cache = passphrases.toMutableMap() this.protector = PasswordBasedSecretKeyRingProtector(protectionSettings, this) this.provider = missingPassphraseCallback } /** - * Add a passphrase to the cache. - * If the cache already contains a passphrase for the given key-id, a [IllegalArgumentException] is thrown. - * The reason for this is to prevent accidental override of passphrases when dealing with multiple key rings - * containing a key with the same key-id but different passphrases. + * Add a passphrase to the cache. If the cache already contains a passphrase for the given + * key-id, a [IllegalArgumentException] is thrown. The reason for this is to prevent accidental + * override of passphrases when dealing with multiple key rings containing a key with the same + * key-id but different passphrases. * - * If you can ensure that there will be no key-id clash, and you want to replace the passphrase, you can use - * [replacePassphrase] to replace the passphrase. + * If you can ensure that there will be no key-id clash, and you want to replace the passphrase, + * you can use [replacePassphrase] to replace the passphrase. * * @param keyId id of the key * @param passphrase passphrase @@ -55,7 +59,7 @@ class CachingSecretKeyRingProtector : SecretKeyRingProtector, SecretKeyPassphras fun addPassphrase(keyId: Long, passphrase: Passphrase) = apply { require(!cache.containsKey(keyId)) { "The cache already holds a passphrase for ID ${keyId.openPgpKeyId()}.\n" + - "If you want to replace this passphrase, use replacePassphrase(Long, Passphrase) instead." + "If you want to replace this passphrase, use replacePassphrase(Long, Passphrase) instead." } cache[keyId] = passphrase } @@ -66,22 +70,20 @@ class CachingSecretKeyRingProtector : SecretKeyRingProtector, SecretKeyPassphras * @param keyId keyId * @param passphrase passphrase */ - fun replacePassphrase(keyId: Long, passphrase: Passphrase) = apply { - cache[keyId] = passphrase - } + fun replacePassphrase(keyId: Long, passphrase: Passphrase) = apply { cache[keyId] = passphrase } /** - * Remember the given passphrase for all keys in the given key ring. - * If for the key-id of any key on the key ring the cache already contains a passphrase, a - * [IllegalArgumentException] is thrown before any changes are committed to the cache. - * This is to prevent accidental passphrase override when dealing with multiple key rings containing - * keys with conflicting key-ids. + * Remember the given passphrase for all keys in the given key ring. If for the key-id of any + * key on the key ring the cache already contains a passphrase, a [IllegalArgumentException] is + * thrown before any changes are committed to the cache. This is to prevent accidental + * passphrase override when dealing with multiple key rings containing keys with conflicting + * key-ids. * - * If you can ensure that there will be no key-id clashes, and you want to replace the passphrases for the key ring, - * use [replacePassphrase] instead. + * If you can ensure that there will be no key-id clashes, and you want to replace the + * passphrases for the key ring, use [replacePassphrase] instead. * - * If you need to unlock multiple [PGPKeyRing], it is advised to use a separate [CachingSecretKeyRingProtector] - * instance for each ring. + * If you need to unlock multiple [PGPKeyRing], it is advised to use a separate + * [CachingSecretKeyRingProtector] instance for each ring. * * @param keyRing key ring * @param passphrase passphrase @@ -91,14 +93,12 @@ class CachingSecretKeyRingProtector : SecretKeyRingProtector, SecretKeyPassphras keyRing.publicKeys.forEach { require(!cache.containsKey(it.keyID)) { "The cache already holds a passphrase for the key with ID ${it.keyID.openPgpKeyId()}.\n" + - "If you want to replace the passphrase, use replacePassphrase(PGPKeyRing, Passphrase) instead." + "If you want to replace the passphrase, use replacePassphrase(PGPKeyRing, Passphrase) instead." } } // only then instert - keyRing.publicKeys.forEach { - cache[it.keyID] = passphrase - } + keyRing.publicKeys.forEach { cache[it.keyID] = passphrase } } /** @@ -118,7 +118,7 @@ class CachingSecretKeyRingProtector : SecretKeyRingProtector, SecretKeyPassphras * @param passphrase passphrase */ fun addPassphrase(key: PGPPublicKey, passphrase: Passphrase) = - addPassphrase(key.keyID, passphrase) + addPassphrase(key.keyID, passphrase) /** * Remember the given passphrase for the key with the given fingerprint. @@ -127,17 +127,14 @@ class CachingSecretKeyRingProtector : SecretKeyRingProtector, SecretKeyPassphras * @param passphrase passphrase */ fun addPassphrase(fingerprint: OpenPgpFingerprint, passphrase: Passphrase) = - addPassphrase(fingerprint.keyId, passphrase) + addPassphrase(fingerprint.keyId, passphrase) /** - * Remove a passphrase from the cache. - * The passphrase will be cleared and then removed. + * Remove a passphrase from the cache. The passphrase will be cleared and then removed. * * @param keyId id of the key */ - fun forgetPassphrase(keyId: Long) = apply { - cache.remove(keyId)?.clear() - } + fun forgetPassphrase(keyId: Long) = apply { cache.remove(keyId)?.clear() } /** * Forget the passphrase to all keys in the provided key ring. @@ -153,15 +150,11 @@ class CachingSecretKeyRingProtector : SecretKeyRingProtector, SecretKeyPassphras * * @param key key */ - fun forgetPassphrase(key: PGPPublicKey) = apply { - forgetPassphrase(key.keyID) - } + fun forgetPassphrase(key: PGPPublicKey) = apply { forgetPassphrase(key.keyID) } override fun getPassphraseFor(keyId: Long): Passphrase? { - return if (hasPassphrase(keyId)) - cache[keyId] - else - provider?.getPassphraseFor(keyId)?.also { cache[keyId] = it } + return if (hasPassphrase(keyId)) cache[keyId] + else provider?.getPassphraseFor(keyId)?.also { cache[keyId] = it } } override fun hasPassphrase(keyId: Long) = cache[keyId]?.isValid ?: false @@ -171,4 +164,4 @@ class CachingSecretKeyRingProtector : SecretKeyRingProtector, SecretKeyPassphras override fun getDecryptor(keyId: Long) = protector.getDecryptor(keyId) override fun getEncryptor(keyId: Long) = protector.getEncryptor(keyId) -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/KeyRingProtectionSettings.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/KeyRingProtectionSettings.kt index 6158d322..c7566f6d 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/KeyRingProtectionSettings.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/KeyRingProtectionSettings.kt @@ -8,49 +8,48 @@ import org.pgpainless.algorithm.HashAlgorithm import org.pgpainless.algorithm.SymmetricKeyAlgorithm /** - * Secret key protection settings for iterated and salted S2K. - * The salt gets randomly chosen by the library each time. - * Note, that the s2kCount is the already encoded single-octet number. - * - * @see Encoding Formula + * Secret key protection settings for iterated and salted S2K. The salt gets randomly chosen by the + * library each time. Note, that the s2kCount is the already encoded single-octet number. * * @param encryptionAlgorithm encryption algorithm * @param hashAlgorithm hash algorithm * @param s2kCount encoded (!) s2k iteration count + * @see Encoding Formula */ data class KeyRingProtectionSettings( - val encryptionAlgorithm: SymmetricKeyAlgorithm, - val hashAlgorithm: HashAlgorithm, - val s2kCount: Int + val encryptionAlgorithm: SymmetricKeyAlgorithm, + val hashAlgorithm: HashAlgorithm, + val s2kCount: Int ) { /** - * Create a [KeyRingProtectionSettings] object using the given encryption algorithm, [HashAlgorithm.SHA1] and - * 65536 iterations. - * It is okay to use SHA1 here, since we don't care about collisions. + * Create a [KeyRingProtectionSettings] object using the given encryption algorithm, + * [HashAlgorithm.SHA1] and 65536 iterations. It is okay to use SHA1 here, since we don't care + * about collisions. * * @param encryptionAlgorithm encryption algorithm */ - constructor(encryptionAlgorithm: SymmetricKeyAlgorithm): this(encryptionAlgorithm, HashAlgorithm.SHA1, 0x60) + constructor( + encryptionAlgorithm: SymmetricKeyAlgorithm + ) : this(encryptionAlgorithm, HashAlgorithm.SHA1, 0x60) init { require(encryptionAlgorithm != SymmetricKeyAlgorithm.NULL) { "Unencrypted is not allowed here!" } - require(s2kCount > 0) { - "s2kCount cannot be less than 1." - } + require(s2kCount > 0) { "s2kCount cannot be less than 1." } } companion object { /** - * Secure default settings using [SymmetricKeyAlgorithm.AES_256], [HashAlgorithm.SHA256] - * and an iteration count of 65536. + * Secure default settings using [SymmetricKeyAlgorithm.AES_256], [HashAlgorithm.SHA256] and + * an iteration count of 65536. * * @return secure protection settings */ @JvmStatic - fun secureDefaultSettings() = KeyRingProtectionSettings(SymmetricKeyAlgorithm.AES_256, HashAlgorithm.SHA256, 0x60) + fun secureDefaultSettings() = + KeyRingProtectionSettings(SymmetricKeyAlgorithm.AES_256, HashAlgorithm.SHA256, 0x60) } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/PasswordBasedSecretKeyRingProtector.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/PasswordBasedSecretKeyRingProtector.kt index 1b8df815..74ef881f 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/PasswordBasedSecretKeyRingProtector.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/PasswordBasedSecretKeyRingProtector.kt @@ -10,54 +10,64 @@ import org.pgpainless.key.protection.passphrase_provider.SecretKeyPassphraseProv import org.pgpainless.util.Passphrase /** - * Provides [PBESecretKeyDecryptor] and [PBESecretKeyEncryptor] objects while getting the passphrases - * from a [SecretKeyPassphraseProvider] and using settings from an [KeyRingProtectionSettings]. + * Provides [PBESecretKeyDecryptor] and [PBESecretKeyEncryptor] objects while getting the + * passphrases from a [SecretKeyPassphraseProvider] and using settings from an + * [KeyRingProtectionSettings]. */ class PasswordBasedSecretKeyRingProtector : BaseSecretKeyRingProtector { - constructor(passphraseProvider: SecretKeyPassphraseProvider): super(passphraseProvider) + constructor(passphraseProvider: SecretKeyPassphraseProvider) : super(passphraseProvider) /** - * Constructor. - * Passphrases for keys are sourced from the `passphraseProvider` and decryptors/encryptors are constructed - * following the settings given in `settings`. + * Constructor. Passphrases for keys are sourced from the `passphraseProvider` and + * decryptors/encryptors are constructed following the settings given in `settings`. * * @param settings S2K settings etc. * @param passphraseProvider provider which provides passphrases. */ - constructor(settings: KeyRingProtectionSettings, - passphraseProvider: SecretKeyPassphraseProvider): super(passphraseProvider, settings) + constructor( + settings: KeyRingProtectionSettings, + passphraseProvider: SecretKeyPassphraseProvider + ) : super(passphraseProvider, settings) companion object { @JvmStatic - fun forKey(keyRing: PGPKeyRing, passphrase: Passphrase): PasswordBasedSecretKeyRingProtector { + fun forKey( + keyRing: PGPKeyRing, + passphrase: Passphrase + ): PasswordBasedSecretKeyRingProtector { return object : SecretKeyPassphraseProvider { - override fun getPassphraseFor(keyId: Long): Passphrase? { - return if (hasPassphrase(keyId)) passphrase else null - } + override fun getPassphraseFor(keyId: Long): Passphrase? { + return if (hasPassphrase(keyId)) passphrase else null + } - override fun hasPassphrase(keyId: Long): Boolean { - return keyRing.getPublicKey(keyId) != null + override fun hasPassphrase(keyId: Long): Boolean { + return keyRing.getPublicKey(keyId) != null + } } - }.let { PasswordBasedSecretKeyRingProtector(it) } + .let { PasswordBasedSecretKeyRingProtector(it) } } @JvmStatic fun forKey(key: PGPSecretKey, passphrase: Passphrase): PasswordBasedSecretKeyRingProtector = - forKeyId(key.publicKey.keyID, passphrase) + forKeyId(key.publicKey.keyID, passphrase) @JvmStatic - fun forKeyId(singleKeyId: Long, passphrase: Passphrase): PasswordBasedSecretKeyRingProtector { + fun forKeyId( + singleKeyId: Long, + passphrase: Passphrase + ): PasswordBasedSecretKeyRingProtector { return object : SecretKeyPassphraseProvider { - override fun getPassphraseFor(keyId: Long): Passphrase? { - return if (hasPassphrase(keyId)) passphrase else null - } + override fun getPassphraseFor(keyId: Long): Passphrase? { + return if (hasPassphrase(keyId)) passphrase else null + } - override fun hasPassphrase(keyId: Long): Boolean { - return keyId == singleKeyId + override fun hasPassphrase(keyId: Long): Boolean { + return keyId == singleKeyId + } } - }.let { PasswordBasedSecretKeyRingProtector(it) } + .let { PasswordBasedSecretKeyRingProtector(it) } } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/SecretKeyRingProtector.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/SecretKeyRingProtector.kt index 8bdac8d6..5e86d950 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/SecretKeyRingProtector.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/SecretKeyRingProtector.kt @@ -4,6 +4,7 @@ package org.pgpainless.key.protection +import kotlin.jvm.Throws import org.bouncycastle.openpgp.PGPException import org.bouncycastle.openpgp.PGPSecretKey import org.bouncycastle.openpgp.PGPSecretKeyRing @@ -12,14 +13,14 @@ import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor import org.pgpainless.key.protection.passphrase_provider.SecretKeyPassphraseProvider import org.pgpainless.key.protection.passphrase_provider.SolitaryPassphraseProvider import org.pgpainless.util.Passphrase -import kotlin.jvm.Throws /** * Task of the [SecretKeyRingProtector] is to map encryptor/decryptor objects to key-ids. - * [PBESecretKeyEncryptor]/[PBESecretKeyDecryptor] are used to encrypt/decrypt secret keys using a passphrase. + * [PBESecretKeyEncryptor]/[PBESecretKeyDecryptor] are used to encrypt/decrypt secret keys using a + * passphrase. * - * While it is easy to create an implementation of this interface that fits your needs, there are a bunch of - * implementations ready for use. + * While it is easy to create an implementation of this interface that fits your needs, there are a + * bunch of implementations ready for use. */ interface SecretKeyRingProtector { @@ -32,56 +33,61 @@ interface SecretKeyRingProtector { fun hasPassphraseFor(keyId: Long): Boolean /** - * Return a decryptor for the key of id `keyId`. - * This method returns null if the key is unprotected. + * Return a decryptor for the key of id `keyId`. This method returns null if the key is + * unprotected. * * @param keyId id of the key * @return decryptor for the key */ - @Throws(PGPException::class) - fun getDecryptor(keyId: Long): PBESecretKeyDecryptor? + @Throws(PGPException::class) fun getDecryptor(keyId: Long): PBESecretKeyDecryptor? /** - * Return an encryptor for the key of id `keyId`. - * This method returns null if the key is unprotected. + * Return an encryptor for the key of id `keyId`. This method returns null if the key is + * unprotected. * * @param keyId id of the key * @return encryptor for the key */ - @Throws(PGPException::class) - fun getEncryptor(keyId: Long): PBESecretKeyEncryptor? + @Throws(PGPException::class) fun getEncryptor(keyId: Long): PBESecretKeyEncryptor? companion object { /** - * Return a protector for secret keys. - * The protector maintains an in-memory cache of passphrases and can be extended with new passphrases - * at runtime. + * Return a protector for secret keys. The protector maintains an in-memory cache of + * passphrases and can be extended with new passphrases at runtime. * - * See [CachingSecretKeyRingProtector] for how to memorize/forget additional passphrases during runtime. + * See [CachingSecretKeyRingProtector] for how to memorize/forget additional passphrases + * during runtime. * * @param missingPassphraseCallback callback that is used to provide missing passphrases. * @return caching secret key protector */ @JvmStatic fun defaultSecretKeyRingProtector( - missingPassphraseCallback: SecretKeyPassphraseProvider? - ): CachingSecretKeyRingProtector = CachingSecretKeyRingProtector( - mapOf(), KeyRingProtectionSettings.secureDefaultSettings(), missingPassphraseCallback) + missingPassphraseCallback: SecretKeyPassphraseProvider? + ): CachingSecretKeyRingProtector = + CachingSecretKeyRingProtector( + mapOf(), + KeyRingProtectionSettings.secureDefaultSettings(), + missingPassphraseCallback) /** * Use the provided passphrase to lock/unlock all keys in the provided key ring. * - * This protector will use the provided passphrase to lock/unlock all subkeys present in the provided keys object. - * For other keys that are not present in the ring, it will return null. + * This protector will use the provided passphrase to lock/unlock all subkeys present in the + * provided keys object. For other keys that are not present in the ring, it will return + * null. * * @param passphrase passphrase * @param keys key ring * @return protector */ @JvmStatic - fun unlockEachKeyWith(passphrase: Passphrase, keys: PGPSecretKeyRing): SecretKeyRingProtector = - fromPassphraseMap(keys.map { it.keyID }.associateWith { passphrase }) + fun unlockEachKeyWith( + passphrase: Passphrase, + keys: PGPSecretKeyRing + ): SecretKeyRingProtector = + fromPassphraseMap(keys.map { it.keyID }.associateWith { passphrase }) /** * Use the provided passphrase to unlock any key. @@ -91,11 +97,11 @@ interface SecretKeyRingProtector { */ @JvmStatic fun unlockAnyKeyWith(passphrase: Passphrase): SecretKeyRingProtector = - BaseSecretKeyRingProtector(SolitaryPassphraseProvider(passphrase)) + BaseSecretKeyRingProtector(SolitaryPassphraseProvider(passphrase)) /** - * Use the provided passphrase to lock/unlock only the provided (sub-)key. - * This protector will only return a non-null encryptor/decryptor based on the provided passphrase if + * Use the provided passphrase to lock/unlock only the provided (sub-)key. This protector + * will only return a non-null encryptor/decryptor based on the provided passphrase if * [getEncryptor]/[getDecryptor] is getting called with the key-id of the provided key. * * Otherwise, this protector will always return null. @@ -106,11 +112,11 @@ interface SecretKeyRingProtector { */ @JvmStatic fun unlockSingleKeyWith(passphrase: Passphrase, key: PGPSecretKey): SecretKeyRingProtector = - PasswordBasedSecretKeyRingProtector.forKey(key, passphrase) + PasswordBasedSecretKeyRingProtector.forKey(key, passphrase) /** - * Use the provided passphrase to lock/unlock only the provided (sub-)key. - * This protector will only return a non-null encryptor/decryptor based on the provided passphrase if + * Use the provided passphrase to lock/unlock only the provided (sub-)key. This protector + * will only return a non-null encryptor/decryptor based on the provided passphrase if * [getEncryptor]/[getDecryptor] is getting called with the key-id of the provided key. * * Otherwise, this protector will always return null. @@ -121,21 +127,20 @@ interface SecretKeyRingProtector { */ @JvmStatic fun unlockSingleKeyWith(passphrase: Passphrase, keyId: Long): SecretKeyRingProtector = - PasswordBasedSecretKeyRingProtector.forKeyId(keyId, passphrase) + PasswordBasedSecretKeyRingProtector.forKeyId(keyId, passphrase) /** - * Protector for unprotected keys. - * This protector returns null for all [getEncryptor]/[getDecryptor] calls, - * no matter what the key-id is. + * Protector for unprotected keys. This protector returns null for all + * [getEncryptor]/[getDecryptor] calls, no matter what the key-id is. * - * As a consequence, this protector can only "unlock" keys which are not protected using a passphrase, and it will - * leave keys unprotected, should it be used to "protect" a key - * (e.g. in [org.pgpainless.key.modification.secretkeyring.SecretKeyRingEditor.changePassphraseFromOldPassphrase]). + * As a consequence, this protector can only "unlock" keys which are not protected using a + * passphrase, and it will leave keys unprotected, should it be used to "protect" a key + * (e.g. in + * [org.pgpainless.key.modification.secretkeyring.SecretKeyRingEditor.changePassphraseFromOldPassphrase]). * * @return protector */ - @JvmStatic - fun unprotectedKeys() = UnprotectedKeysProtector() + @JvmStatic fun unprotectedKeys() = UnprotectedKeysProtector() /** * Use the provided map of key-ids and passphrases to unlock keys. @@ -145,6 +150,7 @@ interface SecretKeyRingProtector { */ @JvmStatic fun fromPassphraseMap(passphraseMap: Map): SecretKeyRingProtector = - CachingSecretKeyRingProtector(passphraseMap, KeyRingProtectionSettings.secureDefaultSettings(), null) + CachingSecretKeyRingProtector( + passphraseMap, KeyRingProtectionSettings.secureDefaultSettings(), null) } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/UnlockSecretKey.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/UnlockSecretKey.kt index 84d7d0d7..f9fe3e1d 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/UnlockSecretKey.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/UnlockSecretKey.kt @@ -5,6 +5,7 @@ package org.pgpainless.key.protection +import kotlin.jvm.Throws import openpgp.openPgpKeyId import org.bouncycastle.extensions.isEncrypted import org.bouncycastle.openpgp.PGPException @@ -16,7 +17,6 @@ import org.pgpainless.exception.KeyIntegrityException import org.pgpainless.exception.WrongPassphraseException import org.pgpainless.key.util.PublicKeyParameterValidationUtil import org.pgpainless.util.Passphrase -import kotlin.jvm.Throws class UnlockSecretKey { @@ -24,7 +24,10 @@ class UnlockSecretKey { @JvmStatic @Throws(PGPException::class, KeyIntegrityException::class) - fun unlockSecretKey(secretKey: PGPSecretKey, protector: SecretKeyRingProtector): PGPPrivateKey { + fun unlockSecretKey( + secretKey: PGPSecretKey, + protector: SecretKeyRingProtector + ): PGPPrivateKey { return if (secretKey.isEncrypted()) { unlockSecretKey(secretKey, protector.getDecryptor(secretKey.keyID)) } else { @@ -34,23 +37,29 @@ class UnlockSecretKey { @JvmStatic @Throws(PGPException::class) - fun unlockSecretKey(secretKey: PGPSecretKey, decryptor: PBESecretKeyDecryptor?): PGPPrivateKey { - val privateKey = try { - secretKey.extractPrivateKey(decryptor) - } catch (e : PGPException) { - throw WrongPassphraseException(secretKey.keyID, e) - } + fun unlockSecretKey( + secretKey: PGPSecretKey, + decryptor: PBESecretKeyDecryptor? + ): PGPPrivateKey { + val privateKey = + try { + secretKey.extractPrivateKey(decryptor) + } catch (e: PGPException) { + throw WrongPassphraseException(secretKey.keyID, e) + } if (privateKey == null) { if (secretKey.s2K.type in 100..110) { - throw PGPException("Cannot decrypt secret key ${secretKey.keyID.openPgpKeyId()}: \n" + + throw PGPException( + "Cannot decrypt secret key ${secretKey.keyID.openPgpKeyId()}: \n" + "Unsupported private S2K type ${secretKey.s2K.type}") } throw PGPException("Cannot decrypt secret key.") } if (PGPainless.getPolicy().isEnableKeyParameterValidation()) { - PublicKeyParameterValidationUtil.verifyPublicKeyParameterIntegrity(privateKey, secretKey.publicKey) + PublicKeyParameterValidationUtil.verifyPublicKeyParameterIntegrity( + privateKey, secretKey.publicKey) } return privateKey @@ -61,8 +70,9 @@ class UnlockSecretKey { return if (passphrase == null) { unlockSecretKey(secretKey, SecretKeyRingProtector.unprotectedKeys()) } else { - unlockSecretKey(secretKey, SecretKeyRingProtector.unlockSingleKeyWith(passphrase, secretKey)) + unlockSecretKey( + secretKey, SecretKeyRingProtector.unlockSingleKeyWith(passphrase, secretKey)) } } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/UnprotectedKeysProtector.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/UnprotectedKeysProtector.kt index f87c6d3d..a25bb31a 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/UnprotectedKeysProtector.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/UnprotectedKeysProtector.kt @@ -4,7 +4,8 @@ package org.pgpainless.key.protection /** - * Implementation of the [SecretKeyRingProtector] which assumes that all handled keys are not password protected. + * Implementation of the [SecretKeyRingProtector] which assumes that all handled keys are not + * password protected. */ class UnprotectedKeysProtector : SecretKeyRingProtector { override fun hasPassphraseFor(keyId: Long) = true @@ -12,4 +13,4 @@ class UnprotectedKeysProtector : SecretKeyRingProtector { override fun getDecryptor(keyId: Long) = null override fun getEncryptor(keyId: Long) = null -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/fixes/S2KUsageFix.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/fixes/S2KUsageFix.kt index c02486ac..43cca885 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/fixes/S2KUsageFix.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/fixes/S2KUsageFix.kt @@ -14,12 +14,14 @@ import org.pgpainless.implementation.ImplementationFactory import org.pgpainless.key.protection.SecretKeyRingProtector /** - * Repair class to fix keys which use S2K usage of value [SecretKeyPacket.USAGE_CHECKSUM]. - * The method [replaceUsageChecksumWithUsageSha1] ensures that such keys are encrypted using - * S2K usage [SecretKeyPacket.USAGE_SHA1] instead. + * Repair class to fix keys which use S2K usage of value [SecretKeyPacket.USAGE_CHECKSUM]. The + * method [replaceUsageChecksumWithUsageSha1] ensures that such keys are encrypted using S2K usage + * [SecretKeyPacket.USAGE_SHA1] instead. * - * @see Related PGPainless Bug Report - * @see Related PGPainless Feature Request + * @see Related PGPainless Bug + * Report + * @see Related PGPainless Feature + * Request * @see Related upstream BC bug report */ class S2KUsageFix { @@ -27,23 +29,26 @@ class S2KUsageFix { companion object { /** - * Repair method for keys which use S2K usage
USAGE_CHECKSUM
which is deemed insecure. - * This method fixes the private keys by changing them to
USAGE_SHA1
instead. + * Repair method for keys which use S2K usage
USAGE_CHECKSUM
which is deemed + * insecure. This method fixes the private keys by changing them to
USAGE_SHA1
+ * instead. * * @param keys keys * @param protector protector to unlock and re-lock affected private keys - * @param skipKeysWithMissingPassphrase if set to true, missing subkey passphrases will cause the subkey to stay unaffected. + * @param skipKeysWithMissingPassphrase if set to true, missing subkey passphrases will + * cause the subkey to stay unaffected. * @return fixed key ring * @throws PGPException in case of a PGP error. */ @JvmStatic @JvmOverloads fun replaceUsageChecksumWithUsageSha1( - keys: PGPSecretKeyRing, - protector: SecretKeyRingProtector, - skipKeysWithMissingPassphrase: Boolean = false + keys: PGPSecretKeyRing, + protector: SecretKeyRingProtector, + skipKeysWithMissingPassphrase: Boolean = false ): PGPSecretKeyRing { - val digestCalculator = ImplementationFactory.getInstance().getPGPDigestCalculator(HashAlgorithm.SHA1) + val digestCalculator = + ImplementationFactory.getInstance().getPGPDigestCalculator(HashAlgorithm.SHA1) val keyList = mutableListOf() for (key in keys) { // CHECKSUM is not recommended @@ -59,21 +64,22 @@ class S2KUsageFix { keyList.add(key) continue } - throw WrongPassphraseException("Missing passphrase for key with ID " + java.lang.Long.toHexString(keyId)) + throw WrongPassphraseException( + "Missing passphrase for key with ID " + java.lang.Long.toHexString(keyId)) } val privateKey = key.unlock(protector) // This constructor makes use of USAGE_SHA1 by default - val fixedKey = PGPSecretKey( + val fixedKey = + PGPSecretKey( privateKey, key.publicKey, digestCalculator, key.isMasterKey, - protector.getEncryptor(keyId) - ) + protector.getEncryptor(keyId)) keyList.add(fixedKey) } return PGPSecretKeyRing(keyList) } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/passphrase_provider/MapBasedPassphraseProvider.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/passphrase_provider/MapBasedPassphraseProvider.kt index 22260126..ffa3619a 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/passphrase_provider/MapBasedPassphraseProvider.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/passphrase_provider/MapBasedPassphraseProvider.kt @@ -7,10 +7,11 @@ package org.pgpainless.key.protection.passphrase_provider import org.pgpainless.util.Passphrase /** - * Implementation of the [SecretKeyPassphraseProvider] that holds a map of key-IDs and respective [Passphrase]. - * It will return the right passphrase depending on the key-id. + * Implementation of the [SecretKeyPassphraseProvider] that holds a map of key-IDs and respective + * [Passphrase]. It will return the right passphrase depending on the key-id. * * Note: This provider might return null! + * * TODO: Make this null-safe and throw an exception instead? */ class MapBasedPassphraseProvider(val map: Map) : SecretKeyPassphraseProvider { @@ -18,4 +19,4 @@ class MapBasedPassphraseProvider(val map: Map) : SecretKeyPass override fun getPassphraseFor(keyId: Long): Passphrase? = map[keyId] override fun hasPassphrase(keyId: Long): Boolean = map.containsKey(keyId) -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/passphrase_provider/SecretKeyPassphraseProvider.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/passphrase_provider/SecretKeyPassphraseProvider.kt index aaf24b70..d872da51 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/passphrase_provider/SecretKeyPassphraseProvider.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/passphrase_provider/SecretKeyPassphraseProvider.kt @@ -7,16 +7,13 @@ package org.pgpainless.key.protection.passphrase_provider import org.bouncycastle.openpgp.PGPSecretKey import org.pgpainless.util.Passphrase -/** - * Interface to allow the user to provide a [Passphrase] for an encrypted OpenPGP secret key. - */ +/** Interface to allow the user to provide a [Passphrase] for an encrypted OpenPGP secret key. */ interface SecretKeyPassphraseProvider { /** - * Return a passphrase for the given secret key. - * If no record is found, return null. - * Note: In case of an unprotected secret key, this method must may not return null, but a [Passphrase] with - * a content of null. + * Return a passphrase for the given secret key. If no record is found, return null. Note: In + * case of an unprotected secret key, this method must may not return null, but a [Passphrase] + * with a content of null. * * @param secretKey secret key * @return passphrase or null, if no passphrase record is found. @@ -26,9 +23,9 @@ interface SecretKeyPassphraseProvider { } /** - * Return a passphrase for the given key. If no record has been found, return null. - * Note: In case of an unprotected secret key, this method must may not return null, but a [Passphrase] with - * a content of null. + * Return a passphrase for the given key. If no record has been found, return null. Note: In + * case of an unprotected secret key, this method must may not return null, but a [Passphrase] + * with a content of null. * * @param keyId if of the secret key * @return passphrase or null, if no passphrase record has been found. @@ -36,4 +33,4 @@ interface SecretKeyPassphraseProvider { fun getPassphraseFor(keyId: Long): Passphrase? fun hasPassphrase(keyId: Long): Boolean -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/passphrase_provider/SolitaryPassphraseProvider.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/passphrase_provider/SolitaryPassphraseProvider.kt index 46f77342..a8a1735d 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/passphrase_provider/SolitaryPassphraseProvider.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/protection/passphrase_provider/SolitaryPassphraseProvider.kt @@ -6,12 +6,10 @@ package org.pgpainless.key.protection.passphrase_provider import org.pgpainless.util.Passphrase -/** - * Implementation of the [SecretKeyPassphraseProvider] that holds a single [Passphrase]. - */ +/** Implementation of the [SecretKeyPassphraseProvider] that holds a single [Passphrase]. */ class SolitaryPassphraseProvider(val passphrase: Passphrase?) : SecretKeyPassphraseProvider { override fun getPassphraseFor(keyId: Long): Passphrase? = passphrase override fun hasPassphrase(keyId: Long): Boolean = true -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/util/KeyIdUtil.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/util/KeyIdUtil.kt index 63b65672..b26d84dc 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/util/KeyIdUtil.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/util/KeyIdUtil.kt @@ -12,25 +12,24 @@ class KeyIdUtil { companion object { /** - * Convert a long key-id into a key-id. - * A long key-id is a 16 digit hex string. + * Convert a long key-id into a key-id. A long key-id is a 16 digit hex string. * * @param longKeyId 16-digit hexadecimal string * @return key-id converted to {@link Long}. */ @JvmStatic - @Deprecated("Superseded by Long extension method.", - ReplaceWith("Long.fromHexKeyId(longKeyId)")) + @Deprecated( + "Superseded by Long extension method.", ReplaceWith("Long.fromHexKeyId(longKeyId)")) fun fromLongKeyId(longKeyId: String) = Long.fromOpenPgpKeyId(longKeyId) /** * Format a long key-ID as upper-case hex string. + * * @param keyId keyId * @return hex encoded key ID */ @JvmStatic - @Deprecated("Superseded by Long extension method.", - ReplaceWith("keyId.hexKeyId()")) + @Deprecated("Superseded by Long extension method.", ReplaceWith("keyId.hexKeyId()")) fun formatKeyId(keyId: Long) = keyId.openPgpKeyId() } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/util/KeyRingUtils.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/util/KeyRingUtils.kt index 79215408..29f343c1 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/util/KeyRingUtils.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/util/KeyRingUtils.kt @@ -4,6 +4,8 @@ package org.pgpainless.key.util +import java.io.ByteArrayOutputStream +import kotlin.jvm.Throws import openpgp.openPgpKeyId import org.bouncycastle.bcpg.S2K import org.bouncycastle.bcpg.SecretKeyPacket @@ -17,34 +19,32 @@ import org.pgpainless.key.protection.SecretKeyRingProtector import org.pgpainless.key.protection.fixes.S2KUsageFix import org.slf4j.Logger import org.slf4j.LoggerFactory -import java.io.ByteArrayOutputStream -import kotlin.jvm.Throws class KeyRingUtils { companion object { - @JvmStatic - private val LOGGER: Logger = LoggerFactory.getLogger(KeyRingUtils::class.java) + @JvmStatic private val LOGGER: Logger = LoggerFactory.getLogger(KeyRingUtils::class.java) /** - * Return the primary {@link PGPSecretKey} from the provided {@link PGPSecretKeyRing}. - * If it has no primary secret key, throw a {@link NoSuchElementException}. + * Return the primary {@link PGPSecretKey} from the provided {@link PGPSecretKeyRing}. If it + * has no primary secret key, throw a {@link NoSuchElementException}. * * @param secretKeys secret keys * @return primary secret key */ @JvmStatic - @Deprecated("Deprecated in favor of PGPSecretKeyRing extension function.", - ReplaceWith("secretKeys.requireSecretKey(keyId)")) + @Deprecated( + "Deprecated in favor of PGPSecretKeyRing extension function.", + ReplaceWith("secretKeys.requireSecretKey(keyId)")) fun requirePrimarySecretKeyFrom(secretKeys: PGPSecretKeyRing): PGPSecretKey { return secretKeys.requireSecretKey(secretKeys.publicKey.keyID) } /** - * Return the primary secret key from the given secret key ring. - * If the key ring only contains subkeys (e.g. if the primary secret key is stripped), - * this method may return null. + * Return the primary secret key from the given secret key ring. If the key ring only + * contains subkeys (e.g. if the primary secret key is stripped), this method may return + * null. * * @param secretKeys secret key ring * @return primary secret key @@ -61,8 +61,8 @@ class KeyRingUtils { } /** - * Return the primary {@link PGPPublicKey} from the provided key ring. - * Throws a {@link NoSuchElementException} if the key ring has no primary public key. + * Return the primary {@link PGPPublicKey} from the provided key ring. Throws a {@link + * NoSuchElementException} if the key ring has no primary public key. * * @param keyRing key ring * @return primary public key @@ -70,11 +70,12 @@ class KeyRingUtils { @JvmStatic fun requirePrimaryPublicKeyFrom(keyRing: PGPKeyRing): PGPPublicKey { return getPrimaryPublicKey(keyRing) - ?: throw NoSuchElementException("Provided PGPKeyRing has no primary public key.") + ?: throw NoSuchElementException("Provided PGPKeyRing has no primary public key.") } /** - * Return the primary {@link PGPPublicKey} from the provided key ring or null if it has none. + * Return the primary {@link PGPPublicKey} from the provided key ring or null if it has + * none. * * @param keyRing key ring * @return primary public key @@ -91,8 +92,8 @@ class KeyRingUtils { } /** - * Require the public key with the given subKeyId from the keyRing. - * If no such subkey exists, throw an {@link NoSuchElementException}. + * Require the public key with the given subKeyId from the keyRing. If no such subkey + * exists, throw an {@link NoSuchElementException}. * * @param keyRing key ring * @param subKeyId subkey id @@ -101,12 +102,13 @@ class KeyRingUtils { @JvmStatic fun requirePublicKeyFrom(keyRing: PGPKeyRing, subKeyId: Long): PGPPublicKey { return keyRing.getPublicKey(subKeyId) - ?: throw NoSuchElementException("KeyRing does not contain public key with keyId ${subKeyId.openPgpKeyId()}.") + ?: throw NoSuchElementException( + "KeyRing does not contain public key with keyId ${subKeyId.openPgpKeyId()}.") } /** - * Require the secret key with the given secret subKeyId from the secret keyRing. - * If no such subkey exists, throw an {@link NoSuchElementException}. + * Require the secret key with the given secret subKeyId from the secret keyRing. If no such + * subkey exists, throw an {@link NoSuchElementException}. * * @param keyRing secret key ring * @param subKeyId subkey id @@ -115,7 +117,8 @@ class KeyRingUtils { @JvmStatic fun requireSecretKeyFrom(keyRing: PGPSecretKeyRing, subKeyId: Long): PGPSecretKey { return keyRing.getSecretKey(subKeyId) - ?: throw NoSuchElementException("KeyRing does not contain secret key with keyID ${subKeyId.openPgpKeyId()}.") + ?: throw NoSuchElementException( + "KeyRing does not contain secret key with keyID ${subKeyId.openPgpKeyId()}.") } @JvmStatic @@ -128,57 +131,67 @@ class KeyRingUtils { } /** - * Extract a {@link PGPPublicKeyRing} containing all public keys from the provided {@link PGPSecretKeyRing}. + * Extract a {@link PGPPublicKeyRing} containing all public keys from the provided {@link + * PGPSecretKeyRing}. * * @param secretKeys secret key ring * @return public key ring */ @JvmStatic - @Deprecated("Deprecated in favor of PGPSecretKeyRing extension method.", - ReplaceWith("secretKeys.certificate", "org.bouncycastle.extensions.certificate")) + @Deprecated( + "Deprecated in favor of PGPSecretKeyRing extension method.", + ReplaceWith("secretKeys.certificate", "org.bouncycastle.extensions.certificate")) fun publicKeyRingFrom(secretKeys: PGPSecretKeyRing): PGPPublicKeyRing { return secretKeys.certificate } /** - * Extract {@link PGPPublicKeyRing PGPPublicKeyRings} from all {@link PGPSecretKeyRing PGPSecretKeyRings} in - * the given {@link PGPSecretKeyRingCollection} and return them as a {@link PGPPublicKeyRingCollection}. + * Extract {@link PGPPublicKeyRing PGPPublicKeyRings} from all {@link PGPSecretKeyRing + * PGPSecretKeyRings} in the given {@link PGPSecretKeyRingCollection} and return them as a + * {@link PGPPublicKeyRingCollection}. * * @param secretKeyRings secret key ring collection * @return public key ring collection */ @JvmStatic - fun publicKeyRingCollectionFrom(secretKeyRings: PGPSecretKeyRingCollection): PGPPublicKeyRingCollection { + fun publicKeyRingCollectionFrom( + secretKeyRings: PGPSecretKeyRingCollection + ): PGPPublicKeyRingCollection { return PGPPublicKeyRingCollection( - secretKeyRings.keyRings.asSequence() - .map { it.certificate } - .toList()) + secretKeyRings.keyRings.asSequence().map { it.certificate }.toList()) } /** - * Create a new {@link PGPPublicKeyRingCollection} from an array of {@link PGPPublicKeyRing PGPPublicKeyRings}. + * Create a new {@link PGPPublicKeyRingCollection} from an array of {@link PGPPublicKeyRing + * PGPPublicKeyRings}. * * @param certificates array of public key rings * @return key ring collection */ @JvmStatic - fun keyRingsToKeyRingCollection(vararg certificates: PGPPublicKeyRing): PGPPublicKeyRingCollection { + fun keyRingsToKeyRingCollection( + vararg certificates: PGPPublicKeyRing + ): PGPPublicKeyRingCollection { return PGPPublicKeyRingCollection(certificates.toList()) } /** - * Create a new {@link PGPSecretKeyRingCollection} from an array of {@link PGPSecretKeyRing PGPSecretKeyRings}. + * Create a new {@link PGPSecretKeyRingCollection} from an array of {@link PGPSecretKeyRing + * PGPSecretKeyRings}. * * @param secretKeys array of secret key rings * @return secret key ring collection */ @JvmStatic - fun keyRingsToKeyRingCollection(vararg secretKeys: PGPSecretKeyRing): PGPSecretKeyRingCollection { + fun keyRingsToKeyRingCollection( + vararg secretKeys: PGPSecretKeyRing + ): PGPSecretKeyRingCollection { return PGPSecretKeyRingCollection(secretKeys.toList()) } /** - * Return true, if the given {@link PGPPublicKeyRing} contains a {@link PGPPublicKey} for the given key id. + * Return true, if the given {@link PGPPublicKeyRing} contains a {@link PGPPublicKey} for + * the given key id. * * @param certificate public key ring * @param keyId id of the key in question @@ -194,8 +207,8 @@ class KeyRingUtils { * * @param keyRing key ring * @param certification key signature - * @return key ring with injected signature * @param either {@link PGPPublicKeyRing} or {@link PGPSecretKeyRing} + * @return key ring with injected signature */ @JvmStatic fun injectCertification(keyRing: T, certification: PGPSignature): T { @@ -210,27 +223,35 @@ class KeyRingUtils { * @param certification key signature * @param either {@link PGPPublicKeyRing} or {@link PGPSecretKeyRing} * @return key ring with injected signature - * * @throws NoSuchElementException in case that the signed key is not part of the key ring */ @JvmStatic - fun injectCertification(keyRing: T, certifiedKey: PGPPublicKey, certification: PGPSignature): T { + fun injectCertification( + keyRing: T, + certifiedKey: PGPPublicKey, + certification: PGPSignature + ): T { val secretAndPublicKeys = secretAndPublicKeys(keyRing) val secretKeys: PGPSecretKeyRing? = secretAndPublicKeys.first var certificate: PGPPublicKeyRing = secretAndPublicKeys.second if (!keyRingContainsKeyWithId(certificate, certifiedKey.keyID)) { - throw NoSuchElementException("Cannot find public key with id ${certifiedKey.keyID.openPgpKeyId()} in the provided key ring.") + throw NoSuchElementException( + "Cannot find public key with id ${certifiedKey.keyID.openPgpKeyId()} in the provided key ring.") } - certificate = PGPPublicKeyRing( - certificate.publicKeys.asSequence().map { - if (it.keyID == certifiedKey.keyID) { - PGPPublicKey.addCertification(it, certification) - } else { - it + certificate = + PGPPublicKeyRing( + certificate.publicKeys + .asSequence() + .map { + if (it.keyID == certifiedKey.keyID) { + PGPPublicKey.addCertification(it, certification) + } else { + it + } } - }.toList()) + .toList()) return if (secretKeys == null) { certificate as T } else { @@ -248,16 +269,23 @@ class KeyRingUtils { * @return key ring with injected certification */ @JvmStatic - fun injectCertification(keyRing: T, userId: CharSequence, certification: PGPSignature): T { + fun injectCertification( + keyRing: T, + userId: CharSequence, + certification: PGPSignature + ): T { val secretAndPublicKeys = secretAndPublicKeys(keyRing) val secretKeys: PGPSecretKeyRing? = secretAndPublicKeys.first var certificate: PGPPublicKeyRing = secretAndPublicKeys.second - certificate = PGPPublicKeyRing( + certificate = + PGPPublicKeyRing( listOf( - PGPPublicKey.addCertification(requirePrimaryPublicKeyFrom(certificate), - userId.toString(), certification) - ).plus(certificate.publicKeys.asSequence().drop(1))) + PGPPublicKey.addCertification( + requirePrimaryPublicKeyFrom(certificate), + userId.toString(), + certification)) + .plus(certificate.publicKeys.asSequence().drop(1))) return if (secretKeys == null) { certificate as T @@ -276,16 +304,23 @@ class KeyRingUtils { * @return key ring with injected user-attribute certification */ @JvmStatic - fun injectCertification(keyRing: T, userAttributes: PGPUserAttributeSubpacketVector, certification: PGPSignature): T { + fun injectCertification( + keyRing: T, + userAttributes: PGPUserAttributeSubpacketVector, + certification: PGPSignature + ): T { val secretAndPublicKeys = secretAndPublicKeys(keyRing) val secretKeys: PGPSecretKeyRing? = secretAndPublicKeys.first var certificate: PGPPublicKeyRing = secretAndPublicKeys.second - certificate = PGPPublicKeyRing( + certificate = + PGPPublicKeyRing( listOf( - PGPPublicKey.addCertification(requirePrimaryPublicKeyFrom(certificate), - userAttributes, certification) - ).plus(certificate.publicKeys.asSequence().drop(1))) + PGPPublicKey.addCertification( + requirePrimaryPublicKeyFrom(certificate), + userAttributes, + certification)) + .plus(certificate.publicKeys.asSequence().drop(1))) return if (secretKeys == null) { certificate as T @@ -316,7 +351,9 @@ class KeyRingUtils { } @JvmStatic - private fun secretAndPublicKeys(keyRing: PGPKeyRing): Pair { + private fun secretAndPublicKeys( + keyRing: PGPKeyRing + ): Pair { var secretKeys: PGPSecretKeyRing? = null val certificate: PGPPublicKeyRing when (keyRing) { @@ -327,7 +364,9 @@ class KeyRingUtils { is PGPPublicKeyRing -> { certificate = keyRing } - else -> throw IllegalArgumentException("keyRing is an unknown PGPKeyRing subclass: ${keyRing.javaClass.name}") + else -> + throw IllegalArgumentException( + "keyRing is an unknown PGPKeyRing subclass: ${keyRing.javaClass.name}") } return secretKeys to certificate } @@ -340,12 +379,16 @@ class KeyRingUtils { * @return secret key ring with injected secret key */ @JvmStatic - fun keysPlusSecretKey(secretKeys: PGPSecretKeyRing, secretKey: PGPSecretKey): PGPSecretKeyRing { + fun keysPlusSecretKey( + secretKeys: PGPSecretKeyRing, + secretKey: PGPSecretKey + ): PGPSecretKeyRing { return PGPSecretKeyRing.insertSecretKey(secretKeys, secretKey) } /** * Inject the given signature into the public part of the given secret key. + * * @param secretKey secret key * @param signature signature * @return secret key with the signature injected in its public key @@ -358,15 +401,16 @@ class KeyRingUtils { } /** - * Remove the secret key of the subkey identified by the given secret key id from the key ring. - * The public part stays attached to the key ring, so that it can still be used for encryption / verification of signatures. + * Remove the secret key of the subkey identified by the given secret key id from the key + * ring. The public part stays attached to the key ring, so that it can still be used for + * encryption / verification of signatures. * - * This method is intended to be used to remove secret primary keys from live keys when those are kept in offline storage. + * This method is intended to be used to remove secret primary keys from live keys when + * those are kept in offline storage. * * @param secretKeys secret key ring * @param keyId id of the secret key to remove * @return secret key ring with removed secret key - * * @throws IOException in case of an error during serialization / deserialization of the key * @throws PGPException in case of a broken key */ @@ -376,7 +420,8 @@ class KeyRingUtils { "Bouncy Castle currently cannot deal with stripped primary secret keys." } if (secretKeys.getSecretKey(keyId) == null) { - throw NoSuchElementException("PGPSecretKeyRing does not contain secret key ${keyId.openPgpKeyId()}.") + throw NoSuchElementException( + "PGPSecretKeyRing does not contain secret key ${keyId.openPgpKeyId()}.") } val out = ByteArrayOutputStream() @@ -389,10 +434,9 @@ class KeyRingUtils { it.encode(out) } } - secretKeys.extraPublicKeys.forEach { - it.encode(out) - } - return PGPSecretKeyRing(out.toByteArray(), ImplementationFactory.getInstance().keyFingerprintCalculator) + secretKeys.extraPublicKeys.forEach { it.encode(out) } + return PGPSecretKeyRing( + out.toByteArray(), ImplementationFactory.getInstance().keyFingerprintCalculator) } /** @@ -404,7 +448,9 @@ class KeyRingUtils { */ @JvmStatic fun getStrippedDownPublicKey(bloatedKey: PGPPublicKey): PGPPublicKey { - return PGPPublicKey(bloatedKey.publicKeyPacket, ImplementationFactory.getInstance().keyFingerprintCalculator) + return PGPPublicKey( + bloatedKey.publicKeyPacket, + ImplementationFactory.getInstance().keyFingerprintCalculator) } @JvmStatic @@ -413,7 +459,7 @@ class KeyRingUtils { key.rawUserIDs.forEach { try { add(Strings.fromUTF8ByteArray(it)) - } catch (e : IllegalArgumentException) { + } catch (e: IllegalArgumentException) { LOGGER.warn("Invalid UTF-8 user-ID encountered: ${String(it)}") } } @@ -422,50 +468,62 @@ class KeyRingUtils { @JvmStatic @Throws(MissingPassphraseException::class, PGPException::class) - fun changePassphrase(keyId: Long?, - secretKeys: PGPSecretKeyRing, - oldProtector: SecretKeyRingProtector, - newProtector: SecretKeyRingProtector): PGPSecretKeyRing { + fun changePassphrase( + keyId: Long?, + secretKeys: PGPSecretKeyRing, + oldProtector: SecretKeyRingProtector, + newProtector: SecretKeyRingProtector + ): PGPSecretKeyRing { return if (keyId == null) { - PGPSecretKeyRing( - secretKeys.secretKeys.asSequence().map { - reencryptPrivateKey(it, oldProtector, newProtector) - }.toList() - ) - } else { - PGPSecretKeyRing( - secretKeys.secretKeys.asSequence().map { - if (it.keyID == keyId) { - reencryptPrivateKey(it, oldProtector, newProtector) - } else { - it + PGPSecretKeyRing( + secretKeys.secretKeys + .asSequence() + .map { reencryptPrivateKey(it, oldProtector, newProtector) } + .toList()) + } else { + PGPSecretKeyRing( + secretKeys.secretKeys + .asSequence() + .map { + if (it.keyID == keyId) { + reencryptPrivateKey(it, oldProtector, newProtector) + } else { + it + } } - }.toList() - ) - }.let { s2kUsageFixIfNecessary(it, newProtector) } + .toList()) + } + .let { s2kUsageFixIfNecessary(it, newProtector) } } @JvmStatic - fun reencryptPrivateKey(secretKey: PGPSecretKey, - oldProtector: SecretKeyRingProtector, - newProtector: SecretKeyRingProtector): PGPSecretKey { + fun reencryptPrivateKey( + secretKey: PGPSecretKey, + oldProtector: SecretKeyRingProtector, + newProtector: SecretKeyRingProtector + ): PGPSecretKey { if (secretKey.s2K != null && secretKey.s2K.type == S2K.GNU_DUMMY_S2K) { // If the key uses GNU_DUMMY_S2K we leave it as is return secretKey } - return PGPSecretKey.copyWithNewPassword(secretKey, - oldProtector.getDecryptor(secretKey.keyID), - newProtector.getEncryptor(secretKey.keyID)) + return PGPSecretKey.copyWithNewPassword( + secretKey, + oldProtector.getDecryptor(secretKey.keyID), + newProtector.getEncryptor(secretKey.keyID)) } @JvmStatic - fun s2kUsageFixIfNecessary(secretKeys: PGPSecretKeyRing, protector: SecretKeyRingProtector): PGPSecretKeyRing { - if (secretKeys.secretKeys.asSequence().any { it.s2KUsage == SecretKeyPacket.USAGE_CHECKSUM }) { + fun s2kUsageFixIfNecessary( + secretKeys: PGPSecretKeyRing, + protector: SecretKeyRingProtector + ): PGPSecretKeyRing { + if (secretKeys.secretKeys.asSequence().any { + it.s2KUsage == SecretKeyPacket.USAGE_CHECKSUM + }) { return S2KUsageFix.replaceUsageChecksumWithUsageSha1(secretKeys, protector, true) } return secretKeys } - } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/util/PublicKeyParameterValidationUtil.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/util/PublicKeyParameterValidationUtil.kt index b5576928..57f0d423 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/util/PublicKeyParameterValidationUtil.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/util/PublicKeyParameterValidationUtil.kt @@ -4,6 +4,10 @@ package org.pgpainless.key.util +import java.io.ByteArrayOutputStream +import java.io.IOException +import java.math.BigInteger +import java.security.SecureRandom import org.bouncycastle.bcpg.* import org.bouncycastle.extensions.publicKeyAlgorithm import org.bouncycastle.openpgp.* @@ -15,16 +19,12 @@ import org.pgpainless.algorithm.SignatureType import org.pgpainless.algorithm.SymmetricKeyAlgorithm import org.pgpainless.exception.KeyIntegrityException import org.pgpainless.implementation.ImplementationFactory.Companion.getInstance -import java.io.ByteArrayOutputStream -import java.io.IOException -import java.math.BigInteger -import java.security.SecureRandom /** - * Utility class to verify keys against Key Overwriting (KO) attacks. - * This class of attacks is only possible if the attacker has access to the (encrypted) secret key material. - * To execute the attack, they would modify the unauthenticated parameters of the users public key. - * Using the modified public key in combination with the unmodified secret key material can then lead to the + * Utility class to verify keys against Key Overwriting (KO) attacks. This class of attacks is only + * possible if the attacker has access to the (encrypted) secret key material. To execute the + * attack, they would modify the unauthenticated parameters of the users public key. Using the + * modified public key in combination with the unmodified secret key material can then lead to the * extraction of secret key parameters via weakly crafted messages. * * @see Key Overwriting (KO) Attacks against OpenPGP @@ -41,19 +41,32 @@ class PublicKeyParameterValidationUtil { val key = privateKey.privateKeyDataPacket when (privateKey.privateKeyDataPacket) { is RSASecretBCPGKey -> - valid = verifyRSAKeyIntegrity(key as RSASecretBCPGKey, publicKey.publicKeyPacket.key as RSAPublicBCPGKey) + valid = + verifyRSAKeyIntegrity( + key as RSASecretBCPGKey, + publicKey.publicKeyPacket.key as RSAPublicBCPGKey) is EdSecretBCPGKey -> - valid = verifyEdDsaKeyIntegrity(key as EdSecretBCPGKey, publicKey.publicKeyPacket.key as EdDSAPublicBCPGKey) + valid = + verifyEdDsaKeyIntegrity( + key as EdSecretBCPGKey, + publicKey.publicKeyPacket.key as EdDSAPublicBCPGKey) is DSASecretBCPGKey -> - valid = verifyDsaKeyIntegrity(key as DSASecretBCPGKey, publicKey.publicKeyPacket.key as DSAPublicBCPGKey) + valid = + verifyDsaKeyIntegrity( + key as DSASecretBCPGKey, + publicKey.publicKeyPacket.key as DSAPublicBCPGKey) is ElGamalSecretBCPGKey -> - valid = verifyElGamalKeyIntegrity(key as ElGamalSecretBCPGKey, publicKey.publicKeyPacket.key as ElGamalPublicBCPGKey) + valid = + verifyElGamalKeyIntegrity( + key as ElGamalSecretBCPGKey, + publicKey.publicKeyPacket.key as ElGamalPublicBCPGKey) } if (!valid) throw KeyIntegrityException() // Additional to the algorithm-specific tests further above, we also perform - // generic functionality tests with the key, such as whether it is able to decrypt encrypted data + // generic functionality tests with the key, such as whether it is able to decrypt + // encrypted data // or verify signatures. // These tests should be more or less constant time. if (algorithm.isSigningCapable()) { @@ -68,21 +81,30 @@ class PublicKeyParameterValidationUtil { @JvmStatic @Throws(KeyIntegrityException::class) - private fun verifyRSAKeyIntegrity(secretKey: RSASecretBCPGKey, publicKey: RSAPublicBCPGKey): Boolean { + private fun verifyRSAKeyIntegrity( + secretKey: RSASecretBCPGKey, + publicKey: RSAPublicBCPGKey + ): Boolean { // Verify that the public keys N is equal to private keys p*q return publicKey.modulus.equals(secretKey.primeP.multiply(secretKey.primeQ)) } @JvmStatic @Throws(KeyIntegrityException::class) - private fun verifyEdDsaKeyIntegrity(secretKey: EdSecretBCPGKey, publicKey: EdDSAPublicBCPGKey): Boolean { + private fun verifyEdDsaKeyIntegrity( + secretKey: EdSecretBCPGKey, + publicKey: EdDSAPublicBCPGKey + ): Boolean { // TODO: Implement return true } @JvmStatic @Throws(KeyIntegrityException::class) - private fun verifyDsaKeyIntegrity(privateKey: DSASecretBCPGKey, publicKey: DSAPublicBCPGKey): Boolean { + private fun verifyDsaKeyIntegrity( + privateKey: DSASecretBCPGKey, + publicKey: DSAPublicBCPGKey + ): Boolean { // Not sure what value to put here in order to have a "robust" primality check // I went with 40, since that's what SO recommends: // https://stackoverflow.com/a/6330138 @@ -134,15 +156,20 @@ class PublicKeyParameterValidationUtil { /** * Validate ElGamal public key parameters. * - * Original implementation by the openpgpjs authors: - * OpenPGP.js + * source + * * @param secretKey secret key * @param publicKey public key * @return true if supposedly valid, false if invalid */ @JvmStatic @Throws(KeyIntegrityException::class) - private fun verifyElGamalKeyIntegrity(secretKey: ElGamalSecretBCPGKey, publicKey: ElGamalPublicBCPGKey): Boolean { + private fun verifyElGamalKeyIntegrity( + secretKey: ElGamalSecretBCPGKey, + publicKey: ElGamalPublicBCPGKey + ): Boolean { val p = publicKey.p val g = publicKey.g val y = publicKey.y @@ -185,7 +212,9 @@ class PublicKeyParameterValidationUtil { } /** - * Verify that the public key can be used to successfully verify a signature made by the private key. + * Verify that the public key can be used to successfully verify a signature made by the + * private key. + * * @param privateKey private key * @param publicKey public key * @return false if signature verification fails @@ -193,16 +222,23 @@ class PublicKeyParameterValidationUtil { @JvmStatic private fun verifyCanSign(privateKey: PGPPrivateKey, publicKey: PGPPublicKey): Boolean { val data = ByteArray(512).also { SecureRandom().nextBytes(it) } - val signatureGenerator = PGPSignatureGenerator( - getInstance().getPGPContentSignerBuilder(requireFromId(publicKey.algorithm), HashAlgorithm.SHA256)) + val signatureGenerator = + PGPSignatureGenerator( + getInstance() + .getPGPContentSignerBuilder( + requireFromId(publicKey.algorithm), HashAlgorithm.SHA256)) return try { - signatureGenerator.apply { - init(SignatureType.TIMESTAMP.code, privateKey) - update(data) - }.generate().apply { - init(getInstance().pgpContentVerifierBuilderProvider, publicKey) - update(data) - }.verify() + signatureGenerator + .apply { + init(SignatureType.TIMESTAMP.code, privateKey) + update(data) + } + .generate() + .apply { + init(getInstance().pgpContentVerifierBuilderProvider, publicKey) + update(data) + } + .verify() } catch (e: PGPException) { false } @@ -211,6 +247,7 @@ class PublicKeyParameterValidationUtil { /** * Verify that the public key can be used to encrypt a message which can successfully be * decrypted using the private key. + * * @param privateKey private key * @param publicKey public key * @return false if decryption of a message encrypted with the public key fails @@ -218,10 +255,12 @@ class PublicKeyParameterValidationUtil { @JvmStatic private fun verifyCanDecrypt(privateKey: PGPPrivateKey, publicKey: PGPPublicKey): Boolean { val data = ByteArray(1024).also { SecureRandom().nextBytes(it) } - val encryptedDataGenerator = PGPEncryptedDataGenerator( - getInstance().getPGPDataEncryptorBuilder(SymmetricKeyAlgorithm.AES_256)).apply { - addMethod(getInstance().getPublicKeyKeyEncryptionMethodGenerator(publicKey)) - } + val encryptedDataGenerator = + PGPEncryptedDataGenerator( + getInstance().getPGPDataEncryptorBuilder(SymmetricKeyAlgorithm.AES_256)) + .apply { + addMethod(getInstance().getPublicKeyKeyEncryptionMethodGenerator(publicKey)) + } var out = ByteArrayOutputStream() try { @@ -230,7 +269,8 @@ class PublicKeyParameterValidationUtil { encryptedDataGenerator.close() val encryptedDataList = PGPEncryptedDataList(out.toByteArray()) val decryptorFactory = getInstance().getPublicKeyDataDecryptorFactory(privateKey) - val encryptedData = encryptedDataList.encryptedDataObjects.next() as PGPPublicKeyEncryptedData + val encryptedData = + encryptedDataList.encryptedDataObjects.next() as PGPPublicKeyEncryptedData val decrypted = encryptedData.getDataStream(decryptorFactory) out = ByteArrayOutputStream() Streams.pipeAll(decrypted, out) @@ -243,4 +283,4 @@ class PublicKeyParameterValidationUtil { return Arrays.constantTimeAreEqual(data, out.toByteArray()) } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/util/RevocationAttributes.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/util/RevocationAttributes.kt index 39f69fbe..2300325a 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/util/RevocationAttributes.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/util/RevocationAttributes.kt @@ -4,50 +4,43 @@ package org.pgpainless.key.util -class RevocationAttributes( - val reason: Reason, - val description: String) { +class RevocationAttributes(val reason: Reason, val description: String) { /** - * Reason for revocation. - * There are two kinds of reasons: hard and soft reason. + * Reason for revocation. There are two kinds of reasons: hard and soft reason. * - * Soft revocation reasons gracefully disable keys or user-ids. - * Softly revoked keys can no longer be used to encrypt data to or to generate signatures. - * Any signature made after a key has been soft revoked is deemed invalid. - * Any signature made before the key has been soft revoked stays valid. - * Soft revoked info can be re-certified at a later point. + * Soft revocation reasons gracefully disable keys or user-ids. Softly revoked keys can no + * longer be used to encrypt data to or to generate signatures. Any signature made after a key + * has been soft revoked is deemed invalid. Any signature made before the key has been soft + * revoked stays valid. Soft revoked info can be re-certified at a later point. * * Hard revocation reasons on the other hand renders the key or user-id invalid immediately. - * Hard reasons are suitable to use if for example a key got compromised. - * Any signature made before or after a key has been hard revoked is no longer considered valid. - * Hard revoked information can also not be re-certified. + * Hard reasons are suitable to use if for example a key got compromised. Any signature made + * before or after a key has been hard revoked is no longer considered valid. Hard revoked + * information can also not be re-certified. */ enum class Reason(val code: Byte) { /** - * The key or certification is being revoked without a reason. - * This is a HARD revocation reason and cannot be undone. + * The key or certification is being revoked without a reason. This is a HARD revocation + * reason and cannot be undone. */ NO_REASON(0), /** - * The key was superseded by another key. - * This is a SOFT revocation reason and can be undone. + * The key was superseded by another key. This is a SOFT revocation reason and can be + * undone. */ KEY_SUPERSEDED(1), /** - * The key has potentially been compromised. - * This is a HARD revocation reason and cannot be undone. + * The key has potentially been compromised. This is a HARD revocation reason and cannot be + * undone. */ KEY_COMPROMISED(2), /** - * The key was retired and shall no longer be used. - * This is a SOFT revocation reason can can be undone. + * The key was retired and shall no longer be used. This is a SOFT revocation reason can can + * be undone. */ KEY_RETIRED(3), - /** - * The user-id is no longer valid. - * This is a SOFT revocation reason and can be undone. - */ + /** The user-id is no longer valid. This is a SOFT revocation reason and can be undone. */ USER_ID_NO_LONGER_VALID(32), ; @@ -59,8 +52,7 @@ class RevocationAttributes( companion object { - @JvmStatic - private val MAP = values().associateBy { it.code } + @JvmStatic private val MAP = values().associateBy { it.code } /** * Decode a machine-readable reason code. @@ -69,13 +61,13 @@ class RevocationAttributes( * @return reason */ @JvmStatic - fun fromCode(code: Byte) = MAP[code] ?: throw IllegalArgumentException("Invalid revocation reason: $code") + fun fromCode(code: Byte) = + MAP[code] ?: throw IllegalArgumentException("Invalid revocation reason: $code") /** - * Return true if the [Reason] the provided code encodes is a hard revocation reason, false - * otherwise. - * Hard revocations cannot be undone, while keys or certifications with soft revocations can be - * re-certified by placing another signature on them. + * Return true if the [Reason] the provided code encodes is a hard revocation reason, + * false otherwise. Hard revocations cannot be undone, while keys or certifications with + * soft revocations can be re-certified by placing another signature on them. * * @param code reason code * @return is hard @@ -84,21 +76,25 @@ class RevocationAttributes( fun isHardRevocation(code: Byte) = MAP[code]?.let { isHardRevocation(it) } ?: true /** - * Return true if the given [Reason] is a hard revocation, false otherwise. - * Hard revocations cannot be undone, while keys or certifications with soft revocations can be - * re-certified by placing another signature on them. + * Return true if the given [Reason] is a hard revocation, false otherwise. Hard + * revocations cannot be undone, while keys or certifications with soft revocations can + * be re-certified by placing another signature on them. * * @param reason reason * @return is hard */ @JvmStatic - fun isHardRevocation(reason: Reason) = when (reason) { - KEY_SUPERSEDED, KEY_RETIRED, USER_ID_NO_LONGER_VALID -> false - else -> true - } + fun isHardRevocation(reason: Reason) = + when (reason) { + KEY_SUPERSEDED, + KEY_RETIRED, + USER_ID_NO_LONGER_VALID -> false + else -> true + } /** * Return true if the given reason code denotes a key revocation. + * * @param code reason code * @return is key revocation */ @@ -107,14 +103,16 @@ class RevocationAttributes( /** * Return true if the given [Reason] denotes a key revocation. + * * @param reason reason * @return is key revocation */ @JvmStatic - fun isKeyRevocation(reason: Reason) = when (reason) { - USER_ID_NO_LONGER_VALID -> false - else -> true - } + fun isKeyRevocation(reason: Reason) = + when (reason) { + USER_ID_NO_LONGER_VALID -> false + else -> true + } } } @@ -124,11 +122,9 @@ class RevocationAttributes( } companion object { - @JvmStatic - fun createKeyRevocation() = WithReason(RevocationType.KEY_REVOCATION) + @JvmStatic fun createKeyRevocation() = WithReason(RevocationType.KEY_REVOCATION) - @JvmStatic - fun createCertificateRevocation() = WithReason(RevocationType.CERT_REVOCATION) + @JvmStatic fun createCertificateRevocation() = WithReason(RevocationType.CERT_REVOCATION) } class WithReason(val type: RevocationType) { @@ -143,13 +139,16 @@ class RevocationAttributes( private fun reasonTypeMatches(reason: Reason, type: RevocationType): Boolean { return when (type) { RevocationType.KEY_REVOCATION -> reason != Reason.USER_ID_NO_LONGER_VALID - RevocationType.CERT_REVOCATION -> reason == Reason.USER_ID_NO_LONGER_VALID || reason == Reason.NO_REASON + RevocationType.CERT_REVOCATION -> + reason == Reason.USER_ID_NO_LONGER_VALID || reason == Reason.NO_REASON } } } class WithDescription(val reason: Reason) { - fun withDescription(description: String): RevocationAttributes = RevocationAttributes(reason, description) + fun withDescription(description: String): RevocationAttributes = + RevocationAttributes(reason, description) + fun withoutDescription() = RevocationAttributes(reason, "") } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/util/UserId.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/util/UserId.kt index b461f6b4..c2b81700 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/util/UserId.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/util/UserId.kt @@ -4,11 +4,7 @@ package org.pgpainless.key.util -class UserId internal constructor( - name: String?, - comment: String?, - email: String? -) : CharSequence { +class UserId internal constructor(name: String?, comment: String?, email: String?) : CharSequence { private val _name: String? val comment: String? @@ -60,9 +56,9 @@ class UserId internal constructor( if (other !is UserId) { return false } - return isComponentEqual(_name, other._name, false) - && isComponentEqual(comment, other.comment, false) - && isComponentEqual(email, other.email, true) + return isComponentEqual(_name, other._name, false) && + isComponentEqual(comment, other.comment, false) && + isComponentEqual(email, other.email, true) } override fun get(index: Int): Char { @@ -81,59 +77,73 @@ class UserId internal constructor( return full } - private fun isComponentEqual(value: String?, otherValue: String?, ignoreCase: Boolean): Boolean = value.equals(otherValue, ignoreCase) + private fun isComponentEqual( + value: String?, + otherValue: String?, + ignoreCase: Boolean + ): Boolean = value.equals(otherValue, ignoreCase) - fun toBuilder() = builder().also { builder -> - if (this._name != null) builder.withName(_name) - if (this.comment != null) builder.withComment(comment) - if (this.email != null) builder.withEmail(email) - } + fun toBuilder() = + builder().also { builder -> + if (this._name != null) builder.withName(_name) + if (this.comment != null) builder.withComment(comment) + if (this.email != null) builder.withEmail(email) + } companion object { // Email regex: https://emailregex.com/ - // switched "a-z0-9" to "\p{L}\u0900-\u097F0-9" for better support for international characters + // switched "a-z0-9" to "\p{L}\u0900-\u097F0-9" for better support for international + // characters // \\p{L} = Unicode Letters // \u0900-\u097F = Hindi Letters @JvmStatic - private val emailPattern = ("(?:[\\p{L}\\u0900-\\u097F0-9!#\\$%&'*+/=?^_`{|}~-]+(?:\\.[\\p{L}\\u0900-\\u097F0-9!#\\$%&'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-" + - "\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[\\p{L}\\u0900-\\u097F0-9](?:[\\p{L}\\u0900-\\u097F0-9" + - "-]*[\\p{L}\\u0900-\\u097F0-9])?\\.)+[\\p{L}\\u0900-\\u097F0-9](?:[\\p{L}\\u0900-\\u097F0-9-]*[\\p{L}\\u0900-\\u097F0-9])?|\\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)" + - "\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[$\\p{L}\\u0900-\\u097F0-9-]*[\\p{L}\\u0900-\\u097F0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f" + - "\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)])").toPattern() + private val emailPattern = + ("(?:[\\p{L}\\u0900-\\u097F0-9!#\\$%&'*+/=?^_`{|}~-]+(?:\\.[\\p{L}\\u0900-\\u097F0-9!#\\$%&'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-" + + "\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[\\p{L}\\u0900-\\u097F0-9](?:[\\p{L}\\u0900-\\u097F0-9" + + "-]*[\\p{L}\\u0900-\\u097F0-9])?\\.)+[\\p{L}\\u0900-\\u097F0-9](?:[\\p{L}\\u0900-\\u097F0-9-]*[\\p{L}\\u0900-\\u097F0-9])?|\\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)" + + "\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[$\\p{L}\\u0900-\\u097F0-9-]*[\\p{L}\\u0900-\\u097F0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f" + + "\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)])") + .toPattern() // User-ID Regex // "Firstname Lastname (Comment) " // All groups are optional // https://www.rfc-editor.org/rfc/rfc5322#page-16 @JvmStatic - private val nameAddrPattern = "^((?.+?)\\s)?(\\((?.+?)\\)\\s)?(<(?.+?)>)?$".toPattern() + private val nameAddrPattern = + "^((?.+?)\\s)?(\\((?.+?)\\)\\s)?(<(?.+?)>)?$".toPattern() /** - * Parse a [UserId] from free-form text,
name-addr
or
mailbox
string and split it - * up into its components. - * Example inputs for this method: + * Parse a [UserId] from free-form text,
name-addr
or
mailbox
string + * and split it up into its components. Example inputs for this method: *
    - *
  • john@pgpainless.org
  • - *
  • <john@pgpainless.org>
  • - *
  • John Doe
  • - *
  • John Doe <john@pgpainless.org>
  • - *
  • John Doe (work email) <john@pgpainless.org>
  • + *
  • john@pgpainless.org
  • + *
  • <john@pgpainless.org>
  • + *
  • John Doe
  • + *
  • John Doe <john@pgpainless.org>
  • + *
  • John Doe (work email) <john@pgpainless.org>
  • *
- * In these cases, this method will detect email addresses, names and comments and expose those - * via the respective getters. - * This method does not support parsing mail addresses of the following formats: - *
    - *
  • Local domains without TLDs (
    user@localdomain1
    )
  • - *
  • " "@example.org
    (spaces between the quotes)
  • - *
  • "very.(),:;<>[]\".VERY.\"very@\\ \"very\".unusual"@strange.example.com
  • - *
- * Note: This method does not guarantee that
string.equals(UserId.parse(string).toString())
is true. - * For example,
UserId.parse("alice@pgpainless.org").toString()
wraps the mail address in angled brackets. * - * @see RFC5322 §3.4. Address Specification + * In these cases, this method will detect email addresses, names and comments and expose + * those via the respective getters. This method does not support parsing mail addresses of + * the following formats: + *
    + *
  • Local domains without TLDs (
    user@localdomain1
    )
  • + *
  • " "@example.org
    (spaces between the quotes)
  • + *
  • "very.(),:;<>[]\".VERY.\"very@\\ \"very\".unusual"@strange.example.com
  • + *
+ * + * Note: This method does not guarantee that + *
string.equals(UserId.parse(string).toString())
is true. For example, + *
UserId.parse("alice@pgpainless.org").toString()
wraps the mail address in + * + * angled brackets. + * * @param string user-id * @return parsed UserId object + * @see RFC5322 §3.4. Address + * Specification */ @JvmStatic fun parse(string: String): UserId { @@ -152,21 +162,19 @@ class UserId internal constructor( } } - @JvmStatic - fun onlyEmail(email: String) = UserId(null, null, email) + @JvmStatic fun onlyEmail(email: String) = UserId(null, null, email) + + @JvmStatic fun nameAndEmail(name: String, email: String) = UserId(name, null, email) @JvmStatic - fun nameAndEmail(name: String, email: String) = UserId(name, null, email) - - @JvmStatic - fun compare(u1: UserId?, u2: UserId?, comparator: Comparator) = comparator.compare(u1, u2) + fun compare(u1: UserId?, u2: UserId?, comparator: Comparator) = + comparator.compare(u1, u2) @JvmStatic @Deprecated("Deprecated in favor of builde() method.", ReplaceWith("builder()")) fun newBuilder() = builder() - @JvmStatic - fun builder() = Builder() + @JvmStatic fun builder() = Builder() } class Builder internal constructor() { @@ -175,11 +183,15 @@ class UserId internal constructor( var email: String? = null fun withName(name: String) = apply { this.name = name } - fun withComment(comment: String) = apply { this.comment = comment} + + fun withComment(comment: String) = apply { this.comment = comment } + fun withEmail(email: String) = apply { this.email = email } fun noName() = apply { this.name = null } + fun noComment() = apply { this.comment = null } + fun noEmail() = apply { this.email = null } fun build() = UserId(name, comment, email) @@ -188,19 +200,18 @@ class UserId internal constructor( class DefaultComparator : Comparator { override fun compare(o1: UserId?, o2: UserId?): Int { return compareBy { it?._name } - .thenBy { it?.comment } - .thenBy { it?.email } - .compare(o1, o2) + .thenBy { it?.comment } + .thenBy { it?.email } + .compare(o1, o2) } } class DefaultIgnoreCaseComparator : Comparator { override fun compare(p0: UserId?, p1: UserId?): Int { return compareBy { it?._name?.lowercase() } - .thenBy { it?.comment?.lowercase() } - .thenBy { it?.email?.lowercase() } - .compare(p0, p1) + .thenBy { it?.comment?.lowercase() } + .thenBy { it?.email?.lowercase() } + .compare(p0, p1) } - } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/policy/Policy.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/policy/Policy.kt index b7c9f39e..22f42cd9 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/policy/Policy.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/policy/Policy.kt @@ -4,21 +4,23 @@ package org.pgpainless.policy +import java.util.* import org.pgpainless.algorithm.* import org.pgpainless.util.DateUtil import org.pgpainless.util.NotationRegistry -import java.util.* class Policy( - var signatureHashAlgorithmPolicy: HashAlgorithmPolicy, - var revocationSignatureHashAlgorithmPolicy: HashAlgorithmPolicy, - var symmetricKeyEncryptionAlgorithmPolicy: SymmetricKeyAlgorithmPolicy, - var symmetricKeyDecryptionAlgorithmPolicy: SymmetricKeyAlgorithmPolicy, - var compressionAlgorithmPolicy: CompressionAlgorithmPolicy, - var publicKeyAlgorithmPolicy: PublicKeyAlgorithmPolicy, - var notationRegistry: NotationRegistry) { + var signatureHashAlgorithmPolicy: HashAlgorithmPolicy, + var revocationSignatureHashAlgorithmPolicy: HashAlgorithmPolicy, + var symmetricKeyEncryptionAlgorithmPolicy: SymmetricKeyAlgorithmPolicy, + var symmetricKeyDecryptionAlgorithmPolicy: SymmetricKeyAlgorithmPolicy, + var compressionAlgorithmPolicy: CompressionAlgorithmPolicy, + var publicKeyAlgorithmPolicy: PublicKeyAlgorithmPolicy, + var notationRegistry: NotationRegistry +) { - constructor(): this( + constructor() : + this( HashAlgorithmPolicy.smartSignatureHashAlgorithmPolicy(), HashAlgorithmPolicy.smartSignatureHashAlgorithmPolicy(), SymmetricKeyAlgorithmPolicy.symmetricKeyEncryptionPolicy2022(), @@ -33,29 +35,32 @@ class Policy( fun isEnableKeyParameterValidation() = enableKeyParameterValidation - /** - * Create a HashAlgorithmPolicy which accepts all [HashAlgorithms][HashAlgorithm] from the - * given map, if the queried usage date is BEFORE the respective termination date. - * A termination date value of
null
means no termination, resulting in the algorithm being - * acceptable, regardless of usage date. + * Create a HashAlgorithmPolicy which accepts all [HashAlgorithms][HashAlgorithm] from the given + * map, if the queried usage date is BEFORE the respective termination date. A termination date + * value of
null
means no termination, resulting in the algorithm being acceptable, + * regardless of usage date. * * @param defaultHashAlgorithm default hash algorithm * @param algorithmTerminationDates map of acceptable algorithms and their termination dates */ class HashAlgorithmPolicy( - val defaultHashAlgorithm: HashAlgorithm, - val acceptableHashAlgorithmsAndTerminationDates: Map) { + val defaultHashAlgorithm: HashAlgorithm, + val acceptableHashAlgorithmsAndTerminationDates: Map + ) { /** - * Create a [HashAlgorithmPolicy] which accepts all [HashAlgorithms][HashAlgorithm] listed in - * the given list, regardless of usage date. + * Create a [HashAlgorithmPolicy] which accepts all [HashAlgorithms][HashAlgorithm] listed + * in the given list, regardless of usage date. * - * @param defaultHashAlgorithm default hash algorithm (e.g. used as fallback if negotiation fails) + * @param defaultHashAlgorithm default hash algorithm (e.g. used as fallback if negotiation + * fails) * @param acceptableHashAlgorithms list of acceptable hash algorithms */ - constructor(defaultHashAlgorithm: HashAlgorithm, acceptableHashAlgorithms: List) : - this(defaultHashAlgorithm, acceptableHashAlgorithms.associateWith { null }) + constructor( + defaultHashAlgorithm: HashAlgorithm, + acceptableHashAlgorithms: List + ) : this(defaultHashAlgorithm, acceptableHashAlgorithms.associateWith { null }) fun isAcceptable(hashAlgorithm: HashAlgorithm) = isAcceptable(hashAlgorithm, Date()) @@ -64,13 +69,13 @@ class Policy( * * @param hashAlgorithm algorithm * @param referenceTime usage date (e.g. signature creation time) - * * @return acceptance */ fun isAcceptable(hashAlgorithm: HashAlgorithm, referenceTime: Date): Boolean { if (!acceptableHashAlgorithmsAndTerminationDates.containsKey(hashAlgorithm)) return false - val terminationDate = acceptableHashAlgorithmsAndTerminationDates[hashAlgorithm] ?: return true + val terminationDate = + acceptableHashAlgorithmsAndTerminationDates[hashAlgorithm] ?: return true return terminationDate > referenceTime } @@ -85,66 +90,73 @@ class Policy( companion object { @JvmStatic - fun smartSignatureHashAlgorithmPolicy() = HashAlgorithmPolicy( - HashAlgorithm.SHA512, buildMap { - put(HashAlgorithm.SHA3_512, null) - put(HashAlgorithm.SHA3_256, null) - put(HashAlgorithm.SHA512, null) - put(HashAlgorithm.SHA384, null) - put(HashAlgorithm.SHA256, null) - put(HashAlgorithm.SHA224, null) - put(HashAlgorithm.RIPEMD160, DateUtil.parseUTCDate("2013-02-01 00:00:00 UTC")) - put(HashAlgorithm.SHA1, DateUtil.parseUTCDate("2013-02-01 00:00:00 UTC")) - put(HashAlgorithm.MD5, DateUtil.parseUTCDate("1997-02-01 00:00:00 UTC")) - }) + fun smartSignatureHashAlgorithmPolicy() = + HashAlgorithmPolicy( + HashAlgorithm.SHA512, + buildMap { + put(HashAlgorithm.SHA3_512, null) + put(HashAlgorithm.SHA3_256, null) + put(HashAlgorithm.SHA512, null) + put(HashAlgorithm.SHA384, null) + put(HashAlgorithm.SHA256, null) + put(HashAlgorithm.SHA224, null) + put( + HashAlgorithm.RIPEMD160, + DateUtil.parseUTCDate("2013-02-01 00:00:00 UTC")) + put(HashAlgorithm.SHA1, DateUtil.parseUTCDate("2013-02-01 00:00:00 UTC")) + put(HashAlgorithm.MD5, DateUtil.parseUTCDate("1997-02-01 00:00:00 UTC")) + }) /** - * [HashAlgorithmPolicy] which only accepts signatures made using algorithms which are acceptable - * according to 2022 standards. + * [HashAlgorithmPolicy] which only accepts signatures made using algorithms which are + * acceptable according to 2022 standards. * * Particularly this policy only accepts algorithms from the SHA2 and SHA3 families. * * @return static signature algorithm policy */ @JvmStatic - fun static2022SignatureHashAlgorithmPolicy() = HashAlgorithmPolicy( + fun static2022SignatureHashAlgorithmPolicy() = + HashAlgorithmPolicy( HashAlgorithm.SHA512, listOf( - HashAlgorithm.SHA3_512, - HashAlgorithm.SHA3_256, - HashAlgorithm.SHA512, - HashAlgorithm.SHA384, - HashAlgorithm.SHA256, - HashAlgorithm.SHA224) - ) + HashAlgorithm.SHA3_512, + HashAlgorithm.SHA3_256, + HashAlgorithm.SHA512, + HashAlgorithm.SHA384, + HashAlgorithm.SHA256, + HashAlgorithm.SHA224)) /** - * Hash algorithm policy for revocation signatures, which accepts SHA1 and SHA2 algorithms, as well as RIPEMD160. + * Hash algorithm policy for revocation signatures, which accepts SHA1 and SHA2 + * algorithms, as well as RIPEMD160. * * @return static revocation signature hash algorithm policy */ @JvmStatic - fun static2022RevocationSignatureHashAlgorithmPolicy() = HashAlgorithmPolicy( + fun static2022RevocationSignatureHashAlgorithmPolicy() = + HashAlgorithmPolicy( HashAlgorithm.SHA512, listOf( - HashAlgorithm.SHA3_512, - HashAlgorithm.SHA3_256, - HashAlgorithm.SHA512, - HashAlgorithm.SHA384, - HashAlgorithm.SHA256, - HashAlgorithm.SHA224, - HashAlgorithm.SHA1, - HashAlgorithm.RIPEMD160 - ) - ) + HashAlgorithm.SHA3_512, + HashAlgorithm.SHA3_256, + HashAlgorithm.SHA512, + HashAlgorithm.SHA384, + HashAlgorithm.SHA256, + HashAlgorithm.SHA224, + HashAlgorithm.SHA1, + HashAlgorithm.RIPEMD160)) } } class SymmetricKeyAlgorithmPolicy( - val defaultSymmetricKeyAlgorithm: SymmetricKeyAlgorithm, - val acceptableSymmetricKeyAlgorithms: List) { + val defaultSymmetricKeyAlgorithm: SymmetricKeyAlgorithm, + val acceptableSymmetricKeyAlgorithms: List + ) { + + fun isAcceptable(algorithm: SymmetricKeyAlgorithm) = + acceptableSymmetricKeyAlgorithms.contains(algorithm) - fun isAcceptable(algorithm: SymmetricKeyAlgorithm) = acceptableSymmetricKeyAlgorithms.contains(algorithm) fun isAcceptable(algorithmId: Int): Boolean { val algorithm = SymmetricKeyAlgorithm.fromId(algorithmId) ?: return false return isAcceptable(algorithm) @@ -169,29 +181,29 @@ class Policy( */ @JvmStatic @Deprecated( - "Not expressive - will be removed in a future release", - ReplaceWith("symmetricKeyEncryptionPolicy2022")) + "Not expressive - will be removed in a future release", + ReplaceWith("symmetricKeyEncryptionPolicy2022")) fun defaultSymmetricKeyEncryptionAlgorithmPolicy() = symmetricKeyEncryptionPolicy2022() /** - * Policy for symmetric encryption algorithms in the context of message production (encryption). - * This suite contains algorithms that are deemed safe to use in 2022. + * Policy for symmetric encryption algorithms in the context of message production + * (encryption). This suite contains algorithms that are deemed safe to use in 2022. * * @return 2022 symmetric key encryption algorithm policy */ @JvmStatic - fun symmetricKeyEncryptionPolicy2022() = SymmetricKeyAlgorithmPolicy( + fun symmetricKeyEncryptionPolicy2022() = + SymmetricKeyAlgorithmPolicy( SymmetricKeyAlgorithm.AES_128, // Reject: Unencrypted, IDEA, TripleDES, CAST5, Blowfish listOf( - SymmetricKeyAlgorithm.AES_256, - SymmetricKeyAlgorithm.AES_192, - SymmetricKeyAlgorithm.AES_128, - SymmetricKeyAlgorithm.TWOFISH, - SymmetricKeyAlgorithm.CAMELLIA_256, - SymmetricKeyAlgorithm.CAMELLIA_192, - SymmetricKeyAlgorithm.CAMELLIA_128 - )) + SymmetricKeyAlgorithm.AES_256, + SymmetricKeyAlgorithm.AES_192, + SymmetricKeyAlgorithm.AES_128, + SymmetricKeyAlgorithm.TWOFISH, + SymmetricKeyAlgorithm.CAMELLIA_256, + SymmetricKeyAlgorithm.CAMELLIA_192, + SymmetricKeyAlgorithm.CAMELLIA_128)) /** * The default symmetric decryption algorithm policy of PGPainless. @@ -200,38 +212,42 @@ class Policy( * @deprecated not expressive - will be removed in a future update */ @JvmStatic - @Deprecated("not expressive - will be removed in a future update", - ReplaceWith("symmetricKeyDecryptionPolicy2022()")) + @Deprecated( + "not expressive - will be removed in a future update", + ReplaceWith("symmetricKeyDecryptionPolicy2022()")) fun defaultSymmetricKeyDecryptionAlgorithmPolicy() = symmetricKeyDecryptionPolicy2022() /** - * Policy for symmetric key encryption algorithms in the context of message consumption (decryption). - * This suite contains algorithms that are deemed safe to use in 2022. + * Policy for symmetric key encryption algorithms in the context of message consumption + * (decryption). This suite contains algorithms that are deemed safe to use in 2022. * * @return 2022 symmetric key decryption algorithm policy */ @JvmStatic - fun symmetricKeyDecryptionPolicy2022() = SymmetricKeyAlgorithmPolicy( + fun symmetricKeyDecryptionPolicy2022() = + SymmetricKeyAlgorithmPolicy( SymmetricKeyAlgorithm.AES_128, // Reject: Unencrypted, IDEA, TripleDES, Blowfish listOf( - SymmetricKeyAlgorithm.AES_256, - SymmetricKeyAlgorithm.AES_192, - SymmetricKeyAlgorithm.AES_128, - SymmetricKeyAlgorithm.TWOFISH, - SymmetricKeyAlgorithm.CAMELLIA_256, - SymmetricKeyAlgorithm.CAMELLIA_192, - SymmetricKeyAlgorithm.CAMELLIA_128, - SymmetricKeyAlgorithm.CAST5 - )) + SymmetricKeyAlgorithm.AES_256, + SymmetricKeyAlgorithm.AES_192, + SymmetricKeyAlgorithm.AES_128, + SymmetricKeyAlgorithm.TWOFISH, + SymmetricKeyAlgorithm.CAMELLIA_256, + SymmetricKeyAlgorithm.CAMELLIA_192, + SymmetricKeyAlgorithm.CAMELLIA_128, + SymmetricKeyAlgorithm.CAST5)) } } class CompressionAlgorithmPolicy( - val defaultCompressionAlgorithm: CompressionAlgorithm, - val acceptableCompressionAlgorithms: List) { + val defaultCompressionAlgorithm: CompressionAlgorithm, + val acceptableCompressionAlgorithms: List + ) { + + fun isAcceptable(algorithm: CompressionAlgorithm) = + acceptableCompressionAlgorithms.contains(algorithm) - fun isAcceptable(algorithm: CompressionAlgorithm) = acceptableCompressionAlgorithms.contains(algorithm) fun isAcceptable(algorithmId: Int): Boolean { val algorithm = CompressionAlgorithm.fromId(algorithmId) ?: return false return isAcceptable(algorithm) @@ -242,30 +258,33 @@ class Policy( companion object { /** - * Default {@link CompressionAlgorithmPolicy} of PGPainless. - * The default compression algorithm policy accepts any compression algorithm. + * Default {@link CompressionAlgorithmPolicy} of PGPainless. The default compression + * algorithm policy accepts any compression algorithm. * * @return default algorithm policy * @deprecated not expressive - might be removed in a future release */ @JvmStatic - @Deprecated("not expressive - might be removed in a future release", - ReplaceWith("anyCompressionAlgorithmPolicy()")) + @Deprecated( + "not expressive - might be removed in a future release", + ReplaceWith("anyCompressionAlgorithmPolicy()")) fun defaultCompressionAlgorithmPolicy() = anyCompressionAlgorithmPolicy() /** - * Policy that accepts any known compression algorithm and offers [CompressionAlgorithm.ZIP] as - * default algorithm. + * Policy that accepts any known compression algorithm and offers + * [CompressionAlgorithm.ZIP] as default algorithm. * * @return compression algorithm policy */ @JvmStatic - fun anyCompressionAlgorithmPolicy() = CompressionAlgorithmPolicy( + fun anyCompressionAlgorithmPolicy() = + CompressionAlgorithmPolicy( CompressionAlgorithm.ZIP, - listOf(CompressionAlgorithm.UNCOMPRESSED, - CompressionAlgorithm.ZIP, - CompressionAlgorithm.BZIP2, - CompressionAlgorithm.ZLIB)) + listOf( + CompressionAlgorithm.UNCOMPRESSED, + CompressionAlgorithm.ZIP, + CompressionAlgorithm.BZIP2, + CompressionAlgorithm.ZLIB)) } } @@ -283,77 +302,84 @@ class Policy( companion object { /** - * Return PGPainless' default public key algorithm policy. - * This policy is based upon recommendations made by the German Federal Office for Information Security (BSI). + * Return PGPainless' default public key algorithm policy. This policy is based upon + * recommendations made by the German Federal Office for Information Security (BSI). * * @return default algorithm policy * @deprecated not expressive - might be removed in a future release */ @JvmStatic - @Deprecated("not expressive - might be removed in a future release", - ReplaceWith("bsi2021PublicKeyAlgorithmPolicy()")) + @Deprecated( + "not expressive - might be removed in a future release", + ReplaceWith("bsi2021PublicKeyAlgorithmPolicy()")) fun defaultPublicKeyAlgorithmPolicy() = bsi2021PublicKeyAlgorithmPolicy() /** - * This policy is based upon recommendations made by the German Federal Office for Information Security (BSI). + * This policy is based upon recommendations made by the German Federal Office for + * Information Security (BSI). * - * Basically this policy requires keys based on elliptic curves to have a bit strength of at least 250, - * and keys based on prime number factorization / discrete logarithm problems to have a strength of at least 2000 bits. - * - * @see BSI - Technical Guideline - Cryptographic Mechanisms: Recommendations and Key Lengths (2021-01) - * @see BlueKrypt | Cryptographic Key Length Recommendation + * Basically this policy requires keys based on elliptic curves to have a bit strength + * of at least 250, and keys based on prime number factorization / discrete logarithm + * problems to have a strength of at least 2000 bits. * * @return default algorithm policy + * @see BSI - + * Technical Guideline - Cryptographic Mechanisms: Recommendations and Key Lengths + * (2021-01) + * @see BlueKrypt | Cryptographic Key Length + * Recommendation */ @JvmStatic - fun bsi2021PublicKeyAlgorithmPolicy() = PublicKeyAlgorithmPolicy(buildMap { - // §5.4.1 - put(PublicKeyAlgorithm.RSA_GENERAL, 2000) - put(PublicKeyAlgorithm.RSA_SIGN, 2000) - put(PublicKeyAlgorithm.RSA_ENCRYPT, 2000) - // Note: ElGamal is not mentioned in the BSI document. - // We assume that the requirements are similar to other DH algorithms - put(PublicKeyAlgorithm.ELGAMAL_ENCRYPT, 2000) - put(PublicKeyAlgorithm.ELGAMAL_GENERAL, 2000) - // §5.4.2 - put(PublicKeyAlgorithm.DSA, 2000) - // §5.4.3 - put(PublicKeyAlgorithm.ECDSA, 250) - // Note: EdDSA is not mentioned in the BSI document. - // We assume that the requirements are similar to other EC algorithms. - put(PublicKeyAlgorithm.EDDSA, 250) - // §7.2.1 - put(PublicKeyAlgorithm.DIFFIE_HELLMAN, 2000) - // §7.2.2 - put(PublicKeyAlgorithm.ECDH, 250) - }) + fun bsi2021PublicKeyAlgorithmPolicy() = + PublicKeyAlgorithmPolicy( + buildMap { + // §5.4.1 + put(PublicKeyAlgorithm.RSA_GENERAL, 2000) + put(PublicKeyAlgorithm.RSA_SIGN, 2000) + put(PublicKeyAlgorithm.RSA_ENCRYPT, 2000) + // Note: ElGamal is not mentioned in the BSI document. + // We assume that the requirements are similar to other DH algorithms + put(PublicKeyAlgorithm.ELGAMAL_ENCRYPT, 2000) + put(PublicKeyAlgorithm.ELGAMAL_GENERAL, 2000) + // §5.4.2 + put(PublicKeyAlgorithm.DSA, 2000) + // §5.4.3 + put(PublicKeyAlgorithm.ECDSA, 250) + // Note: EdDSA is not mentioned in the BSI document. + // We assume that the requirements are similar to other EC algorithms. + put(PublicKeyAlgorithm.EDDSA, 250) + // §7.2.1 + put(PublicKeyAlgorithm.DIFFIE_HELLMAN, 2000) + // §7.2.2 + put(PublicKeyAlgorithm.ECDH, 250) + }) } } enum class SignerUserIdValidationLevel { /** - * PGPainless will verify {@link org.bouncycastle.bcpg.sig.SignerUserID} subpackets in signatures strictly. - * This means, that signatures with Signer's User-ID subpackets containing a value that does not match the signer key's - * user-id exactly, will be rejected. - * E.g. Signer's user-id "alice@pgpainless.org", User-ID: "Alice <alice@pgpainless.org>" does not - * match exactly and is therefore rejected. + * PGPainless will verify {@link org.bouncycastle.bcpg.sig.SignerUserID} subpackets in + * signatures strictly. This means, that signatures with Signer's User-ID subpackets + * containing a value that does not match the signer key's user-id exactly, will be + * rejected. E.g. Signer's user-id "alice@pgpainless.org", User-ID: "Alice + * <alice@pgpainless.org>" does not match exactly and is therefore rejected. */ STRICT, /** - * PGPainless will ignore {@link org.bouncycastle.bcpg.sig.SignerUserID} subpackets on signature. + * PGPainless will ignore {@link org.bouncycastle.bcpg.sig.SignerUserID} subpackets on + * signature. */ DISABLED } companion object { - @Volatile - private var INSTANCE: Policy? = null + @Volatile private var INSTANCE: Policy? = null @JvmStatic - fun getInstance() = INSTANCE ?: synchronized(this) { - INSTANCE ?: Policy().also { INSTANCE = it } - } + fun getInstance() = + INSTANCE ?: synchronized(this) { INSTANCE ?: Policy().also { INSTANCE = it } } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/provider/BouncyCastleProviderFactory.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/provider/BouncyCastleProviderFactory.kt index 4654f529..27192953 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/provider/BouncyCastleProviderFactory.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/provider/BouncyCastleProviderFactory.kt @@ -4,9 +4,9 @@ package org.pgpainless.provider -import org.bouncycastle.jce.provider.BouncyCastleProvider import java.security.Provider +import org.bouncycastle.jce.provider.BouncyCastleProvider class BouncyCastleProviderFactory : ProviderFactory() { override val securityProvider: Provider = BouncyCastleProvider() -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/provider/ProviderFactory.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/provider/ProviderFactory.kt index 3fe127f2..531ae54b 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/provider/ProviderFactory.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/provider/ProviderFactory.kt @@ -7,11 +7,10 @@ package org.pgpainless.provider import java.security.Provider /** - * Allow the use of different [Provider] implementations to provide cryptographic primitives by setting - * a [ProviderFactory] singleton. - * By default, the class is initialized with a [BouncyCastleProviderFactory]. - * To make use of your own custom [Provider], call [setFactory], passing your - * own custom [ProviderFactory] instance. + * Allow the use of different [Provider] implementations to provide cryptographic primitives by + * setting a [ProviderFactory] singleton. By default, the class is initialized with a + * [BouncyCastleProviderFactory]. To make use of your own custom [Provider], call [setFactory], + * passing your own custom [ProviderFactory] instance. */ abstract class ProviderFactory { @@ -21,16 +20,14 @@ abstract class ProviderFactory { companion object { // singleton instance - @JvmStatic - var factory: ProviderFactory = BouncyCastleProviderFactory() + @JvmStatic var factory: ProviderFactory = BouncyCastleProviderFactory() @JvmStatic val provider: Provider - @JvmName("getProvider") - get() = factory.securityProvider + @JvmName("getProvider") get() = factory.securityProvider @JvmStatic val providerName: String get() = factory.securityProviderName } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/SignatureUtils.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/SignatureUtils.kt index 492c4fe4..e84ed0d3 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/SignatureUtils.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/SignatureUtils.kt @@ -4,6 +4,9 @@ package org.pgpainless.signature +import java.io.IOException +import java.io.InputStream +import java.util.* import openpgp.plusSeconds import org.bouncycastle.bcpg.sig.KeyExpirationTime import org.bouncycastle.extensions.* @@ -14,9 +17,6 @@ import org.pgpainless.implementation.ImplementationFactory import org.pgpainless.key.OpenPgpFingerprint import org.pgpainless.key.util.RevocationAttributes.Reason import org.pgpainless.util.ArmorUtils -import java.io.IOException -import java.io.InputStream -import java.util.* const val MAX_ITERATIONS = 10000 @@ -24,89 +24,103 @@ class SignatureUtils { companion object { /** - * Extract and return the key expiration date value from the given signature. - * If the signature does not carry a [KeyExpirationTime] subpacket, return null. + * Extract and return the key expiration date value from the given signature. If the + * signature does not carry a [KeyExpirationTime] subpacket, return null. * * @param keyCreationDate creation date of the key * @param signature signature * @return key expiration date as given by the signature */ @JvmStatic - @Deprecated("Deprecated in favor of PGPSignature extension method.", - ReplaceWith("signature.getKeyExpirationDate(keyCreationDate)", "org.bouncycastle.extensions.getKeyExpirationDate")) + @Deprecated( + "Deprecated in favor of PGPSignature extension method.", + ReplaceWith( + "signature.getKeyExpirationDate(keyCreationDate)", + "org.bouncycastle.extensions.getKeyExpirationDate")) fun getKeyExpirationDate(keyCreationDate: Date, signature: PGPSignature): Date? { return signature.getKeyExpirationDate(keyCreationDate) } /** - * Return the expiration date of the signature. - * If the signature has no expiration date, this will return null. + * Return the expiration date of the signature. If the signature has no expiration date, + * this will return null. * * @param signature signature * @return expiration date of the signature, or null if it does not expire. */ @JvmStatic - @Deprecated("Deprecated in favor of PGPSignature extension method.", - ReplaceWith("signature.signatureExpirationDate", "org.bouncycastle.extensions.signatureExpirationDate")) - fun getSignatureExpirationDate(signature: PGPSignature): Date? = signature.signatureExpirationDate + @Deprecated( + "Deprecated in favor of PGPSignature extension method.", + ReplaceWith( + "signature.signatureExpirationDate", + "org.bouncycastle.extensions.signatureExpirationDate")) + fun getSignatureExpirationDate(signature: PGPSignature): Date? = + signature.signatureExpirationDate /** * Return a new date which represents the given date plus the given amount of seconds added. * - * Since '0' is a special date value in the OpenPGP specification - * (e.g. '0' means no expiration for expiration dates), this method will return 'null' if seconds is 0. + * Since '0' is a special date value in the OpenPGP specification (e.g. '0' means no + * expiration for expiration dates), this method will return 'null' if seconds is 0. * * @param date date * @param seconds number of seconds to be added * @return date plus seconds or null if seconds is '0' */ @JvmStatic - @Deprecated("Deprecated in favor of Date extension method.", - ReplaceWith("date.plusSeconds(seconds)", "openpgp.plusSeconds")) + @Deprecated( + "Deprecated in favor of Date extension method.", + ReplaceWith("date.plusSeconds(seconds)", "openpgp.plusSeconds")) fun datePlusSeconds(date: Date, seconds: Long): Date? { return date.plusSeconds(seconds) } /** - * Return true, if the expiration date of the [PGPSignature] lays in the past. - * If no expiration date is present in the signature, it is considered non-expired. + * Return true, if the expiration date of the [PGPSignature] lays in the past. If no + * expiration date is present in the signature, it is considered non-expired. * * @param signature signature * @return true if expired, false otherwise */ @JvmStatic - @Deprecated("Deprecated in favor of PGPSignature extension method.", - ReplaceWith("signature.isExpired()", "org.bouncycastle.extensions.isExpired")) + @Deprecated( + "Deprecated in favor of PGPSignature extension method.", + ReplaceWith("signature.isExpired()", "org.bouncycastle.extensions.isExpired")) fun isSignatureExpired(signature: PGPSignature): Boolean { return signature.isExpired() } /** - * Return true, if the expiration date of the given [PGPSignature] is past the given comparison [Date]. - * If no expiration date is present in the signature, it is considered non-expiring. + * Return true, if the expiration date of the given [PGPSignature] is past the given + * comparison [Date]. If no expiration date is present in the signature, it is considered + * non-expiring. * * @param signature signature * @param referenceTime reference date * @return true if sig is expired at reference date, false otherwise */ @JvmStatic - @Deprecated("Deprecated in favor of PGPSignature extension method.", - ReplaceWith("signature.isExpired(referenceTime)", "org.bouncycastle.extensions.isExpired")) + @Deprecated( + "Deprecated in favor of PGPSignature extension method.", + ReplaceWith( + "signature.isExpired(referenceTime)", "org.bouncycastle.extensions.isExpired")) fun isSignatureExpired(signature: PGPSignature, referenceTime: Date): Boolean { return signature.isExpired(referenceTime) } /** - * Return true if the provided signature is a hard revocation. - * Hard revocations are revocation signatures which either carry a revocation reason of - * [Reason.KEY_COMPROMISED] or [Reason.NO_REASON], or no reason at all. + * Return true if the provided signature is a hard revocation. Hard revocations are + * revocation signatures which either carry a revocation reason of [Reason.KEY_COMPROMISED] + * or [Reason.NO_REASON], or no reason at all. * * @param signature signature * @return true if signature is a hard revocation */ @JvmStatic - @Deprecated("Deprecated in favor of PGPSignature extension function.", - ReplaceWith("signature.isHardRevocation", "org.bouncycastle.extensions.isHardRevocation")) + @Deprecated( + "Deprecated in favor of PGPSignature extension function.", + ReplaceWith( + "signature.isHardRevocation", "org.bouncycastle.extensions.isHardRevocation")) fun isHardRevocation(signature: PGPSignature): Boolean { return signature.isHardRevocation } @@ -128,8 +142,8 @@ class SignatureUtils { } /** - * Read and return [PGPSignatures][PGPSignature]. - * This method can deal with signatures that may be binary, armored and may contain marker packets. + * Read and return [PGPSignatures][PGPSignature]. This method can deal with signatures that + * may be binary, armored and may contain marker packets. * * @param inputStream input stream * @param maxIterations number of loop iterations until reading is aborted @@ -143,13 +157,18 @@ class SignatureUtils { var i = 0 var nextObject: Any? = null - while (i++ < maxIterations && objectFactory.nextObject().also { nextObject = it } != null) { - // Since signatures are indistinguishable from randomness, there is no point in having them compressed, - // except for an attacker who is trying to exploit flaws in the decompression algorithm. + while (i++ < maxIterations && + objectFactory.nextObject().also { nextObject = it } != null) { + // Since signatures are indistinguishable from randomness, there is no point in + // having them compressed, + // except for an attacker who is trying to exploit flaws in the decompression + // algorithm. // Therefore, we ignore compressed data packets without attempting decompression. if (nextObject is PGPCompressedData) { // getInputStream() does not do decompression, contrary to getDataStream(). - Streams.drain((nextObject as PGPCompressedData).inputStream) // Skip packet without decompressing + Streams.drain( + (nextObject as PGPCompressedData) + .inputStream) // Skip packet without decompressing } if (nextObject is PGPSignatureList) { @@ -166,17 +185,20 @@ class SignatureUtils { } /** - * Determine the issuer key-id of a [PGPSignature]. - * This method first inspects the [org.bouncycastle.bcpg.sig.IssuerKeyID] subpacket of the signature and returns the key-id if present. - * If not, it inspects the [org.bouncycastle.bcpg.sig.IssuerFingerprint] packet and retrieves the key-id from the fingerprint. + * Determine the issuer key-id of a [PGPSignature]. This method first inspects the + * [org.bouncycastle.bcpg.sig.IssuerKeyID] subpacket of the signature and returns the key-id + * if present. If not, it inspects the [org.bouncycastle.bcpg.sig.IssuerFingerprint] packet + * and retrieves the key-id from the fingerprint. * * Otherwise, it returns 0. + * * @param signature signature * @return signatures issuing key id */ @JvmStatic - @Deprecated("Deprecated in favor of PGPSignature extension method.", - ReplaceWith("signature.issuerKeyId", "org.bouncycastle.extensions.issuerKeyId")) + @Deprecated( + "Deprecated in favor of PGPSignature extension method.", + ReplaceWith("signature.issuerKeyId", "org.bouncycastle.extensions.issuerKeyId")) fun determineIssuerKeyId(signature: PGPSignature): Long { return signature.issuerKeyId } @@ -193,22 +215,26 @@ class SignatureUtils { } @JvmStatic - @Deprecated("Deprecated in favor of PGPSignature extension method", - ReplaceWith("signature.wasIssuedBy(fingerprint)", "org.bouncycastle.extensions.wasIssuedBy")) + @Deprecated( + "Deprecated in favor of PGPSignature extension method", + ReplaceWith( + "signature.wasIssuedBy(fingerprint)", "org.bouncycastle.extensions.wasIssuedBy")) fun wasIssuedBy(fingerprint: ByteArray, signature: PGPSignature): Boolean { return signature.wasIssuedBy(fingerprint) } @JvmStatic - @Deprecated("Deprecated in favor of PGPSignature extension method", - ReplaceWith("signature.wasIssuedBy(fingerprint)", "org.bouncycastle.extensions.wasIssuedBy")) + @Deprecated( + "Deprecated in favor of PGPSignature extension method", + ReplaceWith( + "signature.wasIssuedBy(fingerprint)", "org.bouncycastle.extensions.wasIssuedBy")) fun wasIssuedBy(fingerprint: OpenPgpFingerprint, signature: PGPSignature): Boolean { return signature.wasIssuedBy(fingerprint) } /** - * Extract all signatures from the given
key
which were issued by
issuerKeyId
- * over
userId
. + * Extract all signatures from the given
key
which were issued by + *
issuerKeyId
over
userId
. * * @param key public key * @param userId user-id @@ -216,28 +242,33 @@ class SignatureUtils { * @return (potentially empty) list of signatures */ @JvmStatic - fun getSignaturesOverUserIdBy(key: PGPPublicKey, userId: String, issuer: Long): List { + fun getSignaturesOverUserIdBy( + key: PGPPublicKey, + userId: String, + issuer: Long + ): List { val signatures = key.getSignaturesForID(userId) ?: return listOf() - return signatures - .asSequence() - .filter { it.keyID == issuer } - .toList() + return signatures.asSequence().filter { it.keyID == issuer }.toList() } @JvmStatic fun getDelegations(key: PGPPublicKeyRing): List { return key.publicKey.keySignatures - .asSequence() - .filter { key.getPublicKey(it.keyID) == null } // Filter out back-sigs from subkeys - .toList() + .asSequence() + .filter { key.getPublicKey(it.keyID) == null } // Filter out back-sigs from subkeys + .toList() } @JvmStatic - fun get3rdPartyCertificationsFor(key: PGPPublicKeyRing, userId: String): List { - return key.publicKey.getSignaturesForID(userId) - .asSequence() - .filter { it.keyID != key.publicKey.keyID } // Filter out self-sigs - .toList() + fun get3rdPartyCertificationsFor( + key: PGPPublicKeyRing, + userId: String + ): List { + return key.publicKey + .getSignaturesForID(userId) + .asSequence() + .filter { it.keyID != key.publicKey.keyID } // Filter out self-sigs + .toList() } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/consumer/CertificateValidator.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/consumer/CertificateValidator.kt index 126803d4..43c92959 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/consumer/CertificateValidator.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/consumer/CertificateValidator.kt @@ -4,6 +4,8 @@ package org.pgpainless.signature.consumer +import java.io.InputStream +import java.util.* import openpgp.openPgpKeyId import org.bouncycastle.extensions.issuerKeyId import org.bouncycastle.openpgp.PGPPublicKey @@ -17,18 +19,16 @@ import org.pgpainless.key.util.KeyRingUtils import org.pgpainless.policy.Policy import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil import org.slf4j.LoggerFactory -import java.io.InputStream -import java.util.* /** - * A collection of static methods that validate signing certificates (public keys) and verify signature correctness. + * A collection of static methods that validate signing certificates (public keys) and verify + * signature correctness. */ class CertificateValidator { companion object { - @JvmStatic - private val LOGGER = LoggerFactory.getLogger(CertificateValidator::class.java) + @JvmStatic private val LOGGER = LoggerFactory.getLogger(CertificateValidator::class.java) /** * Check if the signing key was eligible to create the provided signature. @@ -47,43 +47,56 @@ class CertificateValidator { */ @JvmStatic @Throws(SignatureValidationException::class) - fun validateCertificate(signature: PGPSignature, - signingKeyRing: PGPPublicKeyRing, - policy: Policy = PGPainless.getPolicy()): Boolean { - val signingSubkey: PGPPublicKey = signingKeyRing.getPublicKey(signature.issuerKeyId) - ?: throw SignatureValidationException("Provided key ring does not contain a subkey with id ${signature.issuerKeyId.openPgpKeyId()}.") + fun validateCertificate( + signature: PGPSignature, + signingKeyRing: PGPPublicKeyRing, + policy: Policy = PGPainless.getPolicy() + ): Boolean { + val signingSubkey: PGPPublicKey = + signingKeyRing.getPublicKey(signature.issuerKeyId) + ?: throw SignatureValidationException( + "Provided key ring does not contain a subkey with id ${signature.issuerKeyId.openPgpKeyId()}.") val primaryKey = signingKeyRing.publicKey!! val directKeyAndRevSigs = mutableListOf() val rejections = mutableMapOf() // revocations - primaryKey.getSignaturesOfType(SignatureType.KEY_REVOCATION.code).asSequence() - .filter { it.issuerKeyId == primaryKey.keyID } // We do not support external rev keys - .forEach { - try { - if (SignatureVerifier.verifyKeyRevocationSignature(it, primaryKey, policy, signature.creationTime)) { - directKeyAndRevSigs.add(it) - } - } catch (e: SignatureValidationException) { - rejections[it] = e - LOGGER.debug("Rejecting key revocation signature: ${e.message}", e) + primaryKey + .getSignaturesOfType(SignatureType.KEY_REVOCATION.code) + .asSequence() + .filter { + it.issuerKeyId == primaryKey.keyID + } // We do not support external rev keys + .forEach { + try { + if (SignatureVerifier.verifyKeyRevocationSignature( + it, primaryKey, policy, signature.creationTime)) { + directKeyAndRevSigs.add(it) } + } catch (e: SignatureValidationException) { + rejections[it] = e + LOGGER.debug("Rejecting key revocation signature: ${e.message}", e) } + } // direct-key sigs - primaryKey.getSignaturesOfType(SignatureType.DIRECT_KEY.code).asSequence() - .filter { it.issuerKeyId == primaryKey.keyID } - .forEach { - try { - if (SignatureVerifier.verifyDirectKeySignature(it, primaryKey, policy, signature.creationTime)) { - directKeyAndRevSigs.add(it) - } - } catch (e: SignatureValidationException) { - rejections[it] = e - LOGGER.debug("Rejecting key signature: ${e.message}, e") + primaryKey + .getSignaturesOfType(SignatureType.DIRECT_KEY.code) + .asSequence() + .filter { it.issuerKeyId == primaryKey.keyID } + .forEach { + try { + if (SignatureVerifier.verifyDirectKeySignature( + it, primaryKey, policy, signature.creationTime)) { + directKeyAndRevSigs.add(it) } + } catch (e: SignatureValidationException) { + rejections[it] = e + LOGGER.debug("Rejecting key signature: ${e.message}, e") } + } - directKeyAndRevSigs.sortWith(SignatureValidityComparator(SignatureCreationDateComparator.Order.NEW_TO_OLD)) + directKeyAndRevSigs.sortWith( + SignatureValidityComparator(SignatureCreationDateComparator.Order.NEW_TO_OLD)) if (directKeyAndRevSigs.isNotEmpty()) { if (directKeyAndRevSigs[0].signatureType == SignatureType.KEY_REVOCATION.code) { throw SignatureValidationException("Primary key has been revoked.") @@ -94,12 +107,18 @@ class CertificateValidator { val userIdSignatures = mutableMapOf>() KeyRingUtils.getUserIdsIgnoringInvalidUTF8(primaryKey).forEach { userId -> buildList { - primaryKey.getSignaturesForID(userId) + primaryKey + .getSignaturesForID(userId) .asSequence() .filter { it.issuerKeyId == primaryKey.keyID } .forEach { uidSig -> try { - if (SignatureVerifier.verifySignatureOverUserId(userId, uidSig, primaryKey, policy, signature.creationTime)) { + if (SignatureVerifier.verifySignatureOverUserId( + userId, + uidSig, + primaryKey, + policy, + signature.creationTime)) { add(uidSig) } } catch (e: SignatureValidationException) { @@ -107,14 +126,19 @@ class CertificateValidator { LOGGER.debug("Rejecting user-id signature: ${e.message}", e) } } - }.sortedWith(SignatureValidityComparator(SignatureCreationDateComparator.Order.NEW_TO_OLD)) - .let { userIdSignatures[userId] = it } + } + .sortedWith( + SignatureValidityComparator( + SignatureCreationDateComparator.Order.NEW_TO_OLD)) + .let { userIdSignatures[userId] = it } } val hasAnyUserIds = userIdSignatures.isNotEmpty() - val isAnyUserIdValid = userIdSignatures.any { entry -> - entry.value.isNotEmpty() && entry.value[0].signatureType != SignatureType.CERTIFICATION_REVOCATION.code - } + val isAnyUserIdValid = + userIdSignatures.any { entry -> + entry.value.isNotEmpty() && + entry.value[0].signatureType != SignatureType.CERTIFICATION_REVOCATION.code + } if (hasAnyUserIds && !isAnyUserIdValid) { throw SignatureValidationException("No valid user-id found.", rejections) @@ -124,12 +148,15 @@ class CertificateValidator { if (policy.signerUserIdValidationLevel == Policy.SignerUserIdValidationLevel.STRICT) { SignatureSubpacketsUtil.getSignerUserID(signature)?.let { if (userIdSignatures[it.id] == null || userIdSignatures[it.id]!!.isEmpty()) { - throw SignatureValidationException("Signature was allegedly made by user-id '${it.id}'," + + throw SignatureValidationException( + "Signature was allegedly made by user-id '${it.id}'," + " but we have no valid signatures for that on the certificate.") } - if (userIdSignatures[it.id]!![0].signatureType == SignatureType.CERTIFICATION_REVOCATION.code) { - throw SignatureValidationException("Signature was made with user-id '${it.id}' which is revoked.") + if (userIdSignatures[it.id]!![0].signatureType == + SignatureType.CERTIFICATION_REVOCATION.code) { + throw SignatureValidationException( + "Signature was made with user-id '${it.id}' which is revoked.") } } } @@ -144,32 +171,39 @@ class CertificateValidator { } } else { // signing key is subkey val subkeySigs = mutableListOf() - signingSubkey.getSignaturesOfType(SignatureType.SUBKEY_REVOCATION.code).asSequence() - .filter { it.issuerKeyId == primaryKey.keyID } - .forEach { - try { - if (SignatureVerifier.verifySubkeyBindingRevocation(it, primaryKey, signingSubkey, policy, signature.creationTime)) { - subkeySigs.add(it) - } - } catch (e : SignatureValidationException) { - rejections[it] = e - LOGGER.debug("REjecting subkey revocation signature: ${e.message}", e) + signingSubkey + .getSignaturesOfType(SignatureType.SUBKEY_REVOCATION.code) + .asSequence() + .filter { it.issuerKeyId == primaryKey.keyID } + .forEach { + try { + if (SignatureVerifier.verifySubkeyBindingRevocation( + it, primaryKey, signingSubkey, policy, signature.creationTime)) { + subkeySigs.add(it) } + } catch (e: SignatureValidationException) { + rejections[it] = e + LOGGER.debug("REjecting subkey revocation signature: ${e.message}", e) } + } - signingSubkey.getSignaturesOfType(SignatureType.SUBKEY_BINDING.code).asSequence() - .forEach { - try { - if (SignatureVerifier.verifySubkeyBindingSignature(it, primaryKey, signingSubkey, policy, signature.creationTime)) { - subkeySigs.add(it) - } - } catch (e : SignatureValidationException) { - rejections[it] = e - LOGGER.debug("Rejecting subkey binding signature: ${e.message}", e) + signingSubkey + .getSignaturesOfType(SignatureType.SUBKEY_BINDING.code) + .asSequence() + .forEach { + try { + if (SignatureVerifier.verifySubkeyBindingSignature( + it, primaryKey, signingSubkey, policy, signature.creationTime)) { + subkeySigs.add(it) } + } catch (e: SignatureValidationException) { + rejections[it] = e + LOGGER.debug("Rejecting subkey binding signature: ${e.message}", e) } + } - subkeySigs.sortWith(SignatureValidityComparator(SignatureCreationDateComparator.Order.NEW_TO_OLD)) + subkeySigs.sortWith( + SignatureValidityComparator(SignatureCreationDateComparator.Order.NEW_TO_OLD)) if (subkeySigs.isEmpty()) { throw SignatureValidationException("Subkey is not bound.", rejections) } @@ -180,15 +214,17 @@ class CertificateValidator { val keyFlags = SignatureSubpacketsUtil.getKeyFlags(subkeySigs[0]) if (keyFlags == null || !KeyFlag.hasKeyFlag(keyFlags.flags, KeyFlag.SIGN_DATA)) { - throw SignatureValidationException("Signature was made by key which is not capable of signing (no keyflag).") + throw SignatureValidationException( + "Signature was made by key which is not capable of signing (no keyflag).") } } return true } /** - * Validate the given signing key and then verify the given signature while parsing out the signed data. - * Uninitialized means that no signed data has been read and the hash generators state has not yet been updated. + * Validate the given signing key and then verify the given signature while parsing out the + * signed data. Uninitialized means that no signed data has been read and the hash + * generators state has not yet been updated. * * @param signature uninitialized signature * @param signedData input stream containing signed data @@ -200,18 +236,25 @@ class CertificateValidator { */ @JvmStatic @Throws(SignatureValidationException::class) - fun validateCertificateAndVerifyUninitializedSignature(signature: PGPSignature, - signedData: InputStream, - signingKeyRing: PGPPublicKeyRing, - policy: Policy, - referenceTime: Date = signature.creationTime): Boolean { - return validateCertificate(signature, signingKeyRing, policy) - && SignatureVerifier.verifyUninitializedSignature(signature, signedData, signingKeyRing.getPublicKey(signature.issuerKeyId)!!, policy, referenceTime) + fun validateCertificateAndVerifyUninitializedSignature( + signature: PGPSignature, + signedData: InputStream, + signingKeyRing: PGPPublicKeyRing, + policy: Policy, + referenceTime: Date = signature.creationTime + ): Boolean { + return validateCertificate(signature, signingKeyRing, policy) && + SignatureVerifier.verifyUninitializedSignature( + signature, + signedData, + signingKeyRing.getPublicKey(signature.issuerKeyId)!!, + policy, + referenceTime) } /** - * Validate the signing key and the given initialized signature. - * Initialized means that the signatures hash generator has already been updated by reading the signed data completely. + * Validate the signing key and the given initialized signature. Initialized means that the + * signatures hash generator has already been updated by reading the signed data completely. * * @param signature initialized signature * @param verificationKeys key ring containing the verification key @@ -221,11 +264,17 @@ class CertificateValidator { */ @JvmStatic @Throws(SignatureValidationException::class) - fun validateCertificateAndVerifyInitializedSignature(signature: PGPSignature, - verificationKeys: PGPPublicKeyRing, - policy: Policy): Boolean { + fun validateCertificateAndVerifyInitializedSignature( + signature: PGPSignature, + verificationKeys: PGPPublicKeyRing, + policy: Policy + ): Boolean { return validateCertificate(signature, verificationKeys, policy) && - SignatureVerifier.verifyInitializedSignature(signature, verificationKeys.getPublicKey(signature.issuerKeyId), policy, signature.creationTime) + SignatureVerifier.verifyInitializedSignature( + signature, + verificationKeys.getPublicKey(signature.issuerKeyId), + policy, + signature.creationTime) } /** @@ -238,12 +287,18 @@ class CertificateValidator { */ @JvmStatic @Throws(SignatureValidationException::class) - fun validateCertificateAndVerifyOnePassSignature(onePassSignature: OnePassSignatureCheck, - policy: Policy): Boolean { - return validateCertificate(onePassSignature.signature!!, onePassSignature.verificationKeys, policy) && - SignatureVerifier.verifyOnePassSignature(onePassSignature.signature!!, - onePassSignature.verificationKeys.getPublicKey(onePassSignature.signature!!.issuerKeyId), - onePassSignature, policy) + fun validateCertificateAndVerifyOnePassSignature( + onePassSignature: OnePassSignatureCheck, + policy: Policy + ): Boolean { + return validateCertificate( + onePassSignature.signature!!, onePassSignature.verificationKeys, policy) && + SignatureVerifier.verifyOnePassSignature( + onePassSignature.signature!!, + onePassSignature.verificationKeys.getPublicKey( + onePassSignature.signature!!.issuerKeyId), + onePassSignature, + policy) } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/consumer/OnePassSignatureCheck.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/consumer/OnePassSignatureCheck.kt index a315f577..4a89e0b2 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/consumer/OnePassSignatureCheck.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/consumer/OnePassSignatureCheck.kt @@ -10,18 +10,19 @@ import org.bouncycastle.openpgp.PGPSignature import org.pgpainless.key.SubkeyIdentifier /** - * Tuple-class that bundles together a [PGPOnePassSignature] object, a [PGPPublicKeyRing] - * destined to verify the signature, the [PGPSignature] itself and a record of whether the signature - * was verified. + * Tuple-class that bundles together a [PGPOnePassSignature] object, a [PGPPublicKeyRing] destined + * to verify the signature, the [PGPSignature] itself and a record of whether the signature was + * verified. * * @param onePassSignature the one-pass-signature packet * @param verificationKeys certificate containing the signing subkey * @param signature the signature packet */ data class OnePassSignatureCheck( - val onePassSignature: PGPOnePassSignature, - val verificationKeys: PGPPublicKeyRing, - var signature: PGPSignature? = null) { + val onePassSignature: PGPOnePassSignature, + val verificationKeys: PGPPublicKeyRing, + var signature: PGPSignature? = null +) { /** * Return an identifier for the signing key. @@ -30,4 +31,4 @@ data class OnePassSignatureCheck( */ val signingKey: SubkeyIdentifier get() = SubkeyIdentifier(verificationKeys, onePassSignature.keyID) -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/consumer/SignatureCheck.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/consumer/SignatureCheck.kt index 48a3aa96..15564773 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/consumer/SignatureCheck.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/consumer/SignatureCheck.kt @@ -9,15 +9,17 @@ import org.bouncycastle.openpgp.PGPSignature import org.pgpainless.key.SubkeyIdentifier /** - * Tuple-class which bundles together a signature, the signing key that created the signature, - * an identifier of the signing key and a record of whether the signature was verified. + * Tuple-class which bundles together a signature, the signing key that created the signature, an + * identifier of the signing key and a record of whether the signature was verified. * * @param signature OpenPGP signature - * @param signingKeyIdentifier identifier pointing to the exact signing key which was used to create the signature - * @param signingKeyRing certificate or key ring that contains the signing key that created the signature + * @param signingKeyIdentifier identifier pointing to the exact signing key which was used to create + * the signature + * @param signingKeyRing certificate or key ring that contains the signing key that created the + * signature */ data class SignatureCheck( - val signature: PGPSignature, - val signingKeyRing: PGPKeyRing, - val signingKeyIdentifier: SubkeyIdentifier) { -} \ No newline at end of file + val signature: PGPSignature, + val signingKeyRing: PGPKeyRing, + val signingKeyIdentifier: SubkeyIdentifier +) {} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/consumer/SignatureCreationDateComparator.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/consumer/SignatureCreationDateComparator.kt index 75cd274c..a913bf32 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/consumer/SignatureCreationDateComparator.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/consumer/SignatureCreationDateComparator.kt @@ -8,27 +8,23 @@ import org.bouncycastle.openpgp.PGPSignature /** * Create a new comparator which sorts signatures according to the passed ordering. + * * @param order ordering */ -class SignatureCreationDateComparator( - private val order: Order = Order.OLD_TO_NEW -) : Comparator { +class SignatureCreationDateComparator(private val order: Order = Order.OLD_TO_NEW) : + Comparator { enum class Order { - /** - * Oldest signatures first. - */ + /** Oldest signatures first. */ OLD_TO_NEW, - /** - * Newest signatures first. - */ + /** Newest signatures first. */ NEW_TO_OLD } override fun compare(one: PGPSignature, two: PGPSignature): Int { - return when(order) { + return when (order) { Order.OLD_TO_NEW -> one.creationTime.compareTo(two.creationTime) Order.NEW_TO_OLD -> two.creationTime.compareTo(one.creationTime) } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/consumer/SignaturePicker.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/consumer/SignaturePicker.kt index 9c5b9a8d..56db7ee0 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/consumer/SignaturePicker.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/consumer/SignaturePicker.kt @@ -4,8 +4,8 @@ package org.pgpainless.signature.consumer +import java.util.Date import org.bouncycastle.extensions.getPublicKeyFor -import org.bouncycastle.extensions.hasPublicKey import org.bouncycastle.extensions.isExpired import org.bouncycastle.extensions.wasIssuedBy import org.bouncycastle.openpgp.PGPKeyRing @@ -14,30 +14,24 @@ import org.bouncycastle.openpgp.PGPSignature import org.pgpainless.algorithm.SignatureType import org.pgpainless.exception.SignatureValidationException import org.pgpainless.policy.Policy -import java.util.Date -import kotlin.math.sign /** * Pick signatures from keys. * * The format of a V4 OpenPGP key is: * - * Primary-Key - * [Revocation Self Signature] - * [Direct Key Signature...] - * User ID [Signature ...] - * [User ID [Signature ...] ...] - * [User Attribute [Signature ...] ...] - * [[Subkey [Binding-Signature-Revocation] Primary-Key-Binding-Signature] ...] + * Primary-Key [Revocation Self Signature] [Direct Key Signature...] User ID [Signature ...] [User + * ID [Signature ...] ...] [User Attribute [Signature ...] ...] [[Subkey + * [Binding-Signature-Revocation] Primary-Key-Binding-Signature] ...] */ class SignaturePicker { companion object { /** - * Pick the at validation date most recent valid key revocation signature. - * If there are hard revocation signatures, the latest hard revocation sig is picked, even if it was created after - * validationDate or if it is already expired. + * Pick the at validation date most recent valid key revocation signature. If there are hard + * revocation signatures, the latest hard revocation sig is picked, even if it was created + * after validationDate or if it is already expired. * * @param keyRing key ring * @param policy policy @@ -45,21 +39,26 @@ class SignaturePicker { * @return most recent, valid key revocation signature */ @JvmStatic - fun pickCurrentRevocationSelfSignature(keyRing: PGPKeyRing, policy: Policy, referenceTime: Date): PGPSignature? { + fun pickCurrentRevocationSelfSignature( + keyRing: PGPKeyRing, + policy: Policy, + referenceTime: Date + ): PGPSignature? { val primaryKey = keyRing.publicKey return getSortedSignaturesOfType(primaryKey, SignatureType.KEY_REVOCATION).lastOrNull { return@lastOrNull try { - SignatureVerifier.verifyKeyRevocationSignature(it, primaryKey, policy, referenceTime) + SignatureVerifier.verifyKeyRevocationSignature( + it, primaryKey, policy, referenceTime) true // valid - } catch (e : SignatureValidationException) { + } catch (e: SignatureValidationException) { false // not valid } } } /** - * Pick the at validationDate most recent, valid direct key signature. - * This method might return null, if there is no direct key self-signature which is valid at validationDate. + * Pick the at validationDate most recent, valid direct key signature. This method might + * return null, if there is no direct key self-signature which is valid at validationDate. * * @param keyRing key ring * @param policy policy @@ -67,28 +66,38 @@ class SignaturePicker { * @return direct-key self-signature */ @JvmStatic - fun pickCurrentDirectKeySelfSignature(keyRing: PGPKeyRing, policy: Policy, referenceTime: Date): PGPSignature? { + fun pickCurrentDirectKeySelfSignature( + keyRing: PGPKeyRing, + policy: Policy, + referenceTime: Date + ): PGPSignature? { val primaryKey = keyRing.publicKey return pickCurrentDirectKeySignature(primaryKey, primaryKey, policy, referenceTime) } @JvmStatic - fun pickCurrentDirectKeySignature(signingKey: PGPPublicKey, signedKey: PGPPublicKey, policy: Policy, referenceTime: Date): PGPSignature? { + fun pickCurrentDirectKeySignature( + signingKey: PGPPublicKey, + signedKey: PGPPublicKey, + policy: Policy, + referenceTime: Date + ): PGPSignature? { return getSortedSignaturesOfType(signedKey, SignatureType.DIRECT_KEY).lastOrNull { return@lastOrNull try { - SignatureVerifier.verifyDirectKeySignature(it, signingKey, signedKey, policy, referenceTime) + SignatureVerifier.verifyDirectKeySignature( + it, signingKey, signedKey, policy, referenceTime) true - } catch (e : SignatureValidationException) { + } catch (e: SignatureValidationException) { false } } } /** - * Pick the at validationDate latest direct key signature. - * This method might return an expired signature. - * If there are more than one direct-key signature, and some of those are not expired, the latest non-expired - * yet already effective direct-key signature will be returned. + * Pick the at validationDate latest direct key signature. This method might return an + * expired signature. If there are more than one direct-key signature, and some of those are + * not expired, the latest non-expired yet already effective direct-key signature will be + * returned. * * @param keyRing key ring * @param policy policy @@ -96,15 +105,20 @@ class SignaturePicker { * @return latest direct key signature */ @JvmStatic - fun pickLatestDirectKeySignature(keyRing: PGPKeyRing, policy: Policy, referenceTime: Date): PGPSignature? { - return pickLatestDirectKeySignature(keyRing.publicKey, keyRing.publicKey, policy, referenceTime) + fun pickLatestDirectKeySignature( + keyRing: PGPKeyRing, + policy: Policy, + referenceTime: Date + ): PGPSignature? { + return pickLatestDirectKeySignature( + keyRing.publicKey, keyRing.publicKey, policy, referenceTime) } /** * Pick the at validationDate latest direct key signature made by signingKey on signedKey. - * This method might return an expired signature. - * If a non-expired direct-key signature exists, the latest non-expired yet already effective direct-key - * signature will be returned. + * This method might return an expired signature. If a non-expired direct-key signature + * exists, the latest non-expired yet already effective direct-key signature will be + * returned. * * @param signingKey signing key (key that made the sig) * @param signedKey signed key (key that carries the sig) @@ -113,7 +127,12 @@ class SignaturePicker { * @return latest direct key sig */ @JvmStatic - fun pickLatestDirectKeySignature(signingKey: PGPPublicKey, signedKey: PGPPublicKey, policy: Policy, referenceTime: Date): PGPSignature? { + fun pickLatestDirectKeySignature( + signingKey: PGPPublicKey, + signedKey: PGPPublicKey, + policy: Policy, + referenceTime: Date + ): PGPSignature? { var latest: PGPSignature? = null return getSortedSignaturesOfType(signedKey, SignatureType.DIRECT_KEY).lastOrNull { try { @@ -126,16 +145,16 @@ class SignaturePicker { SignatureValidator.correctSignatureOverKey(signingKey, signedKey).verify(it) latest = it true - } catch (e : SignatureValidationException) { + } catch (e: SignatureValidationException) { false } } } /** - * Pick the at validationDate most recent, valid user-id revocation signature. - * If there are hard revocation signatures, the latest hard revocation sig is picked, even if it was created after - * validationDate or if it is already expired. + * Pick the at validationDate most recent, valid user-id revocation signature. If there are + * hard revocation signatures, the latest hard revocation sig is picked, even if it was + * created after validationDate or if it is already expired. * * @param keyRing key ring * @param userId user-Id that gets revoked @@ -144,23 +163,31 @@ class SignaturePicker { * @return revocation signature */ @JvmStatic - fun pickCurrentUserIdRevocationSignature(keyRing: PGPKeyRing, userId: CharSequence, policy: Policy, referenceTime: Date): PGPSignature? { + fun pickCurrentUserIdRevocationSignature( + keyRing: PGPKeyRing, + userId: CharSequence, + policy: Policy, + referenceTime: Date + ): PGPSignature? { val primaryKey = keyRing.publicKey - return getSortedSignaturesOfType(primaryKey, SignatureType.CERTIFICATION_REVOCATION).lastOrNull { - keyRing.getPublicKeyFor(it) ?: return@lastOrNull false // signature made by external key. skip. - return@lastOrNull try { - SignatureVerifier.verifyUserIdRevocation(userId.toString(), it, primaryKey, policy, referenceTime) - true - } catch (e : SignatureValidationException) { - false // signature not valid + return getSortedSignaturesOfType(primaryKey, SignatureType.CERTIFICATION_REVOCATION) + .lastOrNull { + keyRing.getPublicKeyFor(it) + ?: return@lastOrNull false // signature made by external key. skip. + return@lastOrNull try { + SignatureVerifier.verifyUserIdRevocation( + userId.toString(), it, primaryKey, policy, referenceTime) + true + } catch (e: SignatureValidationException) { + false // signature not valid + } } - } } /** - * Pick the at validationDate latest, valid certification self-signature for the given user-id. - * This method might return null, if there is no certification self signature for that user-id which is valid - * at validationDate. + * Pick the at validationDate latest, valid certification self-signature for the given + * user-id. This method might return null, if there is no certification self signature for + * that user-id which is valid at validationDate. * * @param keyRing keyring * @param userId userid @@ -169,25 +196,34 @@ class SignaturePicker { * @return user-id certification */ @JvmStatic - fun pickCurrentUserIdCertificationSignature(keyRing: PGPKeyRing, userId: CharSequence, policy: Policy, referenceTime: Date): PGPSignature? { + fun pickCurrentUserIdCertificationSignature( + keyRing: PGPKeyRing, + userId: CharSequence, + policy: Policy, + referenceTime: Date + ): PGPSignature? { val primaryKey = keyRing.publicKey - return primaryKey.getSignaturesForID(userId.toString()).asSequence() - .sortedWith(SignatureCreationDateComparator()) - .lastOrNull { - return@lastOrNull it.wasIssuedBy(primaryKey) && try { - SignatureVerifier.verifyUserIdCertification(userId.toString(), it, primaryKey, policy, referenceTime) + return primaryKey + .getSignaturesForID(userId.toString()) + .asSequence() + .sortedWith(SignatureCreationDateComparator()) + .lastOrNull { + return@lastOrNull it.wasIssuedBy(primaryKey) && + try { + SignatureVerifier.verifyUserIdCertification( + userId.toString(), it, primaryKey, policy, referenceTime) true - } catch (e : SignatureValidationException) { + } catch (e: SignatureValidationException) { false } - } + } } /** * Pick the at validationDate latest certification self-signature for the given user-id. - * This method might return an expired signature. - * If a non-expired user-id certification signature exists, the latest non-expired yet already effective - * user-id certification signature for the given user-id will be returned. + * This method might return an expired signature. If a non-expired user-id certification + * signature exists, the latest non-expired yet already effective user-id certification + * signature for the given user-id will be returned. * * @param keyRing keyring * @param userId userid @@ -196,28 +232,38 @@ class SignaturePicker { * @return user-id certification */ @JvmStatic - fun pickLatestUserIdCertificationSignature(keyRing: PGPKeyRing, userId: CharSequence, policy: Policy, referenceTime: Date): PGPSignature? { + fun pickLatestUserIdCertificationSignature( + keyRing: PGPKeyRing, + userId: CharSequence, + policy: Policy, + referenceTime: Date + ): PGPSignature? { val primaryKey = keyRing.publicKey - return primaryKey.getSignaturesForID(userId.toString()).asSequence() - .sortedWith(SignatureCreationDateComparator()) - .lastOrNull { - return@lastOrNull try { - SignatureValidator.wasPossiblyMadeByKey(primaryKey).verify(it) - SignatureValidator.signatureIsCertification().verify(it) - SignatureValidator.signatureStructureIsAcceptable(primaryKey, policy).verify(it) - SignatureValidator.signatureIsAlreadyEffective(referenceTime).verify(it) - SignatureValidator.correctSignatureOverUserId(userId.toString(), primaryKey, primaryKey).verify(it) - true - } catch (e : SignatureValidationException) { - false - } + return primaryKey + .getSignaturesForID(userId.toString()) + .asSequence() + .sortedWith(SignatureCreationDateComparator()) + .lastOrNull { + return@lastOrNull try { + SignatureValidator.wasPossiblyMadeByKey(primaryKey).verify(it) + SignatureValidator.signatureIsCertification().verify(it) + SignatureValidator.signatureStructureIsAcceptable(primaryKey, policy) + .verify(it) + SignatureValidator.signatureIsAlreadyEffective(referenceTime).verify(it) + SignatureValidator.correctSignatureOverUserId( + userId.toString(), primaryKey, primaryKey) + .verify(it) + true + } catch (e: SignatureValidationException) { + false } + } } /** - * Pick the at validationDate most recent, valid subkey revocation signature. - * If there are hard revocation signatures, the latest hard revocation sig is picked, even if it was created after - * validationDate or if it is already expired. + * Pick the at validationDate most recent, valid subkey revocation signature. If there are + * hard revocation signatures, the latest hard revocation sig is picked, even if it was + * created after validationDate or if it is already expired. * * @param keyRing keyring * @param subkey subkey @@ -226,14 +272,22 @@ class SignaturePicker { * @return subkey revocation signature */ @JvmStatic - fun pickCurrentSubkeyBindingRevocationSignature(keyRing: PGPKeyRing, subkey: PGPPublicKey, policy: Policy, referenceTime: Date): PGPSignature? { + fun pickCurrentSubkeyBindingRevocationSignature( + keyRing: PGPKeyRing, + subkey: PGPPublicKey, + policy: Policy, + referenceTime: Date + ): PGPSignature? { val primaryKey = keyRing.publicKey - require(primaryKey.keyID != subkey.keyID) { "Primary key cannot have subkey binding revocations." } + require(primaryKey.keyID != subkey.keyID) { + "Primary key cannot have subkey binding revocations." + } return getSortedSignaturesOfType(subkey, SignatureType.SUBKEY_REVOCATION).lastOrNull { return@lastOrNull try { - SignatureVerifier.verifySubkeyBindingRevocation(it, primaryKey, subkey, policy, referenceTime) + SignatureVerifier.verifySubkeyBindingRevocation( + it, primaryKey, subkey, policy, referenceTime) true - } catch (e : SignatureValidationException) { + } catch (e: SignatureValidationException) { false } } @@ -241,8 +295,8 @@ class SignaturePicker { /** * Pick the at validationDate latest, valid subkey binding signature for the given subkey. - * This method might return null, if there is no subkey binding signature which is valid - * at validationDate. + * This method might return null, if there is no subkey binding signature which is valid at + * validationDate. * * @param keyRing key ring * @param subkey subkey @@ -251,24 +305,32 @@ class SignaturePicker { * @return most recent valid subkey binding signature */ @JvmStatic - fun pickCurrentSubkeyBindingSignature(keyRing: PGPKeyRing, subkey: PGPPublicKey, policy: Policy, referenceTime: Date): PGPSignature? { + fun pickCurrentSubkeyBindingSignature( + keyRing: PGPKeyRing, + subkey: PGPPublicKey, + policy: Policy, + referenceTime: Date + ): PGPSignature? { val primaryKey = keyRing.publicKey - require(primaryKey.keyID != subkey.keyID) { "Primary key cannot have subkey binding signatures." } + require(primaryKey.keyID != subkey.keyID) { + "Primary key cannot have subkey binding signatures." + } return getSortedSignaturesOfType(subkey, SignatureType.SUBKEY_BINDING).lastOrNull { return@lastOrNull try { - SignatureVerifier.verifySubkeyBindingSignature(it, primaryKey, subkey, policy, referenceTime) + SignatureVerifier.verifySubkeyBindingSignature( + it, primaryKey, subkey, policy, referenceTime) true - } catch (e : SignatureValidationException) { + } catch (e: SignatureValidationException) { false } } } /** - * Pick the at validationDate latest subkey binding signature for the given subkey. - * This method might return an expired signature. - * If a non-expired subkey binding signature exists, the latest non-expired yet already effective - * subkey binding signature for the given subkey will be returned. + * Pick the at validationDate latest subkey binding signature for the given subkey. This + * method might return an expired signature. If a non-expired subkey binding signature + * exists, the latest non-expired yet already effective subkey binding signature for the + * given subkey will be returned. * * @param keyRing key ring * @param subkey subkey @@ -277,9 +339,16 @@ class SignaturePicker { * @return subkey binding signature */ @JvmStatic - fun pickLatestSubkeyBindingSignature(keyRing: PGPKeyRing, subkey: PGPPublicKey, policy: Policy, referenceTime: Date): PGPSignature? { + fun pickLatestSubkeyBindingSignature( + keyRing: PGPKeyRing, + subkey: PGPPublicKey, + policy: Policy, + referenceTime: Date + ): PGPSignature? { val primaryKey = keyRing.publicKey - require(primaryKey.keyID != subkey.keyID) { "Primary key cannot have subkey binding signatures." } + require(primaryKey.keyID != subkey.keyID) { + "Primary key cannot have subkey binding signatures." + } var latest: PGPSignature? = null return getSortedSignaturesOfType(subkey, SignatureType.SUBKEY_BINDING).lastOrNull { return@lastOrNull try { @@ -287,24 +356,28 @@ class SignaturePicker { SignatureValidator.signatureStructureIsAcceptable(primaryKey, policy).verify(it) SignatureValidator.signatureDoesNotPredateSignee(subkey).verify(it) SignatureValidator.signatureIsAlreadyEffective(referenceTime).verify(it) - // if the currently latest signature is not yet expired, check if the next candidate is not yet expired + // if the currently latest signature is not yet expired, check if the next + // candidate is not yet expired if (latest != null && !latest!!.isExpired(referenceTime)) { SignatureValidator.signatureIsNotYetExpired(referenceTime).verify(it) } SignatureValidator.correctSubkeyBindingSignature(primaryKey, subkey).verify(it) latest = it true - } catch (e : SignatureValidationException) { + } catch (e: SignatureValidationException) { false } } } @JvmStatic - private fun getSortedSignaturesOfType(key: PGPPublicKey, type: SignatureType): List = - key.getSignaturesOfType(type.code).asSequence() - .sortedWith(SignatureCreationDateComparator()) - .toList() + private fun getSortedSignaturesOfType( + key: PGPPublicKey, + type: SignatureType + ): List = + key.getSignaturesOfType(type.code) + .asSequence() + .sortedWith(SignatureCreationDateComparator()) + .toList() } - -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/consumer/SignatureValidityComparator.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/consumer/SignatureValidityComparator.kt index f2b586ae..38c409b7 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/consumer/SignatureValidityComparator.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/consumer/SignatureValidityComparator.kt @@ -10,15 +10,17 @@ import org.bouncycastle.openpgp.PGPSignature /** * Comparator which sorts signatures based on an ordering and on revocation hardness. * - * If a list of signatures gets ordered using this comparator, hard revocations will always - * come first. - * Further, signatures are ordered by date according to the [SignatureCreationDateComparator.Order]. + * If a list of signatures gets ordered using this comparator, hard revocations will always come + * first. Further, signatures are ordered by date according to the + * [SignatureCreationDateComparator.Order]. */ class SignatureValidityComparator( - order: SignatureCreationDateComparator.Order = SignatureCreationDateComparator.Order.OLD_TO_NEW + order: SignatureCreationDateComparator.Order = SignatureCreationDateComparator.Order.OLD_TO_NEW ) : Comparator { - private val creationDateComparator: SignatureCreationDateComparator = SignatureCreationDateComparator(order) + private val creationDateComparator: SignatureCreationDateComparator = + SignatureCreationDateComparator(order) + override fun compare(one: PGPSignature, two: PGPSignature): Int { return if (one.isHardRevocation == two.isHardRevocation) { // Both have the same hardness, so compare creation time @@ -27,5 +29,4 @@ class SignatureValidityComparator( // else favor the "harder" signature else if (one.isHardRevocation) -1 else 1 } - -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/BaseSignatureSubpackets.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/BaseSignatureSubpackets.kt index 66618b23..3586eecf 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/BaseSignatureSubpackets.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/BaseSignatureSubpackets.kt @@ -4,14 +4,14 @@ package org.pgpainless.signature.subpackets +import java.io.IOException +import java.net.URL +import java.util.* import org.bouncycastle.bcpg.sig.* import org.bouncycastle.openpgp.PGPPublicKey import org.bouncycastle.openpgp.PGPSignature import org.pgpainless.algorithm.HashAlgorithm import org.pgpainless.algorithm.PublicKeyAlgorithm -import java.io.IOException -import java.net.URL -import java.util.* interface BaseSignatureSubpackets { @@ -22,9 +22,8 @@ interface BaseSignatureSubpackets { * * @param key key * @return this - * - * @deprecated this method MUST NOT be used for OpenPGP v6, since v6 signatures MUST NOT contain any - * [IssuerKeyID] packets. + * @deprecated this method MUST NOT be used for OpenPGP v6, since v6 signatures MUST NOT contain + * any [IssuerKeyID] packets. */ fun setIssuerFingerprintAndKeyId(key: PGPPublicKey): BaseSignatureSubpackets @@ -46,13 +45,22 @@ interface BaseSignatureSubpackets { fun setSignatureCreationTime(creationTime: SignatureCreationTime?): BaseSignatureSubpackets - fun setSignatureExpirationTime(creationTime: Date, expirationTime: Date?): BaseSignatureSubpackets + fun setSignatureExpirationTime( + creationTime: Date, + expirationTime: Date? + ): BaseSignatureSubpackets - fun setSignatureExpirationTime(isCritical: Boolean, creationTime: Date, expirationTime: Date?): BaseSignatureSubpackets + fun setSignatureExpirationTime( + isCritical: Boolean, + creationTime: Date, + expirationTime: Date? + ): BaseSignatureSubpackets fun setSignatureExpirationTime(isCritical: Boolean, seconds: Long): BaseSignatureSubpackets - fun setSignatureExpirationTime(expirationTime: SignatureExpirationTime?): BaseSignatureSubpackets + fun setSignatureExpirationTime( + expirationTime: SignatureExpirationTime? + ): BaseSignatureSubpackets fun setSignerUserId(userId: CharSequence): BaseSignatureSubpackets @@ -60,9 +68,18 @@ interface BaseSignatureSubpackets { fun setSignerUserId(signerUserID: SignerUserID?): BaseSignatureSubpackets - fun addNotationData(isCritical: Boolean, notationName: String, notationValue: String): BaseSignatureSubpackets + fun addNotationData( + isCritical: Boolean, + notationName: String, + notationValue: String + ): BaseSignatureSubpackets - fun addNotationData(isCritical: Boolean, isHumanReadable: Boolean, notationName: String, notationValue: String): BaseSignatureSubpackets + fun addNotationData( + isCritical: Boolean, + isHumanReadable: Boolean, + notationName: String, + notationValue: String + ): BaseSignatureSubpackets fun addNotationData(notationData: NotationData): BaseSignatureSubpackets @@ -70,9 +87,14 @@ interface BaseSignatureSubpackets { fun addIntendedRecipientFingerprint(recipientKey: PGPPublicKey): BaseSignatureSubpackets - fun addIntendedRecipientFingerprint(isCritical: Boolean, recipientKey: PGPPublicKey): BaseSignatureSubpackets + fun addIntendedRecipientFingerprint( + isCritical: Boolean, + recipientKey: PGPPublicKey + ): BaseSignatureSubpackets - fun addIntendedRecipientFingerprint(intendedRecipient: IntendedRecipientFingerprint): BaseSignatureSubpackets + fun addIntendedRecipientFingerprint( + intendedRecipient: IntendedRecipientFingerprint + ): BaseSignatureSubpackets fun clearIntendedRecipientFingerprints(): BaseSignatureSubpackets @@ -104,9 +126,18 @@ interface BaseSignatureSubpackets { fun setRevocable(revocable: Revocable?): BaseSignatureSubpackets - fun setSignatureTarget(keyAlgorithm: PublicKeyAlgorithm, hashAlgorithm: HashAlgorithm, hashData: ByteArray): BaseSignatureSubpackets + fun setSignatureTarget( + keyAlgorithm: PublicKeyAlgorithm, + hashAlgorithm: HashAlgorithm, + hashData: ByteArray + ): BaseSignatureSubpackets - fun setSignatureTarget(isCritical: Boolean, keyAlgorithm: PublicKeyAlgorithm, hashAlgorithm: HashAlgorithm, hashData: ByteArray): BaseSignatureSubpackets + fun setSignatureTarget( + isCritical: Boolean, + keyAlgorithm: PublicKeyAlgorithm, + hashAlgorithm: HashAlgorithm, + hashData: ByteArray + ): BaseSignatureSubpackets fun setSignatureTarget(signatureTarget: SignatureTarget?): BaseSignatureSubpackets @@ -125,4 +156,4 @@ interface BaseSignatureSubpackets { fun addEmbeddedSignature(embeddedSignature: EmbeddedSignature): BaseSignatureSubpackets fun clearEmbeddedSignatures(): BaseSignatureSubpackets -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/CertificationSubpackets.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/CertificationSubpackets.kt index 423edd8e..c3edf2c4 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/CertificationSubpackets.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/CertificationSubpackets.kt @@ -7,5 +7,4 @@ package org.pgpainless.signature.subpackets interface CertificationSubpackets : BaseSignatureSubpackets { interface Callback : SignatureSubpacketCallback - -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/RevocationSignatureSubpackets.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/RevocationSignatureSubpackets.kt index 327ed4a9..2e152f9e 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/RevocationSignatureSubpackets.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/RevocationSignatureSubpackets.kt @@ -11,11 +11,20 @@ interface RevocationSignatureSubpackets : BaseSignatureSubpackets { interface Callback : SignatureSubpacketCallback - fun setRevocationReason(revocationAttributes: RevocationAttributes): RevocationSignatureSubpackets + fun setRevocationReason( + revocationAttributes: RevocationAttributes + ): RevocationSignatureSubpackets - fun setRevocationReason(isCritical: Boolean, revocationAttributes: RevocationAttributes): RevocationSignatureSubpackets + fun setRevocationReason( + isCritical: Boolean, + revocationAttributes: RevocationAttributes + ): RevocationSignatureSubpackets - fun setRevocationReason(isCritical: Boolean, reason: RevocationAttributes.Reason, description: CharSequence): RevocationSignatureSubpackets + fun setRevocationReason( + isCritical: Boolean, + reason: RevocationAttributes.Reason, + description: CharSequence + ): RevocationSignatureSubpackets fun setRevocationReason(reason: RevocationReason?): RevocationSignatureSubpackets -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/SelfSignatureSubpackets.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/SelfSignatureSubpackets.kt index ce8e09a9..d1b2b428 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/SelfSignatureSubpackets.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/SelfSignatureSubpackets.kt @@ -4,6 +4,7 @@ package org.pgpainless.signature.subpackets +import java.util.* import org.bouncycastle.bcpg.sig.Features import org.bouncycastle.bcpg.sig.KeyExpirationTime import org.bouncycastle.bcpg.sig.KeyFlags @@ -12,7 +13,6 @@ import org.bouncycastle.bcpg.sig.PrimaryUserID import org.bouncycastle.bcpg.sig.RevocationKey import org.bouncycastle.openpgp.PGPPublicKey import org.pgpainless.algorithm.* -import java.util.* interface SelfSignatureSubpackets : BaseSignatureSubpackets { @@ -34,35 +34,66 @@ interface SelfSignatureSubpackets : BaseSignatureSubpackets { fun setKeyExpirationTime(key: PGPPublicKey, keyExpirationTime: Date?): SelfSignatureSubpackets - fun setKeyExpirationTime(keyCreationTime: Date, keyExpirationTime: Date?): SelfSignatureSubpackets + fun setKeyExpirationTime( + keyCreationTime: Date, + keyExpirationTime: Date? + ): SelfSignatureSubpackets - fun setKeyExpirationTime(isCritical: Boolean, keyCreationTime: Date, keyExpirationTime: Date?): SelfSignatureSubpackets + fun setKeyExpirationTime( + isCritical: Boolean, + keyCreationTime: Date, + keyExpirationTime: Date? + ): SelfSignatureSubpackets - fun setKeyExpirationTime(isCritical: Boolean, secondsFromCreationToExpiration: Long): SelfSignatureSubpackets + fun setKeyExpirationTime( + isCritical: Boolean, + secondsFromCreationToExpiration: Long + ): SelfSignatureSubpackets fun setKeyExpirationTime(keyExpirationTime: KeyExpirationTime?): SelfSignatureSubpackets - fun setPreferredCompressionAlgorithms(vararg algorithms: CompressionAlgorithm): SelfSignatureSubpackets + fun setPreferredCompressionAlgorithms( + vararg algorithms: CompressionAlgorithm + ): SelfSignatureSubpackets - fun setPreferredCompressionAlgorithms(algorithms: Collection): SelfSignatureSubpackets + fun setPreferredCompressionAlgorithms( + algorithms: Collection + ): SelfSignatureSubpackets - fun setPreferredCompressionAlgorithms(isCritical: Boolean, algorithms: Collection): SelfSignatureSubpackets + fun setPreferredCompressionAlgorithms( + isCritical: Boolean, + algorithms: Collection + ): SelfSignatureSubpackets - fun setPreferredCompressionAlgorithms(preferredAlgorithms: PreferredAlgorithms?): SelfSignatureSubpackets + fun setPreferredCompressionAlgorithms( + preferredAlgorithms: PreferredAlgorithms? + ): SelfSignatureSubpackets - fun setPreferredSymmetricKeyAlgorithms(vararg algorithms: SymmetricKeyAlgorithm): SelfSignatureSubpackets + fun setPreferredSymmetricKeyAlgorithms( + vararg algorithms: SymmetricKeyAlgorithm + ): SelfSignatureSubpackets - fun setPreferredSymmetricKeyAlgorithms(algorithms: Collection): SelfSignatureSubpackets + fun setPreferredSymmetricKeyAlgorithms( + algorithms: Collection + ): SelfSignatureSubpackets - fun setPreferredSymmetricKeyAlgorithms(isCritical: Boolean, algorithms: Collection): SelfSignatureSubpackets + fun setPreferredSymmetricKeyAlgorithms( + isCritical: Boolean, + algorithms: Collection + ): SelfSignatureSubpackets - fun setPreferredSymmetricKeyAlgorithms(algorithms: PreferredAlgorithms?): SelfSignatureSubpackets + fun setPreferredSymmetricKeyAlgorithms( + algorithms: PreferredAlgorithms? + ): SelfSignatureSubpackets fun setPreferredHashAlgorithms(vararg algorithms: HashAlgorithm): SelfSignatureSubpackets fun setPreferredHashAlgorithms(algorithms: Collection): SelfSignatureSubpackets - fun setPreferredHashAlgorithms(isCritical: Boolean, algorithms: Collection): SelfSignatureSubpackets + fun setPreferredHashAlgorithms( + isCritical: Boolean, + algorithms: Collection + ): SelfSignatureSubpackets fun setPreferredHashAlgorithms(algorithms: PreferredAlgorithms?): SelfSignatureSubpackets @@ -70,7 +101,11 @@ interface SelfSignatureSubpackets : BaseSignatureSubpackets { fun addRevocationKey(isCritical: Boolean, revocationKey: PGPPublicKey): SelfSignatureSubpackets - fun addRevocationKey(isCritical: Boolean, isSensitive: Boolean, revocationKey: PGPPublicKey): SelfSignatureSubpackets + fun addRevocationKey( + isCritical: Boolean, + isSensitive: Boolean, + revocationKey: PGPPublicKey + ): SelfSignatureSubpackets fun addRevocationKey(revocationKey: RevocationKey): SelfSignatureSubpackets @@ -81,4 +116,4 @@ interface SelfSignatureSubpackets : BaseSignatureSubpackets { fun setFeatures(isCritical: Boolean, vararg features: Feature): SelfSignatureSubpackets fun setFeatures(features: Features?): SelfSignatureSubpackets -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/SignatureSubpacketCallback.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/SignatureSubpacketCallback.kt index d147dc97..fbc56035 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/SignatureSubpacketCallback.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/SignatureSubpacketCallback.kt @@ -23,4 +23,4 @@ interface SignatureSubpacketCallback { fun modifyUnhashedSubpackets(unhashedSubpackets: S) { // Empty default implementation to allow for cleaner overriding } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/SignatureSubpackets.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/SignatureSubpackets.kt index a88e4e8a..e8fe2d94 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/SignatureSubpackets.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/SignatureSubpackets.kt @@ -4,6 +4,10 @@ package org.pgpainless.signature.subpackets +import java.lang.IllegalArgumentException +import java.net.URL +import java.util.* +import kotlin.experimental.or import openpgp.secondsTill import openpgp.toSecondsPrecision import org.bouncycastle.bcpg.SignatureSubpacket @@ -14,13 +18,12 @@ import org.bouncycastle.openpgp.PGPSignature import org.bouncycastle.openpgp.PGPSignatureSubpacketVector import org.pgpainless.algorithm.* import org.pgpainless.key.util.RevocationAttributes -import java.lang.IllegalArgumentException -import java.net.URL -import java.util.* -import kotlin.experimental.or -class SignatureSubpackets - : BaseSignatureSubpackets, SelfSignatureSubpackets, CertificationSubpackets, RevocationSignatureSubpackets { +class SignatureSubpackets : + BaseSignatureSubpackets, + SelfSignatureSubpackets, + CertificationSubpackets, + RevocationSignatureSubpackets { interface Callback : SignatureSubpacketCallback @@ -52,7 +55,10 @@ class SignatureSubpackets companion object { @JvmStatic - fun refreshHashedSubpackets(issuer: PGPPublicKey, oldSignature: PGPSignature): SignatureSubpackets { + fun refreshHashedSubpackets( + issuer: PGPPublicKey, + oldSignature: PGPSignature + ): SignatureSubpackets { return createHashedSubpacketsFrom(issuer, oldSignature.hashedSubPackets) } @@ -62,10 +68,11 @@ class SignatureSubpackets } @JvmStatic - fun createHashedSubpacketsFrom(issuer: PGPPublicKey, base: PGPSignatureSubpacketVector): SignatureSubpackets { - return createSubpacketsFrom(base).apply { - setIssuerFingerprintAndKeyId(issuer) - } + fun createHashedSubpacketsFrom( + issuer: PGPPublicKey, + base: PGPSignatureSubpacketVector + ): SignatureSubpackets { + return createSubpacketsFrom(base).apply { setIssuerFingerprintAndKeyId(issuer) } } @JvmStatic @@ -84,15 +91,23 @@ class SignatureSubpackets } } - override fun setRevocationReason(revocationAttributes: RevocationAttributes): SignatureSubpackets = apply { - setRevocationReason(false, revocationAttributes) + override fun setRevocationReason( + revocationAttributes: RevocationAttributes + ): SignatureSubpackets = apply { setRevocationReason(false, revocationAttributes) } + + override fun setRevocationReason( + isCritical: Boolean, + revocationAttributes: RevocationAttributes + ): SignatureSubpackets = apply { + setRevocationReason( + isCritical, revocationAttributes.reason, revocationAttributes.description) } - override fun setRevocationReason(isCritical: Boolean, revocationAttributes: RevocationAttributes): SignatureSubpackets = apply { - setRevocationReason(isCritical, revocationAttributes.reason, revocationAttributes.description) - } - - override fun setRevocationReason(isCritical: Boolean, reason: RevocationAttributes.Reason, description: CharSequence): SignatureSubpackets = apply { + override fun setRevocationReason( + isCritical: Boolean, + reason: RevocationAttributes.Reason, + description: CharSequence + ): SignatureSubpackets = apply { setRevocationReason(RevocationReason(isCritical, reason.code, description.toString())) } @@ -108,17 +123,16 @@ class SignatureSubpackets setKeyFlags(true, *keyFlags.toTypedArray()) } - override fun setKeyFlags(isCritical: Boolean, vararg keyFlags: KeyFlag): SignatureSubpackets = apply { - setKeyFlags(KeyFlags(isCritical, KeyFlag.toBitmask(*keyFlags))) - } + override fun setKeyFlags(isCritical: Boolean, vararg keyFlags: KeyFlag): SignatureSubpackets = + apply { + setKeyFlags(KeyFlags(isCritical, KeyFlag.toBitmask(*keyFlags))) + } override fun setKeyFlags(keyFlags: KeyFlags?): SignatureSubpackets = apply { this.keyFlagsSubpacket = keyFlags } - override fun setPrimaryUserId(): SignatureSubpackets = apply { - setPrimaryUserId(true) - } + override fun setPrimaryUserId(): SignatureSubpackets = apply { setPrimaryUserId(true) } override fun setPrimaryUserId(isCritical: Boolean): SignatureSubpackets = apply { setPrimaryUserId(PrimaryUserID(isCritical, true)) @@ -128,15 +142,23 @@ class SignatureSubpackets this.primaryUserIdSubpacket = primaryUserID } - override fun setKeyExpirationTime(key: PGPPublicKey, keyExpirationTime: Date?): SignatureSubpackets = apply { - setKeyExpirationTime(key.creationTime, keyExpirationTime) - } + override fun setKeyExpirationTime( + key: PGPPublicKey, + keyExpirationTime: Date? + ): SignatureSubpackets = apply { setKeyExpirationTime(key.creationTime, keyExpirationTime) } - override fun setKeyExpirationTime(keyCreationTime: Date, keyExpirationTime: Date?): SignatureSubpackets = apply { + override fun setKeyExpirationTime( + keyCreationTime: Date, + keyExpirationTime: Date? + ): SignatureSubpackets = apply { setKeyExpirationTime(true, keyCreationTime, keyExpirationTime) } - override fun setKeyExpirationTime(isCritical: Boolean, keyCreationTime: Date, keyExpirationTime: Date?): SignatureSubpackets = apply { + override fun setKeyExpirationTime( + isCritical: Boolean, + keyCreationTime: Date, + keyExpirationTime: Date? + ): SignatureSubpackets = apply { if (keyExpirationTime == null) { setKeyExpirationTime(isCritical, 0) } else { @@ -144,94 +166,124 @@ class SignatureSubpackets } } - override fun setKeyExpirationTime(isCritical: Boolean, secondsFromCreationToExpiration: Long): SignatureSubpackets = apply { + override fun setKeyExpirationTime( + isCritical: Boolean, + secondsFromCreationToExpiration: Long + ): SignatureSubpackets = apply { enforceExpirationBounds(secondsFromCreationToExpiration) setKeyExpirationTime(KeyExpirationTime(isCritical, secondsFromCreationToExpiration)) } - override fun setKeyExpirationTime(keyExpirationTime: KeyExpirationTime?): SignatureSubpackets = apply { - this.keyExpirationTimeSubpacket = keyExpirationTime - } + override fun setKeyExpirationTime(keyExpirationTime: KeyExpirationTime?): SignatureSubpackets = + apply { + this.keyExpirationTimeSubpacket = keyExpirationTime + } - override fun setPreferredCompressionAlgorithms(vararg algorithms: CompressionAlgorithm): SignatureSubpackets = apply { - setPreferredCompressionAlgorithms(setOf(*algorithms)) - } + override fun setPreferredCompressionAlgorithms( + vararg algorithms: CompressionAlgorithm + ): SignatureSubpackets = apply { setPreferredCompressionAlgorithms(setOf(*algorithms)) } - override fun setPreferredCompressionAlgorithms(algorithms: Collection): SignatureSubpackets = apply { - setPreferredCompressionAlgorithms(false, algorithms) - } + override fun setPreferredCompressionAlgorithms( + algorithms: Collection + ): SignatureSubpackets = apply { setPreferredCompressionAlgorithms(false, algorithms) } - override fun setPreferredCompressionAlgorithms(isCritical: Boolean, algorithms: Collection): SignatureSubpackets = apply { - setPreferredCompressionAlgorithms(PreferredAlgorithms( + override fun setPreferredCompressionAlgorithms( + isCritical: Boolean, + algorithms: Collection + ): SignatureSubpackets = apply { + setPreferredCompressionAlgorithms( + PreferredAlgorithms( SignatureSubpacketTags.PREFERRED_COMP_ALGS, isCritical, algorithms.map { it.algorithmId }.toIntArray())) } - override fun setPreferredCompressionAlgorithms(algorithms: PreferredAlgorithms?): SignatureSubpackets = apply { - require(algorithms == null || algorithms.type == SignatureSubpacketTags.PREFERRED_COMP_ALGS) { - "Invalid preferred compression algorithms type." - } + override fun setPreferredCompressionAlgorithms( + algorithms: PreferredAlgorithms? + ): SignatureSubpackets = apply { + require( + algorithms == null || algorithms.type == SignatureSubpacketTags.PREFERRED_COMP_ALGS) { + "Invalid preferred compression algorithms type." + } this.preferredCompressionAlgorithmsSubpacket = algorithms } - override fun setPreferredSymmetricKeyAlgorithms(vararg algorithms: SymmetricKeyAlgorithm): SignatureSubpackets = apply { - setPreferredSymmetricKeyAlgorithms(setOf(*algorithms)) - } + override fun setPreferredSymmetricKeyAlgorithms( + vararg algorithms: SymmetricKeyAlgorithm + ): SignatureSubpackets = apply { setPreferredSymmetricKeyAlgorithms(setOf(*algorithms)) } - override fun setPreferredSymmetricKeyAlgorithms(algorithms: Collection): SignatureSubpackets = apply { - setPreferredSymmetricKeyAlgorithms(false, algorithms) - } + override fun setPreferredSymmetricKeyAlgorithms( + algorithms: Collection + ): SignatureSubpackets = apply { setPreferredSymmetricKeyAlgorithms(false, algorithms) } - override fun setPreferredSymmetricKeyAlgorithms(isCritical: Boolean, algorithms: Collection): SignatureSubpackets = apply { - setPreferredSymmetricKeyAlgorithms(PreferredAlgorithms( + override fun setPreferredSymmetricKeyAlgorithms( + isCritical: Boolean, + algorithms: Collection + ): SignatureSubpackets = apply { + setPreferredSymmetricKeyAlgorithms( + PreferredAlgorithms( SignatureSubpacketTags.PREFERRED_SYM_ALGS, isCritical, - algorithms.map { it.algorithmId }.toIntArray() - )) + algorithms.map { it.algorithmId }.toIntArray())) } - override fun setPreferredSymmetricKeyAlgorithms(algorithms: PreferredAlgorithms?): SignatureSubpackets = apply { - require(algorithms == null || algorithms.type == SignatureSubpacketTags.PREFERRED_SYM_ALGS) { - "Invalid preferred symmetric algorithms type." - } + override fun setPreferredSymmetricKeyAlgorithms( + algorithms: PreferredAlgorithms? + ): SignatureSubpackets = apply { + require( + algorithms == null || algorithms.type == SignatureSubpacketTags.PREFERRED_SYM_ALGS) { + "Invalid preferred symmetric algorithms type." + } this.preferredSymmetricKeyAlgorithmsSubpacket = algorithms } - override fun setPreferredHashAlgorithms(vararg algorithms: HashAlgorithm): SignatureSubpackets = apply { - setPreferredHashAlgorithms(setOf(*algorithms)) - } + override fun setPreferredHashAlgorithms(vararg algorithms: HashAlgorithm): SignatureSubpackets = + apply { + setPreferredHashAlgorithms(setOf(*algorithms)) + } - override fun setPreferredHashAlgorithms(algorithms: Collection): SignatureSubpackets = apply { - setPreferredHashAlgorithms(false, algorithms) - } + override fun setPreferredHashAlgorithms( + algorithms: Collection + ): SignatureSubpackets = apply { setPreferredHashAlgorithms(false, algorithms) } - override fun setPreferredHashAlgorithms(isCritical: Boolean, algorithms: Collection): SignatureSubpackets = apply { - setPreferredHashAlgorithms(PreferredAlgorithms( + override fun setPreferredHashAlgorithms( + isCritical: Boolean, + algorithms: Collection + ): SignatureSubpackets = apply { + setPreferredHashAlgorithms( + PreferredAlgorithms( SignatureSubpacketTags.PREFERRED_HASH_ALGS, isCritical, - algorithms.map { it.algorithmId }.toIntArray() - )) + algorithms.map { it.algorithmId }.toIntArray())) } - override fun setPreferredHashAlgorithms(algorithms: PreferredAlgorithms?): SignatureSubpackets = apply { - require(algorithms == null || algorithms.type == SignatureSubpacketTags.PREFERRED_HASH_ALGS) { - "Invalid preferred hash algorithms type." + override fun setPreferredHashAlgorithms(algorithms: PreferredAlgorithms?): SignatureSubpackets = + apply { + require( + algorithms == null || + algorithms.type == SignatureSubpacketTags.PREFERRED_HASH_ALGS) { + "Invalid preferred hash algorithms type." + } + this.preferredHashAlgorithmsSubpacket = algorithms } - this.preferredHashAlgorithmsSubpacket = algorithms - } override fun addRevocationKey(revocationKey: PGPPublicKey): SignatureSubpackets = apply { addRevocationKey(true, revocationKey) } - override fun addRevocationKey(isCritical: Boolean, revocationKey: PGPPublicKey): SignatureSubpackets = apply { - addRevocationKey(isCritical, false, revocationKey) - } + override fun addRevocationKey( + isCritical: Boolean, + revocationKey: PGPPublicKey + ): SignatureSubpackets = apply { addRevocationKey(isCritical, false, revocationKey) } - override fun addRevocationKey(isCritical: Boolean, isSensitive: Boolean, revocationKey: PGPPublicKey): SignatureSubpackets = apply { + override fun addRevocationKey( + isCritical: Boolean, + isSensitive: Boolean, + revocationKey: PGPPublicKey + ): SignatureSubpackets = apply { val clazz = 0x80.toByte() or if (isSensitive) 0x40.toByte() else 0x00.toByte() - addRevocationKey(RevocationKey(isCritical, clazz, revocationKey.algorithm, revocationKey.fingerprint)) + addRevocationKey( + RevocationKey(isCritical, clazz, revocationKey.algorithm, revocationKey.fingerprint)) } override fun addRevocationKey(revocationKey: RevocationKey): SignatureSubpackets = apply { @@ -246,9 +298,10 @@ class SignatureSubpackets setFeatures(true, *features) } - override fun setFeatures(isCritical: Boolean, vararg features: Feature): SignatureSubpackets = apply { - setFeatures(Features(isCritical, Feature.toBitmask(*features))) - } + override fun setFeatures(isCritical: Boolean, vararg features: Feature): SignatureSubpackets = + apply { + setFeatures(Features(isCritical, Feature.toBitmask(*features))) + } override fun setFeatures(features: Features?): SignatureSubpackets = apply { this.featuresSubpacket = features @@ -271,7 +324,10 @@ class SignatureSubpackets this.issuerKeyIdSubpacket = issuerKeyID } - override fun setIssuerFingerprint(isCritical: Boolean, issuer: PGPPublicKey): SignatureSubpackets = apply { + override fun setIssuerFingerprint( + isCritical: Boolean, + issuer: PGPPublicKey + ): SignatureSubpackets = apply { setIssuerFingerprint(IssuerFingerprint(isCritical, issuer.version, issuer.fingerprint)) } @@ -279,44 +335,60 @@ class SignatureSubpackets setIssuerFingerprint(false, issuer) } - override fun setIssuerFingerprint(fingerprint: IssuerFingerprint?): SignatureSubpackets = apply { - this.issuerFingerprintSubpacket = fingerprint - } + override fun setIssuerFingerprint(fingerprint: IssuerFingerprint?): SignatureSubpackets = + apply { + this.issuerFingerprintSubpacket = fingerprint + } override fun setSignatureCreationTime(creationTime: Date): SignatureSubpackets = apply { setSignatureCreationTime(true, creationTime) } - override fun setSignatureCreationTime(isCritical: Boolean, creationTime: Date): SignatureSubpackets = apply { + override fun setSignatureCreationTime( + isCritical: Boolean, + creationTime: Date + ): SignatureSubpackets = apply { setSignatureCreationTime(SignatureCreationTime(isCritical, creationTime)) } - override fun setSignatureCreationTime(creationTime: SignatureCreationTime?): SignatureSubpackets = apply { - this.signatureCreationTimeSubpacket = creationTime - } - override fun setSignatureExpirationTime(creationTime: Date, expirationTime: Date?): SignatureSubpackets = apply { + override fun setSignatureCreationTime( + creationTime: SignatureCreationTime? + ): SignatureSubpackets = apply { this.signatureCreationTimeSubpacket = creationTime } + + override fun setSignatureExpirationTime( + creationTime: Date, + expirationTime: Date? + ): SignatureSubpackets = apply { setSignatureExpirationTime(true, creationTime, expirationTime) } - override fun setSignatureExpirationTime(isCritical: Boolean, creationTime: Date, expirationTime: Date?): SignatureSubpackets = apply { + override fun setSignatureExpirationTime( + isCritical: Boolean, + creationTime: Date, + expirationTime: Date? + ): SignatureSubpackets = apply { if (expirationTime != null) { require(creationTime.toSecondsPrecision() < expirationTime.toSecondsPrecision()) { "Expiration time MUST NOT be less or equal the creation time." } - setSignatureExpirationTime(SignatureExpirationTime(isCritical, creationTime.secondsTill(expirationTime))) + setSignatureExpirationTime( + SignatureExpirationTime(isCritical, creationTime.secondsTill(expirationTime))) } else { setSignatureExpirationTime(SignatureExpirationTime(isCritical, 0)) } } - override fun setSignatureExpirationTime(isCritical: Boolean, seconds: Long): SignatureSubpackets = apply { + override fun setSignatureExpirationTime( + isCritical: Boolean, + seconds: Long + ): SignatureSubpackets = apply { enforceExpirationBounds(seconds) setSignatureExpirationTime(SignatureExpirationTime(isCritical, seconds)) } /** - * Enforce that
seconds
is within bounds of an unsigned 32bit number. - * Values less than 0 are illegal, as well as values greater 0xffffffff. + * Enforce that
seconds
is within bounds of an unsigned 32bit number. Values less + * than 0 are illegal, as well as values greater 0xffffffff. * * @param seconds number to check * @throws IllegalArgumentException in case of an under- or overflow @@ -325,32 +397,40 @@ class SignatureSubpackets require(seconds <= 0xffffffffL) { "Integer overflow. Seconds from creation to expiration (${seconds}) cannot be larger than ${0xffffffffL}." } - require(seconds >= 0) { - "Seconds from creation to expiration cannot be less than 0." - } + require(seconds >= 0) { "Seconds from creation to expiration cannot be less than 0." } } - override fun setSignatureExpirationTime(expirationTime: SignatureExpirationTime?): SignatureSubpackets = apply { - this.signatureExpirationTimeSubpacket = expirationTime - } + override fun setSignatureExpirationTime( + expirationTime: SignatureExpirationTime? + ): SignatureSubpackets = apply { this.signatureExpirationTimeSubpacket = expirationTime } override fun setSignerUserId(userId: CharSequence): SignatureSubpackets = apply { setSignerUserId(false, userId) } - override fun setSignerUserId(isCritical: Boolean, userId: CharSequence): SignatureSubpackets = apply { - setSignerUserId(SignerUserID(isCritical, userId.toString())) - } + override fun setSignerUserId(isCritical: Boolean, userId: CharSequence): SignatureSubpackets = + apply { + setSignerUserId(SignerUserID(isCritical, userId.toString())) + } override fun setSignerUserId(signerUserID: SignerUserID?): SignatureSubpackets = apply { this.signerUserIdSubpacket = signerUserID } - override fun addNotationData(isCritical: Boolean, notationName: String, notationValue: String): SignatureSubpackets = apply { + override fun addNotationData( + isCritical: Boolean, + notationName: String, + notationValue: String + ): SignatureSubpackets = apply { addNotationData(isCritical, true, notationName, notationValue) } - override fun addNotationData(isCritical: Boolean, isHumanReadable: Boolean, notationName: String, notationValue: String): SignatureSubpackets = apply { + override fun addNotationData( + isCritical: Boolean, + isHumanReadable: Boolean, + notationName: String, + notationValue: String + ): SignatureSubpackets = apply { addNotationData(NotationData(isCritical, isHumanReadable, notationName, notationValue)) } @@ -362,15 +442,23 @@ class SignatureSubpackets (this.notationDataSubpackets as MutableList).clear() } - override fun addIntendedRecipientFingerprint(recipientKey: PGPPublicKey): SignatureSubpackets = apply { - addIntendedRecipientFingerprint(false, recipientKey) + override fun addIntendedRecipientFingerprint(recipientKey: PGPPublicKey): SignatureSubpackets = + apply { + addIntendedRecipientFingerprint(false, recipientKey) + } + + override fun addIntendedRecipientFingerprint( + isCritical: Boolean, + recipientKey: PGPPublicKey + ): SignatureSubpackets = apply { + addIntendedRecipientFingerprint( + IntendedRecipientFingerprint( + isCritical, recipientKey.version, recipientKey.fingerprint)) } - override fun addIntendedRecipientFingerprint(isCritical: Boolean, recipientKey: PGPPublicKey): SignatureSubpackets = apply { - addIntendedRecipientFingerprint(IntendedRecipientFingerprint(isCritical, recipientKey.version, recipientKey.fingerprint)) - } - - override fun addIntendedRecipientFingerprint(intendedRecipient: IntendedRecipientFingerprint): SignatureSubpackets = apply { + override fun addIntendedRecipientFingerprint( + intendedRecipient: IntendedRecipientFingerprint + ): SignatureSubpackets = apply { (this.intendedRecipientFingerprintSubpackets as MutableList).add(intendedRecipient) } @@ -378,17 +466,16 @@ class SignatureSubpackets (this.intendedRecipientFingerprintSubpackets as MutableList).clear() } - override fun setExportable(): SignatureSubpackets = apply { - setExportable(true) - } + override fun setExportable(): SignatureSubpackets = apply { setExportable(true) } override fun setExportable(isExportable: Boolean): SignatureSubpackets = apply { setExportable(true, isExportable) } - override fun setExportable(isCritical: Boolean, isExportable: Boolean): SignatureSubpackets = apply { - setExportable(Exportable(isCritical, isExportable)) - } + override fun setExportable(isCritical: Boolean, isExportable: Boolean): SignatureSubpackets = + apply { + setExportable(Exportable(isCritical, isExportable)) + } override fun setExportable(exportable: Exportable?): SignatureSubpackets = apply { this.exportableSubpacket = exportable @@ -410,7 +497,10 @@ class SignatureSubpackets setRegularExpression(false, regex) } - override fun setRegularExpression(isCritical: Boolean, regex: CharSequence): SignatureSubpackets = apply { + override fun setRegularExpression( + isCritical: Boolean, + regex: CharSequence + ): SignatureSubpackets = apply { setRegularExpression(RegularExpression(isCritical, regex.toString())) } @@ -418,41 +508,53 @@ class SignatureSubpackets this.regularExpressionSubpacket = regex } - override fun setRevocable(): SignatureSubpackets = apply { - setRevocable(true) - } + override fun setRevocable(): SignatureSubpackets = apply { setRevocable(true) } override fun setRevocable(isRevocable: Boolean): SignatureSubpackets = apply { setRevocable(true, isRevocable) } - override fun setRevocable(isCritical: Boolean, isRevocable: Boolean): SignatureSubpackets = apply { - setRevocable(Revocable(isCritical, isRevocable)) - } + override fun setRevocable(isCritical: Boolean, isRevocable: Boolean): SignatureSubpackets = + apply { + setRevocable(Revocable(isCritical, isRevocable)) + } override fun setRevocable(revocable: Revocable?): SignatureSubpackets = apply { this.revocableSubpacket = revocable } - override fun setSignatureTarget(keyAlgorithm: PublicKeyAlgorithm, hashAlgorithm: HashAlgorithm, hashData: ByteArray): SignatureSubpackets = apply { + override fun setSignatureTarget( + keyAlgorithm: PublicKeyAlgorithm, + hashAlgorithm: HashAlgorithm, + hashData: ByteArray + ): SignatureSubpackets = apply { setSignatureTarget(true, keyAlgorithm, hashAlgorithm, hashData) } - override fun setSignatureTarget(isCritical: Boolean, keyAlgorithm: PublicKeyAlgorithm, hashAlgorithm: HashAlgorithm, hashData: ByteArray): SignatureSubpackets = apply { - setSignatureTarget(SignatureTarget(isCritical, keyAlgorithm.algorithmId, hashAlgorithm.algorithmId, hashData)) + override fun setSignatureTarget( + isCritical: Boolean, + keyAlgorithm: PublicKeyAlgorithm, + hashAlgorithm: HashAlgorithm, + hashData: ByteArray + ): SignatureSubpackets = apply { + setSignatureTarget( + SignatureTarget( + isCritical, keyAlgorithm.algorithmId, hashAlgorithm.algorithmId, hashData)) } - override fun setSignatureTarget(signatureTarget: SignatureTarget?): SignatureSubpackets = apply { - this.signatureTargetSubpacket = signatureTarget - } + override fun setSignatureTarget(signatureTarget: SignatureTarget?): SignatureSubpackets = + apply { + this.signatureTargetSubpacket = signatureTarget + } override fun setTrust(depth: Int, amount: Int): SignatureSubpackets = apply { setTrust(true, depth, amount) } - override fun setTrust(isCritical: Boolean, depth: Int, amount: Int): SignatureSubpackets = apply { - setTrust(TrustSignature(isCritical, depth, amount)) - } + override fun setTrust(isCritical: Boolean, depth: Int, amount: Int): SignatureSubpackets = + apply { + setTrust(TrustSignature(isCritical, depth, amount)) + } override fun setTrust(trust: TrustSignature?): SignatureSubpackets = apply { this.trustSubpacket = trust @@ -462,26 +564,31 @@ class SignatureSubpackets addEmbeddedSignature(true, signature) } - override fun addEmbeddedSignature(isCritical: Boolean, signature: PGPSignature): SignatureSubpackets = apply { + override fun addEmbeddedSignature( + isCritical: Boolean, + signature: PGPSignature + ): SignatureSubpackets = apply { val sig = signature.encoded - val data = if (sig.size - 1 > 256) { - ByteArray(sig.size - 3) - } else { - ByteArray(sig.size - 2) - } + val data = + if (sig.size - 1 > 256) { + ByteArray(sig.size - 3) + } else { + ByteArray(sig.size - 2) + } System.arraycopy(sig, sig.size - data.size, data, 0, data.size) addEmbeddedSignature(EmbeddedSignature(isCritical, false, data)) } - override fun addEmbeddedSignature(embeddedSignature: EmbeddedSignature): SignatureSubpackets = apply { - (this.embeddedSignatureSubpackets as MutableList).add(embeddedSignature) - } + override fun addEmbeddedSignature(embeddedSignature: EmbeddedSignature): SignatureSubpackets = + apply { + (this.embeddedSignatureSubpackets as MutableList).add(embeddedSignature) + } override fun clearEmbeddedSignatures(): SignatureSubpackets = apply { (this.embeddedSignatureSubpackets as MutableList).clear() } - fun addResidualSubpacket(subpacket: org.bouncycastle.bcpg.SignatureSubpacket): SignatureSubpackets = apply { - (residualSubpackets as MutableList).add(subpacket) - } -} \ No newline at end of file + fun addResidualSubpacket( + subpacket: org.bouncycastle.bcpg.SignatureSubpacket + ): SignatureSubpackets = apply { (residualSubpackets as MutableList).add(subpacket) } +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/SignatureSubpacketsHelper.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/SignatureSubpacketsHelper.kt index 6767b0af..6c39432e 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/SignatureSubpacketsHelper.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/SignatureSubpacketsHelper.kt @@ -14,87 +14,135 @@ class SignatureSubpacketsHelper { companion object { @JvmStatic - fun applyFrom(vector: PGPSignatureSubpacketVector, subpackets: SignatureSubpackets) = subpackets.apply { - for (subpacket in vector.toArray()) { - val type = SignatureSubpacket.requireFromCode(subpacket.type) - when (type) { - SignatureSubpacket.signatureCreationTime, - SignatureSubpacket.issuerKeyId, - SignatureSubpacket.issuerFingerprint -> { /* ignore, we override this anyway */ } - SignatureSubpacket.signatureExpirationTime -> (subpacket as SignatureExpirationTime).let { - subpackets.setSignatureExpirationTime(it.isCritical, it.time) - } - SignatureSubpacket.exportableCertification -> (subpacket as Exportable).let { - subpackets.setExportable(it.isCritical, it.isExportable) - } - SignatureSubpacket.trustSignature -> (subpacket as TrustSignature).let { - subpackets.setTrust(it.isCritical, it.depth, it.trustAmount) - } - SignatureSubpacket.revocable -> (subpacket as Revocable).let { - subpackets.setRevocable(it.isCritical, it.isRevocable) - } - SignatureSubpacket.keyExpirationTime -> (subpacket as KeyExpirationTime).let { - subpackets.setKeyExpirationTime(it.isCritical, it.time) - } - SignatureSubpacket.preferredSymmetricAlgorithms -> (subpacket as PreferredAlgorithms).let { - subpackets.setPreferredSymmetricKeyAlgorithms(PreferredAlgorithms(it.type, it.isCritical, it.isLongLength, it.data)) - } - SignatureSubpacket.preferredHashAlgorithms -> (subpacket as PreferredAlgorithms).let { - subpackets.setPreferredHashAlgorithms(PreferredAlgorithms(it.type, it.isCritical, it.isLongLength, it.data)) - } - SignatureSubpacket.preferredCompressionAlgorithms -> (subpacket as PreferredAlgorithms).let { - subpackets.setPreferredCompressionAlgorithms(PreferredAlgorithms(it.type, it.isCritical, it.isLongLength, it.data)) - } - SignatureSubpacket.revocationKey -> (subpacket as RevocationKey).let { - subpackets.addRevocationKey(RevocationKey(it.isCritical, it.signatureClass, it.algorithm, it.fingerprint)) - } - SignatureSubpacket.notationData -> (subpacket as NotationData).let { - subpackets.addNotationData(it.isCritical, it.isHumanReadable, it.notationName, it.notationValue) - } - SignatureSubpacket.primaryUserId -> (subpacket as PrimaryUserID).let { - subpackets.setPrimaryUserId(PrimaryUserID(it.isCritical, it.isPrimaryUserID)) - } - SignatureSubpacket.keyFlags -> (subpacket as KeyFlags).let { - subpackets.setKeyFlags(it.isCritical, *(KeyFlag.fromBitmask(it.flags).toTypedArray())) - } - SignatureSubpacket.signerUserId -> (subpacket as SignerUserID).let { - subpackets.setSignerUserId(it.isCritical, it.id) - } - SignatureSubpacket.revocationReason -> (subpacket as RevocationReason).let { - subpackets.setRevocationReason(it.isCritical, RevocationAttributes.Reason.fromCode(it.revocationReason), it.revocationDescription) - } - SignatureSubpacket.features -> (subpacket as Features).let { - subpackets.setFeatures(it.isCritical, *(Feature.fromBitmask(it.features.toInt()).toTypedArray())) - } - SignatureSubpacket.signatureTarget -> (subpacket as SignatureTarget).let { - subpackets.setSignatureTarget(it.isCritical, - PublicKeyAlgorithm.requireFromId(it.publicKeyAlgorithm), - HashAlgorithm.requireFromId(it.hashAlgorithm), - it.hashData) - } - SignatureSubpacket.embeddedSignature -> (subpacket as EmbeddedSignature).let { - subpackets.addEmbeddedSignature(it) - } - SignatureSubpacket.intendedRecipientFingerprint -> (subpacket as IntendedRecipientFingerprint).let { - subpackets.addIntendedRecipientFingerprint(it) - } - SignatureSubpacket.policyUrl -> (subpacket as PolicyURI).let { - subpackets.setPolicyUrl(it) - } - SignatureSubpacket.regularExpression -> (subpacket as RegularExpression).let { - subpackets.setRegularExpression(it) - } - SignatureSubpacket.keyServerPreferences, + fun applyFrom(vector: PGPSignatureSubpacketVector, subpackets: SignatureSubpackets) = + subpackets.apply { + for (subpacket in vector.toArray()) { + val type = SignatureSubpacket.requireFromCode(subpacket.type) + when (type) { + SignatureSubpacket.signatureCreationTime, + SignatureSubpacket.issuerKeyId, + SignatureSubpacket.issuerFingerprint -> { + /* ignore, we override this anyway */ + } + SignatureSubpacket.signatureExpirationTime -> + (subpacket as SignatureExpirationTime).let { + subpackets.setSignatureExpirationTime(it.isCritical, it.time) + } + SignatureSubpacket.exportableCertification -> + (subpacket as Exportable).let { + subpackets.setExportable(it.isCritical, it.isExportable) + } + SignatureSubpacket.trustSignature -> + (subpacket as TrustSignature).let { + subpackets.setTrust(it.isCritical, it.depth, it.trustAmount) + } + SignatureSubpacket.revocable -> + (subpacket as Revocable).let { + subpackets.setRevocable(it.isCritical, it.isRevocable) + } + SignatureSubpacket.keyExpirationTime -> + (subpacket as KeyExpirationTime).let { + subpackets.setKeyExpirationTime(it.isCritical, it.time) + } + SignatureSubpacket.preferredSymmetricAlgorithms -> + (subpacket as PreferredAlgorithms).let { + subpackets.setPreferredSymmetricKeyAlgorithms( + PreferredAlgorithms( + it.type, it.isCritical, it.isLongLength, it.data)) + } + SignatureSubpacket.preferredHashAlgorithms -> + (subpacket as PreferredAlgorithms).let { + subpackets.setPreferredHashAlgorithms( + PreferredAlgorithms( + it.type, it.isCritical, it.isLongLength, it.data)) + } + SignatureSubpacket.preferredCompressionAlgorithms -> + (subpacket as PreferredAlgorithms).let { + subpackets.setPreferredCompressionAlgorithms( + PreferredAlgorithms( + it.type, it.isCritical, it.isLongLength, it.data)) + } + SignatureSubpacket.revocationKey -> + (subpacket as RevocationKey).let { + subpackets.addRevocationKey( + RevocationKey( + it.isCritical, + it.signatureClass, + it.algorithm, + it.fingerprint)) + } + SignatureSubpacket.notationData -> + (subpacket as NotationData).let { + subpackets.addNotationData( + it.isCritical, + it.isHumanReadable, + it.notationName, + it.notationValue) + } + SignatureSubpacket.primaryUserId -> + (subpacket as PrimaryUserID).let { + subpackets.setPrimaryUserId( + PrimaryUserID(it.isCritical, it.isPrimaryUserID)) + } + SignatureSubpacket.keyFlags -> + (subpacket as KeyFlags).let { + subpackets.setKeyFlags( + it.isCritical, *(KeyFlag.fromBitmask(it.flags).toTypedArray())) + } + SignatureSubpacket.signerUserId -> + (subpacket as SignerUserID).let { + subpackets.setSignerUserId(it.isCritical, it.id) + } + SignatureSubpacket.revocationReason -> + (subpacket as RevocationReason).let { + subpackets.setRevocationReason( + it.isCritical, + RevocationAttributes.Reason.fromCode(it.revocationReason), + it.revocationDescription) + } + SignatureSubpacket.features -> + (subpacket as Features).let { + subpackets.setFeatures( + it.isCritical, + *(Feature.fromBitmask(it.features.toInt()).toTypedArray())) + } + SignatureSubpacket.signatureTarget -> + (subpacket as SignatureTarget).let { + subpackets.setSignatureTarget( + it.isCritical, + PublicKeyAlgorithm.requireFromId(it.publicKeyAlgorithm), + HashAlgorithm.requireFromId(it.hashAlgorithm), + it.hashData) + } + SignatureSubpacket.embeddedSignature -> + (subpacket as EmbeddedSignature).let { + subpackets.addEmbeddedSignature(it) + } + SignatureSubpacket.intendedRecipientFingerprint -> + (subpacket as IntendedRecipientFingerprint).let { + subpackets.addIntendedRecipientFingerprint(it) + } + SignatureSubpacket.policyUrl -> + (subpacket as PolicyURI).let { subpackets.setPolicyUrl(it) } + SignatureSubpacket.regularExpression -> + (subpacket as RegularExpression).let { + subpackets.setRegularExpression(it) + } + SignatureSubpacket.keyServerPreferences, SignatureSubpacket.preferredKeyServers, SignatureSubpacket.placeholder, SignatureSubpacket.preferredAEADAlgorithms, - SignatureSubpacket.attestedCertification -> subpackets.addResidualSubpacket(subpacket) + SignatureSubpacket.attestedCertification -> + subpackets.addResidualSubpacket(subpacket) + } } } - } @JvmStatic - fun applyTo(subpackets: SignatureSubpackets, generator: PGPSignatureSubpacketGenerator): PGPSignatureSubpacketGenerator { + fun applyTo( + subpackets: SignatureSubpackets, + generator: PGPSignatureSubpacketGenerator + ): PGPSignatureSubpacketGenerator { return generator.apply { addSubpacket(subpackets.issuerKeyIdSubpacket) addSubpacket(subpackets.issuerFingerprintSubpacket) @@ -134,7 +182,9 @@ class SignatureSubpacketsHelper { } @JvmStatic - private fun PGPSignatureSubpacketGenerator.addSubpacket(subpacket: org.bouncycastle.bcpg.SignatureSubpacket?) { + private fun PGPSignatureSubpacketGenerator.addSubpacket( + subpacket: org.bouncycastle.bcpg.SignatureSubpacket? + ) { if (subpacket != null) { this.addCustomSubpacket(subpacket) } @@ -156,4 +206,4 @@ class SignatureSubpacketsHelper { } } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/SignatureSubpacketsUtil.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/SignatureSubpacketsUtil.kt index 2cf79d72..31e1f53c 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/SignatureSubpacketsUtil.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/signature/subpackets/SignatureSubpacketsUtil.kt @@ -4,6 +4,7 @@ package org.pgpainless.signature.subpackets +import java.util.* import openpgp.openPgpKeyId import openpgp.plusSeconds import org.bouncycastle.bcpg.sig.* @@ -19,27 +20,26 @@ import org.pgpainless.key.OpenPgpV4Fingerprint import org.pgpainless.key.OpenPgpV5Fingerprint import org.pgpainless.key.OpenPgpV6Fingerprint import org.pgpainless.key.generation.type.KeyType -import org.pgpainless.signature.SignatureUtils -import java.util.* class SignatureSubpacketsUtil { companion object { /** - * Return the issuer-fingerprint subpacket of the signature. - * Since this packet is self-authenticating, we expect it to be in the unhashed area, - * however as it cannot hurt we search for it in the hashed area first. + * Return the issuer-fingerprint subpacket of the signature. Since this packet is + * self-authenticating, we expect it to be in the unhashed area, however as it cannot hurt + * we search for it in the hashed area first. * * @param signature signature * @return issuer fingerprint or null */ @JvmStatic fun getIssuerFingerprint(signature: PGPSignature): IssuerFingerprint? = - hashedOrUnhashed(signature, SignatureSubpacket.issuerFingerprint) + hashedOrUnhashed(signature, SignatureSubpacket.issuerFingerprint) /** - * Return the [IssuerFingerprint] subpacket of the signature into a [org.pgpainless.key.OpenPgpFingerprint]. - * If no v4, v5 or v6 issuer fingerprint is present in the signature, return null. + * Return the [IssuerFingerprint] subpacket of the signature into a + * [org.pgpainless.key.OpenPgpFingerprint]. If no v4, v5 or v6 issuer fingerprint is present + * in the signature, return null. * * @param signature signature * @return fingerprint of the issuer, or null @@ -47,7 +47,7 @@ class SignatureSubpacketsUtil { @JvmStatic fun getIssuerFingerprintAsOpenPgpFingerprint(signature: PGPSignature): OpenPgpFingerprint? { val subpacket = getIssuerFingerprint(signature) ?: return null - return when(subpacket.keyVersion) { + return when (subpacket.keyVersion) { 4 -> OpenPgpV4Fingerprint(subpacket.fingerprint) 5 -> OpenPgpV5Fingerprint(subpacket.fingerprint) 6 -> OpenPgpV6Fingerprint(subpacket.fingerprint) @@ -57,85 +57,83 @@ class SignatureSubpacketsUtil { @JvmStatic fun getIssuerKeyId(signature: PGPSignature): IssuerKeyID? = - hashedOrUnhashed(signature, SignatureSubpacket.issuerKeyId) + hashedOrUnhashed(signature, SignatureSubpacket.issuerKeyId) /** - * Inspect the given signature's [IssuerKeyID] packet to determine the issuer key-id. - * If no such packet is present, return null. + * Inspect the given signature's [IssuerKeyID] packet to determine the issuer key-id. If no + * such packet is present, return null. * * @param signature signature * @return issuer key-id as {@link Long} */ @JvmStatic - fun getIssuerKeyIdAsLong(signature: PGPSignature): Long? = - getIssuerKeyId(signature)?.keyID + fun getIssuerKeyIdAsLong(signature: PGPSignature): Long? = getIssuerKeyId(signature)?.keyID /** - * Return the revocation reason subpacket of the signature. - * Since this packet is rather important for revocations, we only search for it in the - * hashed area of the signature. + * Return the revocation reason subpacket of the signature. Since this packet is rather + * important for revocations, we only search for it in the hashed area of the signature. * * @param signature signature * @return revocation reason */ @JvmStatic fun getRevocationReason(signature: PGPSignature): RevocationReason? = - hashed(signature, SignatureSubpacket.revocationReason) + hashed(signature, SignatureSubpacket.revocationReason) /** - * Return the signature creation time subpacket. - * Since this packet is rather important, we only search for it in the hashed area - * of the signature. + * Return the signature creation time subpacket. Since this packet is rather important, we + * only search for it in the hashed area of the signature. * * @param signature signature * @return signature creation time subpacket */ @JvmStatic fun getSignatureCreationTime(signature: PGPSignature): SignatureCreationTime? = - if (signature.version == 3) SignatureCreationTime(false, signature.creationTime) - else hashed(signature, SignatureSubpacket.signatureCreationTime) + if (signature.version == 3) SignatureCreationTime(false, signature.creationTime) + else hashed(signature, SignatureSubpacket.signatureCreationTime) /** - * Return the signature expiration time subpacket of the signature. - * Since this packet is rather important, we only search for it in the hashed area of the signature. + * Return the signature expiration time subpacket of the signature. Since this packet is + * rather important, we only search for it in the hashed area of the signature. * * @param signature signature * @return signature expiration time */ @JvmStatic fun getSignatureExpirationTime(signature: PGPSignature): SignatureExpirationTime? = - hashed(signature, SignatureSubpacket.signatureExpirationTime) + hashed(signature, SignatureSubpacket.signatureExpirationTime) /** - * Return the signatures' expiration time as a date. - * The expiration date is computed by adding the expiration time to the signature creation date. - * If the signature has no expiration time subpacket, or the expiration time is set to '0', this message returns null. + * Return the signatures' expiration time as a date. The expiration date is computed by + * adding the expiration time to the signature creation date. If the signature has no + * expiration time subpacket, or the expiration time is set to '0', this message returns + * null. * * @param signature signature * @return expiration time as date */ @JvmStatic fun getSignatureExpirationTimeAsDate(signature: PGPSignature): Date? = - getSignatureExpirationTime(signature)?.let { - signature.creationTime.plusSeconds(it.time) - } + getSignatureExpirationTime(signature)?.let { + signature.creationTime.plusSeconds(it.time) + } /** - * Return the key expiration time subpacket of this signature. - * We only look for it in the hashed area of the signature. + * Return the key expiration time subpacket of this signature. We only look for it in the + * hashed area of the signature. * * @param signature signature * @return key expiration time */ @JvmStatic fun getKeyExpirationTime(signature: PGPSignature): KeyExpirationTime? = - hashed(signature, SignatureSubpacket.keyExpirationTime) + hashed(signature, SignatureSubpacket.keyExpirationTime) /** - * Return the signatures key-expiration time as a date. - * The expiration date is computed by adding the signatures' key-expiration time to the signing keys - * creation date. - * If the signature does not have a key-expiration time subpacket, or its value is '0', this method returns null. + * Return the signatures key-expiration time as a date. The expiration date is computed by + * adding the signatures' key-expiration time to the signing keys creation date. If the + * signature does not have a key-expiration time subpacket, or its value is '0', this method + * returns null. * * @param signature self-signature carrying the key-expiration time subpacket * @param signingKey signature creation key @@ -143,9 +141,10 @@ class SignatureSubpacketsUtil { */ @JvmStatic fun getKeyExpirationTimeAsDate(signature: PGPSignature, signingKey: PGPPublicKey): Date? = - require(signature.keyID == signingKey.keyID) { + require(signature.keyID == signingKey.keyID) { "Provided key (${signingKey.keyID.openPgpKeyId()}) did not create the signature (${signature.keyID.openPgpKeyId()})" - }.run { + } + .run { getKeyExpirationTime(signature)?.let { signingKey.creationTime.plusSeconds(it.time) } @@ -160,25 +159,25 @@ class SignatureSubpacketsUtil { */ @JvmStatic fun getKeyLifetimeInSeconds(creationTime: Date, expirationTime: Date?): Long = - expirationTime?.let { - require(creationTime <= it) { + expirationTime?.let { + require(creationTime <= it) { "Key MUST NOT expire before being created.\n" + - "(creation: $creationTime, expiration: $it)" - }.run { - (it.time - creationTime.time) / 1000 + "(creation: $creationTime, expiration: $it)" } - } ?: 0 // 0 means "no expiration" + .run { (it.time - creationTime.time) / 1000 } + } + ?: 0 // 0 means "no expiration" /** - * Return the revocable subpacket of this signature. - * We only look for it in the hashed area of the signature. + * Return the revocable subpacket of this signature. We only look for it in the hashed area + * of the signature. * * @param signature signature * @return revocable subpacket */ @JvmStatic fun getRevocable(signature: PGPSignature): Revocable? = - hashed(signature, SignatureSubpacket.revocable) + hashed(signature, SignatureSubpacket.revocable) /** * Return the symmetric algorithm preferences from the signatures hashed area. @@ -188,23 +187,28 @@ class SignatureSubpacketsUtil { */ @JvmStatic fun getPreferredSymmetricAlgorithms(signature: PGPSignature): PreferredAlgorithms? = - hashed(signature, SignatureSubpacket.preferredSymmetricAlgorithms) + hashed(signature, SignatureSubpacket.preferredSymmetricAlgorithms) /** - * Return the preferred [SymmetricKeyAlgorithms][SymmetricKeyAlgorithm] as present in the signature. - * If no preference is given with regard to symmetric encryption algorithms, return an empty set. + * Return the preferred [SymmetricKeyAlgorithms][SymmetricKeyAlgorithm] as present in the + * signature. If no preference is given with regard to symmetric encryption algorithms, + * return an empty set. * * In any case, the resulting set is ordered by occurrence. + * * @param signature signature * @return ordered set of symmetric key algorithm preferences */ @JvmStatic - fun parsePreferredSymmetricKeyAlgorithms(signature: PGPSignature): Set = - getPreferredSymmetricAlgorithms(signature) - ?.preferences - ?.map { SymmetricKeyAlgorithm.fromId(it) } - ?.filterNotNull() - ?.toSet() ?: setOf() + fun parsePreferredSymmetricKeyAlgorithms( + signature: PGPSignature + ): Set = + getPreferredSymmetricAlgorithms(signature) + ?.preferences + ?.map { SymmetricKeyAlgorithm.fromId(it) } + ?.filterNotNull() + ?.toSet() + ?: setOf() /** * Return the hash algorithm preferences from the signatures hashed area. @@ -214,23 +218,25 @@ class SignatureSubpacketsUtil { */ @JvmStatic fun getPreferredHashAlgorithms(signature: PGPSignature): PreferredAlgorithms? = - hashed(signature, SignatureSubpacket.preferredHashAlgorithms) + hashed(signature, SignatureSubpacket.preferredHashAlgorithms) /** - * Return the preferred [HashAlgorithms][HashAlgorithm] as present in the signature. - * If no preference is given with regard to hash algorithms, return an empty set. + * Return the preferred [HashAlgorithms][HashAlgorithm] as present in the signature. If no + * preference is given with regard to hash algorithms, return an empty set. * * In any case, the resulting set is ordered by occurrence. + * * @param signature signature * @return ordered set of hash algorithm preferences */ @JvmStatic fun parsePreferredHashAlgorithms(signature: PGPSignature): Set = - getPreferredHashAlgorithms(signature) - ?.preferences - ?.map { HashAlgorithm.fromId(it) } - ?.filterNotNull() - ?.toSet() ?: setOf() + getPreferredHashAlgorithms(signature) + ?.preferences + ?.map { HashAlgorithm.fromId(it) } + ?.filterNotNull() + ?.toSet() + ?: setOf() /** * Return the compression algorithm preferences from the signatures hashed area. @@ -240,27 +246,32 @@ class SignatureSubpacketsUtil { */ @JvmStatic fun getPreferredCompressionAlgorithms(signature: PGPSignature): PreferredAlgorithms? = - hashed(signature, SignatureSubpacket.preferredCompressionAlgorithms) + hashed(signature, SignatureSubpacket.preferredCompressionAlgorithms) /** - * Return the preferred [CompressionAlgorithms][CompressionAlgorithm] as present in the signature. - * If no preference is given with regard to compression algorithms, return an empty set. + * Return the preferred [CompressionAlgorithms][CompressionAlgorithm] as present in the + * signature. If no preference is given with regard to compression algorithms, return an + * empty set. * * In any case, the resulting set is ordered by occurrence. + * * @param signature signature * @return ordered set of compression algorithm preferences */ @JvmStatic - fun parsePreferredCompressionAlgorithms(signature: PGPSignature): Set = - getPreferredCompressionAlgorithms(signature) - ?.preferences - ?.map { CompressionAlgorithm.fromId(it) } - ?.filterNotNull() - ?.toSet() ?: setOf() + fun parsePreferredCompressionAlgorithms( + signature: PGPSignature + ): Set = + getPreferredCompressionAlgorithms(signature) + ?.preferences + ?.map { CompressionAlgorithm.fromId(it) } + ?.filterNotNull() + ?.toSet() + ?: setOf() @JvmStatic fun getPreferredAeadAlgorithms(signature: PGPSignature): PreferredAEADCiphersuites? = - hashed(signature, SignatureSubpacket.preferredAEADAlgorithms) + hashed(signature, SignatureSubpacket.preferredAEADAlgorithms) /** * Return the primary user-id subpacket from the signatures hashed area. @@ -270,7 +281,7 @@ class SignatureSubpacketsUtil { */ @JvmStatic fun getPrimaryUserId(signature: PGPSignature): PrimaryUserID? = - hashed(signature, SignatureSubpacket.primaryUserId) + hashed(signature, SignatureSubpacket.primaryUserId) /** * Return the key flags subpacket from the signatures hashed area. @@ -280,22 +291,18 @@ class SignatureSubpacketsUtil { */ @JvmStatic fun getKeyFlags(signature: PGPSignature): KeyFlags? = - hashed(signature, SignatureSubpacket.keyFlags) + hashed(signature, SignatureSubpacket.keyFlags) /** - * Return a list of key flags carried by the signature. - * If the signature is null, or has no [KeyFlags] subpacket, return null. + * Return a list of key flags carried by the signature. If the signature is null, or has no + * [KeyFlags] subpacket, return null. * * @param signature signature * @return list of key flags */ @JvmStatic fun parseKeyFlags(signature: PGPSignature?): List? = - signature?.let { sig -> - getKeyFlags(sig)?.let { - KeyFlag.fromBitmask(it.flags) - } - } + signature?.let { sig -> getKeyFlags(sig)?.let { KeyFlag.fromBitmask(it.flags) } } /** * Return the features subpacket from the signatures hashed area. @@ -305,32 +312,29 @@ class SignatureSubpacketsUtil { */ @JvmStatic fun getFeatures(signature: PGPSignature): Features? = - hashed(signature, SignatureSubpacket.features) + hashed(signature, SignatureSubpacket.features) /** - * Parse out the features subpacket of a signature. - * If the signature has no features subpacket, return null. - * Otherwise, return the features as a feature set. + * Parse out the features subpacket of a signature. If the signature has no features + * subpacket, return null. Otherwise, return the features as a feature set. * * @param signature signature * @return features as set */ @JvmStatic fun parseFeatures(signature: PGPSignature): Set? = - getFeatures(signature)?.let { - Feature.fromBitmask(it.features.toInt()).toSet() - } + getFeatures(signature)?.let { Feature.fromBitmask(it.features.toInt()).toSet() } /** - * Return the signature target subpacket from the signature. - * We search for this subpacket in the hashed and unhashed area (in this order). + * Return the signature target subpacket from the signature. We search for this subpacket in + * the hashed and unhashed area (in this order). * * @param signature signature * @return signature target */ @JvmStatic fun getSignatureTarget(signature: PGPSignature): SignatureTarget? = - hashedOrUnhashed(signature, SignatureSubpacket.signatureTarget) + hashedOrUnhashed(signature, SignatureSubpacket.signatureTarget) /** * Return the notation data subpackets from the signatures hashed area. @@ -340,20 +344,22 @@ class SignatureSubpacketsUtil { */ @JvmStatic fun getHashedNotationData(signature: PGPSignature): List = - signature.hashedSubPackets.notationDataOccurrences.toList() + signature.hashedSubPackets.notationDataOccurrences.toList() /** - * Return a list of all [NotationData] objects from the hashed area of the signature that have a - * notation name equal to the given notationName argument. + * Return a list of all [NotationData] objects from the hashed area of the signature that + * have a notation name equal to the given notationName argument. * * @param signature signature * @param notationName notation name * @return list of matching notation data objects */ @JvmStatic - fun getHashedNotationData(signature: PGPSignature, notationName: String): List = - getHashedNotationData(signature) - .filter { it.notationName == notationName } + fun getHashedNotationData( + signature: PGPSignature, + notationName: String + ): List = + getHashedNotationData(signature).filter { it.notationName == notationName } /** * Return the notation data subpackets from the signatures unhashed area. @@ -363,11 +369,11 @@ class SignatureSubpacketsUtil { */ @JvmStatic fun getUnhashedNotationData(signature: PGPSignature): List = - signature.unhashedSubPackets.notationDataOccurrences.toList() + signature.unhashedSubPackets.notationDataOccurrences.toList() /** - * Return a list of all [NotationData] objects from the unhashed area of the signature that have a - * notation name equal to the given notationName argument. + * Return a list of all [NotationData] objects from the unhashed area of the signature that + * have a notation name equal to the given notationName argument. * * @param signature signature * @param notationName notation name @@ -375,8 +381,7 @@ class SignatureSubpacketsUtil { */ @JvmStatic fun getUnhashedNotationData(signature: PGPSignature, notationName: String) = - getUnhashedNotationData(signature) - .filter { it.notationName == notationName } + getUnhashedNotationData(signature).filter { it.notationName == notationName } /** * Return the revocation key subpacket from the signatures hashed area. @@ -386,28 +391,32 @@ class SignatureSubpacketsUtil { */ @JvmStatic fun getRevocationKey(signature: PGPSignature): RevocationKey? = - hashed(signature, SignatureSubpacket.revocationKey) + hashed(signature, SignatureSubpacket.revocationKey) /** * Return the signers user-id from the hashed area of the signature. - * TODO: Can this subpacket also be found in the unhashed area? * * @param signature signature * @return signers user-id + * + * TODO: Can this subpacket also be found in the unhashed area? */ @JvmStatic fun getSignerUserID(signature: PGPSignature): SignerUserID? = - hashed(signature, SignatureSubpacket.signerUserId) + hashed(signature, SignatureSubpacket.signerUserId) /** - * Return the intended recipients fingerprint subpackets from the hashed area of this signature. + * Return the intended recipients fingerprint subpackets from the hashed area of this + * signature. * * @param signature signature * @return intended recipient fingerprint subpackets */ @JvmStatic - fun getIntendedRecipientFingerprints(signature: PGPSignature): List = - signature.hashedSubPackets.intendedRecipientFingerprints.toList() + fun getIntendedRecipientFingerprints( + signature: PGPSignature + ): List = + signature.hashedSubPackets.intendedRecipientFingerprints.toList() /** * Return the embedded signature subpacket from the signatures hashed area or unhashed area. @@ -417,10 +426,9 @@ class SignatureSubpacketsUtil { */ @JvmStatic fun getEmbeddedSignature(signature: PGPSignature): PGPSignatureList = - signature.hashedSubPackets.embeddedSignatures.let { - if (it.isEmpty) signature.unhashedSubPackets.embeddedSignatures - else it - } + signature.hashedSubPackets.embeddedSignatures.let { + if (it.isEmpty) signature.unhashedSubPackets.embeddedSignatures else it + } /** * Return the signatures exportable certification subpacket from the hashed area. @@ -430,14 +438,12 @@ class SignatureSubpacketsUtil { */ @JvmStatic fun getExportableCertification(signature: PGPSignature): Exportable? = - hashed(signature, SignatureSubpacket.exportableCertification) + hashed(signature, SignatureSubpacket.exportableCertification) - /** - * Return true, if the signature is not explicitly marked as non-exportable. - */ + /** Return true, if the signature is not explicitly marked as non-exportable. */ @JvmStatic fun isExportable(signature: PGPSignature): Boolean = - getExportableCertification(signature)?.isExportable ?: true + getExportableCertification(signature)?.isExportable ?: true /** * Return the trust signature packet from the signatures hashed area. @@ -447,11 +453,11 @@ class SignatureSubpacketsUtil { */ @JvmStatic fun getTrustSignature(signature: PGPSignature): TrustSignature? = - hashed(signature, SignatureSubpacket.trustSignature) + hashed(signature, SignatureSubpacket.trustSignature) /** - * Return the trust depth set in the signatures [TrustSignature] packet, or [defaultDepth] if no such packet - * is found. + * Return the trust depth set in the signatures [TrustSignature] packet, or [defaultDepth] + * if no such packet is found. * * @param signature signature * @param defaultDepth default value that is returned if no trust signature packet is found @@ -459,11 +465,11 @@ class SignatureSubpacketsUtil { */ @JvmStatic fun getTrustDepthOr(signature: PGPSignature, defaultDepth: Int): Int = - getTrustSignature(signature)?.depth ?: defaultDepth + getTrustSignature(signature)?.depth ?: defaultDepth /** - * Return the trust amount set in the signatures [TrustSignature] packet, or [defaultAmount] if no such packet - * is found. + * Return the trust amount set in the signatures [TrustSignature] packet, or [defaultAmount] + * if no such packet is found. * * @param signature signature * @param defaultAmount default value that is returned if no trust signature packet is found @@ -471,7 +477,7 @@ class SignatureSubpacketsUtil { */ @JvmStatic fun getTrustAmountOr(signature: PGPSignature, defaultAmount: Int): Int = - getTrustSignature(signature)?.trustAmount ?: defaultAmount + getTrustSignature(signature)?.trustAmount ?: defaultAmount /** * Return all regular expression subpackets from the hashed area of the given signature. @@ -481,11 +487,11 @@ class SignatureSubpacketsUtil { */ @JvmStatic fun getRegularExpressions(signature: PGPSignature): List = - signature.hashedSubPackets.regularExpressions.toList() + signature.hashedSubPackets.regularExpressions.toList() /** - * Select a list of all signature subpackets of the given type, which are present in either the hashed - * or the unhashed area of the given signature. + * Select a list of all signature subpackets of the given type, which are present in either + * the hashed or the unhashed area of the given signature. * * @param signature signature * @param type subpacket type @@ -493,13 +499,16 @@ class SignatureSubpacketsUtil { * @return list of subpackets from the hashed/unhashed area */ @JvmStatic - fun

hashedOrUnhashed(signature: PGPSignature, type: SignatureSubpacket): P? { + fun

hashedOrUnhashed( + signature: PGPSignature, + type: SignatureSubpacket + ): P? { return hashed(signature, type) ?: unhashed(signature, type) } /** - * Select a list of all signature subpackets of the given type, which are present in the hashed area of - * the given signature. + * Select a list of all signature subpackets of the given type, which are present in the + * hashed area of the given signature. * * @param signature signature * @param type subpacket type @@ -507,13 +516,16 @@ class SignatureSubpacketsUtil { * @return list of subpackets from the hashed area */ @JvmStatic - fun

hashed(signature: PGPSignature, type: SignatureSubpacket): P? { + fun

hashed( + signature: PGPSignature, + type: SignatureSubpacket + ): P? { return getSignatureSubpacket(signature.hashedSubPackets, type) } /** - * Select a list of all signature subpackets of the given type, which are present in the unhashed area of - * the given signature. + * Select a list of all signature subpackets of the given type, which are present in the + * unhashed area of the given signature. * * @param signature signature * @param type subpacket type @@ -521,7 +533,10 @@ class SignatureSubpacketsUtil { * @return list of subpackets from the unhashed area */ @JvmStatic - fun

unhashed(signature: PGPSignature, type: SignatureSubpacket): P? { + fun

unhashed( + signature: PGPSignature, + type: SignatureSubpacket + ): P? { return getSignatureSubpacket(signature.unhashedSubPackets, type) } @@ -534,13 +549,13 @@ class SignatureSubpacketsUtil { * @return last occurrence of the subpacket in the vector */ @JvmStatic - fun

getSignatureSubpacket(vector: PGPSignatureSubpacketVector?, type: SignatureSubpacket): P? { + fun

getSignatureSubpacket( + vector: PGPSignatureSubpacketVector?, + type: SignatureSubpacket + ): P? { val allPackets = vector?.getSubpackets(type.code) ?: return null - return if (allPackets.isEmpty()) - null - else - @Suppress("UNCHECKED_CAST") - allPackets.last() as P + return if (allPackets.isEmpty()) null + else @Suppress("UNCHECKED_CAST") allPackets.last() as P } @JvmStatic @@ -553,24 +568,29 @@ class SignatureSubpacketsUtil { val mask = toBitmask(*flags) if (!algorithm.isSigningCapable() && hasKeyFlag(mask, KeyFlag.CERTIFY_OTHER)) { - throw IllegalArgumentException("Algorithm $algorithm cannot be used with key flag CERTIFY_OTHER.") + throw IllegalArgumentException( + "Algorithm $algorithm cannot be used with key flag CERTIFY_OTHER.") } if (!algorithm.isSigningCapable() && hasKeyFlag(mask, KeyFlag.SIGN_DATA)) { - throw IllegalArgumentException("Algorithm $algorithm cannot be used with key flag SIGN_DATA.") + throw IllegalArgumentException( + "Algorithm $algorithm cannot be used with key flag SIGN_DATA.") } if (!algorithm.isEncryptionCapable() && hasKeyFlag(mask, KeyFlag.ENCRYPT_COMMS)) { - throw IllegalArgumentException("Algorithm $algorithm cannot be used with key flag ENCRYPT_COMMS.") + throw IllegalArgumentException( + "Algorithm $algorithm cannot be used with key flag ENCRYPT_COMMS.") } if (!algorithm.isEncryptionCapable() && hasKeyFlag(mask, KeyFlag.ENCRYPT_STORAGE)) { - throw IllegalArgumentException("Algorithm $algorithm cannot be used with key flag ENCRYPT_STORAGE.") + throw IllegalArgumentException( + "Algorithm $algorithm cannot be used with key flag ENCRYPT_STORAGE.") } if (!algorithm.isSigningCapable() && hasKeyFlag(mask, KeyFlag.AUTHENTICATION)) { - throw IllegalArgumentException("Algorithm $algorithm cannot be used with key flag AUTHENTICATION.") + throw IllegalArgumentException( + "Algorithm $algorithm cannot be used with key flag AUTHENTICATION.") } } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/util/ArmorUtils.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/util/ArmorUtils.kt index 53bdca0b..70146e0f 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/util/ArmorUtils.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/util/ArmorUtils.kt @@ -4,6 +4,10 @@ package org.pgpainless.util +import java.io.ByteArrayOutputStream +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream import org.bouncycastle.bcpg.ArmoredInputStream import org.bouncycastle.bcpg.ArmoredOutputStream import org.bouncycastle.openpgp.PGPKeyRing @@ -20,35 +24,21 @@ import org.pgpainless.algorithm.HashAlgorithm import org.pgpainless.decryption_verification.OpenPgpInputStream import org.pgpainless.key.OpenPgpFingerprint import org.pgpainless.key.util.KeyRingUtils -import java.io.ByteArrayOutputStream -import java.io.IOException -import java.io.InputStream -import java.io.OutputStream class ArmorUtils { companion object { // MessageIDs are 32 printable characters private val PATTER_MESSAGE_ID = "^\\S{32}$".toRegex() - /** - * Constant armor key for comments. - */ + /** Constant armor key for comments. */ const val HEADER_COMMENT = "Comment" - /** - * Constant armor key for program versions. - */ + /** Constant armor key for program versions. */ const val HEADER_VERSION = "Version" - /** - * Constant armor key for message IDs. Useful for split messages. - */ + /** Constant armor key for message IDs. Useful for split messages. */ const val HEADER_MESSAGEID = "MessageID" - /** - * Constant armor key for used hash algorithms in clearsigned messages. - */ + /** Constant armor key for used hash algorithms in clearsigned messages. */ const val HEADER_HASH = "Hash" - /** - * Constant armor key for message character sets. - */ + /** Constant armor key for message character sets. */ const val HEADER_CHARSET = "Charset" /** @@ -56,116 +46,111 @@ class ArmorUtils { * * @param secretKey secret key * @return ASCII armored encoding - * * @throws IOException in case of an io error */ @JvmStatic @Throws(IOException::class) fun toAsciiArmoredString(secretKey: PGPSecretKey): String = - toAsciiArmoredString(secretKey.encoded, keyToHeader(secretKey.publicKey)) + toAsciiArmoredString(secretKey.encoded, keyToHeader(secretKey.publicKey)) /** * Return the ASCII armored encoding of the given [PGPPublicKey]. * * @param publicKey public key * @return ASCII armored encoding - * * @throws IOException in case of an io error */ @JvmStatic @Throws(IOException::class) fun toAsciiArmoredString(publicKey: PGPPublicKey): String = - toAsciiArmoredString(publicKey.encoded, keyToHeader(publicKey)) + toAsciiArmoredString(publicKey.encoded, keyToHeader(publicKey)) /** * Return the ASCII armored encoding of the given [PGPSecretKeyRing]. * * @param secretKeys secret key ring * @return ASCII armored encoding - * * @throws IOException in case of an io error */ @JvmStatic @Throws(IOException::class) fun toAsciiArmoredString(secretKeys: PGPSecretKeyRing): String = - toAsciiArmoredString(secretKeys.encoded, keyToHeader(secretKeys.publicKey)) + toAsciiArmoredString(secretKeys.encoded, keyToHeader(secretKeys.publicKey)) /** * Return the ASCII armored encoding of the given [PGPPublicKeyRing]. * * @param certificate public key ring * @return ASCII armored encoding - * * @throws IOException in case of an io error */ @JvmStatic @Throws(IOException::class) fun toAsciiArmoredString(certificate: PGPPublicKeyRing): String = - toAsciiArmoredString(certificate.encoded, keyToHeader(certificate.publicKey)) + toAsciiArmoredString(certificate.encoded, keyToHeader(certificate.publicKey)) /** - * Return the ASCII armored encoding of the given [PGPSecretKeyRingCollection]. - * The encoding will use per-key ASCII armors protecting each [PGPSecretKeyRing] individually. - * Those armors are then concatenated with newlines in between. + * Return the ASCII armored encoding of the given [PGPSecretKeyRingCollection]. The encoding + * will use per-key ASCII armors protecting each [PGPSecretKeyRing] individually. Those + * armors are then concatenated with newlines in between. * * @param secretKeysCollection secret key ring collection * @return ASCII armored encoding - * * @throws IOException in case of an io error */ @JvmStatic @Throws(IOException::class) fun toAsciiArmoredString(secretKeysCollection: PGPSecretKeyRingCollection): String = - secretKeysCollection.keyRings.asSequence() - .joinToString("\n") { toAsciiArmoredString(it) } + secretKeysCollection.keyRings.asSequence().joinToString("\n") { + toAsciiArmoredString(it) + } /** - * Return the ASCII armored encoding of the given [PGPPublicKeyRingCollection]. - * The encoding will use per-key ASCII armors protecting each [PGPPublicKeyRing] individually. - * Those armors are then concatenated with newlines in between. + * Return the ASCII armored encoding of the given [PGPPublicKeyRingCollection]. The encoding + * will use per-key ASCII armors protecting each [PGPPublicKeyRing] individually. Those + * armors are then concatenated with newlines in between. * * @param certificates public key ring collection * @return ascii armored encoding - * * @throws IOException in case of an io error */ @JvmStatic @Throws(IOException::class) fun toAsciiArmoredString(certificates: PGPPublicKeyRingCollection): String = - certificates.joinToString("\n") { toAsciiArmoredString(it) } + certificates.joinToString("\n") { toAsciiArmoredString(it) } /** - * Return the ASCII armored representation of the given detached signature. - * If [export] is true, the signature will be stripped of non-exportable subpackets or trust-packets. - * If it is false, the signature will be encoded as-is. + * Return the ASCII armored representation of the given detached signature. If [export] is + * true, the signature will be stripped of non-exportable subpackets or trust-packets. If it + * is false, the signature will be encoded as-is. * * @param signature signature * @param export whether to exclude non-exportable subpackets or trust-packets. * @return ascii armored string - * * @throws IOException in case of an error in the [ArmoredOutputStream] */ @JvmStatic @JvmOverloads @Throws(IOException::class) fun toAsciiArmoredString(signature: PGPSignature, export: Boolean = false): String = - toAsciiArmoredString(signature.getEncoded(export)) + toAsciiArmoredString(signature.getEncoded(export)) /** - * Return the ASCII armored encoding of the given OpenPGP data bytes. - * The ASCII armor will include headers from the header map. + * Return the ASCII armored encoding of the given OpenPGP data bytes. The ASCII armor will + * include headers from the header map. * * @param bytes OpenPGP data * @param header header map * @return ASCII armored encoding - * * @throws IOException in case of an io error */ @JvmStatic @JvmOverloads @Throws(IOException::class) - fun toAsciiArmoredString(bytes: ByteArray, header: Map>? = null): String = - toAsciiArmoredString(bytes.inputStream(), header) + fun toAsciiArmoredString( + bytes: ByteArray, + header: Map>? = null + ): String = toAsciiArmoredString(bytes.inputStream(), header) /** * Return the ASCII armored encoding of the OpenPGP data from the given {@link InputStream}. @@ -174,26 +159,30 @@ class ArmorUtils { * @param inputStream input stream of OpenPGP data * @param header ASCII armor header map * @return ASCII armored encoding - * * @throws IOException in case of an io error */ @JvmStatic @JvmOverloads @Throws(IOException::class) - fun toAsciiArmoredString(inputStream: InputStream, header: Map>? = null): String = - ByteArrayOutputStream().apply { + fun toAsciiArmoredString( + inputStream: InputStream, + header: Map>? = null + ): String = + ByteArrayOutputStream() + .apply { toAsciiArmoredStream(this, header).run { Streams.pipeAll(inputStream, this) this.close() } - }.toString() + } + .toString() /** - * Return an [ArmoredOutputStream] prepared with headers for the given key ring, which wraps the given - * {@link OutputStream}. + * Return an [ArmoredOutputStream] prepared with headers for the given key ring, which wraps + * the given {@link OutputStream}. * - * The armored output stream can be used to encode the key ring by calling [PGPKeyRing.encode] - * with the armored output stream as an argument. + * The armored output stream can be used to encode the key ring by calling + * [PGPKeyRing.encode] with the armored output stream as an argument. * * @param keys OpenPGP key or certificate * @param outputStream wrapped output stream @@ -201,16 +190,18 @@ class ArmorUtils { */ @JvmStatic @Throws(IOException::class) - fun toAsciiArmoredStream(keys: PGPKeyRing, outputStream: OutputStream): ArmoredOutputStream = - toAsciiArmoredStream(outputStream, keyToHeader(keys.publicKey)) + fun toAsciiArmoredStream( + keys: PGPKeyRing, + outputStream: OutputStream + ): ArmoredOutputStream = toAsciiArmoredStream(outputStream, keyToHeader(keys.publicKey)) /** - * Create an [ArmoredOutputStream] wrapping the given [OutputStream]. - * The armored output stream will be prepared with armor headers given by header. + * Create an [ArmoredOutputStream] wrapping the given [OutputStream]. The armored output + * stream will be prepared with armor headers given by header. * * Note: Since the armored output stream is retrieved from [ArmoredOutputStreamFactory.get], - * it may already come with custom headers. Hence, the header entries given by header are appended below those - * already populated headers. + * it may already come with custom headers. Hence, the header entries given by header are + * appended below those already populated headers. * * @param outputStream output stream to wrap * @param header map of header entries @@ -219,19 +210,20 @@ class ArmorUtils { @JvmStatic @JvmOverloads @Throws(IOException::class) - fun toAsciiArmoredStream(outputStream: OutputStream, header: Map>? = null): ArmoredOutputStream = - ArmoredOutputStreamFactory.get(outputStream).apply { - header?.forEach { entry -> - entry.value.forEach { value -> - addHeader(entry.key, value) - } - } + fun toAsciiArmoredStream( + outputStream: OutputStream, + header: Map>? = null + ): ArmoredOutputStream = + ArmoredOutputStreamFactory.get(outputStream).apply { + header?.forEach { entry -> + entry.value.forEach { value -> addHeader(entry.key, value) } } + } /** - * Generate a header map for ASCII armor from the given [PGPPublicKey]. - * The header map consists of a comment field of the keys pretty-printed fingerprint, - * as well as the primary or first user-id plus the count of remaining user-ids. + * Generate a header map for ASCII armor from the given [PGPPublicKey]. The header map + * consists of a comment field of the keys pretty-printed fingerprint, as well as the + * primary or first user-id plus the count of remaining user-ids. * * @param publicKey public key * @return header map @@ -241,124 +233,142 @@ class ArmorUtils { val headerMap = mutableMapOf>() val userIds = KeyRingUtils.getUserIdsIgnoringInvalidUTF8(publicKey) val first: String? = userIds.firstOrNull() - val primary: String? = userIds.firstOrNull { - publicKey.getSignaturesForID(it)?.asSequence()?.any { sig -> - sig.hashedSubPackets.isPrimaryUserID - } ?: false - } + val primary: String? = + userIds.firstOrNull { + publicKey.getSignaturesForID(it)?.asSequence()?.any { sig -> + sig.hashedSubPackets.isPrimaryUserID + } + ?: false + } // Fingerprint - headerMap.getOrPut(HEADER_COMMENT) { mutableSetOf() }.add(OpenPgpFingerprint.of(publicKey).prettyPrint()) + headerMap + .getOrPut(HEADER_COMMENT) { mutableSetOf() } + .add(OpenPgpFingerprint.of(publicKey).prettyPrint()) // Primary / First User ID - (primary ?: first)?.let { headerMap.getOrPut(HEADER_COMMENT) { mutableSetOf() }.add(it) } + (primary ?: first)?.let { + headerMap.getOrPut(HEADER_COMMENT) { mutableSetOf() }.add(it) + } // X-1 further identities when (userIds.size) { - 0, 1 -> {} + 0, + 1 -> {} 2 -> headerMap.getOrPut(HEADER_COMMENT) { mutableSetOf() }.add("1 further identity") - else -> headerMap.getOrPut(HEADER_COMMENT) { mutableSetOf() }.add("${userIds.size - 1} further identities") + else -> + headerMap + .getOrPut(HEADER_COMMENT) { mutableSetOf() } + .add("${userIds.size - 1} further identities") } return headerMap } /** - * Set the version header entry in the ASCII armor. - * If the version info is null or only contains whitespace characters, then the version header will be removed. + * Set the version header entry in the ASCII armor. If the version info is null or only + * contains whitespace characters, then the version header will be removed. * * @param armor armored output stream * @param version version header. */ @JvmStatic - @Deprecated("Changing ASCII armor headers after ArmoredOutputStream creation is deprecated. " + + @Deprecated( + "Changing ASCII armor headers after ArmoredOutputStream creation is deprecated. " + "Use ArmoredOutputStream builder instead.") fun setVersionHeader(armor: ArmoredOutputStream, version: String?) = - armor.setHeader(HEADER_VERSION, version?.let { it.ifBlank { null } }) + armor.setHeader(HEADER_VERSION, version?.let { it.ifBlank { null } }) /** - * Add an ASCII armor header entry about the used hash algorithm into the [ArmoredOutputStream]. + * Add an ASCII armor header entry about the used hash algorithm into the + * [ArmoredOutputStream]. * * @param armor armored output stream * @param hashAlgorithm hash algorithm - * - * @see - * RFC 4880 - OpenPGP Message Format §6.2. Forming ASCII Armor + * @see RFC 4880 - + * OpenPGP Message Format §6.2. Forming ASCII Armor */ @JvmStatic - @Deprecated("Changing ASCII armor headers after ArmoredOutputStream creation is deprecated. " + + @Deprecated( + "Changing ASCII armor headers after ArmoredOutputStream creation is deprecated. " + "Use ArmoredOutputStream builder instead.") fun addHashAlgorithmHeader(armor: ArmoredOutputStream, hashAlgorithm: HashAlgorithm) = - armor.addHeader(HEADER_HASH, hashAlgorithm.algorithmName) + armor.addHeader(HEADER_HASH, hashAlgorithm.algorithmName) /** * Add an ASCII armor comment header entry into the [ArmoredOutputStream]. * * @param armor armored output stream * @param comment free-text comment - * - * @see - * RFC 4880 - OpenPGP Message Format §6.2. Forming ASCII Armor + * @see RFC 4880 - + * OpenPGP Message Format §6.2. Forming ASCII Armor */ @JvmStatic - @Deprecated("Changing ASCII armor headers after ArmoredOutputStream creation is deprecated. " + + @Deprecated( + "Changing ASCII armor headers after ArmoredOutputStream creation is deprecated. " + "Use ArmoredOutputStream builder instead.") fun addCommentHeader(armor: ArmoredOutputStream, comment: String) = - armor.addHeader(HEADER_COMMENT, comment) + armor.addHeader(HEADER_COMMENT, comment) /** * Add an ASCII armor message-id header entry into the [ArmoredOutputStream]. * * @param armor armored output stream * @param messageId message id - * - * @see - * RFC 4880 - OpenPGP Message Format §6.2. Forming ASCII Armor + * @see RFC 4880 - + * OpenPGP Message Format §6.2. Forming ASCII Armor */ @JvmStatic - @Deprecated("Changing ASCII armor headers after ArmoredOutputStream creation is deprecated. " + + @Deprecated( + "Changing ASCII armor headers after ArmoredOutputStream creation is deprecated. " + "Use ArmoredOutputStream builder instead.") fun addMessageIdHeader(armor: ArmoredOutputStream, messageId: String) { - require(PATTER_MESSAGE_ID.matches(messageId)) { "MessageIDs MUST consist of 32 printable characters." } + require(PATTER_MESSAGE_ID.matches(messageId)) { + "MessageIDs MUST consist of 32 printable characters." + } armor.addHeader(HEADER_MESSAGEID, messageId) } /** - * Extract all ASCII armor header values of type comment from the given [ArmoredInputStream]. + * Extract all ASCII armor header values of type comment from the given + * [ArmoredInputStream]. * * @param armor armored input stream * @return list of comment headers */ @JvmStatic fun getCommentHeaderValues(armor: ArmoredInputStream): List = - getArmorHeaderValues(armor, HEADER_COMMENT) + getArmorHeaderValues(armor, HEADER_COMMENT) /** - * Extract all ASCII armor header values of type message id from the given [ArmoredInputStream]. + * Extract all ASCII armor header values of type message id from the given + * [ArmoredInputStream]. * * @param armor armored input stream * @return list of message-id headers */ @JvmStatic fun getMessageIdHeaderValues(armor: ArmoredInputStream): List = - getArmorHeaderValues(armor, HEADER_MESSAGEID) + getArmorHeaderValues(armor, HEADER_MESSAGEID) /** - * Return all ASCII armor header values of type hash-algorithm from the given [ArmoredInputStream]. + * Return all ASCII armor header values of type hash-algorithm from the given + * [ArmoredInputStream]. * * @param armor armored input stream * @return list of hash headers */ @JvmStatic fun getHashHeaderValues(armor: ArmoredInputStream): List = - getArmorHeaderValues(armor, HEADER_HASH) + getArmorHeaderValues(armor, HEADER_HASH) /** - * Return a list of [HashAlgorithm] enums extracted from the hash header entries of the given [ArmoredInputStream]. + * Return a list of [HashAlgorithm] enums extracted from the hash header entries of the + * given [ArmoredInputStream]. * * @param armor armored input stream * @return list of hash algorithms from the ASCII header */ @JvmStatic fun getHashAlgorithms(armor: ArmoredInputStream): List = - getHashHeaderValues(armor).mapNotNull { HashAlgorithm.fromName(it) } + getHashHeaderValues(armor).mapNotNull { HashAlgorithm.fromName(it) } /** * Return all ASCII armor header values of type version from the given [ArmoredInputStream]. @@ -368,7 +378,7 @@ class ArmorUtils { */ @JvmStatic fun getVersionHeaderValues(armor: ArmoredInputStream): List = - getArmorHeaderValues(armor, HEADER_VERSION) + getArmorHeaderValues(armor, HEADER_VERSION) /** * Return all ASCII armor header values of type charset from the given [ArmoredInputStream]. @@ -378,10 +388,11 @@ class ArmorUtils { */ @JvmStatic fun getCharsetHeaderValues(armor: ArmoredInputStream): List = - getArmorHeaderValues(armor, HEADER_CHARSET) + getArmorHeaderValues(armor, HEADER_CHARSET) /** - * Return all ASCII armor header values of the given headerKey from the given [ArmoredInputStream]. + * Return all ASCII armor header values of the given headerKey from the given + * [ArmoredInputStream]. * * @param armor armored input stream * @param key ASCII armor header key @@ -389,34 +400,33 @@ class ArmorUtils { */ @JvmStatic fun getArmorHeaderValues(armor: ArmoredInputStream, key: String): List = - armor.armorHeaders - .filter { it.startsWith("$key: ") } - .map { it.substring(key.length + 2) } // key.len + ": ".len + armor.armorHeaders + .filter { it.startsWith("$key: ") } + .map { it.substring(key.length + 2) } // key.len + ": ".len /** - * Hacky workaround for #96. - * For `PGPPublicKeyRingCollection(InputStream, KeyFingerPrintCalculator)` - * or `PGPSecretKeyRingCollection(InputStream, KeyFingerPrintCalculator)` - * to read all PGPKeyRings properly, we apparently have to make sure that the [InputStream] that is given - * as constructor argument is a [PGPUtil.BufferedInputStreamExt]. - * Since [PGPUtil.getDecoderStream] will return an [org.bouncycastle.bcpg.ArmoredInputStream] - * if the underlying input stream contains armored data, we first dearmor the data ourselves to make sure that the - * end-result is a [PGPUtil.BufferedInputStreamExt]. + * Hacky workaround for #96. For `PGPPublicKeyRingCollection(InputStream, + * KeyFingerPrintCalculator)` or `PGPSecretKeyRingCollection(InputStream, + * KeyFingerPrintCalculator)` to read all PGPKeyRings properly, we apparently have to make + * sure that the [InputStream] that is given as constructor argument is a + * [PGPUtil.BufferedInputStreamExt]. Since [PGPUtil.getDecoderStream] will return an + * [org.bouncycastle.bcpg.ArmoredInputStream] if the underlying input stream contains + * armored data, we first dearmor the data ourselves to make sure that the end-result is a + * [PGPUtil.BufferedInputStreamExt]. * * @param inputStream input stream * @return BufferedInputStreamExt - * * @throws IOException in case of an IO error */ @JvmStatic @Throws(IOException::class) fun getDecoderStream(inputStream: InputStream): InputStream = - OpenPgpInputStream(inputStream).let { - if (it.isAsciiArmored) { - PGPUtil.getDecoderStream(ArmoredInputStreamFactory.get(it)) - } else { - it - } + OpenPgpInputStream(inputStream).let { + if (it.isAsciiArmored) { + PGPUtil.getDecoderStream(ArmoredInputStreamFactory.get(it)) + } else { + it } + } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/util/ArmoredInputStreamFactory.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/util/ArmoredInputStreamFactory.kt index 254b5c88..8198214d 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/util/ArmoredInputStreamFactory.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/util/ArmoredInputStreamFactory.kt @@ -4,14 +4,14 @@ package org.pgpainless.util -import org.bouncycastle.bcpg.ArmoredInputStream -import org.pgpainless.decryption_verification.ConsumerOptions import java.io.IOException import java.io.InputStream +import org.bouncycastle.bcpg.ArmoredInputStream +import org.pgpainless.decryption_verification.ConsumerOptions /** - * Factory class for instantiating preconfigured [ArmoredInputStream] instances. - * [get] will return an [ArmoredInputStream] that is set up to properly detect CRC errors v4 style. + * Factory class for instantiating preconfigured [ArmoredInputStream] instances. [get] will return + * an [ArmoredInputStream] that is set up to properly detect CRC errors v4 style. */ class ArmoredInputStreamFactory { @@ -31,14 +31,15 @@ class ArmoredInputStreamFactory { return when (inputStream) { is CRCingArmoredInputStreamWrapper -> inputStream is ArmoredInputStream -> CRCingArmoredInputStreamWrapper(inputStream) - else -> CRCingArmoredInputStreamWrapper( - ArmoredInputStream.builder().apply { - setParseForHeaders(true) - options?.let { - setIgnoreCRC(it.isDisableAsciiArmorCRC) + else -> + CRCingArmoredInputStreamWrapper( + ArmoredInputStream.builder() + .apply { + setParseForHeaders(true) + options?.let { setIgnoreCRC(it.isDisableAsciiArmorCRC) } } - }.build(inputStream)) + .build(inputStream)) } } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/util/ArmoredOutputStreamFactory.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/util/ArmoredOutputStreamFactory.kt index 69a5520f..caf14e53 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/util/ArmoredOutputStreamFactory.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/util/ArmoredOutputStreamFactory.kt @@ -4,25 +4,25 @@ package org.pgpainless.util +import java.io.OutputStream import org.bouncycastle.bcpg.ArmoredOutputStream import org.pgpainless.encryption_signing.ProducerOptions -import java.io.OutputStream /** - * Factory to create configured [ArmoredOutputStream] instances. - * The configuration entails setting custom version and comment headers. + * Factory to create configured [ArmoredOutputStream] instances. The configuration entails setting + * custom version and comment headers. */ class ArmoredOutputStreamFactory { companion object { private const val PGPAINLESS = "PGPainless" - @JvmStatic - private var version: String? = PGPAINLESS + @JvmStatic private var version: String? = PGPAINLESS private var comment: String? = null /** - * Return an instance of the [ArmoredOutputStream] which might have pre-populated armor headers. + * Return an instance of the [ArmoredOutputStream] which might have pre-populated armor + * headers. * * @param outputStream output stream * @param options options @@ -31,39 +31,43 @@ class ArmoredOutputStreamFactory { @JvmStatic @JvmOverloads fun get(outputStream: OutputStream, options: ProducerOptions? = null): ArmoredOutputStream { - val builder = ArmoredOutputStream.builder().apply { - // set fields defined in ArmoredOutputStreamFactory - if (!version.isNullOrBlank()) setVersion(version) - if (!comment.isNullOrBlank()) setComment(comment) + val builder = + ArmoredOutputStream.builder().apply { + // set fields defined in ArmoredOutputStreamFactory + if (!version.isNullOrBlank()) setVersion(version) + if (!comment.isNullOrBlank()) setComment(comment) - // set (and potentially overwrite with) values from ProducerOptions - options?.let { - enableCRC(!it.isDisableAsciiArmorCRC) - if (it.isHideArmorHeaders) clearHeaders() - if (it.hasVersion()) setVersion(it.version) - if (it.hasComment()) addComment(it.comment) - // TODO: configure CRC + // set (and potentially overwrite with) values from ProducerOptions + options?.let { + enableCRC(!it.isDisableAsciiArmorCRC) + if (it.isHideArmorHeaders) clearHeaders() + if (it.hasVersion()) setVersion(it.version) + if (it.hasComment()) addComment(it.comment) + // TODO: configure CRC + } } - } return get(outputStream, builder) } /** - * Build an [ArmoredOutputStream] around the given [outputStream], configured according to the passed in - * [ArmoredOutputStream.Builder] instance. + * Build an [ArmoredOutputStream] around the given [outputStream], configured according to + * the passed in [ArmoredOutputStream.Builder] instance. * * @param outputStream output stream * @param builder builder instance */ @JvmStatic - fun get(outputStream: OutputStream, builder: ArmoredOutputStream.Builder): ArmoredOutputStream { + fun get( + outputStream: OutputStream, + builder: ArmoredOutputStream.Builder + ): ArmoredOutputStream { return builder.build(outputStream) } /** - * Overwrite the version header of ASCII armors with a custom value. - * Newlines in the version info string result in multiple version header entries. - * If this is set to

null
, then the version header is omitted altogether. + * Overwrite the version header of ASCII armors with a custom value. Newlines in the version + * info string result in multiple version header entries. If this is set to
null
, + * then the version header is omitted altogether. * * @param versionString version string */ @@ -72,25 +76,24 @@ class ArmoredOutputStreamFactory { version = if (versionString.isNullOrBlank()) null else versionString.trim() } - /** - * Reset the version header to its default value of [PGPAINLESS]. - */ + /** Reset the version header to its default value of [PGPAINLESS]. */ @JvmStatic fun resetVersionInfo() { version = PGPAINLESS } /** - * Set a comment header value in the ASCII armor header. - * If the comment contains newlines, it will be split into multiple header entries. - * - * @see [ProducerOptions.setComment] for how to set comments for individual messages. + * Set a comment header value in the ASCII armor header. If the comment contains newlines, + * it will be split into multiple header entries. * * @param commentString comment + * @see [ProducerOptions.setComment] for how to set comments for individual messages. */ @JvmStatic fun setComment(commentString: String) { - require(commentString.isNotBlank()) { "Comment cannot be empty. See resetComment() to clear the comment." } + require(commentString.isNotBlank()) { + "Comment cannot be empty. See resetComment() to clear the comment." + } comment = commentString.trim() } @@ -99,4 +102,4 @@ class ArmoredOutputStreamFactory { comment = null } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/util/DateUtil.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/util/DateUtil.kt index de2052d6..712ac262 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/util/DateUtil.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/util/DateUtil.kt @@ -4,10 +4,10 @@ package org.pgpainless.util +import java.util.* import openpgp.formatUTC import openpgp.parseUTC import openpgp.toSecondsPrecision -import java.util.* class DateUtil { @@ -19,8 +19,7 @@ class DateUtil { * @param dateString timestamp * @return date */ - @JvmStatic - fun parseUTCDate(dateString: String): Date = dateString.parseUTC() + @JvmStatic fun parseUTCDate(dateString: String): Date = dateString.parseUTC() /** * Format a date as UTC timestamp. @@ -28,23 +27,21 @@ class DateUtil { * @param date date * @return timestamp */ - @JvmStatic - fun formatUTCDate(date: Date): String = date.formatUTC() + @JvmStatic fun formatUTCDate(date: Date): String = date.formatUTC() /** * Floor a date down to seconds precision. + * * @param date date * @return floored date */ - @JvmStatic - fun toSecondsPrecision(date: Date): Date = date.toSecondsPrecision() + @JvmStatic fun toSecondsPrecision(date: Date): Date = date.toSecondsPrecision() /** * Return the current date "floored" to UTC precision. * * @return now */ - @JvmStatic - fun now() = toSecondsPrecision(Date()) + @JvmStatic fun now() = toSecondsPrecision(Date()) } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/util/MultiMap.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/util/MultiMap.kt index 9ca193a6..3aa22d0d 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/util/MultiMap.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/util/MultiMap.kt @@ -8,13 +8,13 @@ class MultiMap : Iterable>> { private val map: Map> - constructor(): this(mutableMapOf()) - constructor(other: MultiMap): this(other.map) + constructor() : this(mutableMapOf()) + + constructor(other: MultiMap) : this(other.map) + constructor(content: Map>) { map = mutableMapOf() - content.forEach { - map[it.key] = it.value.toMutableSet() - } + content.forEach { map[it.key] = it.value.toMutableSet() } } override fun iterator(): Iterator>> { @@ -23,45 +23,58 @@ class MultiMap : Iterable>> { val size: Int get() = map.size + fun size() = size + val keys: Set get() = map.keys + fun keySet() = keys + val values: Collection> get() = map.values + fun values() = values + val entries: Set>> get() = map.entries + fun entrySet() = entries + fun isEmpty(): Boolean = map.isEmpty() + fun containsKey(key: K): Boolean = map.containsKey(key) + fun containsValue(value: V): Boolean = map.values.any { it.contains(value) } + fun contains(key: K, value: V): Boolean = map[key]?.contains(value) ?: false + operator fun get(key: K): Set? = map[key] - fun put(key: K, value: V) = - (map as MutableMap).put(key, mutableSetOf(value)) - fun plus(key: K, value: V) = - (map as MutableMap).getOrPut(key) { mutableSetOf() }.add(value) - fun put(key: K, values: Set) = - (map as MutableMap).put(key, values.toMutableSet()) + + fun put(key: K, value: V) = (map as MutableMap).put(key, mutableSetOf(value)) + + fun plus(key: K, value: V) = (map as MutableMap).getOrPut(key) { mutableSetOf() }.add(value) + + fun put(key: K, values: Set) = (map as MutableMap).put(key, values.toMutableSet()) + fun plus(key: K, values: Set) = - (map as MutableMap).getOrPut(key) { mutableSetOf() }.addAll(values) - fun putAll(other: MultiMap) = other.map.entries.forEach { - put(it.key, it.value) - } - fun plusAll(other: MultiMap) = other.map.entries.forEach { - plus(it.key, it.value) - } + (map as MutableMap).getOrPut(key) { mutableSetOf() }.addAll(values) + + fun putAll(other: MultiMap) = other.map.entries.forEach { put(it.key, it.value) } + + fun plusAll(other: MultiMap) = other.map.entries.forEach { plus(it.key, it.value) } + fun removeAll(key: K) = (map as MutableMap).remove(key) + fun remove(key: K, value: V) = (map as MutableMap)[key]?.remove(value) + fun clear() = (map as MutableMap).clear() + fun flatten() = map.flatMap { it.value }.toSet() override fun equals(other: Any?): Boolean { - return if (other == null) - false - else if (other !is MultiMap<*, *>) - false + return if (other == null) false + else if (other !is MultiMap<*, *>) false else if (this === other) { true } else { @@ -72,4 +85,4 @@ class MultiMap : Iterable>> { override fun hashCode(): Int { return map.hashCode() } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/util/NotationRegistry.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/util/NotationRegistry.kt index 15dbe886..d2295f32 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/util/NotationRegistry.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/util/NotationRegistry.kt @@ -5,9 +5,9 @@ package org.pgpainless.util /** - * Registry for known notations. - * Since signature verification must reject signatures with critical notations that are not known to the application, - * there must be some way to tell PGPainless which notations actually are known. + * Registry for known notations. Since signature verification must reject signatures with critical + * notations that are not known to the application, there must be some way to tell PGPainless which + * notations actually are known. * * To add a notation name, call {@link #addKnownNotation(String)}. */ @@ -19,8 +19,8 @@ class NotationRegistry constructor(notations: Set = setOf()) { } /** - * Add a known notation name into the registry. - * This will cause critical notations with that name to no longer invalidate the signature. + * Add a known notation name into the registry. This will cause critical notations with that + * name to no longer invalidate the signature. * * @param notationName name of the notation */ @@ -36,10 +36,8 @@ class NotationRegistry constructor(notations: Set = setOf()) { */ fun isKnownNotation(notationName: String): Boolean = knownNotations.contains(notationName) - /** - * Clear all known notations from the registry. - */ + /** Clear all known notations from the registry. */ fun clear() { knownNotations.clear() } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/util/Passphrase.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/util/Passphrase.kt index a64fda53..4d1e49d2 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/util/Passphrase.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/util/Passphrase.kt @@ -11,9 +11,7 @@ import org.bouncycastle.util.Arrays * * @param chars may be null for empty passwords. */ -class Passphrase( - chars: CharArray? -) { +class Passphrase(chars: CharArray?) { private val lock = Any() private var valid = true private val chars: CharArray? @@ -23,17 +21,17 @@ class Passphrase( } /** - * Return a copy of the underlying char array. - * A return value of null represents an empty password. + * Return a copy of the underlying char array. A return value of null represents an empty + * password. * * @return passphrase chars. - * * @throws IllegalStateException in case the password has been cleared at this point. */ - fun getChars(): CharArray? = synchronized(lock) { - check(valid) { "Passphrase has been cleared." } - chars?.copyOf() - } + fun getChars(): CharArray? = + synchronized(lock) { + check(valid) { "Passphrase has been cleared." } + chars?.copyOf() + } /** * Return true if the passphrase has not yet been cleared. @@ -51,23 +49,20 @@ class Passphrase( val isEmpty: Boolean get() = synchronized(lock) { valid && chars == null } - /** - * Overwrite the char array with spaces and mark the [Passphrase] as invalidated. - */ - fun clear() = synchronized(lock) { - chars?.fill(' ') - valid = false - } + /** Overwrite the char array with spaces and mark the [Passphrase] as invalidated. */ + fun clear() = + synchronized(lock) { + chars?.fill(' ') + valid = false + } override fun equals(other: Any?): Boolean { - return if (other == null) - false - else if (this === other) - true - else if (other !is Passphrase) - false + return if (other == null) false + else if (this === other) true + else if (other !is Passphrase) false else - getChars() == null && other.getChars() == null || Arrays.constantTimeAreEqual(getChars(), other.getChars()) + getChars() == null && other.getChars() == null || + Arrays.constantTimeAreEqual(getChars(), other.getChars()) } override fun hashCode(): Int = getChars()?.let { String(it) }.hashCode() @@ -83,23 +78,23 @@ class Passphrase( @JvmStatic fun fromPassword(password: CharSequence) = Passphrase(password.toString().toCharArray()) - @JvmStatic - fun emptyPassphrase() = Passphrase(null) + @JvmStatic fun emptyPassphrase() = Passphrase(null) /** - * Return a copy of the passed in char array, with leading and trailing whitespace characters removed. - * If the passed in char array is null, return null. - * If the resulting char array is empty, return null as well. + * Return a copy of the passed in char array, with leading and trailing whitespace + * characters removed. If the passed in char array is null, return null. If the resulting + * char array is empty, return null as well. * * @param chars char array * @return copy of char array with leading and trailing whitespace characters removed */ @JvmStatic private fun trimWhitespace(chars: CharArray?): CharArray? { - return chars?.dropWhile { it.isWhitespace() } - ?.dropLastWhile { it.isWhitespace() } - ?.toCharArray() - ?.let { if (it.isEmpty()) null else it } + return chars + ?.dropWhile { it.isWhitespace() } + ?.dropLastWhile { it.isWhitespace() } + ?.toCharArray() + ?.let { if (it.isEmpty()) null else it } } } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/util/SessionKey.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/util/SessionKey.kt index 894d0869..ef8eb9e7 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/util/SessionKey.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/util/SessionKey.kt @@ -10,21 +10,22 @@ import org.pgpainless.algorithm.SymmetricKeyAlgorithm /** * A [SessionKey] is the symmetric key that is used to encrypt/decrypt an OpenPGP message payload. - * The OpenPGP message header contains a copy of the session key, encrypted for the public key of each recipient. + * The OpenPGP message header contains a copy of the session key, encrypted for the public key of + * each recipient. * * @param algorithm symmetric key algorithm * @param key bytes of the key */ -data class SessionKey(val algorithm: SymmetricKeyAlgorithm, - val key: ByteArray) { +data class SessionKey(val algorithm: SymmetricKeyAlgorithm, val key: ByteArray) { /** * Constructor to create a session key from a BC [PGPSessionKey] object. * * @param sessionKey BC session key */ - constructor(sessionKey: PGPSessionKey): - this(SymmetricKeyAlgorithm.requireFromId(sessionKey.algorithm), sessionKey.key) + constructor( + sessionKey: PGPSessionKey + ) : this(SymmetricKeyAlgorithm.requireFromId(sessionKey.algorithm), sessionKey.key) override fun toString(): String { return "${algorithm.algorithmId}:${Hex.toHexString(key)}" @@ -45,4 +46,4 @@ data class SessionKey(val algorithm: SymmetricKeyAlgorithm, override fun hashCode(): Int { return 31 * algorithm.hashCode() + key.contentHashCode() } -} \ No newline at end of file +} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/util/selection/userid/SelectUserId.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/util/selection/userid/SelectUserId.kt index 3c215d80..f2794925 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/util/selection/userid/SelectUserId.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/util/selection/userid/SelectUserId.kt @@ -4,15 +4,13 @@ package org.pgpainless.util.selection.userid +import java.util.function.Predicate import org.bouncycastle.openpgp.PGPKeyRing import org.pgpainless.PGPainless -import java.util.function.Predicate abstract class SelectUserId : Predicate, (String) -> Boolean { - /** - * Legacy glue code to forward accept() calls to invoke() instead. - */ + /** Legacy glue code to forward accept() calls to invoke() instead. */ @Deprecated("Use invoke() instead.", ReplaceWith("invoke(userId)")) protected fun accept(userId: String): Boolean = invoke(userId) @@ -27,10 +25,10 @@ abstract class SelectUserId : Predicate, (String) -> Boolean { * @return filter */ @JvmStatic - fun exactMatch(query: CharSequence) = object : SelectUserId() { - override fun invoke(userId: String): Boolean = - userId == query - } + fun exactMatch(query: CharSequence) = + object : SelectUserId() { + override fun invoke(userId: String): Boolean = userId == query + } /** * Filter for user-ids which start with the given [substring]. @@ -39,10 +37,10 @@ abstract class SelectUserId : Predicate, (String) -> Boolean { * @return filter */ @JvmStatic - fun startsWith(substring: CharSequence) = object : SelectUserId() { - override fun invoke(userId: String): Boolean = - userId.startsWith(substring) - } + fun startsWith(substring: CharSequence) = + object : SelectUserId() { + override fun invoke(userId: String): Boolean = userId.startsWith(substring) + } /** * Filter for user-ids which contain the given [substring]. @@ -51,54 +49,53 @@ abstract class SelectUserId : Predicate, (String) -> Boolean { * @return filter */ @JvmStatic - fun containsSubstring(substring: CharSequence) = object : SelectUserId() { - override fun invoke(userId: String): Boolean = - userId.contains(substring) - } + fun containsSubstring(substring: CharSequence) = + object : SelectUserId() { + override fun invoke(userId: String): Boolean = userId.contains(substring) + } /** - * Filter for user-ids which contain the given [email] address. - * Note: This only accepts user-ids which properly have the email address surrounded by angle brackets. + * Filter for user-ids which contain the given [email] address. Note: This only accepts + * user-ids which properly have the email address surrounded by angle brackets. * - * The argument [email] can both be a plain email address (`foo@bar.baz`), - * or surrounded by angle brackets (``), the result of the filter will be the same. + * The argument [email] can both be a plain email address (`foo@bar.baz`), or surrounded by + * angle brackets (``), the result of the filter will be the same. * * @param email email address * @return filter */ @JvmStatic fun containsEmailAddress(email: CharSequence) = - if (email.startsWith('<') && email.endsWith('>')) - containsSubstring(email) - else - containsSubstring("<$email>") + if (email.startsWith('<') && email.endsWith('>')) containsSubstring(email) + else containsSubstring("<$email>") @JvmStatic fun byEmail(email: CharSequence) = or(exactMatch(email), containsEmailAddress(email)) @JvmStatic - fun validUserId(keyRing: PGPKeyRing) = object : SelectUserId() { - private val info = PGPainless.inspectKeyRing(keyRing) - override fun invoke(userId: String): Boolean = - info.isUserIdValid(userId) - } + fun validUserId(keyRing: PGPKeyRing) = + object : SelectUserId() { + private val info = PGPainless.inspectKeyRing(keyRing) + + override fun invoke(userId: String): Boolean = info.isUserIdValid(userId) + } @JvmStatic - fun and(vararg filters: SelectUserId) = object : SelectUserId() { - override fun invoke(userId: String): Boolean = - filters.all { it.invoke(userId) } - } + fun and(vararg filters: SelectUserId) = + object : SelectUserId() { + override fun invoke(userId: String): Boolean = filters.all { it.invoke(userId) } + } @JvmStatic - fun or(vararg filters: SelectUserId) = object : SelectUserId() { - override fun invoke(userId: String): Boolean = - filters.any { it.invoke(userId) } - } + fun or(vararg filters: SelectUserId) = + object : SelectUserId() { + override fun invoke(userId: String): Boolean = filters.any { it.invoke(userId) } + } @JvmStatic - fun not(filter: SelectUserId) = object : SelectUserId() { - override fun invoke(userId: String): Boolean = - !filter.invoke(userId) - } + fun not(filter: SelectUserId) = + object : SelectUserId() { + override fun invoke(userId: String): Boolean = !filter.invoke(userId) + } } -} \ No newline at end of file +}