@openentity/sdk 0.4.1 → 0.5.1
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 +0 -3
- package/README.md +2 -0
- package/dist/{chunk-IWY5EQIP.js → chunk-BYNZITFU.js} +82 -1
- package/dist/index.d.ts +31 -0
- package/dist/index.js +11 -3
- package/dist/node.d.ts +3 -2
- package/dist/node.js +13 -9
- package/package.json +1 -1
package/LICENSE
CHANGED
|
@@ -19,6 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
19
19
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
20
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
21
|
SOFTWARE.
|
|
22
|
-
|
|
23
|
-
The specification (`spec/`) and the skill (`skill/`) are dedicated to the
|
|
24
|
-
public domain under CC0-1.0; see the LICENSE file in each directory.
|
package/README.md
CHANGED
|
@@ -10,4 +10,6 @@ Most agents want the CLI instead: `npx openentity@latest adopt`. Start at https:
|
|
|
10
10
|
|
|
11
11
|
Shared core: `adopt(kp, {name, client})`, `trust(eid)` (a `TrustReport` from registry reads only, every signature verified locally, cached 5 minutes, `ok: false` on any failure), the issuer helpers `signAttestation`, `signRevocation`, `buildDidDocument`, `buildOpenEntityJson`, and in Node `import { Identity } from "@openentity/sdk/node"` for `Identity.loadOrCreate()` (OS keychain, else `~/.openentity/identity.key` at 0600).
|
|
12
12
|
|
|
13
|
+
HTTP message signatures (RFC 9421, Web Bot Auth profile): `signRequest({method, url, body, privateKey, did})` returns `Signature-Input`, `Signature` and, for a non-empty body, `Content-Digest` (ed25519, `keyid` = the signer's did:key, covers `@method`, `@target-uri`, `content-digest`; `created`, `expires`, `nonce`). `verifyRequest({method, url, headers, body})` returns `{did, keyid, created, expires, nonce}` or throws `HttpSigError` with a `.code` (`missing_headers`, `unknown_alg`, `bad_keyid`, `digest_mismatch`, `created_in_future`, `created_too_old`, `expired`, `lifetime_too_long`, `missing_nonce`, `bad_signature`). Nonce replay tracking is yours. Shared vectors: `test-vectors/http-signature.json`.
|
|
14
|
+
|
|
13
15
|
MIT licensed. Source: https://github.com/moltbot-den/openentity
|
|
@@ -1946,6 +1946,83 @@ async function readTrust(eid, base, now, get, resolver) {
|
|
|
1946
1946
|
}
|
|
1947
1947
|
return report;
|
|
1948
1948
|
}
|
|
1949
|
+
var HttpSigError = class extends Error {
|
|
1950
|
+
code;
|
|
1951
|
+
constructor(code) {
|
|
1952
|
+
super(code);
|
|
1953
|
+
this.code = code;
|
|
1954
|
+
this.name = "HttpSigError";
|
|
1955
|
+
}
|
|
1956
|
+
};
|
|
1957
|
+
var HTTPSIG_SKEW = 5;
|
|
1958
|
+
var contentDigest = (body) => `sha-256=:${b64(sha256Sync(body))}:`;
|
|
1959
|
+
function httpSigBase(method, url, components, digest, params) {
|
|
1960
|
+
const value = { "@method": method.toUpperCase(), "@target-uri": url, "content-digest": digest };
|
|
1961
|
+
return utf8([...components.map((c) => `"${c}": ${value[c]}`), `"@signature-params": ${params}`].join("\n"));
|
|
1962
|
+
}
|
|
1963
|
+
async function signRequest(o) {
|
|
1964
|
+
const seed = o.privateKey instanceof Uint8Array ? o.privateKey : unb64url(o.privateKey.privateJwk.d);
|
|
1965
|
+
const created = o.created ?? Math.floor(Date.now() / 1e3);
|
|
1966
|
+
const nonce = o.nonce ?? b64url(crypto.getRandomValues(new Uint8Array(16)));
|
|
1967
|
+
const out = {};
|
|
1968
|
+
const comps = ["@method", "@target-uri"];
|
|
1969
|
+
if (o.body?.length) {
|
|
1970
|
+
out["Content-Digest"] = contentDigest(o.body);
|
|
1971
|
+
comps.push("content-digest");
|
|
1972
|
+
}
|
|
1973
|
+
const params = `(${comps.map((c) => `"${c}"`).join(" ")});created=${created};expires=${created + (o.expiresIn ?? 60)};nonce="${nonce}";keyid="${o.did}";alg="ed25519"`;
|
|
1974
|
+
const sig = ed25519.sign(httpSigBase(o.method, o.url, comps, out["Content-Digest"], params), seed);
|
|
1975
|
+
out["Signature-Input"] = `sig1=${params}`;
|
|
1976
|
+
out["Signature"] = `sig1=:${b64(sig)}:`;
|
|
1977
|
+
return out;
|
|
1978
|
+
}
|
|
1979
|
+
async function verifyRequest(o) {
|
|
1980
|
+
const fail = (code) => {
|
|
1981
|
+
throw new HttpSigError(code);
|
|
1982
|
+
};
|
|
1983
|
+
const h = Object.fromEntries(Object.entries(o.headers).map(([k, v]) => [k.toLowerCase(), v]));
|
|
1984
|
+
const maxAge = o.maxAge ?? 300;
|
|
1985
|
+
const body = o.body ?? new Uint8Array();
|
|
1986
|
+
if (!h["signature-input"] || !h["signature"]) fail("missing_headers");
|
|
1987
|
+
const m = /(?:^|,)\s*sig1=(\(([^)]*)\)((?:;[a-z]+=(?:\d+|"[^"]*"))*))/.exec(h["signature-input"]);
|
|
1988
|
+
const s = /(?:^|,)\s*sig1=:([A-Za-z0-9+/]+={0,2}):/.exec(h["signature"]);
|
|
1989
|
+
if (!m || !s) return fail("missing_headers");
|
|
1990
|
+
const params = m[1];
|
|
1991
|
+
const comps = [...m[2].matchAll(/"([^"]*)"/g)].map((x) => x[1]);
|
|
1992
|
+
const p = {};
|
|
1993
|
+
for (const [, k, v] of m[3].matchAll(/;([a-z]+)=(\d+|"[^"]*")/g)) p[k] = v.startsWith('"') ? v.slice(1, -1) : Number(v);
|
|
1994
|
+
if (p.alg !== "ed25519") fail("unknown_alg");
|
|
1995
|
+
const keyid = String(p.keyid ?? "");
|
|
1996
|
+
let pub;
|
|
1997
|
+
try {
|
|
1998
|
+
pub = publicKeyFromDidKey(keyid);
|
|
1999
|
+
if (didKeyFromPublicKey(pub) !== keyid) throw 0;
|
|
2000
|
+
} catch {
|
|
2001
|
+
return fail("bad_keyid");
|
|
2002
|
+
}
|
|
2003
|
+
if (typeof p.created !== "number" || typeof p.expires !== "number") fail("missing_headers");
|
|
2004
|
+
const created = p.created, expires = p.expires;
|
|
2005
|
+
const nonce = String(p.nonce ?? "");
|
|
2006
|
+
if (nonce.length < 22) fail("missing_nonce");
|
|
2007
|
+
if (comps[0] !== "@method" || comps[1] !== "@target-uri" || comps.length > 3 || comps.length === 3 && comps[2] !== "content-digest") fail("missing_headers");
|
|
2008
|
+
let digest;
|
|
2009
|
+
if (body.length || comps.includes("content-digest")) {
|
|
2010
|
+
digest = h["content-digest"];
|
|
2011
|
+
if (!comps.includes("content-digest") || digest !== contentDigest(body)) fail("digest_mismatch");
|
|
2012
|
+
}
|
|
2013
|
+
const now = o.now ?? Math.floor(Date.now() / 1e3);
|
|
2014
|
+
if (created > now + HTTPSIG_SKEW) fail("created_in_future");
|
|
2015
|
+
if (now - created > maxAge) fail("created_too_old");
|
|
2016
|
+
if (expires < now) fail("expired");
|
|
2017
|
+
if (expires - created > maxAge) fail("lifetime_too_long");
|
|
2018
|
+
let ok = false;
|
|
2019
|
+
try {
|
|
2020
|
+
ok = ed25519.verify(unb64(s[1]), httpSigBase(o.method, o.url, comps, digest, params), pub);
|
|
2021
|
+
} catch {
|
|
2022
|
+
}
|
|
2023
|
+
if (!ok) fail("bad_signature");
|
|
2024
|
+
return { did: keyid, keyid, created, expires, nonce };
|
|
2025
|
+
}
|
|
1949
2026
|
|
|
1950
2027
|
export {
|
|
1951
2028
|
SPEC,
|
|
@@ -2008,5 +2085,9 @@ export {
|
|
|
2008
2085
|
adopt,
|
|
2009
2086
|
clearTrustCache,
|
|
2010
2087
|
didWebResolver,
|
|
2011
|
-
trust
|
|
2088
|
+
trust,
|
|
2089
|
+
HttpSigError,
|
|
2090
|
+
httpSigBase,
|
|
2091
|
+
signRequest,
|
|
2092
|
+
verifyRequest
|
|
2012
2093
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -359,4 +359,35 @@ export declare function trust(eid: string, o?: {
|
|
|
359
359
|
fetchJson?: FetchJson;
|
|
360
360
|
resolver?: Resolver;
|
|
361
361
|
}): Promise<TrustReport>;
|
|
362
|
+
export declare class HttpSigError extends Error {
|
|
363
|
+
code: string;
|
|
364
|
+
constructor(code: string);
|
|
365
|
+
}
|
|
366
|
+
export declare function httpSigBase(method: string, url: string, components: string[], digest: string | undefined, params: string): Uint8Array;
|
|
367
|
+
/** Sign a request. `privateKey` is a KeyPair or a 32-byte Ed25519 seed. Returns Signature-Input, Signature and, for a non-empty body, Content-Digest. */
|
|
368
|
+
export declare function signRequest(o: {
|
|
369
|
+
method: string;
|
|
370
|
+
url: string;
|
|
371
|
+
body?: Uint8Array;
|
|
372
|
+
privateKey: KeyPair | Uint8Array;
|
|
373
|
+
did: string;
|
|
374
|
+
created?: number;
|
|
375
|
+
expiresIn?: number;
|
|
376
|
+
nonce?: string;
|
|
377
|
+
}): Promise<Record<string, string>>;
|
|
378
|
+
/** Verify a request signed by signRequest. Throws HttpSigError with `.code`. Nonce replay tracking is the caller's job. */
|
|
379
|
+
export declare function verifyRequest(o: {
|
|
380
|
+
method: string;
|
|
381
|
+
url: string;
|
|
382
|
+
headers: Record<string, string>;
|
|
383
|
+
body?: Uint8Array;
|
|
384
|
+
now?: number;
|
|
385
|
+
maxAge?: number;
|
|
386
|
+
}): Promise<{
|
|
387
|
+
did: string;
|
|
388
|
+
keyid: string;
|
|
389
|
+
created: number;
|
|
390
|
+
expires: number;
|
|
391
|
+
nonce: string;
|
|
392
|
+
}>;
|
|
362
393
|
export {};
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
DECLARATION_SHA256,
|
|
5
5
|
DECLARATION_TEXT,
|
|
6
6
|
DECLARATION_VERSION,
|
|
7
|
+
HttpSigError,
|
|
7
8
|
SPEC,
|
|
8
9
|
a2aExtension,
|
|
9
10
|
adopt,
|
|
@@ -27,6 +28,7 @@ import {
|
|
|
27
28
|
eip712Digest,
|
|
28
29
|
generateKeyPair,
|
|
29
30
|
hashData,
|
|
31
|
+
httpSigBase,
|
|
30
32
|
inclusionProof,
|
|
31
33
|
keyPairFromJwk,
|
|
32
34
|
keyPairFromSeed,
|
|
@@ -49,6 +51,7 @@ import {
|
|
|
49
51
|
signNote,
|
|
50
52
|
signObject,
|
|
51
53
|
signProof,
|
|
54
|
+
signRequest,
|
|
52
55
|
signRevocation,
|
|
53
56
|
successorDocument,
|
|
54
57
|
trust,
|
|
@@ -59,14 +62,16 @@ import {
|
|
|
59
62
|
verifyNote,
|
|
60
63
|
verifyObject,
|
|
61
64
|
verifyPresentation,
|
|
62
|
-
verifyProof
|
|
63
|
-
|
|
65
|
+
verifyProof,
|
|
66
|
+
verifyRequest
|
|
67
|
+
} from "./chunk-BYNZITFU.js";
|
|
64
68
|
export {
|
|
65
69
|
A2A_EXTENSION_URI,
|
|
66
70
|
CLIENTS,
|
|
67
71
|
DECLARATION_SHA256,
|
|
68
72
|
DECLARATION_TEXT,
|
|
69
73
|
DECLARATION_VERSION,
|
|
74
|
+
HttpSigError,
|
|
70
75
|
SPEC,
|
|
71
76
|
a2aExtension,
|
|
72
77
|
adopt,
|
|
@@ -90,6 +95,7 @@ export {
|
|
|
90
95
|
eip712Digest,
|
|
91
96
|
generateKeyPair,
|
|
92
97
|
hashData,
|
|
98
|
+
httpSigBase,
|
|
93
99
|
inclusionProof,
|
|
94
100
|
keyPairFromJwk,
|
|
95
101
|
keyPairFromSeed,
|
|
@@ -112,6 +118,7 @@ export {
|
|
|
112
118
|
signNote,
|
|
113
119
|
signObject,
|
|
114
120
|
signProof,
|
|
121
|
+
signRequest,
|
|
115
122
|
signRevocation,
|
|
116
123
|
successorDocument,
|
|
117
124
|
trust,
|
|
@@ -122,5 +129,6 @@ export {
|
|
|
122
129
|
verifyNote,
|
|
123
130
|
verifyObject,
|
|
124
131
|
verifyPresentation,
|
|
125
|
-
verifyProof
|
|
132
|
+
verifyProof,
|
|
133
|
+
verifyRequest
|
|
126
134
|
};
|
package/dist/node.d.ts
CHANGED
|
@@ -6,10 +6,11 @@ export interface Identity {
|
|
|
6
6
|
seed: Uint8Array;
|
|
7
7
|
source: "keychain" | "file";
|
|
8
8
|
}
|
|
9
|
-
/** The
|
|
10
|
-
|
|
9
|
+
/** The existing identity, or null. Never creates one. */
|
|
10
|
+
declare function load(): Promise<Identity | null>;
|
|
11
11
|
declare function loadOrCreate(): Promise<Identity>;
|
|
12
12
|
export declare const Identity: {
|
|
13
|
+
load: typeof load;
|
|
13
14
|
loadOrCreate: typeof loadOrCreate;
|
|
14
15
|
};
|
|
15
16
|
export {};
|
package/dist/node.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
keyPairFromSeed
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-BYNZITFU.js";
|
|
4
4
|
|
|
5
5
|
// src/node.ts
|
|
6
6
|
import { execFileSync } from "node:child_process";
|
|
@@ -22,15 +22,19 @@ var keychainSet = (seedHex) => !keychainOn() ? false : process.platform === "dar
|
|
|
22
22
|
var identityFile = () => join(process.env.OPENENTITY_HOME || join(homedir(), ".openentity"), "identity.key");
|
|
23
23
|
var unhex = (h) => Uint8Array.from(h.match(/../g), (x) => parseInt(x, 16));
|
|
24
24
|
var hex = (b) => Array.from(b, (x) => x.toString(16).padStart(2, "0")).join("");
|
|
25
|
-
async
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const file = identityFile();
|
|
25
|
+
var make = async (seedHex, source) => {
|
|
26
|
+
const seed = unhex(seedHex), kp = await keyPairFromSeed(seed);
|
|
27
|
+
return { kp, did: kp.did, seed, source };
|
|
28
|
+
};
|
|
29
|
+
async function load() {
|
|
31
30
|
const fromKeychain = keychainGet();
|
|
32
31
|
if (fromKeychain) return make(fromKeychain, "keychain");
|
|
33
|
-
|
|
32
|
+
return existsSync(identityFile()) ? make(readFileSync(identityFile(), "utf8").trim(), "file") : null;
|
|
33
|
+
}
|
|
34
|
+
async function loadOrCreate() {
|
|
35
|
+
const found = await load();
|
|
36
|
+
if (found) return found;
|
|
37
|
+
const file = identityFile();
|
|
34
38
|
const seedHex = hex(crypto.getRandomValues(new Uint8Array(32)));
|
|
35
39
|
if (keychainSet(seedHex)) return make(seedHex, "keychain");
|
|
36
40
|
mkdirSync(dirname(file), { recursive: true, mode: 448 });
|
|
@@ -45,7 +49,7 @@ async function loadOrCreate() {
|
|
|
45
49
|
chmodSync(file, 384);
|
|
46
50
|
return make(seedHex, "file");
|
|
47
51
|
}
|
|
48
|
-
var Identity = { loadOrCreate };
|
|
52
|
+
var Identity = { load, loadOrCreate };
|
|
49
53
|
export {
|
|
50
54
|
Identity,
|
|
51
55
|
identityFile
|