@dxos/keys 0.5.8 → 0.5.9-main.079a532
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/lib/browser/index.mjs +309 -9
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +294 -10
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/types/src/dxn.d.ts +38 -0
- package/dist/types/src/dxn.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +2 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/public-key.d.ts +3 -0
- package/dist/types/src/public-key.d.ts.map +1 -1
- package/dist/types/src/random-bytes.d.ts +2 -0
- package/dist/types/src/random-bytes.d.ts.map +1 -0
- package/dist/types/src/space-id.d.ts +16 -0
- package/dist/types/src/space-id.d.ts.map +1 -0
- package/dist/types/src/space-id.test.d.ts +2 -0
- package/dist/types/src/space-id.test.d.ts.map +1 -0
- package/dist/types/src/types.d.ts.map +1 -1
- package/package.json +8 -4
- package/src/dxn.ts +88 -0
- package/src/index.ts +2 -0
- package/src/public-key.test.ts +7 -0
- package/src/public-key.ts +18 -10
- package/src/random-bytes.ts +13 -0
- package/src/space-id.test.ts +17 -0
- package/src/space-id.ts +45 -0
- package/src/types.ts +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/keys",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.9-main.079a532",
|
|
4
4
|
"description": "Key utils and definitions.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -21,9 +21,13 @@
|
|
|
21
21
|
"src"
|
|
22
22
|
],
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@dxos/debug": "0.5.
|
|
25
|
-
"@dxos/invariant": "0.5.
|
|
26
|
-
"@dxos/node-std": "0.5.
|
|
24
|
+
"@dxos/debug": "0.5.9-main.079a532",
|
|
25
|
+
"@dxos/invariant": "0.5.9-main.079a532",
|
|
26
|
+
"@dxos/node-std": "0.5.9-main.079a532"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"base32-decode": "^1.0.0",
|
|
30
|
+
"base32-encode": "^2.0.0"
|
|
27
31
|
},
|
|
28
32
|
"publishConfig": {
|
|
29
33
|
"access": "public"
|
package/src/dxn.ts
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { invariant } from '@dxos/invariant';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* DXN unambiguously names a resource like an ECHO object, schema definition, plugin, etc.
|
|
9
|
+
* Each DXN starts with a dx prefix, followed by a resource kind.
|
|
10
|
+
* Colon Symbol : is used a delimiter between parts.
|
|
11
|
+
* DXNs may contain slashes.
|
|
12
|
+
* '@' in the place of the space id is used to denote that the DXN should be resolved in the local space.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
*
|
|
16
|
+
* ```
|
|
17
|
+
* dx:echo:<space key>:<echo id>
|
|
18
|
+
* dx:echo:BA25QRC2FEWCSAMRP4RZL65LWJ7352CKE:01J00J9B45YHYSGZQTQMSKMGJ6
|
|
19
|
+
* dx:echo:@:01J00J9B45YHYSGZQTQMSKMGJ6
|
|
20
|
+
* dx:type:dxos.org/type/Calendar
|
|
21
|
+
* dx:plugin:dxos.org/agent/plugin/functions
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export class DXN {
|
|
25
|
+
/**
|
|
26
|
+
* Kind constants.
|
|
27
|
+
*/
|
|
28
|
+
static kind = Object.freeze({
|
|
29
|
+
ECHO: 'echo',
|
|
30
|
+
TYPE: 'type',
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
static parse(dxn: string): DXN {
|
|
34
|
+
const [prefix, kind, ...parts] = dxn.split(':');
|
|
35
|
+
if (!(prefix === 'dxn')) {
|
|
36
|
+
throw new Error('Invalid DXN');
|
|
37
|
+
}
|
|
38
|
+
if (!(typeof kind === 'string' && kind.length > 0)) {
|
|
39
|
+
throw new Error('Invalid DXN');
|
|
40
|
+
}
|
|
41
|
+
if (!(parts.length > 0)) {
|
|
42
|
+
throw new Error('Invalid DXN');
|
|
43
|
+
}
|
|
44
|
+
return new DXN(kind, parts);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
#kind: string;
|
|
48
|
+
#parts: string[];
|
|
49
|
+
|
|
50
|
+
constructor(kind: string, parts: string[]) {
|
|
51
|
+
invariant(parts.length > 0);
|
|
52
|
+
invariant(parts.every((part) => typeof part === 'string' && part.length > 0 && part.indexOf(':') === -1));
|
|
53
|
+
|
|
54
|
+
// Per-type validation.
|
|
55
|
+
switch (kind) {
|
|
56
|
+
case DXN.kind.ECHO:
|
|
57
|
+
invariant(parts.length === 2);
|
|
58
|
+
break;
|
|
59
|
+
case DXN.kind.TYPE:
|
|
60
|
+
invariant(parts.length === 1);
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
this.#kind = kind;
|
|
65
|
+
this.#parts = parts;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
get kind() {
|
|
69
|
+
return this.#kind;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
get parts() {
|
|
73
|
+
return this.#parts;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
isTypeDXNOf(typename: string) {
|
|
77
|
+
return this.#kind === DXN.kind.TYPE && this.#parts.length === 1 && this.#parts[0] === typename;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
toString() {
|
|
81
|
+
return `dxn:${this.#kind}:${this.#parts.join(':')}`;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Tags for ECHO DXNs that should resolve the object ID in the local space.
|
|
87
|
+
*/
|
|
88
|
+
export const LOCAL_SPACE_TAG = '@';
|
package/src/index.ts
CHANGED
package/src/public-key.test.ts
CHANGED
|
@@ -60,4 +60,11 @@ describe('PublicKey', () => {
|
|
|
60
60
|
const key = PublicKey.random();
|
|
61
61
|
expect(PublicKey.equals(key, PublicKey.from(key.toHex()))).to.be.true;
|
|
62
62
|
});
|
|
63
|
+
|
|
64
|
+
test('base32', () => {
|
|
65
|
+
const key = PublicKey.randomOfLength(20); // Space keys will be cut to first 20 bytes of sha-256 hash.
|
|
66
|
+
const encoded = key.toMultibase32();
|
|
67
|
+
|
|
68
|
+
expect(PublicKey.fromMultibase32(encoded).toHex()).to.equal(key.toHex());
|
|
69
|
+
});
|
|
63
70
|
});
|
package/src/public-key.ts
CHANGED
|
@@ -2,11 +2,15 @@
|
|
|
2
2
|
// Copyright 2020 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import base32Decode from 'base32-decode';
|
|
6
|
+
import base32Encode from 'base32-encode';
|
|
5
7
|
import { inspect, type InspectOptionsStylized } from 'node:util';
|
|
6
8
|
|
|
7
9
|
import { truncateKey, devtoolsFormatter, type DevtoolsFormatter, equalsSymbol, type Equatable } from '@dxos/debug';
|
|
8
10
|
import { invariant } from '@dxos/invariant';
|
|
9
11
|
|
|
12
|
+
import { randomBytes } from './random-bytes';
|
|
13
|
+
|
|
10
14
|
export const PUBLIC_KEY_LENGTH = 32;
|
|
11
15
|
export const SECRET_KEY_LENGTH = 64;
|
|
12
16
|
|
|
@@ -89,6 +93,10 @@ export class PublicKey implements Equatable {
|
|
|
89
93
|
return PublicKey.from(randomBytes(PUBLIC_KEY_LENGTH));
|
|
90
94
|
}
|
|
91
95
|
|
|
96
|
+
static randomOfLength(length: number): PublicKey {
|
|
97
|
+
return PublicKey.from(randomBytes(length));
|
|
98
|
+
}
|
|
99
|
+
|
|
92
100
|
static *randomSequence(): Generator<PublicKey> {
|
|
93
101
|
for (let i = 0; i < 1_0000; i++) {
|
|
94
102
|
// Counter just to protect against infinite loops.
|
|
@@ -157,6 +165,12 @@ export class PublicKey implements Equatable {
|
|
|
157
165
|
return key.toHex();
|
|
158
166
|
}
|
|
159
167
|
|
|
168
|
+
static fromMultibase32(encoded: string): PublicKey {
|
|
169
|
+
invariant(encoded.startsWith('B'), 'Invalid multibase32 encoding');
|
|
170
|
+
|
|
171
|
+
return new PublicKey(new Uint8Array(base32Decode(encoded.slice(1), 'RFC4648')));
|
|
172
|
+
}
|
|
173
|
+
|
|
160
174
|
constructor(private readonly _value: Uint8Array) {
|
|
161
175
|
if (!(_value instanceof Uint8Array)) {
|
|
162
176
|
throw new TypeError(`Expected Uint8Array, got: ${_value}`);
|
|
@@ -183,6 +197,10 @@ export class PublicKey implements Equatable {
|
|
|
183
197
|
return this.asBuffer().toString('hex');
|
|
184
198
|
}
|
|
185
199
|
|
|
200
|
+
toMultibase32(): string {
|
|
201
|
+
return 'B' + base32Encode(this._value, 'RFC4648');
|
|
202
|
+
}
|
|
203
|
+
|
|
186
204
|
truncate(length = undefined) {
|
|
187
205
|
return truncateKey(this, length);
|
|
188
206
|
}
|
|
@@ -291,13 +309,3 @@ export class PublicKey implements Equatable {
|
|
|
291
309
|
return this.equals(other);
|
|
292
310
|
}
|
|
293
311
|
}
|
|
294
|
-
|
|
295
|
-
const randomBytes = (length: number) => {
|
|
296
|
-
// globalThis.crypto is not available in Node.js when running in vitest even though the documentation says it should be.
|
|
297
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
298
|
-
const webCrypto = globalThis.crypto ?? require('node:crypto').webcrypto;
|
|
299
|
-
|
|
300
|
-
const bytes = new Uint8Array(length);
|
|
301
|
-
webCrypto.getRandomValues(bytes);
|
|
302
|
-
return bytes;
|
|
303
|
-
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
export const randomBytes = (length: number) => {
|
|
6
|
+
// globalThis.crypto is not available in Node.js when running in vitest even though the documentation says it should be.
|
|
7
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
8
|
+
const webCrypto = globalThis.crypto ?? require('node:crypto').webcrypto;
|
|
9
|
+
|
|
10
|
+
const bytes = new Uint8Array(length);
|
|
11
|
+
webCrypto.getRandomValues(bytes);
|
|
12
|
+
return bytes;
|
|
13
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { expect, test } from 'vitest';
|
|
6
|
+
|
|
7
|
+
import { SpaceId } from './space-id';
|
|
8
|
+
|
|
9
|
+
test('space-id', () => {
|
|
10
|
+
const id = SpaceId.random();
|
|
11
|
+
|
|
12
|
+
expect(SpaceId.isValid(id)).toBe(true);
|
|
13
|
+
expect(id.length).toBe(33);
|
|
14
|
+
const decoded = SpaceId.decode(id);
|
|
15
|
+
expect(decoded.length).toBe(SpaceId.byteLength);
|
|
16
|
+
expect(SpaceId.encode(decoded)).toBe(id);
|
|
17
|
+
});
|
package/src/space-id.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import base32Decode from 'base32-decode';
|
|
6
|
+
import base32Encode from 'base32-encode';
|
|
7
|
+
|
|
8
|
+
import { invariant } from '@dxos/invariant';
|
|
9
|
+
|
|
10
|
+
import { randomBytes } from './random-bytes';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* A unique identifier for a space.
|
|
14
|
+
* Space keys are generated by creating a keypair, and then taking the first 20 bytes of the SHA-256 hash of the public key and encoding them to multibase RFC4648 base-32 format (prefixed with B, see Multibase Table).
|
|
15
|
+
* @example BA25QRC2FEWCSAMRP4RZL65LWJ7352CKE
|
|
16
|
+
*/
|
|
17
|
+
export type SpaceId = string & { __SpaceId: true };
|
|
18
|
+
|
|
19
|
+
export const SpaceId = Object.freeze({
|
|
20
|
+
byteLength: 20,
|
|
21
|
+
encode: (value: Uint8Array): SpaceId => {
|
|
22
|
+
invariant(value instanceof Uint8Array, 'Invalid type');
|
|
23
|
+
invariant(value.length === SpaceId.byteLength, 'Invalid length');
|
|
24
|
+
|
|
25
|
+
return (MULTIBASE_PREFIX + base32Encode(value, 'RFC4648')) as SpaceId;
|
|
26
|
+
},
|
|
27
|
+
decode: (value: SpaceId): Uint8Array => {
|
|
28
|
+
invariant(value.startsWith(MULTIBASE_PREFIX), 'Invalid multibase32 encoding');
|
|
29
|
+
|
|
30
|
+
return new Uint8Array(base32Decode(value.slice(1), 'RFC4648'));
|
|
31
|
+
},
|
|
32
|
+
isValid: (value: string): value is SpaceId => {
|
|
33
|
+
return typeof value === 'string' && value.startsWith(MULTIBASE_PREFIX) && value.length === ENCODED_LENGTH;
|
|
34
|
+
},
|
|
35
|
+
random: (): SpaceId => {
|
|
36
|
+
return SpaceId.encode(randomBytes(SpaceId.byteLength));
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Denotes RFC4648 base-32 format.
|
|
42
|
+
*/
|
|
43
|
+
const MULTIBASE_PREFIX = 'B';
|
|
44
|
+
|
|
45
|
+
const ENCODED_LENGTH = 33;
|