@pulse-compute/crypto 0.0.0 → 1.0.0-beta.1
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 +125 -3
- package/as/pulse-hmac-as.ts +315 -0
- package/dist/contracts.d.ts +198 -0
- package/dist/contracts.d.ts.map +1 -0
- package/dist/contracts.js +149 -0
- package/dist/contracts.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +52 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/data.d.ts +10 -0
- package/dist/internal/data.d.ts.map +1 -0
- package/dist/internal/data.js +41 -0
- package/dist/internal/data.js.map +1 -0
- package/dist/internal/realization.d.ts +11 -0
- package/dist/internal/realization.d.ts.map +1 -0
- package/dist/internal/realization.js +140 -0
- package/dist/internal/realization.js.map +1 -0
- package/dist/internal/verification.d.ts +67 -0
- package/dist/internal/verification.d.ts.map +1 -0
- package/dist/internal/verification.js +273 -0
- package/dist/internal/verification.js.map +1 -0
- package/package.json +36 -6
- package/pulsewasm.native.cjs +85 -0
package/README.md
CHANGED
|
@@ -1,5 +1,127 @@
|
|
|
1
|
-
#
|
|
1
|
+
# `@pulse-compute/crypto`
|
|
2
2
|
|
|
3
|
-
|
|
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
|
+
> **Canonical documentation:** [Package guide](https://pulsecompute.io/v1.0.0-beta.1/packages/crypto/)
|
|
10
|
+
>
|
|
11
|
+
> This release-status block is generated from the synchronized `Pulse 1.0.0-beta.1` package policy.
|
|
12
|
+
<!-- pulse-package-status:end -->
|
|
4
13
|
|
|
5
|
-
|
|
14
|
+
Provider-neutral cryptographic verification contracts for Pulse.
|
|
15
|
+
|
|
16
|
+
This package and `@pulse-compute/jwt` are members of the synchronized
|
|
17
|
+
`1.0.0-beta.1` release catalog.
|
|
18
|
+
|
|
19
|
+
## Verification contract
|
|
20
|
+
|
|
21
|
+
This package owns the verification surface while target planning owns backend
|
|
22
|
+
selection. HS256 uses the MAC seam:
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import { crypto } from '@pulse-compute/crypto'
|
|
26
|
+
|
|
27
|
+
const result = await crypto.mac.verify({
|
|
28
|
+
algorithm: 'HS256',
|
|
29
|
+
key: {
|
|
30
|
+
type: 'hmac-key-bytes',
|
|
31
|
+
bytes: secretBytes,
|
|
32
|
+
},
|
|
33
|
+
data: messageBytes,
|
|
34
|
+
tag: authenticatorBytes,
|
|
35
|
+
})
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Keys, messages, and authenticators are byte inputs. The package never performs
|
|
39
|
+
ambient string encoding. HS256 keys must be between 32 bytes and 4 KiB, data is
|
|
40
|
+
bounded to 1 MiB, and authenticators are exactly 32 bytes.
|
|
41
|
+
|
|
42
|
+
Verification returns one frozen, status-only result:
|
|
43
|
+
|
|
44
|
+
- `valid`
|
|
45
|
+
- `invalid-authenticator`
|
|
46
|
+
- `invalid-key`
|
|
47
|
+
- `invalid-input`
|
|
48
|
+
- `realization-failure`
|
|
49
|
+
|
|
50
|
+
Backend objects, error text, key material, messages, and authenticators do not
|
|
51
|
+
cross the result boundary.
|
|
52
|
+
|
|
53
|
+
G3 also activates ES256 through the signature seam. Its normalized request
|
|
54
|
+
contains an exact 64-byte P-256 public key (`x || y`), the original signing
|
|
55
|
+
input bytes, and an exact 64-byte JOSE signature (`r || s`). JavaScript keeps
|
|
56
|
+
Web Crypto key objects inside the runtime-builtin adapter. Native uses the
|
|
57
|
+
audited `guest-linked:pulse-es256-rustcrypto-p256` unit. Both return the same
|
|
58
|
+
status-only result taxonomy, and neither retries another realization.
|
|
59
|
+
|
|
60
|
+
Pulse profiles can declare HS256 and ES256 globally or replace that declaration
|
|
61
|
+
in a named profile:
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
pulse: {
|
|
65
|
+
crypto: ['HS256', 'ES256'],
|
|
66
|
+
},
|
|
67
|
+
prod: {
|
|
68
|
+
crypto: {
|
|
69
|
+
HS256: { realization: 'guest-source:pulse-hmac-as' },
|
|
70
|
+
ES256: { realization: 'guest-linked:pulse-es256-rustcrypto-p256' },
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
An empty array or object explicitly declares no algorithms. Planning considers
|
|
76
|
+
only requirements emitted by reachable packages, resolves every declared
|
|
77
|
+
algorithm against the selected target, reports the exact realization, and
|
|
78
|
+
fails before lowering when the relationship
|
|
79
|
+
`reachable demand ⊆ profile declaration ⊆ target capability` is invalid.
|
|
80
|
+
Selection never enables detection or fallback.
|
|
81
|
+
|
|
82
|
+
JavaScript targets now execute the selected `runtime-builtin` realization
|
|
83
|
+
through Web Crypto with explicit HMAC/SHA-256 or ECDSA/P-256/SHA-256
|
|
84
|
+
parameters. ES256 validates the normalized point before constructing a
|
|
85
|
+
runtime-private public JWK, so malformed points cannot escape into a target's
|
|
86
|
+
Web Crypto implementation. Key import and verification stay inside the
|
|
87
|
+
byte-only public boundary; invalid authenticators, invalid keys, unavailable
|
|
88
|
+
runtimes, and operation failures return normalized status-only results.
|
|
89
|
+
|
|
90
|
+
The implementation imports no Node API and represents Node, browser, and
|
|
91
|
+
Fastly JavaScript uniformly as `runtime-builtin`. It is not a Fastly Native
|
|
92
|
+
host ABI.
|
|
93
|
+
|
|
94
|
+
The Native realization is `guest-source:pulse-hmac-as`: first-party
|
|
95
|
+
AssemblyScript compiled into the primary Native module. It implements bounded
|
|
96
|
+
SHA-256 and HMAC-SHA-256, scans every authenticator byte before branching, and
|
|
97
|
+
has no host imports, guest-link dependency, runtime probing, or fallback. Its
|
|
98
|
+
source provenance, hash, resource limits, exports, and disabled-fallback posture
|
|
99
|
+
are recorded in the Native manifest without recording key, data, or tag bytes.
|
|
100
|
+
|
|
101
|
+
The crypto seal uses one versioned corpus for the JavaScript Web Crypto and Native
|
|
102
|
+
AssemblyScript realizations. The corpus includes published RFC 4231 vectors,
|
|
103
|
+
authenticator mismatches at the first, middle, and last byte, altered inputs,
|
|
104
|
+
invalid key and tag shapes, and empty, maximum, and over-limit messages. The
|
|
105
|
+
same proof covers profile replacement without array or object merging,
|
|
106
|
+
unavailable exact realization pins, target capability failures, normalized
|
|
107
|
+
result categories, disabled fallback, and report redaction.
|
|
108
|
+
|
|
109
|
+
The Phase C seal records the locally observed artifact impact rather than treating
|
|
110
|
+
it as a portable benchmark. With the repository's pinned toolchain, the
|
|
111
|
+
default Native fixture grew from 2,212 to 5,385 bytes (3,173 bytes), and the
|
|
112
|
+
experimental size profile grew from 2,058 to 4,951 bytes (2,893 bytes). The
|
|
113
|
+
five emitted JavaScript runtime modules total 7,676 bytes, or 1,962 gzip bytes.
|
|
114
|
+
|
|
115
|
+
## JWT composition
|
|
116
|
+
|
|
117
|
+
`@pulse-compute/jwt` depends directly on this package. It uses `mac.verify` for
|
|
118
|
+
HS256 and `signature.verify` for ES256. JWT owns compact-JWS, bounded public
|
|
119
|
+
JWK/JWKS selection, and claims semantics; crypto owns fixed-width byte
|
|
120
|
+
normalization, backend realization, and normalized authenticity status.
|
|
121
|
+
Neither package duplicates the other's responsibility, and JWT has no active
|
|
122
|
+
`jose`, Web Crypto, guest-direct, or provider-direct crypto path.
|
|
123
|
+
|
|
124
|
+
The G3 composition and shared ES256 corpus are recorded in
|
|
125
|
+
`wasm/.test-results/jwt-g3/jwt-g3-evidence.json`. This working-candidate proof
|
|
126
|
+
does not add signing, remote key discovery, fallback, publication, or
|
|
127
|
+
deployment.
|
|
@@ -0,0 +1,315 @@
|
|
|
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
|
+
}
|
|
@@ -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"}
|