From b164900a59e2c6aa1bbaf705bf95f7d6c79e9c5f Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Fri, 2 Feb 2024 16:34:04 +0100 Subject: [PATCH] Add test for adding image to key --- .../key/generation/GenerateOpenPgpKey.kt | 37 ++++++++---------- .../key/generation/GenerateOpenPgpKeyTest.kt | 26 ++++++++++-- .../src/test/resources/suzanne.jpg | Bin 0 -> 23761 bytes 3 files changed, 39 insertions(+), 24 deletions(-) create mode 100644 pgpainless-core/src/test/resources/suzanne.jpg diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/GenerateOpenPgpKey.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/GenerateOpenPgpKey.kt index 726ff644..ea899c2b 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/GenerateOpenPgpKey.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/generation/GenerateOpenPgpKey.kt @@ -4,8 +4,10 @@ package org.pgpainless.key.generation -import org.bouncycastle.bcpg.attr.ImageAttribute +import java.io.IOException +import java.io.InputStream import java.util.* +import org.bouncycastle.bcpg.attr.ImageAttribute import org.bouncycastle.openpgp.PGPSecretKey import org.bouncycastle.openpgp.PGPSecretKeyRing import org.bouncycastle.openpgp.PGPUserAttributeSubpacketVector @@ -19,8 +21,6 @@ import org.pgpainless.key.generation.type.KeyType import org.pgpainless.key.protection.SecretKeyRingProtector import org.pgpainless.policy.Policy import org.pgpainless.signature.subpackets.SelfSignatureSubpackets -import java.io.File -import java.io.IOException /** * OpenPGP key builder. This implementation supersedes the old [KeyRingBuilder]. @@ -41,16 +41,14 @@ open class GenerateOpenPgpKey( protected val preferences: AlgorithmSuite ) { - /** - * Make sure, that the chosen [KeyType] is allowed. - */ + /** Make sure, that the chosen [KeyType] is allowed. */ open fun sanitizePublicKeyAlgorithms(keyType: KeyType, policy: Policy) { verifyAlgorithmComplianceWithPolicy(keyType, policy) } /** - * Make sure, that the chosen [KeyType] complies to the given [Policy] by comparing it to the - * [Policy.PublicKeyAlgorithmPolicy]. + * Make sure, that the chosen [KeyType] complies to the given [Policy] by comparing it to + * the [Policy.PublicKeyAlgorithmPolicy]. * * @throws IllegalArgumentException if [keyType] fails to be accepted by [policy] */ @@ -73,7 +71,8 @@ open class GenerateOpenPgpKey( fun buildV4Key( keyType: KeyType, flags: List? = listOf(KeyFlag.CERTIFY_OTHER) - ): V4GenerateOpenPgpKey = V4GenerateOpenPgpKey(keyType, flags, policy, referenceTime, preferences) + ): V4GenerateOpenPgpKey = + V4GenerateOpenPgpKey(keyType, flags, policy, referenceTime, preferences) /** * Builder for version 4 OpenPGP keys. @@ -146,24 +145,22 @@ open class GenerateOpenPgpKey( } /** - * Add the contents of a JPEG file as image attribute to the key. + * Add the contents of a JPEG input stream as image attribute to the key. * - * @param jpegFile file containing a JPEG image - * @param subpacketsCallback callback to modify the user-attribute binding signature subpackets. + * @param jpegInputStream input stream containing a JPEG image + * @param subpacketsCallback callback to modify the user-attribute binding signature + * subpackets. * @return this */ @Throws(IOException::class) fun addJpegImage( - jpegFile: File, + jpegInputStream: InputStream, subpacketsCallback: SelfSignatureSubpackets.Callback = SelfSignatureSubpackets.nop() ) = apply { - jpegFile.inputStream() - .let { Streams.readAll(it) } - .let { - PGPUserAttributeSubpacketVectorGenerator().apply { - setImageAttribute(ImageAttribute.JPEG, it) - }.generate() - }.let { addUserAttribute(it, subpacketsCallback) } + PGPUserAttributeSubpacketVectorGenerator() + .apply { setImageAttribute(ImageAttribute.JPEG, Streams.readAll(jpegInputStream)) } + .generate() + .let { addUserAttribute(it, subpacketsCallback) } } /** diff --git a/pgpainless-core/src/test/kotlin/org/pgpainless/key/generation/GenerateOpenPgpKeyTest.kt b/pgpainless-core/src/test/kotlin/org/pgpainless/key/generation/GenerateOpenPgpKeyTest.kt index 978924c4..20cf8ba2 100644 --- a/pgpainless-core/src/test/kotlin/org/pgpainless/key/generation/GenerateOpenPgpKeyTest.kt +++ b/pgpainless-core/src/test/kotlin/org/pgpainless/key/generation/GenerateOpenPgpKeyTest.kt @@ -1,9 +1,15 @@ +// SPDX-FileCopyrightText: 2024 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + package org.pgpainless.key.generation import org.bouncycastle.bcpg.attr.ImageAttribute import org.bouncycastle.openpgp.PGPUserAttributeSubpacketVectorGenerator +import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows +import org.opentest4j.TestAbortedException import org.pgpainless.PGPainless import org.pgpainless.algorithm.KeyFlag import org.pgpainless.algorithm.PublicKeyAlgorithm @@ -14,6 +20,7 @@ import org.pgpainless.key.generation.type.xdh.XDHSpec import org.pgpainless.key.protection.SecretKeyRingProtector import org.pgpainless.policy.Policy import org.pgpainless.util.DateUtil +import java.io.InputStream class GenerateOpenPgpKeyTest { @@ -27,7 +34,8 @@ class GenerateOpenPgpKeyTest { .addUserAttribute( PGPUserAttributeSubpacketVectorGenerator() .apply { setImageAttribute(ImageAttribute.JPEG, byteArrayOf()) } - .generate()) + .generate(), + ) .addEncryptionSubkey(KeyType.XDH(XDHSpec._X25519)) .addSigningSubkey(KeyType.EDDSA(EdDSACurve._Ed25519)) .build(SecretKeyRingProtector.unprotectedKeys()) @@ -59,7 +67,8 @@ class GenerateOpenPgpKeyTest { val policy = Policy( publicKeyAlgorithmPolicy = - Policy.PublicKeyAlgorithmPolicy(mapOf(PublicKeyAlgorithm.RSA_GENERAL to 4096))) + Policy.PublicKeyAlgorithmPolicy(mapOf(PublicKeyAlgorithm.RSA_GENERAL to 4096)), + ) val builder = GenerateOpenPgpKey(policy) assertThrows { @@ -74,8 +83,17 @@ class GenerateOpenPgpKeyTest { @Test fun testKeyGenerationWithJPEGAttribute() { - GenerateOpenPgpKey(Policy.getInstance()) + val key = GenerateOpenPgpKey(Policy.getInstance()) .buildV4Key(KeyType.EDDSA(EdDSACurve._Ed25519)) - .addJpegImage() + .addJpegImage(requireResource("suzanne.jpg")) + .build() + + assertTrue(key.publicKey.userAttributes.hasNext()) + } + + private fun requireResource(resourceName: String): InputStream { + return javaClass.classLoader.getResourceAsStream(resourceName) + ?: throw TestAbortedException( + "Cannot read resource $resourceName: InputStream is null.") } } diff --git a/pgpainless-core/src/test/resources/suzanne.jpg b/pgpainless-core/src/test/resources/suzanne.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c343ef095195149d44dff4f7f81fa9fbd69d4e17 GIT binary patch literal 23761 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 TYytvQ{4jtO-Xlh$zW4qYFOuA9 literal 0 HcmV?d00001