@chizalam/safe-crypto 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 ADDED
@@ -0,0 +1,69 @@
1
+ # ๐Ÿ›ก๏ธ Safe Crypto
2
+ ### Production-ready AES-256-GCM encryption with built-in safety rails.
3
+
4
+ A lightweight, zero-dependency TypeScript wrapper around Node's `crypto` module. Designed with the **Safe Result Pattern** to eliminate unhandled runtime exceptions.
5
+
6
+ ---
7
+
8
+ ## โœจ Features
9
+ * **๐Ÿ”’ AES-256-GCM:** Industry-standard authenticated encryption.
10
+ * **๐Ÿšฆ Safe Result Pattern:** No more `try/catch` blocks. Functions return a status object.
11
+ * **๐Ÿ›ก๏ธ Tamper Proof:** Built-in "Auth Tag" validation (GCM magic).
12
+ * **๐Ÿ—œ๏ธ Small & Fast:** Zero external dependencies (uses `node:crypto`).
13
+ * **๐Ÿ’ช Fully Typed:** Written in TypeScript for excellent IDE support.
14
+
15
+ ---
16
+
17
+ ## ๐Ÿ“ฆ Installation
18
+
19
+ ```bash
20
+ npm install @chizalam/safe-crypto
21
+
22
+ ๐Ÿš€ Quick Start
23
+ 1. Initialize the EngineYou need a 64-character hex string (32 bytes) as your encryption key.
24
+
25
+ import { AesGcm } from '@chizalam/safe-crypto';
26
+
27
+ const crypto = new AesGcm({
28
+ encryptionKey: 'your-64-character-hex-string-goes-here'
29
+ });
30
+
31
+ 2. Encrypting (Safe Pattern)
32
+ The safeEncrypt method handles errors internally and returns a descriptive object.
33
+
34
+ const result = crypto.safeEncrypt("Hello Secret World");
35
+
36
+ if (result.success) {
37
+ console.log(result.data); // Output: "iv:tag:ciphertext"
38
+ } else {
39
+ console.error("Encryption failed:", result.error);
40
+ }
41
+ 3. Decrypting (Safe Pattern)
42
+ The safeDecrypt method ensures the data is valid before returning it.
43
+ const result = crypto.safeDecrypt("iv:tag:ciphertext");
44
+
45
+ if (result.success) {
46
+ // TypeScript knows 'data' is a string here
47
+ console.log("Decrypted value:", result.data);
48
+ } else {
49
+ // Handles tampered data, invalid formats, or wrong keys
50
+ console.error("Decryption failed:", result.error);
51
+ }
52
+ ๐Ÿ› ๏ธ Utility
53
+ FunctionsisEncrypted(value)
54
+ Check if a string matches the iv:tag:ciphertext format before attempting decryption.
55
+
56
+ if (crypto.isEncrypted(inputValue)) {
57
+ const result = crypto.safeDecrypt(inputValue);
58
+ }
59
+ โš™๏ธ Configuration
60
+ Key64 Hex Chars (Must be a valid 256-bit key in hexadecimal).
61
+ Node Version16.x or higher (Uses node:crypto built-ins).
62
+ IV Length 12Bytes (Fixed to NIST-recommended GCM standard.)
63
+
64
+ ๐Ÿงช Testing
65
+ We use Vitest for all unit tests.
66
+ npm test
67
+
68
+
69
+ ๐Ÿ“œ LicenseMIT ยฉ 2026 Chizalam
@@ -0,0 +1,38 @@
1
+ /** * Represents a successful or failed crypto operation.
2
+ */
3
+ export type CryptoResult<T> = {
4
+ success: true;
5
+ data: T;
6
+ error: null;
7
+ } | {
8
+ success: false;
9
+ data: null;
10
+ error: string;
11
+ };
12
+ /** * Formatted as hex strings: `iv:tag:ciphertext`
13
+ */
14
+ export type EncryptedString = `${string}:${string}:${string}`;
15
+ export interface CryptoConfig {
16
+ /** A 64-character hex string (256-bit key) */
17
+ encryptionKey: string;
18
+ }
19
+ /**
20
+ * A production-ready AES-256-GCM encryption engine.
21
+ */
22
+ export declare class AesGcm {
23
+ private readonly algorithm;
24
+ private readonly ivLength;
25
+ private readonly secretKey;
26
+ constructor(config: CryptoConfig);
27
+ /**
28
+ * Encrypts a string and returns a status object.
29
+ */
30
+ safeEncrypt(plaintext: string): CryptoResult<EncryptedString>;
31
+ /**
32
+ * Decrypts a string and returns a status object.
33
+ */
34
+ safeDecrypt(payload: EncryptedString): CryptoResult<string>;
35
+ private encrypt;
36
+ private decrypt;
37
+ isEncrypted(value: any): value is EncryptedString;
38
+ }
package/dist/index.js ADDED
@@ -0,0 +1,60 @@
1
+ import { randomBytes, createCipheriv, createDecipheriv, createSecretKey } from "node:crypto";
2
+ /**
3
+ * A production-ready AES-256-GCM encryption engine.
4
+ */
5
+ export class AesGcm {
6
+ algorithm = "aes-256-gcm";
7
+ ivLength = 12;
8
+ secretKey;
9
+ constructor(config) {
10
+ if (!/^[0-9A-Fa-f]{64}$/.test(config.encryptionKey)) {
11
+ throw new Error("Encryption key must be a 64-character hex string.");
12
+ }
13
+ this.secretKey = createSecretKey(Buffer.from(config.encryptionKey, "hex"));
14
+ }
15
+ /**
16
+ * Encrypts a string and returns a status object.
17
+ */
18
+ safeEncrypt(plaintext) {
19
+ try {
20
+ const data = this.encrypt(plaintext);
21
+ return { success: true, data, error: null };
22
+ }
23
+ catch (e) {
24
+ return { success: false, data: null, error: e instanceof Error ? e.message : "Encryption failed" };
25
+ }
26
+ }
27
+ /**
28
+ * Decrypts a string and returns a status object.
29
+ */
30
+ safeDecrypt(payload) {
31
+ try {
32
+ const data = this.decrypt(payload);
33
+ return { success: true, data, error: null };
34
+ }
35
+ catch (e) {
36
+ return { success: false, data: null, error: e instanceof Error ? e.message : "Decryption failed" };
37
+ }
38
+ }
39
+ encrypt(plaintext) {
40
+ const iv = randomBytes(this.ivLength);
41
+ const cipher = createCipheriv(this.algorithm, this.secretKey, iv);
42
+ const ciphertext = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
43
+ const tag = cipher.getAuthTag();
44
+ return `${iv.toString("hex")}:${tag.toString("hex")}:${ciphertext.toString("hex")}`;
45
+ }
46
+ decrypt(payload) {
47
+ const [ivHex, tagHex, ctHex] = payload.split(":");
48
+ if (!ivHex || !tagHex || !ctHex)
49
+ throw new Error("Invalid format");
50
+ const decipher = createDecipheriv(this.algorithm, this.secretKey, Buffer.from(ivHex, "hex"));
51
+ decipher.setAuthTag(Buffer.from(tagHex, "hex"));
52
+ return Buffer.concat([decipher.update(Buffer.from(ctHex, "hex")), decipher.final()]).toString("utf8");
53
+ }
54
+ isEncrypted(value) {
55
+ if (typeof value !== "string")
56
+ return false;
57
+ const parts = value.split(":");
58
+ return parts.length === 3 && parts.every(p => /^[0-9A-Fa-f]+$/.test(p));
59
+ }
60
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,20 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { AesGcm } from './index.js';
3
+ describe('AesGcm Engine', () => {
4
+ const key = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef';
5
+ const engine = new AesGcm({ encryptionKey: key });
6
+ it('should handle safe decryption of invalid data without crashing', () => {
7
+ const result = engine.safeDecrypt('bad:format:data');
8
+ expect(result.success).toBe(false);
9
+ expect(result.error).toBeTypeOf('string');
10
+ });
11
+ it('should successfully encrypt and decrypt', () => {
12
+ const original = "Top Secret";
13
+ const encrypted = engine.safeEncrypt(original);
14
+ if (encrypted.success) {
15
+ const decrypted = engine.safeDecrypt(encrypted.data);
16
+ expect(decrypted.success).toBe(true);
17
+ expect(decrypted.data).toBe(original);
18
+ }
19
+ });
20
+ });
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@chizalam/safe-crypto",
3
+ "version": "1.0.0",
4
+ "description": "A production-ready AES-256-GCM encryption engine with safe result handling.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "files": ["dist"],
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "test": "vitest",
12
+ "prepublishOnly": "npm run build"
13
+ },
14
+ "keywords": [
15
+ "crypto",
16
+ "aes-256-gcm",
17
+ "encryption",
18
+ "safe-result",
19
+ "typescript"
20
+ ],
21
+ "author": "Chizalam",
22
+ "license": "MIT",
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "git+https://github.com/cemuchay/safe-crypto.git"
26
+ },
27
+ "bugs": {
28
+ "url": "https://github.com/cemuchay/safe-crypto/issues"
29
+ },
30
+ "homepage": "https://github.com/cemuchay/safe-crypto#readme",
31
+ "devDependencies": {
32
+ "@types/node": "^25.5.2",
33
+ "typescript": "^6.0.2",
34
+ "vitest": "^4.1.3"
35
+ }
36
+ }