001// SPDX-FileCopyrightText: 2020 Paul Schaub <vanitasvitae@fsfe.org>
002//
003// SPDX-License-Identifier: Apache-2.0
004
005package org.pgpainless.key.modification.secretkeyring;
006
007import java.io.IOException;
008import java.security.InvalidAlgorithmParameterException;
009import java.security.NoSuchAlgorithmException;
010import java.util.Date;
011import javax.annotation.Nonnull;
012import javax.annotation.Nullable;
013
014import org.bouncycastle.openpgp.PGPException;
015import org.bouncycastle.openpgp.PGPKeyPair;
016import org.bouncycastle.openpgp.PGPSecretKeyRing;
017import org.bouncycastle.openpgp.PGPSignature;
018import org.pgpainless.algorithm.KeyFlag;
019import org.pgpainless.key.OpenPgpFingerprint;
020import org.pgpainless.key.generation.KeySpec;
021import org.pgpainless.key.protection.KeyRingProtectionSettings;
022import org.pgpainless.key.protection.SecretKeyRingProtector;
023import org.pgpainless.key.util.RevocationAttributes;
024import org.pgpainless.signature.subpackets.RevocationSignatureSubpackets;
025import org.pgpainless.signature.subpackets.SelfSignatureSubpackets;
026import org.pgpainless.util.Passphrase;
027import org.pgpainless.util.selection.userid.SelectUserId;
028
029public interface SecretKeyRingEditorInterface {
030
031    /**
032     * Add a user-id to the key ring.
033     *
034     * @param userId user-id
035     * @param secretKeyRingProtector protector to unlock the secret key
036     * @return the builder
037     */
038    SecretKeyRingEditorInterface addUserId(
039            @Nonnull CharSequence userId,
040            @Nonnull SecretKeyRingProtector secretKeyRingProtector)
041            throws PGPException;
042
043    /**
044     * Add a user-id to the key ring.
045     *
046     * @param userId user-id
047     * @param signatureSubpacketCallback callback that can be used to modify signature subpackets of the
048     *                                   certification signature.
049     * @param protector protector to unlock the primary secret key
050     * @return the builder
051     */
052    SecretKeyRingEditorInterface addUserId(
053            @Nonnull CharSequence userId,
054            @Nullable SelfSignatureSubpackets.Callback signatureSubpacketCallback,
055            @Nonnull SecretKeyRingProtector protector)
056            throws PGPException;
057
058    /**
059     * Add a user-id to the key ring and mark it as primary.
060     * If the user-id is already present, a new certification signature will be created.
061     *
062     * @param userId user id
063     * @param protector protector to unlock the secret key
064     * @return the builder
065     */
066    SecretKeyRingEditorInterface addPrimaryUserId(
067            @Nonnull CharSequence userId,
068            @Nonnull SecretKeyRingProtector protector)
069            throws PGPException;
070
071    /**
072     * Convenience method to revoke selected user-ids using soft revocation signatures.
073     * The revocation will use {@link RevocationAttributes.Reason#USER_ID_NO_LONGER_VALID}, so that the user-id
074     * can be re-certified at a later point.
075     *
076     * @param userIdSelector selector to select user-ids
077     * @param protector protector to unlock the primary key
078     * @return the builder
079     */
080    SecretKeyRingEditorInterface removeUserId(SelectUserId userIdSelector,
081                                              SecretKeyRingProtector protector)
082        throws PGPException;
083
084    /**
085     * Convenience method to revoke a single user-id using a soft revocation signature.
086     * The revocation will use {@link RevocationAttributes.Reason#USER_ID_NO_LONGER_VALID}. so that the user-id
087     * can be re-certified at a later point.
088     *
089     * @param userId user-id to revoke
090     * @param protector protector to unlock the primary key
091     * @return the builder
092     */
093    SecretKeyRingEditorInterface removeUserId(CharSequence userId,
094                                              SecretKeyRingProtector protector)
095        throws PGPException;
096
097    /**
098     * Add a subkey to the key ring.
099     * The subkey will be generated from the provided {@link KeySpec}.
100     *
101     * @param keySpec key specification
102     * @param subKeyPassphrase passphrase to encrypt the sub key
103     * @param secretKeyRingProtector protector to unlock the secret key of the key ring
104     * @return the builder
105     */
106    SecretKeyRingEditorInterface addSubKey(
107            @Nonnull KeySpec keySpec,
108            @Nonnull Passphrase subKeyPassphrase,
109            @Nonnull SecretKeyRingProtector secretKeyRingProtector)
110            throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException, IOException;
111
112    /**
113     * Add a subkey to the key ring.
114     * The subkey will be generated from the provided {@link KeySpec}.
115     *
116     * @param keySpec key spec of the subkey
117     * @param subkeyPassphrase passphrase to encrypt the subkey
118     * @param subpacketsCallback callback to modify the subpackets of the subkey binding signature
119     * @param secretKeyRingProtector protector to unlock the primary key
120     * @return builder
121     */
122    SecretKeyRingEditorInterface addSubKey(
123            @Nonnull KeySpec keySpec,
124            @Nonnull Passphrase subkeyPassphrase,
125            @Nullable SelfSignatureSubpackets.Callback subpacketsCallback,
126            @Nonnull SecretKeyRingProtector secretKeyRingProtector)
127            throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException;
128
129    /**
130     * Add a subkey to the key ring.
131     *
132     * @param subkey subkey key pair
133     * @param bindingSignatureCallback callback to modify the subpackets of the subkey binding signature
134     * @param subkeyProtector protector to unlock and encrypt the subkey
135     * @param primaryKeyProtector protector to unlock the primary key
136     * @param keyFlag first key flag for the subkey
137     * @param additionalKeyFlags optional additional key flags
138     * @return builder
139     */
140    SecretKeyRingEditorInterface addSubKey(
141            @Nonnull PGPKeyPair subkey,
142            @Nullable SelfSignatureSubpackets.Callback bindingSignatureCallback,
143            @Nonnull SecretKeyRingProtector subkeyProtector,
144            @Nonnull SecretKeyRingProtector primaryKeyProtector,
145            @Nonnull KeyFlag keyFlag,
146            KeyFlag... additionalKeyFlags)
147            throws PGPException, IOException, NoSuchAlgorithmException;
148
149    /**
150     * Revoke the key ring.
151     * The revocation will be a hard revocation, rendering the whole key invalid for any past or future signatures.
152     *
153     * @param secretKeyRingProtector protector of the primary key
154     * @return the builder
155     */
156    default SecretKeyRingEditorInterface revoke(
157            @Nonnull SecretKeyRingProtector secretKeyRingProtector)
158            throws PGPException {
159        return revoke(secretKeyRingProtector, (RevocationAttributes) null);
160    }
161
162    /**
163     * Revoke the key ring using the provided revocation attributes.
164     * The attributes define, whether the revocation was a hard revocation or not.
165     *
166     * @param secretKeyRingProtector protector of the primary key
167     * @param revocationAttributes reason for the revocation
168     * @return the builder
169     */
170    SecretKeyRingEditorInterface revoke(
171            @Nonnull SecretKeyRingProtector secretKeyRingProtector,
172            @Nullable RevocationAttributes revocationAttributes)
173            throws PGPException;
174
175    /**
176     * Revoke the key ring.
177     * You can use the {@link RevocationSignatureSubpackets.Callback} to modify the revocation signatures
178     * subpackets, e.g. in order to define whether this is a hard or soft revocation.
179     *
180     * @param secretKeyRingProtector protector to unlock the primary secret key
181     * @param subpacketsCallback callback to modify the revocations subpackets
182     * @return builder
183     */
184    SecretKeyRingEditorInterface revoke(
185            @Nonnull SecretKeyRingProtector secretKeyRingProtector,
186            @Nullable RevocationSignatureSubpackets.Callback subpacketsCallback) throws PGPException;
187
188    /**
189     * Revoke the subkey binding signature of a subkey.
190     * The subkey with the provided fingerprint will be revoked.
191     * If no suitable subkey is found, a {@link java.util.NoSuchElementException} will be thrown.
192     *
193     * Note: This method will hard-revoke the provided subkey, meaning it cannot be re-certified at a later point.
194     * If you instead want to temporarily "deactivate" the subkey, provide a soft revocation reason,
195     * e.g. by calling {@link #revokeSubKey(OpenPgpFingerprint, SecretKeyRingProtector, RevocationAttributes)}
196     * and provide a suitable {@link RevocationAttributes} object.
197     *
198     * @param fingerprint fingerprint of the subkey to be revoked
199     * @param secretKeyRingProtector protector to unlock the secret key ring
200     * @return the builder
201     */
202    default SecretKeyRingEditorInterface revokeSubKey(
203            @Nonnull OpenPgpFingerprint fingerprint,
204            @Nonnull SecretKeyRingProtector secretKeyRingProtector)
205            throws PGPException {
206        return revokeSubKey(fingerprint, secretKeyRingProtector, null);
207    }
208
209    /**
210     * Revoke the subkey binding signature of a subkey.
211     * The subkey with the provided fingerprint will be revoked.
212     * If no suitable subkey is found, a {@link java.util.NoSuchElementException} will be thrown.
213     *
214     * @param fingerprint fingerprint of the subkey to be revoked
215     * @param secretKeyRingProtector protector to unlock the primary key
216     * @param revocationAttributes reason for the revocation
217     * @return the builder
218     */
219    default SecretKeyRingEditorInterface revokeSubKey(
220            OpenPgpFingerprint fingerprint,
221            SecretKeyRingProtector secretKeyRingProtector,
222            RevocationAttributes revocationAttributes)
223            throws PGPException {
224        return revokeSubKey(fingerprint.getKeyId(),
225                secretKeyRingProtector,
226                revocationAttributes);
227    }
228
229    /**
230     * Revoke the subkey binding signature of a subkey.
231     * The subkey with the provided key-id will be revoked.
232     * If no suitable subkey is found, a {@link java.util.NoSuchElementException} will be thrown.
233     *
234     * @param subKeyId id of the subkey
235     * @param secretKeyRingProtector protector to unlock the primary key
236     * @param revocationAttributes reason for the revocation
237     * @return the builder
238     */
239    SecretKeyRingEditorInterface revokeSubKey(
240            long subKeyId,
241            SecretKeyRingProtector secretKeyRingProtector,
242            RevocationAttributes revocationAttributes)
243            throws PGPException;
244
245    /**
246     * Revoke the subkey binding signature of a subkey.
247     * The subkey with the provided key-id will be revoked.
248     * If no suitable subkey is found, q {@link java.util.NoSuchElementException} will be thrown.
249     *
250     * Note: This method will hard-revoke the subkey, meaning it cannot be re-bound at a later point.
251     * If you intend to re-bind the subkey in order to make it usable again at a later point in time,
252     * consider using {@link #revokeSubKey(long, SecretKeyRingProtector, RevocationAttributes)}
253     * and provide a soft revocation reason.
254     *
255     * @param subKeyId id of the subkey
256     * @param secretKeyRingProtector protector to unlock the secret key ring
257     * @return the builder
258     */
259    default SecretKeyRingEditorInterface revokeSubKey(
260            long subKeyId,
261            @Nonnull SecretKeyRingProtector secretKeyRingProtector)
262            throws PGPException {
263
264        return revokeSubKey(
265                subKeyId,
266                secretKeyRingProtector,
267                (RevocationSignatureSubpackets.Callback) null);
268    }
269
270    /**
271     * Revoke the subkey binding signature of a subkey.
272     * The subkey with the provided key-id will be revoked.
273     * If no suitable subkey is found, q {@link java.util.NoSuchElementException} will be thrown.
274     *
275     * The provided subpackets callback is used to modify the revocation signatures subpackets.
276     *
277     * @param keyID id of the subkey
278     * @param secretKeyRingProtector protector to unlock the secret key ring
279     * @param subpacketsCallback callback which can be used to modify the subpackets of the revocation
280     *                           signature
281     * @return the builder
282     */
283    SecretKeyRingEditorInterface revokeSubKey(
284            long keyID,
285            @Nonnull SecretKeyRingProtector secretKeyRingProtector,
286            @Nullable RevocationSignatureSubpackets.Callback subpacketsCallback)
287            throws PGPException;
288
289    /**
290     * Revoke the given userID.
291     * The revocation will be a hard revocation, rendering the user-id invalid for any past or future signatures.
292     * If you intend to re-certify the user-id at a later point in time, consider using
293     * {@link #revokeUserId(CharSequence, SecretKeyRingProtector, RevocationAttributes)} instead and provide
294     * a soft revocation reason.
295     *
296     * @param userId userId to revoke
297     * @param secretKeyRingProtector protector to unlock the primary key
298     * @return the builder
299     */
300    default SecretKeyRingEditorInterface revokeUserId(
301            @Nonnull CharSequence userId,
302            @Nonnull SecretKeyRingProtector secretKeyRingProtector)
303            throws PGPException {
304        return revokeUserId(userId, secretKeyRingProtector, (RevocationAttributes) null);
305    }
306
307    /**
308     * Revoke the given userID using the provided revocation attributes.
309     *
310     * @param userId userId to revoke
311     * @param secretKeyRingProtector protector to unlock the primary key
312     * @param revocationAttributes reason for the revocation
313     * @return the builder
314     */
315    SecretKeyRingEditorInterface revokeUserId(
316            @Nonnull CharSequence userId,
317            @Nonnull SecretKeyRingProtector secretKeyRingProtector,
318            @Nullable RevocationAttributes revocationAttributes)
319            throws PGPException;
320
321    /**
322     * Revoke the provided user-id.
323     * Note: If you don't provide a {@link RevocationSignatureSubpackets.Callback} which
324     * sets a revocation reason ({@link RevocationAttributes}), the revocation might be considered hard.
325     * So if you intend to re-certify the user-id at a later point to make it valid again,
326     * make sure to set a soft revocation reason in the signatures hashed area using the subpacket callback.
327     *
328     * @param userId userid to be revoked
329     * @param secretKeyRingProtector protector to unlock the primary secret key
330     * @param subpacketCallback callback to modify the revocations subpackets
331     * @return builder
332     */
333    SecretKeyRingEditorInterface revokeUserId(
334            @Nonnull CharSequence userId,
335            @Nonnull SecretKeyRingProtector secretKeyRingProtector,
336            @Nullable RevocationSignatureSubpackets.Callback subpacketCallback)
337            throws PGPException;
338
339    /**
340     * Revoke all user-ids that match the provided {@link SelectUserId} filter.
341     * The provided {@link RevocationAttributes} will be set as reason for revocation in each
342     * revocation signature.
343     *
344     * Note: If you intend to re-certify these user-ids at a later point, make sure to choose
345     * a soft revocation reason. See {@link RevocationAttributes.Reason} for more information.
346     *
347     * @param userIdSelector user-id selector
348     * @param secretKeyRingProtector protector to unlock the primary secret key
349     * @param revocationAttributes revocation attributes
350     * @return builder
351     * @throws PGPException
352     */
353    SecretKeyRingEditorInterface revokeUserIds(
354            @Nonnull SelectUserId userIdSelector,
355            @Nonnull SecretKeyRingProtector secretKeyRingProtector,
356            @Nullable RevocationAttributes revocationAttributes)
357            throws PGPException;
358
359    /**
360     * Revoke all user-ids that match the provided {@link SelectUserId} filter.
361     * The provided {@link RevocationSignatureSubpackets.Callback} will be used to modify the
362     * revocation signatures subpackets.
363     *
364     * Note: If you intend to re-certify these user-ids at a later point, make sure to set
365     * a soft revocation reason in the revocation signatures hashed subpacket area using the callback.
366     *
367     * See {@link RevocationAttributes.Reason} for more information.
368     *
369     * @param userIdSelector user-id selector
370     * @param secretKeyRingProtector protector to unlock the primary secret key
371     * @param subpacketsCallback callback to modify the revocations subpackets
372     * @return builder
373     * @throws PGPException
374     */
375    SecretKeyRingEditorInterface revokeUserIds(
376            @Nonnull SelectUserId userIdSelector,
377            @Nonnull SecretKeyRingProtector secretKeyRingProtector,
378            @Nullable RevocationSignatureSubpackets.Callback subpacketsCallback)
379            throws PGPException;
380
381    /**
382     * Set the expiration date for the primary key of the key ring.
383     * If the key is supposed to never expire, then an expiration date of null is expected.
384     *
385     * @param expiration new expiration date or null
386     * @param secretKeyRingProtector to unlock the secret key
387     * @return the builder
388     */
389    SecretKeyRingEditorInterface setExpirationDate(
390            @Nullable Date expiration,
391            @Nonnull SecretKeyRingProtector secretKeyRingProtector)
392            throws PGPException;
393
394    /**
395     * Create a detached revocation certificate, which can be used to revoke the whole key.
396     *
397     * @param secretKeyRingProtector protector to unlock the primary key.
398     * @param revocationAttributes reason for the revocation
399     * @return revocation certificate
400     */
401    PGPSignature createRevocationCertificate(
402            @Nonnull SecretKeyRingProtector secretKeyRingProtector,
403            @Nullable RevocationAttributes revocationAttributes)
404            throws PGPException;
405
406    /**
407     * Create a detached revocation certificate, which can be used to revoke the specified subkey.
408     *
409     * @param subkeyId id of the subkey to be revoked
410     * @param secretKeyRingProtector protector to unlock the primary key.
411     * @param revocationAttributes reason for the revocation
412     * @return revocation certificate
413     */
414    PGPSignature createRevocationCertificate(
415            long subkeyId,
416            @Nonnull SecretKeyRingProtector secretKeyRingProtector,
417            @Nullable RevocationAttributes revocationAttributes)
418            throws PGPException;
419
420    /**
421     * Create a detached revocation certificate, which can be used to revoke the specified subkey.
422     *
423     * @param subkeyId id of the subkey to be revoked
424     * @param secretKeyRingProtector protector to unlock the primary key.
425     * @param certificateSubpacketsCallback callback to modify the subpackets of the revocation certificate.
426     * @return revocation certificate
427     */
428    PGPSignature createRevocationCertificate(
429            long subkeyId,
430            @Nonnull SecretKeyRingProtector secretKeyRingProtector,
431            @Nullable RevocationSignatureSubpackets.Callback certificateSubpacketsCallback)
432            throws PGPException;
433
434    /**
435     * Create a detached revocation certificate, which can be used to revoke the specified subkey.
436     *
437     * @param subkeyFingerprint fingerprint of the subkey to be revoked
438     * @param secretKeyRingProtector protector to unlock the primary key.
439     * @param revocationAttributes reason for the revocation
440     * @return revocation certificate
441     */
442    default PGPSignature createRevocationCertificate(
443            OpenPgpFingerprint subkeyFingerprint,
444            SecretKeyRingProtector secretKeyRingProtector,
445            @Nullable RevocationAttributes revocationAttributes)
446            throws PGPException {
447
448        return createRevocationCertificate(
449                subkeyFingerprint.getKeyId(),
450                secretKeyRingProtector,
451                revocationAttributes);
452    }
453
454    /**
455     * Change the passphrase of the whole key ring.
456     *
457     * @param oldPassphrase old passphrase or null, if the key was unprotected
458     * @return next builder step
459     */
460    default WithKeyRingEncryptionSettings changePassphraseFromOldPassphrase(
461            @Nullable Passphrase oldPassphrase) {
462        return changePassphraseFromOldPassphrase(oldPassphrase, KeyRingProtectionSettings.secureDefaultSettings());
463    }
464
465    /**
466     * Change the passphrase of the whole key ring.
467     *
468     * @param oldPassphrase old passphrase or null, if the key was unprotected
469     * @param oldProtectionSettings custom settings for the old passphrase
470     * @return next builder step
471     */
472    WithKeyRingEncryptionSettings changePassphraseFromOldPassphrase(
473            @Nullable Passphrase oldPassphrase,
474            @Nonnull KeyRingProtectionSettings oldProtectionSettings);
475
476    /**
477     * Change the passphrase of a single subkey in the key ring.
478     *
479     * Note: While it is a valid use-case to have different passphrases per subKey,
480     *  this is one of the reasons why OpenPGP sucks in practice.
481     *
482     * @param keyId id of the subkey
483     * @param oldPassphrase old passphrase
484     * @return next builder step
485     */
486    default WithKeyRingEncryptionSettings changeSubKeyPassphraseFromOldPassphrase(
487            @Nonnull Long keyId,
488            @Nullable Passphrase oldPassphrase) {
489        return changeSubKeyPassphraseFromOldPassphrase(keyId, oldPassphrase, KeyRingProtectionSettings.secureDefaultSettings());
490    }
491
492    WithKeyRingEncryptionSettings changeSubKeyPassphraseFromOldPassphrase(
493            @Nonnull Long keyId,
494            @Nullable Passphrase oldPassphrase,
495            @Nonnull KeyRingProtectionSettings oldProtectionSettings);
496
497    interface WithKeyRingEncryptionSettings {
498
499        /**
500         * Set secure default settings for the symmetric passphrase encryption.
501         * Note that this obviously has no effect if you decide to set {@link WithPassphrase#toNoPassphrase()}.
502         *
503         * @return next builder step
504         */
505        WithPassphrase withSecureDefaultSettings();
506
507        /**
508         * Set custom settings for the symmetric passphrase encryption.
509         *
510         * @param settings custom settings
511         * @return next builder step
512         */
513        WithPassphrase withCustomSettings(KeyRingProtectionSettings settings);
514
515    }
516
517    interface WithPassphrase {
518
519        /**
520         * Set the passphrase.
521         *
522         * @param passphrase passphrase
523         * @return editor builder
524         */
525        SecretKeyRingEditorInterface toNewPassphrase(Passphrase passphrase)
526                throws PGPException;
527
528        /**
529         * Leave the key unprotected.
530         *
531         * @return editor builder
532         */
533        SecretKeyRingEditorInterface toNoPassphrase() throws PGPException;
534    }
535
536    /**
537     * Return the {@link PGPSecretKeyRing}.
538     * @return the key
539     */
540    PGPSecretKeyRing done();
541
542}