ephem 0.1.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/LICENSE +15 -0
- package/README.md +481 -0
- package/dist/client/index.d.mts +3 -0
- package/dist/client/index.d.ts +3 -0
- package/dist/client/index.js +88 -0
- package/dist/client/index.mjs +63 -0
- package/dist/index.d.mts +87 -0
- package/dist/index.d.ts +89 -0
- package/dist/index.js +308 -0
- package/dist/index.mjs +291 -0
- package/package.json +50 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
type Key = string;
|
|
2
|
+
interface Capsule {
|
|
3
|
+
privateKey: Key;
|
|
4
|
+
maxOpens: number;
|
|
5
|
+
openCount: number;
|
|
6
|
+
expiresAt: number;
|
|
7
|
+
}
|
|
8
|
+
type OnCreationFunction = (cid: string, privateKey: string, openCount: number, maxOpens: number, expiresAt: number) => Promise<boolean>;
|
|
9
|
+
type OnOpenFunction = (cid: string, openCount: number) => Promise<boolean>;
|
|
10
|
+
type OnGetByCIDFunction = (cid: string) => Promise<{
|
|
11
|
+
privateKey: unknown;
|
|
12
|
+
maxOpens: unknown;
|
|
13
|
+
openCount: unknown;
|
|
14
|
+
expiresAt: unknown;
|
|
15
|
+
} | null>;
|
|
16
|
+
type CapsuleDeletionReason = 'expired' | 'max_opened';
|
|
17
|
+
type OnDeletionFuntion = (cid: string, deleteReason: CapsuleDeletionReason) => Promise<void>;
|
|
18
|
+
interface EphemConfigInMemory {
|
|
19
|
+
inMemory?: true;
|
|
20
|
+
maxConcurrentCapsules?: number;
|
|
21
|
+
defaultCaptsuleLifetimeMS?: number;
|
|
22
|
+
capsuleCleanupIntervalMS?: number;
|
|
23
|
+
onCapsuleCreation?: OnCreationFunction;
|
|
24
|
+
onCapsuleOpen?: OnOpenFunction;
|
|
25
|
+
OnGetCapsuleByCID?: OnGetByCIDFunction;
|
|
26
|
+
onCapsuleDelete?: OnDeletionFuntion;
|
|
27
|
+
logging?: boolean;
|
|
28
|
+
}
|
|
29
|
+
interface EphemConfigPersistent {
|
|
30
|
+
inMemory: false;
|
|
31
|
+
maxConcurrentCapsules?: number;
|
|
32
|
+
defaultCaptsuleLifetimeMS?: number;
|
|
33
|
+
capsuleCleanupIntervalMS?: number;
|
|
34
|
+
onCapsuleCreation: OnCreationFunction;
|
|
35
|
+
onCapsuleOpen: OnOpenFunction;
|
|
36
|
+
OnGetCapsuleByCID: OnGetByCIDFunction;
|
|
37
|
+
onCapsuleDelete: OnDeletionFuntion;
|
|
38
|
+
logging?: boolean;
|
|
39
|
+
}
|
|
40
|
+
type EphemConfig = EphemConfigInMemory | EphemConfigPersistent;
|
|
41
|
+
interface CreateCapsuleReturnObj {
|
|
42
|
+
cid: string;
|
|
43
|
+
publicKey: string;
|
|
44
|
+
}
|
|
45
|
+
interface CreateCapsuleConfig {
|
|
46
|
+
maxOpens?: number;
|
|
47
|
+
lifetimeDurationMS?: number;
|
|
48
|
+
}
|
|
49
|
+
type CreateCapsuleCallback = (res: CreateCapsuleReturnObj | null) => void | Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Keeps track of ephem's analytics since initialization.
|
|
52
|
+
*/
|
|
53
|
+
declare class Analytics {
|
|
54
|
+
totalCreatedCapsules: number;
|
|
55
|
+
totalUsedCapsules: number;
|
|
56
|
+
totalExpiredCapsules: number;
|
|
57
|
+
constructor();
|
|
58
|
+
}
|
|
59
|
+
declare class Ephem {
|
|
60
|
+
private readonly config;
|
|
61
|
+
private genUUID;
|
|
62
|
+
private slug;
|
|
63
|
+
private capsules;
|
|
64
|
+
private analytics;
|
|
65
|
+
private log;
|
|
66
|
+
constructor(config?: EphemConfig);
|
|
67
|
+
/**
|
|
68
|
+
* Alias Handshake
|
|
69
|
+
*/
|
|
70
|
+
createCapsule(callback: CreateCapsuleCallback, config?: CreateCapsuleConfig): Promise<void>;
|
|
71
|
+
createCapsulePromise(config?: CreateCapsuleConfig): Promise<CreateCapsuleReturnObj | null>;
|
|
72
|
+
/**
|
|
73
|
+
* Handles periodic cleanup of in-memory capsules.
|
|
74
|
+
* Works only if capsules are in memory.
|
|
75
|
+
* Else, user should implement their own cleanup elsewhere
|
|
76
|
+
*/
|
|
77
|
+
private capsuleCleanUp;
|
|
78
|
+
getCapsule(cid: string): Promise<Capsule | null>;
|
|
79
|
+
removeIndividualCapsule(cid: string, reason: CapsuleDeletionReason): void;
|
|
80
|
+
/**
|
|
81
|
+
* Decrypts encrypted data.
|
|
82
|
+
*/
|
|
83
|
+
open(cipher: string): Promise<string | null>;
|
|
84
|
+
getAnalytics(): Readonly<Analytics>;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export { type Capsule, type CapsuleDeletionReason, type CreateCapsuleCallback, type CreateCapsuleConfig, type CreateCapsuleReturnObj, type EphemConfig, type EphemConfigInMemory, type EphemConfigPersistent, type Key, type OnCreationFunction, type OnDeletionFuntion, type OnGetByCIDFunction, type OnOpenFunction, Ephem as default };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
type Key = string;
|
|
2
|
+
interface Capsule {
|
|
3
|
+
privateKey: Key;
|
|
4
|
+
maxOpens: number;
|
|
5
|
+
openCount: number;
|
|
6
|
+
expiresAt: number;
|
|
7
|
+
}
|
|
8
|
+
type OnCreationFunction = (cid: string, privateKey: string, openCount: number, maxOpens: number, expiresAt: number) => Promise<boolean>;
|
|
9
|
+
type OnOpenFunction = (cid: string, openCount: number) => Promise<boolean>;
|
|
10
|
+
type OnGetByCIDFunction = (cid: string) => Promise<{
|
|
11
|
+
privateKey: unknown;
|
|
12
|
+
maxOpens: unknown;
|
|
13
|
+
openCount: unknown;
|
|
14
|
+
expiresAt: unknown;
|
|
15
|
+
} | null>;
|
|
16
|
+
type CapsuleDeletionReason = 'expired' | 'max_opened';
|
|
17
|
+
type OnDeletionFuntion = (cid: string, deleteReason: CapsuleDeletionReason) => Promise<void>;
|
|
18
|
+
interface EphemConfigInMemory {
|
|
19
|
+
inMemory?: true;
|
|
20
|
+
maxConcurrentCapsules?: number;
|
|
21
|
+
defaultCaptsuleLifetimeMS?: number;
|
|
22
|
+
capsuleCleanupIntervalMS?: number;
|
|
23
|
+
onCapsuleCreation?: OnCreationFunction;
|
|
24
|
+
onCapsuleOpen?: OnOpenFunction;
|
|
25
|
+
OnGetCapsuleByCID?: OnGetByCIDFunction;
|
|
26
|
+
onCapsuleDelete?: OnDeletionFuntion;
|
|
27
|
+
logging?: boolean;
|
|
28
|
+
}
|
|
29
|
+
interface EphemConfigPersistent {
|
|
30
|
+
inMemory: false;
|
|
31
|
+
maxConcurrentCapsules?: number;
|
|
32
|
+
defaultCaptsuleLifetimeMS?: number;
|
|
33
|
+
capsuleCleanupIntervalMS?: number;
|
|
34
|
+
onCapsuleCreation: OnCreationFunction;
|
|
35
|
+
onCapsuleOpen: OnOpenFunction;
|
|
36
|
+
OnGetCapsuleByCID: OnGetByCIDFunction;
|
|
37
|
+
onCapsuleDelete: OnDeletionFuntion;
|
|
38
|
+
logging?: boolean;
|
|
39
|
+
}
|
|
40
|
+
type EphemConfig = EphemConfigInMemory | EphemConfigPersistent;
|
|
41
|
+
interface CreateCapsuleReturnObj {
|
|
42
|
+
cid: string;
|
|
43
|
+
publicKey: string;
|
|
44
|
+
}
|
|
45
|
+
interface CreateCapsuleConfig {
|
|
46
|
+
maxOpens?: number;
|
|
47
|
+
lifetimeDurationMS?: number;
|
|
48
|
+
}
|
|
49
|
+
type CreateCapsuleCallback = (res: CreateCapsuleReturnObj | null) => void | Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Keeps track of ephem's analytics since initialization.
|
|
52
|
+
*/
|
|
53
|
+
declare class Analytics {
|
|
54
|
+
totalCreatedCapsules: number;
|
|
55
|
+
totalUsedCapsules: number;
|
|
56
|
+
totalExpiredCapsules: number;
|
|
57
|
+
constructor();
|
|
58
|
+
}
|
|
59
|
+
declare class Ephem {
|
|
60
|
+
private readonly config;
|
|
61
|
+
private genUUID;
|
|
62
|
+
private slug;
|
|
63
|
+
private capsules;
|
|
64
|
+
private analytics;
|
|
65
|
+
private log;
|
|
66
|
+
constructor(config?: EphemConfig);
|
|
67
|
+
/**
|
|
68
|
+
* Alias Handshake
|
|
69
|
+
*/
|
|
70
|
+
createCapsule(callback: CreateCapsuleCallback, config?: CreateCapsuleConfig): Promise<void>;
|
|
71
|
+
createCapsulePromise(config?: CreateCapsuleConfig): Promise<CreateCapsuleReturnObj | null>;
|
|
72
|
+
/**
|
|
73
|
+
* Handles periodic cleanup of in-memory capsules.
|
|
74
|
+
* Works only if capsules are in memory.
|
|
75
|
+
* Else, user should implement their own cleanup elsewhere
|
|
76
|
+
*/
|
|
77
|
+
private capsuleCleanUp;
|
|
78
|
+
getCapsule(cid: string): Promise<Capsule | null>;
|
|
79
|
+
removeIndividualCapsule(cid: string, reason: CapsuleDeletionReason): void;
|
|
80
|
+
/**
|
|
81
|
+
* Decrypts encrypted data.
|
|
82
|
+
*/
|
|
83
|
+
open(cipher: string): Promise<string | null>;
|
|
84
|
+
getAnalytics(): Readonly<Analytics>;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// @ts-ignore
|
|
88
|
+
export = Ephem;
|
|
89
|
+
export type { Capsule, CapsuleDeletionReason, CreateCapsuleCallback, CreateCapsuleConfig, CreateCapsuleReturnObj, EphemConfig, EphemConfigInMemory, EphemConfigPersistent, Key, OnCreationFunction, OnDeletionFuntion, OnGetByCIDFunction, OnOpenFunction };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
default: () => Ephem
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(index_exports);
|
|
26
|
+
var import_node_crypto = require("crypto");
|
|
27
|
+
var import_node_crypto2 = require("crypto");
|
|
28
|
+
var Analytics = class {
|
|
29
|
+
totalCreatedCapsules;
|
|
30
|
+
totalUsedCapsules;
|
|
31
|
+
totalExpiredCapsules;
|
|
32
|
+
constructor() {
|
|
33
|
+
this.totalCreatedCapsules = 0;
|
|
34
|
+
this.totalExpiredCapsules = 0;
|
|
35
|
+
this.totalUsedCapsules = 0;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
var DEFAULT_IN_MEMORY_CONFIG = {
|
|
39
|
+
inMemory: true,
|
|
40
|
+
maxConcurrentCapsules: Infinity,
|
|
41
|
+
defaultCaptsuleLifetimeMS: Infinity,
|
|
42
|
+
capsuleCleanupIntervalMS: 6e4
|
|
43
|
+
};
|
|
44
|
+
function normalizeConfig(config) {
|
|
45
|
+
if (config?.inMemory === false) {
|
|
46
|
+
return {
|
|
47
|
+
...DEFAULT_IN_MEMORY_CONFIG,
|
|
48
|
+
...config,
|
|
49
|
+
inMemory: false
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
...DEFAULT_IN_MEMORY_CONFIG,
|
|
54
|
+
...config,
|
|
55
|
+
inMemory: true
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function generateCapsuleKeyPair() {
|
|
59
|
+
return new Promise((resolve, reject) => {
|
|
60
|
+
(0, import_node_crypto.generateKeyPair)(
|
|
61
|
+
"rsa",
|
|
62
|
+
{
|
|
63
|
+
modulusLength: 2048,
|
|
64
|
+
publicKeyEncoding: {
|
|
65
|
+
type: "spki",
|
|
66
|
+
format: "pem"
|
|
67
|
+
},
|
|
68
|
+
privateKeyEncoding: {
|
|
69
|
+
type: "pkcs8",
|
|
70
|
+
format: "pem"
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
(err, publicKey, privateKey) => {
|
|
74
|
+
if (err) return reject(err);
|
|
75
|
+
resolve({ publicKey, privateKey });
|
|
76
|
+
}
|
|
77
|
+
);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
function base64ToBuffer(b64) {
|
|
81
|
+
return Buffer.from(b64, "base64");
|
|
82
|
+
}
|
|
83
|
+
var Ephem = class {
|
|
84
|
+
config;
|
|
85
|
+
genUUID = () => (0, import_node_crypto.randomUUID)();
|
|
86
|
+
slug = `Ephem`;
|
|
87
|
+
capsules;
|
|
88
|
+
analytics;
|
|
89
|
+
log(...message) {
|
|
90
|
+
if (this.config.logging) {
|
|
91
|
+
console.log(`${this.slug} =>`, ...message);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
constructor(config) {
|
|
95
|
+
this.analytics = new Analytics();
|
|
96
|
+
this.config = normalizeConfig(config);
|
|
97
|
+
this.capsules = /* @__PURE__ */ new Map();
|
|
98
|
+
this.log(`initialized with ${this.config.inMemory ? `in-memory` : `persistent`} mode.`);
|
|
99
|
+
if (this.config.inMemory) {
|
|
100
|
+
setTimeout(() => {
|
|
101
|
+
this.capsuleCleanUp();
|
|
102
|
+
}, this.config.capsuleCleanupIntervalMS || 6e4);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Alias Handshake
|
|
107
|
+
*/
|
|
108
|
+
async createCapsule(callback, config = {}) {
|
|
109
|
+
try {
|
|
110
|
+
if (this.config.inMemory && this.capsules.size >= (this.config.maxConcurrentCapsules || Infinity)) {
|
|
111
|
+
callback(null);
|
|
112
|
+
} else {
|
|
113
|
+
const { privateKey, publicKey } = await generateCapsuleKeyPair();
|
|
114
|
+
const cid = this.genUUID();
|
|
115
|
+
const lifetime = config.lifetimeDurationMS === void 0 ? this.config.defaultCaptsuleLifetimeMS ? Math.max(1, this.config.defaultCaptsuleLifetimeMS) : Infinity : Math.max(1, config.lifetimeDurationMS);
|
|
116
|
+
const expiresAt = lifetime === Infinity ? Infinity : Date.now() + lifetime;
|
|
117
|
+
const maxOpens = Math.max(1, config.maxOpens || 0);
|
|
118
|
+
const cbData = {
|
|
119
|
+
publicKey,
|
|
120
|
+
cid
|
|
121
|
+
};
|
|
122
|
+
if (this.config.inMemory) {
|
|
123
|
+
const capsule = {
|
|
124
|
+
expiresAt: expiresAt === Infinity ? 0 : expiresAt,
|
|
125
|
+
maxOpens: maxOpens === Infinity ? 0 : maxOpens,
|
|
126
|
+
openCount: 0,
|
|
127
|
+
privateKey
|
|
128
|
+
};
|
|
129
|
+
this.capsules.set(cid, capsule);
|
|
130
|
+
this.analytics.totalCreatedCapsules++;
|
|
131
|
+
this.log(`new capsul with id ${cid}`);
|
|
132
|
+
callback(cbData);
|
|
133
|
+
} else if (this.config.onCapsuleCreation) {
|
|
134
|
+
const res = await this.config.onCapsuleCreation(cid, privateKey, 0, maxOpens === Infinity ? 0 : maxOpens, expiresAt === Infinity ? 0 : expiresAt);
|
|
135
|
+
if (res) {
|
|
136
|
+
this.analytics.totalCreatedCapsules++;
|
|
137
|
+
this.log(`new capsul with id ${cid}`);
|
|
138
|
+
callback(cbData);
|
|
139
|
+
} else {
|
|
140
|
+
this.log(`Error encountered during capsule creation:`, `onCapsuleCreation returned false.`);
|
|
141
|
+
callback(null);
|
|
142
|
+
}
|
|
143
|
+
} else {
|
|
144
|
+
this.log(`Error encountered during capsule creation:`, `onCapsuleCreation not defined in persistent mode.`);
|
|
145
|
+
callback(null);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
} catch (error) {
|
|
149
|
+
this.log(`Error encountered during capsule creation:`, error);
|
|
150
|
+
callback(null);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
createCapsulePromise(config = {}) {
|
|
154
|
+
return new Promise(async (resolve, reject) => {
|
|
155
|
+
this.createCapsule((r) => {
|
|
156
|
+
if (r === null) {
|
|
157
|
+
reject(r);
|
|
158
|
+
} else {
|
|
159
|
+
resolve(r);
|
|
160
|
+
}
|
|
161
|
+
}, config);
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Handles periodic cleanup of in-memory capsules.
|
|
166
|
+
* Works only if capsules are in memory.
|
|
167
|
+
* Else, user should implement their own cleanup elsewhere
|
|
168
|
+
*/
|
|
169
|
+
capsuleCleanUp() {
|
|
170
|
+
this.log(`cleanup initialized.`);
|
|
171
|
+
const start = Date.now();
|
|
172
|
+
let n = 0;
|
|
173
|
+
for (const [cid, capsule] of this.capsules) {
|
|
174
|
+
if (capsule.expiresAt <= Date.now()) {
|
|
175
|
+
this.analytics.totalExpiredCapsules++;
|
|
176
|
+
this.capsules.delete(cid);
|
|
177
|
+
n++;
|
|
178
|
+
} else if (capsule.openCount >= capsule.maxOpens && capsule.maxOpens != 0) {
|
|
179
|
+
this.analytics.totalUsedCapsules++;
|
|
180
|
+
this.capsules.delete(cid);
|
|
181
|
+
n++;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (n) {
|
|
185
|
+
this.log(`${n} capsule${n == 1 ? "" : "s"} deleted due to expiry or max usage.`);
|
|
186
|
+
} else {
|
|
187
|
+
this.log(`no capsule deleted.`);
|
|
188
|
+
}
|
|
189
|
+
const duration = this.config.capsuleCleanupIntervalMS || 6e4;
|
|
190
|
+
const elapsed = Date.now() - start;
|
|
191
|
+
if (elapsed >= duration) {
|
|
192
|
+
this.capsuleCleanUp();
|
|
193
|
+
} else {
|
|
194
|
+
setTimeout(() => this.capsuleCleanUp(), Math.max(0, duration - elapsed));
|
|
195
|
+
}
|
|
196
|
+
this.log(`cleanup ended.`);
|
|
197
|
+
}
|
|
198
|
+
async getCapsule(cid) {
|
|
199
|
+
if (this.config.inMemory) {
|
|
200
|
+
if (this.capsules.has(cid)) {
|
|
201
|
+
return this.capsules.get(cid);
|
|
202
|
+
} else {
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
} else {
|
|
206
|
+
if (this.config.OnGetCapsuleByCID) {
|
|
207
|
+
const r = await this.config.OnGetCapsuleByCID(cid);
|
|
208
|
+
if (r) {
|
|
209
|
+
const capsule = {
|
|
210
|
+
expiresAt: Math.max(Number(r.expiresAt) || 0, 0),
|
|
211
|
+
maxOpens: Math.max(Number(r.maxOpens) || 0, 1),
|
|
212
|
+
openCount: Math.max(Number(r.openCount) || 0, 0),
|
|
213
|
+
privateKey: String(r.privateKey) || ""
|
|
214
|
+
};
|
|
215
|
+
return capsule;
|
|
216
|
+
} else {
|
|
217
|
+
this.log(`Get capsule error: OnGetCapsuleByCID did not return a capsule.`);
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
} else {
|
|
221
|
+
this.log(`Get capsule error: OnGetCapsuleByCID not supplied.`);
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
removeIndividualCapsule(cid, reason) {
|
|
227
|
+
if (this.config.inMemory) {
|
|
228
|
+
if (this.capsules.has(cid)) {
|
|
229
|
+
this.capsules.delete(cid);
|
|
230
|
+
this.log(`${cid} removed due to '${reason}'.`);
|
|
231
|
+
}
|
|
232
|
+
} else {
|
|
233
|
+
if (this.config.onCapsuleDelete) {
|
|
234
|
+
this.config.onCapsuleDelete(cid, reason);
|
|
235
|
+
} else {
|
|
236
|
+
this.log(`warn: onCapsuleDelete was just required, but not implemented.`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Decrypts encrypted data.
|
|
242
|
+
*/
|
|
243
|
+
async open(cipher) {
|
|
244
|
+
if (cipher.startsWith("##INSECURE##")) {
|
|
245
|
+
const plainText = cipher.slice("##INSECURE##".length);
|
|
246
|
+
this.log(`WARN: Insecure dev-mode capsule opened.`);
|
|
247
|
+
return plainText;
|
|
248
|
+
}
|
|
249
|
+
const parts = cipher.split(".");
|
|
250
|
+
if (parts.length !== 4) return null;
|
|
251
|
+
let [cid, ivB64, cipherTextB64, encKeyB64] = parts;
|
|
252
|
+
cid = cid || "InvalidPlaceHolder";
|
|
253
|
+
const capsule = await this.getCapsule(cid);
|
|
254
|
+
if (!capsule) return null;
|
|
255
|
+
const { expiresAt, maxOpens, openCount, privateKey } = capsule;
|
|
256
|
+
if (expiresAt !== 0 && Date.now() >= expiresAt) {
|
|
257
|
+
this.removeIndividualCapsule(cid, "expired");
|
|
258
|
+
this.analytics.totalExpiredCapsules++;
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
if (maxOpens !== 0 && openCount >= maxOpens) {
|
|
262
|
+
this.removeIndividualCapsule(cid, "max_opened");
|
|
263
|
+
this.analytics.totalUsedCapsules++;
|
|
264
|
+
return null;
|
|
265
|
+
}
|
|
266
|
+
try {
|
|
267
|
+
const aesKey = (0, import_node_crypto2.privateDecrypt)(
|
|
268
|
+
{
|
|
269
|
+
key: privateKey,
|
|
270
|
+
padding: import_node_crypto2.constants.RSA_PKCS1_OAEP_PADDING,
|
|
271
|
+
oaepHash: "sha256"
|
|
272
|
+
},
|
|
273
|
+
base64ToBuffer(encKeyB64 || "")
|
|
274
|
+
);
|
|
275
|
+
const iv = base64ToBuffer(ivB64 || "");
|
|
276
|
+
const cipherBuf = base64ToBuffer(cipherTextB64 || "");
|
|
277
|
+
const authTag = cipherBuf.subarray(cipherBuf.length - 16);
|
|
278
|
+
const encrypted = cipherBuf.subarray(0, cipherBuf.length - 16);
|
|
279
|
+
const decipher = (0, import_node_crypto2.createDecipheriv)("aes-256-gcm", aesKey, iv);
|
|
280
|
+
decipher.setAuthTag(authTag);
|
|
281
|
+
decipher.setAAD(Buffer.from(cid));
|
|
282
|
+
const decrypted = decipher.update(encrypted) + decipher.final("utf8");
|
|
283
|
+
capsule.openCount++;
|
|
284
|
+
if (this.config.inMemory) {
|
|
285
|
+
this.capsules.set(cid, capsule);
|
|
286
|
+
} else if (this.config.onCapsuleOpen) {
|
|
287
|
+
await this.config.onCapsuleOpen(cid, capsule.openCount);
|
|
288
|
+
}
|
|
289
|
+
if (capsule.expiresAt !== 0 && Date.now() >= capsule.expiresAt) {
|
|
290
|
+
this.removeIndividualCapsule(cid, "expired");
|
|
291
|
+
this.analytics.totalExpiredCapsules++;
|
|
292
|
+
}
|
|
293
|
+
if (capsule.maxOpens !== 0 && capsule.openCount >= capsule.maxOpens) {
|
|
294
|
+
this.removeIndividualCapsule(cid, "max_opened");
|
|
295
|
+
this.analytics.totalUsedCapsules++;
|
|
296
|
+
}
|
|
297
|
+
return decrypted;
|
|
298
|
+
} catch (err) {
|
|
299
|
+
this.log("open() failed:", err);
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
getAnalytics() {
|
|
304
|
+
const snap = { ...this.analytics };
|
|
305
|
+
return snap;
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
if (module.exports.default) { module.exports = module.exports.default; }
|