1
0
Fork 0
mirror of https://github.com/pgpainless/pgpainless.git synced 2025-12-10 22:31:09 +01:00

Make SignatureSubpackets more procedural

This commit is contained in:
Paul Schaub 2021-11-09 12:21:57 +01:00
parent 3f09fa0cc7
commit 3d5a005ec7
9 changed files with 916 additions and 792 deletions

View file

@ -19,6 +19,7 @@ import java.util.List;
import java.util.Set;
import javax.annotation.Nonnull;
import org.bouncycastle.bcpg.sig.KeyFlags;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPKeyPair;
import org.bouncycastle.openpgp.PGPKeyRingGenerator;
@ -44,8 +45,8 @@ import org.pgpainless.key.generation.type.KeyType;
import org.pgpainless.key.protection.SecretKeyRingProtector;
import org.pgpainless.key.protection.UnlockSecretKey;
import org.pgpainless.provider.ProviderFactory;
import org.pgpainless.signature.subpackets.SignatureSubpacketGeneratorUtil;
import org.pgpainless.signature.subpackets.SignatureSubpacketGeneratorWrapper;
import org.pgpainless.signature.subpackets.SignatureSubpackets;
import org.pgpainless.signature.subpackets.SignatureSubpacketsHelper;
import org.pgpainless.util.Passphrase;
public class KeyRingBuilder implements KeyRingBuilderInterface<KeyRingBuilder> {
@ -112,9 +113,8 @@ public class KeyRingBuilder implements KeyRingBuilderInterface<KeyRingBuilder> {
}
private boolean hasCertifyOthersFlag(KeySpec keySpec) {
return SignatureSubpacketGeneratorUtil.hasKeyFlag(KeyFlag.CERTIFY_OTHER,
keySpec.getSubpacketGenerator() == null ? null :
keySpec.getSubpacketGenerator().getGenerator());
KeyFlags keyFlags = keySpec.getSubpacketGenerator().getKeyFlagsSubpacket();
return keyFlags != null && KeyFlag.hasKeyFlag(keyFlags.getFlags(), KeyFlag.CERTIFY_OTHER);
}
private boolean keyIsCertificationCapable(KeySpec keySpec) {
@ -137,12 +137,14 @@ public class KeyRingBuilder implements KeyRingBuilderInterface<KeyRingBuilder> {
PGPKeyPair certKey = generateKeyPair(primaryKeySpec);
PGPContentSignerBuilder signer = buildContentSigner(certKey);
signatureGenerator = new PGPSignatureGenerator(signer);
SignatureSubpacketGeneratorWrapper hashedSubPacketGenerator = primaryKeySpec.getSubpacketGenerator();
SignatureSubpackets hashedSubPacketGenerator = primaryKeySpec.getSubpacketGenerator();
hashedSubPacketGenerator.setPrimaryUserId();
if (expirationDate != null) {
hashedSubPacketGenerator.setKeyExpirationTime(certKey.getPublicKey(), expirationDate);
}
PGPSignatureSubpacketVector hashedSubPackets = hashedSubPacketGenerator.getGenerator().generate();
PGPSignatureSubpacketGenerator generator = new PGPSignatureSubpacketGenerator();
SignatureSubpacketsHelper.applyTo(hashedSubPacketGenerator, generator);
PGPSignatureSubpacketVector hashedSubPackets = generator.generate();
// Generator which the user can get the key pair from
PGPKeyRingGenerator ringGenerator = buildRingGenerator(certKey, signer, hashedSubPackets);

View file

@ -10,12 +10,13 @@ import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator;
import org.bouncycastle.openpgp.PGPSignatureSubpacketVector;
import org.pgpainless.algorithm.KeyFlag;
import org.pgpainless.key.generation.type.KeyType;
import org.pgpainless.signature.subpackets.SignatureSubpacketGeneratorWrapper;
import org.pgpainless.signature.subpackets.SignatureSubpackets;
import org.pgpainless.signature.subpackets.SignatureSubpacketsHelper;
public class KeySpec {
private final KeyType keyType;
private final SignatureSubpacketGeneratorWrapper subpacketGenerator;
private final SignatureSubpackets subpacketGenerator;
private final boolean inheritedSubPackets;
KeySpec(@Nonnull KeyType type,
@ -23,12 +24,12 @@ public class KeySpec {
boolean inheritedSubPackets) {
this(
type,
SignatureSubpacketGeneratorWrapper.createSubpacketsFrom(subpacketGenerator.generate()),
SignatureSubpackets.createSubpacketsFrom(subpacketGenerator.generate()),
inheritedSubPackets);
}
KeySpec(@Nonnull KeyType type,
@Nonnull SignatureSubpacketGeneratorWrapper subpacketGenerator,
@Nonnull SignatureSubpackets subpacketGenerator,
boolean inheritedSubPackets) {
this.keyType = type;
this.subpacketGenerator = subpacketGenerator;
@ -42,11 +43,11 @@ public class KeySpec {
@Nonnull
public PGPSignatureSubpacketVector getSubpackets() {
return subpacketGenerator.getGenerator().generate();
return SignatureSubpacketsHelper.toVector(subpacketGenerator);
}
@Nonnull
SignatureSubpacketGeneratorWrapper getSubpacketGenerator() {
SignatureSubpackets getSubpacketGenerator() {
return subpacketGenerator;
}

View file

@ -21,7 +21,8 @@ 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.SignatureSubpacketGeneratorWrapper;
import org.pgpainless.signature.subpackets.SignatureSubpackets;
import org.pgpainless.signature.subpackets.SignatureSubpacketsHelper;
public abstract class AbstractSignatureBuilder<B extends AbstractSignatureBuilder<B>> {
protected final PGPPrivateKey privateSigningKey;
@ -30,8 +31,8 @@ public abstract class AbstractSignatureBuilder<B extends AbstractSignatureBuilde
protected HashAlgorithm hashAlgorithm;
protected SignatureType signatureType;
protected SignatureSubpacketGeneratorWrapper unhashedSubpackets;
protected SignatureSubpacketGeneratorWrapper hashedSubpackets;
protected SignatureSubpackets unhashedSubpackets;
protected SignatureSubpackets hashedSubpackets;
public AbstractSignatureBuilder(SignatureType signatureType, PGPSecretKey signingKey, SecretKeyRingProtector protector)
throws WrongPassphraseException {
@ -43,9 +44,9 @@ public abstract class AbstractSignatureBuilder<B extends AbstractSignatureBuilde
this.publicSigningKey = signingKey.getPublicKey();
this.hashAlgorithm = negotiateHashAlgorithm(publicSigningKey);
unhashedSubpackets = SignatureSubpacketGeneratorWrapper.createEmptySubpackets();
unhashedSubpackets = new SignatureSubpackets();
// Prepopulate hashed subpackets with default values (key-id etc.)
hashedSubpackets = SignatureSubpacketGeneratorWrapper.createHashedSubpackets(publicSigningKey);
hashedSubpackets = SignatureSubpackets.createHashedSubpackets(publicSigningKey);
}
public AbstractSignatureBuilder(PGPSecretKey certificationKey, SecretKeyRingProtector protector, PGPSignature archetypeSignature)
@ -59,8 +60,8 @@ public abstract class AbstractSignatureBuilder<B extends AbstractSignatureBuilde
this.publicSigningKey = certificationKey.getPublicKey();
this.hashAlgorithm = negotiateHashAlgorithm(publicSigningKey);
unhashedSubpackets = SignatureSubpacketGeneratorWrapper.refreshUnhashedSubpackets(archetypeSignature);
hashedSubpackets = SignatureSubpacketGeneratorWrapper.refreshHashedSubpackets(publicSigningKey, archetypeSignature);
unhashedSubpackets = SignatureSubpackets.refreshUnhashedSubpackets(archetypeSignature);
hashedSubpackets = SignatureSubpackets.refreshHashedSubpackets(publicSigningKey, archetypeSignature);
}
/**
@ -104,8 +105,8 @@ public abstract class AbstractSignatureBuilder<B extends AbstractSignatureBuilde
publicSigningKey.getAlgorithm(), hashAlgorithm.getAlgorithmId()
)
);
generator.setUnhashedSubpackets(unhashedSubpackets.getGenerator().generate());
generator.setHashedSubpackets(hashedSubpackets.getGenerator().generate());
generator.setUnhashedSubpackets(SignatureSubpacketsHelper.toVector(unhashedSubpackets));
generator.setHashedSubpackets(SignatureSubpacketsHelper.toVector(hashedSubpackets));
generator.init(signatureType.getCode(), privateSigningKey);
return generator;
}

View file

@ -14,7 +14,7 @@ import org.pgpainless.algorithm.SignatureType;
import org.pgpainless.exception.WrongPassphraseException;
import org.pgpainless.key.protection.SecretKeyRingProtector;
import org.pgpainless.signature.subpackets.BaseSignatureSubpackets;
import org.pgpainless.signature.subpackets.SignatureSubpacketGeneratorWrapper;
import org.pgpainless.signature.subpackets.SignatureSubpackets;
/**
* Signature builder without restrictions on subpacket contents.
@ -36,11 +36,11 @@ public class UniversalSignatureBuilder extends AbstractSignatureBuilder<Universa
return true;
}
public SignatureSubpacketGeneratorWrapper getHashedSubpackets() {
public SignatureSubpackets getHashedSubpackets() {
return hashedSubpackets;
}
public SignatureSubpacketGeneratorWrapper getUnhashedSubpackets() {
public SignatureSubpackets getUnhashedSubpackets() {
return unhashedSubpackets;
}

View file

@ -28,7 +28,7 @@ import org.pgpainless.algorithm.PublicKeyAlgorithm;
public interface BaseSignatureSubpackets {
interface Callback extends SignatureSubpacketCallback<SignatureSubpacketGeneratorWrapper> {
interface Callback extends SignatureSubpacketCallback<SignatureSubpackets> {
}

View file

@ -1,719 +0,0 @@
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.signature.subpackets;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.bouncycastle.bcpg.SignatureSubpacket;
import org.bouncycastle.bcpg.SignatureSubpacketTags;
import org.bouncycastle.bcpg.sig.EmbeddedSignature;
import org.bouncycastle.bcpg.sig.Exportable;
import org.bouncycastle.bcpg.sig.Features;
import org.bouncycastle.bcpg.sig.IntendedRecipientFingerprint;
import org.bouncycastle.bcpg.sig.IssuerFingerprint;
import org.bouncycastle.bcpg.sig.IssuerKeyID;
import org.bouncycastle.bcpg.sig.KeyExpirationTime;
import org.bouncycastle.bcpg.sig.KeyFlags;
import org.bouncycastle.bcpg.sig.NotationData;
import org.bouncycastle.bcpg.sig.PreferredAlgorithms;
import org.bouncycastle.bcpg.sig.PrimaryUserID;
import org.bouncycastle.bcpg.sig.Revocable;
import org.bouncycastle.bcpg.sig.RevocationKey;
import org.bouncycastle.bcpg.sig.RevocationReason;
import org.bouncycastle.bcpg.sig.SignatureCreationTime;
import org.bouncycastle.bcpg.sig.SignatureExpirationTime;
import org.bouncycastle.bcpg.sig.SignatureTarget;
import org.bouncycastle.bcpg.sig.SignerUserID;
import org.bouncycastle.bcpg.sig.TrustSignature;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator;
import org.bouncycastle.openpgp.PGPSignatureSubpacketVector;
import org.pgpainless.algorithm.CompressionAlgorithm;
import org.pgpainless.algorithm.Feature;
import org.pgpainless.algorithm.HashAlgorithm;
import org.pgpainless.algorithm.KeyFlag;
import org.pgpainless.algorithm.PublicKeyAlgorithm;
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
import org.pgpainless.key.util.RevocationAttributes;
public class SignatureSubpacketGeneratorWrapper
implements BaseSignatureSubpackets, SelfSignatureSubpackets, CertificationSubpackets, RevocationSignatureSubpackets {
private SignatureCreationTime signatureCreationTime;
private SignatureExpirationTime signatureExpirationTime;
private IssuerKeyID issuerKeyID;
private IssuerFingerprint issuerFingerprint;
private final List<NotationData> notationDataList = new ArrayList<>();
private final List<IntendedRecipientFingerprint> intendedRecipientFingerprintList = new ArrayList<>();
private final List<RevocationKey> revocationKeyList = new ArrayList<>();
private Exportable exportable;
private SignatureTarget signatureTarget;
private Features features;
private KeyFlags keyFlags;
private TrustSignature trust;
private PreferredAlgorithms preferredCompressionAlgorithms;
private PreferredAlgorithms preferredSymmetricKeyAlgorithms;
private PreferredAlgorithms preferredHashAlgorithms;
private final List<EmbeddedSignature> embeddedSignatureList = new ArrayList<>();
private SignerUserID signerUserId;
private KeyExpirationTime keyExpirationTime;
private PrimaryUserID primaryUserId;
private Revocable revocable;
private RevocationReason revocationReason;
private final List<SignatureSubpacket> unsupportedSubpackets = new ArrayList<>();
public SignatureSubpacketGeneratorWrapper() {
}
public static SignatureSubpacketGeneratorWrapper refreshHashedSubpackets(PGPPublicKey issuer, PGPSignature oldSignature) {
return createHashedSubpacketsFrom(issuer, oldSignature.getHashedSubPackets());
}
public static SignatureSubpacketGeneratorWrapper refreshUnhashedSubpackets(PGPSignature oldSignature) {
return createSubpacketsFrom(oldSignature.getUnhashedSubPackets());
}
public static SignatureSubpacketGeneratorWrapper createHashedSubpacketsFrom(PGPPublicKey issuer, PGPSignatureSubpacketVector base) {
SignatureSubpacketGeneratorWrapper wrapper = createSubpacketsFrom(base);
wrapper.setIssuerFingerprintAndKeyId(issuer);
return wrapper;
}
public static SignatureSubpacketGeneratorWrapper createSubpacketsFrom(PGPSignatureSubpacketVector base) {
SignatureSubpacketGeneratorWrapper wrapper = new SignatureSubpacketGeneratorWrapper();
wrapper.extractSubpacketsFromVector(base);
return wrapper;
}
public static SignatureSubpacketGeneratorWrapper createEmptySubpackets() {
return new SignatureSubpacketGeneratorWrapper();
}
public static SignatureSubpacketGeneratorWrapper createHashedSubpackets() {
SignatureSubpacketGeneratorWrapper wrapper = new SignatureSubpacketGeneratorWrapper();
wrapper.setSignatureCreationTime(new Date());
return wrapper;
}
public static SignatureSubpacketGeneratorWrapper createHashedSubpackets(PGPPublicKey issuer) {
SignatureSubpacketGeneratorWrapper wrapper = createHashedSubpackets();
wrapper.setIssuerFingerprintAndKeyId(issuer);
return wrapper;
}
private void extractSubpacketsFromVector(PGPSignatureSubpacketVector base) {
for (SignatureSubpacket subpacket : base.toArray()) {
org.pgpainless.algorithm.SignatureSubpacket type = org.pgpainless.algorithm.SignatureSubpacket.fromCode(subpacket.getType());
switch (type) {
case signatureCreationTime:
case issuerKeyId:
case issuerFingerprint:
// ignore, we override this anyways
break;
case signatureExpirationTime:
SignatureExpirationTime sigExpTime = (SignatureExpirationTime) subpacket;
setSignatureExpirationTime(sigExpTime.isCritical(), sigExpTime.getTime());
break;
case exportableCertification:
Exportable exp = (Exportable) subpacket;
setExportable(exp.isCritical(), exp.isExportable());
break;
case trustSignature:
TrustSignature trustSignature = (TrustSignature) subpacket;
setTrust(trustSignature.isCritical(), trustSignature.getDepth(), trustSignature.getTrustAmount());
break;
case revocable:
Revocable rev = (Revocable) subpacket;
setRevocable(rev.isCritical(), rev.isRevocable());
break;
case keyExpirationTime:
KeyExpirationTime keyExpTime = (KeyExpirationTime) subpacket;
setKeyExpirationTime(keyExpTime.isCritical(), keyExpTime.getTime());
break;
case preferredSymmetricAlgorithms:
setPreferredSymmetricKeyAlgorithms((PreferredAlgorithms) subpacket);
break;
case revocationKey:
RevocationKey revocationKey = (RevocationKey) subpacket;
addRevocationKey(revocationKey);
break;
case notationData:
NotationData notationData = (NotationData) subpacket;
addNotationData(notationData.isCritical(), notationData.getNotationName(), notationData.getNotationValue());
break;
case preferredHashAlgorithms:
setPreferredHashAlgorithms((PreferredAlgorithms) subpacket);
break;
case preferredCompressionAlgorithms:
setPreferredCompressionAlgorithms((PreferredAlgorithms) subpacket);
break;
case primaryUserId:
PrimaryUserID primaryUserID = (PrimaryUserID) subpacket;
setPrimaryUserId(primaryUserID);
break;
case keyFlags:
KeyFlags flags = (KeyFlags) subpacket;
setKeyFlags(flags.isCritical(), KeyFlag.fromBitmask(flags.getFlags()).toArray(new KeyFlag[0]));
break;
case signerUserId:
SignerUserID signerUserID = (SignerUserID) subpacket;
setSignerUserId(signerUserID.isCritical(), signerUserID.getID());
break;
case revocationReason:
RevocationReason reason = (RevocationReason) subpacket;
setRevocationReason(reason.isCritical(),
RevocationAttributes.Reason.fromCode(reason.getRevocationReason()),
reason.getRevocationDescription());
break;
case features:
Features f = (Features) subpacket;
setFeatures(f.isCritical(), Feature.fromBitmask(f.getData()[0]).toArray(new Feature[0]));
break;
case signatureTarget:
SignatureTarget target = (SignatureTarget) subpacket;
setSignatureTarget(target.isCritical(),
PublicKeyAlgorithm.fromId(target.getPublicKeyAlgorithm()),
HashAlgorithm.fromId(target.getHashAlgorithm()),
target.getHashData());
break;
case embeddedSignature:
EmbeddedSignature embeddedSignature = (EmbeddedSignature) subpacket;
addEmbeddedSignature(embeddedSignature);
break;
case intendedRecipientFingerprint:
IntendedRecipientFingerprint intendedRecipientFingerprint = (IntendedRecipientFingerprint) subpacket;
addIntendedRecipientFingerprint(intendedRecipientFingerprint);
break;
case regularExpression:
case keyServerPreferences:
case preferredKeyServers:
case policyUrl:
case placeholder:
case preferredAEADAlgorithms:
case attestedCertification:
unsupportedSubpackets.add(subpacket);
break;
}
}
}
public PGPSignatureSubpacketGenerator getGenerator() {
PGPSignatureSubpacketGenerator generator = new PGPSignatureSubpacketGenerator();
addSubpacket(generator, issuerKeyID);
addSubpacket(generator, issuerFingerprint);
addSubpacket(generator, signatureCreationTime);
addSubpacket(generator, signatureExpirationTime);
addSubpacket(generator, exportable);
for (NotationData notationData : notationDataList) {
addSubpacket(generator, notationData);
}
for (IntendedRecipientFingerprint intendedRecipientFingerprint : intendedRecipientFingerprintList) {
addSubpacket(generator, intendedRecipientFingerprint);
}
for (RevocationKey revocationKey : revocationKeyList) {
addSubpacket(generator, revocationKey);
}
addSubpacket(generator, signatureTarget);
addSubpacket(generator, features);
addSubpacket(generator, keyFlags);
addSubpacket(generator, trust);
addSubpacket(generator, preferredCompressionAlgorithms);
addSubpacket(generator, preferredSymmetricKeyAlgorithms);
addSubpacket(generator, preferredHashAlgorithms);
for (EmbeddedSignature embeddedSignature : embeddedSignatureList) {
addSubpacket(generator, embeddedSignature);
}
addSubpacket(generator, signerUserId);
addSubpacket(generator, keyExpirationTime);
addSubpacket(generator, primaryUserId);
addSubpacket(generator, revocable);
addSubpacket(generator, revocationReason);
for (SignatureSubpacket subpacket : unsupportedSubpackets) {
addSubpacket(generator, subpacket);
}
return generator;
}
@Override
public SignatureSubpacketGeneratorWrapper setIssuerFingerprintAndKeyId(PGPPublicKey key) {
setIssuerKeyId(key.getKeyID());
setIssuerFingerprint(key);
return this;
}
@Override
public SignatureSubpacketGeneratorWrapper setIssuerKeyId(long keyId) {
return setIssuerKeyId(true, keyId);
}
@Override
public SignatureSubpacketGeneratorWrapper setIssuerKeyId(boolean isCritical, long keyId) {
return setIssuerKeyId(new IssuerKeyID(isCritical, keyId));
}
@Override
public SignatureSubpacketGeneratorWrapper setIssuerKeyId(@Nullable IssuerKeyID issuerKeyID) {
this.issuerKeyID = issuerKeyID;
return this;
}
@Override
public SignatureSubpacketGeneratorWrapper setIssuerFingerprint(@Nonnull PGPPublicKey key) {
return setIssuerFingerprint(true, key);
}
@Override
public SignatureSubpacketGeneratorWrapper setIssuerFingerprint(boolean isCritical, @Nonnull PGPPublicKey key) {
return setIssuerFingerprint(new IssuerFingerprint(isCritical, key.getVersion(), key.getFingerprint()));
}
@Override
public SignatureSubpacketGeneratorWrapper setIssuerFingerprint(@Nullable IssuerFingerprint fingerprint) {
this.issuerFingerprint = fingerprint;
return this;
}
@Override
public SignatureSubpacketGeneratorWrapper setKeyFlags(KeyFlag... keyFlags) {
return setKeyFlags(true, keyFlags);
}
@Override
public SignatureSubpacketGeneratorWrapper setKeyFlags(boolean isCritical, KeyFlag... keyFlags) {
int bitmask = KeyFlag.toBitmask(keyFlags);
return setKeyFlags(new KeyFlags(isCritical, bitmask));
}
@Override
public SignatureSubpacketGeneratorWrapper setKeyFlags(@Nullable KeyFlags keyFlags) {
this.keyFlags = keyFlags;
return this;
}
@Override
public SignatureSubpacketGeneratorWrapper setSignatureCreationTime(@Nonnull Date creationTime) {
return setSignatureCreationTime(true, creationTime);
}
@Override
public SignatureSubpacketGeneratorWrapper setSignatureCreationTime(boolean isCritical, @Nonnull Date creationTime) {
return setSignatureCreationTime(new SignatureCreationTime(isCritical, creationTime));
}
@Override
public SignatureSubpacketGeneratorWrapper setSignatureCreationTime(@Nullable SignatureCreationTime signatureCreationTime) {
this.signatureCreationTime = signatureCreationTime;
return this;
}
@Override
public SignatureSubpacketGeneratorWrapper setSignatureExpirationTime(@Nonnull Date creationTime, @Nonnull Date expirationTime) {
return setSignatureExpirationTime(true, creationTime, expirationTime);
}
@Override
public SignatureSubpacketGeneratorWrapper setSignatureExpirationTime(boolean isCritical, @Nonnull Date creationTime, @Nonnull Date expirationTime) {
return setSignatureExpirationTime(isCritical, (expirationTime.getTime() / 1000) - (creationTime.getTime() / 1000));
}
@Override
public SignatureSubpacketGeneratorWrapper setSignatureExpirationTime(boolean isCritical, long seconds) {
if (seconds < 0) {
throw new IllegalArgumentException("Expiration time cannot be negative.");
}
return setSignatureExpirationTime(new SignatureExpirationTime(isCritical, seconds));
}
@Override
public SignatureSubpacketGeneratorWrapper setSignatureExpirationTime(@Nullable SignatureExpirationTime expirationTime) {
this.signatureExpirationTime = expirationTime;
return this;
}
@Override
public SignatureSubpacketGeneratorWrapper setSignerUserId(@Nonnull String userId) {
return setSignerUserId(false, userId);
}
@Override
public SignatureSubpacketGeneratorWrapper setSignerUserId(boolean isCritical, @Nonnull String userId) {
return setSignerUserId(new SignerUserID(isCritical, userId));
}
@Override
public SignatureSubpacketGeneratorWrapper setSignerUserId(@Nullable SignerUserID signerUserId) {
this.signerUserId = signerUserId;
return this;
}
@Override
public SignatureSubpacketGeneratorWrapper setPrimaryUserId() {
return setPrimaryUserId(true);
}
@Override
public SignatureSubpacketGeneratorWrapper setPrimaryUserId(boolean isCritical) {
return setPrimaryUserId(new PrimaryUserID(isCritical, true));
}
@Override
public SignatureSubpacketGeneratorWrapper setPrimaryUserId(@Nullable PrimaryUserID primaryUserId) {
this.primaryUserId = primaryUserId;
return this;
}
@Override
public SignatureSubpacketGeneratorWrapper setKeyExpirationTime(@Nonnull PGPPublicKey key, @Nonnull Date keyExpirationTime) {
return setKeyExpirationTime(key.getCreationTime(), keyExpirationTime);
}
@Override
public SignatureSubpacketGeneratorWrapper setKeyExpirationTime(@Nonnull Date keyCreationTime, @Nonnull Date keyExpirationTime) {
return setKeyExpirationTime(true, keyCreationTime, keyExpirationTime);
}
@Override
public SignatureSubpacketGeneratorWrapper setKeyExpirationTime(boolean isCritical, @Nonnull Date keyCreationTime, @Nonnull Date keyExpirationTime) {
return setKeyExpirationTime(isCritical, (keyExpirationTime.getTime() / 1000) - (keyCreationTime.getTime() / 1000));
}
@Override
public SignatureSubpacketGeneratorWrapper setKeyExpirationTime(boolean isCritical, long secondsFromCreationToExpiration) {
if (secondsFromCreationToExpiration < 0) {
throw new IllegalArgumentException("Seconds from key creation to expiration cannot be less than 0.");
}
return setKeyExpirationTime(new KeyExpirationTime(isCritical, secondsFromCreationToExpiration));
}
@Override
public SignatureSubpacketGeneratorWrapper setKeyExpirationTime(@Nullable KeyExpirationTime keyExpirationTime) {
this.keyExpirationTime = keyExpirationTime;
return this;
}
@Override
public SignatureSubpacketGeneratorWrapper setPreferredCompressionAlgorithms(CompressionAlgorithm... algorithms) {
return setPreferredCompressionAlgorithms(new LinkedHashSet<>(Arrays.asList(algorithms)));
}
@Override
public SignatureSubpacketGeneratorWrapper setPreferredCompressionAlgorithms(Set<CompressionAlgorithm> algorithms) {
return setPreferredCompressionAlgorithms(true, algorithms);
}
@Override
public SignatureSubpacketGeneratorWrapper setPreferredCompressionAlgorithms(boolean isCritical, Set<CompressionAlgorithm> algorithms) {
int[] ids = new int[algorithms.size()];
Iterator<CompressionAlgorithm> iterator = algorithms.iterator();
for (int i = 0; i < algorithms.size(); i++) {
ids[i] = iterator.next().getAlgorithmId();
}
return setPreferredCompressionAlgorithms(new PreferredAlgorithms(
SignatureSubpacketTags.PREFERRED_COMP_ALGS, isCritical, ids));
}
@Override
public SignatureSubpacketGeneratorWrapper setPreferredCompressionAlgorithms(@Nullable PreferredAlgorithms algorithms) {
if (algorithms == null) {
this.preferredCompressionAlgorithms = null;
return this;
}
if (algorithms.getType() != SignatureSubpacketTags.PREFERRED_COMP_ALGS) {
throw new IllegalArgumentException("Invalid preferred compression algorithms type.");
}
this.preferredCompressionAlgorithms = algorithms;
return this;
}
@Override
public SignatureSubpacketGeneratorWrapper setPreferredSymmetricKeyAlgorithms(SymmetricKeyAlgorithm... algorithms) {
return setPreferredSymmetricKeyAlgorithms(new LinkedHashSet<>(Arrays.asList(algorithms)));
}
@Override
public SignatureSubpacketGeneratorWrapper setPreferredSymmetricKeyAlgorithms(Set<SymmetricKeyAlgorithm> algorithms) {
return setPreferredSymmetricKeyAlgorithms(true, algorithms);
}
@Override
public SignatureSubpacketGeneratorWrapper setPreferredSymmetricKeyAlgorithms(boolean isCritical, Set<SymmetricKeyAlgorithm> algorithms) {
int[] ids = new int[algorithms.size()];
Iterator<SymmetricKeyAlgorithm> iterator = algorithms.iterator();
for (int i = 0; i < algorithms.size(); i++) {
ids[i] = iterator.next().getAlgorithmId();
}
return setPreferredSymmetricKeyAlgorithms(new PreferredAlgorithms(
SignatureSubpacketTags.PREFERRED_SYM_ALGS, isCritical, ids));
}
@Override
public SignatureSubpacketGeneratorWrapper setPreferredSymmetricKeyAlgorithms(@Nullable PreferredAlgorithms algorithms) {
if (algorithms == null) {
this.preferredSymmetricKeyAlgorithms = null;
return this;
}
if (algorithms.getType() != SignatureSubpacketTags.PREFERRED_SYM_ALGS) {
throw new IllegalArgumentException("Invalid preferred symmetric key algorithms type.");
}
this.preferredSymmetricKeyAlgorithms = algorithms;
return this;
}
@Override
public SignatureSubpacketGeneratorWrapper setPreferredHashAlgorithms(HashAlgorithm... algorithms) {
return setPreferredHashAlgorithms(new LinkedHashSet<>(Arrays.asList(algorithms)));
}
@Override
public SignatureSubpacketGeneratorWrapper setPreferredHashAlgorithms(Set<HashAlgorithm> algorithms) {
return setPreferredHashAlgorithms(true, algorithms);
}
@Override
public SignatureSubpacketGeneratorWrapper setPreferredHashAlgorithms(boolean isCritical, Set<HashAlgorithm> algorithms) {
int[] ids = new int[algorithms.size()];
Iterator<HashAlgorithm> iterator = algorithms.iterator();
for (int i = 0; i < ids.length; i++) {
ids[i] = iterator.next().getAlgorithmId();
}
return setPreferredHashAlgorithms(new PreferredAlgorithms(
SignatureSubpacketTags.PREFERRED_HASH_ALGS, isCritical, ids));
}
@Override
public SignatureSubpacketGeneratorWrapper setPreferredHashAlgorithms(@Nullable PreferredAlgorithms algorithms) {
if (algorithms == null) {
preferredHashAlgorithms = null;
return this;
}
if (algorithms.getType() != SignatureSubpacketTags.PREFERRED_HASH_ALGS) {
throw new IllegalArgumentException("Invalid preferred hash algorithms type.");
}
this.preferredHashAlgorithms = algorithms;
return this;
}
@Override
public SignatureSubpacketGeneratorWrapper addNotationData(boolean isCritical, @Nonnull String notationName, @Nonnull String notationValue) {
return addNotationData(isCritical, true, notationName, notationValue);
}
@Override
public SignatureSubpacketGeneratorWrapper addNotationData(boolean isCritical, boolean isHumanReadable, @Nonnull String notationName, @Nonnull String notationValue) {
return addNotationData(new NotationData(isCritical, isHumanReadable, notationName, notationValue));
}
@Override
public SignatureSubpacketGeneratorWrapper addNotationData(@Nonnull NotationData notationData) {
notationDataList.add(notationData);
return this;
}
@Override
public SignatureSubpacketGeneratorWrapper clearNotationData() {
notationDataList.clear();
return this;
}
@Override
public SignatureSubpacketGeneratorWrapper addIntendedRecipientFingerprint(@Nonnull PGPPublicKey recipient) {
return addIntendedRecipientFingerprint(false, recipient);
}
@Override
public SignatureSubpacketGeneratorWrapper addIntendedRecipientFingerprint(boolean isCritical, @Nonnull PGPPublicKey recipient) {
return addIntendedRecipientFingerprint(new IntendedRecipientFingerprint(isCritical, recipient.getVersion(), recipient.getFingerprint()));
}
@Override
public SignatureSubpacketGeneratorWrapper addIntendedRecipientFingerprint(IntendedRecipientFingerprint intendedRecipientFingerprint) {
this.intendedRecipientFingerprintList.add(intendedRecipientFingerprint);
return this;
}
@Override
public SignatureSubpacketGeneratorWrapper clearIntendedRecipientFingerprints() {
intendedRecipientFingerprintList.clear();
return this;
}
@Override
public SignatureSubpacketGeneratorWrapper setExportable(boolean isCritical, boolean isExportable) {
return setExportable(new Exportable(isCritical, isExportable));
}
@Override
public SignatureSubpacketGeneratorWrapper setExportable(@Nullable Exportable exportable) {
this.exportable = exportable;
return this;
}
@Override
public SignatureSubpacketGeneratorWrapper setRevocable(boolean isCritical, boolean isRevocable) {
return setRevocable(new Revocable(isCritical, isRevocable));
}
@Override
public SignatureSubpacketGeneratorWrapper setRevocable(@Nullable Revocable revocable) {
this.revocable = revocable;
return this;
}
@Override
public SignatureSubpacketGeneratorWrapper addRevocationKey(@Nonnull PGPPublicKey revocationKey) {
return addRevocationKey(true, revocationKey);
}
@Override
public SignatureSubpacketGeneratorWrapper addRevocationKey(boolean isCritical, @Nonnull PGPPublicKey revocationKey) {
return addRevocationKey(isCritical, false, revocationKey);
}
@Override
public SignatureSubpacketGeneratorWrapper addRevocationKey(boolean isCritical, boolean isSensitive, @Nonnull PGPPublicKey revocationKey) {
byte clazz = (byte) 0x80;
clazz |= (isSensitive ? 0x40 : 0x00);
return addRevocationKey(new RevocationKey(isCritical, clazz, revocationKey.getAlgorithm(), revocationKey.getFingerprint()));
}
@Override
public SignatureSubpacketGeneratorWrapper addRevocationKey(@Nonnull RevocationKey revocationKey) {
this.revocationKeyList.add(revocationKey);
return this;
}
@Override
public SignatureSubpacketGeneratorWrapper clearRevocationKeys() {
revocationKeyList.clear();
return this;
}
@Override
public SignatureSubpacketGeneratorWrapper setRevocationReason(RevocationAttributes revocationAttributes) {
return setRevocationReason(true, revocationAttributes);
}
@Override
public SignatureSubpacketGeneratorWrapper setRevocationReason(boolean isCritical, RevocationAttributes revocationAttributes) {
return setRevocationReason(isCritical, revocationAttributes.getReason(), revocationAttributes.getDescription());
}
@Override
public SignatureSubpacketGeneratorWrapper setRevocationReason(boolean isCritical, RevocationAttributes.Reason reason, @Nonnull String description) {
return setRevocationReason(new RevocationReason(isCritical, reason.code(), description));
}
@Override
public SignatureSubpacketGeneratorWrapper setRevocationReason(@Nullable RevocationReason reason) {
this.revocationReason = reason;
return this;
}
@Override
public SignatureSubpacketGeneratorWrapper setSignatureTarget(@Nonnull PublicKeyAlgorithm keyAlgorithm, @Nonnull HashAlgorithm hashAlgorithm, @Nonnull byte[] hashData) {
return setSignatureTarget(true, keyAlgorithm, hashAlgorithm, hashData);
}
@Override
public SignatureSubpacketGeneratorWrapper setSignatureTarget(boolean isCritical, @Nonnull PublicKeyAlgorithm keyAlgorithm, @Nonnull HashAlgorithm hashAlgorithm, @Nonnull byte[] hashData) {
return setSignatureTarget(new SignatureTarget(isCritical, keyAlgorithm.getAlgorithmId(), hashAlgorithm.getAlgorithmId(), hashData));
}
@Override
public SignatureSubpacketGeneratorWrapper setSignatureTarget(@Nullable SignatureTarget signatureTarget) {
this.signatureTarget = signatureTarget;
return this;
}
@Override
public SignatureSubpacketGeneratorWrapper setFeatures(Feature... features) {
return setFeatures(true, features);
}
@Override
public SignatureSubpacketGeneratorWrapper setFeatures(boolean isCritical, Feature... features) {
byte bitmask = Feature.toBitmask(features);
return setFeatures(new Features(isCritical, bitmask));
}
@Override
public SignatureSubpacketGeneratorWrapper setFeatures(@Nullable Features features) {
this.features = features;
return this;
}
@Override
public SignatureSubpacketGeneratorWrapper setTrust(int depth, int amount) {
return setTrust(true, depth, amount);
}
@Override
public SignatureSubpacketGeneratorWrapper setTrust(boolean isCritical, int depth, int amount) {
return setTrust(new TrustSignature(isCritical, depth, amount));
}
@Override
public SignatureSubpacketGeneratorWrapper setTrust(@Nullable TrustSignature trust) {
this.trust = trust;
return this;
}
@Override
public SignatureSubpacketGeneratorWrapper addEmbeddedSignature(@Nonnull PGPSignature signature) throws IOException {
return addEmbeddedSignature(true, signature);
}
@Override
public SignatureSubpacketGeneratorWrapper addEmbeddedSignature(boolean isCritical, @Nonnull PGPSignature signature) throws IOException {
byte[] sig = signature.getEncoded();
byte[] data;
if (sig.length - 1 > 256) {
data = new byte[sig.length - 3];
}
else {
data = new byte[sig.length - 2];
}
System.arraycopy(sig, sig.length - data.length, data, 0, data.length);
return addEmbeddedSignature(new EmbeddedSignature(isCritical, false, data));
}
@Override
public SignatureSubpacketGeneratorWrapper addEmbeddedSignature(@Nonnull EmbeddedSignature embeddedSignature) {
this.embeddedSignatureList.add(embeddedSignature);
return this;
}
@Override
public SignatureSubpacketGeneratorWrapper clearEmbeddedSignatures() {
this.embeddedSignatureList.clear();
return this;
}
private static void addSubpacket(PGPSignatureSubpacketGenerator generator, SignatureSubpacket subpacket) {
if (subpacket != null) {
generator.addCustomSubpacket(subpacket);
}
}
}

View file

@ -0,0 +1,660 @@
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.signature.subpackets;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.bouncycastle.bcpg.SignatureSubpacket;
import org.bouncycastle.bcpg.SignatureSubpacketTags;
import org.bouncycastle.bcpg.sig.EmbeddedSignature;
import org.bouncycastle.bcpg.sig.Exportable;
import org.bouncycastle.bcpg.sig.Features;
import org.bouncycastle.bcpg.sig.IntendedRecipientFingerprint;
import org.bouncycastle.bcpg.sig.IssuerFingerprint;
import org.bouncycastle.bcpg.sig.IssuerKeyID;
import org.bouncycastle.bcpg.sig.KeyExpirationTime;
import org.bouncycastle.bcpg.sig.KeyFlags;
import org.bouncycastle.bcpg.sig.NotationData;
import org.bouncycastle.bcpg.sig.PreferredAlgorithms;
import org.bouncycastle.bcpg.sig.PrimaryUserID;
import org.bouncycastle.bcpg.sig.Revocable;
import org.bouncycastle.bcpg.sig.RevocationKey;
import org.bouncycastle.bcpg.sig.RevocationReason;
import org.bouncycastle.bcpg.sig.SignatureCreationTime;
import org.bouncycastle.bcpg.sig.SignatureExpirationTime;
import org.bouncycastle.bcpg.sig.SignatureTarget;
import org.bouncycastle.bcpg.sig.SignerUserID;
import org.bouncycastle.bcpg.sig.TrustSignature;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPSignatureSubpacketVector;
import org.pgpainless.algorithm.CompressionAlgorithm;
import org.pgpainless.algorithm.Feature;
import org.pgpainless.algorithm.HashAlgorithm;
import org.pgpainless.algorithm.KeyFlag;
import org.pgpainless.algorithm.PublicKeyAlgorithm;
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
import org.pgpainless.key.util.RevocationAttributes;
public class SignatureSubpackets
implements BaseSignatureSubpackets, SelfSignatureSubpackets, CertificationSubpackets, RevocationSignatureSubpackets {
private SignatureCreationTime signatureCreationTime;
private SignatureExpirationTime signatureExpirationTime;
private IssuerKeyID issuerKeyID;
private IssuerFingerprint issuerFingerprint;
private final List<NotationData> notationDataList = new ArrayList<>();
private final List<IntendedRecipientFingerprint> intendedRecipientFingerprintList = new ArrayList<>();
private final List<RevocationKey> revocationKeyList = new ArrayList<>();
private Exportable exportable;
private SignatureTarget signatureTarget;
private Features features;
private KeyFlags keyFlags;
private TrustSignature trust;
private PreferredAlgorithms preferredCompressionAlgorithms;
private PreferredAlgorithms preferredSymmetricKeyAlgorithms;
private PreferredAlgorithms preferredHashAlgorithms;
private final List<EmbeddedSignature> embeddedSignatureList = new ArrayList<>();
private SignerUserID signerUserId;
private KeyExpirationTime keyExpirationTime;
private PrimaryUserID primaryUserId;
private Revocable revocable;
private RevocationReason revocationReason;
private final List<SignatureSubpacket> residualSubpackets = new ArrayList<>();
public SignatureSubpackets() {
}
public static SignatureSubpackets refreshHashedSubpackets(PGPPublicKey issuer, PGPSignature oldSignature) {
return createHashedSubpacketsFrom(issuer, oldSignature.getHashedSubPackets());
}
public static SignatureSubpackets refreshUnhashedSubpackets(PGPSignature oldSignature) {
return createSubpacketsFrom(oldSignature.getUnhashedSubPackets());
}
public static SignatureSubpackets createHashedSubpacketsFrom(PGPPublicKey issuer, PGPSignatureSubpacketVector base) {
SignatureSubpackets wrapper = createSubpacketsFrom(base);
wrapper.setIssuerFingerprintAndKeyId(issuer);
return wrapper;
}
public static SignatureSubpackets createSubpacketsFrom(PGPSignatureSubpacketVector base) {
SignatureSubpackets wrapper = new SignatureSubpackets();
SignatureSubpacketsHelper.applyFrom(base, wrapper);
return wrapper;
}
public static SignatureSubpackets createHashedSubpackets(PGPPublicKey issuer) {
SignatureSubpackets wrapper = new SignatureSubpackets();
wrapper.setIssuerFingerprintAndKeyId(issuer);
return wrapper;
}
@Override
public SignatureSubpackets setIssuerFingerprintAndKeyId(PGPPublicKey key) {
setIssuerKeyId(key.getKeyID());
setIssuerFingerprint(key);
return this;
}
@Override
public SignatureSubpackets setIssuerKeyId(long keyId) {
return setIssuerKeyId(true, keyId);
}
@Override
public SignatureSubpackets setIssuerKeyId(boolean isCritical, long keyId) {
return setIssuerKeyId(new IssuerKeyID(isCritical, keyId));
}
@Override
public SignatureSubpackets setIssuerKeyId(@Nullable IssuerKeyID issuerKeyID) {
this.issuerKeyID = issuerKeyID;
return this;
}
public IssuerKeyID getIssuerKeyIdSubpacket() {
return issuerKeyID;
}
@Override
public SignatureSubpackets setIssuerFingerprint(@Nonnull PGPPublicKey key) {
return setIssuerFingerprint(true, key);
}
@Override
public SignatureSubpackets setIssuerFingerprint(boolean isCritical, @Nonnull PGPPublicKey key) {
return setIssuerFingerprint(new IssuerFingerprint(isCritical, key.getVersion(), key.getFingerprint()));
}
@Override
public SignatureSubpackets setIssuerFingerprint(@Nullable IssuerFingerprint fingerprint) {
this.issuerFingerprint = fingerprint;
return this;
}
public IssuerFingerprint getIssuerFingerprintSubpacket() {
return issuerFingerprint;
}
@Override
public SignatureSubpackets setKeyFlags(KeyFlag... keyFlags) {
return setKeyFlags(true, keyFlags);
}
@Override
public SignatureSubpackets setKeyFlags(boolean isCritical, KeyFlag... keyFlags) {
int bitmask = KeyFlag.toBitmask(keyFlags);
return setKeyFlags(new KeyFlags(isCritical, bitmask));
}
@Override
public SignatureSubpackets setKeyFlags(@Nullable KeyFlags keyFlags) {
this.keyFlags = keyFlags;
return this;
}
public KeyFlags getKeyFlagsSubpacket() {
return keyFlags;
}
@Override
public SignatureSubpackets setSignatureCreationTime(@Nonnull Date creationTime) {
return setSignatureCreationTime(true, creationTime);
}
@Override
public SignatureSubpackets setSignatureCreationTime(boolean isCritical, @Nonnull Date creationTime) {
return setSignatureCreationTime(new SignatureCreationTime(isCritical, creationTime));
}
@Override
public SignatureSubpackets setSignatureCreationTime(@Nullable SignatureCreationTime signatureCreationTime) {
this.signatureCreationTime = signatureCreationTime;
return this;
}
public SignatureCreationTime getSignatureCreationTimeSubpacket() {
return signatureCreationTime;
}
@Override
public SignatureSubpackets setSignatureExpirationTime(@Nonnull Date creationTime, @Nonnull Date expirationTime) {
return setSignatureExpirationTime(true, creationTime, expirationTime);
}
@Override
public SignatureSubpackets setSignatureExpirationTime(boolean isCritical, @Nonnull Date creationTime, @Nonnull Date expirationTime) {
return setSignatureExpirationTime(isCritical, (expirationTime.getTime() / 1000) - (creationTime.getTime() / 1000));
}
@Override
public SignatureSubpackets setSignatureExpirationTime(boolean isCritical, long seconds) {
if (seconds < 0) {
throw new IllegalArgumentException("Expiration time cannot be negative.");
}
return setSignatureExpirationTime(new SignatureExpirationTime(isCritical, seconds));
}
@Override
public SignatureSubpackets setSignatureExpirationTime(@Nullable SignatureExpirationTime expirationTime) {
this.signatureExpirationTime = expirationTime;
return this;
}
public SignatureExpirationTime getSignatureExpirationTimeSubpacket() {
return signatureExpirationTime;
}
@Override
public SignatureSubpackets setSignerUserId(@Nonnull String userId) {
return setSignerUserId(false, userId);
}
@Override
public SignatureSubpackets setSignerUserId(boolean isCritical, @Nonnull String userId) {
return setSignerUserId(new SignerUserID(isCritical, userId));
}
@Override
public SignatureSubpackets setSignerUserId(@Nullable SignerUserID signerUserId) {
this.signerUserId = signerUserId;
return this;
}
public SignerUserID getSignerUserIdSubpacket() {
return signerUserId;
}
@Override
public SignatureSubpackets setPrimaryUserId() {
return setPrimaryUserId(true);
}
@Override
public SignatureSubpackets setPrimaryUserId(boolean isCritical) {
return setPrimaryUserId(new PrimaryUserID(isCritical, true));
}
@Override
public SignatureSubpackets setPrimaryUserId(@Nullable PrimaryUserID primaryUserId) {
this.primaryUserId = primaryUserId;
return this;
}
public PrimaryUserID getPrimaryUserIdSubpacket() {
return primaryUserId;
}
@Override
public SignatureSubpackets setKeyExpirationTime(@Nonnull PGPPublicKey key, @Nonnull Date keyExpirationTime) {
return setKeyExpirationTime(key.getCreationTime(), keyExpirationTime);
}
@Override
public SignatureSubpackets setKeyExpirationTime(@Nonnull Date keyCreationTime, @Nonnull Date keyExpirationTime) {
return setKeyExpirationTime(true, keyCreationTime, keyExpirationTime);
}
@Override
public SignatureSubpackets setKeyExpirationTime(boolean isCritical, @Nonnull Date keyCreationTime, @Nonnull Date keyExpirationTime) {
return setKeyExpirationTime(isCritical, (keyExpirationTime.getTime() / 1000) - (keyCreationTime.getTime() / 1000));
}
@Override
public SignatureSubpackets setKeyExpirationTime(boolean isCritical, long secondsFromCreationToExpiration) {
if (secondsFromCreationToExpiration < 0) {
throw new IllegalArgumentException("Seconds from key creation to expiration cannot be less than 0.");
}
return setKeyExpirationTime(new KeyExpirationTime(isCritical, secondsFromCreationToExpiration));
}
@Override
public SignatureSubpackets setKeyExpirationTime(@Nullable KeyExpirationTime keyExpirationTime) {
this.keyExpirationTime = keyExpirationTime;
return this;
}
public KeyExpirationTime getKeyExpirationTimeSubpacket() {
return keyExpirationTime;
}
@Override
public SignatureSubpackets setPreferredCompressionAlgorithms(CompressionAlgorithm... algorithms) {
return setPreferredCompressionAlgorithms(new LinkedHashSet<>(Arrays.asList(algorithms)));
}
@Override
public SignatureSubpackets setPreferredCompressionAlgorithms(Set<CompressionAlgorithm> algorithms) {
return setPreferredCompressionAlgorithms(true, algorithms);
}
@Override
public SignatureSubpackets setPreferredCompressionAlgorithms(boolean isCritical, Set<CompressionAlgorithm> algorithms) {
int[] ids = new int[algorithms.size()];
Iterator<CompressionAlgorithm> iterator = algorithms.iterator();
for (int i = 0; i < algorithms.size(); i++) {
ids[i] = iterator.next().getAlgorithmId();
}
return setPreferredCompressionAlgorithms(new PreferredAlgorithms(
SignatureSubpacketTags.PREFERRED_COMP_ALGS, isCritical, ids));
}
@Override
public SignatureSubpackets setPreferredCompressionAlgorithms(@Nullable PreferredAlgorithms algorithms) {
if (algorithms == null) {
this.preferredCompressionAlgorithms = null;
return this;
}
if (algorithms.getType() != SignatureSubpacketTags.PREFERRED_COMP_ALGS) {
throw new IllegalArgumentException("Invalid preferred compression algorithms type.");
}
this.preferredCompressionAlgorithms = algorithms;
return this;
}
public PreferredAlgorithms getPreferredCompressionAlgorithmsSubpacket() {
return preferredCompressionAlgorithms;
}
@Override
public SignatureSubpackets setPreferredSymmetricKeyAlgorithms(SymmetricKeyAlgorithm... algorithms) {
return setPreferredSymmetricKeyAlgorithms(new LinkedHashSet<>(Arrays.asList(algorithms)));
}
@Override
public SignatureSubpackets setPreferredSymmetricKeyAlgorithms(Set<SymmetricKeyAlgorithm> algorithms) {
return setPreferredSymmetricKeyAlgorithms(true, algorithms);
}
@Override
public SignatureSubpackets setPreferredSymmetricKeyAlgorithms(boolean isCritical, Set<SymmetricKeyAlgorithm> algorithms) {
int[] ids = new int[algorithms.size()];
Iterator<SymmetricKeyAlgorithm> iterator = algorithms.iterator();
for (int i = 0; i < algorithms.size(); i++) {
ids[i] = iterator.next().getAlgorithmId();
}
return setPreferredSymmetricKeyAlgorithms(new PreferredAlgorithms(
SignatureSubpacketTags.PREFERRED_SYM_ALGS, isCritical, ids));
}
@Override
public SignatureSubpackets setPreferredSymmetricKeyAlgorithms(@Nullable PreferredAlgorithms algorithms) {
if (algorithms == null) {
this.preferredSymmetricKeyAlgorithms = null;
return this;
}
if (algorithms.getType() != SignatureSubpacketTags.PREFERRED_SYM_ALGS) {
throw new IllegalArgumentException("Invalid preferred symmetric key algorithms type.");
}
this.preferredSymmetricKeyAlgorithms = algorithms;
return this;
}
public PreferredAlgorithms getPreferredSymmetricKeyAlgorithmsSubpacket() {
return preferredSymmetricKeyAlgorithms;
}
@Override
public SignatureSubpackets setPreferredHashAlgorithms(HashAlgorithm... algorithms) {
return setPreferredHashAlgorithms(new LinkedHashSet<>(Arrays.asList(algorithms)));
}
@Override
public SignatureSubpackets setPreferredHashAlgorithms(Set<HashAlgorithm> algorithms) {
return setPreferredHashAlgorithms(true, algorithms);
}
@Override
public SignatureSubpackets setPreferredHashAlgorithms(boolean isCritical, Set<HashAlgorithm> algorithms) {
int[] ids = new int[algorithms.size()];
Iterator<HashAlgorithm> iterator = algorithms.iterator();
for (int i = 0; i < ids.length; i++) {
ids[i] = iterator.next().getAlgorithmId();
}
return setPreferredHashAlgorithms(new PreferredAlgorithms(
SignatureSubpacketTags.PREFERRED_HASH_ALGS, isCritical, ids));
}
@Override
public SignatureSubpackets setPreferredHashAlgorithms(@Nullable PreferredAlgorithms algorithms) {
if (algorithms == null) {
preferredHashAlgorithms = null;
return this;
}
if (algorithms.getType() != SignatureSubpacketTags.PREFERRED_HASH_ALGS) {
throw new IllegalArgumentException("Invalid preferred hash algorithms type.");
}
this.preferredHashAlgorithms = algorithms;
return this;
}
public PreferredAlgorithms getPreferredHashAlgorithmsSubpacket() {
return preferredHashAlgorithms;
}
@Override
public SignatureSubpackets addNotationData(boolean isCritical, @Nonnull String notationName, @Nonnull String notationValue) {
return addNotationData(isCritical, true, notationName, notationValue);
}
@Override
public SignatureSubpackets addNotationData(boolean isCritical, boolean isHumanReadable, @Nonnull String notationName, @Nonnull String notationValue) {
return addNotationData(new NotationData(isCritical, isHumanReadable, notationName, notationValue));
}
@Override
public SignatureSubpackets addNotationData(@Nonnull NotationData notationData) {
notationDataList.add(notationData);
return this;
}
@Override
public SignatureSubpackets clearNotationData() {
notationDataList.clear();
return this;
}
public List<NotationData> getNotationDataSubpackets() {
return new ArrayList<>(notationDataList);
}
@Override
public SignatureSubpackets addIntendedRecipientFingerprint(@Nonnull PGPPublicKey recipient) {
return addIntendedRecipientFingerprint(false, recipient);
}
@Override
public SignatureSubpackets addIntendedRecipientFingerprint(boolean isCritical, @Nonnull PGPPublicKey recipient) {
return addIntendedRecipientFingerprint(new IntendedRecipientFingerprint(isCritical, recipient.getVersion(), recipient.getFingerprint()));
}
@Override
public SignatureSubpackets addIntendedRecipientFingerprint(IntendedRecipientFingerprint intendedRecipientFingerprint) {
this.intendedRecipientFingerprintList.add(intendedRecipientFingerprint);
return this;
}
@Override
public SignatureSubpackets clearIntendedRecipientFingerprints() {
intendedRecipientFingerprintList.clear();
return this;
}
public List<IntendedRecipientFingerprint> getIntendedRecipientFingerprintSubpackets() {
return new ArrayList<>(intendedRecipientFingerprintList);
}
@Override
public SignatureSubpackets setExportable(boolean isCritical, boolean isExportable) {
return setExportable(new Exportable(isCritical, isExportable));
}
@Override
public SignatureSubpackets setExportable(@Nullable Exportable exportable) {
this.exportable = exportable;
return this;
}
public Exportable getExportableSubpacket() {
return exportable;
}
@Override
public SignatureSubpackets setRevocable(boolean isCritical, boolean isRevocable) {
return setRevocable(new Revocable(isCritical, isRevocable));
}
@Override
public SignatureSubpackets setRevocable(@Nullable Revocable revocable) {
this.revocable = revocable;
return this;
}
public Revocable getRevocableSubpacket() {
return revocable;
}
@Override
public SignatureSubpackets addRevocationKey(@Nonnull PGPPublicKey revocationKey) {
return addRevocationKey(true, revocationKey);
}
@Override
public SignatureSubpackets addRevocationKey(boolean isCritical, @Nonnull PGPPublicKey revocationKey) {
return addRevocationKey(isCritical, false, revocationKey);
}
@Override
public SignatureSubpackets addRevocationKey(boolean isCritical, boolean isSensitive, @Nonnull PGPPublicKey revocationKey) {
byte clazz = (byte) 0x80;
clazz |= (isSensitive ? 0x40 : 0x00);
return addRevocationKey(new RevocationKey(isCritical, clazz, revocationKey.getAlgorithm(), revocationKey.getFingerprint()));
}
@Override
public SignatureSubpackets addRevocationKey(@Nonnull RevocationKey revocationKey) {
this.revocationKeyList.add(revocationKey);
return this;
}
@Override
public SignatureSubpackets clearRevocationKeys() {
revocationKeyList.clear();
return this;
}
public List<RevocationKey> getRevocationKeySubpackets() {
return new ArrayList<>(revocationKeyList);
}
@Override
public SignatureSubpackets setRevocationReason(RevocationAttributes revocationAttributes) {
return setRevocationReason(true, revocationAttributes);
}
@Override
public SignatureSubpackets setRevocationReason(boolean isCritical, RevocationAttributes revocationAttributes) {
return setRevocationReason(isCritical, revocationAttributes.getReason(), revocationAttributes.getDescription());
}
@Override
public SignatureSubpackets setRevocationReason(boolean isCritical, RevocationAttributes.Reason reason, @Nonnull String description) {
return setRevocationReason(new RevocationReason(isCritical, reason.code(), description));
}
@Override
public SignatureSubpackets setRevocationReason(@Nullable RevocationReason reason) {
this.revocationReason = reason;
return this;
}
public RevocationReason getRevocationReasonSubpacket() {
return revocationReason;
}
@Override
public SignatureSubpackets setSignatureTarget(@Nonnull PublicKeyAlgorithm keyAlgorithm, @Nonnull HashAlgorithm hashAlgorithm, @Nonnull byte[] hashData) {
return setSignatureTarget(true, keyAlgorithm, hashAlgorithm, hashData);
}
@Override
public SignatureSubpackets setSignatureTarget(boolean isCritical, @Nonnull PublicKeyAlgorithm keyAlgorithm, @Nonnull HashAlgorithm hashAlgorithm, @Nonnull byte[] hashData) {
return setSignatureTarget(new SignatureTarget(isCritical, keyAlgorithm.getAlgorithmId(), hashAlgorithm.getAlgorithmId(), hashData));
}
@Override
public SignatureSubpackets setSignatureTarget(@Nullable SignatureTarget signatureTarget) {
this.signatureTarget = signatureTarget;
return this;
}
public SignatureTarget getSignatureTargetSubpacket() {
return signatureTarget;
}
@Override
public SignatureSubpackets setFeatures(Feature... features) {
return setFeatures(true, features);
}
@Override
public SignatureSubpackets setFeatures(boolean isCritical, Feature... features) {
byte bitmask = Feature.toBitmask(features);
return setFeatures(new Features(isCritical, bitmask));
}
@Override
public SignatureSubpackets setFeatures(@Nullable Features features) {
this.features = features;
return this;
}
public Features getFeaturesSubpacket() {
return features;
}
@Override
public SignatureSubpackets setTrust(int depth, int amount) {
return setTrust(true, depth, amount);
}
@Override
public SignatureSubpackets setTrust(boolean isCritical, int depth, int amount) {
return setTrust(new TrustSignature(isCritical, depth, amount));
}
@Override
public SignatureSubpackets setTrust(@Nullable TrustSignature trust) {
this.trust = trust;
return this;
}
public TrustSignature getTrustSubpacket() {
return trust;
}
@Override
public SignatureSubpackets addEmbeddedSignature(@Nonnull PGPSignature signature) throws IOException {
return addEmbeddedSignature(true, signature);
}
@Override
public SignatureSubpackets addEmbeddedSignature(boolean isCritical, @Nonnull PGPSignature signature) throws IOException {
byte[] sig = signature.getEncoded();
byte[] data;
if (sig.length - 1 > 256) {
data = new byte[sig.length - 3];
}
else {
data = new byte[sig.length - 2];
}
System.arraycopy(sig, sig.length - data.length, data, 0, data.length);
return addEmbeddedSignature(new EmbeddedSignature(isCritical, false, data));
}
@Override
public SignatureSubpackets addEmbeddedSignature(@Nonnull EmbeddedSignature embeddedSignature) {
this.embeddedSignatureList.add(embeddedSignature);
return this;
}
@Override
public SignatureSubpackets clearEmbeddedSignatures() {
this.embeddedSignatureList.clear();
return this;
}
public List<EmbeddedSignature> getEmbeddedSignatureSubpackets() {
return new ArrayList<>(embeddedSignatureList);
}
public SignatureSubpackets addResidualSubpacket(SignatureSubpacket subpacket) {
this.residualSubpackets.add(subpacket);
return this;
}
public List<SignatureSubpacket> getResidualSubpackets() {
return new ArrayList<>(residualSubpackets);
}
}

View file

@ -0,0 +1,180 @@
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.signature.subpackets;
import org.bouncycastle.bcpg.SignatureSubpacket;
import org.bouncycastle.bcpg.sig.EmbeddedSignature;
import org.bouncycastle.bcpg.sig.Exportable;
import org.bouncycastle.bcpg.sig.Features;
import org.bouncycastle.bcpg.sig.IntendedRecipientFingerprint;
import org.bouncycastle.bcpg.sig.KeyExpirationTime;
import org.bouncycastle.bcpg.sig.KeyFlags;
import org.bouncycastle.bcpg.sig.NotationData;
import org.bouncycastle.bcpg.sig.PreferredAlgorithms;
import org.bouncycastle.bcpg.sig.PrimaryUserID;
import org.bouncycastle.bcpg.sig.Revocable;
import org.bouncycastle.bcpg.sig.RevocationKey;
import org.bouncycastle.bcpg.sig.RevocationReason;
import org.bouncycastle.bcpg.sig.SignatureExpirationTime;
import org.bouncycastle.bcpg.sig.SignatureTarget;
import org.bouncycastle.bcpg.sig.SignerUserID;
import org.bouncycastle.bcpg.sig.TrustSignature;
import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator;
import org.bouncycastle.openpgp.PGPSignatureSubpacketVector;
import org.pgpainless.algorithm.Feature;
import org.pgpainless.algorithm.HashAlgorithm;
import org.pgpainless.algorithm.KeyFlag;
import org.pgpainless.algorithm.PublicKeyAlgorithm;
import org.pgpainless.key.util.RevocationAttributes;
public class SignatureSubpacketsHelper {
public static SignatureSubpackets applyFrom(PGPSignatureSubpacketVector vector, SignatureSubpackets subpackets) {
for (SignatureSubpacket subpacket : vector.toArray()) {
org.pgpainless.algorithm.SignatureSubpacket type = org.pgpainless.algorithm.SignatureSubpacket.fromCode(subpacket.getType());
switch (type) {
case signatureCreationTime:
case issuerKeyId:
case issuerFingerprint:
// ignore, we override this anyways
break;
case signatureExpirationTime:
SignatureExpirationTime sigExpTime = (SignatureExpirationTime) subpacket;
subpackets.setSignatureExpirationTime(sigExpTime.isCritical(), sigExpTime.getTime());
break;
case exportableCertification:
Exportable exp = (Exportable) subpacket;
subpackets.setExportable(exp.isCritical(), exp.isExportable());
break;
case trustSignature:
TrustSignature trustSignature = (TrustSignature) subpacket;
subpackets.setTrust(trustSignature.isCritical(), trustSignature.getDepth(), trustSignature.getTrustAmount());
break;
case revocable:
Revocable rev = (Revocable) subpacket;
subpackets.setRevocable(rev.isCritical(), rev.isRevocable());
break;
case keyExpirationTime:
KeyExpirationTime keyExpTime = (KeyExpirationTime) subpacket;
subpackets.setKeyExpirationTime(keyExpTime.isCritical(), keyExpTime.getTime());
break;
case preferredSymmetricAlgorithms:
subpackets.setPreferredSymmetricKeyAlgorithms((PreferredAlgorithms) subpacket);
break;
case revocationKey:
RevocationKey revocationKey = (RevocationKey) subpacket;
subpackets.addRevocationKey(revocationKey);
break;
case notationData:
NotationData notationData = (NotationData) subpacket;
subpackets.addNotationData(notationData.isCritical(), notationData.getNotationName(), notationData.getNotationValue());
break;
case preferredHashAlgorithms:
subpackets.setPreferredHashAlgorithms((PreferredAlgorithms) subpacket);
break;
case preferredCompressionAlgorithms:
subpackets.setPreferredCompressionAlgorithms((PreferredAlgorithms) subpacket);
break;
case primaryUserId:
PrimaryUserID primaryUserID = (PrimaryUserID) subpacket;
subpackets.setPrimaryUserId(primaryUserID);
break;
case keyFlags:
KeyFlags flags = (KeyFlags) subpacket;
subpackets.setKeyFlags(flags.isCritical(), KeyFlag.fromBitmask(flags.getFlags()).toArray(new KeyFlag[0]));
break;
case signerUserId:
SignerUserID signerUserID = (SignerUserID) subpacket;
subpackets.setSignerUserId(signerUserID.isCritical(), signerUserID.getID());
break;
case revocationReason:
RevocationReason reason = (RevocationReason) subpacket;
subpackets.setRevocationReason(reason.isCritical(),
RevocationAttributes.Reason.fromCode(reason.getRevocationReason()),
reason.getRevocationDescription());
break;
case features:
Features f = (Features) subpacket;
subpackets.setFeatures(f.isCritical(), Feature.fromBitmask(f.getData()[0]).toArray(new Feature[0]));
break;
case signatureTarget:
SignatureTarget target = (SignatureTarget) subpacket;
subpackets.setSignatureTarget(target.isCritical(),
PublicKeyAlgorithm.fromId(target.getPublicKeyAlgorithm()),
HashAlgorithm.fromId(target.getHashAlgorithm()),
target.getHashData());
break;
case embeddedSignature:
EmbeddedSignature embeddedSignature = (EmbeddedSignature) subpacket;
subpackets.addEmbeddedSignature(embeddedSignature);
break;
case intendedRecipientFingerprint:
IntendedRecipientFingerprint intendedRecipientFingerprint = (IntendedRecipientFingerprint) subpacket;
subpackets.addIntendedRecipientFingerprint(intendedRecipientFingerprint);
break;
case regularExpression:
case keyServerPreferences:
case preferredKeyServers:
case policyUrl:
case placeholder:
case preferredAEADAlgorithms:
case attestedCertification:
subpackets.addResidualSubpacket(subpacket);
break;
}
}
return subpackets;
}
public static PGPSignatureSubpacketGenerator applyTo(SignatureSubpackets subpackets, PGPSignatureSubpacketGenerator generator) {
addSubpacket(generator, subpackets.getIssuerKeyIdSubpacket());
addSubpacket(generator, subpackets.getIssuerFingerprintSubpacket());
addSubpacket(generator, subpackets.getSignatureCreationTimeSubpacket());
addSubpacket(generator, subpackets.getSignatureExpirationTimeSubpacket());
addSubpacket(generator, subpackets.getExportableSubpacket());
for (NotationData notationData : subpackets.getNotationDataSubpackets()) {
addSubpacket(generator, notationData);
}
for (IntendedRecipientFingerprint intendedRecipientFingerprint : subpackets.getIntendedRecipientFingerprintSubpackets()) {
addSubpacket(generator, intendedRecipientFingerprint);
}
for (RevocationKey revocationKey : subpackets.getRevocationKeySubpackets()) {
addSubpacket(generator, revocationKey);
}
addSubpacket(generator, subpackets.getSignatureTargetSubpacket());
addSubpacket(generator, subpackets.getFeaturesSubpacket());
addSubpacket(generator, subpackets.getKeyFlagsSubpacket());
addSubpacket(generator, subpackets.getTrustSubpacket());
addSubpacket(generator, subpackets.getPreferredCompressionAlgorithmsSubpacket());
addSubpacket(generator, subpackets.getPreferredSymmetricKeyAlgorithmsSubpacket());
addSubpacket(generator, subpackets.getPreferredHashAlgorithmsSubpacket());
for (EmbeddedSignature embeddedSignature : subpackets.getEmbeddedSignatureSubpackets()) {
addSubpacket(generator, embeddedSignature);
}
addSubpacket(generator, subpackets.getSignerUserIdSubpacket());
addSubpacket(generator, subpackets.getKeyExpirationTimeSubpacket());
addSubpacket(generator, subpackets.getPrimaryUserIdSubpacket());
addSubpacket(generator, subpackets.getRevocableSubpacket());
addSubpacket(generator, subpackets.getRevocationReasonSubpacket());
for (SignatureSubpacket subpacket : subpackets.getResidualSubpackets()) {
addSubpacket(generator, subpacket);
}
return generator;
}
private static void addSubpacket(PGPSignatureSubpacketGenerator generator, SignatureSubpacket subpacket) {
if (subpacket != null) {
generator.addCustomSubpacket(subpacket);
}
}
public static PGPSignatureSubpacketVector toVector(SignatureSubpackets subpackets) {
PGPSignatureSubpacketGenerator generator = new PGPSignatureSubpacketGenerator();
applyTo(subpackets, generator);
return generator.generate();
}
}