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

WIP: Implement delegations

THERE ARE THINGS BROKEN NOW. DO NOT MERGE!
This commit is contained in:
Paul Schaub 2022-05-07 21:48:36 +02:00
parent c1170773bc
commit fa5ddfd112
7 changed files with 272 additions and 55 deletions

View file

@ -165,7 +165,7 @@ public final class PGPainless {
return Policy.getInstance();
}
public static CertifyCertificate certifyCertificate() {
public static CertifyCertificate certify() {
return new CertifyCertificate();
}
}

View file

@ -0,0 +1,111 @@
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.algorithm;
public class Trustworthiness {
private final int amount;
private final int depth;
public static final int THRESHOLD_FULLY_CONVINCED = 120;
public static final int THRESHOLD_MARGINALLY_CONVINCED = 60;
public static final int THRESHOLD_NOT_TRUSTED = 0;
public Trustworthiness(int amount, int depth) {
this.amount = capAmount(amount);
this.depth = capDepth(depth);
}
public int getAmount() {
return amount;
}
public int getDepth() {
return depth;
}
/**
* 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(THRESHOLD_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(THRESHOLD_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 level <pre>n</pre> meta introducer.
* This key can introduce meta introducers of depth <pre>n - 1</pre>.
*
* @param n depth
* @return trust
*/
public Trustworthiness levelNIntroducer(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

@ -13,111 +13,171 @@ 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.DirectKeySignatureBuilder;
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;
public class CertifyCertificate {
CertifyUserId certifyUserId(PGPPublicKeyRing certificate, String userId) {
return new CertifyUserId(certificate, userId);
CertificationOnUserId userIdOnCertificate(@Nonnull String userId, @Nonnull PGPPublicKeyRing certificate) {
return new CertificationOnUserId(userId, certificate, CertificationType.GENERIC);
}
public static class CertifyUserId {
CertificationOnUserId userIdOnCertificate(@Nonnull String userid, @Nonnull PGPPublicKeyRing certificate, @Nonnull CertificationType certificationType) {
return new CertificationOnUserId(userid, certificate, certificationType);
}
DelegationOnCertificate certificate(@Nonnull PGPPublicKeyRing certificate) {
return certificate(certificate, null);
}
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;
CertifyUserId(PGPPublicKeyRing certificate, String userId) {
this(certificate, userId, CertificationType.GENERIC);
}
CertifyUserId(PGPPublicKeyRing certificate, String userId, CertificationType certificationType) {
this.certificate = certificate;
CertificationOnUserId(@Nonnull String userId, @Nonnull PGPPublicKeyRing certificate, @Nonnull CertificationType certificationType) {
this.userId = userId;
this.certificate = certificate;
this.certificationType = certificationType;
}
CertifyUserIdWithSubpackets withKey(PGPSecretKeyRing certificationKey, SecretKeyRingProtector protector) throws PGPException {
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());
}
CertificationOnUserIdWithSubpackets withKey(@Nonnull PGPSecretKeyRing certificationKey, @Nonnull SecretKeyRingProtector protector) throws PGPException {
PGPSecretKey secretKey = getCertificationSecretKey(certificationKey);
ThirdPartyCertificationSignatureBuilder sigBuilder = new ThirdPartyCertificationSignatureBuilder(
certificationType.asSignatureType(), secretKey, protector);
return new CertifyUserIdWithSubpackets(certificate, userId, sigBuilder);
return new CertificationOnUserIdWithSubpackets(certificate, userId, sigBuilder);
}
}
public static class CertifyUserIdWithSubpackets {
public static class CertificationOnUserIdWithSubpackets {
private final PGPPublicKeyRing certificate;
private final String userId;
private final ThirdPartyCertificationSignatureBuilder sigBuilder;
CertifyUserIdWithSubpackets(PGPPublicKeyRing certificate, String userId, ThirdPartyCertificationSignatureBuilder sigBuilder) {
CertificationOnUserIdWithSubpackets(@Nonnull PGPPublicKeyRing certificate, @Nonnull String userId, @Nonnull ThirdPartyCertificationSignatureBuilder sigBuilder) {
this.certificate = certificate;
this.userId = userId;
this.sigBuilder = sigBuilder;
}
public CertifyUserIdResult withSubpackets(CertificationSubpackets.Callback subpacketCallback) throws PGPException {
public CertificationResult withSubpackets(@Nonnull CertificationSubpackets.Callback subpacketCallback) throws PGPException {
sigBuilder.applyCallback(subpacketCallback);
return build();
}
public CertifyUserIdResult build() throws PGPException {
public CertificationResult build() throws PGPException {
PGPSignature signature = sigBuilder.build(certificate, userId);
return new CertifyUserIdResult(certificate, userId, signature);
PGPPublicKeyRing certifiedCertificate = KeyRingUtils.injectCertification(certificate, userId, signature);
return new CertificationResult(certifiedCertificate, signature);
}
}
public static class CertifyUserIdResult {
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;
}
public DelegationOnCertificateWithSubpackets withKey(@Nonnull PGPSecretKeyRing certificationKey, @Nonnull SecretKeyRingProtector protector) throws PGPException {
PGPSecretKey secretKey = getCertificationSecretKey(certificationKey);
DirectKeySignatureBuilder sigBuilder = new DirectKeySignatureBuilder(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 DirectKeySignatureBuilder sigBuilder;
public DelegationOnCertificateWithSubpackets(@Nonnull PGPPublicKeyRing certificate, @Nonnull DirectKeySignatureBuilder sigBuilder) {
this.certificate = certificate;
this.sigBuilder = sigBuilder;
}
public CertificationResult withSubpackets(@Nonnull CertificationSubpackets.Callback subpacketsCallback) throws PGPException {
sigBuilder.applyCallback(subpacketsCallback);
return build();
}
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 String userId;
private final PGPSignature certification;
CertifyUserIdResult(PGPPublicKeyRing certificate, String userId, PGPSignature certification) {
CertificationResult(@Nonnull PGPPublicKeyRing certificate, @Nonnull PGPSignature certification) {
this.certificate = certificate;
this.userId = userId;
this.certification = certification;
}
@Nonnull
public PGPSignature getCertification() {
return certification;
}
@Nonnull
public PGPPublicKeyRing getCertifiedCertificate() {
// inject the signature
PGPPublicKeyRing certified = KeyRingUtils.injectCertification(certificate, userId, certification);
return certified;
return certificate;
}
}
private static PGPSecretKey getCertificationSecretKey(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

@ -59,6 +59,7 @@ import org.pgpainless.key.util.RevocationAttributes;
import org.pgpainless.signature.builder.DirectKeySignatureBuilder;
import org.pgpainless.signature.builder.RevocationSignatureBuilder;
import org.pgpainless.signature.builder.SelfSignatureBuilder;
import org.pgpainless.signature.subpackets.CertificationSubpackets;
import org.pgpainless.signature.subpackets.RevocationSignatureSubpackets;
import org.pgpainless.signature.subpackets.SelfSignatureSubpackets;
import org.pgpainless.signature.subpackets.SignatureSubpackets;
@ -613,9 +614,11 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface {
final Date keyCreationTime = publicKey.getCreationTime();
DirectKeySignatureBuilder builder = new DirectKeySignatureBuilder(primaryKey, secretKeyRingProtector, prevDirectKeySig);
builder.applyCallback(new SelfSignatureSubpackets.Callback() {
System.out.println("FIXME"); // will cause checkstyle warning so I remember
/*
builder.applyCallback(new CertificationSubpackets.Callback() {
@Override
public void modifyHashedSubpackets(SelfSignatureSubpackets hashedSubpackets) {
public void modifyHashedSubpackets(CertificationSubpackets hashedSubpackets) {
if (expiration != null) {
hashedSubpackets.setKeyExpirationTime(keyCreationTime, expiration);
} else {
@ -623,6 +626,7 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface {
}
}
});
*/
return builder.build(publicKey);
}

View file

@ -10,9 +10,10 @@ 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.SelfSignatureSubpackets;
import org.pgpainless.signature.subpackets.CertificationSubpackets;
public class DirectKeySignatureBuilder extends AbstractSignatureBuilder<DirectKeySignatureBuilder> {
@ -25,15 +26,15 @@ public class DirectKeySignatureBuilder extends AbstractSignatureBuilder<DirectKe
super(SignatureType.DIRECT_KEY, signingKey, protector);
}
public SelfSignatureSubpackets getHashedSubpackets() {
public CertificationSubpackets getHashedSubpackets() {
return hashedSubpackets;
}
public SelfSignatureSubpackets getUnhashedSubpackets() {
public CertificationSubpackets getUnhashedSubpackets() {
return unhashedSubpackets;
}
public void applyCallback(@Nullable SelfSignatureSubpackets.Callback callback) {
public void applyCallback(@Nullable CertificationSubpackets.Callback callback) {
if (callback != null) {
callback.modifyHashedSubpackets(getHashedSubpackets());
callback.modifyUnhashedSubpackets(getUnhashedSubpackets());
@ -41,8 +42,12 @@ public class DirectKeySignatureBuilder extends AbstractSignatureBuilder<DirectKe
}
public PGPSignature build(PGPPublicKey key) throws PGPException {
return buildAndInitSignatureGenerator()
.generateCertification(key);
PGPSignatureGenerator signatureGenerator = buildAndInitSignatureGenerator();
if (key.getKeyID() != publicSigningKey.getKeyID()) {
return signatureGenerator.generateCertification(publicSigningKey, key);
} else {
return signatureGenerator.generateCertification(key);
}
}
@Override