นอกจากนี้บน Elliptic Curves


29

นอกจากนี้บน Elliptic Curves

ข้อสงวนสิทธิ์: สิ่งนี้ไม่ได้ทำเพื่อความยุติธรรมในหัวข้อที่สมบูรณ์ของเส้นโค้งรูปไข่ มันง่ายขึ้นมาก เมื่อเร็ว ๆ นี้เส้นโค้งรูปไข่ได้รับความสนใจจากสื่อเป็นอย่างมากในบริบทของการเข้ารหัสฉันต้องการที่จะให้ข้อมูลเชิงลึกเล็กน้อยเกี่ยวกับวิธีการ "คำนวณ" บนเส้นโค้งรูปไข่ที่ใช้งานได้จริง

บทนำ

เส้นโค้งรูปไข่เป็นชุดของจุดในระนาบของฟอร์ม(x,y) y^2 = x^3+Ax+B(นอกจากนี้4A^3+27B^2 ≠ 0เพื่อหลีกเลี่ยงภาวะเอกฐานที่น่ารังเกียจ) คุณสามารถพิจารณาส่วนโค้งเหล่านี้ได้ในทุกสาขา หากคุณใช้ฟิลด์ตัวเลขจริงเส้นโค้งสามารถมองเห็นได้และจะมีลักษณะดังนี้:

ตัวอย่างของเส้นโค้งรูปไข่
แหล่ง

สิ่งที่พิเศษเกี่ยวกับเส้นโค้งเหล่านี้เป็นสิ่งที่พวกเขามีในตัวดำเนินการทางคณิตศาสตร์ซึ่งเป็นอนาล็อกของการเพิ่ม คุณสามารถเพิ่มและลบคะแนนและการดำเนินการนี้เป็นทั้งการเชื่อมโยงและการสลับ (กลุ่ม abelian)

นอกจากนี้ทำงานอย่างไร

หมายเหตุ: การเพิ่มจุดต่าง ๆ บนวงรีรูปไข่นั้นไม่ง่าย การเพิ่มชนิดนี้ถูกกำหนดในแบบที่เป็นเพราะมีคุณสมบัติที่ดีบางอย่าง มันแปลก แต่ก็ใช้งานได้

เมื่อเส้นโค้งรูปไข่เป็นกลุ่มจึงมีตัวตนเพิ่มเติมที่เทียบเท่ากับ 0 นั่นคือการเพิ่ม0ไปยังจุดใด ๆ จะไม่เปลี่ยนผลลัพธ์ ตัวตนเพิ่มเติมนี้คือ "จุด" ที่ไม่มีที่สิ้นสุด เส้นทั้งหมดบนเครื่องบินจะรวมจุดนี้ไว้ที่ระยะอินฟินิตี้ดังนั้นการเพิ่มเข้าไปจึงไม่แตกต่างกัน

พูด Let 's ที่ตัดสายใดก็ตามโค้งสามจุดซึ่งอาจจะเป็นและว่าผลรวมของทั้งสามจุดคือ0 0โปรดจำไว้ว่าให้ดูที่ภาพนี้

กรณีพิเศษนอกจากนี้
แหล่ง

คำถามธรรมชาติคืออะไรP+Q? ดีถ้าP+Q+R = 0แล้วP+Q = -R(เขียนอีกทางหนึ่งเป็นR') อยู่ที่ไหน-R? มันเป็นที่ที่R + (-R) = 0ซึ่งเป็นในด้านอื่น ๆ ของแกน x จากRดังนั้นบรรทัดผ่านพวกเขาอยู่ในแนวตั้งตัดเพียงR, และ-R 0คุณสามารถเห็นสิ่งนี้ได้ในส่วนแรกของภาพนี้:

ไดอะแกรมของส่วนเพิ่มเติมต่าง ๆ บนรูปไข่โค้ง แหล่ง

อีกสิ่งที่คุณสามารถเห็นได้ในภาพเหล่านี้คือผลรวมของจุดด้วยตัวเองหมายความว่าเส้นสัมผัสกับส่วนโค้ง

วิธีการหาจุดตัดของเส้นและเส้นโค้งรูปไข่

ในกรณีที่มีสองจุดที่แตกต่างกัน

P=(x0,y0), Q=(x1,y1)โดยทั่วไปมีตรงหนึ่งเส้นผ่านจุดสองจุด สมมติว่ามันไม่ใช่แนวตั้งและจุดสองจุดนั้นแตกต่างกันเราสามารถเขียนมันy = m*x+qได้ เมื่อเราต้องการหาจุดตัดด้วยเส้นโค้งรูปไข่เราก็เขียนได้

0 = x^3+Ax+B-y^2 = x^3+Ax+B-(m*x+q)^2

ซึ่งเป็นพหุนามระดับสาม เหล่านี้มักจะไม่ง่ายที่จะแก้ แต่เรารู้อยู่แล้วว่าสองศูนย์ของพหุนามนี้: สองx-coordinates x0, x1ของทั้งสองจุดที่เราต้องการเพิ่ม!

ว่าปัจจัยทางที่เราออกจากปัจจัยเชิงเส้น(x-x0)และ(x-x1)และจะเหลือเป็นปัจจัยเชิงเส้นที่สามที่มีรากเป็นประสานงานของจุดx R( -Rเช่นกันเนื่องจากความสมมาตรโปรดทราบว่าหากเป็นเช่นR = (x2,y2)นั้นแล้ว-R = (x2,-y2)กลุ่ม-นั้นมาจากไม่ใช่กลุ่มเวกเตอร์ลบ)

ในกรณีของการเพิ่มจุดหนึ่งPไปยังตัวของมันเอง

P=(x0,y0)ในกรณีนี้เราต้องคำนวณสัมผัสของเส้นโค้งที่ เราสามารถเขียนโดยตรงmและqในแง่ของA,B,x0,y0:

     3*x0^2 + A
m = ------------
        2*y0

     -x0^3 + A*x0 + 2*B
q = --------------------
          2*y0

เราได้รับสมการy = m*x+qและสามารถดำเนินการเช่นเดียวกับในวรรคข้างต้น

ต้นไม้กรณีที่สมบูรณ์

นี่คือรายการทั้งหมดของวิธีจัดการกรณีเหล่านี้ทั้งหมด:

อนุญาตP,Qเป็นจุดบนโค้งรูปไข่ (รวมถึงจุด "อนันต์" 0)

  • หากP = 0หรือQ = 0จากนั้นP+Q = QหรือP+Q = Pตามลำดับ
  • อื่นP ≠ 0และQ ≠ 0เพื่อให้P = (x0,y0)และQ = (x1,y1):
    • หากP = -Q(นั่นหมายถึงx0 = x1และy0 = -y1) จากนั้นP+Q = 0
    • อื่น P ≠ -Q
      • หากx0 = x1แล้วเรามีP=Qและเราคำนวณสัมผัสกัน (ดูด้านบน) Rเพื่อที่จะได้รับ แล้วก็P+Q = P+P = 2P = -R
      • อื่น ๆ : เราสามารถสร้างเส้นของรูปแบบy = m*x+yผ่านทั้งสองจุด (ดูด้านบน) Rเพื่อคำนวณ แล้วก็P+Q=-R

ฟิลด์ จำกัด

สำหรับความท้าทายนี้เราจะพิจารณาด้านของขนาดpที่pเป็นสำคัญ (และเพราะรายละเอียดบางอย่างp ≠ 2, p ≠ 3) นี่คือข้อดีที่คุณสามารถคำนวณได้mod pได้ เลขคณิตในสาขาอื่นมีความซับซ้อนมากขึ้น

นี้ในตัวอย่างนี้เราตั้งp = 5และ equalities ทั้งหมดที่นี่มี mod 5congruences

2+4 ≡ 6 ≡ 1
2-4 ≡ -2 ≡ 3
2*4 ≡ 8 ≡ 3
2/4 ≡ 2*4 ≡ 3 because 4*4 ≡ 16 ≡ 1, therefore 1/4 ≡ 4

ท้าทาย

เมื่อกำหนดพารามิเตอร์A,Bของเส้นโค้งรูปไข่ลักษณะเฉพาะของเขตข้อมูลpและจุดสองจุดP,Qบนเส้นโค้งรูปไข่จะคืนค่าผลรวมของพวกเขา

  • คุณสามารถสันนิษฐานได้ว่าพารามิเตอร์A,Bอธิบายเส้นโค้งรูปไข่จริง ๆ นั่นหมายความว่า4A^3+27B^2 ≠ 0จริงอธิบายไข่โค้งซึ่งหมายความว่า
  • คุณสามารถสมมติว่าP,Qเป็นจริงจุดบนเส้นโค้งรูปไข่หรือ0-point
  • คุณสามารถสรุปได้ว่าp ≠ 2,3มันยอดเยี่ยม

กรณีทดสอบ

ฉันใช้งาน (ไม่หรูหรามาก) ใน MATLAB / Octave ซึ่งคุณสามารถใช้สำหรับกรณีทดสอบของคุณเอง: ideone.comฉันหวังว่ามันจะถูกต้อง อย่างน้อยก็ทำซ้ำการคำนวณบางอย่างที่ฉันทำด้วยมือ

สังเกตกรณีทดสอบเล็ก ๆ น้อย ๆ ที่ใช้งานได้กับทุกส่วนโค้งที่เราพิจารณาที่นี่:

การเพิ่มศูนย์: การP+0 = P เพิ่มอินเวอร์ส:(x,y) + (x,-y) = 0


สำหรับp = 7, A = 0, B = 5จุดสองจุดP = (3,2)และQ = (6,2)อยู่บนโค้งวงรี จากนั้นถือดังต่อไปนี้:

2*Q = Q+Q = P
2*P = P+P = (5,2)
3*P = P+P+P = (5,2)+P = (6,5)
4*P = P+P+P+P = (5,2)+(5,2) = (6,5)+(5,2) = Q

poins ทั้งหมดบนเส้นโค้งรูปไข่คือ (3,2),(5,2),(6,2),(3,5),(5,5),(6,5),0


สำหรับp = 13, A = 3, B = 8พวกเรา

(1,8)+(9,7) = (2,10)
(2,3)+(12,11) = (9,7)
2*(9,6) = (9,7)
3*(9,6) = 0

สำหรับp = 17, A = 2, B = 2และP=(5,1) เราได้รับ

2*P = (6,3)
3*P = (10,6)
4*P = (3,1)
5*P = (9,16)
6*P = (16,13)
7*P = (0,6)
8*P = (13,7)
9*P = (7,6)
10*P = (7,11)

หากคุณทะเยอทะยานจริงๆ

p = 1550031797834347859248576414813139942411
A = 1009296542191532464076260367525816293976
x0 = 1317953763239595888465524145589872695690
y0 = 434829348619031278460656303481105428081
x1 = 1247392211317907151303247721489640699240
y1 = 207534858442090452193999571026315995117

และพยายามที่จะหาจำนวนธรรมชาติดังกล่าวว่าn ข้อมูลเพิ่มเติมที่นี่n*(x0,y0) = (x1,y1)

ภาคผนวก

ก่อนอื่นต้องขอขอบคุณ @ El'endiaStarman ที่ยิ่งใหญ่สำหรับการตรวจสอบและแก้ไขร่างของฉัน!

ทำไมเส้นโค้งรูปไข่

มันอาจดูเหมือนสมการที่กำหนดเองบางอย่าง แต่ก็ไม่เป็นเช่นนั้นโดยทั่วไป: โดยทั่วไปแล้วเราพิจารณา "รูปทรง" ทางเรขาคณิตเหล่านั้นในแบบระนาบ projective (นั่นคือที่ "อนันต์" มาจากที่นั่นเราพิจารณาความเป็นเนื้อเดียวกันทั้งหมดชื่อพหุนามของระดับที่สาม (ระดับที่ต่ำกว่าหรือสูงกว่านั้นยากเกินไปหรือเพียงเล็กน้อยที่จะตรวจสอบ) หลังจากใช้ข้อ จำกัด บางอย่างเพื่อให้ได้คุณสมบัติที่ดีที่เราต้องการและหลังจาก dehomogenizing ชื่อพหุนามเหล่านั้น ) เราลงท้ายด้วยสมการเช่นy^2+a*x*y+b*y = x^3+c*x^2+d*x+eนี่เป็นรูปวงรีรูปไข่ในรูปแบบ Weierstrass ที่ยาว โดยทั่วไปแล้วสิ่งเหล่านี้เป็นเส้นโค้งแบบเดียวกับที่เราพิจารณา แต่ค่อนข้างเบ้ ด้วยการแปลงพิกัดเชิงเส้นคุณสามารถสร้างสมการ Weierstras สั้น ๆ ได้อย่างง่ายดาย ตัวอย่างซึ่งยังคงมีคุณสมบัติที่น่าสนใจทั้งหมด

ทำไมเราถึงแยกออกp=2,3?

สิ่งนี้เกี่ยวข้องกับความจริงที่ว่าสำหรับรูปแบบ Weierstrass สั้น ๆ นั้นเราจำเป็นต้องมีข้อ จำกัด4A^3+27B^2 ≠ 0เพื่อหลีกเลี่ยงภาวะเอกฐาน (เพิ่มเติมจากด้านล่าง) ในสาขาลักษณะ 2 เรามี4 = 0และในสาขาลักษณะ 3 เรามี27 = 0สิ่งนี้ทำให้เป็นไปไม่ได้ที่จะมีส่วนโค้งในรูปแบบ Weierstrass สั้น ๆ สำหรับสาขาเหล่านั้น

สิ่งแปลกประหลาดคืออะไร?

หากสมการ4A^3+27B^2=0ดำรงไว้เรามีเอกฐานดังต่อไปนี้ดังที่คุณเห็นจุดเหล่านั้นคุณไม่สามารถหาอนุพันธ์ได้ดังนั้นจึงไม่มีการแทนเจนต์ซึ่ง "ฆ่า" การดำเนินการ คุณอาจดูสมการy^2 = x^3หรือy^2 = x^3-3*x+2

ทำไมพวกเขาเรียกว่ารูปไข่โค้งหรือไม่?

เหตุผลก็คือสมการของรูปร่างนี้ปรากฏขึ้นในอินทิกรัลของไข่ซึ่งเป็นสิ่งที่คุณได้รับเมื่อคุณต้องการที่จะคำนวณเช่นความยาวของวงรี สไลด์โชว์สั้น ๆ เกี่ยวกับที่มาของชื่อ

พวกเขาต้องทำอะไรกับการเข้ารหัส?

มีวิธีการคำนวณnP = P+P+...+Pอย่างมีประสิทธิภาพมาก นี้สามารถนำมาใช้เช่นในการแลกเปลี่ยนคีย์ Diffie Hellman คณิตศาสตร์แบบแยกส่วนสามารถถูกแทนที่ด้วยการเพิ่มในกลุ่มย่อยแรงบิดเหล่านี้เป็นเพียงจุดบนเส้นโค้งที่มีลำดับที่แน่นอน (นั่นหมายความว่าmP = 0สำหรับบางคนmซึ่งโดยทั่วไปเป็นเพียงการคำนวณmod m)

คำตอบ:


4

Pyth, 105 100 ไบต์

A,@Q3eQ?qGZH?qHZG?&=YqhHhGqeG%_eHhQZ_m%dhQ,-*J?Y*+*3^hG2@Q1^*2eG-hQ2*-eGeH^-hGhH-hQ2-hGK--^J2hGhHeGK

การป้อนข้อมูลที่คาดว่าเป็น(p, A, P, Q)ที่PและQมีจุดสองจุดในรูปแบบ(x, y)หรือหากพวกเขาเป็นพิเศษจุดเช่นเดียวกับที่0 0คุณสามารถลองออนไลน์ที่นี่ สองตัวอย่างสุดท้ายแสดงให้เห็นว่าการ0ทำงานพิเศษนั้นอย่างไร

เพื่อประหยัดไม่กี่ไบต์ฉันจะใช้เฉพาะmod pคำตอบสุดท้าย ซึ่งหมายความว่าทำสิ่งต่าง ๆ ได้x0^pสองสามครั้งโดยไม่ต้องใช้การยกกำลังแบบแยกส่วนดังนั้นมันอาจช้ามาก

มันทำงานโดยทำตามลอจิกเดียวกับฟังก์ชัน Python:

def add_ellip(p, A, P, Q): # points are in format (x, y)
    z = 0 # representing special 0 point

    if (P == z):
        return Q
    if (Q == z):
        return P

    if P[0] == Q[0]:
        if (P == (Q[0], -Q[1] % p)):
            return z
        else:
            m = ((3*pow(P[0], 2, p) + A)*pow(2*P[1], p-2, p)) % p
    else:
        m = (P[1] - Q[1])*pow(P[0] - Q[0], p-2, p) % p

    x = (pow(m, 2, p) - P[0] - Q[0]) % p
    y = (m*(P[0] - x) - P[1]) % p
    return (x, y)

นี่คือการพึ่งพาอย่างหนักกับความจริงที่ว่าการผกผันการคูณแบบแยกส่วนของxมีค่าเท่ากับx^(p-2) mod pถ้าpเป็นนายก ดังนั้นเราสามารถคำนวณmความชันของเส้นโดยการค้นหาอินเวอร์สการคูณแบบแยกส่วนของตัวส่วนและคูณมันด้วยตัวเศษ ค่อนข้างมีประโยชน์ ฟังก์ชั่นหลามควรคำนวณขนาดใหญ่ปัญหาเล็ก ๆ น้อย ๆ powได้อย่างมีประสิทธิภาพมากขึ้นเพราะการใช้งานของ

ฉันยังใช้ทางลัดที่แสดงในหน้า Wikipedia ในหัวข้อนี้ มันค่อนข้างน่าสนใจที่ฉันจะลงเอยด้วยการใช้Aครั้งเดียวเท่านั้นและBไม่ได้ทั้งหมด

นอกจากนี้เพื่อความสนุกสนาน:

def pow2floor(x):
    p = 1
    x >>= 1
    while (x > 0):
        x >>= 1
        p <<= 1
    return p

def multi_nP(p, A, n, P):
    d = {}

    def rec_helper(n, P):
        if (n == 0):
            return (0, 0)
        elif (n == 1):
            return P
        elif (n in d):
            return d[n]
        else:
            p2f = pow2floor(n)
            remainder = n - p2f

            lower_half = rec_helper(p2f//2, P)
            d[p2f//2] = lower_half
            nP = add_ellip(p, A, lower_half, lower_half)

            if (remainder):
                nP = add_ellip(p, A, nP, rec_helper(remainder, P))

            d[n] = nP
            return nP

    return rec_helper(n, P)

multi_nPคำนวณฟังก์ชั่นn*Pสำหรับจำนวนเต็มรับและจุดn Pโดยจะใช้กลยุทธ์ recursive โดยแยกnออกเป็นสองส่วนp2fและremainderเช่นนั้นและว่าp2f + remainder = n จากนั้นเราเรียกใช้ฟังก์ชันอีกครั้งในส่วนที่เพิ่มผลด้วยp2f = 2^k ฉันยังใช้วิธีการเขียนโปรแกรมแบบไดนามิกขั้นพื้นฐานด้วยการประหยัดค่าคำนวณแล้วในบริการพจนานุกรมadd_ellipd

ฟังก์ชันถัดไปในทางทฤษฎีจะแก้ปัญหาโบนัสโดยใช้กลยุทธ์เดียวกัน:

def find_nPQ(p, A, P, Q): # P is input point, Q is what we're looking for
    d = {}
    found_Q = False

    def rec_helper(n, P):
        if (n == 0):
            return (0, 0)
        elif (n == 1):
            return P
        elif (n in d):
            return d[n]
        else:
            p2f = pow2floor(n)
            remainder = n - p2f

            lower_half = rec_helper(p2f//2, P)
            d[p2f//2] = lower_half

            nP = add_ellip(p, A, lower_half, lower_half)

            if (remainder):
                nP = add_ellip(p, A, nP, rec_helper(remainder, P))

            d[n] = nP
            return nP


    for x in range(p):
        xP = rec_helper(x, P)
        if (xP == Q):
            return x

น่าเสียดายที่มันไม่มีที่ไหนใกล้เร็วพอที่จะคำนวณได้ ฉันเดาว่าอาจมีวิธีที่มีประสิทธิภาพมากขึ้นในการทำเช่นนี้โดยเฉพาะอย่างยิ่งถ้าเราไม่ต้องทำซ้ำในทุกสิ่งที่เป็นไปnได้


เยี่ยมมากฉันไม่ได้คาดหวังกับคำตอบใด ๆ อีกต่อไปแล้ว =) คุณจัดการกับประเด็นที่ไม่มีที่สิ้นสุดได้อย่างไร? (โปรดทราบว่าy^2 = x^3 + xเป็นรูปวงรีรูปไข่ที่ถูกต้องและ(0,0) ≠ 0เป็นจุดบนเส้นโค้ง!)
ข้อบกพร่อง

เป็นคำถามที่ดี ... ฉันเดาว่าฉันไม่ได้จัดการ! : P ขอโทษด้วยฉันจำได้ว่าเห็นรูปแรกที่ไหนB = 0และสงสัยว่า0จะทำงานอย่างไร ... แล้วฉันก็ลืมไป ฉันคิดว่าฉันคิดว่าBคงไม่สามารถเป็น 0 ในบางช่วงเวลาเมื่อคืนนี้ คุณมีข้อเสนอแนะใด ๆ เกี่ยวกับสิ่งที่อินพุตควรเป็นเช่นนี้? บางทีถ้าเป็นB = 0เช่นนั้นกำหนด0 = (-1, -1)? ฉันมีความสุขที่จะปรับรหัสของฉันเพื่อจัดการกับมันฉันแค่คิดว่ามันจะดีถ้ามันเป็นมาตรฐานสำหรับการส่งอื่น ๆ เช่นกัน ...
Rhyzomatic

ฉันออกจากที่ไม่เปิดเพื่อให้การส่งสามารถใช้สิ่งที่สะดวกสำหรับพวกเขา แต่แน่นอนคุณอาจพูดได้ว่าเช่นทุกจุดจำกัดบนเส้นโค้งนั้นมีพิกัดไม่ติดลบและทุกสิ่งทุกอย่างก็ถือเป็นจุดอินฟินิตี้หรือบางอย่างที่คล้ายกัน หรือถ้ามันง่ายกว่าคุณอาจสมมติว่าอินพุต[0](พิกัดเดียวเท่านั้น) คือจุดอินฟินิตี้หรือสิ่งอื่นที่คล้ายคลึงกัน!
ข้อบกพร่อง

แจ้งให้เราทราบหากไม่สามารถจัดการได้ดีพอ และขอบคุณที่ช่วยฉัน 5 ไบต์ได้จริง!
Rhyzomatic

@ flawr คุณจะสามารถบอกฉันได้หรือไม่ว่าฉันอยู่ในแนวทางที่ถูกต้องในการคำนวณอย่างมีประสิทธิภาพnP? คุณช่วยชี้ให้ฉันดูทรัพยากรใด ๆ ในหัวข้อเพื่อทำให้ความคิดไหลเวียนได้หรือไม่? ฉันมีปัญหาในการหาสิ่งที่ Googling รอบ ขอบคุณ!
Rhyzomatic

0

Python 3, 193 191 ไบต์

วิธีการแก้ปัญหาขึ้นอยู่กับคำตอบ Pyth ของ Rhyzomaticและตรรกะ Python ของพวกเขา โดยเฉพาะอย่างยิ่ง. ฉันชอบวิธีที่พวกเขาพบรากที่สามของ monic ลูกบาศก์พหุนามx^3 + bx^2 + cx + dเมื่อคุณมีสองรากx_1และx_2โดยการสังเกตb == x_1 + x_2 + x_3และลบตามนั้น ฉันวางแผนที่จะเพิ่มคำอธิบายตีกอล์ฟนี้และอาจจะเปลี่ยนเป็น Ruby หากทับทิมกลายเป็นสั้นกว่า

def e(p,A,B,P,Q):
 if P==0:return Q
 if Q==0:return P
 f,g=P;j,k=Q
 if f==j:
  if g==-k%p:return 0
  m=(3*f*f+A)*pow(2*j,p-2)
 else:m=(g-k)*pow(f-j,p-2)
 x=m*m-f-j;y=m*(f-x)-g;return(x%p,y%p)

Ungolfing:

def elliptic_curve_addition(p, A, B, P, Q):
    if P == 0:
        return Q
    if Q == 0:
        return P
    f,q = P
    j,k = Q
    if f==j:
        if g == (-k) % p:
            return 0
        m = (3 * f**2 + A) * pow(2*j, p-2)
    else:
        m = (g-k) * pow(f-j, p-2)
    x = m**2 - f - j
    y = m * (f-x) - g
    return (x%p, y%p)

ฉันประหลาดใจที่ Python นั้นมีค่าน้อยกว่าสองเท่าของคำตอบของ Pyth!
ข้อผิดพลาด
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.