diff --git a/Readme.txt b/Readme.txt index b98d955a3..8ba3b8299 100644 --- a/Readme.txt +++ b/Readme.txt @@ -14,9 +14,9 @@ Currently the library contains the following algorithms: AES and AES candidates AES (Rijndael), RC6, MARS, Twofish, Serpent, CAST-256 - ARIA, IDEA, Triple-DES (DES-EDE2 and DES-EDE3), - other block ciphers Camellia, SEED, Kalyna, RC5, Blowfish, TEA, XTEA, - Threefish, Skipjack, SHACAL-2 + ARIA, IDEA, Blowfish, Triple-DES (DES-EDE2 and + other block ciphers DES-EDE3), Camellia, SEED, Kalyna, RC5, SPECK-64, + SPECK-128, Threefish, Skipjack, SHACAL-2, TEA, XTEA block cipher modes of operation ECB, CBC, CBC ciphertext stealing (CTS), CFB, OFB, counter mode (CTR) diff --git a/speck.cpp b/speck.cpp index ab19269a3..c82042996 100644 --- a/speck.cpp +++ b/speck.cpp @@ -18,108 +18,138 @@ using CryptoPP::rotrFixed; using CryptoPP::rotlVariable; using CryptoPP::rotrVariable; +//! \brief Forward round transformation +//! \tparam W word type template inline void TF83(W& x, W& y, const W& k) { - x = rotrFixed(x, 8); - x += y; x ^= k; - y = rotlFixed(y, 3); - y ^= x; + x = rotrFixed(x, 8); + x += y; x ^= k; + y = rotlFixed(y, 3); + y ^= x; } +//! \brief Reverse round transformation +//! \tparam W word type template inline void TR83(W& x, W& y, const W& k) { - y^=x; - y=rotrFixed(y,3); - x^=k; - x-=y; - x=rotlFixed(x,8); + y^=x; + y=rotrFixed(y,3); + x^=k; x-=y; + x=rotlFixed(x,8); } -// W is word type -// R is number of rounds -// p = plain text, k = key, c = cipher text +//! \brief Forward transformation +//! \tparam W word type +//! \tparam R number of rounds +//! \param c output array +//! \param p input array +//! \param k subkey array template inline void SPECK_Encrypt(W c[2], const W p[2], const W k[R]) { - c[0]=p[0]; c[1]=p[1]; + c[0]=p[0]; c[1]=p[1]; - // Don't unroll this loop. Things slow down. - for(W i=0; static_cast(i)(i) inline void SPECK_Decrypt(W p[2], const W c[2], const W k[R]) { - p[0]=c[0]; p[1]=c[1]; + p[0]=c[0]; p[1]=c[1]; - // Don't unroll this loop. Things slow down. - for(W i=R-1; static_cast(i)>=0; --i) - TR83(p[0], p[1], k[i]); + // Don't unroll this loop. Things slow down. + for(W i=R-1; static_cast(i)>=0; --i) + TR83(p[0], p[1], k[i]); } +//! \brief Subkey generation function +//! \details Used when the user key consists of 2 words +//! \tparam W word type +//! \tparam R number of rounds +//! \param key empty subkey array +//! \param k user key array template inline void SPECK_RoundKeys_2W(W key[R], const W k[2]) { - CRYPTOPP_ASSERT(R==32); - W i=0, B=k[1], A=k[0]; - - while(i inline void SPECK_RoundKeys_3W(W key[R], const W k[3]) { - CRYPTOPP_ASSERT(R==33 || R==26); - W i=0, C=k[2], B=k[1], A=k[0]; - - unsigned int blocks = R/2; - while(blocks--) - { - key[i+0]=A; TF83(B, A, i+0); - key[i+1]=A; TF83(C, A, i+1); - i+=2; - } - - // The constexpr residue should allow the optimizer to remove unneeded statements - if(R%2 == 1) - { - key[R-1]=A; - } + CRYPTOPP_ASSERT(R==33 || R==26); + W i=0, C=k[2], B=k[1], A=k[0]; + + unsigned int blocks = R/2; + while(blocks--) + { + key[i+0]=A; TF83(B, A, i+0); + key[i+1]=A; TF83(C, A, i+1); + i+=2; + } + + // The constexpr residue should allow the optimizer to remove unneeded statements + if(R%2 == 1) + { + key[R-1]=A; + } } +//! \brief Subkey generation function +//! \details Used when the user key consists of 4 words +//! \tparam W word type +//! \tparam R number of rounds +//! \param key empty subkey array +//! \param k user key array template inline void SPECK_RoundKeys_4W(W key[R], const W k[4]) { - CRYPTOPP_ASSERT(R==34 || R==27); - W i=0, D=k[3], C=k[2], B=k[1], A=k[0]; - - unsigned int blocks = R/3; - while(blocks--) - { - key[i+0]=A; TF83(B, A, i+0); - key[i+1]=A; TF83(C, A, i+1); - key[i+2]=A; TF83(D, A, i+2); - i+=3; - } - - // The constexpr residue should allow the optimizer to remove unneeded statements - if(R%3 == 1) - { - key[R-1]=A; - } - else if(R%3 == 2) - { - key[R-2]=A; TF83(B, A, W(R-2)); - key[R-1]=A; - } + CRYPTOPP_ASSERT(R==34 || R==27); + W i=0, D=k[3], C=k[2], B=k[1], A=k[0]; + + unsigned int blocks = R/3; + while(blocks--) + { + key[i+0]=A; TF83(B, A, i+0); + key[i+1]=A; TF83(C, A, i+1); + key[i+2]=A; TF83(D, A, i+2); + i+=3; + } + + // The constexpr residue should allow the optimizer to remove unneeded statements + if(R%3 == 1) + { + key[R-1]=A; + } + else if(R%3 == 2) + { + key[R-2]=A; TF83(B, A, W(R-2)); + key[R-1]=A; + } } ANONYMOUS_NAMESPACE_END @@ -128,169 +158,169 @@ NAMESPACE_BEGIN(CryptoPP) void SPECK64::Base::UncheckedSetKey(const byte *userKey, unsigned int keyLength, const NameValuePairs ¶ms) { - CRYPTOPP_ASSERT(keyLength == 12 || keyLength == 16); - - // Building the key schedule table requires {3,4} words workspace. - // Encrypting and decrypting requires 4 words workspace. - m_kwords = keyLength/sizeof(word32); - m_wspace.New(STDMAX(m_kwords,4U)); - - // Avoid GetUserKey. SPECK does unusual things with key string and word ordering - // {A,B} -> {B,A}, {A,B,C} -> {C,B,A}, etc. - typedef GetBlock InBlock; - InBlock iblk(userKey); - - switch (m_kwords) - { - case 3: - m_rkey.New(26); - iblk(m_wspace[2])(m_wspace[1])(m_wspace[0]); - SPECK_RoundKeys_3W(m_rkey, m_wspace); - break; - case 4: - m_rkey.New(27); - iblk(m_wspace[3])(m_wspace[2])(m_wspace[1])(m_wspace[0]); - SPECK_RoundKeys_4W(m_rkey, m_wspace); - break; - default: - CRYPTOPP_ASSERT(0);; - } + CRYPTOPP_ASSERT(keyLength == 12 || keyLength == 16); + + // Building the key schedule table requires {3,4} words workspace. + // Encrypting and decrypting requires 4 words workspace. + m_kwords = keyLength/sizeof(word32); + m_wspace.New(STDMAX(m_kwords,4U)); + + // Avoid GetUserKey. SPECK does unusual things with key string and word ordering + // {A,B} -> {B,A}, {A,B,C} -> {C,B,A}, etc. + typedef GetBlock InBlock; + InBlock iblk(userKey); + + switch (m_kwords) + { + case 3: + m_rkey.New(26); + iblk(m_wspace[2])(m_wspace[1])(m_wspace[0]); + SPECK_RoundKeys_3W(m_rkey, m_wspace); + break; + case 4: + m_rkey.New(27); + iblk(m_wspace[3])(m_wspace[2])(m_wspace[1])(m_wspace[0]); + SPECK_RoundKeys_4W(m_rkey, m_wspace); + break; + default: + CRYPTOPP_ASSERT(0);; + } } void SPECK64::Enc::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const { // Reverse bytes on LittleEndian; align pointer on BigEndian - typedef GetBlock InBlock; - InBlock iblk(inBlock); iblk(m_wspace[0])(m_wspace[1]); - - switch (m_kwords) - { - case 3: - SPECK_Encrypt(m_wspace+2, m_wspace+0, m_rkey); - break; - case 4: - SPECK_Encrypt(m_wspace+2, m_wspace+0, m_rkey); - break; - default: - CRYPTOPP_ASSERT(0);; - } - - // Reverse bytes on LittleEndian; align pointer on BigEndian + typedef GetBlock InBlock; + InBlock iblk(inBlock); iblk(m_wspace[0])(m_wspace[1]); + + switch (m_kwords) + { + case 3: + SPECK_Encrypt(m_wspace+2, m_wspace+0, m_rkey); + break; + case 4: + SPECK_Encrypt(m_wspace+2, m_wspace+0, m_rkey); + break; + default: + CRYPTOPP_ASSERT(0);; + } + + // Reverse bytes on LittleEndian; align pointer on BigEndian typedef PutBlock OutBlock; - OutBlock oblk(xorBlock, outBlock); oblk(m_wspace[2])(m_wspace[3]); + OutBlock oblk(xorBlock, outBlock); oblk(m_wspace[2])(m_wspace[3]); } void SPECK64::Dec::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const { // Reverse bytes on LittleEndian; align pointer on BigEndian - typedef GetBlock InBlock; - InBlock iblk(inBlock); iblk(m_wspace[0])(m_wspace[1]); - - switch (m_kwords) - { - case 3: - SPECK_Decrypt(m_wspace+2, m_wspace+0, m_rkey); - break; - case 4: - SPECK_Decrypt(m_wspace+2, m_wspace+0, m_rkey); - break; - default: - CRYPTOPP_ASSERT(0);; - } - - // Reverse bytes on LittleEndian; align pointer on BigEndian + typedef GetBlock InBlock; + InBlock iblk(inBlock); iblk(m_wspace[0])(m_wspace[1]); + + switch (m_kwords) + { + case 3: + SPECK_Decrypt(m_wspace+2, m_wspace+0, m_rkey); + break; + case 4: + SPECK_Decrypt(m_wspace+2, m_wspace+0, m_rkey); + break; + default: + CRYPTOPP_ASSERT(0);; + } + + // Reverse bytes on LittleEndian; align pointer on BigEndian typedef PutBlock OutBlock; - OutBlock oblk(xorBlock, outBlock); oblk(m_wspace[2])(m_wspace[3]); + OutBlock oblk(xorBlock, outBlock); oblk(m_wspace[2])(m_wspace[3]); } /////////////////////////////////////////////////////////// void SPECK128::Base::UncheckedSetKey(const byte *userKey, unsigned int keyLength, const NameValuePairs ¶ms) { - CRYPTOPP_ASSERT(keyLength == 16 || keyLength == 24 || keyLength == 32); - - // Building the key schedule table requires {2,3,4} words workspace. - // Encrypting and decrypting requires 4 words workspace. - m_kwords = keyLength/sizeof(word64); - m_wspace.New(STDMAX(m_kwords,4U)); - - // Avoid GetUserKey. SPECK does unusual things with key string and word ordering - // {A,B} -> {B,A}, {A,B,C} -> {C,B,A}, etc. - typedef GetBlock InBlock; - InBlock iblk(userKey); - - switch (m_kwords) - { - case 2: - m_rkey.New(32); - iblk(m_wspace[1])(m_wspace[0]); - SPECK_RoundKeys_2W(m_rkey, m_wspace); - break; - case 3: - m_rkey.New(33); - iblk(m_wspace[2])(m_wspace[1])(m_wspace[0]); - SPECK_RoundKeys_3W(m_rkey, m_wspace); - break; - case 4: - m_rkey.New(34); - iblk(m_wspace[3])(m_wspace[2])(m_wspace[1])(m_wspace[0]); - SPECK_RoundKeys_4W(m_rkey, m_wspace); - break; - default: - CRYPTOPP_ASSERT(0);; - } + CRYPTOPP_ASSERT(keyLength == 16 || keyLength == 24 || keyLength == 32); + + // Building the key schedule table requires {2,3,4} words workspace. + // Encrypting and decrypting requires 4 words workspace. + m_kwords = keyLength/sizeof(word64); + m_wspace.New(STDMAX(m_kwords,4U)); + + // Avoid GetUserKey. SPECK does unusual things with key string and word ordering + // {A,B} -> {B,A}, {A,B,C} -> {C,B,A}, etc. + typedef GetBlock InBlock; + InBlock iblk(userKey); + + switch (m_kwords) + { + case 2: + m_rkey.New(32); + iblk(m_wspace[1])(m_wspace[0]); + SPECK_RoundKeys_2W(m_rkey, m_wspace); + break; + case 3: + m_rkey.New(33); + iblk(m_wspace[2])(m_wspace[1])(m_wspace[0]); + SPECK_RoundKeys_3W(m_rkey, m_wspace); + break; + case 4: + m_rkey.New(34); + iblk(m_wspace[3])(m_wspace[2])(m_wspace[1])(m_wspace[0]); + SPECK_RoundKeys_4W(m_rkey, m_wspace); + break; + default: + CRYPTOPP_ASSERT(0);; + } } void SPECK128::Enc::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const { // Reverse bytes on LittleEndian; align pointer on BigEndian - typedef GetBlock InBlock; - InBlock iblk(inBlock); iblk(m_wspace[0])(m_wspace[1]); - - switch (m_kwords) - { - case 2: - SPECK_Encrypt(m_wspace+2, m_wspace+0, m_rkey); - break; - case 3: - SPECK_Encrypt(m_wspace+2, m_wspace+0, m_rkey); - break; - case 4: - SPECK_Encrypt(m_wspace+2, m_wspace+0, m_rkey); - break; - default: - CRYPTOPP_ASSERT(0);; - } - - // Reverse bytes on LittleEndian; align pointer on BigEndian + typedef GetBlock InBlock; + InBlock iblk(inBlock); iblk(m_wspace[0])(m_wspace[1]); + + switch (m_kwords) + { + case 2: + SPECK_Encrypt(m_wspace+2, m_wspace+0, m_rkey); + break; + case 3: + SPECK_Encrypt(m_wspace+2, m_wspace+0, m_rkey); + break; + case 4: + SPECK_Encrypt(m_wspace+2, m_wspace+0, m_rkey); + break; + default: + CRYPTOPP_ASSERT(0);; + } + + // Reverse bytes on LittleEndian; align pointer on BigEndian typedef PutBlock OutBlock; - OutBlock oblk(xorBlock, outBlock); oblk(m_wspace[2])(m_wspace[3]); + OutBlock oblk(xorBlock, outBlock); oblk(m_wspace[2])(m_wspace[3]); } void SPECK128::Dec::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const { // Reverse bytes on LittleEndian; align pointer on BigEndian - typedef GetBlock InBlock; - InBlock iblk(inBlock); iblk(m_wspace[0])(m_wspace[1]); - - switch (m_kwords) - { - case 2: - SPECK_Decrypt(m_wspace+2, m_wspace+0, m_rkey); - break; - case 3: - SPECK_Decrypt(m_wspace+2, m_wspace+0, m_rkey); - break; - case 4: - SPECK_Decrypt(m_wspace+2, m_wspace+0, m_rkey); - break; - default: - CRYPTOPP_ASSERT(0);; - } - - // Reverse bytes on LittleEndian; align pointer on BigEndian + typedef GetBlock InBlock; + InBlock iblk(inBlock); iblk(m_wspace[0])(m_wspace[1]); + + switch (m_kwords) + { + case 2: + SPECK_Decrypt(m_wspace+2, m_wspace+0, m_rkey); + break; + case 3: + SPECK_Decrypt(m_wspace+2, m_wspace+0, m_rkey); + break; + case 4: + SPECK_Decrypt(m_wspace+2, m_wspace+0, m_rkey); + break; + default: + CRYPTOPP_ASSERT(0);; + } + + // Reverse bytes on LittleEndian; align pointer on BigEndian typedef PutBlock OutBlock; - OutBlock oblk(xorBlock, outBlock); oblk(m_wspace[2])(m_wspace[3]); + OutBlock oblk(xorBlock, outBlock); oblk(m_wspace[2])(m_wspace[3]); } NAMESPACE_END diff --git a/speck.h b/speck.h index 81917ed5d..b40926893 100644 --- a/speck.h +++ b/speck.h @@ -21,6 +21,9 @@ NAMESPACE_BEGIN(CryptoPP) //! \class SPECK_Info //! \brief SPECK block cipher information //! \tparam BS block size of the cipher, in bytes +//! \tparam D default key length, in bytes +//! \tparam N minimum key length, in bytes +//! \tparam M maximum key length, in bytes //! \since Crypto++ 6.0 template struct SPECK_Info : public FixedBlockSize, VariableKeyLength @@ -35,23 +38,25 @@ struct SPECK_Info : public FixedBlockSize, VariableKeyLength //! \class SPECK_Base //! \brief SPECK block cipher base class //! \tparam BS block size of the cipher, in bytes -//! \details User code should use SPECK128, SPECK512, SPECK1024 -//! \sa SPECK32, SPECK48, SPECK64, SPECK96, SPECK128, SPECK +//! \details User code should use SPECK64 or SPECK128 +//! \sa SPECK64, SPECK128, SPECK //! \since Crypto++ 6.0 template struct SPECK_Base { + virtual ~SPECK_Base() {} + typedef SecBlock > AlignedSecBlock; mutable AlignedSecBlock m_wspace; // workspace AlignedSecBlock m_rkey; // round keys - unsigned int m_kwords; // number of key words + unsigned int m_kwords; // number of key words }; //! \class SPECK64 //! \brief SPECK 64-bit block cipher //! \details SPECK64 provides 64-bit block size. The valid key sizes are 98-bit and 128-bit. //! \note Crypto++ provides a byte oriented implementation -//! \sa SPECK32, SPECK64, and SPECK128, SPECK +//! \sa SPECK64, SPECK128, SPECK //! \since Crypto++ 6.0 class CRYPTOPP_NO_VTABLE SPECK64 : public SPECK_Info<8, 12, 12, 16>, public BlockCipherDocumentation { @@ -61,26 +66,34 @@ class CRYPTOPP_NO_VTABLE SPECK64 : public SPECK_Info<8, 12, 12, 16>, public Bloc //! \since Crypto++ 6.0 class CRYPTOPP_NO_VTABLE Base : protected SPECK_Base, public BlockCipherImpl > { - public: - std::string AlgorithmName() const { - return StaticAlgorithmName() + "(" + IntToString(m_kwords*sizeof(word32)*8) + ")"; - } + public: + std::string AlgorithmName() const { + return StaticAlgorithmName() + "(" + IntToString(m_kwords*sizeof(word32)*8) + ")"; + } protected: void UncheckedSetKey(const byte *userKey, unsigned int keyLength, const NameValuePairs ¶ms); }; - class CRYPTOPP_NO_VTABLE Enc : public Base - { - protected: - void ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const; - }; + //! \brief Provides implementation for encryption transformation + //! \details Enc provides implementation for encryption transformation. All key + //! sizes are supported. + //! \since Crypto++ 6.0 + class CRYPTOPP_NO_VTABLE Enc : public Base + { + protected: + void ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const; + }; - class CRYPTOPP_NO_VTABLE Dec : public Base - { - protected: - void ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const; - }; + //! \brief Provides implementation for encryption transformation + //! \details Dec provides implementation for decryption transformation. All key + //! sizes are supported. + //! \since Crypto++ 6.0 + class CRYPTOPP_NO_VTABLE Dec : public Base + { + protected: + void ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const; + }; typedef BlockCipherFinal Encryption; typedef BlockCipherFinal Decryption; @@ -90,7 +103,7 @@ class CRYPTOPP_NO_VTABLE SPECK64 : public SPECK_Info<8, 12, 12, 16>, public Bloc //! \brief SPECK 128-bit block cipher //! \details SPECK128 provides 128-bit block size. The valid key sizes are 128-bit, 192-bit and 256-bit. //! \note Crypto++ provides a byte oriented implementation -//! \sa SPECK32, SPECK64, and SPECK128, SPECK +//! \sa SPECK64, SPECK128, SPECK //! \since Crypto++ 6.0 class CRYPTOPP_NO_VTABLE SPECK128 : public SPECK_Info<16, 16, 16, 32>, public BlockCipherDocumentation { @@ -100,26 +113,34 @@ class CRYPTOPP_NO_VTABLE SPECK128 : public SPECK_Info<16, 16, 16, 32>, public Bl //! \since Crypto++ 6.0 class CRYPTOPP_NO_VTABLE Base : protected SPECK_Base, public BlockCipherImpl > { - public: - std::string AlgorithmName() const { - return StaticAlgorithmName() + "(" + IntToString(sizeof(word64)*8) + ")"; - } + public: + std::string AlgorithmName() const { + return StaticAlgorithmName() + "(" + IntToString(sizeof(word64)*8) + ")"; + } protected: void UncheckedSetKey(const byte *userKey, unsigned int keyLength, const NameValuePairs ¶ms); }; - class CRYPTOPP_NO_VTABLE Enc : public Base - { - protected: - void ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const; - }; - - class CRYPTOPP_NO_VTABLE Dec : public Base - { - protected: - void ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const; - }; + //! \brief Provides implementation for encryption transformation + //! \details Enc provides implementation for encryption transformation. All key + //! sizes are supported. + //! \since Crypto++ 6.0 + class CRYPTOPP_NO_VTABLE Enc : public Base + { + protected: + void ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const; + }; + + //! \brief Provides implementation for encryption transformation + //! \details Dec provides implementation for decryption transformation. All key + //! sizes are supported. + //! \since Crypto++ 6.0 + class CRYPTOPP_NO_VTABLE Dec : public Base + { + protected: + void ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const; + }; typedef BlockCipherFinal Encryption; typedef BlockCipherFinal Decryption;