nervur 0.14.0 → 0.16.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 +2 -1
- package/NOTICE +5 -1
- package/README.md +23 -448
- package/index.js +17 -0
- package/nervur.js +5 -0
- package/package.json +32 -15
- package/GETTING-STARTED.md +0 -111
- package/SECURITY.md +0 -11
- package/admin.mjs +0 -83
- package/being.mjs +0 -639
- package/blueprint.mjs +0 -512
- package/client.mjs +0 -70
- package/contract.mjs +0 -98
- package/ground.mjs +0 -382
- package/index.mjs +0 -20
- package/modules.mjs +0 -55
- package/pointer.mjs +0 -79
- package/program.mjs +0 -119
- package/tools.mjs +0 -162
- package/voice.mjs +0 -107
package/tools.mjs
DELETED
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
import { ed25519 } from '@noble/curves/ed25519.js';
|
|
2
|
-
import { sha256 } from '@noble/hashes/sha2.js';
|
|
3
|
-
|
|
4
|
-
const HEX = '0123456789abcdef';
|
|
5
|
-
|
|
6
|
-
const utf8 = new TextEncoder();
|
|
7
|
-
const text8 = new TextDecoder();
|
|
8
|
-
|
|
9
|
-
// Buffer is Node's alone. Every byte string here is a Uint8Array, and these
|
|
10
|
-
// are the encodings the substrate names: hex on a digest, base64 on a
|
|
11
|
-
// signature, utf8 under canon.
|
|
12
|
-
const Bytes = Object.freeze({
|
|
13
|
-
base64: (bytes) => {
|
|
14
|
-
let binary = '';
|
|
15
|
-
for (const byte of bytes) {
|
|
16
|
-
binary += String.fromCharCode(byte);
|
|
17
|
-
}
|
|
18
|
-
return btoa(binary);
|
|
19
|
-
},
|
|
20
|
-
concat: (parts) => {
|
|
21
|
-
const whole = new Uint8Array(parts.reduce((run, one) => run + one.length, 0));
|
|
22
|
-
let at = 0;
|
|
23
|
-
for (const one of parts) {
|
|
24
|
-
whole.set(one, at);
|
|
25
|
-
at += one.length;
|
|
26
|
-
}
|
|
27
|
-
return whole;
|
|
28
|
-
},
|
|
29
|
-
equal: (one, other) => one.length === other.length && one.every((byte, at) => byte === other[at]),
|
|
30
|
-
hex: (bytes) => {
|
|
31
|
-
let out = '';
|
|
32
|
-
for (const byte of bytes) {
|
|
33
|
-
out += HEX[byte >> 4] + HEX[byte & 15];
|
|
34
|
-
}
|
|
35
|
-
return out;
|
|
36
|
-
},
|
|
37
|
-
of: (value) => utf8.encode(value),
|
|
38
|
-
read: (bytes) => text8.decode(bytes),
|
|
39
|
-
unbase64: (value) => {
|
|
40
|
-
const binary = atob(value);
|
|
41
|
-
const bytes = new Uint8Array(binary.length);
|
|
42
|
-
for (let at = 0; at < binary.length; at += 1) {
|
|
43
|
-
bytes[at] = binary.charCodeAt(at);
|
|
44
|
-
}
|
|
45
|
-
return bytes;
|
|
46
|
-
},
|
|
47
|
-
unhex: (value) => {
|
|
48
|
-
const bytes = new Uint8Array(value.length >> 1);
|
|
49
|
-
for (let at = 0; at < bytes.length; at += 1) {
|
|
50
|
-
bytes[at] = Number.parseInt(value.substr(at << 1, 2), 16);
|
|
51
|
-
}
|
|
52
|
-
return bytes;
|
|
53
|
-
},
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
const B32 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
|
|
57
|
-
|
|
58
|
-
const VERSION_G = 6 << 3;
|
|
59
|
-
|
|
60
|
-
const MEASURE = 8_388_608;
|
|
61
|
-
|
|
62
|
-
const SPAN = 60_000;
|
|
63
|
-
|
|
64
|
-
const ordered = (value) =>
|
|
65
|
-
value === null || typeof value !== 'object'
|
|
66
|
-
? value
|
|
67
|
-
: Array.isArray(value)
|
|
68
|
-
? value.map(ordered)
|
|
69
|
-
: Object.fromEntries(
|
|
70
|
-
Object.keys(value)
|
|
71
|
-
.sort()
|
|
72
|
-
.map((key) => [key, ordered(value[key])]),
|
|
73
|
-
);
|
|
74
|
-
|
|
75
|
-
const crc16 = (bytes) => {
|
|
76
|
-
let crc = 0;
|
|
77
|
-
for (const b of bytes) {
|
|
78
|
-
crc ^= b << 8;
|
|
79
|
-
for (let i = 0; i < 8; i += 1) {
|
|
80
|
-
crc = (crc & 0x8000 ? (crc << 1) ^ 0x1021 : crc << 1) & 0xffff;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
return crc;
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
const base32 = (bytes) => {
|
|
87
|
-
let bits = 0;
|
|
88
|
-
let value = 0;
|
|
89
|
-
let out = '';
|
|
90
|
-
for (const b of bytes) {
|
|
91
|
-
value = (value << 8) | b;
|
|
92
|
-
bits += 8;
|
|
93
|
-
while (bits >= 5) {
|
|
94
|
-
out += B32[(value >>> (bits - 5)) & 31];
|
|
95
|
-
bits -= 5;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
return out;
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
const unbase32 = (text) => {
|
|
102
|
-
let bits = 0;
|
|
103
|
-
let value = 0;
|
|
104
|
-
const out = [];
|
|
105
|
-
for (const c of text) {
|
|
106
|
-
const index = B32.indexOf(c);
|
|
107
|
-
if (index < 0) {
|
|
108
|
-
return null;
|
|
109
|
-
}
|
|
110
|
-
value = (value << 5) | index;
|
|
111
|
-
bits += 5;
|
|
112
|
-
if (bits >= 8) {
|
|
113
|
-
out.push((value >>> (bits - 8)) & 0xff);
|
|
114
|
-
bits -= 8;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
return Uint8Array.from(out);
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
const Tools = Object.freeze({
|
|
121
|
-
address: (pk) => {
|
|
122
|
-
const payload = Bytes.concat([Uint8Array.of(VERSION_G), pk]);
|
|
123
|
-
const crc = crc16(payload);
|
|
124
|
-
return base32(Bytes.concat([payload, Uint8Array.of(crc & 0xff, crc >> 8)]));
|
|
125
|
-
},
|
|
126
|
-
canon: (claim) => Bytes.of(JSON.stringify(ordered(claim))),
|
|
127
|
-
// The label arrives as text and the digest is over its utf8 bytes, which is
|
|
128
|
-
// the byte string the substrate has always hashed.
|
|
129
|
-
derive: (root, label) => sha256.create().update(root).update(Bytes.of(label)).digest(),
|
|
130
|
-
digest: (bytes) => Bytes.hex(sha256(bytes)),
|
|
131
|
-
envelope: async (voice, to, op, seq) => {
|
|
132
|
-
const from = Tools.address(voice.pk);
|
|
133
|
-
return { from, op, seq, sig: await voice.sign(Tools.canon({ from, op, seq, to })), to };
|
|
134
|
-
},
|
|
135
|
-
pkOf: (address) => {
|
|
136
|
-
const bytes = typeof address === 'string' && address.length === 56 ? unbase32(address) : null;
|
|
137
|
-
if (bytes === null || bytes[0] !== VERSION_G) {
|
|
138
|
-
return null;
|
|
139
|
-
}
|
|
140
|
-
const payload = bytes.subarray(0, 33);
|
|
141
|
-
const checksum = bytes[33] | (bytes[34] << 8);
|
|
142
|
-
return crc16(payload) === checksum ? payload.slice(1) : null;
|
|
143
|
-
},
|
|
144
|
-
secret: () => crypto.getRandomValues(new Uint8Array(32)),
|
|
145
|
-
verifies: (pk, bytes, sig) => {
|
|
146
|
-
try {
|
|
147
|
-
return ed25519.verify(Bytes.unbase64(sig), bytes, pk);
|
|
148
|
-
} catch {
|
|
149
|
-
return false;
|
|
150
|
-
}
|
|
151
|
-
},
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
const Stamp = () => {
|
|
155
|
-
let last = 0;
|
|
156
|
-
return () => {
|
|
157
|
-
last = Math.max(last + 1, Date.now());
|
|
158
|
-
return last;
|
|
159
|
-
};
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
export { Bytes, MEASURE, SPAN, Stamp, Tools };
|
package/voice.mjs
DELETED
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
import { ed25519, x25519 } from '@noble/curves/ed25519.js';
|
|
2
|
-
import { sha256 } from '@noble/hashes/sha2.js';
|
|
3
|
-
import { Bytes, Tools } from './tools.mjs';
|
|
4
|
-
|
|
5
|
-
// A private key travels as PKCS8 DER in base64 — the shape every stored
|
|
6
|
-
// bundle and every hand on the wire already carries — and a PKCS8 Ed25519 or
|
|
7
|
-
// X25519 key is one fixed prefix and the thirty-two seed bytes, so the bytes
|
|
8
|
-
// are the same whoever assembles them.
|
|
9
|
-
const ED_PKCS8 = Bytes.unhex('302e020100300506032b657004220420');
|
|
10
|
-
const X_PKCS8 = Bytes.unhex('302e020100300506032b656e04220420');
|
|
11
|
-
|
|
12
|
-
const seeded = () => crypto.getRandomValues(new Uint8Array(32));
|
|
13
|
-
|
|
14
|
-
const packed = (raw, prefix) => Bytes.base64(Bytes.concat([prefix, raw]));
|
|
15
|
-
|
|
16
|
-
const unpacked = (text, prefix) => {
|
|
17
|
-
const der = Bytes.unbase64(text);
|
|
18
|
-
if (der.length !== prefix.length + 32 || !Bytes.equal(der.subarray(0, prefix.length), prefix)) {
|
|
19
|
-
throw new Error('not a key of this curve');
|
|
20
|
-
}
|
|
21
|
-
return der.subarray(prefix.length);
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
const agreed = (box, rawPub) => sha256(x25519.getSharedSecret(box, rawPub));
|
|
25
|
-
|
|
26
|
-
const committed = /^[0-9a-f]{64}$/;
|
|
27
|
-
|
|
28
|
-
const voiced = ({ present, next, box, nextPkHash }) =>
|
|
29
|
-
Object.freeze({
|
|
30
|
-
boxPk: x25519.getPublicKey(box),
|
|
31
|
-
nextPkHash: next === undefined ? nextPkHash : Tools.digest(ed25519.getPublicKey(next)),
|
|
32
|
-
open: (wrapped, cipher) => {
|
|
33
|
-
try {
|
|
34
|
-
const [ephemeral, sealed] = wrapped.split('.');
|
|
35
|
-
return cipher.unseal(agreed(box, Bytes.unbase64(ephemeral)), sealed);
|
|
36
|
-
} catch {
|
|
37
|
-
return undefined;
|
|
38
|
-
}
|
|
39
|
-
},
|
|
40
|
-
hand: () => ({
|
|
41
|
-
box: packed(box, X_PKCS8),
|
|
42
|
-
nextPkHash: next === undefined ? nextPkHash : Tools.digest(ed25519.getPublicKey(next)),
|
|
43
|
-
present: packed(present, ED_PKCS8),
|
|
44
|
-
}),
|
|
45
|
-
pack: () =>
|
|
46
|
-
next === undefined
|
|
47
|
-
? { box: packed(box, X_PKCS8), nextPkHash, present: packed(present, ED_PKCS8) }
|
|
48
|
-
: {
|
|
49
|
-
box: packed(box, X_PKCS8),
|
|
50
|
-
next: packed(next, ED_PKCS8),
|
|
51
|
-
present: packed(present, ED_PKCS8),
|
|
52
|
-
},
|
|
53
|
-
pk: ed25519.getPublicKey(present),
|
|
54
|
-
sign: (bytes) => Bytes.base64(ed25519.sign(bytes, present)),
|
|
55
|
-
succeed: () =>
|
|
56
|
-
next === undefined
|
|
57
|
-
? undefined
|
|
58
|
-
: voiced({
|
|
59
|
-
box: seeded(),
|
|
60
|
-
next: seeded(),
|
|
61
|
-
present: next,
|
|
62
|
-
}),
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
const Voice = Object.freeze(
|
|
66
|
-
Object.assign(
|
|
67
|
-
(nextPkHash) => {
|
|
68
|
-
if (nextPkHash === undefined) {
|
|
69
|
-
return voiced({ box: seeded(), next: seeded(), present: seeded() });
|
|
70
|
-
}
|
|
71
|
-
return committed.test(nextPkHash)
|
|
72
|
-
? voiced({ box: seeded(), nextPkHash, present: seeded() })
|
|
73
|
-
: undefined;
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
revive: (bundle) => {
|
|
77
|
-
try {
|
|
78
|
-
if (bundle.next === undefined) {
|
|
79
|
-
return committed.test(bundle.nextPkHash)
|
|
80
|
-
? voiced({
|
|
81
|
-
box: unpacked(bundle.box, X_PKCS8),
|
|
82
|
-
nextPkHash: bundle.nextPkHash,
|
|
83
|
-
present: unpacked(bundle.present, ED_PKCS8),
|
|
84
|
-
})
|
|
85
|
-
: undefined;
|
|
86
|
-
}
|
|
87
|
-
return voiced({
|
|
88
|
-
box: unpacked(bundle.box, X_PKCS8),
|
|
89
|
-
next: unpacked(bundle.next, ED_PKCS8),
|
|
90
|
-
present: unpacked(bundle.present, ED_PKCS8),
|
|
91
|
-
});
|
|
92
|
-
} catch {
|
|
93
|
-
return undefined;
|
|
94
|
-
}
|
|
95
|
-
},
|
|
96
|
-
sealTo: (boxPk, value, cipher) => {
|
|
97
|
-
const ephemeral = seeded();
|
|
98
|
-
const shut = cipher?.seal(agreed(ephemeral, boxPk), value);
|
|
99
|
-
return shut === undefined
|
|
100
|
-
? undefined
|
|
101
|
-
: `${Bytes.base64(x25519.getPublicKey(ephemeral))}.${shut}`;
|
|
102
|
-
},
|
|
103
|
-
},
|
|
104
|
-
),
|
|
105
|
-
);
|
|
106
|
-
|
|
107
|
-
export { Voice };
|