toilscript 0.1.16 → 0.1.17
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/cli.js +756 -17
- package/dist/cli.js.map +2 -2
- package/dist/importmap.json +2 -2
- package/dist/web.js +3 -3
- package/package.json +1 -1
- package/std/assembly/bindings/dom.ts +2 -9
- package/std/assembly/bindings/webcrypto.ts +106 -0
- package/std/assembly/crypto/algorithms.ts +316 -0
- package/std/assembly/crypto/key.ts +38 -0
- package/std/assembly/crypto/subtle.ts +151 -0
- package/std/assembly/crypto.ts +139 -4
package/dist/importmap.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"imports": {
|
|
3
|
-
"toilscript": "https://cdn.jsdelivr.net/npm/toilscript@0.1.
|
|
4
|
-
"toilscript/cli": "https://cdn.jsdelivr.net/npm/toilscript@0.1.
|
|
3
|
+
"toilscript": "https://cdn.jsdelivr.net/npm/toilscript@0.1.17/dist/toilscript.js",
|
|
4
|
+
"toilscript/cli": "https://cdn.jsdelivr.net/npm/toilscript@0.1.17/dist/cli.js",
|
|
5
5
|
"binaryen": "https://cdn.jsdelivr.net/npm/binaryen@129.0.0-nightly.20260428/index.js",
|
|
6
6
|
"long": "https://cdn.jsdelivr.net/npm/long@5.3.2/index.js"
|
|
7
7
|
}
|
package/dist/web.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
var ASSEMBLYSCRIPT_VERSION = "0.1.
|
|
1
|
+
var ASSEMBLYSCRIPT_VERSION = "0.1.17";
|
|
2
2
|
var ASSEMBLYSCRIPT_IMPORTMAP = {
|
|
3
3
|
"imports": {
|
|
4
|
-
"toilscript": "https://cdn.jsdelivr.net/npm/toilscript@0.1.
|
|
5
|
-
"toilscript/cli": "https://cdn.jsdelivr.net/npm/toilscript@0.1.
|
|
4
|
+
"toilscript": "https://cdn.jsdelivr.net/npm/toilscript@0.1.17/dist/toilscript.js",
|
|
5
|
+
"toilscript/cli": "https://cdn.jsdelivr.net/npm/toilscript@0.1.17/dist/cli.js",
|
|
6
6
|
"binaryen": "https://cdn.jsdelivr.net/npm/binaryen@129.0.0-nightly.20260428/index.js",
|
|
7
7
|
"long": "https://cdn.jsdelivr.net/npm/long@5.3.2/index.js"
|
|
8
8
|
}
|
package/package.json
CHANGED
|
@@ -280,12 +280,5 @@ export declare namespace performance {
|
|
|
280
280
|
export function now(): f64;
|
|
281
281
|
}
|
|
282
282
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
let values = getRandomValuesN(array.length);
|
|
286
|
-
array.set(values);
|
|
287
|
-
}
|
|
288
|
-
@external("env", "crypto.getRandomValuesN")
|
|
289
|
-
@external.js("let a = new Uint8Array(n); crypto.getRandomValues(a); return a;")
|
|
290
|
-
export declare function getRandomValuesN(n: u32): Uint8Array;
|
|
291
|
-
}
|
|
283
|
+
// crypto.getRandomValues moved to the dedicated Web Crypto module
|
|
284
|
+
// (std/assembly/crypto.ts), backed by the `crypto.fill_random` host import.
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// Host-import declarations for the Web Crypto surface. The production edge
|
|
2
|
+
// (toil-backend `src/wasm/host/import_functions/crypto`) and the toiljs dev
|
|
3
|
+
// server both provide these under the `env` namespace. All byte regions are
|
|
4
|
+
// passed as a (pointer, length) pair into guest linear memory.
|
|
5
|
+
//
|
|
6
|
+
// Variable-length results use a two-step pull: the op returns the result
|
|
7
|
+
// length (>= 0) or a negative error code; the guest then allocates a buffer of
|
|
8
|
+
// that length and calls `take_result` to copy the bytes out.
|
|
9
|
+
//
|
|
10
|
+
// This is the guest half of the ABI contract in `crypto/algorithms.ts`; the
|
|
11
|
+
// byte layout of the packed params buffer is defined there.
|
|
12
|
+
|
|
13
|
+
export namespace webcrypto {
|
|
14
|
+
// CSPRNG: fill outPtr..outPtr+len with random bytes.
|
|
15
|
+
// @ts-ignore: decorator
|
|
16
|
+
@external("env", "crypto.fill_random")
|
|
17
|
+
export declare function fillRandom(outPtr: usize, len: i32): void;
|
|
18
|
+
|
|
19
|
+
// Write 16 random bytes to outPtr (guest applies RFC 4122 v4 bits + format).
|
|
20
|
+
// @ts-ignore: decorator
|
|
21
|
+
@external("env", "crypto.random_uuid")
|
|
22
|
+
export declare function randomUuid(outPtr: usize): void;
|
|
23
|
+
|
|
24
|
+
// Copy the last stashed variable-length result into outPtr (outLen must equal
|
|
25
|
+
// the length the producing op returned). Returns bytes written.
|
|
26
|
+
// @ts-ignore: decorator
|
|
27
|
+
@external("env", "crypto.take_result")
|
|
28
|
+
export declare function takeResult(outPtr: usize, outLen: i32): i32;
|
|
29
|
+
|
|
30
|
+
// digest(alg, data) -> digest length (stashed) or negative error.
|
|
31
|
+
// @ts-ignore: decorator
|
|
32
|
+
@external("env", "crypto.digest")
|
|
33
|
+
export declare function digest(alg: i32, dataPtr: usize, dataLen: i32): i32;
|
|
34
|
+
|
|
35
|
+
// importKey -> opaque key handle (>= 0) or negative error.
|
|
36
|
+
// @ts-ignore: decorator
|
|
37
|
+
@external("env", "crypto.import_key")
|
|
38
|
+
export declare function importKey(
|
|
39
|
+
format: i32,
|
|
40
|
+
keyPtr: usize,
|
|
41
|
+
keyLen: i32,
|
|
42
|
+
paramsPtr: usize,
|
|
43
|
+
paramsLen: i32,
|
|
44
|
+
extractable: i32,
|
|
45
|
+
usages: i32
|
|
46
|
+
): i32;
|
|
47
|
+
|
|
48
|
+
// exportKey -> exported length (stashed) or negative error.
|
|
49
|
+
// @ts-ignore: decorator
|
|
50
|
+
@external("env", "crypto.export_key")
|
|
51
|
+
export declare function exportKey(format: i32, handle: i32): i32;
|
|
52
|
+
|
|
53
|
+
// encrypt / decrypt -> output length (stashed) or negative error.
|
|
54
|
+
// @ts-ignore: decorator
|
|
55
|
+
@external("env", "crypto.encrypt")
|
|
56
|
+
export declare function encrypt(
|
|
57
|
+
handle: i32,
|
|
58
|
+
paramsPtr: usize,
|
|
59
|
+
paramsLen: i32,
|
|
60
|
+
dataPtr: usize,
|
|
61
|
+
dataLen: i32
|
|
62
|
+
): i32;
|
|
63
|
+
// @ts-ignore: decorator
|
|
64
|
+
@external("env", "crypto.decrypt")
|
|
65
|
+
export declare function decrypt(
|
|
66
|
+
handle: i32,
|
|
67
|
+
paramsPtr: usize,
|
|
68
|
+
paramsLen: i32,
|
|
69
|
+
dataPtr: usize,
|
|
70
|
+
dataLen: i32
|
|
71
|
+
): i32;
|
|
72
|
+
|
|
73
|
+
// sign -> signature length (stashed) or negative error.
|
|
74
|
+
// @ts-ignore: decorator
|
|
75
|
+
@external("env", "crypto.sign")
|
|
76
|
+
export declare function sign(
|
|
77
|
+
handle: i32,
|
|
78
|
+
paramsPtr: usize,
|
|
79
|
+
paramsLen: i32,
|
|
80
|
+
dataPtr: usize,
|
|
81
|
+
dataLen: i32
|
|
82
|
+
): i32;
|
|
83
|
+
|
|
84
|
+
// verify -> 1 (valid), 0 (invalid), or negative error.
|
|
85
|
+
// @ts-ignore: decorator
|
|
86
|
+
@external("env", "crypto.verify")
|
|
87
|
+
export declare function verify(
|
|
88
|
+
handle: i32,
|
|
89
|
+
paramsPtr: usize,
|
|
90
|
+
paramsLen: i32,
|
|
91
|
+
sigPtr: usize,
|
|
92
|
+
sigLen: i32,
|
|
93
|
+
dataPtr: usize,
|
|
94
|
+
dataLen: i32
|
|
95
|
+
): i32;
|
|
96
|
+
|
|
97
|
+
// deriveBits -> derived length (stashed) or negative error.
|
|
98
|
+
// @ts-ignore: decorator
|
|
99
|
+
@external("env", "crypto.derive_bits")
|
|
100
|
+
export declare function deriveBits(
|
|
101
|
+
handle: i32,
|
|
102
|
+
paramsPtr: usize,
|
|
103
|
+
paramsLen: i32,
|
|
104
|
+
lengthBits: i32
|
|
105
|
+
): i32;
|
|
106
|
+
}
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
// The Web Crypto ABI contract: algorithm / format / curve ids, usage flags,
|
|
2
|
+
// error codes, and the packed-parameter classes. This is the single source of
|
|
3
|
+
// truth that the toil-backend Rust host and the toiljs dev mock mirror.
|
|
4
|
+
//
|
|
5
|
+
// Parameterized algorithms can't ride in plain scalars (AssemblyScript has no
|
|
6
|
+
// object literals across the host boundary), so each `*Params` class serializes
|
|
7
|
+
// itself into one little-endian buffer via `DataWriter` (whose `writeBytes`
|
|
8
|
+
// emits a u32 length prefix + bytes — the blob format the host reads).
|
|
9
|
+
|
|
10
|
+
import { DataWriter } from "data";
|
|
11
|
+
|
|
12
|
+
// --- Algorithm ids (also used as the inner-hash id for HMAC/ECDSA/KDF) ------
|
|
13
|
+
export const ALG_SHA_1: i32 = 1;
|
|
14
|
+
export const ALG_SHA_256: i32 = 2;
|
|
15
|
+
export const ALG_SHA_384: i32 = 3;
|
|
16
|
+
export const ALG_SHA_512: i32 = 4;
|
|
17
|
+
export const ALG_SHA3_256: i32 = 5;
|
|
18
|
+
export const ALG_SHA3_384: i32 = 6;
|
|
19
|
+
export const ALG_SHA3_512: i32 = 7;
|
|
20
|
+
export const ALG_AES_GCM: i32 = 10;
|
|
21
|
+
export const ALG_AES_CBC: i32 = 11;
|
|
22
|
+
export const ALG_AES_CTR: i32 = 12;
|
|
23
|
+
export const ALG_AES_KW: i32 = 13;
|
|
24
|
+
export const ALG_HMAC: i32 = 20;
|
|
25
|
+
export const ALG_ECDSA: i32 = 32;
|
|
26
|
+
export const ALG_ED25519: i32 = 33;
|
|
27
|
+
export const ALG_ECDH: i32 = 50;
|
|
28
|
+
export const ALG_X25519: i32 = 51;
|
|
29
|
+
export const ALG_HKDF: i32 = 52;
|
|
30
|
+
export const ALG_PBKDF2: i32 = 53;
|
|
31
|
+
|
|
32
|
+
// --- Key formats ------------------------------------------------------------
|
|
33
|
+
export const FMT_RAW: i32 = 0;
|
|
34
|
+
export const FMT_PKCS8: i32 = 1;
|
|
35
|
+
export const FMT_SPKI: i32 = 2;
|
|
36
|
+
export const FMT_JWK: i32 = 3; // not supported (throws guest-side)
|
|
37
|
+
|
|
38
|
+
// --- Named curves -----------------------------------------------------------
|
|
39
|
+
export const CURVE_P256: i32 = 1;
|
|
40
|
+
export const CURVE_P384: i32 = 2;
|
|
41
|
+
export const CURVE_P521: i32 = 3; // not supported
|
|
42
|
+
|
|
43
|
+
// --- Key-usage bit flags (OR together) --------------------------------------
|
|
44
|
+
export const USAGE_ENCRYPT: i32 = 0x01;
|
|
45
|
+
export const USAGE_DECRYPT: i32 = 0x02;
|
|
46
|
+
export const USAGE_SIGN: i32 = 0x04;
|
|
47
|
+
export const USAGE_VERIFY: i32 = 0x08;
|
|
48
|
+
export const USAGE_DERIVE_KEY: i32 = 0x10;
|
|
49
|
+
export const USAGE_DERIVE_BITS: i32 = 0x20;
|
|
50
|
+
export const USAGE_WRAP_KEY: i32 = 0x40;
|
|
51
|
+
export const USAGE_UNWRAP_KEY: i32 = 0x80;
|
|
52
|
+
|
|
53
|
+
function asciiUpper(s: string): string {
|
|
54
|
+
let out = "";
|
|
55
|
+
for (let i = 0; i < s.length; i++) {
|
|
56
|
+
let c = s.charCodeAt(i);
|
|
57
|
+
if (c >= 97 && c <= 122) c -= 32;
|
|
58
|
+
out += String.fromCharCode(c);
|
|
59
|
+
}
|
|
60
|
+
return out;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/// Map a Web Crypto algorithm name (case-insensitive) to its id, or 0.
|
|
64
|
+
export function algId(name: string): i32 {
|
|
65
|
+
let n = asciiUpper(name);
|
|
66
|
+
if (n == "SHA-1") return ALG_SHA_1;
|
|
67
|
+
if (n == "SHA-256") return ALG_SHA_256;
|
|
68
|
+
if (n == "SHA-384") return ALG_SHA_384;
|
|
69
|
+
if (n == "SHA-512") return ALG_SHA_512;
|
|
70
|
+
if (n == "SHA3-256") return ALG_SHA3_256;
|
|
71
|
+
if (n == "SHA3-384") return ALG_SHA3_384;
|
|
72
|
+
if (n == "SHA3-512") return ALG_SHA3_512;
|
|
73
|
+
if (n == "AES-GCM") return ALG_AES_GCM;
|
|
74
|
+
if (n == "AES-CBC") return ALG_AES_CBC;
|
|
75
|
+
if (n == "AES-CTR") return ALG_AES_CTR;
|
|
76
|
+
if (n == "AES-KW") return ALG_AES_KW;
|
|
77
|
+
if (n == "HMAC") return ALG_HMAC;
|
|
78
|
+
if (n == "ECDSA") return ALG_ECDSA;
|
|
79
|
+
if (n == "ED25519") return ALG_ED25519;
|
|
80
|
+
if (n == "ECDH") return ALG_ECDH;
|
|
81
|
+
if (n == "X25519") return ALG_X25519;
|
|
82
|
+
if (n == "HKDF") return ALG_HKDF;
|
|
83
|
+
if (n == "PBKDF2") return ALG_PBKDF2;
|
|
84
|
+
return 0;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/// Reverse of {@link algId}, for `CryptoKey.algorithmName`.
|
|
88
|
+
export function algName(id: i32): string {
|
|
89
|
+
if (id == ALG_SHA_1) return "SHA-1";
|
|
90
|
+
if (id == ALG_SHA_256) return "SHA-256";
|
|
91
|
+
if (id == ALG_SHA_384) return "SHA-384";
|
|
92
|
+
if (id == ALG_SHA_512) return "SHA-512";
|
|
93
|
+
if (id == ALG_AES_GCM) return "AES-GCM";
|
|
94
|
+
if (id == ALG_AES_CBC) return "AES-CBC";
|
|
95
|
+
if (id == ALG_AES_CTR) return "AES-CTR";
|
|
96
|
+
if (id == ALG_AES_KW) return "AES-KW";
|
|
97
|
+
if (id == ALG_HMAC) return "HMAC";
|
|
98
|
+
if (id == ALG_ECDSA) return "ECDSA";
|
|
99
|
+
if (id == ALG_ED25519) return "Ed25519";
|
|
100
|
+
if (id == ALG_ECDH) return "ECDH";
|
|
101
|
+
if (id == ALG_X25519) return "X25519";
|
|
102
|
+
if (id == ALG_HKDF) return "HKDF";
|
|
103
|
+
if (id == ALG_PBKDF2) return "PBKDF2";
|
|
104
|
+
return "unknown";
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/// Map a key-format name to its id, or -1.
|
|
108
|
+
export function formatId(name: string): i32 {
|
|
109
|
+
let n = asciiUpper(name);
|
|
110
|
+
if (n == "RAW") return FMT_RAW;
|
|
111
|
+
if (n == "PKCS8") return FMT_PKCS8;
|
|
112
|
+
if (n == "SPKI") return FMT_SPKI;
|
|
113
|
+
if (n == "JWK") return FMT_JWK;
|
|
114
|
+
return -1;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/// Map a named-curve string to its id, or 0.
|
|
118
|
+
export function curveId(name: string): i32 {
|
|
119
|
+
let n = asciiUpper(name);
|
|
120
|
+
if (n == "P-256") return CURVE_P256;
|
|
121
|
+
if (n == "P-384") return CURVE_P384;
|
|
122
|
+
if (n == "P-521") return CURVE_P521;
|
|
123
|
+
return 0;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/// Decode a negative host error code into a human-readable message.
|
|
127
|
+
export function cryptoError(code: i32): string {
|
|
128
|
+
if (code == -1) return "crypto: operation failed";
|
|
129
|
+
if (code == -2) return "crypto: invalid key handle";
|
|
130
|
+
if (code == -3) return "crypto: unsupported algorithm";
|
|
131
|
+
if (code == -4) return "crypto: invalid parameters";
|
|
132
|
+
if (code == -5) return "crypto: operation failed (bad tag, padding, or key)";
|
|
133
|
+
if (code == -6) return "crypto: key usage not permitted";
|
|
134
|
+
if (code == -7) return "crypto: key is not extractable";
|
|
135
|
+
return "crypto: error " + code.toString();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const EMPTY: Uint8Array = new Uint8Array(0);
|
|
139
|
+
|
|
140
|
+
// --- Parameter classes ------------------------------------------------------
|
|
141
|
+
|
|
142
|
+
/// Base for every algorithm-parameter object. Subclasses write a fixed
|
|
143
|
+
/// `(algId, hash)` header followed by their tail.
|
|
144
|
+
export abstract class AlgorithmParams {
|
|
145
|
+
abstract serialize(w: DataWriter): void;
|
|
146
|
+
/// Pack into a standalone little-endian buffer for the host.
|
|
147
|
+
pack(): Uint8Array {
|
|
148
|
+
let w = new DataWriter();
|
|
149
|
+
this.serialize(w);
|
|
150
|
+
return w.toBytes();
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export class AesGcmParams extends AlgorithmParams {
|
|
155
|
+
iv: Uint8Array;
|
|
156
|
+
additionalData: Uint8Array;
|
|
157
|
+
tagLength: i32;
|
|
158
|
+
constructor(iv: Uint8Array, additionalData: Uint8Array = EMPTY, tagLength: i32 = 128) {
|
|
159
|
+
super();
|
|
160
|
+
this.iv = iv;
|
|
161
|
+
this.additionalData = additionalData;
|
|
162
|
+
this.tagLength = tagLength;
|
|
163
|
+
}
|
|
164
|
+
serialize(w: DataWriter): void {
|
|
165
|
+
w.writeI32(ALG_AES_GCM);
|
|
166
|
+
w.writeI32(0);
|
|
167
|
+
w.writeBytes(this.iv);
|
|
168
|
+
w.writeI32(this.tagLength);
|
|
169
|
+
w.writeBytes(this.additionalData);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export class AesCbcParams extends AlgorithmParams {
|
|
174
|
+
iv: Uint8Array;
|
|
175
|
+
constructor(iv: Uint8Array) {
|
|
176
|
+
super();
|
|
177
|
+
this.iv = iv;
|
|
178
|
+
}
|
|
179
|
+
serialize(w: DataWriter): void {
|
|
180
|
+
w.writeI32(ALG_AES_CBC);
|
|
181
|
+
w.writeI32(0);
|
|
182
|
+
w.writeBytes(this.iv);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export class AesCtrParams extends AlgorithmParams {
|
|
187
|
+
counter: Uint8Array;
|
|
188
|
+
length: i32; // counter length in bits (must be 128)
|
|
189
|
+
constructor(counter: Uint8Array, length: i32 = 128) {
|
|
190
|
+
super();
|
|
191
|
+
this.counter = counter;
|
|
192
|
+
this.length = length;
|
|
193
|
+
}
|
|
194
|
+
serialize(w: DataWriter): void {
|
|
195
|
+
w.writeI32(ALG_AES_CTR);
|
|
196
|
+
w.writeI32(0);
|
|
197
|
+
w.writeBytes(this.counter);
|
|
198
|
+
w.writeI32(this.length);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/// HMAC key import: carries the inner hash id.
|
|
203
|
+
export class HmacImportParams extends AlgorithmParams {
|
|
204
|
+
hash: i32;
|
|
205
|
+
constructor(hash: i32) {
|
|
206
|
+
super();
|
|
207
|
+
this.hash = hash;
|
|
208
|
+
}
|
|
209
|
+
serialize(w: DataWriter): void {
|
|
210
|
+
w.writeI32(ALG_HMAC);
|
|
211
|
+
w.writeI32(this.hash);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/// HMAC sign/verify: the hash is taken from the key.
|
|
216
|
+
export class HmacParams extends AlgorithmParams {
|
|
217
|
+
serialize(w: DataWriter): void {
|
|
218
|
+
w.writeI32(ALG_HMAC);
|
|
219
|
+
w.writeI32(0);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export class Pbkdf2Params extends AlgorithmParams {
|
|
224
|
+
hash: i32;
|
|
225
|
+
salt: Uint8Array;
|
|
226
|
+
iterations: u32;
|
|
227
|
+
constructor(hash: i32, salt: Uint8Array, iterations: u32) {
|
|
228
|
+
super();
|
|
229
|
+
this.hash = hash;
|
|
230
|
+
this.salt = salt;
|
|
231
|
+
this.iterations = iterations;
|
|
232
|
+
}
|
|
233
|
+
serialize(w: DataWriter): void {
|
|
234
|
+
w.writeI32(ALG_PBKDF2);
|
|
235
|
+
w.writeI32(this.hash);
|
|
236
|
+
w.writeU32(this.iterations);
|
|
237
|
+
w.writeBytes(this.salt);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export class HkdfParams extends AlgorithmParams {
|
|
242
|
+
hash: i32;
|
|
243
|
+
salt: Uint8Array;
|
|
244
|
+
info: Uint8Array;
|
|
245
|
+
constructor(hash: i32, salt: Uint8Array, info: Uint8Array = EMPTY) {
|
|
246
|
+
super();
|
|
247
|
+
this.hash = hash;
|
|
248
|
+
this.salt = salt;
|
|
249
|
+
this.info = info;
|
|
250
|
+
}
|
|
251
|
+
serialize(w: DataWriter): void {
|
|
252
|
+
w.writeI32(ALG_HKDF);
|
|
253
|
+
w.writeI32(this.hash);
|
|
254
|
+
w.writeBytes(this.salt);
|
|
255
|
+
w.writeBytes(this.info);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/// ECDSA sign/verify: carries the hash (P-256↔SHA-256, P-384↔SHA-384).
|
|
260
|
+
export class EcdsaParams extends AlgorithmParams {
|
|
261
|
+
hash: i32;
|
|
262
|
+
constructor(hash: i32) {
|
|
263
|
+
super();
|
|
264
|
+
this.hash = hash;
|
|
265
|
+
}
|
|
266
|
+
serialize(w: DataWriter): void {
|
|
267
|
+
w.writeI32(ALG_ECDSA);
|
|
268
|
+
w.writeI32(this.hash);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/// EC key import (ECDSA or ECDH): carries the named curve.
|
|
273
|
+
export class EcKeyImportParams extends AlgorithmParams {
|
|
274
|
+
alg: i32; // ALG_ECDSA or ALG_ECDH
|
|
275
|
+
namedCurve: i32;
|
|
276
|
+
constructor(alg: i32, namedCurve: i32) {
|
|
277
|
+
super();
|
|
278
|
+
this.alg = alg;
|
|
279
|
+
this.namedCurve = namedCurve;
|
|
280
|
+
}
|
|
281
|
+
serialize(w: DataWriter): void {
|
|
282
|
+
w.writeI32(this.alg);
|
|
283
|
+
w.writeI32(0);
|
|
284
|
+
w.writeI32(this.namedCurve);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
export class Ed25519Params extends AlgorithmParams {
|
|
289
|
+
serialize(w: DataWriter): void {
|
|
290
|
+
w.writeI32(ALG_ED25519);
|
|
291
|
+
w.writeI32(0);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export class X25519ImportParams extends AlgorithmParams {
|
|
296
|
+
serialize(w: DataWriter): void {
|
|
297
|
+
w.writeI32(ALG_X25519);
|
|
298
|
+
w.writeI32(0);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/// ECDH / X25519 deriveBits: carries the peer public key handle.
|
|
303
|
+
export class EcdhParams extends AlgorithmParams {
|
|
304
|
+
alg: i32; // ALG_ECDH or ALG_X25519
|
|
305
|
+
publicKeyHandle: i32;
|
|
306
|
+
constructor(alg: i32, publicKeyHandle: i32) {
|
|
307
|
+
super();
|
|
308
|
+
this.alg = alg;
|
|
309
|
+
this.publicKeyHandle = publicKeyHandle;
|
|
310
|
+
}
|
|
311
|
+
serialize(w: DataWriter): void {
|
|
312
|
+
w.writeI32(this.alg);
|
|
313
|
+
w.writeI32(0);
|
|
314
|
+
w.writeI32(this.publicKeyHandle);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Opaque CryptoKey wrapping a per-request host keystore handle. Handles are
|
|
2
|
+
// only valid within the request that created them (the host keystore is reset
|
|
3
|
+
// between requests), so a CryptoKey must not be cached across dispatches.
|
|
4
|
+
|
|
5
|
+
import { algName } from "crypto/algorithms";
|
|
6
|
+
|
|
7
|
+
export class CryptoKey {
|
|
8
|
+
readonly handle: i32;
|
|
9
|
+
readonly type: string; // "secret" | "public" | "private"
|
|
10
|
+
readonly extractable: bool;
|
|
11
|
+
readonly algorithm: i32; // ALG_*
|
|
12
|
+
readonly usages: i32; // USAGE_* bitmask
|
|
13
|
+
|
|
14
|
+
constructor(handle: i32, type: string, extractable: bool, algorithm: i32, usages: i32) {
|
|
15
|
+
this.handle = handle;
|
|
16
|
+
this.type = type;
|
|
17
|
+
this.extractable = extractable;
|
|
18
|
+
this.algorithm = algorithm;
|
|
19
|
+
this.usages = usages;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
algorithmName(): string {
|
|
23
|
+
return algName(this.algorithm);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
hasUsage(u: i32): bool {
|
|
27
|
+
return (this.usages & u) != 0;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export class CryptoKeyPair {
|
|
32
|
+
readonly publicKey: CryptoKey;
|
|
33
|
+
readonly privateKey: CryptoKey;
|
|
34
|
+
constructor(publicKey: CryptoKey, privateKey: CryptoKey) {
|
|
35
|
+
this.publicKey = publicKey;
|
|
36
|
+
this.privateKey = privateKey;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
// Synchronous SubtleCrypto. Mirrors the Web Crypto method names but, because
|
|
2
|
+
// ToilScript has no Promises, every method returns its value directly rather
|
|
3
|
+
// than a Promise. Variable-length results use the two-step pull (op returns
|
|
4
|
+
// the length, then `take_result` copies the bytes out).
|
|
5
|
+
|
|
6
|
+
import { webcrypto } from "bindings/webcrypto";
|
|
7
|
+
import { CryptoKey } from "crypto/key";
|
|
8
|
+
import {
|
|
9
|
+
AlgorithmParams,
|
|
10
|
+
algId,
|
|
11
|
+
formatId,
|
|
12
|
+
cryptoError,
|
|
13
|
+
FMT_JWK,
|
|
14
|
+
FMT_PKCS8,
|
|
15
|
+
FMT_SPKI,
|
|
16
|
+
ALG_AES_GCM,
|
|
17
|
+
ALG_AES_CBC,
|
|
18
|
+
ALG_AES_CTR,
|
|
19
|
+
ALG_AES_KW,
|
|
20
|
+
ALG_HMAC,
|
|
21
|
+
ALG_PBKDF2,
|
|
22
|
+
ALG_HKDF,
|
|
23
|
+
} from "crypto/algorithms";
|
|
24
|
+
|
|
25
|
+
/// Pull a stashed variable-length result out of the host, or throw on error.
|
|
26
|
+
function drain(len: i32): Uint8Array {
|
|
27
|
+
if (len < 0) throw new Error(cryptoError(len));
|
|
28
|
+
let out = new Uint8Array(len);
|
|
29
|
+
if (len > 0) webcrypto.takeResult(out.dataStart, len);
|
|
30
|
+
return out;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function isSymmetricAlg(alg: i32): bool {
|
|
34
|
+
return (
|
|
35
|
+
alg == ALG_AES_GCM ||
|
|
36
|
+
alg == ALG_AES_CBC ||
|
|
37
|
+
alg == ALG_AES_CTR ||
|
|
38
|
+
alg == ALG_AES_KW ||
|
|
39
|
+
alg == ALG_HMAC ||
|
|
40
|
+
alg == ALG_PBKDF2 ||
|
|
41
|
+
alg == ALG_HKDF
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export class SubtleCrypto {
|
|
46
|
+
digest(algorithm: string, data: Uint8Array): Uint8Array {
|
|
47
|
+
let id = algId(algorithm);
|
|
48
|
+
if (id == 0) throw new Error("Unrecognized algorithm: " + algorithm);
|
|
49
|
+
return drain(webcrypto.digest(id, data.dataStart, data.byteLength));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
importKey(
|
|
53
|
+
format: string,
|
|
54
|
+
keyData: Uint8Array,
|
|
55
|
+
algorithm: AlgorithmParams,
|
|
56
|
+
extractable: bool,
|
|
57
|
+
usages: i32
|
|
58
|
+
): CryptoKey {
|
|
59
|
+
let fmt = formatId(format);
|
|
60
|
+
if (fmt < 0) throw new Error("Unknown key format: " + format);
|
|
61
|
+
if (fmt == FMT_JWK) throw new Error("jwk key format is not supported");
|
|
62
|
+
let p = algorithm.pack();
|
|
63
|
+
let alg = load<i32>(p.dataStart); // first packed field is the alg id
|
|
64
|
+
let handle = webcrypto.importKey(
|
|
65
|
+
fmt,
|
|
66
|
+
keyData.dataStart,
|
|
67
|
+
keyData.byteLength,
|
|
68
|
+
p.dataStart,
|
|
69
|
+
p.byteLength,
|
|
70
|
+
extractable ? 1 : 0,
|
|
71
|
+
usages
|
|
72
|
+
);
|
|
73
|
+
if (handle < 0) throw new Error(cryptoError(handle));
|
|
74
|
+
|
|
75
|
+
let type: string;
|
|
76
|
+
if (fmt == FMT_PKCS8) type = "private";
|
|
77
|
+
else if (fmt == FMT_SPKI) type = "public";
|
|
78
|
+
else type = isSymmetricAlg(alg) ? "secret" : "public";
|
|
79
|
+
|
|
80
|
+
return new CryptoKey(handle, type, extractable, alg, usages);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
exportKey(format: string, key: CryptoKey): Uint8Array {
|
|
84
|
+
let fmt = formatId(format);
|
|
85
|
+
if (fmt < 0) throw new Error("Unknown key format: " + format);
|
|
86
|
+
if (fmt == FMT_JWK) throw new Error("jwk key format is not supported");
|
|
87
|
+
return drain(webcrypto.exportKey(fmt, key.handle));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
encrypt(algorithm: AlgorithmParams, key: CryptoKey, data: Uint8Array): Uint8Array {
|
|
91
|
+
let p = algorithm.pack();
|
|
92
|
+
return drain(
|
|
93
|
+
webcrypto.encrypt(key.handle, p.dataStart, p.byteLength, data.dataStart, data.byteLength)
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
decrypt(algorithm: AlgorithmParams, key: CryptoKey, data: Uint8Array): Uint8Array {
|
|
98
|
+
let p = algorithm.pack();
|
|
99
|
+
return drain(
|
|
100
|
+
webcrypto.decrypt(key.handle, p.dataStart, p.byteLength, data.dataStart, data.byteLength)
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
sign(algorithm: AlgorithmParams, key: CryptoKey, data: Uint8Array): Uint8Array {
|
|
105
|
+
let p = algorithm.pack();
|
|
106
|
+
return drain(
|
|
107
|
+
webcrypto.sign(key.handle, p.dataStart, p.byteLength, data.dataStart, data.byteLength)
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
verify(
|
|
112
|
+
algorithm: AlgorithmParams,
|
|
113
|
+
key: CryptoKey,
|
|
114
|
+
signature: Uint8Array,
|
|
115
|
+
data: Uint8Array
|
|
116
|
+
): bool {
|
|
117
|
+
let p = algorithm.pack();
|
|
118
|
+
let r = webcrypto.verify(
|
|
119
|
+
key.handle,
|
|
120
|
+
p.dataStart,
|
|
121
|
+
p.byteLength,
|
|
122
|
+
signature.dataStart,
|
|
123
|
+
signature.byteLength,
|
|
124
|
+
data.dataStart,
|
|
125
|
+
data.byteLength
|
|
126
|
+
);
|
|
127
|
+
if (r < 0) throw new Error(cryptoError(r));
|
|
128
|
+
return r == 1;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/// Derive `length` bits from `baseKey` (PBKDF2/HKDF/ECDH/X25519).
|
|
132
|
+
deriveBits(algorithm: AlgorithmParams, baseKey: CryptoKey, length: i32): Uint8Array {
|
|
133
|
+
let p = algorithm.pack();
|
|
134
|
+
return drain(webcrypto.deriveBits(baseKey.handle, p.dataStart, p.byteLength, length));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/// Composed from deriveBits + importKey. Unlike the web API, the derived bit
|
|
138
|
+
/// length is passed explicitly (`lengthBits`) since ToilScript can't infer
|
|
139
|
+
/// it from `derivedKeyAlgorithm` the way the spec does.
|
|
140
|
+
deriveKey(
|
|
141
|
+
algorithm: AlgorithmParams,
|
|
142
|
+
baseKey: CryptoKey,
|
|
143
|
+
lengthBits: i32,
|
|
144
|
+
derivedKeyAlgorithm: AlgorithmParams,
|
|
145
|
+
extractable: bool,
|
|
146
|
+
usages: i32
|
|
147
|
+
): CryptoKey {
|
|
148
|
+
let bits = this.deriveBits(algorithm, baseKey, lengthBits);
|
|
149
|
+
return this.importKey("raw", bits, derivedKeyAlgorithm, extractable, usages);
|
|
150
|
+
}
|
|
151
|
+
}
|