summaryrefslogtreecommitdiff
path: root/simon.cpp
diff options
context:
space:
mode:
authorJeffrey Walton <noloader@gmail.com>2017-11-21 04:58:51 -0500
committerJeffrey Walton <noloader@gmail.com>2017-11-21 04:58:51 -0500
commit3970a066e35f8ba278401ce7b4f1a6ce24ffee15 (patch)
tree7ef8e42b43afc4bcefec2ef0d48a363f29926bf3 /simon.cpp
parent5007c13fbd585628d10e2a92b7e96702d66defcc (diff)
downloadcryptopp-git-3970a066e35f8ba278401ce7b4f1a6ce24ffee15.tar.gz
Add SIMON-64 and SIMON-128 lightweight block ciphers (GH #539)
Diffstat (limited to 'simon.cpp')
-rw-r--r--simon.cpp365
1 files changed, 365 insertions, 0 deletions
diff --git a/simon.cpp b/simon.cpp
new file mode 100644
index 00000000..5852ee5e
--- /dev/null
+++ b/simon.cpp
@@ -0,0 +1,365 @@
+// simon.h - written and placed in the public domain by Jeffrey Walton
+
+#include "pch.h"
+#include "config.h"
+
+#include "simon.h"
+#include "misc.h"
+
+ANONYMOUS_NAMESPACE_BEGIN
+
+using CryptoPP::word32;
+using CryptoPP::word64;
+using CryptoPP::rotlFixed;
+using CryptoPP::rotrFixed;
+
+//! \brief Round transformation helper
+//! \tparam W word type
+//! \param v value
+template <class W>
+inline W f(const W v)
+{
+ return (rotlFixed(v, 1) & rotlFixed(v, 8)) ^ rotlFixed(v, 2);
+}
+
+//! \brief Round transformation
+//! \tparam W word type
+//! \param x value
+//! \param y value
+//! \param k value
+//! \param l value
+template <class W>
+inline void R2(W& x, W& y, const W k, const W l)
+{
+ y ^= f(x); y ^= k;
+ x ^= f(y); x ^= l;
+}
+
+//! \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 <class W, unsigned int R>
+inline void SIMON_Encrypt(W c[2], const W p[2], const W k[R])
+{
+ c[0]=p[0]; c[1]=p[1];
+
+ for (size_t i = 0; static_cast<int>(i) < R-1; i += 2)
+ R2(c[0], c[1], k[i], k[i + 1]);
+
+ // The constexpr residue should allow the optimizer to remove unneeded statements
+ if (R%2 == 1)
+ {
+ c[1] ^= f(c[0]); c[1] ^= k[R-1];
+ W t = c[0]; c[0] = c[1]; c[1] = t;
+ }
+}
+
+//! \brief Reverse transformation
+//! \tparam W word type
+//! \tparam R number of rounds
+//! \param p output array
+//! \param c input array
+//! \param k subkey array
+template <class W, unsigned int R>
+inline void SIMON_Decrypt(W p[2], const W c[2], const W k[R])
+{
+ p[0]=c[0]; p[1]=c[1];
+
+ // The constexpr residue should allow the optimizer to remove unneeded statements
+ if (R % 2 == 0)
+ {
+ for (size_t i = R-2; static_cast<int>(i) >= 0; i -= 2)
+ R2(p[1], p[0], k[i+1], k[i]);
+ }
+ else
+ {
+ const W t = p[1]; p[1] = p[0];
+ p[0] = t; p[1] ^= k[R-1]; p[1] ^= f(p[0]);
+
+ for (size_t i = R-3; static_cast<int>(i) >= 0; i -= 2)
+ R2(p[1], p[0], k[i+1], k[i]);
+ }
+}
+
+//! \brief Subkey generation function
+//! \details Used for SIMON-64 with 96-bit key and 42 rounds. A template was
+//! not worthwile because all instantiations would need specialization.
+//! \param key empty subkey array
+//! \param k user key array
+inline void SPECK64_ExpandKey_42R3K(word32 key[42], const word32 k[3])
+{
+ const word32 c = 0xfffffffc;
+ word64 z = 0x7369f885192c0ef5LL;
+
+ key[0] = k[0]; key[1] = k[1]; key[2] = k[2];
+ for (size_t i = 3; i<42; ++i)
+ {
+ key[i] = c ^ (z & 1) ^ key[i-3] ^ rotrFixed(key[i-1], 3) ^ rotrFixed(key[i-1], 4);
+ z >>= 1;
+ }
+}
+
+//! \brief Subkey generation function
+//! \details Used for SIMON-64 with 128-bit key and 44 rounds. A template was
+//! not worthwile because all instantiations would need specialization.
+//! \param key empty subkey array
+//! \param k user key array
+inline void SPECK64_ExpandKey_44R4K(word32 key[44], const word32 k[4])
+{
+ const word32 c = 0xfffffffc;
+ word64 z = W64LIT(0xfc2ce51207a635db);
+
+ key[0] = k[0]; key[1] = k[1]; key[2] = k[2]; key[3] = k[3];
+ for (size_t i = 4; i<44; ++i)
+ {
+ key[i] = c ^ (z & 1) ^ key[i-4] ^ rotrFixed(key[i-1], 3) ^ key[i-3] ^ rotrFixed(key[i-1], 4) ^ rotrFixed(key[i-3], 1);
+ z >>= 1;
+ }
+}
+
+//! \brief Subkey generation function
+//! \details Used for SIMON-128 with 128-bit key and 68 rounds. A template was
+//! not worthwile because all instantiations would need specialization.
+//! \param key empty subkey array
+//! \param k user key array
+inline void SIMON128_ExpandKey_68R2K(word64 key[68], const word64 k[2])
+{
+ const word64 c = W64LIT(0xfffffffffffffffc);
+ word64 z = W64LIT(0x7369f885192c0ef5);
+
+ key[0] = k[0]; key[1] = k[1];
+ for (size_t i=2; i<66; ++i)
+ {
+ key[i] = c^(z&1)^key[i-2]^rotrFixed(key[i-1],3)^rotrFixed(key[i-1],4);
+ z>>=1;
+ }
+
+ key[66] = c^1^key[64]^rotrFixed(key[65],3)^rotrFixed(key[65],4);
+ key[67] = c^key[65]^rotrFixed(key[66],3)^rotrFixed(key[66],4);
+}
+
+//! \brief Subkey generation function
+//! \details Used for SIMON-128 with 192-bit key and 69 rounds. A template was
+//! not worthwile because all instantiations would need specialization.
+//! \param key empty subkey array
+//! \param k user key array
+inline void SIMON128_ExpandKey_69R3K(word64 key[69], const word64 k[3])
+{
+ const word64 c = W64LIT(0xfffffffffffffffc);
+ word64 z = W64LIT(0xfc2ce51207a635db);
+
+ key[0]=k[0]; key[1]=k[1]; key[2]=k[2];
+ for (size_t i=3; i<67; ++i)
+ {
+ key[i] = c^(z&1)^key[i-3]^rotrFixed(key[i-1],3)^rotrFixed(key[i-1],4);
+ z>>=1;
+ }
+
+ key[67] = c^key[64]^rotrFixed(key[66],3)^rotrFixed(key[66],4);
+ key[68] = c^1^key[65]^rotrFixed(key[67],3)^rotrFixed(key[67],4);
+}
+
+//! \brief Subkey generation function
+//! \details Used for SIMON-128 with 256-bit key and 72 rounds. A template was
+//! not worthwile because all instantiations would need specialization.
+//! \param key empty subkey array
+//! \param k user key array
+inline void SIMON128_ExpandKey_72R4K(word64 key[72], const word64 k[4])
+{
+ const word64 c = W64LIT(0xfffffffffffffffc);
+ word64 z = W64LIT(0xfdc94c3a046d678b);
+
+ key[0]=k[0]; key[1]=k[1]; key[2]=k[2]; key[3]=k[3];
+ for (size_t i=4; i<68; ++i)
+ {
+ key[i] = c^(z&1)^key[i-4]^rotrFixed(key[i-1],3)^key[i-3]^rotrFixed(key[i-1],4)^rotrFixed(key[i-3],1);
+ z>>=1;
+ }
+
+ key[68] = c^key[64]^rotrFixed(key[67],3)^key[65]^rotrFixed(key[67],4)^rotrFixed(key[65],1);
+ key[69] = c^1^key[65]^rotrFixed(key[68],3)^key[66]^rotrFixed(key[68],4)^rotrFixed(key[66],1);
+ key[70] = c^key[66]^rotrFixed(key[69],3)^key[67]^rotrFixed(key[69],4)^rotrFixed(key[67],1);
+ key[71] = c^key[67]^rotrFixed(key[70],3)^key[68]^rotrFixed(key[70],4)^rotrFixed(key[68],1);
+}
+
+ANONYMOUS_NAMESPACE_END
+
+///////////////////////////////////////////////////////////
+
+NAMESPACE_BEGIN(CryptoPP)
+
+#if 1
+void SIMON64::Base::UncheckedSetKey(const byte *userKey, unsigned int keyLength, const NameValuePairs &params)
+{
+ CRYPTOPP_ASSERT(keyLength == 12 || keyLength == 16);
+ CRYPTOPP_UNUSED(params);
+
+ // 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. SIMON does unusual things with key string and word ordering
+ // {A,B} -> {B,A}, {A,B,C} -> {C,B,A}, etc.
+ typedef GetBlock<word32, BigEndian, false> InBlock;
+ InBlock iblk(userKey);
+
+ switch (m_kwords)
+ {
+ case 3:
+ m_rkey.New(42);
+ iblk(m_wspace[2])(m_wspace[1])(m_wspace[0]);
+ SPECK64_ExpandKey_42R3K(m_rkey, m_wspace);
+ break;
+ case 4:
+ m_rkey.New(44);
+ iblk(m_wspace[3])(m_wspace[2])(m_wspace[1])(m_wspace[0]);
+ SPECK64_ExpandKey_44R4K(m_rkey, m_wspace);
+ break;
+ default:
+ CRYPTOPP_ASSERT(0);;
+ }
+}
+
+void SIMON64::Enc::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const
+{
+ // Reverse bytes on LittleEndian; align pointer on BigEndian
+ typedef GetBlock<word32, BigEndian, false> InBlock;
+ InBlock iblk(inBlock); iblk(m_wspace[0])(m_wspace[1]);
+
+ switch (m_kwords)
+ {
+ case 3:
+ SIMON_Encrypt<word32, 42>(m_wspace+2, m_wspace+0, m_rkey);
+ break;
+ case 4:
+ SIMON_Encrypt<word32, 44>(m_wspace+2, m_wspace+0, m_rkey);
+ break;
+ default:
+ CRYPTOPP_ASSERT(0);;
+ }
+
+ // Reverse bytes on LittleEndian; align pointer on BigEndian
+ typedef PutBlock<word32, BigEndian, false> OutBlock;
+ OutBlock oblk(xorBlock, outBlock); oblk(m_wspace[2])(m_wspace[3]);
+}
+
+void SIMON64::Dec::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const
+{
+ // Reverse bytes on LittleEndian; align pointer on BigEndian
+ typedef GetBlock<word32, BigEndian, false> InBlock;
+ InBlock iblk(inBlock); iblk(m_wspace[0])(m_wspace[1]);
+
+ switch (m_kwords)
+ {
+ case 3:
+ SIMON_Decrypt<word32, 42>(m_wspace+2, m_wspace+0, m_rkey);
+ break;
+ case 4:
+ SIMON_Decrypt<word32, 44>(m_wspace+2, m_wspace+0, m_rkey);
+ break;
+ default:
+ CRYPTOPP_ASSERT(0);;
+ }
+
+ // Reverse bytes on LittleEndian; align pointer on BigEndian
+ typedef PutBlock<word32, BigEndian, false> OutBlock;
+ OutBlock oblk(xorBlock, outBlock); oblk(m_wspace[2])(m_wspace[3]);
+}
+#endif
+
+///////////////////////////////////////////////////////////
+
+void SIMON128::Base::UncheckedSetKey(const byte *userKey, unsigned int keyLength, const NameValuePairs &params)
+{
+ CRYPTOPP_ASSERT(keyLength == 16 || keyLength == 24 || keyLength == 32);
+ CRYPTOPP_UNUSED(params);
+
+ // 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. SIMON does unusual things with key string and word ordering
+ // {A,B} -> {B,A}, {A,B,C} -> {C,B,A}, etc.
+ typedef GetBlock<word64, BigEndian, false> InBlock;
+ InBlock iblk(userKey);
+
+ switch (m_kwords)
+ {
+ case 2:
+ m_rkey.New(68);
+ iblk(m_wspace[1])(m_wspace[0]);
+ SIMON128_ExpandKey_68R2K(m_rkey, m_wspace);
+ break;
+ case 3:
+ m_rkey.New(69);
+ iblk(m_wspace[2])(m_wspace[1])(m_wspace[0]);
+ SIMON128_ExpandKey_69R3K(m_rkey, m_wspace);
+ break;
+ case 4:
+ m_rkey.New(72);
+ iblk(m_wspace[3])(m_wspace[2])(m_wspace[1])(m_wspace[0]);
+ SIMON128_ExpandKey_72R4K(m_rkey, m_wspace);
+ break;
+ default:
+ CRYPTOPP_ASSERT(0);;
+ }
+}
+
+void SIMON128::Enc::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const
+{
+ // Reverse bytes on LittleEndian; align pointer on BigEndian
+ typedef GetBlock<word64, BigEndian, false> InBlock;
+ InBlock iblk(inBlock); iblk(m_wspace[0])(m_wspace[1]);
+
+ switch (m_kwords)
+ {
+ case 2:
+ SIMON_Encrypt<word64, 68>(m_wspace+2, m_wspace+0, m_rkey);
+ break;
+ case 3:
+ SIMON_Encrypt<word64, 69>(m_wspace+2, m_wspace+0, m_rkey);
+ break;
+ case 4:
+ SIMON_Encrypt<word64, 72>(m_wspace+2, m_wspace+0, m_rkey);
+ break;
+ default:
+ CRYPTOPP_ASSERT(0);;
+ }
+
+ // Reverse bytes on LittleEndian; align pointer on BigEndian
+ typedef PutBlock<word64, BigEndian, false> OutBlock;
+ OutBlock oblk(xorBlock, outBlock); oblk(m_wspace[2])(m_wspace[3]);
+}
+
+void SIMON128::Dec::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const
+{
+ // Reverse bytes on LittleEndian; align pointer on BigEndian
+ typedef GetBlock<word64, BigEndian, false> InBlock;
+ InBlock iblk(inBlock); iblk(m_wspace[0])(m_wspace[1]);
+
+ switch (m_kwords)
+ {
+ case 2:
+ SIMON_Decrypt<word64, 68>(m_wspace+2, m_wspace+0, m_rkey);
+ break;
+ case 3:
+ SIMON_Decrypt<word64, 69>(m_wspace+2, m_wspace+0, m_rkey);
+ break;
+ case 4:
+ SIMON_Decrypt<word64, 72>(m_wspace+2, m_wspace+0, m_rkey);
+ break;
+ default:
+ CRYPTOPP_ASSERT(0);;
+ }
+
+ // Reverse bytes on LittleEndian; align pointer on BigEndian
+ typedef PutBlock<word64, BigEndian, false> OutBlock;
+ OutBlock oblk(xorBlock, outBlock); oblk(m_wspace[2])(m_wspace[3]);
+}
+
+NAMESPACE_END