Saya menulis jawaban yang menjelaskan secara rinci bagaimana getrandom()
blok menunggu entropi awal.
Namun, menurut saya dia sedikit melebih-lebihkan urandom dengan mengatakan bahwa "satu-satunya saat di mana /dev/urandom mungkin menyiratkan masalah keamanan karena entropi rendah adalah pada saat-saat pertama penginstalan OS otomatis yang baru."
Kekhawatiran Anda beralasan. Saya memiliki pertanyaan terbuka tentang hal itu dan implikasinya. Masalahnya adalah benih acak persisten membutuhkan waktu cukup lama untuk berpindah dari kumpulan input ke kumpulan keluaran (kumpulan pemblokiran dan CRNG). Masalah ini berarti bahwa /dev/urandom
akan menampilkan nilai yang berpotensi dapat diprediksi selama beberapa menit setelah boot. Solusinya adalah, seperti yang Anda katakan, menggunakan /dev/random
pemblokiran , atau menggunakan getrandom()
setel ke blokir.
Faktanya, tidak jarang melihat baris seperti ini di log kernel saat boot awal:
random: sn: uninitialized urandom read (4 bytes read, 7 bits of entropy available)
random: sn: uninitialized urandom read (4 bytes read, 15 bits of entropy available)
random: sn: uninitialized urandom read (4 bytes read, 16 bits of entropy available)
random: sn: uninitialized urandom read (4 bytes read, 16 bits of entropy available)
random: sn: uninitialized urandom read (4 bytes read, 20 bits of entropy available)
Semua ini adalah contoh ketika kumpulan non-pemblokiran diakses bahkan sebelum cukup entropi dikumpulkan. Masalahnya adalah jumlah entropi terlalu rendah untuk cukup aman secara kriptografis pada saat ini. Seharusnya ada 2 kemungkinan nilai 4 byte, namun dengan hanya tersedia 7 bit entropi, itu berarti hanya ada 2, atau 128, kemungkinan yang berbeda.
Halderman juga tampaknya mengatakan bahwa kumpulan entropi terisi pada setiap boot, dan tidak, seperti yang dikatakan Pornin dalam jawabannya, pada instalasi OS pertama. Meskipun tidak terlalu penting untuk aplikasi saya, saya bertanya-tanya:yang mana?
Ini sebenarnya masalah semantik. Entropi kumpulan yang sebenarnya (halaman memori yang disimpan di kernel yang berisi nilai acak) diisi pada setiap boot oleh benih entropi yang persisten dan kebisingan lingkungan. Namun, benih entropi itu sendiri adalah file yang dibuat saat penginstalan dan diperbarui dengan nilai acak baru setiap kali sistem dimatikan. Saya membayangkan Pornin sedang mempertimbangkan benih acak untuk menjadi bagian dari kumpulan entropi (seperti dalam, bagian dari sistem distribusi dan pengumpulan entropi umum), sedangkan Halderman menganggapnya terpisah (karena kumpulan entropi secara teknis adalah halaman dari memori, tidak lebih). Sebenarnya benih entropi dimasukkan ke kumpulan entropi di setiap boot, tetapi perlu waktu beberapa menit untuk benar-benar memengaruhi kumpulan tersebut.
Ringkasan dari tiga sumber keacakan:
-
/dev/random
- Perangkat karakter pemblokir mengurangi "jumlah entropi" setiap kali dibaca (meskipun entropi tidak benar-benar habis). Namun, itu juga memblokir sampai entropi yang cukup dikumpulkan saat boot, sehingga aman untuk digunakan sejak dini. -
/dev/urandom
- Perangkat karakter non-pemblokiran akan mengeluarkan data acak setiap kali ada yang membacanya. Setelah entropi yang cukup dikumpulkan, itu akan menghasilkan aliran yang hampir tidak terbatas yang tidak dapat dibedakan dari data acak. Sayangnya, untuk alasan kompatibilitas, ini dapat dibaca bahkan sejak awal boot sebelum entropi satu kali yang cukup dikumpulkan. -
getrandom()
- Syscall yang akan menampilkan data acak selama kumpulan entropi telah diinisialisasi dengan benar dengan jumlah minimum entropi yang diperlukan. Ini default untuk membaca dari kumpulan non-pemblokiran. Jika diberiGRND_NONBLOCK
bendera, itu akan mengembalikan kesalahan jika tidak ada cukup entropi. Jika diberiGRND_RANDOM
flag, itu akan berperilaku identik dengan/dev/random
, cukup blokir hingga tersedia entropi.
Saya sarankan Anda menggunakan opsi ketiga, getrandom()
syscall. Ini akan memungkinkan proses untuk membaca data acak yang aman secara kriptografis dengan kecepatan tinggi, dan hanya akan memblokir sejak awal boot ketika tidak cukup entropi yang dikumpulkan. Jika os.urandom()
Python fungsi bertindak sebagai pembungkus syscall ini seperti yang Anda katakan, maka itu harus digunakan dengan baik. Sepertinya ada banyak diskusi tentang apakah itu harus terjadi atau tidak, berakhir dengan pemblokiran sampai entropi yang cukup tersedia.
Berpikir sedikit lebih jauh:apa praktik terbaik untuk lingkungan yang segar dan naif seperti yang saya jelaskan di atas, tetapi yang berjalan pada perangkat dengan prospek yang cukup buruk untuk pembuatan entropi awal?
Ini adalah situasi umum, dan ada beberapa cara untuk menghadapinya:
-
Pastikan Anda memblokir saat boot awal, misalnya dengan menggunakan
/dev/random
ataugetrandom()
. -
Pertahankan seed acak persisten, jika memungkinkan (yaitu jika Anda dapat menulis ke penyimpanan di setiap boot).
-
Yang terpenting, gunakan RNG perangkat keras . Ini adalah tindakan paling efektif #1.
Menggunakan generator nomor acak perangkat keras sangat penting. Kernel Linux akan menginisialisasi kumpulan entropinya dengan antarmuka HWRNG yang didukung jika ada, sepenuhnya menghilangkan lubang boot entropi. Banyak perangkat yang disematkan memiliki generator keacakan sendiri.
Hal ini sangat penting untuk banyak perangkat yang disematkan, karena mereka mungkin tidak memiliki pengatur waktu resolusi tinggi yang diperlukan kernel untuk menghasilkan entropi dengan aman dari kebisingan lingkungan. Beberapa versi prosesor MIPS, misalnya, tidak memiliki penghitung siklus.
Bagaimana dan mengapa Anda menyarankan menggunakan urandom untuk menyemai (saya kira userland?) CSPRNG? Bagaimana ini bisa mengalahkan getrandom?
Perangkat keacakan non-pemblokiran tidak dirancang untuk kinerja tinggi. Sampai saat ini, perangkat sangat lambat karena menggunakan SHA-1 untuk keacakan daripada stream cipher seperti sekarang. Menggunakan antarmuka kernel untuk keacakan bisa jadi kurang efisien daripada CSPRNG ruang pengguna lokal karena setiap panggilan ke kernel memerlukan sakelar konteks yang mahal. Kernel telah dirancang untuk memperhitungkan aplikasi yang ingin mengambil banyak darinya, tetapi komentar di kode sumber memperjelas bahwa mereka tidak melihat ini sebagai hal yang benar untuk dilakukan:
/*
* Hack to deal with crazy userspace progams when they are all trying
* to access /dev/urandom in parallel. The programs are almost
* certainly doing something terribly wrong, but we'll work around
* their brain damage.
*/
Pustaka crypto populer seperti dukungan OpenSSL menghasilkan data acak. Mereka dapat diunggulkan sekali atau diunggulkan kembali sesekali, dan dapat memperoleh manfaat lebih banyak dari paralelisasi. Ini juga memungkinkan untuk menulis kode portabel yang tidak bergantung pada perilaku sistem operasi atau versi sistem operasi tertentu.
Jika Anda tidak membutuhkan keacakan dalam jumlah besar, tidak apa-apa menggunakan antarmuka kernel. Jika Anda sedang mengembangkan aplikasi crypto yang membutuhkan banyak keacakan sepanjang masa aktifnya, Anda mungkin ingin menggunakan pustaka seperti OpenSSL untuk menanganinya.
Ada tiga status yang dapat digunakan sistem:
- Belum mengumpulkan cukup entropi untuk menginisialisasi CPRNG dengan aman.
-
Telah mengumpulkan cukup entropi untuk menginisialisasi CPRNG dengan aman, dan:
2a. Telah memberikan lebih banyak entropi daripada yang dikumpulkan.
2b. Telah memberikan lebih sedikit entropi daripada yang dikumpulkan.
Secara historis, orang mengira perbedaan antara (2a) dan (2b) itu penting. Ini menyebabkan dua masalah. Pertama, itu salah – perbedaannya tidak ada artinya untuk CPRNG yang dirancang dengan baik. Dan kedua, penekanan pada perbedaan (2a)-vs-(2b) menyebabkan orang kehilangan perbedaan antara (1) dan (2), yang sebenarnya sangat penting. Orang-orang hanya menciutkan (1) menjadi kasus khusus dari (2a).
Yang benar-benar Anda inginkan adalah sesuatu yang memblokir di status (1), dan tidak memblokir di status (2a) atau (2b).
Sayangnya, di masa lalu, kebingungan antara (1) dan (2a) berarti ini bukan pilihan. Dua pilihan Anda hanyalah /dev/random
, yang diblokir dalam kasus (1) dan (2a), dan /dev/urandom
, yang tidak pernah diblokir. Tetapi status (1) hampir tidak pernah terjadi – dan tidak terjadi sama sekali dalam sistem yang terkonfigurasi dengan baik, lihat di bawah – lalu /dev/urandom
lebih baik untuk hampir semua sistem, hampir sepanjang waktu. Dari situlah semua postingan blog tentang "selalu gunakan urandom" berasal – mereka mencoba meyakinkan orang untuk berhenti membuat perbedaan yang tidak berarti dan berbahaya antara status (2a) dan (2b).
Tapi, ya, tidak satu pun dari ini yang sebenarnya Anda inginkan. Jadi, getrandom
yang lebih baru syscall, yang secara default memblokir di status (1), dan tidak memblokir di status (2a) atau (2b). Jadi pada Linux modern, ortodoksi harus diperbarui menjadi:selalu gunakan getrandom
dengan setelan default .
Kerutan ekstra:
-
getrandom
juga mendukung mode non-default yang berfungsi seperti/dev/random
, yang dapat diminta melaluiGRND_RANDOM
bendera. AFAIK bendera ini sebenarnya tidak pernah berguna, untuk semua alasan yang sama seperti yang dijelaskan oleh posting blog lama. Jangan gunakan itu. -
getrandom
juga memiliki beberapa manfaat bonus tambahan di atas/dev/urandom
:ini berfungsi terlepas dari tata letak sistem file Anda, dan tidak memerlukan pembukaan deskriptor file, keduanya bermasalah untuk pustaka umum yang ingin membuat asumsi minimal tentang lingkungan tempat mereka akan digunakan. Ini tidak memengaruhi keamanan kriptografi , tetapi secara operasional bagus. -
Sistem yang terkonfigurasi dengan baik akan selalu memiliki entropi yang tersedia, bahkan di boot awal (yaitu, Anda seharusnya tidak pernah masuk ke status (1), selamanya). Ada banyak cara untuk mengelola ini:simpan beberapa entropi dari boot sebelumnya untuk digunakan pada boot berikutnya. Pasang RNG perangkat keras. Kontainer Docker menggunakan kernel host, dan dengan demikian mendapatkan akses ke kumpulan entropinya. Penyiapan virtualisasi berkualitas tinggi memiliki cara untuk membiarkan sistem tamu mengambil entropi dari sistem host melalui antarmuka hypervisor (mis. Cari "virtio rng"). Namun tentu saja, tidak semua sistem terkonfigurasi dengan baik. Jika Anda memiliki sistem yang terkonfigurasi dengan buruk, Anda harus melihat apakah Anda dapat membuatnya terkonfigurasi dengan baik. Pada prinsipnya ini harus murah dengan mudah, tetapi pada kenyataannya orang tidak memprioritaskan keamanan jadi... mungkin perlu melakukan hal-hal seperti mengganti penyedia cloud, atau beralih ke platform tersemat yang berbeda. Dan sayangnya, Anda mungkin menemukan bahwa ini lebih mahal daripada yang bersedia Anda (atau bos Anda) bayarkan, sehingga Anda terjebak berurusan dengan sistem yang tidak terkonfigurasi dengan baik. Simpati saya jika demikian.
-
Seperti yang dicatat oleh @forest, jika Anda memerlukan banyak nilai CPRNG, maka jika Anda sangat berhati-hati, Anda dapat mempercepatnya dengan menjalankan CPRNG Anda sendiri di ruang pengguna, sambil menggunakan
getrandom
untuk (ulang) pembibitan. Ini adalah hal yang "khusus ahli", sama seperti situasi apa pun di mana Anda mendapati diri Anda menerapkan primitif crypto Anda sendiri. Anda hanya boleh melakukannya jika Anda telah mengukur dan menemukannya menggunakangetrandom
langsung terlalu lambat untuk kebutuhan Anda dan Anda memiliki keahlian kriptografi yang signifikan. Sangat mudah untuk mengacaukan implementasi CPRNG sedemikian rupa sehingga keamanan Anda benar-benar rusak, tetapi hasilnya masih "terlihat" acak sehingga Anda tidak menyadarinya.