@arikajs/encryption 0.0.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 +195 -0
- package/dist/Contracts/Encrypter.d.ts +10 -0
- package/dist/Contracts/Encrypter.js +2 -0
- package/dist/Encrypter.d.ts +26 -0
- package/dist/Encrypter.js +87 -0
- package/dist/Exceptions/DecryptionException.d.ts +3 -0
- package/dist/Exceptions/DecryptionException.js +10 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +20 -0
- package/package.json +25 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ArikaJs
|
|
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,195 @@
|
|
|
1
|
+
## Arika Encryption
|
|
2
|
+
|
|
3
|
+
`@arikajs/encryption` provides secure, application-level encryption for the ArikaJS framework.
|
|
4
|
+
|
|
5
|
+
It is responsible for encrypting and decrypting sensitive data such as sessions, cookies, signed payloads, and internal framework values β similar in spirit to Laravelβs `Illuminate\Encryption`.
|
|
6
|
+
|
|
7
|
+
This package is framework-agnostic at runtime, but designed to integrate seamlessly with `@arikajs/foundation` via service providers.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## β¨ Features
|
|
12
|
+
|
|
13
|
+
- π **AES-256-GCM encryption** (modern & secure)
|
|
14
|
+
- π **Single application key** (`APP_KEY`)
|
|
15
|
+
- π§Ύ **Authenticated encryption** (tamper detection)
|
|
16
|
+
- π **Encrypt / decrypt strings & JSON**
|
|
17
|
+
- π§ **Stateless design** (safe for queues & workers)
|
|
18
|
+
- π§© **Framework service friendly**
|
|
19
|
+
- π¦ **TypeScript-first**
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## π¦ Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install @arikajs/encryption
|
|
27
|
+
# or
|
|
28
|
+
yarn add @arikajs/encryption
|
|
29
|
+
# or
|
|
30
|
+
pnpm add @arikajs/encryption
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## π Application Key (Required)
|
|
36
|
+
|
|
37
|
+
This package requires an application key, usually provided via:
|
|
38
|
+
|
|
39
|
+
```ini
|
|
40
|
+
APP_KEY=base64:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
> **Note:** The key must be 32 bytes, Base64-encoded.
|
|
44
|
+
> You can generate one using:
|
|
45
|
+
> `arika key:generate`
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## π Basic Usage (Standalone)
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
import { Encrypter } from '@arikajs/encryption';
|
|
53
|
+
|
|
54
|
+
const encrypter = new Encrypter('base64:your-app-key');
|
|
55
|
+
|
|
56
|
+
const encrypted = encrypter.encrypt('secret data');
|
|
57
|
+
const decrypted = encrypter.decrypt(encrypted);
|
|
58
|
+
|
|
59
|
+
console.log(decrypted); // "secret data"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### π¦ Encrypting Objects / JSON
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
const payload = {
|
|
66
|
+
userId: 1,
|
|
67
|
+
role: 'admin',
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const token = encrypter.encrypt(payload);
|
|
71
|
+
|
|
72
|
+
const data = encrypter.decrypt(token);
|
|
73
|
+
// { userId: 1, role: 'admin' }
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Internally, objects are JSON-serialized automatically.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## π§ Integration with ArikaJS
|
|
81
|
+
|
|
82
|
+
### Register as a service (via Foundation)
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
import { Encrypter } from '@arikajs/encryption';
|
|
86
|
+
|
|
87
|
+
this.app.singleton('encrypter', () => {
|
|
88
|
+
const key = config('app.key');
|
|
89
|
+
|
|
90
|
+
if (!key) {
|
|
91
|
+
throw new Error('APP_KEY is not defined.');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return new Encrypter(key);
|
|
95
|
+
});
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Usage anywhere in the app
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
const encrypter = app.make<Encrypter>('encrypter');
|
|
102
|
+
|
|
103
|
+
const value = encrypter.encrypt('hello');
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## π Security Guarantees
|
|
109
|
+
|
|
110
|
+
- Uses **AES-256-GCM**
|
|
111
|
+
- Every payload includes:
|
|
112
|
+
- Random IV (Initialization Vector)
|
|
113
|
+
- Authentication tag
|
|
114
|
+
- Any tampering β automatic decryption failure
|
|
115
|
+
- No weak or legacy algorithms
|
|
116
|
+
- If data is modified, `decrypt()` will throw.
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## π§© Intended Consumers
|
|
121
|
+
|
|
122
|
+
This package is a core dependency for:
|
|
123
|
+
|
|
124
|
+
| Package | Usage |
|
|
125
|
+
| :--- | :--- |
|
|
126
|
+
| `@arikajs/session` | Encrypted sessions |
|
|
127
|
+
| `@arikajs/http` | Encrypted cookies |
|
|
128
|
+
| `@arikajs/queue` | Secure job payloads |
|
|
129
|
+
| `@arikajs/auth` | Token encryption |
|
|
130
|
+
| `@arikajs/mail` | Signed mail data |
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## π§ API Reference
|
|
135
|
+
|
|
136
|
+
### `new Encrypter(key: string)`
|
|
137
|
+
Creates a new encrypter instance.
|
|
138
|
+
|
|
139
|
+
### `encrypt(value: string | object): string`
|
|
140
|
+
Encrypts a value and returns a string payload.
|
|
141
|
+
|
|
142
|
+
### `decrypt(payload: string): any`
|
|
143
|
+
Decrypts a payload and returns the original value.
|
|
144
|
+
|
|
145
|
+
Throws if:
|
|
146
|
+
- Payload is invalid
|
|
147
|
+
- Payload is tampered
|
|
148
|
+
- Key is incorrect
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## π Architecture
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
encryption/
|
|
156
|
+
βββ src/
|
|
157
|
+
β βββ Encrypter.ts
|
|
158
|
+
β βββ Contracts/
|
|
159
|
+
β β βββ Encrypter.ts
|
|
160
|
+
β βββ Exceptions/
|
|
161
|
+
β β βββ DecryptionException.ts
|
|
162
|
+
β βββ index.ts
|
|
163
|
+
βββ tests/
|
|
164
|
+
βββ package.json
|
|
165
|
+
βββ tsconfig.json
|
|
166
|
+
βββ README.md
|
|
167
|
+
βββ LICENSE
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## π§ Planned (v1.x+)
|
|
173
|
+
|
|
174
|
+
- π Key rotation support
|
|
175
|
+
- π§ͺ Encrypted payload versioning
|
|
176
|
+
- π Multiple key support (fallback decryption)
|
|
177
|
+
- π§· Signed-only (non-encrypted) payloads
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## π§ Philosophy
|
|
182
|
+
|
|
183
|
+
> "Encryption should be invisible, mandatory, and impossible to misuse."
|
|
184
|
+
|
|
185
|
+
This package:
|
|
186
|
+
- Enforces strong defaults
|
|
187
|
+
- Centralizes cryptography
|
|
188
|
+
- Avoids configuration sprawl
|
|
189
|
+
- Keeps security boring and safe
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## π License
|
|
194
|
+
|
|
195
|
+
`@arikajs/encryption` is open-source software licensed under the **MIT License**.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Encrypter as EncrypterContract } from './Contracts/Encrypter';
|
|
2
|
+
export declare class Encrypter implements EncrypterContract {
|
|
3
|
+
private readonly key;
|
|
4
|
+
private readonly cipher;
|
|
5
|
+
constructor(key: string);
|
|
6
|
+
/**
|
|
7
|
+
* Parse the encryption key.
|
|
8
|
+
*/
|
|
9
|
+
protected parseKey(key: string): Buffer;
|
|
10
|
+
/**
|
|
11
|
+
* Encrypt the given value.
|
|
12
|
+
*/
|
|
13
|
+
encrypt(value: any): string;
|
|
14
|
+
/**
|
|
15
|
+
* Decrypt the given value.
|
|
16
|
+
*/
|
|
17
|
+
decrypt(payload: string): any;
|
|
18
|
+
/**
|
|
19
|
+
* Get the JSON payload from the given string.
|
|
20
|
+
*/
|
|
21
|
+
protected getJsonPayload(payload: string): any;
|
|
22
|
+
/**
|
|
23
|
+
* Verify that the encryption payload is valid.
|
|
24
|
+
*/
|
|
25
|
+
protected validPayload(payload: any): boolean;
|
|
26
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Encrypter = void 0;
|
|
4
|
+
const crypto_1 = require("crypto");
|
|
5
|
+
const DecryptionException_1 = require("./Exceptions/DecryptionException");
|
|
6
|
+
class Encrypter {
|
|
7
|
+
key;
|
|
8
|
+
cipher = 'aes-256-gcm';
|
|
9
|
+
constructor(key) {
|
|
10
|
+
this.key = this.parseKey(key);
|
|
11
|
+
if (this.key.length !== 32) {
|
|
12
|
+
throw new Error('The encryption key must be 32 bytes.');
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Parse the encryption key.
|
|
17
|
+
*/
|
|
18
|
+
parseKey(key) {
|
|
19
|
+
if (key.startsWith('base64:')) {
|
|
20
|
+
return Buffer.from(key.substring(7), 'base64');
|
|
21
|
+
}
|
|
22
|
+
return Buffer.from(key);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Encrypt the given value.
|
|
26
|
+
*/
|
|
27
|
+
encrypt(value) {
|
|
28
|
+
const iv = (0, crypto_1.randomBytes)(16);
|
|
29
|
+
const cipher = (0, crypto_1.createCipheriv)(this.cipher, this.key, iv);
|
|
30
|
+
const jsonValue = JSON.stringify(value);
|
|
31
|
+
let encrypted = cipher.update(jsonValue, 'utf8', 'base64');
|
|
32
|
+
encrypted += cipher.final('base64');
|
|
33
|
+
const tag = cipher.getAuthTag();
|
|
34
|
+
const payload = {
|
|
35
|
+
iv: iv.toString('base64'),
|
|
36
|
+
value: encrypted,
|
|
37
|
+
tag: tag.toString('base64'),
|
|
38
|
+
};
|
|
39
|
+
return Buffer.from(JSON.stringify(payload)).toString('base64');
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Decrypt the given value.
|
|
43
|
+
*/
|
|
44
|
+
decrypt(payload) {
|
|
45
|
+
const jsonPayload = this.getJsonPayload(payload);
|
|
46
|
+
const iv = Buffer.from(jsonPayload.iv, 'base64');
|
|
47
|
+
const tag = Buffer.from(jsonPayload.tag, 'base64');
|
|
48
|
+
const value = jsonPayload.value;
|
|
49
|
+
try {
|
|
50
|
+
const decipher = (0, crypto_1.createDecipheriv)(this.cipher, this.key, iv);
|
|
51
|
+
decipher.setAuthTag(tag);
|
|
52
|
+
let decrypted = decipher.update(value, 'base64', 'utf8');
|
|
53
|
+
decrypted += decipher.final('utf8');
|
|
54
|
+
return JSON.parse(decrypted);
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
throw new DecryptionException_1.DecryptionException();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Get the JSON payload from the given string.
|
|
62
|
+
*/
|
|
63
|
+
getJsonPayload(payload) {
|
|
64
|
+
try {
|
|
65
|
+
const decoded = Buffer.from(payload, 'base64').toString('utf8');
|
|
66
|
+
const data = JSON.parse(decoded);
|
|
67
|
+
if (!this.validPayload(data)) {
|
|
68
|
+
throw new DecryptionException_1.DecryptionException();
|
|
69
|
+
}
|
|
70
|
+
return data;
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
throw new DecryptionException_1.DecryptionException();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Verify that the encryption payload is valid.
|
|
78
|
+
*/
|
|
79
|
+
validPayload(payload) {
|
|
80
|
+
return typeof payload === 'object' &&
|
|
81
|
+
payload !== null &&
|
|
82
|
+
payload.iv &&
|
|
83
|
+
payload.value &&
|
|
84
|
+
payload.tag;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
exports.Encrypter = Encrypter;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DecryptionException = void 0;
|
|
4
|
+
class DecryptionException extends Error {
|
|
5
|
+
constructor(message = 'The payload is invalid or has been tampered with.') {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = 'DecryptionException';
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
exports.DecryptionException = DecryptionException;
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.Encrypter = void 0;
|
|
18
|
+
var Encrypter_1 = require("./Encrypter");
|
|
19
|
+
Object.defineProperty(exports, "Encrypter", { enumerable: true, get: function () { return Encrypter_1.Encrypter; } });
|
|
20
|
+
__exportStar(require("./Exceptions/DecryptionException"), exports);
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@arikajs/encryption",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Secure, application-level encryption for the ArikaJS framework.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "tsc -p tsconfig.json",
|
|
10
|
+
"clean": "rm -rf dist",
|
|
11
|
+
"prepare": "echo skip"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist"
|
|
15
|
+
],
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=20.0.0"
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@types/node": "^20.11.24",
|
|
22
|
+
"typescript": "^5.3.3"
|
|
23
|
+
},
|
|
24
|
+
"author": "Prakash Tank"
|
|
25
|
+
}
|