Cara hash kata sandi yang panjang (> 72 karakter) dengan blowfish


91

Minggu terakhir saya membaca banyak artikel tentang hashing kata sandi dan Blowfish tampaknya menjadi (salah satu) algoritme hashing terbaik saat ini - tetapi bukan itu topik dari pertanyaan ini!

Batas 72 karakter

Blowfish hanya mempertimbangkan 72 karakter pertama dalam kata sandi yang dimasukkan:

<?php
$password = "Wow. This is a super secret and super, super long password. Let's add some special ch4r4ct3rs a#d everything is fine :)";
$hash = password_hash($password, PASSWORD_BCRYPT);
var_dump($password);

$input = substr($password, 0, 72);
var_dump($input);

var_dump(password_verify($input, $hash));
?>

Outputnya adalah:

string(119) "Wow. This is a super secret and super, super long password. Let's add some special ch4r4ct3rs a#d everything is fine :)"
string(72) "Wow. This is a super secret and super, super long password. Let's add so"
bool(true)

Seperti yang Anda lihat, hanya 72 karakter pertama yang penting. Twitter menggunakan blowfish alias bcrypt untuk menyimpan kata sandi mereka ( https://shouldichangemypassword.com/twitter-hacked.php ) dan coba tebak: ubah kata sandi twitter Anda menjadi kata sandi panjang dengan lebih dari 72 karakter dan Anda dapat masuk ke akun Anda dengan memasukkan hanya 72 karakter pertama.

Blowfish dan Pepper

Ada banyak pendapat berbeda tentang kata sandi "membumbui". Beberapa orang mengatakan itu tidak perlu, karena Anda harus berasumsi bahwa string merica rahasia juga diketahui / diterbitkan sehingga tidak meningkatkan hash. Saya memiliki server database terpisah sehingga sangat mungkin hanya database yang bocor dan bukan lada konstan.

Dalam hal ini (lada tidak bocor) Anda membuat serangan berdasarkan kamus lebih sulit (koreksi saya jika ini tidak benar). Jika benang merica Anda juga bocor: tidak terlalu buruk - Anda masih memiliki garam dan itu terlindungi sebaik hash tanpa merica.

Jadi saya pikir membumbui kata sandi setidaknya bukanlah pilihan yang buruk.

Saran

Saran saya untuk mendapatkan hash Blowfish untuk kata sandi dengan lebih dari 72 karakter (dan merica) adalah:

<?php
$pepper = "foIwUVmkKGrGucNJMOkxkvcQ79iPNzP5OKlbIdGPCMTjJcDYnR";

// Generate Hash
$password = "Wow. This is a super secret and super, super long password. Let's add some special ch4r4ct3rs a#d everything is fine :)";
$password_peppered = hash_hmac('sha256', $password, $pepper);
$hash = password_hash($password_peppered, PASSWORD_BCRYPT);

// Check
$input = substr($password, 0, 72);
$input_peppered = hash_hmac('sha256', $input, $pepper);

var_dump(password_verify($input_peppered, $hash));
?>

Ini didasarkan pada pertanyaan ini : password_verifykembali false.

Pertanyaan

Apa cara yang lebih aman? Mendapatkan hash SHA-256 terlebih dahulu (yang mengembalikan 64 karakter) atau hanya mempertimbangkan 72 karakter pertama dari kata sandi?

Pro

  • Pengguna tidak dapat masuk hanya dengan memasukkan 72 karakter pertama
  • Anda dapat menambahkan lada tanpa melebihi batas karakter
  • Output dari hash_hmac mungkin akan memiliki lebih banyak entropi daripada kata sandi itu sendiri
  • Kata sandi di-hash oleh dua fungsi berbeda

Kontra

  • Hanya 64 karakter yang digunakan untuk membuat hash blowfish


Sunting 1: Pertanyaan ini hanya membahas integrasi PHP dari blowfish / bcrypt. Terima kasih atas komentarnya!


3
Blowfish bukan satu-satunya yang memotong kata sandi, menyesatkan orang untuk berpikir itu lebih aman daripada yang sebenarnya. Berikut sejarah menarik tentang batas 8 karakter.
DOK

2
Apakah pemotongan 72 karakter mendasar untuk algoritma Blowfish, atau hanya implementasi PHP? IIRC Blowfish juga digunakan pada (setidaknya beberapa) 'nix untuk mengenkripsi kata sandi pengguna.
Douglas B.Staple

3
Masalahnya ada pada Bcrypt, bukan Blowfish. Saya dapat mereproduksi masalah ini hanya dengan Python dan Bcrypt.
Blender

@ Blender: Terima kasih atas komentar Anda dan pekerjaan Anda di dalamnya. Saya tidak dapat menemukan fungsi yang berbeda di php untuk blowfish dan bcrypt dan meskipun keduanya sama. Tapi apakah tidak ada bedanya bagi saya di php? Saya lebih suka menggunakan fungsi php standar.
Frederik Kammer

1
Lihat juga kerangka kerja hashing kata sandi PHP Openwall (PHPass). Ini portabel dan diperkuat terhadap sejumlah serangan umum terhadap kata sandi pengguna. Orang yang menulis kerangka kerja (SolarDesigner) adalah orang yang sama yang menulis John The Ripper dan duduk sebagai juri di Kompetisi Hashing Kata Sandi . Jadi dia tahu satu atau dua hal tentang serangan terhadap password.
jww

Jawaban:


137

Masalahnya di sini pada dasarnya adalah masalah entropi. Jadi mari kita mulai mencari di sana:

Entropi Per Karakter

Jumlah bit entropi per byte adalah:

  • Karakter Hex
    • Bit: 4
    • Nilai: 16
    • Entropi Dalam 72 Karakter: 288 bit
  • Alpha-Numeric
    • Bit: 6
    • Nilai: 62
    • Entropi Dalam 72 Karakter: 432 bit
  • Simbol "Umum"
    • Bit: 6.5
    • Nilai: 94
    • Entropi Dalam 72 Karakter: 468 bit
  • Byte Penuh
    • Bit: 8
    • Nilai: 255
    • Entropi Dalam 72 Karakter: 576 bit

Jadi, bagaimana kita bertindak tergantung pada tipe karakter yang kita harapkan.

Masalah Pertama

Masalah pertama dengan kode Anda, adalah langkah hash "pepper" Anda mengeluarkan karakter hex (karena parameter keempat ke hash_hmac()tidak disetel).

Oleh karena itu, dengan memasukkan lada Anda, Anda secara efektif memotong entropi maksimum yang tersedia untuk kata sandi dengan faktor 2 (dari 576 hingga 288 bit yang mungkin ).

Masalah Kedua

Namun, sha256hanya menyediakan 256bit entropi di tempat pertama. Jadi Anda secara efektif memotong kemungkinan 576 bit menjadi 256 bit. Langkah hash Anda * segera *, menurut definisi, kehilangan setidaknya 50% dari kemungkinan entropi dalam kata sandi.

Anda dapat menyelesaikan sebagian ini dengan beralih ke SHA512, di mana Anda hanya akan mengurangi entropi yang tersedia sekitar 12%. Tapi itu masih perbedaan yang tidak signifikan. 12% itu mengurangi jumlah permutasi dengan faktor 1.8e19. Itu angka yang besar ... Dan itulah faktor yang dikuranginya dengan ...

Masalah yang Mendasari

Masalah yang mendasarinya adalah ada tiga jenis kata sandi dengan lebih dari 72 karakter. Dampak sistem gaya ini terhadap mereka akan sangat berbeda:

Catatan: mulai saat ini saya mengasumsikan kami membandingkan dengan sistem lada yang digunakan SHA512dengan keluaran mentah (bukan hex).

  • Kata sandi acak dengan entropi tinggi

    Ini adalah pengguna Anda yang menggunakan generator kata sandi yang menghasilkan kunci besar untuk kata sandi. Mereka acak (dihasilkan, bukan dipilih manusia), dan memiliki entropi tinggi per karakter. Tipe ini menggunakan byte tinggi (karakter> 127) dan beberapa karakter kontrol.

    Untuk grup ini, fungsi hashing Anda akan secara signifikan mengurangi entropi yang tersedia menjadi bcrypt.

    Biar saya katakan lagi. Untuk pengguna yang menggunakan entropi tinggi, sandi panjang, solusi Anda secara signifikan mengurangi kekuatan sandi mereka dengan jumlah yang dapat diukur. (62 bit entropi hilang untuk sandi 72 karakter, dan lebih banyak lagi untuk sandi yang lebih panjang)

  • Kata sandi acak entropi sedang

    Grup ini menggunakan sandi yang berisi simbol umum, tetapi tidak ada byte tinggi atau karakter kontrol. Ini adalah kata sandi Anda yang dapat diketik.

    Untuk grup ini, Anda akan membuka sedikit lebih banyak entropi (bukan membuatnya, tetapi mengizinkan lebih banyak entropi untuk masuk ke dalam kata sandi bcrypt). Ketika saya mengatakan sedikit, maksud saya sedikit. Impas terjadi ketika Anda memaksimalkan 512 bit yang dimiliki SHA512. Oleh karena itu, puncaknya ada pada 78 karakter.

    Biar saya katakan lagi. Untuk kelas kata sandi ini, Anda hanya dapat menyimpan 6 karakter tambahan sebelum Anda kehabisan entropi.

  • Kata sandi non-acak entropi rendah

    Ini adalah grup yang menggunakan karakter alfanumerik yang mungkin tidak dibuat secara acak. Sesuatu seperti kutipan Alkitab atau semacamnya. Frase ini memiliki sekitar 2,3 bit entropi per karakter.

    Untuk grup ini, Anda dapat membuka lebih banyak entropi secara signifikan (bukan membuatnya, tetapi mengizinkan lebih banyak untuk dimasukkan ke dalam input kata sandi bcrypt) dengan melakukan hashing. Titik impas adalah sekitar 223 karakter sebelum Anda kehabisan entropi.

    Katakan itu lagi. Untuk kelas kata sandi ini, pra-hashing pasti meningkatkan keamanan secara signifikan.

Kembali Ke Dunia Nyata

Perhitungan entropi semacam ini tidak terlalu penting di dunia nyata. Yang penting adalah menebak entropi. Itulah yang secara langsung mempengaruhi apa yang dapat dilakukan penyerang. Itulah yang ingin Anda maksimalkan.

Meskipun ada sedikit penelitian yang dilakukan untuk menebak entropi, ada beberapa poin yang ingin saya tunjukkan.

Peluang menebak secara acak 72 karakter yang benar berturut-turut sangat rendah. Anda lebih mungkin memenangkan lotre Powerball 21 kali, daripada mengalami tabrakan ini ... Itulah jumlah yang sedang kita bicarakan.

Tapi kita mungkin tidak tersandung pada statistik itu. Dalam kasus frase, kemungkinan 72 karakter pertama menjadi sama jauh lebih tinggi daripada kata sandi acak. Tapi itu masih sangat rendah (Anda lebih cenderung memenangkan lotere Powerball 5 kali, berdasarkan 2,3 bit per karakter).

Praktis

Secara praktis, itu tidak terlalu penting. Peluang seseorang menebak 72 karakter pertama dengan benar, sedangkan yang terakhir membuat perbedaan yang signifikan sangat rendah sehingga tidak perlu dikhawatirkan. Mengapa?

Nah, katakanlah Anda mengambil frase. Jika orang tersebut bisa mendapatkan 72 karakter pertama dengan benar, mereka benar - benar beruntung (tidak mungkin), atau itu adalah frasa yang umum. Jika itu frase umum, satu-satunya variabel adalah berapa panjangnya.

Mari kita ambil contoh. Mari kita ambil kutipan dari Alkitab (hanya karena itu adalah sumber umum dari teks panjang, bukan karena alasan lain):

Anda tidak akan mengingini rumah tetangga Anda. Anda tidak boleh mengingini istri tetangga Anda, atau pelayan atau pembantunya, lembu atau keledainya, atau apapun yang menjadi milik tetangga Anda.

Itu 180 karakter. Karakter ke-73 adalah gyang kedua neighbor's. Jika Anda sudah menebak sebanyak itu, kemungkinan besar Anda tidak akan berhenti nei, tetapi melanjutkan dengan sisa ayat (karena begitulah kemungkinan besar kata sandi akan digunakan). Oleh karena itu, "hash" Anda tidak menambahkan banyak.

BTW: Saya BENAR-BENAR TIDAK menganjurkan penggunaan kutipan Alkitab. Padahal, justru sebaliknya.

Kesimpulan

Anda tidak akan banyak membantu orang yang menggunakan kata sandi panjang dengan melakukan hashing terlebih dahulu. Beberapa grup yang pasti bisa Anda bantu. Beberapa Anda pasti bisa melukai.

Tapi pada akhirnya, tidak ada yang terlalu signifikan. Angka-angka yang kita hadapi hanya WAY terlalu tinggi. Perbedaan entropi tidak akan banyak.

Anda lebih baik membiarkan bcrypt apa adanya. Anda lebih cenderung mengacaukan hashing (secara harfiah, Anda sudah melakukannya, dan Anda bukan yang pertama, atau terakhir membuat kesalahan itu) daripada serangan yang Anda coba cegah akan terjadi.

Berfokuslah untuk mengamankan sisa situs. Dan tambahkan pengukur entropi kata sandi ke kotak kata sandi saat pendaftaran untuk menunjukkan kekuatan kata sandi (dan tunjukkan jika kata sandi terlalu panjang sehingga pengguna mungkin ingin mengubahnya) ...

Itu setidaknya $ 0,02 saya (atau mungkin lebih dari $ 0,02) ...

Sejauh Menggunakan Lada "Rahasia":

Secara harfiah tidak ada penelitian untuk memasukkan satu fungsi hash ke bcrypt. Oleh karena itu, tidak jelas apakah memasukkan hash yang "dibumbui" ke dalam bcrypt akan pernah menyebabkan kerentanan yang tidak diketahui (kami tahu bahwa tindakan tersebut hash1(hash2($value))dapat mengekspos kerentanan yang signifikan di sekitar resistensi tabrakan dan serangan preimage).

Mengingat Anda sudah mempertimbangkan untuk menyimpan kunci rahasia ("lada"), mengapa tidak menggunakannya dengan cara yang telah dipelajari dan dipahami dengan baik? Mengapa tidak mengenkripsi hash sebelum menyimpannya?

Pada dasarnya, setelah Anda mencirikan kata sandi, masukkan seluruh keluaran hash ke dalam algoritma enkripsi yang kuat. Kemudian simpan hasil yang dienkripsi.

Sekarang, serangan SQL-Injection tidak akan membocorkan sesuatu yang berguna, karena mereka tidak memiliki kunci sandi. Dan jika kuncinya bocor, penyerang tidak lebih baik daripada jika Anda menggunakan hash biasa (yang dapat dibuktikan, sesuatu dengan lada "pra-hash" tidak tersedia).

Catatan: jika Anda memilih untuk melakukan ini, gunakan perpustakaan. Untuk PHP, saya sangat merekomendasikan paket Zend Framework 2 Zend\Crypt. Ini sebenarnya satu-satunya yang saya rekomendasikan pada saat ini. Ini telah ditinjau dengan kuat, dan itu membuat semua keputusan untuk Anda (yang merupakan hal yang sangat baik) ...

Sesuatu seperti:

use Zend\Crypt\BlockCipher;

public function createHash($password) {
    $hash = password_hash($password, PASSWORD_BCRYPT, ["cost"=>$this->cost]);

    $blockCipher = BlockCipher::factory('mcrypt', array('algo' => 'aes'));
    $blockCipher->setKey($this->key);
    return $blockCipher->encrypt($hash);
}

public function verifyHash($password, $hash) {
    $blockCipher = BlockCipher::factory('mcrypt', array('algo' => 'aes'));
    $blockCipher->setKey($this->key);
    $hash = $blockCipher->decrypt($hash);

    return password_verify($password, $hash);
}

Dan itu bermanfaat karena Anda menggunakan semua algoritme dengan cara yang dipahami dengan baik dan dipelajari dengan baik (setidaknya secara relatif). Ingat:

Siapa pun, dari amatir yang paling tidak mengerti hingga kriptografer terbaik, dapat membuat algoritme yang tidak dapat dia pecahkan sendiri.


6
Terima kasih banyak atas jawaban mendetail ini. Ini sangat membantu saya!
Frederik Kammer

1
Pujian saya untuk jawaban ini. Namun, satu pilihan kecil, itu adalah sebagian besar pengguna, yang menggunakan kata sandi yang sangat lemah, kata-kata dan turunan yang terkandung dalam kamus untuk memecahkan kata sandi, lada akan melindungi mereka terlepas dari pertanyaan entropi. Untuk menghindari kehilangan entrofi, Anda cukup menggabungkan kata sandi dan merica. Namun saran Anda tentang mengenkripsi nilai hash mungkin merupakan solusi terbaik untuk menambahkan rahasia sisi server.
martinstoeckli

2
@martinstoeckli: Masalah saya dengan konsep lada bukanlah pada nilainya. Di sinilah penerapan "lada" masuk ke wilayah yang belum dipetakan dalam kaitannya dengan algoritme kriptografi. Itu bukanlah hal yang baik. Sebaliknya, saya percaya bahwa primitif kriptografi harus digabungkan sedemikian rupa sehingga mereka dirancang untuk berjalan bersama. Pada dasarnya, konsep inti lada terdengar di telinga saya seperti beberapa orang yang tidak tahu apa-apa tentang kriptografi berkata "Lebih banyak hash lebih baik! Kami punya garam, merica juga bagus!" . Saya lebih suka memiliki impl yang lebih sederhana, lebih teruji, dan lebih langsung
ircmaxell

@ ircmaxell - Ya, saya tahu sudut pandang Anda dan saya setuju, selama nilai hash akan dienkripsi setelahnya. Jika Anda tidak mengambil langkah tambahan ini, serangan kamus hanya akan mengungkapkan terlalu banyak kata sandi yang lemah, bahkan dengan algoritma hash yang baik.
martinstoeckli

1
@martinstoeckli: Saya tidak setuju di sana. Menyimpan rahasia bukanlah hal yang sepele untuk dilakukan. Sebaliknya, jika Anda menggunakan bcrypt dengan biaya yang bagus (12 hari ini), semua kecuali kelas kata sandi yang paling lemah aman (kamus, dan kata sandi yang remeh adalah yang lemah). Jadi saya lebih suka merekomendasikan orang-orang untuk fokus mendidik pengguna dengan pengukur kekuatan dan membuat mereka menggunakan kata sandi yang lebih baik di tempat pertama ...
ircmaxell

5

Membocorkan kata sandi jelas merupakan hal yang baik untuk dilakukan, tetapi mari kita lihat alasannya.

Pertama kita harus menjawab pertanyaan kapan tepatnya merica membantu. Lada hanya melindungi kata sandi, selama tetap rahasia, jadi jika penyerang memiliki akses ke server itu sendiri, itu tidak ada gunanya. Serangan yang jauh lebih mudah adalah injeksi SQL, yang memungkinkan akses baca ke database (ke nilai hash kami), saya menyiapkan demo injeksi SQL untuk menunjukkan betapa mudahnya (klik panah berikutnya untuk menyiapkan memasukkan).

Lalu apa manfaat sebenarnya dari lada? Selama lada tetap dirahasiakan, lada akan melindungi sandi yang lemah dari serangan kamus. Kata sandi 1234kemudian akan menjadi seperti ini 1234-p*deDIUZeRweretWy+.O. Kata sandi ini tidak hanya lebih panjang, tetapi juga mengandung karakter khusus dan tidak akan pernah menjadi bagian dari kamus mana pun.

Sekarang kami dapat memperkirakan kata sandi apa yang akan digunakan pengguna kami, mungkin akan lebih banyak pengguna yang memasukkan kata sandi yang lemah, karena ada pengguna dengan kata sandi antara 64-72 karakter (sebenarnya ini akan sangat jarang).

Poin lainnya adalah kisaran untuk pemaksaan kasar. Fungsi hash sha256 akan mengembalikan output 256 bit atau kombinasi 1.2E77, itu terlalu banyak untuk pemaksaan kasar, bahkan untuk GPU (jika saya menghitung dengan benar, ini akan membutuhkan sekitar 2E61 tahun pada GPU pada tahun 2013). Jadi kita tidak mendapatkan kerugian nyata mengaplikasikan lada. Karena nilai hash tidak sistematis, Anda tidak dapat mempercepat pemaksaan dengan pola umum.

PS Sejauh yang saya tahu, batas 72 karakter khusus untuk algoritma BCrypt itu sendiri. Jawaban terbaik yang saya temukan adalah ini .

PPS Saya pikir contoh Anda cacat, Anda tidak dapat menghasilkan hash dengan panjang kata sandi lengkap, dan memverifikasinya dengan yang terpotong. Anda mungkin bermaksud menerapkan lada dengan cara yang sama untuk menghasilkan hash dan untuk verifikasi hash.


Mengenai PPS Anda, saya bisa mengatakan: Ya, dia dapat memverifikasi kata sandi yang terpotong dengan hash yang tidak terpotong dan masih mendapatkan true. Tentang itulah pertanyaan ini. Lihat sendiri: viper-7.com/RLKFnB
Sliq

@ Panique - Masalahnya bukan pada perhitungan hash BCrypt, melainkan HMAC sebelumnya. Untuk membuat hash SHA, OP menggunakan kata sandi lengkap dan menggunakan hasilnya sebagai input untuk BCrypt. Untuk verifikasi, dia memotong kata sandi sebelum menghitung hash SHA, lalu menggunakan hasil yang sama sekali berbeda ini sebagai masukan untuk BCrypt. HMAC menerima masukan dengan panjang berapa pun.
martinstoeckli

2

Bcrypt menggunakan algoritma yang didasarkan pada algoritma penyiapan kunci Blowfish yang mahal.

Batas kata sandi 56 byte yang direkomendasikan (termasuk byte terminasi nol) untuk bcrypt berhubungan dengan batas 448 bit dari kunci Blowfish. Setiap byte di luar batas itu tidak sepenuhnya tercampur ke dalam hash yang dihasilkan. Oleh karena itu, batas absolut 72 byte pada kata sandi bcrypt kurang relevan, ketika Anda mempertimbangkan efek sebenarnya pada hash yang dihasilkan oleh byte tersebut.

Jika menurut Anda pengguna Anda biasanya akan memilih kata sandi yang panjangnya lebih dari 55 byte, ingatlah Anda selalu dapat meningkatkan putaran peregangan kata sandi, untuk meningkatkan keamanan dalam kasus pelanggaran tabel kata sandi (meskipun ini harus banyak dibandingkan dengan menambahkan tambahan karakter). Jika hak akses pengguna sangat penting sehingga pengguna biasanya memerlukan kata sandi yang sangat panjang, maka masa berlaku kata sandi juga harus singkat, seperti 2 minggu. Ini berarti bahwa kata sandi jauh lebih kecil kemungkinannya untuk tetap valid sementara peretas menginvestasikan sumber dayanya untuk mengalahkan faktor kerja yang terlibat dalam pengujian setiap kata sandi percobaan untuk melihat apakah itu akan menghasilkan hash yang cocok.

Tentu saja, jika tabel kata sandi tidak dilanggar, kami hanya mengizinkan peretas, paling banyak, sepuluh upaya untuk menebak kata sandi 55 byte pengguna, sebelum mengunci akun pengguna;)

Jika Anda memutuskan untuk melakukan pra-hash kata sandi yang lebih panjang dari 55 byte, maka Anda harus menggunakan SHA-384, karena memiliki keluaran terbesar tanpa melewati batas.


1
"kadaluwarsa kata sandi juga harus pendek, seperti 2 minggu" dari "kata sandi yang sangat panjang", sungguh, mengapa repot-repot menyimpan kata sandi itu, cukup gunakan reset kata sandi setiap saat. Serius, itu solusi yang salah, pindah ke otentikasi dua faktor dengan token.
zaf

Terima kasih @fragma. Apakah Anda dapat menunjukkan kepada saya contohnya? Kedengarannya menarik.
Phil

[DRAFT NIST Special Publication 800-63B Digital Authentication Guideline] ( pages.nist.gov/800-63-3/sp800-63b.html ), 5.1.1.2. Penguji Rahasia yang Hafal: Penguji TIDAK SEHARUSNYA mengharuskan rahasia yang dihafal diubah secara sewenang-wenang (misalnya, secara berkala) . Lihat juga Toward Better Password Requirements oleh Jim Fenton.
zaf

1
Masalahnya adalah bahwa semakin sering pengguna diminta untuk mengubah kata sandi, semakin buruk pilihan kata sandi menjadi sehingga mengurangi keamanan. Pengguna memiliki jumlah terbatas dari kata sandi yang dapat diingat yang baik dan mereka kehabisan, baik memilih kata sandi yang benar-benar buruk atau menuliskannya di catatan
tempel yang
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.