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 b9474247..cf398806 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 @@ -21,7 +21,7 @@ interface HashAlgorithmNegotiator { * @param orderedPrefs hash algorithm preferences * @return picked algorithms */ - fun negotiateHashAlgorithm(orderedPrefs: Set): HashAlgorithm + fun negotiateHashAlgorithm(orderedPrefs: Set?): HashAlgorithm companion object { @@ -62,9 +62,9 @@ interface HashAlgorithmNegotiator { ): HashAlgorithmNegotiator { return object : HashAlgorithmNegotiator { override fun negotiateHashAlgorithm( - orderedPrefs: Set + orderedPrefs: Set? ): HashAlgorithm { - return orderedPrefs.firstOrNull { hashAlgorithmPolicy.isAcceptable(it) } + return orderedPrefs?.firstOrNull { hashAlgorithmPolicy.isAcceptable(it) } ?: hashAlgorithmPolicy.defaultHashAlgorithm() } } 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 8b2baeef..7c008b82 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 @@ -482,7 +482,7 @@ class SigningOptions { * @return selected hash algorithm */ private fun negotiateHashAlgorithm( - preferences: Set, + preferences: Set?, policy: Policy ): HashAlgorithm { return _hashAlgorithmOverride 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 dd1e5a0c..f0c1a755 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 @@ -240,7 +240,7 @@ class KeyRingInfo( getKeyFlagsOf(keyIdentifier).contains(KeyFlag.CERTIFY_OTHER) /** [HashAlgorithm] preferences of the primary user-ID or if absent, of the primary key. */ - val preferredHashAlgorithms: Set + val preferredHashAlgorithms: Set? get() = primaryUserId?.let { getPreferredHashAlgorithms(it) } ?: getPreferredHashAlgorithms(keyIdentifier) @@ -248,19 +248,19 @@ class KeyRingInfo( /** * [SymmetricKeyAlgorithm] preferences of the primary user-ID or if absent of the primary key. */ - val preferredSymmetricKeyAlgorithms: Set + val preferredSymmetricKeyAlgorithms: Set? get() = primaryUserId?.let { getPreferredSymmetricKeyAlgorithms(it) } ?: getPreferredSymmetricKeyAlgorithms(keyIdentifier) /** [CompressionAlgorithm] preferences of the primary user-ID or if absent, the primary key. */ - val preferredCompressionAlgorithms: Set + val preferredCompressionAlgorithms: Set? get() = primaryUserId?.let { getPreferredCompressionAlgorithms(it) } ?: getPreferredCompressionAlgorithms(keyIdentifier) /** [AEADCipherMode] preferences of the primary user-id, or if absent, the primary key. */ - val preferredAEADCipherSuites: Set + val preferredAEADCipherSuites: Set? get() = primaryUserId?.let { getPreferredAEADCipherSuites(it) } ?: getPreferredAEADCipherSuites(keyIdentifier) @@ -738,12 +738,11 @@ class KeyRingInfo( * @param userId user-id * @return ordered set of preferred [HashAlgorithms][HashAlgorithm] (descending order) */ - fun getPreferredHashAlgorithms(userId: CharSequence): Set { - return keys - .getUserId(userId.toString()) - ?.getHashAlgorithmPreferences(referenceDate) + fun getPreferredHashAlgorithms(userId: CharSequence): Set? { + return (keys.getUserId(userId.toString()) + ?: throw NoSuchElementException("No user-id '$userId' found on this key.")) + .getHashAlgorithmPreferences(referenceDate) ?.toHashAlgorithms() - ?: throw NoSuchElementException("No user-id '$userId' found on this key.") } /** @@ -752,18 +751,17 @@ class KeyRingInfo( * @param keyIdentifier identifier of a [OpenPGPComponentKey] * @return ordered set of preferred [HashAlgorithms][HashAlgorithm] (descending order) */ - fun getPreferredHashAlgorithms(keyIdentifier: KeyIdentifier): Set { - return keys - .getKey(keyIdentifier) - ?.getHashAlgorithmPreferences(referenceDate) + fun getPreferredHashAlgorithms(keyIdentifier: KeyIdentifier): Set? { + return (keys.getKey(keyIdentifier) + ?: throw NoSuchElementException( + "No subkey with key-id $keyIdentifier found on this key.")) + .getHashAlgorithmPreferences(referenceDate) ?.toHashAlgorithms() - ?: throw NoSuchElementException( - "No subkey with key-id $keyIdentifier found on this key.") } /** [HashAlgorithm] preferences of the given key. */ @Deprecated("Pass KeyIdentifier instead.") - fun getPreferredHashAlgorithms(keyId: Long): Set { + fun getPreferredHashAlgorithms(keyId: Long): Set? { return getPreferredHashAlgorithms(KeyIdentifier(keyId)) } @@ -774,12 +772,11 @@ class KeyRingInfo( * @return ordered set of preferred [SymmetricKeyAlgorithms][SymmetricKeyAlgorithm] (descending * order) */ - fun getPreferredSymmetricKeyAlgorithms(userId: CharSequence): Set { - return keys - .getUserId(userId.toString()) - ?.getSymmetricCipherPreferences(referenceDate) + fun getPreferredSymmetricKeyAlgorithms(userId: CharSequence): Set? { + return (keys.getUserId(userId.toString()) + ?: throw NoSuchElementException("No user-id '$userId' found on this key.")) + .getSymmetricCipherPreferences(referenceDate) ?.toSymmetricKeyAlgorithms() - ?: throw NoSuchElementException("No user-id '$userId' found on this key.") } /** @@ -792,18 +789,17 @@ class KeyRingInfo( */ fun getPreferredSymmetricKeyAlgorithms( keyIdentifier: KeyIdentifier - ): Set { - return keys - .getKey(keyIdentifier) - ?.getSymmetricCipherPreferences(referenceDate) + ): Set? { + return (keys.getKey(keyIdentifier) + ?: throw NoSuchElementException( + "No subkey with key-id $keyIdentifier found on this key.")) + .getSymmetricCipherPreferences(referenceDate) ?.toSymmetricKeyAlgorithms() - ?: throw NoSuchElementException( - "No subkey with key-id $keyIdentifier found on this key.") } /** [SymmetricKeyAlgorithm] preferences of the given key. */ @Deprecated("Pass KeyIdentifier instead.") - fun getPreferredSymmetricKeyAlgorithms(keyId: Long): Set { + fun getPreferredSymmetricKeyAlgorithms(keyId: Long): Set? { return getPreferredSymmetricKeyAlgorithms(KeyIdentifier(keyId)) } @@ -814,12 +810,11 @@ class KeyRingInfo( * @return ordered set of preferred [CompressionAlgorithms][CompressionAlgorithm] (descending * order) */ - fun getPreferredCompressionAlgorithms(userId: CharSequence): Set { - return keys - .getUserId(userId.toString()) - ?.getCompressionAlgorithmPreferences(referenceDate) + fun getPreferredCompressionAlgorithms(userId: CharSequence): Set? { + return (keys.getUserId(userId.toString()) + ?: throw NoSuchElementException("No user-id '$userId' found on this key.")) + .getCompressionAlgorithmPreferences(referenceDate) ?.toCompressionAlgorithms() - ?: throw NoSuchElementException("No user-id '$userId' found on this key.") } /** @@ -830,18 +825,19 @@ class KeyRingInfo( * @return ordered set of preferred [CompressionAlgorithms][CompressionAlgorithm] (descending * order) */ - fun getPreferredCompressionAlgorithms(keyIdentifier: KeyIdentifier): Set { - return keys - .getKey(keyIdentifier) - ?.getCompressionAlgorithmPreferences(referenceDate) + fun getPreferredCompressionAlgorithms( + keyIdentifier: KeyIdentifier + ): Set? { + return (keys.getKey(keyIdentifier) + ?: throw NoSuchElementException( + "No subkey with key-id $keyIdentifier found on this key.")) + .getCompressionAlgorithmPreferences(referenceDate) ?.toCompressionAlgorithms() - ?: throw NoSuchElementException( - "No subkey with key-id $keyIdentifier found on this key.") } /** [CompressionAlgorithm] preferences of the given key. */ @Deprecated("Pass in a KeyIdentifier instead.") - fun getPreferredCompressionAlgorithms(keyId: Long): Set { + fun getPreferredCompressionAlgorithms(keyId: Long): Set? { return getPreferredCompressionAlgorithms(KeyIdentifier(keyId)) } @@ -852,12 +848,11 @@ class KeyRingInfo( * @return ordered set of [AEADCipherModes][AEADCipherMode] (descending order, including * implicitly supported AEAD modes) */ - fun getPreferredAEADCipherSuites(userId: CharSequence): Set { - return keys - .getUserId(userId.toString()) - ?.getAEADCipherSuitePreferences(referenceDate) + fun getPreferredAEADCipherSuites(userId: CharSequence): Set? { + return (keys.getUserId(userId.toString()) + ?: throw NoSuchElementException("No user-id '$userId' found on this key.")) + .getAEADCipherSuitePreferences(referenceDate) ?.toAEADCipherModes() - ?: throw NoSuchElementException("No user-id '$userId' found on this key.") } /** @@ -868,17 +863,16 @@ class KeyRingInfo( * @return ordered set of [AEADCipherModes][AEADCipherMode] (descending order, including * implicitly supported AEAD modes) */ - fun getPreferredAEADCipherSuites(keyIdentifier: KeyIdentifier): Set { - return keys - .getKey(keyIdentifier) - ?.getAEADCipherSuitePreferences(referenceDate) + fun getPreferredAEADCipherSuites(keyIdentifier: KeyIdentifier): Set? { + return (keys.getKey(keyIdentifier) + ?: throw NoSuchElementException( + "No subkey with key-id $keyIdentifier found on this key.")) + .getAEADCipherSuitePreferences(referenceDate) ?.toAEADCipherModes() - ?: throw NoSuchElementException( - "No subkey with key-id $keyIdentifier found on this key.") } @Deprecated("Pass KeyIdentifier instead.") - fun getPreferredAEADCipherSuites(keyId: Long): Set { + fun getPreferredAEADCipherSuites(keyId: Long): Set? { return getPreferredAEADCipherSuites(KeyIdentifier(keyId)) } 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 12478de4..eb19941c 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 @@ -17,11 +17,12 @@ import org.bouncycastle.openpgp.api.OpenPGPCertificate.OpenPGPSubkey import org.bouncycastle.openpgp.api.OpenPGPImplementation import org.bouncycastle.openpgp.api.OpenPGPKey import org.bouncycastle.openpgp.api.OpenPGPKey.OpenPGPSecretKey +import org.bouncycastle.openpgp.api.OpenPGPKeyEditor import org.bouncycastle.openpgp.api.OpenPGPSignature +import org.bouncycastle.openpgp.api.SignatureParameters import org.pgpainless.PGPainless import org.pgpainless.PGPainless.Companion.inspectKeyRing import org.pgpainless.algorithm.AlgorithmSuite -import org.pgpainless.algorithm.Feature import org.pgpainless.algorithm.KeyFlag import org.pgpainless.algorithm.OpenPGPKeyVersion import org.pgpainless.algorithm.SignatureType @@ -59,48 +60,54 @@ class SecretKeyRingEditor(var key: OpenPGPKey, override val referenceTime: Date callback: SelfSignatureSubpackets.Callback?, protector: SecretKeyRingProtector ): SecretKeyRingEditorInterface { - val sanitizedUserId = sanitizeUserId(userId).toString() - val primaryKey = secretKeyRing.secretKey - - val info = inspectKeyRing(secretKeyRing, referenceTime) + key = PGPainless.getInstance().toKey(secretKeyRing) + val info = inspectKeyRing(key, referenceTime) require(!info.isHardRevoked(userId)) { "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 = + info.preferredHashAlgorithms ?: AlgorithmSuite.defaultHashAlgorithms + val symmetricAlgorithmPreferences = + info.preferredSymmetricKeyAlgorithms ?: AlgorithmSuite.defaultSymmetricKeyAlgorithms + val compressionAlgorithmPreferences = + info.preferredCompressionAlgorithms ?: AlgorithmSuite.defaultCompressionAlgorithms + val aeadAlgorithmPreferences = + info.preferredAEADCipherSuites ?: AlgorithmSuite.defaultAEADAlgorithmSuites - val builder = - SelfSignatureBuilder(key.primarySecretKey, protector).apply { - hashedSubpackets.setSignatureCreationTime(referenceTime) - setSignatureType(SignatureType.POSITIVE_CERTIFICATION) - } - builder.hashedSubpackets.apply { - setKeyFlags(info.getKeyFlagsOf(primaryKey.keyID)) - hashAlgorithmPreferences - hashAlgorithmPreferences?.let { setPreferredHashAlgorithms(it) } - symmetricKeyAlgorithmPreferences?.let { setPreferredSymmetricKeyAlgorithms(it) } - compressionAlgorithmPreferences?.let { setPreferredCompressionAlgorithms(it) } - setFeatures(Feature.MODIFICATION_DETECTION) - } - builder.applyCallback(callback) - secretKeyRing = - injectCertification(secretKeyRing, sanitizedUserId, builder.build(sanitizedUserId)) - key = PGPainless.getInstance().toKey(secretKeyRing) + key = + OpenPGPKeyEditor(key, protector) + .addUserId( + sanitizeUserId(userId).toString(), + object : SignatureParameters.Callback { + override fun apply(parameters: SignatureParameters): SignatureParameters { + return parameters + .setSignatureCreationTime(referenceTime) + .setHashedSubpacketsFunction { subpacketGenerator -> + val subpackets = SignatureSubpackets(subpacketGenerator) + subpackets.setAppropriateIssuerInfo(secretKeyRing.publicKey) + + subpackets.setKeyFlags(info.getKeyFlagsOf(key.keyIdentifier)) + subpackets.setPreferredHashAlgorithms(hashAlgorithmPreferences) + subpackets.setPreferredSymmetricKeyAlgorithms( + symmetricAlgorithmPreferences) + subpackets.setPreferredCompressionAlgorithms( + compressionAlgorithmPreferences) + subpackets.setPreferredAEADCiphersuites( + aeadAlgorithmPreferences) + + callback?.modifyHashedSubpackets(subpackets) + subpacketGenerator + } + .setUnhashedSubpacketsFunction { subpacketGenerator -> + callback?.modifyUnhashedSubpackets( + SignatureSubpackets(subpacketGenerator)) + subpacketGenerator + } + } + }) + .done() + secretKeyRing = key.pgpSecretKeyRing return this } 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 94e7b1c5..f15f0071 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 @@ -113,7 +113,9 @@ interface SelfSignatureSubpackets : BaseSignatureSubpackets { fun setPreferredHashAlgorithms(algorithms: PreferredAlgorithms?): SelfSignatureSubpackets - fun setPreferredAEADCiphersuites(aeadAlgorithms: Set): SelfSignatureSubpackets + fun setPreferredAEADCiphersuites( + aeadAlgorithms: Collection + ): SelfSignatureSubpackets fun setPreferredAEADCiphersuites( algorithms: PreferredAEADCiphersuites.Builder? 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 7869ca08..e30c6100 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 @@ -314,7 +314,7 @@ class SignatureSubpackets( } override fun setPreferredAEADCiphersuites( - aeadAlgorithms: Set + aeadAlgorithms: Collection ): SignatureSubpackets = setPreferredAEADCiphersuites( PreferredAEADCiphersuites.builder(false).apply {