@vritti/api-sdk 0.3.1 → 0.3.3
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/dist/document-BoS0NIbf.d.cts +12 -0
- package/dist/document-BoS0NIbf.d.ts +12 -0
- package/dist/license.cjs +9 -56
- package/dist/license.cjs.map +1 -1
- package/dist/license.d.cts +2 -13
- package/dist/license.d.ts +2 -13
- package/dist/license.js +8 -51
- package/dist/license.js.map +1 -1
- package/dist/signing.cjs +159 -0
- package/dist/signing.cjs.map +1 -0
- package/dist/signing.d.cts +35 -0
- package/dist/signing.d.ts +35 -0
- package/dist/signing.js +128 -0
- package/dist/signing.js.map +1 -0
- package/package.json +11 -1
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
interface SignedDocument<T> {
|
|
2
|
+
payload: T;
|
|
3
|
+
signature: string;
|
|
4
|
+
}
|
|
5
|
+
declare function generateSigningKeyPair(): {
|
|
6
|
+
privateKey: string;
|
|
7
|
+
publicKey: string;
|
|
8
|
+
};
|
|
9
|
+
declare function signDocument<T>(payload: T, privateKeyBase64: string): SignedDocument<T>;
|
|
10
|
+
declare function verifyDocument<T>(doc: SignedDocument<T>, publicKeyBase64: string): boolean;
|
|
11
|
+
|
|
12
|
+
export { type SignedDocument as S, generateSigningKeyPair as g, signDocument as s, verifyDocument as v };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
interface SignedDocument<T> {
|
|
2
|
+
payload: T;
|
|
3
|
+
signature: string;
|
|
4
|
+
}
|
|
5
|
+
declare function generateSigningKeyPair(): {
|
|
6
|
+
privateKey: string;
|
|
7
|
+
publicKey: string;
|
|
8
|
+
};
|
|
9
|
+
declare function signDocument<T>(payload: T, privateKeyBase64: string): SignedDocument<T>;
|
|
10
|
+
declare function verifyDocument<T>(doc: SignedDocument<T>, publicKeyBase64: string): boolean;
|
|
11
|
+
|
|
12
|
+
export { type SignedDocument as S, generateSigningKeyPair as g, signDocument as s, verifyDocument as v };
|
package/dist/license.cjs
CHANGED
|
@@ -21,24 +21,18 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
// src/license/index.ts
|
|
22
22
|
var license_exports = {};
|
|
23
23
|
__export(license_exports, {
|
|
24
|
-
|
|
25
|
-
generateLicenseKeyPair: () => generateLicenseKeyPair,
|
|
26
|
-
hashSnapshot: () => hashSnapshot,
|
|
27
|
-
signDocument: () => signDocument,
|
|
28
|
-
verifyDocument: () => verifyDocument
|
|
24
|
+
hashSnapshot: () => hashSnapshot
|
|
29
25
|
});
|
|
30
26
|
module.exports = __toCommonJS(license_exports);
|
|
31
27
|
|
|
32
|
-
// src/license/
|
|
28
|
+
// src/license/hash.ts
|
|
33
29
|
var import_node_crypto = require("crypto");
|
|
30
|
+
|
|
31
|
+
// src/signing/canonical.ts
|
|
34
32
|
function canonicalStringify(value) {
|
|
35
33
|
return JSON.stringify(sortKeysDeep(value));
|
|
36
34
|
}
|
|
37
35
|
__name(canonicalStringify, "canonicalStringify");
|
|
38
|
-
function hashSnapshot(value) {
|
|
39
|
-
return (0, import_node_crypto.createHash)("sha256").update(canonicalStringify(value), "utf8").digest("hex");
|
|
40
|
-
}
|
|
41
|
-
__name(hashSnapshot, "hashSnapshot");
|
|
42
36
|
function sortKeysDeep(value) {
|
|
43
37
|
if (Array.isArray(value)) return value.map(sortKeysDeep);
|
|
44
38
|
if (value !== null && typeof value === "object") {
|
|
@@ -51,54 +45,13 @@ function sortKeysDeep(value) {
|
|
|
51
45
|
}
|
|
52
46
|
__name(sortKeysDeep, "sortKeysDeep");
|
|
53
47
|
|
|
54
|
-
// src/license/
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const { privateKey, publicKey } = (0, import_node_crypto2.generateKeyPairSync)("ed25519");
|
|
58
|
-
return {
|
|
59
|
-
privateKey: privateKey.export({
|
|
60
|
-
type: "pkcs8",
|
|
61
|
-
format: "der"
|
|
62
|
-
}).toString("base64"),
|
|
63
|
-
publicKey: publicKey.export({
|
|
64
|
-
type: "spki",
|
|
65
|
-
format: "der"
|
|
66
|
-
}).toString("base64")
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
__name(generateLicenseKeyPair, "generateLicenseKeyPair");
|
|
70
|
-
function signDocument(payload, privateKeyBase64) {
|
|
71
|
-
const key = (0, import_node_crypto2.createPrivateKey)({
|
|
72
|
-
key: Buffer.from(privateKeyBase64, "base64"),
|
|
73
|
-
format: "der",
|
|
74
|
-
type: "pkcs8"
|
|
75
|
-
});
|
|
76
|
-
const signature = (0, import_node_crypto2.sign)(null, Buffer.from(canonicalStringify(payload), "utf8"), key).toString("base64");
|
|
77
|
-
return {
|
|
78
|
-
payload,
|
|
79
|
-
signature
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
__name(signDocument, "signDocument");
|
|
83
|
-
function verifyDocument(doc, publicKeyBase64) {
|
|
84
|
-
try {
|
|
85
|
-
const key = (0, import_node_crypto2.createPublicKey)({
|
|
86
|
-
key: Buffer.from(publicKeyBase64, "base64"),
|
|
87
|
-
format: "der",
|
|
88
|
-
type: "spki"
|
|
89
|
-
});
|
|
90
|
-
return (0, import_node_crypto2.verify)(null, Buffer.from(canonicalStringify(doc.payload), "utf8"), key, Buffer.from(doc.signature, "base64"));
|
|
91
|
-
} catch {
|
|
92
|
-
return false;
|
|
93
|
-
}
|
|
48
|
+
// src/license/hash.ts
|
|
49
|
+
function hashSnapshot(value) {
|
|
50
|
+
return (0, import_node_crypto.createHash)("sha256").update(canonicalStringify(value), "utf8").digest("hex");
|
|
94
51
|
}
|
|
95
|
-
__name(
|
|
52
|
+
__name(hashSnapshot, "hashSnapshot");
|
|
96
53
|
// Annotate the CommonJS export names for ESM import in node:
|
|
97
54
|
0 && (module.exports = {
|
|
98
|
-
|
|
99
|
-
generateLicenseKeyPair,
|
|
100
|
-
hashSnapshot,
|
|
101
|
-
signDocument,
|
|
102
|
-
verifyDocument
|
|
55
|
+
hashSnapshot
|
|
103
56
|
});
|
|
104
57
|
//# sourceMappingURL=license.cjs.map
|
package/dist/license.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/license/index.ts","../src/license/
|
|
1
|
+
{"version":3,"sources":["../src/license/index.ts","../src/license/hash.ts","../src/signing/canonical.ts"],"sourcesContent":["// License module — licensing artifacts: signed catalog/entitlement document types and snapshot hashing\nexport { hashSnapshot } from './hash';\nexport type { CatalogLicense, OrgEntitlement, SignedDocument } from './types';\n","import { createHash } from 'node:crypto';\nimport { canonicalStringify } from '../signing/canonical';\n\n// Computes the sha256 hex digest of a value's canonical JSON — idempotency key for catalog snapshots\nexport function hashSnapshot(value: unknown): string {\n return createHash('sha256').update(canonicalStringify(value), 'utf8').digest('hex');\n}\n","// Serializes a value as deterministic JSON: object keys recursively sorted, arrays keep their order\nexport function canonicalStringify(value: unknown): string {\n return JSON.stringify(sortKeysDeep(value));\n}\n\n// Recursively rebuilds objects with sorted keys so JSON.stringify output is order-independent\nfunction sortKeysDeep(value: unknown): unknown {\n if (Array.isArray(value)) return value.map(sortKeysDeep);\n if (value !== null && typeof value === 'object') {\n const record = value as Record<string, unknown>;\n const sorted: Record<string, unknown> = {};\n for (const key of Object.keys(record).sort()) sorted[key] = sortKeysDeep(record[key]);\n return sorted;\n }\n return value;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;;;;;;;ACAA,yBAA2B;;;ACCpB,SAASA,mBAAmBC,OAAc;AAC/C,SAAOC,KAAKC,UAAUC,aAAaH,KAAAA,CAAAA;AACrC;AAFgBD;AAKhB,SAASI,aAAaH,OAAc;AAClC,MAAII,MAAMC,QAAQL,KAAAA,EAAQ,QAAOA,MAAMM,IAAIH,YAAAA;AAC3C,MAAIH,UAAU,QAAQ,OAAOA,UAAU,UAAU;AAC/C,UAAMO,SAASP;AACf,UAAMQ,SAAkC,CAAC;AACzC,eAAWC,OAAOC,OAAOC,KAAKJ,MAAAA,EAAQK,KAAI,EAAIJ,QAAOC,GAAAA,IAAON,aAAaI,OAAOE,GAAAA,CAAI;AACpF,WAAOD;EACT;AACA,SAAOR;AACT;AATSG;;;ADFF,SAASU,aAAaC,OAAc;AACzC,aAAOC,+BAAW,QAAA,EAAUC,OAAOC,mBAAmBH,KAAAA,GAAQ,MAAA,EAAQI,OAAO,KAAA;AAC/E;AAFgBL;","names":["canonicalStringify","value","JSON","stringify","sortKeysDeep","Array","isArray","map","record","sorted","key","Object","keys","sort","hashSnapshot","value","createHash","update","canonicalStringify","digest"]}
|
package/dist/license.d.cts
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
import { V as VersionSnapshot } from './types-DQRwj5rr.cjs';
|
|
2
|
+
export { S as SignedDocument } from './document-BoS0NIbf.cjs';
|
|
2
3
|
|
|
3
|
-
declare function canonicalStringify(value: unknown): string;
|
|
4
4
|
declare function hashSnapshot(value: unknown): string;
|
|
5
5
|
|
|
6
|
-
interface SignedDocument<T> {
|
|
7
|
-
payload: T;
|
|
8
|
-
signature: string;
|
|
9
|
-
}
|
|
10
6
|
interface CatalogLicense {
|
|
11
7
|
deploymentId: string;
|
|
12
8
|
version: string;
|
|
@@ -22,11 +18,4 @@ interface OrgEntitlement {
|
|
|
22
18
|
issuedAt: string;
|
|
23
19
|
}
|
|
24
20
|
|
|
25
|
-
|
|
26
|
-
privateKey: string;
|
|
27
|
-
publicKey: string;
|
|
28
|
-
};
|
|
29
|
-
declare function signDocument<T>(payload: T, privateKeyBase64: string): SignedDocument<T>;
|
|
30
|
-
declare function verifyDocument<T>(doc: SignedDocument<T>, publicKeyBase64: string): boolean;
|
|
31
|
-
|
|
32
|
-
export { type CatalogLicense, type OrgEntitlement, type SignedDocument, canonicalStringify, generateLicenseKeyPair, hashSnapshot, signDocument, verifyDocument };
|
|
21
|
+
export { type CatalogLicense, type OrgEntitlement, hashSnapshot };
|
package/dist/license.d.ts
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
import { V as VersionSnapshot } from './types-DQRwj5rr.js';
|
|
2
|
+
export { S as SignedDocument } from './document-BoS0NIbf.js';
|
|
2
3
|
|
|
3
|
-
declare function canonicalStringify(value: unknown): string;
|
|
4
4
|
declare function hashSnapshot(value: unknown): string;
|
|
5
5
|
|
|
6
|
-
interface SignedDocument<T> {
|
|
7
|
-
payload: T;
|
|
8
|
-
signature: string;
|
|
9
|
-
}
|
|
10
6
|
interface CatalogLicense {
|
|
11
7
|
deploymentId: string;
|
|
12
8
|
version: string;
|
|
@@ -22,11 +18,4 @@ interface OrgEntitlement {
|
|
|
22
18
|
issuedAt: string;
|
|
23
19
|
}
|
|
24
20
|
|
|
25
|
-
|
|
26
|
-
privateKey: string;
|
|
27
|
-
publicKey: string;
|
|
28
|
-
};
|
|
29
|
-
declare function signDocument<T>(payload: T, privateKeyBase64: string): SignedDocument<T>;
|
|
30
|
-
declare function verifyDocument<T>(doc: SignedDocument<T>, publicKeyBase64: string): boolean;
|
|
31
|
-
|
|
32
|
-
export { type CatalogLicense, type OrgEntitlement, type SignedDocument, canonicalStringify, generateLicenseKeyPair, hashSnapshot, signDocument, verifyDocument };
|
|
21
|
+
export { type CatalogLicense, type OrgEntitlement, hashSnapshot };
|
package/dist/license.js
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
2
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
3
|
|
|
4
|
-
// src/license/
|
|
4
|
+
// src/license/hash.ts
|
|
5
5
|
import { createHash } from "crypto";
|
|
6
|
+
|
|
7
|
+
// src/signing/canonical.ts
|
|
6
8
|
function canonicalStringify(value) {
|
|
7
9
|
return JSON.stringify(sortKeysDeep(value));
|
|
8
10
|
}
|
|
9
11
|
__name(canonicalStringify, "canonicalStringify");
|
|
10
|
-
function hashSnapshot(value) {
|
|
11
|
-
return createHash("sha256").update(canonicalStringify(value), "utf8").digest("hex");
|
|
12
|
-
}
|
|
13
|
-
__name(hashSnapshot, "hashSnapshot");
|
|
14
12
|
function sortKeysDeep(value) {
|
|
15
13
|
if (Array.isArray(value)) return value.map(sortKeysDeep);
|
|
16
14
|
if (value !== null && typeof value === "object") {
|
|
@@ -23,53 +21,12 @@ function sortKeysDeep(value) {
|
|
|
23
21
|
}
|
|
24
22
|
__name(sortKeysDeep, "sortKeysDeep");
|
|
25
23
|
|
|
26
|
-
// src/license/
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const { privateKey, publicKey } = generateKeyPairSync("ed25519");
|
|
30
|
-
return {
|
|
31
|
-
privateKey: privateKey.export({
|
|
32
|
-
type: "pkcs8",
|
|
33
|
-
format: "der"
|
|
34
|
-
}).toString("base64"),
|
|
35
|
-
publicKey: publicKey.export({
|
|
36
|
-
type: "spki",
|
|
37
|
-
format: "der"
|
|
38
|
-
}).toString("base64")
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
__name(generateLicenseKeyPair, "generateLicenseKeyPair");
|
|
42
|
-
function signDocument(payload, privateKeyBase64) {
|
|
43
|
-
const key = createPrivateKey({
|
|
44
|
-
key: Buffer.from(privateKeyBase64, "base64"),
|
|
45
|
-
format: "der",
|
|
46
|
-
type: "pkcs8"
|
|
47
|
-
});
|
|
48
|
-
const signature = sign(null, Buffer.from(canonicalStringify(payload), "utf8"), key).toString("base64");
|
|
49
|
-
return {
|
|
50
|
-
payload,
|
|
51
|
-
signature
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
__name(signDocument, "signDocument");
|
|
55
|
-
function verifyDocument(doc, publicKeyBase64) {
|
|
56
|
-
try {
|
|
57
|
-
const key = createPublicKey({
|
|
58
|
-
key: Buffer.from(publicKeyBase64, "base64"),
|
|
59
|
-
format: "der",
|
|
60
|
-
type: "spki"
|
|
61
|
-
});
|
|
62
|
-
return verify(null, Buffer.from(canonicalStringify(doc.payload), "utf8"), key, Buffer.from(doc.signature, "base64"));
|
|
63
|
-
} catch {
|
|
64
|
-
return false;
|
|
65
|
-
}
|
|
24
|
+
// src/license/hash.ts
|
|
25
|
+
function hashSnapshot(value) {
|
|
26
|
+
return createHash("sha256").update(canonicalStringify(value), "utf8").digest("hex");
|
|
66
27
|
}
|
|
67
|
-
__name(
|
|
28
|
+
__name(hashSnapshot, "hashSnapshot");
|
|
68
29
|
export {
|
|
69
|
-
|
|
70
|
-
generateLicenseKeyPair,
|
|
71
|
-
hashSnapshot,
|
|
72
|
-
signDocument,
|
|
73
|
-
verifyDocument
|
|
30
|
+
hashSnapshot
|
|
74
31
|
};
|
|
75
32
|
//# sourceMappingURL=license.js.map
|
package/dist/license.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/license/
|
|
1
|
+
{"version":3,"sources":["../src/license/hash.ts","../src/signing/canonical.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport { canonicalStringify } from '../signing/canonical';\n\n// Computes the sha256 hex digest of a value's canonical JSON — idempotency key for catalog snapshots\nexport function hashSnapshot(value: unknown): string {\n return createHash('sha256').update(canonicalStringify(value), 'utf8').digest('hex');\n}\n","// Serializes a value as deterministic JSON: object keys recursively sorted, arrays keep their order\nexport function canonicalStringify(value: unknown): string {\n return JSON.stringify(sortKeysDeep(value));\n}\n\n// Recursively rebuilds objects with sorted keys so JSON.stringify output is order-independent\nfunction sortKeysDeep(value: unknown): unknown {\n if (Array.isArray(value)) return value.map(sortKeysDeep);\n if (value !== null && typeof value === 'object') {\n const record = value as Record<string, unknown>;\n const sorted: Record<string, unknown> = {};\n for (const key of Object.keys(record).sort()) sorted[key] = sortKeysDeep(record[key]);\n return sorted;\n }\n return value;\n}\n"],"mappings":";;;;AAAA,SAASA,kBAAkB;;;ACCpB,SAASC,mBAAmBC,OAAc;AAC/C,SAAOC,KAAKC,UAAUC,aAAaH,KAAAA,CAAAA;AACrC;AAFgBD;AAKhB,SAASI,aAAaH,OAAc;AAClC,MAAII,MAAMC,QAAQL,KAAAA,EAAQ,QAAOA,MAAMM,IAAIH,YAAAA;AAC3C,MAAIH,UAAU,QAAQ,OAAOA,UAAU,UAAU;AAC/C,UAAMO,SAASP;AACf,UAAMQ,SAAkC,CAAC;AACzC,eAAWC,OAAOC,OAAOC,KAAKJ,MAAAA,EAAQK,KAAI,EAAIJ,QAAOC,GAAAA,IAAON,aAAaI,OAAOE,GAAAA,CAAI;AACpF,WAAOD;EACT;AACA,SAAOR;AACT;AATSG;;;ADFF,SAASU,aAAaC,OAAc;AACzC,SAAOC,WAAW,QAAA,EAAUC,OAAOC,mBAAmBH,KAAAA,GAAQ,MAAA,EAAQI,OAAO,KAAA;AAC/E;AAFgBL;","names":["createHash","canonicalStringify","value","JSON","stringify","sortKeysDeep","Array","isArray","map","record","sorted","key","Object","keys","sort","hashSnapshot","value","createHash","update","canonicalStringify","digest"]}
|
package/dist/signing.cjs
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
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 __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
|
|
21
|
+
// src/signing/index.ts
|
|
22
|
+
var signing_exports = {};
|
|
23
|
+
__export(signing_exports, {
|
|
24
|
+
buildRequestCanonical: () => buildRequestCanonical,
|
|
25
|
+
canonicalStringify: () => canonicalStringify,
|
|
26
|
+
generateSigningKeyPair: () => generateSigningKeyPair,
|
|
27
|
+
signDocument: () => signDocument,
|
|
28
|
+
signRequestHeaders: () => signRequestHeaders,
|
|
29
|
+
verifyDocument: () => verifyDocument,
|
|
30
|
+
verifySignedRequest: () => verifySignedRequest
|
|
31
|
+
});
|
|
32
|
+
module.exports = __toCommonJS(signing_exports);
|
|
33
|
+
|
|
34
|
+
// src/signing/canonical.ts
|
|
35
|
+
function canonicalStringify(value) {
|
|
36
|
+
return JSON.stringify(sortKeysDeep(value));
|
|
37
|
+
}
|
|
38
|
+
__name(canonicalStringify, "canonicalStringify");
|
|
39
|
+
function sortKeysDeep(value) {
|
|
40
|
+
if (Array.isArray(value)) return value.map(sortKeysDeep);
|
|
41
|
+
if (value !== null && typeof value === "object") {
|
|
42
|
+
const record = value;
|
|
43
|
+
const sorted = {};
|
|
44
|
+
for (const key of Object.keys(record).sort()) sorted[key] = sortKeysDeep(record[key]);
|
|
45
|
+
return sorted;
|
|
46
|
+
}
|
|
47
|
+
return value;
|
|
48
|
+
}
|
|
49
|
+
__name(sortKeysDeep, "sortKeysDeep");
|
|
50
|
+
|
|
51
|
+
// src/signing/document.ts
|
|
52
|
+
var import_node_crypto = require("crypto");
|
|
53
|
+
function generateSigningKeyPair() {
|
|
54
|
+
const { privateKey, publicKey } = (0, import_node_crypto.generateKeyPairSync)("ed25519");
|
|
55
|
+
return {
|
|
56
|
+
privateKey: privateKey.export({
|
|
57
|
+
type: "pkcs8",
|
|
58
|
+
format: "der"
|
|
59
|
+
}).toString("base64"),
|
|
60
|
+
publicKey: publicKey.export({
|
|
61
|
+
type: "spki",
|
|
62
|
+
format: "der"
|
|
63
|
+
}).toString("base64")
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
__name(generateSigningKeyPair, "generateSigningKeyPair");
|
|
67
|
+
function signDocument(payload, privateKeyBase64) {
|
|
68
|
+
const key = (0, import_node_crypto.createPrivateKey)({
|
|
69
|
+
key: Buffer.from(privateKeyBase64, "base64"),
|
|
70
|
+
format: "der",
|
|
71
|
+
type: "pkcs8"
|
|
72
|
+
});
|
|
73
|
+
const signature = (0, import_node_crypto.sign)(null, Buffer.from(canonicalStringify(payload), "utf8"), key).toString("base64");
|
|
74
|
+
return {
|
|
75
|
+
payload,
|
|
76
|
+
signature
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
__name(signDocument, "signDocument");
|
|
80
|
+
function verifyDocument(doc, publicKeyBase64) {
|
|
81
|
+
try {
|
|
82
|
+
const key = (0, import_node_crypto.createPublicKey)({
|
|
83
|
+
key: Buffer.from(publicKeyBase64, "base64"),
|
|
84
|
+
format: "der",
|
|
85
|
+
type: "spki"
|
|
86
|
+
});
|
|
87
|
+
return (0, import_node_crypto.verify)(null, Buffer.from(canonicalStringify(doc.payload), "utf8"), key, Buffer.from(doc.signature, "base64"));
|
|
88
|
+
} catch {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
__name(verifyDocument, "verifyDocument");
|
|
93
|
+
|
|
94
|
+
// src/signing/request.ts
|
|
95
|
+
var import_node_crypto2 = require("crypto");
|
|
96
|
+
function buildRequestCanonical(input) {
|
|
97
|
+
const bodyHash = (0, import_node_crypto2.createHash)("sha256").update(input.body ?? "").digest("hex");
|
|
98
|
+
return [
|
|
99
|
+
input.method.toUpperCase(),
|
|
100
|
+
input.path,
|
|
101
|
+
input.orgId ?? "",
|
|
102
|
+
bodyHash,
|
|
103
|
+
String(input.timestamp)
|
|
104
|
+
].join("\n");
|
|
105
|
+
}
|
|
106
|
+
__name(buildRequestCanonical, "buildRequestCanonical");
|
|
107
|
+
function signRequestHeaders(input, privateKeyBase64) {
|
|
108
|
+
const timestamp = Math.floor(Date.now() / 1e3);
|
|
109
|
+
const canonical = buildRequestCanonical({
|
|
110
|
+
...input,
|
|
111
|
+
timestamp
|
|
112
|
+
});
|
|
113
|
+
const key = (0, import_node_crypto2.createPrivateKey)({
|
|
114
|
+
key: Buffer.from(privateKeyBase64, "base64"),
|
|
115
|
+
format: "der",
|
|
116
|
+
type: "pkcs8"
|
|
117
|
+
});
|
|
118
|
+
const signature = (0, import_node_crypto2.sign)(null, Buffer.from(canonical, "utf8"), key).toString("base64");
|
|
119
|
+
return {
|
|
120
|
+
"x-timestamp": String(timestamp),
|
|
121
|
+
"x-signature": signature
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
__name(signRequestHeaders, "signRequestHeaders");
|
|
125
|
+
function verifySignedRequest(input) {
|
|
126
|
+
try {
|
|
127
|
+
const timestamp = Number(input.timestamp);
|
|
128
|
+
if (!Number.isFinite(timestamp)) return false;
|
|
129
|
+
const maxSkew = input.maxSkewSeconds ?? 300;
|
|
130
|
+
if (Math.abs(Math.floor(Date.now() / 1e3) - timestamp) > maxSkew) return false;
|
|
131
|
+
const canonical = buildRequestCanonical({
|
|
132
|
+
method: input.method,
|
|
133
|
+
path: input.path,
|
|
134
|
+
orgId: input.orgId,
|
|
135
|
+
body: input.rawBody,
|
|
136
|
+
timestamp
|
|
137
|
+
});
|
|
138
|
+
const key = (0, import_node_crypto2.createPublicKey)({
|
|
139
|
+
key: Buffer.from(input.publicKey, "base64"),
|
|
140
|
+
format: "der",
|
|
141
|
+
type: "spki"
|
|
142
|
+
});
|
|
143
|
+
return (0, import_node_crypto2.verify)(null, Buffer.from(canonical, "utf8"), key, Buffer.from(input.signature, "base64"));
|
|
144
|
+
} catch {
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
__name(verifySignedRequest, "verifySignedRequest");
|
|
149
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
150
|
+
0 && (module.exports = {
|
|
151
|
+
buildRequestCanonical,
|
|
152
|
+
canonicalStringify,
|
|
153
|
+
generateSigningKeyPair,
|
|
154
|
+
signDocument,
|
|
155
|
+
signRequestHeaders,
|
|
156
|
+
verifyDocument,
|
|
157
|
+
verifySignedRequest
|
|
158
|
+
});
|
|
159
|
+
//# sourceMappingURL=signing.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/signing/index.ts","../src/signing/canonical.ts","../src/signing/document.ts","../src/signing/request.ts"],"sourcesContent":["// Signing module — Ed25519 primitives: keypairs, canonical-JSON document signing, and signed control-plane requests\nexport { canonicalStringify } from './canonical';\nexport { generateSigningKeyPair, type SignedDocument, signDocument, verifyDocument } from './document';\nexport {\n buildRequestCanonical,\n type RequestCanonicalInput,\n type SignRequestInput,\n signRequestHeaders,\n type VerifySignedRequestInput,\n verifySignedRequest,\n} from './request';\n","// Serializes a value as deterministic JSON: object keys recursively sorted, arrays keep their order\nexport function canonicalStringify(value: unknown): string {\n return JSON.stringify(sortKeysDeep(value));\n}\n\n// Recursively rebuilds objects with sorted keys so JSON.stringify output is order-independent\nfunction sortKeysDeep(value: unknown): unknown {\n if (Array.isArray(value)) return value.map(sortKeysDeep);\n if (value !== null && typeof value === 'object') {\n const record = value as Record<string, unknown>;\n const sorted: Record<string, unknown> = {};\n for (const key of Object.keys(record).sort()) sorted[key] = sortKeysDeep(record[key]);\n return sorted;\n }\n return value;\n}\n","import { createPrivateKey, createPublicKey, generateKeyPairSync, sign, verify } from 'node:crypto';\nimport { canonicalStringify } from './canonical';\n\nexport interface SignedDocument<T> {\n payload: T;\n signature: string;\n}\n\n// Generates an Ed25519 key pair as base64 DER strings (pkcs8 private / spki public)\nexport function generateSigningKeyPair(): { privateKey: string; publicKey: string } {\n const { privateKey, publicKey } = generateKeyPairSync('ed25519');\n return {\n privateKey: privateKey.export({ type: 'pkcs8', format: 'der' }).toString('base64'),\n publicKey: publicKey.export({ type: 'spki', format: 'der' }).toString('base64'),\n };\n}\n\n// Signs a payload's canonical JSON with an Ed25519 private key (base64 pkcs8 DER)\nexport function signDocument<T>(payload: T, privateKeyBase64: string): SignedDocument<T> {\n const key = createPrivateKey({ key: Buffer.from(privateKeyBase64, 'base64'), format: 'der', type: 'pkcs8' });\n const signature = sign(null, Buffer.from(canonicalStringify(payload), 'utf8'), key).toString('base64');\n return { payload, signature };\n}\n\n// Verifies a signed document against an Ed25519 public key (base64 spki DER); malformed input ⇒ false\nexport function verifyDocument<T>(doc: SignedDocument<T>, publicKeyBase64: string): boolean {\n try {\n const key = createPublicKey({ key: Buffer.from(publicKeyBase64, 'base64'), format: 'der', type: 'spki' });\n return verify(\n null,\n Buffer.from(canonicalStringify(doc.payload), 'utf8'),\n key,\n Buffer.from(doc.signature, 'base64'),\n );\n } catch {\n return false;\n }\n}\n","import { createHash, createPrivateKey, createPublicKey, sign, verify } from 'node:crypto';\n\nexport interface RequestCanonicalInput {\n method: string;\n path: string;\n orgId?: string;\n body?: string | Buffer;\n timestamp: number;\n}\n\nexport interface SignRequestInput {\n method: string;\n path: string;\n orgId?: string;\n body?: string | Buffer;\n}\n\nexport interface VerifySignedRequestInput {\n method: string;\n path: string;\n orgId?: string;\n rawBody?: string | Buffer;\n timestamp: string | number;\n signature: string;\n publicKey: string;\n maxSkewSeconds?: number;\n}\n\n// Builds the canonical string signed for a control-plane request: METHOD\\npath\\norgId\\nsha256hex(body)\\ntimestamp\nexport function buildRequestCanonical(input: RequestCanonicalInput): string {\n const bodyHash = createHash('sha256')\n .update(input.body ?? '')\n .digest('hex');\n return [input.method.toUpperCase(), input.path, input.orgId ?? '', bodyHash, String(input.timestamp)].join('\\n');\n}\n\n// Signs a request with an Ed25519 private key (base64 pkcs8 DER), stamping the current unix time\nexport function signRequestHeaders(\n input: SignRequestInput,\n privateKeyBase64: string,\n): { 'x-timestamp': string; 'x-signature': string } {\n const timestamp = Math.floor(Date.now() / 1000);\n const canonical = buildRequestCanonical({ ...input, timestamp });\n const key = createPrivateKey({ key: Buffer.from(privateKeyBase64, 'base64'), format: 'der', type: 'pkcs8' });\n const signature = sign(null, Buffer.from(canonical, 'utf8'), key).toString('base64');\n return { 'x-timestamp': String(timestamp), 'x-signature': signature };\n}\n\n// Verifies a signed request (signature + timestamp skew) against an Ed25519 public key; malformed input ⇒ false\nexport function verifySignedRequest(input: VerifySignedRequestInput): boolean {\n try {\n const timestamp = Number(input.timestamp);\n if (!Number.isFinite(timestamp)) return false;\n const maxSkew = input.maxSkewSeconds ?? 300;\n if (Math.abs(Math.floor(Date.now() / 1000) - timestamp) > maxSkew) return false;\n const canonical = buildRequestCanonical({\n method: input.method,\n path: input.path,\n orgId: input.orgId,\n body: input.rawBody,\n timestamp,\n });\n const key = createPublicKey({ key: Buffer.from(input.publicKey, 'base64'), format: 'der', type: 'spki' });\n return verify(null, Buffer.from(canonical, 'utf8'), key, Buffer.from(input.signature, 'base64'));\n } catch {\n return false;\n }\n}\n"],"mappings":"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","names":["canonicalStringify","value","JSON","stringify","sortKeysDeep","Array","isArray","map","record","sorted","key","Object","keys","sort","generateSigningKeyPair","privateKey","publicKey","generateKeyPairSync","export","type","format","toString","signDocument","payload","privateKeyBase64","key","createPrivateKey","Buffer","from","signature","sign","canonicalStringify","verifyDocument","doc","publicKeyBase64","createPublicKey","verify","import_node_crypto","buildRequestCanonical","input","bodyHash","createHash","update","body","digest","method","toUpperCase","path","orgId","String","timestamp","join","signRequestHeaders","privateKeyBase64","Math","floor","Date","now","canonical","key","createPrivateKey","Buffer","from","format","type","signature","sign","toString","verifySignedRequest","Number","isFinite","maxSkew","maxSkewSeconds","abs","rawBody","createPublicKey","publicKey","verify"]}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export { S as SignedDocument, g as generateSigningKeyPair, s as signDocument, v as verifyDocument } from './document-BoS0NIbf.cjs';
|
|
2
|
+
|
|
3
|
+
declare function canonicalStringify(value: unknown): string;
|
|
4
|
+
|
|
5
|
+
interface RequestCanonicalInput {
|
|
6
|
+
method: string;
|
|
7
|
+
path: string;
|
|
8
|
+
orgId?: string;
|
|
9
|
+
body?: string | Buffer;
|
|
10
|
+
timestamp: number;
|
|
11
|
+
}
|
|
12
|
+
interface SignRequestInput {
|
|
13
|
+
method: string;
|
|
14
|
+
path: string;
|
|
15
|
+
orgId?: string;
|
|
16
|
+
body?: string | Buffer;
|
|
17
|
+
}
|
|
18
|
+
interface VerifySignedRequestInput {
|
|
19
|
+
method: string;
|
|
20
|
+
path: string;
|
|
21
|
+
orgId?: string;
|
|
22
|
+
rawBody?: string | Buffer;
|
|
23
|
+
timestamp: string | number;
|
|
24
|
+
signature: string;
|
|
25
|
+
publicKey: string;
|
|
26
|
+
maxSkewSeconds?: number;
|
|
27
|
+
}
|
|
28
|
+
declare function buildRequestCanonical(input: RequestCanonicalInput): string;
|
|
29
|
+
declare function signRequestHeaders(input: SignRequestInput, privateKeyBase64: string): {
|
|
30
|
+
'x-timestamp': string;
|
|
31
|
+
'x-signature': string;
|
|
32
|
+
};
|
|
33
|
+
declare function verifySignedRequest(input: VerifySignedRequestInput): boolean;
|
|
34
|
+
|
|
35
|
+
export { type RequestCanonicalInput, type SignRequestInput, type VerifySignedRequestInput, buildRequestCanonical, canonicalStringify, signRequestHeaders, verifySignedRequest };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export { S as SignedDocument, g as generateSigningKeyPair, s as signDocument, v as verifyDocument } from './document-BoS0NIbf.js';
|
|
2
|
+
|
|
3
|
+
declare function canonicalStringify(value: unknown): string;
|
|
4
|
+
|
|
5
|
+
interface RequestCanonicalInput {
|
|
6
|
+
method: string;
|
|
7
|
+
path: string;
|
|
8
|
+
orgId?: string;
|
|
9
|
+
body?: string | Buffer;
|
|
10
|
+
timestamp: number;
|
|
11
|
+
}
|
|
12
|
+
interface SignRequestInput {
|
|
13
|
+
method: string;
|
|
14
|
+
path: string;
|
|
15
|
+
orgId?: string;
|
|
16
|
+
body?: string | Buffer;
|
|
17
|
+
}
|
|
18
|
+
interface VerifySignedRequestInput {
|
|
19
|
+
method: string;
|
|
20
|
+
path: string;
|
|
21
|
+
orgId?: string;
|
|
22
|
+
rawBody?: string | Buffer;
|
|
23
|
+
timestamp: string | number;
|
|
24
|
+
signature: string;
|
|
25
|
+
publicKey: string;
|
|
26
|
+
maxSkewSeconds?: number;
|
|
27
|
+
}
|
|
28
|
+
declare function buildRequestCanonical(input: RequestCanonicalInput): string;
|
|
29
|
+
declare function signRequestHeaders(input: SignRequestInput, privateKeyBase64: string): {
|
|
30
|
+
'x-timestamp': string;
|
|
31
|
+
'x-signature': string;
|
|
32
|
+
};
|
|
33
|
+
declare function verifySignedRequest(input: VerifySignedRequestInput): boolean;
|
|
34
|
+
|
|
35
|
+
export { type RequestCanonicalInput, type SignRequestInput, type VerifySignedRequestInput, buildRequestCanonical, canonicalStringify, signRequestHeaders, verifySignedRequest };
|
package/dist/signing.js
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
|
+
|
|
4
|
+
// src/signing/canonical.ts
|
|
5
|
+
function canonicalStringify(value) {
|
|
6
|
+
return JSON.stringify(sortKeysDeep(value));
|
|
7
|
+
}
|
|
8
|
+
__name(canonicalStringify, "canonicalStringify");
|
|
9
|
+
function sortKeysDeep(value) {
|
|
10
|
+
if (Array.isArray(value)) return value.map(sortKeysDeep);
|
|
11
|
+
if (value !== null && typeof value === "object") {
|
|
12
|
+
const record = value;
|
|
13
|
+
const sorted = {};
|
|
14
|
+
for (const key of Object.keys(record).sort()) sorted[key] = sortKeysDeep(record[key]);
|
|
15
|
+
return sorted;
|
|
16
|
+
}
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
__name(sortKeysDeep, "sortKeysDeep");
|
|
20
|
+
|
|
21
|
+
// src/signing/document.ts
|
|
22
|
+
import { createPrivateKey, createPublicKey, generateKeyPairSync, sign, verify } from "crypto";
|
|
23
|
+
function generateSigningKeyPair() {
|
|
24
|
+
const { privateKey, publicKey } = generateKeyPairSync("ed25519");
|
|
25
|
+
return {
|
|
26
|
+
privateKey: privateKey.export({
|
|
27
|
+
type: "pkcs8",
|
|
28
|
+
format: "der"
|
|
29
|
+
}).toString("base64"),
|
|
30
|
+
publicKey: publicKey.export({
|
|
31
|
+
type: "spki",
|
|
32
|
+
format: "der"
|
|
33
|
+
}).toString("base64")
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
__name(generateSigningKeyPair, "generateSigningKeyPair");
|
|
37
|
+
function signDocument(payload, privateKeyBase64) {
|
|
38
|
+
const key = createPrivateKey({
|
|
39
|
+
key: Buffer.from(privateKeyBase64, "base64"),
|
|
40
|
+
format: "der",
|
|
41
|
+
type: "pkcs8"
|
|
42
|
+
});
|
|
43
|
+
const signature = sign(null, Buffer.from(canonicalStringify(payload), "utf8"), key).toString("base64");
|
|
44
|
+
return {
|
|
45
|
+
payload,
|
|
46
|
+
signature
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
__name(signDocument, "signDocument");
|
|
50
|
+
function verifyDocument(doc, publicKeyBase64) {
|
|
51
|
+
try {
|
|
52
|
+
const key = createPublicKey({
|
|
53
|
+
key: Buffer.from(publicKeyBase64, "base64"),
|
|
54
|
+
format: "der",
|
|
55
|
+
type: "spki"
|
|
56
|
+
});
|
|
57
|
+
return verify(null, Buffer.from(canonicalStringify(doc.payload), "utf8"), key, Buffer.from(doc.signature, "base64"));
|
|
58
|
+
} catch {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
__name(verifyDocument, "verifyDocument");
|
|
63
|
+
|
|
64
|
+
// src/signing/request.ts
|
|
65
|
+
import { createHash, createPrivateKey as createPrivateKey2, createPublicKey as createPublicKey2, sign as sign2, verify as verify2 } from "crypto";
|
|
66
|
+
function buildRequestCanonical(input) {
|
|
67
|
+
const bodyHash = createHash("sha256").update(input.body ?? "").digest("hex");
|
|
68
|
+
return [
|
|
69
|
+
input.method.toUpperCase(),
|
|
70
|
+
input.path,
|
|
71
|
+
input.orgId ?? "",
|
|
72
|
+
bodyHash,
|
|
73
|
+
String(input.timestamp)
|
|
74
|
+
].join("\n");
|
|
75
|
+
}
|
|
76
|
+
__name(buildRequestCanonical, "buildRequestCanonical");
|
|
77
|
+
function signRequestHeaders(input, privateKeyBase64) {
|
|
78
|
+
const timestamp = Math.floor(Date.now() / 1e3);
|
|
79
|
+
const canonical = buildRequestCanonical({
|
|
80
|
+
...input,
|
|
81
|
+
timestamp
|
|
82
|
+
});
|
|
83
|
+
const key = createPrivateKey2({
|
|
84
|
+
key: Buffer.from(privateKeyBase64, "base64"),
|
|
85
|
+
format: "der",
|
|
86
|
+
type: "pkcs8"
|
|
87
|
+
});
|
|
88
|
+
const signature = sign2(null, Buffer.from(canonical, "utf8"), key).toString("base64");
|
|
89
|
+
return {
|
|
90
|
+
"x-timestamp": String(timestamp),
|
|
91
|
+
"x-signature": signature
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
__name(signRequestHeaders, "signRequestHeaders");
|
|
95
|
+
function verifySignedRequest(input) {
|
|
96
|
+
try {
|
|
97
|
+
const timestamp = Number(input.timestamp);
|
|
98
|
+
if (!Number.isFinite(timestamp)) return false;
|
|
99
|
+
const maxSkew = input.maxSkewSeconds ?? 300;
|
|
100
|
+
if (Math.abs(Math.floor(Date.now() / 1e3) - timestamp) > maxSkew) return false;
|
|
101
|
+
const canonical = buildRequestCanonical({
|
|
102
|
+
method: input.method,
|
|
103
|
+
path: input.path,
|
|
104
|
+
orgId: input.orgId,
|
|
105
|
+
body: input.rawBody,
|
|
106
|
+
timestamp
|
|
107
|
+
});
|
|
108
|
+
const key = createPublicKey2({
|
|
109
|
+
key: Buffer.from(input.publicKey, "base64"),
|
|
110
|
+
format: "der",
|
|
111
|
+
type: "spki"
|
|
112
|
+
});
|
|
113
|
+
return verify2(null, Buffer.from(canonical, "utf8"), key, Buffer.from(input.signature, "base64"));
|
|
114
|
+
} catch {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
__name(verifySignedRequest, "verifySignedRequest");
|
|
119
|
+
export {
|
|
120
|
+
buildRequestCanonical,
|
|
121
|
+
canonicalStringify,
|
|
122
|
+
generateSigningKeyPair,
|
|
123
|
+
signDocument,
|
|
124
|
+
signRequestHeaders,
|
|
125
|
+
verifyDocument,
|
|
126
|
+
verifySignedRequest
|
|
127
|
+
};
|
|
128
|
+
//# sourceMappingURL=signing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/signing/canonical.ts","../src/signing/document.ts","../src/signing/request.ts"],"sourcesContent":["// Serializes a value as deterministic JSON: object keys recursively sorted, arrays keep their order\nexport function canonicalStringify(value: unknown): string {\n return JSON.stringify(sortKeysDeep(value));\n}\n\n// Recursively rebuilds objects with sorted keys so JSON.stringify output is order-independent\nfunction sortKeysDeep(value: unknown): unknown {\n if (Array.isArray(value)) return value.map(sortKeysDeep);\n if (value !== null && typeof value === 'object') {\n const record = value as Record<string, unknown>;\n const sorted: Record<string, unknown> = {};\n for (const key of Object.keys(record).sort()) sorted[key] = sortKeysDeep(record[key]);\n return sorted;\n }\n return value;\n}\n","import { createPrivateKey, createPublicKey, generateKeyPairSync, sign, verify } from 'node:crypto';\nimport { canonicalStringify } from './canonical';\n\nexport interface SignedDocument<T> {\n payload: T;\n signature: string;\n}\n\n// Generates an Ed25519 key pair as base64 DER strings (pkcs8 private / spki public)\nexport function generateSigningKeyPair(): { privateKey: string; publicKey: string } {\n const { privateKey, publicKey } = generateKeyPairSync('ed25519');\n return {\n privateKey: privateKey.export({ type: 'pkcs8', format: 'der' }).toString('base64'),\n publicKey: publicKey.export({ type: 'spki', format: 'der' }).toString('base64'),\n };\n}\n\n// Signs a payload's canonical JSON with an Ed25519 private key (base64 pkcs8 DER)\nexport function signDocument<T>(payload: T, privateKeyBase64: string): SignedDocument<T> {\n const key = createPrivateKey({ key: Buffer.from(privateKeyBase64, 'base64'), format: 'der', type: 'pkcs8' });\n const signature = sign(null, Buffer.from(canonicalStringify(payload), 'utf8'), key).toString('base64');\n return { payload, signature };\n}\n\n// Verifies a signed document against an Ed25519 public key (base64 spki DER); malformed input ⇒ false\nexport function verifyDocument<T>(doc: SignedDocument<T>, publicKeyBase64: string): boolean {\n try {\n const key = createPublicKey({ key: Buffer.from(publicKeyBase64, 'base64'), format: 'der', type: 'spki' });\n return verify(\n null,\n Buffer.from(canonicalStringify(doc.payload), 'utf8'),\n key,\n Buffer.from(doc.signature, 'base64'),\n );\n } catch {\n return false;\n }\n}\n","import { createHash, createPrivateKey, createPublicKey, sign, verify } from 'node:crypto';\n\nexport interface RequestCanonicalInput {\n method: string;\n path: string;\n orgId?: string;\n body?: string | Buffer;\n timestamp: number;\n}\n\nexport interface SignRequestInput {\n method: string;\n path: string;\n orgId?: string;\n body?: string | Buffer;\n}\n\nexport interface VerifySignedRequestInput {\n method: string;\n path: string;\n orgId?: string;\n rawBody?: string | Buffer;\n timestamp: string | number;\n signature: string;\n publicKey: string;\n maxSkewSeconds?: number;\n}\n\n// Builds the canonical string signed for a control-plane request: METHOD\\npath\\norgId\\nsha256hex(body)\\ntimestamp\nexport function buildRequestCanonical(input: RequestCanonicalInput): string {\n const bodyHash = createHash('sha256')\n .update(input.body ?? '')\n .digest('hex');\n return [input.method.toUpperCase(), input.path, input.orgId ?? '', bodyHash, String(input.timestamp)].join('\\n');\n}\n\n// Signs a request with an Ed25519 private key (base64 pkcs8 DER), stamping the current unix time\nexport function signRequestHeaders(\n input: SignRequestInput,\n privateKeyBase64: string,\n): { 'x-timestamp': string; 'x-signature': string } {\n const timestamp = Math.floor(Date.now() / 1000);\n const canonical = buildRequestCanonical({ ...input, timestamp });\n const key = createPrivateKey({ key: Buffer.from(privateKeyBase64, 'base64'), format: 'der', type: 'pkcs8' });\n const signature = sign(null, Buffer.from(canonical, 'utf8'), key).toString('base64');\n return { 'x-timestamp': String(timestamp), 'x-signature': signature };\n}\n\n// Verifies a signed request (signature + timestamp skew) against an Ed25519 public key; malformed input ⇒ false\nexport function verifySignedRequest(input: VerifySignedRequestInput): boolean {\n try {\n const timestamp = Number(input.timestamp);\n if (!Number.isFinite(timestamp)) return false;\n const maxSkew = input.maxSkewSeconds ?? 300;\n if (Math.abs(Math.floor(Date.now() / 1000) - timestamp) > maxSkew) return false;\n const canonical = buildRequestCanonical({\n method: input.method,\n path: input.path,\n orgId: input.orgId,\n body: input.rawBody,\n timestamp,\n });\n const key = createPublicKey({ key: Buffer.from(input.publicKey, 'base64'), format: 'der', type: 'spki' });\n return verify(null, Buffer.from(canonical, 'utf8'), key, Buffer.from(input.signature, 'base64'));\n } catch {\n return false;\n }\n}\n"],"mappings":"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","names":["canonicalStringify","value","JSON","stringify","sortKeysDeep","Array","isArray","map","record","sorted","key","Object","keys","sort","createPrivateKey","createPublicKey","generateKeyPairSync","sign","verify","generateSigningKeyPair","privateKey","publicKey","generateKeyPairSync","export","type","format","toString","signDocument","payload","privateKeyBase64","key","createPrivateKey","Buffer","from","signature","sign","canonicalStringify","verifyDocument","doc","publicKeyBase64","createPublicKey","verify","createHash","createPrivateKey","createPublicKey","sign","verify","buildRequestCanonical","input","bodyHash","createHash","update","body","digest","method","toUpperCase","path","orgId","String","timestamp","join","signRequestHeaders","privateKeyBase64","Math","floor","Date","now","canonical","key","createPrivateKey","Buffer","from","format","type","signature","sign","toString","verifySignedRequest","Number","isFinite","maxSkew","maxSkewSeconds","abs","rawBody","createPublicKey","publicKey","verify"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vritti/api-sdk",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.3",
|
|
5
5
|
"main": "./dist/index.cjs",
|
|
6
6
|
"module": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -126,6 +126,16 @@
|
|
|
126
126
|
"default": "./dist/license.cjs"
|
|
127
127
|
}
|
|
128
128
|
},
|
|
129
|
+
"./signing": {
|
|
130
|
+
"import": {
|
|
131
|
+
"types": "./dist/signing.d.ts",
|
|
132
|
+
"default": "./dist/signing.js"
|
|
133
|
+
},
|
|
134
|
+
"require": {
|
|
135
|
+
"types": "./dist/signing.d.cts",
|
|
136
|
+
"default": "./dist/signing.cjs"
|
|
137
|
+
}
|
|
138
|
+
},
|
|
129
139
|
"./cache": {
|
|
130
140
|
"import": {
|
|
131
141
|
"types": "./dist/cache.d.ts",
|