1 /** 2 * RC5 3 * 4 * Copyright: 5 * (C) 1999-2007 Jack Lloyd 6 * (C) 2014-2015 Etienne Cimon 7 * 8 * License: 9 * Botan is released under the Simplified BSD License (see LICENSE.md) 10 */ 11 module botan.block.rc5; 12 13 import botan.constants; 14 static if (BOTAN_HAS_RC5): 15 16 import botan.utils.loadstor; 17 import botan.utils.rotate; 18 import botan.utils.parsing; 19 import std.algorithm; 20 import botan.block.block_cipher; 21 import std.conv : to; 22 import botan.utils.mem_ops; 23 24 /** 25 * RC5 26 */ 27 final class RC5 : BlockCipherFixedParams!(8, 1, 32), BlockCipher, SymmetricAlgorithm 28 { 29 public: 30 /* 31 * RC5 Encryption 32 */ 33 override void encryptN(const(ubyte)* input, ubyte* output, size_t blocks) 34 { 35 foreach (size_t i; 0 .. blocks) 36 { 37 uint A = loadLittleEndian!uint(input, 0); 38 uint B = loadLittleEndian!uint(input, 1); 39 40 A += m_S[0]; B += m_S[1]; 41 for (size_t j = 0; j != m_rounds; j += 4) 42 { 43 A = rotateLeft(A ^ B, B % 32) + m_S[2*j+2]; 44 B = rotateLeft(B ^ A, A % 32) + m_S[2*j+3]; 45 46 A = rotateLeft(A ^ B, B % 32) + m_S[2*j+4]; 47 B = rotateLeft(B ^ A, A % 32) + m_S[2*j+5]; 48 49 A = rotateLeft(A ^ B, B % 32) + m_S[2*j+6]; 50 B = rotateLeft(B ^ A, A % 32) + m_S[2*j+7]; 51 52 A = rotateLeft(A ^ B, B % 32) + m_S[2*j+8]; 53 B = rotateLeft(B ^ A, A % 32) + m_S[2*j+9]; 54 } 55 56 storeLittleEndian(output, A, B); 57 58 input += BLOCK_SIZE; 59 output += BLOCK_SIZE; 60 } 61 } 62 63 /* 64 * RC5 Decryption 65 */ 66 override void decryptN(const(ubyte)* input, ubyte* output, size_t blocks) 67 { 68 foreach (size_t i; 0 .. blocks) 69 { 70 uint A = loadLittleEndian!uint(input, 0); 71 uint B = loadLittleEndian!uint(input, 1); 72 73 for (size_t j = m_rounds; j != 0; j -= 4) 74 { 75 B = rotateRight(B - m_S[2*j+1], A % 32) ^ A; 76 A = rotateRight(A - m_S[2*j ], B % 32) ^ B; 77 78 B = rotateRight(B - m_S[2*j-1], A % 32) ^ A; 79 A = rotateRight(A - m_S[2*j-2], B % 32) ^ B; 80 81 B = rotateRight(B - m_S[2*j-3], A % 32) ^ A; 82 A = rotateRight(A - m_S[2*j-4], B % 32) ^ B; 83 84 B = rotateRight(B - m_S[2*j-5], A % 32) ^ A; 85 A = rotateRight(A - m_S[2*j-6], B % 32) ^ B; 86 } 87 B -= m_S[1]; A -= m_S[0]; 88 89 storeLittleEndian(output, A, B); 90 91 input += BLOCK_SIZE; 92 output += BLOCK_SIZE; 93 } 94 } 95 96 override void clear() 97 { 98 zap(m_S); 99 } 100 101 /* 102 * Return the name of this type 103 */ 104 override @property string name() const 105 { 106 return "RC5(" ~ to!string(m_rounds) ~ ")"; 107 } 108 109 override @property size_t parallelism() const { return 1; } 110 override BlockCipher clone() const { return new RC5(m_rounds); } 111 112 override size_t blockSize() const { return super.blockSize(); } 113 override KeyLengthSpecification keySpec() const { return super.keySpec(); } 114 115 /** 116 * RC5 Constructor 117 * Params: 118 * r = the number of RC5 rounds to run. Must be between 119 * 8 and 32 and a multiple of 4. 120 */ 121 this(size_t r) 122 { 123 m_rounds = r; 124 if (m_rounds < 8 || m_rounds > 32 || (m_rounds % 4 != 0)) 125 throw new InvalidArgument("RC5: Invalid number of rounds " ~ 126 to!string(m_rounds)); 127 } 128 protected: 129 130 /* 131 * RC5 Key Schedule 132 */ 133 override void keySchedule(const(ubyte)* key, size_t length) 134 { 135 m_S.resize(2*m_rounds + 2); 136 137 const size_t WORD_KEYLENGTH = (((length - 1) / 4) + 1); 138 const size_t MIX_ROUNDS = 3 * std.algorithm.max(WORD_KEYLENGTH, m_S.length); 139 140 m_S[0] = 0xB7E15163; 141 foreach (size_t i; 1 .. m_S.length) 142 m_S[i] = m_S[i-1] + 0x9E3779B9; 143 144 SecureVector!uint K = SecureVector!uint(8); 145 146 for (int i = cast(int) length-1; i >= 0; --i) 147 K[i/4] = (K[i/4] << 8) + key[i]; 148 149 uint A = 0, B = 0; 150 151 foreach (size_t i; 0 .. MIX_ROUNDS) 152 { 153 A = rotateLeft(m_S[i % m_S.length] + A + B, 3); 154 B = rotateLeft(K[i % WORD_KEYLENGTH] + A + B, (A + B) % 32); 155 m_S[i % m_S.length] = A; 156 K[i % WORD_KEYLENGTH] = B; 157 } 158 } 159 160 161 size_t m_rounds; 162 SecureVector!uint m_S; 163 }