react-native-aes-lite 1.0.0 → 1.0.1-0.rc
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.
- package/README.md +25 -9
- package/dist/AES.js +329 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +4 -0
- package/dist/keygen.js +26 -0
- package/dist/randomBytes.js +23 -0
- package/package.json +15 -14
- package/src/AES.js +0 -296
- package/src/index.d.ts +0 -4
- package/src/index.js +0 -4
- package/src/keygen.js +0 -39
package/README.md
CHANGED
|
@@ -1,18 +1,34 @@
|
|
|
1
1
|
|
|
2
|
-
# react-native-aes-lite
|
|
2
|
+
# 📦 react-native-aes-lite
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
[](https://www.npmjs.com/package/react-native-aes-lite)
|
|
5
|
+
[]()
|
|
6
|
+
[]()
|
|
7
|
+
[]()
|
|
5
8
|
|
|
6
|
-
|
|
9
|
+
Lightweight AES encryption/decryption utility for React Native and Expo. No native modules, no linking — just pure JavaScript.
|
|
7
10
|
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 📖 Overview
|
|
14
|
+
|
|
15
|
+
react-native-aes-lite provides simple AES block-based text encryption and decryption using a 256-bit key. It is ideal for app-side data scrambling, offline encoding, demos, and educational use.
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
## 🐞 Troubleshooting
|
|
19
|
+
|
|
20
|
+
### Error: AES is not a constructor
|
|
21
|
+
Use default import fallback:
|
|
8
22
|
|
|
9
|
-
## Usage
|
|
10
23
|
```js
|
|
11
|
-
import
|
|
24
|
+
import aesKit from "react-native-aes-lite";
|
|
25
|
+
const { AES } = aesKit;
|
|
26
|
+
```
|
|
12
27
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const encrypted = aes.encrypt("hello", key);
|
|
16
|
-
const decrypted = aes.decrypt(encrypted, key);
|
|
28
|
+
### TS2305 — generateKey missing
|
|
29
|
+
Ensure latest version is installed.
|
|
17
30
|
|
|
31
|
+
---
|
|
18
32
|
|
|
33
|
+
## 📜 License
|
|
34
|
+
MIT © 2025 Ammachi
|
package/dist/AES.js
ADDED
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
// AES.js
|
|
2
|
+
import { randomBytes } from "./randomBytes";
|
|
3
|
+
|
|
4
|
+
/* ============================================================
|
|
5
|
+
UTF-8 Encoder / Decoder (Hermes-safe)
|
|
6
|
+
============================================================ */
|
|
7
|
+
function utf8Encode(str) {
|
|
8
|
+
const out = [];
|
|
9
|
+
for (let i = 0; i < str.length; i++) {
|
|
10
|
+
let c = str.charCodeAt(i);
|
|
11
|
+
if (c < 0x80) out.push(c);
|
|
12
|
+
else if (c < 0x800) {
|
|
13
|
+
out.push(0xc0 | (c >> 6));
|
|
14
|
+
out.push(0x80 | (c & 0x3f));
|
|
15
|
+
} else {
|
|
16
|
+
out.push(0xe0 | (c >> 12));
|
|
17
|
+
out.push(0x80 | ((c >> 6) & 0x3f));
|
|
18
|
+
out.push(0x80 | (c & 0x3f));
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return out;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function utf8Decode(bytes) {
|
|
25
|
+
let s = "";
|
|
26
|
+
for (let i = 0; i < bytes.length; ) {
|
|
27
|
+
const c = bytes[i++];
|
|
28
|
+
if (c < 0x80) {
|
|
29
|
+
s += String.fromCharCode(c);
|
|
30
|
+
} else if (c < 0xe0) {
|
|
31
|
+
const c2 = bytes[i++];
|
|
32
|
+
s += String.fromCharCode(((c & 0x1f) << 6) | (c2 & 0x3f));
|
|
33
|
+
} else {
|
|
34
|
+
const c2 = bytes[i++],
|
|
35
|
+
c3 = bytes[i++];
|
|
36
|
+
s += String.fromCharCode(
|
|
37
|
+
((c & 0x0f) << 12) | ((c2 & 0x3f) << 6) | (c3 & 0x3f),
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return s;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/* ============================================================
|
|
45
|
+
AES CLASS
|
|
46
|
+
============================================================ */
|
|
47
|
+
|
|
48
|
+
class AES {
|
|
49
|
+
constructor() {
|
|
50
|
+
this.encode = utf8Encode;
|
|
51
|
+
this.decode = utf8Decode;
|
|
52
|
+
|
|
53
|
+
this.SBOX = [
|
|
54
|
+
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b,
|
|
55
|
+
0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,
|
|
56
|
+
0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26,
|
|
57
|
+
0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
|
|
58
|
+
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2,
|
|
59
|
+
0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,
|
|
60
|
+
0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed,
|
|
61
|
+
0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
|
|
62
|
+
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f,
|
|
63
|
+
0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
|
|
64
|
+
0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec,
|
|
65
|
+
0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
|
|
66
|
+
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14,
|
|
67
|
+
0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,
|
|
68
|
+
0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d,
|
|
69
|
+
0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
|
|
70
|
+
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f,
|
|
71
|
+
0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,
|
|
72
|
+
0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11,
|
|
73
|
+
0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
|
|
74
|
+
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f,
|
|
75
|
+
0xb0, 0x54, 0xbb, 0x16,
|
|
76
|
+
];
|
|
77
|
+
|
|
78
|
+
this.INV_SBOX = new Array(256);
|
|
79
|
+
for (let i = 0; i < 256; i++) this.INV_SBOX[this.SBOX[i]] = i;
|
|
80
|
+
|
|
81
|
+
this.RCON = [
|
|
82
|
+
0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c,
|
|
83
|
+
0xd8, 0xab, 0x4d, 0x9a,
|
|
84
|
+
];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* ============================================================
|
|
88
|
+
UTILITY FUNCTIONS
|
|
89
|
+
============================================================= */
|
|
90
|
+
hexToBytes(h) {
|
|
91
|
+
return Uint8Array.from(h.match(/../g).map((x) => parseInt(x, 16)));
|
|
92
|
+
}
|
|
93
|
+
bytesToHex(b) {
|
|
94
|
+
return [...b].map((x) => x.toString(16).padStart(2, "0")).join("");
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
parseKey(hex) {
|
|
98
|
+
const b = this.hexToBytes(hex);
|
|
99
|
+
if (![16, 24, 32].includes(b.length))
|
|
100
|
+
throw new Error("Invalid AES key length");
|
|
101
|
+
return [...b];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
pkcs7Pad(arr) {
|
|
105
|
+
const pad = 16 - (arr.length % 16);
|
|
106
|
+
return [...arr, ...Array(pad).fill(pad)];
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
pkcs7Unpad(arr) {
|
|
110
|
+
const pad = arr[arr.length - 1];
|
|
111
|
+
return arr.slice(0, -pad);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/* ============================================================
|
|
115
|
+
AES KEY EXPANSION
|
|
116
|
+
============================================================= */
|
|
117
|
+
keyExpansion(key) {
|
|
118
|
+
const Nk = key.length / 4;
|
|
119
|
+
const Nr = Nk + 6;
|
|
120
|
+
const w = new Array(4 * (Nr + 1));
|
|
121
|
+
|
|
122
|
+
for (let i = 0; i < Nk; i++) {
|
|
123
|
+
w[i] = key.slice(4 * i, 4 * i + 4);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
for (let i = Nk; i < w.length; i++) {
|
|
127
|
+
let t = [...w[i - 1]];
|
|
128
|
+
|
|
129
|
+
if (i % Nk === 0) {
|
|
130
|
+
t.push(t.shift());
|
|
131
|
+
t = t.map((b) => this.SBOX[b]);
|
|
132
|
+
t[0] ^= this.RCON[i / Nk];
|
|
133
|
+
} else if (Nk > 6 && i % Nk === 4) {
|
|
134
|
+
t = t.map((b) => this.SBOX[b]);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
w[i] = w[i - Nk].map((b, j) => b ^ t[j]);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return { w, Nr };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/* ============================================================
|
|
144
|
+
AES ROUND FUNCTIONS
|
|
145
|
+
============================================================= */
|
|
146
|
+
subBytes(s) {
|
|
147
|
+
for (let r = 0; r < 4; r++)
|
|
148
|
+
for (let c = 0; c < 4; c++) s[r][c] = this.SBOX[s[r][c]];
|
|
149
|
+
return s;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
invSubBytes(s) {
|
|
153
|
+
for (let r = 0; r < 4; r++)
|
|
154
|
+
for (let c = 0; c < 4; c++) s[r][c] = this.INV_SBOX[s[r][c]];
|
|
155
|
+
return s;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
shiftRows(s) {
|
|
159
|
+
s[1].push(s[1].shift());
|
|
160
|
+
s[2].push(...s[2].splice(0, 2));
|
|
161
|
+
s[3].unshift(s[3].pop());
|
|
162
|
+
return s;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
invShiftRows(s) {
|
|
166
|
+
s[1].unshift(s[1].pop());
|
|
167
|
+
s[2].unshift(...s[2].splice(2));
|
|
168
|
+
s[3].push(s[3].shift());
|
|
169
|
+
return s;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
xtime(a) {
|
|
173
|
+
return ((a << 1) ^ ((a >> 7) & 1 ? 0x1b : 0)) & 0xff;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
mixColumns(s) {
|
|
177
|
+
for (let c = 0; c < 4; c++) {
|
|
178
|
+
const a = s.map((r) => r[c]);
|
|
179
|
+
s[0][c] = this.xtime(a[0]) ^ this.xtime(a[1]) ^ a[1] ^ a[2] ^ a[3];
|
|
180
|
+
s[1][c] = a[0] ^ this.xtime(a[1]) ^ this.xtime(a[2]) ^ a[2] ^ a[3];
|
|
181
|
+
s[2][c] = a[0] ^ a[1] ^ this.xtime(a[2]) ^ this.xtime(a[3]) ^ a[3];
|
|
182
|
+
s[3][c] = this.xtime(a[0]) ^ a[0] ^ a[1] ^ a[2] ^ this.xtime(a[3]);
|
|
183
|
+
}
|
|
184
|
+
return s;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
invMixColumns(s) {
|
|
188
|
+
const mul = (a, b) => {
|
|
189
|
+
let res = 0;
|
|
190
|
+
for (let i = 0; i < 8; i++) {
|
|
191
|
+
if (b & 1) res ^= a;
|
|
192
|
+
const hi = a & 0x80;
|
|
193
|
+
a = (a << 1) & 0xff;
|
|
194
|
+
if (hi) a ^= 0x1b;
|
|
195
|
+
b >>= 1;
|
|
196
|
+
}
|
|
197
|
+
return res;
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
for (let c = 0; c < 4; c++) {
|
|
201
|
+
const a = s.map((r) => r[c]);
|
|
202
|
+
s[0][c] = mul(a[0], 14) ^ mul(a[1], 11) ^ mul(a[2], 13) ^ mul(a[3], 9);
|
|
203
|
+
s[1][c] = mul(a[0], 9) ^ mul(a[1], 14) ^ mul(a[2], 11) ^ mul(a[3], 13);
|
|
204
|
+
s[2][c] = mul(a[0], 13) ^ mul(a[1], 9) ^ mul(a[2], 14) ^ mul(a[3], 11);
|
|
205
|
+
s[3][c] = mul(a[0], 11) ^ mul(a[1], 13) ^ mul(a[2], 9) ^ mul(a[3], 14);
|
|
206
|
+
}
|
|
207
|
+
return s;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
addRoundKey(s, w, r) {
|
|
211
|
+
for (let c = 0; c < 4; c++) {
|
|
212
|
+
const word = w[r * 4 + c];
|
|
213
|
+
s[0][c] ^= word[0];
|
|
214
|
+
s[1][c] ^= word[1];
|
|
215
|
+
s[2][c] ^= word[2];
|
|
216
|
+
s[3][c] ^= word[3];
|
|
217
|
+
}
|
|
218
|
+
return s;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/* ============================================================
|
|
222
|
+
CIPHER (ENCRYPT ONE BLOCK)
|
|
223
|
+
============================================================= */
|
|
224
|
+
cipher(input, w, Nr) {
|
|
225
|
+
let s = [[], [], [], []];
|
|
226
|
+
for (let i = 0; i < 16; i++) {
|
|
227
|
+
s[i % 4][(i / 4) | 0] = input[i];
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
s = this.addRoundKey(s, w, 0);
|
|
231
|
+
|
|
232
|
+
for (let round = 1; round < Nr; round++) {
|
|
233
|
+
s = this.subBytes(s);
|
|
234
|
+
s = this.shiftRows(s);
|
|
235
|
+
s = this.mixColumns(s);
|
|
236
|
+
s = this.addRoundKey(s, w, round);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
s = this.subBytes(s);
|
|
240
|
+
s = this.shiftRows(s);
|
|
241
|
+
s = this.addRoundKey(s, w, Nr);
|
|
242
|
+
|
|
243
|
+
const out = new Array(16);
|
|
244
|
+
for (let i = 0; i < 16; i++) {
|
|
245
|
+
out[i] = s[i % 4][(i / 4) | 0];
|
|
246
|
+
}
|
|
247
|
+
return out;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/* ============================================================
|
|
251
|
+
INVERSE CIPHER (DECRYPT ONE BLOCK)
|
|
252
|
+
============================================================= */
|
|
253
|
+
invCipher(input, w, Nr) {
|
|
254
|
+
let s = [[], [], [], []];
|
|
255
|
+
|
|
256
|
+
for (let i = 0; i < 16; i++) {
|
|
257
|
+
s[i % 4][(i / 4) | 0] = input[i];
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
s = this.addRoundKey(s, w, Nr);
|
|
261
|
+
|
|
262
|
+
for (let round = Nr - 1; round >= 1; round--) {
|
|
263
|
+
s = this.invShiftRows(s);
|
|
264
|
+
s = this.invSubBytes(s);
|
|
265
|
+
s = this.addRoundKey(s, w, round);
|
|
266
|
+
s = this.invMixColumns(s);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
s = this.invShiftRows(s);
|
|
270
|
+
s = this.invSubBytes(s);
|
|
271
|
+
s = this.addRoundKey(s, w, 0);
|
|
272
|
+
|
|
273
|
+
const out = new Array(16);
|
|
274
|
+
for (let i = 0; i < 16; i++) {
|
|
275
|
+
out[i] = s[i % 4][(i / 4) | 0];
|
|
276
|
+
}
|
|
277
|
+
return out;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/* ============================================================
|
|
281
|
+
CBC MODE
|
|
282
|
+
============================================================= */
|
|
283
|
+
|
|
284
|
+
encrypt(text, keyHex) {
|
|
285
|
+
const key = this.parseKey(keyHex);
|
|
286
|
+
const { w, Nr } = this.keyExpansion(key);
|
|
287
|
+
|
|
288
|
+
const iv = randomBytes(16);
|
|
289
|
+
const pt = this.encode(text);
|
|
290
|
+
const padded = this.pkcs7Pad(pt);
|
|
291
|
+
|
|
292
|
+
let out = [];
|
|
293
|
+
let prev = iv;
|
|
294
|
+
|
|
295
|
+
for (let i = 0; i < padded.length; i += 16) {
|
|
296
|
+
const block = padded.slice(i, i + 16);
|
|
297
|
+
const xored = block.map((b, j) => b ^ prev[j]);
|
|
298
|
+
const enc = this.cipher(xored, w, Nr);
|
|
299
|
+
out.push(...enc);
|
|
300
|
+
prev = enc;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return this.bytesToHex(iv) + ":" + this.bytesToHex(out);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
decrypt(input, keyHex) {
|
|
307
|
+
const [ivHex, dataHex] = input.split(":");
|
|
308
|
+
const key = this.parseKey(keyHex);
|
|
309
|
+
const { w, Nr } = this.keyExpansion(key);
|
|
310
|
+
|
|
311
|
+
const iv = [...this.hexToBytes(ivHex)];
|
|
312
|
+
const data = [...this.hexToBytes(dataHex)];
|
|
313
|
+
|
|
314
|
+
let out = [];
|
|
315
|
+
let prev = iv;
|
|
316
|
+
|
|
317
|
+
for (let i = 0; i < data.length; i += 16) {
|
|
318
|
+
const block = data.slice(i, i + 16);
|
|
319
|
+
const dec = this.invCipher(block, w, Nr);
|
|
320
|
+
const x = dec.map((b, j) => b ^ prev[j]);
|
|
321
|
+
out.push(...x);
|
|
322
|
+
prev = block;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
return this.decode(this.pkcs7Unpad(out));
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
export default AES;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export class AES {
|
|
2
|
+
encrypt(text: string, key: string): string;
|
|
3
|
+
decrypt(cipherHex: string, key: string): string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export function generateKey(
|
|
7
|
+
format?: string,
|
|
8
|
+
options?: { length?: number; readable?: boolean }
|
|
9
|
+
): string;
|
|
10
|
+
|
|
11
|
+
declare const _default: {
|
|
12
|
+
AES: typeof AES;
|
|
13
|
+
generateKey: typeof generateKey;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export default _default;
|
package/dist/index.js
ADDED
package/dist/keygen.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// generateKey.js
|
|
2
|
+
import { randomBytes } from "./randomBytes";
|
|
3
|
+
|
|
4
|
+
export function generateKey(format = "hex", { length = 32 } = {}) {
|
|
5
|
+
if (![16, 24, 32].includes(length)) {
|
|
6
|
+
throw new Error("Key must be 16, 24, or 32 bytes (AES-128/192/256)");
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const bytes = randomBytes(length); // <-- SAFE ALWAYS
|
|
10
|
+
|
|
11
|
+
if (format === "hex") {
|
|
12
|
+
return [...bytes].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (format === "base64") {
|
|
16
|
+
let binary = "";
|
|
17
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
18
|
+
binary += String.fromCharCode(bytes[i]);
|
|
19
|
+
}
|
|
20
|
+
return globalThis.btoa
|
|
21
|
+
? btoa(binary)
|
|
22
|
+
: Buffer.from(bytes).toString("base64");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return String.fromCharCode(...bytes);
|
|
26
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// randomBytes.js
|
|
2
|
+
|
|
3
|
+
export function randomBytes(length) {
|
|
4
|
+
const out = new Uint8Array(length);
|
|
5
|
+
|
|
6
|
+
// 1️⃣ Browser / Expo Web
|
|
7
|
+
if (
|
|
8
|
+
typeof globalThis !== "undefined" &&
|
|
9
|
+
globalThis.crypto &&
|
|
10
|
+
typeof globalThis.crypto.getRandomValues === "function"
|
|
11
|
+
) {
|
|
12
|
+
globalThis.crypto.getRandomValues(out);
|
|
13
|
+
return out;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// 2️⃣ Hermes fallback RNG (React Native)
|
|
17
|
+
for (let i = 0; i < length; i++) {
|
|
18
|
+
out[i] = (Math.random() * 256) & 0xff;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
console.warn("⚠️ Using fallback RNG (not cryptographically secure)");
|
|
22
|
+
return out;
|
|
23
|
+
}
|
package/package.json
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-aes-lite",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "AES encryption for React Native",
|
|
5
|
-
"main": "
|
|
6
|
-
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
|
|
10
|
-
"
|
|
11
|
-
|
|
3
|
+
"version": "1.0.10.rc",
|
|
4
|
+
"description": "AES encryption for React Native + Expo Web",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"keywords": [
|
|
11
|
+
"aes",
|
|
12
|
+
"encryption",
|
|
13
|
+
"react-native",
|
|
14
|
+
"expo",
|
|
15
|
+
"web"
|
|
16
|
+
],
|
|
12
17
|
"author": "Ammachi",
|
|
13
|
-
"license": "MIT"
|
|
14
|
-
"devDependencies": {
|
|
15
|
-
"@types/node": "^25.0.3",
|
|
16
|
-
"typescript": "^5.9.3"
|
|
17
|
-
}
|
|
18
|
+
"license": "MIT"
|
|
18
19
|
}
|
package/src/AES.js
DELETED
|
@@ -1,296 +0,0 @@
|
|
|
1
|
-
class AES {
|
|
2
|
-
constructor() {
|
|
3
|
-
this.SBOX = [
|
|
4
|
-
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b,
|
|
5
|
-
0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,
|
|
6
|
-
0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26,
|
|
7
|
-
0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
|
|
8
|
-
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2,
|
|
9
|
-
0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,
|
|
10
|
-
0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed,
|
|
11
|
-
0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
|
|
12
|
-
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f,
|
|
13
|
-
0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
|
|
14
|
-
0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec,
|
|
15
|
-
0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
|
|
16
|
-
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14,
|
|
17
|
-
0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,
|
|
18
|
-
0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d,
|
|
19
|
-
0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
|
|
20
|
-
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f,
|
|
21
|
-
0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,
|
|
22
|
-
0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11,
|
|
23
|
-
0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
|
|
24
|
-
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f,
|
|
25
|
-
0xb0, 0x54, 0xbb, 0x16,
|
|
26
|
-
];
|
|
27
|
-
this.INV_SBOX = new Array(256);
|
|
28
|
-
for (let i = 0; i < 256; i++) this.INV_SBOX[this.SBOX[i]] = i;
|
|
29
|
-
this.RCON = [
|
|
30
|
-
0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36,
|
|
31
|
-
];
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
xtime(x) {
|
|
35
|
-
x &= 0xff;
|
|
36
|
-
return ((x << 1) ^ (((x >> 7) & 1) * 0x1b)) & 0xff;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
parseKey(keyString) {
|
|
40
|
-
if (keyString.length === 32) {
|
|
41
|
-
// Assume hex
|
|
42
|
-
const bytes = [];
|
|
43
|
-
for (let i = 0; i < 32; i += 2) {
|
|
44
|
-
bytes.push(parseInt(keyString.substr(i, 2), 16));
|
|
45
|
-
}
|
|
46
|
-
if (bytes.some(isNaN)) throw new Error("Invalid hex key");
|
|
47
|
-
return bytes;
|
|
48
|
-
} else if (keyString.length === 16) {
|
|
49
|
-
// ASCII
|
|
50
|
-
return keyString.split("").map((c) => c.charCodeAt(0));
|
|
51
|
-
} else {
|
|
52
|
-
throw new Error("Key must be 16 ASCII chars or 32 hex chars");
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
keyExpansion(key) {
|
|
57
|
-
const w = new Array(44); // 4 * (10 + 1)
|
|
58
|
-
for (let i = 0; i < 4; i++) {
|
|
59
|
-
w[i] = [key[4 * i], key[4 * i + 1], key[4 * i + 2], key[4 * i + 3]];
|
|
60
|
-
}
|
|
61
|
-
for (let i = 4; i < 44; i++) {
|
|
62
|
-
let temp = w[i - 1].slice();
|
|
63
|
-
if (i % 4 === 0) {
|
|
64
|
-
// RotWord
|
|
65
|
-
const t = temp[0];
|
|
66
|
-
temp[0] = temp[1];
|
|
67
|
-
temp[1] = temp[2];
|
|
68
|
-
temp[2] = temp[3];
|
|
69
|
-
temp[3] = t;
|
|
70
|
-
// SubWord
|
|
71
|
-
for (let j = 0; j < 4; j++) {
|
|
72
|
-
temp[j] = this.SBOX[temp[j]];
|
|
73
|
-
}
|
|
74
|
-
// Rcon
|
|
75
|
-
temp[0] ^= this.RCON[i / 4];
|
|
76
|
-
}
|
|
77
|
-
w[i] = [
|
|
78
|
-
w[i - 4][0] ^ temp[0],
|
|
79
|
-
w[i - 4][1] ^ temp[1],
|
|
80
|
-
w[i - 4][2] ^ temp[2],
|
|
81
|
-
w[i - 4][3] ^ temp[3],
|
|
82
|
-
];
|
|
83
|
-
}
|
|
84
|
-
return w;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
cipher(input, w) {
|
|
88
|
-
let state = [
|
|
89
|
-
[0, 0, 0, 0],
|
|
90
|
-
[0, 0, 0, 0],
|
|
91
|
-
[0, 0, 0, 0],
|
|
92
|
-
[0, 0, 0, 0],
|
|
93
|
-
];
|
|
94
|
-
for (let r = 0; r < 4; r++)
|
|
95
|
-
for (let c = 0; c < 4; c++) state[r][c] = input[r + 4 * c];
|
|
96
|
-
|
|
97
|
-
state = this.addRoundKey(state, w, 0);
|
|
98
|
-
|
|
99
|
-
for (let round = 1; round < 10; round++) {
|
|
100
|
-
state = this.subBytes(state);
|
|
101
|
-
state = this.shiftRows(state);
|
|
102
|
-
state = this.mixColumns(state);
|
|
103
|
-
state = this.addRoundKey(state, w, round);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
state = this.subBytes(state);
|
|
107
|
-
state = this.shiftRows(state);
|
|
108
|
-
state = this.addRoundKey(state, w, 10);
|
|
109
|
-
|
|
110
|
-
const output = new Array(16);
|
|
111
|
-
for (let r = 0; r < 4; r++)
|
|
112
|
-
for (let c = 0; c < 4; c++) output[r + 4 * c] = state[r][c];
|
|
113
|
-
return output;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
invCipher(input, w) {
|
|
117
|
-
let state = [
|
|
118
|
-
[0, 0, 0, 0],
|
|
119
|
-
[0, 0, 0, 0],
|
|
120
|
-
[0, 0, 0, 0],
|
|
121
|
-
[0, 0, 0, 0],
|
|
122
|
-
];
|
|
123
|
-
for (let r = 0; r < 4; r++)
|
|
124
|
-
for (let c = 0; c < 4; c++) state[r][c] = input[r + 4 * c];
|
|
125
|
-
|
|
126
|
-
state = this.addRoundKey(state, w, 10);
|
|
127
|
-
|
|
128
|
-
for (let round = 9; round >= 1; round--) {
|
|
129
|
-
state = this.invShiftRows(state);
|
|
130
|
-
state = this.invSubBytes(state);
|
|
131
|
-
state = this.addRoundKey(state, w, round);
|
|
132
|
-
state = this.invMixColumns(state);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
state = this.invShiftRows(state);
|
|
136
|
-
state = this.invSubBytes(state);
|
|
137
|
-
state = this.addRoundKey(state, w, 0);
|
|
138
|
-
|
|
139
|
-
const output = new Array(16);
|
|
140
|
-
for (let r = 0; r < 4; r++)
|
|
141
|
-
for (let c = 0; c < 4; c++) output[r + 4 * c] = state[r][c];
|
|
142
|
-
return output;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
subBytes(s) {
|
|
146
|
-
for (let r = 0; r < 4; r++)
|
|
147
|
-
for (let c = 0; c < 4; c++) s[r][c] = this.SBOX[s[r][c]];
|
|
148
|
-
return s;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
invSubBytes(s) {
|
|
152
|
-
for (let r = 0; r < 4; r++)
|
|
153
|
-
for (let c = 0; c < 4; c++) s[r][c] = this.INV_SBOX[s[r][c]];
|
|
154
|
-
return s;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
shiftRows(s) {
|
|
158
|
-
const t = s.map((r) => [...r]);
|
|
159
|
-
s[1][0] = t[1][1];
|
|
160
|
-
s[1][1] = t[1][2];
|
|
161
|
-
s[1][2] = t[1][3];
|
|
162
|
-
s[1][3] = t[1][0];
|
|
163
|
-
s[2][0] = t[2][2];
|
|
164
|
-
s[2][1] = t[2][3];
|
|
165
|
-
s[2][2] = t[2][0];
|
|
166
|
-
s[2][3] = t[2][1];
|
|
167
|
-
s[3][0] = t[3][3];
|
|
168
|
-
s[3][1] = t[3][0];
|
|
169
|
-
s[3][2] = t[3][1];
|
|
170
|
-
s[3][3] = t[3][2];
|
|
171
|
-
return s;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
invShiftRows(s) {
|
|
175
|
-
const t = s.map((r) => [...r]);
|
|
176
|
-
s[1][0] = t[1][3];
|
|
177
|
-
s[1][1] = t[1][0];
|
|
178
|
-
s[1][2] = t[1][1];
|
|
179
|
-
s[1][3] = t[1][2];
|
|
180
|
-
s[2][0] = t[2][2];
|
|
181
|
-
s[2][1] = t[2][3];
|
|
182
|
-
s[2][2] = t[2][0];
|
|
183
|
-
s[2][3] = t[2][1];
|
|
184
|
-
s[3][0] = t[3][1];
|
|
185
|
-
s[3][1] = t[3][2];
|
|
186
|
-
s[3][2] = t[3][3];
|
|
187
|
-
s[3][3] = t[3][0];
|
|
188
|
-
return s;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
mixColumns(s) {
|
|
192
|
-
for (let c = 0; c < 4; c++) {
|
|
193
|
-
const a0 = s[0][c],
|
|
194
|
-
a1 = s[1][c],
|
|
195
|
-
a2 = s[2][c],
|
|
196
|
-
a3 = s[3][c];
|
|
197
|
-
s[0][c] = this.xtime(a0) ^ this.xtime(a1) ^ a1 ^ a2 ^ a3;
|
|
198
|
-
s[1][c] = a0 ^ this.xtime(a1) ^ this.xtime(a2) ^ a2 ^ a3;
|
|
199
|
-
s[2][c] = a0 ^ a1 ^ this.xtime(a2) ^ this.xtime(a3) ^ a3;
|
|
200
|
-
s[3][c] = this.xtime(a0) ^ a0 ^ a1 ^ a2 ^ this.xtime(a3);
|
|
201
|
-
}
|
|
202
|
-
return s;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
invMixColumns(s) {
|
|
206
|
-
for (let c = 0; c < 4; c++) {
|
|
207
|
-
const a = [s[0][c], s[1][c], s[2][c], s[3][c]];
|
|
208
|
-
const xtime2 = (v) => this.xtime(v);
|
|
209
|
-
const xtime4 = (v) => this.xtime(xtime2(v));
|
|
210
|
-
const xtime8 = (v) => this.xtime(xtime4(v));
|
|
211
|
-
const mul = (v, b) => {
|
|
212
|
-
let res = 0;
|
|
213
|
-
if (b & 1) res ^= v;
|
|
214
|
-
if (b & 2) res ^= xtime2(v);
|
|
215
|
-
if (b & 4) res ^= xtime4(v);
|
|
216
|
-
if (b & 8) res ^= xtime8(v);
|
|
217
|
-
return res & 0xff;
|
|
218
|
-
};
|
|
219
|
-
s[0][c] =
|
|
220
|
-
mul(a[0], 0x0e) ^ mul(a[1], 0x0b) ^ mul(a[2], 0x0d) ^ mul(a[3], 0x09);
|
|
221
|
-
s[1][c] =
|
|
222
|
-
mul(a[0], 0x09) ^ mul(a[1], 0x0e) ^ mul(a[2], 0x0b) ^ mul(a[3], 0x0d);
|
|
223
|
-
s[2][c] =
|
|
224
|
-
mul(a[0], 0x0d) ^ mul(a[1], 0x09) ^ mul(a[2], 0x0e) ^ mul(a[3], 0x0b);
|
|
225
|
-
s[3][c] =
|
|
226
|
-
mul(a[0], 0x0b) ^ mul(a[1], 0x0d) ^ mul(a[2], 0x09) ^ mul(a[3], 0x0e);
|
|
227
|
-
}
|
|
228
|
-
return s;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
addRoundKey(s, w, round) {
|
|
232
|
-
for (let c = 0; c < 4; c++) {
|
|
233
|
-
const word = w[round * 4 + c];
|
|
234
|
-
s[0][c] ^= word[0];
|
|
235
|
-
s[1][c] ^= word[1];
|
|
236
|
-
s[2][c] ^= word[2];
|
|
237
|
-
s[3][c] ^= word[3];
|
|
238
|
-
}
|
|
239
|
-
return s;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
pkcs7Pad(bytes) {
|
|
243
|
-
const padding = 16 - (bytes.length % 16);
|
|
244
|
-
const result = bytes.slice();
|
|
245
|
-
for (let i = 0; i < padding; i++) result.push(padding);
|
|
246
|
-
return result;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
pkcs7Unpad(bytes) {
|
|
250
|
-
const padding = bytes[bytes.length - 1];
|
|
251
|
-
if (padding < 1 || padding > 16 || bytes.length < padding) {
|
|
252
|
-
throw new Error("Invalid PKCS#7 padding");
|
|
253
|
-
}
|
|
254
|
-
for (let i = 1; i <= padding; i++) {
|
|
255
|
-
if (bytes[bytes.length - i] !== padding) {
|
|
256
|
-
throw new Error("Corrupted PKCS#7 padding");
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
return bytes.slice(0, -padding);
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
encrypt(text, keyString) {
|
|
263
|
-
const key = this.parseKey(keyString);
|
|
264
|
-
const w = this.keyExpansion(key);
|
|
265
|
-
let inputBytes = text.split("").map((c) => c.charCodeAt(0));
|
|
266
|
-
inputBytes = this.pkcs7Pad(inputBytes);
|
|
267
|
-
let outputBytes = [];
|
|
268
|
-
for (let i = 0; i < inputBytes.length; i += 16) {
|
|
269
|
-
outputBytes.push(...this.cipher(inputBytes.slice(i, i + 16), w));
|
|
270
|
-
}
|
|
271
|
-
return outputBytes.map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
decrypt(hexString, keyString) {
|
|
275
|
-
if (hexString.length % 2 !== 0)
|
|
276
|
-
throw new Error("Hex string must have even length");
|
|
277
|
-
const key = this.parseKey(keyString);
|
|
278
|
-
const w = this.keyExpansion(key);
|
|
279
|
-
let inputBytes = [];
|
|
280
|
-
for (let i = 0; i < hexString.length; i += 2) {
|
|
281
|
-
inputBytes.push(parseInt(hexString.substr(i, 2), 16));
|
|
282
|
-
}
|
|
283
|
-
if (inputBytes.length % 16 !== 0)
|
|
284
|
-
throw new Error("Ciphertext length must be multiple of 16");
|
|
285
|
-
let decryptedBytes = [];
|
|
286
|
-
for (let i = 0; i < inputBytes.length; i += 16) {
|
|
287
|
-
decryptedBytes.push(...this.invCipher(inputBytes.slice(i, i + 16), w));
|
|
288
|
-
}
|
|
289
|
-
decryptedBytes = this.pkcs7Unpad(decryptedBytes);
|
|
290
|
-
return String.fromCharCode.apply(null, decryptedBytes);
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
export default AES;
|
|
294
|
-
|
|
295
|
-
// Export singleton
|
|
296
|
-
// export default new AES();
|
package/src/index.d.ts
DELETED
package/src/index.js
DELETED
package/src/keygen.js
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
function generateKey(format = "hex", options = {}) {
|
|
2
|
-
const { length = 16, readable = false } = options;
|
|
3
|
-
|
|
4
|
-
if (![16, 24, 32].includes(length)) {
|
|
5
|
-
throw new Error("Key length must be 16, 24, or 32 bytes");
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
const bytes = new Uint8Array(length);
|
|
9
|
-
|
|
10
|
-
if (global.crypto?.getRandomValues) {
|
|
11
|
-
crypto.getRandomValues(bytes);
|
|
12
|
-
} else {
|
|
13
|
-
throw new Error("Secure random generator not available");
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const toHex = (buf) =>
|
|
17
|
-
Array.from(buf, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
18
|
-
|
|
19
|
-
const toBase64 = (buf) => Buffer.from(buf).toString("base64");
|
|
20
|
-
|
|
21
|
-
const toReadable = (buf) => {
|
|
22
|
-
const chars =
|
|
23
|
-
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+-=";
|
|
24
|
-
return Array.from(buf, (b) => chars[b % chars.length]).join("");
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
switch (format) {
|
|
28
|
-
case "hex":
|
|
29
|
-
return toHex(bytes);
|
|
30
|
-
case "base64":
|
|
31
|
-
return toBase64(bytes);
|
|
32
|
-
case "ascii":
|
|
33
|
-
return readable ? toReadable(bytes) : String.fromCharCode(...bytes);
|
|
34
|
-
default:
|
|
35
|
-
throw new Error("Invalid format");
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
module.exports = generateKey;
|