pgpainless/pgpainless-core/src/main/kotlin/org/pgpainless/signature/builder/AbstractSignatureBuilder.kt

136 lines
5.1 KiB
Kotlin

// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.signature.builder
import java.util.function.Predicate
import org.bouncycastle.openpgp.PGPException
import org.bouncycastle.openpgp.PGPPrivateKey
import org.bouncycastle.openpgp.PGPPublicKey
import org.bouncycastle.openpgp.PGPSecretKey
import org.bouncycastle.openpgp.PGPSignature
import org.bouncycastle.openpgp.PGPSignatureGenerator
import org.pgpainless.PGPainless
import org.pgpainless.algorithm.HashAlgorithm
import org.pgpainless.algorithm.SignatureType
import org.pgpainless.algorithm.negotiation.HashAlgorithmNegotiator
import org.pgpainless.implementation.ImplementationFactory
import org.pgpainless.key.protection.SecretKeyRingProtector
import org.pgpainless.key.protection.UnlockSecretKey
import org.pgpainless.key.util.OpenPgpKeyAttributeUtil
import org.pgpainless.signature.subpackets.SignatureSubpackets
import org.pgpainless.signature.subpackets.SignatureSubpacketsHelper
abstract class AbstractSignatureBuilder<B : AbstractSignatureBuilder<B>>(
protected val privateSigningKey: PGPPrivateKey,
protected val publicSigningKey: PGPPublicKey,
protected var _hashAlgorithm: HashAlgorithm,
protected var _signatureType: SignatureType,
protected val _hashedSubpackets: SignatureSubpackets,
protected val _unhashedSubpackets: SignatureSubpackets
) {
protected abstract val signatureTypePredicate: Predicate<SignatureType>
init {
require(signatureTypePredicate.test(_signatureType)) { "Invalid signature type." }
}
@Throws(PGPException::class)
protected constructor(
signatureType: SignatureType,
signingKey: PGPSecretKey,
protector: SecretKeyRingProtector,
hashAlgorithm: HashAlgorithm,
hashedSubpackets: SignatureSubpackets,
unhashedSubpackets: SignatureSubpackets
) : this(
UnlockSecretKey.unlockSecretKey(signingKey, protector),
signingKey.publicKey,
hashAlgorithm,
signatureType,
hashedSubpackets,
unhashedSubpackets)
@Throws(PGPException::class)
constructor(
signatureType: SignatureType,
signingKey: PGPSecretKey,
protector: SecretKeyRingProtector
) : this(
signatureType,
signingKey,
protector,
negotiateHashAlgorithm(signingKey.publicKey),
SignatureSubpackets.createHashedSubpackets(signingKey.publicKey),
SignatureSubpackets.createEmptySubpackets())
@Throws(PGPException::class)
constructor(
signingKey: PGPSecretKey,
protector: SecretKeyRingProtector,
archetypeSignature: PGPSignature
) : this(
SignatureType.requireFromCode(archetypeSignature.signatureType),
signingKey,
protector,
negotiateHashAlgorithm(signingKey.publicKey),
SignatureSubpackets.refreshHashedSubpackets(signingKey.publicKey, archetypeSignature),
SignatureSubpackets.refreshUnhashedSubpackets(archetypeSignature))
val hashAlgorithm = _hashAlgorithm
fun overrideHashAlgorithm(hashAlgorithm: HashAlgorithm) =
apply { _hashAlgorithm = hashAlgorithm } as B
/**
* Set the builders [SignatureType]. Note that only those types who are valid for the concrete
* subclass of this [AbstractSignatureBuilder] are allowed. Invalid choices result in an
* [IllegalArgumentException] to be thrown.
*
* @param type signature type
* @return builder
*/
fun setSignatureType(type: SignatureType) =
apply {
require(signatureTypePredicate.test(type)) { "Invalid signature type: $type" }
_signatureType = type
}
as B
/**
* Build an instance of [PGPSignatureGenerator] initialized with the signing key and with hashed
* and unhashed subpackets.
*
* @return pgp signature generator
* @throws PGPException if the signature generator cannot be initialized
*/
@Throws(PGPException::class)
protected fun buildAndInitSignatureGenerator(): PGPSignatureGenerator =
PGPSignatureGenerator(
ImplementationFactory.getInstance()
.getPGPContentSignerBuilder(
publicSigningKey.algorithm, hashAlgorithm.algorithmId))
.apply {
setUnhashedSubpackets(SignatureSubpacketsHelper.toVector(_unhashedSubpackets))
setHashedSubpackets(SignatureSubpacketsHelper.toVector(_hashedSubpackets))
init(_signatureType.code, privateSigningKey)
}
companion object {
/**
* Negotiate a [HashAlgorithm] to be used when creating the signature.
*
* @param publicKey signing public key
* @return hash algorithm
*/
@JvmStatic
fun negotiateHashAlgorithm(publicKey: PGPPublicKey): HashAlgorithm =
HashAlgorithmNegotiator.negotiateSignatureHashAlgorithm(PGPainless.getPolicy())
.negotiateHashAlgorithm(
OpenPgpKeyAttributeUtil.getOrGuessPreferredHashAlgorithms(publicKey))
}
}