@abhedyam/sdk 1.0.5
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 +69 -0
- package/index.js +146 -0
- package/package.json +21 -0
- package/test.js +35 -0
package/README.md
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Abhedya SDK for Node.js
|
|
2
|
+
|
|
3
|
+
**Unmatched Speed. Unbreakable Security.**
|
|
4
|
+
|
|
5
|
+
Abhedya is a high-performance Post-Quantum Cryptography (PQC) library enabling **Sanskrit-Encoded** encryption. This SDK provides Node.js bindings to the optimized Rust core.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Quantum Safety**: Lattice-based LWE implementation ($N=768$).
|
|
10
|
+
- **Dual Mode**:
|
|
11
|
+
- **Standard**: High-throughput binary encryption.
|
|
12
|
+
- **Metered**: Steganographic mode producing Sanskrit-metered ciphertexts.
|
|
13
|
+
- **Performance**: High-speed C-FFI using `koffi` (Node.js 25+ compatible).
|
|
14
|
+
- **Zero-Copy**: Efficient buffer management.
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install abhedya-sdk
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
```javascript
|
|
25
|
+
const { Abhedya, EncryptionMode } = require("abhedya-sdk");
|
|
26
|
+
|
|
27
|
+
// Initialize
|
|
28
|
+
const sdk = new Abhedya();
|
|
29
|
+
|
|
30
|
+
// 1. Generate KeyPair
|
|
31
|
+
console.log("Generating Keys...");
|
|
32
|
+
const keys = sdk.keygen();
|
|
33
|
+
// keys.publicKey, keys.secretKey (Buffers)
|
|
34
|
+
|
|
35
|
+
// 2. Encrypt
|
|
36
|
+
const message = "The quick brown fox";
|
|
37
|
+
const ciphertext = sdk.encrypt(
|
|
38
|
+
keys.publicKey,
|
|
39
|
+
message,
|
|
40
|
+
EncryptionMode.STANDARD,
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
// 3. Decrypt
|
|
44
|
+
const plaintext = sdk.decrypt(keys.secretKey, ciphertext);
|
|
45
|
+
console.log("Decrypted:", plaintext.toString());
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## API
|
|
49
|
+
|
|
50
|
+
### `keygen()`
|
|
51
|
+
|
|
52
|
+
Returns `{ publicKey: Buffer, secretKey: Buffer }`.
|
|
53
|
+
|
|
54
|
+
### `encrypt(publicKey, message, mode)`
|
|
55
|
+
|
|
56
|
+
- `publicKey`: Buffer
|
|
57
|
+
- `message`: String or Buffer
|
|
58
|
+
- `mode`: `EncryptionMode.STANDARD` (0) or `EncryptionMode.METERED` (1)
|
|
59
|
+
- Returns: `Buffer` (Ciphertext)
|
|
60
|
+
|
|
61
|
+
### `decrypt(secretKey, ciphertext)`
|
|
62
|
+
|
|
63
|
+
- `secretKey`: Buffer
|
|
64
|
+
- `ciphertext`: Buffer
|
|
65
|
+
- Returns: `Buffer`
|
|
66
|
+
|
|
67
|
+
## License
|
|
68
|
+
|
|
69
|
+
MIT License.
|
package/index.js
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
const koffi = require('koffi');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
|
|
5
|
+
const EncryptionMode = {
|
|
6
|
+
STANDARD: 0,
|
|
7
|
+
METERED: 1
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
class Abhedya {
|
|
11
|
+
constructor(libPath) {
|
|
12
|
+
if (!libPath) {
|
|
13
|
+
libPath = this._findLibrary();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
this.lib = koffi.load(libPath);
|
|
18
|
+
} catch (e) {
|
|
19
|
+
throw new Error(`Failed to load library at ${libPath}: ${e.message}`);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Define Types
|
|
23
|
+
// Rust boolean is usually 1 byte, but C bool can be int.
|
|
24
|
+
// We use size_t for lengths.
|
|
25
|
+
|
|
26
|
+
// KeyGen: (pk_buf, pk_len, sk_buf, sk_len) -> int
|
|
27
|
+
this.fn_keygen = this.lib.func('abhedya_keygen', 'int', ['uint8*', '_Inout_ size_t*', 'uint8*', '_Inout_ size_t*']);
|
|
28
|
+
|
|
29
|
+
// Encrypt: (pk, pk_len, msg, msg_len, mode, out, out_len) -> int
|
|
30
|
+
this.fn_encrypt = this.lib.func('abhedya_encrypt', 'int', ['uint8*', 'size_t', 'uint8*', 'size_t', 'int', 'uint8*', '_Inout_ size_t*']);
|
|
31
|
+
|
|
32
|
+
// Decrypt: (sk, sk_len, ct, ct_len, out, out_len) -> int
|
|
33
|
+
this.fn_decrypt = this.lib.func('abhedya_decrypt', 'int', ['uint8*', 'size_t', 'uint8*', 'size_t', 'uint8*', '_Inout_ size_t*']);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
_findLibrary() {
|
|
37
|
+
const platform = os.platform();
|
|
38
|
+
let name = 'libabhedya_ffi';
|
|
39
|
+
let ext = '';
|
|
40
|
+
|
|
41
|
+
if (platform === 'darwin') {
|
|
42
|
+
ext = '.dylib';
|
|
43
|
+
} else if (platform === 'win32') {
|
|
44
|
+
name = 'abhedya_ffi';
|
|
45
|
+
ext = '.dll';
|
|
46
|
+
} else {
|
|
47
|
+
ext = '.so';
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const filename = name + ext;
|
|
51
|
+
|
|
52
|
+
// Search paths:
|
|
53
|
+
// 1. Env var ABHEDYA_LIB
|
|
54
|
+
// 2. Local debug/release targets (dev mode)
|
|
55
|
+
// 3. System paths (production)
|
|
56
|
+
|
|
57
|
+
if (process.env.ABHEDYA_LIB) return process.env.ABHEDYA_LIB;
|
|
58
|
+
|
|
59
|
+
const devRelease = path.join(__dirname, '../../target/release', filename);
|
|
60
|
+
if (require('fs').existsSync(devRelease)) return devRelease;
|
|
61
|
+
|
|
62
|
+
const devDebug = path.join(__dirname, '../../target/debug', filename);
|
|
63
|
+
if (require('fs').existsSync(devDebug)) return devDebug;
|
|
64
|
+
|
|
65
|
+
// Fallback to expecting it in current dir or globally loadable
|
|
66
|
+
return filename;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
keygen() {
|
|
70
|
+
// Prepare output buffers
|
|
71
|
+
// Kyber-768/Abhedya keys are approx: PK=1184, SK=2400.
|
|
72
|
+
// We allocate slightly more to be safe.
|
|
73
|
+
const PK_CAP = 2048;
|
|
74
|
+
const SK_CAP = 4096;
|
|
75
|
+
|
|
76
|
+
let pk = Buffer.alloc(PK_CAP);
|
|
77
|
+
let sk = Buffer.alloc(SK_CAP);
|
|
78
|
+
|
|
79
|
+
// We need pointers to lengths
|
|
80
|
+
let pk_len = [PK_CAP];
|
|
81
|
+
let sk_len = [SK_CAP];
|
|
82
|
+
|
|
83
|
+
const res = this.fn_keygen(pk, pk_len, sk, sk_len);
|
|
84
|
+
|
|
85
|
+
if (res !== 0) {
|
|
86
|
+
throw new Error(`KeyGen failed with code ${res}`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Slice to actual size
|
|
90
|
+
// koffi updates the array/object passed for Inout
|
|
91
|
+
return {
|
|
92
|
+
publicKey: pk.subarray(0, pk_len[0]),
|
|
93
|
+
secretKey: sk.subarray(0, sk_len[0])
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
encrypt(pk, msg, mode = EncryptionMode.STANDARD) {
|
|
98
|
+
if (!Buffer.isBuffer(pk)) pk = Buffer.from(pk);
|
|
99
|
+
if (!Buffer.isBuffer(msg)) msg = Buffer.from(msg);
|
|
100
|
+
|
|
101
|
+
// Estimate ciphertext size
|
|
102
|
+
// Standard ~1KB, Metered can be larger.
|
|
103
|
+
// 2x message size + overhead should be plenty for safety,
|
|
104
|
+
// or just a fixed large buffer (e.g. 16KB) if unsure.
|
|
105
|
+
// For Abhedya, CT is fixed size for a block, but let's assume worst case.
|
|
106
|
+
const CT_CAP = 1024 * 64; // 64KB safe upper bound
|
|
107
|
+
let ct = Buffer.alloc(CT_CAP);
|
|
108
|
+
let ct_len = [CT_CAP];
|
|
109
|
+
|
|
110
|
+
const res = this.fn_encrypt(
|
|
111
|
+
pk, pk.length,
|
|
112
|
+
msg, msg.length,
|
|
113
|
+
mode,
|
|
114
|
+
ct, ct_len
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
if (res !== 0) {
|
|
118
|
+
throw new Error(`Encrypt failed with code ${res}`);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return ct.subarray(0, ct_len[0]);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
decrypt(sk, ct) {
|
|
125
|
+
if (!Buffer.isBuffer(sk)) sk = Buffer.from(sk);
|
|
126
|
+
if (!Buffer.isBuffer(ct)) ct = Buffer.from(ct);
|
|
127
|
+
|
|
128
|
+
const OUT_CAP = 1024 * 64;
|
|
129
|
+
let out = Buffer.alloc(OUT_CAP);
|
|
130
|
+
let out_len = [OUT_CAP];
|
|
131
|
+
|
|
132
|
+
const res = this.fn_decrypt(
|
|
133
|
+
sk, sk.length,
|
|
134
|
+
ct, ct.length,
|
|
135
|
+
out, out_len
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
if (res !== 0) {
|
|
139
|
+
throw new Error(`Decrypt failed with code ${res}`);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return out.subarray(0, out_len[0]);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
module.exports = { Abhedya, EncryptionMode };
|
package/package.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@abhedyam/sdk",
|
|
3
|
+
"version": "1.0.5",
|
|
4
|
+
"description": "Node.js SDK for Abhedya: Sanskrit-Encoded Post-Quantum Cryptography",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"main": "index.js",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "node test.js"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"koffi": "^2.9.0"
|
|
14
|
+
},
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/ParamTatva-org/abhedyam.git"
|
|
18
|
+
},
|
|
19
|
+
"author": "Abhedyam Project",
|
|
20
|
+
"license": "MIT"
|
|
21
|
+
}
|
package/test.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const { Abhedya, EncryptionMode } = require('./index');
|
|
2
|
+
|
|
3
|
+
console.log("--- Testing Abhedya Node.js SDK ---");
|
|
4
|
+
|
|
5
|
+
try {
|
|
6
|
+
const crypto = new Abhedya();
|
|
7
|
+
|
|
8
|
+
// 1. KeyGen
|
|
9
|
+
console.log("Generating Keys...");
|
|
10
|
+
const keys = crypto.keygen();
|
|
11
|
+
console.log(`KeyGen Success. PK Size: ${keys.publicKey.length}, SK Size: ${keys.secretKey.length}`);
|
|
12
|
+
|
|
13
|
+
// 2. Encrypt
|
|
14
|
+
const msg = "Hello from Node.js!";
|
|
15
|
+
console.log(`Encrypting message: "${msg}"`);
|
|
16
|
+
const ct = crypto.encrypt(keys.publicKey, msg, EncryptionMode.STANDARD);
|
|
17
|
+
console.log(`Encryption Success. CT Size: ${ct.length}`);
|
|
18
|
+
|
|
19
|
+
// 3. Decrypt
|
|
20
|
+
console.log("Decrypting...");
|
|
21
|
+
const recovered = crypto.decrypt(keys.secretKey, ct);
|
|
22
|
+
const recoveredStr = recovered.toString('utf8').replace(/\0/g, ''); // Trim nulls
|
|
23
|
+
console.log(`Decrypted: "${recoveredStr}"`);
|
|
24
|
+
|
|
25
|
+
if (recovered.includes(Buffer.from(msg))) {
|
|
26
|
+
console.log("PASS: Decrypted message matches original.");
|
|
27
|
+
} else {
|
|
28
|
+
console.log("FAIL: Mismatch.");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
} catch (e) {
|
|
32
|
+
console.error("Test Failed:", e.message);
|
|
33
|
+
// Note: This will fail if ffi-napi is not installed.
|
|
34
|
+
// This is expected in this environment if npm install hasn't run.
|
|
35
|
+
}
|