| // SPDX-License-Identifier: BSD-2-Clause |
| /* LibTomCrypt, modular cryptographic library -- Tom St Denis |
| * |
| * LibTomCrypt is a library that provides various cryptographic |
| * algorithms in a highly modular and flexible manner. |
| * |
| * The library is free for all purposes without any express |
| * guarantee it works. |
| */ |
| |
| /* Based on idea.cpp - originally written and placed in the public domain by Wei Dai |
| https://github.com/weidai11/cryptopp/blob/master/idea.cpp |
| |
| Patents should be expired. On 2017-10-16 wikipedia says: |
| https://en.wikipedia.org/wiki/International_Data_Encryption_Algorithm |
| |
| A patent application for IDEA was first filed in Switzerland (CH A 1690/90) on May 18, 1990, |
| then an international patent application was filed under the Patent Cooperation Treaty on |
| May 16, 1991. Patents were eventually granted in Austria, France, Germany, Italy, the Netherlands, |
| Spain, Sweden, Switzerland, the United Kingdom, (European Patent Register entry for European |
| patent no. 0482154, filed May 16, 1991, issued June 22, 1994 and expired May 16, 2011), |
| the United States (U.S. Patent 5,214,703, issued May 25, 1993 and expired January 7, 2012) |
| and Japan (JP 3225440) (expired May 16, 2011). |
| */ |
| |
| #include "tomcrypt_private.h" |
| |
| #ifdef LTC_IDEA |
| |
| const struct ltc_cipher_descriptor idea_desc = { |
| "idea", |
| 24, /* cipher_ID */ |
| 16, 16, 8, 8, /* min_key_len, max_key_len, block_len, default_rounds */ |
| &idea_setup, |
| &idea_ecb_encrypt, |
| &idea_ecb_decrypt, |
| &idea_test, |
| &idea_done, |
| &idea_keysize, |
| NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL |
| }; |
| |
| typedef unsigned short int ushort16; |
| |
| #define _LOW16(x) ((x)&0xffff) /* compiler should be able to optimize this away if x is 16 bits */ |
| #define _HIGH16(x) ((x)>>16) |
| #define _MUL(a,b) { \ |
| ulong32 p = (ulong32)_LOW16(a) * b; \ |
| if (p) { \ |
| p = _LOW16(p) - _HIGH16(p); \ |
| a = (ushort16)p - (ushort16)_HIGH16(p); \ |
| } \ |
| else \ |
| a = 1 - a - b; \ |
| } |
| #define _STORE16(x,y) { (y)[0] = (unsigned char)(((x)>>8)&255); (y)[1] = (unsigned char)((x)&255); } |
| #define _LOAD16(x,y) { x = ((ushort16)((y)[0] & 255)<<8) | ((ushort16)((y)[1] & 255)); } |
| |
| static ushort16 _mul_inv(ushort16 x) |
| { |
| ushort16 y = x; |
| unsigned i; |
| |
| for (i = 0; i < 15; i++) { |
| _MUL(y, _LOW16(y)); |
| _MUL(y, x); |
| } |
| return _LOW16(y); |
| } |
| |
| static ushort16 _add_inv(ushort16 x) |
| { |
| return _LOW16(0 - x); |
| } |
| |
| static int _setup_key(const unsigned char *key, symmetric_key *skey) |
| { |
| int i, j; |
| ushort16 *e_key = skey->idea.ek; |
| ushort16 *d_key = skey->idea.dk; |
| |
| /* prepare enc key */ |
| for (i = 0; i < 8; i++) { |
| _LOAD16(e_key[i], key + 2 * i); |
| } |
| for (; i < LTC_IDEA_KEYLEN; i++) { |
| j = (i - i % 8) - 8; |
| e_key[i] = _LOW16((e_key[j+(i+1)%8] << 9) | (e_key[j+(i+2)%8] >> 7)); |
| } |
| |
| /* prepare dec key */ |
| for (i = 0; i < LTC_IDEA_ROUNDS; i++) { |
| d_key[i*6+0] = _mul_inv(e_key[(LTC_IDEA_ROUNDS-i)*6+0]); |
| d_key[i*6+1] = _add_inv(e_key[(LTC_IDEA_ROUNDS-i)*6+1+(i>0 ? 1 : 0)]); |
| d_key[i*6+2] = _add_inv(e_key[(LTC_IDEA_ROUNDS-i)*6+2-(i>0 ? 1 : 0)]); |
| d_key[i*6+3] = _mul_inv(e_key[(LTC_IDEA_ROUNDS-i)*6+3]); |
| d_key[i*6+4] = e_key[(LTC_IDEA_ROUNDS-1-i)*6+4]; |
| d_key[i*6+5] = e_key[(LTC_IDEA_ROUNDS-1-i)*6+5]; |
| } |
| d_key[i*6+0] = _mul_inv(e_key[(LTC_IDEA_ROUNDS-i)*6+0]); |
| d_key[i*6+1] = _add_inv(e_key[(LTC_IDEA_ROUNDS-i)*6+1]); |
| d_key[i*6+2] = _add_inv(e_key[(LTC_IDEA_ROUNDS-i)*6+2]); |
| d_key[i*6+3] = _mul_inv(e_key[(LTC_IDEA_ROUNDS-i)*6+3]); |
| |
| return CRYPT_OK; |
| } |
| |
| static int _process_block(const unsigned char *in, unsigned char *out, const ushort16 *m_key) |
| { |
| int i; |
| ushort16 x0, x1, x2, x3, t0, t1; |
| |
| _LOAD16(x0, in + 0); |
| _LOAD16(x1, in + 2); |
| _LOAD16(x2, in + 4); |
| _LOAD16(x3, in + 6); |
| |
| for (i = 0; i < LTC_IDEA_ROUNDS; i++) { |
| _MUL(x0, m_key[i*6+0]); |
| x1 += m_key[i*6+1]; |
| x2 += m_key[i*6+2]; |
| _MUL(x3, m_key[i*6+3]); |
| t0 = x0^x2; |
| _MUL(t0, m_key[i*6+4]); |
| t1 = t0 + (x1^x3); |
| _MUL(t1, m_key[i*6+5]); |
| t0 += t1; |
| x0 ^= t1; |
| x3 ^= t0; |
| t0 ^= x1; |
| x1 = x2^t1; |
| x2 = t0; |
| } |
| |
| _MUL(x0, m_key[LTC_IDEA_ROUNDS*6+0]); |
| x2 += m_key[LTC_IDEA_ROUNDS*6+1]; |
| x1 += m_key[LTC_IDEA_ROUNDS*6+2]; |
| _MUL(x3, m_key[LTC_IDEA_ROUNDS*6+3]); |
| |
| _STORE16(x0, out + 0); |
| _STORE16(x2, out + 2); |
| _STORE16(x1, out + 4); |
| _STORE16(x3, out + 6); |
| |
| return CRYPT_OK; |
| } |
| |
| int idea_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey) |
| { |
| LTC_ARGCHK(key != NULL); |
| LTC_ARGCHK(skey != NULL); |
| |
| if (num_rounds != 0 && num_rounds != 8) return CRYPT_INVALID_ROUNDS; |
| if (keylen != 16) return CRYPT_INVALID_KEYSIZE; |
| |
| return _setup_key(key, skey); |
| } |
| |
| int idea_ecb_encrypt(const unsigned char *pt, unsigned char *ct, const symmetric_key *skey) |
| { |
| int err = _process_block(pt, ct, skey->idea.ek); |
| #ifdef LTC_CLEAN_STACK |
| burn_stack(sizeof(ushort16) * 6 + sizeof(int)); |
| #endif |
| return err; |
| } |
| |
| int idea_ecb_decrypt(const unsigned char *ct, unsigned char *pt, const symmetric_key *skey) |
| { |
| int err = _process_block(ct, pt, skey->idea.dk); |
| #ifdef LTC_CLEAN_STACK |
| burn_stack(sizeof(ushort16) * 6 + sizeof(int)); |
| #endif |
| return err; |
| } |
| |
| void idea_done(symmetric_key *skey) |
| { |
| LTC_UNUSED_PARAM(skey); |
| } |
| |
| int idea_keysize(int *keysize) |
| { |
| LTC_ARGCHK(keysize != NULL); |
| if (*keysize < 16) { |
| return CRYPT_INVALID_KEYSIZE; |
| } |
| *keysize = 16; |
| return CRYPT_OK; |
| } |
| |
| int idea_test(void) |
| { |
| #ifndef LTC_TEST |
| return CRYPT_NOP; |
| #else |
| static const struct { |
| unsigned char key[16], pt[8], ct[8]; |
| } tests[] = { |
| { |
| /* key */ { 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, |
| /* pt */ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, |
| /* ct */ { 0xB1, 0xF5, 0xF7, 0xF8, 0x79, 0x01, 0x37, 0x0F } |
| }, |
| { |
| /* key */ { 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, |
| /* pt */ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, |
| /* ct */ { 0xB3, 0x92, 0x7D, 0xFF, 0xB6, 0x35, 0x86, 0x26 } |
| }, |
| { |
| /* key */ { 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, |
| /* pt */ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, |
| /* ct */ { 0xE9, 0x87, 0xE0, 0x02, 0x9F, 0xB9, 0x97, 0x85 } |
| }, |
| { |
| /* key */ { 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, |
| /* pt */ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, |
| /* ct */ { 0x75, 0x4A, 0x03, 0xCE, 0x08, 0xDB, 0x7D, 0xAA } |
| }, |
| { |
| /* key */ { 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, |
| /* pt */ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, |
| /* ct */ { 0xF0, 0x15, 0xF9, 0xFB, 0x0C, 0xFC, 0x7E, 0x1C } |
| }, |
| }; |
| |
| unsigned char buf[2][8]; |
| symmetric_key key; |
| int err, x; |
| |
| if (sizeof(ushort16) != 2) { |
| return CRYPT_FAIL_TESTVECTOR; |
| } |
| |
| for (x = 0; x < (int)(sizeof(tests)/sizeof(tests[0])); x++) { |
| if ((err = idea_setup(tests[x].key, 16, 8, &key)) != CRYPT_OK) { |
| return err; |
| } |
| if ((err = idea_ecb_encrypt(tests[x].pt, buf[0], &key)) != CRYPT_OK) { |
| return err; |
| } |
| if (compare_testvector(buf[0], 8, tests[x].ct, 8, "IDEA Encrypt", x)) { |
| return CRYPT_FAIL_TESTVECTOR; |
| } |
| if ((err = idea_ecb_decrypt(tests[x].ct, buf[1], &key)) != CRYPT_OK) { |
| return err; |
| } |
| if (compare_testvector(buf[1], 8, tests[x].pt, 8, "IDEA Decrypt", x)) { |
| return CRYPT_FAIL_TESTVECTOR; |
| } |
| } |
| |
| return CRYPT_OK; |
| #endif |
| } |
| |
| #endif |
| |
| /* ref: $Format:%D$ */ |
| /* git commit: $Format:%H$ */ |
| /* commit time: $Format:%ai$ */ |