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}