@pulse-compute/crypto 0.0.0 → 1.0.0-beta.2

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/README.md CHANGED
@@ -1,5 +1,140 @@
1
- # @pulse-compute/crypto
1
+ # `@pulse-compute/crypto`
2
2
 
3
- > Bootstrap placeholder — no runtime or API.
3
+ <!-- pulse-package-status:start -->
4
+ > **Support tier:** Supported provider/extension surface<br>
5
+ > **Audience:** Applications and first-party packages using provider-neutral cryptographic verification.<br>
6
+ > **Install directly:** Yes, when an application uses the crypto verification surface directly; JWT applications receive it transitively.<br>
7
+ > **Supported entry points:** `@pulse-compute/crypto`<br>
8
+ > **Stability:** The package root is the supported bounded verification contract; realization and Native integration subpaths remain toolchain-only.<br>
9
+ > **npm:** [`@pulse-compute/crypto`](https://www.npmjs.com/package/@pulse-compute/crypto)<br>
10
+ > **Canonical documentation:** [Package guide](https://pulsecompute.io/v1.0.0-beta.2/packages/crypto/)
11
+ >
12
+ > This release-status block is generated from the synchronized `Pulse 1.0.0-beta.2` package policy.
13
+ <!-- pulse-package-status:end -->
4
14
 
5
- This inert 0.0.0 package reserves the npm name for future Pulse releases.
15
+ Provider-neutral cryptographic verification contracts for Pulse.
16
+
17
+ This package and `@pulse-compute/jwt` are members of the synchronized
18
+ `1.0.0-beta.2` release catalog.
19
+
20
+ ## Verification contract
21
+
22
+ This package owns the verification surface while target planning owns backend
23
+ selection. HS256 uses the MAC seam:
24
+
25
+ ```ts
26
+ import { crypto } from '@pulse-compute/crypto'
27
+
28
+ const result = await crypto.mac.verify({
29
+ algorithm: 'HS256',
30
+ key: {
31
+ type: 'hmac-key-bytes',
32
+ bytes: secretBytes,
33
+ },
34
+ data: messageBytes,
35
+ tag: authenticatorBytes,
36
+ })
37
+ ```
38
+
39
+ Keys, messages, and authenticators are byte inputs. The package never performs
40
+ ambient string encoding. HS256 keys must be between 32 bytes and 4 KiB, data is
41
+ bounded to 1 MiB, and authenticators are exactly 32 bytes.
42
+
43
+ Verification returns one frozen, status-only result:
44
+
45
+ - `valid`
46
+ - `invalid-authenticator`
47
+ - `invalid-key`
48
+ - `invalid-input`
49
+ - `realization-failure`
50
+
51
+ Backend objects, error text, key material, messages, and authenticators do not
52
+ cross the result boundary.
53
+
54
+ G3 also activates ES256 through the signature seam. Its normalized request
55
+ contains an exact 64-byte P-256 public key (`x || y`), the original signing
56
+ input bytes, and an exact 64-byte JOSE signature (`r || s`). JavaScript keeps
57
+ Web Crypto key objects inside the runtime-builtin adapter. Native uses the
58
+ audited `guest-linked:pulse-es256-rustcrypto-p256` unit. Both return the same
59
+ status-only result taxonomy, and neither retries another realization.
60
+
61
+ Pulse profiles can declare HS256 and ES256 globally or replace that declaration
62
+ in a named profile:
63
+
64
+ ```ts
65
+ pulse: {
66
+ crypto: ['HS256', 'ES256'],
67
+ },
68
+ prod: {
69
+ crypto: {
70
+ HS256: { realization: 'guest-source:pulse-hmac-as' },
71
+ ES256: { realization: 'guest-linked:pulse-es256-rustcrypto-p256' },
72
+ },
73
+ },
74
+ ```
75
+
76
+ An empty array or object explicitly declares no algorithms. Planning considers
77
+ only requirements emitted by reachable packages, resolves every declared
78
+ algorithm against the selected target, reports the exact realization, and
79
+ fails before lowering when the relationship
80
+ `reachable demand ⊆ profile declaration ⊆ target capability` is invalid.
81
+ Selection never enables detection or fallback.
82
+
83
+ JavaScript targets now execute the selected `runtime-builtin` realization
84
+ through Web Crypto with explicit HMAC/SHA-256 or ECDSA/P-256/SHA-256
85
+ parameters. ES256 validates the normalized point before constructing a
86
+ runtime-private public JWK, so malformed points cannot escape into a target's
87
+ Web Crypto implementation. Key import and verification stay inside the
88
+ byte-only public boundary; invalid authenticators, invalid keys, unavailable
89
+ runtimes, and operation failures return normalized status-only results.
90
+
91
+ The implementation imports no Node API and represents Node, browser, and
92
+ Fastly JavaScript uniformly as `runtime-builtin`. It is not a Fastly Native
93
+ host ABI.
94
+
95
+ The Native realization is `guest-source:pulse-hmac-as`: first-party
96
+ AssemblyScript compiled into the primary Native module. It implements bounded
97
+ SHA-256 and HMAC-SHA-256, scans every authenticator byte before branching, and
98
+ has no host imports, guest-link dependency, runtime probing, or fallback. Its
99
+ source provenance, hash, resource limits, exports, and disabled-fallback posture
100
+ are recorded in the Native manifest without recording key, data, or tag bytes.
101
+
102
+ The crypto seal uses one versioned corpus for the JavaScript Web Crypto and Native
103
+ AssemblyScript realizations. The corpus includes published RFC 4231 vectors,
104
+ authenticator mismatches at the first, middle, and last byte, altered inputs,
105
+ invalid key and tag shapes, and empty, maximum, and over-limit messages. The
106
+ same proof covers profile replacement without array or object merging,
107
+ unavailable exact realization pins, target capability failures, normalized
108
+ result categories, disabled fallback, and report redaction.
109
+
110
+ The Phase C seal records the locally observed artifact impact rather than treating
111
+ it as a portable benchmark. With the repository's pinned toolchain, the
112
+ default Native fixture grew from 2,212 to 5,385 bytes (3,173 bytes), and the
113
+ experimental size profile grew from 2,058 to 4,951 bytes (2,893 bytes). The
114
+ five emitted JavaScript runtime modules total 7,676 bytes, or 1,962 gzip bytes.
115
+
116
+ ## JWT composition
117
+
118
+ `@pulse-compute/jwt` depends directly on this package. It uses `mac.verify` for
119
+ HS256 and `signature.verify` for ES256. JWT owns compact-JWS, bounded public
120
+ JWK/JWKS selection, and claims semantics; crypto owns fixed-width byte
121
+ normalization, backend realization, and normalized authenticity status.
122
+ Neither package duplicates the other's responsibility, and JWT has no active
123
+ `jose`, Web Crypto, guest-direct, or provider-direct crypto path.
124
+
125
+ The G3 composition and shared ES256 corpus are recorded in
126
+ `wasm/.test-results/jwt-g3/jwt-g3-evidence.json`. This working-candidate proof
127
+ does not add signing, remote key discovery, fallback, publication, or
128
+ deployment.
129
+
130
+ ## Trusted byte-output composition
131
+
132
+ S3 uses Crypto-owned SHA-256 and HMAC-SHA256 byte outputs. Native composes the
133
+ existing guest-source unit; Node JavaScript explicitly binds Web Crypto through
134
+ `@pulse-compute/crypto/provider`. The trusted seam snapshots Uint8Array input,
135
+ allows at most 32768 data bytes and 8192 HMAC key bytes, returns exactly 32 bytes,
136
+ and wipes input staging on success or failure. It rejects unavailable selected
137
+ primitives and does not fall back. Empty HMAC keys use the equivalent padded
138
+ zero block required by HMAC, preserving Native semantics despite Web Crypto's
139
+ zero-length import restriction. This provider seam does not add an author-facing
140
+ signing API or change JWT key limits.
@@ -0,0 +1,344 @@
1
+ /*
2
+ * Pulse first-party HS256 guest-source realization.
3
+ *
4
+ * Algorithm sources:
5
+ * - NIST FIPS 180-4, Secure Hash Standard (SHA-256)
6
+ * - RFC 2104, HMAC
7
+ * - RFC 4231, HMAC-SHA test vectors
8
+ *
9
+ * This implementation is original Apache-2.0 Pulse source derived from the
10
+ * published algorithms. It has no host imports, ambient text encoding, or
11
+ * guest-link dependency.
12
+ */
13
+
14
+ const __PULSE_CRYPTO_VALID: i32 = 1
15
+ const __PULSE_CRYPTO_INVALID_AUTHENTICATOR: i32 = 0
16
+ const __PULSE_CRYPTO_INVALID_KEY: i32 = -1
17
+ const __PULSE_CRYPTO_INVALID_INPUT: i32 = -2
18
+
19
+ const __PULSE_CRYPTO_HS256_KEY_MIN: i32 = 32
20
+ const __PULSE_CRYPTO_HS256_KEY_MAX: i32 = 4096
21
+ const __PULSE_CRYPTO_HS256_DATA_MAX: i32 = 1024 * 1024
22
+ const __PULSE_CRYPTO_HS256_TAG_BYTES: i32 = 32
23
+ const __PULSE_CRYPTO_SHA256_BLOCK_BYTES: i32 = 64
24
+
25
+ const __PULSE_CRYPTO_SHA256_K = StaticArray.fromArray<u32>([
26
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
27
+ 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
28
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
29
+ 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
30
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
31
+ 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
32
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
33
+ 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
34
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
35
+ 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
36
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
37
+ 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
38
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
39
+ 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
40
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
41
+ 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
42
+ ])
43
+
44
+ @inline
45
+ function __pulse_crypto_rotr(value: u32, bits: i32): u32 {
46
+ return (value >>> bits) | (value << (32 - bits))
47
+ }
48
+
49
+ class __PulseCryptoSha256 {
50
+ private state: StaticArray<u32> = StaticArray.fromArray<u32>([
51
+ 0x6a09e667,
52
+ 0xbb67ae85,
53
+ 0x3c6ef372,
54
+ 0xa54ff53a,
55
+ 0x510e527f,
56
+ 0x9b05688c,
57
+ 0x1f83d9ab,
58
+ 0x5be0cd19,
59
+ ])
60
+ private block: StaticArray<u8> = new StaticArray<u8>(__PULSE_CRYPTO_SHA256_BLOCK_BYTES)
61
+ private schedule: StaticArray<u32> = new StaticArray<u32>(64)
62
+ private blockLength: i32 = 0
63
+ private totalBytes: u64 = 0
64
+
65
+ updateByte(value: u8): void {
66
+ unchecked(this.block[this.blockLength] = value)
67
+ this.blockLength += 1
68
+ this.totalBytes += 1
69
+ if (this.blockLength == __PULSE_CRYPTO_SHA256_BLOCK_BYTES) {
70
+ this.transform()
71
+ this.blockLength = 0
72
+ }
73
+ }
74
+
75
+ updateMemory(pointer: usize, length: i32): void {
76
+ for (let index: i32 = 0; index < length; index += 1) {
77
+ this.updateByte(load<u8>(pointer + <usize>index))
78
+ }
79
+ }
80
+
81
+ updateStatic(bytes: StaticArray<u8>, length: i32): void {
82
+ for (let index: i32 = 0; index < length; index += 1) {
83
+ this.updateByte(unchecked(bytes[index]))
84
+ }
85
+ }
86
+
87
+ finish(): StaticArray<u8> {
88
+ const bitLength = this.totalBytes * 8
89
+ this.updateByte(0x80)
90
+ while (this.blockLength != 56) this.updateByte(0)
91
+ for (let shift: i32 = 56; shift >= 0; shift -= 8) {
92
+ this.updateByte(<u8>(bitLength >> shift))
93
+ }
94
+
95
+ const output = new StaticArray<u8>(__PULSE_CRYPTO_HS256_TAG_BYTES)
96
+ for (let wordIndex: i32 = 0; wordIndex < 8; wordIndex += 1) {
97
+ const word = unchecked(this.state[wordIndex])
98
+ const offset = wordIndex * 4
99
+ unchecked(output[offset] = <u8>(word >> 24))
100
+ unchecked(output[offset + 1] = <u8>(word >> 16))
101
+ unchecked(output[offset + 2] = <u8>(word >> 8))
102
+ unchecked(output[offset + 3] = <u8>word)
103
+ }
104
+ for (let index: i32 = 0; index < this.state.length; index += 1) {
105
+ unchecked(this.state[index] = 0)
106
+ }
107
+ for (let index: i32 = 0; index < this.block.length; index += 1) {
108
+ unchecked(this.block[index] = 0)
109
+ }
110
+ for (let index: i32 = 0; index < this.schedule.length; index += 1) {
111
+ unchecked(this.schedule[index] = 0)
112
+ }
113
+ return output
114
+ }
115
+
116
+ private transform(): void {
117
+ const schedule = this.schedule
118
+ for (let index: i32 = 0; index < 16; index += 1) {
119
+ const offset = index * 4
120
+ unchecked(schedule[index] =
121
+ (<u32>unchecked(this.block[offset]) << 24)
122
+ | (<u32>unchecked(this.block[offset + 1]) << 16)
123
+ | (<u32>unchecked(this.block[offset + 2]) << 8)
124
+ | <u32>unchecked(this.block[offset + 3]))
125
+ }
126
+ for (let index: i32 = 16; index < 64; index += 1) {
127
+ const before15 = unchecked(schedule[index - 15])
128
+ const before2 = unchecked(schedule[index - 2])
129
+ const sigma0 = __pulse_crypto_rotr(before15, 7)
130
+ ^ __pulse_crypto_rotr(before15, 18)
131
+ ^ (before15 >>> 3)
132
+ const sigma1 = __pulse_crypto_rotr(before2, 17)
133
+ ^ __pulse_crypto_rotr(before2, 19)
134
+ ^ (before2 >>> 10)
135
+ unchecked(schedule[index] =
136
+ unchecked(schedule[index - 16])
137
+ + sigma0
138
+ + unchecked(schedule[index - 7])
139
+ + sigma1)
140
+ }
141
+
142
+ let a = unchecked(this.state[0])
143
+ let b = unchecked(this.state[1])
144
+ let c = unchecked(this.state[2])
145
+ let d = unchecked(this.state[3])
146
+ let e = unchecked(this.state[4])
147
+ let f = unchecked(this.state[5])
148
+ let g = unchecked(this.state[6])
149
+ let h = unchecked(this.state[7])
150
+
151
+ for (let index: i32 = 0; index < 64; index += 1) {
152
+ const sum1 = __pulse_crypto_rotr(e, 6)
153
+ ^ __pulse_crypto_rotr(e, 11)
154
+ ^ __pulse_crypto_rotr(e, 25)
155
+ const choose = (e & f) ^ ((~e) & g)
156
+ const temporary1 = h
157
+ + sum1
158
+ + choose
159
+ + unchecked(__PULSE_CRYPTO_SHA256_K[index])
160
+ + unchecked(schedule[index])
161
+ const sum0 = __pulse_crypto_rotr(a, 2)
162
+ ^ __pulse_crypto_rotr(a, 13)
163
+ ^ __pulse_crypto_rotr(a, 22)
164
+ const majority = (a & b) ^ (a & c) ^ (b & c)
165
+ const temporary2 = sum0 + majority
166
+
167
+ h = g
168
+ g = f
169
+ f = e
170
+ e = d + temporary1
171
+ d = c
172
+ c = b
173
+ b = a
174
+ a = temporary1 + temporary2
175
+ }
176
+
177
+ unchecked(this.state[0] += a)
178
+ unchecked(this.state[1] += b)
179
+ unchecked(this.state[2] += c)
180
+ unchecked(this.state[3] += d)
181
+ unchecked(this.state[4] += e)
182
+ unchecked(this.state[5] += f)
183
+ unchecked(this.state[6] += g)
184
+ unchecked(this.state[7] += h)
185
+ }
186
+ }
187
+
188
+ function __pulse_crypto_wipe(bytes: StaticArray<u8>): void {
189
+ for (let index: i32 = 0; index < bytes.length; index += 1) {
190
+ unchecked(bytes[index] = 0)
191
+ }
192
+ }
193
+
194
+ function __pulse_crypto_memory_range_valid(pointer: i32, length: i32): bool {
195
+ if (pointer < 0 || length < 0) return false
196
+ if (length == 0) return true
197
+ const start = <u64><u32>pointer
198
+ const byteLength = <u64>memory.size() << 16
199
+ const end = start + <u64><u32>length
200
+ return end >= start && end <= byteLength
201
+ }
202
+
203
+ function __pulse_crypto_sha256_memory(pointer: usize, length: i32): StaticArray<u8> {
204
+ const sha = new __PulseCryptoSha256()
205
+ sha.updateMemory(pointer, length)
206
+ return sha.finish()
207
+ }
208
+
209
+ function __pulse_crypto_hmac_sha256(
210
+ keyPointer: usize,
211
+ keyLength: i32,
212
+ dataPointer: usize,
213
+ dataLength: i32,
214
+ ): StaticArray<u8> {
215
+ const keyBlock = new StaticArray<u8>(__PULSE_CRYPTO_SHA256_BLOCK_BYTES)
216
+ if (keyLength > __PULSE_CRYPTO_SHA256_BLOCK_BYTES) {
217
+ const keyDigest = __pulse_crypto_sha256_memory(keyPointer, keyLength)
218
+ for (let index: i32 = 0; index < __PULSE_CRYPTO_HS256_TAG_BYTES; index += 1) {
219
+ unchecked(keyBlock[index] = unchecked(keyDigest[index]))
220
+ }
221
+ __pulse_crypto_wipe(keyDigest)
222
+ } else {
223
+ for (let index: i32 = 0; index < keyLength; index += 1) {
224
+ unchecked(keyBlock[index] = load<u8>(keyPointer + <usize>index))
225
+ }
226
+ }
227
+
228
+ const inner = new __PulseCryptoSha256()
229
+ for (let index: i32 = 0; index < __PULSE_CRYPTO_SHA256_BLOCK_BYTES; index += 1) {
230
+ inner.updateByte(unchecked(keyBlock[index]) ^ 0x36)
231
+ }
232
+ inner.updateMemory(dataPointer, dataLength)
233
+ const innerDigest = inner.finish()
234
+
235
+ const outer = new __PulseCryptoSha256()
236
+ for (let index: i32 = 0; index < __PULSE_CRYPTO_SHA256_BLOCK_BYTES; index += 1) {
237
+ outer.updateByte(unchecked(keyBlock[index]) ^ 0x5c)
238
+ }
239
+ outer.updateStatic(innerDigest, __PULSE_CRYPTO_HS256_TAG_BYTES)
240
+ const output = outer.finish()
241
+
242
+ __pulse_crypto_wipe(keyBlock)
243
+ __pulse_crypto_wipe(innerDigest)
244
+ return output
245
+ }
246
+
247
+ /*
248
+ * Proof-only SHA-256 export used to bind the embedded primitive to FIPS 180-4
249
+ * vectors. It is not a configured public Pulse algorithm surface.
250
+ */
251
+ export function pulse_crypto_sha256_digest(
252
+ dataPointer: i32,
253
+ dataLength: i32,
254
+ outputPointer: i32,
255
+ outputLength: i32,
256
+ ): i32 {
257
+ if (
258
+ dataLength < 0
259
+ || dataLength > __PULSE_CRYPTO_HS256_DATA_MAX
260
+ || outputLength != __PULSE_CRYPTO_HS256_TAG_BYTES
261
+ || !__pulse_crypto_memory_range_valid(dataPointer, dataLength)
262
+ || !__pulse_crypto_memory_range_valid(outputPointer, outputLength)
263
+ ) return __PULSE_CRYPTO_INVALID_INPUT
264
+
265
+ const digest = __pulse_crypto_sha256_memory(<usize><u32>dataPointer, dataLength)
266
+ for (let index: i32 = 0; index < __PULSE_CRYPTO_HS256_TAG_BYTES; index += 1) {
267
+ store<u8>(<usize><u32>outputPointer + <usize>index, unchecked(digest[index]))
268
+ }
269
+ __pulse_crypto_wipe(digest)
270
+ return __PULSE_CRYPTO_VALID
271
+ }
272
+
273
+ export function pulse_crypto_hs256_verify(
274
+ keyPointer: i32,
275
+ keyLength: i32,
276
+ dataPointer: i32,
277
+ dataLength: i32,
278
+ tagPointer: i32,
279
+ tagLength: i32,
280
+ ): i32 {
281
+ if (
282
+ keyLength < __PULSE_CRYPTO_HS256_KEY_MIN
283
+ || keyLength > __PULSE_CRYPTO_HS256_KEY_MAX
284
+ || !__pulse_crypto_memory_range_valid(keyPointer, keyLength)
285
+ ) return __PULSE_CRYPTO_INVALID_KEY
286
+
287
+ if (
288
+ dataLength < 0
289
+ || dataLength > __PULSE_CRYPTO_HS256_DATA_MAX
290
+ || tagLength != __PULSE_CRYPTO_HS256_TAG_BYTES
291
+ || !__pulse_crypto_memory_range_valid(dataPointer, dataLength)
292
+ || !__pulse_crypto_memory_range_valid(tagPointer, tagLength)
293
+ ) return __PULSE_CRYPTO_INVALID_INPUT
294
+
295
+ const expected = __pulse_crypto_hmac_sha256(
296
+ <usize><u32>keyPointer,
297
+ keyLength,
298
+ <usize><u32>dataPointer,
299
+ dataLength,
300
+ )
301
+
302
+ // Every authenticator byte participates before the result branch.
303
+ let mismatch: u32 = 0
304
+ for (let index: i32 = 0; index < __PULSE_CRYPTO_HS256_TAG_BYTES; index += 1) {
305
+ mismatch |= <u32>(
306
+ unchecked(expected[index])
307
+ ^ load<u8>(<usize><u32>tagPointer + <usize>index)
308
+ )
309
+ }
310
+ const result = mismatch == 0
311
+ ? __PULSE_CRYPTO_VALID
312
+ : __PULSE_CRYPTO_INVALID_AUTHENTICATOR
313
+ __pulse_crypto_wipe(expected)
314
+ return result
315
+ }
316
+
317
+ // Internal digest/MAC output contract. This frame is a rooted allocation owned
318
+ // by Crypto; host callers may stage synchronously, copy the output, then wipe.
319
+ // It does not grow Wasm memory per operation or overlap allocator-owned memory.
320
+ const __pulse_crypto_bytes_frame = new Uint8Array(8192 + 32768 + 32)
321
+ export function pulse_crypto_bytes_frame_v1(): i32 {
322
+ return <i32>__pulse_crypto_bytes_frame.dataStart
323
+ }
324
+
325
+ export function pulse_crypto_sha256_bytes_v1(dataPointer: i32, dataLength: i32, outputPointer: i32, outputLength: i32): i32 {
326
+ if (dataLength < 0 || dataLength > 32768 || outputLength != 32
327
+ || !__pulse_crypto_memory_range_valid(dataPointer, dataLength)
328
+ || !__pulse_crypto_memory_range_valid(outputPointer, outputLength)) return __PULSE_CRYPTO_INVALID_INPUT
329
+ const digest = __pulse_crypto_sha256_memory(<usize><u32>dataPointer, dataLength)
330
+ for (let i: i32 = 0; i < 32; i++) store<u8>(<usize><u32>outputPointer + <usize>i, unchecked(digest[i]))
331
+ __pulse_crypto_wipe(digest)
332
+ return __PULSE_CRYPTO_VALID
333
+ }
334
+
335
+ export function pulse_crypto_hmac_sha256_bytes_v1(keyPointer: i32, keyLength: i32, dataPointer: i32, dataLength: i32, outputPointer: i32, outputLength: i32): i32 {
336
+ if (keyLength < 0 || keyLength > 8192 || !__pulse_crypto_memory_range_valid(keyPointer, keyLength)) return __PULSE_CRYPTO_INVALID_KEY
337
+ if (dataLength < 0 || dataLength > 32768 || outputLength != 32
338
+ || !__pulse_crypto_memory_range_valid(dataPointer, dataLength)
339
+ || !__pulse_crypto_memory_range_valid(outputPointer, outputLength)) return __PULSE_CRYPTO_INVALID_INPUT
340
+ const digest = __pulse_crypto_hmac_sha256(<usize><u32>keyPointer, keyLength, <usize><u32>dataPointer, dataLength)
341
+ for (let i: i32 = 0; i < 32; i++) store<u8>(<usize><u32>outputPointer + <usize>i, unchecked(digest[i]))
342
+ __pulse_crypto_wipe(digest)
343
+ return __PULSE_CRYPTO_VALID
344
+ }
@@ -0,0 +1,198 @@
1
+ export declare const CRYPTO_CONTRACT_VERSION: "pulse.crypto.verification.v1";
2
+ export declare const CRYPTO_RUNTIME_BUILTIN_IMPLEMENTATION: "webcrypto.subtle.hmac-sha-256.v1";
3
+ export declare const CRYPTO_GUEST_SOURCE_IMPLEMENTATION: "pulse-hmac-as.v1";
4
+ export declare const CRYPTO_ES256_CONTRACT_VERSION: "pulse.crypto.es256-verification.v1";
5
+ export declare const CRYPTO_ES256_RUNTIME_BUILTIN_IMPLEMENTATION: "webcrypto.subtle.ecdsa-p256-sha-256.v1";
6
+ export declare const CRYPTO_ES256_GUEST_LINKED_REALIZATION: "guest-linked:pulse-es256-rustcrypto-p256";
7
+ export declare const CRYPTO_ES256_GUEST_LINKED_IMPLEMENTATION: "rustcrypto.p256-0.13.2.ecdsa-0.16.9.sha2-0.10.9.v1";
8
+ /**
9
+ * Executable algorithms remain intentionally narrower than frozen future
10
+ * contracts. G3, not G0, owns activation of ES256.
11
+ */
12
+ export declare const CRYPTO_ALGORITHMS: readonly ["HS256", "ES256"];
13
+ export type CryptoAlgorithm = typeof CRYPTO_ALGORITHMS[number];
14
+ export type MacAlgorithm = 'HS256';
15
+ export type SignatureAlgorithm = 'ES256';
16
+ export declare const CRYPTO_VERIFICATION_STATUSES: readonly ["valid", "invalid-authenticator", "invalid-key", "invalid-input", "realization-failure"];
17
+ export type CryptoVerificationStatus = typeof CRYPTO_VERIFICATION_STATUSES[number];
18
+ export type CryptoVerificationResult = {
19
+ readonly status: 'valid';
20
+ } | {
21
+ readonly status: 'invalid-authenticator';
22
+ } | {
23
+ readonly status: 'invalid-key';
24
+ } | {
25
+ readonly status: 'invalid-input';
26
+ } | {
27
+ readonly status: 'realization-failure';
28
+ };
29
+ /**
30
+ * Raw HMAC key material. The algorithm-specific discriminant prevents this
31
+ * descriptor from being accepted as a future asymmetric verification key.
32
+ */
33
+ export interface HmacKeyBytes {
34
+ readonly type: 'hmac-key-bytes';
35
+ readonly bytes: Uint8Array;
36
+ }
37
+ export type HmacVerificationKey = HmacKeyBytes;
38
+ export interface MacVerifyRequest {
39
+ readonly algorithm: MacAlgorithm;
40
+ readonly key: HmacVerificationKey;
41
+ readonly data: Uint8Array;
42
+ readonly tag: Uint8Array;
43
+ }
44
+ /**
45
+ * Package-private normalized P-256 public point. JWK, SEC1, SPKI, PEM, and
46
+ * certificate representations never cross the crypto realization boundary.
47
+ */
48
+ export interface P256PublicKeyBytes {
49
+ readonly type: 'p256-public-key-bytes';
50
+ /** Exactly 64 bytes: 32-byte big-endian x followed by 32-byte big-endian y. */
51
+ readonly bytes: Uint8Array;
52
+ }
53
+ export interface SignatureVerifyRequest {
54
+ readonly algorithm: SignatureAlgorithm;
55
+ readonly key: P256PublicKeyBytes;
56
+ /** Exact original signing bytes; never a reserialized JWT representation. */
57
+ readonly data: Uint8Array;
58
+ /** Exactly 64 bytes: 32-byte big-endian r followed by 32-byte big-endian s. */
59
+ readonly signature: Uint8Array;
60
+ }
61
+ /**
62
+ * Crypto-owned adapter input for the one JOSE representation accepted by the
63
+ * JWT composition. JWT has already validated the public JWK policy; crypto
64
+ * owns canonical coordinate/signature decoding into its fixed-width request.
65
+ */
66
+ export interface Es256JoseVerifyInput {
67
+ readonly key: Readonly<{
68
+ readonly x: string;
69
+ readonly y: string;
70
+ }>;
71
+ readonly data: Uint8Array;
72
+ readonly signature: string;
73
+ }
74
+ export interface P256PublicKeyCoordinates {
75
+ readonly x: string;
76
+ readonly y: string;
77
+ }
78
+ export declare const CRYPTO_RESOURCE_LIMITS: Readonly<{
79
+ readonly hmacKeyBytesMinimum: 32;
80
+ readonly hmacKeyBytesMaximum: number;
81
+ readonly macDataBytesMaximum: number;
82
+ readonly hs256TagBytes: 32;
83
+ readonly es256PublicKeyBytes: 64;
84
+ readonly es256SignatureBytes: 64;
85
+ readonly es256SigningInputBytesMaximum: 16340;
86
+ }>;
87
+ export declare const CRYPTO_ES256_CONTRACT: Readonly<{
88
+ readonly version: "pulse.crypto.es256-verification.v1";
89
+ readonly status: "executable-g3";
90
+ readonly semanticOwner: "@pulse-compute/crypto";
91
+ readonly algorithm: "ES256";
92
+ readonly primitive: Readonly<{
93
+ kind: "ecdsa";
94
+ curve: "P-256";
95
+ hash: "SHA-256";
96
+ }>;
97
+ readonly operation: "verify";
98
+ readonly request: Readonly<{
99
+ function: "crypto.signature.verify";
100
+ keyType: "p256-public-key-bytes";
101
+ publicKeyEncoding: "raw-x-y-big-endian";
102
+ publicKeyBytes: 64;
103
+ dataBytesMaximum: 16340;
104
+ signatureEncoding: "jose-r-s-big-endian";
105
+ signatureBytes: 64;
106
+ derAccepted: false;
107
+ }>;
108
+ readonly runtimeBuiltin: Readonly<{
109
+ realization: "runtime-builtin";
110
+ implementation: "webcrypto.subtle.ecdsa-p256-sha-256.v1";
111
+ eligibleTargets: readonly ["node-javascript", "fastly-javascript"];
112
+ }>;
113
+ readonly native: Readonly<{
114
+ realization: "guest-linked:pulse-es256-rustcrypto-p256";
115
+ implementation: "rustcrypto.p256-0.13.2.ecdsa-0.16.9.sha2-0.10.9.v1";
116
+ eligibleTargets: readonly ["node-native", "fastly-native"];
117
+ }>;
118
+ readonly guest: Readonly<{
119
+ unitVersion: "pulse.guest-unit.v2";
120
+ unitId: "pulse.crypto.es256.rustcrypto-p256.v1";
121
+ module: "pulse_crypto_es256";
122
+ owner: "@pulse-compute/crypto";
123
+ abi: "pulse.crypto.es256.verify.v1";
124
+ export: Readonly<{
125
+ name: "pulse_crypto_es256_verify";
126
+ parameters: readonly ["i32", "i32"];
127
+ results: readonly ["i32"];
128
+ }>;
129
+ origin: "package-prebuilt";
130
+ sourceBuildDuringApplicationBuild: false;
131
+ memory: Readonly<{
132
+ identity: "pulse.guest-memory.invocation-frame.v2";
133
+ import: "env.memory";
134
+ minimumPages: 32;
135
+ maximumPages: 32;
136
+ growable: false;
137
+ }>;
138
+ start: "forbidden";
139
+ hostCallbacks: false;
140
+ allocator: false;
141
+ }>;
142
+ readonly frame: Readonly<{
143
+ identity: "pulse.crypto.es256.invocation-frame.v2";
144
+ byteOrder: "little-endian";
145
+ pointerWidthBits: 32;
146
+ magic: 844318032;
147
+ version: 2;
148
+ headerBytes: 64;
149
+ capacityBytes: 16640;
150
+ alignmentBytes: 16;
151
+ signingInputBytesMaximum: 16340;
152
+ publicKeyBytes: 64;
153
+ signatureBytes: 64;
154
+ rawPayloadBytesMaximum: 16468;
155
+ reservedHeaderAndAlignmentBytes: 172;
156
+ canonicalOffsets: Readonly<{
157
+ publicKey: 64;
158
+ signature: 128;
159
+ signingInput: 192;
160
+ }>;
161
+ header: Readonly<{
162
+ magic: 0;
163
+ version: 4;
164
+ headerLength: 8;
165
+ totalLength: 12;
166
+ algorithm: 16;
167
+ flags: 20;
168
+ signingInputOffset: 24;
169
+ signingInputLength: 28;
170
+ publicKeyOffset: 32;
171
+ publicKeyLength: 36;
172
+ signatureOffset: 40;
173
+ signatureLength: 44;
174
+ reserved0: 48;
175
+ reserved1: 52;
176
+ reserved2: 56;
177
+ reserved3: 60;
178
+ }>;
179
+ algorithmCode: 1;
180
+ flags: 0;
181
+ reservedWordCount: 4;
182
+ reservedWordValue: 0;
183
+ callerOwned: true;
184
+ synchronous: true;
185
+ retainable: false;
186
+ clearCapacityAfterReturnOrTrap: true;
187
+ }>;
188
+ readonly resultStatuses: readonly ["valid", "invalid-authenticator", "invalid-key", "invalid-input", "realization-failure"];
189
+ readonly resultCodes: Readonly<{
190
+ valid: 1;
191
+ invalidAuthenticator: 0;
192
+ invalidKey: -1;
193
+ invalidInput: -2;
194
+ realizationFailure: -3;
195
+ }>;
196
+ readonly automaticFallback: false;
197
+ }>;
198
+ //# sourceMappingURL=contracts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contracts.d.ts","sourceRoot":"","sources":["../src/contracts.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,uBAAuB,EAAG,8BAAuC,CAAC;AAC/E,eAAO,MAAM,qCAAqC,EAChD,kCAA2C,CAAC;AAC9C,eAAO,MAAM,kCAAkC,EAC7C,kBAA2B,CAAC;AAC9B,eAAO,MAAM,6BAA6B,EACxC,oCAA6C,CAAC;AAChD,eAAO,MAAM,2CAA2C,EACtD,wCAAiD,CAAC;AACpD,eAAO,MAAM,qCAAqC,EAChD,0CAAmD,CAAC;AACtD,eAAO,MAAM,wCAAwC,EACnD,oDAA6D,CAAC;AAEhE;;;GAGG;AACH,eAAO,MAAM,iBAAiB,6BAGnB,CAAC;AAEZ,MAAM,MAAM,eAAe,GAAG,OAAO,iBAAiB,CAAC,MAAM,CAAC,CAAC;AAC/D,MAAM,MAAM,YAAY,GAAG,OAAO,CAAC;AACnC,MAAM,MAAM,kBAAkB,GAAG,OAAO,CAAC;AAEzC,eAAO,MAAM,4BAA4B,oGAM9B,CAAC;AAEZ,MAAM,MAAM,wBAAwB,GAClC,OAAO,4BAA4B,CAAC,MAAM,CAAC,CAAC;AAE9C,MAAM,MAAM,wBAAwB,GAChC;IAAE,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;CAAE,GAC5B;IAAE,QAAQ,CAAC,MAAM,EAAE,uBAAuB,CAAA;CAAE,GAC5C;IAAE,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAA;CAAE,GAClC;IAAE,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAA;CAAE,GACpC;IAAE,QAAQ,CAAC,MAAM,EAAE,qBAAqB,CAAA;CAAE,CAAC;AAE/C;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAChC,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;CAC5B;AAED,MAAM,MAAM,mBAAmB,GAAG,YAAY,CAAC;AAE/C,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,SAAS,EAAE,YAAY,CAAC;IACjC,QAAQ,CAAC,GAAG,EAAE,mBAAmB,CAAC;IAClC,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC;CAC1B;AAED;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,uBAAuB,CAAC;IACvC,+EAA+E;IAC/E,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;CAC5B;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,SAAS,EAAE,kBAAkB,CAAC;IACvC,QAAQ,CAAC,GAAG,EAAE,kBAAkB,CAAC;IACjC,6EAA6E;IAC7E,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,+EAA+E;IAC/E,QAAQ,CAAC,SAAS,EAAE,UAAU,CAAC;CAChC;AAED;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC;QACrB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;QACnB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC,CAAC;IACH,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,sBAAsB;;;;;;;;EAQxB,CAAC;AAEZ,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoHvB,CAAC"}