1
0
Fork 0
mirror of https://github.com/pgpainless/pgpainless.git synced 2024-11-14 00:12:06 +01:00

Merge branch 'certification'

This commit is contained in:
Paul Schaub 2022-06-29 16:01:03 +02:00
commit b8f4cc3935
11 changed files with 811 additions and 9 deletions

View file

@ -16,6 +16,7 @@ import org.pgpainless.decryption_verification.DecryptionBuilder;
import org.pgpainless.decryption_verification.DecryptionStream; import org.pgpainless.decryption_verification.DecryptionStream;
import org.pgpainless.encryption_signing.EncryptionBuilder; import org.pgpainless.encryption_signing.EncryptionBuilder;
import org.pgpainless.encryption_signing.EncryptionStream; import org.pgpainless.encryption_signing.EncryptionStream;
import org.pgpainless.key.certification.CertifyCertificate;
import org.pgpainless.key.generation.KeyRingBuilder; import org.pgpainless.key.generation.KeyRingBuilder;
import org.pgpainless.key.generation.KeyRingTemplates; import org.pgpainless.key.generation.KeyRingTemplates;
import org.pgpainless.key.info.KeyRingInfo; import org.pgpainless.key.info.KeyRingInfo;
@ -163,4 +164,13 @@ public final class PGPainless {
public static Policy getPolicy() { public static Policy getPolicy() {
return Policy.getInstance(); return Policy.getInstance();
} }
/**
* Create different kinds of signatures on other keys.
*
* @return builder
*/
public static CertifyCertificate certify() {
return new CertifyCertificate();
}
} }

View file

@ -0,0 +1,46 @@
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.algorithm;
import javax.annotation.Nonnull;
/**
* Subset of {@link SignatureType}, reduced to certification types.
*/
public enum CertificationType {
/**
* The issuer of this certification does not make any particular assertion as to how well the certifier has
* checked that the owner of the key is in fact the person described by the User ID.
*/
GENERIC(SignatureType.GENERIC_CERTIFICATION),
/**
* The issuer of this certification has not done any verification of the claim that the owner of this key is
* the User ID specified.
*/
NONE(SignatureType.NO_CERTIFICATION),
/**
* The issuer of this certification has done some casual verification of the claim of identity.
*/
CASUAL(SignatureType.CASUAL_CERTIFICATION),
/**
* The issuer of this certification has done some casual verification of the claim of identity.
*/
POSITIVE(SignatureType.POSITIVE_CERTIFICATION),
;
private final SignatureType signatureType;
CertificationType(@Nonnull SignatureType signatureType) {
this.signatureType = signatureType;
}
public @Nonnull SignatureType asSignatureType() {
return signatureType;
}
}

View file

@ -0,0 +1,188 @@
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.algorithm;
/**
* Facade class for {@link org.bouncycastle.bcpg.sig.TrustSignature}.
* A trust signature subpacket marks the trustworthiness of a certificate and defines its capabilities to act
* as a trusted introducer.
*/
public class Trustworthiness {
private final int amount;
private final int depth;
public static final int THRESHOLD_FULLY_CONVINCED = 120; // greater or equal is fully trusted
public static final int MARGINALLY_CONVINCED = 60; // default value for marginally convinced
public static final int NOT_TRUSTED = 0; // 0 is not trusted
public Trustworthiness(int amount, int depth) {
this.amount = capAmount(amount);
this.depth = capDepth(depth);
}
/**
* Get the trust amount.
* This value means how confident the issuer of the signature is in validity of the binding.
*
* @return trust amount
*/
public int getAmount() {
return amount;
}
/**
* Get the depth of the trust signature.
* This value controls, whether the certificate can act as a trusted introducer.
*
* @return depth
*/
public int getDepth() {
return depth;
}
/**
* Returns true, if the trust amount is equal to 0.
* This means the key is not trusted.
*
* Otherwise return false
* @return true if untrusted
*/
public boolean isNotTrusted() {
return getAmount() == NOT_TRUSTED;
}
/**
* Return true if the certificate is at least marginally trusted.
* That is the case, if the trust amount is greater than 0.
*
* @return true if the cert is at least marginally trusted
*/
public boolean isMarginallyTrusted() {
return getAmount() > NOT_TRUSTED;
}
/**
* Return true if the certificate is fully trusted. That is the case if the trust amount is
* greater than or equal to 120.
*
* @return true if the cert is fully trusted
*/
public boolean isFullyTrusted() {
return getAmount() >= THRESHOLD_FULLY_CONVINCED;
}
/**
* Return true, if the cert is an introducer. That is the case if the depth is greater 0.
*
* @return true if introducer
*/
public boolean isIntroducer() {
return getDepth() >= 1;
}
/**
* Return true, if the certified cert can introduce certificates with trust depth of <pre>otherDepth</pre>.
*
* @param otherDepth other certifications trust depth
* @return true if the cert can introduce the other
*/
public boolean canIntroduce(int otherDepth) {
return getDepth() > otherDepth;
}
/**
* Return true, if the certified cert can introduce certificates with the given <pre>other</pre> trust depth.
*
* @param other other certificates trust depth
* @return true if the cert can introduce the other
*/
public boolean canIntroduce(Trustworthiness other) {
return canIntroduce(other.getDepth());
}
/**
* This means that we are fully convinced of the trustworthiness of the key.
*
* @return builder
*/
public static Builder fullyTrusted() {
return new Builder(THRESHOLD_FULLY_CONVINCED);
}
/**
* This means that we are marginally (partially) convinced of the trustworthiness of the key.
*
* @return builder
*/
public static Builder marginallyTrusted() {
return new Builder(MARGINALLY_CONVINCED);
}
/**
* This means that we do not trust the key.
* Can be used to overwrite previous trust.
*
* @return builder
*/
public static Builder untrusted() {
return new Builder(NOT_TRUSTED);
}
public static final class Builder {
private final int amount;
private Builder(int amount) {
this.amount = amount;
}
/**
* The key is a trusted introducer (depth 1).
* Certifications made by this key are considered trustworthy.
*
* @return trust
*/
public Trustworthiness introducer() {
return new Trustworthiness(amount, 1);
}
/**
* The key is a meta introducer (depth 2).
* This key can introduce trusted introducers of depth 1.
*
* @return trust
*/
public Trustworthiness metaIntroducer() {
return new Trustworthiness(amount, 2);
}
/**
* The key is a meta introducer of depth <pre>n</pre>.
* This key can introduce meta introducers of depth <pre>n - 1</pre>.
*
* @param n depth
* @return trust
*/
public Trustworthiness metaIntroducerOfDepth(int n) {
return new Trustworthiness(amount, n);
}
}
private static int capAmount(int amount) {
if (amount < 0 || amount > 255) {
throw new IllegalArgumentException("Trust amount MUST be a value between 0 and 255");
}
return amount;
}
private static int capDepth(int depth) {
if (depth < 0 || depth > 255) {
throw new IllegalArgumentException("Trust depth MUST be a value between 0 and 255");
}
return depth;
}
}

View file

@ -0,0 +1,293 @@
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.key.certification;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSignature;
import org.pgpainless.PGPainless;
import org.pgpainless.algorithm.CertificationType;
import org.pgpainless.algorithm.KeyFlag;
import org.pgpainless.algorithm.Trustworthiness;
import org.pgpainless.exception.KeyException;
import org.pgpainless.key.OpenPgpFingerprint;
import org.pgpainless.key.info.KeyRingInfo;
import org.pgpainless.key.protection.SecretKeyRingProtector;
import org.pgpainless.key.util.KeyRingUtils;
import org.pgpainless.signature.builder.ThirdPartyDirectKeySignatureBuilder;
import org.pgpainless.signature.builder.ThirdPartyCertificationSignatureBuilder;
import org.pgpainless.signature.subpackets.CertificationSubpackets;
import org.pgpainless.util.DateUtil;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Date;
/**
* API for creating certifications and delegations (Signatures) on keys.
* This API can be used to sign another persons OpenPGP key.
*
* A certification over a user-id is thereby used to attest, that the user believes that the user-id really belongs
* to the owner of the certificate.
* A delegation over a key can be used to delegate trust by marking the certificate as a trusted introducer.
*/
public class CertifyCertificate {
/**
* Create a certification over a User-Id.
* By default, this method will use {@link CertificationType#GENERIC} to create the signature.
* If you need to create another type of certification, use
* {@link #userIdOnCertificate(String, PGPPublicKeyRing, CertificationType)} instead.
*
* @param userId user-id to certify
* @param certificate certificate
* @return API
*/
public CertificationOnUserId userIdOnCertificate(@Nonnull String userId,
@Nonnull PGPPublicKeyRing certificate) {
return new CertificationOnUserId(userId, certificate, CertificationType.GENERIC);
}
/**
* Create a certification of the given {@link CertificationType} over a User-Id.
*
* @param userid user-id to certify
* @param certificate certificate
* @param certificationType type of signature
* @return API
*/
public CertificationOnUserId userIdOnCertificate(@Nonnull String userid,
@Nonnull PGPPublicKeyRing certificate,
@Nonnull CertificationType certificationType) {
return new CertificationOnUserId(userid, certificate, certificationType);
}
/**
* Create a delegation (direct key signature) over a certificate.
* This can be used to mark a certificate as a trusted introducer
* (see {@link #certificate(PGPPublicKeyRing, Trustworthiness)}).
*
* @param certificate certificate
* @return API
*/
public DelegationOnCertificate certificate(@Nonnull PGPPublicKeyRing certificate) {
return certificate(certificate, null);
}
/**
* Create a delegation (direct key signature) containing a {@link org.bouncycastle.bcpg.sig.TrustSignature} packet
* over a certificate.
* This can be used to mark a certificate as a trusted introducer.
*
* @param certificate certificate
* @param trustworthiness trustworthiness of the certificate
* @return API
*/
public DelegationOnCertificate certificate(@Nonnull PGPPublicKeyRing certificate,
@Nullable Trustworthiness trustworthiness) {
return new DelegationOnCertificate(certificate, trustworthiness);
}
public static class CertificationOnUserId {
private final PGPPublicKeyRing certificate;
private final String userId;
private final CertificationType certificationType;
CertificationOnUserId(@Nonnull String userId,
@Nonnull PGPPublicKeyRing certificate,
@Nonnull CertificationType certificationType) {
this.userId = userId;
this.certificate = certificate;
this.certificationType = certificationType;
}
/**
* Create the certification using the given key.
*
* @param certificationKey key used to create the certification
* @param protector protector to unlock the certification key
* @return API
* @throws PGPException in case of an OpenPGP related error
*/
public CertificationOnUserIdWithSubpackets withKey(@Nonnull PGPSecretKeyRing certificationKey,
@Nonnull SecretKeyRingProtector protector)
throws PGPException {
PGPSecretKey secretKey = getCertifyingSecretKey(certificationKey);
ThirdPartyCertificationSignatureBuilder sigBuilder = new ThirdPartyCertificationSignatureBuilder(
certificationType.asSignatureType(), secretKey, protector);
return new CertificationOnUserIdWithSubpackets(certificate, userId, sigBuilder);
}
}
public static class CertificationOnUserIdWithSubpackets {
private final PGPPublicKeyRing certificate;
private final String userId;
private final ThirdPartyCertificationSignatureBuilder sigBuilder;
CertificationOnUserIdWithSubpackets(@Nonnull PGPPublicKeyRing certificate,
@Nonnull String userId,
@Nonnull ThirdPartyCertificationSignatureBuilder sigBuilder) {
this.certificate = certificate;
this.userId = userId;
this.sigBuilder = sigBuilder;
}
/**
* Apply the given signature subpackets and build the certification.
*
* @param subpacketCallback callback to modify the signatures subpackets
* @return result
* @throws PGPException in case of an OpenPGP related error
*/
public CertificationResult buildWithSubpackets(@Nonnull CertificationSubpackets.Callback subpacketCallback)
throws PGPException {
sigBuilder.applyCallback(subpacketCallback);
return build();
}
/**
* Build the certification signature.
*
* @return result
* @throws PGPException in case of an OpenPGP related error
*/
public CertificationResult build() throws PGPException {
PGPSignature signature = sigBuilder.build(certificate, userId);
PGPPublicKeyRing certifiedCertificate = KeyRingUtils.injectCertification(certificate, userId, signature);
return new CertificationResult(certifiedCertificate, signature);
}
}
public static class DelegationOnCertificate {
private final PGPPublicKeyRing certificate;
private final Trustworthiness trustworthiness;
DelegationOnCertificate(@Nonnull PGPPublicKeyRing certificate,
@Nullable Trustworthiness trustworthiness) {
this.certificate = certificate;
this.trustworthiness = trustworthiness;
}
/**
* Build the delegation using the given certification key.
*
* @param certificationKey key to create the certification with
* @param protector protector to unlock the certification key
* @return API
* @throws PGPException in case of an OpenPGP related error
*/
public DelegationOnCertificateWithSubpackets withKey(@Nonnull PGPSecretKeyRing certificationKey,
@Nonnull SecretKeyRingProtector protector)
throws PGPException {
PGPSecretKey secretKey = getCertifyingSecretKey(certificationKey);
ThirdPartyDirectKeySignatureBuilder sigBuilder = new ThirdPartyDirectKeySignatureBuilder(secretKey, protector);
if (trustworthiness != null) {
sigBuilder.getHashedSubpackets().setTrust(true, trustworthiness.getDepth(), trustworthiness.getAmount());
}
return new DelegationOnCertificateWithSubpackets(certificate, sigBuilder);
}
}
public static class DelegationOnCertificateWithSubpackets {
private final PGPPublicKeyRing certificate;
private final ThirdPartyDirectKeySignatureBuilder sigBuilder;
DelegationOnCertificateWithSubpackets(@Nonnull PGPPublicKeyRing certificate,
@Nonnull ThirdPartyDirectKeySignatureBuilder sigBuilder) {
this.certificate = certificate;
this.sigBuilder = sigBuilder;
}
/**
* Apply the given signature subpackets and build the delegation signature.
*
* @param subpacketsCallback callback to modify the signatures subpackets
* @return result
* @throws PGPException in case of an OpenPGP related error
*/
public CertificationResult buildWithSubpackets(@Nonnull CertificationSubpackets.Callback subpacketsCallback)
throws PGPException {
sigBuilder.applyCallback(subpacketsCallback);
return build();
}
/**
* Build the delegation signature.
*
* @return result
* @throws PGPException in case of an OpenPGP related error
*/
public CertificationResult build() throws PGPException {
PGPPublicKey delegatedKey = certificate.getPublicKey();
PGPSignature delegation = sigBuilder.build(delegatedKey);
PGPPublicKeyRing delegatedCertificate = KeyRingUtils.injectCertification(certificate, delegatedKey, delegation);
return new CertificationResult(delegatedCertificate, delegation);
}
}
public static class CertificationResult {
private final PGPPublicKeyRing certificate;
private final PGPSignature certification;
CertificationResult(@Nonnull PGPPublicKeyRing certificate, @Nonnull PGPSignature certification) {
this.certificate = certificate;
this.certification = certification;
}
/**
* Return the signature.
*
* @return signature
*/
@Nonnull
public PGPSignature getCertification() {
return certification;
}
/**
* Return the certificate, which now contains the signature.
*
* @return certificate + signature
*/
@Nonnull
public PGPPublicKeyRing getCertifiedCertificate() {
return certificate;
}
}
private static PGPSecretKey getCertifyingSecretKey(PGPSecretKeyRing certificationKey) {
Date now = DateUtil.now();
KeyRingInfo info = PGPainless.inspectKeyRing(certificationKey, now);
// We only support certification-capable primary keys
OpenPgpFingerprint fingerprint = info.getFingerprint();
PGPPublicKey certificationPubKey = info.getPublicKey(fingerprint);
if (!info.isKeyValidlyBound(certificationPubKey.getKeyID())) {
throw new KeyException.RevokedKeyException(fingerprint);
}
Date expirationDate = info.getExpirationDateForUse(KeyFlag.CERTIFY_OTHER);
if (expirationDate != null && expirationDate.before(now)) {
throw new KeyException.ExpiredKeyException(fingerprint, expirationDate);
}
PGPSecretKey secretKey = certificationKey.getSecretKey(certificationPubKey.getKeyID());
if (secretKey == null) {
throw new KeyException.MissingSecretKeyException(fingerprint, certificationPubKey.getKeyID());
}
return secretKey;
}
}

View file

@ -0,0 +1,8 @@
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
/**
* API for key certifications.
*/
package org.pgpainless.key.certification;

View file

@ -56,7 +56,7 @@ import org.pgpainless.key.protection.fixes.S2KUsageFix;
import org.pgpainless.key.protection.passphrase_provider.SolitaryPassphraseProvider; import org.pgpainless.key.protection.passphrase_provider.SolitaryPassphraseProvider;
import org.pgpainless.key.util.KeyRingUtils; import org.pgpainless.key.util.KeyRingUtils;
import org.pgpainless.key.util.RevocationAttributes; import org.pgpainless.key.util.RevocationAttributes;
import org.pgpainless.signature.builder.DirectKeySignatureBuilder; import org.pgpainless.signature.builder.DirectKeySelfSignatureBuilder;
import org.pgpainless.signature.builder.RevocationSignatureBuilder; import org.pgpainless.signature.builder.RevocationSignatureBuilder;
import org.pgpainless.signature.builder.SelfSignatureBuilder; import org.pgpainless.signature.builder.SelfSignatureBuilder;
import org.pgpainless.signature.subpackets.RevocationSignatureSubpackets; import org.pgpainless.signature.subpackets.RevocationSignatureSubpackets;
@ -612,7 +612,7 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface {
PGPPublicKey publicKey = primaryKey.getPublicKey(); PGPPublicKey publicKey = primaryKey.getPublicKey();
final Date keyCreationTime = publicKey.getCreationTime(); final Date keyCreationTime = publicKey.getCreationTime();
DirectKeySignatureBuilder builder = new DirectKeySignatureBuilder(primaryKey, secretKeyRingProtector, prevDirectKeySig); DirectKeySelfSignatureBuilder builder = new DirectKeySelfSignatureBuilder(primaryKey, secretKeyRingProtector, prevDirectKeySig);
builder.applyCallback(new SelfSignatureSubpackets.Callback() { builder.applyCallback(new SelfSignatureSubpackets.Callback() {
@Override @Override
public void modifyHashedSubpackets(SelfSignatureSubpackets hashedSubpackets) { public void modifyHashedSubpackets(SelfSignatureSubpackets hashedSubpackets) {

View file

@ -10,18 +10,19 @@ import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKey; import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPSecretKey; import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSignature; import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPSignatureGenerator;
import org.pgpainless.algorithm.SignatureType; import org.pgpainless.algorithm.SignatureType;
import org.pgpainless.key.protection.SecretKeyRingProtector; import org.pgpainless.key.protection.SecretKeyRingProtector;
import org.pgpainless.signature.subpackets.SelfSignatureSubpackets; import org.pgpainless.signature.subpackets.SelfSignatureSubpackets;
public class DirectKeySignatureBuilder extends AbstractSignatureBuilder<DirectKeySignatureBuilder> { public class DirectKeySelfSignatureBuilder extends AbstractSignatureBuilder<DirectKeySelfSignatureBuilder> {
public DirectKeySignatureBuilder(PGPSecretKey certificationKey, SecretKeyRingProtector protector, PGPSignature archetypeSignature) public DirectKeySelfSignatureBuilder(PGPSecretKey certificationKey, SecretKeyRingProtector protector, PGPSignature archetypeSignature)
throws PGPException { throws PGPException {
super(certificationKey, protector, archetypeSignature); super(certificationKey, protector, archetypeSignature);
} }
public DirectKeySignatureBuilder(PGPSecretKey signingKey, SecretKeyRingProtector protector) throws PGPException { public DirectKeySelfSignatureBuilder(PGPSecretKey signingKey, SecretKeyRingProtector protector) throws PGPException {
super(SignatureType.DIRECT_KEY, signingKey, protector); super(SignatureType.DIRECT_KEY, signingKey, protector);
} }
@ -41,8 +42,12 @@ public class DirectKeySignatureBuilder extends AbstractSignatureBuilder<DirectKe
} }
public PGPSignature build(PGPPublicKey key) throws PGPException { public PGPSignature build(PGPPublicKey key) throws PGPException {
return buildAndInitSignatureGenerator() PGPSignatureGenerator signatureGenerator = buildAndInitSignatureGenerator();
.generateCertification(key); if (key.getKeyID() != publicSigningKey.getKeyID()) {
return signatureGenerator.generateCertification(publicSigningKey, key);
} else {
return signatureGenerator.generateCertification(key);
}
} }
@Override @Override

View file

@ -0,0 +1,57 @@
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.signature.builder;
import javax.annotation.Nullable;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPSignatureGenerator;
import org.pgpainless.algorithm.SignatureType;
import org.pgpainless.key.protection.SecretKeyRingProtector;
import org.pgpainless.signature.subpackets.CertificationSubpackets;
public class ThirdPartyDirectKeySignatureBuilder extends AbstractSignatureBuilder<ThirdPartyDirectKeySignatureBuilder> {
public ThirdPartyDirectKeySignatureBuilder(PGPSecretKey certificationKey, SecretKeyRingProtector protector, PGPSignature archetypeSignature)
throws PGPException {
super(certificationKey, protector, archetypeSignature);
}
public ThirdPartyDirectKeySignatureBuilder(PGPSecretKey signingKey, SecretKeyRingProtector protector) throws PGPException {
super(SignatureType.DIRECT_KEY, signingKey, protector);
}
public CertificationSubpackets getHashedSubpackets() {
return hashedSubpackets;
}
public CertificationSubpackets getUnhashedSubpackets() {
return unhashedSubpackets;
}
public void applyCallback(@Nullable CertificationSubpackets.Callback callback) {
if (callback != null) {
callback.modifyHashedSubpackets(getHashedSubpackets());
callback.modifyUnhashedSubpackets(getUnhashedSubpackets());
}
}
public PGPSignature build(PGPPublicKey key) throws PGPException {
PGPSignatureGenerator signatureGenerator = buildAndInitSignatureGenerator();
if (key.getKeyID() != publicSigningKey.getKeyID()) {
return signatureGenerator.generateCertification(publicSigningKey, key);
} else {
return signatureGenerator.generateCertification(key);
}
}
@Override
protected boolean isValidSignatureType(SignatureType type) {
return type == SignatureType.DIRECT_KEY;
}
}

View file

@ -0,0 +1,87 @@
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.algorithm;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
public class TrustworthinessTest {
@Test
public void fullyTrustedIntroducer() {
Trustworthiness it = Trustworthiness.fullyTrusted().introducer();
assertTrue(it.isFullyTrusted());
assertFalse(it.isNotTrusted());
assertTrue(it.isIntroducer());
assertFalse(it.canIntroduce(it));
}
@Test
public void marginallyTrustedIntroducer() {
Trustworthiness it = Trustworthiness.marginallyTrusted().introducer();
assertFalse(it.isFullyTrusted());
assertTrue(it.isMarginallyTrusted());
assertFalse(it.isNotTrusted());
assertTrue(it.isIntroducer());
assertFalse(it.canIntroduce(2));
}
@Test
public void nonTrustedIntroducer() {
Trustworthiness it = Trustworthiness.untrusted().introducer();
assertTrue(it.isNotTrusted());
assertFalse(it.isMarginallyTrusted());
assertFalse(it.isFullyTrusted());
assertTrue(it.isIntroducer());
}
@Test
public void trustedMetaIntroducer() {
Trustworthiness it = Trustworthiness.fullyTrusted().metaIntroducer();
assertTrue(it.isFullyTrusted());
assertTrue(it.isIntroducer());
Trustworthiness that = Trustworthiness.fullyTrusted().introducer();
assertTrue(it.canIntroduce(that));
assertFalse(that.canIntroduce(it));
}
@Test
public void invalidArguments() {
assertThrows(IllegalArgumentException.class, () -> new Trustworthiness(300, 1));
assertThrows(IllegalArgumentException.class, () -> new Trustworthiness(60, 300));
assertThrows(IllegalArgumentException.class, () -> new Trustworthiness(-4, 1));
assertThrows(IllegalArgumentException.class, () -> new Trustworthiness(120, -1));
}
@Test
public void inBetweenValues() {
Trustworthiness it = new Trustworthiness(30, 1);
assertTrue(it.isMarginallyTrusted());
assertFalse(it.isFullyTrusted());
it = new Trustworthiness(140, 1);
assertTrue(it.isFullyTrusted());
}
@Test
public void depthHierarchyTest() {
Trustworthiness l1 = Trustworthiness.fullyTrusted().metaIntroducerOfDepth(1);
Trustworthiness l2 = Trustworthiness.fullyTrusted().metaIntroducerOfDepth(2);
Trustworthiness l3 = Trustworthiness.fullyTrusted().metaIntroducerOfDepth(3);
assertTrue(l3.canIntroduce(l2));
assertTrue(l3.canIntroduce(l1));
assertTrue(l2.canIntroduce(l1));
assertFalse(l1.canIntroduce(l2));
assertFalse(l1.canIntroduce(l3));
}
}

View file

@ -0,0 +1,108 @@
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.key.certification;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import org.bouncycastle.bcpg.sig.TrustSignature;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.util.Arrays;
import org.junit.jupiter.api.Test;
import org.pgpainless.PGPainless;
import org.pgpainless.algorithm.SignatureType;
import org.pgpainless.algorithm.Trustworthiness;
import org.pgpainless.key.protection.SecretKeyRingProtector;
import org.pgpainless.signature.consumer.SignatureVerifier;
import org.pgpainless.util.CollectionUtils;
import org.pgpainless.util.DateUtil;
public class CertifyCertificateTest {
@Test
public void testUserIdCertification() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException {
SecretKeyRingProtector protector = SecretKeyRingProtector.unprotectedKeys();
PGPSecretKeyRing alice = PGPainless.generateKeyRing().modernKeyRing("Alice <alice@pgpainless.org>");
String bobUserId = "Bob <bob@pgpainless.org>";
PGPSecretKeyRing bob = PGPainless.generateKeyRing().modernKeyRing(bobUserId);
PGPPublicKeyRing bobCertificate = PGPainless.extractCertificate(bob);
CertifyCertificate.CertificationResult result = PGPainless.certify()
.userIdOnCertificate(bobUserId, bobCertificate)
.withKey(alice, protector)
.build();
assertNotNull(result);
PGPSignature signature = result.getCertification();
assertNotNull(signature);
assertEquals(SignatureType.GENERIC_CERTIFICATION, SignatureType.valueOf(signature.getSignatureType()));
assertEquals(alice.getPublicKey().getKeyID(), signature.getKeyID());
assertTrue(SignatureVerifier.verifyUserIdCertification(
bobUserId, signature, alice.getPublicKey(), bob.getPublicKey(), PGPainless.getPolicy(), DateUtil.now()));
PGPPublicKeyRing bobCertified = result.getCertifiedCertificate();
PGPPublicKey bobCertifiedKey = bobCertified.getPublicKey();
// There are 2 sigs now, bobs own and alice'
assertEquals(2, CollectionUtils.iteratorToList(bobCertifiedKey.getSignaturesForID(bobUserId)).size());
List<PGPSignature> sigsByAlice = CollectionUtils.iteratorToList(
bobCertifiedKey.getSignaturesForKeyID(alice.getPublicKey().getKeyID()));
assertEquals(1, sigsByAlice.size());
assertEquals(signature, sigsByAlice.get(0));
assertFalse(Arrays.areEqual(bobCertificate.getEncoded(), bobCertified.getEncoded()));
}
@Test
public void testKeyDelegation() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException {
SecretKeyRingProtector protector = SecretKeyRingProtector.unprotectedKeys();
PGPSecretKeyRing alice = PGPainless.generateKeyRing().modernKeyRing("Alice <alice@pgpainless.org>");
PGPSecretKeyRing bob = PGPainless.generateKeyRing().modernKeyRing("Bob <bob@pgpainless.org>");
PGPPublicKeyRing bobCertificate = PGPainless.extractCertificate(bob);
CertifyCertificate.CertificationResult result = PGPainless.certify()
.certificate(bobCertificate, Trustworthiness.fullyTrusted().introducer())
.withKey(alice, protector)
.build();
assertNotNull(result);
PGPSignature signature = result.getCertification();
assertNotNull(signature);
assertEquals(SignatureType.DIRECT_KEY, SignatureType.valueOf(signature.getSignatureType()));
assertEquals(alice.getPublicKey().getKeyID(), signature.getKeyID());
TrustSignature trustSignaturePacket = signature.getHashedSubPackets().getTrust();
assertNotNull(trustSignaturePacket);
Trustworthiness trustworthiness = new Trustworthiness(trustSignaturePacket.getTrustAmount(), trustSignaturePacket.getDepth());
assertTrue(trustworthiness.isFullyTrusted());
assertTrue(trustworthiness.isIntroducer());
assertFalse(trustworthiness.canIntroduce(1));
assertTrue(SignatureVerifier.verifyDirectKeySignature(
signature, alice.getPublicKey(), bob.getPublicKey(), PGPainless.getPolicy(), DateUtil.now()));
PGPPublicKeyRing bobCertified = result.getCertifiedCertificate();
PGPPublicKey bobCertifiedKey = bobCertified.getPublicKey();
List<PGPSignature> sigsByAlice = CollectionUtils.iteratorToList(
bobCertifiedKey.getSignaturesForKeyID(alice.getPublicKey().getKeyID()));
assertEquals(1, sigsByAlice.size());
assertEquals(signature, sigsByAlice.get(0));
assertFalse(Arrays.areEqual(bobCertificate.getEncoded(), bobCertified.getEncoded()));
}
}

View file

@ -29,14 +29,14 @@ import org.pgpainless.key.util.KeyRingUtils;
import org.pgpainless.signature.subpackets.SelfSignatureSubpackets; import org.pgpainless.signature.subpackets.SelfSignatureSubpackets;
import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil; import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil;
public class DirectKeySignatureBuilderTest { public class ThirdPartyDirectKeySignatureBuilderTest {
@Test @Test
public void testDirectKeySignatureBuilding() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, InterruptedException { public void testDirectKeySignatureBuilding() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, InterruptedException {
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing() PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
.modernKeyRing("Alice"); .modernKeyRing("Alice");
DirectKeySignatureBuilder dsb = new DirectKeySignatureBuilder( DirectKeySelfSignatureBuilder dsb = new DirectKeySelfSignatureBuilder(
secretKeys.getSecretKey(), secretKeys.getSecretKey(),
SecretKeyRingProtector.unprotectedKeys()); SecretKeyRingProtector.unprotectedKeys());