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

Add SecretKeyRingEditor.replaceUserId(old,new,protector)

This commit is contained in:
Paul Schaub 2022-07-15 14:00:41 +02:00
parent 4730ac427b
commit dec3c8be60
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
3 changed files with 149 additions and 2 deletions

View file

@ -205,12 +205,61 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface {
@Override
public SecretKeyRingEditorInterface removeUserId(
CharSequence userId,
SecretKeyRingProtector protector) throws PGPException {
SecretKeyRingProtector protector)
throws PGPException {
return removeUserId(
SelectUserId.exactMatch(userId.toString()),
protector);
}
@Override
public SecretKeyRingEditorInterface replaceUserId(@Nonnull CharSequence oldUserId,
@Nonnull CharSequence newUserId,
@Nonnull SecretKeyRingProtector protector)
throws PGPException {
String oldUID = oldUserId.toString().trim();
String newUID = newUserId.toString().trim();
if (oldUID.isEmpty()) {
throw new IllegalArgumentException("Old user-id cannot be empty.");
}
if (newUID.isEmpty()) {
throw new IllegalArgumentException("New user-id cannot be empty.");
}
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeyRing);
if (!info.isUserIdValid(oldUID)) {
throw new NoSuchElementException("Key does not carry user-id '" + oldUID + "', or it is not valid.");
}
PGPSignature oldCertification = info.getLatestUserIdCertification(oldUID);
if (oldCertification == null) {
throw new AssertionError("Certification for old user-id MUST NOT be null.");
}
// Bind new user-id
addUserId(newUserId, new SelfSignatureSubpackets.Callback() {
@Override
public void modifyHashedSubpackets(SelfSignatureSubpackets hashedSubpackets) {
SignatureSubpacketsHelper.applyFrom(oldCertification.getHashedSubPackets(), (SignatureSubpackets) hashedSubpackets);
// Primary user-id
if (oldUID.equals(info.getPrimaryUserId())) {
// Implicit primary user-id
if (!oldCertification.getHashedSubPackets().isPrimaryUserID()) {
hashedSubpackets.setPrimaryUserId();
}
}
}
@Override
public void modifyUnhashedSubpackets(SelfSignatureSubpackets unhashedSubpackets) {
SignatureSubpacketsHelper.applyFrom(oldCertification.getUnhashedSubPackets(), (SignatureSubpackets) unhashedSubpackets);
}
}, protector);
return revokeUserId(oldUID, protector);
}
// TODO: Move to utility class?
private String sanitizeUserId(@Nonnull CharSequence userId) {
// TODO: Further research how to sanitize user IDs.

View file

@ -104,6 +104,26 @@ public interface SecretKeyRingEditorInterface {
SecretKeyRingProtector protector)
throws PGPException;
/**
* Replace a user-id on the key with a new one.
* The old user-id gets soft revoked and the new user-id gets bound with the same signature subpackets as the
* old one, with one exception:
* If the old user-id was implicitly primary (did not carry a {@link org.bouncycastle.bcpg.sig.PrimaryUserID} packet,
* but effectively was primary, then the new user-id will be explicitly marked as primary.
*
* @param oldUserId old user-id
* @param newUserId new user-id
* @param protector protector to unlock the secret key
* @return the builder
* @throws PGPException in case we cannot generate a revocation and certification signature
* @throws java.util.NoSuchElementException if the old user-id was not found on the key; or if the oldUserId
* was already invalid
*/
SecretKeyRingEditorInterface replaceUserId(CharSequence oldUserId,
CharSequence newUserId,
SecretKeyRingProtector protector)
throws PGPException;
/**
* Add a subkey to the key ring.
* The subkey will be generated from the provided {@link KeySpec}.