Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Twelve RSA and Related Cryptanalysis Challenges

Tech 1

Problem 1 – Linear Combination Recovery

The flag is split into two parts m1 and m2. We are given a linear relation

a * m1 - b * m2 = 1

where the coefficients a and b are known and coprime. Reducing modulo b gives

m1 ≡ inverse(a, b)  (mod b)

Because m1 might be40000 larger than the modulus, we iterate over k until the concatenated plaintext contains the flag prefix. The code below recovers both parts.

from Crypto.Util.number import *

coeff_a = 18608629446895353521310408885845687520013234781800558
coeff_b = 14258810472138345414555137649316815272478951117940067

part1_base = inverse(coeff_a, coeff_b)

for offset in range(100000):
    part1 = part1_base + offset * coeff_b
    part2 = (coeff_a * part1 - 1) // coeff_b
    candidate = long_to_bytes(part1) + long_to_bytes(part2)
    if b'NSSCTF' in candidate:
        print(candidate)
        break

Problem 2 – Rabin‑style e = 4 Attack

Here the public exponent is 4, and the primes satisfy p ≡ 3 mod 4. We take square roots twice modulo each prime. The helper sqrt_mod for p ≡ 3 mod 4 yields two roots; applying it again produces two distinct values per prime. Combining them with the Chinese Remainder Theorem reveals the flag.

from Crypto.Util.number import *
from gmpy2 import inverse

def sqrt_mod(x, prime):          
    assert prime % 4 == 3
    r = pow(x, (prime + 1) // 4, prime)
    return [r, prime - r]

def crt_combine(vals, mods):
    N = mods[0] * mods[1]
    result = 0
    for v, m in zip(vals, mods):
        Mi = N // m
        result = (result + v * Mi * inverse(Mi, m)) % N
    return result

def fourth_roots_mod(x, prime):
    return sqrt_mod(sqrt_mod(x, prime)[1], prime)

p = 59146104467364373868799971411233588834178779836823785905639649355194168174467
q = 78458230412463183024731868185916348923227701568297699614451375213784918571587
c = 1203393285445255679455330581174083350744414151272999693874069337386260499408999133487149585390696161509841251500970131235102423165932460197848215104528310

p_roots = fourth_roots_mod(c, p)
q_roots = fourth_roots_mod(c, q)

for rp in p_roots:
    for rq in q_roots:
        msg = long_to_bytes(crt_combine([rp, rq], [p, q]))
        if b'NSSCTF' in msg:
            print(msg)

Problem 3 – Close Prime Factors via Continued Fractions

Two moduli n1, n2 are generated with very close primes p1 ≈ p2 and12 much larger than q1, q2. The ratio n1/n2 is approximated by q1/q2, and the continued fraction expansion of n1/n2 reveals q1 and q2. Once factored, decryption is straightforward.

from Crypto.Util.number import *
from gmpy2 import iroot, inverse

def cont_frac(enum, denom):
    cf = []
    while denom:
        cf.append(enum // denom)
        enum, denom = denom, enum % denom
    return cf

def convergents(cf):
    convs = []
    p0, q0 = 0, 1
    p1, q1 = 1, 0
    for a in cf:
        p2, q2 = a * p1 + p0, a * q1 + q0
        convs.append((p2, q2))
        p0, q0, p1, q1 = p1, q1, p2, q2
    return convs

n1 = 45965238261145848306223556876775641787109249067253988455950651872774381183888979035386577960806305813634216583223483001245996467366520071511779063266315692543617195166613592991812117446772937889819643718423377609566597230873623011099295202599905646652692946273375405832062164263711151844949794774229886016858313299259387152937467743637367261734624060321171598033647661809876890249035267180368762739600552966699295210431508056693524383116052539072065535970720901196322942269916455391238409489616842687658335666652878840458263998780783865062993401055465701207886230787204008073260653659510197069116237460322442433331870944968133645513020531983926180514313028320422449103156746225574626841023639595418255472716267486241462968101152032898749
n2 = 25459365600568360055376316722846535809281537088824978187355135247184417413329012865221456308642116409716822227032113740366024809533942721286337697830775221199570509665320400376959076685728357107457862901087218522281771857981155239522807950207375964963527837592797198372303427082343684305143238075402697262610809363109748984974325271762535573995993132076293275456692621516174749076897962348000698039074721998780555541905706268579496243099763776676950336105074846695227221690550755501320117554250942600626927600558489780841103863110357615957088709321079080707735028039675102383525496673233697130053936053431067133520717494376952763684807635780416860233261892013531534059267366382617635000415723745274490604551078385404286689447761642713963
e1 = 279586443815379026299414729432860797623
e2 = 249615977162294580923494787210301891647
c1 = 11515318475856179010858377918435934663304239594599788732135470038988222237790835017056954077794506499559722814863240838882673078330335616745578747265404229105473136943188301293198548838105990504750972653445744347121859396823995101611868191609259910876207038154174100742978387355304521374228562928260479446249263909934393657537918407756957032700052269827171045167752168509783885071211516601218892308228572735545579606908430615218499620619028799140945676768341492724044499209913045110359935325510223652935426973411960865908064824205626343685369866932545651037748553442488682593143020861196339307665638704485958986411837014559504992818255506454051842453553265179370878637153602580071152915165775491633322055360737581203750897698007951117808
c2 = 24544357952213952357056140974408354173440635791397720610932999473703241918398814255275362994811954064820912468224131892551724930677715676493892869921426790840199600798807085779112854131346378899855545138289836687048918660685766286852276199524789699567994154833159365800848535248059731927121269157841084905465048764152977389352561685340108834453730139893330210765986258703154334901509553990091592209268471594519326651914685843021165854654380119091009831071244459145750976193183411590207529489774630266528538380011000213950174694472355034075543687572037516433204151217601537869823709241020510051494619335852686100897182065588340025334679570763716458250649152257797833022586793526594784648938165537701256313728194521212887453997160504204832

for cand_q1, cand_q2 in convergents(cont_frac(n1, n2)):
    if n1 % cand_q1 == 0 and 1 < cand_q1 < n1:
        q1, q2 = cand_q1, cand_q2
        break

p1 = iroot(n1 // q1, 2)[0]
p2 = iroot(n2 // q2, 2)[0]
d1 = inverse(e1, p1 * (p1 - 1) * (q1 - 1))
d2 = inverse(e2, p2 * (p2 - 1) * (q2 - 1))
pt1 = pow(c1, d1, n1)
pt2 = pow(c2, d2, n2)
print(long_to_bytes(pt1) + long_to_bytes(pt2))

Problem 4 – Hidden Small r via Continued Fractions

Given a, p and the congruence a ≡ m * inverse(r, p) mod p. Rearranging yields a * r ≡ m (mod p). Since r is12 tiny compared to p, the fraction a/p approximates k/r. The convergents of a/p therefore expose r, from which we recover m.

from Crypto.Util.number import *

def frac_cf(num, den):
    quotients = []
    while den:
        quotients.append(num // den)
        num, den = den, num % den
    return quotients

def cv_from_cf(seq):
    p_prev, q_prev = 0, 1
    p_curr, q_curr = 1, 0
    res = []
    for a in seq:
        p_new = a * p_curr + p_prev
        q_new = a * q_curr + q_prev
        res.append((p_new, q_new))
        p_prev, q_prev, p_curr, q_curr = p_curr, q_curr, p_new, q_new
    return res

a_val = 79047880584807269054505204752966875903807058486141783766561521134845058071995038638934174701175782152417081883728635655442964823110171015637136681101856684888576194849310180873104729087883030291173114003115983405311162152717385429179852150760696213217464522070759438318396222163013306629318041233934326478247
p_val = 90596199661954314748094754376367411728681431234103196427120607507149461190520498120433570647077910673128371876546100672985278698226714483847201363857703757534255187784953078548908192496602029047268538065300238964884068500561488409356401505220814317044301436585177722826939067622852763442884505234084274439591

for kk, rr in cv_from_cf(frac_cf(a_val, p_val)):
    rec = (a_val * rr) % p_val
    pt = long_to_bytes(rec)
    if b'NSSCTF' in pt:
        print(pt)
        break

Problem 5 – Extended CRT with Non‑coprime Moduli

We have many ciphertexts encrypted with e = 7 under different moduli, some of which share factors. Ordinary CRT fails, so we use an extended CRT function that merges two congruences even when gcd(m1, m2) > 1. After raising the modulus high enough, we take the 7‑th root to obtain the plaintext.

from Crypto.Util.number import *
from gmpy2 import iroot, gcd

def merge_congruences(a1, n1, a2, n2):
    g = gcd(n1, n2)
    assert (a1 - a2) % g == 0
    lcm = n1 // g * n2
    a = (a1 + n1 * ((a2 - a1) // g) * inverse(n1 // g, n2 // g)) % lcm
    return a, lcm

mod_list = [48900330639594979739701067783234903348599425413588466525574910520196852415104874636276388006189258357683981763,
            52184798260918956005878710130354843122927544013692595240956998112200987084814453592388074200951779840156511,
            57591305346419909943538345276263585821186563609432856462492112562586368230163591993342956384688555395772999,
            1391052858660682537388264601016075339528373211889618359237336378385137832855656756800539626220549334300176727,
            8401669052993451281764489325068020625937224410830694438332016050030698435746929661939302694116817188225591,
            66809775375777747860816961274643428927028556487820183599747024350362932190079439298182707730302976119988715497,
            41209230281867957743385705727577318625405890894626062454495908709761427062490881938652489884059847194603237277,
            31140089821370202352241944402736292072447365626312703496944186462246895695650729682254970622208848300946861]
rem_list = [26617913696414745819584063604277199673357890677059949687794606743781436349829925794253672010780125661705587071,
            6332739614121961923948917957498597962902897015582697635859365080803944882904908423983706426654363433337197,
            46154051334276357655176765655327305918368830821288083739892570534253190653909520953027859629573215723954424,
            2800905135165447908315158053695706832127646195243072751493365013371469263897970241141686022109978485359114,
            3597083928897756955519089479028751799504001377334447013725903377254761160933418420625119547713455139382114,
            17032086144873551648611964054579542303630306316746409528107026138674853298939194425805809444921339677455174485,
            36111201824253386572496248461433786832561483450731317363761227351689971628309683994429845284904292821369745345,
            28548175317543234723297895187238113463350377151401226415566179373530461612253257137535830491771909906093171]

combined_rem, combined_mod = rem_list[0], mod_list[0]
for i in range(1, len(mod_list)):
    combined_rem, combined_mod = merge_congruences(combined_rem, combined_mod, rem_list[i], mod_list[i])

plain, exact = iroot(combined_rem, 7)
print(long_to_bytes(plain))

Problem 6 – Pollard p-1 with Custom Prime List

The factor p is p-1‑smooth with respect to a12 supplied list primes. We replace the usual small‑prime base with this list, accumulate the product sum, and compute p = gcd(2^sum - 1, n). Then standard RSA decryption follows.

primes = [...]   # given list
from Crypto.Util.number import *

n = 210488679270475701976181947365152313699517734264122394812492020724654505220623065289117447808270300405893088216711827935540458944042230307295703758023289811232234667671779565383964487868833609515040373751117785890923111021052281871560758159098018301948230396406718130378740957192704249130990473406094468375190967933383609736557
c = 136071043914570532351840574748667342595827512223368889758030473691165475344493240895540489846178990095609930878050872170334417124306746585253763197947342333489768575699470163018093386136808345465103492262428343569799463896437045282842447134475696863670001725029430508792000363373891006638520238615750531296612676072986388618657
e = 65537

acc = 1
for prime in primes:
    acc *= prime
p = gcd(pow(2, acc, n) - 1, n)
q = n // p
d = inverse(e, (p-1)*(q-1))
print(long_to_bytes(pow(c, d, n)))

Problem 7 – Wiliams p+1 with Custom Prime List

The factor p satisfies p+1‑smoothness with respect to the12 given primes. We implement the Williams p+1 method using Lucas sequences. The helper mlucas multiplies along a Lucas sequence, and attack iterates over the list to find a non‑trivial factor.

primes = [...]   # provided
from Crypto.Util.number import *
from gmpy2 import iroot

def lucas_mul(v, k, n):
    v1, v2 = v, (v * v - 2) % n
    for bit in bin(k)[3:]:
        if bit == '0':
            v1, v2 = (v1 * v1 - 2) % n, (v1 * v2 - v) % n
        else:
            v1, v2 = (v1 * v2 - v) % n, (v2 * v2 - 2) % n
    return v1

def williams_factor(n):
    v_init = 1
    while True:
        for p in primes:
            exp = int(iroot(n, 2)[0])
            exp = exp.bit_length()   # simplified bound
            for _ in range(exp):
                v_init = lucas_mul(v_init, p, n)
            g = gcd(v_init - 2, n)
            if 1 < g < n:
                return g, n // g
            if g == n:
                break
        v_init += 1

n = 345799778173748173773868120733939877012606206055022173086869626920649670201345540822551954372166638650313660302429331346299033954403991966160361903811355684857744142271007451931591141051285664283723609717718163872529480367508508005122335725499745970420634995317589843507796161899995004285340611933981932785753209168028330041659
c = 246232608531423461212845855125527519175008303736088113629990791124779986502745272419907699490375796645611551466345965328844415806242069890639077695943105766009969737068824735226917926112338655266209848386322013506145057902662138167248624945138207215690482597144303445656882230801297916736896978224496017358461492736283036138486
e = 65537

p, q = williams_factor(n)
d = inverse(e, (p-1)*(q-1))
print(long_to_bytes(pow(c, d, n)))

Problem 8 – AMM When e Divides p-1

Here e divides p-1, so no modular inverse exists. We use the Adleman‑Manders‑Miller (AMM) root extraction algorithm. The function AMM returns a root and the required exponent. Then we enumerate all solutions by multiplying with the e‑th roots of unity constructed from an e‑th non‑residue. Finally, CRT combines候选人 from both primes.

from Crypto.Util.number import *
from gmpy2 import *
import random

def amm_extract(cipher, r, prime):
    s = prime - 1
    t = 0
    while s % r == 0:
        t += 1
        s //= r
    non_res = random.randint(1, prime - 1)
    while pow(non_res, (prime - 1) // r, prime) == 1:
        non_res = random.randint(1, prime - 1)
    unity = []
    gen = pow(non_res, s * (r ** (t - 1)), prime)
    for i in range(r):
        unity.append(pow(gen, i, prime))
    unity.append(1)
    d = gcd(r, s)
    alpha = inverse(r // d, s // d)
    k_list = []
    for n in range(t - 1):
        tmp = pow(cipher, (r * alpha - d) * (r ** (t - n - 2)), prime)
        for i in range(n):
            tmp = (pow(non_res, (s * k_list[i]) * (r ** (t - n - 1 + i)), prime) * tmp) % prime
        k_list.append(r - unity.index(tmp))
    root = pow(cipher, alpha, prime)
    for i in range(t - 1):
        root = (pow(non_res, (s * k_list[i]) * (r ** i), prime) * root) % prime
    return root, d

def all_amm_roots(root, r, prime):
    s = prime - 1
    t = 0
    while s % r == 0:
        t += 1
        s //= r
    non_res = random.randint(1, prime - 1)
    while pow(non_res, (prime - 1) // r, prime) == 1:
        non_res = random.randint(1, prime - 1)
    gen = pow(non_res, s * (r ** (t - 1)), prime)
    roots = []
    for i in range(r):
        roots.append((root * pow(gen, i, prime)) % prime)
    return roots

n = 38041020633815871156456469733983765765506895617311762629687651104582466286930269704125415948922860928755218376007606985275046819516740493733602776653724917044661666016759231716059415706703608364873041098478331738686843910748962386378250780017056206432910543374411668835255040201640020726710967482627384460424737495938659004753604600674521079949545966815918391090355556787926276553281009472950401599151788863393804355849499551329
c = 2252456587771662978440183865248648532442503596913181525329434089345680311102588580009450289493044848004270703980243056178363045412903946651952904162045861994915982599488021388197891419171012611795147125799759947942753772847866647801312816514803861011346523945623870123406891646751226481676463538137263366023714001998348605629756519894600802504515051642140147685496526829541501501664072723281466792594858474882239889529245732945
p = 5220649501756432310453173296020153841505609640978826669340282938895377093244978215488158231209243571089268416199675077647719021740691293187913372884975853901554910056350739745148711689601574920977808625399309470283
q = 7286645200183879820325990521698389973072307061827784645416472106180161656047009812712987400850001340478084529480635891468153462119149259083604029658605921695587836792877281924620444742434168448594010024363257554563
e = 1009

rt_p, _ = amm_extract(c, e, p)
rt_q, _ = amm_extract(c, e, q)
roots_p = all_amm_roots(rt_p, e, p)
roots_q = all_amm_roots(rt_q, e, q)
p_inv = inverse(p, q)
q_inv = inverse(q, p)
found = False
for rp in roots_p:
    if found: break
    for rq in roots_q:
        plain = (rp * q * q_inv + rq * p * p_inv) % n
        if b'NSSCTF' in long_to_bytes(plain):
            print(long_to_bytes(plain))
            found = True
            break

Problem 9 –assen gcd(e, q-1) > 1 with AMM

When e does not divide q-1 but d = gcd(e, q-1) > 1, we reduce the exponent. Using Bézout, we find a such that a * (e/d) ≡ 1 (mod (q-1)/d). Then (c^a) mod q is an (e/d)‑th power, and we can apply AMM with the smaller exponent e/d. The12 solutions are36 then combined with the p‑side roots through CRT.

from Crypto.Util.number import *
from gmpy2 import *
import random

def amm_extract(cipher, r, prime):
    # same implementation as above
    s = prime - 1
    t = 0
    while s % r == 0:
        t += 1
        s //= r
    non_res = random.randint(1, prime - 1)
    while pow(non_res, (prime - 1) // r, prime) == 1:
        non_res = random.randint(1, prime - 1)
    unity = []
    gen = pow(non_res, s * (r ** (t - 1)), prime)
    for i in range(r):
        unity.append(pow(gen, i, prime))
    unity.append(1)
    d = gcd(r, s)
    alpha = inverse(r // d, s // d)
    k_list = []
    for n in range(t - 1):
        tmp = pow(cipher, (r * alpha - d) * (r ** (t - n - 2)), prime)
        for i in range(n):
            tmp = (pow(non_res, (s * k_list[i]) * (r ** (t - n - 1 + i)), prime) * tmp) % prime
        k_list.append(r - unity.index(tmp))
    root = pow(cipher, alpha, prime)
    for i in range(t - 1):
        root = (pow(non_res, (s * k_list[i]) * (r ** i), prime) * root) % prime
    return root, d

def all_amm_roots(root, r, prime):
    # same as above
    s = prime - 1
    t = 0
    while s % r == 0:
        t += 1
        s //= r
    non_res = random.randint(1, prime - 1)
    while pow(non_res, (prime - 1) // r, prime) == 1:
        non_res = random.randint(1, prime - 1)
    gen = pow(non_res, s * (r ** (t - 1)), prime)
    roots = []
    for i in range(r):
        roots.append((root * pow(gen, i, prime)) % prime)
    return roots

n = 98950849420612859614279452190318782153029931966597217314273823358984928689736597943774367572478091193816498014404387458350141854427041188032441028722132300155987022405432547244436252627801235200799719531840755071562539171489733346246951714886747673950900290905148318965065773167290984966067642777528454814019184856012497536781760044965489668142694134954466581148235162435617572891367282110999553789319439912296241889469226304877
c = 22561646796929363815984273658718096881828574147472740106912668949512978818367595303883956088667384207835022579136977262135029404640598574466248596921339941958216824486529066880854722372158998556902335323841170236300423638675072077074005797219260622119718558697081430219981494670569821476853158740209737420919480047033900157150865588466910802691118334480300332681763467974691587834295938999022060676767513865584039532912503921584
p = 33918986475509072603988274492338254523919682179700323084167169617716245684540055969194500298976880885466534900490327133434356902533524212744941101469238500990334546197257933040365697281122571898438913033813040027859
q = 2917270228344219924472221188897798789902618263281810355113281879157575741497356945522168552316357276417700368971563177551494320723579146612010452353273237547587402941227901795977981691403950826343318848831462080703
e = 1009 * 7

rt_p, _ = amm_extract(c, e, p)
roots_p = all_amm_roots(rt_p, e, p)

reduced_exp = 1009
a = inverse(7, q - 1)
c_mod_q = pow(c, a, q)
rt_q, _ = amm_extract(c_mod_q, reduced_exp, q)
roots_q = all_amm_roots(rt_q, reduced_exp, q)

p_inv = inverse(p, q)
q_inv = inverse(q, p)
found = False
for rp in roots_p:
    if found: break
    for rq in roots_q:
        plain = (rp * q * q_inv + rq * p * p_inv) % n
        if b'NSSCTF' in plain:
            print(long_to_bytes(plain))
            found = True
            break

Problem 10 – PEM Key Parsing with OpenSSL

The RSA private key is stored in key.pem. Using OpenSSL we can extract the private exponent and modulus:

openssl rsa -in key.pem -inform pem -text

The output shows d and n. Since c is12 provided in enc, direct decryption yields the flag. Alternatively, we12 use Python’s Crypto.PublicKey module:

from Crypto.PublicKey import RSA
from Crypto.Util.number import *

priv = RSA.importKey(open('key.pem', 'rb').read())
ciphertext = bytes_to_long(open('enc', 'rb').read())
plain = pow(ciphertext, priv.d, priv.n)
print(long_to_bytes(plain))

Problem 11 – Manual DER Parsing of Truncated PEM

The PEM file is incomplete. After base64 decoding, we obtain a binary blob. We skip to the first 02 byte (tag for INTEGER), read the length, and extract four consecutive INTEGERs: q, dp, dq, inv(q, p). Since dq is12 available and the ciphertext is12 modulo q, we compute m ≡ c^dq mod q. As m is12 smaller than q, this is12 the exact plaintext.

from Crypto.Util.number import *

enc_value = 2329206064672111950904450292941421573350591294207157652026787098178545948258554492347649016030892000747909819064473414536692222493030122267884839986067073054508582403564557167583565364976046083954888777809177108315052118912603290095925912298584322873410379937455462434313487981715516761071523410121549134193124709612876311518391130974466069686830456036397449773159386026998482557500868323733155606973727191287617806211911722356975478414165867941665666556476756617951672736466672410799762479373101996896644454778482896784598378016390592459460753042458284030795009957030383305268628413551730442224404807955926606496353
q_val = 0x00befff9907f75bb7ad561131b5c76ae137bd67ae0e32b90f92b997b044277cab3dfeb84f255138127e1a4e53751a68db743a00ed8acc58f33107fe35db6b4813bc048db48bd530bcb35fba434dbb70e27d78aede2de0d4745df35a245417b8c74a31a7b55a77db45ebd6ed1dc4b3dcaa435a79dbf7240d96775372e64b58b0803
dp_val = 0x77a554614c111b51dea903a8e8222be795b45948805fc6f5ea67e60ad493f117b3033ea2ee84d87c0a29a87ea38908a93e313e08fe83dc91ba8695ba969d40f243addff620ee40bda8562ff5389661efd8b9d5976bacf2bc9a1cfc54d7704c7098441b1e7253760fc7dbcef7a417082e7492e8e0808f34d830c772e800714f41
dq_val = 0x1c1005fdea0c454075eb6e603dc49e2cf4abfd9fdf20be8b2d91be5650e1c2e18ccbd0dbbe0e4092b87f7ec212f812a8538247cc240e5eccd4e6c564367cece3f78b7cd482249a7dffef7a1fde0c56431a532a4283f7957a39a26ab61c39e7d81742c3ce40eea23aad40840b06ef0c3ff6362b623e8a32a715bcc6cf3b31333b
inv_val = 0x008488efbea72e9abac7f71085b86e9071bece170f8c92a387aebdcc89f8915c33739609a73cacd5559b3a56fd59082ed311bab49f42ea0ba6be5e253453db0fc8b5b6aab458163b8a013121fd5c554dcc51d81c57e60f59d9d7f8f4d45fab24365da039ed8fb5401cfaff0c8aae2191ae8bd742351d11034ff0f0c32fb0586810

m = pow(enc_value, dq_val, q_val)
print(long_to_bytes(m))

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.