抽象原語和操作模式
我正在開發一個對稱加密庫並遇到了障礙。查看分組密碼,很明顯所有分組密碼都可以簡單地抽象為一個簡單的原語,包括:
- 一個關鍵的時間表
- 一個置換函式(將一個鍵作為輸入,可能是一個“調整”和一個數據輸入)
這使得所有分組密碼都易於在任何操作模式下使用,並且可以輕鬆地“交換”各種算法以支持其他算法,而無需重寫大量程式碼 - 只有上述組件在算法之間有所不同。
我無法使用散列函式觀察相同級別的抽象。它們由它們的壓縮函式(直截了當)來描述,但似乎也有一個內置的操作模式,雖然經常在各種散列函式之間共享,但並不意味著要改變,例如,沒有人將 MD5 壓縮函式與 Davies 一起使用-Meyer 結構,它總是與 Merkle-Damgård 一起使用,因為這就是 MD5 的整體。
而且這些操作模式並不完全相同,例如 Merkle-Damgård 在消息末尾應用一些簡單的填充來散列,然後將消息分成塊並像這樣處理它,而 UBI 構造使用額外的“配置”輸入”在其壓縮功能中,這需要以完全不同的方式處理消息。
所以我的問題是:有沒有一種方法可以像使用分組密碼一樣優雅地在特定框架中很好地抽象散列函式,而無需以不同的方式專門編寫每個散列函式,以實現最佳的程式碼重用?
我能想到的最佳折衷方案是根據它們使用的操作模式(例如 MD5、SHA1、SHA2、RIPEMD 等)對不同的散列函式進行分組分類在 UBI 類別中,等等),這將使與消息填充和處理相關的程式碼在必要時被重用,但也會略微增加程式碼複雜性。
這也是 HMAC 結構的一個問題。有一個完全抽象的 HMAC 結構,它適用於任何散列函式,而不管其內部結構如何,但是較新的散列函式開始提供自己特定的 HMAC 設計(例如,Skein 及其 HMAC 配置塊),這些設計比“標準”更有效“ 方法。
在沒有根本理由存在的地方嘗試找到抽象並不是一個好主意。在建構加密或散列方法的方式中存在一些“意外”結構並不能說明未來的加密或散列函式。
此外,您可能需要針對側通道攻擊等應用特定保護措施。如果您精心建構的軟體設計妨礙了創建更安全的庫,那將是一種恥辱。
請注意,如果沒有特定抽象的根本原因,創建它們可能沒有任何優勢。例如,您不會看到關於程式碼重複的太多優勢。
許多算法都有特定的命名約定和參考實現。您不應該將這些硬塞到您的設計中。
我已經成功地做出了你想要的抽象。您不應該抽象內部實現細節,而是抽象介面。內部實現可以通過多種方式恢復,但不是抽象的重點。我已經包含了一些摘錄,展示了它是如何工作的。
這是顯示類關係的標頭檔的(稍作編輯)部分。我刪除了不重要的細節,包括模板。
struct Binary; // This is an array of bytes and inherits from a "string". struct Digest; struct Digestor; struct BlockDigestor; struct Md2Digestor; struct Md4Digestor; struct Md5Digestor; struct RipeMd128Digestor; struct RipeMd160Digestor; struct RipeMd256Digestor; struct RipeMd320Digestor; struct RipeMdDigestor; struct Sha160Digestor; struct Sha256Digestor; struct Sha512Digestor; struct Encryptor; struct Angerona; struct BlockEncryptor; struct Des; struct Rijndael; struct Tdea;
這是摘要標題的一部分
struct Digest : Binary { public: Digest(); Digest(Digest const & digest); ~Digest(); void operator=(Digest const & digest); void consume(unsigned4 n); ///< Consume the first n bytes of the encryption buffer. using Binary::erase; using Binary::extent; using Binary::length; using Binary::size; virtual void erase(); ///< Erase transformation data. virtual unsigned4 extent() const; ///< Return the length of the transformed data. virtual unsigned4 length() const; ///< Return the length of the transformed data. virtual unsigned4 size() const; ///< Return the length of the transf ormed data. virtual void swap(Digest & digest); ///< Swap contents with another digest. };
對於 Digestor(帶有註釋,因為它是關鍵的抽象)……
/** * This is an abstract base class for all dynamic classes which * "digest" a buffer resulting in a "digest". The general framework * is modeled as follows... * * 1. Segmentation * * The input is divided into a number of blocks of equal * length and the last, generally incomplete, block is * padded in a unique and reversible way. * * 2. Initialization * * The initial chaining state is set equal to a value fixed * by the specification. * * 3. Iteration * * The chaining state is updated sequentially by a chaining * transformation for all blocks. * * 4. Result * * The digest is obtained from the final chaining state by * the output transformation. * * This model is realized by the following protocol. * * 1. Immediately following construction or a call to digest() the * internal state is such that calls to digest(buffer,n) will * perform (2). * * 2. Zero or more calls to digest(buffer,n) will perform (1) and * (3). An internal buffer will contain any undigested input * which is the result of partial segmentation. * * 3. Calls to digest() will treat any undigested input as an * incomplete final block and pad that block appropriately. It * will then perform (4) so that the digestor is a final digest. * Referencing the digest is an error before digest() has been * called. * * 4. Calls to digest(buffer) will effectively call digest(buffer,n) * and then digest(). */ struct Digestor : Digest { public: virtual Digestor const & digest() = 0; ///< Finalize digest. virtual Digestor const & digest(void const * buffer, unsigned4 n) = 0; ///< Digest raw buffer. template <class Data> Digestor const & digest(Data const & buffer); ///< Digest data buffer. Digestor const & operator()(); Digestor const & operator()(void const * buffer, unsigned4 n); template <class Data> Digestor const & operator()(Data const & buffer); ///< Digest data buffer. virtual unsigned4 blockSize() const = 0; ///< Return length of block. virtual Byte const * digestData() const = 0; ///< Return pointer to digest. virtual unsigned4 digestSize() const = 0; ///< Return length of digest. void hmac( ///< Generate keyed Hash Message Authentication Code (HMAC). void const * key, unsigned4 keyLength, void const * message, unsigned4 messageLength); static Digestor * digestor(HashDigestor hash); ///< Return a new digestor. };
BlockDigestor 實現了基於塊的算法所需的介面。
/** * A BlockDigestor is a Digestor which digests its input in blocks. * Each block is N bytes long. The final block will contain a length * field which is M bytes long and contains the number of input blocks. * M is either 8 or 16. The length of the final digest is L bytes. */ template <unsigned4 N, unsigned4 M, unsigned4 L, bool littleEndian> struct BlockDigestor : Digestor { public: template <class Data> Digestor const & digest(Data const * string); ///< Digest string. template <class Data> Digestor const & digest(Data const & buffer); ///< Digest data buffer. Digestor const & digest(); Digestor const & digest(void const * buffer, unsigned4 n); protected: virtual void digestBlock(void const * buffer) = 0; ///< Digest block. virtual void finalize(); ///< Finalize digest. virtual void initialize() = 0; ///< Initialize digest. unsigned8 digested$; Byte buffer$[N]; }; }
並且(最後)這裡是 SHA 256 的標題,它顯示了所有內容是如何組合在一起的。
/** * An Sha256Digestor instance is a digest which is the cryptographically * secure, one-way hash of a buffer based on the SHA-2 federal standard * (FIPS 180-2 issued by NIST). * * Note: If this class is instantiated where N has any value other than * 224 or 256 then a link error will result. This is intentional. */ template <unsigned4 N> struct Sha256Digestor : BlockDigestor<64,8,N/8,false> { public: Sha256Digestor(); Sha256Digestor(Sha256Digestor<N> const & digest); void operator=(Sha256Digestor<N> const & digest); protected: void digestBlock(void const * buffer); ///< Digest block. void finalize(); ///< Finalize digest. void initialize(); ///< Initialize digest. enum { wordWidth$ = 4, ///< Number of bytes in each word - 4 (SHA-1, SHA-256) or 8 (SHA-384, SHA-512). blockSize$ = 64, ///< Number of bytes in block. lengthOffset$ = 56, ///< Offset in block to length field. digestSize$ = N / 8, ///< Size of digest in bytes. digestPad$ = (256 - N) / 8 ///< Length of additional internal state. }; union { unsigned1 digest1$[32]; ///< digestSize$ + digestPad$ unsigned4 digest4$[32/4]; }; private: static unsigned4 mixer0(unsigned4 x); static unsigned4 mixer1(unsigned4 x); static unsigned4 mixer2(unsigned4 x); static unsigned4 mixer3(unsigned4 x); static void roundA(unsigned4 * w, unsigned4 & h0, unsigned4 & h1, unsigned4 & h2, unsigned4 & h3, unsigned4 & h4, unsigned4 & h5, unsigned4 & h6, unsigned4 & h7, unsigned i, unsigned4 const * b); static void roundB(unsigned4 * w, unsigned4 & h0, unsigned4 & h1, unsigned4 & h2, unsigned4 & h3, unsigned4 & h4, unsigned4 & h5, unsigned4 & h6, unsigned4 & h7, unsigned i); }; namespace ShaConstants { extern unsigned4 sha32Constants[64]; } typedef DigestorFinal<Sha256Digestor<224> > Sha224; typedef DigestorFinal<Sha256Digestor<256> > Sha256;