xxtea 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b8eca82a1c887086264d8807bf55a1980dc28f35f956c02a5bf66ebef33ab8f6
4
- data.tar.gz: d6707b17d68788035c7d8f2d90210f15408ba3a1d7a0e3c4dc0636d2b519dd10
3
+ metadata.gz: 89a73616644886a2d6c15b0567b6a123ba8029a410affd9aa7c76213358ba5b9
4
+ data.tar.gz: 181fff72b4214e44978ccd5ad26209d1362b0dcccaa949944bcf8e29c14b3577
5
5
  SHA512:
6
- metadata.gz: e829728fcc9f4296e74f9bee45568aab867679971c866ca992146a715a62444fea17602bf21322041c82bed745d7ba7a7f10905e7df4ecf194547b41164ac686
7
- data.tar.gz: 3a4224238e8e3d062dfc83671981834e1ab571c2e293bb90095db8ad46de86921c94d87972dcd0f3180ab6c562d9913e1dd1f2f4692dd07815752ffb12b8349e
6
+ metadata.gz: 5dc86b066d959b3d9bbb013fa9f3ba7cd772608cc5dcb434889185c8a0fccb4ab0b01f210fa3c628745d6efd9e760a29e175d469febb2d4618d61e62e13b0ad8
7
+ data.tar.gz: 804d0e50a19dcfcfbfb087376612e4d7f92309b9192463e324b92e133cf291466f0812127b6dabe22927bb48bb160032290beea8e596f3be4b66f25374c2c42e
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.2.0
4
+
5
+ - Add the `:length_word_suffix` and `:length_word_prefix` padding schemes, matching all five padding modes of [Python xxtea](https://github.com/ifduyue/xxtea) 6.2.0.
6
+ - `:length_word_suffix`: zero-pads to a 4-byte boundary, then appends one little-endian `uint32` with the original length (Cocos Creator JSC files).
7
+ - `:length_word_prefix`: prepends one little-endian `uint32` with the original length, then zero-pads the data to a 4-byte boundary.
8
+ - Both support empty input (padded to XXTEA's 2-word minimum) and raise `ArgumentError` on decrypt when the zero padding or length word is inconsistent.
9
+ - Add `XXTEA::LENGTH_WORD_SUFFIX` and `XXTEA::LENGTH_WORD_PREFIX` constant aliases.
10
+
11
+ ## 1.1.0
12
+
13
+ - Add named padding schemes: `:pkcs7_4_min8` (default, also `true`), `:pkcs7_8`, and `:none` (also `false`).
14
+ - `:pkcs7_4_min8` is 4-byte PKCS#7-like with an 8-byte minimum (pad values 5–8 for short inputs), compatible with Python xxtea.
15
+ - `:pkcs7_8` is standard 8-byte PKCS#7, compatible with Python [xxteang](https://github.com/ifduyue/xxteang).
16
+
3
17
  ## 1.0.0
4
18
 
5
19
  - Rewrite as a Ruby C extension compatible with Python [xxtea](https://github.com/ifduyue/xxtea) 5.3.3.
data/README.md CHANGED
@@ -5,7 +5,8 @@
5
5
 
6
6
  [XXTEA](https://en.wikipedia.org/wiki/XXTEA) implemented as a Ruby C extension, licensed under 2-clause BSD.
7
7
 
8
- Ciphertext is compatible with the [Python xxtea](https://github.com/ifduyue/xxtea) package.
8
+ Default ciphertext is compatible with the [Python xxtea](https://github.com/ifduyue/xxtea) package.
9
+ `padding: :pkcs7_8` is compatible with [Python xxteang](https://github.com/ifduyue/xxteang).
9
10
 
10
11
  The XXTEA algorithm takes a 128-bit key and operates on an array of 32-bit
11
12
  integers (at least 2 integers), but it doesn't define the conversions between
@@ -21,12 +22,11 @@ integers, which is required by the XXTEA algorithm). As a result of these
21
22
  measures, you can encrypt not only texts, but also any binary bytes of any
22
23
  length.
23
24
 
24
- > **Note:** This implementation uses a **non-standard** 4-byte block PKCS#7
25
- > padding instead of the conventional 8-byte or 16-byte block. For inputs
26
- > shorter than 4 bytes, a non-standard hack pads an extra 4 bytes (producing
27
- > pad values 5–8) to satisfy XXTEA's 2-word minimum. This means the output is
28
- > **NOT** compatible with other XXTEA implementations. Pass `padding: false`
29
- > for raw XXTEA (requires data length ≥ 8 and multiple of 4).
25
+ > **Note:** The default (`:pkcs7_4_min8`) is **not** standard 4-byte PKCS#7.
26
+ > For inputs shorter than 4 bytes it pads an extra 4 bytes (pad values 5–8)
27
+ > to satisfy XXTEA's 2-word minimum. Pass `padding: :pkcs7_8` for standard
28
+ > 8-byte PKCS#7 (compatible with Python xxteang), or `padding: false` for
29
+ > raw XXTEA (requires data length ≥ 8 and multiple of 4).
30
30
 
31
31
  ## Installation
32
32
 
@@ -87,9 +87,11 @@ They are stored on the object and used by every `encrypt`, `decrypt`,
87
87
  `encrypt_hex`, and `decrypt_hex` call:
88
88
 
89
89
  ```ruby
90
- c = XXTEA.new(key) # rounds=0, padding=true
91
- c = XXTEA.new(key, rounds: 64) # override rounds
92
- c = XXTEA.new(key, padding: false) # disable padding
90
+ c = XXTEA.new(key) # rounds=0, padding=:pkcs7_4_min8
91
+ c = XXTEA.new(key, rounds: 64) # override rounds
92
+ c = XXTEA.new(key, padding: false) # disable padding
93
+ c = XXTEA.new(key, padding: :pkcs7_8) # 8-byte PKCS#7
94
+ c = XXTEA.new(key, padding: :length_word_suffix) # length word + zero padding
93
95
  c = XXTEA.new(key, padding: false, rounds: 42)
94
96
  ```
95
97
 
@@ -103,19 +105,43 @@ s == XXTEA.decrypt([hexenc].pack("H*"), key) # => true
103
105
 
104
106
  ## Padding
105
107
 
106
- Padding is enabled by default, using a **non-standard 4-byte block PKCS#7**
107
- scheme. The pad byte value is `4 - (data.bytesize & 3)` (range 1–4), plus an
108
- extra 4 bytes when the input is shorter than 4 bytes to meet XXTEA's 2-word
109
- minimum (producing pad values 5–8).
108
+ `padding` accepts a scheme name, so more paddings can be added later:
110
109
 
111
- Because padding always adds at least one byte, encrypting an 8-byte input
112
- produces a 12-byte ciphertext. This is incompatible with other XXTEA
113
- implementations that use a standard block size or skip padding altogether.
114
- Use `padding: false` for raw, unpadded XXTEA.
110
+ | Value | Meaning |
111
+ | --- | --- |
112
+ | `true` or `:pkcs7_4_min8` (default) | 4-byte PKCS#7-like, padded to at least 8 bytes. Compatible with Python xxtea. Not standard 4-byte PKCS#7 |
113
+ | `:pkcs7_8` | Standard **8-byte** PKCS#7, compatible with Python xxteang |
114
+ | `:length_word_suffix` | Zero-pad to a 4-byte boundary, then append one little-endian `uint32` with the original length. Cocos Creator JSC files using this layout can be decrypted. Compatible with Python xxtea 6.2.0 |
115
+ | `:length_word_prefix` | Prepend one little-endian `uint32` with the original length, then zero-pad the data to a 4-byte boundary. Compatible with Python xxtea 6.2.0 |
116
+ | `false` or `:none` | No padding (raw XXTEA) |
117
+
118
+ `XXTEA::PKCS7_4_MIN8`, `XXTEA::PKCS7_8`, `XXTEA::LENGTH_WORD_PREFIX`, and
119
+ `XXTEA::LENGTH_WORD_SUFFIX` are aliases for the symbols.
120
+
121
+ The default `:pkcs7_4_min8` scheme uses pad byte value `4 - (data.bytesize & 3)`
122
+ (range 1–4), plus an extra 4 bytes when the input is shorter than 4 bytes
123
+ to meet XXTEA's 2-word minimum (producing pad values 5–8). Standard 4-byte
124
+ PKCS#7 never uses pad values 5–8. Because padding always adds at least one
125
+ byte, encrypting an 8-byte input produces a 12-byte ciphertext.
126
+
127
+ 8-byte PKCS#7 uses pad byte value `8 - (data.bytesize & 7)` (range 1–8).
128
+ Encrypting an 8-byte input produces a 16-byte ciphertext.
129
+
130
+ The length-word schemes store the original byte length in a little-endian
131
+ `uint32` word, so the plaintext length must fit in 32 bits (`RangeError`
132
+ otherwise). The data is zero-padded to a 4-byte boundary around that word:
133
+ the length word is the last word for `:length_word_suffix` and the first
134
+ word for `:length_word_prefix`. Because of XXTEA's 2-word minimum, empty
135
+ input produces an 8-byte ciphertext in either scheme.
115
136
 
116
137
  ```ruby
117
138
  XXTEA.decrypt_hex(XXTEA.encrypt_hex("", key), key) # => ""
118
139
  XXTEA.decrypt_hex(XXTEA.encrypt_hex(" ", key), key) # => " "
140
+
141
+ XXTEA.encrypt("12345678", key).bytesize # => 12 (:pkcs7_4_min8)
142
+ XXTEA.encrypt("12345678", key, padding: :pkcs7_8).bytesize # => 16 (:pkcs7_8)
143
+ XXTEA.encrypt("12345678", key, padding: :length_word_suffix).bytesize # => 12
144
+ XXTEA.encrypt("12345678", key, padding: :length_word_prefix).bytesize # => 12
119
145
  ```
120
146
 
121
147
  You can disable padding by setting `padding: false`.
@@ -196,7 +222,8 @@ XXTEA.new("k" * 16, rounds: 2**32)
196
222
 
197
223
  ## Compatibility
198
224
 
199
- - Compatible with [Python xxtea](https://github.com/ifduyue/xxtea) (same padding, endianness, and rounds).
225
+ - Compatible with [Python xxtea](https://github.com/ifduyue/xxtea) 6.2.0: all five padding schemes (`:pkcs7_4_min8`, `:pkcs7_8`, `:length_word_prefix`, `:length_word_suffix`, `:none`), endianness, and rounds.
226
+ - `padding: :pkcs7_8` is compatible with [Python xxteang](https://github.com/ifduyue/xxteang).
200
227
  - The `XXTEA.encrypt(data, key)` / `XXTEA.decrypt(data, key)` class methods remain compatible with gem 0.0.1 for valid ciphertext. `XXTEA` is now a class rather than a module, and invalid padding raises `ArgumentError` instead of returning stripped bytes.
201
228
 
202
229
  ## Releasing
data/ext/xxtea/xxtea.c CHANGED
@@ -22,9 +22,13 @@
22
22
  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23
23
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24
24
  *
25
- * Ciphertext is compatible with the Python xxtea package
25
+ * Default ciphertext is compatible with the Python xxtea package
26
26
  * (https://github.com/ifduyue/xxtea): little-endian 32-bit words and
27
- * non-standard 4-byte PKCS#7 padding (pad+4 for inputs shorter than 4 bytes).
27
+ * :pkcs7_4_min8 padding (4-byte PKCS#7-like, pad+4 for inputs shorter than 4 bytes).
28
+ * padding: :pkcs7_8 uses 8-byte PKCS#7, compatible with Python xxteang
29
+ * (https://github.com/ifduyue/xxteang); :length_word_prefix and
30
+ * :length_word_suffix prepend/append a little-endian uint32 length word
31
+ * with zero padding, matching Python xxtea 6.2.0.
28
32
  */
29
33
 
30
34
  #include "ruby.h"
@@ -45,6 +49,11 @@
45
49
 
46
50
  static ID id_padding;
47
51
  static ID id_rounds;
52
+ static ID id_none;
53
+ static ID id_pkcs7_4_min8;
54
+ static ID id_pkcs7_8;
55
+ static ID id_length_word_prefix;
56
+ static ID id_length_word_suffix;
48
57
  static VALUE cXXTEA;
49
58
 
50
59
  typedef struct {
@@ -122,36 +131,84 @@ bytes2longs(const char *in, long inlen, uint32_t *out, int padding)
122
131
  long i, nwords;
123
132
  int pad;
124
133
  const unsigned char *s = (const unsigned char *)in;
134
+ uint32_t *dst = out;
135
+
136
+ if (padding == 2) {
137
+ /* uint32 store, not memcpy(&inlen): 64-bit would copy the high half. */
138
+ out[0] = (uint32_t)inlen;
139
+ dst = out + 1;
140
+ }
125
141
 
126
142
  nwords = inlen >> 2;
127
143
  for (i = 0; i < nwords; i++) {
128
144
  #if XXTEA_LITTLE_ENDIAN
129
- memcpy(&out[i], s + 4 * i, 4);
145
+ memcpy(&dst[i], s + 4 * i, 4);
130
146
  #else
131
147
  const unsigned char *p = s + 4 * i;
132
- out[i] = (uint32_t)p[0] | ((uint32_t)p[1] << 8) |
148
+ dst[i] = (uint32_t)p[0] | ((uint32_t)p[1] << 8) |
133
149
  ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24);
134
150
  #endif
135
151
  }
136
152
 
153
+ i = nwords << 2;
154
+
155
+ if (padding == 1 || padding == 2) {
156
+ /* XXTEA needs two words, so empty input gets an extra zero word. */
157
+ uint32_t w = 0;
158
+ int shift = 0;
159
+ for (; i < inlen; i++, shift += 8) {
160
+ w |= (uint32_t)s[i] << shift;
161
+ }
162
+ if ((inlen & 3) != 0) {
163
+ dst[nwords] = w;
164
+ nwords++;
165
+ }
166
+ if (nwords < 1) {
167
+ dst[nwords++] = 0;
168
+ }
169
+ if (padding == 1) {
170
+ dst[nwords] = (uint32_t)inlen;
171
+ }
172
+ return;
173
+ }
174
+
175
+ if (padding == 8) {
176
+ /*
177
+ * 8-byte PKCS#7 (xxteang): pad = 8 - (len & 7), range 1-8.
178
+ * Completes the partial word, then adds a whole extra pad word
179
+ * unless the length is 4 mod 8.
180
+ */
181
+ uint32_t w = 0;
182
+ int r = (int)(inlen & 3);
183
+ int shift = 0;
184
+ uint32_t pw;
185
+ for (; i < inlen; i++, shift += 8) {
186
+ w |= (uint32_t)s[i] << shift;
187
+ }
188
+ pad = 8 - (int)(inlen & 7);
189
+ pw = (uint32_t)pad * 0x01010101u;
190
+ w |= pw & (~0u << (8 * r));
191
+ out[nwords] = w;
192
+ if ((inlen & 4) == 0) {
193
+ out[nwords + 1] = pw;
194
+ }
195
+ return;
196
+ }
197
+
137
198
  /*
138
- * Assemble the final partial word (0-3 leftover data bytes plus
139
- * padding) in a local and store it with a single write, so every
140
- * output byte is written exactly once and the caller does not need
141
- * to zero the buffer first. Inputs shorter than 4 bytes are padded
142
- * to two words, which also guarantees the minimum XXTEA block size.
199
+ * :pkcs7_4_min8 (default): 4-byte PKCS#7-like, but inputs shorter
200
+ * than 4 bytes are padded to two words (pad values 5-8) for XXTEA's
201
+ * 2-word minimum. Not standard PKCS#7.
143
202
  */
144
- i = nwords << 2;
145
- if (padding || (inlen & 3) != 0) {
203
+ if (padding == 4 || (inlen & 3) != 0) {
146
204
  uint32_t w = 0;
147
205
  int r = (int)(inlen & 3);
148
206
  int shift = 0;
149
207
  for (; i < inlen; i++, shift += 8) {
150
208
  w |= (uint32_t)s[i] << shift;
151
209
  }
152
- if (padding) {
210
+ if (padding == 4) {
153
211
  pad = 4 - r;
154
- /* Ensure XXTEA always has at least two 32-bit words. */
155
212
  if (inlen < 4) {
156
213
  pad += 4;
157
214
  }
@@ -190,8 +247,41 @@ longs2bytes(const uint32_t *in, long inlen, char *out, int padding)
190
247
 
191
248
  outlen = inlen * 4;
192
249
 
193
- /* 4-byte PKCS#7-style unpadding. */
194
- if (padding) {
250
+ if (padding == 1 || padding == 2) {
251
+ int prefix = padding == 2;
252
+ long n = outlen - 4;
253
+ long leftover, pad_from, pad_to;
254
+ uint32_t m32;
255
+ if (prefix) {
256
+ m32 = (uint32_t)s[0] | ((uint32_t)s[1] << 8) |
257
+ ((uint32_t)s[2] << 16) | ((uint32_t)s[3] << 24);
258
+ }
259
+ else {
260
+ m32 = (uint32_t)s[n] | ((uint32_t)s[n + 1] << 8) |
261
+ ((uint32_t)s[n + 2] << 16) | ((uint32_t)s[n + 3] << 24);
262
+ }
263
+ if ((size_t)m32 > (size_t)n) {
264
+ return -1;
265
+ }
266
+ leftover = n - (long)m32;
267
+ pad_from = prefix ? 4 + (long)m32 : (long)m32;
268
+ pad_to = prefix ? outlen : n;
269
+ /* leftover 4 is the empty 2-word case; otherwise 0-3 zero-pad bytes. */
270
+ if (leftover > 3 && !(m32 == 0 && leftover == 4)) {
271
+ return -1;
272
+ }
273
+ for (i = pad_from; i < pad_to; i++) {
274
+ if (s[i] != 0) {
275
+ return -1;
276
+ }
277
+ }
278
+ if (prefix && m32 != 0) {
279
+ memmove(s, s + 4, (size_t)m32);
280
+ }
281
+ outlen = (long)m32;
282
+ }
283
+ /* PKCS#7-style unpadding (4-byte or 8-byte; pad values 1-8). */
284
+ else if (padding) {
195
285
  pad = s[outlen - 1];
196
286
  outlen -= pad;
197
287
 
@@ -266,13 +356,56 @@ parse_rounds(VALUE obj)
266
356
  return NUM2UINT(obj);
267
357
  }
268
358
 
359
+ /* 0 = none, 1 = :length_word_suffix, 2 = :length_word_prefix,
360
+ * 4 = :pkcs7_4_min8 (default), 8 = :pkcs7_8. */
361
+ static int
362
+ parse_padding(VALUE obj)
363
+ {
364
+ ID id;
365
+
366
+ if (obj == Qfalse) {
367
+ return 0;
368
+ }
369
+ if (obj == Qtrue) {
370
+ return 4;
371
+ }
372
+ if (RB_TYPE_P(obj, T_STRING)) {
373
+ obj = rb_str_intern(obj);
374
+ }
375
+ if (!SYMBOL_P(obj)) {
376
+ rb_raise(rb_eTypeError,
377
+ "padding must be true, false, :none, :pkcs7_4_min8, "
378
+ ":pkcs7_8, :length_word_prefix, or :length_word_suffix");
379
+ }
380
+ id = SYM2ID(obj);
381
+ if (id == id_none) {
382
+ return 0;
383
+ }
384
+ if (id == id_length_word_suffix) {
385
+ return 1;
386
+ }
387
+ if (id == id_length_word_prefix) {
388
+ return 2;
389
+ }
390
+ if (id == id_pkcs7_4_min8) {
391
+ return 4;
392
+ }
393
+ if (id == id_pkcs7_8) {
394
+ return 8;
395
+ }
396
+ rb_raise(rb_eArgError,
397
+ "unknown padding %+"PRIsVALUE" (expected :none, :pkcs7_4_min8, "
398
+ ":pkcs7_8, :length_word_prefix, or :length_word_suffix)",
399
+ obj);
400
+ }
401
+
269
402
  static void
270
403
  parse_opts(VALUE opts, int *padding, unsigned int *rounds)
271
404
  {
272
405
  ID kwids[2];
273
406
  VALUE kwvals[2];
274
407
 
275
- *padding = 1;
408
+ *padding = 4;
276
409
  *rounds = 0;
277
410
  if (NIL_P(opts)) {
278
411
  return;
@@ -283,7 +416,7 @@ parse_opts(VALUE opts, int *padding, unsigned int *rounds)
283
416
  rb_get_kwargs(opts, kwids, 0, 2, kwvals);
284
417
 
285
418
  if (kwvals[0] != Qundef) {
286
- *padding = RTEST(kwvals[0]);
419
+ *padding = parse_padding(kwvals[0]);
287
420
  }
288
421
  if (kwvals[1] != Qundef) {
289
422
  *rounds = parse_rounds(kwvals[1]);
@@ -315,7 +448,33 @@ encrypt_impl(VALUE data, const char key[16], int padding, unsigned int rounds)
315
448
  "Data length must be a multiple of 4 bytes and must not be less than 8 bytes");
316
449
  }
317
450
 
318
- alen = data_len < 4 ? 2 : (data_len >> 2) + padding;
451
+ if (padding == 8) {
452
+ if (data_len > LONG_MAX - 8) {
453
+ rb_raise(rb_eRangeError, "data too large");
454
+ }
455
+ alen = ((data_len & ~7L) + 8) >> 2;
456
+ }
457
+ else if (padding == 4) {
458
+ alen = data_len < 4 ? 2 : (data_len >> 2) + 1;
459
+ }
460
+ else if (padding == 1 || padding == 2) {
461
+ /* The length word is a uint32, so the plaintext length must fit. */
462
+ #if LONG_MAX > 2147483647L
463
+ if (data_len > 0xFFFFFFFFL) {
464
+ rb_raise(rb_eRangeError, "data too large");
465
+ }
466
+ #endif
467
+ if (data_len > LONG_MAX - 4) {
468
+ rb_raise(rb_eRangeError, "data too large");
469
+ }
470
+ alen = (data_len >> 2) + ((data_len & 3) != 0 ? 1 : 0) + 1;
471
+ if (alen < 2) {
472
+ alen = 2;
473
+ }
474
+ }
475
+ else {
476
+ alen = data_len >> 2;
477
+ }
319
478
  if (alen > INT_MAX || alen > LONG_MAX / 4) {
320
479
  rb_raise(rb_eRangeError, "data too large");
321
480
  }
@@ -501,7 +660,7 @@ xxtea_alloc(VALUE klass)
501
660
  xxtea_cipher_t *cipher;
502
661
  VALUE obj = TypedData_Make_Struct(klass, xxtea_cipher_t, &xxtea_cipher_type, cipher);
503
662
  memset(cipher, 0, sizeof(*cipher));
504
- cipher->padding = 1;
663
+ cipher->padding = 4;
505
664
  cipher->rounds = 0;
506
665
  return obj;
507
666
  }
@@ -556,7 +715,11 @@ xxtea_inspect(VALUE self)
556
715
  xxtea_cipher_t *cipher = xxtea_get(self);
557
716
  return rb_sprintf("#<%s:%p padding=%s rounds=%u>",
558
717
  rb_obj_classname(self), (void *)self,
559
- cipher->padding ? "true" : "false", cipher->rounds);
718
+ cipher->padding == 0 ? "false" :
719
+ cipher->padding == 1 ? "length_word_suffix" :
720
+ cipher->padding == 2 ? "length_word_prefix" :
721
+ cipher->padding == 4 ? "pkcs7_4_min8" : "pkcs7_8",
722
+ cipher->rounds);
560
723
  }
561
724
 
562
725
  void
@@ -566,6 +729,11 @@ Init_xxtea(void)
566
729
 
567
730
  id_padding = rb_intern("padding");
568
731
  id_rounds = rb_intern("rounds");
732
+ id_none = rb_intern("none");
733
+ id_pkcs7_4_min8 = rb_intern("pkcs7_4_min8");
734
+ id_pkcs7_8 = rb_intern("pkcs7_8");
735
+ id_length_word_prefix = rb_intern("length_word_prefix");
736
+ id_length_word_suffix = rb_intern("length_word_suffix");
569
737
 
570
738
  cXXTEA = rb_define_class("XXTEA", rb_cObject);
571
739
  rb_define_alloc_func(cXXTEA, xxtea_alloc);
@@ -581,4 +749,9 @@ Init_xxtea(void)
581
749
  rb_define_method(cXXTEA, "encrypt_hex", xxtea_encrypt_hex, 1);
582
750
  rb_define_method(cXXTEA, "decrypt_hex", xxtea_decrypt_hex, 1);
583
751
  rb_define_method(cXXTEA, "inspect", xxtea_inspect, 0);
752
+
753
+ rb_define_const(cXXTEA, "PKCS7_4_MIN8", ID2SYM(id_pkcs7_4_min8));
754
+ rb_define_const(cXXTEA, "PKCS7_8", ID2SYM(id_pkcs7_8));
755
+ rb_define_const(cXXTEA, "LENGTH_WORD_PREFIX", ID2SYM(id_length_word_prefix));
756
+ rb_define_const(cXXTEA, "LENGTH_WORD_SUFFIX", ID2SYM(id_length_word_suffix));
584
757
  }
data/lib/xxtea/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class XXTEA
4
- VERSION = "1.0.0"
4
+ VERSION = "1.2.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: xxtea
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yue Du
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-08-20 00:00:00.000000000 Z
11
+ date: 2026-08-24 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |
14
14
  XXTEA implemented as a Ruby C extension. Ciphertext is compatible with the