@crossdyne/security 0.2.0-beta.1
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/LICENSE +21 -0
- package/README.md +15 -0
- package/dist/index.cjs +249 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +47 -0
- package/dist/index.d.ts +47 -0
- package/dist/index.js +244 -0
- package/dist/index.js.map +1 -0
- package/package.json +52 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Crossdyne
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# @quantropic/security
|
|
2
|
+
|
|
3
|
+
Security library with SRP authentication and cryptography utilities.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- SRP (Secure Remote Password) protocol implementation
|
|
8
|
+
- Cryptographic utilities
|
|
9
|
+
- Key derivation functions
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install @quantropic/security
|
|
15
|
+
```
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/utils/security.utils.ts
|
|
4
|
+
var SecurityUtils = class {
|
|
5
|
+
/*
|
|
6
|
+
Base64 helpers
|
|
7
|
+
*/
|
|
8
|
+
static toBase64(bytes) {
|
|
9
|
+
let binary = "";
|
|
10
|
+
for (let i = 0; i < bytes.byteLength; i++)
|
|
11
|
+
binary += String.fromCharCode(bytes[i]);
|
|
12
|
+
return window.btoa(binary);
|
|
13
|
+
}
|
|
14
|
+
static fromBase64(base64) {
|
|
15
|
+
const cleaned = base64.replace(/-/g, "+").replace(/_/g, "/").replace(/\s/g, "");
|
|
16
|
+
const binary = window.atob(cleaned);
|
|
17
|
+
const bytes = new Uint8Array(binary.length);
|
|
18
|
+
for (let i = 0; i < binary.length; i++)
|
|
19
|
+
bytes[i] = binary.charCodeAt(i);
|
|
20
|
+
return bytes;
|
|
21
|
+
}
|
|
22
|
+
static bigIntToFixedBytes(bn, length) {
|
|
23
|
+
let hex = bn.toString(16);
|
|
24
|
+
if (hex.length % 2 !== 0)
|
|
25
|
+
hex = "0" + hex;
|
|
26
|
+
if (hex.length > length * 2)
|
|
27
|
+
throw new Error(`\u0417\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u0441\u043B\u0438\u0448\u043A\u043E\u043C \u0432\u0435\u043B\u0438\u043A\u043E \u0434\u043B\u044F ${length} \u0431\u0430\u0439\u0442`);
|
|
28
|
+
hex = hex.padStart(length * 2, "0");
|
|
29
|
+
return this.fromHex(hex);
|
|
30
|
+
}
|
|
31
|
+
static fromHex(hex) {
|
|
32
|
+
const matches = hex.match(/.{1,2}/g);
|
|
33
|
+
return new Uint8Array(matches ? matches.map((byte) => parseInt(byte, 16)) : []);
|
|
34
|
+
}
|
|
35
|
+
/*
|
|
36
|
+
Math
|
|
37
|
+
*/
|
|
38
|
+
static expMod(base, exp, mod) {
|
|
39
|
+
let res = BigInt(1);
|
|
40
|
+
base = base % mod;
|
|
41
|
+
while (exp > 0) {
|
|
42
|
+
if (exp % BigInt(2) === BigInt(1)) res = res * base % mod;
|
|
43
|
+
base = base * base % mod;
|
|
44
|
+
exp = exp / BigInt(2);
|
|
45
|
+
}
|
|
46
|
+
return res;
|
|
47
|
+
}
|
|
48
|
+
/*
|
|
49
|
+
SRP helper
|
|
50
|
+
*/
|
|
51
|
+
static async hashAsPerSrp6a(items) {
|
|
52
|
+
const buffers = items.map(({ value, length }) => this.bigIntToFixedBytes(value, length));
|
|
53
|
+
const totalLen = buffers.reduce((sum, buf) => sum + buf.length, 0);
|
|
54
|
+
const combined = new Uint8Array(totalLen);
|
|
55
|
+
let offset = 0;
|
|
56
|
+
for (const buf of buffers) {
|
|
57
|
+
combined.set(buf, offset);
|
|
58
|
+
offset += buf.length;
|
|
59
|
+
}
|
|
60
|
+
const hash = await crypto.subtle.digest("SHA-256", combined);
|
|
61
|
+
return this.bytesToBigInt(new Uint8Array(hash));
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
SecurityUtils.MODULUS_SIZE = 384;
|
|
65
|
+
SecurityUtils.NONCE_SIZE = 12;
|
|
66
|
+
SecurityUtils.TAG_SIZE = 16;
|
|
67
|
+
SecurityUtils.KEY_SIZE = 32;
|
|
68
|
+
SecurityUtils.N = BigInt("0xAC6BDB41324A9A9BF166DE5E1F403D434A6E1B3B94A7E62AC1211858E002C75AD4455C9D19C0A3180296917A376205164043E20144FF485719D181A99EB574671AC58054457ED444A67032EA17D03AD43464D2397449CA593630A670D90D95A78E846A3C8AF80862098D80F33C42ED7059E75225E0A52718E2379369F65B79680A6560B080092EE71986066735A96A7D42E7597116742B02D3A154471B6A23D84E0D642C790D597A2BB7F5A48F734898BDD138C69493E723491959C1B4BD40C91C1C7924F88D046467A006507E781220A80C55A927906A7C6C9C227E674686DD5D1B855D28F0D604E24586C608630B9A34C4808381A54F0D9080A5F90B60187F");
|
|
69
|
+
SecurityUtils.g = BigInt(2);
|
|
70
|
+
SecurityUtils.k = BigInt("0xD55AE1AEC9F9115621E93E16E5DE4517DF8450D0957024D1256AA32B71E4E412");
|
|
71
|
+
/*
|
|
72
|
+
BigInt helpers
|
|
73
|
+
*/
|
|
74
|
+
SecurityUtils.bytesToBigInt = (bytes) => BigInt("0x" + Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join(""));
|
|
75
|
+
|
|
76
|
+
// src/crypto/crypto.service.ts
|
|
77
|
+
var CryptoService = class {
|
|
78
|
+
constructor() {
|
|
79
|
+
this.generateRandomBytes = (length = 32) => crypto.getRandomValues(new Uint8Array(length));
|
|
80
|
+
}
|
|
81
|
+
async encryptData(dataModel, key) {
|
|
82
|
+
const encoder = new TextEncoder();
|
|
83
|
+
let jsonString;
|
|
84
|
+
if (dataModel instanceof Uint8Array) {
|
|
85
|
+
jsonString = `"${SecurityUtils.toBase64(dataModel)}"`;
|
|
86
|
+
} else {
|
|
87
|
+
jsonString = JSON.stringify(dataModel);
|
|
88
|
+
}
|
|
89
|
+
const plainBytes = encoder.encode(jsonString);
|
|
90
|
+
const nonce = crypto.getRandomValues(new Uint8Array(SecurityUtils.NONCE_SIZE));
|
|
91
|
+
const cryptoKey = await crypto.subtle.importKey(
|
|
92
|
+
"raw",
|
|
93
|
+
key,
|
|
94
|
+
"AES-GCM",
|
|
95
|
+
false,
|
|
96
|
+
["encrypt"]
|
|
97
|
+
);
|
|
98
|
+
const encryptedContent = await crypto.subtle.encrypt(
|
|
99
|
+
{
|
|
100
|
+
name: "AES-GCM",
|
|
101
|
+
iv: nonce,
|
|
102
|
+
tagLength: SecurityUtils.TAG_SIZE * 8
|
|
103
|
+
},
|
|
104
|
+
cryptoKey,
|
|
105
|
+
plainBytes
|
|
106
|
+
);
|
|
107
|
+
const result = new Uint8Array(SecurityUtils.NONCE_SIZE + encryptedContent.byteLength);
|
|
108
|
+
result.set(nonce, 0);
|
|
109
|
+
result.set(new Uint8Array(encryptedContent), SecurityUtils.NONCE_SIZE);
|
|
110
|
+
return SecurityUtils.toBase64(result);
|
|
111
|
+
}
|
|
112
|
+
async decryptedData(encryptedBase64, key) {
|
|
113
|
+
if (!encryptedBase64)
|
|
114
|
+
return null;
|
|
115
|
+
const encryptedBytes = SecurityUtils.fromBase64(encryptedBase64);
|
|
116
|
+
if (encryptedBytes.length < SecurityUtils.NONCE_SIZE + SecurityUtils.TAG_SIZE)
|
|
117
|
+
throw new Error("\u041D\u0435\u0434\u043E\u043F\u0443\u0441\u0442\u0438\u043C\u044B\u0439 \u0444\u043E\u0440\u043C\u0430\u0442 \u0437\u0430\u0448\u0438\u0444\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0445 \u0434\u0430\u043D\u043D\u044B\u0445");
|
|
118
|
+
const nonce = encryptedBytes.slice(0, SecurityUtils.NONCE_SIZE);
|
|
119
|
+
const ciphertextWithTag = encryptedBytes.slice(SecurityUtils.NONCE_SIZE);
|
|
120
|
+
const cryptoKey = await crypto.subtle.importKey(
|
|
121
|
+
"raw",
|
|
122
|
+
key,
|
|
123
|
+
"AES-GCM",
|
|
124
|
+
false,
|
|
125
|
+
["decrypt"]
|
|
126
|
+
);
|
|
127
|
+
try {
|
|
128
|
+
const decryptedBuffer = await crypto.subtle.decrypt(
|
|
129
|
+
{
|
|
130
|
+
name: "AES-GCM",
|
|
131
|
+
iv: nonce,
|
|
132
|
+
tagLength: SecurityUtils.TAG_SIZE * 8
|
|
133
|
+
},
|
|
134
|
+
cryptoKey,
|
|
135
|
+
ciphertextWithTag
|
|
136
|
+
);
|
|
137
|
+
const decoder = new TextDecoder();
|
|
138
|
+
const jsonString = decoder.decode(decryptedBuffer);
|
|
139
|
+
return JSON.parse(jsonString);
|
|
140
|
+
} catch (e) {
|
|
141
|
+
console.error("\u041E\u0448\u0438\u0431\u043A\u0430 \u0434\u0435\u0448\u0438\u0444\u0440\u043E\u0432\u0430\u043D\u0438\u044F:", e);
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
// src/crypto/key-derivation.service.ts
|
|
148
|
+
var KeyDerivationService = class {
|
|
149
|
+
constructor() {
|
|
150
|
+
this.ITERATIONS = 6e5;
|
|
151
|
+
this.KEY_SIZE = 32;
|
|
152
|
+
}
|
|
153
|
+
async deriveKeysFromPassword(login, password, salt) {
|
|
154
|
+
const safeSalt = new Uint8Array(salt);
|
|
155
|
+
const encoder = new TextEncoder();
|
|
156
|
+
const normalizedLogin = login.trim().toLocaleLowerCase();
|
|
157
|
+
const combinedPassword = `${normalizedLogin}:${password}`;
|
|
158
|
+
const passwordBytes = encoder.encode(combinedPassword);
|
|
159
|
+
const baseKey = await crypto.subtle.importKey("raw", passwordBytes, "PBKDF2", false, ["deriveBits", "deriveKey"]);
|
|
160
|
+
const masterKeyBits = await crypto.subtle.deriveBits({
|
|
161
|
+
name: "PBKDF2",
|
|
162
|
+
salt: safeSalt,
|
|
163
|
+
iterations: this.ITERATIONS,
|
|
164
|
+
hash: "SHA-256"
|
|
165
|
+
}, baseKey, this.KEY_SIZE * 8);
|
|
166
|
+
const masterKey = await crypto.subtle.importKey("raw", masterKeyBits, "HKDF", false, ["deriveBits"]);
|
|
167
|
+
const kek = await crypto.subtle.deriveBits(
|
|
168
|
+
{ name: "HKDF", hash: "SHA-256", salt: new Uint8Array(0), info: encoder.encode("AES-GCM-KEK-v1") },
|
|
169
|
+
masterKey,
|
|
170
|
+
this.KEY_SIZE * 8
|
|
171
|
+
);
|
|
172
|
+
const authBytes = await crypto.subtle.deriveBits(
|
|
173
|
+
{ name: "HKDF", hash: "SHA-256", salt: new Uint8Array(0), info: encoder.encode("SERVER-AUTH-HASH-v1") },
|
|
174
|
+
masterKey,
|
|
175
|
+
this.KEY_SIZE * 8
|
|
176
|
+
);
|
|
177
|
+
return {
|
|
178
|
+
kek: new Uint8Array(kek),
|
|
179
|
+
authHash: SecurityUtils.toBase64(new Uint8Array(authBytes))
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
// src/srp/srp.service.ts
|
|
185
|
+
var SrpService = class {
|
|
186
|
+
constructor() {
|
|
187
|
+
this.keyDerivation = new KeyDerivationService();
|
|
188
|
+
}
|
|
189
|
+
async generateSrpVerifier(authHash) {
|
|
190
|
+
const x = SecurityUtils.bytesToBigInt(SecurityUtils.fromBase64(authHash));
|
|
191
|
+
const v = SecurityUtils.expMod(SecurityUtils.g, x, SecurityUtils.N);
|
|
192
|
+
return SecurityUtils.toBase64(SecurityUtils.bigIntToFixedBytes(v, SecurityUtils.MODULUS_SIZE));
|
|
193
|
+
}
|
|
194
|
+
async generateSrpProof(login, password, saltBase64, B_base64) {
|
|
195
|
+
const salt = SecurityUtils.fromBase64(saltBase64);
|
|
196
|
+
const { authHash } = await this.keyDerivation.deriveKeysFromPassword(login, password, salt);
|
|
197
|
+
const x = SecurityUtils.bytesToBigInt(SecurityUtils.fromBase64(authHash));
|
|
198
|
+
const aBytes = crypto.getRandomValues(new Uint8Array(32));
|
|
199
|
+
const a = SecurityUtils.bytesToBigInt(aBytes);
|
|
200
|
+
const A = SecurityUtils.expMod(SecurityUtils.g, a, SecurityUtils.N);
|
|
201
|
+
if (A % SecurityUtils.N === 0n)
|
|
202
|
+
throw new Error("\u041A\u0440\u0438\u0442\u0438\u0447\u0435\u0441\u043A\u0430\u044F \u043E\u0448\u0438\u0431\u043A\u0430: A % N === 0");
|
|
203
|
+
const B = SecurityUtils.bytesToBigInt(SecurityUtils.fromBase64(B_base64));
|
|
204
|
+
if (B % SecurityUtils.N === 0n)
|
|
205
|
+
throw new Error("\u041A\u0440\u0438\u0442\u0438\u0447\u0435\u0441\u043A\u0430\u044F \u043E\u0448\u0438\u0431\u043A\u0430: B % N === 0");
|
|
206
|
+
const u = await SecurityUtils.hashAsPerSrp6a([
|
|
207
|
+
{ value: A, length: SecurityUtils.MODULUS_SIZE },
|
|
208
|
+
{ value: B, length: SecurityUtils.MODULUS_SIZE }
|
|
209
|
+
]);
|
|
210
|
+
if (u === 0n)
|
|
211
|
+
throw new Error("\u041D\u0435\u0434\u043E\u043F\u0443\u0441\u0442\u0438\u043C\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435 u");
|
|
212
|
+
const gX = SecurityUtils.expMod(SecurityUtils.g, x, SecurityUtils.N);
|
|
213
|
+
const term = SecurityUtils.k * gX % SecurityUtils.N;
|
|
214
|
+
const base = (B - term + SecurityUtils.N) % SecurityUtils.N;
|
|
215
|
+
const exponent = a + u * x;
|
|
216
|
+
const S = SecurityUtils.expMod(base, exponent, SecurityUtils.N);
|
|
217
|
+
if (S === 0n)
|
|
218
|
+
throw new Error("\u041A\u0440\u0438\u0442\u0438\u0447\u0435\u0441\u043A\u0430\u044F \u043E\u0448\u0438\u0431\u043A\u0430: S === 0");
|
|
219
|
+
const M1 = await SecurityUtils.hashAsPerSrp6a([
|
|
220
|
+
{ value: A, length: SecurityUtils.MODULUS_SIZE },
|
|
221
|
+
{ value: B, length: SecurityUtils.MODULUS_SIZE },
|
|
222
|
+
{ value: S, length: SecurityUtils.MODULUS_SIZE }
|
|
223
|
+
]);
|
|
224
|
+
return {
|
|
225
|
+
A: SecurityUtils.toBase64(SecurityUtils.bigIntToFixedBytes(A, SecurityUtils.MODULUS_SIZE)),
|
|
226
|
+
M1: SecurityUtils.toBase64(SecurityUtils.bigIntToFixedBytes(M1, 32)),
|
|
227
|
+
S: SecurityUtils.toBase64(SecurityUtils.bigIntToFixedBytes(S, SecurityUtils.MODULUS_SIZE))
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
async verifyServerM2(A_b64, M1_b64, S_b64, serverM2_b64) {
|
|
231
|
+
const A = SecurityUtils.bytesToBigInt(SecurityUtils.fromBase64(A_b64));
|
|
232
|
+
const M1 = SecurityUtils.bytesToBigInt(SecurityUtils.fromBase64(M1_b64));
|
|
233
|
+
const S = SecurityUtils.bytesToBigInt(SecurityUtils.fromBase64(S_b64));
|
|
234
|
+
const computedM2 = await SecurityUtils.hashAsPerSrp6a([
|
|
235
|
+
{ value: A, length: SecurityUtils.MODULUS_SIZE },
|
|
236
|
+
{ value: M1, length: 32 },
|
|
237
|
+
{ value: S, length: SecurityUtils.MODULUS_SIZE }
|
|
238
|
+
]);
|
|
239
|
+
const computedM2_b64 = SecurityUtils.toBase64(SecurityUtils.bigIntToFixedBytes(computedM2, 32));
|
|
240
|
+
return serverM2_b64 === computedM2_b64;
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
exports.CryptoService = CryptoService;
|
|
245
|
+
exports.KeyDerivationService = KeyDerivationService;
|
|
246
|
+
exports.SecurityUtils = SecurityUtils;
|
|
247
|
+
exports.SrpService = SrpService;
|
|
248
|
+
//# sourceMappingURL=index.cjs.map
|
|
249
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/security.utils.ts","../src/crypto/crypto.service.ts","../src/crypto/key-derivation.service.ts","../src/srp/srp.service.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["export class SecurityUtils {\r\n static readonly MODULUS_SIZE : number = 384;\r\n static readonly NONCE_SIZE : number = 12;\r\n static readonly TAG_SIZE : number = 16;\r\n static readonly KEY_SIZE : number = 32;\r\n\r\n static readonly N : bigint = BigInt(\"0xAC6BDB41324A9A9BF166DE5E1F403D434A6E1B3B94A7E62AC1211858E002C75AD4455C9D19C0A3180296917A376205164043E20144FF485719D181A99EB574671AC58054457ED444A67032EA17D03AD43464D2397449CA593630A670D90D95A78E846A3C8AF80862098D80F33C42ED7059E75225E0A52718E2379369F65B79680A6560B080092EE71986066735A96A7D42E7597116742B02D3A154471B6A23D84E0D642C790D597A2BB7F5A48F734898BDD138C69493E723491959C1B4BD40C91C1C7924F88D046467A006507E781220A80C55A927906A7C6C9C227E674686DD5D1B855D28F0D604E24586C608630B9A34C4808381A54F0D9080A5F90B60187F\");\r\n static readonly g : bigint = BigInt(2);\r\n static readonly k : bigint = BigInt(\"0xD55AE1AEC9F9115621E93E16E5DE4517DF8450D0957024D1256AA32B71E4E412\");\r\n\r\n /* \r\n Base64 helpers\r\n */\r\n\r\n static toBase64(bytes: Uint8Array): string {\r\n let binary = '';\r\n for (let i = 0; i < bytes.byteLength; i++)\r\n binary += String.fromCharCode(bytes[i]);\r\n\r\n return window.btoa(binary);\r\n }\r\n\r\n static fromBase64(base64: string): Uint8Array {\r\n const cleaned = base64.replace(/-/g, '+').replace(/_/g, '/').replace(/\\s/g, '');\r\n const binary = window.atob(cleaned);\r\n const bytes = new Uint8Array(binary.length);\r\n\r\n for (let i = 0; i < binary.length; i++)\r\n bytes[i] = binary.charCodeAt(i);\r\n\r\n return bytes;\r\n }\r\n\r\n /* \r\n BigInt helpers\r\n */\r\n\r\n static bytesToBigInt = (bytes: Uint8Array): bigint => BigInt('0x' + Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join(''));\r\n\r\n static bigIntToFixedBytes(bn: bigint, length: number): Uint8Array {\r\n let hex = bn.toString(16);\r\n\r\n if (hex.length % 2 !== 0) \r\n hex = '0' + hex;\r\n\r\n if (hex.length > length * 2) \r\n throw new Error(`Значение слишком велико для ${length} байт`);\r\n \r\n hex = hex.padStart(length * 2, '0');\r\n\r\n return this.fromHex(hex);\r\n }\r\n\r\n private static fromHex(hex: string): Uint8Array {\r\n const matches = hex.match(/.{1,2}/g);\r\n return new Uint8Array(matches ? matches.map(byte => parseInt(byte, 16)) : []);\r\n } \r\n\r\n /* \r\n Math\r\n */ \r\n \r\n static expMod(base: bigint, exp: bigint, mod: bigint): bigint {\r\n let res = BigInt(1);\r\n base = base % mod;\r\n\r\n while (exp > 0) {\r\n if (exp % BigInt(2) === BigInt(1)) res = (res * base) % mod;\r\n base = (base * base) % mod;\r\n exp = exp / BigInt(2);\r\n }\r\n\r\n return res;\r\n }\r\n\r\n /* \r\n SRP helper\r\n */ \r\n static async hashAsPerSrp6a(items: { value: bigint; length: number }[]): Promise<bigint> {\r\n const buffers = items.map(({ value, length }) => this.bigIntToFixedBytes(value, length));\r\n const totalLen = buffers.reduce((sum, buf) => sum + buf.length, 0);\r\n const combined = new Uint8Array(totalLen);\r\n\r\n let offset = 0;\r\n\r\n for (const buf of buffers) {\r\n combined.set(buf, offset);\r\n offset += buf.length;\r\n }\r\n\r\n const hash = await crypto.subtle.digest('SHA-256', combined);\r\n\r\n return this.bytesToBigInt(new Uint8Array(hash));\r\n }\r\n}","import { SecurityUtils } from '../utils/index.js'\r\n\r\nexport class CryptoService {\r\n\r\n async encryptData<T>(dataModel: T, key: Uint8Array): Promise<string> {\r\n const encoder = new TextEncoder();\r\n let jsonString: string;\r\n if (dataModel instanceof Uint8Array) {\r\n jsonString = `\"${SecurityUtils.toBase64(dataModel)}\"`;\r\n } else {\r\n jsonString = JSON.stringify(dataModel);\r\n }\r\n const plainBytes = encoder.encode(jsonString);\r\n const nonce = crypto.getRandomValues(new Uint8Array(SecurityUtils.NONCE_SIZE));\r\n\r\n const cryptoKey = await crypto.subtle.importKey(\r\n 'raw', \r\n key as BufferSource, \r\n 'AES-GCM', \r\n false, \r\n ['encrypt']);\r\n\r\n const encryptedContent = await crypto.subtle.encrypt({ \r\n name: 'AES-GCM',\r\n iv: nonce, \r\n tagLength: SecurityUtils.TAG_SIZE * 8},\r\n cryptoKey,\r\n plainBytes\r\n );\r\n\r\n const result = new Uint8Array(SecurityUtils.NONCE_SIZE + encryptedContent.byteLength);\r\n result.set(nonce, 0);\r\n result.set(new Uint8Array(encryptedContent), SecurityUtils.NONCE_SIZE);\r\n \r\n return SecurityUtils.toBase64(result);\r\n }\r\n\r\n async decryptedData<T>(encryptedBase64: string, key: Uint8Array): Promise<T | null>{\r\n if (!encryptedBase64) \r\n return null;\r\n\r\n const encryptedBytes = SecurityUtils.fromBase64(encryptedBase64);\r\n\r\n if (encryptedBytes.length < SecurityUtils.NONCE_SIZE + SecurityUtils.TAG_SIZE)\r\n throw new Error('Недопустимый формат зашифрованных данных');\r\n\r\n const nonce = encryptedBytes.slice(0, SecurityUtils.NONCE_SIZE);\r\n const ciphertextWithTag = encryptedBytes.slice(SecurityUtils.NONCE_SIZE);\r\n\r\n const cryptoKey = await crypto.subtle.importKey(\r\n 'raw',\r\n key as BufferSource, \r\n 'AES-GCM', \r\n false,\r\n ['decrypt']);\r\n\r\n try {\r\n const decryptedBuffer = await crypto.subtle.decrypt({ \r\n name: 'AES-GCM',\r\n iv: nonce,\r\n tagLength: \r\n SecurityUtils.TAG_SIZE * 8\r\n },\r\n cryptoKey,\r\n ciphertextWithTag);\r\n\r\n const decoder = new TextDecoder();\r\n const jsonString = decoder.decode(decryptedBuffer);\r\n\r\n return JSON.parse(jsonString) as T;\r\n } catch (e) {\r\n console.error('Ошибка дешифрования:', e);\r\n return null;\r\n }\r\n }\r\n\r\n generateRandomBytes = (length = 32): Uint8Array => crypto.getRandomValues(new Uint8Array(length));\r\n}","import { SecurityUtils } from \"../utils/security.utils.js\";\r\n\r\nexport class KeyDerivationService{\r\n readonly ITERATIONS = 600_000;\r\n readonly KEY_SIZE = 32;\r\n\r\n async deriveKeysFromPassword(login: string, password: string, salt: Uint8Array): Promise<{ kek: Uint8Array; authHash: string }> {\r\n const safeSalt = new Uint8Array(salt);\r\n \r\n const encoder = new TextEncoder();\r\n const normalizedLogin = login.trim().toLocaleLowerCase();\r\n const combinedPassword = `${normalizedLogin}:${password}`;\r\n const passwordBytes = encoder.encode(combinedPassword);\r\n const baseKey = await crypto.subtle.importKey('raw', passwordBytes, 'PBKDF2', false, ['deriveBits', 'deriveKey']);\r\n\r\n const masterKeyBits = await crypto.subtle.deriveBits({\r\n name: 'PBKDF2',\r\n salt: safeSalt, \r\n iterations: this.ITERATIONS,\r\n hash: 'SHA-256'\r\n }, baseKey, this.KEY_SIZE * 8);\r\n\r\n const masterKey = await crypto.subtle.importKey('raw', masterKeyBits, 'HKDF', false, ['deriveBits']);\r\n\r\n const kek = await crypto.subtle.deriveBits(\r\n { name: 'HKDF', hash: 'SHA-256', salt: new Uint8Array(0), info: encoder.encode('AES-GCM-KEK-v1') },\r\n masterKey,\r\n this.KEY_SIZE * 8\r\n );\r\n\r\n const authBytes = await crypto.subtle.deriveBits(\r\n { name: 'HKDF', hash: 'SHA-256', salt: new Uint8Array(0), info: encoder.encode('SERVER-AUTH-HASH-v1') },\r\n masterKey,\r\n this.KEY_SIZE * 8\r\n );\r\n\r\n return {\r\n kek: new Uint8Array(kek),\r\n authHash: SecurityUtils.toBase64(new Uint8Array(authBytes)),\r\n };\r\n }\r\n}","import { KeyDerivationService } from '../crypto/index.js';\r\nimport { SecurityUtils } from '../utils/index.js';\r\n\r\nexport class SrpService {\r\n private readonly keyDerivation = new KeyDerivationService();\r\n\r\n async generateSrpVerifier(authHash: string): Promise<string> {\r\n const x = SecurityUtils.bytesToBigInt(SecurityUtils.fromBase64(authHash));\r\n const v = SecurityUtils.expMod(SecurityUtils.g, x, SecurityUtils.N);\r\n return SecurityUtils.toBase64(SecurityUtils.bigIntToFixedBytes(v, SecurityUtils.MODULUS_SIZE));\r\n }\r\n\r\n async generateSrpProof(login: string, password: string, saltBase64: string, B_base64: string): Promise<{ A: string; M1: string; S: string }> {\r\n const salt = SecurityUtils.fromBase64(saltBase64);\r\n const { authHash } = await this.keyDerivation.deriveKeysFromPassword(login, password, salt);\r\n const x = SecurityUtils.bytesToBigInt(SecurityUtils.fromBase64(authHash));\r\n\r\n const aBytes = crypto.getRandomValues(new Uint8Array(32));\r\n const a = SecurityUtils.bytesToBigInt(aBytes);\r\n\r\n const A = SecurityUtils.expMod(SecurityUtils.g, a, SecurityUtils.N);\r\n if (A % SecurityUtils.N === 0n) \r\n throw new Error('Критическая ошибка: A % N === 0');\r\n\r\n const B = SecurityUtils.bytesToBigInt(SecurityUtils.fromBase64(B_base64));\r\n if (B % SecurityUtils.N === 0n) \r\n throw new Error('Критическая ошибка: B % N === 0');\r\n\r\n const u = await SecurityUtils.hashAsPerSrp6a([\r\n { value: A, length: SecurityUtils.MODULUS_SIZE },\r\n { value: B, length: SecurityUtils.MODULUS_SIZE },\r\n ]);\r\n if (u === 0n) \r\n throw new Error('Недопустимое значение u');\r\n\r\n const gX = SecurityUtils.expMod(SecurityUtils.g, x, SecurityUtils.N);\r\n const term = (SecurityUtils.k * gX) % SecurityUtils.N;\r\n const base = (B - term + SecurityUtils.N) % SecurityUtils.N;\r\n const exponent = a + (u * x);\r\n const S = SecurityUtils.expMod(base, exponent, SecurityUtils.N);\r\n\r\n if (S === 0n) \r\n throw new Error('Критическая ошибка: S === 0');\r\n\r\n const M1 = await SecurityUtils.hashAsPerSrp6a([\r\n { value: A, length: SecurityUtils.MODULUS_SIZE },\r\n { value: B, length: SecurityUtils.MODULUS_SIZE },\r\n { value: S, length: SecurityUtils.MODULUS_SIZE },\r\n ]);\r\n\r\n return {\r\n A: SecurityUtils.toBase64(SecurityUtils.bigIntToFixedBytes(A, SecurityUtils.MODULUS_SIZE)),\r\n M1: SecurityUtils.toBase64(SecurityUtils.bigIntToFixedBytes(M1, 32)),\r\n S: SecurityUtils.toBase64(SecurityUtils.bigIntToFixedBytes(S, SecurityUtils.MODULUS_SIZE)),\r\n };\r\n }\r\n\r\n async verifyServerM2(A_b64: string, M1_b64: string, S_b64: string, serverM2_b64: string): Promise<boolean> {\r\n const A = SecurityUtils.bytesToBigInt(SecurityUtils.fromBase64(A_b64));\r\n const M1 = SecurityUtils.bytesToBigInt(SecurityUtils.fromBase64(M1_b64));\r\n const S = SecurityUtils.bytesToBigInt(SecurityUtils.fromBase64(S_b64));\r\n\r\n const computedM2 = await SecurityUtils.hashAsPerSrp6a([\r\n { value: A, length: SecurityUtils.MODULUS_SIZE },\r\n { value: M1, length: 32 },\r\n { value: S, length: SecurityUtils.MODULUS_SIZE },\r\n ]);\r\n const computedM2_b64 = SecurityUtils.toBase64(SecurityUtils.bigIntToFixedBytes(computedM2, 32));\r\n\r\n return serverM2_b64 === computedM2_b64;\r\n }\r\n}"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
declare class CryptoService {
|
|
2
|
+
encryptData<T>(dataModel: T, key: Uint8Array): Promise<string>;
|
|
3
|
+
decryptedData<T>(encryptedBase64: string, key: Uint8Array): Promise<T | null>;
|
|
4
|
+
generateRandomBytes: (length?: number) => Uint8Array;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
declare class KeyDerivationService {
|
|
8
|
+
readonly ITERATIONS = 600000;
|
|
9
|
+
readonly KEY_SIZE = 32;
|
|
10
|
+
deriveKeysFromPassword(login: string, password: string, salt: Uint8Array): Promise<{
|
|
11
|
+
kek: Uint8Array;
|
|
12
|
+
authHash: string;
|
|
13
|
+
}>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
declare class SecurityUtils {
|
|
17
|
+
static readonly MODULUS_SIZE: number;
|
|
18
|
+
static readonly NONCE_SIZE: number;
|
|
19
|
+
static readonly TAG_SIZE: number;
|
|
20
|
+
static readonly KEY_SIZE: number;
|
|
21
|
+
static readonly N: bigint;
|
|
22
|
+
static readonly g: bigint;
|
|
23
|
+
static readonly k: bigint;
|
|
24
|
+
static toBase64(bytes: Uint8Array): string;
|
|
25
|
+
static fromBase64(base64: string): Uint8Array;
|
|
26
|
+
static bytesToBigInt: (bytes: Uint8Array) => bigint;
|
|
27
|
+
static bigIntToFixedBytes(bn: bigint, length: number): Uint8Array;
|
|
28
|
+
private static fromHex;
|
|
29
|
+
static expMod(base: bigint, exp: bigint, mod: bigint): bigint;
|
|
30
|
+
static hashAsPerSrp6a(items: {
|
|
31
|
+
value: bigint;
|
|
32
|
+
length: number;
|
|
33
|
+
}[]): Promise<bigint>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
declare class SrpService {
|
|
37
|
+
private readonly keyDerivation;
|
|
38
|
+
generateSrpVerifier(authHash: string): Promise<string>;
|
|
39
|
+
generateSrpProof(login: string, password: string, saltBase64: string, B_base64: string): Promise<{
|
|
40
|
+
A: string;
|
|
41
|
+
M1: string;
|
|
42
|
+
S: string;
|
|
43
|
+
}>;
|
|
44
|
+
verifyServerM2(A_b64: string, M1_b64: string, S_b64: string, serverM2_b64: string): Promise<boolean>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export { CryptoService, KeyDerivationService, SecurityUtils, SrpService };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
declare class CryptoService {
|
|
2
|
+
encryptData<T>(dataModel: T, key: Uint8Array): Promise<string>;
|
|
3
|
+
decryptedData<T>(encryptedBase64: string, key: Uint8Array): Promise<T | null>;
|
|
4
|
+
generateRandomBytes: (length?: number) => Uint8Array;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
declare class KeyDerivationService {
|
|
8
|
+
readonly ITERATIONS = 600000;
|
|
9
|
+
readonly KEY_SIZE = 32;
|
|
10
|
+
deriveKeysFromPassword(login: string, password: string, salt: Uint8Array): Promise<{
|
|
11
|
+
kek: Uint8Array;
|
|
12
|
+
authHash: string;
|
|
13
|
+
}>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
declare class SecurityUtils {
|
|
17
|
+
static readonly MODULUS_SIZE: number;
|
|
18
|
+
static readonly NONCE_SIZE: number;
|
|
19
|
+
static readonly TAG_SIZE: number;
|
|
20
|
+
static readonly KEY_SIZE: number;
|
|
21
|
+
static readonly N: bigint;
|
|
22
|
+
static readonly g: bigint;
|
|
23
|
+
static readonly k: bigint;
|
|
24
|
+
static toBase64(bytes: Uint8Array): string;
|
|
25
|
+
static fromBase64(base64: string): Uint8Array;
|
|
26
|
+
static bytesToBigInt: (bytes: Uint8Array) => bigint;
|
|
27
|
+
static bigIntToFixedBytes(bn: bigint, length: number): Uint8Array;
|
|
28
|
+
private static fromHex;
|
|
29
|
+
static expMod(base: bigint, exp: bigint, mod: bigint): bigint;
|
|
30
|
+
static hashAsPerSrp6a(items: {
|
|
31
|
+
value: bigint;
|
|
32
|
+
length: number;
|
|
33
|
+
}[]): Promise<bigint>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
declare class SrpService {
|
|
37
|
+
private readonly keyDerivation;
|
|
38
|
+
generateSrpVerifier(authHash: string): Promise<string>;
|
|
39
|
+
generateSrpProof(login: string, password: string, saltBase64: string, B_base64: string): Promise<{
|
|
40
|
+
A: string;
|
|
41
|
+
M1: string;
|
|
42
|
+
S: string;
|
|
43
|
+
}>;
|
|
44
|
+
verifyServerM2(A_b64: string, M1_b64: string, S_b64: string, serverM2_b64: string): Promise<boolean>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export { CryptoService, KeyDerivationService, SecurityUtils, SrpService };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
// src/utils/security.utils.ts
|
|
2
|
+
var SecurityUtils = class {
|
|
3
|
+
/*
|
|
4
|
+
Base64 helpers
|
|
5
|
+
*/
|
|
6
|
+
static toBase64(bytes) {
|
|
7
|
+
let binary = "";
|
|
8
|
+
for (let i = 0; i < bytes.byteLength; i++)
|
|
9
|
+
binary += String.fromCharCode(bytes[i]);
|
|
10
|
+
return window.btoa(binary);
|
|
11
|
+
}
|
|
12
|
+
static fromBase64(base64) {
|
|
13
|
+
const cleaned = base64.replace(/-/g, "+").replace(/_/g, "/").replace(/\s/g, "");
|
|
14
|
+
const binary = window.atob(cleaned);
|
|
15
|
+
const bytes = new Uint8Array(binary.length);
|
|
16
|
+
for (let i = 0; i < binary.length; i++)
|
|
17
|
+
bytes[i] = binary.charCodeAt(i);
|
|
18
|
+
return bytes;
|
|
19
|
+
}
|
|
20
|
+
static bigIntToFixedBytes(bn, length) {
|
|
21
|
+
let hex = bn.toString(16);
|
|
22
|
+
if (hex.length % 2 !== 0)
|
|
23
|
+
hex = "0" + hex;
|
|
24
|
+
if (hex.length > length * 2)
|
|
25
|
+
throw new Error(`\u0417\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u0441\u043B\u0438\u0448\u043A\u043E\u043C \u0432\u0435\u043B\u0438\u043A\u043E \u0434\u043B\u044F ${length} \u0431\u0430\u0439\u0442`);
|
|
26
|
+
hex = hex.padStart(length * 2, "0");
|
|
27
|
+
return this.fromHex(hex);
|
|
28
|
+
}
|
|
29
|
+
static fromHex(hex) {
|
|
30
|
+
const matches = hex.match(/.{1,2}/g);
|
|
31
|
+
return new Uint8Array(matches ? matches.map((byte) => parseInt(byte, 16)) : []);
|
|
32
|
+
}
|
|
33
|
+
/*
|
|
34
|
+
Math
|
|
35
|
+
*/
|
|
36
|
+
static expMod(base, exp, mod) {
|
|
37
|
+
let res = BigInt(1);
|
|
38
|
+
base = base % mod;
|
|
39
|
+
while (exp > 0) {
|
|
40
|
+
if (exp % BigInt(2) === BigInt(1)) res = res * base % mod;
|
|
41
|
+
base = base * base % mod;
|
|
42
|
+
exp = exp / BigInt(2);
|
|
43
|
+
}
|
|
44
|
+
return res;
|
|
45
|
+
}
|
|
46
|
+
/*
|
|
47
|
+
SRP helper
|
|
48
|
+
*/
|
|
49
|
+
static async hashAsPerSrp6a(items) {
|
|
50
|
+
const buffers = items.map(({ value, length }) => this.bigIntToFixedBytes(value, length));
|
|
51
|
+
const totalLen = buffers.reduce((sum, buf) => sum + buf.length, 0);
|
|
52
|
+
const combined = new Uint8Array(totalLen);
|
|
53
|
+
let offset = 0;
|
|
54
|
+
for (const buf of buffers) {
|
|
55
|
+
combined.set(buf, offset);
|
|
56
|
+
offset += buf.length;
|
|
57
|
+
}
|
|
58
|
+
const hash = await crypto.subtle.digest("SHA-256", combined);
|
|
59
|
+
return this.bytesToBigInt(new Uint8Array(hash));
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
SecurityUtils.MODULUS_SIZE = 384;
|
|
63
|
+
SecurityUtils.NONCE_SIZE = 12;
|
|
64
|
+
SecurityUtils.TAG_SIZE = 16;
|
|
65
|
+
SecurityUtils.KEY_SIZE = 32;
|
|
66
|
+
SecurityUtils.N = BigInt("0xAC6BDB41324A9A9BF166DE5E1F403D434A6E1B3B94A7E62AC1211858E002C75AD4455C9D19C0A3180296917A376205164043E20144FF485719D181A99EB574671AC58054457ED444A67032EA17D03AD43464D2397449CA593630A670D90D95A78E846A3C8AF80862098D80F33C42ED7059E75225E0A52718E2379369F65B79680A6560B080092EE71986066735A96A7D42E7597116742B02D3A154471B6A23D84E0D642C790D597A2BB7F5A48F734898BDD138C69493E723491959C1B4BD40C91C1C7924F88D046467A006507E781220A80C55A927906A7C6C9C227E674686DD5D1B855D28F0D604E24586C608630B9A34C4808381A54F0D9080A5F90B60187F");
|
|
67
|
+
SecurityUtils.g = BigInt(2);
|
|
68
|
+
SecurityUtils.k = BigInt("0xD55AE1AEC9F9115621E93E16E5DE4517DF8450D0957024D1256AA32B71E4E412");
|
|
69
|
+
/*
|
|
70
|
+
BigInt helpers
|
|
71
|
+
*/
|
|
72
|
+
SecurityUtils.bytesToBigInt = (bytes) => BigInt("0x" + Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join(""));
|
|
73
|
+
|
|
74
|
+
// src/crypto/crypto.service.ts
|
|
75
|
+
var CryptoService = class {
|
|
76
|
+
constructor() {
|
|
77
|
+
this.generateRandomBytes = (length = 32) => crypto.getRandomValues(new Uint8Array(length));
|
|
78
|
+
}
|
|
79
|
+
async encryptData(dataModel, key) {
|
|
80
|
+
const encoder = new TextEncoder();
|
|
81
|
+
let jsonString;
|
|
82
|
+
if (dataModel instanceof Uint8Array) {
|
|
83
|
+
jsonString = `"${SecurityUtils.toBase64(dataModel)}"`;
|
|
84
|
+
} else {
|
|
85
|
+
jsonString = JSON.stringify(dataModel);
|
|
86
|
+
}
|
|
87
|
+
const plainBytes = encoder.encode(jsonString);
|
|
88
|
+
const nonce = crypto.getRandomValues(new Uint8Array(SecurityUtils.NONCE_SIZE));
|
|
89
|
+
const cryptoKey = await crypto.subtle.importKey(
|
|
90
|
+
"raw",
|
|
91
|
+
key,
|
|
92
|
+
"AES-GCM",
|
|
93
|
+
false,
|
|
94
|
+
["encrypt"]
|
|
95
|
+
);
|
|
96
|
+
const encryptedContent = await crypto.subtle.encrypt(
|
|
97
|
+
{
|
|
98
|
+
name: "AES-GCM",
|
|
99
|
+
iv: nonce,
|
|
100
|
+
tagLength: SecurityUtils.TAG_SIZE * 8
|
|
101
|
+
},
|
|
102
|
+
cryptoKey,
|
|
103
|
+
plainBytes
|
|
104
|
+
);
|
|
105
|
+
const result = new Uint8Array(SecurityUtils.NONCE_SIZE + encryptedContent.byteLength);
|
|
106
|
+
result.set(nonce, 0);
|
|
107
|
+
result.set(new Uint8Array(encryptedContent), SecurityUtils.NONCE_SIZE);
|
|
108
|
+
return SecurityUtils.toBase64(result);
|
|
109
|
+
}
|
|
110
|
+
async decryptedData(encryptedBase64, key) {
|
|
111
|
+
if (!encryptedBase64)
|
|
112
|
+
return null;
|
|
113
|
+
const encryptedBytes = SecurityUtils.fromBase64(encryptedBase64);
|
|
114
|
+
if (encryptedBytes.length < SecurityUtils.NONCE_SIZE + SecurityUtils.TAG_SIZE)
|
|
115
|
+
throw new Error("\u041D\u0435\u0434\u043E\u043F\u0443\u0441\u0442\u0438\u043C\u044B\u0439 \u0444\u043E\u0440\u043C\u0430\u0442 \u0437\u0430\u0448\u0438\u0444\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0445 \u0434\u0430\u043D\u043D\u044B\u0445");
|
|
116
|
+
const nonce = encryptedBytes.slice(0, SecurityUtils.NONCE_SIZE);
|
|
117
|
+
const ciphertextWithTag = encryptedBytes.slice(SecurityUtils.NONCE_SIZE);
|
|
118
|
+
const cryptoKey = await crypto.subtle.importKey(
|
|
119
|
+
"raw",
|
|
120
|
+
key,
|
|
121
|
+
"AES-GCM",
|
|
122
|
+
false,
|
|
123
|
+
["decrypt"]
|
|
124
|
+
);
|
|
125
|
+
try {
|
|
126
|
+
const decryptedBuffer = await crypto.subtle.decrypt(
|
|
127
|
+
{
|
|
128
|
+
name: "AES-GCM",
|
|
129
|
+
iv: nonce,
|
|
130
|
+
tagLength: SecurityUtils.TAG_SIZE * 8
|
|
131
|
+
},
|
|
132
|
+
cryptoKey,
|
|
133
|
+
ciphertextWithTag
|
|
134
|
+
);
|
|
135
|
+
const decoder = new TextDecoder();
|
|
136
|
+
const jsonString = decoder.decode(decryptedBuffer);
|
|
137
|
+
return JSON.parse(jsonString);
|
|
138
|
+
} catch (e) {
|
|
139
|
+
console.error("\u041E\u0448\u0438\u0431\u043A\u0430 \u0434\u0435\u0448\u0438\u0444\u0440\u043E\u0432\u0430\u043D\u0438\u044F:", e);
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
// src/crypto/key-derivation.service.ts
|
|
146
|
+
var KeyDerivationService = class {
|
|
147
|
+
constructor() {
|
|
148
|
+
this.ITERATIONS = 6e5;
|
|
149
|
+
this.KEY_SIZE = 32;
|
|
150
|
+
}
|
|
151
|
+
async deriveKeysFromPassword(login, password, salt) {
|
|
152
|
+
const safeSalt = new Uint8Array(salt);
|
|
153
|
+
const encoder = new TextEncoder();
|
|
154
|
+
const normalizedLogin = login.trim().toLocaleLowerCase();
|
|
155
|
+
const combinedPassword = `${normalizedLogin}:${password}`;
|
|
156
|
+
const passwordBytes = encoder.encode(combinedPassword);
|
|
157
|
+
const baseKey = await crypto.subtle.importKey("raw", passwordBytes, "PBKDF2", false, ["deriveBits", "deriveKey"]);
|
|
158
|
+
const masterKeyBits = await crypto.subtle.deriveBits({
|
|
159
|
+
name: "PBKDF2",
|
|
160
|
+
salt: safeSalt,
|
|
161
|
+
iterations: this.ITERATIONS,
|
|
162
|
+
hash: "SHA-256"
|
|
163
|
+
}, baseKey, this.KEY_SIZE * 8);
|
|
164
|
+
const masterKey = await crypto.subtle.importKey("raw", masterKeyBits, "HKDF", false, ["deriveBits"]);
|
|
165
|
+
const kek = await crypto.subtle.deriveBits(
|
|
166
|
+
{ name: "HKDF", hash: "SHA-256", salt: new Uint8Array(0), info: encoder.encode("AES-GCM-KEK-v1") },
|
|
167
|
+
masterKey,
|
|
168
|
+
this.KEY_SIZE * 8
|
|
169
|
+
);
|
|
170
|
+
const authBytes = await crypto.subtle.deriveBits(
|
|
171
|
+
{ name: "HKDF", hash: "SHA-256", salt: new Uint8Array(0), info: encoder.encode("SERVER-AUTH-HASH-v1") },
|
|
172
|
+
masterKey,
|
|
173
|
+
this.KEY_SIZE * 8
|
|
174
|
+
);
|
|
175
|
+
return {
|
|
176
|
+
kek: new Uint8Array(kek),
|
|
177
|
+
authHash: SecurityUtils.toBase64(new Uint8Array(authBytes))
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
// src/srp/srp.service.ts
|
|
183
|
+
var SrpService = class {
|
|
184
|
+
constructor() {
|
|
185
|
+
this.keyDerivation = new KeyDerivationService();
|
|
186
|
+
}
|
|
187
|
+
async generateSrpVerifier(authHash) {
|
|
188
|
+
const x = SecurityUtils.bytesToBigInt(SecurityUtils.fromBase64(authHash));
|
|
189
|
+
const v = SecurityUtils.expMod(SecurityUtils.g, x, SecurityUtils.N);
|
|
190
|
+
return SecurityUtils.toBase64(SecurityUtils.bigIntToFixedBytes(v, SecurityUtils.MODULUS_SIZE));
|
|
191
|
+
}
|
|
192
|
+
async generateSrpProof(login, password, saltBase64, B_base64) {
|
|
193
|
+
const salt = SecurityUtils.fromBase64(saltBase64);
|
|
194
|
+
const { authHash } = await this.keyDerivation.deriveKeysFromPassword(login, password, salt);
|
|
195
|
+
const x = SecurityUtils.bytesToBigInt(SecurityUtils.fromBase64(authHash));
|
|
196
|
+
const aBytes = crypto.getRandomValues(new Uint8Array(32));
|
|
197
|
+
const a = SecurityUtils.bytesToBigInt(aBytes);
|
|
198
|
+
const A = SecurityUtils.expMod(SecurityUtils.g, a, SecurityUtils.N);
|
|
199
|
+
if (A % SecurityUtils.N === 0n)
|
|
200
|
+
throw new Error("\u041A\u0440\u0438\u0442\u0438\u0447\u0435\u0441\u043A\u0430\u044F \u043E\u0448\u0438\u0431\u043A\u0430: A % N === 0");
|
|
201
|
+
const B = SecurityUtils.bytesToBigInt(SecurityUtils.fromBase64(B_base64));
|
|
202
|
+
if (B % SecurityUtils.N === 0n)
|
|
203
|
+
throw new Error("\u041A\u0440\u0438\u0442\u0438\u0447\u0435\u0441\u043A\u0430\u044F \u043E\u0448\u0438\u0431\u043A\u0430: B % N === 0");
|
|
204
|
+
const u = await SecurityUtils.hashAsPerSrp6a([
|
|
205
|
+
{ value: A, length: SecurityUtils.MODULUS_SIZE },
|
|
206
|
+
{ value: B, length: SecurityUtils.MODULUS_SIZE }
|
|
207
|
+
]);
|
|
208
|
+
if (u === 0n)
|
|
209
|
+
throw new Error("\u041D\u0435\u0434\u043E\u043F\u0443\u0441\u0442\u0438\u043C\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435 u");
|
|
210
|
+
const gX = SecurityUtils.expMod(SecurityUtils.g, x, SecurityUtils.N);
|
|
211
|
+
const term = SecurityUtils.k * gX % SecurityUtils.N;
|
|
212
|
+
const base = (B - term + SecurityUtils.N) % SecurityUtils.N;
|
|
213
|
+
const exponent = a + u * x;
|
|
214
|
+
const S = SecurityUtils.expMod(base, exponent, SecurityUtils.N);
|
|
215
|
+
if (S === 0n)
|
|
216
|
+
throw new Error("\u041A\u0440\u0438\u0442\u0438\u0447\u0435\u0441\u043A\u0430\u044F \u043E\u0448\u0438\u0431\u043A\u0430: S === 0");
|
|
217
|
+
const M1 = await SecurityUtils.hashAsPerSrp6a([
|
|
218
|
+
{ value: A, length: SecurityUtils.MODULUS_SIZE },
|
|
219
|
+
{ value: B, length: SecurityUtils.MODULUS_SIZE },
|
|
220
|
+
{ value: S, length: SecurityUtils.MODULUS_SIZE }
|
|
221
|
+
]);
|
|
222
|
+
return {
|
|
223
|
+
A: SecurityUtils.toBase64(SecurityUtils.bigIntToFixedBytes(A, SecurityUtils.MODULUS_SIZE)),
|
|
224
|
+
M1: SecurityUtils.toBase64(SecurityUtils.bigIntToFixedBytes(M1, 32)),
|
|
225
|
+
S: SecurityUtils.toBase64(SecurityUtils.bigIntToFixedBytes(S, SecurityUtils.MODULUS_SIZE))
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
async verifyServerM2(A_b64, M1_b64, S_b64, serverM2_b64) {
|
|
229
|
+
const A = SecurityUtils.bytesToBigInt(SecurityUtils.fromBase64(A_b64));
|
|
230
|
+
const M1 = SecurityUtils.bytesToBigInt(SecurityUtils.fromBase64(M1_b64));
|
|
231
|
+
const S = SecurityUtils.bytesToBigInt(SecurityUtils.fromBase64(S_b64));
|
|
232
|
+
const computedM2 = await SecurityUtils.hashAsPerSrp6a([
|
|
233
|
+
{ value: A, length: SecurityUtils.MODULUS_SIZE },
|
|
234
|
+
{ value: M1, length: 32 },
|
|
235
|
+
{ value: S, length: SecurityUtils.MODULUS_SIZE }
|
|
236
|
+
]);
|
|
237
|
+
const computedM2_b64 = SecurityUtils.toBase64(SecurityUtils.bigIntToFixedBytes(computedM2, 32));
|
|
238
|
+
return serverM2_b64 === computedM2_b64;
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
export { CryptoService, KeyDerivationService, SecurityUtils, SrpService };
|
|
243
|
+
//# sourceMappingURL=index.js.map
|
|
244
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/security.utils.ts","../src/crypto/crypto.service.ts","../src/crypto/key-derivation.service.ts","../src/srp/srp.service.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["export class SecurityUtils {\r\n static readonly MODULUS_SIZE : number = 384;\r\n static readonly NONCE_SIZE : number = 12;\r\n static readonly TAG_SIZE : number = 16;\r\n static readonly KEY_SIZE : number = 32;\r\n\r\n static readonly N : bigint = BigInt(\"0xAC6BDB41324A9A9BF166DE5E1F403D434A6E1B3B94A7E62AC1211858E002C75AD4455C9D19C0A3180296917A376205164043E20144FF485719D181A99EB574671AC58054457ED444A67032EA17D03AD43464D2397449CA593630A670D90D95A78E846A3C8AF80862098D80F33C42ED7059E75225E0A52718E2379369F65B79680A6560B080092EE71986066735A96A7D42E7597116742B02D3A154471B6A23D84E0D642C790D597A2BB7F5A48F734898BDD138C69493E723491959C1B4BD40C91C1C7924F88D046467A006507E781220A80C55A927906A7C6C9C227E674686DD5D1B855D28F0D604E24586C608630B9A34C4808381A54F0D9080A5F90B60187F\");\r\n static readonly g : bigint = BigInt(2);\r\n static readonly k : bigint = BigInt(\"0xD55AE1AEC9F9115621E93E16E5DE4517DF8450D0957024D1256AA32B71E4E412\");\r\n\r\n /* \r\n Base64 helpers\r\n */\r\n\r\n static toBase64(bytes: Uint8Array): string {\r\n let binary = '';\r\n for (let i = 0; i < bytes.byteLength; i++)\r\n binary += String.fromCharCode(bytes[i]);\r\n\r\n return window.btoa(binary);\r\n }\r\n\r\n static fromBase64(base64: string): Uint8Array {\r\n const cleaned = base64.replace(/-/g, '+').replace(/_/g, '/').replace(/\\s/g, '');\r\n const binary = window.atob(cleaned);\r\n const bytes = new Uint8Array(binary.length);\r\n\r\n for (let i = 0; i < binary.length; i++)\r\n bytes[i] = binary.charCodeAt(i);\r\n\r\n return bytes;\r\n }\r\n\r\n /* \r\n BigInt helpers\r\n */\r\n\r\n static bytesToBigInt = (bytes: Uint8Array): bigint => BigInt('0x' + Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join(''));\r\n\r\n static bigIntToFixedBytes(bn: bigint, length: number): Uint8Array {\r\n let hex = bn.toString(16);\r\n\r\n if (hex.length % 2 !== 0) \r\n hex = '0' + hex;\r\n\r\n if (hex.length > length * 2) \r\n throw new Error(`Значение слишком велико для ${length} байт`);\r\n \r\n hex = hex.padStart(length * 2, '0');\r\n\r\n return this.fromHex(hex);\r\n }\r\n\r\n private static fromHex(hex: string): Uint8Array {\r\n const matches = hex.match(/.{1,2}/g);\r\n return new Uint8Array(matches ? matches.map(byte => parseInt(byte, 16)) : []);\r\n } \r\n\r\n /* \r\n Math\r\n */ \r\n \r\n static expMod(base: bigint, exp: bigint, mod: bigint): bigint {\r\n let res = BigInt(1);\r\n base = base % mod;\r\n\r\n while (exp > 0) {\r\n if (exp % BigInt(2) === BigInt(1)) res = (res * base) % mod;\r\n base = (base * base) % mod;\r\n exp = exp / BigInt(2);\r\n }\r\n\r\n return res;\r\n }\r\n\r\n /* \r\n SRP helper\r\n */ \r\n static async hashAsPerSrp6a(items: { value: bigint; length: number }[]): Promise<bigint> {\r\n const buffers = items.map(({ value, length }) => this.bigIntToFixedBytes(value, length));\r\n const totalLen = buffers.reduce((sum, buf) => sum + buf.length, 0);\r\n const combined = new Uint8Array(totalLen);\r\n\r\n let offset = 0;\r\n\r\n for (const buf of buffers) {\r\n combined.set(buf, offset);\r\n offset += buf.length;\r\n }\r\n\r\n const hash = await crypto.subtle.digest('SHA-256', combined);\r\n\r\n return this.bytesToBigInt(new Uint8Array(hash));\r\n }\r\n}","import { SecurityUtils } from '../utils/index.js'\r\n\r\nexport class CryptoService {\r\n\r\n async encryptData<T>(dataModel: T, key: Uint8Array): Promise<string> {\r\n const encoder = new TextEncoder();\r\n let jsonString: string;\r\n if (dataModel instanceof Uint8Array) {\r\n jsonString = `\"${SecurityUtils.toBase64(dataModel)}\"`;\r\n } else {\r\n jsonString = JSON.stringify(dataModel);\r\n }\r\n const plainBytes = encoder.encode(jsonString);\r\n const nonce = crypto.getRandomValues(new Uint8Array(SecurityUtils.NONCE_SIZE));\r\n\r\n const cryptoKey = await crypto.subtle.importKey(\r\n 'raw', \r\n key as BufferSource, \r\n 'AES-GCM', \r\n false, \r\n ['encrypt']);\r\n\r\n const encryptedContent = await crypto.subtle.encrypt({ \r\n name: 'AES-GCM',\r\n iv: nonce, \r\n tagLength: SecurityUtils.TAG_SIZE * 8},\r\n cryptoKey,\r\n plainBytes\r\n );\r\n\r\n const result = new Uint8Array(SecurityUtils.NONCE_SIZE + encryptedContent.byteLength);\r\n result.set(nonce, 0);\r\n result.set(new Uint8Array(encryptedContent), SecurityUtils.NONCE_SIZE);\r\n \r\n return SecurityUtils.toBase64(result);\r\n }\r\n\r\n async decryptedData<T>(encryptedBase64: string, key: Uint8Array): Promise<T | null>{\r\n if (!encryptedBase64) \r\n return null;\r\n\r\n const encryptedBytes = SecurityUtils.fromBase64(encryptedBase64);\r\n\r\n if (encryptedBytes.length < SecurityUtils.NONCE_SIZE + SecurityUtils.TAG_SIZE)\r\n throw new Error('Недопустимый формат зашифрованных данных');\r\n\r\n const nonce = encryptedBytes.slice(0, SecurityUtils.NONCE_SIZE);\r\n const ciphertextWithTag = encryptedBytes.slice(SecurityUtils.NONCE_SIZE);\r\n\r\n const cryptoKey = await crypto.subtle.importKey(\r\n 'raw',\r\n key as BufferSource, \r\n 'AES-GCM', \r\n false,\r\n ['decrypt']);\r\n\r\n try {\r\n const decryptedBuffer = await crypto.subtle.decrypt({ \r\n name: 'AES-GCM',\r\n iv: nonce,\r\n tagLength: \r\n SecurityUtils.TAG_SIZE * 8\r\n },\r\n cryptoKey,\r\n ciphertextWithTag);\r\n\r\n const decoder = new TextDecoder();\r\n const jsonString = decoder.decode(decryptedBuffer);\r\n\r\n return JSON.parse(jsonString) as T;\r\n } catch (e) {\r\n console.error('Ошибка дешифрования:', e);\r\n return null;\r\n }\r\n }\r\n\r\n generateRandomBytes = (length = 32): Uint8Array => crypto.getRandomValues(new Uint8Array(length));\r\n}","import { SecurityUtils } from \"../utils/security.utils.js\";\r\n\r\nexport class KeyDerivationService{\r\n readonly ITERATIONS = 600_000;\r\n readonly KEY_SIZE = 32;\r\n\r\n async deriveKeysFromPassword(login: string, password: string, salt: Uint8Array): Promise<{ kek: Uint8Array; authHash: string }> {\r\n const safeSalt = new Uint8Array(salt);\r\n \r\n const encoder = new TextEncoder();\r\n const normalizedLogin = login.trim().toLocaleLowerCase();\r\n const combinedPassword = `${normalizedLogin}:${password}`;\r\n const passwordBytes = encoder.encode(combinedPassword);\r\n const baseKey = await crypto.subtle.importKey('raw', passwordBytes, 'PBKDF2', false, ['deriveBits', 'deriveKey']);\r\n\r\n const masterKeyBits = await crypto.subtle.deriveBits({\r\n name: 'PBKDF2',\r\n salt: safeSalt, \r\n iterations: this.ITERATIONS,\r\n hash: 'SHA-256'\r\n }, baseKey, this.KEY_SIZE * 8);\r\n\r\n const masterKey = await crypto.subtle.importKey('raw', masterKeyBits, 'HKDF', false, ['deriveBits']);\r\n\r\n const kek = await crypto.subtle.deriveBits(\r\n { name: 'HKDF', hash: 'SHA-256', salt: new Uint8Array(0), info: encoder.encode('AES-GCM-KEK-v1') },\r\n masterKey,\r\n this.KEY_SIZE * 8\r\n );\r\n\r\n const authBytes = await crypto.subtle.deriveBits(\r\n { name: 'HKDF', hash: 'SHA-256', salt: new Uint8Array(0), info: encoder.encode('SERVER-AUTH-HASH-v1') },\r\n masterKey,\r\n this.KEY_SIZE * 8\r\n );\r\n\r\n return {\r\n kek: new Uint8Array(kek),\r\n authHash: SecurityUtils.toBase64(new Uint8Array(authBytes)),\r\n };\r\n }\r\n}","import { KeyDerivationService } from '../crypto/index.js';\r\nimport { SecurityUtils } from '../utils/index.js';\r\n\r\nexport class SrpService {\r\n private readonly keyDerivation = new KeyDerivationService();\r\n\r\n async generateSrpVerifier(authHash: string): Promise<string> {\r\n const x = SecurityUtils.bytesToBigInt(SecurityUtils.fromBase64(authHash));\r\n const v = SecurityUtils.expMod(SecurityUtils.g, x, SecurityUtils.N);\r\n return SecurityUtils.toBase64(SecurityUtils.bigIntToFixedBytes(v, SecurityUtils.MODULUS_SIZE));\r\n }\r\n\r\n async generateSrpProof(login: string, password: string, saltBase64: string, B_base64: string): Promise<{ A: string; M1: string; S: string }> {\r\n const salt = SecurityUtils.fromBase64(saltBase64);\r\n const { authHash } = await this.keyDerivation.deriveKeysFromPassword(login, password, salt);\r\n const x = SecurityUtils.bytesToBigInt(SecurityUtils.fromBase64(authHash));\r\n\r\n const aBytes = crypto.getRandomValues(new Uint8Array(32));\r\n const a = SecurityUtils.bytesToBigInt(aBytes);\r\n\r\n const A = SecurityUtils.expMod(SecurityUtils.g, a, SecurityUtils.N);\r\n if (A % SecurityUtils.N === 0n) \r\n throw new Error('Критическая ошибка: A % N === 0');\r\n\r\n const B = SecurityUtils.bytesToBigInt(SecurityUtils.fromBase64(B_base64));\r\n if (B % SecurityUtils.N === 0n) \r\n throw new Error('Критическая ошибка: B % N === 0');\r\n\r\n const u = await SecurityUtils.hashAsPerSrp6a([\r\n { value: A, length: SecurityUtils.MODULUS_SIZE },\r\n { value: B, length: SecurityUtils.MODULUS_SIZE },\r\n ]);\r\n if (u === 0n) \r\n throw new Error('Недопустимое значение u');\r\n\r\n const gX = SecurityUtils.expMod(SecurityUtils.g, x, SecurityUtils.N);\r\n const term = (SecurityUtils.k * gX) % SecurityUtils.N;\r\n const base = (B - term + SecurityUtils.N) % SecurityUtils.N;\r\n const exponent = a + (u * x);\r\n const S = SecurityUtils.expMod(base, exponent, SecurityUtils.N);\r\n\r\n if (S === 0n) \r\n throw new Error('Критическая ошибка: S === 0');\r\n\r\n const M1 = await SecurityUtils.hashAsPerSrp6a([\r\n { value: A, length: SecurityUtils.MODULUS_SIZE },\r\n { value: B, length: SecurityUtils.MODULUS_SIZE },\r\n { value: S, length: SecurityUtils.MODULUS_SIZE },\r\n ]);\r\n\r\n return {\r\n A: SecurityUtils.toBase64(SecurityUtils.bigIntToFixedBytes(A, SecurityUtils.MODULUS_SIZE)),\r\n M1: SecurityUtils.toBase64(SecurityUtils.bigIntToFixedBytes(M1, 32)),\r\n S: SecurityUtils.toBase64(SecurityUtils.bigIntToFixedBytes(S, SecurityUtils.MODULUS_SIZE)),\r\n };\r\n }\r\n\r\n async verifyServerM2(A_b64: string, M1_b64: string, S_b64: string, serverM2_b64: string): Promise<boolean> {\r\n const A = SecurityUtils.bytesToBigInt(SecurityUtils.fromBase64(A_b64));\r\n const M1 = SecurityUtils.bytesToBigInt(SecurityUtils.fromBase64(M1_b64));\r\n const S = SecurityUtils.bytesToBigInt(SecurityUtils.fromBase64(S_b64));\r\n\r\n const computedM2 = await SecurityUtils.hashAsPerSrp6a([\r\n { value: A, length: SecurityUtils.MODULUS_SIZE },\r\n { value: M1, length: 32 },\r\n { value: S, length: SecurityUtils.MODULUS_SIZE },\r\n ]);\r\n const computedM2_b64 = SecurityUtils.toBase64(SecurityUtils.bigIntToFixedBytes(computedM2, 32));\r\n\r\n return serverM2_b64 === computedM2_b64;\r\n }\r\n}"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@crossdyne/security",
|
|
3
|
+
"version": "0.2.0-beta.1",
|
|
4
|
+
"description": "Security library with SRP authentication and cryptography",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"publishConfig": {
|
|
17
|
+
"access": "public"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"README.md",
|
|
22
|
+
"LICENSE"
|
|
23
|
+
],
|
|
24
|
+
"sideEffects": false,
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "tsup",
|
|
27
|
+
"dev": "tsup --watch",
|
|
28
|
+
"test": "vitest",
|
|
29
|
+
"test:coverage": "vitest --coverage",
|
|
30
|
+
"lint": "eslint src/**/*.ts",
|
|
31
|
+
"prepublishOnly": "npm run build"
|
|
32
|
+
},
|
|
33
|
+
"keywords": [
|
|
34
|
+
"security",
|
|
35
|
+
"srp",
|
|
36
|
+
"cryptography",
|
|
37
|
+
"authentication",
|
|
38
|
+
"typescript"
|
|
39
|
+
],
|
|
40
|
+
"author": "IgorBoytsov",
|
|
41
|
+
"license": "MIT",
|
|
42
|
+
"repository": {
|
|
43
|
+
"type": "git",
|
|
44
|
+
"url": "https://github.com/crossdyne/security-typescript.git"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@types/node": "^20.10.0",
|
|
48
|
+
"tsup": "^8.0.0",
|
|
49
|
+
"typescript": "^5.3.0",
|
|
50
|
+
"vitest": "^1.0.0"
|
|
51
|
+
}
|
|
52
|
+
}
|