resolved: add ML-DSA-44 DNSSEC verification
Classical DNSSEC algorithms such as RSA and ECDSA are vulnerable to quantum computers. Add support for post-quantum ML-DSA-44, DNSSEC algorithm 18. Verify pure ML-DSA through OpenSSL. Report the algorithm as unsupported only when it is unavailable. Treat key import and verification failures as hard errors. Add the draft test vector and negative tests. https://datatracker.ietf.org/doc/draft-westerbaan-dnssec-mldsa/ Signed-off-by: Ayoub Zaki <ayoub.zaki@embetrix.com>
This commit is contained in:
committed by
Luca Boccassi
parent
bc5de99fd0
commit
2d6bc0328c
@@ -144,6 +144,10 @@ executables += [
|
||||
'sources' : files('test-dnssec-crypto.c'),
|
||||
'conditions' : ['HAVE_OPENSSL'],
|
||||
},
|
||||
resolve_test_template + {
|
||||
'sources' : files('test-dnssec-mldsa.c'),
|
||||
'conditions' : ['HAVE_OPENSSL'],
|
||||
},
|
||||
resolve_test_template + {
|
||||
'sources' : files('test-dns-search-domain.c'),
|
||||
},
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
|
||||
#define VERIFY_RRS_MAX 256
|
||||
#define MAX_KEY_SIZE (32*1024)
|
||||
#define MLDSA44_PUBLIC_KEY_SIZE 1312
|
||||
#define MLDSA44_SIGNATURE_SIZE 2420
|
||||
|
||||
/* Permit a maximum clock skew of 1h 10min. This should be enough to deal with DST confusion */
|
||||
#define SKEW_MAX (1*USEC_PER_HOUR + 10*USEC_PER_MINUTE)
|
||||
@@ -399,6 +401,81 @@ static int dnssec_eddsa_verify(
|
||||
dnskey->dnskey.key, key_size);
|
||||
}
|
||||
|
||||
static int dnssec_mldsa_verify(
|
||||
const void *data,
|
||||
size_t data_size,
|
||||
DnsResourceRecord *rrsig,
|
||||
DnsResourceRecord *dnskey) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(data);
|
||||
assert(data_size > 0);
|
||||
assert(rrsig);
|
||||
assert(dnskey);
|
||||
|
||||
if (dnskey->dnskey.key_size != MLDSA44_PUBLIC_KEY_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
if (rrsig->rrsig.signature_size != MLDSA44_SIGNATURE_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
/* Capability detection must only consider errors from this operation. */
|
||||
sym_ERR_clear_error();
|
||||
|
||||
_cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *kctx = sym_EVP_PKEY_CTX_new_from_name(
|
||||
/* libctx= */ NULL,
|
||||
"ML-DSA-44",
|
||||
/* propquery= */ NULL);
|
||||
if (!kctx)
|
||||
return log_openssl_errors(LOG_DEBUG, "Failed to create ML-DSA-44 key context");
|
||||
|
||||
if (sym_EVP_PKEY_fromdata_init(kctx) <= 0)
|
||||
return dnssec_verify_errno(
|
||||
log_openssl_errors(LOG_DEBUG, "Failed to initialize ML-DSA-44 key creation"));
|
||||
|
||||
OSSL_PARAM params[] = {
|
||||
sym_OSSL_PARAM_construct_octet_string(
|
||||
OSSL_PKEY_PARAM_PUB_KEY, dnskey->dnskey.key, dnskey->dnskey.key_size),
|
||||
sym_OSSL_PARAM_construct_end(),
|
||||
};
|
||||
|
||||
_cleanup_(EVP_PKEY_freep) EVP_PKEY *evkey = NULL;
|
||||
if (sym_EVP_PKEY_fromdata(kctx, &evkey, EVP_PKEY_PUBLIC_KEY, params) <= 0)
|
||||
return dnssec_verify_errno(
|
||||
log_openssl_errors(LOG_DEBUG, "Failed to load ML-DSA-44 public key from raw data"));
|
||||
|
||||
_cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX *ctx = sym_EVP_MD_CTX_new();
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
/* ML-DSA DNSSEC uses pure ML-DSA with the default empty context, not HashML-DSA */
|
||||
if (sym_EVP_DigestVerifyInit(
|
||||
ctx,
|
||||
/* pctx= */ NULL,
|
||||
/* type= */ NULL,
|
||||
/* engine= */ NULL,
|
||||
evkey) <= 0)
|
||||
return dnssec_verify_errno(
|
||||
log_openssl_errors(LOG_DEBUG, "Failed to initialize ML-DSA-44 verification"));
|
||||
|
||||
r = sym_EVP_DigestVerify(
|
||||
ctx,
|
||||
rrsig->rrsig.signature,
|
||||
rrsig->rrsig.signature_size,
|
||||
data,
|
||||
data_size);
|
||||
if (r < 0)
|
||||
return dnssec_verify_errno(
|
||||
log_openssl_errors(LOG_DEBUG, "ML-DSA-44 signature verification failed"));
|
||||
if (r == 0)
|
||||
/* An invalid signature is an expected outcome but leaves entries in the thread-local
|
||||
* OpenSSL error queue. Clear them so a later failure translates its own error */
|
||||
sym_ERR_clear_error();
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int md_add_uint8(EVP_MD_CTX *ctx, uint8_t v) {
|
||||
return sym_EVP_DigestUpdate(ctx, &v, sizeof(v));
|
||||
}
|
||||
@@ -664,6 +741,8 @@ static int dnssec_rrset_verify_sig(
|
||||
dnskey));
|
||||
case DNSSEC_ALGORITHM_ED448:
|
||||
return -EOPNOTSUPP;
|
||||
case DNSSEC_ALGORITHM_MLDSA44:
|
||||
return dnssec_mldsa_verify(sig_data, sig_size, rrsig, dnskey);
|
||||
default:
|
||||
/* OK, the RRs are now in canonical order. Let's calculate the digest */
|
||||
md_algorithm = algorithm_to_implementation_id(rrsig->rrsig.algorithm);
|
||||
|
||||
@@ -0,0 +1,219 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "crypto-util.h"
|
||||
#include "dns-answer.h"
|
||||
#include "dns-rr.h"
|
||||
#include "fileio.h"
|
||||
#include "hexdecoct.h"
|
||||
#include "resolved-dns-dnssec.h"
|
||||
#include "tests.h"
|
||||
#include "time-util.h"
|
||||
|
||||
static void* load_base64_test_vector(const char *name, size_t *ret_size) {
|
||||
_cleanup_free_ char *encoded = NULL, *path = NULL;
|
||||
void *decoded = NULL;
|
||||
|
||||
assert(name);
|
||||
assert(ret_size);
|
||||
|
||||
assert_se(get_testdata_dir(name, &path) >= 0);
|
||||
assert_se(read_full_file(path, &encoded, /* ret_size= */ NULL) >= 0);
|
||||
assert_se(unbase64mem(encoded, &decoded, ret_size) >= 0);
|
||||
|
||||
return decoded;
|
||||
}
|
||||
|
||||
static void mldsa44_fixture_new(
|
||||
DnsResourceRecord **ret_mx,
|
||||
DnsResourceRecord **ret_rrsig,
|
||||
DnsResourceRecord **ret_dnskey,
|
||||
DnsAnswer **ret_answer) {
|
||||
|
||||
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *dnskey = NULL, *mx = NULL, *rrsig = NULL;
|
||||
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
|
||||
|
||||
assert(ret_mx);
|
||||
assert(ret_rrsig);
|
||||
assert(ret_dnskey);
|
||||
assert(ret_answer);
|
||||
|
||||
/* Deterministic example from Section 6 of draft-westerbaan-dnssec-mldsa-04. */
|
||||
assert_se(dnskey = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNSKEY, "example.com."));
|
||||
dnskey->dnskey.flags = 257;
|
||||
dnskey->dnskey.protocol = 3;
|
||||
dnskey->dnskey.algorithm = DNSSEC_ALGORITHM_MLDSA44;
|
||||
dnskey->dnskey.key = load_base64_test_vector(
|
||||
"test-resolve/mldsa44-dnskey.b64", &dnskey->dnskey.key_size);
|
||||
assert_se(dnskey->dnskey.key_size == 1312);
|
||||
|
||||
assert_se(mx = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_MX, "example.com."));
|
||||
mx->ttl = 3600;
|
||||
mx->mx.priority = 10;
|
||||
assert_se(mx->mx.exchange = strdup("mail.example.com."));
|
||||
|
||||
assert_se(rrsig = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_RRSIG, "example.com."));
|
||||
rrsig->ttl = 3600;
|
||||
rrsig->rrsig.type_covered = DNS_TYPE_MX;
|
||||
rrsig->rrsig.algorithm = DNSSEC_ALGORITHM_MLDSA44;
|
||||
rrsig->rrsig.labels = 2;
|
||||
rrsig->rrsig.original_ttl = 3600;
|
||||
rrsig->rrsig.expiration = 1440021600;
|
||||
rrsig->rrsig.inception = 1438207200;
|
||||
rrsig->rrsig.key_tag = 59829;
|
||||
assert_se(rrsig->rrsig.signer = strdup("example.com."));
|
||||
rrsig->rrsig.signature = load_base64_test_vector(
|
||||
"test-resolve/mldsa44-rrsig.b64", &rrsig->rrsig.signature_size);
|
||||
assert_se(rrsig->rrsig.signature_size == 2420);
|
||||
|
||||
assert_se(dnssec_keytag(dnskey, false) == rrsig->rrsig.key_tag);
|
||||
assert_se(dnssec_key_match_rrsig(mx->key, rrsig) > 0);
|
||||
assert_se(dnssec_rrsig_match_dnskey(rrsig, dnskey, false) > 0);
|
||||
|
||||
assert_se(answer = dns_answer_new(1));
|
||||
assert_se(dns_answer_add(answer, mx, 0, DNS_ANSWER_AUTHENTICATED, /* rrsig= */ NULL) >= 0);
|
||||
|
||||
*ret_mx = TAKE_PTR(mx);
|
||||
*ret_rrsig = TAKE_PTR(rrsig);
|
||||
*ret_dnskey = TAKE_PTR(dnskey);
|
||||
*ret_answer = TAKE_PTR(answer);
|
||||
}
|
||||
|
||||
static bool mldsa44_available(void) {
|
||||
sym_ERR_clear_error();
|
||||
_cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = sym_EVP_PKEY_CTX_new_from_name(
|
||||
/* libctx= */ NULL, "ML-DSA-44", /* propquery= */ NULL);
|
||||
if (ctx)
|
||||
return true;
|
||||
|
||||
ASSERT_ERROR(log_openssl_errors(LOG_DEBUG, "ML-DSA-44 capability check failed"), EOPNOTSUPP);
|
||||
return false;
|
||||
}
|
||||
|
||||
TEST(dnssec_mldsa44_verify) {
|
||||
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *dnskey = NULL, *mx = NULL, *rrsig = NULL;
|
||||
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
|
||||
_cleanup_free_ char *algorithm = NULL;
|
||||
DnssecResult result;
|
||||
|
||||
ASSERT_OK(dnssec_algorithm_to_string_alloc(DNSSEC_ALGORITHM_MLDSA44, &algorithm));
|
||||
ASSERT_STREQ(algorithm, "MLDSA44");
|
||||
mldsa44_fixture_new(&mx, &rrsig, &dnskey, &answer);
|
||||
|
||||
bool available = mldsa44_available();
|
||||
ASSERT_OK(dnssec_verify_rrset(
|
||||
answer, mx->key, rrsig, dnskey, rrsig->rrsig.inception * USEC_PER_SEC, &result));
|
||||
if (!available) {
|
||||
ASSERT_EQ(result, DNSSEC_UNSUPPORTED_ALGORITHM);
|
||||
return (void) log_tests_skipped("OpenSSL does not provide ML-DSA-44");
|
||||
}
|
||||
ASSERT_EQ(result, DNSSEC_VALIDATED);
|
||||
|
||||
mx->mx.priority++;
|
||||
dns_resource_record_clear_wire_format(mx);
|
||||
ASSERT_OK(dnssec_verify_rrset(
|
||||
answer, mx->key, rrsig, dnskey, rrsig->rrsig.inception * USEC_PER_SEC, &result));
|
||||
ASSERT_EQ(result, DNSSEC_INVALID);
|
||||
mx->mx.priority--;
|
||||
dns_resource_record_clear_wire_format(mx);
|
||||
|
||||
((uint8_t*) rrsig->rrsig.signature)[0] ^= 1;
|
||||
ASSERT_OK(dnssec_verify_rrset(
|
||||
answer, mx->key, rrsig, dnskey, rrsig->rrsig.inception * USEC_PER_SEC, &result));
|
||||
ASSERT_EQ(result, DNSSEC_INVALID);
|
||||
}
|
||||
|
||||
TEST(dnssec_mldsa44_corrupted_key) {
|
||||
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *dnskey = NULL, *mx = NULL, *rrsig = NULL;
|
||||
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
|
||||
DnssecResult result;
|
||||
|
||||
mldsa44_fixture_new(&mx, &rrsig, &dnskey, &answer);
|
||||
|
||||
if (!mldsa44_available())
|
||||
return (void) log_tests_skipped("OpenSSL does not provide ML-DSA-44");
|
||||
|
||||
ASSERT_OK(dnssec_verify_rrset(
|
||||
answer, mx->key, rrsig, dnskey, rrsig->rrsig.inception * USEC_PER_SEC, &result));
|
||||
ASSERT_EQ(result, DNSSEC_VALIDATED);
|
||||
|
||||
((uint8_t*) dnskey->dnskey.key)[0] ^= 1;
|
||||
ASSERT_EQ(dnskey->dnskey.key_size, (size_t) 1312);
|
||||
|
||||
ASSERT_OK(dnssec_verify_rrset(
|
||||
answer, mx->key, rrsig, dnskey, rrsig->rrsig.inception * USEC_PER_SEC, &result));
|
||||
ASSERT_EQ(result, DNSSEC_INVALID);
|
||||
}
|
||||
|
||||
TEST(dnssec_mldsa44_sizes) {
|
||||
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *dnskey = NULL, *mx = NULL, *rrsig = NULL;
|
||||
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
|
||||
DnssecResult result;
|
||||
|
||||
mldsa44_fixture_new(&mx, &rrsig, &dnskey, &answer);
|
||||
|
||||
dnskey->dnskey.key_size--;
|
||||
ASSERT_ERROR(dnssec_verify_rrset(
|
||||
answer, mx->key, rrsig, dnskey,
|
||||
rrsig->rrsig.inception * USEC_PER_SEC, &result), EINVAL);
|
||||
dnskey->dnskey.key_size++;
|
||||
|
||||
rrsig->rrsig.signature_size--;
|
||||
ASSERT_ERROR(dnssec_verify_rrset(
|
||||
answer, mx->key, rrsig, dnskey,
|
||||
rrsig->rrsig.inception * USEC_PER_SEC, &result), EINVAL);
|
||||
}
|
||||
|
||||
static int fail_key_import(EVP_PKEY_CTX *ctx, EVP_PKEY **pkey, int selection, OSSL_PARAM params[]) {
|
||||
/* Generate a real unsupported-algorithm error after capability detection has succeeded. */
|
||||
_cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *unsupported = sym_EVP_PKEY_CTX_new_from_name(
|
||||
/* libctx= */ NULL, "systemd-test-nonexistent-algorithm", /* propquery= */ NULL);
|
||||
ASSERT_NULL(unsupported);
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST(dnssec_mldsa44_import_failure) {
|
||||
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *dnskey = NULL, *mx = NULL, *rrsig = NULL;
|
||||
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
|
||||
DnssecResult result;
|
||||
int r;
|
||||
|
||||
if (!mldsa44_available())
|
||||
return (void) log_tests_skipped("OpenSSL does not provide ML-DSA-44");
|
||||
|
||||
mldsa44_fixture_new(&mx, &rrsig, &dnskey, &answer);
|
||||
typeof(sym_EVP_PKEY_fromdata) saved_import = sym_EVP_PKEY_fromdata;
|
||||
sym_EVP_PKEY_fromdata = fail_key_import;
|
||||
r = dnssec_verify_rrset(
|
||||
answer, mx->key, rrsig, dnskey, rrsig->rrsig.inception * USEC_PER_SEC, &result);
|
||||
sym_EVP_PKEY_fromdata = saved_import;
|
||||
ASSERT_ERROR(r, EIO);
|
||||
}
|
||||
|
||||
TEST(dnssec_mldsa44_stale_errors) {
|
||||
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *dnskey = NULL, *mx = NULL, *rrsig = NULL;
|
||||
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
|
||||
DnssecResult result;
|
||||
|
||||
mldsa44_fixture_new(&mx, &rrsig, &dnskey, &answer);
|
||||
bool available = mldsa44_available();
|
||||
|
||||
/* Leave an unrelated key-import error in OpenSSL's queue. */
|
||||
_cleanup_(EVP_PKEY_freep) EVP_PKEY *bad = sym_EVP_PKEY_new_raw_public_key(
|
||||
EVP_PKEY_ED25519, /* engine= */ NULL, dnskey->dnskey.key, 1);
|
||||
ASSERT_NULL(bad);
|
||||
|
||||
ASSERT_OK(dnssec_verify_rrset(
|
||||
answer, mx->key, rrsig, dnskey, rrsig->rrsig.inception * USEC_PER_SEC, &result));
|
||||
ASSERT_EQ(result, available ? DNSSEC_VALIDATED : DNSSEC_UNSUPPORTED_ALGORITHM);
|
||||
ASSERT_EQ(sym_ERR_get_error(), 0UL);
|
||||
}
|
||||
|
||||
static int intro(void) {
|
||||
if (dlopen_libcrypto(LOG_DEBUG) < 0)
|
||||
return EXIT_TEST_SKIP;
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
DEFINE_TEST_MAIN_WITH_INTRO(LOG_DEBUG, intro);
|
||||
@@ -2643,6 +2643,7 @@ static const char* const dnssec_algorithm_table[_DNSSEC_ALGORITHM_MAX_DEFINED] =
|
||||
[DNSSEC_ALGORITHM_ECDSAP384SHA384] = "ECDSAP384SHA384",
|
||||
[DNSSEC_ALGORITHM_ED25519] = "ED25519",
|
||||
[DNSSEC_ALGORITHM_ED448] = "ED448",
|
||||
[DNSSEC_ALGORITHM_MLDSA44] = "MLDSA44",
|
||||
[DNSSEC_ALGORITHM_INDIRECT] = "INDIRECT",
|
||||
[DNSSEC_ALGORITHM_PRIVATEDNS] = "PRIVATEDNS",
|
||||
[DNSSEC_ALGORITHM_PRIVATEOID] = "PRIVATEOID",
|
||||
|
||||
@@ -34,6 +34,7 @@ enum {
|
||||
DNSSEC_ALGORITHM_ECDSAP384SHA384 = 14, /* RFC 6605 */
|
||||
DNSSEC_ALGORITHM_ED25519 = 15, /* RFC 8080 */
|
||||
DNSSEC_ALGORITHM_ED448 = 16, /* RFC 8080 */
|
||||
DNSSEC_ALGORITHM_MLDSA44 = 18, /* draft-westerbaan-dnssec-mldsa */
|
||||
DNSSEC_ALGORITHM_INDIRECT = 252,
|
||||
DNSSEC_ALGORITHM_PRIVATEDNS,
|
||||
DNSSEC_ALGORITHM_PRIVATEOID,
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
17K0clSq4NtF55MNSpjSyX2PE5fReJ2voXAksxbpvslPyZRtQvGbeadBO7qj
|
||||
PnFJy0LtURVpOsBB+suYit61/g4dhjEYSZW1ksOX0ilOLhT5CqQUujgmiZrE
|
||||
P0zMrLwm6agyuVEY1ctDPL75ZgsAE44IF/YediyidMNq1VTrIqrBFi5KsBrL
|
||||
oeOMTv2PgLZbMz0PcuVd/nHOnB67mInnxWEGwP1zgDoq7P6v3teqPLLO2lTR
|
||||
K9jNNqeM+XWUO0er0l6ICsRS5XQu0ejRqCr6huWQx1jBWuTShA2SvKGlCQ9A
|
||||
SWWX/KfYuVE/GhvabpUKqpjeRnUH1KT1pPBZkhZYLDVy9i7aiQWrNYFnDEoC
|
||||
d3oz4Mpylf2PT/bRoKOnaD1l9fX3/GDaAj6CbF+SFEwC99G6EHWYdVPqk2f8
|
||||
122ZC3+pnNRa/biDbUPkWfUYffBYR5cJoB6mg1k1+nBGCZDNPcG6QBupS6sd
|
||||
3kGsZ6szGdysoGBI1MTu8n7hOpwX0FOPQw8tZC3CQVZg3niHfY2KvHJSOXjA
|
||||
QuQoX0MZhGxEEmJCl2hEwQ5Va6IVtacZ5Z0MayqW05hZBx/cws3nUkp77a5U
|
||||
6FsxjoVOj+Ky8+36yXGRKCcKr9HlBEw6T9r9n/MfkHhLjo5FlhRKDa9YZRHT
|
||||
2ZYrnqla8Ze05fxg8rHtFd46W+9fib3HnZEFHZsoFudPpUUx79wcvnTUSIV/
|
||||
R2vNWPIcC2U7O3ak4HamVZowJxhVXMY/dIWaq6uSXwI4YcqM0Pe62yhx9n1V
|
||||
Mm10URNa1F9KG6aRGPuyyKMO7JOS7z+XcGbJrdXHEMxkexUU0hfZWMcBfD6Q
|
||||
/SDATmdLkEhuk3CjGgAdMvRzl55JBnSefkd/oLdFCPil8jeDErg8Jb04jKCw
|
||||
//dHi69CtxZn7arJfEaxKWQ+WG5bBVoMIRlG1PNuZ1vtWGD6BCoxXZgmFk1q
|
||||
kjfDWl+/SVSQpb1N8ki5XEqud4S2BWcxZqxCRbW0sIKgnpMj5i8geMW3Z4NE
|
||||
be/XNq06NwLUmwiYRJAKYYMzl7xEGbMNepegs4fBkRR0xNQbU+Mql3rLbw6n
|
||||
XbZbs55Z5wHnaVfe9vLURVnDGncSK1IE47XCGfFoixTtC8C4AbPm6C3NQ+nA
|
||||
6fQXRM2YFb0byIINi7Ej8E+s0bG2hd1aKxuNu/PtkzZw8JWhgLTxktCLELj6
|
||||
u9/MKyRRjjLuoKXgyQTKhEeACD87DNLQuLavZ7w1W5SUAl3HsKePqA46Lb/r
|
||||
UTKIUdYHgZjpSTZRrnh+wCUfkiujDp9R32Km1yeEzz3SBTkxdt+jJKUSvZSX
|
||||
CjbdNKUUqGeR8Os28BRbCatkZRtKAxOymWEaKhxIiRYnWYdooxFAYLpEQ0ht
|
||||
9RUioc6IswmFwhb45u0XjdVnswSg1Mr7qIKig0LxepqiauWNtjAIPSw1j99W
|
||||
bD9dYqQoVnvJ6ozpXKoPNUdLC/qPM5olCrTfzyCDvo7vvBBV4Y/hU3DuyyYF
|
||||
Ztg/8GshGq7EPKKbVMzQD4gVokZe8LRlFcx+QfMSTwnv/3OTCatYspoUWaAL
|
||||
zlA46TjJZ49y6w5O5f2q5m2fhXP8l/xCtJWfS/i2HXhDPoawM11ukZHE2L9I
|
||||
ezkFwQjP1qwksM633LfPUfhNDtaHuV6uscUzwG8NlwI9kqcIJYN7Wbpst9Tl
|
||||
awqHwgOGKujzFbpZJejt76Z5NpoiAnZhUfFqll+fgeznbMBwtVhp5NuXhM8F
|
||||
yDCzJCyDEg==
|
||||
@@ -0,0 +1,54 @@
|
||||
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
|
||||
Reference in New Issue
Block a user