xxtea 0.0.1 → 1.0.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
- SHA1:
3
- metadata.gz: 40a67d849e79434013e6648f21ddc02e7e6e93cf
4
- data.tar.gz: 480a724d92d0ec28c64a264e7ababd1daf0a23ad
2
+ SHA256:
3
+ metadata.gz: b8eca82a1c887086264d8807bf55a1980dc28f35f956c02a5bf66ebef33ab8f6
4
+ data.tar.gz: d6707b17d68788035c7d8f2d90210f15408ba3a1d7a0e3c4dc0636d2b519dd10
5
5
  SHA512:
6
- metadata.gz: f9344b00aae36904b61f0facd0900a734c462e036335021dce7494f083f3fe0949a3e9102ba3bfd5dd8cc0094bd7cb22a46995dac76f9a28f8bef8eddf1e92a3
7
- data.tar.gz: 844b8fea4b61f4a71863952af8f0117f0c1eb52da0335ecc1fb8098470cc810619cc65c8c5d0f162dad4ec5ae674c5fd6cc6d28f9961d20331d68cec53025644
6
+ metadata.gz: e829728fcc9f4296e74f9bee45568aab867679971c866ca992146a715a62444fea17602bf21322041c82bed745d7ba7a7f10905e7df4ecf194547b41164ac686
7
+ data.tar.gz: 3a4224238e8e3d062dfc83671981834e1ab571c2e293bb90095db8ad46de86921c94d87972dcd0f3180ab6c562d9913e1dd1f2f4692dd07815752ffb12b8349e
data/CHANGELOG.md ADDED
@@ -0,0 +1,16 @@
1
+ # Changelog
2
+
3
+ ## 1.0.0
4
+
5
+ - Rewrite as a Ruby C extension compatible with Python [xxtea](https://github.com/ifduyue/xxtea) 5.3.3.
6
+ - Keep `XXTEA.encrypt` / `XXTEA.decrypt` (16-byte key, non-standard 4-byte PKCS#7 padding).
7
+ - Add `padding:` and `rounds:` keyword arguments.
8
+ - Add `encrypt_hex` / `decrypt_hex`.
9
+ - Add `XXTEA.new(key, padding: true, rounds: 0)` cipher objects.
10
+ - Validate PKCS#7 padding on decrypt; reject invalid padding with `ArgumentError`.
11
+ - Encode ciphertext as little-endian bytes on all architectures.
12
+ - Require Ruby >= 3.1.
13
+
14
+ ## 0.0.1 — 2015-03-08
15
+
16
+ - Initial release.
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014-2026, Yue Du
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without modification,
5
+ are permitted provided that the following conditions are met:
6
+
7
+ * Redistributions of source code must retain the above copyright notice,
8
+ this list of conditions and the following disclaimer.
9
+ * Redistributions in binary form must reproduce the above copyright notice,
10
+ this list of conditions and the following disclaimer in the documentation
11
+ and/or other materials provided with the distribution.
12
+
13
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
17
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
20
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
21
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
22
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,216 @@
1
+ # xxtea
2
+
3
+ [![CI](https://github.com/ifduyue/ruby-xxtea/actions/workflows/test.yml/badge.svg)](https://github.com/ifduyue/ruby-xxtea/actions/workflows/test.yml)
4
+ [![Gem Version](https://badge.fury.io/rb/xxtea.svg)](https://rubygems.org/gems/xxtea)
5
+
6
+ [XXTEA](https://en.wikipedia.org/wiki/XXTEA) implemented as a Ruby C extension, licensed under 2-clause BSD.
7
+
8
+ Ciphertext is compatible with the [Python xxtea](https://github.com/ifduyue/xxtea) package.
9
+
10
+ The XXTEA algorithm takes a 128-bit key and operates on an array of 32-bit
11
+ integers (at least 2 integers), but it doesn't define the conversions between
12
+ bytes and array. Due to this reason, many XXTEA implementations out there are
13
+ not compatible with each other.
14
+
15
+ In this implementation, the conversions between bytes and array are taken care
16
+ of by `longs2bytes` and `bytes2longs`. A non-standard 4-byte block
17
+ [PKCS#7](https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7) padding is
18
+ used to make sure that the input bytes are padded to a multiple of 4-byte (the
19
+ size of a 32-bit integer) and at least 8-byte long (the size of two 32-bit
20
+ integers, which is required by the XXTEA algorithm). As a result of these
21
+ measures, you can encrypt not only texts, but also any binary bytes of any
22
+ length.
23
+
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).
30
+
31
+ ## Installation
32
+
33
+ A C compiler is required. Then:
34
+
35
+ ```
36
+ $ gem install xxtea
37
+ ```
38
+
39
+ Or add to your Gemfile:
40
+
41
+ ```ruby
42
+ gem "xxtea"
43
+ ```
44
+
45
+ ## Usage
46
+
47
+ This gem provides four class methods: `encrypt`, `decrypt`, `encrypt_hex`, and
48
+ `decrypt_hex`, plus an `XXTEA` type for reusable cipher objects.
49
+
50
+ ```ruby
51
+ require "xxtea"
52
+ require "securerandom"
53
+
54
+ key = SecureRandom.random_bytes(16) # Key must be a 16-byte string.
55
+ s = "xxtea is good"
56
+
57
+ enc = XXTEA.encrypt(s, key)
58
+ dec = XXTEA.decrypt(enc, key)
59
+ s == dec # => true
60
+
61
+ hexenc = XXTEA.encrypt_hex(s, key)
62
+ s == XXTEA.decrypt_hex(hexenc, key) # => true
63
+
64
+ enc.unpack1("H*") == hexenc # => true
65
+ ```
66
+
67
+ ## XXTEA Type
68
+
69
+ The `XXTEA` type holds a 16-byte key, rounds, and padding setting, so you can
70
+ encrypt and decrypt multiple times without passing them each call.
71
+
72
+ ```ruby
73
+ cipher = XXTEA.new(key, padding: false, rounds: 128)
74
+ cipher
75
+ # => #<XXTEA:0x... padding=false rounds=128>
76
+
77
+ enc = cipher.encrypt("12345678")
78
+ cipher.decrypt(enc) # => "12345678"
79
+
80
+ hexenc = cipher.encrypt_hex("12345678")
81
+ cipher.decrypt_hex(hexenc) # => "12345678"
82
+ ```
83
+
84
+ `rounds` defaults to `0` (auto), `padding` defaults to `true`.
85
+ `rounds: 0` means `6 + 52 / n`, where n is the number of 32-bit words in the data.
86
+ They are stored on the object and used by every `encrypt`, `decrypt`,
87
+ `encrypt_hex`, and `decrypt_hex` call:
88
+
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
93
+ c = XXTEA.new(key, padding: false, rounds: 42)
94
+ ```
95
+
96
+ `encrypt_hex` and `decrypt_hex` operate on ciphertext in a hexadecimal
97
+ representation. They are exactly equivalent to:
98
+
99
+ ```ruby
100
+ hexenc = XXTEA.encrypt(s, key).unpack1("H*")
101
+ s == XXTEA.decrypt([hexenc].pack("H*"), key) # => true
102
+ ```
103
+
104
+ ## Padding
105
+
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).
110
+
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.
115
+
116
+ ```ruby
117
+ XXTEA.decrypt_hex(XXTEA.encrypt_hex("", key), key) # => ""
118
+ XXTEA.decrypt_hex(XXTEA.encrypt_hex(" ", key), key) # => " "
119
+ ```
120
+
121
+ You can disable padding by setting `padding: false`.
122
+ In this case data will not be padded, so data length must be a multiple of 4
123
+ bytes and must not be less than 8 bytes. Otherwise `ArgumentError` will be
124
+ raised:
125
+
126
+ ```ruby
127
+ XXTEA.encrypt_hex("", key, padding: false)
128
+ # ArgumentError: Data length must be a multiple of 4 bytes and must not be less than 8 bytes
129
+
130
+ XXTEA.encrypt_hex("xxtea is good", key, padding: false)
131
+ # ArgumentError: Data length must be a multiple of 4 bytes and must not be less than 8 bytes
132
+
133
+ XXTEA.decrypt_hex(XXTEA.encrypt_hex("12345678", key, padding: false), key, padding: false)
134
+ # => "12345678"
135
+ ```
136
+
137
+ ## Rounds
138
+
139
+ By default xxtea manipulates the input data for `6 + 52 / n` rounds,
140
+ where n denotes how many 32-bit integers the input data can fit in.
141
+ We can change this by setting the `rounds` parameter.
142
+
143
+ Do note that the more rounds it is, the more time will be consumed.
144
+ `rounds` must fit in a 32-bit unsigned integer; values exceeding
145
+ `2**32 - 1` raise `RangeError`.
146
+
147
+ ```ruby
148
+ data = "0123456789"
149
+ key = "abcdefghijklmnop"
150
+ XXTEA.encrypt_hex(data, key)
151
+ # => "5b80b08a5d1923e4cd992dd5"
152
+ 6 + 52 / ((data.bytesize + 3) / 4) # 23
153
+ XXTEA.encrypt_hex(data, key, rounds: 23)
154
+ # => "5b80b08a5d1923e4cd992dd5"
155
+ XXTEA.encrypt_hex(data, key, rounds: 1024)
156
+ # => "1577bbf28c43ced93bd50720"
157
+ ```
158
+
159
+ ## Catching Exceptions
160
+
161
+ When calling these methods, an `ArgumentError`, `TypeError`, or `RangeError`
162
+ may be raised.
163
+
164
+ ```ruby
165
+ begin
166
+ XXTEA.decrypt("", key: "")
167
+ rescue => e
168
+ puts "#{e.class} : #{e.message}"
169
+ end
170
+ # ArgumentError : Need a 16-byte key.
171
+
172
+ XXTEA.decrypt("", " " * 16)
173
+ # ArgumentError : Data length must be a multiple of 4 bytes and must not be less than 8 bytes
174
+
175
+ XXTEA.decrypt(" " * 8, " " * 16)
176
+ # ArgumentError : Invalid data, illegal padding. Could be using a wrong key.
177
+
178
+ XXTEA.decrypt_hex(" " * 8, " " * 16)
179
+ # ArgumentError : Non-hexadecimal digit found
180
+
181
+ XXTEA.decrypt_hex("abc", " " * 16)
182
+ # ArgumentError : Odd-length string
183
+
184
+ XXTEA.decrypt_hex("abcd", " " * 16)
185
+ # ArgumentError : Data length must be a multiple of 4 bytes and must not be less than 8 bytes
186
+
187
+ XXTEA.encrypt("x", "k" * 16, rounds: 2**32)
188
+ # RangeError : rounds value too large
189
+
190
+ XXTEA.new("short")
191
+ # ArgumentError : Need a 16-byte key.
192
+
193
+ XXTEA.new("k" * 16, rounds: 2**32)
194
+ # RangeError : rounds value too large
195
+ ```
196
+
197
+ ## Compatibility
198
+
199
+ - Compatible with [Python xxtea](https://github.com/ifduyue/xxtea) (same padding, endianness, and rounds).
200
+ - 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
+
202
+ ## Releasing
203
+
204
+ Push a `v*` tag (for example `v1.0.0`). [`.github/workflows/build.yml`](.github/workflows/build.yml) builds the gem, publishes it to RubyGems.org via [Trusted Publishing](https://guides.rubygems.org/trusted-publishing/), and creates a GitHub Release.
205
+
206
+ The trusted publisher on RubyGems.org must match:
207
+
208
+ - Repository owner: `ifduyue`
209
+ - Repository name: `ruby-xxtea`
210
+ - Workflow filename: `build.yml`
211
+ - Environment: `release`
212
+
213
+ ## License
214
+
215
+ BSD-2-Clause. See [LICENSE](LICENSE).
216
+
data/ext/xxtea/extconf.rb CHANGED
@@ -1,5 +1,5 @@
1
- require 'mkmf'
1
+ # frozen_string_literal: true
2
2
 
3
- $CFLAGS += ' -g -O3 -Wall '
3
+ require "mkmf"
4
4
 
5
- create_makefile('xxtea/xxtea')
5
+ create_makefile("xxtea/xxtea")
data/ext/xxtea/xxtea.c CHANGED
@@ -1,19 +1,84 @@
1
-
1
+ /*
2
+ * Copyright (c) 2014-2026, Yue Du
3
+ * All rights reserved.
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without modification,
6
+ * are permitted provided that the following conditions are met:
7
+ *
8
+ * * Redistributions of source code must retain the above copyright notice,
9
+ * this list of conditions and the following disclaimer.
10
+ * * Redistributions in binary form must reproduce the above copyright notice,
11
+ * this list of conditions and the following disclaimer in the documentation
12
+ * and/or other materials provided with the distribution.
13
+ *
14
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
18
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24
+ *
25
+ * Ciphertext is compatible with the Python xxtea package
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).
28
+ */
29
+
30
+ #include "ruby.h"
31
+ #include "ruby/encoding.h"
32
+
33
+ #include <limits.h>
2
34
  #include <stdint.h>
3
- #include <stdlib.h>
4
- #include <ruby.h>
5
-
6
-
7
- #define DELTA 0x9e3779b9
8
- #define MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z)))
35
+ #include <string.h>
36
+
37
+ #define DELTA 0x9e3779b9U
38
+ #define MX (((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4)) ^ ((sum ^ y) + (key[(p & 3) ^ e] ^ z)))
39
+
40
+ #if defined(WORDS_BIGENDIAN)
41
+ # define XXTEA_LITTLE_ENDIAN 0
42
+ #else
43
+ # define XXTEA_LITTLE_ENDIAN 1
44
+ #endif
45
+
46
+ static ID id_padding;
47
+ static ID id_rounds;
48
+ static VALUE cXXTEA;
49
+
50
+ typedef struct {
51
+ char key[16];
52
+ unsigned int rounds;
53
+ int padding;
54
+ } xxtea_cipher_t;
55
+
56
+ static const rb_data_type_t xxtea_cipher_type = {
57
+ .wrap_struct_name = "XXTEA",
58
+ .function = {
59
+ .dmark = NULL,
60
+ .dfree = RUBY_DEFAULT_FREE,
61
+ .dsize = NULL,
62
+ },
63
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
64
+ };
65
+
66
+ static inline xxtea_cipher_t *
67
+ xxtea_get(VALUE obj)
68
+ {
69
+ xxtea_cipher_t *cipher;
70
+ TypedData_Get_Struct(obj, xxtea_cipher_t, &xxtea_cipher_type, cipher);
71
+ return cipher;
72
+ }
9
73
 
10
- static void btea(uint32_t *v, int n, uint32_t const key[4])
74
+ static void
75
+ btea(uint32_t *v, int n, uint32_t const key[4], unsigned int rounds)
11
76
  {
12
77
  uint32_t y, z, sum;
13
- unsigned p, rounds, e;
78
+ unsigned p, e;
14
79
 
15
80
  if (n > 1) { /* Coding Part */
16
- rounds = 6 + 52 / n;
81
+ rounds = rounds == 0 ? (unsigned)(6 + 52 / n) : rounds;
17
82
  sum = 0;
18
83
  z = v[n - 1];
19
84
 
@@ -21,26 +86,25 @@ static void btea(uint32_t *v, int n, uint32_t const key[4])
21
86
  sum += DELTA;
22
87
  e = (sum >> 2) & 3;
23
88
 
24
- for (p = 0; p < n - 1; p++) {
89
+ for (p = 0; p < (unsigned)(n - 1); p++) {
25
90
  y = v[p + 1];
26
91
  z = v[p] += MX;
27
92
  }
28
93
 
29
94
  y = v[0];
30
95
  z = v[n - 1] += MX;
31
- }
32
- while (--rounds);
96
+ } while (--rounds);
33
97
  }
34
98
  else if (n < -1) { /* Decoding Part */
35
99
  n = -n;
36
- rounds = 6 + 52 / n;
37
- sum = rounds * DELTA;
100
+ rounds = rounds == 0 ? (unsigned)(6 + 52 / n) : rounds;
101
+ sum = (uint32_t)(rounds * DELTA);
38
102
  y = v[0];
39
103
 
40
104
  do {
41
105
  e = (sum >> 2) & 3;
42
106
 
43
- for (p = n - 1; p > 0; p--) {
107
+ for (p = (unsigned)(n - 1); p > 0; p--) {
44
108
  z = v[p - 1];
45
109
  y = v[p] -= MX;
46
110
  }
@@ -48,182 +112,473 @@ static void btea(uint32_t *v, int n, uint32_t const key[4])
48
112
  z = v[n - 1];
49
113
  y = v[0] -= MX;
50
114
  sum -= DELTA;
51
- }
52
- while (--rounds);
115
+ } while (--rounds);
53
116
  }
54
117
  }
55
118
 
56
- static int bytes2longs(const char *in, int inlen, uint32_t *out, int padding)
119
+ static void
120
+ bytes2longs(const char *in, long inlen, uint32_t *out, int padding)
57
121
  {
58
- int i, pad;
59
- const unsigned char *s;
60
-
61
- s = (const unsigned char *)in;
62
-
63
- /* (i & 3) << 3 -> [0, 8, 16, 24] */
64
- for (i = 0; i < inlen; i++) {
65
- out[i >> 2] |= s[i] << ((i & 3) << 3);
122
+ long i, nwords;
123
+ int pad;
124
+ const unsigned char *s = (const unsigned char *)in;
125
+
126
+ nwords = inlen >> 2;
127
+ for (i = 0; i < nwords; i++) {
128
+ #if XXTEA_LITTLE_ENDIAN
129
+ memcpy(&out[i], s + 4 * i, 4);
130
+ #else
131
+ const unsigned char *p = s + 4 * i;
132
+ out[i] = (uint32_t)p[0] | ((uint32_t)p[1] << 8) |
133
+ ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24);
134
+ #endif
66
135
  }
67
136
 
68
- /* PKCS#7 padding */
69
- if (padding) {
70
- pad = 4 - (inlen & 3);
71
- /* make sure lenght of out >= 2 */
72
- pad = (inlen < 4) ? pad + 4 : pad;
73
- for (i = inlen; i < inlen + pad; i++) {
74
- out[i >> 2] |= pad << ((i & 3) << 3);
137
+ /*
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.
143
+ */
144
+ i = nwords << 2;
145
+ if (padding || (inlen & 3) != 0) {
146
+ uint32_t w = 0;
147
+ int r = (int)(inlen & 3);
148
+ int shift = 0;
149
+ for (; i < inlen; i++, shift += 8) {
150
+ w |= (uint32_t)s[i] << shift;
151
+ }
152
+ if (padding) {
153
+ pad = 4 - r;
154
+ /* Ensure XXTEA always has at least two 32-bit words. */
155
+ if (inlen < 4) {
156
+ pad += 4;
157
+ }
158
+ w |= (uint32_t)pad * 0x01010101u & (~0u << (8 * r));
159
+ if (inlen < 4) {
160
+ out[nwords + 1] = (uint32_t)pad * 0x01010101u;
161
+ }
75
162
  }
163
+ out[nwords] = w;
76
164
  }
77
-
78
- /* Divided by 4, and then rounded up (ceil) to an integer.
79
- * Which is the number of how many longs we've got.
80
- */
81
- return ((i - 1) >> 2) + 1;
82
165
  }
83
166
 
84
- static int longs2bytes(uint32_t *in, int inlen, char *out, int padding)
167
+ static long
168
+ longs2bytes(const uint32_t *in, long inlen, char *out, int padding)
85
169
  {
86
- int i, pad;
87
- unsigned char *s;
88
-
89
- s = (unsigned char *)out;
90
-
170
+ long i, outlen;
171
+ int pad;
172
+ unsigned char *s = (unsigned char *)out;
173
+
174
+ #if XXTEA_LITTLE_ENDIAN
175
+ /* Callers always pass the same buffer; words are already LE bytes. */
176
+ (void)in;
177
+ #else
178
+ /*
179
+ * Big endian: write each word as little-endian bytes. Snapshot the
180
+ * whole word first because in and out alias.
181
+ */
91
182
  for (i = 0; i < inlen; i++) {
92
- s[4 * i] = in[i] & 0xFF;
93
- s[4 * i + 1] = (in[i] >> 8) & 0xFF;
94
- s[4 * i + 2] = (in[i] >> 16) & 0xFF;
95
- s[4 * i + 3] = (in[i] >> 24) & 0xFF;
183
+ uint32_t word = in[i];
184
+ s[4 * i] = (unsigned char)(word & 0xFF);
185
+ s[4 * i + 1] = (unsigned char)((word >> 8) & 0xFF);
186
+ s[4 * i + 2] = (unsigned char)((word >> 16) & 0xFF);
187
+ s[4 * i + 3] = (unsigned char)((word >> 24) & 0xFF);
96
188
  }
189
+ #endif
97
190
 
98
- i *= 4;
191
+ outlen = inlen * 4;
99
192
 
100
- /* PKCS#7 unpadding */
193
+ /* 4-byte PKCS#7-style unpadding. */
101
194
  if (padding) {
102
- pad = s[i - 1];
103
- i -= pad;
104
- }
195
+ pad = s[outlen - 1];
196
+ outlen -= pad;
197
+
198
+ if (pad < 1 || pad > 8) {
199
+ return -1;
200
+ }
105
201
 
106
- s[i] = '\0';
202
+ if (outlen < 0) {
203
+ return -2;
204
+ }
205
+
206
+ for (i = outlen; i < inlen * 4; i++) {
207
+ if (s[i] != pad) {
208
+ return -3;
209
+ }
210
+ }
211
+ }
107
212
 
108
- /* How many bytes we've got */
109
- return i;
213
+ s[outlen] = '\0';
214
+ return outlen;
110
215
  }
111
216
 
112
- VALUE xxtea_encrypt(VALUE mod, VALUE data, VALUE key)
217
+ typedef struct {
218
+ const char *data;
219
+ long data_len;
220
+ char key[16];
221
+ uint32_t *buf;
222
+ int n;
223
+ int padding;
224
+ unsigned int rounds;
225
+ long rc;
226
+ } crypt_job;
227
+
228
+ static void *
229
+ encrypt_nogvl(void *ptr)
113
230
  {
114
- int alen, dlen, klen;
115
- uint32_t *d, k[4];
116
- VALUE retval;
117
- char *retbuf;
231
+ crypt_job *j = (crypt_job *)ptr;
232
+ uint32_t k[4];
233
+
234
+ bytes2longs(j->data, j->data_len, j->buf, j->padding);
235
+ bytes2longs(j->key, 16, k, 0);
236
+ btea(j->buf, j->n, k, j->rounds);
237
+ #if !XXTEA_LITTLE_ENDIAN
238
+ longs2bytes(j->buf, j->n, (char *)j->buf, 0);
239
+ #endif
240
+ return NULL;
241
+ }
118
242
 
119
- d = NULL;
120
- k[0] = k[1] = k[2] = k[3] = 0;
243
+ static void *
244
+ decrypt_nogvl(void *ptr)
245
+ {
246
+ crypt_job *j = (crypt_job *)ptr;
247
+ uint32_t k[4];
248
+
249
+ bytes2longs(j->data, j->data_len, j->buf, 0);
250
+ bytes2longs(j->key, 16, k, 0);
251
+ btea(j->buf, -j->n, k, j->rounds);
252
+ j->rc = longs2bytes(j->buf, j->n, (char *)j->buf, j->padding);
253
+ return NULL;
254
+ }
121
255
 
122
- Check_Type(data, T_STRING);
123
- Check_Type(key, T_STRING);
256
+ static unsigned int
257
+ parse_rounds(VALUE obj)
258
+ {
259
+ if (!RB_INTEGER_TYPE_P(obj)) {
260
+ rb_raise(rb_eTypeError, "rounds must be an Integer");
261
+ }
262
+ if (RTEST(rb_funcall(obj, rb_intern("<"), 1, INT2FIX(0))) ||
263
+ RTEST(rb_funcall(obj, rb_intern(">"), 1, UINT2NUM(UINT_MAX)))) {
264
+ rb_raise(rb_eRangeError, "rounds value too large");
265
+ }
266
+ return NUM2UINT(obj);
267
+ }
124
268
 
125
- dlen = RSTRING_LEN(data);
126
- klen = RSTRING_LEN(key);
269
+ static void
270
+ parse_opts(VALUE opts, int *padding, unsigned int *rounds)
271
+ {
272
+ ID kwids[2];
273
+ VALUE kwvals[2];
127
274
 
128
- if (klen != 16) {
129
- rb_raise(rb_eArgError, "Need a 16-byte key.");
130
- return Qnil;
275
+ *padding = 1;
276
+ *rounds = 0;
277
+ if (NIL_P(opts)) {
278
+ return;
131
279
  }
132
280
 
133
- alen = dlen < 4 ? 2 : (dlen >> 2) + 1;
134
- d = (uint32_t *)calloc(alen, sizeof(uint32_t));
281
+ kwids[0] = id_padding;
282
+ kwids[1] = id_rounds;
283
+ rb_get_kwargs(opts, kwids, 0, 2, kwvals);
284
+
285
+ if (kwvals[0] != Qundef) {
286
+ *padding = RTEST(kwvals[0]);
287
+ }
288
+ if (kwvals[1] != Qundef) {
289
+ *rounds = parse_rounds(kwvals[1]);
290
+ }
291
+ }
135
292
 
136
- if (!d) {
137
- rb_raise(rb_eNoMemError, "calloc failed.");
138
- return Qnil;
293
+ static void
294
+ require_key(VALUE key, char out[16])
295
+ {
296
+ StringValue(key);
297
+ if (RSTRING_LEN(key) != 16) {
298
+ rb_raise(rb_eArgError, "Need a 16-byte key.");
139
299
  }
300
+ memcpy(out, RSTRING_PTR(key), 16);
301
+ }
140
302
 
141
- bytes2longs(StringValuePtr(data), dlen, d, 1);
142
- bytes2longs(StringValuePtr(key), klen, k, 0);
143
- btea(d, alen, k);
303
+ static VALUE
304
+ encrypt_impl(VALUE data, const char key[16], int padding, unsigned int rounds)
305
+ {
306
+ crypt_job job;
307
+ VALUE retval;
308
+ long data_len, alen;
309
+
310
+ StringValue(data);
311
+ data_len = RSTRING_LEN(data);
144
312
 
145
- retval = rb_str_new(NULL, (alen << 2));
313
+ if (!padding && (data_len < 8 || (data_len & 3) != 0)) {
314
+ rb_raise(rb_eArgError,
315
+ "Data length must be a multiple of 4 bytes and must not be less than 8 bytes");
316
+ }
146
317
 
147
- if (!retval) {
148
- free(d);
149
- return Qnil;
318
+ alen = data_len < 4 ? 2 : (data_len >> 2) + padding;
319
+ if (alen > INT_MAX || alen > LONG_MAX / 4) {
320
+ rb_raise(rb_eRangeError, "data too large");
150
321
  }
151
322
 
152
- retbuf = RSTRING_PTR(retval);
153
- longs2bytes(d, alen, retbuf, 0);
323
+ retval = rb_str_new(NULL, alen << 2);
324
+ rb_enc_associate(retval, rb_ascii8bit_encoding());
154
325
 
326
+ memset(&job, 0, sizeof(job));
327
+ job.data = RSTRING_PTR(data);
328
+ job.data_len = data_len;
329
+ memcpy(job.key, key, 16);
330
+ job.buf = (uint32_t *)RSTRING_PTR(retval);
331
+ job.n = (int)alen;
332
+ job.padding = padding;
333
+ job.rounds = rounds;
334
+
335
+ encrypt_nogvl(&job);
336
+ RB_GC_GUARD(data);
155
337
  return retval;
156
338
  }
157
339
 
158
- VALUE xxtea_decrypt(VALUE mod, VALUE data, VALUE key)
340
+ static VALUE
341
+ decrypt_impl(VALUE data, const char key[16], int padding, unsigned int rounds)
159
342
  {
160
- int alen, dlen, klen, rc;
161
- uint32_t *d, k[4];
343
+ crypt_job job;
162
344
  VALUE retval;
163
- char *retbuf;
164
-
165
- d = NULL;
166
- k[0] = k[1] = k[2] = k[3] = 0;
167
-
168
- Check_Type(data, T_STRING);
169
- Check_Type(key, T_STRING);
345
+ long data_len, alen;
170
346
 
171
- dlen = RSTRING_LEN(data);
172
- klen = RSTRING_LEN(key);
347
+ StringValue(data);
348
+ data_len = RSTRING_LEN(data);
173
349
 
174
- if (klen != 16) {
175
- rb_raise(rb_eArgError, "Need a 16-byte key.");
176
- return Qnil;
350
+ if (data_len & 3 || data_len < 8) {
351
+ rb_raise(rb_eArgError,
352
+ "Data length must be a multiple of 4 bytes and must not be less than 8 bytes");
177
353
  }
178
354
 
179
- /* not divided by 4, or length < 8 */
180
- if (dlen & 3 || dlen < 8) {
181
- rb_raise(rb_eArgError, "Invalid data.");
182
- return Qnil;
355
+ alen = data_len / 4;
356
+ if (alen > INT_MAX || alen > LONG_MAX / 4) {
357
+ rb_raise(rb_eRangeError, "data too large");
183
358
  }
184
359
 
185
- retval = rb_str_new(NULL, dlen);
360
+ retval = rb_str_new(NULL, data_len);
361
+ rb_enc_associate(retval, rb_ascii8bit_encoding());
362
+
363
+ memset(&job, 0, sizeof(job));
364
+ job.data = RSTRING_PTR(data);
365
+ job.data_len = data_len;
366
+ memcpy(job.key, key, 16);
367
+ job.buf = (uint32_t *)RSTRING_PTR(retval);
368
+ job.n = (int)alen;
369
+ job.padding = padding;
370
+ job.rounds = rounds;
186
371
 
187
- if (!retval) {
188
- free(d);
189
- return Qnil;
372
+ decrypt_nogvl(&job);
373
+ RB_GC_GUARD(data);
374
+
375
+ if (padding) {
376
+ if (job.rc < 0) {
377
+ rb_raise(rb_eArgError,
378
+ "Invalid data, illegal padding. Could be using a wrong key.");
379
+ }
380
+ rb_str_resize(retval, job.rc);
190
381
  }
191
382
 
192
- retbuf = RSTRING_PTR(retval);
383
+ return retval;
384
+ }
193
385
 
194
- alen = dlen >> 2;
195
- d = (uint32_t *)calloc(alen, sizeof(uint32_t));
386
+ static VALUE
387
+ bytes_to_hex(VALUE bytes)
388
+ {
389
+ static const char hex[] = "0123456789abcdef";
390
+ const unsigned char *s;
391
+ char *d;
392
+ long i, len;
393
+ VALUE out;
394
+
395
+ StringValue(bytes);
396
+ len = RSTRING_LEN(bytes);
397
+ out = rb_usascii_str_new(NULL, len * 2);
398
+ s = (const unsigned char *)RSTRING_PTR(bytes);
399
+ d = RSTRING_PTR(out);
400
+ for (i = 0; i < len; i++) {
401
+ d[i * 2] = hex[s[i] >> 4];
402
+ d[i * 2 + 1] = hex[s[i] & 0x0f];
403
+ }
404
+ return out;
405
+ }
406
+
407
+ static int
408
+ hex_nibble(int c)
409
+ {
410
+ if (c >= '0' && c <= '9') return c - '0';
411
+ if (c >= 'a' && c <= 'f') return c - 'a' + 10;
412
+ if (c >= 'A' && c <= 'F') return c - 'A' + 10;
413
+ return -1;
414
+ }
196
415
 
197
- if (!d) {
198
- rb_raise(rb_eNoMemError, "calloc failed.");
199
- return Qnil;
416
+ static VALUE
417
+ hex_to_bytes(VALUE hex)
418
+ {
419
+ const unsigned char *s;
420
+ unsigned char *d;
421
+ long i, len;
422
+ VALUE out;
423
+
424
+ StringValue(hex);
425
+ len = RSTRING_LEN(hex);
426
+ if (len & 1) {
427
+ rb_raise(rb_eArgError, "Odd-length string");
200
428
  }
201
429
 
202
- retval = rb_str_new(NULL, (alen << 2));
430
+ out = rb_str_new(NULL, len / 2);
431
+ rb_enc_associate(out, rb_ascii8bit_encoding());
432
+ s = (const unsigned char *)RSTRING_PTR(hex);
433
+ d = (unsigned char *)RSTRING_PTR(out);
203
434
 
204
- if (!retval) {
205
- free(d);
206
- return NULL;
435
+ for (i = 0; i < len; i += 2) {
436
+ int hi = hex_nibble(s[i]);
437
+ int lo = hex_nibble(s[i + 1]);
438
+ if (hi < 0 || lo < 0) {
439
+ rb_raise(rb_eArgError, "Non-hexadecimal digit found");
440
+ }
441
+ d[i / 2] = (unsigned char)((hi << 4) | lo);
207
442
  }
443
+ return out;
444
+ }
208
445
 
209
- retbuf = RSTRING_PTR(retval);
446
+ static VALUE
447
+ xxtea_s_encrypt(int argc, VALUE *argv, VALUE klass)
448
+ {
449
+ VALUE data, key, opts;
450
+ int padding;
451
+ unsigned int rounds;
452
+ char keybuf[16];
453
+
454
+ (void)klass;
455
+ rb_scan_args(argc, argv, "2:", &data, &key, &opts);
456
+ parse_opts(opts, &padding, &rounds);
457
+ require_key(key, keybuf);
458
+ return encrypt_impl(data, keybuf, padding, rounds);
459
+ }
210
460
 
211
- bytes2longs(StringValuePtr(data), dlen, d, 0);
212
- bytes2longs(StringValuePtr(key), klen, k, 0);
213
- btea(d, -alen, k);
461
+ static VALUE
462
+ xxtea_s_decrypt(int argc, VALUE *argv, VALUE klass)
463
+ {
464
+ VALUE data, key, opts;
465
+ int padding;
466
+ unsigned int rounds;
467
+ char keybuf[16];
468
+
469
+ (void)klass;
470
+ rb_scan_args(argc, argv, "2:", &data, &key, &opts);
471
+ parse_opts(opts, &padding, &rounds);
472
+ require_key(key, keybuf);
473
+ return decrypt_impl(data, keybuf, padding, rounds);
474
+ }
214
475
 
215
- if ((rc = longs2bytes(d, alen, retbuf, 1)) != dlen) {
216
- /* Remove PKCS#7 padded chars */
217
- rb_str_resize(retval, rc);
218
- }
476
+ static VALUE
477
+ xxtea_s_encrypt_hex(int argc, VALUE *argv, VALUE klass)
478
+ {
479
+ return bytes_to_hex(xxtea_s_encrypt(argc, argv, klass));
480
+ }
219
481
 
220
- return retval;
482
+ static VALUE
483
+ xxtea_s_decrypt_hex(int argc, VALUE *argv, VALUE klass)
484
+ {
485
+ VALUE data, key, opts;
486
+ int padding;
487
+ unsigned int rounds;
488
+ char keybuf[16];
489
+ VALUE raw;
490
+
491
+ rb_scan_args(argc, argv, "2:", &data, &key, &opts);
492
+ parse_opts(opts, &padding, &rounds);
493
+ raw = hex_to_bytes(data);
494
+ require_key(key, keybuf);
495
+ return decrypt_impl(raw, keybuf, padding, rounds);
221
496
  }
222
497
 
498
+ static VALUE
499
+ xxtea_alloc(VALUE klass)
500
+ {
501
+ xxtea_cipher_t *cipher;
502
+ VALUE obj = TypedData_Make_Struct(klass, xxtea_cipher_t, &xxtea_cipher_type, cipher);
503
+ memset(cipher, 0, sizeof(*cipher));
504
+ cipher->padding = 1;
505
+ cipher->rounds = 0;
506
+ return obj;
507
+ }
223
508
 
224
- void Init_xxtea()
509
+ static VALUE
510
+ xxtea_initialize(int argc, VALUE *argv, VALUE self)
225
511
  {
226
- VALUE mXXTEA = rb_define_module("XXTEA");
227
- rb_define_singleton_method(mXXTEA, "encrypt", xxtea_encrypt, 2);
228
- rb_define_singleton_method(mXXTEA, "decrypt", xxtea_decrypt, 2);
512
+ VALUE key, opts;
513
+ xxtea_cipher_t *cipher = xxtea_get(self);
514
+ int padding;
515
+ unsigned int rounds;
516
+
517
+ rb_scan_args(argc, argv, "1:", &key, &opts);
518
+ parse_opts(opts, &padding, &rounds);
519
+ require_key(key, cipher->key);
520
+ cipher->padding = padding;
521
+ cipher->rounds = rounds;
522
+ return self;
523
+ }
524
+
525
+ static VALUE
526
+ xxtea_encrypt(VALUE self, VALUE data)
527
+ {
528
+ xxtea_cipher_t *cipher = xxtea_get(self);
529
+ return encrypt_impl(data, cipher->key, cipher->padding, cipher->rounds);
530
+ }
531
+
532
+ static VALUE
533
+ xxtea_decrypt(VALUE self, VALUE data)
534
+ {
535
+ xxtea_cipher_t *cipher = xxtea_get(self);
536
+ return decrypt_impl(data, cipher->key, cipher->padding, cipher->rounds);
537
+ }
538
+
539
+ static VALUE
540
+ xxtea_encrypt_hex(VALUE self, VALUE data)
541
+ {
542
+ return bytes_to_hex(xxtea_encrypt(self, data));
543
+ }
544
+
545
+ static VALUE
546
+ xxtea_decrypt_hex(VALUE self, VALUE data)
547
+ {
548
+ xxtea_cipher_t *cipher = xxtea_get(self);
549
+ VALUE raw = hex_to_bytes(data);
550
+ return decrypt_impl(raw, cipher->key, cipher->padding, cipher->rounds);
551
+ }
552
+
553
+ static VALUE
554
+ xxtea_inspect(VALUE self)
555
+ {
556
+ xxtea_cipher_t *cipher = xxtea_get(self);
557
+ return rb_sprintf("#<%s:%p padding=%s rounds=%u>",
558
+ rb_obj_classname(self), (void *)self,
559
+ cipher->padding ? "true" : "false", cipher->rounds);
560
+ }
561
+
562
+ void
563
+ Init_xxtea(void)
564
+ {
565
+ rb_ext_ractor_safe(true);
566
+
567
+ id_padding = rb_intern("padding");
568
+ id_rounds = rb_intern("rounds");
569
+
570
+ cXXTEA = rb_define_class("XXTEA", rb_cObject);
571
+ rb_define_alloc_func(cXXTEA, xxtea_alloc);
572
+
573
+ rb_define_singleton_method(cXXTEA, "encrypt", xxtea_s_encrypt, -1);
574
+ rb_define_singleton_method(cXXTEA, "decrypt", xxtea_s_decrypt, -1);
575
+ rb_define_singleton_method(cXXTEA, "encrypt_hex", xxtea_s_encrypt_hex, -1);
576
+ rb_define_singleton_method(cXXTEA, "decrypt_hex", xxtea_s_decrypt_hex, -1);
577
+
578
+ rb_define_method(cXXTEA, "initialize", xxtea_initialize, -1);
579
+ rb_define_method(cXXTEA, "encrypt", xxtea_encrypt, 1);
580
+ rb_define_method(cXXTEA, "decrypt", xxtea_decrypt, 1);
581
+ rb_define_method(cXXTEA, "encrypt_hex", xxtea_encrypt_hex, 1);
582
+ rb_define_method(cXXTEA, "decrypt_hex", xxtea_decrypt_hex, 1);
583
+ rb_define_method(cXXTEA, "inspect", xxtea_inspect, 0);
229
584
  }
data/lib/xxtea/version.rb CHANGED
@@ -1,3 +1,5 @@
1
- module XXTEA
2
- VERSION = "0.0.1"
1
+ # frozen_string_literal: true
2
+
3
+ class XXTEA
4
+ VERSION = "1.0.0"
3
5
  end
data/lib/xxtea.rb CHANGED
@@ -1,13 +1,4 @@
1
- require 'xxtea/version'
2
- require 'xxtea/xxtea'
1
+ # frozen_string_literal: true
3
2
 
4
-
5
- module XXTEA
6
- def self.encrypt_hex(data, key)
7
- encrypt(data, key).unpack('H*').first
8
- end
9
-
10
- def self.decrypt_hex(data, key)
11
- decrypt([data].pack('H*'), key)
12
- end
13
- end
3
+ require "xxtea/version"
4
+ require "xxtea/xxtea"
metadata CHANGED
@@ -1,16 +1,19 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: xxtea
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yue Du
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-03-08 00:00:00.000000000 Z
11
+ date: 2026-08-20 00:00:00.000000000 Z
12
12
  dependencies: []
13
- description: Ruby xxtea module
13
+ description: |
14
+ XXTEA implemented as a Ruby C extension. Ciphertext is compatible with the
15
+ Python xxtea package: little-endian 32-bit words and non-standard 4-byte
16
+ PKCS#7 padding.
14
17
  email:
15
18
  - ifduyue@gmail.com
16
19
  executables: []
@@ -18,19 +21,23 @@ extensions:
18
21
  - ext/xxtea/extconf.rb
19
22
  extra_rdoc_files: []
20
23
  files:
21
- - ".gitignore"
22
- - Gemfile
23
- - Rakefile
24
+ - CHANGELOG.md
25
+ - LICENSE
26
+ - README.md
24
27
  - ext/xxtea/extconf.rb
25
28
  - ext/xxtea/xxtea.c
26
29
  - lib/xxtea.rb
27
30
  - lib/xxtea/version.rb
28
- - xxtea.gemspec
29
- homepage: http://github.com/ifduyue/xxtea
31
+ homepage: https://github.com/ifduyue/ruby-xxtea
30
32
  licenses:
31
- - BSD
32
- metadata: {}
33
- post_install_message:
33
+ - BSD-2-Clause
34
+ metadata:
35
+ homepage_uri: https://github.com/ifduyue/ruby-xxtea
36
+ source_code_uri: https://github.com/ifduyue/ruby-xxtea
37
+ changelog_uri: https://github.com/ifduyue/ruby-xxtea/blob/master/CHANGELOG.md
38
+ bug_tracker_uri: https://github.com/ifduyue/ruby-xxtea/issues
39
+ rubygems_mfa_required: 'true'
40
+ post_install_message:
34
41
  rdoc_options: []
35
42
  require_paths:
36
43
  - lib
@@ -38,16 +45,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
38
45
  requirements:
39
46
  - - ">="
40
47
  - !ruby/object:Gem::Version
41
- version: '0'
48
+ version: 3.1.0
42
49
  required_rubygems_version: !ruby/object:Gem::Requirement
43
50
  requirements:
44
51
  - - ">="
45
52
  - !ruby/object:Gem::Version
46
53
  version: '0'
47
54
  requirements: []
48
- rubyforge_project:
49
- rubygems_version: 2.4.2
50
- signing_key:
55
+ rubygems_version: 3.5.22
56
+ signing_key:
51
57
  specification_version: 4
52
- summary: Ruby xxtea module
58
+ summary: XXTEA block cipher as a Ruby C extension
53
59
  test_files: []
data/.gitignore DELETED
@@ -1,3 +0,0 @@
1
- *.bundle
2
- *.gem
3
- *.o
data/Gemfile DELETED
@@ -1,6 +0,0 @@
1
- source "http://rubygems.org"
2
-
3
- gemspec
4
-
5
- gem 'rake'
6
- gem 'rake-compiler'
data/Rakefile DELETED
@@ -1,6 +0,0 @@
1
- require 'bundler/gem_tasks'
2
- require 'rake/extensiontask'
3
-
4
- Rake::ExtensionTask.new('xxtea') do |ext|
5
- ext.lib_dir = 'lib/xxtea'
6
- end
data/xxtea.gemspec DELETED
@@ -1,21 +0,0 @@
1
- # -*- encoding: utf-8 -*-
2
-
3
- lib = File.expand_path('../lib', __FILE__)
4
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
-
6
- require 'xxtea/version'
7
- Gem::Specification.new do |gem|
8
- gem.name = "xxtea"
9
- gem.version = XXTEA::VERSION
10
- gem.authors = ["Yue Du"]
11
- gem.email = ["ifduyue@gmail.com"]
12
- gem.description = %q{Ruby xxtea module}
13
- gem.summary = %q{Ruby xxtea module}
14
- gem.homepage = "http://github.com/ifduyue/xxtea"
15
- gem.license = 'BSD'
16
- gem.files = `git ls-files`.split($/)
17
- gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
18
- gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
- gem.require_paths = ["lib"]
20
- gem.extensions = ["ext/xxtea/extconf.rb"]
21
- end