@oesp/core 5.0.0 → 6.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/package.json +1 -1
- package/dist/index.cjs +0 -271
- package/dist/index.d.cts +0 -73
package/package.json
CHANGED
package/dist/index.cjs
DELETED
|
@@ -1,271 +0,0 @@
|
|
|
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
|
-
OESPClient: () => OESPClient,
|
|
24
|
-
b64urlDecode: () => b64urlDecode,
|
|
25
|
-
b64urlEncode: () => b64urlEncode,
|
|
26
|
-
base64Decode: () => base64Decode,
|
|
27
|
-
base64Encode: () => base64Encode,
|
|
28
|
-
canonicalJsonBytes: () => canonicalJsonBytes
|
|
29
|
-
});
|
|
30
|
-
module.exports = __toCommonJS(index_exports);
|
|
31
|
-
|
|
32
|
-
// src/utils.ts
|
|
33
|
-
function b64urlEncode(bytes) {
|
|
34
|
-
const base64 = base64Encode(bytes);
|
|
35
|
-
return base64.replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
|
|
36
|
-
}
|
|
37
|
-
function b64urlDecode(s) {
|
|
38
|
-
const pad = s.length % 4 === 0 ? "" : "=".repeat(4 - s.length % 4);
|
|
39
|
-
const base64 = s.replace(/-/g, "+").replace(/_/g, "/") + pad;
|
|
40
|
-
return base64Decode(base64);
|
|
41
|
-
}
|
|
42
|
-
function canonicalJsonBytes(obj) {
|
|
43
|
-
const json = canonicalStringify(obj);
|
|
44
|
-
return new TextEncoder().encode(json);
|
|
45
|
-
}
|
|
46
|
-
function canonicalStringify(obj) {
|
|
47
|
-
if (obj === null || typeof obj !== "object") {
|
|
48
|
-
return JSON.stringify(obj);
|
|
49
|
-
}
|
|
50
|
-
if (Array.isArray(obj)) {
|
|
51
|
-
const arr = obj.map((v) => JSON.parse(canonicalStringify(v)));
|
|
52
|
-
return JSON.stringify(arr);
|
|
53
|
-
}
|
|
54
|
-
const entries = Object.entries(obj).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
|
|
55
|
-
const out = {};
|
|
56
|
-
for (const [k, v] of entries) {
|
|
57
|
-
out[k] = JSON.parse(canonicalStringify(v));
|
|
58
|
-
}
|
|
59
|
-
return JSON.stringify(out);
|
|
60
|
-
}
|
|
61
|
-
function deriveDidWithSha(ed25519Pub, sha256) {
|
|
62
|
-
const sha = sha256(ed25519Pub);
|
|
63
|
-
const b32 = base32encode(sha).toLowerCase().replace(/=+$/g, "");
|
|
64
|
-
return `oesp:did:${b32}`;
|
|
65
|
-
}
|
|
66
|
-
function base32encode(bytes) {
|
|
67
|
-
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
|
68
|
-
let bits = 0;
|
|
69
|
-
let value = 0;
|
|
70
|
-
let output = "";
|
|
71
|
-
for (const byte of bytes) {
|
|
72
|
-
value = value << 8 | byte;
|
|
73
|
-
bits += 8;
|
|
74
|
-
while (bits >= 5) {
|
|
75
|
-
output += alphabet[value >>> bits - 5 & 31];
|
|
76
|
-
bits -= 5;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
if (bits > 0) {
|
|
80
|
-
output += alphabet[value << 5 - bits & 31];
|
|
81
|
-
}
|
|
82
|
-
while (output.length % 8 !== 0) output += "=";
|
|
83
|
-
return output;
|
|
84
|
-
}
|
|
85
|
-
var base64Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
86
|
-
function base64Encode(bytes) {
|
|
87
|
-
let output = "";
|
|
88
|
-
let i = 0;
|
|
89
|
-
while (i < bytes.length) {
|
|
90
|
-
const a = bytes[i++] ?? 0;
|
|
91
|
-
const b = bytes[i++] ?? 0;
|
|
92
|
-
const c = bytes[i++] ?? 0;
|
|
93
|
-
const triplet = a << 16 | b << 8 | c;
|
|
94
|
-
output += base64Alphabet[triplet >> 18 & 63];
|
|
95
|
-
output += base64Alphabet[triplet >> 12 & 63];
|
|
96
|
-
output += i - 2 < bytes.length ? base64Alphabet[triplet >> 6 & 63] : "=";
|
|
97
|
-
output += i - 1 < bytes.length ? base64Alphabet[triplet & 63] : "=";
|
|
98
|
-
}
|
|
99
|
-
return output;
|
|
100
|
-
}
|
|
101
|
-
function base64Decode(s) {
|
|
102
|
-
s = s.replace(/\s/g, "");
|
|
103
|
-
const len = s.length;
|
|
104
|
-
if (len % 4 !== 0) throw new Error("Invalid base64");
|
|
105
|
-
const output = [];
|
|
106
|
-
let i = 0;
|
|
107
|
-
while (i < len) {
|
|
108
|
-
const a = base64Alphabet.indexOf(s[i++]);
|
|
109
|
-
const b = base64Alphabet.indexOf(s[i++]);
|
|
110
|
-
const c = base64Alphabet.indexOf(s[i++]);
|
|
111
|
-
const d = base64Alphabet.indexOf(s[i++]);
|
|
112
|
-
const triplet = a << 18 | b << 12 | (c & 63) << 6 | d & 63;
|
|
113
|
-
output.push(triplet >> 16 & 255);
|
|
114
|
-
if (s[i - 2] !== "=") output.push(triplet >> 8 & 255);
|
|
115
|
-
if (s[i - 1] !== "=") output.push(triplet & 255);
|
|
116
|
-
}
|
|
117
|
-
return new Uint8Array(output);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// src/index.ts
|
|
121
|
-
var OESPClient = class {
|
|
122
|
-
constructor(deps) {
|
|
123
|
-
this.deps = deps;
|
|
124
|
-
}
|
|
125
|
-
async getDid() {
|
|
126
|
-
const id = await this.ensureIdentity();
|
|
127
|
-
return deriveDidWithSha(id.ed25519Pub, this.deps.crypto.sha256);
|
|
128
|
-
}
|
|
129
|
-
async pack(toDid, body, opts) {
|
|
130
|
-
const { crypto } = this.deps;
|
|
131
|
-
const ttlSec = opts?.ttlSec ?? 600;
|
|
132
|
-
const now = Math.floor(Date.now() / 1e3);
|
|
133
|
-
const exp = now + ttlSec;
|
|
134
|
-
const mid = b64urlEncode(crypto.randomBytes(12));
|
|
135
|
-
const id = await this.ensureIdentity();
|
|
136
|
-
const sid = deriveDidWithSha(id.ed25519Pub, crypto.sha256);
|
|
137
|
-
const edPubB64 = b64urlEncode(id.ed25519Pub);
|
|
138
|
-
const sessionKey = crypto.randomBytes(32);
|
|
139
|
-
const sealed = crypto.x25519Seal(id.x25519Pub, sessionKey);
|
|
140
|
-
const env = {
|
|
141
|
-
v: 1,
|
|
142
|
-
typ: "oesp.envelope",
|
|
143
|
-
mid,
|
|
144
|
-
sid,
|
|
145
|
-
ts: now,
|
|
146
|
-
exp,
|
|
147
|
-
from: { did: sid, pub: edPubB64 },
|
|
148
|
-
to: { did: toDid },
|
|
149
|
-
enc: "CHACHA20-POLY1305",
|
|
150
|
-
kex: "X25519",
|
|
151
|
-
ek: b64urlEncode(sealed),
|
|
152
|
-
iv: "",
|
|
153
|
-
ct: "",
|
|
154
|
-
sig_alg: "Ed25519",
|
|
155
|
-
sig: ""
|
|
156
|
-
};
|
|
157
|
-
const aad = canonicalJsonBytes(envFiltered(env, ["ct", "sig", "iv"]));
|
|
158
|
-
const bodyBytes = body instanceof Uint8Array ? body : canonicalJsonBytes(body);
|
|
159
|
-
const { iv, ct, tag } = crypto.aeadEncrypt(sessionKey, bodyBytes, aad);
|
|
160
|
-
env.iv = b64urlEncode(iv);
|
|
161
|
-
env.ct = b64urlEncode(ct);
|
|
162
|
-
if (tag) env["tag"] = b64urlEncode(tag);
|
|
163
|
-
const toSignBase = canonicalJsonBytes(envFiltered(env, ["sig"]));
|
|
164
|
-
const dataToSign = concatBytes(toSignBase, b64urlDecode(String(env.ct)));
|
|
165
|
-
const sig = crypto.ed25519Sign(id.ed25519Priv, dataToSign);
|
|
166
|
-
env.sig = b64urlEncode(sig);
|
|
167
|
-
const tokenPayload = canonicalJsonBytes(env);
|
|
168
|
-
return `OESP1.${b64urlEncode(tokenPayload)}`;
|
|
169
|
-
}
|
|
170
|
-
async verify(token, opts) {
|
|
171
|
-
const env = parseToken(token);
|
|
172
|
-
const now = opts?.now ?? Math.floor(Date.now() / 1e3);
|
|
173
|
-
const { crypto } = this.deps;
|
|
174
|
-
if (!opts?.allowExpired && env.exp < now) {
|
|
175
|
-
throw new Error("ExpiredError");
|
|
176
|
-
}
|
|
177
|
-
const pubBytes = b64urlDecode(env.from.pub);
|
|
178
|
-
const derived = deriveDidWithSha(pubBytes, crypto.sha256);
|
|
179
|
-
if (derived !== env.from.did) throw new Error("InvalidDIDError");
|
|
180
|
-
const envDict = envToDict(env);
|
|
181
|
-
const toSignBase = canonicalJsonBytes(excludeKeys(envDict, ["sig"]));
|
|
182
|
-
const ctBytes = b64urlDecode(env.ct);
|
|
183
|
-
const dataToSign = concatBytes(toSignBase, ctBytes);
|
|
184
|
-
const sigBytes = b64urlDecode(env.sig);
|
|
185
|
-
const ok = crypto.ed25519Verify(pubBytes, dataToSign, sigBytes);
|
|
186
|
-
if (!ok) throw new Error("InvalidSignatureError");
|
|
187
|
-
const id = await this.ensureIdentity();
|
|
188
|
-
const seen = await this.deps.replay.seen(env.sid, env.mid);
|
|
189
|
-
if (seen) throw new Error("ReplayError");
|
|
190
|
-
await this.deps.replay.markSeen(env.sid, env.mid);
|
|
191
|
-
return { envelope: envDict, verified: true, signerDid: env.from.did };
|
|
192
|
-
}
|
|
193
|
-
async unpack(token, opts) {
|
|
194
|
-
const env = parseToken(token);
|
|
195
|
-
await this.verify(token, opts);
|
|
196
|
-
const id = await this.ensureIdentity();
|
|
197
|
-
const ivBytes = b64urlDecode(env.iv);
|
|
198
|
-
const ctBytes = b64urlDecode(env.ct);
|
|
199
|
-
const ekBytes = b64urlDecode(env.ek);
|
|
200
|
-
const sessionKey = this.deps.crypto.x25519Open(id.x25519Priv, ekBytes);
|
|
201
|
-
const aad = canonicalJsonBytes(envFiltered(envToDict(env), ["ct", "sig", "iv"]));
|
|
202
|
-
const plaintext = this.deps.crypto.aeadDecrypt(sessionKey, ivBytes, ctBytes, aad, env.tag ? b64urlDecode(env.tag) : void 0);
|
|
203
|
-
return {
|
|
204
|
-
mid: env.mid,
|
|
205
|
-
sid: env.sid,
|
|
206
|
-
ts: env.ts,
|
|
207
|
-
exp: env.exp,
|
|
208
|
-
fromDid: env.from.did,
|
|
209
|
-
toDid: env.to.did,
|
|
210
|
-
plaintext
|
|
211
|
-
};
|
|
212
|
-
}
|
|
213
|
-
async ensureIdentity() {
|
|
214
|
-
if (!this.identity) this.identity = await this.deps.keystore.getOrCreateIdentity();
|
|
215
|
-
return this.identity;
|
|
216
|
-
}
|
|
217
|
-
};
|
|
218
|
-
function parseToken(token) {
|
|
219
|
-
if (!token.startsWith("OESP1.")) throw new Error("InvalidFormatError");
|
|
220
|
-
const payloadB64 = token.slice("OESP1.".length);
|
|
221
|
-
const jsonBytes = b64urlDecode(payloadB64);
|
|
222
|
-
const json = new TextDecoder().decode(jsonBytes);
|
|
223
|
-
const d = JSON.parse(json);
|
|
224
|
-
return d;
|
|
225
|
-
}
|
|
226
|
-
function envFiltered(env, excludeKeys2) {
|
|
227
|
-
const e = envToDict(env);
|
|
228
|
-
for (const k of excludeKeys2) delete e[k];
|
|
229
|
-
return e;
|
|
230
|
-
}
|
|
231
|
-
function envToDict(env) {
|
|
232
|
-
const d = {
|
|
233
|
-
v: env.v,
|
|
234
|
-
typ: env.typ,
|
|
235
|
-
mid: env.mid,
|
|
236
|
-
sid: env.sid,
|
|
237
|
-
ts: env.ts,
|
|
238
|
-
exp: env.exp,
|
|
239
|
-
from: env.from,
|
|
240
|
-
to: env.to,
|
|
241
|
-
enc: env.enc,
|
|
242
|
-
kex: env.kex,
|
|
243
|
-
ek: env.ek,
|
|
244
|
-
iv: env.iv,
|
|
245
|
-
ct: env.ct,
|
|
246
|
-
sig_alg: env.sig_alg,
|
|
247
|
-
sig: env.sig
|
|
248
|
-
};
|
|
249
|
-
if (env.tag) d["tag"] = env.tag;
|
|
250
|
-
return d;
|
|
251
|
-
}
|
|
252
|
-
function excludeKeys(obj, keys) {
|
|
253
|
-
const out = { ...obj };
|
|
254
|
-
for (const k of keys) delete out[k];
|
|
255
|
-
return out;
|
|
256
|
-
}
|
|
257
|
-
function concatBytes(a, b) {
|
|
258
|
-
const out = new Uint8Array(a.length + b.length);
|
|
259
|
-
out.set(a, 0);
|
|
260
|
-
out.set(b, a.length);
|
|
261
|
-
return out;
|
|
262
|
-
}
|
|
263
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
264
|
-
0 && (module.exports = {
|
|
265
|
-
OESPClient,
|
|
266
|
-
b64urlDecode,
|
|
267
|
-
b64urlEncode,
|
|
268
|
-
base64Decode,
|
|
269
|
-
base64Encode,
|
|
270
|
-
canonicalJsonBytes
|
|
271
|
-
});
|
package/dist/index.d.cts
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
interface CryptoProvider {
|
|
2
|
-
sha256(bytes: Uint8Array): Uint8Array;
|
|
3
|
-
ed25519Sign(priv: Uint8Array, data: Uint8Array): Uint8Array;
|
|
4
|
-
ed25519Verify(pub: Uint8Array, data: Uint8Array, sig: Uint8Array): boolean;
|
|
5
|
-
x25519Seal(recipientPub: Uint8Array, sessionKey: Uint8Array): Uint8Array;
|
|
6
|
-
x25519Open(recipientPriv: Uint8Array, sealed: Uint8Array): Uint8Array;
|
|
7
|
-
aeadEncrypt(key: Uint8Array, plaintext: Uint8Array, aad: Uint8Array): {
|
|
8
|
-
iv: Uint8Array;
|
|
9
|
-
ct: Uint8Array;
|
|
10
|
-
tag?: Uint8Array;
|
|
11
|
-
};
|
|
12
|
-
aeadDecrypt(key: Uint8Array, iv: Uint8Array, ct: Uint8Array, aad: Uint8Array, tag?: Uint8Array): Uint8Array;
|
|
13
|
-
randomBytes(n: number): Uint8Array;
|
|
14
|
-
}
|
|
15
|
-
interface Identity {
|
|
16
|
-
ed25519Priv: Uint8Array;
|
|
17
|
-
ed25519Pub: Uint8Array;
|
|
18
|
-
x25519Priv: Uint8Array;
|
|
19
|
-
x25519Pub: Uint8Array;
|
|
20
|
-
}
|
|
21
|
-
interface KeyStore {
|
|
22
|
-
getOrCreateIdentity(): Promise<Identity>;
|
|
23
|
-
}
|
|
24
|
-
interface ReplayStore {
|
|
25
|
-
seen(fromDid: string, mid: string): Promise<boolean>;
|
|
26
|
-
markSeen(fromDid: string, mid: string): Promise<void>;
|
|
27
|
-
}
|
|
28
|
-
interface DecodedMessage {
|
|
29
|
-
mid: string;
|
|
30
|
-
sid: string;
|
|
31
|
-
ts: number;
|
|
32
|
-
exp: number;
|
|
33
|
-
fromDid: string;
|
|
34
|
-
toDid: string;
|
|
35
|
-
plaintext: Uint8Array;
|
|
36
|
-
}
|
|
37
|
-
interface VerifiedEnvelope {
|
|
38
|
-
envelope: Record<string, unknown>;
|
|
39
|
-
verified: boolean;
|
|
40
|
-
signerDid: string;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
declare function b64urlEncode(bytes: Uint8Array): string;
|
|
44
|
-
declare function b64urlDecode(s: string): Uint8Array;
|
|
45
|
-
declare function canonicalJsonBytes(obj: unknown): Uint8Array;
|
|
46
|
-
declare function base64Encode(bytes: Uint8Array): string;
|
|
47
|
-
declare function base64Decode(s: string): Uint8Array;
|
|
48
|
-
|
|
49
|
-
interface OESPClientDeps {
|
|
50
|
-
crypto: CryptoProvider;
|
|
51
|
-
keystore: KeyStore;
|
|
52
|
-
replay: ReplayStore;
|
|
53
|
-
}
|
|
54
|
-
declare class OESPClient {
|
|
55
|
-
private deps;
|
|
56
|
-
private identity?;
|
|
57
|
-
constructor(deps: OESPClientDeps);
|
|
58
|
-
getDid(): Promise<string>;
|
|
59
|
-
pack(toDid: string, body: object | Uint8Array, opts?: {
|
|
60
|
-
ttlSec?: number;
|
|
61
|
-
}): Promise<string>;
|
|
62
|
-
verify(token: string, opts?: {
|
|
63
|
-
now?: number;
|
|
64
|
-
allowExpired?: boolean;
|
|
65
|
-
}): Promise<VerifiedEnvelope>;
|
|
66
|
-
unpack(token: string, opts?: {
|
|
67
|
-
now?: number;
|
|
68
|
-
allowExpired?: boolean;
|
|
69
|
-
}): Promise<DecodedMessage>;
|
|
70
|
-
private ensureIdentity;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export { type CryptoProvider, type DecodedMessage, type Identity, type KeyStore, OESPClient, type OESPClientDeps, type ReplayStore, type VerifiedEnvelope, b64urlDecode, b64urlEncode, base64Decode, base64Encode, canonicalJsonBytes };
|