see The SCOP Stream Cipher by Simeon V. Maltchev, Peter T. Antonov
//////////////////////////////////////////////////////////////////////////////// /// \class cScopStream /// \brief "SCOP"라는 Stream Cipher 알고리즘 구현. /// /// Simeon Maltchev의 문서에 있는 소스를 대충 정리했다. 자세한 내용은 아래의 /// 링크를 참고하기 바란다. /// /// http://www.geocities.com/smaltchev/scop.html //////////////////////////////////////////////////////////////////////////////// class cScopStream { private: struct st_key { UINT32 v[384]; UINT8 i; UINT8 j; UINT8 t3; }; struct st_gp8 { UINT8 coef[8][4]; UINT32 x[4]; }; st_key m_Key; st_gp8 m_Internal; public: cScopStream(UINT8* key, UINT32 key_size) { UINT32 odd; UINT32 t[4]; ExpandKey(key, key_size); for (int i = 0; i < 8; i++) GeneratePolynoms(t); for (int i = 0; i < 12; i++) { for (int j = 0; j < 8; j++) GeneratePolynoms(m_Key.v + i * 32 + j * 4); GeneratePolynoms(t); } GeneratePolynoms(t); m_Key.i =(UINT8)(t[3] >> 24); m_Key.j =(UINT8)(t[3] >> 16); m_Key.t3 =(UINT8)(t[3] >> 8); odd = t[3] & 0x7F; m_Key.v[odd] |= 1; } virtual ~cScopStream() {} public: void Encrypt(UINT32* buf, int buflen) { UINT8 i = m_Key.i; UINT8 j = m_Key.j; UINT32 t1 = 0; UINT32 t2 = 0; UINT32 t3 = m_Key.t3; UINT32 k = 0; UINT32 t = 0; UINT32* word = buf; UINT32* v = m_Key.v; while (word < buf + buflen) { t1 = v[128 + j]; j = j + static_cast<UINT8>(t3); t = v[i]; t2 = v[128 + j]; /* If you want to compile with Borland's 32-bit C compiler using optimizations, change the line below to: i =(i + 1) & 255; */ i++; t3 = t2 + t; v[128 + j] = t3; j = j + static_cast<UINT8>(t2); k = t1 + t2; *word++ += k; } } void Decrypt(UINT32* buf, int buflen) { UINT32 k = 0; UINT32 t = 0; UINT8 i = m_Key.i; UINT8 j = m_Key.j; UINT32 t1 = 0; UINT32 t2 = 0; UINT32 t3 = m_Key.t3; UINT32* v = m_Key.v; UINT32* word = buf; while (word < buf + buflen) { t1 = v[128 + j]; j = j + static_cast<UINT8>(t3); t = v[i]; t2 = v[128 + j]; // If you want to compile with Borland's 32-bit C compiler // using optimizations, change the line below to: // i =(i + 1) & 255; i++; t3 = t2 + t; v[128 + j] = t3; j = j + static_cast<UINT8>(t2); k = t1 + t2; *word++ -= k; } } private: void ExpandKey(UINT8* in, UINT32 in_size) { Assert(in_size >= 2 && in_size <= 48); UINT8* p = reinterpret_cast<UINT8*>(&m_Internal); for (UINT32 i = 0; i < in_size; i++) p[i] = in[i]; for (UINT32 i = in_size; i < 48; i++) p[i] =(UINT8)(p[i - in_size] + p[i - in_size + 1]); UINT8 counter = 1; for (UINT32 i = 0; i < 32; i++) { if(p[i] == 0) p[i] = counter++; } } void GeneratePolynoms(UINT32* out) { UINT32 y1, y2; UINT32 x_1, x_2, x_3, x_4; UINT32 newx[4]; int i, i2; for (i = 0; i < 8; i += 2) { i2 = i >> 1; x_1 = m_Internal.x[i2] >> 16; x_2 = x_1 * x_1; x_3 = x_2 * x_1; x_4 = x_3 * x_1; y1 = m_Internal.coef[i][0] * x_4 + m_Internal.coef[i][1] * x_3 + m_Internal.coef[i][2] * x_2 + m_Internal.coef[i][3] * x_1 + 1; x_1 = m_Internal.x[i2] & 0xFFFFL; x_2 = x_1 * x_1; x_3 = x_2 * x_1; x_4 = x_3 * x_1; y2 = m_Internal.coef[i + 1][0] * x_4 + m_Internal.coef[i + 1][1] * x_3 + m_Internal.coef[i + 1][2] * x_2 + m_Internal.coef[i + 1][3] * x_1 + 1; out[i2] = (y1 << 16) | (y2 & 0xFFFFL); newx[i2] = (y1 & 0xFFFF0000L) | (y2 >> 16); } m_Internal.x[0] = (newx[0] >> 16) | (newx[3] << 16); m_Internal.x[1] = (newx[0] << 16) | (newx[1] >> 16); m_Internal.x[2] = (newx[1] << 16) | (newx[2] >> 16); m_Internal.x[3] = (newx[2] << 16) | (newx[3] >> 16); } };
#!cpp UINT8 mykey[] = { 0x29, 0x04, 0x19, 0x72, 0xFB, 0x42, 0xBA, 0x5F, 0xC7, 0x12, 0x77, 0x12, 0xF1, 0x38, 0x29, 0xC9 }; cScopStream stream1(mykey, sizeof(mykey)); cScopStream stream2(mykey, sizeof(mykey)); UINT32 original[] = { 100, 200, 300, 400, 500, 600 }; UINT32 dest[] = { 100, 200, 300, 400, 500, 600 }; stream1.Encrypt(dest, 6); stream2.Decrypt(dest, 6); for (int i=0; i<6; ++i) { if (original[i] != dest[i]) { Assert(false); } }