@etsoo/appscript 1.1.66 → 1.1.67
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/__tests__/app/CoreApp.ts +124 -0
- package/lib/cjs/app/CoreApp.d.ts +18 -2
- package/lib/cjs/app/CoreApp.js +50 -3
- package/lib/mjs/app/CoreApp.d.ts +18 -2
- package/lib/mjs/app/CoreApp.js +51 -4
- package/package.json +5 -5
- package/src/app/CoreApp.ts +85 -11
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import {
|
|
2
|
+
INotificaseBase,
|
|
3
|
+
INotification,
|
|
4
|
+
Notification,
|
|
5
|
+
NotificationCallProps,
|
|
6
|
+
NotificationContainer,
|
|
7
|
+
NotificationRenderProps
|
|
8
|
+
} from '@etsoo/notificationbase';
|
|
9
|
+
import { ApiAuthorizationScheme, createClient } from '@etsoo/restclient';
|
|
10
|
+
import { DataTypes, DomUtils, Utils } from '@etsoo/shared';
|
|
11
|
+
import { AddressUtils } from '../../src/address/AddressUtils';
|
|
12
|
+
import { IAppSettings } from '../../src/app/AppSettings';
|
|
13
|
+
import { CoreApp } from '../../src/app/CoreApp';
|
|
14
|
+
import { zhCN } from '../../src/i18n/zhCN';
|
|
15
|
+
|
|
16
|
+
// Detected country or region
|
|
17
|
+
const { detectedCountry } = DomUtils;
|
|
18
|
+
|
|
19
|
+
// Detected culture
|
|
20
|
+
const { detectedCulture } = DomUtils;
|
|
21
|
+
|
|
22
|
+
// Supported cultures
|
|
23
|
+
const supportedCultures: DataTypes.CultureDefinition[] = [zhCN({})];
|
|
24
|
+
|
|
25
|
+
// Supported regions
|
|
26
|
+
const supportedRegions = ['CN'];
|
|
27
|
+
|
|
28
|
+
// Class implementation for tests
|
|
29
|
+
class NotificationTest extends Notification<any, NotificationCallProps> {
|
|
30
|
+
render(props: NotificationRenderProps, className?: string, options?: any) {
|
|
31
|
+
throw new Error('Method not implemented.');
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
class NotificationContainerTest extends NotificationContainer<
|
|
36
|
+
any,
|
|
37
|
+
NotificationCallProps
|
|
38
|
+
> {
|
|
39
|
+
protected addRaw(
|
|
40
|
+
data: INotificaseBase<any, NotificationCallProps>
|
|
41
|
+
): INotification<any, NotificationCallProps> {
|
|
42
|
+
throw new Error('Method not implemented.');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Container
|
|
47
|
+
var container = new NotificationContainerTest((update) => {});
|
|
48
|
+
|
|
49
|
+
// Arrange
|
|
50
|
+
class CoreAppTest extends CoreApp<IAppSettings, {}, NotificationCallProps> {
|
|
51
|
+
/**
|
|
52
|
+
* Constructor
|
|
53
|
+
* @param settings Settings
|
|
54
|
+
* @param name Application name
|
|
55
|
+
*/
|
|
56
|
+
constructor() {
|
|
57
|
+
super(
|
|
58
|
+
{
|
|
59
|
+
/**
|
|
60
|
+
* Endpoint of the API service
|
|
61
|
+
*/
|
|
62
|
+
endpoint: 'http://{hostname}/com.etsoo.SmartERPApi/api/',
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* App root url
|
|
66
|
+
*/
|
|
67
|
+
homepage: '',
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Web url of the cloud
|
|
71
|
+
*/
|
|
72
|
+
webUrl: 'http://localhost',
|
|
73
|
+
|
|
74
|
+
// Authorization scheme
|
|
75
|
+
authScheme: ApiAuthorizationScheme.Bearer,
|
|
76
|
+
|
|
77
|
+
// Detected culture
|
|
78
|
+
detectedCulture,
|
|
79
|
+
|
|
80
|
+
// Supported cultures
|
|
81
|
+
cultures: supportedCultures,
|
|
82
|
+
|
|
83
|
+
// Supported regions
|
|
84
|
+
regions: supportedRegions,
|
|
85
|
+
|
|
86
|
+
// Browser's time zone
|
|
87
|
+
timeZone: Utils.getTimeZone(),
|
|
88
|
+
|
|
89
|
+
// Current country or region
|
|
90
|
+
currentRegion: AddressUtils.getRegion(
|
|
91
|
+
supportedRegions,
|
|
92
|
+
detectedCountry,
|
|
93
|
+
detectedCulture
|
|
94
|
+
),
|
|
95
|
+
|
|
96
|
+
// Current culture
|
|
97
|
+
currentCulture: DomUtils.getCulture(
|
|
98
|
+
supportedCultures,
|
|
99
|
+
detectedCulture
|
|
100
|
+
)!
|
|
101
|
+
},
|
|
102
|
+
createClient(),
|
|
103
|
+
container,
|
|
104
|
+
'SmartERP'
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
freshCountdownUI(callback?: () => PromiseLike<unknown>): void {
|
|
109
|
+
throw new Error('Method not implemented.');
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const app = new CoreAppTest();
|
|
114
|
+
|
|
115
|
+
test('Tests for encrypt / decrypt', () => {
|
|
116
|
+
// Arrange
|
|
117
|
+
const input = 'Hello, world!';
|
|
118
|
+
const passphrase = 'My password';
|
|
119
|
+
|
|
120
|
+
// Act
|
|
121
|
+
const encrypted = app.encrypt(input, passphrase);
|
|
122
|
+
const plain = app.decrypt(encrypted, passphrase);
|
|
123
|
+
expect(plain).toEqual(input);
|
|
124
|
+
});
|
package/lib/cjs/app/CoreApp.d.ts
CHANGED
|
@@ -133,9 +133,10 @@ export interface ICoreApp<S extends IAppSettings, N, C extends NotificationCallP
|
|
|
133
133
|
* Encrypt message
|
|
134
134
|
* @param message Message
|
|
135
135
|
* @param passphrase Secret passphrase
|
|
136
|
+
* @param iterations Iterations, 1000 times, 1 - 99
|
|
136
137
|
* @returns Result
|
|
137
138
|
*/
|
|
138
|
-
encrypt(message: string, passphrase: string): string;
|
|
139
|
+
encrypt(message: string, passphrase: string, iterations?: number): string;
|
|
139
140
|
/**
|
|
140
141
|
* Format date to string
|
|
141
142
|
* @param input Input date
|
|
@@ -216,6 +217,13 @@ export interface ICoreApp<S extends IAppSettings, N, C extends NotificationCallP
|
|
|
216
217
|
* @returns Time zone
|
|
217
218
|
*/
|
|
218
219
|
getTimeZone(): string | undefined;
|
|
220
|
+
/**
|
|
221
|
+
* Hash message, SHA3 or HmacSHA512, 512 as Base64
|
|
222
|
+
* https://cryptojs.gitbook.io/docs/
|
|
223
|
+
* @param message Message
|
|
224
|
+
* @param passphrase Secret passphrase
|
|
225
|
+
*/
|
|
226
|
+
hash(message: string, passphrase?: string): string;
|
|
219
227
|
/**
|
|
220
228
|
* Check use has the specific role permission or not
|
|
221
229
|
* @param roles Roles to check
|
|
@@ -403,9 +411,10 @@ export declare abstract class CoreApp<S extends IAppSettings, N, C extends Notif
|
|
|
403
411
|
* Encrypt message
|
|
404
412
|
* @param message Message
|
|
405
413
|
* @param passphrase Secret passphrase
|
|
414
|
+
* @param iterations Iterations, 1000 times, 1 - 99
|
|
406
415
|
* @returns Result
|
|
407
416
|
*/
|
|
408
|
-
encrypt(message: string, passphrase: string): string;
|
|
417
|
+
encrypt(message: string, passphrase: string, iterations?: number): string;
|
|
409
418
|
/**
|
|
410
419
|
* Enchance secret passphrase
|
|
411
420
|
* @param passphrase Secret passphrase
|
|
@@ -488,6 +497,13 @@ export declare abstract class CoreApp<S extends IAppSettings, N, C extends Notif
|
|
|
488
497
|
* @returns Time zone
|
|
489
498
|
*/
|
|
490
499
|
getTimeZone(): string | undefined;
|
|
500
|
+
/**
|
|
501
|
+
* Hash message, SHA3 or HmacSHA512, 512 as Base64
|
|
502
|
+
* https://cryptojs.gitbook.io/docs/
|
|
503
|
+
* @param message Message
|
|
504
|
+
* @param passphrase Secret passphrase
|
|
505
|
+
*/
|
|
506
|
+
hash(message: string, passphrase?: string): string;
|
|
491
507
|
/**
|
|
492
508
|
* Check use has the specific role permission or not
|
|
493
509
|
* @param roles Roles to check
|
package/lib/cjs/app/CoreApp.js
CHANGED
|
@@ -203,10 +203,25 @@ class CoreApp {
|
|
|
203
203
|
* @returns Pure text
|
|
204
204
|
*/
|
|
205
205
|
decrypt(messageEncrypted, passphrase) {
|
|
206
|
+
// Timestamp splitter
|
|
206
207
|
const pos = messageEncrypted.indexOf('+');
|
|
207
208
|
const timestamp = messageEncrypted.substring(0, pos);
|
|
208
209
|
const message = messageEncrypted.substring(pos + 1);
|
|
209
|
-
|
|
210
|
+
// Iterations
|
|
211
|
+
const iterations = parseInt(message.substring(0, 2), 10);
|
|
212
|
+
const salt = crypto_js_1.enc.Hex.parse(message.substring(2, 34));
|
|
213
|
+
const iv = crypto_js_1.enc.Hex.parse(message.substring(34, 66));
|
|
214
|
+
const encrypted = message.substring(66);
|
|
215
|
+
const key = (0, crypto_js_1.PBKDF2)(this.encryptionEnhance(passphrase, timestamp), salt, {
|
|
216
|
+
keySize: 8,
|
|
217
|
+
hasher: crypto_js_1.algo.SHA256,
|
|
218
|
+
iterations: 1000 * iterations
|
|
219
|
+
});
|
|
220
|
+
return crypto_js_1.AES.decrypt(encrypted, key, {
|
|
221
|
+
iv,
|
|
222
|
+
padding: crypto_js_1.pad.Pkcs7,
|
|
223
|
+
mode: crypto_js_1.mode.CBC
|
|
224
|
+
}).toString(crypto_js_1.enc.Utf8);
|
|
210
225
|
}
|
|
211
226
|
/**
|
|
212
227
|
* Detect IP data, call only one time
|
|
@@ -245,13 +260,33 @@ class CoreApp {
|
|
|
245
260
|
* Encrypt message
|
|
246
261
|
* @param message Message
|
|
247
262
|
* @param passphrase Secret passphrase
|
|
263
|
+
* @param iterations Iterations, 1000 times, 1 - 99
|
|
248
264
|
* @returns Result
|
|
249
265
|
*/
|
|
250
|
-
encrypt(message, passphrase) {
|
|
266
|
+
encrypt(message, passphrase, iterations) {
|
|
267
|
+
// Default 1 * 1000
|
|
268
|
+
iterations !== null && iterations !== void 0 ? iterations : (iterations = 1);
|
|
269
|
+
// Timestamp
|
|
251
270
|
const timestamp = shared_1.Utils.numberToChars(new Date().getTime());
|
|
271
|
+
const bits = 16; // 128 / 8
|
|
272
|
+
const salt = crypto_js_1.lib.WordArray.random(bits);
|
|
273
|
+
const key = (0, crypto_js_1.PBKDF2)(this.encryptionEnhance(passphrase, timestamp), salt, {
|
|
274
|
+
keySize: 8,
|
|
275
|
+
hasher: crypto_js_1.algo.SHA256,
|
|
276
|
+
iterations: 1000 * iterations
|
|
277
|
+
});
|
|
278
|
+
const iv = crypto_js_1.lib.WordArray.random(bits);
|
|
252
279
|
return (timestamp +
|
|
253
280
|
'+' +
|
|
254
|
-
|
|
281
|
+
iterations.toString().padStart(2, '0') +
|
|
282
|
+
salt.toString(crypto_js_1.enc.Hex) +
|
|
283
|
+
iv.toString(crypto_js_1.enc.Hex) +
|
|
284
|
+
crypto_js_1.AES.encrypt(message, key, {
|
|
285
|
+
iv,
|
|
286
|
+
padding: crypto_js_1.pad.Pkcs7,
|
|
287
|
+
mode: crypto_js_1.mode.CBC
|
|
288
|
+
}).toString() // enc.Base64
|
|
289
|
+
);
|
|
255
290
|
}
|
|
256
291
|
/**
|
|
257
292
|
* Enchance secret passphrase
|
|
@@ -391,6 +426,18 @@ class CoreApp {
|
|
|
391
426
|
// settings.timeZone = Utils.getTimeZone()
|
|
392
427
|
return (_a = this.settings.timeZone) !== null && _a !== void 0 ? _a : (_b = this.ipData) === null || _b === void 0 ? void 0 : _b.timezone;
|
|
393
428
|
}
|
|
429
|
+
/**
|
|
430
|
+
* Hash message, SHA3 or HmacSHA512, 512 as Base64
|
|
431
|
+
* https://cryptojs.gitbook.io/docs/
|
|
432
|
+
* @param message Message
|
|
433
|
+
* @param passphrase Secret passphrase
|
|
434
|
+
*/
|
|
435
|
+
hash(message, passphrase) {
|
|
436
|
+
if (passphrase == null)
|
|
437
|
+
return (0, crypto_js_1.SHA3)(message, { outputLength: 512 }).toString(crypto_js_1.enc.Base64);
|
|
438
|
+
else
|
|
439
|
+
return (0, crypto_js_1.HmacSHA512)(message, passphrase).toString(crypto_js_1.enc.Base64);
|
|
440
|
+
}
|
|
394
441
|
/**
|
|
395
442
|
* Check use has the specific role permission or not
|
|
396
443
|
* @param roles Roles to check
|
package/lib/mjs/app/CoreApp.d.ts
CHANGED
|
@@ -133,9 +133,10 @@ export interface ICoreApp<S extends IAppSettings, N, C extends NotificationCallP
|
|
|
133
133
|
* Encrypt message
|
|
134
134
|
* @param message Message
|
|
135
135
|
* @param passphrase Secret passphrase
|
|
136
|
+
* @param iterations Iterations, 1000 times, 1 - 99
|
|
136
137
|
* @returns Result
|
|
137
138
|
*/
|
|
138
|
-
encrypt(message: string, passphrase: string): string;
|
|
139
|
+
encrypt(message: string, passphrase: string, iterations?: number): string;
|
|
139
140
|
/**
|
|
140
141
|
* Format date to string
|
|
141
142
|
* @param input Input date
|
|
@@ -216,6 +217,13 @@ export interface ICoreApp<S extends IAppSettings, N, C extends NotificationCallP
|
|
|
216
217
|
* @returns Time zone
|
|
217
218
|
*/
|
|
218
219
|
getTimeZone(): string | undefined;
|
|
220
|
+
/**
|
|
221
|
+
* Hash message, SHA3 or HmacSHA512, 512 as Base64
|
|
222
|
+
* https://cryptojs.gitbook.io/docs/
|
|
223
|
+
* @param message Message
|
|
224
|
+
* @param passphrase Secret passphrase
|
|
225
|
+
*/
|
|
226
|
+
hash(message: string, passphrase?: string): string;
|
|
219
227
|
/**
|
|
220
228
|
* Check use has the specific role permission or not
|
|
221
229
|
* @param roles Roles to check
|
|
@@ -403,9 +411,10 @@ export declare abstract class CoreApp<S extends IAppSettings, N, C extends Notif
|
|
|
403
411
|
* Encrypt message
|
|
404
412
|
* @param message Message
|
|
405
413
|
* @param passphrase Secret passphrase
|
|
414
|
+
* @param iterations Iterations, 1000 times, 1 - 99
|
|
406
415
|
* @returns Result
|
|
407
416
|
*/
|
|
408
|
-
encrypt(message: string, passphrase: string): string;
|
|
417
|
+
encrypt(message: string, passphrase: string, iterations?: number): string;
|
|
409
418
|
/**
|
|
410
419
|
* Enchance secret passphrase
|
|
411
420
|
* @param passphrase Secret passphrase
|
|
@@ -488,6 +497,13 @@ export declare abstract class CoreApp<S extends IAppSettings, N, C extends Notif
|
|
|
488
497
|
* @returns Time zone
|
|
489
498
|
*/
|
|
490
499
|
getTimeZone(): string | undefined;
|
|
500
|
+
/**
|
|
501
|
+
* Hash message, SHA3 or HmacSHA512, 512 as Base64
|
|
502
|
+
* https://cryptojs.gitbook.io/docs/
|
|
503
|
+
* @param message Message
|
|
504
|
+
* @param passphrase Secret passphrase
|
|
505
|
+
*/
|
|
506
|
+
hash(message: string, passphrase?: string): string;
|
|
491
507
|
/**
|
|
492
508
|
* Check use has the specific role permission or not
|
|
493
509
|
* @param roles Roles to check
|
package/lib/mjs/app/CoreApp.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { NotificationAlign, NotificationMessageType } from '@etsoo/notificationbase';
|
|
2
2
|
import { ApiDataError } from '@etsoo/restclient';
|
|
3
3
|
import { DateUtils, DomUtils, NumberUtils, StorageUtils, Utils } from '@etsoo/shared';
|
|
4
|
-
import { AES } from 'crypto-js';
|
|
4
|
+
import { AES, algo, enc, HmacSHA512, lib, mode, pad, PBKDF2, SHA3 } from 'crypto-js';
|
|
5
5
|
import { AddressRegion } from '../address/AddressRegion';
|
|
6
6
|
import { AddressUtils } from '../address/AddressUtils';
|
|
7
7
|
import { ActionResultError } from '../result/ActionResultError';
|
|
@@ -200,10 +200,25 @@ export class CoreApp {
|
|
|
200
200
|
* @returns Pure text
|
|
201
201
|
*/
|
|
202
202
|
decrypt(messageEncrypted, passphrase) {
|
|
203
|
+
// Timestamp splitter
|
|
203
204
|
const pos = messageEncrypted.indexOf('+');
|
|
204
205
|
const timestamp = messageEncrypted.substring(0, pos);
|
|
205
206
|
const message = messageEncrypted.substring(pos + 1);
|
|
206
|
-
|
|
207
|
+
// Iterations
|
|
208
|
+
const iterations = parseInt(message.substring(0, 2), 10);
|
|
209
|
+
const salt = enc.Hex.parse(message.substring(2, 34));
|
|
210
|
+
const iv = enc.Hex.parse(message.substring(34, 66));
|
|
211
|
+
const encrypted = message.substring(66);
|
|
212
|
+
const key = PBKDF2(this.encryptionEnhance(passphrase, timestamp), salt, {
|
|
213
|
+
keySize: 8,
|
|
214
|
+
hasher: algo.SHA256,
|
|
215
|
+
iterations: 1000 * iterations
|
|
216
|
+
});
|
|
217
|
+
return AES.decrypt(encrypted, key, {
|
|
218
|
+
iv,
|
|
219
|
+
padding: pad.Pkcs7,
|
|
220
|
+
mode: mode.CBC
|
|
221
|
+
}).toString(enc.Utf8);
|
|
207
222
|
}
|
|
208
223
|
/**
|
|
209
224
|
* Detect IP data, call only one time
|
|
@@ -242,13 +257,33 @@ export class CoreApp {
|
|
|
242
257
|
* Encrypt message
|
|
243
258
|
* @param message Message
|
|
244
259
|
* @param passphrase Secret passphrase
|
|
260
|
+
* @param iterations Iterations, 1000 times, 1 - 99
|
|
245
261
|
* @returns Result
|
|
246
262
|
*/
|
|
247
|
-
encrypt(message, passphrase) {
|
|
263
|
+
encrypt(message, passphrase, iterations) {
|
|
264
|
+
// Default 1 * 1000
|
|
265
|
+
iterations !== null && iterations !== void 0 ? iterations : (iterations = 1);
|
|
266
|
+
// Timestamp
|
|
248
267
|
const timestamp = Utils.numberToChars(new Date().getTime());
|
|
268
|
+
const bits = 16; // 128 / 8
|
|
269
|
+
const salt = lib.WordArray.random(bits);
|
|
270
|
+
const key = PBKDF2(this.encryptionEnhance(passphrase, timestamp), salt, {
|
|
271
|
+
keySize: 8,
|
|
272
|
+
hasher: algo.SHA256,
|
|
273
|
+
iterations: 1000 * iterations
|
|
274
|
+
});
|
|
275
|
+
const iv = lib.WordArray.random(bits);
|
|
249
276
|
return (timestamp +
|
|
250
277
|
'+' +
|
|
251
|
-
|
|
278
|
+
iterations.toString().padStart(2, '0') +
|
|
279
|
+
salt.toString(enc.Hex) +
|
|
280
|
+
iv.toString(enc.Hex) +
|
|
281
|
+
AES.encrypt(message, key, {
|
|
282
|
+
iv,
|
|
283
|
+
padding: pad.Pkcs7,
|
|
284
|
+
mode: mode.CBC
|
|
285
|
+
}).toString() // enc.Base64
|
|
286
|
+
);
|
|
252
287
|
}
|
|
253
288
|
/**
|
|
254
289
|
* Enchance secret passphrase
|
|
@@ -388,6 +423,18 @@ export class CoreApp {
|
|
|
388
423
|
// settings.timeZone = Utils.getTimeZone()
|
|
389
424
|
return (_a = this.settings.timeZone) !== null && _a !== void 0 ? _a : (_b = this.ipData) === null || _b === void 0 ? void 0 : _b.timezone;
|
|
390
425
|
}
|
|
426
|
+
/**
|
|
427
|
+
* Hash message, SHA3 or HmacSHA512, 512 as Base64
|
|
428
|
+
* https://cryptojs.gitbook.io/docs/
|
|
429
|
+
* @param message Message
|
|
430
|
+
* @param passphrase Secret passphrase
|
|
431
|
+
*/
|
|
432
|
+
hash(message, passphrase) {
|
|
433
|
+
if (passphrase == null)
|
|
434
|
+
return SHA3(message, { outputLength: 512 }).toString(enc.Base64);
|
|
435
|
+
else
|
|
436
|
+
return HmacSHA512(message, passphrase).toString(enc.Base64);
|
|
437
|
+
}
|
|
391
438
|
/**
|
|
392
439
|
* Check use has the specific role permission or not
|
|
393
440
|
* @param roles Roles to check
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@etsoo/appscript",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.67",
|
|
4
4
|
"description": "Applications shared TypeScript framework",
|
|
5
5
|
"main": "lib/cjs/index.js",
|
|
6
6
|
"module": "lib/mjs/index.js",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"testMatch": [
|
|
24
24
|
"<rootDir>/__tests__/**/*.ts"
|
|
25
25
|
],
|
|
26
|
-
"testEnvironment": "
|
|
26
|
+
"testEnvironment": "jsdom",
|
|
27
27
|
"transform": {
|
|
28
28
|
".+\\.jsx?$": "babel-jest",
|
|
29
29
|
".+\\.tsx?$": "ts-jest"
|
|
@@ -65,9 +65,9 @@
|
|
|
65
65
|
"@babel/preset-env": "^7.16.4",
|
|
66
66
|
"@babel/runtime-corejs3": "^7.16.3",
|
|
67
67
|
"@types/jest": "^27.0.3",
|
|
68
|
-
"@typescript-eslint/eslint-plugin": "^5.
|
|
69
|
-
"@typescript-eslint/parser": "^5.
|
|
70
|
-
"eslint": "^8.4.
|
|
68
|
+
"@typescript-eslint/eslint-plugin": "^5.6.0",
|
|
69
|
+
"@typescript-eslint/parser": "^5.6.0",
|
|
70
|
+
"eslint": "^8.4.1",
|
|
71
71
|
"eslint-config-airbnb-base": "^15.0.0",
|
|
72
72
|
"eslint-plugin-import": "^2.25.3",
|
|
73
73
|
"jest": "^27.4.3",
|
package/src/app/CoreApp.ts
CHANGED
|
@@ -15,7 +15,17 @@ import {
|
|
|
15
15
|
StorageUtils,
|
|
16
16
|
Utils
|
|
17
17
|
} from '@etsoo/shared';
|
|
18
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
AES,
|
|
20
|
+
algo,
|
|
21
|
+
enc,
|
|
22
|
+
HmacSHA512,
|
|
23
|
+
lib,
|
|
24
|
+
mode,
|
|
25
|
+
pad,
|
|
26
|
+
PBKDF2,
|
|
27
|
+
SHA3
|
|
28
|
+
} from 'crypto-js';
|
|
19
29
|
import { AddressRegion } from '../address/AddressRegion';
|
|
20
30
|
import { AddressUtils } from '../address/AddressUtils';
|
|
21
31
|
import { ActionResultError } from '../result/ActionResultError';
|
|
@@ -184,9 +194,10 @@ export interface ICoreApp<
|
|
|
184
194
|
* Encrypt message
|
|
185
195
|
* @param message Message
|
|
186
196
|
* @param passphrase Secret passphrase
|
|
197
|
+
* @param iterations Iterations, 1000 times, 1 - 99
|
|
187
198
|
* @returns Result
|
|
188
199
|
*/
|
|
189
|
-
encrypt(message: string, passphrase: string): string;
|
|
200
|
+
encrypt(message: string, passphrase: string, iterations?: number): string;
|
|
190
201
|
|
|
191
202
|
/**
|
|
192
203
|
* Format date to string
|
|
@@ -290,6 +301,14 @@ export interface ICoreApp<
|
|
|
290
301
|
*/
|
|
291
302
|
getTimeZone(): string | undefined;
|
|
292
303
|
|
|
304
|
+
/**
|
|
305
|
+
* Hash message, SHA3 or HmacSHA512, 512 as Base64
|
|
306
|
+
* https://cryptojs.gitbook.io/docs/
|
|
307
|
+
* @param message Message
|
|
308
|
+
* @param passphrase Secret passphrase
|
|
309
|
+
*/
|
|
310
|
+
hash(message: string, passphrase?: string): string;
|
|
311
|
+
|
|
293
312
|
/**
|
|
294
313
|
* Check use has the specific role permission or not
|
|
295
314
|
* @param roles Roles to check
|
|
@@ -660,13 +679,33 @@ export abstract class CoreApp<
|
|
|
660
679
|
* @returns Pure text
|
|
661
680
|
*/
|
|
662
681
|
decrypt(messageEncrypted: string, passphrase: string) {
|
|
682
|
+
// Timestamp splitter
|
|
663
683
|
const pos = messageEncrypted.indexOf('+');
|
|
664
684
|
const timestamp = messageEncrypted.substring(0, pos);
|
|
665
685
|
const message = messageEncrypted.substring(pos + 1);
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
686
|
+
|
|
687
|
+
// Iterations
|
|
688
|
+
const iterations = parseInt(message.substring(0, 2), 10);
|
|
689
|
+
|
|
690
|
+
const salt = enc.Hex.parse(message.substring(2, 34));
|
|
691
|
+
const iv = enc.Hex.parse(message.substring(34, 66));
|
|
692
|
+
const encrypted = message.substring(66);
|
|
693
|
+
|
|
694
|
+
const key = PBKDF2(
|
|
695
|
+
this.encryptionEnhance(passphrase, timestamp),
|
|
696
|
+
salt,
|
|
697
|
+
{
|
|
698
|
+
keySize: 8, // 256 / 32
|
|
699
|
+
hasher: algo.SHA256,
|
|
700
|
+
iterations: 1000 * iterations
|
|
701
|
+
}
|
|
702
|
+
);
|
|
703
|
+
|
|
704
|
+
return AES.decrypt(encrypted, key, {
|
|
705
|
+
iv,
|
|
706
|
+
padding: pad.Pkcs7,
|
|
707
|
+
mode: mode.CBC
|
|
708
|
+
}).toString(enc.Utf8);
|
|
670
709
|
}
|
|
671
710
|
|
|
672
711
|
/**
|
|
@@ -713,17 +752,40 @@ export abstract class CoreApp<
|
|
|
713
752
|
* Encrypt message
|
|
714
753
|
* @param message Message
|
|
715
754
|
* @param passphrase Secret passphrase
|
|
755
|
+
* @param iterations Iterations, 1000 times, 1 - 99
|
|
716
756
|
* @returns Result
|
|
717
757
|
*/
|
|
718
|
-
encrypt(message: string, passphrase: string) {
|
|
758
|
+
encrypt(message: string, passphrase: string, iterations?: number) {
|
|
759
|
+
// Default 1 * 1000
|
|
760
|
+
iterations ??= 1;
|
|
761
|
+
|
|
762
|
+
// Timestamp
|
|
719
763
|
const timestamp = Utils.numberToChars(new Date().getTime());
|
|
764
|
+
|
|
765
|
+
const bits = 16; // 128 / 8
|
|
766
|
+
const salt = lib.WordArray.random(bits);
|
|
767
|
+
const key = PBKDF2(
|
|
768
|
+
this.encryptionEnhance(passphrase, timestamp),
|
|
769
|
+
salt,
|
|
770
|
+
{
|
|
771
|
+
keySize: 8, // 256 / 32
|
|
772
|
+
hasher: algo.SHA256,
|
|
773
|
+
iterations: 1000 * iterations
|
|
774
|
+
}
|
|
775
|
+
);
|
|
776
|
+
const iv = lib.WordArray.random(bits);
|
|
777
|
+
|
|
720
778
|
return (
|
|
721
779
|
timestamp +
|
|
722
780
|
'+' +
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
781
|
+
iterations.toString().padStart(2, '0') +
|
|
782
|
+
salt.toString(enc.Hex) +
|
|
783
|
+
iv.toString(enc.Hex) +
|
|
784
|
+
AES.encrypt(message, key, {
|
|
785
|
+
iv,
|
|
786
|
+
padding: pad.Pkcs7,
|
|
787
|
+
mode: mode.CBC
|
|
788
|
+
}).toString() // enc.Base64
|
|
727
789
|
);
|
|
728
790
|
}
|
|
729
791
|
|
|
@@ -897,6 +959,18 @@ export abstract class CoreApp<
|
|
|
897
959
|
return this.settings.timeZone ?? this.ipData?.timezone;
|
|
898
960
|
}
|
|
899
961
|
|
|
962
|
+
/**
|
|
963
|
+
* Hash message, SHA3 or HmacSHA512, 512 as Base64
|
|
964
|
+
* https://cryptojs.gitbook.io/docs/
|
|
965
|
+
* @param message Message
|
|
966
|
+
* @param passphrase Secret passphrase
|
|
967
|
+
*/
|
|
968
|
+
hash(message: string, passphrase?: string) {
|
|
969
|
+
if (passphrase == null)
|
|
970
|
+
return SHA3(message, { outputLength: 512 }).toString(enc.Base64);
|
|
971
|
+
else return HmacSHA512(message, passphrase).toString(enc.Base64);
|
|
972
|
+
}
|
|
973
|
+
|
|
900
974
|
/**
|
|
901
975
|
* Check use has the specific role permission or not
|
|
902
976
|
* @param roles Roles to check
|