@crossdyne/security 0.5.0-beta.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +358 -8
- package/dist/index.cjs +301 -320
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +132 -123
- package/dist/index.d.ts +132 -123
- package/dist/index.js +301 -321
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,15 +1,365 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Crossdyne.Security
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> Cross-platform cryptographic library — [.NET](https://github.com/crossdyne/dotnet-security) | [TypeScript](https://github.com/crossdyne/typescript-security)
|
|
4
|
+
>
|
|
5
|
+
> [English](#english) | [Русский](#русский)
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
---
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
<a name="english"></a>
|
|
10
|
+
## English
|
|
11
|
+
|
|
12
|
+
TypeScript / JavaScript implementation of the Crossdyne.Security cryptographic library via the native **Web Crypto API**.
|
|
10
13
|
|
|
11
14
|
## Installation
|
|
12
15
|
|
|
13
16
|
```bash
|
|
14
|
-
npm install @
|
|
15
|
-
```
|
|
17
|
+
npm install @crossdyne/security
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### Features
|
|
21
|
+
|
|
22
|
+
- **AES-256-GCM** — authenticated symmetric encryption via `crypto.subtle`
|
|
23
|
+
- **PBKDF2 + HKDF** — secure key derivation from passwords
|
|
24
|
+
- **SRP-6a** — password-authenticated key exchange without sending the password to the server
|
|
25
|
+
- **Zero-memory** — sensitive buffers are explicitly overwritten after use
|
|
26
|
+
- **No custom crypto primitives** — relies entirely on the browser / Node.js Web Crypto API
|
|
27
|
+
- **Cross-platform** — compatible with the [.NET implementation](https://github.com/crossdyne/dotnet-security). Encrypted payloads and SRP messages are interchangeable between TS and .NET.
|
|
28
|
+
|
|
29
|
+
### Requirements
|
|
30
|
+
|
|
31
|
+
- TypeScript 5.3+
|
|
32
|
+
- Web Crypto API (`crypto.subtle`) — modern browsers and Node.js 18+
|
|
33
|
+
|
|
34
|
+
### Project Structure
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
crossdyne-security/
|
|
38
|
+
├── crypto/
|
|
39
|
+
│ ├── crypto-service.ts # AES-GCM encrypt / decrypt
|
|
40
|
+
│ ├── key-derivation-service.ts # Derive KEK and AuthHash (PBKDF2 → HKDF)
|
|
41
|
+
│ ├── crypto-profile-registry.ts # Versioned crypto profiles
|
|
42
|
+
│ ├── crypto-version.ts # Enum: V1, V2, ...
|
|
43
|
+
│ └── kdf-options.ts # PBKDF2 / HKDF parameters
|
|
44
|
+
├── srp/
|
|
45
|
+
│ ├── srp-server-service.ts # SRP server (challenge, verify)
|
|
46
|
+
│ ├── srp-client-service.ts # SRP client (proof, verifier)
|
|
47
|
+
│ ├── srp-key-derivation-service.ts # Derive auth-hash for SRP
|
|
48
|
+
│ ├── srp-context-factory.ts # Creates SRP context (N, g, k, hash)
|
|
49
|
+
│ ├── srp-group.ts # Enum: Group2048, Group4096, ...
|
|
50
|
+
│ └── srp-encoding.ts # BigInt ↔ bytes helpers
|
|
51
|
+
└── utils/
|
|
52
|
+
├── security-utils.ts # Base64, BigInt, fixed-time compare
|
|
53
|
+
└── srp-encoding.ts # Hash moduli, session key, M1 / M2
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Quick Start
|
|
57
|
+
|
|
58
|
+
#### Encrypt / Decrypt
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
import { CryptoService } from './crypto/crypto-service.js';
|
|
62
|
+
import { CryptoVersion } from './crypto/crypto-version.js';
|
|
63
|
+
|
|
64
|
+
const crypto = new CryptoService();
|
|
65
|
+
const key = crypto.generateRandomBytes(32); // AES-256
|
|
66
|
+
|
|
67
|
+
const encrypted = await crypto.encryptData(
|
|
68
|
+
{ message: "Hello, World!" },
|
|
69
|
+
key,
|
|
70
|
+
CryptoVersion.V1
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const decrypted = await crypto.decryptData<MyData>(encrypted, key);
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Encrypted payload format (Base64):
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
[Version (1 byte)][Nonce (N bytes)][Ciphertext + Tag]
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
#### Key Derivation from Password
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
import { KeyDerivationService } from './crypto/key-derivation-service.js';
|
|
86
|
+
import { CryptoVersion } from './crypto/crypto-version.js';
|
|
87
|
+
|
|
88
|
+
const kdf = new KeyDerivationService();
|
|
89
|
+
const salt = crypto.getRandomValues(new Uint8Array(16));
|
|
90
|
+
|
|
91
|
+
const { kek, authHash } = await kdf.deriveKeysFromPassword(
|
|
92
|
+
"user@example.com",
|
|
93
|
+
"SuperSecret123!",
|
|
94
|
+
salt,
|
|
95
|
+
CryptoVersion.V1
|
|
96
|
+
);
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
- `kek` — `Uint8Array` key for AES-GCM
|
|
100
|
+
- `authHash` — Base64 string for SRP authentication
|
|
101
|
+
|
|
102
|
+
#### SRP Authentication Flow
|
|
103
|
+
|
|
104
|
+
**Server — generate challenge**
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
import { SrpServerService } from './srp/srp-server-service.js';
|
|
108
|
+
import { SrpGroup } from './srp/srp-group.js';
|
|
109
|
+
|
|
110
|
+
const server = new SrpServerService();
|
|
111
|
+
|
|
112
|
+
const session = await server.getSrpChallenge(
|
|
113
|
+
"user@example.com",
|
|
114
|
+
verifierBytes,
|
|
115
|
+
salt,
|
|
116
|
+
SrpGroup.Group2048
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
// Send to client: salt + session.publicKeyB (B)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**Client — generate proof**
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
import { SrpClientService } from './srp/srp-client-service.js';
|
|
126
|
+
import { SrpKeyDerivationService } from './srp/srp-key-derivation-service.js';
|
|
127
|
+
import { SrpGroup } from './srp/srp-group.js';
|
|
128
|
+
import { CryptoVersion } from './crypto/crypto-version.js';
|
|
129
|
+
|
|
130
|
+
const client = new SrpClientService();
|
|
131
|
+
const kdf = new SrpKeyDerivationService();
|
|
132
|
+
|
|
133
|
+
// 1. Derive auth-hash from password
|
|
134
|
+
const authHash = await kdf.deriveAuthHashForSrp(
|
|
135
|
+
identity, password, salt, SrpGroup.Group2048, CryptoVersion.V1
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
// 2. Generate proof
|
|
139
|
+
const { A, M1, SessionKeyK } = await client.generateSrpProof(
|
|
140
|
+
identity,
|
|
141
|
+
authHash,
|
|
142
|
+
btoa(String.fromCharCode(...salt)),
|
|
143
|
+
btoa(String.fromCharCode(...session.publicKeyB)),
|
|
144
|
+
SrpGroup.Group2048
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
// Send to server: A + M1
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**Server — verify proof**
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
const serverM2 = await server.verifySrpProof(
|
|
154
|
+
session,
|
|
155
|
+
A,
|
|
156
|
+
M1,
|
|
157
|
+
SrpGroup.Group2048
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
// Send to client: serverM2
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
**Client — verify server proof**
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
const isValid = await client.verifyServerM2(
|
|
167
|
+
A,
|
|
168
|
+
M1,
|
|
169
|
+
SessionKeyK,
|
|
170
|
+
serverM2,
|
|
171
|
+
SrpGroup.Group2048
|
|
172
|
+
);
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Security Notes
|
|
176
|
+
|
|
177
|
+
- All sensitive buffers are explicitly overwritten after use
|
|
178
|
+
- Salt must be **at least 16 bytes**
|
|
179
|
+
- AES-256 key must be **exactly 32 bytes**
|
|
180
|
+
- SRP groups are protected against small-subgroup attacks (checks `A % N != 0`, `B != 0`)
|
|
181
|
+
- Relies entirely on the native Web Crypto API — no custom crypto primitives
|
|
182
|
+
|
|
183
|
+
### License
|
|
184
|
+
|
|
185
|
+
MIT
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
<a name="русский"></a>
|
|
190
|
+
## Русский
|
|
191
|
+
|
|
192
|
+
Реализация криптографической библиотеки Crossdyne.Security для TypeScript / JavaScript через нативный **Web Crypto API**.
|
|
193
|
+
|
|
194
|
+
## Установка
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
npm install @crossdyne/security
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Возможности
|
|
201
|
+
|
|
202
|
+
- **AES-256-GCM** — симметричное шифрование с аутентификацией через `crypto.subtle`
|
|
203
|
+
- **PBKDF2 + HKDF** — надёжный вывод ключей из пароля
|
|
204
|
+
- **SRP-6a** — протокол аутентификации без передачи пароля на сервер
|
|
205
|
+
- **Zero-memory** — чувствительные буферы явно перезаписываются после использования
|
|
206
|
+
- **Никаких самописных криптопримитивов** — только нативный Web Crypto API браузера / Node.js
|
|
207
|
+
- **Кроссплатформенность** — совместима с [.NET-реализацией](https://github.com/crossdyne/dotnet-security). Зашифрованные данные и SRP-сообщения взаимозаменяемы между TS и .NET.
|
|
208
|
+
|
|
209
|
+
### Требования
|
|
210
|
+
|
|
211
|
+
- TypeScript 5.3+
|
|
212
|
+
- Web Crypto API (`crypto.subtle`) — современные браузеры и Node.js 18+
|
|
213
|
+
|
|
214
|
+
### Структура проекта
|
|
215
|
+
|
|
216
|
+
```
|
|
217
|
+
crossdyne-security/
|
|
218
|
+
├── crypto/
|
|
219
|
+
│ ├── crypto-service.ts # AES-GCM шифрование / дешифрование
|
|
220
|
+
│ ├── key-derivation-service.ts # Вывод KEK и AuthHash (PBKDF2 → HKDF)
|
|
221
|
+
│ ├── crypto-profile-registry.ts # Версионированные криптопрофили
|
|
222
|
+
│ ├── crypto-version.ts # Enum: V1, V2, ...
|
|
223
|
+
│ └── kdf-options.ts # Параметры PBKDF2 / HKDF
|
|
224
|
+
├── srp/
|
|
225
|
+
│ ├── srp-server-service.ts # SRP сервер (challenge, проверка)
|
|
226
|
+
│ ├── srp-client-service.ts # SRP клиент (proof, верификатор)
|
|
227
|
+
│ ├── srp-key-derivation-service.ts # Вывод auth-hash для SRP
|
|
228
|
+
│ ├── srp-context-factory.ts # Создание SRP-контекста (N, g, k, hash)
|
|
229
|
+
│ ├── srp-group.ts # Enum: Group2048, Group4096, ...
|
|
230
|
+
│ └── srp-encoding.ts # BigInt ↔ bytes хелперы
|
|
231
|
+
└── utils/
|
|
232
|
+
├── security-utils.ts # Base64, BigInt, сравнение в постоянное время
|
|
233
|
+
└── srp-encoding.ts # Хеш модулей, сессионный ключ, M1 / M2
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### Быстрый старт
|
|
237
|
+
|
|
238
|
+
#### Шифрование / дешифрование
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
import { CryptoService } from './crypto/crypto-service.js';
|
|
242
|
+
import { CryptoVersion } from './crypto/crypto-version.js';
|
|
243
|
+
|
|
244
|
+
const crypto = new CryptoService();
|
|
245
|
+
const key = crypto.generateRandomBytes(32); // AES-256
|
|
246
|
+
|
|
247
|
+
const encrypted = await crypto.encryptData(
|
|
248
|
+
{ message: "Hello, World!" },
|
|
249
|
+
key,
|
|
250
|
+
CryptoVersion.V1
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
const decrypted = await crypto.decryptData<MyData>(encrypted, key);
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Формат зашифрованных данных (Base64):
|
|
257
|
+
|
|
258
|
+
```
|
|
259
|
+
[Version (1 byte)][Nonce (N bytes)][Ciphertext + Tag]
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
#### Вывод ключей из пароля
|
|
263
|
+
|
|
264
|
+
```typescript
|
|
265
|
+
import { KeyDerivationService } from './crypto/key-derivation-service.js';
|
|
266
|
+
import { CryptoVersion } from './crypto/crypto-version.js';
|
|
267
|
+
|
|
268
|
+
const kdf = new KeyDerivationService();
|
|
269
|
+
const salt = crypto.getRandomValues(new Uint8Array(16));
|
|
270
|
+
|
|
271
|
+
const { kek, authHash } = await kdf.deriveKeysFromPassword(
|
|
272
|
+
"user@example.com",
|
|
273
|
+
"SuperSecret123!",
|
|
274
|
+
salt,
|
|
275
|
+
CryptoVersion.V1
|
|
276
|
+
);
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
- `kek` — `Uint8Array` ключ для AES-GCM
|
|
280
|
+
- `authHash` — Base64-строка для SRP-аутентификации
|
|
281
|
+
|
|
282
|
+
#### SRP-аутентификация
|
|
283
|
+
|
|
284
|
+
**Сервер — генерация challenge**
|
|
285
|
+
|
|
286
|
+
```typescript
|
|
287
|
+
import { SrpServerService } from './srp/srp-server-service.js';
|
|
288
|
+
import { SrpGroup } from './srp/srp-group.js';
|
|
289
|
+
|
|
290
|
+
const server = new SrpServerService();
|
|
291
|
+
|
|
292
|
+
const session = await server.getSrpChallenge(
|
|
293
|
+
"user@example.com",
|
|
294
|
+
verifierBytes,
|
|
295
|
+
salt,
|
|
296
|
+
SrpGroup.Group2048
|
|
297
|
+
);
|
|
298
|
+
|
|
299
|
+
// Отправить клиенту: salt + session.publicKeyB (B)
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
**Клиент — генерация proof**
|
|
303
|
+
|
|
304
|
+
```typescript
|
|
305
|
+
import { SrpClientService } from './srp/srp-client-service.js';
|
|
306
|
+
import { SrpKeyDerivationService } from './srp/srp-key-derivation-service.js';
|
|
307
|
+
import { SrpGroup } from './srp/srp-group.js';
|
|
308
|
+
import { CryptoVersion } from './crypto/crypto-version.js';
|
|
309
|
+
|
|
310
|
+
const client = new SrpClientService();
|
|
311
|
+
const kdf = new SrpKeyDerivationService();
|
|
312
|
+
|
|
313
|
+
// 1. Вывести auth-hash из пароля
|
|
314
|
+
const authHash = await kdf.deriveAuthHashForSrp(
|
|
315
|
+
identity, password, salt, SrpGroup.Group2048, CryptoVersion.V1
|
|
316
|
+
);
|
|
317
|
+
|
|
318
|
+
// 2. Сгенерировать proof
|
|
319
|
+
const { A, M1, SessionKeyK } = await client.generateSrpProof(
|
|
320
|
+
identity,
|
|
321
|
+
authHash,
|
|
322
|
+
btoa(String.fromCharCode(...salt)),
|
|
323
|
+
btoa(String.fromCharCode(...session.publicKeyB)),
|
|
324
|
+
SrpGroup.Group2048
|
|
325
|
+
);
|
|
326
|
+
|
|
327
|
+
// Отправить серверу: A + M1
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
**Сервер — проверка proof**
|
|
331
|
+
|
|
332
|
+
```typescript
|
|
333
|
+
const serverM2 = await server.verifySrpProof(
|
|
334
|
+
session,
|
|
335
|
+
A,
|
|
336
|
+
M1,
|
|
337
|
+
SrpGroup.Group2048
|
|
338
|
+
);
|
|
339
|
+
|
|
340
|
+
// Отправить клиенту: serverM2
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
**Клиент — проверка server proof**
|
|
344
|
+
|
|
345
|
+
```typescript
|
|
346
|
+
const isValid = await client.verifyServerM2(
|
|
347
|
+
A,
|
|
348
|
+
M1,
|
|
349
|
+
SessionKeyK,
|
|
350
|
+
serverM2,
|
|
351
|
+
SrpGroup.Group2048
|
|
352
|
+
);
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
### Безопасность
|
|
356
|
+
|
|
357
|
+
- Все чувствительные буферы явно перезаписываются после использования
|
|
358
|
+
- Соль должна быть **минимум 16 байт**
|
|
359
|
+
- Ключ AES-256 — **строго 32 байта**
|
|
360
|
+
- SRP-группы защищены от атак на малые подгруппы (проверки `A % N != 0`, `B != 0`)
|
|
361
|
+
- Полностью опирается на нативный Web Crypto API — никаких самописных криптопримитивов
|
|
362
|
+
|
|
363
|
+
### Лицензия
|
|
364
|
+
|
|
365
|
+
MIT
|