@pulse-compute/crypto 1.0.0-beta.1 → 1.0.0-beta.3

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
@@ -6,15 +6,16 @@
6
6
  > **Install directly:** Yes, when an application uses the crypto verification surface directly; JWT applications receive it transitively.<br>
7
7
  > **Supported entry points:** `@pulse-compute/crypto`<br>
8
8
  > **Stability:** The package root is the supported bounded verification contract; realization and Native integration subpaths remain toolchain-only.<br>
9
- > **Canonical documentation:** [Package guide](https://pulsecompute.io/v1.0.0-beta.1/packages/crypto/)
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.3/packages/crypto/)
10
11
  >
11
- > This release-status block is generated from the synchronized `Pulse 1.0.0-beta.1` package policy.
12
+ > This release-status block is generated from the synchronized `Pulse 1.0.0-beta.3` package policy.
12
13
  <!-- pulse-package-status:end -->
13
14
 
14
15
  Provider-neutral cryptographic verification contracts for Pulse.
15
16
 
16
17
  This package and `@pulse-compute/jwt` are members of the synchronized
17
- `1.0.0-beta.1` release catalog.
18
+ `1.0.0-beta.3` release catalog.
18
19
 
19
20
  ## Verification contract
20
21
 
@@ -125,3 +126,15 @@ The G3 composition and shared ES256 corpus are recorded in
125
126
  `wasm/.test-results/jwt-g3/jwt-g3-evidence.json`. This working-candidate proof
126
127
  does not add signing, remote key discovery, fallback, publication, or
127
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.
@@ -313,3 +313,32 @@ export function pulse_crypto_hs256_verify(
313
313
  __pulse_crypto_wipe(expected)
314
314
  return result
315
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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pulse-compute/crypto",
3
- "version": "1.0.0-beta.1",
3
+ "version": "1.0.0-beta.3",
4
4
  "license": "Apache-2.0",
5
5
  "engines": {
6
6
  "node": "^22.14.0 || ^24.0.0"
@@ -14,13 +14,15 @@
14
14
  "import": "./dist/index.js",
15
15
  "default": "./dist/index.js"
16
16
  },
17
- "./pulsewasm-native": "./pulsewasm.native.cjs"
17
+ "./pulsewasm-native": "./pulsewasm.native.cjs",
18
+ "./provider": "./src/provider.cjs"
18
19
  },
19
20
  "files": [
20
21
  "as",
21
22
  "dist",
22
23
  "pulsewasm.native.cjs",
23
24
  "README.md",
25
+ "src/provider.cjs",
24
26
  "LICENSE",
25
27
  "NOTICE"
26
28
  ],
@@ -35,7 +37,7 @@
35
37
  "bugs": {
36
38
  "url": "https://github.com/pulse-compute/pulse/issues"
37
39
  },
38
- "homepage": "https://pulsecompute.io/v1.0.0-beta.1/packages/crypto/",
40
+ "homepage": "https://pulsecompute.io/v1.0.0-beta.3/packages/crypto/",
39
41
  "main": "./dist/index.js",
40
42
  "types": "./dist/index.d.ts",
41
43
  "scripts": {
@@ -14,7 +14,8 @@ function sha256(value) {
14
14
  return crypto.createHash('sha256').update(value).digest('hex');
15
15
  }
16
16
 
17
- function pulseHmacAssemblyScriptSource() {
17
+ function pulseHmacAssemblyScriptSource(algorithm = 'HS256') {
18
+ if (!['HS256', 'SHA-256', 'HMAC-SHA256'].includes(algorithm)) throw new TypeError('Unsupported Crypto guest-source algorithm.');
18
19
  const source = fs.readFileSync(sourceFile, 'utf8');
19
20
  return Object.freeze({
20
21
  version: CRYPTO_GUEST_SOURCE_CONTRACT_VERSION,
@@ -25,7 +26,7 @@ function pulseHmacAssemblyScriptSource() {
25
26
  kind: 'guest-source',
26
27
  backend: 'pulse-hmac-as',
27
28
  backendVersion: 'pulse-hmac-as.v1',
28
- algorithm: 'HS256',
29
+ algorithm,
29
30
  language: 'assemblyscript',
30
31
  license: 'Apache-2.0',
31
32
  origin: 'package-source',
@@ -42,13 +43,20 @@ function pulseHmacAssemblyScriptSource() {
42
43
  imports: Object.freeze([]),
43
44
  exports: Object.freeze([
44
45
  'pulse_crypto_hs256_verify',
45
- 'pulse_crypto_sha256_digest'
46
+ 'pulse_crypto_sha256_digest',
47
+ 'pulse_crypto_bytes_frame_v1',
48
+ 'pulse_crypto_sha256_bytes_v1',
49
+ 'pulse_crypto_hmac_sha256_bytes_v1'
46
50
  ]),
47
51
  resourceLimits: Object.freeze({
48
52
  hmacKeyBytesMinimum: 32,
49
53
  hmacKeyBytesMaximum: 4 * 1024,
50
54
  macDataBytesMaximum: 1024 * 1024,
51
- hs256TagBytes: 32
55
+ hs256TagBytes: 32,
56
+ byteOperationKeyBytesMaximum: 8192,
57
+ byteOperationDataBytesMaximum: 32768,
58
+ byteOperationOutputBytes: 32,
59
+ byteOperationFrameBytes: 40992
52
60
  }),
53
61
  resultCodes: Object.freeze({
54
62
  valid: 1,
@@ -66,6 +74,39 @@ function pulseHmacAssemblyScriptSource() {
66
74
  });
67
75
  }
68
76
 
77
+ function bindNativeDigestMac(moduleExports) {
78
+ const memory = moduleExports && moduleExports.memory;
79
+ const names = ['pulse_crypto_bytes_frame_v1', 'pulse_crypto_sha256_bytes_v1', 'pulse_crypto_hmac_sha256_bytes_v1'];
80
+ if (!(memory instanceof WebAssembly.Memory) || names.some((name) => typeof moduleExports[name] !== 'function')) {
81
+ throw new TypeError('Native Crypto digest/MAC exports are unavailable.');
82
+ }
83
+ const frameBytes = 8192 + 32768 + 32;
84
+ function run(data, key) {
85
+ if (!(data instanceof Uint8Array) || data.length > 32768
86
+ || (key !== undefined && (!(key instanceof Uint8Array) || key.length > 8192))) {
87
+ throw new TypeError('Native Crypto digest/MAC input exceeds its byte contract.');
88
+ }
89
+ const pointer = moduleExports.pulse_crypto_bytes_frame_v1() >>> 0;
90
+ if (pointer + frameBytes > memory.buffer.byteLength) throw new TypeError('Native Crypto frame is outside memory.');
91
+ const dataPointer = pointer + 8192;
92
+ const outputPointer = dataPointer + 32768;
93
+ try {
94
+ const view = new Uint8Array(memory.buffer);
95
+ view.set(data, dataPointer);
96
+ if (key !== undefined) view.set(key, pointer);
97
+ const code = key === undefined
98
+ ? moduleExports.pulse_crypto_sha256_bytes_v1(dataPointer, data.length, outputPointer, 32)
99
+ : moduleExports.pulse_crypto_hmac_sha256_bytes_v1(pointer, key.length, dataPointer, data.length, outputPointer, 32);
100
+ if (code !== 1) throw new TypeError('Native Crypto digest/MAC realization failed.');
101
+ return new Uint8Array(memory.buffer, outputPointer, 32).slice();
102
+ } finally {
103
+ // The Wasm allocator can grow memory while hashing: acquire a fresh view.
104
+ new Uint8Array(memory.buffer, pointer, frameBytes).fill(0);
105
+ }
106
+ }
107
+ return Object.freeze({ sha256: (data) => run(data), hmacSha256: (key, data) => run(data, key) });
108
+ }
109
+
69
110
  function pulseEs256GuestUnit() {
70
111
  return Object.freeze({
71
112
  version: GUEST_UNIT_CONTRIBUTION_VERSION,
@@ -81,5 +122,6 @@ module.exports = Object.freeze({
81
122
  CRYPTO_GUEST_SOURCE_CONTRACT_VERSION,
82
123
  GUEST_UNIT_CONTRIBUTION_VERSION,
83
124
  pulseHmacAssemblyScriptSource,
125
+ bindNativeDigestMac,
84
126
  pulseEs256GuestUnit
85
127
  });
@@ -0,0 +1,43 @@
1
+ 'use strict';
2
+
3
+ // Trusted provider byte-output seam. JWT verification keeps its own key limits.
4
+ const IMPLEMENTATIONS = Object.freeze({
5
+ 'SHA-256': 'webcrypto.subtle.sha-256-bytes.v1',
6
+ 'HMAC-SHA256': 'webcrypto.subtle.hmac-sha-256-bytes.v1'
7
+ });
8
+ function snapshot(value, maximum) {
9
+ if (!(value instanceof Uint8Array) || value.byteLength > maximum) throw new TypeError('Crypto byte input is outside its bound.');
10
+ return new Uint8Array(value);
11
+ }
12
+ function result(value) {
13
+ if (!(value instanceof ArrayBuffer) || value.byteLength !== 32) throw new TypeError('Crypto byte output is invalid.');
14
+ return new Uint8Array(value);
15
+ }
16
+ function bindJavascriptDigestMac(algorithms, subtle = globalThis.crypto && globalThis.crypto.subtle) {
17
+ if (!Array.isArray(algorithms) || new Set(algorithms).size !== algorithms.length || algorithms.some((name) => !Object.hasOwn(IMPLEMENTATIONS, name))) throw new TypeError('Crypto requires explicit supported byte algorithms.');
18
+ if (!subtle || algorithms.includes('SHA-256') && typeof subtle.digest !== 'function'
19
+ || algorithms.includes('HMAC-SHA256') && (typeof subtle.sign !== 'function' || typeof subtle.importKey !== 'function')) throw new TypeError('Selected Web Crypto byte realization is unavailable.');
20
+ const bytes = {};
21
+ if (algorithms.includes('SHA-256')) bytes.sha256 = async (input) => {
22
+ const data = snapshot(input, 32768);
23
+ try { return result(await subtle.digest({ name: 'SHA-256' }, data)); }
24
+ finally { data.fill(0); }
25
+ };
26
+ if (algorithms.includes('HMAC-SHA256')) bytes.hmacSha256 = async (keyInput, input) => {
27
+ let key = snapshot(keyInput, 8192);
28
+ let data;
29
+ try {
30
+ data = snapshot(input, 32768);
31
+ // HMAC pads an empty key to a zero block (RFC 2104). Web Crypto rejects
32
+ // a zero-length import; the equivalent padded block retains Native parity.
33
+ if (key.length === 0) key = new Uint8Array(64);
34
+ const imported = await subtle.importKey('raw', key, { name: 'HMAC', hash: 'SHA-256', length: key.length * 8 }, false, ['sign']);
35
+ return result(await subtle.sign({ name: 'HMAC' }, imported, data));
36
+ } finally { key.fill(0); if (data) data.fill(0); }
37
+ };
38
+ return Object.freeze({ bytes: Object.freeze(bytes), realization: Object.freeze({
39
+ target: 'javascript', automaticFallback: false,
40
+ algorithms: Object.freeze(algorithms.map((algorithm) => Object.freeze({ algorithm, realization: 'runtime-builtin', implementation: IMPLEMENTATIONS[algorithm], available: true, automaticFallback: false })))
41
+ }) });
42
+ }
43
+ module.exports = Object.freeze({ IMPLEMENTATIONS, bindJavascriptDigestMac });