mirror of
https://github.com/moparisthebest/open-keychain
synced 2024-11-27 11:12:15 -05:00
introduce handling of notation direct key signatures, and experimental pin notation packet
This commit is contained in:
parent
576e6fd0cc
commit
c7b0b650c1
@ -370,13 +370,15 @@ public abstract class OperationResult implements Parcelable {
|
||||
MSG_KC_ERROR_MASTER_ALGO (LogLevel.ERROR, R.string.msg_kc_error_master_algo),
|
||||
MSG_KC_ERROR_DUP_KEY (LogLevel.ERROR, R.string.msg_kc_error_dup_key),
|
||||
MSG_KC_MASTER (LogLevel.DEBUG, R.string.msg_kc_master),
|
||||
MSG_KC_BAD_TYPE(LogLevel.WARN, R.string.msg_kc_bad_type),
|
||||
MSG_KC_REVOKE_BAD_ERR (LogLevel.WARN, R.string.msg_kc_revoke_bad_err),
|
||||
MSG_KC_REVOKE_BAD_LOCAL (LogLevel.WARN, R.string.msg_kc_revoke_bad_local),
|
||||
MSG_KC_REVOKE_BAD_TIME (LogLevel.WARN, R.string.msg_kc_revoke_bad_time),
|
||||
MSG_KC_REVOKE_BAD_TYPE (LogLevel.WARN, R.string.msg_kc_revoke_bad_type),
|
||||
MSG_KC_REVOKE_BAD_TYPE_UID (LogLevel.WARN, R.string.msg_kc_revoke_bad_type_uid),
|
||||
MSG_KC_REVOKE_BAD (LogLevel.WARN, R.string.msg_kc_revoke_bad),
|
||||
MSG_KC_REVOKE_DUP (LogLevel.DEBUG, R.string.msg_kc_revoke_dup),
|
||||
MSG_KC_NOTATION_DUP (LogLevel.DEBUG, R.string.msg_kc_notation_dup),
|
||||
MSG_KC_NOTATION_EMPTY (LogLevel.DEBUG, R.string.msg_kc_notation_empty),
|
||||
MSG_KC_SUB (LogLevel.DEBUG, R.string.msg_kc_sub),
|
||||
MSG_KC_SUB_BAD(LogLevel.WARN, R.string.msg_kc_sub_bad),
|
||||
MSG_KC_SUB_BAD_ERR(LogLevel.WARN, R.string.msg_kc_sub_bad_err),
|
||||
|
@ -34,6 +34,7 @@ import org.spongycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.spongycastle.openpgp.PGPSignature;
|
||||
import org.spongycastle.openpgp.PGPSignatureGenerator;
|
||||
import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
|
||||
import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
|
||||
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
|
||||
import org.spongycastle.openpgp.operator.PBESecretKeyEncryptor;
|
||||
import org.spongycastle.openpgp.operator.PGPContentSignerBuilder;
|
||||
@ -877,7 +878,8 @@ public class PgpKeyOperation {
|
||||
log.add(LogType.MSG_MF_PASSPHRASE, indent);
|
||||
indent += 1;
|
||||
|
||||
sKR = applyNewUnlock(sKR, masterPublicKey, passphrase, saveParcel.mNewUnlock, log, indent);
|
||||
sKR = applyNewUnlock(sKR, masterPublicKey, masterPrivateKey,
|
||||
passphrase, saveParcel.mNewUnlock, log, indent);
|
||||
if (sKR == null) {
|
||||
// The error has been logged above, just return a bad state
|
||||
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
|
||||
@ -909,19 +911,60 @@ public class PgpKeyOperation {
|
||||
private static PGPSecretKeyRing applyNewUnlock(
|
||||
PGPSecretKeyRing sKR,
|
||||
PGPPublicKey masterPublicKey,
|
||||
PGPPrivateKey masterPrivateKey,
|
||||
String passphrase,
|
||||
ChangeUnlockParcel newUnlock,
|
||||
OperationLog log, int indent) throws PGPException {
|
||||
|
||||
if (newUnlock.mNewPassphrase != null) {
|
||||
return applyNewPassphrase(sKR, masterPublicKey, passphrase, newUnlock.mNewPassphrase, log, indent);
|
||||
sKR = applyNewPassphrase(sKR, masterPublicKey, passphrase, newUnlock.mNewPassphrase, log, indent);
|
||||
|
||||
// add packet with EMPTY notation data (updates old one, but will be stripped later)
|
||||
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
|
||||
masterPrivateKey.getPublicKeyPacket().getAlgorithm(), HashAlgorithmTags.SHA512)
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
|
||||
{ // set subpackets
|
||||
PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator();
|
||||
hashedPacketsGen.setExportable(false, false);
|
||||
sGen.setHashedSubpackets(hashedPacketsGen.generate());
|
||||
}
|
||||
sGen.init(PGPSignature.DIRECT_KEY, masterPrivateKey);
|
||||
PGPSignature emptySig = sGen.generateCertification(masterPublicKey);
|
||||
|
||||
masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, emptySig);
|
||||
PGPSecretKey.replacePublicKey(sKR.getSecretKey(), masterPublicKey);
|
||||
|
||||
return sKR;
|
||||
}
|
||||
|
||||
if (newUnlock.mNewPin != null) {
|
||||
sKR = applyNewPassphrase(sKR, masterPublicKey, passphrase, newUnlock.mNewPin, log, indent);
|
||||
|
||||
// add packet with EMPTY notation data (updates old one, but will be stripped later)
|
||||
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
|
||||
masterPrivateKey.getPublicKeyPacket().getAlgorithm(), HashAlgorithmTags.SHA512)
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
|
||||
{ // set subpackets
|
||||
PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator();
|
||||
hashedPacketsGen.setExportable(false, false);
|
||||
hashedPacketsGen.setNotationData(false, false, "pin@unlock.sufficientlysecure.org", "1");
|
||||
sGen.setHashedSubpackets(hashedPacketsGen.generate());
|
||||
}
|
||||
sGen.init(PGPSignature.DIRECT_KEY, masterPrivateKey);
|
||||
PGPSignature emptySig = sGen.generateCertification(masterPublicKey);
|
||||
|
||||
masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, emptySig);
|
||||
PGPSecretKey.replacePublicKey(sKR.getSecretKey(), masterPublicKey);
|
||||
|
||||
return sKR;
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException("PIN passphrases not yet implemented!");
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static PGPSecretKeyRing applyNewPassphrase(
|
||||
PGPSecretKeyRing sKR,
|
||||
PGPPublicKey masterPublicKey,
|
||||
|
@ -41,7 +41,6 @@ import org.sufficientlysecure.keychain.util.IterableIterator;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.Utf8Util;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
@ -302,6 +301,7 @@ public class UncachedKeyRing {
|
||||
|
||||
PGPPublicKey modified = masterKey;
|
||||
PGPSignature revocation = null;
|
||||
PGPSignature notation = null;
|
||||
for (PGPSignature zert : new IterableIterator<PGPSignature>(masterKey.getKeySignatures())) {
|
||||
int type = zert.getSignatureType();
|
||||
|
||||
@ -318,9 +318,9 @@ public class UncachedKeyRing {
|
||||
}
|
||||
WrappedSignature cert = new WrappedSignature(zert);
|
||||
|
||||
if (type != PGPSignature.KEY_REVOCATION) {
|
||||
if (type != PGPSignature.KEY_REVOCATION && type != PGPSignature.DIRECT_KEY) {
|
||||
// Unknown type, just remove
|
||||
log.add(LogType.MSG_KC_REVOKE_BAD_TYPE, indent, "0x" + Integer.toString(type, 16));
|
||||
log.add(LogType.MSG_KC_BAD_TYPE, indent, "0x" + Integer.toString(type, 16));
|
||||
modified = PGPPublicKey.removeCertification(modified, zert);
|
||||
badCerts += 1;
|
||||
continue;
|
||||
@ -334,14 +334,6 @@ public class UncachedKeyRing {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cert.isLocal()) {
|
||||
// Remove revocation certs with "local" flag
|
||||
log.add(LogType.MSG_KC_REVOKE_BAD_LOCAL, indent);
|
||||
modified = PGPPublicKey.removeCertification(modified, zert);
|
||||
badCerts += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
cert.init(masterKey);
|
||||
if (!cert.verifySignature(masterKey)) {
|
||||
@ -357,6 +349,41 @@ public class UncachedKeyRing {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cert.isLocal()) {
|
||||
// Remove revocation certs with "local" flag
|
||||
log.add(LogType.MSG_KC_REVOKE_BAD_LOCAL, indent);
|
||||
modified = PGPPublicKey.removeCertification(modified, zert);
|
||||
badCerts += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// special case: direct key signatures!
|
||||
if (cert.getSignatureType() == PGPSignature.DIRECT_KEY) {
|
||||
// must be local, otherwise strip!
|
||||
if (!cert.isLocal()) {
|
||||
log.add(LogType.MSG_KC_BAD_TYPE, indent);
|
||||
modified = PGPPublicKey.removeCertification(modified, zert);
|
||||
badCerts += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// first notation? fine then.
|
||||
if (notation == null) {
|
||||
notation = zert;
|
||||
// more notations? at least one is superfluous, then.
|
||||
} else if (notation.getCreationTime().before(zert.getCreationTime())) {
|
||||
log.add(LogType.MSG_KC_NOTATION_DUP, indent);
|
||||
modified = PGPPublicKey.removeCertification(modified, notation);
|
||||
redundantCerts += 1;
|
||||
notation = zert;
|
||||
} else {
|
||||
log.add(LogType.MSG_KC_NOTATION_DUP, indent);
|
||||
modified = PGPPublicKey.removeCertification(modified, zert);
|
||||
redundantCerts += 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// first revocation? fine then.
|
||||
if (revocation == null) {
|
||||
revocation = zert;
|
||||
@ -373,6 +400,16 @@ public class UncachedKeyRing {
|
||||
}
|
||||
}
|
||||
|
||||
// If we have a notation packet, check if there is even any data in it?
|
||||
if (notation != null) {
|
||||
// If there isn't, might as well strip it
|
||||
if (new WrappedSignature(notation).getNotation().isEmpty()) {
|
||||
log.add(LogType.MSG_KC_NOTATION_EMPTY, indent);
|
||||
modified = PGPPublicKey.removeCertification(modified, notation);
|
||||
redundantCerts += 1;
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList<String> processedUserIds = new ArrayList<String>();
|
||||
for (byte[] rawUserId : new IterableIterator<byte[]>(masterKey.getRawUserIDs())) {
|
||||
String userId = Utf8Util.fromUTF8ByteArrayReplaceBadEncoding(rawUserId);
|
||||
|
@ -21,6 +21,7 @@ package org.sufficientlysecure.keychain.pgp;
|
||||
import org.spongycastle.bcpg.SignatureSubpacket;
|
||||
import org.spongycastle.bcpg.SignatureSubpacketTags;
|
||||
import org.spongycastle.bcpg.sig.Exportable;
|
||||
import org.spongycastle.bcpg.sig.NotationData;
|
||||
import org.spongycastle.bcpg.sig.Revocable;
|
||||
import org.spongycastle.bcpg.sig.RevocationReason;
|
||||
import org.spongycastle.openpgp.PGPException;
|
||||
@ -37,6 +38,7 @@ import java.io.IOException;
|
||||
import java.security.SignatureException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
|
||||
/** OpenKeychain wrapper around PGPSignature objects.
|
||||
*
|
||||
@ -239,4 +241,20 @@ public class WrappedSignature {
|
||||
SignatureSubpacket p = mSig.getHashedSubPackets().getSubpacket(SignatureSubpacketTags.EXPORTABLE);
|
||||
return ! ((Exportable) p).isExportable();
|
||||
}
|
||||
|
||||
public HashMap<String,byte[]> getNotation() {
|
||||
HashMap<String,byte[]> result = new HashMap<String,byte[]>();
|
||||
|
||||
// If there is any notation data
|
||||
if (mSig.getHashedSubPackets() != null
|
||||
&& mSig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.NOTATION_DATA)) {
|
||||
// Iterate over notation data
|
||||
for (NotationData data : mSig.getHashedSubPackets().getNotationDataOccurrences()) {
|
||||
result.put(data.getNotationName(), data.getNotationValueBytes());
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -607,7 +607,7 @@
|
||||
<string name="msg_kc_revoke_bad_err">Entferne fehlerhaftes Schlüsselbund Widerrufszertifikat</string>
|
||||
<string name="msg_kc_revoke_bad_local">Entferne Schlüsselbund Widerrufszertifikat mit \"Lokal\" Attribut</string>
|
||||
<string name="msg_kc_revoke_bad_time">Entferne Schlüsselbund Widerrufszertifikat mit zukünftigem Zeitstempel</string>
|
||||
<string name="msg_kc_revoke_bad_type">Entferne Hauptschlüsselbeglaubigung unbekannter Art (%s)</string>
|
||||
<string name="msg_kc_bad_type">Entferne Hauptschlüsselbeglaubigung unbekannter Art (%s)</string>
|
||||
<string name="msg_kc_revoke_bad">Entferne fehlerhaftes Schlüsselbund Widerrufszertifikat</string>
|
||||
<string name="msg_kc_revoke_dup">Entferne redundantes Schlüsselbund Widerrufszertifikat</string>
|
||||
<string name="msg_kc_sub">Verarbeite Unterschlüssel %s</string>
|
||||
|
@ -652,7 +652,7 @@
|
||||
<string name="msg_kc_revoke_bad_err">Eliminando certificado defectuoso de revocación de juego de claves</string>
|
||||
<string name="msg_kc_revoke_bad_local">Eliminando certificado de revocación de juego de claves, con distintivo \"local\"</string>
|
||||
<string name="msg_kc_revoke_bad_time">Eliminando certificado de revocación de juego de claves, con marca de tiempo futura</string>
|
||||
<string name="msg_kc_revoke_bad_type">Eliminando certificado de clave maestra, de tipo desconocido (%s)</string>
|
||||
<string name="msg_kc_bad_type">Eliminando certificado de clave maestra, de tipo desconocido (%s)</string>
|
||||
<string name="msg_kc_revoke_bad_type_uid">Eliminando certificado de identificación de usuario en posición incorrecta</string>
|
||||
<string name="msg_kc_revoke_bad">Eliminando certificado defectuoso de revocación de juego de claves</string>
|
||||
<string name="msg_kc_revoke_dup">Eliminando certificado redundante de revocación de juego de claves </string>
|
||||
|
@ -652,7 +652,7 @@
|
||||
<string name="msg_kc_revoke_bad_err">Suppression du mauvais certificat de révocation du trousseau</string>
|
||||
<string name="msg_kc_revoke_bad_local">Suppression du certificat de révocation du trousseau ayant le drapeau « local »</string>
|
||||
<string name="msg_kc_revoke_bad_time">Suppression du certificat de révocation du trousseau ayant une estampille temporelle dans le futur</string>
|
||||
<string name="msg_kc_revoke_bad_type">Suppression du certificat de clef maîtresse de type inconnu (%s)</string>
|
||||
<string name="msg_kc_bad_type">Suppression du certificat de clef maîtresse de type inconnu (%s)</string>
|
||||
<string name="msg_kc_revoke_bad_type_uid">Suppression du certificat de l\'ID d\'utilisateur en mauvaise position</string>
|
||||
<string name="msg_kc_revoke_bad">Suppression du mauvais certificat de révocation du trousseau</string>
|
||||
<string name="msg_kc_revoke_dup"> Suppression du certificat redondant de révocation du trousseau</string>
|
||||
|
@ -602,7 +602,7 @@ Permetti accesso?\n\nATTENZIONE: Se non sai perche\' questo schermata e\' appars
|
||||
<string name="msg_kc_revoke_bad_err">Rimozione di certificato di revoca del portachiavi corrotto</string>
|
||||
<string name="msg_kc_revoke_bad_local">Rimozione certificato di revoca del portachiavi con caratteristica \"locale\"</string>
|
||||
<string name="msg_kc_revoke_bad_time">Rimozione certificato di revoca del portachiavi con marca temporale futura</string>
|
||||
<string name="msg_kc_revoke_bad_type">Rimozione certificato della chiave principale di tipo sconosciuto (%s)</string>
|
||||
<string name="msg_kc_bad_type">Rimozione certificato della chiave principale di tipo sconosciuto (%s)</string>
|
||||
<string name="msg_kc_revoke_bad">Rimozione certificato di revoca del portachiavi corrotto</string>
|
||||
<string name="msg_kc_revoke_dup">Rimozione certificato di revoca del portachiavi ridondante</string>
|
||||
<string name="msg_kc_sub">Elaborazione sottochiave %s</string>
|
||||
|
@ -618,7 +618,7 @@
|
||||
<string name="msg_kc_revoke_bad_err">問題のある鍵輪の破棄証明を破棄中</string>
|
||||
<string name="msg_kc_revoke_bad_local">鍵輪のローカルフラグ付き破棄証明を破棄中</string>
|
||||
<string name="msg_kc_revoke_bad_time">鍵輪の未来にタイムスタンプがある破棄証明を破棄中</string>
|
||||
<string name="msg_kc_revoke_bad_type">問題のある主鍵の不明な型 (%s) の証明を破棄中</string>
|
||||
<string name="msg_kc_bad_type">問題のある主鍵の不明な型 (%s) の証明を破棄中</string>
|
||||
<string name="msg_kc_revoke_bad_type_uid">不正な位置のユーザID検証を破棄中</string>
|
||||
<string name="msg_kc_revoke_bad">問題のある鍵輪の破棄証明を破棄中</string>
|
||||
<string name="msg_kc_revoke_dup">重複している鍵輪の破棄証明を破棄中</string>
|
||||
|
@ -450,7 +450,7 @@
|
||||
<string name="msg_kc_revoke_bad_err">Umikam slab certifikat za preklic zbirk ključev</string>
|
||||
<string name="msg_kc_revoke_bad_local">Umikam certifikat za preklic zbirk ključev z oznako \"lokalno\"</string>
|
||||
<string name="msg_kc_revoke_bad_time">Umikam certifikat za preklic zbirk ključev s časovno znamko v prihodnosti</string>
|
||||
<string name="msg_kc_revoke_bad_type">Umikam certifikat glavnega ključa neznanega tipa (%s)</string>
|
||||
<string name="msg_kc_bad_type">Umikam certifikat glavnega ključa neznanega tipa (%s)</string>
|
||||
<string name="msg_kc_revoke_bad">Umikam slab certifikat za preklic zbirk ključev</string>
|
||||
<string name="msg_kc_revoke_dup">Umikam odvečen certifikat za preklic zbirk ključev</string>
|
||||
<string name="msg_kc_sub">Obdelujem podključ %s</string>
|
||||
|
@ -672,7 +672,7 @@
|
||||
<string name="msg_kc_revoke_bad_err">Уклањам лош сертификат опозива привеска</string>
|
||||
<string name="msg_kc_revoke_bad_local">Уклањам сертификат опозива привеска са заставицом „локални“</string>
|
||||
<string name="msg_kc_revoke_bad_time">Уклањам сертификат опозива привеска са временском ознаком у будућности</string>
|
||||
<string name="msg_kc_revoke_bad_type">Уклањам сертификат главног кључа непознатог типа (%s)</string>
|
||||
<string name="msg_kc_bad_type">Уклањам сертификат главног кључа непознатог типа (%s)</string>
|
||||
<string name="msg_kc_revoke_bad_type_uid">Уклањам сертификат корисничког ИД-а са погрешног места</string>
|
||||
<string name="msg_kc_revoke_bad">Уклањам лош сертификат опозива привеска</string>
|
||||
<string name="msg_kc_revoke_dup">Уклањам сувишни сертификат опозива привеска</string>
|
||||
|
@ -573,7 +573,7 @@
|
||||
<string name="msg_kc_master">Bearbetar huvudnyckel</string>
|
||||
<string name="msg_kc_revoke_bad_err">Tar bort dåligt återkallelsecertifikat för nyckelring</string>
|
||||
<string name="msg_kc_revoke_bad_time">Tar bort återkallelsecertifikat för nyckelring med framtida tidstämpel</string>
|
||||
<string name="msg_kc_revoke_bad_type">Tar bort huvudnyckelcertifikat av okänd typ (%s)</string>
|
||||
<string name="msg_kc_bad_type">Tar bort huvudnyckelcertifikat av okänd typ (%s)</string>
|
||||
<string name="msg_kc_revoke_bad">Tar bort dåligt återkallelsecertifikat för nyckelring</string>
|
||||
<string name="msg_kc_revoke_dup">Tar bort överflödigt återkallelsecertifikat för nyckelring</string>
|
||||
<string name="msg_kc_sub">Bearbetar undernyckel %s</string>
|
||||
|
@ -709,13 +709,15 @@
|
||||
<string name="msg_kc_error_master_algo">"The master key uses an unknown (%s) algorithm!"</string>
|
||||
<string name="msg_kc_error_dup_key">"Subkey %s occurs twice in keyring. Keyring is malformed, not importing!"</string>
|
||||
<string name="msg_kc_master">"Processing master key"</string>
|
||||
<string name="msg_kc_bad_type">"Removing master key certificate of unknown type (%s)"</string>
|
||||
<string name="msg_kc_revoke_bad_err">"Removing bad keyring revocation certificate"</string>
|
||||
<string name="msg_kc_revoke_bad_local">"Removing keyring revocation certificate with "local" flag"</string>
|
||||
<string name="msg_kc_revoke_bad_time">"Removing keyring revocation certificate with future timestamp"</string>
|
||||
<string name="msg_kc_revoke_bad_type">"Removing master key certificate of unknown type (%s)"</string>
|
||||
<string name="msg_kc_revoke_bad_type_uid">"Removing user ID certificate in bad position"</string>
|
||||
<string name="msg_kc_revoke_bad">"Removing bad keyring revocation certificate"</string>
|
||||
<string name="msg_kc_revoke_dup">"Removing redundant keyring revocation certificate"</string>
|
||||
<string name="msg_kc_notation_dup">"Removing redundant notation certificate"</string>
|
||||
<string name="msg_kc_notation_empty">"Removing empty notation certificate"</string>
|
||||
<string name="msg_kc_sub">"Processing subkey %s"</string>
|
||||
<string name="msg_kc_sub_bad">"Removing invalid subkey binding certificate"</string>
|
||||
<string name="msg_kc_sub_bad_err">"Removing bad subkey binding certificate"</string>
|
||||
|
Loading…
Reference in New Issue
Block a user