forked from wolfSSL/wolfssl
fix pwdbased + NO_MD5 build
This commit is contained in:
@@ -28,10 +28,11 @@
|
|||||||
#ifndef NO_PWDBASED
|
#ifndef NO_PWDBASED
|
||||||
|
|
||||||
#ifdef WOLFSSL_PIC32MZ_HASH
|
#ifdef WOLFSSL_PIC32MZ_HASH
|
||||||
|
#ifndef NO_MD5
|
||||||
#define wc_InitMd5 wc_InitMd5_sw
|
#define wc_InitMd5 wc_InitMd5_sw
|
||||||
#define wc_Md5Update wc_Md5Update_sw
|
#define wc_Md5Update wc_Md5Update_sw
|
||||||
#define wc_Md5Final wc_Md5Final_sw
|
#define wc_Md5Final wc_Md5Final_sw
|
||||||
|
#endif /* NO_MD5 */
|
||||||
|
|
||||||
#define wc_InitSha wc_InitSha_sw
|
#define wc_InitSha wc_InitSha_sw
|
||||||
#define wc_ShaUpdate wc_ShaUpdate_sw
|
#define wc_ShaUpdate wc_ShaUpdate_sw
|
||||||
@@ -40,7 +41,6 @@
|
|||||||
#define wc_InitSha256 wc_InitSha256_sw
|
#define wc_InitSha256 wc_InitSha256_sw
|
||||||
#define wc_Sha256Update wc_Sha256Update_sw
|
#define wc_Sha256Update wc_Sha256Update_sw
|
||||||
#define wc_Sha256Final wc_Sha256Final_sw
|
#define wc_Sha256Final wc_Sha256Final_sw
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <wolfssl/wolfcrypt/pwdbased.h>
|
#include <wolfssl/wolfcrypt/pwdbased.h>
|
||||||
@@ -57,7 +57,6 @@
|
|||||||
#include <wolfcrypt/src/misc.c>
|
#include <wolfcrypt/src/misc.c>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#ifndef min
|
#ifndef min
|
||||||
|
|
||||||
static INLINE word32 min(word32 a, word32 b)
|
static INLINE word32 min(word32 a, word32 b)
|
||||||
@@ -68,48 +67,63 @@
|
|||||||
#endif /* min */
|
#endif /* min */
|
||||||
|
|
||||||
|
|
||||||
|
/* PBKDF1 needs at least SHA available */
|
||||||
int wc_PBKDF1(byte* output, const byte* passwd, int pLen, const byte* salt,
|
int wc_PBKDF1(byte* output, const byte* passwd, int pLen, const byte* salt,
|
||||||
int sLen, int iterations, int kLen, int hashType)
|
int sLen, int iterations, int kLen, int hashType)
|
||||||
{
|
{
|
||||||
Md5 md5;
|
|
||||||
Sha sha;
|
Sha sha;
|
||||||
int hLen = (hashType == MD5) ? (int)MD5_DIGEST_SIZE : (int)SHA_DIGEST_SIZE;
|
#ifndef NO_MD5
|
||||||
|
Md5 md5;
|
||||||
|
#endif
|
||||||
|
int hLen = (int)SHA_DIGEST_SIZE;
|
||||||
int i, ret = 0;
|
int i, ret = 0;
|
||||||
byte buffer[SHA_DIGEST_SIZE]; /* max size */
|
byte buffer[SHA_DIGEST_SIZE]; /* max size */
|
||||||
|
|
||||||
if (hashType != MD5 && hashType != SHA)
|
if (hashType != MD5 && hashType != SHA)
|
||||||
return BAD_FUNC_ARG;
|
return BAD_FUNC_ARG;
|
||||||
|
|
||||||
|
#ifndef NO_MD5
|
||||||
|
if (hashType == MD5)
|
||||||
|
hLen = (int)MD5_DIGEST_SIZE;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (kLen > hLen)
|
if (kLen > hLen)
|
||||||
return BAD_FUNC_ARG;
|
return BAD_FUNC_ARG;
|
||||||
|
|
||||||
if (iterations < 1)
|
if (iterations < 1)
|
||||||
return BAD_FUNC_ARG;
|
return BAD_FUNC_ARG;
|
||||||
|
|
||||||
if (hashType == MD5) {
|
switch (hashType) {
|
||||||
|
#ifndef NO_MD5
|
||||||
|
case MD5:
|
||||||
wc_InitMd5(&md5);
|
wc_InitMd5(&md5);
|
||||||
wc_Md5Update(&md5, passwd, pLen);
|
wc_Md5Update(&md5, passwd, pLen);
|
||||||
wc_Md5Update(&md5, salt, sLen);
|
wc_Md5Update(&md5, salt, sLen);
|
||||||
wc_Md5Final(&md5, buffer);
|
wc_Md5Final(&md5, buffer);
|
||||||
}
|
break;
|
||||||
else {
|
#endif /* NO_MD5 */
|
||||||
|
case SHA:
|
||||||
|
default:
|
||||||
ret = wc_InitSha(&sha);
|
ret = wc_InitSha(&sha);
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
return ret;
|
return ret;
|
||||||
wc_ShaUpdate(&sha, passwd, pLen);
|
wc_ShaUpdate(&sha, passwd, pLen);
|
||||||
wc_ShaUpdate(&sha, salt, sLen);
|
wc_ShaUpdate(&sha, salt, sLen);
|
||||||
wc_ShaFinal(&sha, buffer);
|
wc_ShaFinal(&sha, buffer);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 1; i < iterations; i++) {
|
for (i = 1; i < iterations; i++) {
|
||||||
if (hashType == MD5) {
|
if (hashType == SHA) {
|
||||||
wc_Md5Update(&md5, buffer, hLen);
|
|
||||||
wc_Md5Final(&md5, buffer);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
wc_ShaUpdate(&sha, buffer, hLen);
|
wc_ShaUpdate(&sha, buffer, hLen);
|
||||||
wc_ShaFinal(&sha, buffer);
|
wc_ShaFinal(&sha, buffer);
|
||||||
}
|
}
|
||||||
|
#ifndef NO_MD5
|
||||||
|
else {
|
||||||
|
wc_Md5Update(&md5, buffer, hLen);
|
||||||
|
wc_Md5Final(&md5, buffer);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
XMEMCPY(output, buffer, kLen);
|
XMEMCPY(output, buffer, kLen);
|
||||||
|
|
||||||
@@ -117,6 +131,37 @@ int wc_PBKDF1(byte* output, const byte* passwd, int pLen, const byte* salt,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int GetDigestSize(int hashType)
|
||||||
|
{
|
||||||
|
int hLen;
|
||||||
|
|
||||||
|
switch (hashType) {
|
||||||
|
#ifndef NO_MD5
|
||||||
|
case MD5:
|
||||||
|
hLen = MD5_DIGEST_SIZE;
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
case SHA:
|
||||||
|
hLen = SHA_DIGEST_SIZE;
|
||||||
|
break;
|
||||||
|
#ifndef NO_SHA256
|
||||||
|
case SHA256:
|
||||||
|
hLen = SHA256_DIGEST_SIZE;
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
#ifdef WOLFSSL_SHA512
|
||||||
|
case SHA512:
|
||||||
|
hLen = SHA512_DIGEST_SIZE;
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
return BAD_FUNC_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int wc_PBKDF2(byte* output, const byte* passwd, int pLen, const byte* salt,
|
int wc_PBKDF2(byte* output, const byte* passwd, int pLen, const byte* salt,
|
||||||
int sLen, int iterations, int kLen, int hashType)
|
int sLen, int iterations, int kLen, int hashType)
|
||||||
{
|
{
|
||||||
@@ -130,23 +175,8 @@ int wc_PBKDF2(byte* output, const byte* passwd, int pLen, const byte* salt,
|
|||||||
byte buffer[MAX_DIGEST_SIZE];
|
byte buffer[MAX_DIGEST_SIZE];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (hashType == MD5) {
|
hLen = GetDigestSize(hashType);
|
||||||
hLen = MD5_DIGEST_SIZE;
|
if (hLen < 0)
|
||||||
}
|
|
||||||
else if (hashType == SHA) {
|
|
||||||
hLen = SHA_DIGEST_SIZE;
|
|
||||||
}
|
|
||||||
#ifndef NO_SHA256
|
|
||||||
else if (hashType == SHA256) {
|
|
||||||
hLen = SHA256_DIGEST_SIZE;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#ifdef WOLFSSL_SHA512
|
|
||||||
else if (hashType == SHA512) {
|
|
||||||
hLen = SHA512_DIGEST_SIZE;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
else
|
|
||||||
return BAD_FUNC_ARG;
|
return BAD_FUNC_ARG;
|
||||||
|
|
||||||
#ifdef WOLFSSL_SMALL_STACK
|
#ifdef WOLFSSL_SMALL_STACK
|
||||||
@@ -220,6 +250,148 @@ int wc_PBKDF2(byte* output, const byte* passwd, int pLen, const byte* salt,
|
|||||||
#define PBKDF_DIGEST_SIZE SHA_DIGEST_SIZE
|
#define PBKDF_DIGEST_SIZE SHA_DIGEST_SIZE
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* helper for wc_PKCS12_PBKDF(), sets block and digest sizes */
|
||||||
|
int GetPKCS12HashSizes(int hashType, word32* v, word32* u)
|
||||||
|
{
|
||||||
|
if (!v || !u)
|
||||||
|
return BAD_FUNC_ARG;
|
||||||
|
|
||||||
|
switch (hashType) {
|
||||||
|
#ifndef NO_MD5
|
||||||
|
case MD5:
|
||||||
|
*v = MD5_BLOCK_SIZE;
|
||||||
|
*u = MD5_DIGEST_SIZE;
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
case SHA:
|
||||||
|
*v = SHA_BLOCK_SIZE;
|
||||||
|
*u = SHA_DIGEST_SIZE;
|
||||||
|
break;
|
||||||
|
#ifndef NO_SHA256
|
||||||
|
case SHA256:
|
||||||
|
*v = SHA256_BLOCK_SIZE;
|
||||||
|
*u = SHA256_DIGEST_SIZE;
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
#ifdef WOLFSSL_SHA512
|
||||||
|
case SHA512:
|
||||||
|
*v = SHA512_BLOCK_SIZE;
|
||||||
|
*u = SHA512_DIGEST_SIZE;
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
return BAD_FUNC_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper for PKCS12_PBKDF(), does hash operation */
|
||||||
|
int DoPKCS12Hash(int hashType, byte* buffer, word32 totalLen,
|
||||||
|
byte* Ai, word32 u, int iterations)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (buffer == NULL || Ai == NULL)
|
||||||
|
return BAD_FUNC_ARG;
|
||||||
|
|
||||||
|
switch (hashType) {
|
||||||
|
#ifndef NO_MD5
|
||||||
|
case MD5:
|
||||||
|
{
|
||||||
|
Md5 md5;
|
||||||
|
wc_InitMd5(&md5);
|
||||||
|
wc_Md5Update(&md5, buffer, totalLen);
|
||||||
|
wc_Md5Final(&md5, Ai);
|
||||||
|
|
||||||
|
for (i = 1; i < iterations; i++) {
|
||||||
|
wc_Md5Update(&md5, Ai, u);
|
||||||
|
wc_Md5Final(&md5, Ai);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#endif /* NO_MD5 */
|
||||||
|
case SHA:
|
||||||
|
{
|
||||||
|
Sha sha;
|
||||||
|
ret = wc_InitSha(&sha);
|
||||||
|
if (ret != 0)
|
||||||
|
break;
|
||||||
|
wc_ShaUpdate(&sha, buffer, totalLen);
|
||||||
|
wc_ShaFinal(&sha, Ai);
|
||||||
|
|
||||||
|
for (i = 1; i < iterations; i++) {
|
||||||
|
wc_ShaUpdate(&sha, Ai, u);
|
||||||
|
wc_ShaFinal(&sha, Ai);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#ifndef NO_SHA256
|
||||||
|
case SHA256:
|
||||||
|
{
|
||||||
|
Sha256 sha256;
|
||||||
|
ret = wc_InitSha256(&sha256);
|
||||||
|
if (ret != 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
ret = wc_Sha256Update(&sha256, buffer, totalLen);
|
||||||
|
if (ret != 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
ret = wc_Sha256Final(&sha256, Ai);
|
||||||
|
if (ret != 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
for (i = 1; i < iterations; i++) {
|
||||||
|
ret = wc_Sha256Update(&sha256, Ai, u);
|
||||||
|
if (ret != 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
ret = wc_Sha256Final(&sha256, Ai);
|
||||||
|
if (ret != 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#endif /* NO_SHA256 */
|
||||||
|
#ifdef WOLFSSL_SHA512
|
||||||
|
case SHA512:
|
||||||
|
{
|
||||||
|
Sha512 sha512;
|
||||||
|
ret = wc_InitSha512(&sha512);
|
||||||
|
if (ret != 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
ret = wc_Sha512Update(&sha512, buffer, totalLen);
|
||||||
|
if (ret != 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
ret = wc_Sha512Final(&sha512, Ai);
|
||||||
|
if (ret != 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
for (i = 1; i < iterations; i++) {
|
||||||
|
ret = wc_Sha512Update(&sha512, Ai, u);
|
||||||
|
if (ret != 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
ret = wc_Sha512Final(&sha512, Ai);
|
||||||
|
if (ret != 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#endif /* WOLFSSL_SHA512 */
|
||||||
|
|
||||||
|
default:
|
||||||
|
ret = BAD_FUNC_ARG;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
int wc_PKCS12_PBKDF(byte* output, const byte* passwd, int passLen,const byte* salt,
|
int wc_PKCS12_PBKDF(byte* output, const byte* passwd, int passLen,const byte* salt,
|
||||||
int saltLen, int iterations, int kLen, int hashType, int id)
|
int saltLen, int iterations, int kLen, int hashType, int id)
|
||||||
{
|
{
|
||||||
@@ -247,27 +419,8 @@ int wc_PKCS12_PBKDF(byte* output, const byte* passwd, int passLen,const byte* sa
|
|||||||
if (!iterations)
|
if (!iterations)
|
||||||
iterations = 1;
|
iterations = 1;
|
||||||
|
|
||||||
if (hashType == MD5) {
|
ret = GetPKCS12HashSizes(hashType, &v, &u);
|
||||||
v = MD5_BLOCK_SIZE;
|
if (ret < 0)
|
||||||
u = MD5_DIGEST_SIZE;
|
|
||||||
}
|
|
||||||
else if (hashType == SHA) {
|
|
||||||
v = SHA_BLOCK_SIZE;
|
|
||||||
u = SHA_DIGEST_SIZE;
|
|
||||||
}
|
|
||||||
#ifndef NO_SHA256
|
|
||||||
else if (hashType == SHA256) {
|
|
||||||
v = SHA256_BLOCK_SIZE;
|
|
||||||
u = SHA256_DIGEST_SIZE;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#ifdef WOLFSSL_SHA512
|
|
||||||
else if (hashType == SHA512) {
|
|
||||||
v = SHA512_BLOCK_SIZE;
|
|
||||||
u = SHA512_DIGEST_SIZE;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
else
|
|
||||||
return BAD_FUNC_ARG;
|
return BAD_FUNC_ARG;
|
||||||
|
|
||||||
#ifdef WOLFSSL_SMALL_STACK
|
#ifdef WOLFSSL_SMALL_STACK
|
||||||
@@ -282,6 +435,9 @@ int wc_PKCS12_PBKDF(byte* output, const byte* passwd, int passLen,const byte* sa
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
XMEMSET(Ai, 0, PBKDF_DIGEST_SIZE);
|
||||||
|
XMEMSET(B, 0, PBKDF_DIGEST_SIZE);
|
||||||
|
|
||||||
dLen = v;
|
dLen = v;
|
||||||
sLen = v * ((saltLen + v - 1) / v);
|
sLen = v * ((saltLen + v - 1) / v);
|
||||||
if (passLen)
|
if (passLen)
|
||||||
@@ -320,86 +476,9 @@ int wc_PKCS12_PBKDF(byte* output, const byte* passwd, int passLen,const byte* sa
|
|||||||
word32 currentLen;
|
word32 currentLen;
|
||||||
mp_int B1;
|
mp_int B1;
|
||||||
|
|
||||||
if (hashType == MD5) {
|
ret = DoPKCS12Hash(hashType, buffer, totalLen, Ai, u, iterations);
|
||||||
Md5 md5;
|
if (ret < 0)
|
||||||
|
|
||||||
wc_InitMd5(&md5);
|
|
||||||
wc_Md5Update(&md5, buffer, totalLen);
|
|
||||||
wc_Md5Final(&md5, Ai);
|
|
||||||
|
|
||||||
for (i = 1; i < iterations; i++) {
|
|
||||||
wc_Md5Update(&md5, Ai, u);
|
|
||||||
wc_Md5Final(&md5, Ai);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (hashType == SHA) {
|
|
||||||
Sha sha;
|
|
||||||
|
|
||||||
ret = wc_InitSha(&sha);
|
|
||||||
if (ret != 0)
|
|
||||||
break;
|
break;
|
||||||
wc_ShaUpdate(&sha, buffer, totalLen);
|
|
||||||
wc_ShaFinal(&sha, Ai);
|
|
||||||
|
|
||||||
for (i = 1; i < iterations; i++) {
|
|
||||||
wc_ShaUpdate(&sha, Ai, u);
|
|
||||||
wc_ShaFinal(&sha, Ai);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#ifndef NO_SHA256
|
|
||||||
else if (hashType == SHA256) {
|
|
||||||
Sha256 sha256;
|
|
||||||
|
|
||||||
ret = wc_InitSha256(&sha256);
|
|
||||||
if (ret != 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
ret = wc_Sha256Update(&sha256, buffer, totalLen);
|
|
||||||
if (ret != 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
ret = wc_Sha256Final(&sha256, Ai);
|
|
||||||
if (ret != 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
for (i = 1; i < iterations; i++) {
|
|
||||||
ret = wc_Sha256Update(&sha256, Ai, u);
|
|
||||||
if (ret != 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
ret = wc_Sha256Final(&sha256, Ai);
|
|
||||||
if (ret != 0)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#ifdef WOLFSSL_SHA512
|
|
||||||
else if (hashType == SHA512) {
|
|
||||||
Sha512 sha512;
|
|
||||||
|
|
||||||
ret = wc_InitSha512(&sha512);
|
|
||||||
if (ret != 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
ret = wc_Sha512Update(&sha512, buffer, totalLen);
|
|
||||||
if (ret != 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
ret = wc_Sha512Final(&sha512, Ai);
|
|
||||||
if (ret != 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
for (i = 1; i < iterations; i++) {
|
|
||||||
ret = wc_Sha512Update(&sha512, Ai, u);
|
|
||||||
if (ret != 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
ret = wc_Sha512Final(&sha512, Ai);
|
|
||||||
if (ret != 0)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (i = 0; i < (int)v; i++)
|
for (i = 0; i < (int)v; i++)
|
||||||
B[i] = Ai[i % u];
|
B[i] = Ai[i % u];
|
||||||
|
@@ -26,7 +26,10 @@
|
|||||||
|
|
||||||
#ifndef NO_PWDBASED
|
#ifndef NO_PWDBASED
|
||||||
|
|
||||||
|
#ifndef NO_MD5
|
||||||
#include <wolfssl/wolfcrypt/md5.h> /* for hash type */
|
#include <wolfssl/wolfcrypt/md5.h> /* for hash type */
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <wolfssl/wolfcrypt/sha.h>
|
#include <wolfssl/wolfcrypt/sha.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
@@ -47,6 +50,12 @@ WOLFSSL_API int wc_PKCS12_PBKDF(byte* output, const byte* passwd, int pLen,
|
|||||||
const byte* salt, int sLen, int iterations,
|
const byte* salt, int sLen, int iterations,
|
||||||
int kLen, int typeH, int purpose);
|
int kLen, int typeH, int purpose);
|
||||||
|
|
||||||
|
/* helper functions */
|
||||||
|
WOLFSSL_LOCAL int GetDigestSize(int hashType);
|
||||||
|
WOLFSSL_LOCAL int GetPKCS12HashSizes(int hashType, word32* v, word32* u);
|
||||||
|
WOLFSSL_LOCAL int DoPKCS12Hash(int hashType, byte* buffer, word32 totalLen,
|
||||||
|
byte* Ai, word32 u, int iterations);
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} /* extern "C" */
|
} /* extern "C" */
|
||||||
|
Reference in New Issue
Block a user