@bantis/local-cipher 1.0.0 → 2.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 +266 -130
- package/dist/index.d.ts +479 -42
- package/dist/index.esm.js +1018 -133
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1028 -132
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -1,27 +1,26 @@
|
|
|
1
|
-
# @bantis/local-cipher
|
|
1
|
+
# @bantis/local-cipher v2.0.0
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@bantis/local-cipher)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
|
-
[](https://github.com/master-tech-team/-
|
|
5
|
+
[](https://github.com/master-tech-team/-bantis-local-cipher)
|
|
6
6
|
|
|
7
|
-
Librería de cifrado local AES-256-GCM
|
|
7
|
+
Librería enterprise de cifrado local AES-256-GCM con **configuración personalizable**, **eventos**, **compresión**, **expiración**, **namespaces** y **rotación de claves**. Compatible con **Angular**, **React** y **JavaScript vanilla**.
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## ✨ Novedades v2.0.0
|
|
10
10
|
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
- ✅ **Fallback transparente** - Funciona en navegadores sin Web Crypto API
|
|
11
|
+
- 🎛️ **Configuración Personalizable** - Ajusta iteraciones, longitud de clave, salt e IV
|
|
12
|
+
- 🎯 **Sistema de Eventos** - Escucha eventos de cifrado, expiración, errores, etc.
|
|
13
|
+
- 🗜️ **Compresión Automática** - Gzip para valores > 1KB (configurable)
|
|
14
|
+
- ⏰ **Expiración/TTL** - Establece tiempo de vida con auto-limpieza
|
|
15
|
+
- 🔐 **Validación de Integridad** - Checksums SHA-256 automáticos
|
|
16
|
+
- 📦 **Namespaces** - Organiza datos en espacios aislados
|
|
17
|
+
- 🔄 **Rotación de Claves** - Re-encripta datos con nuevas claves
|
|
18
|
+
- 📊 **Modo Debug** - Logging configurable con niveles
|
|
20
19
|
|
|
21
20
|
## 📦 Instalación
|
|
22
21
|
|
|
23
22
|
```bash
|
|
24
|
-
npm install @
|
|
23
|
+
npm install @bantis/local-cipher
|
|
25
24
|
```
|
|
26
25
|
|
|
27
26
|
## 🚀 Uso Rápido
|
|
@@ -29,28 +28,71 @@ npm install @mtt/local-cipher
|
|
|
29
28
|
### JavaScript Vanilla
|
|
30
29
|
|
|
31
30
|
```javascript
|
|
32
|
-
import {
|
|
31
|
+
import { SecureStorage } from '@bantis/local-cipher';
|
|
32
|
+
|
|
33
|
+
const storage = SecureStorage.getInstance();
|
|
33
34
|
|
|
34
35
|
// Guardar datos encriptados
|
|
35
|
-
await
|
|
36
|
+
await storage.setItem('accessToken', 'mi-token-secreto');
|
|
36
37
|
|
|
37
38
|
// Leer datos desencriptados
|
|
38
|
-
const token = await
|
|
39
|
+
const token = await storage.getItem('accessToken');
|
|
40
|
+
|
|
41
|
+
// Con expiración (1 hora)
|
|
42
|
+
await storage.setItemWithExpiry('session', sessionData, { expiresIn: 3600000 });
|
|
39
43
|
|
|
40
44
|
// Eliminar datos
|
|
41
|
-
await
|
|
45
|
+
await storage.removeItem('accessToken');
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Con Configuración Personalizada
|
|
42
49
|
|
|
43
|
-
|
|
44
|
-
|
|
50
|
+
```javascript
|
|
51
|
+
const storage = SecureStorage.getInstance({
|
|
52
|
+
encryption: {
|
|
53
|
+
iterations: 150000, // PBKDF2 iterations (default: 100000)
|
|
54
|
+
keyLength: 256, // 128, 192, or 256 bits
|
|
55
|
+
saltLength: 16, // Salt size in bytes
|
|
56
|
+
ivLength: 12, // IV size in bytes
|
|
57
|
+
appIdentifier: 'my-app' // Custom app identifier
|
|
58
|
+
},
|
|
59
|
+
storage: {
|
|
60
|
+
compression: true, // Enable compression
|
|
61
|
+
compressionThreshold: 1024, // Compress if > 1KB
|
|
62
|
+
autoCleanup: true, // Auto-clean expired items
|
|
63
|
+
cleanupInterval: 60000 // Cleanup every 60s
|
|
64
|
+
},
|
|
65
|
+
debug: {
|
|
66
|
+
enabled: true, // Enable debug logging
|
|
67
|
+
logLevel: 'verbose', // silent, error, warn, info, debug, verbose
|
|
68
|
+
prefix: 'MyApp' // Log prefix
|
|
69
|
+
}
|
|
70
|
+
});
|
|
45
71
|
```
|
|
46
72
|
|
|
47
73
|
### React
|
|
48
74
|
|
|
49
75
|
```jsx
|
|
50
|
-
import { useSecureStorage } from '@
|
|
76
|
+
import { useSecureStorage, useSecureStorageWithExpiry, useSecureStorageEvents } from '@bantis/local-cipher';
|
|
51
77
|
|
|
52
78
|
function App() {
|
|
79
|
+
// Hook básico
|
|
53
80
|
const [token, setToken, loading] = useSecureStorage('accessToken', '');
|
|
81
|
+
|
|
82
|
+
// Hook con expiración
|
|
83
|
+
const [session, setSession] = useSecureStorageWithExpiry(
|
|
84
|
+
'session',
|
|
85
|
+
null,
|
|
86
|
+
{ expiresIn: 3600000 }
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
// Escuchar eventos
|
|
90
|
+
useSecureStorageEvents('expired', (data) => {
|
|
91
|
+
console.log('Item expired:', data.key);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// Usar namespace
|
|
95
|
+
const userStorage = useNamespace('user');
|
|
54
96
|
|
|
55
97
|
if (loading) return <div>Cargando...</div>;
|
|
56
98
|
|
|
@@ -68,169 +110,261 @@ function App() {
|
|
|
68
110
|
### Angular
|
|
69
111
|
|
|
70
112
|
```typescript
|
|
71
|
-
import { SecureStorageService } from '@
|
|
113
|
+
import { SecureStorageService } from '@bantis/local-cipher';
|
|
72
114
|
|
|
73
115
|
@Component({
|
|
74
116
|
selector: 'app-root',
|
|
75
|
-
template:
|
|
117
|
+
template: `
|
|
118
|
+
<div>{{ token$ | async }}</div>
|
|
119
|
+
<button (click)="saveToken()">Guardar</button>
|
|
120
|
+
`
|
|
76
121
|
})
|
|
77
|
-
export class AppComponent {
|
|
78
|
-
token$ = this.
|
|
79
|
-
|
|
80
|
-
constructor(private
|
|
122
|
+
export class AppComponent implements OnInit {
|
|
123
|
+
token$ = this.storage.getItem('accessToken');
|
|
124
|
+
|
|
125
|
+
constructor(private storage: SecureStorageService) {}
|
|
126
|
+
|
|
127
|
+
ngOnInit() {
|
|
128
|
+
// Escuchar eventos
|
|
129
|
+
this.storage.events$.subscribe(event => {
|
|
130
|
+
console.log('Storage event:', event);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// Eventos específicos
|
|
134
|
+
this.storage.onEvent$('expired').subscribe(event => {
|
|
135
|
+
console.log('Item expired:', event.key);
|
|
136
|
+
});
|
|
137
|
+
}
|
|
81
138
|
|
|
82
|
-
saveToken(
|
|
83
|
-
this.
|
|
139
|
+
saveToken() {
|
|
140
|
+
this.storage.setItemWithExpiry('token', 'value', { expiresIn: 3600000 })
|
|
141
|
+
.subscribe();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
saveObject() {
|
|
145
|
+
this.storage.setObjectWithExpiry('user', { id: 1 }, { expiresIn: 7200000 })
|
|
146
|
+
.subscribe();
|
|
84
147
|
}
|
|
85
148
|
}
|
|
86
149
|
```
|
|
87
150
|
|
|
88
|
-
## 📚
|
|
151
|
+
## 📚 API Completa
|
|
89
152
|
|
|
90
|
-
###
|
|
153
|
+
### SecureStorage
|
|
91
154
|
|
|
92
|
-
####
|
|
155
|
+
#### Métodos Básicos
|
|
93
156
|
|
|
94
|
-
|
|
95
|
-
|
|
157
|
+
```typescript
|
|
158
|
+
setItem(key: string, value: string): Promise<void>
|
|
159
|
+
getItem(key: string): Promise<string | null>
|
|
160
|
+
removeItem(key: string): Promise<void>
|
|
161
|
+
hasItem(key: string): Promise<boolean>
|
|
162
|
+
clear(): void
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
#### Expiración
|
|
96
166
|
|
|
97
|
-
|
|
98
|
-
|
|
167
|
+
```typescript
|
|
168
|
+
setItemWithExpiry(key: string, value: string, options: ExpiryOptions): Promise<void>
|
|
169
|
+
cleanExpired(): Promise<number>
|
|
99
170
|
|
|
100
|
-
|
|
101
|
-
|
|
171
|
+
// Opciones
|
|
172
|
+
interface ExpiryOptions {
|
|
173
|
+
expiresIn?: number; // Milisegundos desde ahora
|
|
174
|
+
expiresAt?: Date; // Fecha absoluta
|
|
175
|
+
}
|
|
176
|
+
```
|
|
102
177
|
|
|
103
|
-
|
|
104
|
-
Verifica si existe una clave.
|
|
178
|
+
#### Eventos
|
|
105
179
|
|
|
106
|
-
|
|
107
|
-
|
|
180
|
+
```typescript
|
|
181
|
+
on(event: StorageEventType, listener: EventListener): void
|
|
182
|
+
once(event: StorageEventType, listener: EventListener): void
|
|
183
|
+
off(event: StorageEventType, listener: EventListener): void
|
|
184
|
+
removeAllListeners(event?: StorageEventType): void
|
|
185
|
+
|
|
186
|
+
// Tipos de eventos
|
|
187
|
+
type StorageEventType =
|
|
188
|
+
| 'encrypted' | 'decrypted' | 'deleted' | 'cleared'
|
|
189
|
+
| 'expired' | 'error' | 'keyRotated' | 'compressed' | 'decompressed';
|
|
190
|
+
```
|
|
108
191
|
|
|
109
|
-
|
|
110
|
-
Migra datos existentes no encriptados a formato encriptado.
|
|
192
|
+
#### Namespaces
|
|
111
193
|
|
|
112
|
-
|
|
194
|
+
```typescript
|
|
195
|
+
namespace(name: string): NamespacedStorage
|
|
113
196
|
|
|
114
|
-
|
|
115
|
-
|
|
197
|
+
// Ejemplo
|
|
198
|
+
const userStorage = storage.namespace('user');
|
|
199
|
+
const sessionStorage = storage.namespace('session');
|
|
116
200
|
|
|
117
|
-
|
|
118
|
-
|
|
201
|
+
await userStorage.setItem('profile', data);
|
|
202
|
+
await userStorage.clearNamespace(); // Solo limpia este namespace
|
|
119
203
|
```
|
|
120
204
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
#### `useSecureStorageItem(key: string)`
|
|
124
|
-
Verifica si existe una clave.
|
|
205
|
+
#### Integridad
|
|
125
206
|
|
|
126
|
-
```
|
|
127
|
-
|
|
207
|
+
```typescript
|
|
208
|
+
verifyIntegrity(key: string): Promise<boolean>
|
|
209
|
+
getIntegrityInfo(key: string): Promise<IntegrityInfo>
|
|
210
|
+
|
|
211
|
+
interface IntegrityInfo {
|
|
212
|
+
valid: boolean;
|
|
213
|
+
lastModified: number;
|
|
214
|
+
checksum: string;
|
|
215
|
+
version: number;
|
|
216
|
+
}
|
|
128
217
|
```
|
|
129
218
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
#### `useSecureStorageDebug()`
|
|
133
|
-
Obtiene información de debug del sistema.
|
|
219
|
+
#### Rotación de Claves
|
|
134
220
|
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
|
|
221
|
+
```typescript
|
|
222
|
+
rotateKeys(): Promise<void>
|
|
223
|
+
exportEncryptedData(): Promise<EncryptedBackup>
|
|
224
|
+
importEncryptedData(backup: EncryptedBackup): Promise<void>
|
|
225
|
+
|
|
226
|
+
// Ejemplo
|
|
227
|
+
const backup = await storage.exportEncryptedData();
|
|
228
|
+
await storage.rotateKeys();
|
|
229
|
+
// Si algo sale mal:
|
|
230
|
+
await storage.importEncryptedData(backup);
|
|
138
231
|
```
|
|
139
232
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
#### `SecureStorageService`
|
|
233
|
+
#### Debug
|
|
143
234
|
|
|
144
235
|
```typescript
|
|
145
|
-
|
|
146
|
-
|
|
236
|
+
getDebugInfo(): {
|
|
237
|
+
cryptoSupported: boolean;
|
|
238
|
+
encryptedKeys: string[];
|
|
239
|
+
unencryptedKeys: string[];
|
|
240
|
+
totalKeys: number;
|
|
241
|
+
config: SecureStorageConfig;
|
|
242
|
+
}
|
|
243
|
+
```
|
|
147
244
|
|
|
148
|
-
|
|
149
|
-
this.secureStorage.setItem('key', 'value').subscribe();
|
|
245
|
+
## 🎯 Casos de Uso
|
|
150
246
|
|
|
151
|
-
|
|
152
|
-
this.secureStorage.getItem('key').subscribe(value => console.log(value));
|
|
247
|
+
### 1. Session Management con Expiración
|
|
153
248
|
|
|
154
|
-
|
|
155
|
-
|
|
249
|
+
```javascript
|
|
250
|
+
// Guardar sesión que expira en 30 minutos
|
|
251
|
+
await storage.setItemWithExpiry('session', sessionData, {
|
|
252
|
+
expiresIn: 30 * 60 * 1000
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
// Auto-limpieza cada minuto
|
|
256
|
+
const storage = SecureStorage.getInstance({
|
|
257
|
+
storage: { autoCleanup: true, cleanupInterval: 60000 }
|
|
258
|
+
});
|
|
259
|
+
```
|
|
156
260
|
|
|
157
|
-
|
|
158
|
-
this.secureStorage.getObject<User>('user').subscribe(user => console.log(user));
|
|
261
|
+
### 2. Organización con Namespaces
|
|
159
262
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
263
|
+
```javascript
|
|
264
|
+
const userStorage = storage.namespace('user');
|
|
265
|
+
const appStorage = storage.namespace('app');
|
|
266
|
+
const tempStorage = storage.namespace('temp');
|
|
267
|
+
|
|
268
|
+
await userStorage.setItem('profile', userData);
|
|
269
|
+
await appStorage.setItem('settings', appSettings);
|
|
270
|
+
await tempStorage.setItem('cache', cacheData);
|
|
163
271
|
|
|
164
|
-
|
|
272
|
+
// Limpiar solo datos temporales
|
|
273
|
+
await tempStorage.clearNamespace();
|
|
274
|
+
```
|
|
165
275
|
|
|
166
|
-
|
|
276
|
+
### 3. Monitoreo con Eventos
|
|
167
277
|
|
|
168
278
|
```javascript
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
279
|
+
storage.on('encrypted', ({ key, metadata }) => {
|
|
280
|
+
console.log(`✅ Encrypted: ${key}`, metadata);
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
storage.on('expired', ({ key }) => {
|
|
284
|
+
console.warn(`⏰ Expired: ${key}`);
|
|
285
|
+
// Refrescar datos o redirigir a login
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
storage.on('error', ({ key, error }) => {
|
|
289
|
+
console.error(`❌ Error on ${key}:`, error);
|
|
290
|
+
// Enviar a sistema de logging
|
|
291
|
+
});
|
|
178
292
|
```
|
|
179
293
|
|
|
180
|
-
|
|
294
|
+
### 4. Rotación de Claves Programada
|
|
181
295
|
|
|
182
|
-
|
|
296
|
+
```javascript
|
|
297
|
+
// Rotar claves cada 30 días
|
|
298
|
+
setInterval(async () => {
|
|
299
|
+
console.log('Rotating encryption keys...');
|
|
300
|
+
const backup = await storage.exportEncryptedData();
|
|
301
|
+
|
|
302
|
+
try {
|
|
303
|
+
await storage.rotateKeys();
|
|
304
|
+
console.log('Keys rotated successfully');
|
|
305
|
+
} catch (error) {
|
|
306
|
+
console.error('Rotation failed, restoring backup');
|
|
307
|
+
await storage.importEncryptedData(backup);
|
|
308
|
+
}
|
|
309
|
+
}, 30 * 24 * 60 * 60 * 1000);
|
|
310
|
+
```
|
|
183
311
|
|
|
184
|
-
|
|
312
|
+
## 🔄 Migración desde v1
|
|
185
313
|
|
|
186
|
-
|
|
187
|
-
✅ **Lectura de archivos locales** - Malware que lee archivos del navegador no puede descifrar los datos
|
|
188
|
-
✅ **Ofuscación** - Los nombres de las claves también están encriptados
|
|
314
|
+
### Cambios Principales
|
|
189
315
|
|
|
190
|
-
|
|
316
|
+
**v1:**
|
|
317
|
+
```javascript
|
|
318
|
+
const storage = SecureStorage.getInstance();
|
|
319
|
+
```
|
|
191
320
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
321
|
+
**v2 (compatible):**
|
|
322
|
+
```javascript
|
|
323
|
+
// Funciona igual que v1
|
|
324
|
+
const storage = SecureStorage.getInstance();
|
|
195
325
|
|
|
196
|
-
|
|
326
|
+
// O con configuración
|
|
327
|
+
const storage = SecureStorage.getInstance({
|
|
328
|
+
encryption: { iterations: 150000 }
|
|
329
|
+
});
|
|
330
|
+
```
|
|
197
331
|
|
|
198
|
-
|
|
199
|
-
2. **Derivación de clave** - Se usa PBKDF2 con 100,000 iteraciones para derivar una clave AES-256
|
|
200
|
-
3. **Cifrado AES-GCM** - Cada valor se encripta con un IV aleatorio único
|
|
201
|
-
4. **Almacenamiento** - Se guarda `IV + datos encriptados` en Base64
|
|
332
|
+
### Migración Automática
|
|
202
333
|
|
|
203
|
-
|
|
334
|
+
Los datos de v1 se migran automáticamente al leerlos. No requiere acción del usuario.
|
|
204
335
|
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
user: '{"id":1,"name":"Juan"}'
|
|
336
|
+
```javascript
|
|
337
|
+
// v1 data format: plain encrypted string
|
|
338
|
+
// v2 data format: JSON with metadata
|
|
209
339
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
__enc_9c3e7b1a5f8d: "QW5vdGhlckVuY3J5cHRlZA..."
|
|
213
|
-
__app_salt: "cmFuZG9tU2FsdEhlcmU="
|
|
340
|
+
// Al hacer getItem(), v1 data se detecta y migra automáticamente
|
|
341
|
+
const value = await storage.getItem('oldKey'); // ✅ Migrado a v2
|
|
214
342
|
```
|
|
215
343
|
|
|
216
|
-
##
|
|
344
|
+
## 🛡️ Seguridad
|
|
217
345
|
|
|
218
|
-
|
|
219
|
-
import { debugEncryptionState, testEncryption, forceMigration } from '@mtt/local-cipher';
|
|
346
|
+
### Protección
|
|
220
347
|
|
|
221
|
-
|
|
222
|
-
|
|
348
|
+
✅ **XSS** - Datos encriptados incluso si script malicioso accede a localStorage
|
|
349
|
+
✅ **Lectura local** - Malware no puede descifrar sin la clave del navegador
|
|
350
|
+
✅ **Ofuscación** - Nombres de claves encriptados
|
|
351
|
+
✅ **Integridad** - Checksums SHA-256 detectan manipulación
|
|
223
352
|
|
|
224
|
-
|
|
225
|
-
await testEncryption();
|
|
353
|
+
### Limitaciones
|
|
226
354
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
355
|
+
❌ **Servidor** - Encriptación solo cliente
|
|
356
|
+
❌ **MITM** - Usa HTTPS
|
|
357
|
+
❌ **Sesión activa** - Clave en memoria durante uso
|
|
230
358
|
|
|
231
|
-
|
|
359
|
+
### Arquitectura
|
|
360
|
+
|
|
361
|
+
1. **Fingerprinting** - Huella única del navegador
|
|
362
|
+
2. **PBKDF2** - 100,000+ iteraciones para derivar clave
|
|
363
|
+
3. **AES-256-GCM** - Cifrado con autenticación
|
|
364
|
+
4. **SHA-256** - Checksums de integridad
|
|
365
|
+
5. **Gzip** - Compresión opcional
|
|
232
366
|
|
|
233
|
-
|
|
367
|
+
## 🌐 Compatibilidad
|
|
234
368
|
|
|
235
369
|
- ✅ Chrome 37+
|
|
236
370
|
- ✅ Firefox 34+
|
|
@@ -238,16 +372,18 @@ Requiere navegadores con soporte para **Web Crypto API**:
|
|
|
238
372
|
- ✅ Edge 12+
|
|
239
373
|
- ✅ Opera 24+
|
|
240
374
|
|
|
241
|
-
**
|
|
375
|
+
**Fallback:** En navegadores sin Web Crypto API, usa localStorage normal.
|
|
242
376
|
|
|
243
377
|
## 📄 Licencia
|
|
244
378
|
|
|
245
379
|
MIT © MTT
|
|
246
380
|
|
|
247
|
-
##
|
|
381
|
+
## 🔗 Enlaces
|
|
248
382
|
|
|
249
|
-
|
|
383
|
+
- [GitHub](https://github.com/master-tech-team/-bantis-local-cipher)
|
|
384
|
+
- [npm](https://www.npmjs.com/package/@bantis/local-cipher)
|
|
385
|
+
- [Changelog](./CHANGELOG.md)
|
|
250
386
|
|
|
251
|
-
##
|
|
387
|
+
## 🤝 Contribuir
|
|
252
388
|
|
|
253
|
-
|
|
389
|
+
Las contribuciones son bienvenidas. Abre un issue o pull request en GitHub.
|