-
Notifications
You must be signed in to change notification settings - Fork 0
/
xxtea.hpp
208 lines (153 loc) · 4.82 KB
/
xxtea.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
#pragma once
#include <vector>
#include <string>
#include <cstdint>
#include <climits>
#include <limits>
/**
* XXTEA encryption algorithm library for C++.
*
* Encryption Algorithm Authors:
* David J. Wheeler
* Roger M. Needham
*
* Code Author: Gabriel Menezes <https://github.com/menezes-/xxtea>
*/
// acording to this stackexchange https://crypto.stackexchange.com/a/12997 comment
// xxtea security can be increase by increasing the number of mixes,
// so i've put this as a definibable constant
#ifndef XXTEA_NUMBER_OF_MIXES
#define XXTEA_NUMBER_OF_MIXES 6
#endif
namespace xxtea
{
using bytes = std::vector<std::uint32_t>;
namespace internal
{
constexpr std::size_t round_up(std::size_t num, std::size_t multiple) {
auto remainder = num % multiple;
if (remainder == 0) {
return num;
}
return num + multiple - remainder;
}
template<class BlockType>
std::vector<BlockType> to_blocks(std::string string) {
constexpr unsigned short block_size = std::numeric_limits<BlockType>::digits;
static_assert(block_size >= CHAR_BIT, "Can't be smaller than CHAR_BIT");
string.resize(round_up(string.size(), block_size), '\x03');
auto number_of_bits = string.size() * CHAR_BIT;
std::vector<BlockType> blocks{};
auto n_blocks = number_of_bits / block_size;
n_blocks = std::max(n_blocks, decltype(n_blocks){1});
blocks.resize(n_blocks, 0);
constexpr auto fit_size = block_size / CHAR_BIT;
for (std::size_t i = 0; i < string.size(); ++i) {
auto bucket = (i * CHAR_BIT) / block_size;
auto shift = CHAR_BIT * (i % fit_size);
auto c = static_cast<unsigned int>(string[i]);
blocks[bucket] += c << shift;
}
return blocks;
}
template<class BlockType>
std::string to_string(const std::vector<BlockType> &blocks) {
constexpr unsigned short block_size = std::numeric_limits<BlockType>::digits;
constexpr auto fit_size = block_size / CHAR_BIT;
std::string s;
s.reserve(fit_size * blocks.size());
for (const auto &i : blocks) {
for (std::size_t j = 0; j < fit_size; ++j) {
auto shift = CHAR_BIT * (j % fit_size);
auto c = static_cast<char >(i >> shift);
if (c != '\x03') {
s.push_back(c);
} else {
break;
}
}
}
return s;
}
constexpr unsigned long delta{0x9E3779B9};
}
#define XXTEA_MX (((z>>5U^y<<2U) + (y>>3U^z<<4U)) ^ ((sum^y) + (k[(p&3U)^e] ^ z)))
/**
* encodes an array of unsigned 32-bit integers using 128-bit key.
* @param v vector of uint32
* @param k 128-bit key, if smaller the vector will be padded
*/
inline void encode(bytes &v, const bytes &k) {
if (v.empty()) {
return;
}
auto n = v.size();
unsigned long z{v[n - 1]};
unsigned long y{v[0]};
unsigned long sum = 0;
long rounds = XXTEA_NUMBER_OF_MIXES + 52 / n;
std::size_t p;
while (rounds-- > 0) {
sum += internal::delta;
unsigned long e = (sum >> 2U) & 3U;
for (p = 0; p < n - 1; ++p) {
y = v[p + 1];
v[p] += XXTEA_MX;
z = v[p];
}
y = v[0];
v[n - 1] += XXTEA_MX;
z = v[n - 1];
}
}
/**
* decodes an array of unsigned 32-bit integers using 128-bit key.
* @param v array to be decoded
* @param k 128-bit key
*/
inline void decode(bytes &v, const bytes &k) {
if (v.empty()) {
return;
}
auto n = v.size();
unsigned long z{v[n - 1]};
unsigned long y{v[0]};
long rounds = XXTEA_NUMBER_OF_MIXES + 52 / n;
unsigned long sum = rounds * internal::delta;
std::size_t p;
for (; sum != 0; sum -= internal::delta) {
unsigned long e = (sum >> 2U) & 3U;
for (p = n - 1; p > 0; --p) {
z = v[p - 1];
v[p] -= XXTEA_MX;
y = v[p];
}
z = v[n - 1];
v[0] -= XXTEA_MX;
y = v[0];
}
}
/**
* Encrypts text using Corrected Block TEA (aka xxtea) algorithm
* @param plaintext String to be encrypted. Handles utf-8.
* @param password Password to be used for encryption (only 128 bits are used).
* @return Encrypted text as uint32_t vector
*/
inline bytes encrypt(const std::string &plaintext, const std::string &password) {
auto text = internal::to_blocks<std::uint32_t>(plaintext);
auto key = internal::to_blocks<std::uint32_t>(password);
encode(text, key);
return text;
}
/**
* Decrypts text using Corrected Block TEA (xxtea) algorithm
* @param encrypted_string @see encrypt
* @param password password used to encrypt the string
* @return utf8 encoded string
*/
inline std::string decrypt(bytes &encrypted_string, const std::string &password) {
auto key = internal::to_blocks<std::uint32_t>(password);
decode(encrypted_string, key);
return internal::to_string<std::uint32_t>(encrypted_string);
}
}