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

Add documentation

This commit is contained in:
Paul Schaub 2025-05-13 13:53:55 +02:00
parent 2014db4112
commit d839e2eed1
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
10 changed files with 144 additions and 43 deletions

View file

@ -8,11 +8,12 @@ import org.bouncycastle.openpgp.api.MessageEncryptionMechanism
/** /**
* AEAD Algorithm. * AEAD Algorithm.
*
* @param algorithmId numeric algorithm id * @param algorithmId numeric algorithm id
* @param ivLength length of the initialization vector * @param ivLength length of the initialization vector
* @param tagLength length of the tag * @param tagLength length of the tag
* * @see
* @see [RFC9580 - AEAD Algorithms](https://www.rfc-editor.org/rfc/rfc9580.html#name-aead-algorithms) * [RFC9580 - AEAD Algorithms](https://www.rfc-editor.org/rfc/rfc9580.html#name-aead-algorithms)
*/ */
enum class AEADAlgorithm(val algorithmId: Int, val ivLength: Int, val tagLength: Int) { enum class AEADAlgorithm(val algorithmId: Int, val ivLength: Int, val tagLength: Int) {
@ -41,8 +42,8 @@ enum class AEADAlgorithm(val algorithmId: Int, val ivLength: Int, val tagLength:
; ;
/** /**
* Return a [MessageEncryptionMechanism] instance representing AEAD using this algorithm and * Return a [MessageEncryptionMechanism] instance representing AEAD using this algorithm and the
* the given [SymmetricKeyAlgorithm]. * given [SymmetricKeyAlgorithm].
* *
* @param ciphermode symmetric key algorithm * @param ciphermode symmetric key algorithm
* @return MessageEncryptionMechanism representing aead(this, ciphermode) * @return MessageEncryptionMechanism representing aead(this, ciphermode)
@ -53,8 +54,8 @@ enum class AEADAlgorithm(val algorithmId: Int, val ivLength: Int, val tagLength:
companion object { companion object {
/** /**
* Parse an [AEADAlgorithm] from an algorithm id. * Parse an [AEADAlgorithm] from an algorithm id. If no matching [AEADAlgorithm] is known,
* If no matching [AEADAlgorithm] is known, return `null`. * return `null`.
* *
* @param id algorithm id * @param id algorithm id
* @return aeadAlgorithm or null * @return aeadAlgorithm or null
@ -65,8 +66,8 @@ enum class AEADAlgorithm(val algorithmId: Int, val ivLength: Int, val tagLength:
} }
/** /**
* Parse an [AEADAlgorithm] from an algorithm id. * Parse an [AEADAlgorithm] from an algorithm id. If no matching [AEADAlgorithm] is known,
* If no matching [AEADAlgorithm] is known, throw a [NoSuchElementException]. * throw a [NoSuchElementException].
* *
* @param id algorithm id * @param id algorithm id
* @return aeadAlgorithm * @return aeadAlgorithm

View file

@ -9,6 +9,11 @@ import org.bouncycastle.openpgp.api.OpenPGPCertificate
import org.bouncycastle.openpgp.api.OpenPGPCertificate.OpenPGPComponentKey import org.bouncycastle.openpgp.api.OpenPGPCertificate.OpenPGPComponentKey
import org.pgpainless.algorithm.OpenPGPKeyVersion import org.pgpainless.algorithm.OpenPGPKeyVersion
/**
* Return the [OpenPGPComponentKey] that issued the given [PGPOnePassSignature].
*
* @param ops one pass signature
*/
fun OpenPGPCertificate.getSigningKeyFor(ops: PGPOnePassSignature): OpenPGPComponentKey? = fun OpenPGPCertificate.getSigningKeyFor(ops: PGPOnePassSignature): OpenPGPComponentKey? =
this.getKey(ops.keyIdentifier) this.getKey(ops.keyIdentifier)

View file

@ -11,10 +11,20 @@ import org.bouncycastle.openpgp.api.OpenPGPImplementation
import org.bouncycastle.openpgp.operator.PGPDataEncryptorBuilder import org.bouncycastle.openpgp.operator.PGPDataEncryptorBuilder
import org.bouncycastle.openpgp.operator.PGPDigestCalculator import org.bouncycastle.openpgp.operator.PGPDigestCalculator
/**
* Return a [PGPDigestCalculator] that is based on [HashAlgorithmTags.SHA1], used for key checksum
* calculations.
*/
fun OpenPGPImplementation.checksumCalculator(): PGPDigestCalculator { fun OpenPGPImplementation.checksumCalculator(): PGPDigestCalculator {
return pgpDigestCalculatorProvider().get(HashAlgorithmTags.SHA1) return pgpDigestCalculatorProvider().get(HashAlgorithmTags.SHA1)
} }
/**
* Return a [PGPDataEncryptorBuilder] for the given [MessageEncryptionMechanism].
*
* @param mechanism
* @return data encryptor builder
*/
fun OpenPGPImplementation.pgpDataEncryptorBuilder( fun OpenPGPImplementation.pgpDataEncryptorBuilder(
mechanism: MessageEncryptionMechanism mechanism: MessageEncryptionMechanism
): PGPDataEncryptorBuilder { ): PGPDataEncryptorBuilder {
@ -32,7 +42,5 @@ fun OpenPGPImplementation.pgpDataEncryptorBuilder(
it.setUseV5AEAD() it.setUseV5AEAD()
} }
} }
return it
} }
} }

View file

@ -5,17 +5,25 @@
package org.pgpainless.bouncycastle.extensions package org.pgpainless.bouncycastle.extensions
import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData
import org.bouncycastle.openpgp.PGPSignature
import org.bouncycastle.openpgp.api.OpenPGPKey import org.bouncycastle.openpgp.api.OpenPGPKey
import org.bouncycastle.openpgp.api.OpenPGPKey.OpenPGPPrivateKey import org.bouncycastle.openpgp.api.OpenPGPKey.OpenPGPPrivateKey
import org.bouncycastle.openpgp.api.OpenPGPKey.OpenPGPSecretKey import org.bouncycastle.openpgp.api.OpenPGPKey.OpenPGPSecretKey
import org.pgpainless.util.Passphrase import org.pgpainless.util.Passphrase
/**
* Return the [OpenPGPSecretKey] that can be used to decrypt the given [PGPPublicKeyEncryptedData].
*
* @param pkesk public-key encrypted session-key packet
* @return secret key or null if no matching secret key was found
*/
fun OpenPGPKey.getSecretKeyFor(pkesk: PGPPublicKeyEncryptedData): OpenPGPSecretKey? = fun OpenPGPKey.getSecretKeyFor(pkesk: PGPPublicKeyEncryptedData): OpenPGPSecretKey? =
this.getSecretKey(pkesk.keyIdentifier) this.getSecretKey(pkesk.keyIdentifier)
fun OpenPGPKey.getSecretKeyFor(signature: PGPSignature): OpenPGPSecretKey? = /**
this.getSecretKey(signature.fingerprint!!.keyIdentifier) * Unlock the [OpenPGPSecretKey], returning the unlocked [OpenPGPPrivateKey].
*
fun OpenPGPKey.OpenPGPSecretKey.unlock(passphrase: Passphrase): OpenPGPPrivateKey = * @param passphrase passphrase to unlock the key
* @return unlocked [OpenPGPPrivateKey]
*/
fun OpenPGPSecretKey.unlock(passphrase: Passphrase): OpenPGPPrivateKey =
this.unlock(passphrase.getChars()) this.unlock(passphrase.getChars())

View file

@ -16,11 +16,22 @@ import org.pgpainless.PGPainless
import org.pgpainless.key.OpenPgpFingerprint import org.pgpainless.key.OpenPgpFingerprint
import org.pgpainless.key.SubkeyIdentifier 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].
*
* @param subkeyIdentifier subkey identifier
* @return true if the [PGPKeyRing] contains the [SubkeyIdentifier]
*/
fun PGPKeyRing.matches(subkeyIdentifier: SubkeyIdentifier): Boolean = fun PGPKeyRing.matches(subkeyIdentifier: SubkeyIdentifier): Boolean =
this.publicKey.keyIdentifier.matches(subkeyIdentifier.certificateIdentifier) && this.publicKey.keyIdentifier.matches(subkeyIdentifier.certificateIdentifier) &&
this.getPublicKey(subkeyIdentifier.componentKeyIdentifier) != null this.getPublicKey(subkeyIdentifier.componentKeyIdentifier) != null
/**
* Return true, if this [PGPKeyRing] contains the given [componentKey].
*
* @param componentKey component key
* @return true if the [PGPKeyRing] contains the [componentKey]
*/
fun PGPKeyRing.matches(componentKey: OpenPGPComponentKey): Boolean = fun PGPKeyRing.matches(componentKey: OpenPGPComponentKey): Boolean =
this.matches(SubkeyIdentifier(componentKey)) this.matches(SubkeyIdentifier(componentKey))
@ -60,13 +71,37 @@ fun PGPKeyRing.hasPublicKey(fingerprint: OpenPgpFingerprint): Boolean =
fun PGPKeyRing.getPublicKey(fingerprint: OpenPgpFingerprint): PGPPublicKey? = fun PGPKeyRing.getPublicKey(fingerprint: OpenPgpFingerprint): PGPPublicKey? =
this.getPublicKey(fingerprint.keyIdentifier) this.getPublicKey(fingerprint.keyIdentifier)
/**
* Return the [PGPPublicKey] with the given [keyIdentifier], or throw a [NoSuchElementException] if
* no matching public key was found.
*
* @param keyIdentifier key identifier
* @return public key
* @throws NoSuchElementException if no matching public key was found
*/
fun PGPKeyRing.requirePublicKey(keyIdentifier: KeyIdentifier): PGPPublicKey = fun PGPKeyRing.requirePublicKey(keyIdentifier: KeyIdentifier): PGPPublicKey =
getPublicKey(keyIdentifier) getPublicKey(keyIdentifier)
?: throw NoSuchElementException("OpenPGP key does not contain key with id $keyIdentifier.") ?: throw NoSuchElementException("OpenPGP key does not contain key with id $keyIdentifier.")
/**
* Return the [PGPPublicKey] with the given key-id, or throw a [NoSuchElementException] if no
* matching public key was found.
*
* @param keyId key id
* @return public key
* @throws NoSuchElementException if no matching public key was found
*/
@Deprecated("Pass in a KeyIdentifier instead.") @Deprecated("Pass in a KeyIdentifier instead.")
fun PGPKeyRing.requirePublicKey(keyId: Long): PGPPublicKey = requirePublicKey(KeyIdentifier(keyId)) fun PGPKeyRing.requirePublicKey(keyId: Long): PGPPublicKey = requirePublicKey(KeyIdentifier(keyId))
/**
* Return the [PGPPublicKey] with the given [fingerprint], or throw a [NoSuchElementException] if no
* matching public key was found.
*
* @param fingerprint key fingerprint
* @return public key
* @throws NoSuchElementException if no matching public key was found
*/
fun PGPKeyRing.requirePublicKey(fingerprint: OpenPgpFingerprint): PGPPublicKey = fun PGPKeyRing.requirePublicKey(fingerprint: OpenPgpFingerprint): PGPPublicKey =
requirePublicKey(fingerprint.keyIdentifier) requirePublicKey(fingerprint.keyIdentifier)
@ -90,9 +125,21 @@ val PGPKeyRing.openPgpFingerprint: OpenPgpFingerprint
/** Return this OpenPGP key as an ASCII armored String. */ /** Return this OpenPGP key as an ASCII armored String. */
fun PGPKeyRing.toAsciiArmor(): String = PGPainless.asciiArmor(this) fun PGPKeyRing.toAsciiArmor(): String = PGPainless.asciiArmor(this)
/**
* Convert the given [PGPKeyRing] into an [OpenPGPCertificate].
*
* @return certificate
*/
@Deprecated("Use toOpenPGPCertificate(implementation) instead.") @Deprecated("Use toOpenPGPCertificate(implementation) instead.")
fun PGPKeyRing.toOpenPGPCertificate(): OpenPGPCertificate = fun PGPKeyRing.toOpenPGPCertificate(): OpenPGPCertificate =
toOpenPGPCertificate(PGPainless.getInstance().implementation) toOpenPGPCertificate(PGPainless.getInstance().implementation)
/**
* Convert the given [PGPKeyRing] into an [OpenPGPCertificate] using the given
* [OpenPGPImplementation].
*
* @param implementation OpenPGP implementation
* @return certificate
*/
fun PGPKeyRing.toOpenPGPCertificate(implementation: OpenPGPImplementation): OpenPGPCertificate = fun PGPKeyRing.toOpenPGPCertificate(implementation: OpenPGPImplementation): OpenPGPCertificate =
OpenPGPCertificate(this, implementation) OpenPGPCertificate(this, implementation)

View file

@ -98,12 +98,30 @@ fun PGPSecretKeyRing.getSecretKeyFor(signature: PGPSignature): PGPSecretKey? =
fun PGPSecretKeyRing.getSecretKeyFor(onePassSignature: PGPOnePassSignature): PGPSecretKey? = fun PGPSecretKeyRing.getSecretKeyFor(onePassSignature: PGPOnePassSignature): PGPSecretKey? =
this.getSecretKey(onePassSignature.keyIdentifier) this.getSecretKey(onePassSignature.keyIdentifier)
/**
* Return the [PGPSecretKey] that can be used to decrypt the given [PGPPublicKeyEncryptedData]
* packet.
*
* @param pkesk public-key encrypted session-key packet
* @return secret-key or null if no matching secret key was found
*/
fun PGPSecretKeyRing.getSecretKeyFor(pkesk: PGPPublicKeyEncryptedData): PGPSecretKey? = fun PGPSecretKeyRing.getSecretKeyFor(pkesk: PGPPublicKeyEncryptedData): PGPSecretKey? =
this.getSecretKey(pkesk.keyIdentifier) this.getSecretKey(pkesk.keyIdentifier)
/**
* Convert the [PGPSecretKeyRing] into an [OpenPGPKey].
*
* @return key
*/
@Deprecated("Use toOpenPGPKey(implementation) instead.") @Deprecated("Use toOpenPGPKey(implementation) instead.")
fun PGPSecretKeyRing.toOpenPGPKey(): OpenPGPKey = fun PGPSecretKeyRing.toOpenPGPKey(): OpenPGPKey =
toOpenPGPKey(PGPainless.getInstance().implementation) toOpenPGPKey(PGPainless.getInstance().implementation)
/**
* Convert the [PGPSecretKeyRing] into an [OpenPGPKey] using the given [OpenPGPImplementation].
*
* @param implementation openpgp implementation
* @return key
*/
fun PGPSecretKeyRing.toOpenPGPKey(implementation: OpenPGPImplementation): OpenPGPKey = fun PGPSecretKeyRing.toOpenPGPKey(implementation: OpenPGPImplementation): OpenPGPKey =
OpenPGPKey(this, implementation) OpenPGPKey(this, implementation)

View file

@ -57,17 +57,29 @@ val PGPSignature.issuerKeyId: Long
} }
} }
/** 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.
*
* @param fingerprint fingerprint of the key
* @return true if signature was likely issued by the key
*/
fun PGPSignature.wasIssuedBy(fingerprint: OpenPgpFingerprint): Boolean = fun PGPSignature.wasIssuedBy(fingerprint: OpenPgpFingerprint): Boolean =
wasIssuedBy(fingerprint.keyIdentifier) wasIssuedBy(fingerprint.keyIdentifier)
/** /**
* Return true, if the signature was likely issued by a key with the given fingerprint. * Return true, if the signature was likely issued by the given key.
* *
* @param key key * @param key key
* @return true if signature was likely issued by the key
*/ */
fun PGPSignature.wasIssuedBy(key: PGPPublicKey): Boolean = wasIssuedBy(key.keyIdentifier) fun PGPSignature.wasIssuedBy(key: PGPPublicKey): Boolean = wasIssuedBy(key.keyIdentifier)
/**
* Return true, if the signature was likely issued by a key with the given identifier.
*
* @param keyIdentifier key identifier
* @return true if signature was likely issued by the key
*/
fun PGPSignature.wasIssuedBy(keyIdentifier: KeyIdentifier): Boolean = fun PGPSignature.wasIssuedBy(keyIdentifier: KeyIdentifier): Boolean =
KeyIdentifier.matches(this.keyIdentifiers, keyIdentifier, true) KeyIdentifier.matches(this.keyIdentifiers, keyIdentifier, true)
@ -87,6 +99,13 @@ val PGPSignature.isHardRevocation
else -> false // Not a revocation else -> false // Not a revocation
} }
/**
* Assert that the signatures creation time falls into the period between [notBefore] and
* [notAfter].
*
* @param notBefore lower bound. If null, do not check the lower bound
* @param notAfter upper bound. If null, do not check the upper bound
*/
fun PGPSignature.assertCreatedInBounds(notBefore: Date?, notAfter: Date?) { fun PGPSignature.assertCreatedInBounds(notBefore: Date?, notAfter: Date?) {
if (notBefore != null && creationTime < notBefore) { if (notBefore != null && creationTime < notBefore) {
throw SignatureValidationException( throw SignatureValidationException(
@ -102,19 +121,35 @@ fun PGPSignature.assertCreatedInBounds(notBefore: Date?, notAfter: Date?) {
} }
} }
/**
* Deduce a [RevocationState] from the signature. Non-revocation signatures result in
* [RevocationState.notRevoked]. Hard revocations result in [RevocationState.hardRevoked], while
* soft revocations return [RevocationState.softRevoked]
*
* @return revocation state
*/
fun PGPSignature?.toRevocationState() = fun PGPSignature?.toRevocationState() =
if (this == null) RevocationState.notRevoked() if (this == null) RevocationState.notRevoked()
else if (isHardRevocation) RevocationState.hardRevoked() else if (isHardRevocation) RevocationState.hardRevoked()
else RevocationState.softRevoked(creationTime) else RevocationState.softRevoked(creationTime)
/** The signatures issuer fingerprint as [OpenPgpFingerprint]. */
val PGPSignature.fingerprint: OpenPgpFingerprint? val PGPSignature.fingerprint: OpenPgpFingerprint?
get() = SignatureSubpacketsUtil.getIssuerFingerprintAsOpenPgpFingerprint(this) get() = SignatureSubpacketsUtil.getIssuerFingerprintAsOpenPgpFingerprint(this)
/** The signatures [PublicKeyAlgorithm]. */
val PGPSignature.publicKeyAlgorithm: PublicKeyAlgorithm val PGPSignature.publicKeyAlgorithm: PublicKeyAlgorithm
get() = PublicKeyAlgorithm.requireFromId(keyAlgorithm) get() = PublicKeyAlgorithm.requireFromId(keyAlgorithm)
/** The signatures [HashAlgorithm]. */
val PGPSignature.signatureHashAlgorithm: HashAlgorithm val PGPSignature.signatureHashAlgorithm: HashAlgorithm
get() = HashAlgorithm.requireFromId(hashAlgorithm) get() = HashAlgorithm.requireFromId(hashAlgorithm)
/**
* Return true if the signature has the given [SignatureType].
*
* @param type signature type
* @return true if the signature type matches the signatures type
*/
fun PGPSignature.isOfType(type: SignatureType): Boolean = fun PGPSignature.isOfType(type: SignatureType): Boolean =
SignatureType.fromCode(signatureType) == type SignatureType.fromCode(signatureType) == type

View file

@ -7,6 +7,7 @@ package org.pgpainless.bouncycastle.extensions
import org.bouncycastle.bcpg.sig.PreferredAEADCiphersuites import org.bouncycastle.bcpg.sig.PreferredAEADCiphersuites
import org.pgpainless.algorithm.AEADCipherMode import org.pgpainless.algorithm.AEADCipherMode
/** Convert the [PreferredAEADCiphersuites] packet into a [Set] of [AEADCipherMode]. */
fun PreferredAEADCiphersuites?.toAEADCipherModes(): Set<AEADCipherMode> { fun PreferredAEADCiphersuites?.toAEADCipherModes(): Set<AEADCipherMode> {
return this?.algorithms?.asSequence()?.map { AEADCipherMode(it) }?.toSet() ?: setOf() return this?.algorithms?.asSequence()?.map { AEADCipherMode(it) }?.toSet() ?: setOf()
} }

View file

@ -9,16 +9,19 @@ import org.pgpainless.algorithm.CompressionAlgorithm
import org.pgpainless.algorithm.HashAlgorithm import org.pgpainless.algorithm.HashAlgorithm
import org.pgpainless.algorithm.SymmetricKeyAlgorithm import org.pgpainless.algorithm.SymmetricKeyAlgorithm
/** Convert the [PreferredAlgorithms] packet into a [Set] of [HashAlgorithm] preferences. */
fun PreferredAlgorithms?.toHashAlgorithms(): Set<HashAlgorithm> { fun PreferredAlgorithms?.toHashAlgorithms(): Set<HashAlgorithm> {
return this?.preferences?.asSequence()?.map { HashAlgorithm.requireFromId(it) }?.toSet() return this?.preferences?.asSequence()?.map { HashAlgorithm.requireFromId(it) }?.toSet()
?: setOf() ?: setOf()
} }
/** Convert the [PreferredAlgorithms] packet into a [Set] of [SymmetricKeyAlgorithm] preferences. */
fun PreferredAlgorithms?.toSymmetricKeyAlgorithms(): Set<SymmetricKeyAlgorithm> { fun PreferredAlgorithms?.toSymmetricKeyAlgorithms(): Set<SymmetricKeyAlgorithm> {
return this?.preferences?.asSequence()?.map { SymmetricKeyAlgorithm.requireFromId(it) }?.toSet() return this?.preferences?.asSequence()?.map { SymmetricKeyAlgorithm.requireFromId(it) }?.toSet()
?: setOf() ?: setOf()
} }
/** Convert the [PreferredAlgorithms] packet into a [Set] of [CompressionAlgorithm] preferences. */
fun PreferredAlgorithms?.toCompressionAlgorithms(): Set<CompressionAlgorithm> { fun PreferredAlgorithms?.toCompressionAlgorithms(): Set<CompressionAlgorithm> {
return this?.preferences?.asSequence()?.map { CompressionAlgorithm.requireFromId(it) }?.toSet() return this?.preferences?.asSequence()?.map { CompressionAlgorithm.requireFromId(it) }?.toSet()
?: setOf() ?: setOf()

View file

@ -4,18 +4,12 @@
package org.pgpainless.bouncycastle.extensions package org.pgpainless.bouncycastle.extensions
import java.io.ByteArrayOutputStream
import org.junit.jupiter.api.Assertions.assertFalse import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertNotNull import org.junit.jupiter.api.Assertions.assertNotNull
import org.junit.jupiter.api.Assertions.assertNull
import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.assertThrows
import org.pgpainless.PGPainless
import org.pgpainless.encryption_signing.ProducerOptions
import org.pgpainless.encryption_signing.SigningOptions
import org.pgpainless.key.TestKeys import org.pgpainless.key.TestKeys
import org.pgpainless.key.protection.SecretKeyRingProtector
class PGPSecretKeyRingExtensionsTest { class PGPSecretKeyRingExtensionsTest {
@ -46,23 +40,4 @@ class PGPSecretKeyRingExtensionsTest {
} }
assertThrows<NoSuchElementException> { key.requireSecretKey(TestKeys.ROMEO_FINGERPRINT) } assertThrows<NoSuchElementException> { key.requireSecretKey(TestKeys.ROMEO_FINGERPRINT) }
} }
@Test
fun testGetSecretKeyForSignature() {
val key = TestKeys.getEmilKey()
val signer =
PGPainless.getInstance()
.generateMessage()
.onOutputStream(ByteArrayOutputStream())
.withOptions(
ProducerOptions.sign(
SigningOptions.get()
.addDetachedSignature(SecretKeyRingProtector.unprotectedKeys(), key)))
signer.write("Hello, World!\n".toByteArray())
signer.close()
val sig = signer.result.detachedSignatures.first().value.first()
assertNotNull(key.getSecretKeyFor(sig))
assertNull(TestKeys.getRomeoSecretKeyRing().getSecretKeyFor(sig))
}
} }