Runtime: Primitif tingkat rendah umum untuk cipher (AES-GCM menjadi yang pertama)

Dibuat pada 29 Agu 2017  ·  143Komentar  ·  Sumber: dotnet/runtime

Alasan

Ada kebutuhan umum untuk sejumlah cipher untuk enkripsi. Campuran antarmuka dan kelas saat ini menjadi sedikit terputus-putus. Juga tidak ada dukungan untuk cipher gaya AEAD karena mereka membutuhkan kemampuan untuk memberikan informasi otentikasi tambahan. Desain saat ini juga rentan terhadap alokasi dan ini sulit dihindari karena array yang kembali.

API yang Diusulkan

Kelas dasar abstrak tujuan umum yang akan diimplementasikan oleh kelas konkret. Ini akan memungkinkan untuk ekspansi dan juga dengan memiliki kelas daripada metode statis, kami memiliki kemampuan untuk membuat metode ekstensi serta menahan status di antara panggilan. API harus memungkinkan daur ulang kelas untuk memungkinkan alokasi yang lebih rendah (tidak memerlukan instance baru setiap kali, dan untuk menangkap katakanlah kunci yang tidak dikelola). Karena sifat sumber daya yang sering tidak dikelola yang dilacak, kelas harus mengimplementasikan IDisposable

public abstract class Cipher : IDisposable
{
    public virtual int TagSize { get; }
    public virtual int IVSize { get; }
    public virtual int BlockSize { get; }
    public virtual bool SupportsAssociatedData { get; }

    public abstract void Init(ReadOnlySpan<byte> key, ReadOnlySpan<byte> iv);
    public abstract void Init(ReadOnlySpan<byte> iv);
    public abstract int Update(ReadOnlySpan<byte> input, Span<byte> output);
    public abstract int Finish(ReadOnlySpan<byte> input, Span<byte> output);
    public abstract void AddAssociatedData(ReadOnlySpan<byte> associatedData);
    public abstract int GetTag(Span<byte> span);
    public abstract void SetTag(ReadOnlySpan<byte> tagSpan);
}

Contoh Penggunaan

(sumber input/output adalah aliran berbasis rentang mitos seperti sumber IO)

using (var cipher = new AesGcmCipher(bitsize: 256))
{
    cipher.Init(myKey, nonce);
    while (!inputSource.EOF)
    {
        var inputSpan = inputSource.ReadSpan(cipher.BlockSize);
        cipher.Update(inputSpan);
        outputSource.Write(inputSpan);
    }
    cipher.AddAssociatedData(extraInformation);
    cipher.Finish(finalBlockData);
    cipher.GetTag(tagData);
}

Perilaku API

  1. Jika tag get dipanggil sebelum selesai, [tipe pengecualian?] harus dilempar dan status internal harus disetel ke tidak valid
  2. Jika tag tidak valid saat selesai untuk mendekripsi, itu harus menjadi pengecualian yang dilemparkan
  3. Setelah selesai dipanggil, panggilan ke apa pun selain salah satu metode Init akan dilakukan
  4. Setelah Init dipanggil, panggilan kedua tanpa "penyelesaian" akan dilakukan
  5. Jika tipe mengharapkan kunci yang disediakan (contoh langsung "baru" ke atas) jika panggilan "Init" Awal hanya memiliki infus, ia akan melempar
  6. Jika jenis dihasilkan, katakan dari kunci berbasis toko dan Anda mencoba mengubah kunci melalui Init dan bukan hanya IV yang akan dibuang
  7. Jika tag get tidak dipanggil sebelum buang atau Init haruskah pengecualian dilempar? Untuk menghentikan pengguna yang tidak mengumpulkan tag secara tidak sengaja?

Referensi dotnet/corefx#7023

Pembaruan

  1. Diubah nonce menjadi IV.
  2. Menambahkan bagian perilaku
  3. Menghapus kasus rentang input/output tunggal dari penyelesaian dan pembaruan, itu hanya bisa menjadi metode ekstensi
  4. Mengubah sejumlah rentang menjadi readonlyspan seperti yang disarankan oleh @bartonjs
  5. Dihapus Reset, Init dengan IV harus digunakan sebagai gantinya
api-suggestion area-System.Security

Komentar yang paling membantu

@bartonjs Anda benar-benar mengabaikan semua analisis teknis dan logis dan alih-alih menggunakan tanggal proyek Go dan libsodium sebagai proxy yang lemah untuk analisis sebenarnya?

Tidak, saya menggunakan saran dari kriptografer profesional yang mengatakan itu sangat berbahaya dan bahwa kita harus menghindari streaming AEAD. Kemudian saya menggunakan informasi dari tim CNG tentang "banyak orang mengatakan mereka menginginkannya secara teori, tetapi dalam praktiknya hampir tidak ada yang melakukannya" (saya tidak tahu berapa banyak telemetri vs anekdot dari permintaan bantuan tangkas). Fakta bahwa perpustakaan lain telah mengambil rute satu kali hanya _memperkuat_ keputusan.

Mengapa permintaan yang ditunjukkan sejauh ini di GitHub tidak mencukupi?

Beberapa skenario telah disebutkan. Memproses buffer yang terfragmentasi mungkin dapat diatasi dengan menerima ReadOnlySequence , jika sepertinya ada cukup skenario untuk menjamin memperumit API alih-alih meminta penelepon melakukan perakitan ulang data.

File besar adalah masalah, tetapi file besar sudah menjadi masalah karena GCM memiliki batas hanya 64GB, yang "tidak terlalu besar" (oke, ini cukup besar, tapi bukan "wah, itu besar" yang itu dulu). File yang dipetakan memori akan memungkinkan Spans (hingga 2^31-1) untuk digunakan tanpa memerlukan RAM 2GB. Jadi kami telah mencukur beberapa bit dari maksimum... itu mungkin akan terjadi dari waktu ke waktu.

Anda menyadari bahwa memutuskan antarmuka non-streaming untuk AEAD menghalangi semua implementasi seperti itu, bukan?

Saya semakin yakin bahwa @GrabYourPitchforks benar (https://github.com/dotnet/corefx/issues/23629#issuecomment-334638891) bahwa mungkin tidak ada antarmuka pemersatu yang masuk akal. GCM _memerlukan_ nonce/IV dan SIV _melarang_ artinya inisialisasi mode/algoritma AEAD sudah membutuhkan pengetahuan tentang apa yang akan terjadi... sebenarnya tidak ada gagasan "abstrak" untuk AEAD. SIV menentukan ke mana "tag" pergi. GCM/CCM tidak. SIV adalah tag-first, berdasarkan spesifikasi.

SIV tidak dapat mulai mengenkripsi sampai memiliki semua data. Jadi enkripsi streamingnya akan dibuang (yang berarti Anda harus tahu untuk tidak memanggilnya) atau buffer (yang dapat menghasilkan n^2 waktu operasi). CCM tidak dapat dimulai sampai panjangnya diketahui; tetapi CNG tidak mengizinkan petunjuk pra-enkripsi panjangnya, jadi itu ada di kapal yang sama.

Kita seharusnya tidak mendesain komponen baru di mana lebih mudah untuk melakukan hal yang salah daripada hal yang benar secara default. Dekripsi streaming membuatnya sangat mudah dan menggoda untuk memasang kelas Stream (ala proposal Anda untuk melakukannya dengan CryptoStream) yang membuatnya sangat mudah untuk mendapatkan bug validasi data sebelum tag diverifikasi, yang hampir seluruhnya meniadakan manfaat AE . ( IGcmDecryptor => CryptoStream => StreamReader => XmlReader => "tunggu, itu bukan XML legal..." => adaptif ciphertext oracle) .

Ini langsung ke intinya ... permintaan pelanggan.

Sayangnya, seperti yang sudah terlalu sering saya dengar dalam hidup saya: Maaf, tapi Anda bukan pelanggan yang kami maksud. Saya akui bahwa mungkin Anda tahu bagaimana melakukan GCM dengan aman. Anda tahu untuk hanya melakukan streaming ke file/buffer/etc yang mudah menguap sampai setelah verifikasi tag. Anda tahu apa artinya manajemen nonce, dan Anda tahu risiko melakukan kesalahan. Anda tahu untuk memperhatikan ukuran aliran dan memotong ke segmen GCM baru setelah 2^36-64 byte. Anda tahu bahwa setelah semuanya dikatakan dan dilakukan, itu adalah kesalahan Anda jika Anda melakukan kesalahan.

Pelanggan yang saya pikirkan, di sisi lain, adalah seseorang yang tahu "Saya harus mengenkripsi ini" karena bos mereka menyuruh mereka. Dan mereka tahu bahwa ketika mencari cara melakukan enkripsi, beberapa tutorial mengatakan "selalu gunakan AE" dan menyebutkan GCM. Kemudian mereka menemukan tutorial "enkripsi dalam .NET" yang menggunakan CryptoStream. Mereka kemudian menghubungkan pipa, tidak tahu bahwa mereka baru saja melakukan hal yang sama seperti memilih SSLv2... mencentang kotak dalam teori, tetapi tidak benar-benar dalam praktiknya. Dan ketika mereka melakukannya _itu_ bug milik semua orang yang tahu lebih baik, tetapi biarkan hal yang salah terlalu mudah dilakukan.

Semua 143 komentar

Umpan balik cepat untuk menilai API yang diusulkan (berusaha membantu):

  • Span<T> tidak ada di NetStandard2 .
  • "Nonce" sangat spesifik untuk implementasi - mis. berbau GCM. Namun, bahkan dokumen GCM (mis. NIST SP800-38D) menyebutnya sebagai "IV", yang - dalam kasus GCM - kebetulan adalah nonce. Namun, dalam kasus implementasi AEAD lainnya, IV tidak harus nonce (mis. pengulangan IV di bawah CBC+HMAC bukanlah bencana).
  • Streaming AEAD harus bekerja dengan lancar dengan CryptoStream , atau menyediakan "AEADCryptoStream" sendiri yang semudah streaming masuk/keluar seperti CryptoStream.
  • Implementasi AEAD API harus diizinkan untuk melakukan derivasi kunci internal berdasarkan AAD (Data Terkait). Menggunakan AAD murni untuk penghitungan/verifikasi tag terlalu membatasi dan mencegah model AEAD yang lebih kuat.
  • Metode "Dapatkan*" harus mengembalikan sesuatu (GetTag). Jika batal, mereka harus mengatur sesuatu/berubah status.
  • Mencoba mendapatkan tag sebelum "selesai" mungkin merupakan ide yang buruk, jadi "Sudah Selesai" mungkin bisa membantu.
  • Orang-orang yang merancang ICryptoTransform memikirkan tentang penggunaan kembali, dukungan multi-blok, dan ukuran blok input/output dengan ukuran berbeda. Kekhawatiran ini tidak ditangkap.

Sebagai bukti kewarasan API AEAD, implementasi pertama dari API yang diusulkan tersebut tidak boleh AES-GCM, tetapi AES-CBC klasik/default dengan tag HMAC. Alasan sederhananya adalah siapa pun dapat membangun implementasi AES-CBC+HMAC AEAD hari ini , dengan kelas .NET sederhana yang sudah ada dan terkenal. Mari kita [AES-CBC+HMAC] lama yang membosankan mengerjakan API AEAD baru terlebih dahulu, karena itu mudah bagi semua orang untuk MVP dan uji coba.

Masalah penamaan nonce/IV adalah sesuatu yang saya ragu-ragu, senang dengan perubahan ke IV jadi akan berubah.

Adapun metode Get mengembalikan sesuatu, ini menghindari alokasi apa pun. Mungkin ada kelebihan Get() yang mengembalikan sesuatu. Mungkin memerlukan perubahan penamaan, tetapi saya cukup menikah dengan gagasan bahwa seluruh API pada dasarnya harus bebas alokasi.

Adapun aliran dll, saya tidak terlalu terganggu dengan itu karena mereka adalah API tingkat tinggi yang dapat dengan mudah dibangun dari primitif tingkat yang lebih rendah.

Mendapatkan Tag sebelum Anda selesai seharusnya tidak diizinkan, namun Anda harus tahu kapan Anda memanggil selesai, jadi saya tidak yakin itu harus di API, namun itu harus menjadi perilaku yang ditentukan jadi saya telah memperbarui desain API untuk disertakan bagian perilaku sehingga kami dapat menangkap orang lain yang dipikirkan.

Adapun cipher mana, saya tidak berpikir cipher spesifik apa pun yang harus menjadi satu-satunya target, untuk membuktikan API tujuan umum baru, itu harus sesuai dengan angka. AES GCM, dan CBC keduanya harus tercakup.

(semua umpan balik topik baik atau buruk selalu membantu!)

  • Kelas, atau antarmuka?
  • Bagaimana, jika ada, kelas SymmetricAlgorithm saat ini berinteraksi dengan ini?
  • Bagaimana ini akan digunakan untuk kunci yang bertahan, seperti yang dapat dilakukan TripleDESCng dan AesCng?
  • Banyak dari Rentang ini sepertinya bisa menjadi ReadOnlySpan.

@Drawae terima kasih telah membuat bola bergulir di API ini. Beberapa pemikiran:

  1. Pembuatan dan verifikasi tag adalah bagian yang sangat penting dari API ini karena penyalahgunaan tag dapat merusak seluruh tujuan. Jika memungkinkan, saya ingin melihat tag dibangun ke dalam operasi inisialisasi dan akhiri untuk memastikan mereka tidak dapat diabaikan secara tidak sengaja. Itu mungkin menyiratkan bahwa mengenkripsi dan mendekripsi tidak boleh menggunakan metode inisialisasi dan finalisasi yang sama.
  2. Saya memiliki perasaan campur aduk tentang mengeluarkan blok selama dekripsi sebelum sampai ke akhir karena data tidak dapat dipercaya sampai tag diperiksa (yang tidak dapat dilakukan sampai semua data diproses). Kita harus mengevaluasi tradeoff itu dengan sangat hati-hati.
  3. Apakah Reset diperlukan? Haruskah selesai hanya mengatur ulang? Kami melakukannya pada hash tambahan (tetapi mereka tidak membutuhkan infus baru)

@bartonjs

  1. Kelas, seperti yang sering terlihat di BCL dengan antarmuka, Anda tidak dapat mengembangkannya nanti tanpa merusak semuanya. Antarmuka seperti anak anjing seumur hidup ... kecuali metode antarmuka default dapat dianggap sebagai solusi untuk masalah itu.
    Juga kelas yang disegel dari tipe abstrak sebenarnya lebih cepat (sampai sekarang) karena jitt dapat mendevirtualisasi metode sekarang ... Jadi pada dasarnya gratis. pengiriman antarmuka tidak sebagus (masih bagus hanya saja tidak sebagus)
  2. Saya tidak tahu bagaimana Anda ingin itu bekerja? Saya memiliki sedikit minat pada hal-hal saat ini karena sangat membingungkan saya hanya akan menambal semua algo modern yang masuk akal langsung (tinggalkan 3DES di Kelas lain :) Tapi saya tidak memiliki semua jawaban jadi apakah Anda memiliki pemikiran lebih lanjut tentang ini?
  3. Kunci yang bertahan harus mudah. Buat metode ekstensi pada metode kunci atau penyimpanan persistensi.
MyKeyStore.GetCipher();

Ini tidak diinisialisasi. Ini sekali pakai sehingga referensi apa pun dapat dijatuhkan dengan pola sekali pakai yang normal. Jika mereka mencoba menyetel kunci, lemparkan pengecualian operasi yang tidak valid.

Ya untuk rentang baca-saja, saya akan menyesuaikan saat saya tidak menggunakan tabung di ponsel saya.

@morganbr tidak masalah... Saya hanya ingin melihatnya terjadi lebih dari apapun ;)

  1. Bisakah Anda memberikan cuplikan kode tentang cara Anda melihatnya bekerja? Tidak yakin, tetapi kode selalu memberikan kejelasan
  2. Sangat disayangkan tetapi Anda benar-benar harus mengeluarkan balok lebih awal. Dengan hmac dan hashing Anda tidak memilikinya tetapi Anda tidak memiliki data sementara hanya statusnya. Jadi dalam hal ini Anda harus buffer sejumlah data yang tidak diketahui. Mari kita lihat contoh pipeline dan TLS. Kita dapat menulis 16k plaintext tetapi buffer pipa saat ini seukuran halaman 4k. Jadi kami paling baik ingin mengenkripsi/mendekripsi 4*4k. Tentu Anda tidak memberi saya jawaban sampai akhir Anda perlu mengalokasikan memori internal untuk menyimpan semua itu dan kemudian saya anggap membuangnya ketika saya mendapatkan hasilnya? Atau Anda akan menghapusnya. Bagaimana jika saya mendekripsi 10mb dan Anda menyimpan memori itu setelah saya sekarang harus khawatir tentang penggunaan memori laten.
  3. Tidak 100% pada hal init/reset (bukan ide Anda, bentuk API saya saat ini) itu tidak cocok dengan saya jadi saya terbuka untuk saran baru!

Saya memiliki sedikit minat pada hal-hal saat ini karena sangat membingungkan saya hanya akan menambal semua algo modern yang masuk akal langsung (tinggalkan 3DES di Kelas lain :)

Masalahnya adalah format wadah seperti EnvelopedCms (atau EncryptedXml) mungkin perlu bekerja dengan 3DES-CBC, AES-CBC, dll. Seseorang yang ingin mendekripsi sesuatu yang dienkripsi dengan ECIES/AES-256-CBC-PKCS7/HMAC-SHA-2 -256 mungkin tidak akan merasa bahwa mereka melakukan hal-hal lama dan kasar.

Jika seharusnya hanya untuk AE, maka itu harus tercermin di suatu tempat dalam nama. Saat ini "sandi" generik (yang saya dulu/saya, di beberapa titik, akan duduk dengan kamus/glosarium dan mencari tahu apakah ada kata untuk "algoritma enkripsi dalam mode operasi", karena saya pikir itu "cipher" == "algoritma", oleh karena itu "Aes").

:) Saya hanya menunjukkan bahwa itu bukan area subjek saya atau sangat menarik bagi saya, jadi saya bersedia untuk tunduk kepada Anda dan komunitas tentang topik ini. Saya belum memikirkan implikasinya untuk ini.


Setelah memindai dengan cepat melalui ini, satu opsi adalah memungkinkan mereka untuk mengambil contoh dari "Cipher" atau apa pun itu disebut kelas. Ini mungkin tidak dilakukan di gelombang pertama, tetapi bisa dengan cepat menindaklanjutinya. Jika API sangat efisien maka saya tidak melihat alasan mereka harus melakukan hal mereka sendiri secara internal dan merupakan kasus penggunaan untuk API ini.

Sebagai bilah samping pada penamaan ... saya harus mengakui itu sulit
Openssl = sandi
Ruby = sandi
Go = paket cipher dengan antarmuka tipe bebek untuk AEAD dll
Java = sandi

Sekarang saya semua untuk menjadi berbeda tapi... ada tren. Jika sesuatu yang lebih baik mungkin, itu keren.

Mungkin "BlockModeCipher" ... ?

Saya telah membuat beberapa perubahan, saya akan mengubah penamaan jika nama yang lebih baik diputuskan.

Ketika saya mulai mencoba menjawab pertanyaan, saya menyadari bahwa API sudah kehilangan diferensiasi enkripsi/dekripsi, jadi dalam contoh Anda, ia tidak tahu apakah akan mengenkripsi atau mendekripsi data. Mendapatkan yang dimasukkan mungkin menambahkan beberapa kejelasan.

Saya dapat membayangkan beberapa cara agar API dapat menerapkan penggunaan tag yang tepat (berdasarkan asumsi bahwa ini adalah API AEAD, bukan hanya enkripsi simetris karena kami telah memiliki SymmetricAlgorithm/ICryptoTransform/CryptoStream). Jangan menganggap ini sebagai preskriptif, hanya sebagai contoh penegakan penandaan.
Dengan metode:

class Cipher
{
   void InitializeEncryption(ReadOnlySpan<byte> key, ReadOnlySpan<byte> iv);
   // Ensures decryptors get a tag
   void InitializeDecryption(ReadOnlySpan<byte> key, ReadOnlySpan<byte> iv, ReadOnlySpan<byte> tag);
   // Ensure encryptors produce a tag
    void FinishEncryption(ReadOnlySpan<byte> input, Span<byte> output, Span<byte> tag);
   // Throws if tag didn't verify
   void FinishDecryption(ReadOnlySpan<byte> input, Span<byte> output);
   // Update and properties are unchanged, but GetTag and SetTag are gone
}

Menurut kelas:

class Cipher
{
    // Has properties and update, but Initialize and Finish aren't present
}
class Encryptor : Cipher
{
    void Initialize(ReadOnlySpan<byte> key, ReadOnlySpan<byte> iv);
    void Finish(ReadOnlySpan<byte> input, Span<byte> output, Span<byte> tag);
}
class Decryptor : Cipher
{
   void Initialize(ReadOnlySpan<byte> key, ReadOnlySpan<byte> iv, ReadOnlySpan<byte> tag);
   // Throws if tag didn't verify
   void Finish(ReadOnlySpan<byte> input, Span<byte> output);
}
class AesGCMEncryptor : Encryptor {}
class AesGCMDecryptor : Decryptor {}
}

Yang mengatakan, jika tidak buffer pada dekripsi, apakah praktis untuk memastikan dekripsi benar-benar selesai dan tag diperiksa? Bisakah Pembaruan entah bagaimana mengetahui bahwa sudah waktunya untuk memeriksa? Apakah itu sesuatu yang harus dilakukan Buang? (Buang agak berbahaya karena Anda mungkin sudah mempercayai data pada saat Anda membuang objek)

Sejauh penamaan, preseden kami adalah SymmetricAlgorithm dan AsymmetricAlgorithm. Jika ini ditujukan untuk AEAD, beberapa ide dapat berupa AuthenticatedSymmetricAlgorithm atau AuthenticatedEncryptionAlgorithm.

Beberapa pemikiran & ide API:

public interface IAEADConfig
{
    // size of the input block (plaintext)
    int BlockSize { get; }

    // size of the output per input-block;
    // typically a multiple of BlockSize or equal to BlockSize.
    int FeedbackSize { get; }

    // IV size; CAESAR completition uses a fixed-length IV
    int IVSize { get; }

    // CAESAR competition uses a fixed-length key
    int KeySize { get; }

    // CAESAR competition states that typical AEAD ciphers have a constant gap between plaintext length
    // and ciphertext length, but the requirement is to have a constant *limit* on the gap.
    int MaxTagSize { get; }

    // allows for AE-only algorithms
    bool IsAdditionalDataSupported { get; }
}

public interface ICryptoAEADTransform : ICryptoTransform
{
    // new AEAD-specific ICryptoTransform interface will allow CryptoStream implementation
    // to distinguish AEAD transforms.
    // AEAD decryptor transforms should throw on auth failure, but current CryptoStream
    // logic swallows exceptions.
    // Alternatively, we can create a new AEAD_Auth_Failed exception class, and
    // CryptoTransform is modified to catch that specific exception.
}

public interface IAEADAlgorithm : IDisposable, IAEADConfig
{
    // separates object creation from initialization/keying; allows for unkeyed factories
    void Initialize(ArraySegment<byte> key);

    void Encrypt(
        ArraySegment<byte> iv, // readonly; covered by authentication
        ArraySegment<byte> plaintext, // readonly; covered by authentication
        ref ArraySegment<byte> ciphertext, // must be of at least [plaintext_length + MaxTagSize] length. iv is not part of ciphertext.
        ArraySegment<byte> additionalData = default(ArraySegment<byte>) // readonly; optional; covered by authentication
        ); // no failures expected under normal operation - abnormal failures will throw

    bool Decrypt(
        ArraySegment<byte> iv, // readonly
        ArraySegment<byte> ciphertext, // readonly
        ref ArraySegment<byte> plaintext, // must be of at least [ciphertext_length - MaxTagSize] length.
        ArraySegment<byte> additionalData = default(ArraySegment<byte>), // readonly; optional
        bool isAuthenticateOnly = false // supports Authentication-only mode
        );// auth failures expected under normal operation - return false on auth failure; throw on abnormal failure; true on success

    /*  Notes:
        * Array.LongLength should be used instead of Array.Length to accomodate byte arrays longer than 2^32.
        * Ciphertext/Plaintext produced by Encrypt()/Decrypt() must be determined *only* by method inputs (combined with a key).
          - (ie. if randomness or other hidden inputs are needed, they must be a part of iv)
        * Encrypt()/Decrypt() are allowed to write to ciphertext/plaintext segments under all conditions (failure/success/abnormal)
          - some implementations might be more stringent than others, and ex. not leak decrypted plaintext on auth failures
    */

    ICryptoAEADTransform CreateEncryptor(
        ArraySegment<byte> key,
        ArraySegment<byte> iv,
        ArraySegment<byte> additionalData = default(ArraySegment<byte>)
        );

    ICryptoAEADTransform CreateDecryptor(
        ArraySegment<byte> key,
        ArraySegment<byte> iv,
        ArraySegment<byte> additionalData = default(ArraySegment<byte>)
        );

    // Streaming AEAD can be done with good-old CryptoStream
    // (possibly modified to be AEAD-aware).
}

@sdrapkin , terima kasih atas kontribusi pemikirannya. Ke mana tag harus pergi di API Anda? Apakah mereka secara implisit bagian dari ciphertext? Jika demikian, itu menyiratkan protokol yang sebenarnya tidak dimiliki beberapa algoritme. Saya ingin memahami protokol apa yang mungkin diperhatikan orang untuk melihat apakah penempatan tag implisit dapat diterima atau perlu dilakukan secara terpisah.

@morganbr Saya perhatikan masalah enkripsi/dekripsi juga tetapi tidak punya waktu malam ini untuk memperbaikinya, sangat senang dengan desain Anda. Saya lebih suka metode daripada kelas karena memungkinkan daur ulang agresif yang lebih baik (buffer untuk kunci dan IV dapat bertambah).

Adapun cek sebelum dibuang. Sayangnya, tidak mungkin untuk mengetahui akhir dari suatu operasi.

Antarmuka @sdrapkin yang menurut saya tidak boleh digunakan karena masalah versi yang disebutkan sebelumnya. Kecuali jika kita mengandalkan implantasi default di masa depan. Juga pengiriman antarmuka lebih lambat .. segmen array juga kami karena rentang adalah primitif bawah yang lebih fleksibel. Namun metode ekstensi dapat ditambahkan untuk rentang array jika ada permintaan nanti.

Beberapa properti Anda menarik sehingga akan diperbarui saat saya di depan komputer daripada di ponsel saya.

Umpan balik yang baik semua bulat!

@morganbr Tag adalah bagian dari ciphertext. Ini dimodelkan setelah CAESAR API (yang mencakup AES-GCM).

@Drawaes Saya telah menggunakan antarmuka untuk mengilustrasikan pemikiran saja - Saya baik-baik saja dengan metode/kelas statis. Menjangkautidak ada. Saya tidak peduli tentang apa yang mungkin atau mungkin tidak akan datang - itu tidak ada di NetStandard2, dan tidak dalam .NET normal yang benar-benar digunakan oleh proyek-proyek serius (ya, ya, saya tahu itu ada di Core, tetapi Core adalah mainan untuk sekarang). Saya akan dengan senang hati mempertimbangkan Spanketika saya melihatnya - sampai saat itu ArraySegmentadalah NetStandard API terdekat yang benar-benar dikirimkan.

Saya akan melihat lebih dalam CAESAR API yang berguna.

Adapun Span, pengirimannya sekitar 2.1 kerangka waktu yang saya yakini merupakan waktu yang sama saat implementasi pertama dari API ini akan dikirimkan (atau setidaknya waktu sedini mungkin).

Jika Anda melihat paket nuget prarilis saat ini, paket ini mendukung hingga .net standar 1.0 dan tidak ada rencana untuk mengubahnya saat rilis.

Mungkin @stephentoub dapat mengonfirmasi bahwa saat dia melakukan pekerjaan untuk menambahkan API berbasis Span di seluruh kerangka saat kita berbicara.

(Nuget untuk Span)[https://www.nuget.org/packages/System.Memory/4.4.0-preview2-25405-01]

Jadi saya akan mengatakan itu satu-satunya pilihan nyata untuk API baru. Kemudian metode ekstensi dll dapat ditambahkan untuk mengambil ArraySegment jika Anda memilih demikian dan jika itu cukup berguna maka dapat ditambahkan ke kerangka kerja tetapi sepele untuk mengubah ArraySegment menjadi Span, tetapi cara lain memerlukan penyalinan data.

Masalah yang saya lihat dengan API di atas adalah akan menjadi bencana bagi kinerja pada data "terpotong". Katakanlah misalnya lalu lintas jaringan, jika satu blok yang diautentikasi dibagi menjadi beberapa pembacaan dari aliran yang ada, saya perlu menyangga semuanya menjadi satu [masukkan struktur data] dan mengenkripsi/mendekripsi sekaligus. Mengalahkan semua upaya untuk melakukan segala jenis salinan nol pada data itu.

Kerangka kerja jaringan seperti yang disediakan Pipelines berhasil menghindari hampir semua salinan, tetapi kemudian jika mereka mengenai jenis kripto apa pun di API ini, semua itu hilang.

Objek konfigurasi terpisah (atau tas) sebenarnya telah terlibat dalam diskusi baru-baru ini tentang API lain yang saya alami. Saya tidak menentangnya pada prinsipnya seolah-olah tumbuh di masa depan dapat menjadi berantakan untuk memiliki sejumlah besar properti pada objek utama.

Beberapa hal telah terjadi pada saya.

  • Proposal saat ini menyebut TagSize sebagai nilai keluar (well, properti get-only). Tetapi untuk GCM dan CCM, ini merupakan input untuk enkripsi (dan dapat diturunkan dari dekripsi karena Anda telah memberikan tag yang sebenarnya).
  • Usulan tersebut mengasumsikan bahwa input dan output dapat terjadi secara bersamaan, dan sedikit demi sedikit.

    • IIRC CCM tidak dapat melakukan enkripsi streaming (panjang plainteks merupakan input ke langkah pertama algoritma).

    • Mode empuk tertinggal dekripsi oleh (setidaknya satu) blok, karena hingga Final dipanggil, mereka tidak tahu apakah lebih banyak data akan datang / jika blok saat ini perlu bantalan dihapus

  • Beberapa algoritme mungkin menganggap elemen AD diperlukan pada awal operasi, membuatnya lebih seperti parameter Init/ctor daripada asosiasi terikat akhir.

Saya tidak tahu apakah format wadah (EnvelopedCms, EncryptedXml) perlu mengekstrak kunci, atau apakah terserah mereka untuk membuatnya dan mengingatnya (selama mereka perlu menuliskannya).

(Rupanya saya tidak menekan tombol "komentar" kemarin, jadi itu tidak akan mengakui apa pun setelah "Saya telah membuat beberapa perubahan" di 1910Z)

Ukuran tag yang sebenarnya harus bervariasi. Sepakat.

Jika kita hanya melihat enkripsi untuk saat ini, untuk menyederhanakan use case. Anda benar bahwa beberapa sandi tidak akan menghasilkan apa-apa, kurang atau lebih. Ada pertanyaan umum seputar apa yang terjadi jika Anda tidak menyediakan buffer yang cukup besar.

Pada antarmuka TextEncoding baru yang menggunakan span, ada saran agar tipe pengembalian enum untuk menentukan apakah ada cukup ruang untuk menghasilkan atau tidak, dan ukuran sebenarnya ditulis dalam parameter "keluar". Ini adalah kemungkinan.

Dalam kasus CCM saya hanya akan mengatakan itu tidak mengembalikan apa-apa dan harus buffer internal sampai Anda menelepon selesai pada titik mana ia ingin membuang semuanya. Tidak ada yang menghalangi Anda untuk hanya memanggil finish sebagai panggilan pertama Anda jika Anda memiliki semua data dalam satu blok (Dalam hal ini mungkin ada nama yang lebih baik). Atau dimungkinkan untuk membuang jika Anda mencoba pembaruan pada cipher tersebut. CNG mengembalikan kesalahan ukuran yang tidak valid jika Anda mencoba melakukan kelanjutan di CCM misalnya.

Adapun ketika tag diatur pada dekripsi, Anda sering tidak mengetahuinya sampai Anda membaca seluruh paket, jika kita mengambil TLS sebagai contoh kita mungkin harus membaca 8 * 2k paket jaringan untuk sampai ke tag di akhir dari blok 16k. Jadi kita sekarang harus menyangga seluruh 16k sebelum kita dapat memulai dekripsi dan tidak ada kesempatan untuk tumpang tindih (saya tidak mengatakan ini akan digunakan untuk TLS hanya saja proses terikat IO umum untuk jenis sandi ini, baik itu disk atau jaringan).

@Drawes re. aliran chunked dan batas buffering:
Anda harus memilih pertempuran Anda. Anda tidak akan dapat membuat API pemersatu yang selaras dengan setiap tujuan yang bagus untuk dimiliki di dunia AE - dan ada banyak tujuan seperti itu. Mantan. ada streaming AEAD chunked yang layak di Inferno , tapi itu bukan standar, dan standar seperti itu tidak ada. Pada tingkat yang lebih tinggi, tujuannya adalah "saluran aman" (lihat this , this , dan this ).

Namun, kita perlu berpikir lebih kecil untuk saat ini. Chunking/buffer-limiting bahkan tidak ada di radar untuk upaya standardisasi (bagian " AEADs with large plaintexts ")..

Operasi Enkripsi/Dekripsi pada dasarnya adalah tentang transformasi. Transformasi ini memerlukan buffer, dan tidak di tempat (buffer output harus lebih besar dari buffer input - setidaknya untuk transformasi Enkripsi).

RFC 5116 mungkin juga menarik.

@Drawaes , menarik bahwa Anda mengangkat TLS. Saya berpendapat bahwa SSLStream (jika menggunakan API ini) tidak boleh mengembalikan hasil yang tidak diautentikasi ke aplikasi karena aplikasi tidak akan memiliki cara untuk mempertahankan dirinya sendiri.

Tentu, tapi itu masalah SSLStreams. Saya telah membuat prototipe hal yang tepat ini (mengelola TLS pada tingkat protokol yang memanggil CNG dan OpenSSL untuk bit kripto) di atas saluran pipa. Logikanya cukup sederhana, data terenkripsi masuk, dekripsi buffer di tempatnya, lampirkan ke outbound dan ulangi sampai Anda mendapatkan tag. Pada panggilan tag selesai...

Jika melempar menutup pipa. Jika tidak melempar maka flush memungkinkan tahap berikutnya untuk bekerja baik pada utas yang sama atau melalui pengiriman.

Bukti konsep saya belum siap untuk primetime tetapi dengan menggunakan ini dan menghindari banyak salinan dll itu menunjukkan peningkatan kinerja yang sangat baik;)

Masalah dengan jaringan apa pun adalah di mana hal-hal dalam pipa mulai mengalokasikan buffer mereka sendiri dan tidak menggunakan sebanyak mungkin buffer yang sudah bergerak melalui sistem.

crypt OpenSsl, dan CNG memiliki metode yang sama Perbarui, Perbarui, Selesai. Selesai bisa ouput tag seperti yang dibahas. Pembaruan harus dalam ukuran blok (untuk CNG) dan untuk OpenSsl melakukan buffering minimal untuk mencapai ukuran blok.

Karena mereka primitif, saya tidak yakin kita akan mengharapkan fungsionalitas tingkat yang lebih tinggi dari mereka. Jika kami merancang API level "pengguna" daripada primitif untuk membangunnya, saya akan berpendapat bahwa pembuatan kunci, konstruksi IV, dan seluruh potongan yang diautentikasi semuanya harus diimplementasikan, jadi saya kira itu tergantung pada level target API ini sebenarnya.

Tombol yang salah

@blowdart , yang memiliki beberapa ide menarik tentang manajemen nonce.

Jadi manajemen nonce pada dasarnya adalah masalah pengguna dan khusus untuk pengaturan mereka.

Jadi... jadikan itu syarat. Anda harus memasukkan manajemen nonce ... dan tidak menyediakan implementasi default, atau implementasi apa pun sama sekali. Ini, bukan yang sederhana

cipher.Init(myKey, nonce);

memaksa pengguna untuk membuat isyarat tertentu yang memahami risikonya.

Ide @blowdart mungkin membantu dengan masalah manajemen nonce dan perbedaan antara algoritma. Saya setuju bahwa sepertinya penting untuk tidak memiliki implementasi bawaan untuk memastikan bahwa pengguna memahami bahwa manajemen nonce adalah masalah yang harus mereka pecahkan. Bagaimana sesuatu seperti ini terlihat?

interface INonceProvider
{
    public void GetNextNonce(Span<byte> writeNonceHere);
}

class AesGcmCipher : Cipher
{
    public AesGcmCipher(ReadOnlySpan<byte> key, INonceProvider nonceProvider);
}

// Enables platform-specific hardware keys
class AesGcmCng : Cipher
{
    public AesGcmCng(CngKey key, INonceProvider nonceProvider);
}

// Example of AEAD that might not need a nonce
class AesCBCHmac : Cipher
{
    public AesCBC(ReadOnlySpan<byte> key)
}

class Cipher
{
    // As above, but doesn't take keys, IVs, or nonces
}

Tapi apa gunanya INonceProvider? Ini hanya antarmuka/tipe tambahan, jika Init hanya membutuhkan tidak ada dan perlu dipanggil sebelum Anda memulai blok apa pun, bukankah itu hal yang sama tanpa antarmuka/tambahan?

Saya juga bukan ahli kripto tetapi tidak AES memerlukan IV (Yang bukan nonce tetapi perlu disediakan oleh pengguna?)

Ini hanya antarmuka/tipe tambahan

Itu intinya. Ini pada dasarnya mengatakan bahwa _nonce management_ adalah masalah, tidak hanya melewatkan array byte yang nol atau bahkan acak. Mungkin juga membantu mencegah penggunaan kembali yang tidak disengaja jika orang menafsirkan GetNextNonce berarti "mengembalikan sesuatu yang berbeda dari yang Anda lakukan terakhir kali".

Ini juga membantu untuk tidak membutuhkannya untuk algoritme yang tidak memiliki masalah manajemen nonce (seperti AES SIV atau mungkin AES+CBC+HMAC).

Persyaratan IV/nonce yang tepat bervariasi berdasarkan mode. Sebagai contoh:

  • AES ECB tidak memerlukan nonce atau IV
  • AES GCM memerlukan nonce 96-bit yang tidak boleh digunakan kembali atau keamanan kunci yang rusak. Entropi rendah baik-baik saja selama nonce tidak digunakan kembali.
  • AES CBC membutuhkan 128-bit IV yang harus acak. Jika IV berulang, itu hanya mengungkapkan apakah pesan yang sama telah dikirim sebelumnya.
  • AES SIV tidak memerlukan IV eksplisit karena ia memperolehnya dari input lain.

AES CBC butuh IV kan? Jadi, apakah Anda akan memiliki InitializationVectorProvider? Ini bukan nonce tapi
tidak suka dan menggunakan kembali blok terakhir menyebabkan serangan tls karena iv dapat diprediksi. Anda secara eksplisit tidak dapat menggunakan say a nonce berurutan untuk CBC.

Ya, tetapi infus bukan nonce sehingga Anda tidak dapat memiliki istilah penyedia nomce

Saya tidak bermaksud menyiratkan bahwa AES CBC tidak membutuhkan infus-- memang demikian. Saya hanya bermaksud berspekulasi tentang beberapa skema yang mendapatkan IV dari data lain.

Tentu saya kira maksud saya adalah saya menyukainya secara umum ... Saya dapat menggabungkan penyedia;) tetapi menyebutnya sebagai penyedia iv atau memiliki 2 antarmuka untuk memperjelas niat

@morganbr INonceProvider pabrik yang diteruskan ke konstruktor sandi adalah desain yang buruk. Ini benar-benar melewatkan fakta bahwa _nonce_ tidak ada dengan sendirinya: batasan "_...digunakan sekali_" memiliki _context_. Dalam kasus mode CTR dan GCM (yang menggunakan CTR), _context_ dari _nonce_ adalah _key_. Yaitu. _nonce provider_ harus mengembalikan nonce yang digunakan hanya sekali dalam konteks _key_ tertentu.

Karena INonceProvider dalam API yang Anda usulkan tidak mengetahui kunci, itu tidak dapat menghasilkan nonce yang benar (selain melalui keacakan, yang bukan merupakan nonce, bahkan jika ruang bit cukup besar untuk keacakan statistik untuk bekerja dengan aman).

Saya tidak sepenuhnya yakin apa yang ingin dicapai oleh utas diskusi ini. Berbagai ide desain Authenticated-Encryption didiskusikan... ok. Bagaimana dengan antarmuka Enkripsi Terotentikasi yang sudah ada di .NET Core -- khususnya ke dalam ASP.NET API-nya? Ada IAuthenticatedEncryptor , dll. Semua kemampuan ini sudah diterapkan, diperluas, dan dikirimkan sebagai bagian dari .NET Core hari ini. Saya tidak mengatakan kripto DataProtection sempurna, tetapi apakah ada rencana untuk mengabaikannya? Ubah? Asimilasi atau refactor?

DataProtection crypto dibangun oleh @GrabYourPitchforks (Levi Broderick). Dia tahu materi pelajarannya, dan pendapat/masukan/masukannya akan sangat berharga bagi komunitas ini. Saya menikmati hiburan bertema kripto sama seperti siapa pun, tetapi jika seseorang ingin serius dengan desain API kripto, maka pakar sebenarnya yang sudah ada di tim MS harus dilibatkan.

@sdrapkin , penyedia nonce yang perlu menyadari kunci adalah poin yang bagus. Saya ingin tahu apakah ada cara yang masuk akal untuk memodifikasi API ini untuk menegakkannya.

DataProtection adalah API yang bagus, tetapi ini adalah konstruksi tingkat yang lebih tinggi. Ini merangkum pembuatan dan manajemen kunci, infus dan protokol keluaran. Jika seseorang perlu menggunakan (katakanlah) GCM dalam implementasi protokol yang berbeda, DataProtection tidak memungkinkannya.

Tim crypto .NET termasuk @bartonjs , @blowdart dan saya sendiri. Tentu saja, jika @GrabYourPitchforks ingin bergabung, dia sangat disambut.

Saya setuju dengan @morganbr karena ini seharusnya primitif tingkat rendah (sebenarnya dikatakan demikian dalam judul). Sementara perlindungan data dll dirancang untuk digunakan secara langsung dalam kode pengguna dan mengurangi kemampuan untuk menembak diri sendiri, cara saya melihat primitif ini adalah untuk memungkinkan kerangka kerja dan perpustakaan untuk membangun konstruksi tingkat yang lebih tinggi pada basis yang sama.

Dengan pemikiran itu, penyedia baik-baik saja jika harus diberikan kapan saja kunci diberikan. Itu membuatnya sedikit berantakan, izinkan saya menjelaskan menggunakan TLS (ini adalah penggunaan mode blok AES yang terkenal untuk lalu lintas jaringan).

Saya mendapatkan "bingkai" (mungkin lebih dari 2 + TU dengan MTU ~ 1500 internet). Ini berisi nonce (atau bagian dari nonce dengan 4 byte tersisa "tersembunyi") Saya kemudian harus menetapkan nilai ini pada "penyedia" Shell dan kemudian memanggil decrypt dan melalui siklus saya mendekripsi buffer untuk mendapatkan satu teks biasa .

Jika Anda senang dengan itu, saya bisa hidup dengan itu. Saya ingin agar ini terus berjalan sehingga ingin memperbarui desain di atas menjadi sesuatu yang dapat kita sepakati.

Terima kasih untuk forking diskusi, mendapatkan beberapa waktu luang untuk melompat ke ini. @Drawaes , dapatkah Anda mengonfirmasi/memperbarui posting teratas sebagai standar / target utama dari percakapan yang berkembang ini? Jika tidak, dapatkah Anda memperbaruinya?

Saya melihat proposal saat ini memiliki masalah fatal dan kemudian masalah lain dengan terlalu cerewet.

// current proposed usage
using (var cipher = new AesGcmCipher(bitsize: 256))
{
    cipher.Init(myKey, nonce);
    while (!inputSource.EOF)
    {
        var inputSpan = inputSource.ReadSpan(cipher.BlockSize);
        cipher.Update(inputSpan);
        outputSource.Write(inputSpan);
    }
    cipher.AddAssociatedData(extraInformation); // <= fatal, one can't just do this
    cipher.Finish(finalBlockData);
    cipher.GetTag(tagData);
}

Jika Anda melihat primitif AEAD yang sebenarnya, data privasi dan data yang diautentikasi adalah langkah kunci campuran. Lihat ini untuk Auth Data 1 dan CipherText1. Ini tentu saja berlanjut untuk beberapa blok, bukan hanya 1.highlighted

Karena seluruh dunia adalah meme, tidak bisa menahan, maaf :)
Can't resist

Juga, API tampaknya cerewet dengan yang baru, init, pembaruan, dll. Saya akan mengusulkan model programmer ini

// proposed, see detailed comments below
using (var cipher = new AesGcmCipher(myKey, iv, aad)) // 1
{
    // 2
    while (!inputSource.EOF) 
    {
        var inputSpan = inputSource.ReadSpan(16411); // 3
        var outSpan = cipher.Encrypt(inputSpan); // 4
        outputSource.Write(outSpan); 
    }    
    var tag = cipher.Finish(finalBlockData); // 5
}
  1. Biasanya AAD << plaintext jadi saya telah melihat cipher.Init(mykey, nonce, aad); di mana seluruh AAD dilewatkan sebagai buffer dan kemudian cipher mengerjakan sisa aliran gigabyte+ yang berpotensi. (misalnya param CipherModeInfo BCryptEncrypts ) . Juga, ukuran myKey sudah menetapkan AES128, 192, 256, tidak perlu parameter lain.
  2. Init menjadi API opsional jika pemanggil ingin menggunakan kembali kelas yang ada, konstanta AES yang ada, dan melewatkan pembuatan subkunci AES jika kunci AES sama
  3. cipher's API harus melindungi penelepon dari internal manajemen ukuran blok seperti kebanyakan API kripto lainnya atau bahkan API .NET yang ada. Penelepon yang peduli dengan ukuran buffer yang dioptimalkan untuk kasus penggunaannya, misalnya IO jaringan melalui buffer 16K+). Demo dengan bilangan prima > 16K untuk menguji asumsi implementasi
  4. inputSpan hanya dapat dibaca. Dan masukan. Jadi butuh outSpan
  5. Perbarui () apakah Enkripsi atau Dekripsi? Cukup miliki antarmuka Enkripsi dan Dekripsi agar sesuai dengan model mental pengembang. tag juga merupakan data terpenting yang diinginkan saat ini, kembalikan itu.

Sebenarnya melangkah lebih jauh, mengapa tidak

using (var cipher = new AesGcmCipher(myKey, iv, aad))
{
    var tag = cipher.EncryptFinal(inputSpan, outputSpan);
}

Juga, tolong jauhi INonceProvider dan sejenisnya. Primitif kripto tidak membutuhkan ini, cukup gunakan byte[] iv (favorit saya untuk data kecil) atau Span (yang seharusnya baru keren tapi terlalu banyak abstraksi IMHO). Penyedia nonce beroperasi pada lapisan di atas dan hasilnya bisa saja iv terlihat di sini.

Masalah dengan membuat primitif begitu primitif adalah orang akan menggunakannya secara tidak benar. Dengan penyedia kami setidaknya bisa memaksakan beberapa pemikiran ke dalam penggunaannya.

Kita berbicara tentang AEAD secara umum yang khusus untuk GCM. Jadi pertama, kasus umum ( iv ) harus mendorong desain, bukan kasus khusus ( nonce ).

Kedua, bagaimana hanya berpindah dari byte[] iv ke GetNextNonce(Span<byte> writeNonceHere) benar-benar menyelesaikan masalah nonce? Anda hanya mengubah nama/label pada masalah sekaligus membuatnya lebih kompleks dari yang seharusnya.

Ketiga, karena kita masuk ke kebijakan tentang perlindungan iv haruskah kita juga masuk ke kebijakan perlindungan utama? Bagaimana dengan kebijakan distribusi utama? Itu jelas merupakan kekhawatiran tingkat yang lebih tinggi.

Akhirnya, nonce sangat situasional pada penggunaan di lapisan yang lebih tinggi. Anda tidak ingin memiliki arsitektur yang rapuh di mana masalah lintas lapisan digabungkan bersama.

Terus terang jika kita bisa menyembunyikan primitif kecuali seseorang membuat isyarat untuk mengatakan saya tahu apa yang saya lakukan, saya akan mendorong untuk itu. Tapi kita tidak bisa. Ada terlalu banyak implementasi crypto yang buruk di luar sana karena orang-orang berpikir "Oh ini tersedia, saya akan menggunakannya". Lihat AES itu sendiri, saya hanya akan menggunakannya tanpa HMAC.

Saya ingin melihat API aman secara default, dan jika itu berarti sedikit lebih menyakitkan maka sejujurnya saya setuju. 99% pengembang tidak tahu apa yang mereka lakukan ketika datang ke crypto dan memudahkan 1% yang melakukannya harus menjadi prioritas yang lebih rendah.

Rentang tidak ada. Saya tidak peduli tentang apa yang mungkin atau mungkin tidak akan datang - itu tidak ada di NetStandard2

@sdrapkin seperti yang ditunjukkan oleh @Drawaes Span<T> adalah .NET Standard 1.0 sehingga dapat digunakan pada kerangka kerja apa pun. Ini juga lebih aman daripada ArraySegment<T> karena hanya memungkinkan Anda mengakses jendela aktual yang dirujuk; daripada seluruh array.

Juga ReadOnlySpan<T> mencegah modifikasi pada jendela itu; lagi tidak seperti segmen array di mana apa pun yang diteruskan dapat memodifikasi dan/atau mempertahankan referensi ke array yang diteruskan.

Span harus menjadi tujuan umum untuk sinkronisasi api (Fakta bahwa api menggunakan Span juga dapat mengatasi stackalloc'd, memori asli serta array; adalah lapisan gulanya)

yaitu
Dengan ArraySegment readonly disarankan melalui docs; dan tidak ada pembacaan/modifikasi di luar batas yang dicegah

void Encrypt(
    ArraySegment<byte> iv, // readonly; covered by authentication
    ArraySegment<byte> plaintext, // readonly; covered by authentication
    ref ArraySegment<byte> ciphertext, // must be of at least [plaintext_length + MaxTagSize] length. iv is not part of ciphertext.
    ArraySegment<byte> additionalData = default(ArraySegment<byte>) // readonly; optional; covered by authentication
    );

Namun dengan Span readonly diberlakukan oleh api; serta pembacaan di luar batas dari array yang dicegah

void Encrypt(
    ReadOnlySpan<byte> iv, // covered by authentication
    ReadOnlySpan<byte> plaintext, // covered by authentication
    Span<byte> ciphertext, // must be of at least [plaintext_length + MaxTagSize] length. iv is not part of ciphertext.
    ReadOnlySpan<byte> additionalData = ReadOnlySpan<byte>.Empty) // optional; covered by authentication
    );

Ini menyampaikan maksud dengan parameter jauh lebih baik; dan lebih sedikit rawan kesalahan sehubungan dengan membaca/menulis di luar batas.

@benaadams @Drawaes tidak pernah mengatakan bahwa Span<T> ada di NetStandard ( NetStandard apa pun yang dikirimkan ). Apa yang dia katakan adalah (1) setuju bahwa Span<T> tidak ada dalam NetStandard yang dikirimkan; (2) bahwa Span<T> akan _"dikirim pada rentang waktu 2.1"_.

Namun, untuk masalah Github khusus ini, (hanya baca) Span<T> diskusi saat ini sedang berlangsung - tidak ada kejelasan tentang ruang lingkup atau tujuan dari API yang akan dirancang.

Entah kita menggunakan API AEAD primitif tingkat rendah mentah (mis. mirip dengan CAESAR):

  • Kelebihan: cocok untuk AES-GCM/CCM, vektor uji yang ada dari sumber yang baik (NIST, RFC). @sidshetye akan senang. @blowdart akan bermeditasi tentang _"membuat primitif begitu primitif"_, tetapi pada akhirnya akan melihat Yin dan Yang karena primitif adalah primitif dan tidak ada cara untuk melindungi mereka.
  • Kontra: Pengguna ahli (pepatah 1%) akan menggunakan API tingkat rendah secara bertanggung jawab, sementara pengguna non-ahli lainnya (99%) akan menyalahgunakannya untuk menulis perangkat lunak .NET yang rusak yang akan bertanggung jawab atas sebagian besar .NET CVE, yang akan sangat berkontribusi pada persepsi bahwa .NET adalah platform yang tidak aman.

Atau kami menggunakan API AEAD tingkat tinggi yang tidak mungkin disalahgunakan atau yang tahan:

  • Kelebihan: 99% pengguna non-ahli akan terus membuat kesalahan, tetapi setidaknya tidak dalam kode AEAD. @blowdart _"Saya ingin melihat API aman secara default"_ bergema secara mendalam di ekosistem, dan keamanan, kemakmuran, dan karma baik menimpa semuanya. Banyak desain & implementasi API yang bagus sudah tersedia.
  • Kekurangan: Tidak ada standar. Tidak ada vektor uji. Tidak ada konsensus tentang apakah AEAD bahkan merupakan tujuan yang tepat untuk ditargetkan untuk API streaming online tingkat tinggi (spoiler: bukan - lihat makalah Rogawaway ).

Atau, kita melakukan keduanya. Atau kita memasuki kelumpuhan analisis dan mungkin juga menutup masalah ini sekarang.

Saya sangat merasa bahwa menjadi bagian dari bahasa inti, crypto perlu memiliki API dasar yang solid dan tingkat rendah. Setelah Anda memilikinya, membuat API tingkat tinggi atau API "roda pelatihan" dapat dijembatani dengan cepat oleh inti atau komunitas. Tapi saya menantang siapa pun untuk melakukan kebalikannya dengan elegan. Ditambah topiknya adalah " Umum tingkat rendah primitif untuk cipher " !

@Drawaes apakah ada garis waktu untuk menyatukan dan menyelesaikan ini? Adakah rencana untuk melibatkan orang-orang non-Microsoft di luar peringatan GitHub seperti itu? Suka panggilan konferensi 30 menit? Saya mencoba untuk menghindari lubang kelinci tetapi kami bertaruh bahwa .NET core crypto akan berada pada tingkat kedewasaan dan stabilitas tertentu .. jadi dapat melakukan triase untuk diskusi semacam itu.

Kami masih memperhatikan dan mengerjakan ini. Kami telah bertemu dengan Microsoft Cryptography Board (kumpulan peneliti dan pakar lain yang menyarankan penggunaan kriptografi Microsoft) dan @bartonjs akan memiliki lebih banyak informasi untuk dibagikan segera.

Berdasarkan sedikit coretan aliran data dan saran dari Crypto Board, kami menemukan yang berikut ini. Model kami adalah GCM, CCM, SIV dan CBC+HMAC (perhatikan bahwa kami tidak berbicara tentang melakukan SIV atau CBC+HMAC sekarang, hanya saja kami ingin membuktikan bentuknya).

```C#
antarmuka publik INonceProvider
{
Hanya BacaSpanGetNextNonce(int nonceSize);
}

kelas abstrak publik AuthenticatedEncryptor : IDisposable
{
public int NonceOrIVSizeInBits { dapatkan; }
public int TagSizeInBits { dapatkan; }
public bool SupportsAssociatedData { dapatkan; }
ReadOnlySpan publikLastNonceOrIV { dapatkan; }
ReadOnlySpan publikTag Terakhir { dapatkan; }

protected AuthenticatedEncryptor(
    int tagSizeInBits,
    bool supportsAssociatedData,
    int nonceOrIVSizeInBits) => throw null;

protected abstract bool TryEncrypt(
    ReadOnlySpan<byte> data,
    ReadOnlySpan<byte> associatedData,
    Span<byte> encryptedData,
    out int bytesWritten,
    Span<byte> tag,
    Span<byte> nonceOrIVUsed);

public abstract void GetEncryptedSizeRange(
    int dataLength,
    out int minEncryptedLength,
    out int maxEncryptedLength);

public bool TryEncrypt(
    ReadOnlySpan<byte> data,
    ReadOnlySpan<byte> associatedData,
    Span<byte> encryptedData,
    out int bytesWritten) => throw null;

public byte[] Encrypt(
    ReadOnlySpan<byte> data,
    ReadOnlySpan<byte> associatedData) => throw null;

// some variant of the Dispose pattern here.

}

kelas tertutup publik AesGcmEncryptor : AuthenticatedEncryptor
{
AesGcmEncryptor publik (ReadOnlySpankeySize, INonceProvider nonceProvider)
: basis(128, benar, 96)
{
}
}

kelas tertutup publik AesCcmEncryptor : AuthenticatedEncryptor
{
Encryptor AesCcm publik (
Hanya BacaSpankunci,
int nonceSizeInBits,
INonceProvider nonceProvider,
int tagSizeInBits)
: base(tagSizeInBits, benar, nonceSizeInBits)
{
memvalidasi nonceSize dan tagSize terhadap spesifikasi algoritme;
}
}

kelas abstrak publik AuthenticatedDecryptor : IDisposable
{
bool abstrak publik TryDecrypt(
Hanya BacaSpanmenandai,
Hanya BacaSpannonceOrIV,
Hanya BacaSpandata terenkripsi,
Hanya BacaSpanterkaitData,
Menjangkaudata,
out int byteDitulis);

public abstract void GetEncryptedSizeRange(
    int encryptedDataLength,
    out int minDecryptedLength,
    out int maxDecryptedLength);

public byte[] Decrypt(
    ReadOnlySpan<byte> tag,
    ReadOnlySpan<byte> nonceOrIV,
    ReadOnlySpan<byte> encryptedData,
    ReadOnlySpan<byte> associatedData) => throw null;

// some variant of the Dispose pattern here.

}

kelas tertutup publik AesGcmDecryptor : AuthenticatedDecryptor
{
AesGcmDecryptor publik (ReadOnlySpankunci) => lempar nol;
}

kelas tertutup publik AesCcmDecryptor : AuthenticatedDecryptor
{
AesCcmDecryptor publik (ReadOnlySpankunci) => lempar nol;
}
```

Proposal ini menghilangkan streaming data. Kami tidak benar-benar memiliki banyak fleksibilitas pada saat itu. Kebutuhan dunia nyata (rendah) dikombinasikan dengan risiko terkait (sangat tinggi untuk GCM) atau ketidakmungkinannya (CCM) berarti itu hilang begitu saja.

Proposal ini menggunakan sumber nonce eksternal untuk enkripsi. Kami tidak akan memiliki implementasi publik dari antarmuka ini. Setiap aplikasi/protokol harus membuat sendiri mengikat kunci ke konteks sehingga dapat memberi makan sesuatu dengan tepat. Meskipun setiap panggilan ke TryEncrypt hanya akan membuat satu panggilan ke GetNextNonce, tidak ada jaminan bahwa TryEncrypt tertentu akan berhasil, jadi masih tergantung pada aplikasi untuk memahami apakah itu berarti harus mencoba ulang nonce. Untuk CBC+HMAC, kami akan membuat antarmuka baru, IIVProvider, untuk menghindari kerancuan terminologi. Untuk SIV IV dibangun, jadi tidak ada parameter yang dapat diterima; dan berdasarkan spesifikasi nonce (bila digunakan) tampaknya hanya dianggap sebagai bagian dari data terkait. Jadi SIV, setidaknya, menunjukkan bahwa memiliki nonceOrIV sebagai parameter untuk TryEncrypt tidak berlaku secara umum.

TryDecrypt paling pasti melempar tag yang tidak valid. Itu hanya mengembalikan false jika tujuannya terlalu kecil (sesuai aturan metode Coba)

Hal-hal yang pasti terbuka untuk umpan balik:

  • Haruskah ukuran dalam bit (seperti bagian penting dari spesifikasi) atau byte (karena hanya %8 nilai yang legal, dan kami akan selalu membagi, dan beberapa bagian spesifikasi membicarakan hal-hal seperti ukuran nonce dalam byte )?
  • Nama parameter dan urutannya.
  • Properti LastTag/LastNonceOrIV. (Menjadikannya (dapat ditulis) Rentang di TryEncrypt publik hanya berarti ada tiga buffer yang mungkin terlalu kecil, dengan membuatnya duduk di samping, "Coba" lebih jelas; kelas dasar dapat berjanji bahwa itu akan jangan pernah menawarkan buffer yang terlalu pendek.).
  • Menawarkan algoritma AE yang tidak berhasil.
  • Haruskah associatedData dipindahkan ke akhir dengan default ReadOnlySpan<byte>.Empty ?

    • Atau kelebihan membuat yang menghilangkannya?

  • Adakah yang ingin menyatakan cinta, atau benci, byte[] -metode pengembalian? (Alokasi rendah dapat dicapai dengan menggunakan metode Span, ini hanya untuk kenyamanan)
  • Metode rentang ukuran agak dibaut di bagian akhir.

    • Tujuan mereka adalah itu

    • Jika rentang tujuan kurang dari min, segera kembalikan false.

    • byte[] -metode pengembalian akan mengalokasikan buffer maksimum, dan kemudian Array.Resize sesuai kebutuhan.

    • Ya, untuk GCM dan CCM min=max=input.Length , tapi itu tidak berlaku untuk CBC+HMAC atau SIV

    • Apakah ada algoritme yang perlu memperhitungkan panjang data terkait?

Pasti byte - bukan bit.
Penyedia nonce yang tidak sadar kunci adalah kesalahan besar.

Penyedia nonce yang tidak sadar kunci adalah kesalahan besar.

Anda dapat menulis penyedia nonce Anda sesuka Anda. Kami tidak menyediakan apapun.

Bagaimana dengan pembersihan deterministik/ IDisposable ?

Bagaimana dengan pembersihan deterministik/IDisposable ?

Panggilan yang bagus. Menambahkannya ke AuthenticatedEncryptor/AuthenticatedDecryptor. Saya tidak berpikir mereka harus menyelidiki disposability pada penyedia nonce, penelepon hanya dapat menumpuk pernyataan using.

INonceProvider konsep/tujuan tidak masuk akal bagi saya (menggemakan orang lain). Biarkan primitif menjadi primitif - berikan nonce dengan cara yang sama seperti Anda memasukkan kunci (mis. sebagai byte - bagaimanapun dideklarasikan). Tidak ada spesifikasi AE/AEAD yang memaksa algoritme tentang bagaimana nonce dihasilkan/diturunkan - ini adalah tanggung jawab lapisan yang lebih tinggi (setidaknya dalam model let-primitives-be-primitive).

Tidak ada streaming? Betulkah? Apa pembenaran untuk menghapus streaming secara paksa dari stream cipher seperti AES-GCM pada tingkat dasar inti?

Misalnya, apa yang direkomendasikan oleh crypto board Anda untuk dua skenario terbaru yang kami ulas ini?

  1. Klien memiliki file perawatan kesehatan besar antara 10-30GB. Inti hanya melihat aliran data antara dua mesin jadi itu satu aliran. Jelas kunci baru dikeluarkan untuk setiap file 10GB tetapi Anda baru saja membuat setiap alur kerja seperti itu tidak berguna. Anda sekarang ingin kami a) menyangga data itu (memori, tanpa pipa) b) melakukan enkripsi (semua mesin dalam pipa sekarang menganggur!) c) menulis data (byte pertama yang ditulis setelah a dan b adalah 100% selesai) ? Tolong katakan padaku kau bercanda. Kalian secara sadar menempatkan "enkripsi adalah beban" kembali ke dalam permainan.

  2. Unit keamanan fisik memiliki beberapa aliran 4K yang juga dienkripsi untuk skenario saat istirahat. Penerbitan kunci baru terjadi pada batas 15GB. Anda mengusulkan buffering seluruh klip?

Saya tidak melihat masukan dari komunitas, dari orang-orang yang benar-benar membangun perangkat lunak dunia nyata, meminta untuk menghapus dukungan streaming. Namun kemudian tim tersebut menghilang dari dialog komunitas, berkerumun secara internal dan kemudian kembali dengan sesuatu yang tidak diminta oleh siapa pun, sesuatu yang mematikan aplikasi nyata dan menegakkan kembali bahwa "enkripsi lambat dan mahal, lewati saja?"

Anda dapat memberikan Encrypt dan EncryptFinal yang akan mendukung kedua opsi alih-alih memaksakan keputusan Anda untuk seluruh ekosistem.

Desain elegan menghilangkan kerumitan, bukan kontrol.

Apa pembenaran untuk menghapus streaming secara paksa dari stream cipher seperti AES-GCM pada tingkat dasar inti?

Saya pikir itu adalah sesuatu seperti

Proposal ini menghilangkan streaming data. Kami tidak benar-benar memiliki banyak fleksibilitas pada saat itu. Kebutuhan dunia nyata (rendah) dikombinasikan dengan risiko terkait (sangat tinggi untuk GCM) atau ketidakmungkinannya (CCM) berarti itu hilang begitu saja.

GCM memiliki terlalu banyak momen oops yang memungkinkan pemulihan kunci. Jika penyerang dapat melakukan ciphertext yang dipilih dan menonton output streaming dari sebelum verifikasi tag, mereka dapat memulihkan kuncinya. (Atau begitu salah satu cryptanalyst memberitahu saya). Secara efektif, jika ada data yang diproses GCM dapat diamati pada titik mana pun sebelum verifikasi tag, maka kuncinya akan disusupi.

Saya cukup yakin bahwa Crypto Board akan merekomendasikan TIDAK menggunakan GCM untuk skenario pertama, melainkan CBC+HMAC.

Jika skenario kedua Anda adalah pembingkaian 4k, dan Anda mengenkripsi setiap bingkai 4k, maka itu berfungsi dengan model ini. Setiap frame 4k + nonce + tag didekripsi dan diverifikasi sebelum Anda mendapatkan byte kembali, jadi Anda tidak akan pernah membocorkan keystream/kunci.

Sebagai perbandingan: Saat ini saya sedang mengembangkan API kripto "biarkan primitif menjadi primitif" ini. Ini adalah kelas saya untuk enkripsi yang diautentikasi.

Bagi saya ternyata berguna untuk dapat berbicara tentang primitif kripto secara independen dari sebuah kunci. Misalnya, saya sering ingin memasukkan primitif tertentu ke dalam metode yang bekerja dengan algoritme AEAD apa pun dan membiarkan pembuatan kunci, dll., ke metode itu. Oleh karena itu ada kelas AeadAlgorithm dan kelas Key yang terpisah.

Hal lain yang sangat berguna yang telah mencegah beberapa bug adalah menggunakan tipe berbeda untuk mewakili data dengan bentuk berbeda, misalnya, Key dan Nonce , daripada menggunakan byte[] atau Span<byte> biasa untuk semuanya.


AeadAlgorithm API (klik untuk meluaskan)

public abstract class AeadAlgorithm : Algorithm
{
    public int KeySize { get; }

    public int NonceSize { get; }

    public int TagSize { get; }

    public byte[] Decrypt(
        Key key,
        Nonce nonce,
        ReadOnlySpan<byte> associatedData,
        ReadOnlySpan<byte> ciphertext)

    public void Decrypt(
        Key key,
        Nonce nonce,
        ReadOnlySpan<byte> associatedData,
        ReadOnlySpan<byte> ciphertext,
        Span<byte> plaintext)

    public byte[] Encrypt(
        Key key,
        Nonce nonce,
        ReadOnlySpan<byte> associatedData,
        ReadOnlySpan<byte> plaintext)

    public void Encrypt(
        Key key,
        Nonce nonce,
        ReadOnlySpan<byte> associatedData,
        ReadOnlySpan<byte> plaintext,
        Span<byte> ciphertext)

    public bool TryDecrypt(
        Key key,
        Nonce nonce,
        ReadOnlySpan<byte> associatedData,
        ReadOnlySpan<byte> ciphertext,
        out byte[] plaintext)

    public bool TryDecrypt(
        Key key,
        Nonce nonce,
        ReadOnlySpan<byte> associatedData,
        ReadOnlySpan<byte> ciphertext,
        Span<byte> plaintext)
}

@bartonjs dia benar, Anda harus mengandalkan program yang tidak mengeluarkan hingga otentikasi. Jadi misalnya jika Anda tidak mengautentikasi (atau belum) Anda dapat mengontrol input untuk sebuah blok dan karenanya mengetahui output dan bekerja mundur dari sana ...

Misalnya seorang pria di tengah serangan dapat menyuntikkan blok dikenal ke dalam aliran cbc dan melakukan serangan sedikit membalik klasik.

Tidak yakin bagaimana memecahkan sebagian besar masalah data yang benar-benar lain terima kasih untuk memotongnya dengan serial nonces atau serupa ... ala TLS.

Baiklah, izinkan saya ulangi yang saya lakukan tetapi hanya dalam kasus jaringan ukuran kecil yang tidak cukup untuk lib tujuan umum.

Dengan semangat keterbukaan, apakah mungkin untuk mengungkapkan siapa yang ada di Dewan Peninjau Kriptografi Microsoft (dan idealnya komentar/pendapat anggota tertentu yang mengulas topik ini)? Brian LaMacchia dan siapa lagi?

_menggunakan psikologi terbalik:_

Saya senang streaming AEAD sudah keluar. Ini berarti bahwa Inferno terus menjadi satu-satunya AEAD streaming praktis berbasis CryptoStream untuk rata-rata Joe. Terima kasih Dewan Peninjau MS Crypto!

Membangun komentar @ektrah , pendekatannya (dia?) didorong oleh RFC 5116 , yang telah saya rujuk sebelumnya. Ada banyak kutipan penting di RFC 5116:

3.1. Persyaratan pada Generasi Nonce
Penting untuk keamanan bahwa nonce dibangun dengan cara yang menghormati persyaratan bahwa setiap nilai nonce berbeda untuk setiap pemanggilan operasi enkripsi yang diautentikasi, untuk setiap nilai kunci yang tetap.
...

  1. Persyaratan pada Spesifikasi Algoritma AEAD
    Setiap algoritma AEAD HARUS menerima nonce apapun dengan panjang antara oktet N_MIN dan N_MAX, inklusif, di mana nilai N_MIN dan N_MAX khusus untuk algoritma tersebut. Nilai N_MAX dan N_MIN MUNGKIN sama. Setiap algoritma HARUS menerima nonce dengan panjang dua belas (12) oktet. Algoritma acak atau stateful, yang dijelaskan di bawah, MUNGKIN memiliki nilai N_MAX nol.
    ...
    Algoritma Enkripsi yang Diotentikasi MUNGKIN menggabungkan atau menggunakan sumber acak, misalnya, untuk menghasilkan vektor inisialisasi internal yang dimasukkan ke dalam output ciphertext. Sebuah algoritma AEAD semacam ini disebut acak; meskipun perhatikan bahwa hanya enkripsi yang acak, dan dekripsi selalu deterministik. Sebuah algoritma acak MUNGKIN memiliki nilai N_MAX yang sama dengan nol.

Algoritme Enkripsi yang Diotentikasi MUNGKIN menggabungkan informasi status internal yang dipertahankan di antara pemanggilan operasi enkripsi, misalnya, untuk memungkinkan konstruksi nilai yang berbeda yang digunakan sebagai nonces internal oleh algoritme. Algoritma AEAD semacam ini disebut stateful. Metode ini dapat digunakan oleh suatu algoritme untuk memberikan keamanan yang baik bahkan ketika aplikasi memasukkan nonces dengan panjang nol. Algoritma stateful MUNGKIN memiliki nilai N_MAX yang sama dengan nol.

Satu ide yang berpotensi untuk dijelajahi adalah pengesahan Nonce panjang-nol/null, yang bahkan mungkin menjadi default. Melewati nilai Nonce "khusus" tersebut akan mengacak nilai Nonce yang sebenarnya, yang akan tersedia sebagai keluaran Encrypt.

Jika INonceProvider tetap karena "alasan", ide lain adalah menambahkan panggilan Reset() , yang akan dipicu setiap kali AuthenticatedEncryptor di-rekey. Sebaliknya, jika rencananya adalah untuk tidak pernah memasukkan kembali instance AuthenticatedEncryptor , ini akan membuang GC jika kita ingin membangun API enkripsi chunk streaming (mis. chunk = paket jaringan), dan setiap chunk harus dienkripsi dengan kunci yang berbeda (mis. protokol Netflix MSL , Inferno, lainnya). Khususnya untuk operasi paralel enc/dec di mana kami ingin mempertahankan kumpulan mesin AEAD, dan meminjam instance dari kumpulan itu untuk melakukan enc/dec. Mari beri GC cinta :)

Dari sudut pandang saya, satu-satunya tujuan kripto primitif adalah untuk menerapkan protokol keamanan tingkat tinggi yang dirancang dengan baik. Setiap protokol tersebut bersikeras menghasilkan nonces dengan cara mereka sendiri. Sebagai contoh:

  • TLS 1.2 mengikuti rekomendasi RFC 5116 dan menggabungkan IV 4-byte dengan penghitung 8-byte,
  • TLS 1.3 xor adalah penghitung 8-byte yang diisi hingga 12 byte dengan IV 12-byte,
  • Noise menggunakan penghitung 8-byte yang diisi hingga 12 byte dalam urutan byte big-endian untuk AES-GCM dan penghitung 8-byte yang diisi hingga 12 byte dalam urutan byte little-endian untuk ChaCha/Poly.

GCM terlalu rapuh untuk nonce acak pada ukuran nonce tipikal (96 bit). Dan saya tidak mengetahui protokol keamanan apa pun yang benar-benar mendukung nonces acak.

Tidak banyak permintaan untuk lebih banyak API yang menyediakan primitif kripto. 99,9% pengembang memerlukan resep tingkat tinggi untuk skenario terkait keamanan: menyimpan kata sandi dalam database, mengenkripsi file saat istirahat, mentransfer pembaruan perangkat lunak dengan aman, dll.

Namun, API untuk resep tingkat tinggi seperti itu jarang terjadi. Satu-satunya API yang tersedia seringkali hanya HTTPS dan kripto primitif, yang memaksa pengembang untuk menggulirkan protokol keamanan mereka sendiri. IMO solusinya adalah tidak berusaha keras dalam merancang API untuk bekerja dengan primitif. Ini adalah API untuk resep tingkat tinggi.

Terima kasih atas tanggapannya, semuanya! Beberapa pertanyaan:

  1. Meskipun dekripsi streaming dapat gagal secara besar-besaran, enkripsi streaming dapat dilakukan. Apakah enkripsi streaming (bersama dengan opsi non-streaming) tetapi hanya dekripsi non-streaming yang terdengar lebih berguna? Jika ya, ada beberapa masalah yang harus dipecahkan:
    sebuah. Beberapa algoritme (CCM, SIV) sebenarnya tidak mendukung streaming. Haruskah kita menempatkan enkripsi streaming pada kelas dasar dan buffer streaming input atau membuang dari kelas turunan?
    B. Streaming AAD kemungkinan tidak mungkin karena kendala implementasi, tetapi algoritma yang berbeda membutuhkannya pada waktu yang berbeda (beberapa membutuhkannya di awal, beberapa tidak membutuhkannya sampai akhir). Haruskah kita memerlukannya di muka atau memiliki metode untuk menambahkannya yang berfungsi ketika masing-masing algoritme memungkinkan?
  1. Kami terbuka untuk peningkatan INonceProvider selama intinya adalah bahwa pengguna perlu menulis kode yang menghasilkan nonce baru. Apakah ada yang punya bentuk lain yang diusulkan untuk itu?

1 . a = Saya pikir mungkin menjadi masalah untuk tidak memperingatkan pengguna lebih awal. Bayangkan skenario dari seseorang di atas, file 10gb. Mereka pikir mereka mendapatkan streaming, kemudian beberapa saat kemudian dev lain mengubah cipher dan hal berikutnya kode buffering 10gb (atau mencoba) sebelum mengembalikan nilai.

  1. b = Sekali lagi dengan "streaming" atau ide jaringan, misalnya AES GCM dll Anda tidak mendapatkan informasi AAD sampai akhir untuk dekripsi. Adapun Enkripsi, saya belum melihat kasus di mana Anda tidak memiliki data di muka. Jadi saya akan mengatakan setidaknya untuk enkripsi Anda harus memerlukannya di awal, dekripsi lebih kompleks.
  1. Saya pikir itu benar-benar bukan masalah, memasok "byte" untuk nonce melalui antarmuka atau hanya secara langsung tidak ada di sini atau di sana. Anda dapat mencapai hal yang sama dengan dua cara, saya hanya merasa itu lebih buruk untuk orang primitif tetapi saya tidak menentang keras jika itu membuat orang tidur lebih nyenyak di malam hari. Saya hanya akan menganggap ini sebagai kesepakatan yang sudah selesai, dan melanjutkan dengan masalah lainnya.

Tentang proses musyawarah

@bartonjs : Kami bisa berdebat sepanjang hari jika keputusan tertutup tanpa keterlibatan masyarakat adalah pembenaran yang efektif tetapi kami akan keluar dari topik, jadi saya akan membiarkannya. Ditambah tanpa komunikasi tatap muka atau waktu nyata yang lebih kaya, saya tidak ingin mengecewakan siapa pun di sana.

Mengenai streaming

1. argumen 'streaming menyiratkan tidak ada keamanan AES-GCM'

Secara khusus, mengukus => mengembalikan data yang didekripsi ke pemanggil sebelum verifikasi tag => tidak ada keamanan. Ini bukan suara. @bartonjs mengklaim 'chosen ciphertext => watch output => restore key' sementara @drawaes mengklaim 'control input for a block => oleh karena itu tahu output => "work from there" '

Nah, di AES-GCM, satu- satunya hal yang dilakukan tag adalah verifikasi integritas (perlindungan tamper). Ini memiliki 0 dampak pada privasi. Faktanya, jika Anda menghapus pemrosesan tag GCM/GHASH dari AES-GCM, Anda cukup mendapatkan mode AES-CTR. Konstruksi inilah yang menangani aspek privasi. Dan RKT dapat ditempa untuk sedikit membalik tetapi tidak "rusak" dengan cara apa pun yang Anda berdua nyatakan (memulihkan kunci atau teks biasa) karena itu berarti primitif AES yang mendasar dikompromikan. Jika cryptanalyst Anda (siapa itu?) mengetahui sesuatu yang tidak diketahui oleh kita semua, dia harus mempublikasikannya. Satu-satunya hal yang mungkin adalah, seorang yang diserang dapat membalik bit N dan mengetahui bahwa bit N dari plaintext telah dibalik - tetapi mereka tidak pernah tahu apa itu plaintext yang sebenarnya.

Jadi

1) privasi plaintext selalu ditegakkan
2) verifikasi integritas hanya ditangguhkan (sampai akhir streaming) dan
3) tidak ada kunci yang pernah dikompromikan.

Untuk produk dan sistem di mana streaming menjadi dasar, Anda sekarang setidaknya dapat merancang tradeoff di mana seseorang untuk sementara turun dari AEAD ke enkripsi AES biasa - lalu kembali ke AEAD setelah verifikasi tag. Itu membuka beberapa konsep inovatif untuk merangkul keamanan alih-alih pergi "Anda ingin menyangga semua itu - apakah Anda gila? Kami tidak dapat melakukan enkripsi!".

Semua karena Anda hanya ingin menerapkan EncryptFinal daripada Encrypt dan EncryptFinal (atau yang setara).

2. Tidak khusus untuk GCM!

Sekarang AES-GCM bukanlah binatang ajaib yang memiliki banyak "momen oops". Ini hanya AES-CTR + GHASH (semacam hash jika saya boleh). Tidak ada pertimbangan terkait privasi yang diwarisi dari mode RKT dan pertimbangan tag yang terkait dengan integritas berasal dari ukuran tag variabel yang diizinkan dalam spesifikasi. Namun, AES-CTR + GHASH sangat mirip dengan sesuatu seperti AES-CBC + HMAC-SHA256 di mana algoritma pertama menangani privasi dan yang kedua menangani integritas. Dalam AES-CBC + HMAC-SHA256, bit membalik dalam ciphertext akan merusak blok yang sesuai dalam teks yang didekripsi (tidak seperti CTR) DAN juga secara deterministik membalik bit di blok plaintext yang didekripsi berikut (seperti CTR). Sekali lagi, penyerang tidak akan tahu apa yang akan dihasilkan plaintext - hanya bit yang dibalik (seperti CTR). Akhirnya, pemeriksaan integritas (HMAC-SHA256) akan menangkapnya. Tetapi hanya memproses byte terakhir (seperti GHASH).

Jadi, jika argumen Anda untuk menahan SEMUA data yang didekripsi hingga integritasnya baik-baik saja, itu benar-benar bagus - itu harus diterapkan secara konsisten. Jadi SEMUA data yang keluar dari jalur AES-CBC juga harus disangga (secara internal oleh perpustakaan) hingga HMAC-SHA256 berlalu. Itu pada dasarnya berarti di .NET, tidak ada data streaming yang bahkan dapat memanfaatkan kemajuan AEAD. .NET memaksa data streaming untuk diturunkan. Untuk memilih antara tanpa enkripsi atau enkripsi biasa. Tidak ada AEAD. Jika buffering secara teknis tidak praktis, arsitek setidaknya harus memiliki opsi untuk memperingatkan pengguna akhir bahwa "rekaman drone mungkin rusak" daripada "tidak memperhatikan Anda".

3. Itu yang terbaik yang kita miliki

Data semakin besar dan keamanan harus lebih kuat. Streaming juga merupakan realitas yang harus dianut oleh para desainer. Sampai dunia membuat algoritme AEAD yang benar-benar terintegrasi yang secara asli dapat mendeteksi korupsi di tengah-tengah gangguan, kami terjebak dengan enkripsi + otentikasi sebagai bolted-on-buddies. Primitif AEAD sejati sedang diteliti tetapi kami baru saja mendapatkan enkripsi + otentikasi untuk saat ini.

Saya tidak terlalu peduli dengan "AES-GCM" seperti halnya saya peduli dengan algoritme AEAD yang cepat dan populer yang dapat mendukung beban kerja streaming - sangat lazim di dunia yang kaya data dan sangat terhubung.

4. Gunakan AES-CBC-HMAC, Gunakan (masukkan solusi)

Crypto Board akan merekomendasikan TIDAK menggunakan GCM untuk skenario pertama, melainkan CBC+HMAC.

Mengesampingkan semua yang disebutkan di atas atau bahkan spesifikasi skenario - menyarankan AES-CBC-HMAC tidak gratis. Ini ~3x lebih lambat dari AES-GCM karena enkripsi AES-CBC tidak dapat diparalelkan dan karena GHASH dapat dipercepat melalui instruksi PCLMULQDQ. Jadi jika Anda berada di 1GB/dtk dengan AES-GCM, sekarang Anda akan mencapai ~300MB/dtk dengan AES-CBC-HMAC. Ini sekali lagi melakukan pola pikir "Crypto memperlambat Anda, lewati saja" - pola pikir yang berusaha keras untuk dilawan oleh petugas keamanan.

mengenkripsi setiap bingkai 4k

codec video harus tiba-tiba melakukan enkripsi? Atau lapisan enkripsi sekarang harus memahami codec video? Ini hanya aliran bit pada lapisan keamanan data. Fakta bahwa itu adalah video/data genom/gambar/format eksklusif dll seharusnya tidak menjadi perhatian lapisan keamanan. Solusi keseluruhan seharusnya tidak menggabungkan tanggung jawab inti.

nonce

NIST memungkinkan untuk infus acak untuk panjang melebihi 96 bit. Lihat bagian 8.2.2 di NIST 800-38D . Tidak ada yang baru di sini, tidak ada persyaratan yang datang dari mode CTR. Yang juga cukup standar di sebagian besar stream cipher . Saya tidak mengerti ketakutan tiba-tiba terhadap nonces - selalu number used once . Namun, meskipun perdebatan INonce membuat antarmuka yang kikuk setidaknya tidak menghilangkan inovasi seperti pemaksaan no-stream-for-you. Saya akan memberikan INonce kapan saja jika kita bisa mendapatkan inovasi beban kerja keamanan + streaming AEAD. Saya benci menyebut sesuatu yang mendasar seperti streaming inovasi - tetapi di situlah saya khawatir kita akan mundur.

Saya ingin terbukti salah

Saya hanya seorang pria yang setelah seharian bekerja, menyerah menonton film malam dengan anak-anak saya untuk mengetik ini. Aku lelah dan bisa saja salah. Tapi setidaknya memiliki dialog komunitas berbasis fakta terbuka daripada anekdot atau "alasan komite" atau voodoo lainnya. Saya dalam bisnis mempromosikan inovasi .NET dan Azure yang aman. Saya pikir kami memiliki tujuan yang selaras.

Berbicara tentang dialog komunitas ...

Bisakah kami melakukan panggilan Skype komunitas? Mengekspresikan topik yang kompleks seperti ini menghantam dinding teks raksasa. tolong cantik?

Tolong jangan lakukan panggilan Skype - itulah definisi "rapat tertutup", tanpa catatan yang tersedia untuk komunitas. Masalah Github adalah kendaraan yang tepat bagi semua pihak untuk memiliki wacana yang didokumentasikan secara sipil (mengabaikan preseden penghapusan komentar MS).

MS Crypto Review Board mungkin melakukan panggilan Skype juga. Ini bukan kesalahan orang-orang MS yang berpartisipasi dalam utas ini - mereka kemungkinan memiliki akses yang sangat terbatas ke & kekuatan persuasi atas menara gading Dewan Peninjau Kripto MS (apa pun/siapa pun itu).

Mengenai streaming AEAD:

Streaming _encryption_ ukuran byte dimungkinkan untuk mode MAC-last seperti GCM, CTR+HMAC, tetapi tidak mungkin untuk mode MAC-first seperti CCM. Streaming _decryption_ berukuran byte pada dasarnya bocor dan oleh karena itu tidak dipertimbangkan oleh siapa pun. Streaming _encryption_ ukuran blok juga dimungkinkan untuk CBC+HMAC, tetapi itu tidak mengubah apa pun. Yaitu. Pendekatan ukuran byte atau ukuran Blok untuk streaming AEAD salah.

Streaming ukuran potongan _encryption_ dan _decryption_ berfungsi dengan baik, tetapi mereka memiliki 2 kendala:

  • mereka membutuhkan buffering (di luar ukuran blok). Ini dapat dilakukan oleh perpustakaan/API jika buffering dikontrol/dibatasi (mis. Inferno), atau diserahkan ke lapisan atas (lapisan pemanggil) untuk ditangani. Cara apa pun berhasil.

  • Streaming terpotong AEAD tidak standar. Mantan. nacl-stream , Inferno, MS-own DataProtection, make-your-own.

Ini hanya ringkasan dari apa yang semua orang dalam diskusi ini sejauh ini sudah tahu.

@sdrapkin , untuk memastikan saya mengerti dengan benar, apakah Anda setuju dengan API ini yang menyediakan enkripsi streaming, tetapi tidak ada dekripsi streaming?

@sdrapkin yah, brainstorming manusia secara real time tentu bermanfaat, masalah pencatatan dapat diselesaikan dengan notulen rapat. Kembali ke sisi teknis, sementara chunking berfungsi untuk dekripsi streaming, itu bukan primitif keamanan tingkat rendah. Ini adalah protokol khusus. Dan yang non-standar seperti yang Anda catat.

@morganbr

_apakah Anda setuju dengan API ini yang menyediakan enkripsi streaming, tetapi tidak ada dekripsi streaming?_

Tidak, bukan aku. Jika API semacam itu tersedia, akan mudah untuk membuat ciphertext terenkripsi aliran dengan ukuran yang tidak dapat didekripsi oleh dekripsi buffer (kehabisan memori).

^^^^ Sejauh ini, belum ada banyak kesepakatan, tapi saya pikir kita semua bisa setuju bagaimanapun caranya, API asimetris akan menjadi bencana. Baik dari "hei adalah metode dekripsi aliran yang saya pikir akan saya andalkan karena ada metode enkripsi", dan karena komentar @sdrapkin di atas.

@Drawes Setuju. API enc/dec asimetris akan mengerikan.

Ada update guys?

Rupanya saya menggabungkan beberapa serangan.

Kelemahan yang melekat pada stream cipher (yaitu AES-CTR dan AES-GCM) memungkinkan serangan ciphertext yang dipilih untuk memungkinkan pemulihan plaintext yang sewenang-wenang. Pertahanan terhadap serangan ciphertext yang dipilih adalah otentikasi; jadi AES-GCM kebal... kecuali jika Anda melakukan dekripsi streaming dan Anda dapat mengidentifikasi dari pengamatan saluran samping seperti apa teks biasa itu. Misalnya, jika data yang didekripsi sedang diproses sebagai XML, itu akan gagal dengan sangat cepat jika karakter selain spasi putih atau < berada di awal data yang didekripsi. Jadi itu "dekripsi streaming memperkenalkan kembali masalah dengan desain stream cipher" (yang, mungkin Anda perhatikan, .NET tidak memilikinya).

Sambil mencari dari mana pemulihan kunci itu berasal, ada makalah seperti Otentikasikelemahan di GCM (Ferguson/Microsoft) , tetapi yang satu itu memulihkan kunci otentikasi berdasarkan ukuran tag pendek (yang merupakan bagian dari mengapa implementasi Windows hanya mengizinkan tag 96-bit). Saya mungkin diberi tahu tentang vektor pemulihan kunci otentikasi lainnya tentang mengapa streaming GCM berbahaya.

Dalam komentar sebelumnya @sdrapkin mencatat "Dekripsi streaming ukuran byte pada dasarnya bocor dan oleh karena itu tidak dipertimbangkan oleh siapa pun. ... Pendekatan ukuran byte atau ukuran Blok untuk streaming AEAD cacat.". Itu, dikombinasikan dengan CCM (dan SIV) yang tidak mampu melakukan enkripsi streaming dan komentar tentang itu akan aneh untuk memiliki satu streaming dan bukan yang lain, menunjukkan bahwa kami kembali ke proposal hanya memiliki satu-shot mengenkripsi dan mendekripsi.

Jadi sepertinya kita kembali ke proposal API terakhir saya (https://github.com/dotnet/corefx/issues/23629#issuecomment-329202845). Kecuali ada masalah luar biasa lainnya yang berhasil saya lupakan saat mengambil cuti.

Selamat datang kembali @bartonjs

Saya akan tidur sebentar tapi sebentar:

  1. Kami telah menggabungkan desain protokol dengan desain primitif sebelumnya di utas ini. Saya hanya akan mengatakan bahwa serangan ciphertext yang dipilih adalah masalah desain protokol, bukan masalah primitif.

  2. Streaming dekripsi AEAD setidaknya memungkinkan Anda memiliki privasi dan kemudian segera meningkatkan privasi + keaslian pada byte terakhir. Tanpa dukungan streaming di AEAD (yaitu hanya privasi tradisional), Anda secara permanen membatasi orang-orang ke jaminan privasi yang lebih rendah saja.

Jika manfaat teknis tidak mencukupi atau Anda (seharusnya) skeptis terhadap otoritas argumen saya, saya akan mencoba rute otoritas luar. Anda harus tahu bahwa implementasi dasar Anda yang sebenarnya mendukung AEAD (termasuk AES GCM) dalam mode streaming. OS inti Windows ( bcrypt ) memungkinkan streaming GCM melalui fungsi BCryptEncrypt atau BCryptDecrypt . Lihat dwFlags di sana. Atau contoh kode pengguna . Atau pembungkus CLR yang dibuat oleh Microsoft . Atau bahwa implementasinya telah disertifikasi NIST FIP-140-2 baru-baru ini seperti awal tahun ini. Atau bahwa Microsoft dan NIST keduanya menghabiskan sumber daya yang signifikan di sekitar implementasi AES dan disertifikasi di sini dan di sini . Dan terlepas dari semua ini, tidak ada yang menyalahkan kaum primitif. Sama sekali tidak masuk akal bagi .NET Core untuk tiba-tiba muncul dan memaksakan tesis kriptonya sendiri untuk mempermudah implementasi dasar yang kuat. Terutama ketika KEDUA streaming dan one-shot dapat didukung secara bersamaan, sangat sepele.

Lagi? Nah, hal di atas berlaku untuk OpenSSL, bahkan dengan evp API 'lebih baru' mereka.

Dan itu benar untuk BouncyCastle.

Dan itu benar dengan Arsitektur Kriptografi Java.

Bersulang!
Sid

@sidshetye ++10 jika cryptoboard sangat khawatir mengapa mereka membiarkan windows CNG melakukan ini?

Jika Anda memeriksa validasi NIST FIPS-140-2 AES Microsoft (mis. # 4064 ), Anda akan melihat hal berikut:

AES-GCM:

  • Panjang Teks Biasa: 0, 8, 1016, 1024
  • Panjang AAD: 0, 8, 1016, 1024

AES-CCM:

  • Panjang Teks Biasa: 0-32
  • Panjang AAD: 0-65536

Tidak ada validasi untuk streaming. Saya bahkan tidak yakin apakah NIST memeriksa mantan itu. Implementasi AES-GCM tidak boleh diizinkan untuk mengenkripsi lebih dari 64Gb plaintext (pembatasan konyol GCM lainnya).

Saya tidak terlalu terikat dengan streaming karena penggunaan saya tidak boleh melebihi 16k namun buffer yang terfragmentasi akan menyenangkan dan tidak menimbulkan risiko sama sekali (saya sebenarnya curiga bahwa cng membuat antarmuka seperti itu untuk tujuan itu) ... misalnya saya ingin dapat melewati sejumlah rentang atau serupa (daftar tertaut misalnya) dan mendekripsinya sekaligus. Jika didekripsi ke buffer yang berdekatan, tidak apa-apa.

Jadi saya kira memindahkan papan crypto bayangan pada API "gaya streaming" tidak boleh dilakukan untuk saat ini, jadi mari kita lanjutkan membuat API satu tembakan. Selalu ada ruang untuk memperluas API JIKA cukup banyak orang menunjukkan kebutuhan nanti

@sdrapkin intinya adalah bahwa itu adalah API streaming yang telah melalui tinjauan ekstensif oleh NIST Labs dan MSFT. Setiap build yang divalidasi adalah antara $80.000 - $50.000 dan MSFT (dan OpenSSL dan Oracle dan crypto kelas berat lainnya) telah berinvestasi BESAR untuk mendapatkan API ini dan implementasi yang divalidasi selama lebih dari 10 tahun. Jangan terganggu oleh ukuran teks biasa khusus rencana pengujian karena saya yakin .NET akan mendukung ukuran selain 0, 8, 1016, 1024 terlepas dari streaming atau satu kali pemotretan. Intinya adalah semua API yang telah teruji pertempuran (secara harfiah; pada sistem pendukung senjata), pada semua platform ini mendukung streaming AEAD di level API kripto-primitif. Sayangnya, setiap argumen sejauh ini menentangnya telah menjadi kekhawatiran tingkat aplikasi atau protokol yang disebut sebagai kekhawatiran semu di tingkat primitif kripto.

Saya mendukung 'biarkan ide terbaik menang' tetapi kecuali tim crypto inti .net (MSFT atau komunitas) memiliki beberapa penemuan terobosan, saya hanya tidak melihat bagaimana semua orang melakukan crypto sejauh ini, dari semua organisasi yang berbeda salah dan mereka benar.

PS: Saya tahu kami berselisih di sini, tetapi kami semua menginginkan yang terbaik untuk platform dan pelanggannya.

@Drawaes kecuali antarmuka AEAD (belum tentu implementasi) yang ditentukan hari ini mendukung permukaan API streaming, saya tidak melihat bagaimana orang dapat memperluasnya tanpa memiliki dua antarmuka atau antarmuka khusus. Itu akan menjadi bencana. Saya berharap diskusi ini mengarah ke antarmuka yang terbukti di masa depan (atau paling tidak, mencerminkan antarmuka AEAD lain yang telah ada selama bertahun-tahun!).

Saya cenderung setuju. Tapi masalah ini tidak akan kemana-mana dengan cepat dan ketika itu terjadi kita cenderung mencapai titik kritis baik itu tidak akan berhasil untuk 2.1 atau harus ditabrak tanpa waktu tersisa untuk menyelesaikan masalah. Saya akan jujur ​​​​saya telah kembali ke pembungkus lama saya dan saya hanya memperbaikinya untuk 2.0;)

Kami memiliki beberapa API referensi untuk Java , OpenSSL atau C# Bouncy Castle atau CLR Security . Terus terang salah satu dari mereka akan melakukannya dan dalam jangka panjang, saya berharap C # memiliki sesuatu seperti 'Arsitektur Kriptografi Java' Java di mana semua implementasi kripto bertentangan dengan antarmuka yang mapan yang memungkinkan seseorang untuk menukar perpustakaan kripto tanpa memengaruhi kode pengguna.

Kembali ke sini, saya pikir sebaiknya kita memperluas antarmuka ICryptoTransform .NET Core sebagai

public interface IAuthenticatedCryptoTransform : ICryptoTransform 
{
    bool CanChainBlocks { get; }
    byte[] GetTag();
    void SetExpectedTag(byte[] tag);
}

Jika kita Span ifying semua byte[] s, itu harus menembus seluruh API di System.Security.Cryptography namespace untuk konsistensi keseluruhan.

Sunting: Memperbaiki tautan JCA

Jika kita melakukan Spanifying semua byte[]s, itu akan menembus seluruh API di namespace System.Security.Cryptography untuk konsistensi keseluruhan.

Kami sudah melakukannya. Semuanya kecuali ICryptoTransform, karena kami tidak dapat mengubah antarmuka.

Saya pikir yang terbaik adalah kita memperluas ICryptoTransform .NET Core ...

Masalah dengan ini adalah pola panggilan sangat canggung dengan mengeluarkan tag di akhir (terutama jika CryptoStream terlibat). Saya menulis ini awalnya, dan itu jelek. Ada juga masalah bagaimana mendapatkan salah satunya, karena parameter GCM berbeda dari parameter CBC/ECB.

Jadi, inilah pemikiran saya.

  • Dekripsi streaming berbahaya bagi AE.
  • Secara umum, saya penggemar "beri saya yang primitif, dan biarkan saya mengelola risiko saya"
  • Saya juga penggemar ".NET tidak boleh (dengan mudah) mengizinkan hal-hal yang benar-benar tidak aman, karena itulah sebagian dari proposisi nilainya"
  • Jika, seperti yang awalnya saya salah pahami, risiko melakukan dekripsi GCM dengan buruk adalah pemulihan kunci input, maka saya masih berada di "ini terlalu tidak aman". (Perbedaan antara .NET dan yang lainnya adalah "setelah membutuhkan waktu lebih lama untuk melakukan ini, dunia telah belajar lebih banyak")
  • Tapi, karena tidak, jika Anda benar-benar ingin roda latihannya lepas, maka saya kira saya akan menerima gagasan itu.

Pikiran saya yang cukup mentah untuk itu (menambahkan saran yang ada, jadi satu-shot tetap ada, meskipun saya kira sebagai impl default virtual alih-alih abstrak):

```C#
kelas parsial AuthenticatedEncryptor
{
// melempar jika operasi sedang berlangsung
public abstract void Inisialisasi (ReadOnlySpandata terkait);
// benar pada keberhasilan, salah pada "tujuan terlalu kecil", pengecualian pada hal lain.
bool abstrak publik TryEncrypt (ReadOnlySpandata, SpanyolenkripsiData, keluar int byteBaca, keluar int byteWritten);
// false jika sisaEncryptedData terlalu kecil, melempar jika input lain terlalu kecil, lihat properti NonceOrIVSizeInBits dan TagSizeInBits.
// NonceOrIvUsed bisa pindah ke Inisialisasi, tapi kemudian bisa ditafsirkan sebagai input.
bool abstrak publik TryFinish(ReadOnlySpansisaData, SpansisaEncryptedData, out int bytesWritten, Spantag, spannonceOrIvUsed);
}

kelas parsial AuthenticatedDecryptor
{
// melempar jika operasi sedang berlangsung
public abstract void Inisialisasi (ReadOnlySpantag, ReadOnlySpannonceOrIv, ReadOnlySpandata terkait);
// benar pada keberhasilan, salah pada "tujuan terlalu kecil", pengecualian pada hal lain.
bool abstrak publik TryDecrypt (ReadOnlySpandata, SpanyoldecryptedData, keluar int byteRead, keluar int byteWritten);
// melempar tag yang buruk, tetapi mungkin membocorkan data.
// (remainingDecryptedData diperlukan untuk CBC+HMAC, dan mungkin juga menambahkan sisaData, saya kira?)
bool abstrak publik TryFinish(ReadOnlySpansisaData, SpansisaDecryptedData, out int byteWritten);
}
```

AssociatedData hadir di Inisialisasi, karena algoritme yang membutuhkannya terakhir dapat mempertahankannya, dan algoritme yang membutuhkannya terlebih dahulu tidak dapat memilikinya dengan cara lain.

Setelah bentuk diputuskan untuk seperti apa streaming itu (dan apakah orang berpikir CCM harus buffer internal, atau harus dibuang, ketika dalam mode enkripsi streaming) maka saya akan kembali ke papan tulis.

@bartonjs Saya tahu apa yang Anda maksud tentang memetik dan memprogram tag dari ujung aliran untuk simetri di seluruh enkripsi/dekripsi. Ini rumit tetapi lebih buruk jika diserahkan kepada setiap pengguna untuk dipecahkan. Saya memiliki implementasi yang dapat saya bagikan di bawah MIT; perlu melihat secara internal dengan tim saya (bukan di meja/ponsel saya)

Jalan tengah bisa seperti OpenSSL atau bcrypt NT di mana Anda perlu memasang tag tepat sebelum panggilan dekripsi terakhir karena saat itulah perbandingan tag terjadi. yaitu SetExpectedTag (sebelum dekripsi akhir) dan GetTag (setelah enkripsi akhir) akan berfungsi tetapi menurunkan manajemen tag ke pengguna. Sebagian besar hanya akan menambahkan tag ke cipherstream karena itu adalah urutan temporal alami.

Saya pikir mengharapkan tag di Initialize itu sendiri (dalam dekripsi) memecah simetri dalam ruang (aliran byte) dan waktu (pemeriksaan tag di akhir, bukan mulai) yang membatasi kegunaannya. Tetapi API Tag di atas menyelesaikannya.

Juga untuk enkripsi, Initialize membutuhkan IV sebelum kripto berubah.

Terakhir, untuk mengenkripsi dan mendekripsi, Initialize memerlukan kunci enkripsi AES sebelum transformasi apa pun. (Saya melewatkan sesuatu yang jelas atau Anda lupa mengetik sedikit?)

Saya pikir mengharapkan tag di Inisialisasi itu sendiri (dalam dekripsi) merusak simetri

Di CBC+HMAC, rekomendasi yang biasa adalah memverifikasi HMAC sebelum memulai dekripsi apa pun, jadi ini adalah algoritme dekripsi tag-pertama. Demikian pula, mungkin ada algoritme "AE murni" yang melakukan operasi destruktif pada tag selama penghitungan dan hanya memeriksa bahwa jawaban akhirnya adalah 0. Jadi, seperti nilai data terkait, karena mungkin ada algoritme yang membutuhkannya terlebih dahulu, ia memiliki untuk menjadi yang pertama dalam API yang sepenuhnya digeneralisasikan.

Mengambang mereka ke SetAssociatedData dan SetTag memiliki masalah bahwa sementara kelas dasar tidak bergantung pada algoritma, penggunaannya menjadi bergantung pada algoritma. Mengubah AesGcm ke AesCbcHmacSha256 atau SomeTagDesctructiveAlgorithm sekarang akan menghasilkan pelemparan TryDecrypt karena tag belum disediakan. Bagi saya itu lebih buruk daripada tidak polimorfik sama sekali, jadi membiarkan fleksibilitas menyarankan memecah model menjadi terisolasi sepenuhnya per algoritma. (Ya, itu dapat dikontrol oleh lebih banyak properti karakteristik identifikasi algoritme seperti NeedsTagFirst , tetapi itu benar-benar membuatnya lebih sulit untuk digunakan)

Juga untuk enkripsi, Inisialisasi membutuhkan IV sebelum transformasi kripto apa pun.

Terakhir, untuk mengenkripsi dan mendekripsi, Inisialisasi membutuhkan kunci enkripsi AES sebelum transformasi apa pun.

Kuncinya adalah parameter ctor kelas. IV/nonce berasal dari penyedia IV/nonce dalam parameter ctor.

Model penyedia memecahkan SIV, di mana tidak ada IV yang diberikan selama enkripsi, satu dihasilkan atas nama data. Jika tidak, SIV memiliki parameter dan mengharuskan nilai kosong diberikan.

atau Anda lupa mengetik sedikit?

Metode streaming sedang ditambahkan ke proposal saya yang sudah ada, yang sudah memiliki kunci dan penyedia IV/nonce sebagai parameter ctor.

@bartonjs : Poin bagus bahwa beberapa algo ingin memberi tag terlebih dahulu sementara yang lain di akhir dan terima kasih atas pengingatnya bahwa ini adalah tambahan untuk spesifikasi asli. Saya menemukan bahwa mempertimbangkan kasus penggunaan membuatnya lebih mudah, jadi inilah contoh cloud-first:

Kami akan melakukan analitik pada satu atau lebih file terenkripsi AES-GCM 10GB (yaitu tag setelah ciphertext) yang disimpan di penyimpanan. Pekerja analitik secara bersamaan mendekripsi beberapa aliran masuk ke mesin/cluster terpisah dan setelah pemeriksaan byte + tag terakhir, memulai setiap beban kerja analisis. Semua penyimpanan, pekerja, VM analitik ada di Azure AS-Barat.

Di sini, tidak ada cara untuk mengambil tag di akhir setiap aliran dan memberikannya ke metode Initialize AuthenticatedDecryptor. Jadi, meskipun pengguna secara sukarela mengubah kode untuk penggunaan GCM, mereka bahkan tidak dapat mulai menggunakan API.

Kalau dipikir-pikir, satu-satunya cara kita bisa memiliki API yang mengakomodasi berbagai AEAD DAN tidak memiliki perubahan kode pengguna, adalah jika penyedia kripto untuk algoritma AEAD yang berbeda secara otomatis menangani tag. Java melakukan ini dengan meletakkan tag di akhir ciphertext untuk GCM dan mencabutnya selama dekripsi tanpa campur tangan pengguna. Selain itu, setiap kali seseorang mengubah algoritme secara signifikan (misalnya CBC-HMAC => GCM), mereka harus memodifikasi kodenya karena sifat pemrosesan tag-first dan tag-last yang saling eksklusif.

IMHO, pertama-tama kita harus memutuskan apakah

Opsi 1) Penyedia algoritme menangani manajemen tag secara internal (seperti Java)

atau

Opsi 2) Cukup ekspos pada API agar pengguna dapat melakukannya sendiri (seperti WinNT bcrypt atau openssl)

Opsi 1 akan sangat menyederhanakan keseluruhan pengalaman bagi konsumen perpustakaan karena manajemen buffer bisa menjadi rumit. Selesaikan dengan baik di perpustakaan dan setiap pengguna tidak perlu menyelesaikannya setiap saat sekarang. Plus semua AEAD mendapatkan antarmuka yang sama (tag-first, tag-last, tag-less) dan menukar algoritma juga lebih sederhana.

Suara saya akan untuk opsi 1.

Akhirnya, kami dapat menggali implementasi kami yang memungkinkan operasi streaming ICryptoTransform melalui GCM untuk secara otomatis mencabut tag sumber in-stream . Ini adalah pembaruan yang signifikan untuk pembungkus CLR Security sendiri dan meskipun ada salinan buffer tambahan, ini masih sangat cepat (~ 4GB/dtk pada macbook pro pengujian kami di bootcamp Windows 10). Kami pada dasarnya membungkus Keamanan CLR untuk membuat opsi 1 untuk diri kami sendiri sehingga kami tidak perlu melakukannya di tempat lain. Visual ini sangat membantu menjelaskan apa yang terjadi di dalam TransformBlock dan TransformFinalBlock dari antarmuka ICryptoTransform .

@sidshetye Saya tidak yakin mengapa contoh cloud-first Anda diblokir. Jika Anda membaca dari penyimpanan, Anda dapat mengunduh beberapa byte tag terakhir terlebih dahulu dan memberikannya ke decryptor ctor. Jika menggunakan API Penyimpanan Azure, ini akan dilakukan melalui CloudBlockBlob.DownloadRangeXxx .

@GrabYourPitchforks Jangan terlalu teralihkan pada contoh itu, tetapi itu adalah kemampuan khusus Penyimpanan Azure Blob. Secara umum, penyimpanan berbasis VM (IaaS) atau beban kerja Penyimpanan non-Azure biasanya mendapatkan aliran jaringan yang tidak dapat dicari.

Saya, secara pribadi, sangat senang melihat @GrabYourPitchforks - yay!

Kami akan melakukan analitik pada satu atau lebih file terenkripsi AES-GCM 10GB (yaitu tag setelah ciphertext) yang disimpan di penyimpanan. Pekerja analitik secara bersamaan mendekripsi beberapa aliran masuk ke mesin/cluster terpisah dan setelah pemeriksaan byte + tag terakhir, memulai setiap beban kerja analisis. Semua penyimpanan, pekerja, VM analitik ada di Azure AS-Barat.

@sidshetye , Anda sangat bersikeras untuk memisahkan protokol primitif bodoh-n-berbahaya dan protokol pintar-n-huggable! Saya punya mimpi - dan saya percaya itu. Dan kemudian Anda melemparkan ini pada kami. Ini adalah protokol - desain sistem. Siapa pun yang merancang protokol yang Anda gambarkan itu - kacau. Tidak ada gunanya menangisi ketidakmampuan untuk memasukkan pasak persegi ke dalam lubang bundar sekarang.

Siapa pun file 10Gb yang dienkripsi GCM tidak hanya hidup sangat dekat dengan tepi primitif (GCM tidak bagus setelah 64Gb), tetapi ada juga pernyataan implisit bahwa seluruh ciphertext perlu di-buffer.

Siapa pun yang mengenkripsi file 10 Gb dengan GCM membuat kesalahan protokol dengan kemungkinan yang sangat besar. Solusinya: enkripsi chunked. TLS memiliki panjang variabel 16k-terbatas chunking, dan ada rasa lain yang lebih sederhana, bebas PKI. Daya tarik seks "mendahulukan awan" dari contoh hipotetis ini tidak mengurangi kesalahan desain.

(Saya memiliki banyak hal yang harus dilakukan di utas ini.)

@sdrapkin mengangkat poin tentang penggunaan kembali antarmuka IAuthenticatedEncryptor dari lapisan Perlindungan Data. Sejujurnya saya tidak berpikir itu abstraksi yang tepat untuk primitif, karena lapisan Perlindungan Data cukup berpendirian dalam cara melakukan kriptografi. Misalnya, itu melarang pemilihan sendiri dari IV atau nonce, itu mengamanatkan bahwa implementasi yang sesuai memahami konsep AAD, dan itu menghasilkan hasil yang agak eksklusif. Dalam kasus AES-GCM, nilai kembalian dari IAuthenticatedEncryptor.Encrypt adalah gabungan dari hal aneh yang hampir tidak ada yang digunakan untuk derivasi subkunci, ciphertext yang dihasilkan dari menjalankan AES-GCM di atas plaintext yang disediakan (tetapi bukan AAD!), dan tag AES-GCM. Jadi sementara setiap langkah yang terlibat dalam menghasilkan muatan yang dilindungi aman, muatan itu sendiri tidak mengikuti semua jenis konvensi yang diterima, dan Anda tidak akan menemukan siapa pun selain dari perpustakaan Perlindungan Data yang dapat berhasil mendekripsi ciphertext yang dihasilkan. Itu menjadikannya kandidat yang baik untuk perpustakaan yang menghadap pengembang aplikasi tetapi kandidat yang mengerikan untuk antarmuka yang akan diimplementasikan oleh primitif.

Saya juga harus mengatakan bahwa saya tidak melihat nilai yang cukup besar dalam memiliki One True Interface(tm) IAuthenticatedEncryptionAlgorithm yang seharusnya diterapkan oleh semua algoritma enkripsi yang diautentikasi. Primitif ini "kompleks", tidak seperti primitif cipher blok sederhana atau primitif hashing. Ada terlalu banyak variabel dalam primitif kompleks ini. Apakah hanya AE primitif, atau AEAD? Apakah algoritma menerima IV / nonce sama sekali? (Saya telah melihat beberapa yang tidak.) Apakah ada masalah dengan bagaimana input IV / nonce atau data harus terstruktur? IMO primitif kompleks seharusnya hanya API yang berdiri sendiri, dan perpustakaan tingkat yang lebih tinggi akan mendukung untuk primitif kompleks spesifik yang mereka pedulikan. Kemudian perpustakaan tingkat yang lebih tinggi mengekspos API seragam apa pun yang diyakini sesuai untuk skenarionya.

@sdrapkin Kita keluar topik lagi. Saya hanya akan mengatakan bahwa sistem dibangun menggunakan primitif. Primitif kripto di sini telanjang dan kuat. Sedangkan lapisan sistem/protokol menangani buffering; itu juga pada tingkat cluster, tentu saja tidak dalam memori sistem utama yang akan dipaksakan oleh primitif satu tembakan. batas 'chunking' adalah X (X=10GB di sini) karena <64GB, karena kapasitas buffering cluster hampir tidak terbatas dan tidak ada yang akan/dapat dimulai hingga byte terakhir dimuat di cluster. Ini persis pemisahan masalah, mengoptimalkan setiap lapisan untuk kekuatannya yang telah saya bicarakan. Dan ini hanya dapat terjadi jika primitif yang mendasarinya tidak menghalangi desain/batasan lapisan yang lebih tinggi (perhatikan bahwa lebih banyak aplikasi dunia nyata datang dengan cacat warisan mereka sendiri).

NIST 800-38d detik9.1 menyatakan:

Untuk mencegah pihak yang tidak berwenang mengendalikan atau mempengaruhi pembuatan infus,
GCM harus diimplementasikan hanya dalam modul kriptografi yang memenuhi persyaratan:
Pub FIPS. 140-2. Secara khusus, batas kriptografi modul harus berisi a
"unit pembangkit" yang menghasilkan infus menurut salah satu konstruksi di Sec. 8.2 di atas.
Dokumentasi modul untuk validasinya terhadap persyaratan FIPS 140-2 harus:
jelaskan bagaimana modul memenuhi persyaratan keunikan pada infus.

Itu menyiratkan kepada saya bahwa GCM IV harus dibuat secara otomatis secara internal (dan tidak diteruskan secara eksternal).

@sdrapkin Poin bagus tetapi jika Anda membaca lebih dekat, Anda akan melihat bahwa untuk panjang IV 96 bit ke atas, bagian 8.2.2 memungkinkan untuk menghasilkan IV dengan generator bit acak (RBG) di mana setidaknya 96 bit acak (Anda hanya bisa 0 bit lainnya). Saya memang menyebutkan ini bulan lalu di utas ini sendiri ( di sini di bawah nonce).

LT;DR: INonce adalah jebakan yang mengarah pada ketidakpatuhan terhadap pedoman NIST dan FIPS.

Bagian 9.1 hanya mengatakan, untuk FIPS 140-2, unit generasi IV (acak penuh yaitu detik 8.2.2 atau implementasi deterministik yaitu bagian 8.2.1) harus berada dalam batas modul yang menjalani validasi FIPS. Sejak ...

  1. RBG sudah divalidasi FIPS
  2. Lensa IV >= 96 direkomendasikan
  3. merancang unit generasi IV yang terus melakukan booting ulang, kehilangan daya tanpa batas ke dalam lapisan primitif kripto itu sulit
  4. mendapatkan 3 di atas diimplementasikan dalam perpustakaan crypto DAN mendapatkan sertifikasi itu sulit dan mahal ($ 50K untuk apa pun yang menghasilkan gambar build yang tidak tepat)
  5. Tidak ada kode pengguna yang akan menerapkan 3 dan mendapatkan sertifikasi karena 4 di atas. (mari kita kesampingkan beberapa instalasi militer/pemerintah yang eksotis).

... sebagian besar perpustakaan kripto (lihat Java Oracle, bcryptprimitives WinNT, OpenSSL dll) yang menjalani sertifikasi FIPS menggunakan rute RBG untuk IV dan cukup ambil array byte untuk input. Perhatikan bahwa memiliki antarmuka INonce sebenarnya adalah jebakan dari perspektif NIST dan FIPS karena secara implisit menyarankan bahwa pengguna harus meneruskan implementasi antarmuka itu ke fungsi crypto. Tetapi implementasi INonce oleh pengguna hampir dijamin TIDAK menjalani proses sertifikasi NIST 9 bulan + dan $50K+. Namun, jika mereka baru saja mengirim array byte menggunakan konstruksi RGB (sudah ada di perpustakaan kripto), mereka akan sepenuhnya mematuhi pedoman.

Saya telah mengatakan sebelumnya - perpustakaan crypto yang ada ini telah mengembangkan permukaan API mereka dan telah diuji dalam berbagai skenario. Lebih dari apa yang telah kita singgung di utas panjang ini. Pilihan saya lagi adalah untuk memanfaatkan pengetahuan dan pengalaman itu di semua perpustakaan itu, semua validasi itu dan semua instalasi itu daripada mencoba menemukan kembali roda itu. Jangan menemukan kembali roda. Gunakan untuk menemukan roket :)

Hai teman-teman,

Ada pembaruan tentang ini? Belum melihat pembaruan apa pun di utas peta jalan crypto @karelz atau di utas AES GCM .

Terima kasih
Sid

Jadi proposal konkret terakhir adalah dari https://github.com/dotnet/corefx/issues/23629#issuecomment -334328439 :

partial class AuthenticatedEncryptor
{
    // throws if an operation is already in progress
    public abstract void Initialize(ReadOnlySpan<byte> associatedData);
    // true on success, false on “destination too small”, exception on anything else.
    public abstract bool TryEncrypt(ReadOnlySpan<byte> data, Span<byte> encryptedData, out int bytesRead, out int bytesWritten);
    // false if remainingEncryptedData is too small, throws if other inputs are too small, see NonceOrIVSizeInBits and TagSizeInBits properties.
    // NonceOrIvUsed could move to Initialize, but then it might be interpreted as an input.
    public abstract bool TryFinish(ReadOnlySpan<byte> remainingData, Span<byte> remainingEncryptedData, out int bytesWritten, Span<byte> tag, Span<byte> nonceOrIvUsed);
}

partial class AuthenticatedDecryptor 
{
    // throws if an operation is already in progress
    public abstract void Initialize(ReadOnlySpan<byte> tag, ReadOnlySpan<byte> nonceOrIv, ReadOnlySpan<byte> associatedData);
    // true on success, false on “destination too small”, exception on anything else.
    public abstract bool TryDecrypt(ReadOnlySpan<byte> data, Span<byte> decryptedData, out int bytesRead, out int bytesWritten);
    // throws on bad tag, but might leak the data anyways.
    // (remainingDecryptedData is required for CBC+HMAC, and so may as well add remainingData, I guess?)
    public abstract bool TryFinish(ReadOnlySpan<byte> remainingData, Span<byte> remainingDecryptedData, out int bytesWritten);
}

Hanya beberapa masalah potensial yang telah diangkat sejak:

  • Tag diperlukan di muka, yang menghalangi skenario tertentu. Entah API harus menjadi jauh lebih kompleks untuk memungkinkan fleksibilitas lebih lanjut, atau masalah ini harus dianggap sebagai masalah protokol (yaitu tingkat tinggi).
  • INonceProvider mungkin tidak perlu rumit dan/atau menyebabkan ketidakpatuhan terhadap pedoman NIST dan FIPS.
  • Abstraksi yang dimaksudkan dari enkripsi primitif yang diautentikasi mungkin hanya mimpi belaka, karena perbedaannya mungkin terlalu besar. Belum ada pembahasan lebih lanjut mengenai usulan ini.

Saya ingin menyarankan yang berikut:

  1. Kompleksitas tambahan karena tidak memerlukan tag di muka tampaknya parah, skenario masalah yang sesuai tampaknya tidak biasa, dan masalahnya memang terdengar sangat mirip dengan masalah protokol. Desain yang bagus bisa menampung banyak, tapi tidak semuanya. Secara pribadi saya merasa nyaman menyerahkan ini pada protokol. (Contoh tandingan yang kuat diterima.)
  2. Diskusi secara konsisten bergerak menuju implementasi tingkat rendah yang fleksibel yang tidak melindungi dari penyalahgunaan, dengan pengecualian generasi IV . Mari kita konsisten. Konsensus umum tampaknya bahwa API tingkat tinggi adalah langkah penting berikutnya, vital untuk penggunaan yang tepat oleh sebagian besar pengembang - ini adalah cara kami lolos dari tidak melindungi dari penyalahgunaan di API tingkat rendah . Tetapi tampaknya dosis ketakutan ekstra telah menopang gagasan pencegahan penyalahgunaan _di bidang generasi IV_. Dalam konteks API tingkat rendah, dan agar konsisten, saya akan condong ke byte[] -setara. Tetapi pertukaran implementasi lebih mulus dengan INonceProvider yang disuntikkan. Apakah komentar @sidshetye tidak dapat disangkal, atau dapatkah implementasi INonceProvider sederhana yang hanya memanggil RNG masih dianggap sesuai?
  3. Abstraksi tampaknya berguna, dan begitu banyak upaya telah dilakukan untuk merancangnya, sehingga sekarang saya yakin mereka akan melakukan lebih banyak manfaat daripada bahaya. Selain itu, API tingkat tinggi masih dapat memilih untuk mengimplementasikan API tingkat rendah yang tidak sesuai dengan abstraksi tingkat rendah.
  4. IV adalah istilah umum, dan nonce adalah jenis IV khusus, benar? Ini meminta penggantian nama dari INonceProvider menjadi IIVProvider , dan dari nonceOrIv* menjadi iv* . Lagi pula, kita selalu berurusan dengan infus, tetapi tidak harus dengan nonce.

Tag di muka adalah non starter untuk skenario saya jadi saya mungkin akan menyimpan implementasi saya sendiri. Yang baik-baik saja Saya tidak yakin itu adalah secangkir teh semua orang untuk menulis kode kinerja tinggi di area ini.

Masalahnya adalah itu akan menyebabkan latensi yang tidak dibutuhkan. Anda harus mem-buffer seluruh pesan terlebih dahulu untuk mendapatkan tag di bagian akhir untuk memulai decoding frame. Ini berarti Anda pada dasarnya tidak dapat tumpang tindih IO dan mendekripsi.

Saya tidak yakin mengapa begitu sulit untuk mengizinkannya pada akhirnya. Tapi saya tidak akan menghalangi jalan untuk API ini, itu tidak akan menarik dalam skenario saya.

IV adalah istilah umum, dan nonce adalah jenis IV khusus, benar?

Tidak. nonce adalah angka yang digunakan sekali . Algoritme yang menentukan nonce menunjukkan bahwa penggunaan kembali melanggar jaminan algoritme. Dalam kasus GCM, menggunakan nonce yang sama dengan kunci yang sama dan pesan yang berbeda dapat mengakibatkan kompromi dari kunci GHASH, mengurangi GCM menjadi RKT.

Dari http://nvlpubs.nist.gov/nistpubs/ir/2013/NIST.IR.7298r2.pdf :

Nonce: Nilai yang digunakan dalam protokol keamanan yang tidak pernah diulang dengan kunci yang sama. Misalnya, nonces yang digunakan sebagai tantangan dalam protokol autentikasi tantangan-respons umumnya tidak boleh diulang sampai kunci autentikasi diubah. Jika tidak, ada kemungkinan serangan replay. Menggunakan nonce sebagai tantangan adalah persyaratan yang berbeda dari tantangan acak, karena nonce belum tentu tidak dapat diprediksi.

Sebuah "IV" tidak memiliki persyaratan ketat yang sama. Misalnya, mengulangi IV dengan CBC hanya akan membocorkan apakah pesan terenkripsi sama atau berbeda dari pesan sebelumnya dengan IV yang sama. Itu tidak melemahkan algoritma.

Nonce adalah angka yang digunakan sekali.
Sebuah "IV" tidak memiliki persyaratan ketat yang sama.

@bartonjs Ya. Saya akan beralasan bahwa, karena nonce digunakan untuk menginisialisasi primitif kripto, itu adalah vektor inisialisasi. Ini sangat sesuai dengan definisi IV apa pun yang dapat saya temukan. Persyaratannya lebih ketat ya, seperti halnya menjadi sapi memiliki persyaratan yang lebih ketat daripada menjadi hewan. Kata-kata saat ini tampaknya meminta parameter "cowOrAnimal". Fakta bahwa mode yang berbeda memiliki persyaratan IV yang berbeda tidak mengubah fakta bahwa mereka semua meminta beberapa bentuk IV. Jika ada sesuatu yang saya lewatkan, tentu saja pertahankan kata-kata saat ini, tetapi sejauh yang saya tahu, hanya "iv" atau "IIVProvider" yang sederhana dan benar.

Untuk menikmati nonceOrIv bikeshedding:

96bit GCM IV terkadang didefinisikan sebagai salt nonce -byte (mis. RFC 5288). RFC 4106 mendefinisikan GCM nonce sebagai 4-byte salt dan 8-byte iv . RFC 5084 (GCM dalam CMS) mengatakan bahwa CCM membutuhkan nonce , GCM membutuhkan iv , tetapi _"...untuk memiliki seperangkat istilah umum untuk AES-CCM dan AES-GCM , AES-GCM IV disebut sebagai nonce dalam sisa dokumen ini."_ RFC 5647 (GCM untuk SSH) mengatakan _"catatan: dalam [RFC5116], IV disebut nonce."_ RFC 4543 ( GCM di IPSec) mengatakan _"kami merujuk ke input AES-GMAC IV sebagai nonce, untuk membedakannya dari bidang IV dalam paket."_ RFC 7714 (GCM untuk SRTP) berbicara tentang IV 12-byte

Mengingat kurangnya konsistensi di sebagian besar spesifikasi GCM, nonceOrIv agak masuk akal. $0,02

Tag dimuka adalah non-starter

Seperti pelanggan lain yang menyuarakan diri mereka sendiri di sini, mewajibkan tag di muka juga bukan langkah awal bagi kami. Tidak mungkin .NET dapat memproses aliran bersamaan dengan batasan yang diperkenalkan secara artifisial ini. Benar-benar membunuh skalabilitas.

Bisakah Anda mendukung pernyataan bahwa itu menambah kerumitan? Karena itu sebenarnya harus sepele. Ditambah tidak ada implementasi crypto khusus platform (yang akan Anda bungkus) memiliki batasan ini. Secara khusus, alasannya adalah bahwa tag input hanya perlu waktu konstan dibandingkan dengan tag yang dihitung. Dan tag yang dihitung hanya tersedia setelah blok terakhir didekripsi selama TryFinish . Jadi pada dasarnya, ketika Anda memulai implementasi, Anda akan menemukan bahwa Anda hanya menyimpan tag di dalam instance Anda hingga TryFinish . Anda bisa memilikinya sebagai input opsional

public abstract bool TryFinish(ReadOnlySpan<byte> remainingData, 
                             Span<byte> remainingDecryptedData, 
                             out int bytesWritten, 
                             ReadOnlySpan<byte> tag = null); // <==

Saya juga berpikir kami berusaha terlalu keras untuk menormalkan ke satu antarmuka yang akan mencakup semua skenario kripto. Saya juga lebih suka antarmuka umum, tetapi tidak pernah dengan mengorbankan fungsionalitas atau skalabilitas - terutama pada lapisan dasar seperti perpustakaan cryto standar dari bahasa itu sendiri. IMHO, jika seseorang menemukan dirinya melakukannya, biasanya itu berarti abstraksinya salah.

Jika diperlukan antarmuka yang konsisten dan sederhana, saya lebih suka pendekatan Java - sebelumnya juga diangkat di sini sebagai opsi 1 . Itu juga menghindari masalah tag pertama/tag terakhir di atas dengan menjaganya tetap dalam implementasi algoritme (IMHO, seperti yang saya pikir seharusnya). Tim saya tidak menerapkan ini, jadi ini bukan keputusan kami TETAPI jika kami harus membuat keputusan dan mulai menerapkannya - kami pasti akan menempuh rute ini.

Harap hindari antarmuka INonce byte[] atau span<> sederhana sudah cukup untuk antarmuka tingkat rendah yang sesuai.

IV vs Nonce - Kasus umum memang IV. Untuk GCM IV harus menjadi Nonce (misalnya Mobil vs RedOrCar). Dan saat saya menyalin-menempel ini, saya baru menyadari @timovzl menggunakan contoh yang sangat mirip :)

@sidshetye Bisakah Anda membuat proposal yang tepat bahwa keduanya (1) mendukung algoritme yang memerlukan tag di muka, dan (2) hanya membutuhkan tag hingga TryFinish dalam semua situasi lain?

Saya kira Anda memikirkan sesuatu ke arah berikut?

  • Tag di Initialize boleh null. Hanya algoritme yang membutuhkannya di muka yang akan membuang nol.
  • Tag di TryFinish wajib diisi, atau (sebagai alternatif) boleh null untuk algoritme yang telah mewajibkannya di awal.

Saya kira hal di atas hanya menambah kerumitan dalam bentuk dokumentasi dan pengetahuan. Untuk API tingkat rendah, ini dapat dianggap sebagai pengorbanan kecil, karena dokumentasi dan pengetahuan yang memadai tetap diperlukan.

Saya mulai yakin bahwa ini mungkin, untuk kompatibilitas dengan implementasi lain, dan streaming.

@timovzl Tentu, saya berharap menganggarkan waktu besok untuk ini.

@Timovzl , saya akhirnya punya waktu hari ini dan ini ternyata menjadi lubang kelinci! Ini panjang tapi saya pikir ini menangkap sebagian besar kasus penggunaan, menangkap kekuatan .NET crypto ( ICryptoTransform ) sambil merangkul arah .NET Core/Standard ( Span<> ). Saya sudah membaca ulang tetapi berharap tidak ada kesalahan ketik di bawah ini. Saya juga berpikir beberapa komunikasi waktu nyata (obrolan, panggilan conf dll) sangat penting untuk brainstorming yang cepat; Saya harap Anda dapat mempertimbangkannya.

Model pemrograman

Pertama-tama saya akan berbicara tentang model pemrograman yang dihasilkan untuk pengguna API.

Enkripsi Streaming

var aesGcm = new AesGcm();
using (var encryptor = aesGcm.CreateAuthenticatedEncryptor(Key, IV, AAD))
{
  using (var cryptoOutStream = new CryptoStream(cipherOutStream, encryptor, CryptoStreamMode.Write))
  {
    clearInStream.CopyTo(cryptoOutStream);
  }
}

Streaming Dekripsi

var aesGcm = new AesGcm();
using (var decryptor = aesGcm.CreateAuthenticatedDecryptor(Key, IV, AAD))
{
  using (var decryptStream = new CryptoStream(cipherInStream, encryptor, CryptoStreamMode.Write))
  {
    decryptStream.CopyTo(clearOutStream);
  }
}

Non-streaming

Karena non-streaming adalah kasus khusus streaming, kami dapat membungkus kode pengguna di atas ke dalam metode pembantu pada AuthenticatedSymmetricAlgorithm (didefinisikan di bawah) untuk mengekspos API yang lebih sederhana. yaitu

public class AesGcm : AuthenticatedSymmetricAlgorithm
{
  ...
  // These return only after consuming entire input buffer

  // Code like Streaming Encrypt from above within here
​  public abstract bool TryEncrypt(ReadOnlySpan<byte> clearData, Span<byte> encryptedData);

  // Code like Streaming Decrypt from above within here
  public abstract bool TryDecrypt(ReadOnlySpan<byte> encryptedData, Span<byte> clearData); 
  ...
}

Ini dapat berfungsi ganda sebagai menyajikan API yang lebih sederhana seperti

Enkripsi non-streaming

var aesGcm = new AesGcm(Key, IV, AAD);
aesGcm.TryEncrypt(clearData, encryptedData);
var tag = aesGcm.Tag;

Dekripsi non-streaming

var aesGcm = new AesGcm(Key, IV, AAD);
aesGcm.Tag = tag;
aesGcm.TryDecrypt(encryptedData, clearData);

Dibawah tenda

Melihat sumber corefx, Span<> ada di mana-mana. Ini termasuk System.Security.Cryptography.* - kecuali untuk cipher simetris, jadi mari kita perbaiki enkripsi terotentikasi pertama dan lapisan di atas.

1. Buat ICipherTransform untuk Span I/O

Ini seperti versi sadar Span dari ICryptoTransform . Saya baru saja mengubah antarmuka itu sendiri sebagai bagian dari pemutakhiran kerangka kerja tetapi karena orang dapat menjadi sensitif tentang itu, saya menyebutnya ICipherTransform .

public partial interface ICipherTransform : System.IDisposable
{
  bool CanReuseTransform { get; }
  bool CanTransformMultipleBlocks { get; } // multiple blocks in single call?
  bool CanChainBlocks { get; }             // multiple blocks across Transform/TransformFinal
  int InputBlockSize { get; }
  int OutputBlockSize { get; }
  int TransformBlock(ReadOnlySpan<byte> inputBuffer, int inputOffset, int inputCount, Span<byte> outputBuffer, int outputOffset);
  Span<byte> TransformFinalBlock(ReadOnlySpan<byte> inputBuffer, int inputOffset, int inputCount);
}

Tandai juga ICryptoTransform sebagai [Obsolete]

Bersikap sopan kepada orang-orang dengan pengetahuan sebelumnya tentang .NET crypto

[Obsolete("See ICipherTransform")]
public partial interface ICryptoTransform : System.IDisposable { ... }

2. Perpanjang kelas SymmetricAlgorithm yang ada untuk Span I/O

public abstract class SymmetricAlgorithm : IDisposable
{
  ...
  public abstract ICipherTransform CreateDecryptor(ReadOnlySpan<byte> Key, ReadOnlySpan<byte> IV);
  public abstract ICipherTransform CreateEncryptor(ReadOnlySpan<byte> Key, ReadOnlySpan<byte> IV);
  public virtual ReadOnlySpan<byte> KeySpan {...}
  public virtual ReadOnlySpan<byte> IVSpan {...}
  public virtual ReadOnlySpan<byte> KeySpan {...}
  ...
}

3. Perpanjang CryptoStream yang ada untuk Span I/O

Ini seperti Stream di System.Runtime. Plus kami akan menambahkan c'tor untuk kasus AEAD kami untuk diikuti.

KRITIS: CryptoStream akan memerlukan peningkatan wajib di FlushFinalBlock untuk menambahkan tag ke akhir aliran selama enkripsi dan secara otomatis mengekstrak tag (byte TagSize) selama dekripsi . Ini mirip dengan API pengujian pertempuran lainnya seperti Arsitektur Kriptografi Java atau C# BouncyCastle. Ini tidak dapat dihindari namun tempat terbaik untuk melakukan ini karena dalam streaming tag diproduksi di akhir namun tidak diperlukan sampai blok terakhir diubah selama dekripsi. Keuntungannya adalah sangat menyederhanakan model pemrograman.

Catatan: 1) Dengan CBC-HMAC, Anda dapat memilih untuk memverifikasi tag terlebih dahulu. Ini adalah opsi yang lebih aman tetapi jika demikian, itu benar-benar menjadikannya algoritma dua jalur. Pass pertama menghitung tag HMAC, lalu pass ke-2 benar-benar melakukan dekripsi. Jadi aliran memori atau aliran jaringan harus selalu disangga dalam memori sehingga menguranginya menjadi model satu-shot; tidak mengalir. Algoritme AEAD yang sebenarnya seperti GCM atau CCM dapat melakukan streaming secara efisien.

public class CryptoStream : Stream, IDisposable
{
  ...
  public CryptoStream(Stream stream, IAuthenticatedCipherTransform transform, CryptoStreamMode mode);
  public override int Read(Span<byte> buffer, int offset, int count);
  public override Task<int> ReadAsync(Span<byte> buffer, int offset, int count, CancellationToken cancellationToken);
  public override void Write(ReadOnlySpan<byte> buffer, int offset, int count);
  public override Task WriteAsync(ReadOnlySpan<byte> buffer, int offset, int count, CancellationToken cancellationToken);

  public void FlushFinalBlock()
  {
    ...
    // If IAuthenticatedCipherTransform
    //    If encrypting, `TransformFinalBlock` -> `GetTag` -> append to out stream
    //    If decryption, extract last `TagSize` bytes -> `SetExpectedTag` -> `TransformFinalBlock`
    ...
  }

  ...
}

Lapisan dalam Enkripsi Terotentikasi

Dengan hal di atas, kita dapat menambahkan bit yang hilang untuk memungkinkan Enkripsi Terotentikasi dengan Data Terkait (AEAD)

Perpanjang ICipherTransform baru untuk AEAD

Hal ini memungkinkan CryptoStream dapat melakukan tugasnya dengan baik. Kami juga dapat menggunakan antarmuka IAuthenticatedCipherTransform untuk mengimplementasikan kelas/penggunaan streaming kustom kami sendiri, tetapi bekerja dengan CryptoStream menghasilkan API kripto .net yang sangat kohesif dan konsisten.

  public interface IAuthenticatedCipherTransform : ICipherTransform
  {
    Span<byte> GetTag();
    void SetExpectedTag(Span<byte> tag);
  }

Kelas dasar Enkripsi yang Diautentikasi

Cukup kembangkan SymmetricAlgorithm

public abstract class AuthenticatedSymmetricAlgorithm : SymmetricAlgorithm
{
  ...
  // Program Key/IV/AAD via class properties OR CreateAuthenticatedEn/Decryptor() params
  public abstract IAuthenticatedCipherTransform CreateAuthenticatedDecryptor(ReadOnlySpan<byte> Key = default, ReadOnlySpan<byte> IV = default, ReadOnlySpan<byte> AuthenticatedData = default);
  public abstract IAuthenticatedCipherTransform CreateAuthenticatedEncryptor(ReadOnlySpan<byte> Key = default, ReadOnlySpan<byte> IV = default, ReadOnlySpan<byte> AuthenticatedData = default);
  public virtual Span<byte> AuthenticatedData {...}
  public virtual Span<byte> Tag {...}
  public virtual int TagSize {...}
  ...
}

Kelas AES GCM

public class AesGcm : AuthenticatedSymmetricAlgorithm
{
  public AesGcm(ReadOnlySpan<byte> Key = default, ReadOnlySpan<byte> IV = default, ReadOnlySpan<byte> AuthenticatedData = default)

  /* other stuff like valid key sizes etc similar to `System.Security.Cryptography.Aes` */
}

@sidshetye saya salut dengan usahanya.

Streaming-Enkripsi melalui GCM dapat dilakukan. Streaming-Dekripsi melalui GCM adalah

  • tidak diperbolehkan di NIST 800-38d. Bagian 5.2.2 "Fungsi Dekripsi yang Diotentikasi" sangat jelas bahwa pengembalian plaintext P yang didekripsi harus menyiratkan otentikasi yang benar melalui tag T.
  • tidak aman. Ada gagasan keamanan tentang algoritme yang aman dalam pengaturan "Rilis Teks Biasa yang Tidak Diverifikasi" (RUP). Keamanan RUP diformalkan dalam makalah 2014 oleh Andreeva-et-al. GCM tidak aman dalam pengaturan RUP. Kompetisi CAESAR di mana setiap entri dibandingkan dengan GCM mencantumkan keamanan RUP sebagai properti yang diinginkan. Plaintext yang belum diverifikasi yang dirilis dari GCM sangat rentan terhadap serangan bit-flipping.

Sebelumnya di utas ini kemungkinan API Enkripsi/Dekripsi asimetris telah dikemukakan (secara konseptual), dan saya pikir konsensusnya adalah bahwa itu akan menjadi ide yang sangat buruk.

Singkatnya, Anda tidak dapat memiliki API streaming byte-granular tingkat tinggi untuk dekripsi GCM. Saya mengatakannya berkali-kali sebelumnya, dan saya mengatakannya lagi. Satu-satunya cara untuk memiliki streaming API adalah enkripsi chunked. Saya akan membiarkan semua orang komidi putar pada enkripsi chunked..

Apa pun yang diputuskan MS untuk dilakukan untuk GCM API, RUP tidak dapat diizinkan.

@sdrapkin RUP dibahas di sini secara rinci dan kami telah melewati jembatan itu. Secara singkat, RUP menyiratkan bahwa data yang didekripsi tidak perlu digunakan hingga tag diverifikasi, tetapi seperti Java JCE, WinNT bcrypt, OpenSSL, dll., RUP tidak harus diterapkan pada batas metode. Seperti kebanyakan crypto primitif, terutama yang tingkat rendah, gunakan dengan hati-hati.

^^ begitu banyak. Saya setuju di API aliran tingkat yang lebih tinggi, dll, lalu terapkan dengan baik. Tetapi ketika saya ingin menggunakan primitif tingkat rendah maka saya harus dapat menggunakan hal-hal seperti buffer split dll, dan terserah saya untuk memastikan data tidak digunakan. Lempar pengecualian dalam penghitungan tag/titik pemeriksaan tetapi jangan melumpuhkan primitif tingkat rendah.

terserah saya untuk memastikan data tidak digunakan

Salah. Ini tidak terserah Anda. AES-GCM memiliki definisi yang sangat _spesifik_, dan definisi tersebut memastikan bahwa itu tidak terserah Anda. Yang Anda inginkan adalah primitif AES-CTR terpisah, dan primitif GHASH terpisah, yang kemudian dapat Anda gabungkan dan terapkan sesuai keinginan Anda. Tapi kita tidak sedang membahas AES-CTR dan GHASH primitif yang terpisah, bukan? Kami sedang mendiskusikan AES-GCM. Dan AES-GCM mengharuskan RUP tidak diperbolehkan.

Saya juga menyarankan untuk meninjau jawaban Ilmari Karonen dari crypto.stackexchange .

@sdrapkin Anda membuat poin yang bagus. Akan lebih baik, bagaimanapun, untuk akhirnya memiliki algoritme yang aman di bawah RUP, dan algoritme itu sesuai dengan API yang diputuskan di sini. Jadi kita harus memilih:

  1. API tidak mendukung streaming. Sederhana, tetapi kurang untuk API tingkat rendah. Kita mungkin menyesali ini suatu hari nanti.
  2. Implementasi AES-GCM mencegah streaming, mengikuti spesifikasi tanpa membatasi API.

Bisakah kami mendeteksi skenario streaming dan memberikan pengecualian yang menjelaskan mengapa penggunaan ini salah? Atau apakah kita perlu menggunakan implementasi streaming yang menghabiskan seluruh buffer? Yang terakhir akan sangat disayangkan: Anda mungkin berpikir Anda sedang streaming, tetapi sebenarnya tidak.

Kita bisa menambahkan metode SupportsStreaming(out string whyNot) yang diperiksa oleh implementasi streaming.

Apakah kami memiliki argumen yang kuat terhadap streaming/tag-at-the-end secara umum ? Jika tidak, maka saya yakin kita harus bertujuan untuk tidak menghalanginya dengan API.

@sdrapkin : Mari kita lihat RUP secara lebih luas karena ini adalah perpustakaan, bukan aplikasi. Jadi ini lebih merupakan masalah buffering dan desain lapisan daripada rilis/penggunaan data yang tidak diverifikasi. Melihat publikasi khusus NIST 800-38D untuk AES GCM , kami melihat bahwa

  1. Spesifikasi GCM mendefinisikan panjang plaintext maksimum menjadi 2^39-256 bit ~ 64 GB. Buffering di mana saja dekat dengan itu di memori sistem tidak masuk akal.

  2. Spesifikasi GCM mendefinisikan output sebagai FAIL jika tag gagal. Tapi itu tidak menentukan tentang lapisan mana dalam implementasi yang harus disangga hingga verifikasi tag. Mari kita lihat tumpukan panggilan seperti:

A => AESGCM_Decrypt_App(key, iv, ciphertext, aad, tag)
B =>  +- AESGCM_Decrypt_DotNet(key, iv, ciphertext, aad, tag)
C =>    +- AESGCM_Decrypt_OpenSSL(key, iv, ciphertext, aad, tag)

Di mana
A adalah AES GCM pada lapisan aplikasi
B adalah AES-GCM pada lapisan bahasa
C adalah AES-GCM pada lapisan platform

Teks biasa dilepaskan pada (A) jika tag keluar tetapi GAGAL dikembalikan jika sebaliknya. Namun sama sekali tidak ada di mana dalam spesifikasi yang menunjukkan bahwa memori utama adalah satu-satunya tempat untuk buffer plaintext-in-progress, atau buffering harus terjadi di (B) atau (C) atau di tempat lain. Bahkan OpenSSL, Windows NT Bcrypt pada contoh (C) di mana streaming memungkinkan buffering di lapisan yang lebih tinggi. Dan Java JCA, Keamanan CLR Microsoft dan proposal saya di atas adalah contoh (B) di mana streaming mengizinkan buffering pada lapisan aplikasi. Terlalu lancang untuk menganggap desainer A tidak memiliki kemampuan buffering yang lebih baik sebelum merilis plaintext. Penyangga itu secara teori dan praktik bisa berupa memori atau SSD atau kluster penyimpanan di seluruh jaringan. Atau kartu punch ;) !

Bahkan mengesampingkan buffering, makalah ini membahas masalah praktis lainnya (lihat Bagian 9.1, pertimbangan desain dan 9.2, Pertimbangan Operasional ) seperti kesegaran kunci atau non-pengulangan IV di seluruh kegagalan daya yang tidak terbatas. Kami jelas tidak akan memanggang ini ke dalam lapisan B yaitu di sini.

@timovzl proposal terbaru di atas membahas kedua skenario - one-shot (arsitek tidak peduli dengan buffering) serta streaming (arsitek memiliki kemampuan buffering yang lebih baik). Selama dokumentasi API streaming tingkat rendah jelas bahwa konsumen sekarang bertanggung jawab untuk menyangganya, tidak ada pengurangan bukti keamanan dan tidak ada penyimpangan dari spesifikasi.

EDIT: tata bahasa, kesalahan ketik, dan mencoba membuat penurunan harga berfungsi

Bingo .. Sekali lagi itu adalah keputusan desainer lapisan di mana verifikasi tag terjadi. Saya sama sekali tidak menganjurkan pelepasan data yang belum diverifikasi ke lapisan aplikasi.

Diskusi kembali ke apakah API tingkat "konsumen" ini atau apakah mereka Primitif sejati. Jika mereka benar-benar primitif maka mereka harus mengekspos fungsionalitas sehingga API "lebih aman" tingkat yang lebih tinggi dapat dibangun di atas. Sudah diputuskan di atas dengan diskusi Nonce bahwa ini harus menjadi primitif sejati yang berarti Anda dapat menembak diri sendiri, saya pikir hal yang sama berlaku untuk penguraian teks sandi streaming/parsial.

Dengan mengatakan itu, akan sangat penting untuk menyediakan tingkat "lebih tinggi" dan API yang lebih aman dengan cepat untuk menghentikan orang-orang "menggulingkan" mereka sendiri di atasnya.

Ketertarikan saya berasal dari jaringan/pipa dan jika Anda tidak dapat melakukan buffer parsial dan harus melakukan "satu tembakan" maka tidak akan ada manfaat untuk kelemahan API ini jadi saya akan terus pergi ke BCrypt/OpenSsl dll secara langsung.

Ketertarikan saya berasal dari jaringan/pipa dan jika Anda tidak dapat melakukan buffer parsial dan harus melakukan "satu tembakan" maka tidak akan ada manfaat untuk kelemahan API ini jadi saya akan terus pergi ke BCrypt/OpenSsl dll secara langsung.

Tepat. Kebutuhan tidak akan hilang, sehingga orang akan menggunakan implementasi lain atau menggulung sendiri. Itu belum tentu merupakan hasil yang lebih aman daripada mengizinkan streaming dengan dokumentasi peringatan yang baik.

@Timovzl , saya pikir kami telah memperoleh banyak umpan balik teknis dan persyaratan desain di sekitar proposal terakhir . Pemikiran tentang implementasi dan rilis?

@Sidshetye telah membuat proposal terperinci yang saya yakini memenuhi semua persyaratan. Kritik tunggal, mengenai RUP, telah ditanggapi tanpa perlawanan lebih lanjut. (Secara khusus, RUP dapat dicegah di salah satu dari beberapa lapisan, dan API tingkat rendah tidak boleh menentukan yang mana; dan menawarkan streaming _no_ diharapkan memiliki efek yang lebih buruk.)

Demi kemajuan, saya ingin mengundang siapa pun yang memiliki masalah lebih lanjut dengan proposal terbaru untuk berbicara - dan, tentu saja, menawarkan alternatif.

Saya antusias dengan proposal ini dan tentang pembentukan API.

@Sidshetye , saya punya beberapa pertanyaan dan saran:

  1. Apakah diinginkan untuk mewarisi dari SymmetricAlgorithm yang ada? Apakah ada komponen yang ingin kita integrasikan? Kecuali saya kehilangan beberapa keuntungan dari pendekatan itu, saya lebih suka melihat AuthenticatedEncryptionAlgorithm tanpa kelas dasar. Jika tidak ada yang lain, itu menghindari mengekspos metode CreateEncryptor / CreateDecryptor yang tidak diinginkan (tidak diautentikasi!).
  2. Tidak ada komponen yang terlibat yang dapat digunakan dengan kripto asimetris, ya? Hampir semua komponen menghilangkan "Simetris" dari namanya, sesuatu yang saya setujui. Kecuali kita terus mewarisi SymmetricAlgorithm , AuthenticatedSymmetricAlgorithm dapat diubah namanya menjadi AuthenticatedEncryptionAlgorithm , mengikuti istilah konvensional Enkripsi Terotentikasi.
  3. Ubah TryEncrypt / TryDecrypt untuk menulis ke / menerima tag, daripada memiliki properti Tag yang dapat disetel pada algoritme.
  4. Apa tujuan pengaturan key , iv , dan authenticatedAdditionalData melalui setter publik? Saya akan menghindari beberapa pendekatan yang valid, dan properti yang bisa berubah sebanyak mungkin. Bisakah Anda membuat proposal yang diperbarui tanpanya?
  5. Apakah kita menginginkan status di AesGcm sama sekali? Naluri saya adalah untuk menghindari iv dan authenticatedAdditionalData , karena ini adalah per-pesan. key mungkin bernilai sebagai status, karena kami biasanya ingin melakukan banyak operasi dengan satu kunci. Tetap saja, dimungkinkan untuk mengambil kunci berdasarkan per-panggilan juga. Pertanyaan yang sama berlaku untuk CreateAuthenticatorEncryptor . Bagaimanapun, kita harus memilih _one way_ untuk melewati parameter. Saya ingin mendiskusikan pro dan kontra. Saya condong ke keadaan kunci di AesGcm , dan sisanya di CreateAuthenticatedEncryptor atau TryEncrypt masing-masing. Jika kami sudah sepakat, tolong tunjukkan kami proposal yang diperbarui. :-)
  6. ICipherTransform mungkin harus berupa kelas abstrak, CipherTransform , sehingga metode dapat ditambahkan tanpa merusak implementasi yang ada.
  7. Semua parameter fungsi harus menggunakan camelCase, yaitu mulai dengan huruf kecil. Juga, haruskah kita mengatakan authenticatedData atau authenticatedAdditionalData ? Selain itu, saya pikir kita harus memilih nama parameter plaintext dan ciphertext .
  8. Di mana pun IV dilewatkan, saya ingin melihatnya sebagai parameter opsional, membuatnya lebih mudah untuk mendapatkan IV yang dihasilkan dengan benar (cryptorandom) daripada menyediakan milik kita sendiri. Membuat penyalahgunaan API tingkat rendah menjadi lebih sulit, setidaknya, dan kami mendapatkannya secara gratis.
  9. Saya masih mencoba mencari tahu bagaimana kode klien TryEncrypt dapat mengetahui panjang rentang yang diperlukan untuk menyediakan ciphertext ! Sama untuk TryDecrypt dan panjang plaintext . Tentunya kita tidak seharusnya mencobanya dalam satu lingkaran sampai berhasil, menggandakan panjangnya setelah setiap iterasi yang gagal?

Akhirnya, berpikir ke depan, seperti apa tampilan API tingkat tinggi yang dibangun di atas ini? Melihat murni penggunaan API, tampaknya hanya ada sedikit ruang untuk perbaikan, karena baik streaming maupun non-streaming API sudah sangat mudah! Perbedaan utama yang saya bayangkan adalah IV otomatis, ukuran output otomatis, dan mungkin batasan jumlah data yang dienkripsi.

Windows memungkinkan streaming. OpenSSL juga demikian. Keduanya sebagian besar mengelompokkannya ke dalam konsep yang ada (meskipun keduanya melemparkan kunci pas dengan "dan ada hal ini di sisi yang harus Anda tangani atau saya akan salah").

Go tidak, dan libsodium tidak.

Sepertinya gelombang pertama mengizinkannya, dan yang berikutnya tidak. Karena kita tidak dapat dibantah dalam gelombang selanjutnya, saya pikir kita akan tetap tidak mengizinkannya. Jika ada peningkatan permintaan untuk streaming setelah memperkenalkan model one-shot (untuk enkripsi / dekripsi, kunci dapat dipertahankan di seluruh panggilan), maka kami dapat mengevaluasi kembali. Jadi proposal API yang mengikuti pola itu tampaknya bermanfaat. Meskipun baik SIV maupun CCM tidak mendukung enkripsi streaming, jadi streaming API untuk mereka berpotensi sangat buffering. Menjaga hal-hal yang jelas tampaknya lebih baik.

Proposal juga tidak boleh menyematkan tag dalam muatan (GCM dan CCM menyebutnya sebagai datum terpisah), kecuali jika algoritma itu sendiri (SIV) memasukkannya ke dalam keluaran enkripsi. ( E(...) => (c, t) vs E(...) => c || t atau E(...) => t || c ). Pengguna API tentu dapat menggunakannya sebagai concat (cukup buka Span dengan tepat).

Spesifikasi GCM tidak mengizinkan pelepasan apa pun selain FAIL pada ketidakcocokan tag. NIST cukup jelas tentang itu. Makalah GCM asli oleh McGrew & Viega juga mengatakan:

Operasi dekripsi akan mengembalikan FAIL daripada plaintext, dan dekapsulasi akan berhenti dan plaintext akan dibuang daripada diteruskan atau diproses lebih lanjut.

Tidak ada komentar sebelumnya yang membahas RUP - mereka hanya melambaikan tangan ("lapisan yang lebih tinggi akan menanganinya" - ya, benar).

Sederhana saja: Enkripsi GCM dapat melakukan streaming. Dekripsi GCM tidak dapat streaming. Apa pun bukan lagi GCM.

Sepertinya gelombang pertama mengizinkannya, dan yang berikutnya tidak. Karena kita tidak dapat dibantah dalam gelombang selanjutnya, saya pikir kita akan tetap tidak mengizinkannya.

@bartonjs Anda benar-benar mengabaikan semua analisis teknis dan logis dan alih-alih menggunakan tanggal proyek Go dan libsodium sebagai proxy yang lemah untuk analisis sebenarnya? Bayangkan jika saya membuat argumen serupa berdasarkan nama proyek. Plus, kami memutuskan antarmuka DAN implementasinya. Anda menyadari bahwa memutuskan antarmuka non-streaming untuk AEAD menghalangi semua implementasi seperti itu, bukan?

Jika ada peningkatan permintaan untuk streaming setelah memperkenalkan model one-shot (untuk enkripsi / dekripsi, kunci dapat dipertahankan di seluruh panggilan), maka kami dapat mengevaluasi kembali.

Mengapa permintaan yang ditunjukkan sejauh ini di GitHub tidak mencukupi? Ini sampai pada titik di mana tampaknya sangat aneh untuk mendukung harus melakukan lebih sedikit pekerjaan daripada manfaat teknis atau permintaan pelanggan.

@bartonjs Anda benar-benar mengabaikan semua analisis teknis dan logis dan alih-alih menggunakan tanggal proyek Go dan libsodium sebagai proxy yang lemah untuk analisis sebenarnya?

Tidak, saya menggunakan saran dari kriptografer profesional yang mengatakan itu sangat berbahaya dan bahwa kita harus menghindari streaming AEAD. Kemudian saya menggunakan informasi dari tim CNG tentang "banyak orang mengatakan mereka menginginkannya secara teori, tetapi dalam praktiknya hampir tidak ada yang melakukannya" (saya tidak tahu berapa banyak telemetri vs anekdot dari permintaan bantuan tangkas). Fakta bahwa perpustakaan lain telah mengambil rute satu kali hanya _memperkuat_ keputusan.

Mengapa permintaan yang ditunjukkan sejauh ini di GitHub tidak mencukupi?

Beberapa skenario telah disebutkan. Memproses buffer yang terfragmentasi mungkin dapat diatasi dengan menerima ReadOnlySequence , jika sepertinya ada cukup skenario untuk menjamin memperumit API alih-alih meminta penelepon melakukan perakitan ulang data.

File besar adalah masalah, tetapi file besar sudah menjadi masalah karena GCM memiliki batas hanya 64GB, yang "tidak terlalu besar" (oke, ini cukup besar, tapi bukan "wah, itu besar" yang itu dulu). File yang dipetakan memori akan memungkinkan Spans (hingga 2^31-1) untuk digunakan tanpa memerlukan RAM 2GB. Jadi kami telah mencukur beberapa bit dari maksimum... itu mungkin akan terjadi dari waktu ke waktu.

Anda menyadari bahwa memutuskan antarmuka non-streaming untuk AEAD menghalangi semua implementasi seperti itu, bukan?

Saya semakin yakin bahwa @GrabYourPitchforks benar (https://github.com/dotnet/corefx/issues/23629#issuecomment-334638891) bahwa mungkin tidak ada antarmuka pemersatu yang masuk akal. GCM _memerlukan_ nonce/IV dan SIV _melarang_ artinya inisialisasi mode/algoritma AEAD sudah membutuhkan pengetahuan tentang apa yang akan terjadi... sebenarnya tidak ada gagasan "abstrak" untuk AEAD. SIV menentukan ke mana "tag" pergi. GCM/CCM tidak. SIV adalah tag-first, berdasarkan spesifikasi.

SIV tidak dapat mulai mengenkripsi sampai memiliki semua data. Jadi enkripsi streamingnya akan dibuang (yang berarti Anda harus tahu untuk tidak memanggilnya) atau buffer (yang dapat menghasilkan n^2 waktu operasi). CCM tidak dapat dimulai sampai panjangnya diketahui; tetapi CNG tidak mengizinkan petunjuk pra-enkripsi panjangnya, jadi itu ada di kapal yang sama.

Kita seharusnya tidak mendesain komponen baru di mana lebih mudah untuk melakukan hal yang salah daripada hal yang benar secara default. Dekripsi streaming membuatnya sangat mudah dan menggoda untuk memasang kelas Stream (ala proposal Anda untuk melakukannya dengan CryptoStream) yang membuatnya sangat mudah untuk mendapatkan bug validasi data sebelum tag diverifikasi, yang hampir seluruhnya meniadakan manfaat AE . ( IGcmDecryptor => CryptoStream => StreamReader => XmlReader => "tunggu, itu bukan XML legal..." => adaptif ciphertext oracle) .

Ini langsung ke intinya ... permintaan pelanggan.

Sayangnya, seperti yang sudah terlalu sering saya dengar dalam hidup saya: Maaf, tapi Anda bukan pelanggan yang kami maksud. Saya akui bahwa mungkin Anda tahu bagaimana melakukan GCM dengan aman. Anda tahu untuk hanya melakukan streaming ke file/buffer/etc yang mudah menguap sampai setelah verifikasi tag. Anda tahu apa artinya manajemen nonce, dan Anda tahu risiko melakukan kesalahan. Anda tahu untuk memperhatikan ukuran aliran dan memotong ke segmen GCM baru setelah 2^36-64 byte. Anda tahu bahwa setelah semuanya dikatakan dan dilakukan, itu adalah kesalahan Anda jika Anda melakukan kesalahan.

Pelanggan yang saya pikirkan, di sisi lain, adalah seseorang yang tahu "Saya harus mengenkripsi ini" karena bos mereka menyuruh mereka. Dan mereka tahu bahwa ketika mencari cara melakukan enkripsi, beberapa tutorial mengatakan "selalu gunakan AE" dan menyebutkan GCM. Kemudian mereka menemukan tutorial "enkripsi dalam .NET" yang menggunakan CryptoStream. Mereka kemudian menghubungkan pipa, tidak tahu bahwa mereka baru saja melakukan hal yang sama seperti memilih SSLv2... mencentang kotak dalam teori, tetapi tidak benar-benar dalam praktiknya. Dan ketika mereka melakukannya _itu_ bug milik semua orang yang tahu lebih baik, tetapi biarkan hal yang salah terlalu mudah dilakukan.

Anda bukan pelanggan yang kami pikirkan [...] Pelanggan yang saya pikirkan, di sisi lain, adalah seseorang yang tahu "Saya harus mengenkripsi ini" karena bos mereka menyuruh mereka [...]

@bartonjs beberapa bulan yang lalu kami telah memutuskan bahwa tujuannya adalah untuk menargetkan dua profil pelanggan dengan memiliki API tingkat rendah (kuat tetapi tidak aman dalam kondisi tertentu) dan API tingkat tinggi (sangat mudah). Itu bahkan dalam judul. Ini tentu saja negara bebas tetapi tidak jujur ​​​​untuk memindahkan tiang gawang dengan mengklaim sebaliknya.

Pelanggan yang saya pikirkan, di sisi lain, adalah seseorang yang tahu "Saya harus mengenkripsi ini" karena bos mereka menyuruh mereka. Dan mereka tahu bahwa ketika mencari cara melakukan enkripsi, beberapa tutorial mengatakan "selalu gunakan AE" dan menyebutkan GCM. Kemudian mereka menemukan tutorial "enkripsi dalam .NET" yang menggunakan CryptoStream. Mereka kemudian menghubungkan pipa, tidak tahu bahwa mereka baru saja melakukan hal yang sama seperti memilih SSLv2... mencentang kotak dalam teori, tetapi tidak benar-benar dalam praktiknya. Dan ketika mereka melakukannya, bug itu milik semua orang yang tahu lebih baik, tetapi biarkan hal yang salah menjadi terlalu mudah dilakukan.

@bartonjs Tunggu, apa yang terjadi dengan primitif tingkat rendah? Saya pikir tujuan dari masalah khusus ini adalah fleksibilitas dalam menjaga anak. Beri tahu kami jika rencananya telah berubah, sehingga kami semua membicarakan hal yang sama.

Juga, apakah metode per blok masih dalam pertimbangan, atau hanya metode sekali pakai?

Saya semakin yakin bahwa @GrabYourPitchforks benar (#23629 (komentar)) bahwa mungkin tidak ada antarmuka pemersatu yang masuk akal.

Melihat semua contoh, ini mulai terlihat semakin sia-sia - terutama untuk API tingkat rendah di mana implementasinya memiliki batasan yang berbeda. Mungkin kita harus memberikan contoh yang solid dengan AES-GCM, daripada antarmuka pemersatu. Sebagai catatan, yang terakhir mungkin masih menarik untuk API tingkat tinggi di masa mendatang. Sifatnya yang lebih membatasi mungkin akan membuat antarmuka pemersatu lebih mudah dicapai di sana.

apakah metode per blok masih dalam pertimbangan, atau hanya metode sekali pakai?

Seperti yang disebutkan di https://github.com/dotnet/corefx/issues/23629#issuecomment -378605071 , kami merasa bahwa risiko vs imbalan vs kasus penggunaan yang diungkapkan mengatakan bahwa kami seharusnya hanya mengizinkan versi satu kali AE.

Saya belum membaca seluruh diskusi, hanya bagian acak. Saya tidak tahu arah yang Anda tuju. Maaf jika apa yang saya tulis tidak masuk akal dalam konteks ini. 2 saya:

  • Aliran itu penting. Jika Anda tidak dapat mendukungnya secara langsung karena itu berarti kerentanan keamanan, maka jika memungkinkan berikan pembungkus tingkat tinggi yang dibangun di atas API tingkat rendah Anda yang akan mengekspos aliran (dengan cara yang tidak efisien tetapi aman).
  • Jika AES-GCM benar-benar tidak dapat menggunakan aliran dalam skenario apa pun, maka berikan implementasi resmi AES-CBC-HMAC yang didasarkan pada aliran. Atau beberapa algoritma AE lainnya.
  • Semakin tinggi levelnya semakin baik. Semakin sedikit area untuk membuat kesalahan oleh pengguna semakin baik. Artinya — ekspos API yang akan menyembunyikan sebanyak mungkin hal (misalnya tag autentikasi ini). Tentu saja, mungkin (harus) ada kelebihan beban yang lebih spesifik juga.
  • IMHO tidak repot dengan menyatukan antarmuka dengan layanan enkripsi lain jika mereka tidak cocok. Itulah yang telah dilakukan openssl dengan CLI mereka dan hasilnya buruk (misalnya tidak ada kemungkinan untuk memberikan tag otentikasi).

Kami (tim keamanan .NET) berunding di antara kami sendiri dan dengan tim crypto yang lebih luas di dalam Microsoft. Kami membahas banyak masalah dan masalah yang disebutkan di utas ini. Pada akhirnya, kekhawatiran ini tidak cukup persuasif untuk menjamin pengenalan streaming GCM API sebagai blok pembangun inti dalam kerangka kerja.

Keputusan ini dapat ditinjau kembali di masa mendatang jika diperlukan. Dan sementara itu, kami tidak membuat keadaan menjadi lebih buruk daripada sekarang: pengembang yang saat ini menggunakan perpustakaan kripto pihak ketiga untuk streaming dukungan GCM dapat terus melakukannya, dan mereka tidak akan terganggu oleh pengenalan yang kami maksudkan tentang a API GCM non-streaming.

Bagaimana cara menangani enkripsi data yang tidak sesuai dengan memori?

@pgolebiowski Anda menggunakan perpustakaan kripto .NET tingkat tinggi yang dirancang khusus untuk menawarkan enkripsi streaming yang aman.

@sdrapkin ini lebih mudah diucapkan daripada dilakukan. "aman" banyak ditanyakan. apa ada yang terbukti dan benar-benar bisa dipercaya? Anda mengatakan diri Anda sendiri:

Pustaka Bouncy Castle c# (rekomendasi khas StackOverflow). Bouncy Castle c# adalah katalog museum crypto yang besar (145k LOC), berkinerja buruk (beberapa di antaranya kuno), dengan implementasi Java lama yang di-porting ke .NET (2.0?).

ok, jadi apa saja pilihannya? mungkin perpustakaan Anda sendiri? tidak benar-benar . hmm... mungkin libsodium-net? tidak benar-benar .

ketika Anda benar-benar mencari perpustakaan yang diaudit yang berasal dari sumber yang agak tepercaya (seperti Microsoft atau banyak digunakan oleh komunitas), saya tidak berpikir perpustakaan seperti itu ada di dunia .NET Core.


  • pelanggan: enkripsi data yang diautentikasi yang tidak sesuai dengan memori?
  • microsoft: Maaf, tapi Anda bukan pelanggan yang kami maksud. gunakan perpustakaan yang tidak diaudit dan keamanannya dipertanyakan, bukan masalah kami jika Anda diserang saluran samping.
  • pelanggan:

@pgolebiowski Pilihannya adalah menggunakan kerangka .NET yang sudah mapan - mis. hal yang telah terbukti dan dapat dipercaya, seperti yang Anda inginkan. Pustaka lain (termasuk milik saya) menunggu Microsoft menambahkan primitif kripto yang hilang seperti ECDH ke dalam NetStandard.

Anda juga dapat melihat garpu Inferno. Setidaknya ada 2 garpu di mana beberapa perubahan sepele dicapai NetStandard20.

Perpustakaan Anda diaudit 2 tahun yang lalu dalam waktu 2 hari oleh satu orang . Saya tidak percaya itu, maaf. Di Microsoft, akan ada tim khusus untuk itu -- orang-orang yang saya percayai lebih tinggi daripada mereka dari Cure53.

Serius, kita dapat berbicara tentang dukungan pihak ketiga untuk banyak hal. Tetapi semua hal terkait keamanan yang dibutuhkan harus disediakan oleh perpustakaan standar.

@pgolebiowski Jauh dari saya untuk mencoba meyakinkan siapa pun untuk mempercayai apa pun, tetapi pernyataan Anda tidak akurat. Inferno diaudit oleh 2 profesional dari organisasi "Cure53". Audit memakan waktu 2 hari, dan seluruh perpustakaan adalah ~1.000 baris kode. Itu ~250 baris kode per auditor/hari - cukup mudah dikelola.

Faktanya, kemampuan audit yang mudah adalah salah satu fitur utama Inferno, tepatnya bagi mereka yang tidak ingin percaya.

Tujuan dari thread ini adalah untuk menambahkan dukungan untuk AES-GCM. Pustaka Anda bahkan tidak mendukung AES-GCM. Jika Anda ingin orang menggunakan kode Anda, kirimkan sebagai proposal untuk corefx. Dalam utas yang sesuai.

Hal lain, meskipun mendukung algoritme ini — belum ditinjau oleh .net crypto board dan bukan merupakan bagian dari corefx. Itu bahkan bukan kandidat untuk tinjauan semacam itu. Ini berarti akhir dari diskusi dan iklan yang sia-sia ini.

@pgolebiowski Saya tidak mengiklankan apa pun - hanya menjawab pertanyaan Anda, menyarankan alternatif untuk usecase Anda, dan mengoreksi klaim yang tidak akurat. AES-GCM tidak cocok untuk enkripsi streaming, dan itu adalah sesuatu yang telah ditinjau dan disetujui oleh tim keamanan .NET, sehingga Anda dapat memercayainya.

apakah saya membela streaming AES-GCM di mana saja? tidak bisa mengingat. tetapi dapat mengingat mengatakan:

  • Aliran itu penting. Jika Anda tidak dapat mendukungnya secara langsung karena itu berarti kerentanan keamanan, maka jika memungkinkan berikan pembungkus tingkat tinggi yang dibangun di atas API tingkat rendah Anda yang akan mengekspos aliran (dengan cara yang tidak efisien tetapi aman).
  • Jika AES-GCM benar-benar tidak dapat menggunakan aliran dalam skenario apa pun, maka berikan implementasi resmi AES-CBC-HMAC yang didasarkan pada aliran. Atau beberapa algoritma AE lainnya.

atau menyatakan masalah terbuka untuk corefx:

Bagaimana cara menangani enkripsi data yang tidak sesuai dengan memori?


bonus:

Anda juga dapat melihat garpu Inferno. Setidaknya ada 2 garpu di mana beberapa perubahan sepele dicapai NetStandard20. [...] Inferno diaudit oleh 2 profesional dari organisasi "Cure53" [...] auditabilitas yang mudah adalah salah satu fitur utama Inferno, tepatnya bagi mereka yang tidak ingin percaya.

Saya tidak mengiklankan apa pun

Selain itu, saya tidak pernah benar-benar menginginkan "streaming" dalam arti kebanyakan orang berpikir. Saya hanya ingin "memblokir" pemrosesan untuk perf dan alasan penggunaan memori. Saya sebenarnya tidak ingin "mengalirkan" hasilnya. Inilah yang tampaknya memalukan untuk kehilangan dukungan openssl dan cng dan pada dasarnya membuat "primatif" tidak berguna dalam skenario apa pun yang dapat saya pikirkan.

@Drawaes Kalau dipikir-pikir, operasi blok mungkin jauh lebih aman daripada menggunakan aliran. Orang awam mungkin menyentuh aliran, tetapi dia lebih suka menggunakan API sekali pakai daripada operasi blok. Selain itu, operasi blok tidak dapat _straightforwardly_ digabungkan dengan, katakanlah, XmlReader . Jadi sebenarnya, banyak bahaya yang dibahas berlaku untuk objek aliran, tetapi tidak untuk memblokir operasi.

Untuk bekerja dengan operasi blok saat API satu-shot juga tersedia, menyarankan agar kita mengetahui apa yang kita lakukan, dan bahwa kita secara khusus memerlukan penyesuaian tingkat rendah. Kita bisa melindungi orang awam _dan_ memiliki fleksibilitas.

Untuk menghindari RUP, saya masih memikirkan seberapa besar keuntungan operasi blok sebenarnya untuk GCM. Enkripsi menuai manfaat penuh, sedangkan dekripsi hanya menguntungkan sedikit. Kita dapat menghindari penyimpanan ciphertext penuh, tetapi kita harus tetap buffer plaintext penuh. Sebuah decryptor _could_ memilih untuk menyimpan plaintext perantara pada disk. Tetapi sebagai imbalannya, kami telah memperkenalkan lebih banyak ruang untuk kesalahan. Apakah kita memiliki argumen yang meyakinkan untuk tidak menyelesaikan masalah ini pada tingkat yang lebih tinggi (misalnya, potongan di sana, atau menggunakan algoritma streaming yang sebenarnya)?

TLS dan pipa. Saat ini (dan untuk masa mendatang) pipeline menggunakan blok 4k tetapi pesan tls dapat berupa 16k teks sandi. Dengan satu kesempatan, Anda perlu menyalin 16k ke buffer kontinu tunggal sebelum Anda dapat mendekripsi. Dengan blok Anda mungkin mengatakan 4 atau 5 dan Anda mungkin perlu buffer hingga 16 byte untuk memastikan blok bersaing.

@Drawae 16k masih konstan dan tidak besar. Apakah itu membuat banyak perbedaan dalam konteks ini?

Ya, itu berarti salinan lain dalam pipa. Ini memiliki efek besar dan terukur pada kinerja.

Apa yang dibutuhkan untuk membuat ini terjadi? Apa langkah selanjutnya? @Drawes

Adapun AES-GCM, saya pikir pengirimannya terganggu karena masalah terkait dikunci: https://github.com/dotnet/corefx/issues/7023. @blowdart , bisakah Anda membuka kunci? Sangat sulit untuk memiliki kemajuan ketika orang tidak bisa berdiskusi. Atau, jika itu bukan pilihan, mungkin usulkan solusi alternatif yang memungkinkan menghadirkan fitur ini ke publik.

Tidak, saya tidak membuka kunci itu. Keputusan telah dibuat, topik selesai.

@blowdart Terima kasih atas jawabannya. Saya mengerti bahwa mungkin ini tidak cukup jelas:

Atau, jika itu bukan pilihan, mungkin usulkan solusi alternatif yang memungkinkan menghadirkan fitur ini ke publik.

Saya menghargai bahwa ada keputusan untuk mendukung AES-GCM. Ini bagus, saya pasti menginginkan algoritma itu. Jadi, sekarang akan keren untuk benar-benar mendukungnya. Apakah Anda ingin diskusi tentang desain dan implementasi AES-GCM diadakan di sini atau di edisi baru?

Juga, jika topik itu sudah selesai, mengapa tidak menutupnya? Dan buat lebih eksplisit dengan mengubah judul masalah itu, karena sekarang ini menunjukkan bahwa diskusi tentang implementasi akan diadakan di sini: https://github.com/dotnet/corefx/issues/7023. Mungkin sesuatu seperti Putuskan algoritma AEAD mana yang harus didukung terlebih dahulu .

Dengan kata lain: Saya memberikan umpan balik bahwa dalam situasi saat ini tidak jelas apa yang dibutuhkan untuk mendorong AES-GCM maju.

@karelz

@pgolebiowski Sudah ada PR yang keluar. Mungkin akan tersedia di master Wednesday minggu depan.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat