Saya baru-baru ini mengobrol dengan pengembang Thunderbird tentang desain API. Selama percakapan ini, saya membagikan pemikiran saya tentang RNP , implementasi baru OpenPGP yang baru-baru ini mulai digunakan Thunderbird alih-alih GnuPG .
Teman bicara skeptis tentang tesis saya bahwa RNP API perlu ditingkatkan, dan bertanya, "Bukankah itu subjektif - API mana yang lebih baik dan mana yang lebih buruk?" Saya setuju bahwa kami tidak memiliki metrik yang baik untuk mengevaluasi API. Tetapi saya tidak setuju bahwa kami pada prinsipnya tidak dapat menilai API.
Faktanya, saya menduga bahwa programmer paling berpengalaman akan mengenali API yang buruk jika mereka melihatnya. Saya berpikir lebih jauh dalam artikel ini akan menghasilkan heuristik yang baik, yang akan saya coba bangun berdasarkan pengalaman saya sendiri dengan (dan lebih) GnuPG, Sequoiadan RNP. Lalu saya akan membahas RNP API. Sayangnya, API ini tidak hanya dapat disalahgunakan dengan mudah, tetapi juga menipu, jadi sebaiknya API ini belum digunakan dalam konteks keamanan kritis. Namun target audiens Thunderbird adalah orang-orang yang dikenal rentan, seperti jurnalis, aktivis, pengacara, dan mitra komunikasinya; semua orang ini membutuhkan perlindungan. Menurut saya, ini berarti Thunderbird harus memikirkan sekali lagi apakah akan menggunakan RNP.
Catatan: Saya juga menyarankan untuk membaca email ini: Mari Menggunakan Perpustakaan GPL di Thunderbird! yang saya serahkan ke pos Perencanaan Pembangunan Thunderbird .
Apa ciri-ciri dari API yang buruk?
Sebelum kami memulai Sequoia dengan Justus dan Kai , kami bertiga bekerja di GnuPG . Kami tidak hanya mempelajari gpg sendiri, tetapi juga berbicara dan berkolaborasi dengan banyak pengguna gpg berikutnya. Orang-orang dapat mengatakan banyak hal baik tentang GnuPG .
Sejauh menyangkut kritik gpg, yang paling signifikan adalah dua jenis kritik tentang API. Intinya adalah: gpg API terlalu dogmatis. Misalnya, gpg menggunakan pendekatan keyring. Oleh karena itu, Anda hanya dapat melihat atau menggunakan sertifikat OpenPGP jika sudah diimpor ke keybase pribadi. Tetapi beberapa pengembang ingin melihat sertifikatnya terlebih dahulu, baru kemudian mengimpornya. Misalnya, saat Anda mencari sertifikat pada server kunci dengan sidik jarinya, Anda dapat memeriksa dan memastikan bahwa sertifikat yang dikembalikan benar-benar yang Anda perlukan.karena URL-nya mengautentikasi sendiri. Ini dapat dilakukan dengan menggunakan gpg, tetapi hanya dengan cara penyelesaian, dengan mengabaikan prinsip-prinsip model pemrograman yang tertanam di dalamnya. Ide dasarnya adalah ini: buat direktori sementara, tambahkan file konfigurasi ke dalamnya, beri tahu gpg untuk menggunakan direktori alternatif, impor sertifikat di sana, periksa sertifikat, lalu hapus direktori sementara. Ini adalah rekomendasi resmi yang ditambahkan oleh Justus berdasarkan percakapan kami dengan pengguna gpg berikutnya. Ya, metode ini berhasil. Tetapi membutuhkan penulisan kode yang khusus untuk sistem operasi, kode ini lambat dan sering terdapat bug di dalamnya.
Kelas komentar lain yang sering kami temui adalah bahwa untuk bekerja dengan gpg, Anda perlu mengetahui banyak hal yang tidak jelas - agar tidak menyalahgunakan mekanisme ini. Atau, dengan kata lain, Anda harus sangat berhati-hati saat menggunakan gpg API untuk menghindari masuknya kerentanan ke dalam kode Anda secara tidak sengaja.
Untuk lebih memahami kekhawatiran kedua, pertimbangkan kerentanan EFAIL... Masalah utama dengan API dekripsi gpg: saat mendekripsi pesan, gpg akan memberikan teks biasa, bahkan jika inputnya rusak. gpg memang mengembalikan kesalahan dalam kasus ini, tetapi beberapa program masih mengeluarkan teks biasa dalam bentuk yang rusak. Jadi kenapa tidak? Jelas lebih baik untuk menunjukkan setidaknya sebagian dari pesan daripada tidak menunjukkan apa-apa, bukan? Kerentanan EFAIL menunjukkan bagaimana penyerang dapat memanfaatkan ini untuk memasukkan bug web ke dalam pesan terenkripsi. Saat pengguna melihat postingan ini, bug web bocor keluar dari postingan. Fiuh.
Jadi, salah siapa bug ini? Pengembang GnuPG bersikeras bahwa masalahnya ada di tingkat aplikasi, karena mereka menggunakan gpg secara tidak benar:
Direkomendasikan agar agen pengguna email menghormati kode status DECRYPTION_FAILED dan tidak menampilkan data, atau setidaknya memilih cara yang tepat untuk menampilkan email yang berpotensi rusak tanpa membuat oracle dan memberi tahu pengguna bahwa email tersebut tidak menimbulkan rasa percaya diri.
gpg menandakan kesalahan; aplikasi tidak menghormati kontrak API. Saya harus setuju dengan para pengembang GnuPG dan menambahkan: antarmuka gpg adalah (dan masih) bom waktu karena tidak memberitahu pengguna bagaimana untuk melanjutkan. Sebaliknya, tindakan yang mudah dan tampaknya menguntungkan adalah salah. Dan API semacam ini sayangnya umum di GnuPG.
Apa yang membuat API bagus?
Menyadari dua hal ini - bahwa gpg API terlalu dogmatis dan sulit digunakan dengan benar - membentuk rencana saya. Ketika kami memulai proyek Sequoia, kami setuju bahwa kami ingin menghindari kesalahan seperti itu. Berdasarkan pengamatan kami, kami mempraktikkan dua pengujian yang terus kami gunakan sebagai titik referensi untuk pengembangan Sequoia API. Pertama, selain API tingkat tinggi, harus ada API tingkat rendah yang tidak dogmatis - dalam arti tidak mencegah pengguna melakukan apa pun yang tidak dilarang. Pada saat yang sama, API harus memandu pengguna ke hal-hal yang benar (hard-code), membuat tindakan yang tepat mudah dijalankan dan paling jelas saat memilih tindakan .
Untuk mencapai dua tujuan yang sedikit bertentangan ini dalam membuat segala sesuatu menjadi mungkin, tetapi mencegah kesalahan, kami sangat mengandalkan dua alat: jenis dan contoh. Jenis menyulitkan penggunaan objek dengan cara yang tidak diinginkan karena kontrak API diformalkan pada waktu kompilasi dan bahkan memberlakukan konversi tertentu . Contoh - potongan kode - akan disalin . Oleh karena itu, contoh yang baik tidak hanya akan mengajari pengguna cara menggunakan fungsi dengan benar, tetapi juga akan sangat memengaruhi cara mereka akan menggunakannya.
Jenis
Saya akan menunjukkan kepada Anda contoh bagaimana kami menggunakan tipe di Sequoia, dan bagaimana mereka membantu kami membuat API yang baik. Untuk memperjelas contoh, akan berguna untuk mengingat beberapa konteks terkait OpenPGP.
OpenPGP
Ada beberapa tipe data fundamental dalam OpenPGP, yaitu sertifikat, komponen (seperti kunci dan ID pengguna), dan tanda tangan yang mengikat. Akar sertifikat adalah kunci utama yang sepenuhnya mengidentifikasi sidik jari sertifikat (sidik jari = Hash (kunci utama)). Sertifikat biasanya menyertakan komponen seperti subkunci dan ID pengguna. OpenPGP mengikat komponen ke sertifikat menggunakan apa yang disebut tanda tangan mengikat. Saat kita menggunakan hash kunci primer biasa sebagai sidik jari dan menggunakan tanda tangan untuk mengikat komponen ke kunci utama, kondisi dibuat sehingga komponen tambahan dapat ditambahkan nanti. Tanda tangan yang mengikat juga mencakup properti. Oleh karena itu, dimungkinkan untuk mengubah komponen, misalnya, untuk memperpanjang masa berlaku subkunci.Akibatnya, beberapa tanda tangan yang valid dapat dikaitkan dengan komponen tertentu. Tanda tangan jangkar tidak hanya mendasar, tetapi juga merupakan bagian integral dari mekanisme keamanan OpenPGP.
Karena ada banyak tanda tangan pengikatan yang valid, perlu ada cara untuk memilih yang Anda inginkan. Sebagai perkiraan pertama, anggaplah tanda tangan yang kita inginkan adalah tanda tangan valid yang terbaru, tidak kedaluwarsa, dan tidak dicabut yang belum ditunda untuk masa mendatang. Tapi apa itu tanda tangan yang sah? Di Sequoia, tanda tangan tidak hanya harus lulus pemeriksaan matematis, tetapi juga konsisten dengan kebijakan. Misalnya, karena kemampuan kami untuk menolak tabrakan yang disusupi , kami hanya mengizinkan SHA-1 dalam sejumlah kecil situasi . ( Paul Schaub , mengerjakan PGPainless , baru-baru ini menulis panjang lebar tentang kompleksitas ini..) Dengan memaksa pengguna API untuk mengingat semua pertimbangan ini, kami menciptakan tempat berkembang biak untuk kerentanan. Di Sequoia, cara mudah untuk mendapatkan waktu kedaluwarsa adalah cara yang aman. Pertimbangkan kode berikut, yang berfungsi seperti yang diharapkan:
let p = &StandardPolicy::new();
let cert = Cert::from_str(CERT)?;
for k in cert.with_policy(p, None)?.keys().subkeys() {
println!("Key {}: expiry: {}",
k.fingerprint(),
if let Some(t) = k.key_expiration_time() {
DateTime::<Utc>::from(t).to_rfc3339()
} else {
"never".into()
});
}
cert
Apakah sertifikat. Kami mulai dengan menerapkan kebijakan untuk itu. (Kebijakan ditentukan pengguna, tetapi sebagai aturan, StandardPolicy tidak hanya cukup tetapi juga paling sesuai). Faktanya, ini adalah tempat tampilan sertifikat dibuat, di mana hanya komponen dengan tanda tangan mengikat yang valid yang terlihat. Yang penting, itu juga memodifikasi dan memperkenalkan sejumlah metode baru. Metode kunci, misalnya, telah diubah untuk menampilkan ValidKeyAmalgamation, bukan KeyAmalgamation . (Ini adalah penggabungan, karena hasilnya tidak hanya mencakup Kunci, tetapi semua tanda tangan yang terkait dengannya; beberapa percaya bahwa proses ini akan lebih baik disebut Katamari... ¯ \ _ (ツ) _ / ¯) ValidKeyAmalgamation memiliki tanda tangan jangkar yang valid sesuai dengan kriteria di atas. Ini juga menyediakan metode seperti key_expiration_time, yang hanya masuk akal dengan kunci yang valid! Perhatikan juga bahwa tipe pengembalian yang digunakan dengan key_expiration_time adalah ergonomis. Alih-alih mengembalikan nilai mentah, key_expiration_time mengembalikan SystemTime , yang aman dan mudah digunakan.
Sejalan dengan prinsip "izinkan semua" pertama kami, pengembang masih memiliki akses ke tanda tangan tunggal dan menjelajahi sub-paketuntuk mengetahui dari pengikatan tanda tangan yang berbeda saat kuncinya kedaluwarsa. Namun, dibandingkan dengan cara Sequoia API seharusnya mengetahui dengan benar kedaluwarsa sebuah kunci, pendekatan lain apa pun akan bertentangan dengan API. Ini adalah API yang bagus menurut kami.
Contoh dari
Rilis 1.0 dari perpustakaan Sequoia berlangsung pada bulan Desember 2020. Sembilan bulan sebelumnya, kami memasuki situasi fitur lengkap dan siap untuk dirilis. Tapi mereka menunggu . Kami membutuhkan sembilan bulan ke depan untuk menambahkan dokumentasi dan contoh ke API publik. Lihat dokumentasi struktur data Cert sebagai contoh, lihat apa yang kami dapatkan. Seperti yang ditunjukkan di posting kami, kami tidak dapat memberikan contoh untuk setiap fitur hingga satu, tetapi kami melakukannya cukup banyak. Sebagai bonus untuk menulis contoh, kami juga berhasil menemukan beberapa sisi kasar, yang kami poles dalam prosesnya.
Setelah rilis, kami dapat berbicara dengan banyak pengembang yang menyertakan Sequoia dalam kode mereka. Benang merah melalui umpan balik mereka adalah pengakuan tentang betapa bermanfaatnya dokumentasi dan contoh-contohnya. Kami dapat mengonfirmasi bahwa meskipun ini adalah kode kami, kami memeriksa dokumentasi hampir setiap hari dan menyalin contoh kami sendiri. Hal ini lebih mudah. Karena contoh menunjukkan kepada Anda bagaimana menggunakan fungsi tertentu dengan benar, mengapa melakukannya kembali dari awal?
RNP API
RNP adalah implementasi baru dari OpenPGP, yang dikembangkan terutama oleh Ribose . Sekitar dua tahun lalu , Thunderbird memutuskan untuk mengintegrasikan Enigmail ke dalam Thunderbird dan sekaligus mengganti GnuPG dengan RNP . Fakta bahwa Thunderbird memilih RNP tidak hanya bagus untuk RNP; ini juga berarti bahwa RNP telah menjadi implementasi OpenPGP yang paling banyak diminta untuk mengenkripsi email.
Kritik mudah untuk dianggap negatif. Saya ingin menjelaskan: Menurut saya pekerjaan yang dilakukan Ribose baik dan penting, saya berterima kasih kepada mereka karena telah menginvestasikan waktu dan tenaga dalam implementasi baru OpenPGP. Ekosistem OpenPGP sangat membutuhkan penambahan variasi. Tapi ini bukan alasan untuk merilis produk yang belum matang untuk digunakan dalam konteks keamanan kritis.
Infrastruktur penting keamanan
Sayangnya, RNP belum mencapai kondisi yang menurut saya bisa di-deploy dengan aman. Enigmail digunakan tidak hanya oleh orang-orang yang peduli dengan privasi datanya, tetapi juga oleh jurnalis, aktivis, dan pengacara yang peduli dengan keselamatan diri sendiri dan lawan bicara. Dalam wawancara tahun 2017, Benjamin Ismail, kepala Reporters Without Borders cabang Asia-Pasifik , mengatakan:
Kami terutama menggunakan GPG untuk berkomunikasi secara bebas dengan sumber kami. Informasi yang mereka berikan kepada kami tentang hak asasi manusia dan pelanggaran hak-hak ini tidak aman bagi mereka, oleh karena itu penting untuk melindungi integritas percakapan kami.
Wawancara dengan Benjamin Ismail dari organisasi Reporters Without Borders
Sangat penting bahwa Thunderbird terus memberikan pengalaman yang paling aman kepada pengguna ini, bahkan selama masa transisi ini.
RNP dan tanda tangan pengikat subkunci
Saat berbicara tentang bagaimana kami menggunakan tipe di Sequoia untuk mempersulit penyalahgunaan API, saya menunjukkan kepada Anda cara mengetahui tanggal kedaluwarsa kunci hanya dalam beberapa baris kode. Saya ingin memulai dengan contoh yang menunjukkan kepada orang yang tidak berpengalaman dalam OpenPGP atau RNP bagaimana fungsi yang sama dapat diimplementasikan menggunakan RNP. Kode berikut mengulangi subkunci sertifikat (kunci) dan menampilkan tanggal kedaluwarsa untuk setiap subkunci. Sebagai pengingat, waktu kedaluwarsa disimpan dalam tanda tangan pengikat subkunci, dan nilai 0 menunjukkan bahwa kunci tidak akan pernah kedaluwarsa.
int i;
for (i = 0; i < sk_count; i ++) {
rnp_key_handle_t sk;
err = rnp_key_get_subkey_at(key, i, &sk);
if (err) {
printf("rnp_key_get_subkey_at(%d): %x\n", i, err);
return 1;
}
uint32_t expiration_time;
err = rnp_key_get_expiration(sk, &expiration_time);
if (err) {
printf("#%d (%s). rnp_key_get_expiration: %x\n",
i + 1, desc[i], err);
} else {
printf("#%d (%s) expires %"PRIu32" seconds after key's creation time.\n",
i + 1, desc[i],
expiration_time);
}
}
Saya menguji kode ini pada sertifikat dengan lima subkunci. Subkunci pertama memiliki tanda tangan mengikat yang valid dan tidak kedaluwarsa; yang kedua memiliki tanda tangan yang mengikat yang sah dan akan habis masa berlakunya di masa mendatang; yang ketiga memiliki tanda tangan yang mengikat yang valid tetapi sudah kedaluwarsa; yang keempat memiliki tanda tangan mengikat yang tidak valid, yang menurutnya subkunci akan kedaluwarsa di masa mendatang; tanda tangan kelima tidak memiliki jangkar sama sekali. Berikut hasilnya:
#1 (doesn't expire) expires 0 seconds after key's creation time.
#2 (expires) expires 94670781 seconds after key's creation time.
#3 (expired) expires 86400 seconds after key's creation time.
#4 (invalid sig) expires 0 seconds after key's creation time.
#5 (no sig) expires 0 seconds after key's creation time.
Pertama, perhatikan bahwa panggilan rnp_key_get_expiration berhasil, terlepas dari apakah subkunci tersebut memiliki tanda tangan mengikat yang valid, tanda tangan mengikat yang tidak valid, atau tidak ada tanda tangan yang mengikat sama sekali. Jika Anda membaca dokumentasinya , perilaku ini tampaknya sedikit mengejutkan. Ia mengatakan:
.
: 0 , .
Karena waktu kedaluwarsa kunci disimpan dalam tanda tangan binding, sebagai pakar OpenPGP, saya memahaminya dengan cara ini: panggilan ke rnp_key_get_expiration hanya akan berhasil jika subkunci memiliki tanda tangan binding yang valid. Faktanya, ternyata jika tidak ada tanda tangan yang mengikat yang valid, maka fungsinya hanya secara default ke 0, yang, mengingat pernyataan di atas, pengguna API akan berharap untuk menafsirkannya sebagai: kunci ini valid tanpa batas.
Untuk meningkatkan kode ini, Anda harus terlebih dahulu memeriksa apakah kunci tersebut memiliki tanda tangan mengikat yang valid. Beberapa fungsi untuk dilakukan baru-baru ini ditambahkan ke RNP ke alamat CVE-2021-23991 . Secara khusus, pengembang RNP menambahkan fungsi rnp_key_is_valid untuk mengembalikan informasi tentang apakah sebuah kunci valid. Add-on ini memperbaiki situasi, tetapi mengharuskan pengembang untuk secara eksplisit memilih apakah pemeriksaan kritis keamanan ini harus dilakukan (daripada secara eksplisit meninggalkan pemeriksaan yang sudah ditetapkan - seperti halnya dengan Sequoia). Karena pemeriksaan keamanan bukan tentang melakukan pekerjaan yang berguna, mudah saja untuk melupakannya: kode berfungsi meskipun tidak ada pemeriksaan keamanan yang dilakukan. Dan karena dibutuhkan pengetahuan ahli untuk membuat pilihan yang tepat tentang apa yang akan diperiksa, pemeriksaan dilupakan.
Kode berikut menyediakan pemeriksaan keamanan dan melewatkan kunci yang dianggap tidak valid oleh rnp_key_is_valid:
int i;
for (i = 0; i < sk_count; i ++) {
rnp_key_handle_t sk;
err = rnp_key_get_subkey_at(key, i, &sk);
if (err) {
printf("rnp_key_get_subkey_at(%d): %x\n", i, err);
return 1;
}
bool is_valid = false;
err = rnp_key_is_valid(sk, &is_valid);
if (err) {
printf("rnp_key_is_valid: %x\n", err);
return 1;
}
if (! is_valid) {
printf("#%d (%s) is invalid, skipping.\n",
i + 1, desc[i]);
continue;
}
uint32_t expiration_time;
err = rnp_key_get_expiration(sk, &expiration_time);
if (err) {
printf("#%d (%s). rnp_key_get_expiration: %x\n",
i + 1, desc[i], err);
} else {
printf("#%d (%s) expires %"PRIu32" seconds after key's creation time.\n",
i + 1, desc[i],
expiration_time);
}
}
Keluaran:
#1 (doesn't expire) expires 0 seconds after key's creation time.
#2 (expires) expires 94670781 seconds after key's creation time.
#3 (expired) is invalid, skipping.
#4 (invalid sig) is invalid, skipping.
#5 (no sig) is invalid, skipping.
Kode ini dengan benar melewatkan dua kunci yang tidak memiliki tanda tangan mengikat yang valid, tetapi juga melewatkan kunci kedaluwarsa - yang mungkin bukan yang kita inginkan, meskipun dokumentasi memperingatkan kita bahwa fungsi ini "memeriksa ... tanggal kedaluwarsa".
Meskipun kami juga tidak ingin menggunakan kunci atau sertifikat yang kedaluwarsa, terkadang kami menggunakan mereka. Misalnya, jika pengguna lupa memperbarui kunci, maka ia harus dapat melihat bahwa kunci telah kedaluwarsa, dan kemudian memeriksa sertifikat, dan juga memperbarui kunci dalam kasus ini. Meskipun
gpg --list-keys
tidak menunjukkan kunci kedaluwarsa, saat mengedit sertifikat, subkunci kadaluwarsa masih terlihat, sehingga pengguna dapat memperbarui validitasnya:
$ gpg --edit-key 93D3A2B8DF67CE4B674999B807A5D8589F2492F9
Secret key is available.
sec ed25519/07A5D8589F2492F9
created: 2021-04-26 expires: 2024-04-26 usage: C
trust: unknown validity: unknown
ssb ed25519/1E2F512A0FE99515
created: 2021-04-27 expires: never usage: S
ssb cv25519/8CDDC2BC5EEB61A3
created: 2021-04-26 expires: 2024-04-26 usage: E
ssb ed25519/142D550E6E6DF02E
created: 2021-04-26 expired: 2021-04-27 usage: S
[ unknown] (1). Alice <alice@example.org>
Ada situasi lain di mana kunci yang kadaluwarsa tidak boleh dibatalkan validasinya. Misalkan, misalnya, Alice mengirimi Bob pesan bertanda tangan: "Saya akan membayar Anda 100 euro selama setahun," dan kunci tanda tangan kedaluwarsa dalam enam bulan. Ketika tahun ini berakhir, apakah Alice akan berutang pada Bob berdasarkan tanda tangan ini? Ya, saya rasa begitu. Tanda tangan itu valid saat dibubuhkan. Fakta bahwa kunci sudah kedaluwarsa tidak relevan. Tentu saja, ketika kuncinya telah kedaluwarsa, tanda tangan yang disegel olehnya setelah saat kadaluwarsa harus dianggap tidak valid. Demikian pula, pesan tidak boleh dienkripsi dengan kunci kadaluwarsa.
Singkatnya, apakah sebuah kunci harus dianggap valid sangat sensitif terhadap konteks. rnp_key_is_valid lebih baik daripada tidak sama sekali, tetapi terlepas dari namanya, fungsi ini cukup berbeda dalam menentukan apakah sebuah kunci valid.
Sebagai bagian dari komit itu, fungsi kedua telah ditambahkan
rnp_key_valid_till
. Fungsi ini mengembalikan "stempel waktu sebelum kunci dianggap valid ... Jika kunci tidak pernah valid, nol dikembalikan sebagai nilainya." Dengan menggunakan fungsi ini, Anda dapat menentukan apakah kunci tersebut pernah valid, untuk ini Anda perlu memeriksa apakah fungsi ini mengembalikan nilai bukan nol:
int i;
for (i = 0; i < sk_count; i ++) {
rnp_key_handle_t sk;
err = rnp_key_get_subkey_at(key, i, &sk);
if (err) {
printf("rnp_key_get_subkey_at(%d): %x\n", i, err);
return 1;
}
uint32_t valid_till;
err = rnp_key_valid_till(sk, &valid_till);
if (err) {
printf("rnp_key_valid_till: %x\n", err);
return 1;
}
printf("#%d (%s) valid till %"PRIu32" seconds after epoch; ",
i + 1, desc[i], valid_till);
if (valid_till == 0) {
printf("invalid, skipping.\n");
continue;
}
uint32_t expiration_time;
err = rnp_key_get_expiration(sk, &expiration_time);
if (err) {
printf("rnp_key_get_expiration: %x\n", err);
} else {
printf("expires %"PRIu32" seconds after key's creation time.\n",
expiration_time);
}
}
Hasil:
#1 (doesn't expire) valid till 1714111110 seconds after epoch; expires 0 seconds after key's creation time.
#2 (expires) valid till 1714111110 seconds after epoch; expires 94670781 seconds after key's creation time.
#3 (expired) valid till 1619527593 seconds after epoch; expires 86400 seconds after key's creation time.
#4 (invalid sig) valid till 0 seconds after epoch; invalid, skipping.
#5 (no sig) valid till 0 seconds after epoch; invalid, skipping.
Sekarang kami mendapatkan hasil yang kami inginkan! Kami menampilkan waktu kedaluwarsa yang benar untuk tiga subkunci pertama, dan juga menunjukkan bahwa dua subkunci terakhir tidak valid.
Tapi mari kita lihat lebih dekat
rnp_key_valid_till
. Pertama, di OpenPGP, waktu kedaluwarsa kunci disimpan sebagai indentasi 32-bit unsigned sejak kunci dibuat, juga dalam format 32-bit unsigned. Oleh karena itu, fungsi tersebut harus menggunakan tipe yang lebih luas, atau setidaknya periksa kode untuk luapan. (Saya melaporkan masalah ini dan sudah diperbaiki.)
Tapi, meski kita mengabaikan kusen ini, fungsinya tetap aneh. Di OpenPGP, sebuah kunci bisa valid untuk beberapa periode waktu tertentu. Misalkan kunci kedaluwarsa pada 1 Juli, dan pengguna hanya memperbaruinya mulai 10 Juli. Selama periode dari 1 Juli hingga 10 Juli, kuncinya tidak valid, dan tanda tangan yang dibuat selama waktu ini juga harus dianggap tidak valid. Jadi apa yang harus dikembalikan fungsi yang dipertimbangkan untuk kunci seperti itu? Lebih penting lagi, bagaimana seharusnya pengguna API seperti itu menafsirkan hasilnya? Apakah tepat untuk menggunakan API seperti itu? ( Ya, saya bertanya .)
Di Sequoia, kami pergi ke arah lain. Alih-alih mengembalikan informasi bahwa kuncinya valid, kami membalikkan situasi; Pengguna API mungkin bertanya: apakah kunci ini berlaku pada waktu t . Dalam pengalaman kami, hanya inilah yang sebenarnya diperlukan dalam semua kasus yang kami ketahui.
Jangan berpikir bahwa saya secara khusus membahas masalah khusus ini dengan RNP API. Ini hanya komplikasi yang saya pikirkan akhir-akhir ini. Saat kami menerapkan ulang RNP API untuk membuat backend OpenPGP alternatif untuk Thunderbird, kami menghadapi banyak masalah serupa .
Kesimpulan
Kesalahan yang dibuat oleh pengembang RNP dapat dimengerti dan dimaafkan. OpenPGP itu kompleks, seperti banyak protokol lainnya. Tapi ini bisa sangat disederhanakan jika kita berusaha untuk tetap fleksibel dan dapat diandalkan PKI , dan tidak hanya memiliki alat enkripsi file.
Namun, RNP API berbahaya. Thunderbird digunakan dalam konteks keamanan kritis. Dalam wawancara tahun 2017 , Michal 'Rysiek' Wozniak dari Pusat Penelitian Kejahatan dan Korupsi Terorganisir (OCCRP) menjelaskan bahwa nyawa seseorang dipertaruhkan:
Saya sangat yakin bahwa jika selama ini kita tidak menggunakan GnuPG, banyak informan dan jurnalis kita akan berada dalam bahaya atau di balik jeruji besi ...
Wawancara dengan Michal 'Rysiek' Wozniak dari Pusat Kajian Korupsi dan Terorganisir Kejahatan
Bagaimana ini akan mempengaruhi Thunderbird? Saya melihat tiga opsi. Pertama, Thunderbird dapat beralih kembali ke Enigmail. Anda mungkin berpikir mem-porting Enigmail ke Thunderbird 78 akan sulit, tetapi saya telah mendengar dari banyak pengembang Thunderbird bahwa ini secara teknis dapat dilakukan dengan lift. Tetapi salah satu alasan mengapa Thunderbird memilih untuk menjauh dari Enigmail adalah banyaknya waktu yang dihabiskan oleh pengembang Enigmail untuk membantu pengguna menginstal dan mengkonfigurasi GnuPG dengan benar. Oleh karena itu, jalan ini tidaklah ideal.
Kedua, Thunderbird dapat beralih ke implementasi OpenPGP yang berbeda. Ada banyak sekali dari mereka saat ini. untuk memilih dari. Secara pribadi, saya pikir Thunderbird seharusnya beralih ke Sequoia. Tentu saja, saya adalah pengembang Sequoia, jadi saya bias. Tetapi ini bukan tentang uang: dana tersebut membayar saya, dan di pasar bebas saya akan ditawari, mungkin, dua kali lipat dari penghasilan saya sekarang. Saya bekerja untuk melindungi pengguna. Namun, selain Sequoia API dan manfaat penerapannya, Thunderbird juga menang dalam hal ini dalam satu hal lagi: kami telah membuat penerapan ini berhasil. Beberapa minggu yang lalu kami merilis Octopus , backend OpenPGP alternatif untuk Thunderbird. Ini tidak hanya memiliki kesamaan fungsional dengan RNP, tetapi juga memiliki sejumlah fitur yang sebelumnya hilang, misalnya, integrasi dengan gpg, serta menambal beberapa lubang keamanan dan memenuhi beberapa persyaratan non-fungsional.
Ketiga, Thunderbird bisa saja berhenti menggunakan OpenPGP sama sekali. Keputusan ini tidak cocok untukku. Namun pada beberapa kesempatan saya mengkhawatirkan keamanan pengguna Thunderbird yang paling rentan, dan saya yakin bahwa tidak memberikan dukungan OpenPGP sama sekali mungkin lebih aman daripada status quo.
Macleod VPS ideal untuk pengembangan API.
Daftar menggunakan tautan di atas atau dengan mengklik spanduk dan dapatkan diskon 10% untuk bulan pertama menyewa server dengan konfigurasi apa pun!