mirror of
https://github.com/pgpainless/pgpainless.git
synced 2025-09-09 18:29:39 +02:00
Work on AlgorithmSuite
This commit is contained in:
parent
67af718db9
commit
5de1e6a56d
8 changed files with 158 additions and 20 deletions
|
@ -14,6 +14,7 @@ import org.bouncycastle.openpgp.api.OpenPGPApi
|
||||||
import org.bouncycastle.openpgp.api.OpenPGPCertificate
|
import org.bouncycastle.openpgp.api.OpenPGPCertificate
|
||||||
import org.bouncycastle.openpgp.api.OpenPGPImplementation
|
import org.bouncycastle.openpgp.api.OpenPGPImplementation
|
||||||
import org.bouncycastle.openpgp.api.OpenPGPKey
|
import org.bouncycastle.openpgp.api.OpenPGPKey
|
||||||
|
import org.bouncycastle.openpgp.api.OpenPGPKeyGenerator
|
||||||
import org.bouncycastle.openpgp.api.OpenPGPKeyReader
|
import org.bouncycastle.openpgp.api.OpenPGPKeyReader
|
||||||
import org.bouncycastle.openpgp.api.bc.BcOpenPGPApi
|
import org.bouncycastle.openpgp.api.bc.BcOpenPGPApi
|
||||||
import org.pgpainless.algorithm.OpenPGPKeyVersion
|
import org.pgpainless.algorithm.OpenPGPKeyVersion
|
||||||
|
@ -43,8 +44,23 @@ class PGPainless(
|
||||||
api = BcOpenPGPApi(implementation)
|
api = BcOpenPGPApi(implementation)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun generateKey(version: OpenPGPKeyVersion = OpenPGPKeyVersion.v4): KeyRingTemplates =
|
@JvmOverloads
|
||||||
KeyRingTemplates(version)
|
fun generateKey(
|
||||||
|
version: OpenPGPKeyVersion = OpenPGPKeyVersion.v4,
|
||||||
|
creationTime: Date = Date()
|
||||||
|
): KeyRingTemplates = KeyRingTemplates(version, creationTime)
|
||||||
|
|
||||||
|
@JvmOverloads
|
||||||
|
fun buildKey(
|
||||||
|
version: OpenPGPKeyVersion = OpenPGPKeyVersion.v4,
|
||||||
|
creationTime: Date = Date()
|
||||||
|
): OpenPGPKeyGenerator =
|
||||||
|
OpenPGPKeyGenerator(
|
||||||
|
implementation, version.numeric, version == OpenPGPKeyVersion.v6, creationTime)
|
||||||
|
.apply {
|
||||||
|
val genAlgs = algorithmPolicy.keyGenerationAlgorithmSuite
|
||||||
|
setDefaultFeatures(genAlgs.features.toSignatureSubpacketsFunction(true))
|
||||||
|
}
|
||||||
|
|
||||||
fun readKey(): OpenPGPKeyReader = api.readKeyOrCertificate()
|
fun readKey(): OpenPGPKeyReader = api.readKeyOrCertificate()
|
||||||
|
|
||||||
|
|
|
@ -4,15 +4,30 @@
|
||||||
|
|
||||||
package org.pgpainless.algorithm
|
package org.pgpainless.algorithm
|
||||||
|
|
||||||
|
import org.bouncycastle.openpgp.api.SignatureSubpacketsFunction
|
||||||
|
|
||||||
class AlgorithmSuite(
|
class AlgorithmSuite(
|
||||||
symmetricKeyAlgorithms: List<SymmetricKeyAlgorithm>,
|
symmetricKeyAlgorithms: List<SymmetricKeyAlgorithm>?,
|
||||||
hashAlgorithms: List<HashAlgorithm>,
|
hashAlgorithms: List<HashAlgorithm>?,
|
||||||
compressionAlgorithms: List<CompressionAlgorithm>
|
compressionAlgorithms: List<CompressionAlgorithm>?,
|
||||||
|
aeadAlgorithms: List<AEADCipherMode>?,
|
||||||
|
features: List<Feature>
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val symmetricKeyAlgorithms: Set<SymmetricKeyAlgorithm> = symmetricKeyAlgorithms.toSet()
|
val symmetricKeyAlgorithms: Set<SymmetricKeyAlgorithm>? = symmetricKeyAlgorithms?.toSet()
|
||||||
val hashAlgorithms: Set<HashAlgorithm> = hashAlgorithms.toSet()
|
val hashAlgorithms: Set<HashAlgorithm>? = hashAlgorithms?.toSet()
|
||||||
val compressionAlgorithms: Set<CompressionAlgorithm> = compressionAlgorithms.toSet()
|
val compressionAlgorithms: Set<CompressionAlgorithm>? = compressionAlgorithms?.toSet()
|
||||||
|
val aeadAlgorithms: Set<AEADCipherMode>? = aeadAlgorithms?.toSet()
|
||||||
|
val features: FeatureSet = FeatureSet(features.toSet())
|
||||||
|
|
||||||
|
class FeatureSet(val features: Set<Feature>) {
|
||||||
|
fun toSignatureSubpacketsFunction(critical: Boolean = true): SignatureSubpacketsFunction {
|
||||||
|
return SignatureSubpacketsFunction {
|
||||||
|
val b = Feature.toBitmask(*features.toTypedArray())
|
||||||
|
it.apply { setFeature(critical, b) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
|
@ -39,9 +54,26 @@ class AlgorithmSuite(
|
||||||
CompressionAlgorithm.ZIP,
|
CompressionAlgorithm.ZIP,
|
||||||
CompressionAlgorithm.UNCOMPRESSED)
|
CompressionAlgorithm.UNCOMPRESSED)
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
val defaultAEADAlgorithmSuites =
|
||||||
|
listOf(
|
||||||
|
AEADCipherMode(AEADAlgorithm.EAX, SymmetricKeyAlgorithm.AES_256),
|
||||||
|
AEADCipherMode(AEADAlgorithm.OCB, SymmetricKeyAlgorithm.AES_256),
|
||||||
|
AEADCipherMode(AEADAlgorithm.GCM, SymmetricKeyAlgorithm.AES_256),
|
||||||
|
AEADCipherMode(AEADAlgorithm.EAX, SymmetricKeyAlgorithm.AES_192),
|
||||||
|
AEADCipherMode(AEADAlgorithm.EAX, SymmetricKeyAlgorithm.AES_192))
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
val defaultFeatures =
|
||||||
|
listOf(Feature.MODIFICATION_DETECTION, Feature.MODIFICATION_DETECTION_2)
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
val defaultAlgorithmSuite =
|
val defaultAlgorithmSuite =
|
||||||
AlgorithmSuite(
|
AlgorithmSuite(
|
||||||
defaultSymmetricKeyAlgorithms, defaultHashAlgorithms, defaultCompressionAlgorithms)
|
defaultSymmetricKeyAlgorithms,
|
||||||
|
defaultHashAlgorithms,
|
||||||
|
defaultCompressionAlgorithms,
|
||||||
|
defaultAEADAlgorithmSuites,
|
||||||
|
defaultFeatures)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
package org.pgpainless.key.generation
|
package org.pgpainless.key.generation
|
||||||
|
|
||||||
|
import java.util.*
|
||||||
import org.bouncycastle.openpgp.api.OpenPGPKey
|
import org.bouncycastle.openpgp.api.OpenPGPKey
|
||||||
import org.pgpainless.PGPainless.Companion.buildKeyRing
|
import org.pgpainless.PGPainless.Companion.buildKeyRing
|
||||||
import org.pgpainless.algorithm.KeyFlag
|
import org.pgpainless.algorithm.KeyFlag
|
||||||
|
@ -15,7 +16,10 @@ import org.pgpainless.key.generation.type.rsa.RsaLength
|
||||||
import org.pgpainless.key.generation.type.xdh_legacy.XDHLegacySpec
|
import org.pgpainless.key.generation.type.xdh_legacy.XDHLegacySpec
|
||||||
import org.pgpainless.util.Passphrase
|
import org.pgpainless.util.Passphrase
|
||||||
|
|
||||||
class KeyRingTemplates(private val version: OpenPGPKeyVersion) {
|
class KeyRingTemplates(
|
||||||
|
private val version: OpenPGPKeyVersion,
|
||||||
|
private val creationTime: Date = Date()
|
||||||
|
) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate an RSA OpenPGP key consisting of an RSA primary key used for certification, a
|
* Generate an RSA OpenPGP key consisting of an RSA primary key used for certification, a
|
||||||
|
|
|
@ -20,11 +20,12 @@ constructor(
|
||||||
|
|
||||||
private val hashedSubpackets: SelfSignatureSubpackets = SignatureSubpackets()
|
private val hashedSubpackets: SelfSignatureSubpackets = SignatureSubpackets()
|
||||||
private val algorithmSuite: AlgorithmSuite = PGPainless.getPolicy().keyGenerationAlgorithmSuite
|
private val algorithmSuite: AlgorithmSuite = PGPainless.getPolicy().keyGenerationAlgorithmSuite
|
||||||
private var preferredCompressionAlgorithms: Set<CompressionAlgorithm> =
|
private var preferredCompressionAlgorithms: Set<CompressionAlgorithm>? =
|
||||||
algorithmSuite.compressionAlgorithms
|
algorithmSuite.compressionAlgorithms
|
||||||
private var preferredHashAlgorithms: Set<HashAlgorithm> = algorithmSuite.hashAlgorithms
|
private var preferredHashAlgorithms: Set<HashAlgorithm>? = algorithmSuite.hashAlgorithms
|
||||||
private var preferredSymmetricAlgorithms: Set<SymmetricKeyAlgorithm> =
|
private var preferredSymmetricAlgorithms: Set<SymmetricKeyAlgorithm>? =
|
||||||
algorithmSuite.symmetricKeyAlgorithms
|
algorithmSuite.symmetricKeyAlgorithms
|
||||||
|
private var preferredAEADAlgorithms: Set<AEADCipherMode>? = algorithmSuite.aeadAlgorithms
|
||||||
private var keyCreationDate: Date? = null
|
private var keyCreationDate: Date? = null
|
||||||
|
|
||||||
constructor(type: KeyType, vararg keyFlags: KeyFlag) : this(type, listOf(*keyFlags))
|
constructor(type: KeyType, vararg keyFlags: KeyFlag) : this(type, listOf(*keyFlags))
|
||||||
|
@ -59,9 +60,10 @@ constructor(
|
||||||
return hashedSubpackets
|
return hashedSubpackets
|
||||||
.apply {
|
.apply {
|
||||||
setKeyFlags(keyFlags)
|
setKeyFlags(keyFlags)
|
||||||
setPreferredCompressionAlgorithms(preferredCompressionAlgorithms)
|
preferredCompressionAlgorithms?.let { setPreferredCompressionAlgorithms(it) }
|
||||||
setPreferredHashAlgorithms(preferredHashAlgorithms)
|
preferredHashAlgorithms?.let { setPreferredHashAlgorithms(it) }
|
||||||
setPreferredSymmetricKeyAlgorithms(preferredSymmetricAlgorithms)
|
preferredSymmetricAlgorithms?.let { setPreferredSymmetricKeyAlgorithms(it) }
|
||||||
|
preferredAEADAlgorithms?.let { setPreferredAEADCiphersuites(it) }
|
||||||
setFeatures(Feature.MODIFICATION_DETECTION)
|
setFeatures(Feature.MODIFICATION_DETECTION)
|
||||||
}
|
}
|
||||||
.let { KeySpec(type, hashedSubpackets as SignatureSubpackets, false, keyCreationDate) }
|
.let { KeySpec(type, hashedSubpackets as SignatureSubpackets, false, keyCreationDate) }
|
||||||
|
|
|
@ -21,14 +21,17 @@ interface BaseSignatureSubpackets {
|
||||||
fun setAppropriateIssuerInfo(key: PGPPublicKey): BaseSignatureSubpackets
|
fun setAppropriateIssuerInfo(key: PGPPublicKey): BaseSignatureSubpackets
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Depending on the given [version], use the appropriate means of setting issuer information.
|
* Depending on the given [version], use the appropriate means of setting issuer information. V6
|
||||||
* V6 signatures for example MUST NOT contain an [IssuerKeyID] packet.
|
* signatures for example MUST NOT contain an [IssuerKeyID] packet.
|
||||||
*
|
*
|
||||||
* @param key issuer key
|
* @param key issuer key
|
||||||
* @param version signature version
|
* @param version signature version
|
||||||
* @return this
|
* @return this
|
||||||
*/
|
*/
|
||||||
fun setAppropriateIssuerInfo(key: PGPPublicKey, version: OpenPGPKeyVersion): BaseSignatureSubpackets
|
fun setAppropriateIssuerInfo(
|
||||||
|
key: PGPPublicKey,
|
||||||
|
version: OpenPGPKeyVersion
|
||||||
|
): BaseSignatureSubpackets
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add both an [IssuerKeyID] and [IssuerFingerprint] subpacket pointing to the given key.
|
* Add both an [IssuerKeyID] and [IssuerFingerprint] subpacket pointing to the given key.
|
||||||
|
|
|
@ -112,6 +112,8 @@ interface SelfSignatureSubpackets : BaseSignatureSubpackets {
|
||||||
|
|
||||||
fun setPreferredHashAlgorithms(algorithms: PreferredAlgorithms?): SelfSignatureSubpackets
|
fun setPreferredHashAlgorithms(algorithms: PreferredAlgorithms?): SelfSignatureSubpackets
|
||||||
|
|
||||||
|
fun setPreferredAEADCiphersuites(aeadAlgorithms: Set<AEADCipherMode>)
|
||||||
|
|
||||||
fun addRevocationKey(revocationKey: PGPPublicKey): SelfSignatureSubpackets
|
fun addRevocationKey(revocationKey: PGPPublicKey): SelfSignatureSubpackets
|
||||||
|
|
||||||
fun addRevocationKey(isCritical: Boolean, revocationKey: PGPPublicKey): SelfSignatureSubpackets
|
fun addRevocationKey(isCritical: Boolean, revocationKey: PGPPublicKey): SelfSignatureSubpackets
|
||||||
|
|
|
@ -360,7 +360,8 @@ class SignatureSubpackets :
|
||||||
when (version) {
|
when (version) {
|
||||||
OpenPGPKeyVersion.v3 -> setIssuerKeyId(key.keyID)
|
OpenPGPKeyVersion.v3 -> setIssuerKeyId(key.keyID)
|
||||||
OpenPGPKeyVersion.v4 -> setIssuerFingerprintAndKeyId(key)
|
OpenPGPKeyVersion.v4 -> setIssuerFingerprintAndKeyId(key)
|
||||||
OpenPGPKeyVersion.librePgp, OpenPGPKeyVersion.v6 -> setIssuerFingerprint(key)
|
OpenPGPKeyVersion.librePgp,
|
||||||
|
OpenPGPKeyVersion.v6 -> setIssuerFingerprint(key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,12 +4,24 @@
|
||||||
|
|
||||||
package org.pgpainless.key.generation;
|
package org.pgpainless.key.generation;
|
||||||
|
|
||||||
|
import org.bouncycastle.bcpg.SignatureSubpacketTags;
|
||||||
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||||
|
import org.bouncycastle.openpgp.api.OpenPGPCertificate;
|
||||||
|
import org.bouncycastle.openpgp.api.OpenPGPKey;
|
||||||
|
import org.bouncycastle.openpgp.api.SignatureParameters;
|
||||||
|
import org.bouncycastle.openpgp.operator.PGPKeyPairGenerator;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.pgpainless.PGPainless;
|
import org.pgpainless.PGPainless;
|
||||||
|
import org.pgpainless.algorithm.KeyFlag;
|
||||||
import org.pgpainless.algorithm.OpenPGPKeyVersion;
|
import org.pgpainless.algorithm.OpenPGPKeyVersion;
|
||||||
|
import org.pgpainless.algorithm.PublicKeyAlgorithm;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
public class GenerateV6KeyTest {
|
public class GenerateV6KeyTest {
|
||||||
|
|
||||||
|
@ -20,4 +32,70 @@ public class GenerateV6KeyTest {
|
||||||
.getPGPSecretKeyRing();
|
.getPGPSecretKeyRing();
|
||||||
assertEquals(6, secretKey.getPublicKey().getVersion());
|
assertEquals(6, secretKey.getPublicKey().getVersion());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildMinimalEd25519V6Key() throws PGPException {
|
||||||
|
OpenPGPKey key = PGPainless.getInstance().buildKey(OpenPGPKeyVersion.v6)
|
||||||
|
.withPrimaryKey(PGPKeyPairGenerator::generateEd25519KeyPair, new SignatureParameters.Callback() {
|
||||||
|
@Override
|
||||||
|
public SignatureParameters apply(SignatureParameters parameters) {
|
||||||
|
return parameters.setHashedSubpacketsFunction(pgpSignatureSubpacketGenerator -> {
|
||||||
|
// TODO: Remove once https://github.com/bcgit/bc-java/pull/2013 lands
|
||||||
|
pgpSignatureSubpacketGenerator.removePacketsOfType(SignatureSubpacketTags.KEY_FLAGS);
|
||||||
|
pgpSignatureSubpacketGenerator.setKeyFlags(KeyFlag.SIGN_DATA.getFlag());
|
||||||
|
return pgpSignatureSubpacketGenerator;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
|
||||||
|
assertTrue(key.isSecretKey());
|
||||||
|
|
||||||
|
assertEquals(1, key.getKeys().size());
|
||||||
|
OpenPGPCertificate.OpenPGPPrimaryKey primaryKey = key.getPrimaryKey();
|
||||||
|
assertEquals(6, primaryKey.getVersion());
|
||||||
|
assertTrue(primaryKey.isPrimaryKey());
|
||||||
|
assertEquals(PublicKeyAlgorithm.ED25519.getAlgorithmId(), primaryKey.getPGPPublicKey().getAlgorithm());
|
||||||
|
assertEquals(primaryKey, key.getSigningKeys().get(0));
|
||||||
|
assertTrue(key.getEncryptionKeys().isEmpty());
|
||||||
|
|
||||||
|
OpenPGPKey.OpenPGPSecretKey primarySecretKey = key.getPrimarySecretKey();
|
||||||
|
assertEquals(primarySecretKey, key.getSecretKey(primaryKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildCompositeCurve25519V6Key()
|
||||||
|
throws PGPException, IOException {
|
||||||
|
OpenPGPKey key = PGPainless.getInstance().buildKey(OpenPGPKeyVersion.v6)
|
||||||
|
.withPrimaryKey(PGPKeyPairGenerator::generateEd25519KeyPair)
|
||||||
|
.addSigningSubkey(PGPKeyPairGenerator::generateEd25519KeyPair)
|
||||||
|
.addEncryptionSubkey(PGPKeyPairGenerator::generateX25519KeyPair)
|
||||||
|
.addUserId("Alice <alice@pgpainless.org>")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
assertTrue(key.isSecretKey());
|
||||||
|
assertEquals(3, key.getKeys().size());
|
||||||
|
OpenPGPCertificate.OpenPGPPrimaryKey primaryKey = key.getPrimaryKey();
|
||||||
|
assertEquals(PublicKeyAlgorithm.ED25519.getAlgorithmId(), primaryKey.getPGPPublicKey().getAlgorithm());
|
||||||
|
assertEquals(6, primaryKey.getVersion());
|
||||||
|
assertTrue(primaryKey.isPrimaryKey());
|
||||||
|
assertEquals(primaryKey, key.getKeys().get(0));
|
||||||
|
|
||||||
|
OpenPGPCertificate.OpenPGPComponentKey signingKey = key.getKeys().get(1);
|
||||||
|
assertTrue(key.getSigningKeys().contains(signingKey));
|
||||||
|
assertEquals(6, signingKey.getVersion());
|
||||||
|
assertEquals(PublicKeyAlgorithm.ED25519.getAlgorithmId(), signingKey.getPGPPublicKey().getAlgorithm());
|
||||||
|
assertFalse(signingKey.isPrimaryKey());
|
||||||
|
|
||||||
|
OpenPGPCertificate.OpenPGPComponentKey encryptionKey = key.getKeys().get(2);
|
||||||
|
assertTrue(key.getEncryptionKeys().contains(encryptionKey));
|
||||||
|
assertEquals(6, encryptionKey.getVersion());
|
||||||
|
assertEquals(PublicKeyAlgorithm.X25519.getAlgorithmId(), encryptionKey.getPGPPublicKey().getAlgorithm());
|
||||||
|
assertFalse(encryptionKey.isPrimaryKey());
|
||||||
|
|
||||||
|
OpenPGPCertificate certificate = key.toCertificate();
|
||||||
|
assertFalse(certificate.isSecretKey());
|
||||||
|
|
||||||
|
System.out.println(certificate.toAsciiArmoredString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue