mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-27 13:53:31 +01:00
Use openssl's version of sha3 when available.
Part of 28837.
This commit is contained in:
parent
77712a5fa2
commit
c393171403
32
configure.ac
32
configure.ac
@ -947,21 +947,24 @@ AC_CHECK_MEMBERS([struct ssl_method_st.get_cipher_by_char], , ,
|
|||||||
[#include <openssl/ssl.h>
|
[#include <openssl/ssl.h>
|
||||||
])
|
])
|
||||||
|
|
||||||
|
dnl OpenSSL functions which we might not have. In theory, we could just
|
||||||
|
dnl check the openssl version number, but in practice that gets pretty
|
||||||
|
dnl confusing with LibreSSL, OpenSSL, and various distributions' patches
|
||||||
|
dnl to them.
|
||||||
AC_CHECK_FUNCS([ \
|
AC_CHECK_FUNCS([ \
|
||||||
ERR_load_KDF_strings \
|
ERR_load_KDF_strings \
|
||||||
SSL_SESSION_get_master_key \
|
EVP_PBE_scrypt \
|
||||||
SSL_get_server_random \
|
EVP_sha3_256 \
|
||||||
SSL_get_client_ciphers \
|
|
||||||
SSL_get_client_random \
|
|
||||||
SSL_CTX_set1_groups_list \
|
|
||||||
SSL_CIPHER_find \
|
SSL_CIPHER_find \
|
||||||
SSL_CTX_set_security_level \
|
SSL_CTX_set1_groups_list \
|
||||||
TLS_method
|
SSL_CTX_set_security_level \
|
||||||
|
SSL_SESSION_get_master_key \
|
||||||
|
SSL_get_client_ciphers \
|
||||||
|
SSL_get_client_random \
|
||||||
|
SSL_get_server_random \
|
||||||
|
TLS_method \
|
||||||
])
|
])
|
||||||
|
|
||||||
dnl Check if OpenSSL has scrypt implementation.
|
|
||||||
AC_CHECK_FUNCS([ EVP_PBE_scrypt ])
|
|
||||||
|
|
||||||
dnl Check if OpenSSL structures are opaque
|
dnl Check if OpenSSL structures are opaque
|
||||||
AC_CHECK_MEMBERS([SSL.state], , ,
|
AC_CHECK_MEMBERS([SSL.state], , ,
|
||||||
[#include <openssl/ssl.h>
|
[#include <openssl/ssl.h>
|
||||||
@ -973,6 +976,15 @@ AC_CHECK_SIZEOF(SHA_CTX, , [AC_INCLUDES_DEFAULT()
|
|||||||
|
|
||||||
fi # enable_nss
|
fi # enable_nss
|
||||||
|
|
||||||
|
dnl We will someday make KECCAK_TINY optional, but for now we still need
|
||||||
|
dnl it for SHAKE, since OpenSSL's SHAKE can't be squeezed more than
|
||||||
|
dnl once. See comment in the definition of crypto_xof_t.
|
||||||
|
|
||||||
|
dnl AM_CONDITIONAL(BUILD_KECCAK_TINY,
|
||||||
|
dnl test "x$ac_cv_func_EVP_sha3_256" != "xyes")
|
||||||
|
|
||||||
|
AM_CONDITIONAL(BUILD_KECCAK_TINY, true)
|
||||||
|
|
||||||
dnl ======================================================================
|
dnl ======================================================================
|
||||||
dnl Can we use KIST?
|
dnl Can we use KIST?
|
||||||
|
|
||||||
|
@ -143,6 +143,7 @@ noinst_HEADERS += $(ED25519_DONNA_HDRS)
|
|||||||
LIBED25519_DONNA=src/ext/ed25519/donna/libed25519_donna.a
|
LIBED25519_DONNA=src/ext/ed25519/donna/libed25519_donna.a
|
||||||
noinst_LIBRARIES += $(LIBED25519_DONNA)
|
noinst_LIBRARIES += $(LIBED25519_DONNA)
|
||||||
|
|
||||||
|
if BUILD_KECCAK_TINY
|
||||||
src_ext_keccak_tiny_libkeccak_tiny_a_CFLAGS=\
|
src_ext_keccak_tiny_libkeccak_tiny_a_CFLAGS=\
|
||||||
@CFLAGS_CONSTTIME@
|
@CFLAGS_CONSTTIME@
|
||||||
|
|
||||||
@ -156,6 +157,7 @@ noinst_HEADERS += $(LIBKECCAK_TINY_HDRS)
|
|||||||
|
|
||||||
LIBKECCAK_TINY=src/ext/keccak-tiny/libkeccak-tiny.a
|
LIBKECCAK_TINY=src/ext/keccak-tiny/libkeccak-tiny.a
|
||||||
noinst_LIBRARIES += $(LIBKECCAK_TINY)
|
noinst_LIBRARIES += $(LIBKECCAK_TINY)
|
||||||
|
endif
|
||||||
|
|
||||||
EXTRA_DIST += \
|
EXTRA_DIST += \
|
||||||
src/ext/timeouts/bench/bench-add.lua \
|
src/ext/timeouts/bench/bench-add.lua \
|
||||||
|
@ -37,6 +37,12 @@ DISABLE_GCC_WARNING(redundant-decls)
|
|||||||
#include <openssl/sha.h>
|
#include <openssl/sha.h>
|
||||||
|
|
||||||
ENABLE_GCC_WARNING(redundant-decls)
|
ENABLE_GCC_WARNING(redundant-decls)
|
||||||
|
|
||||||
|
#ifdef HAVE_EVP_SHA3_256
|
||||||
|
#define OPENSSL_HAS_SHA3
|
||||||
|
#include <openssl/evp.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef ENABLE_NSS
|
#ifdef ENABLE_NSS
|
||||||
@ -150,8 +156,13 @@ crypto_digest256(char *digest, const char *m, size_t len,
|
|||||||
ret = (SHA256((const uint8_t*)m,len,(uint8_t*)digest) != NULL);
|
ret = (SHA256((const uint8_t*)m,len,(uint8_t*)digest) != NULL);
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
|
#ifdef OPENSSL_HAS_SHA3
|
||||||
|
unsigned int dlen = DIGEST256_LEN;
|
||||||
|
ret = EVP_Digest(m, len, (uint8_t*)digest, &dlen, EVP_sha3_256(), NULL);
|
||||||
|
#else
|
||||||
ret = (sha3_256((uint8_t *)digest, DIGEST256_LEN,(const uint8_t *)m, len)
|
ret = (sha3_256((uint8_t *)digest, DIGEST256_LEN,(const uint8_t *)m, len)
|
||||||
> -1);
|
> -1);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ret)
|
if (!ret)
|
||||||
@ -179,8 +190,13 @@ crypto_digest512(char *digest, const char *m, size_t len,
|
|||||||
!= NULL);
|
!= NULL);
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
|
#ifdef OPENSSL_HAS_SHA3
|
||||||
|
unsigned int dlen = DIGEST512_LEN;
|
||||||
|
ret = EVP_Digest(m, len, (uint8_t*)digest, &dlen, EVP_sha3_512(), NULL);
|
||||||
|
#else
|
||||||
ret = (sha3_512((uint8_t*)digest, DIGEST512_LEN, (const uint8_t*)m, len)
|
ret = (sha3_512((uint8_t*)digest, DIGEST512_LEN, (const uint8_t*)m, len)
|
||||||
> -1);
|
> -1);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ret)
|
if (!ret)
|
||||||
@ -282,7 +298,11 @@ struct crypto_digest_t {
|
|||||||
SHA256_CTX sha2; /**< state for SHA256 */
|
SHA256_CTX sha2; /**< state for SHA256 */
|
||||||
SHA512_CTX sha512; /**< state for SHA512 */
|
SHA512_CTX sha512; /**< state for SHA512 */
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef OPENSSL_HAS_SHA3
|
||||||
|
EVP_MD_CTX *md;
|
||||||
|
#else
|
||||||
keccak_state sha3; /**< state for SHA3-[256,512] */
|
keccak_state sha3; /**< state for SHA3-[256,512] */
|
||||||
|
#endif
|
||||||
} d;
|
} d;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -325,9 +345,15 @@ crypto_digest_alloc_bytes(digest_algorithm_t alg)
|
|||||||
case DIGEST_SHA512:
|
case DIGEST_SHA512:
|
||||||
return END_OF_FIELD(d.sha512);
|
return END_OF_FIELD(d.sha512);
|
||||||
#endif
|
#endif
|
||||||
case DIGEST_SHA3_256:
|
#ifdef OPENSSL_HAS_SHA3
|
||||||
|
case DIGEST_SHA3_256: /* Fall through */
|
||||||
|
case DIGEST_SHA3_512:
|
||||||
|
return END_OF_FIELD(d.md);
|
||||||
|
#else
|
||||||
|
case DIGEST_SHA3_256: /* Fall through */
|
||||||
case DIGEST_SHA3_512:
|
case DIGEST_SHA3_512:
|
||||||
return END_OF_FIELD(d.sha3);
|
return END_OF_FIELD(d.sha3);
|
||||||
|
#endif
|
||||||
default:
|
default:
|
||||||
tor_assert(0); // LCOV_EXCL_LINE
|
tor_assert(0); // LCOV_EXCL_LINE
|
||||||
return 0; // LCOV_EXCL_LINE
|
return 0; // LCOV_EXCL_LINE
|
||||||
@ -373,12 +399,29 @@ crypto_digest_new_internal(digest_algorithm_t algorithm)
|
|||||||
SHA512_Init(&r->d.sha512);
|
SHA512_Init(&r->d.sha512);
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef OPENSSL_HAS_SHA3
|
||||||
|
case DIGEST_SHA3_256:
|
||||||
|
r->d.md = EVP_MD_CTX_new();
|
||||||
|
if (!EVP_DigestInit(r->d.md, EVP_sha3_256())) {
|
||||||
|
crypto_digest_free(r);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DIGEST_SHA3_512:
|
||||||
|
r->d.md = EVP_MD_CTX_new();
|
||||||
|
if (!EVP_DigestInit(r->d.md, EVP_sha3_512())) {
|
||||||
|
crypto_digest_free(r);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#else
|
||||||
case DIGEST_SHA3_256:
|
case DIGEST_SHA3_256:
|
||||||
keccak_digest_init(&r->d.sha3, 256);
|
keccak_digest_init(&r->d.sha3, 256);
|
||||||
break;
|
break;
|
||||||
case DIGEST_SHA3_512:
|
case DIGEST_SHA3_512:
|
||||||
keccak_digest_init(&r->d.sha3, 512);
|
keccak_digest_init(&r->d.sha3, 512);
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
default:
|
default:
|
||||||
tor_assert_unreached();
|
tor_assert_unreached();
|
||||||
}
|
}
|
||||||
@ -427,6 +470,14 @@ crypto_digest_free_(crypto_digest_t *digest)
|
|||||||
if (library_supports_digest(digest->algorithm)) {
|
if (library_supports_digest(digest->algorithm)) {
|
||||||
PK11_DestroyContext(digest->d.ctx, PR_TRUE);
|
PK11_DestroyContext(digest->d.ctx, PR_TRUE);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef OPENSSL_HAS_SHA3
|
||||||
|
if (digest->algorithm == DIGEST_SHA3_256 ||
|
||||||
|
digest->algorithm == DIGEST_SHA3_512) {
|
||||||
|
if (digest->d.md) {
|
||||||
|
EVP_MD_CTX_free(digest->d.md);
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
size_t bytes = crypto_digest_alloc_bytes(digest->algorithm);
|
size_t bytes = crypto_digest_alloc_bytes(digest->algorithm);
|
||||||
memwipe(digest, 0, bytes);
|
memwipe(digest, 0, bytes);
|
||||||
@ -471,10 +522,19 @@ crypto_digest_add_bytes(crypto_digest_t *digest, const char *data,
|
|||||||
SHA512_Update(&digest->d.sha512, (void*)data, len);
|
SHA512_Update(&digest->d.sha512, (void*)data, len);
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef OPENSSL_HAS_SHA3
|
||||||
|
case DIGEST_SHA3_256: /* FALLSTHROUGH */
|
||||||
|
case DIGEST_SHA3_512: {
|
||||||
|
int r = EVP_DigestUpdate(digest->d.md, data, len);
|
||||||
|
tor_assert(r);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#else
|
||||||
case DIGEST_SHA3_256: /* FALLSTHROUGH */
|
case DIGEST_SHA3_256: /* FALLSTHROUGH */
|
||||||
case DIGEST_SHA3_512:
|
case DIGEST_SHA3_512:
|
||||||
keccak_digest_update(&digest->d.sha3, (const uint8_t *)data, len);
|
keccak_digest_update(&digest->d.sha3, (const uint8_t *)data, len);
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
default:
|
default:
|
||||||
/* LCOV_EXCL_START */
|
/* LCOV_EXCL_START */
|
||||||
tor_fragile_assert();
|
tor_fragile_assert();
|
||||||
@ -499,12 +559,24 @@ crypto_digest_get_digest(crypto_digest_t *digest,
|
|||||||
tor_assert(out);
|
tor_assert(out);
|
||||||
tor_assert(out_len <= crypto_digest_algorithm_get_length(digest->algorithm));
|
tor_assert(out_len <= crypto_digest_algorithm_get_length(digest->algorithm));
|
||||||
|
|
||||||
/* The SHA-3 code handles copying into a temporary ctx, and also can handle
|
|
||||||
* short output buffers by truncating appropriately. */
|
|
||||||
if (digest->algorithm == DIGEST_SHA3_256 ||
|
if (digest->algorithm == DIGEST_SHA3_256 ||
|
||||||
digest->algorithm == DIGEST_SHA3_512) {
|
digest->algorithm == DIGEST_SHA3_512) {
|
||||||
|
#ifdef OPENSSL_HAS_SHA3
|
||||||
|
unsigned dlen = (unsigned)
|
||||||
|
crypto_digest_algorithm_get_length(digest->algorithm);
|
||||||
|
EVP_MD_CTX *tmp = EVP_MD_CTX_new();
|
||||||
|
EVP_MD_CTX_copy(tmp, digest->d.md);
|
||||||
|
memset(r, 0xff, sizeof(r));
|
||||||
|
int res = EVP_DigestFinal(tmp, r, &dlen);
|
||||||
|
EVP_MD_CTX_free(tmp);
|
||||||
|
tor_assert(res == 1);
|
||||||
|
goto done;
|
||||||
|
#else
|
||||||
|
/* Tiny-Keccak handles copying into a temporary ctx, and also can handle
|
||||||
|
* short output buffers by truncating appropriately. */
|
||||||
keccak_digest_sum(&digest->d.sha3, (uint8_t *)out, out_len);
|
keccak_digest_sum(&digest->d.sha3, (uint8_t *)out, out_len);
|
||||||
return;
|
return;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_NSS
|
#ifdef ENABLE_NSS
|
||||||
@ -550,6 +622,10 @@ crypto_digest_get_digest(crypto_digest_t *digest,
|
|||||||
//LCOV_EXCL_STOP
|
//LCOV_EXCL_STOP
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef OPENSSL_HAS_SHA3
|
||||||
|
done:
|
||||||
|
#endif
|
||||||
memcpy(out, r, out_len);
|
memcpy(out, r, out_len);
|
||||||
memwipe(r, 0, sizeof(r));
|
memwipe(r, 0, sizeof(r));
|
||||||
}
|
}
|
||||||
@ -570,6 +646,13 @@ crypto_digest_dup(const crypto_digest_t *digest)
|
|||||||
if (library_supports_digest(digest->algorithm)) {
|
if (library_supports_digest(digest->algorithm)) {
|
||||||
result->d.ctx = PK11_CloneContext(digest->d.ctx);
|
result->d.ctx = PK11_CloneContext(digest->d.ctx);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef OPENSSL_HAS_SHA3
|
||||||
|
if (digest->algorithm == DIGEST_SHA3_256 ||
|
||||||
|
digest->algorithm == DIGEST_SHA3_512) {
|
||||||
|
result->d.md = EVP_MD_CTX_new();
|
||||||
|
EVP_MD_CTX_copy(result->d.md, digest->d.md);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -637,6 +720,15 @@ crypto_digest_assign(crypto_digest_t *into,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef OPENSSL_HAS_SHA3
|
||||||
|
if (from->algorithm == DIGEST_SHA3_256 ||
|
||||||
|
from->algorithm == DIGEST_SHA3_512) {
|
||||||
|
EVP_MD_CTX_copy(into->d.md, from->d.md);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
memcpy(into,from,alloc_bytes);
|
memcpy(into,from,alloc_bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -779,7 +871,23 @@ crypto_mac_sha3_256(uint8_t *mac_out, size_t len_out,
|
|||||||
|
|
||||||
/** Internal state for a eXtendable-Output Function (XOF). */
|
/** Internal state for a eXtendable-Output Function (XOF). */
|
||||||
struct crypto_xof_t {
|
struct crypto_xof_t {
|
||||||
|
#ifdef OPENSSL_HAS_SHAKE3_EVP
|
||||||
|
/* XXXX We can't enable this yet, because OpenSSL's
|
||||||
|
* DigestFinalXOF function can't be called repeatedly on the same
|
||||||
|
* XOF.
|
||||||
|
*
|
||||||
|
* We could in theory use the undocumented SHA3_absorb and SHA3_squeeze
|
||||||
|
* functions, but let's not mess with undocumented OpenSSL internals any
|
||||||
|
* more than we have to.
|
||||||
|
*
|
||||||
|
* We could also revise our XOF code so that it only allows a single
|
||||||
|
* squeeze operation; we don't require streaming squeeze operations
|
||||||
|
* outside the tests yet.
|
||||||
|
*/
|
||||||
|
EVP_MD_CTX *ctx;
|
||||||
|
#else
|
||||||
keccak_state s;
|
keccak_state s;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Allocate a new XOF object backed by SHAKE-256. The security level
|
/** Allocate a new XOF object backed by SHAKE-256. The security level
|
||||||
@ -792,7 +900,14 @@ crypto_xof_new(void)
|
|||||||
{
|
{
|
||||||
crypto_xof_t *xof;
|
crypto_xof_t *xof;
|
||||||
xof = tor_malloc(sizeof(crypto_xof_t));
|
xof = tor_malloc(sizeof(crypto_xof_t));
|
||||||
|
#ifdef OPENSSL_HAS_SHAKE256
|
||||||
|
xof->ctx = EVP_MD_CTX_new();
|
||||||
|
tor_assert(xof->ctx);
|
||||||
|
int r = EVP_DigestInit(xof->ctx, EVP_shake256());
|
||||||
|
tor_assert(r == 1);
|
||||||
|
#else
|
||||||
keccak_xof_init(&xof->s, 256);
|
keccak_xof_init(&xof->s, 256);
|
||||||
|
#endif
|
||||||
return xof;
|
return xof;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -803,8 +918,13 @@ crypto_xof_new(void)
|
|||||||
void
|
void
|
||||||
crypto_xof_add_bytes(crypto_xof_t *xof, const uint8_t *data, size_t len)
|
crypto_xof_add_bytes(crypto_xof_t *xof, const uint8_t *data, size_t len)
|
||||||
{
|
{
|
||||||
|
#ifdef OPENSSL_HAS_SHAKE256
|
||||||
|
int r = EVP_DigestUpdate(xof->ctx, data, len);
|
||||||
|
tor_assert(r == 1);
|
||||||
|
#else
|
||||||
int i = keccak_xof_absorb(&xof->s, data, len);
|
int i = keccak_xof_absorb(&xof->s, data, len);
|
||||||
tor_assert(i == 0);
|
tor_assert(i == 0);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Squeeze bytes out of a XOF object. Calling this routine will render
|
/** Squeeze bytes out of a XOF object. Calling this routine will render
|
||||||
@ -813,8 +933,13 @@ crypto_xof_add_bytes(crypto_xof_t *xof, const uint8_t *data, size_t len)
|
|||||||
void
|
void
|
||||||
crypto_xof_squeeze_bytes(crypto_xof_t *xof, uint8_t *out, size_t len)
|
crypto_xof_squeeze_bytes(crypto_xof_t *xof, uint8_t *out, size_t len)
|
||||||
{
|
{
|
||||||
|
#ifdef OPENSSL_HAS_SHAKE256
|
||||||
|
int r = EVP_DigestFinalXOF(xof->ctx, out, len);
|
||||||
|
tor_assert(r == 1);
|
||||||
|
#else
|
||||||
int i = keccak_xof_squeeze(&xof->s, out, len);
|
int i = keccak_xof_squeeze(&xof->s, out, len);
|
||||||
tor_assert(i == 0);
|
tor_assert(i == 0);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Cleanse and deallocate a XOF object. */
|
/** Cleanse and deallocate a XOF object. */
|
||||||
@ -823,6 +948,10 @@ crypto_xof_free_(crypto_xof_t *xof)
|
|||||||
{
|
{
|
||||||
if (!xof)
|
if (!xof)
|
||||||
return;
|
return;
|
||||||
|
#ifdef OPENSSL_HAS_SHAKE256
|
||||||
|
if (xof->ctx)
|
||||||
|
EVP_MD_CTX_free(xof->ctx);
|
||||||
|
#endif
|
||||||
memwipe(xof, 0, sizeof(crypto_xof_t));
|
memwipe(xof, 0, sizeof(crypto_xof_t));
|
||||||
tor_free(xof);
|
tor_free(xof);
|
||||||
}
|
}
|
||||||
|
@ -1178,8 +1178,9 @@ test_crypto_sha3_xof(void *arg)
|
|||||||
tt_assert(xof);
|
tt_assert(xof);
|
||||||
for (size_t i = 0; i < sizeof(msg); i++)
|
for (size_t i = 0; i < sizeof(msg); i++)
|
||||||
crypto_xof_add_bytes(xof, msg + i, 1);
|
crypto_xof_add_bytes(xof, msg + i, 1);
|
||||||
for (size_t i = 0; i < sizeof(out); i++)
|
for (size_t i = 0; i < sizeof(out); i++) {
|
||||||
crypto_xof_squeeze_bytes(xof, out + i, 1);
|
crypto_xof_squeeze_bytes(xof, out + i, 1);
|
||||||
|
}
|
||||||
test_memeq_hex(out, squeezed_hex);
|
test_memeq_hex(out, squeezed_hex);
|
||||||
|
|
||||||
done:
|
done:
|
||||||
|
Loading…
Reference in New Issue
Block a user