mirror of
https://github.com/Mbed-TLS/mbedtls.git
synced 2026-03-26 14:11:59 +01:00
Merge pull request #1403 from felixc-arm/bignum-invmod-wrapper
[3.6] Add wrapper `mpi_gcd_invmod_odd()`
This commit is contained in:
@@ -1743,6 +1743,76 @@ int mbedtls_mpi_exp_mod_unsafe(mbedtls_mpi *X, const mbedtls_mpi *A,
|
||||
return mbedtls_mpi_exp_mod_optionally_safe(X, A, E, MBEDTLS_MPI_IS_PUBLIC, N, prec_RR);
|
||||
}
|
||||
|
||||
/* Constant-time GCD and/or modinv with odd modulus and A <= N */
|
||||
int mbedtls_mpi_gcd_modinv_odd(mbedtls_mpi *G,
|
||||
mbedtls_mpi *I,
|
||||
const mbedtls_mpi *A,
|
||||
const mbedtls_mpi *N)
|
||||
{
|
||||
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
|
||||
mbedtls_mpi local_g;
|
||||
mbedtls_mpi_uint *T = NULL;
|
||||
const size_t T_factor = I != NULL ? 5 : 4;
|
||||
const mbedtls_mpi_uint zero = 0;
|
||||
|
||||
/* Check requirements on A and N */
|
||||
if (mbedtls_mpi_cmp_int(A, 0) < 0 ||
|
||||
mbedtls_mpi_cmp_mpi(A, N) > 0 ||
|
||||
mbedtls_mpi_get_bit(N, 0) != 1 ||
|
||||
(I != NULL && mbedtls_mpi_cmp_int(N, 1) == 0)) {
|
||||
return MBEDTLS_ERR_MPI_BAD_INPUT_DATA;
|
||||
}
|
||||
|
||||
/* Check aliasing requirements */
|
||||
if (A == N || (I != NULL && (I == N || G == N))) {
|
||||
return MBEDTLS_ERR_MPI_BAD_INPUT_DATA;
|
||||
}
|
||||
|
||||
mbedtls_mpi_init(&local_g);
|
||||
|
||||
if (G == NULL) {
|
||||
G = &local_g;
|
||||
}
|
||||
|
||||
/* We can't modify the values of G or I before use in the main function,
|
||||
* as they could be aliased to A or N. */
|
||||
MBEDTLS_MPI_CHK(mbedtls_mpi_grow(G, N->n));
|
||||
if (I != NULL) {
|
||||
MBEDTLS_MPI_CHK(mbedtls_mpi_grow(I, N->n));
|
||||
}
|
||||
|
||||
T = mbedtls_calloc(sizeof(mbedtls_mpi_uint) * N->n, T_factor);
|
||||
if (T == NULL) {
|
||||
ret = MBEDTLS_ERR_MPI_ALLOC_FAILED;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
mbedtls_mpi_uint *Ip = I != NULL ? I->p : NULL;
|
||||
/* If A is 0 (null), then A->p would be null, and A->n would be 0,
|
||||
* which would be an issue if A->p and A->n were passed to
|
||||
* mbedtls_mpi_core_gcd_modinv_odd below. */
|
||||
const mbedtls_mpi_uint *Ap = A->p != NULL ? A->p : &zero;
|
||||
size_t An = A->n >= N->n ? N->n : A->p != NULL ? A->n : 1;
|
||||
mbedtls_mpi_core_gcd_modinv_odd(G->p, Ip, Ap, An, N->p, N->n, T);
|
||||
|
||||
G->s = 1;
|
||||
if (I != NULL) {
|
||||
I->s = 1;
|
||||
}
|
||||
|
||||
if (G->n > N->n) {
|
||||
memset(G->p + N->n, 0, ciL * (G->n - N->n));
|
||||
}
|
||||
if (I != NULL && I->n > N->n) {
|
||||
memset(I->p + N->n, 0, ciL * (I->n - N->n));
|
||||
}
|
||||
|
||||
cleanup:
|
||||
mbedtls_mpi_free(&local_g);
|
||||
mbedtls_free(T);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Greatest common divisor: G = gcd(A, B) (HAC 14.54)
|
||||
*/
|
||||
|
||||
@@ -47,4 +47,37 @@ int mbedtls_mpi_exp_mod_unsafe(mbedtls_mpi *X, const mbedtls_mpi *A,
|
||||
const mbedtls_mpi *E, const mbedtls_mpi *N,
|
||||
mbedtls_mpi *prec_RR);
|
||||
|
||||
/**
|
||||
* \brief A wrapper around a constant time function to compute
|
||||
* GCD(A, N) and/or A^-1 mod N if it exists.
|
||||
*
|
||||
* \warning Requires N to be odd, and 0 <= A <= N. Additionally, if
|
||||
* I != NULL, requires N > 1.
|
||||
* The wrapper part of this function is not constant time.
|
||||
*
|
||||
* \note A and N must not alias each other.
|
||||
* When I == NULL (computing only the GCD), G can alias A or N.
|
||||
* When I != NULL (computing the modular inverse), G or I can
|
||||
* alias A, but neither of them can alias N (the modulus).
|
||||
*
|
||||
* \param[out] G The GCD of \p A and \p N.
|
||||
* This may be NULL, to only compute I.
|
||||
* \param[out] I The inverse of \p A modulo \p N if it exists (that is,
|
||||
* if \p G above is 1 on exit); indeterminate otherwise.
|
||||
* This may be NULL, to only compute G.
|
||||
* \param[in] A The 1st operand of GCD and number to invert.
|
||||
* This value must be less than or equal to \p N.
|
||||
* \param[in] N The 2nd operand of GCD and modulus for inversion.
|
||||
* Must be odd or the results are indeterminate.
|
||||
*
|
||||
* \return \c 0 if successful.
|
||||
* \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed.
|
||||
* \return #MBEDTLS_ERR_MPI_BAD_INPUT_DATA if preconditions were not
|
||||
* met.
|
||||
*/
|
||||
int mbedtls_mpi_gcd_modinv_odd(mbedtls_mpi *G,
|
||||
mbedtls_mpi *I,
|
||||
const mbedtls_mpi *A,
|
||||
const mbedtls_mpi *N);
|
||||
|
||||
#endif /* bignum_internal.h */
|
||||
|
||||
@@ -1179,6 +1179,219 @@ exit:
|
||||
}
|
||||
/* END_CASE */
|
||||
|
||||
/* BEGIN_CASE */
|
||||
void mpi_gcd_modinv_odd_both(char *input_A, char *input_N,
|
||||
char *result_G, char *result_I,
|
||||
int return_code)
|
||||
{
|
||||
int has_inverse = strcmp(result_I, "no_inverse") ? 1 : 0;
|
||||
mbedtls_mpi G, I, A, N, exp_G, exp_I;
|
||||
int res;
|
||||
mbedtls_mpi_init(&G); mbedtls_mpi_init(&I); mbedtls_mpi_init(&A); mbedtls_mpi_init(&N);
|
||||
mbedtls_mpi_init(&exp_G); mbedtls_mpi_init(&exp_I);
|
||||
TEST_EQUAL(mbedtls_test_read_mpi(&A, input_A), 0);
|
||||
TEST_EQUAL(mbedtls_test_read_mpi(&N, input_N), 0);
|
||||
TEST_EQUAL(mbedtls_test_read_mpi(&exp_G, result_G), 0);
|
||||
/* If there is no inverse then the value returned in I will be
|
||||
* indeterminate, and so not useful or possible to test. */
|
||||
if (has_inverse) {
|
||||
TEST_EQUAL(mbedtls_test_read_mpi(&exp_I, result_I), 0);
|
||||
}
|
||||
|
||||
res = mbedtls_mpi_gcd_modinv_odd(&G, &I, &A, &N);
|
||||
TEST_EQUAL(res, return_code);
|
||||
if (res == 0) {
|
||||
TEST_ASSERT(sign_is_valid(&G));
|
||||
TEST_EQUAL(mbedtls_mpi_cmp_mpi(&G, &exp_G), 0);
|
||||
if (has_inverse) {
|
||||
TEST_ASSERT(sign_is_valid(&I));
|
||||
TEST_EQUAL(mbedtls_mpi_cmp_mpi(&I, &exp_I), 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Test pointer aliasing where &G == &A. */
|
||||
TEST_EQUAL(mbedtls_test_read_mpi(&G, input_A), 0);
|
||||
res = mbedtls_mpi_gcd_modinv_odd(&G, &I, /* A */ &G, &N);
|
||||
TEST_EQUAL(res, return_code);
|
||||
if (res == 0) {
|
||||
TEST_ASSERT(sign_is_valid(&G));
|
||||
TEST_EQUAL(mbedtls_mpi_cmp_mpi(&G, &exp_G), 0);
|
||||
if (has_inverse) {
|
||||
TEST_ASSERT(sign_is_valid(&I));
|
||||
TEST_EQUAL(mbedtls_mpi_cmp_mpi(&I, &exp_I), 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Test pointer aliasing where &G == &N. This should fail. */
|
||||
TEST_EQUAL(mbedtls_test_read_mpi(&G, input_N), 0);
|
||||
res = mbedtls_mpi_gcd_modinv_odd(&G, &I, &A, /* N */ &G);
|
||||
TEST_EQUAL(res, MBEDTLS_ERR_MPI_BAD_INPUT_DATA);
|
||||
|
||||
/* Test pointer aliasing where &I == &A. */
|
||||
TEST_EQUAL(mbedtls_test_read_mpi(&I, input_A), 0);
|
||||
res = mbedtls_mpi_gcd_modinv_odd(&G, &I, /* A */ &I, &N);
|
||||
TEST_EQUAL(res, return_code);
|
||||
if (res == 0) {
|
||||
TEST_ASSERT(sign_is_valid(&G));
|
||||
TEST_EQUAL(mbedtls_mpi_cmp_mpi(&G, &exp_G), 0);
|
||||
if (has_inverse) {
|
||||
TEST_ASSERT(sign_is_valid(&I));
|
||||
TEST_EQUAL(mbedtls_mpi_cmp_mpi(&I, &exp_I), 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Test pointer aliasing where &I == &N. This should fail. */
|
||||
TEST_EQUAL(mbedtls_test_read_mpi(&I, input_N), 0);
|
||||
res = mbedtls_mpi_gcd_modinv_odd(&G, &I, &A, /* N */ &I);
|
||||
TEST_EQUAL(res, MBEDTLS_ERR_MPI_BAD_INPUT_DATA);
|
||||
|
||||
/* Test pointer aliasing where &A == &N. This should fail. */
|
||||
res = mbedtls_mpi_gcd_modinv_odd(&G, &I, &A, /* N */ &A);
|
||||
TEST_EQUAL(res, MBEDTLS_ERR_MPI_BAD_INPUT_DATA);
|
||||
|
||||
/* Test G & I initialized to a number with more limbs than N. */
|
||||
if (N.n > 0) {
|
||||
TEST_EQUAL(mbedtls_mpi_grow(&G, N.n * 2), 0);
|
||||
memset(G.p, 0x2d, G.n * sizeof(mbedtls_mpi_uint));
|
||||
TEST_EQUAL(mbedtls_mpi_grow(&I, N.n * 2), 0);
|
||||
memset(I.p, 0x2f, I.n * sizeof(mbedtls_mpi_uint));
|
||||
res = mbedtls_mpi_gcd_modinv_odd(&G, &I, &A, &N);
|
||||
TEST_EQUAL(res, return_code);
|
||||
if (res == 0) {
|
||||
TEST_ASSERT(sign_is_valid(&G));
|
||||
TEST_EQUAL(mbedtls_mpi_cmp_mpi(&G, &exp_G), 0);
|
||||
if (has_inverse) {
|
||||
TEST_ASSERT(sign_is_valid(&I));
|
||||
TEST_EQUAL(mbedtls_mpi_cmp_mpi(&I, &exp_I), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
mbedtls_mpi_free(&G); mbedtls_mpi_free(&I); mbedtls_mpi_free(&A); mbedtls_mpi_free(&N);
|
||||
mbedtls_mpi_free(&exp_G); mbedtls_mpi_free(&exp_I);
|
||||
}
|
||||
/* END_CASE */
|
||||
|
||||
/* BEGIN_CASE */
|
||||
void mpi_gcd_modinv_odd_only_gcd(char *input_A, char *input_N,
|
||||
char *result_G, int return_code)
|
||||
{
|
||||
mbedtls_mpi G, A, N, exp_G;
|
||||
int res;
|
||||
mbedtls_mpi_init(&G); mbedtls_mpi_init(&A); mbedtls_mpi_init(&N);
|
||||
mbedtls_mpi_init(&exp_G);
|
||||
TEST_EQUAL(mbedtls_test_read_mpi(&A, input_A), 0);
|
||||
TEST_EQUAL(mbedtls_test_read_mpi(&N, input_N), 0);
|
||||
TEST_EQUAL(mbedtls_test_read_mpi(&exp_G, result_G), 0);
|
||||
|
||||
res = mbedtls_mpi_gcd_modinv_odd(&G, NULL, &A, &N);
|
||||
TEST_EQUAL(res, return_code);
|
||||
if (res == 0) {
|
||||
TEST_ASSERT(sign_is_valid(&G));
|
||||
TEST_EQUAL(mbedtls_mpi_cmp_mpi(&G, &exp_G), 0);
|
||||
}
|
||||
|
||||
/* Test pointer aliasing where &G == &A. */
|
||||
TEST_EQUAL(mbedtls_test_read_mpi(&G, input_A), 0);
|
||||
res = mbedtls_mpi_gcd_modinv_odd(&G, NULL, /* A */ &G, &N);
|
||||
TEST_EQUAL(res, return_code);
|
||||
if (res == 0) {
|
||||
TEST_ASSERT(sign_is_valid(&G));
|
||||
TEST_EQUAL(mbedtls_mpi_cmp_mpi(&G, &exp_G), 0);
|
||||
}
|
||||
|
||||
/* Test pointer aliasing where &G == &N. */
|
||||
TEST_EQUAL(mbedtls_test_read_mpi(&G, input_N), 0);
|
||||
res = mbedtls_mpi_gcd_modinv_odd(&G, NULL, &A, /* N */ &G);
|
||||
TEST_EQUAL(res, return_code);
|
||||
if (res == 0) {
|
||||
TEST_ASSERT(sign_is_valid(&G));
|
||||
TEST_EQUAL(mbedtls_mpi_cmp_mpi(&G, &exp_G), 0);
|
||||
}
|
||||
|
||||
/* Test pointer aliasing where &A == &N. This should fail. */
|
||||
res = mbedtls_mpi_gcd_modinv_odd(&G, NULL, &A, /* N */ &A);
|
||||
TEST_EQUAL(res, MBEDTLS_ERR_MPI_BAD_INPUT_DATA);
|
||||
|
||||
/* Test G initialized to a number with more limbs than N. */
|
||||
if (N.n > 0) {
|
||||
TEST_EQUAL(mbedtls_mpi_grow(&G, N.n * 2), 0);
|
||||
memset(G.p, 0x2b, G.n * sizeof(mbedtls_mpi_uint));
|
||||
res = mbedtls_mpi_gcd_modinv_odd(&G, NULL, &A, &N);
|
||||
TEST_EQUAL(res, return_code);
|
||||
if (res == 0) {
|
||||
TEST_ASSERT(sign_is_valid(&G));
|
||||
TEST_EQUAL(mbedtls_mpi_cmp_mpi(&G, &exp_G), 0);
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
mbedtls_mpi_free(&G); mbedtls_mpi_free(&A); mbedtls_mpi_free(&N);
|
||||
mbedtls_mpi_free(&exp_G);
|
||||
}
|
||||
/* END_CASE */
|
||||
|
||||
/* BEGIN_CASE */
|
||||
void mpi_gcd_modinv_odd_only_modinv(char *input_A, char *input_N,
|
||||
char *result_I, int return_code)
|
||||
{
|
||||
int has_inverse = strcmp(result_I, "no_inverse") ? 1 : 0;
|
||||
mbedtls_mpi I, A, N, exp_I;
|
||||
int res;
|
||||
mbedtls_mpi_init(&I); mbedtls_mpi_init(&A); mbedtls_mpi_init(&N);
|
||||
mbedtls_mpi_init(&exp_I);
|
||||
TEST_EQUAL(mbedtls_test_read_mpi(&A, input_A), 0);
|
||||
TEST_EQUAL(mbedtls_test_read_mpi(&N, input_N), 0);
|
||||
/* If there is no inverse then the value returned in I will be
|
||||
* indeterminate, and so not useful or possible to test. */
|
||||
if (has_inverse) {
|
||||
TEST_EQUAL(mbedtls_test_read_mpi(&exp_I, result_I), 0);
|
||||
}
|
||||
|
||||
res = mbedtls_mpi_gcd_modinv_odd(NULL, &I, &A, &N);
|
||||
TEST_EQUAL(res, return_code);
|
||||
if (res == 0 && has_inverse) {
|
||||
TEST_ASSERT(sign_is_valid(&I));
|
||||
TEST_EQUAL(mbedtls_mpi_cmp_mpi(&I, &exp_I), 0);
|
||||
}
|
||||
|
||||
/* Test pointer aliasing where &I == &A. */
|
||||
TEST_EQUAL(mbedtls_test_read_mpi(&I, input_A), 0);
|
||||
res = mbedtls_mpi_gcd_modinv_odd(NULL, &I, /* A */ &I, &N);
|
||||
TEST_EQUAL(res, return_code);
|
||||
if (res == 0 && has_inverse) {
|
||||
TEST_ASSERT(sign_is_valid(&I));
|
||||
TEST_EQUAL(mbedtls_mpi_cmp_mpi(&I, &exp_I), 0);
|
||||
}
|
||||
|
||||
/* Test pointer aliasing where &I == &N. This should fail. */
|
||||
TEST_EQUAL(mbedtls_test_read_mpi(&I, input_N), 0);
|
||||
res = mbedtls_mpi_gcd_modinv_odd(NULL, &I, &A, /* N */ &I);
|
||||
TEST_EQUAL(res, MBEDTLS_ERR_MPI_BAD_INPUT_DATA);
|
||||
|
||||
/* Test pointer aliasing where &A == &N. This should fail. */
|
||||
res = mbedtls_mpi_gcd_modinv_odd(NULL, &I, &A, /* N */ &A);
|
||||
TEST_EQUAL(res, MBEDTLS_ERR_MPI_BAD_INPUT_DATA);
|
||||
|
||||
/* Test I initialized to a number with more limbs than N. */
|
||||
if (N.n > 0) {
|
||||
TEST_EQUAL(mbedtls_mpi_grow(&I, N.n * 2), 0);
|
||||
memset(I.p, 0x29, I.n * sizeof(mbedtls_mpi_uint));
|
||||
res = mbedtls_mpi_gcd_modinv_odd(NULL, &I, &A, &N);
|
||||
TEST_EQUAL(res, return_code);
|
||||
if (res == 0 && has_inverse) {
|
||||
TEST_ASSERT(sign_is_valid(&I));
|
||||
TEST_EQUAL(mbedtls_mpi_cmp_mpi(&I, &exp_I), 0);
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
mbedtls_mpi_free(&I); mbedtls_mpi_free(&A); mbedtls_mpi_free(&N);
|
||||
mbedtls_mpi_free(&exp_I);
|
||||
}
|
||||
/* END_CASE */
|
||||
|
||||
/* BEGIN_CASE depends_on:MBEDTLS_GENPRIME */
|
||||
void mpi_is_prime(char *input_X, int div_result)
|
||||
{
|
||||
|
||||
@@ -1594,6 +1594,60 @@ mpi_inv_mod:"00":"11":"":MBEDTLS_ERR_MPI_NOT_ACCEPTABLE
|
||||
Test mbedtls_mpi_inv_mod #1
|
||||
mpi_inv_mod:"aa4df5cb14b4c31237f98bd1faf527c283c2d0f3eec89718664ba33f9762907c":"fffbbd660b94412ae61ead9c2906a344116e316a256fd387874c6c675b1d587d":"8d6a5c1d7adeae3e94b9bcd2c47e0d46e778bc8804a2cc25c02d775dc3d05b0c":0
|
||||
|
||||
GCD-modinv wrapper: working, A < N
|
||||
mpi_gcd_modinv_odd_both:"54a":"3999":"1":"30b5":0
|
||||
|
||||
GCD-modinv wrapper: no mod inverse, A = N
|
||||
mpi_gcd_modinv_odd_both:"365":"365":"365":"no_inverse":0
|
||||
|
||||
GCD-modinv wrapper: no mod inverse, A < N
|
||||
mpi_gcd_modinv_odd_both:"5a":"b9":"5":"no_inverse":0
|
||||
|
||||
GCD-modinv wrapper: bad inputs, A > N
|
||||
mpi_gcd_modinv_odd_both:"3999":"54a":"":"":MBEDTLS_ERR_MPI_BAD_INPUT_DATA
|
||||
|
||||
GCD-modinv wrapper: bad inputs, A < 0
|
||||
mpi_gcd_modinv_odd_both:"-5":"54a":"":"":MBEDTLS_ERR_MPI_BAD_INPUT_DATA
|
||||
|
||||
GCD-modinv wrapper: bad inputs, N even
|
||||
mpi_gcd_modinv_odd_both:"89":"540":"":"":MBEDTLS_ERR_MPI_BAD_INPUT_DATA
|
||||
|
||||
GCD-modinv wrapper only gcd: working, A < N
|
||||
mpi_gcd_modinv_odd_only_gcd:"1de3":"31d9":"7":0
|
||||
|
||||
GCD-modinv wrapper only gcd: working, A = N
|
||||
mpi_gcd_modinv_odd_only_gcd:"365":"365":"365":0
|
||||
|
||||
GCD-modinv wrapper only gcd: working, no mod inverse, A < N
|
||||
mpi_gcd_modinv_odd_only_gcd:"19e":"a47f":"9":0
|
||||
|
||||
GCD-modinv wrapper only gcd: bad inputs, A > N
|
||||
mpi_gcd_modinv_odd_only_gcd:"319d":"1de3":"":MBEDTLS_ERR_MPI_BAD_INPUT_DATA
|
||||
|
||||
GCD-modinv wrapper only gcd: bad inputs, A < 0
|
||||
mpi_gcd_modinv_odd_only_gcd:"-628ef":"991827f":"":MBEDTLS_ERR_MPI_BAD_INPUT_DATA
|
||||
|
||||
GCD-modinv wrapper only gcd: bad inputs, N even
|
||||
mpi_gcd_modinv_odd_only_gcd:"319d":"24":"":MBEDTLS_ERR_MPI_BAD_INPUT_DATA
|
||||
|
||||
GCD-modinv wrapper only modinv: working, A < N
|
||||
mpi_gcd_modinv_odd_only_modinv:"28c":"26f9":"84f":0
|
||||
|
||||
GCD-modinv wrapper only modinv: no mod inverse, A = N
|
||||
mpi_gcd_modinv_odd_only_modinv:"365":"365":"no_inverse":0
|
||||
|
||||
GCD-modinv wrapper only modinv: no mod inverse, A < N
|
||||
mpi_gcd_modinv_odd_only_modinv:"19e":"a47f":"no_inverse":0
|
||||
|
||||
GCD-modinv wrapper only modinv: bad inputs, A > N
|
||||
mpi_gcd_modinv_odd_only_modinv:"26f9":"28c":"":MBEDTLS_ERR_MPI_BAD_INPUT_DATA
|
||||
|
||||
GCD-modinv wrapper only modinv: bad inputs, A < 0
|
||||
mpi_gcd_modinv_odd_only_modinv:"-992f":"1000002":"":MBEDTLS_ERR_MPI_BAD_INPUT_DATA
|
||||
|
||||
GCD-modinv wrapper only modinv: bad inputs, N even
|
||||
mpi_gcd_modinv_odd_only_modinv:"28c":"26f0":"":MBEDTLS_ERR_MPI_BAD_INPUT_DATA
|
||||
|
||||
Base test mbedtls_mpi_is_prime #1
|
||||
depends_on:MBEDTLS_GENPRIME
|
||||
mpi_is_prime:"0":MBEDTLS_ERR_MPI_NOT_ACCEPTABLE
|
||||
|
||||
Reference in New Issue
Block a user