Trình tạo mật khẩu XKCD


34

Giới thiệu

Rõ ràng, câu hỏi này đã được hỏi ở đây và nó không may đóng cửa. Tôi nghĩ rằng đó là một ý tưởng tốt để thử lại với nó, nhưng thực hiện đúng.

XKCD xem xét cách chúng tôi được đào tạo để sử dụng "mật khẩu khó nhớ", nghĩ rằng nó an toàn, nhưng thay vào đó, sẽ mất 3 ngày để máy tính bị bẻ khóa. Mặt khác, việc ghi nhớ 4-5 từ mang lại Intropy Password của Kuan và rất dễ nhớ. Điên thế nào mà hoạt động hả?

Thử thách

Công việc hôm nay là tạo 5 mật khẩu bằng từ. 4 từ cho mỗi mật khẩu và tối thiểu 4 chữ cái cho mỗi từ, nhưng không có tối đa. Mật khẩu Intropy của Kuan sẽ cần được tính cho mọi mật khẩu, nhưng mức tối thiểu bắt buộc sẽ không được đặt.

Mật khẩu của Kuan là gì?

Kuan's Password Intropy là một phép đo về mức độ khó đoán của mật khẩu, theo Kuan. Có một phép tính đơn giản: E = log 2 (R) * L . E là Intropy Password của Kuan, R là phạm vi các ký tự có sẵn và L cho độ dài mật khẩu.

Phạm vi của các nhân vật có sẵn là tự giải thích. Đó là phạm vi các ký tự mà mật khẩu có thể có, trong trường hợp này là chữ hoa và chữ thường. Vì có 26 ký tự trong bảng chữ cái, 26 x 2 = 52 ký tự trong toàn bộ phạm vi của mật khẩu.

Độ dài mật khẩu cũng tự giải thích. Đó là tổng chiều dài của mật khẩu sau khi tạo.

Những ràng buộc

  • Không có đầu vào.
  • Một từ không thể xuất hiện lại trong cùng một mật khẩu.
  • Không có ký hiệu hoặc số được cho phép trong một mật khẩu.
  • 4 từ cho mỗi mật khẩu, nhưng tối thiểu bắt buộc là 4 chữ cái cho mỗi từ.
  • Không có khoảng cách giữa các từ.
  • Bạn không thể tạo cùng một mật khẩu nhiều lần.
  • Mỗi từ phải được viết hoa trong một mật khẩu.
  • Đầu ra phải có thể đọc được, phải được đặt cách nhau. Cũng phải bao gồm Mật khẩu mật khẩu của Kuan với mật khẩu sử dụng phương trình Mật khẩu mật khẩu của Kuan ở trên.
  • Từ điển . Bạn phải sử dụng nó, tải xuống dưới dạng tệp văn bản và tích hợp tương ứng. Đây sẽ là danh sách mà bạn lấy từ. Mã của bạn nên giả sử nó có sẵn.
  • Đây là , byte ngắn nhất giành chiến thắng.

Đầu ra

TriedScarProgressPopulation 153.9
TryingPastOnesPutting 119.7
YearnGasesDeerGiven 108.3
DoubtFeetSomebodyCreature 142.5
LiquidSureDreamCatch 114.0

16
Đối với các trường hợp thử nghiệm, tại sao entropy mật khẩu khác nhau? Tất cả 4 mật khẩu từ được tạo từ cùng một từ điển sẽ có cùng một entropy.
Phi tuyến

20
Entropy mật khẩu phụ thuộc vào bộ ký hiệu. Nếu mật khẩu của bạn là Nký hiệu từ tập hợp S, thì entropy mật khẩu là log2(|S|)*N. Ở đây kích thước của bộ ký hiệu là kích thước của từ điển ( |S|=4284) và số lượng ký hiệu là số lượng từ ( N=4), vì vậy entropy cho mỗi mật khẩu là 48.3.
Phi tuyến

48
Định nghĩa về entropy là sai lầm nguy hiểm! Nếu mỗi ký tự được chọn ngẫu nhiên từ một tập hợp kích thước R, thì thực sự mật khẩu L có độ dài R ^ L, vì vậy entropy là nhật ký của điều đó: log₂ (R ^ L) = log₂ (R) * L đó là công thức của bạn. Tuy nhiên, nếu mật khẩu được chọn ngẫu nhiên từ một bộ khác (ví dụ: bạn sẽ không bao giờ có mật khẩu như vậy 3t1ta#asd), thì entropy sẽ là logarit của số lượng mật khẩu có thể. Nếu bạn luôn chọn ngẫu nhiên 4 từ một cách ngẫu nhiên từ một từ điển 4284 từ, thì có tới 4304 ^ 4 mật khẩu, mỗi mật khẩu có entropy log 428 (4284) * 4 48,26.
ShreevatsaR

5
Đối với bản ghi, loại mật khẩu này có trước truyện tranh XKCD. Chúng được gọi là mật khẩu "xúc xắc".
dùng2428118

5
Ngoài vấn đề về các từ có ít entropy hơn các ký tự ngẫu nhiên, câu hỏi của bạn yêu cầu các từ đó được viết hoa, nghĩa là trường hợp này được cố định và không thể được tính cho entropy.
Niet the Dark Tuyệt đối

Câu trả lời:


13

Python 2, 102 101 97 91 byte

from random import*
exec"x=''.join(x.title()for x in sample(f,4));print(x,57*len(x)/10);"*5

Giả sử từ điển như một danh sách có tên f.

Có thể được kiểm tra bằng cách lưu tệp dưới dạng dict.txtvà gọi

f = open('dict.txt').readlines()

Danh sách Python không có phương thức xáo trộn và bạn có thể lưu hai byte trong Python 2 bằng cách xóa dấu ngoặc đơn xung quanh exec( execlà một từ khóa trong Python 2).
Konrad Borowski

@xfix Vâng, đúng vậy shuffle(f);.
Jonathan Allan

Rất tiếc, sửa lỗi đó
càng sớm

4
Bạn có thể sử dụng mẹo của tôi để lưu ý rằng làm tròn ở mức 5,7 đến 1 chữ số thập phân miễn là các lỗi dấu phẩy động không được giới thiệu và lưu năm byte với 57*len(x)/10.. Lưu một byte khác bằng cách loại bỏ các dấu ngoặc đơn làm cho bản in mất một tuple. Đây là phiên bản rút gọn: TIO
Jonathan Allan

Sử dụng sample(f,4)thay vì shuffle. Cũng fcó thể là open('dict.txt').read().split('\n'), open('dict.txt').readlines()hoặc chỉ open('dict.txt')(tôi biết rằng nó không phải là golf mà vẫn còn).
Hội trường Alex

10

PowerShell (3.0+), 77 byte

1..5|%{($p=-join($d|random -c 4|%{culture|% te*|% tot* $_}));57*$p.Length/10}

Hãy thử trực tuyến!

Sử dụng mánh khóe của Jonathan Allan57*len/10 .

$dchứa từ điển như một mảng các từ. Nếu bạn đang chơi ở nhà và muốn điền vào $d:

$d=-split(irm pastebin.com/raw/eMRSQ4u2)

Sử dụng một phiên bản golf (Get-Culture).TextInfo.ToTitleCase()để viết hoa chữ cái đầu tiên; Tôi không nghĩ có cách nào ngắn hơn để làm điều đó trong PowerShell.

Phần còn lại là khá đơn giản tôi nghĩ.

Liên kết TIO có toàn bộ từ điển; vô hiệu hóa bộ nhớ cache và đi hạt!


Ai đó có thể chỉ cho tôi một tài liệu tham khảo cho "trò lừa 57 * len / 10 của Jonathan Allan" không?
James Curran

@JamesCurran Xem phân tích câu trả lời của anh ấy ở đây , và cả nhận xét của anh ấy về câu trả lời này .
briantist

Điều này sẽ không hoạt động trong 2.0 chính xác. Điều đó cần được lưu ý trong tiêu đề. Tôi cũng nghĩ rằng bạn cần phải đọc $dnhư giả định rằng nó có mặt trong môi trường. (gc d)| random..trong đó từ điển là một tệp gọi là d trong cùng thư mục.
Matt

1
@Matt trên SO Tôi có thể tìm cách trả lời để làm việc với v2 (hoặc làm 2 phiên bản), nhưng đây là mã golf! Càng nhiều càng tốt ;-p
briantist

1
Tôi chỉ cố gắng để lưu byte trong tiêu đề câu trả lời của tôi.
Matt

7

Thạch , 22 byte

Ẋḣ4ŒtFµL×57÷⁵⁸,K
çЀ5Y

Một liên kết đơn âm lấy danh sách các danh sách các ký tự, từ điển được phân tích cú pháp (như được phép trong trò chuyện ).

Hãy thử trực tuyến! (Nhấp vào "Đối số" để ẩn từ điển và giảm nhu cầu cuộn.)

Làm sao?

Vì từ điển chỉ chứa các từ hợp lệ (4 ký tự trở lên, chỉ [a-z]), nên không cần kiểm tra điều kiện này.

Vì tất cả các từ trong từ điển đều có độ dài trong độ dài [4-8]mật khẩu có thể có [16,32], và các entropi có thể sẽ không bao giờ làm tròn khác nhau đến một chữ số thập phân so với thay thế log(52,2)bằng 5.7. Vấn đề duy nhất là sử dụng một giá trị dấu chấm động của 5.7sẽ cho dấu chấm tròn lỗi cho chiều dài 18, 2631. Tuy nhiên, nhân với 57sau đó chia bằng 10cách sử dụng ×57÷⁵để tránh điều này (trong khi vẫn ngắn hơn một byte so với in giá trị chính xác của dấu phẩy động đầy đủ bằng cách sử dụng ×52l2¤).

çЀ5Y - Main link: list of list of characters (the parsed dictionary)
   5  - literal 5
 Ѐ   - map across the implicit range [1,2,3,4,5]:
ç     -   last link (1) as a dyad
    Y - join with newlines
      - implicit print

Ẋḣ4ŒtFµL×57÷⁵⁸,K - Link 1, get password and entropy: list of lists of characters, number
Ẋ                - shuffle the list of lists (shuffle all the words)
 ḣ4              - head to 4 (the first four words)
   Œt            - title case (make the first letter of each uppercase)
     F           - flatten into one list of characters
      µ          - monadic chain separation, call that p
       L         - length of p
         57      - 57
        ×        - multiply
            ⁵    - 10
           ÷     - divide -> entropy to 1 decimal place
             ⁸   - link's left argument, p
              ,  - pair -> [p, entropy]
               K - join with (a) space(s)

5

Ruby, 89 83 byte

d.select!{|w|w[3]}
5.times{p w=d.sample(4).map(&:capitalize)*'',5.700439718*w.size}

Giả sử rằng mật khẩu được lưu trữ trong biến d. Bạn có thể thêm dòng này trước mã:

d=$<.map(&:chomp)

và gọi kịch bản ví dụ như thế này:

$ ruby generate_passwords.rb < dictionary_file.txt

Đầu ra mẫu:

"MarginStarvedOnusInsulted"
142.51099295
"KitchenMiseryLurkJoints"
131.110113514
"InducesNotablePitfallsPrecede"
165.312751822
"FarmersAbortFutileWrapper"
142.51099295
"RoutesBishopGlowFaithful"
136.81055323200002

KitchenMiseryLurkJoints ... wow.


-6 byte từ Ajedi32


1
Có thể có thể lưu một vài byte bằng cách loại bỏ shuffle!và thay thế popbằng sample.
Ajedi32

@ Ajedi32 ơi, bạn nói đúng! Tôi thực sự đã nghĩ về nó, nhưng tôi đã đọc sai quy tắc này A word cannot reappear in the same password, nghĩ rằng nó không có nghĩa là sử dụng lại các từ trên tất cả các mật khẩu. Cảm ơn :)
daniero

4

Toán học, 178 byte

t=1;l=Length;While[t<6,s=RandomChoice[Import["https://pastebin.com/raw/eMRSQ4u2"],4];c=Capitalize/@s;f=Flatten@Characters[c];Print[StringJoin[c]," ",Log[2,l@Union@f]*l@f//N];t++]

Dùng thử trực tuyến

sao chép và dán bằng ctrl-v và nhấn shift + enter để chạy


Toán học, 136 byte

giả sử rằng m là từ điển của mã

m=ImportString[Import["C:\a.txt"]]

.

t=1;l=Length;While[t<6,s=RandomChoice[m,4];c=Capitalize/@s;f=Flatten@Characters[c];Print[StringJoin[c]," ",Log[2,l@Union@f]*l@f//N];t++]

"Công việc hôm nay là tạo 5 mật khẩu bằng từ." Cần 5 thay vì một mật khẩu.
KuanHulio

ok ... 5 mật khẩu .. đã sửa ..
J42161217

Tại sao bạn không tạo từ điển có sẵn cục bộ để rút ngắn mã bằng cách tránh văn bản siêu liên kết?
sergiol

để dễ dàng cho bạn kiểm tra nó ...
J42161217

Tốt nhất là cung cấp mã trình trợ giúp đơn giản, không có mã hóa để dễ dàng kiểm tra hơn là gửi golf của bạn ít hơn để nó được khép kín. Ngoài ra, từ điển được coi là biến mà không chiếm quyền điều khiển máy chủ DNS cục bộ (hoặc sửa đổihosts tệp).
wizzwizz4

4

Bash ,66 65 byte

for w in `shuf -n4 -`;{((l+=${#w}));printf ${w^};};bc<<<$l*5.7004

Hãy thử trực tuyến!

Từ điển được đọc lại bởi STDIN. Xáo trộn tất cả các từ trong từ điển và đầu ra 4 đầu tiên.

Đối với mỗi từ, cộng chiều dài của nó trong var l và lặp lại từ viết hoa. Cuối cùng gọi bc để làm toán.

Giải pháp Awk, 112 byte, bốn mật khẩu:

shuf -n16 -|xargs -n4|awk '{for(i=1;i<5;i++)printf toupper(substr($i,1,1))substr($i,2);print(length($0)-3)*5.7}'

3

(Đây là bản phóng tác của câu trả lời của Martmists, nhưng tôi không có đại diện để bình luận)

Con trăn, 88 86 byte

g={*f}
exec('x="".join(g.pop().title()for i in "a"*4);print(x,len(x)*5.700439718);'*5)

Bằng cách khai thác như thế nào setlà không đặc biệt, bạn có thể tránh phải nhập bất kỳ thư viện ngẫu nhiên nào.


Điều này liên tục tạo ra cùng một đầu ra cho tôi. Nếu nó hoạt động trên một số triển khai thì bạn có thể tiết kiệm một vài byte bằng cách thực hiện set(f).pop().
Jonathan Allan

1
Tôi không nghĩ rằng điều này thực sự hợp lệ. Nó không mang tính quyết định, do đó không đảm bảo tạo cùng một mật khẩu, nhưng trong thực tế, nó sẽ hiếm khi tạo ra các kết quả khác nhau.
DJMcMayhem

Tôi nghi ngờ nó có thể phụ thuộc vào việc thực hiện. Tôi đã làm điều đó trên một bản phát hành Windows Anaconda Python 3 mới cài đặt và nó đã hoạt động. Tuy nhiên set(f).pop()không hiệu quả, tôi đã thử nó. Nó cho kết quả tương tự mỗi lần.
dain

"Trên thực tế nó sẽ hiếm khi tạo ra kết quả khác nhau" - đó dường như đối với tôi, đây là một ví dụ: pastebin.com/raw/ZHiHgzxV
dain

@dain Tôi tò mò. Vui lòng cung cấp thông tin về bản dựng Python của bạn.
wizzwizz4

3

Japt , 30 byte

5Ç[V=Uö4 ®g u +Zt1ìMm52 *Vl]¸

Hãy thử trực tuyến!


Tốt đẹp! Nhưng thật không may, nó tạo cùng một mật khẩu 5 lần và nó sẽ khác nhau mỗi lần ..
Iain Ward

Đây có thể là 30 ký tự, nhưng ít nhất là trong UTF-8, hệ thống của tôi đồng hồ ở mức 35 byte.
CVn

1
@ MichaelKjorling Japt sử dụng ISO 8859-1, không phải UTF-8.
Dennis

@Dennis Thú vị. Cảm ơn bạn.
một CVn

3

JavaScript (ES6), 164 byte

d=>{for(i=5;i--;)console.log(p="....".replace(/./g,_=>(w=d.splice(Math.random()*d.length|0,1)[0])[0].toUpperCase()+w.slice(1)),(Math.log2(52)*p.length).toFixed(1))}

Giả sử từ điển được truyền cho hàm dưới dạng một mảng.

Kiểm tra đoạn trích


2

Toán học, 71 byte

Giả sử từ điển đã được tải vào một mảng được gọi là d.

Table[{#,Log[2,52]StringLength[#]}&[""<>Capitalize@d~RandomSample~4],5]

Giải thích:

                                        Capitalize@d                    - Capitalize all the dictionary
                                                    ~RandomSample~4     - make an array with 4 values. By default values can not repeat.
                                    ""<>                                - Concatenate with empty string to turn array into single string.
      {#,Log[2,52]StringLength[#]}&[                               ]    - Put current string next to log(2,52) times length of current string
Table[                                                              ,5] - Repeat this 5 times.

Còn số entropy thì sao?!
Jonathan Allan

Rất tiếc đã bỏ lỡ bit đó. Cập nhật.
Ian Miller

2

ColdFusion 216 byte

p={};z=arrayLen(c);for(x=0;x<5;x++){pw="";r={};while(structCount(r)<4){n=RandRange(1,z);r.append({"#c[n]#":true});}for(w in structKeyList(r)){pw&=REReplace(w,"\b(\w)","\u\1","All");};p.append({"#pw#":57*len(pw)/10})}

Điều này hoạt động trong ColdFusion 11+ và Lucee 4.5+

Để chạy nó: https://trycf.com/gist/ff14e2b27d66f28ff69ab90365361b12/acf11?theme=monokai

Liên kết TryCF có ít golf-ish nhưng cùng mã.

Tôi đã không thực sự mong đợi có một câu trả lời golf cạnh tranh; Tôi chỉ muốn xem những gì sẽ cần để hoàn thành thử thách này trong ColdFusion. Đặc biệt là vì không có nhiều CF trong những câu trả lời này. :-) Sau khi thiết lập, nó ngắn hơn đáng ngạc nhiên so với tôi dự kiến.

Nỗ lực đầu tiên của tôi ngắn hơn một chút cho đến khi tôi nhớ rằng cùng một từ không thể được sử dụng nhiều lần. Mặc dù rất khó có khả năng bộ tạo ngẫu nhiên sẽ chọn cùng một chỉ mục nhiều lần, tôi đổ các chỉ mục vào các khóa của cấu trúc, điều này sẽ ngăn ngừa sự trùng lặp. Sau đó, tôi sử dụng danh sách các khóa đó để xây dựng chuỗi mật khẩu cuối cùng của mình. Tôi cũng đã sử dụng thủ thuật toán học để tìm entropy.


2

PHP , 136 129 byte

-7 byte, cảm ơn Jörg

for(shuffle($a);$i++<5;){for($s='',$c=0;$c<4;)strlen($w=$a[$k++])<4?:$s.=ucfirst($w).!++$c;echo$s.' '.log(52, 2)*strlen($s)."
";}

Hãy thử trực tuyến!


@ JörgHülsermann Điều đó có vẻ hiệu quả, cảm ơn.
TÔI

2

Python 3, 252 byte

Đây là thử thách chơi gôn đầu tiên của tôi. Tôi biết có những câu trả lời Python khác ở đây (có lẽ tốt hơn của tôi) nhưng điều này có vẻ vui, và vì vậy tôi muốn thử dù sao đi nữa. Đây là phiên bản chơi gôn:

import random, math
with open("d") as f: d=f.read()
l=d.split()
for a in range(5):
 u=[]
 p=""
 for b in range(4):
  w=random.choice([w for w in l if not w in u and len(w)>=4])
  u.append(w)
  w=w.title()
  p+=w
 print("%s %s"%(p,math.log2(52)*len(p)))

Tôi sẽ đăng một thử nó trực tuyến! liên kết, nhưng điều đó không hỗ trợ nhiều tập tin. Vì vậy, đây là một liên kết repl.it: https://repl.it/InIl/0

Ngoài ra, đây là phiên bản chưa được chỉnh sửa:

import random
import math
with open("d") as f:
    dictionary = f.read() #this is the dictionary text file, simply saved as "d" as to use as few bytes as possible
words = dictionary.split() #here we turn that dictionary string into a list
for a in range(5): #here we iterate through 5 passwords
    used_words = []
    password = ""
    for b in range(4): #here we iterate through the 4 words in each password
        word = ""
        word = random.choice([word for word in words if not word in used_words and len(word) >= 4]) #Thanks to blackadder1337 from #python on freenode IRC for helping me with this.
        used_words.append(word)
        word = word.title()
        password = password + word
    print("%s %s"%(password, math.log2(52) * len(password)))

Như tôi đã nói, đây là lần đầu tiên tôi viết mã, vì vậy tôi chắc chắn rằng điều này có thể được cải thiện rất nhiều.


Chào mừng đến với PPCG!
Taylor Scott

2

tcl, 137

Không phải là một người chiến thắng chắc chắn, nhưng tôi nghĩ rằng nó có thể là một golf nhiều hơn một chút.

time {set p "";time {set p [string totitle [lindex $d [expr int(rand()*[llength $d])]]]$p} 4;puts $p\ [expr 5.7004*[string length $p]]} 5

bản demo - Mục đích của dòng 1 chỉ là đưa nội dung từ điển vào biếnd


Bạn có thể đánh golf xuống vì mật khẩu yêu cầu 4 từ thay vì 5.
KuanHulio

Và bạn đã yêu cầu 5 mật khẩu thay vì 4. LOL! Tôi không khớp các con số!
huyết thanh

Hahaha! @sergiol
KuanHulio

Đã sửa! @KuanHulio
sergiol

Cái đó tốt hơn. Công việc tốt.
KuanHulio

0

Vim, 87 tổ hợp phím

qq:r!echo "$RANDOM"l<CR>D:w o|e w<CR>@"ev4bd:w|bp<CR>p0~wX~wX~wX~Y:.!wc -c<CR>A*5.7003<Esc>:.!bc<CR>PJq4@q

Giả sử rằng từ điển là trong một tập tin có tên w. Sẽ luôn sử dụng 4 từ liên tiếp

Giải thích:

qq                       Start recording a macro named 'q'
:r!echo "$RANDOM"l<CR>   Append the result of the shell command `echo "$RANDOM"l`
D                        Delete what you just appended
:w o|                    Save the buffer to the file 'o' and ..
e w<CR>                  Open the file 'w'
@"                       Execute the text we deleted as a normal-mode command
                         This will move the cursor a random number of characters
                         to the right
e                        Go to the end of the next word
v4bd                     Delete 4 words backwards
:w|                      Save the file and ..
bp<CR>                   Open the last buffer (the 'o' file)
p                        Paste the 4 words we deleted
0                        Move the cursor to the beginning of the line
~wX~wX~wX~               Remove the spaces between the words and capitalize
Y                        Copy current line
:.!wc -c<CR>             Pipe the current line through 'wc -c'
A*5.7003<Esc>            Append "*5.7003" to the end of the line
:.!bc<CR>                Pipe the current line through 'bc'
P                        Paste the password above the current line
J                        Join with line bellow
q                        Stop recording the 'q' macro
4@q                      Run the 'q' macro 4 times

0

q / kdb +, 76 74 65 56 byte

Dung dịch:

{(x;5.70044*(#)x)}(,/)@[;0;upper]each -4?" "vs(*)(0:)`:w

Thí dụ:

q){(x;5.70044*(#)x)}(,/)@[;0;upper]each -4?" "vs(*)(0:)`:w
"RulingOverheadSaddensPriest"
153.9119

Giải trình:

Đọc trong danh sách từ, tách ra trên "", chọn 4 từ ngẫu nhiên từ danh sách này, viết hoa chữ cái đầu tiên của mỗi từ, sau đó nối lại với nhau. Đưa hàm này vào hàm lambda trả về mật khẩu và 'entropy' được tính toán:

                                                     `:w / the wordlist is a file called 'w'
                                                 (0:)    / read in the file list (\n separated list)
                                              (*)        / take first (and only) item in the list
                                         " "vs           / split this on " "
                                      -4?                / take 4 random items from this list, neg means 'dont put back'
                      @[; ;     ]                        / apply a function to variable at indices (variable is implicit)
                           upper                         / uppercase (the function being applied)
                         0                               / index 0, the first character
                                 each                    / each of the 4 random items
                  (,/)                                   / 'raze' (flatten lists)
{                }                                       / anonymous lambda function
 (x;            )                                        / a 2-item list, x is first item
            (#)x                                         / count x, return the length of the list
    5.70044*                                             / multiply by 5.70044

Ghi chú:

Tôi đã trích dẫn và sử dụng 5.70044 thay vì 2 xlog 52 xexp...

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.