mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-09-26 01:29:33 +02:00
137 lines
5.4 KiB
Kotlin
137 lines
5.4 KiB
Kotlin
|
// SPDX-FileCopyrightText: 2024 Paul Schaub <vanitasvitae@fsfe.org>
|
||
|
//
|
||
|
// SPDX-License-Identifier: Apache-2.0
|
||
|
|
||
|
package org.pgpainless.sop
|
||
|
|
||
|
import java.io.OutputStream
|
||
|
import java.lang.RuntimeException
|
||
|
import java.security.InvalidAlgorithmParameterException
|
||
|
import java.security.NoSuchAlgorithmException
|
||
|
import org.bouncycastle.openpgp.PGPException
|
||
|
import org.bouncycastle.openpgp.PGPSecretKeyRing
|
||
|
import org.pgpainless.PGPainless
|
||
|
import org.pgpainless.algorithm.KeyFlag
|
||
|
import org.pgpainless.key.generation.KeyRingBuilder
|
||
|
import org.pgpainless.key.generation.KeySpec
|
||
|
import org.pgpainless.key.generation.type.KeyType
|
||
|
import org.pgpainless.key.generation.type.eddsa.EdDSACurve
|
||
|
import org.pgpainless.key.generation.type.rsa.RsaLength
|
||
|
import org.pgpainless.key.generation.type.xdh.XDHSpec
|
||
|
import org.pgpainless.util.ArmorUtils
|
||
|
import org.pgpainless.util.Passphrase
|
||
|
import sop.Profile
|
||
|
import sop.Ready
|
||
|
import sop.exception.SOPGPException
|
||
|
import sop.operation.GenerateKey
|
||
|
|
||
|
/** Implementation of the `generate-key` operation using PGPainless. */
|
||
|
class GenerateKeyImpl : GenerateKey {
|
||
|
|
||
|
companion object {
|
||
|
@JvmField
|
||
|
val CURVE25519_PROFILE =
|
||
|
Profile(
|
||
|
"draft-koch-eddsa-for-openpgp-00", "Generate EdDSA / ECDH keys using Curve25519")
|
||
|
@JvmField val RSA4096_PROFILE = Profile("rfc4880", "Generate 4096-bit RSA keys")
|
||
|
|
||
|
@JvmField val SUPPORTED_PROFILES = listOf(CURVE25519_PROFILE, RSA4096_PROFILE)
|
||
|
}
|
||
|
|
||
|
private val userIds = mutableSetOf<String>()
|
||
|
private var armor = true
|
||
|
private var signingOnly = false
|
||
|
private var passphrase = Passphrase.emptyPassphrase()
|
||
|
private var profile = CURVE25519_PROFILE.name
|
||
|
|
||
|
override fun generate(): Ready {
|
||
|
try {
|
||
|
val key = generateKeyWithProfile(profile, userIds, passphrase, signingOnly)
|
||
|
return object : Ready() {
|
||
|
override fun writeTo(outputStream: OutputStream) {
|
||
|
if (armor) {
|
||
|
val armorOut = ArmorUtils.toAsciiArmoredStream(key, outputStream)
|
||
|
key.encode(armorOut)
|
||
|
armorOut.close()
|
||
|
} else {
|
||
|
key.encode(outputStream)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} catch (e: InvalidAlgorithmParameterException) {
|
||
|
throw SOPGPException.UnsupportedAsymmetricAlgo("Unsupported asymmetric algorithm.", e)
|
||
|
} catch (e: NoSuchAlgorithmException) {
|
||
|
throw SOPGPException.UnsupportedAsymmetricAlgo("Unsupported asymmetric algorithm.", e)
|
||
|
} catch (e: PGPException) {
|
||
|
throw RuntimeException(e)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
override fun noArmor(): GenerateKey = apply { armor = false }
|
||
|
|
||
|
override fun profile(profile: String): GenerateKey = apply {
|
||
|
this.profile =
|
||
|
SUPPORTED_PROFILES.find { it.name == profile }?.name
|
||
|
?: throw SOPGPException.UnsupportedProfile("generate-key", profile)
|
||
|
}
|
||
|
|
||
|
override fun signingOnly(): GenerateKey = apply { signingOnly = true }
|
||
|
|
||
|
override fun userId(userId: String): GenerateKey = apply { userIds.add(userId) }
|
||
|
|
||
|
override fun withKeyPassword(password: String): GenerateKey = apply {
|
||
|
this.passphrase = Passphrase.fromPassword(password)
|
||
|
}
|
||
|
|
||
|
private fun generateKeyWithProfile(
|
||
|
profile: String,
|
||
|
userIds: Set<String>,
|
||
|
passphrase: Passphrase,
|
||
|
signingOnly: Boolean
|
||
|
): PGPSecretKeyRing {
|
||
|
val keyBuilder: KeyRingBuilder =
|
||
|
when (profile) {
|
||
|
CURVE25519_PROFILE.name ->
|
||
|
PGPainless.buildKeyRing()
|
||
|
.setPrimaryKey(
|
||
|
KeySpec.getBuilder(
|
||
|
KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.CERTIFY_OTHER))
|
||
|
.addSubkey(
|
||
|
KeySpec.getBuilder(
|
||
|
KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.SIGN_DATA))
|
||
|
.apply {
|
||
|
if (!signingOnly) {
|
||
|
addSubkey(
|
||
|
KeySpec.getBuilder(
|
||
|
KeyType.XDH(XDHSpec._X25519),
|
||
|
KeyFlag.ENCRYPT_COMMS,
|
||
|
KeyFlag.ENCRYPT_STORAGE))
|
||
|
}
|
||
|
}
|
||
|
RSA4096_PROFILE.name -> {
|
||
|
PGPainless.buildKeyRing()
|
||
|
.setPrimaryKey(
|
||
|
KeySpec.getBuilder(KeyType.RSA(RsaLength._4096), KeyFlag.CERTIFY_OTHER))
|
||
|
.addSubkey(
|
||
|
KeySpec.getBuilder(KeyType.RSA(RsaLength._4096), KeyFlag.SIGN_DATA))
|
||
|
.apply {
|
||
|
if (!signingOnly) {
|
||
|
addSubkey(
|
||
|
KeySpec.getBuilder(
|
||
|
KeyType.RSA(RsaLength._4096),
|
||
|
KeyFlag.ENCRYPT_COMMS,
|
||
|
KeyFlag.ENCRYPT_STORAGE))
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else -> throw SOPGPException.UnsupportedProfile("generate-key", profile)
|
||
|
}
|
||
|
|
||
|
userIds.forEach { keyBuilder.addUserId(it) }
|
||
|
if (!passphrase.isEmpty) {
|
||
|
keyBuilder.setPassphrase(passphrase)
|
||
|
}
|
||
|
return keyBuilder.build()
|
||
|
}
|
||
|
}
|