@burnt-labs/expo-satya-attest 3.0.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/PROVIDER_CONFIG.md +36 -12
- package/README.md +68 -19
- package/android/build.gradle +2 -2
- package/android/src/main/java/expo/modules/satyaattest/SatyaAttestModule.kt +104 -25
- package/android/src/main/java/expo/modules/satyaattest/SatyaCompositeReplay.kt +39 -0
- package/android/src/main/jniLibs/arm64-v8a/libsatya_ffi.so +0 -0
- package/android/src/main/jniLibs/armeabi-v7a/libsatya_ffi.so +0 -0
- package/android/src/main/jniLibs/x86_64/libsatya_ffi.so +0 -0
- package/build/SatyaAttestModule.d.ts +1 -1
- package/build/SatyaAttestModule.d.ts.map +1 -1
- package/build/SatyaAttestModule.js.map +1 -1
- package/build/SatyaAttestModule.web.d.ts +1 -1
- package/build/SatyaAttestModule.web.d.ts.map +1 -1
- package/build/SatyaAttestModule.web.js +1 -1
- package/build/SatyaAttestModule.web.js.map +1 -1
- package/build/SatyaMobileClient.d.ts +7 -1
- package/build/SatyaMobileClient.d.ts.map +1 -1
- package/build/SatyaMobileClient.js +210 -20
- package/build/SatyaMobileClient.js.map +1 -1
- package/build/artifactMetadata.js +3 -5
- package/build/artifactMetadata.js.map +1 -1
- package/build/components/providerAttest/compositeCapture.d.ts +9 -3
- package/build/components/providerAttest/compositeCapture.d.ts.map +1 -1
- package/build/components/providerAttest/compositeCapture.js +84 -6
- package/build/components/providerAttest/compositeCapture.js.map +1 -1
- package/build/components/providerAttest/useCompositeCaptureController.d.ts +1 -1
- package/build/components/providerAttest/useCompositeCaptureController.d.ts.map +1 -1
- package/build/components/providerAttest/useCompositeCaptureController.js +14 -2
- package/build/components/providerAttest/useCompositeCaptureController.js.map +1 -1
- package/build/components/providerAttest/useNativeReplayRunner.d.ts +1 -1
- package/build/components/providerAttest/useNativeReplayRunner.d.ts.map +1 -1
- package/build/components/providerAttest/useNativeReplayRunner.js +10 -6
- package/build/components/providerAttest/useNativeReplayRunner.js.map +1 -1
- package/build/nativeReplayArgs.d.ts +8 -0
- package/build/nativeReplayArgs.d.ts.map +1 -0
- package/build/nativeReplayArgs.js +14 -0
- package/build/nativeReplayArgs.js.map +1 -0
- package/build/satyaConfig.d.ts +4 -0
- package/build/satyaConfig.d.ts.map +1 -1
- package/build/satyaConfig.js +34 -1
- package/build/satyaConfig.js.map +1 -1
- package/build/sdkMetadata.generated.d.ts +1 -1
- package/build/sdkMetadata.generated.js +1 -1
- package/build/sdkMetadata.generated.js.map +1 -1
- package/build/types/mobile.types.d.ts +4 -0
- package/build/types/mobile.types.d.ts.map +1 -1
- package/build/types/mobile.types.js.map +1 -1
- package/ios/Frameworks/libsatya_ffi-rs.xcframework/Info.plist +5 -5
- package/ios/Frameworks/libsatya_ffi-rs.xcframework/ios-arm64/libsatya_ffi.a +0 -0
- package/ios/Frameworks/libsatya_ffi-rs.xcframework/ios-arm64_x86_64-simulator/libsatya_ffi.a +0 -0
- package/ios/SatyaAttest.podspec +1 -1
- package/ios/SatyaAttestModule.swift +109 -20
- package/ios/SatyaCompositeReplay.swift +43 -0
- package/package.json +2 -2
- package/src/SatyaAttestModule.ts +2 -1
- package/src/SatyaAttestModule.web.ts +2 -1
- package/src/SatyaMobileClient.ts +287 -28
- package/src/artifactMetadata.ts +3 -5
- package/src/components/providerAttest/compositeCapture.ts +111 -8
- package/src/components/providerAttest/useCompositeCaptureController.ts +18 -3
- package/src/components/providerAttest/useNativeReplayRunner.ts +17 -6
- package/src/nativeReplayArgs.ts +21 -0
- package/src/satyaConfig.ts +52 -1
- package/src/sdkMetadata.generated.ts +1 -1
- package/src/types/mobile.types.ts +4 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
## 3.1.0 - 2026-07-31
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- **Provider-selective partial endpoints (BUR-150).** Apps opt in exact providers
|
|
10
|
+
with `initSatyaAttest({ partialEndpointProviderIds: ['provider.id'] })`. Authenticated
|
|
11
|
+
readiness starts the bounded window when received; otherwise the first accepted capture does.
|
|
12
|
+
The SDK attempts the full plan; at the deadline it retains every confirmed
|
|
13
|
+
2xx endpoint and fails if none succeeded. Provider config and policy hashing
|
|
14
|
+
are unchanged.
|
|
15
|
+
- Schema `2.2.0` binds an explicit partial-completion mode and exact ordered endpoint subset
|
|
16
|
+
into the device-signed statement, semantic hash, aggregate exchange root, and a new
|
|
17
|
+
proof-challenge domain. The verifier validates the subset directly against the current full
|
|
18
|
+
registry proof plan, including policy hash, order, membership, and account-binding
|
|
19
|
+
compatibility. Schemas
|
|
20
|
+
`2.0.0` and strict-all app-bound `2.1.0` remain unchanged.
|
|
21
|
+
- The managed component and public low-level client always verify partial-endpoint artifacts
|
|
22
|
+
online because only the backend has the trusted full provider plan. Strict-all flows continue
|
|
23
|
+
to honor the component's `verifyBackend` setting. Native/local verification returns
|
|
24
|
+
`backend-required` for schema `2.2.0`. Verifier results expose completed and omitted endpoint
|
|
25
|
+
ids plus `planComplete`; relying parties that require the full plan must require
|
|
26
|
+
`planComplete: true`.
|
|
27
|
+
- A stable `no-endpoints-captured` outcome when no endpoint succeeds, plus
|
|
28
|
+
`unconfirmedEndpointIds` diagnostics.
|
|
29
|
+
|
|
3
30
|
## 3.0.0 - 2026-07-28
|
|
4
31
|
|
|
5
32
|
Multi-tenant app gating (BREAKING). Verification is now gated per app: every
|
package/PROVIDER_CONFIG.md
CHANGED
|
@@ -425,9 +425,9 @@ accepting an artifact.
|
|
|
425
425
|
## 8.1 `proofPlans[]` (optional — composite proofs)
|
|
426
426
|
|
|
427
427
|
A **proof plan** gathers claims from several governed endpoints in one authenticated
|
|
428
|
-
provider session as a single
|
|
429
|
-
by id (`proofPlanId`), never by URL or an arbitrary
|
|
430
|
-
plan replays is a pre-approved `endpoints[]` entry.
|
|
428
|
+
provider session as a single artifact (schema v2). The plan always declares the full
|
|
429
|
+
attempt set. The app selects a plan by id (`proofPlanId`), never by URL or an arbitrary
|
|
430
|
+
endpoint list — every endpoint the plan replays is a pre-approved `endpoints[]` entry.
|
|
431
431
|
|
|
432
432
|
```json
|
|
433
433
|
"proofPlans": [
|
|
@@ -443,10 +443,32 @@ plan replays is a pre-approved `endpoints[]` entry.
|
|
|
443
443
|
| Field | Req? | Type | Description |
|
|
444
444
|
|---|---|---|---|
|
|
445
445
|
| `id` | yes | `string` | Plan id. Same character rule as endpoint `id` (starts with a letter; letters/digits/`_`/`-`). Unique per provider. Selected via the `proofPlanId` prop, which is **mutually exclusive** with `endpointId`. |
|
|
446
|
-
| `endpointIds` | yes | `string[]` | Ordered, unique set of **1–8**
|
|
447
|
-
| `maxDurationMs` | yes | `int` | Composite window bound, integer in `[1000, 120000]`.
|
|
446
|
+
| `endpointIds` | yes | `string[]` | Ordered, unique set of **1–8** endpoint ids, each present in `endpoints[]`. Order is order-sensitive and participates in `policyHash`. Strict-all requires every endpoint. |
|
|
447
|
+
| `maxDurationMs` | yes | `int` | Composite window bound, integer in `[1000, 120000]`. For partial-enabled sessions, authenticated `__satyaReady` starts the window when received; otherwise the first accepted capture starts it. |
|
|
448
448
|
| `accountBinding` | yes | `object` | How the artifact proves the endpoints belong to one account. `shared-session` (all endpoints replayed under one proof-local cookie jar) or `claim-equality` (named claims must be equal across endpoints). |
|
|
449
449
|
|
|
450
|
+
**SDK partial completion (BUR-150) — no provider change**
|
|
451
|
+
|
|
452
|
+
Partial completion is enabled for exact provider ids with
|
|
453
|
+
`initSatyaAttest({ partialEndpointProviderIds: ['provider.id'] })`. Do not add
|
|
454
|
+
`completionPolicy`, endpoint permutations, minimum counts, or any other partial-completion
|
|
455
|
+
field to the provider. There is no corresponding Devtool, Provider Registry, or verifier
|
|
456
|
+
configuration. An empty SDK list leaves the provider strict-all.
|
|
457
|
+
|
|
458
|
+
The capture layer attempts every plan endpoint through `maxDurationMs`. If the complete plan
|
|
459
|
+
succeeds, native replay follows the existing strict-all path. Otherwise, when the window expires,
|
|
460
|
+
it passes every endpoint with a confirmed 2xx response to native replay and fails if none
|
|
461
|
+
succeeded. Native replay may further omit an availability, timeout, non-2xx, or missing-claim
|
|
462
|
+
failure while preserving fatal TLS/SPKI/origin/policy failures. Schema `2.2.0` signs the explicit
|
|
463
|
+
`partial-endpoint-subset` mode and binds the exact ordered successful endpoints through the
|
|
464
|
+
semantic hash, exchange root, and platform proof challenge.
|
|
465
|
+
|
|
466
|
+
The verifier validates the non-empty proper subset directly against the current full registry
|
|
467
|
+
proof plan. Unknown, duplicate, reordered, full-sized partial, and account-binding-breaking
|
|
468
|
+
subsets are rejected. Relying parties that require the complete plan must require
|
|
469
|
+
`planComplete: true`; `ok: true` alone means the presented subset is valid, not that every
|
|
470
|
+
configured endpoint completed.
|
|
471
|
+
|
|
450
472
|
**`accountBinding` modes**
|
|
451
473
|
|
|
452
474
|
```json
|
|
@@ -471,11 +493,11 @@ rejected after alias resolution.
|
|
|
471
493
|
|
|
472
494
|
**Policy hash.** The Provider Registry backend canonicalizes the complete plan object
|
|
473
495
|
into `policyHash`: the plan `id`, the **ordered** `endpointIds`, each referenced endpoint's
|
|
474
|
-
complete policy, `maxDurationMs`, and
|
|
475
|
-
|
|
496
|
+
complete policy, `maxDurationMs`, and the `accountBinding` rule. Partial completion adds no
|
|
497
|
+
provider field or policy-hash input. The byte/count/time caps are fixed by the schema-v2 compatibility
|
|
476
498
|
profile and are not part of the plan object. The verifier recomputes the policy hash, loads
|
|
477
499
|
the same plan independently, and rejects the whole artifact if the hash or any exchange,
|
|
478
|
-
binding rule, or aggregate invariant fails.
|
|
500
|
+
binding rule, subset constraint, or aggregate invariant fails.
|
|
479
501
|
|
|
480
502
|
> Composite proofs are a signed, bounded **sequential** observation, not an atomic snapshot
|
|
481
503
|
> of the provider's database. Per-exchange completion times remain visible to verifier
|
|
@@ -515,10 +537,10 @@ whether naturally observed or triggered. Enable it when a logged-out response co
|
|
|
515
537
|
match the endpoint rules.
|
|
516
538
|
|
|
517
539
|
The WebView readiness flag is document-scoped: injection must emit `__satyaReady` on each
|
|
518
|
-
authenticated document instead of persisting it across navigation.
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
540
|
+
authenticated document instead of persisting it across navigation. In a partial-enabled session,
|
|
541
|
+
the signal starts the bounded window; without it, the first accepted ungated endpoint capture does.
|
|
542
|
+
Expiry retains every confirmed 2xx endpoint or fails with `no-endpoints-captured` when none
|
|
543
|
+
succeeded (§8.1). Strict-all retains its first-capture window behavior.
|
|
522
544
|
|
|
523
545
|
**Provider concealment after login.** Once the SDK receives the `__satyaReady` login signal it
|
|
524
546
|
covers the provider WebView with a verification screen automatically: the user sees the login
|
|
@@ -598,6 +620,8 @@ console.log(body.match(/octolytics-actor-login" content="(.*?)"/)?.[1]);
|
|
|
598
620
|
- `capture` (when present) is a non-empty object whose only keys are `trigger`, `headers`,
|
|
599
621
|
`nativeReplayHeaders`, `requestBody`, `landingUrl`, `pollMs`, `maxAttempts`,
|
|
600
622
|
`requireLoginSignal`, with the bounds in §6,
|
|
623
|
+
- unsupported proof-plan fields such as `completionPolicy` are rejected; partial completion
|
|
624
|
+
is an SDK/verifier session option (§8.1),
|
|
601
625
|
- `injection` is a non-empty string / non-empty string array when present; `login.injection`
|
|
602
626
|
and endpoint `injection` are rejected,
|
|
603
627
|
- raw `allowedRequestHeaders`, `allowedResponseBodyJsonPaths`, and `privacyPolicy` are
|
package/README.md
CHANGED
|
@@ -80,7 +80,7 @@ New SDK artifacts also include envelope metadata:
|
|
|
80
80
|
"attestationIdVersion": "statement-sha256-96-v1",
|
|
81
81
|
"producer": {
|
|
82
82
|
"sdk": "@burnt-labs/expo-satya-attest",
|
|
83
|
-
"sdkVersion": "3.
|
|
83
|
+
"sdkVersion": "3.1.0",
|
|
84
84
|
"artifactSchemaVersion": "1.1.0",
|
|
85
85
|
"coreVersion": "0.1.0"
|
|
86
86
|
}
|
|
@@ -227,7 +227,7 @@ is a release tarball produced by `npm pack`, because it contains the same files
|
|
|
227
227
|
would publish:
|
|
228
228
|
|
|
229
229
|
```bash
|
|
230
|
-
npm install https://github.com/<owner>/<repo>/releases/download/expo-satya-attest-
|
|
230
|
+
npm install https://github.com/<owner>/<repo>/releases/download/expo-satya-attest-v3.1.0/burnt-labs-expo-satya-attest-3.1.0.tgz
|
|
231
231
|
npx expo install react-native-webview react-native-safe-area-context
|
|
232
232
|
```
|
|
233
233
|
|
|
@@ -235,8 +235,8 @@ Installing from a Git ref also works when that ref has the SDK package at the re
|
|
|
235
235
|
root, such as a dedicated SDK repo or a subtree/split branch:
|
|
236
236
|
|
|
237
237
|
```bash
|
|
238
|
-
npm install github:<owner>/<sdk-repo>#
|
|
239
|
-
npm install git+https://github.com/<owner>/<sdk-repo>.git#
|
|
238
|
+
npm install github:<owner>/<sdk-repo>#v3.1.0
|
|
239
|
+
npm install git+https://github.com/<owner>/<sdk-repo>.git#v3.1.0
|
|
240
240
|
npx expo install react-native-webview react-native-safe-area-context
|
|
241
241
|
```
|
|
242
242
|
|
|
@@ -491,19 +491,66 @@ effect, then render the hidden launcher:
|
|
|
491
491
|
## Composite Proofs
|
|
492
492
|
|
|
493
493
|
A **composite proof** attests claims from several governed endpoints gathered in **one**
|
|
494
|
-
authenticated provider session
|
|
495
|
-
`2.
|
|
494
|
+
authenticated provider session. Strict-all artifacts are schema `2.0.0` (legacy) or
|
|
495
|
+
app-bound `2.1.0`; signed partial-endpoint artifacts use `2.2.0`. Instead of a
|
|
496
|
+
single `endpointId`, the app passes a `proofPlanId` — a
|
|
496
497
|
registry-defined **proof plan** that selects an ordered set of **1–8** required endpoint
|
|
497
498
|
ids. The app never supplies URLs or an endpoint list; every endpoint the plan replays is a
|
|
498
499
|
pre-approved `endpoints[]` entry, and the plan is folded into the provider `policyHash`.
|
|
499
500
|
|
|
501
|
+
### Provider-selective partial endpoints
|
|
502
|
+
|
|
503
|
+
Some providers cannot guarantee that every proof-plan endpoint fires in every session. An app
|
|
504
|
+
may opt exact provider ids into partial completion without changing provider or proof-plan config:
|
|
505
|
+
|
|
506
|
+
```ts
|
|
507
|
+
initSatyaAttest({
|
|
508
|
+
partialEndpointProviderIds: ['provider.identity.v1'],
|
|
509
|
+
});
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
Matching is exact and case-sensitive; entries are never treated as patterns. An empty or
|
|
513
|
+
omitted list keeps every provider strict-all. There is no matching Provider Registry, Devtool,
|
|
514
|
+
or verifier setting. The SDK list is an app availability preference; the backend remains the
|
|
515
|
+
security authority.
|
|
516
|
+
|
|
517
|
+
An authenticated `__satyaReady` signal starts the capture window and releases
|
|
518
|
+
readiness-gated endpoints. Without it, the first accepted ungated capture starts the window.
|
|
519
|
+
The capture layer attempts the full plan until `maxDurationMs`. Full capture success stays on the
|
|
520
|
+
existing strict-all path. Otherwise, it sends the confirmed 2xx request subset to native replay,
|
|
521
|
+
which requires at least one successful exchange and may further omit availability, timeout,
|
|
522
|
+
non-2xx, or missing-claim failures. TLS trust, SPKI, origin, request-policy, and binding failures
|
|
523
|
+
remain fatal.
|
|
524
|
+
|
|
525
|
+
- the signed set must be a non-empty proper subset in proof-plan order;
|
|
526
|
+
- every claim-equality account-binding endpoint must be present in the final subset;
|
|
527
|
+
- schema `2.2.0` signs `completionMode: "partial-endpoint-subset"`; the semantic hash,
|
|
528
|
+
exchange root, and domain-separated proof challenge bind the exact ordered endpoint IDs;
|
|
529
|
+
- schemas `2.0.0` and `2.1.0` have no completion mode and remain strict-all;
|
|
530
|
+
- the verifier directly compares the signed subset with the current full registry proof plan
|
|
531
|
+
and fully verifies every included exchange;
|
|
532
|
+
- both the managed component and public `SatyaMobileClient` API require successful backend
|
|
533
|
+
verification for schema `2.2.0`, even when `verifyBackend` is false;
|
|
534
|
+
- native/local `verifyArtifact*` returns `backend-required` for schema `2.2.0` and is not an
|
|
535
|
+
acceptance verdict;
|
|
536
|
+
- the verifier verdict always distinguishes `strict-all` from `partial-endpoint-subset` and
|
|
537
|
+
returns `endpointIds`, `omittedEndpointIds`, and `planComplete`;
|
|
538
|
+
- relying parties that require full completion must require `planComplete: true` (equivalently,
|
|
539
|
+
`completionMode: "strict-all"` and an empty `omittedEndpointIds`).
|
|
540
|
+
|
|
541
|
+
The verifier proves that every presented exchange is valid and belongs to the configured plan.
|
|
542
|
+
It cannot prove that the omitted endpoints were actually attempted; that is SDK execution
|
|
543
|
+
behavior, not a property recoverable from a subset proof.
|
|
544
|
+
|
|
545
|
+
If the window expires without a successful endpoint, capture fails with
|
|
546
|
+
`no-endpoints-captured`. Timeout diagnostics list `unconfirmedEndpointIds`.
|
|
547
|
+
|
|
500
548
|
`proofPlanId` is **mutually exclusive** with `endpointId` — pass exactly one. It works on
|
|
501
549
|
the same `SatyaProviderAttestButton` and low-level client:
|
|
502
550
|
|
|
503
|
-
Providers without `proofPlans[]` remain single-endpoint compatible
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
policy.
|
|
551
|
+
Providers without `proofPlans[]` remain single-endpoint compatible when no plan is requested.
|
|
552
|
+
Requesting a `proofPlanId` from a provider with no plans, or requesting an unknown plan id, is
|
|
553
|
+
rejected instead of silently changing the requested policy.
|
|
507
554
|
|
|
508
555
|
```tsx
|
|
509
556
|
<SatyaProviderAttestButton
|
|
@@ -532,14 +579,15 @@ disabled and only 2xx responses accepted. `onProgress` reports `proofPlanId`, `e
|
|
|
532
579
|
- **1–8 endpoints** per plan, ordered; the order is order-sensitive and participates in
|
|
533
580
|
`policyHash`.
|
|
534
581
|
- **Per-response 8 MiB** and **aggregate 16 MiB** response-byte caps across the plan.
|
|
535
|
-
-
|
|
536
|
-
`[1000, 120000]` ms).
|
|
537
|
-
|
|
582
|
+
- Capture selection runs for at most the plan's `maxDurationMs` (an integer in
|
|
583
|
+
`[1000, 120000]` ms). A readiness signal starts the window when received; otherwise the
|
|
584
|
+
first accepted capture starts it. Later readiness never resets an earlier start.
|
|
538
585
|
|
|
539
586
|
### What a v2 artifact contains
|
|
540
587
|
|
|
541
|
-
The Rust core emits
|
|
542
|
-
`
|
|
588
|
+
The Rust core emits schema `2.0.0` for legacy strict-all, `2.1.0` for app-bound strict-all,
|
|
589
|
+
or `2.2.0` for a signed endpoint subset. Each artifact carries, per witnessed
|
|
590
|
+
exchange, its own TLS/handshake commitments,
|
|
543
591
|
request/response commitments, record-log evidence, and endpoint-scoped disclosed claims
|
|
544
592
|
(the same alias may repeat across different endpoint scopes; endpoint provenance is
|
|
545
593
|
preserved). All exchanges are bound by **one** domain-separated, order- and count-bound
|
|
@@ -581,10 +629,11 @@ the preserved single-artifact profile for SDK 1–3, and schema 2 selects
|
|
|
581
629
|
`expo-sdk-2-composite` for SDK 2–3. The backend loads the provider, plan, endpoints, reveal allowlists,
|
|
582
630
|
SNI, pins, freshness, and revocation policy **independently from the registry** (never from
|
|
583
631
|
the artifact), recomputes the provider's canonical `policyHash`, requires the artifact's
|
|
584
|
-
exact hash, and returns a typed verdict with `proofPlanId`, `
|
|
585
|
-
`
|
|
586
|
-
|
|
587
|
-
|
|
632
|
+
exact hash, and returns a typed verdict with `proofPlanId`, `completionMode`, `endpointIds`,
|
|
633
|
+
`omittedEndpointIds`, `planComplete`, `exchangeCount`, and `accountBindingAssurance`.
|
|
634
|
+
Strict-all verification rejects any missing, duplicated, unknown, reordered, failed, or
|
|
635
|
+
policy-mismatched exchange. Partial mode applies the same checks to every endpoint in the
|
|
636
|
+
exact signed subset and validates that subset against the registry's full proof plan.
|
|
588
637
|
|
|
589
638
|
### Tier and activation gate
|
|
590
639
|
|
package/android/build.gradle
CHANGED
|
@@ -4,13 +4,13 @@ plugins {
|
|
|
4
4
|
}
|
|
5
5
|
|
|
6
6
|
group = 'expo.modules.satyaattest'
|
|
7
|
-
version = '3.
|
|
7
|
+
version = '3.1.0'
|
|
8
8
|
|
|
9
9
|
android {
|
|
10
10
|
namespace "expo.modules.satyaattest"
|
|
11
11
|
defaultConfig {
|
|
12
12
|
versionCode 1
|
|
13
|
-
versionName "3.
|
|
13
|
+
versionName "3.1.0"
|
|
14
14
|
// StrongBox key attestation needs API 28+; basic Keystore attestation API 24+.
|
|
15
15
|
minSdkVersion 24
|
|
16
16
|
}
|
|
@@ -40,8 +40,11 @@ import java.security.spec.ECGenParameterSpec
|
|
|
40
40
|
import java.security.cert.X509Certificate
|
|
41
41
|
import java.security.KeyStore
|
|
42
42
|
import java.net.URL
|
|
43
|
+
import java.io.IOException
|
|
43
44
|
import java.util.concurrent.TimeUnit
|
|
44
45
|
import javax.net.ssl.SSLContext
|
|
46
|
+
import javax.net.ssl.SSLHandshakeException
|
|
47
|
+
import javax.net.ssl.SSLPeerUnverifiedException
|
|
45
48
|
import javax.net.ssl.TrustManager
|
|
46
49
|
import javax.net.ssl.TrustManagerFactory
|
|
47
50
|
import javax.net.ssl.X509TrustManager
|
|
@@ -78,6 +81,9 @@ private const val PLAY_INTEGRITY_COMPOSITE_BIND_DOMAIN = "satya-w-playintegrity-
|
|
|
78
81
|
|
|
79
82
|
class SatyaAttestModule : Module() {
|
|
80
83
|
private data class BoundedResponseBody(val bytes: ByteArray, val text: String)
|
|
84
|
+
private data class PartialEndpointSelection(
|
|
85
|
+
val endpointIds: List<String>,
|
|
86
|
+
)
|
|
81
87
|
|
|
82
88
|
@Volatile private var transportConfigJson: String = "{}"
|
|
83
89
|
|
|
@@ -184,13 +190,20 @@ class SatyaAttestModule : Module() {
|
|
|
184
190
|
// exchanges in one attested session, then bind them together with a
|
|
185
191
|
// device-bound proof challenge. Mirrors attestNativeReplay's shape.
|
|
186
192
|
AsyncFunction("attestNativeReplayBundle") {
|
|
187
|
-
providerTemplateJson: String,
|
|
193
|
+
providerTemplateJson: String,
|
|
194
|
+
capturedRequestsJson: String,
|
|
195
|
+
partialEndpointSelectionJson: String?,
|
|
196
|
+
promise: Promise ->
|
|
188
197
|
try {
|
|
189
198
|
require(providerTemplateJson.isNotEmpty()) {
|
|
190
199
|
"attestNativeReplayBundle requires provider template JSON"
|
|
191
200
|
}
|
|
192
201
|
val replayBundleJson =
|
|
193
|
-
buildCompositeReplayBundle(
|
|
202
|
+
buildCompositeReplayBundle(
|
|
203
|
+
providerTemplateJson,
|
|
204
|
+
capturedRequestsJson,
|
|
205
|
+
partialEndpointSelectionJson,
|
|
206
|
+
)
|
|
194
207
|
// Prepare fixes the session id / nonce / issuance window and returns the
|
|
195
208
|
// exact proof challenge the platform attestation must bind to.
|
|
196
209
|
val preparedJson = prepareNativeReplayBundle(replayBundleJson)
|
|
@@ -480,6 +493,7 @@ class SatyaAttestModule : Module() {
|
|
|
480
493
|
private fun buildCompositeReplayBundle(
|
|
481
494
|
providerTemplateJson: String,
|
|
482
495
|
capturedRequestsJson: String,
|
|
496
|
+
partialEndpointSelectionJson: String?,
|
|
483
497
|
): String {
|
|
484
498
|
val template = JSONObject(providerTemplateJson)
|
|
485
499
|
val providerId = template.getString("providerId")
|
|
@@ -536,12 +550,23 @@ class SatyaAttestModule : Module() {
|
|
|
536
550
|
)
|
|
537
551
|
}
|
|
538
552
|
|
|
539
|
-
|
|
553
|
+
val partialSelection = compositePartialEndpointSelection(partialEndpointSelectionJson)
|
|
554
|
+
require(partialSelection == null || configuredAppId() != null) {
|
|
555
|
+
"partial-endpoint replay requires a configured appId"
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// The SDK may opt this provider into partial completion. Without an
|
|
559
|
+
// explicit selection, strict-all requires the complete proof plan.
|
|
560
|
+
val effectiveEndpointIds = CompositeCompletion.effectiveEndpointIds(
|
|
561
|
+
planEndpointIds = planEndpointIds,
|
|
562
|
+
capturedEndpointIds = capturedRequests.keys,
|
|
563
|
+
partialEndpointIds = partialSelection?.endpointIds,
|
|
564
|
+
)
|
|
540
565
|
val orderedRequests =
|
|
541
|
-
CompositeReplayPlan(proofPlanId,
|
|
566
|
+
CompositeReplayPlan(proofPlanId, effectiveEndpointIds).orderedRequests(capturedRequests)
|
|
542
567
|
|
|
543
568
|
// Seed the proof-local jar for every governed replay path.
|
|
544
|
-
val cookieSeedUrls =
|
|
569
|
+
val cookieSeedUrls = effectiveEndpointIds.map { endpointId ->
|
|
545
570
|
val endpoint = endpointsById[endpointId]
|
|
546
571
|
?: error("composite plan endpoint has no template config: $endpointId")
|
|
547
572
|
val templateUrl = endpoint.optString("url").trim()
|
|
@@ -568,9 +593,10 @@ class SatyaAttestModule : Module() {
|
|
|
568
593
|
"composite replay start plan=$proofPlanId endpoints=${orderedRequests.size}",
|
|
569
594
|
)
|
|
570
595
|
var totalResponseBytes = 0L
|
|
571
|
-
val observations =
|
|
572
|
-
|
|
573
|
-
|
|
596
|
+
val observations = ArrayList<CompositeExchangeObservation>(orderedRequests.size)
|
|
597
|
+
try {
|
|
598
|
+
for (exchangeRequest in orderedRequests) {
|
|
599
|
+
try {
|
|
574
600
|
val endpoint = endpointsById[exchangeRequest.endpointId]
|
|
575
601
|
?: error("composite plan endpoint has no template config: ${exchangeRequest.endpointId}")
|
|
576
602
|
val observation = replayCompositeExchange(
|
|
@@ -589,18 +615,33 @@ class SatyaAttestModule : Module() {
|
|
|
589
615
|
)
|
|
590
616
|
totalResponseBytes =
|
|
591
617
|
CompositeByteBudget.accumulate(totalResponseBytes, observation.responseByteCount)
|
|
592
|
-
observation
|
|
618
|
+
observations.add(observation)
|
|
619
|
+
} catch (t: PartialEndpointUnavailableException) {
|
|
620
|
+
if (partialSelection == null) throw t
|
|
621
|
+
Log.i("SatyaComposite", "omitting unavailable endpoint ${exchangeRequest.endpointId}")
|
|
593
622
|
}
|
|
594
|
-
} catch (t: Throwable) {
|
|
595
|
-
// Any failed exchange aborts the bundle.
|
|
596
|
-
Log.e("SatyaComposite", "composite replay ABORTED plan=$proofPlanId: ${t.message}")
|
|
597
|
-
throw t
|
|
598
623
|
}
|
|
624
|
+
} catch (t: Throwable) {
|
|
625
|
+
Log.e("SatyaComposite", "composite replay ABORTED plan=$proofPlanId: ${t.message}")
|
|
626
|
+
throw t
|
|
627
|
+
}
|
|
599
628
|
val completedAtUtc = iso8601UtcNow()
|
|
600
629
|
val elapsedMs = SystemClock.elapsedRealtime() - startedAtMonotonicMs
|
|
601
|
-
if (maxDurationMs > 0 && elapsedMs > maxDurationMs) {
|
|
630
|
+
if (partialSelection == null && maxDurationMs > 0 && elapsedMs > maxDurationMs) {
|
|
602
631
|
error("composite replay exceeded plan maxDurationMs ($elapsedMs > $maxDurationMs)")
|
|
603
632
|
}
|
|
633
|
+
require(observations.isNotEmpty()) {
|
|
634
|
+
"partial-endpoint replay requires at least one successful endpoint"
|
|
635
|
+
}
|
|
636
|
+
val completedEndpointIds = observations.map { it.endpointId }.toSet()
|
|
637
|
+
if (observations.size < planEndpointIds.size && accountBinding.optString("mode") == "claim-equality") {
|
|
638
|
+
val references = accountBinding.optJSONArray("claimReferences") ?: JSONArray()
|
|
639
|
+
for (index in 0 until references.length()) {
|
|
640
|
+
require(completedEndpointIds.contains(references.getJSONObject(index).getString("endpointId"))) {
|
|
641
|
+
"partial-endpoint replay omitted an account-binding endpoint"
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
}
|
|
604
645
|
Log.i(
|
|
605
646
|
"SatyaComposite",
|
|
606
647
|
"composite replay complete plan=$proofPlanId exchanges=${observations.size} bytes=$totalResponseBytes",
|
|
@@ -613,10 +654,14 @@ class SatyaAttestModule : Module() {
|
|
|
613
654
|
put("providerId", providerId)
|
|
614
655
|
put("policyHash", policyHash)
|
|
615
656
|
put("proofPlanId", proofPlanId)
|
|
616
|
-
|
|
657
|
+
// Exact replay set, bound by the signed statement and verifier.
|
|
658
|
+
put("endpointIds", JSONArray(observations.map { it.endpointId }))
|
|
659
|
+
if (observations.size < planEndpointIds.size) {
|
|
660
|
+
put("completionMode", "partial-endpoint-subset")
|
|
661
|
+
}
|
|
617
662
|
put("accountBinding", accountBinding)
|
|
618
663
|
put("startedAtUtc", startedAtUtc)
|
|
619
|
-
put("completedAtUtc", completedAtUtc)
|
|
664
|
+
put("completedAtUtc", observations.lastOrNull()?.completedAtUtc ?: completedAtUtc)
|
|
620
665
|
put("maxDurationMs", maxDurationMs)
|
|
621
666
|
put("totalResponseBytes", totalResponseBytes)
|
|
622
667
|
put("exchanges", exchangesJson)
|
|
@@ -716,18 +761,36 @@ class SatyaAttestModule : Module() {
|
|
|
716
761
|
requestBuilder.method(method, body)
|
|
717
762
|
|
|
718
763
|
val remainingMs = deadlineAtMonotonicMs - SystemClock.elapsedRealtime()
|
|
719
|
-
|
|
720
|
-
|
|
764
|
+
if (remainingMs <= 0) {
|
|
765
|
+
throw PartialEndpointUnavailableException(
|
|
766
|
+
"composite replay exceeded plan maxDurationMs before ${exchangeRequest.endpointId}",
|
|
767
|
+
)
|
|
721
768
|
}
|
|
722
769
|
Log.i("SatyaComposite", "replaying endpoint ${exchangeRequest.endpointId}")
|
|
723
770
|
val call = client.newCall(requestBuilder.build())
|
|
724
771
|
call.timeout().timeout(minOf(remainingMs, 45_000L), TimeUnit.MILLISECONDS)
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
772
|
+
val response = try {
|
|
773
|
+
call.execute()
|
|
774
|
+
} catch (t: SSLPeerUnverifiedException) {
|
|
775
|
+
throw t
|
|
776
|
+
} catch (t: SSLHandshakeException) {
|
|
777
|
+
throw t
|
|
778
|
+
} catch (t: IOException) {
|
|
779
|
+
throw PartialEndpointUnavailableException(
|
|
780
|
+
"composite replay transport unavailable: ${exchangeRequest.endpointId}",
|
|
781
|
+
t,
|
|
782
|
+
)
|
|
783
|
+
}
|
|
784
|
+
response.use {
|
|
785
|
+
if (SystemClock.elapsedRealtime() > deadlineAtMonotonicMs) {
|
|
786
|
+
throw PartialEndpointUnavailableException(
|
|
787
|
+
"composite replay exceeded plan maxDurationMs during ${exchangeRequest.endpointId}",
|
|
788
|
+
)
|
|
728
789
|
}
|
|
729
|
-
|
|
730
|
-
|
|
790
|
+
if (response.code !in 200..299) {
|
|
791
|
+
throw PartialEndpointUnavailableException(
|
|
792
|
+
"composite replay returned HTTP ${response.code}: ${exchangeRequest.endpointId}",
|
|
793
|
+
)
|
|
731
794
|
}
|
|
732
795
|
require(response.request.url.host == expectedSni) {
|
|
733
796
|
"composite replay final host does not match expectedSni: ${exchangeRequest.endpointId}"
|
|
@@ -765,8 +828,10 @@ class SatyaAttestModule : Module() {
|
|
|
765
828
|
value = claim.opt("value"),
|
|
766
829
|
)
|
|
767
830
|
}
|
|
768
|
-
|
|
769
|
-
|
|
831
|
+
if (claims.isEmpty()) {
|
|
832
|
+
throw PartialEndpointUnavailableException(
|
|
833
|
+
"composite replay response had no allowed claim value: ${exchangeRequest.endpointId}",
|
|
834
|
+
)
|
|
770
835
|
}
|
|
771
836
|
Log.i("SatyaComposite", "endpoint ${exchangeRequest.endpointId} replayed bytes=$byteCount")
|
|
772
837
|
|
|
@@ -863,6 +928,20 @@ class SatyaAttestModule : Module() {
|
|
|
863
928
|
}
|
|
864
929
|
}
|
|
865
930
|
|
|
931
|
+
/** Validates the untrusted SDK-to-native partial-mode selection. */
|
|
932
|
+
private fun compositePartialEndpointSelection(
|
|
933
|
+
rawJson: String?,
|
|
934
|
+
): PartialEndpointSelection? {
|
|
935
|
+
if (rawJson.isNullOrBlank()) return null
|
|
936
|
+
val selection = JSONObject(rawJson)
|
|
937
|
+
require(selection.getString("mode") == "partial-endpoint-subset") {
|
|
938
|
+
"partial endpoint selection mode is invalid"
|
|
939
|
+
}
|
|
940
|
+
val endpointIds = stringList(selection.optJSONArray("endpointIds"))
|
|
941
|
+
require(endpointIds.isNotEmpty()) { "partial endpoint selection must not be empty" }
|
|
942
|
+
return PartialEndpointSelection(endpointIds = endpointIds)
|
|
943
|
+
}
|
|
944
|
+
|
|
866
945
|
private fun compositeRevealRules(endpoint: JSONObject): JSONArray {
|
|
867
946
|
val rules = endpoint.optJSONArray("revealRules") ?: JSONArray()
|
|
868
947
|
val reveals = JSONArray()
|
|
@@ -5,6 +5,10 @@ import okhttp3.CookieJar
|
|
|
5
5
|
import okhttp3.HttpUrl
|
|
6
6
|
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
|
7
7
|
|
|
8
|
+
/** Availability/data failure that partial mode may omit without weakening TLS checks. */
|
|
9
|
+
class PartialEndpointUnavailableException(message: String, cause: Throwable? = null) :
|
|
10
|
+
Exception(message, cause)
|
|
11
|
+
|
|
8
12
|
// Pure, JVM-only types for the sequential composite (schema-v2) replay.
|
|
9
13
|
//
|
|
10
14
|
// This file deliberately imports ONLY okhttp3 (a plain JVM library) and the
|
|
@@ -89,6 +93,41 @@ class CompositeReplayPlan(
|
|
|
89
93
|
}
|
|
90
94
|
}
|
|
91
95
|
|
|
96
|
+
/** Pure partial-endpoint resolution, unit-testable without the Expo runtime. */
|
|
97
|
+
object CompositeCompletion {
|
|
98
|
+
/**
|
|
99
|
+
* Resolves an exact candidate replay set. No selection means strict-all.
|
|
100
|
+
* Partial mode may start with a subset or the full plan so native replay can
|
|
101
|
+
* sign only the successful endpoints.
|
|
102
|
+
*/
|
|
103
|
+
fun effectiveEndpointIds(
|
|
104
|
+
planEndpointIds: List<String>,
|
|
105
|
+
capturedEndpointIds: Set<String>,
|
|
106
|
+
partialEndpointIds: List<String>?,
|
|
107
|
+
): List<String> {
|
|
108
|
+
if (partialEndpointIds == null) {
|
|
109
|
+
require(capturedEndpointIds == planEndpointIds.toSet()) {
|
|
110
|
+
"strict-all replay requires every proof-plan endpoint"
|
|
111
|
+
}
|
|
112
|
+
return planEndpointIds
|
|
113
|
+
}
|
|
114
|
+
require(partialEndpointIds.isNotEmpty() && partialEndpointIds.size <= planEndpointIds.size) {
|
|
115
|
+
"selected endpoints must be a non-empty proof-plan subset"
|
|
116
|
+
}
|
|
117
|
+
val partialSet = partialEndpointIds.toSet()
|
|
118
|
+
require(partialSet.size == partialEndpointIds.size && partialEndpointIds.all(planEndpointIds.toSet()::contains)) {
|
|
119
|
+
"selected endpoints must be unique proof-plan endpoints"
|
|
120
|
+
}
|
|
121
|
+
require(partialEndpointIds == planEndpointIds.filter(partialSet::contains)) {
|
|
122
|
+
"selected endpoints must follow proof-plan order"
|
|
123
|
+
}
|
|
124
|
+
require(capturedEndpointIds == partialSet) {
|
|
125
|
+
"captured endpoints do not exactly match the selected subset"
|
|
126
|
+
}
|
|
127
|
+
return partialEndpointIds
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
92
131
|
/** Endpoint-scoped disclosed claim, mirroring the Rust `NativeReplayBundleClaim`. */
|
|
93
132
|
data class CompositeClaim(
|
|
94
133
|
val name: String,
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -3,7 +3,7 @@ import { VerifyResult } from './types';
|
|
|
3
3
|
declare class SatyaAttestModule extends NativeModule<Record<string, never>> {
|
|
4
4
|
satyaCoreVersion(): string;
|
|
5
5
|
attestNativeReplay(providerTemplateJson: string, capturedRequestJson?: string | null): Promise<string>;
|
|
6
|
-
attestNativeReplayBundle(providerTemplateJson: string, capturedRequestsJson: string): Promise<string>;
|
|
6
|
+
attestNativeReplayBundle(providerTemplateJson: string, capturedRequestsJson: string, partialEndpointSelectionJson?: string | null): Promise<string>;
|
|
7
7
|
verifyArtifact(artifactJson: string): Promise<boolean>;
|
|
8
8
|
verifyArtifactDetailed(artifactJson: string): Promise<VerifyResult>;
|
|
9
9
|
initialize(configJson: string): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SatyaAttestModule.d.ts","sourceRoot":"","sources":["../src/SatyaAttestModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,MAAM,MAAM,CAAC;AAEzD,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,OAAO,OAAO,iBAAkB,SAAQ,YAAY,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACzE,gBAAgB,IAAI,MAAM;IAE1B,kBAAkB,CAChB,oBAAoB,EAAE,MAAM,EAC5B,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,GAClC,OAAO,CAAC,MAAM,CAAC;IAElB,wBAAwB,CACtB,oBAAoB,EAAE,MAAM,EAC5B,oBAAoB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"SatyaAttestModule.d.ts","sourceRoot":"","sources":["../src/SatyaAttestModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,MAAM,MAAM,CAAC;AAEzD,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,OAAO,OAAO,iBAAkB,SAAQ,YAAY,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACzE,gBAAgB,IAAI,MAAM;IAE1B,kBAAkB,CAChB,oBAAoB,EAAE,MAAM,EAC5B,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,GAClC,OAAO,CAAC,MAAM,CAAC;IAElB,wBAAwB,CACtB,oBAAoB,EAAE,MAAM,EAC5B,oBAAoB,EAAE,MAAM,EAC5B,4BAA4B,CAAC,EAAE,MAAM,GAAG,IAAI,GAC3C,OAAO,CAAC,MAAM,CAAC;IAElB,cAAc,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IACtD,sBAAsB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAEnE,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAC7C,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IACxB,iCAAiC,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IAC3F,sBAAsB,CACpB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,OAAO,EAChB,kBAAkB,EAAE,OAAO,GAC1B,OAAO,CAAC,OAAO,CAAC;IACnB,qFAAqF;IACrF,wBAAwB,CAAC,CAAC,SAAS,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAC/D,iBAAiB,IAAI,OAAO,CAAC,OAAO,CAAC;CACtC;;AAED,wBAAqE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SatyaAttestModule.js","sourceRoot":"","sources":["../src/SatyaAttestModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"SatyaAttestModule.js","sourceRoot":"","sources":["../src/SatyaAttestModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAE,MAAM,MAAM,CAAC;AAkCzD,eAAe,mBAAmB,CAAoB,aAAa,CAAC,CAAC","sourcesContent":["import { NativeModule, requireNativeModule } from 'expo';\n\nimport { VerifyResult } from './types';\n\ndeclare class SatyaAttestModule extends NativeModule<Record<string, never>> {\n satyaCoreVersion(): string;\n\n attestNativeReplay(\n providerTemplateJson: string,\n capturedRequestJson?: string | null\n ): Promise<string>;\n\n attestNativeReplayBundle(\n providerTemplateJson: string,\n capturedRequestsJson: string,\n partialEndpointSelectionJson?: string | null\n ): Promise<string>;\n\n verifyArtifact(artifactJson: string): Promise<boolean>;\n verifyArtifactDetailed(artifactJson: string): Promise<VerifyResult>;\n\n initialize(configJson: string): Promise<void>;\n dispose(): Promise<void>;\n installWebViewDocumentStartScript(viewTag: number, script: string): Promise<boolean | null>;\n setPopupWindowsEnabled(\n viewTag: number,\n enabled: boolean,\n geolocationEnabled: boolean\n ): Promise<boolean>;\n /** Optional for compatibility with native binaries predating privacy concealment. */\n setPopupWindowsConcealed?(concealed: boolean): Promise<boolean>;\n closePopupWindows(): Promise<boolean>;\n}\n\nexport default requireNativeModule<SatyaAttestModule>('SatyaAttest');\n"]}
|
|
@@ -3,7 +3,7 @@ import { VerifyResult } from './types';
|
|
|
3
3
|
declare class SatyaAttestModule extends NativeModule<Record<string, never>> {
|
|
4
4
|
satyaCoreVersion(): string;
|
|
5
5
|
attestNativeReplay(_providerTemplateJson: string, _capturedRequestJson?: string | null): Promise<string>;
|
|
6
|
-
attestNativeReplayBundle(_providerTemplateJson: string, _capturedRequestsJson: string): Promise<string>;
|
|
6
|
+
attestNativeReplayBundle(_providerTemplateJson: string, _capturedRequestsJson: string, _partialEndpointSelectionJson?: string | null): Promise<string>;
|
|
7
7
|
verifyArtifact(_artifactJson: string): Promise<boolean>;
|
|
8
8
|
verifyArtifactDetailed(_artifactJson: string): Promise<VerifyResult>;
|
|
9
9
|
initialize(_configJson: string): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SatyaAttestModule.web.d.ts","sourceRoot":"","sources":["../src/SatyaAttestModule.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,YAAY,EAAE,MAAM,MAAM,CAAC;AAEvD,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAMvC,cAAM,iBAAkB,SAAQ,YAAY,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACjE,gBAAgB,IAAI,MAAM;IAGpB,kBAAkB,CACtB,qBAAqB,EAAE,MAAM,EAC7B,oBAAoB,CAAC,EAAE,MAAM,GAAG,IAAI,GACnC,OAAO,CAAC,MAAM,CAAC;IAGZ,wBAAwB,CAC5B,qBAAqB,EAAE,MAAM,EAC7B,qBAAqB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"SatyaAttestModule.web.d.ts","sourceRoot":"","sources":["../src/SatyaAttestModule.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,YAAY,EAAE,MAAM,MAAM,CAAC;AAEvD,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAMvC,cAAM,iBAAkB,SAAQ,YAAY,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACjE,gBAAgB,IAAI,MAAM;IAGpB,kBAAkB,CACtB,qBAAqB,EAAE,MAAM,EAC7B,oBAAoB,CAAC,EAAE,MAAM,GAAG,IAAI,GACnC,OAAO,CAAC,MAAM,CAAC;IAGZ,wBAAwB,CAC5B,qBAAqB,EAAE,MAAM,EAC7B,qBAAqB,EAAE,MAAM,EAC7B,6BAA6B,CAAC,EAAE,MAAM,GAAG,IAAI,GAC5C,OAAO,CAAC,MAAM,CAAC;IAGZ,cAAc,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAGvD,sBAAsB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAGpE,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAG9C,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAG/B;;AAMD,wBAAyE"}
|
|
@@ -8,7 +8,7 @@ class SatyaAttestModule extends NativeModule {
|
|
|
8
8
|
async attestNativeReplay(_providerTemplateJson, _capturedRequestJson) {
|
|
9
9
|
throw unsupportedPlatformError();
|
|
10
10
|
}
|
|
11
|
-
async attestNativeReplayBundle(_providerTemplateJson, _capturedRequestsJson) {
|
|
11
|
+
async attestNativeReplayBundle(_providerTemplateJson, _capturedRequestsJson, _partialEndpointSelectionJson) {
|
|
12
12
|
throw unsupportedPlatformError();
|
|
13
13
|
}
|
|
14
14
|
async verifyArtifact(_artifactJson) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SatyaAttestModule.web.js","sourceRoot":"","sources":["../src/SatyaAttestModule.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,MAAM,CAAC;AAGvD,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEjE,MAAM,WAAW,GACf,uFAAuF,CAAC;AAE1F,MAAM,iBAAkB,SAAQ,YAAmC;IACjE,gBAAgB;QACd,MAAM,wBAAwB,EAAE,CAAC;IACnC,CAAC;IACD,KAAK,CAAC,kBAAkB,CACtB,qBAA6B,EAC7B,oBAAoC;QAEpC,MAAM,wBAAwB,EAAE,CAAC;IACnC,CAAC;IACD,KAAK,CAAC,wBAAwB,CAC5B,qBAA6B,EAC7B,qBAA6B;
|
|
1
|
+
{"version":3,"file":"SatyaAttestModule.web.js","sourceRoot":"","sources":["../src/SatyaAttestModule.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,MAAM,CAAC;AAGvD,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEjE,MAAM,WAAW,GACf,uFAAuF,CAAC;AAE1F,MAAM,iBAAkB,SAAQ,YAAmC;IACjE,gBAAgB;QACd,MAAM,wBAAwB,EAAE,CAAC;IACnC,CAAC;IACD,KAAK,CAAC,kBAAkB,CACtB,qBAA6B,EAC7B,oBAAoC;QAEpC,MAAM,wBAAwB,EAAE,CAAC;IACnC,CAAC;IACD,KAAK,CAAC,wBAAwB,CAC5B,qBAA6B,EAC7B,qBAA6B,EAC7B,6BAA6C;QAE7C,MAAM,wBAAwB,EAAE,CAAC;IACnC,CAAC;IACD,KAAK,CAAC,cAAc,CAAC,aAAqB;QACxC,MAAM,wBAAwB,EAAE,CAAC;IACnC,CAAC;IACD,KAAK,CAAC,sBAAsB,CAAC,aAAqB;QAChD,MAAM,wBAAwB,EAAE,CAAC;IACnC,CAAC;IACD,KAAK,CAAC,UAAU,CAAC,WAAmB;QAClC,MAAM,wBAAwB,EAAE,CAAC;IACnC,CAAC;IACD,KAAK,CAAC,OAAO;QACX,MAAM,wBAAwB,EAAE,CAAC;IACnC,CAAC;CACF;AAED,SAAS,wBAAwB;IAC/B,MAAM,UAAU,CAAC,cAAc,CAAC,oBAAoB,EAAE,WAAW,CAAC,CAAC;AACrE,CAAC;AAED,eAAe,iBAAiB,CAAC,iBAAiB,EAAE,mBAAmB,CAAC,CAAC","sourcesContent":["import { registerWebModule, NativeModule } from 'expo';\n\nimport { VerifyResult } from './types';\nimport { SatyaErrorCode, satyaError } from './types/error.types';\n\nconst UNSUPPORTED =\n 'expo-satya-attest is not available on web (it requires native App Attest / Keystore).';\n\nclass SatyaAttestModule extends NativeModule<Record<string, never>> {\n satyaCoreVersion(): string {\n throw unsupportedPlatformError();\n }\n async attestNativeReplay(\n _providerTemplateJson: string,\n _capturedRequestJson?: string | null\n ): Promise<string> {\n throw unsupportedPlatformError();\n }\n async attestNativeReplayBundle(\n _providerTemplateJson: string,\n _capturedRequestsJson: string,\n _partialEndpointSelectionJson?: string | null\n ): Promise<string> {\n throw unsupportedPlatformError();\n }\n async verifyArtifact(_artifactJson: string): Promise<boolean> {\n throw unsupportedPlatformError();\n }\n async verifyArtifactDetailed(_artifactJson: string): Promise<VerifyResult> {\n throw unsupportedPlatformError();\n }\n async initialize(_configJson: string): Promise<void> {\n throw unsupportedPlatformError();\n }\n async dispose(): Promise<void> {\n throw unsupportedPlatformError();\n }\n}\n\nfunction unsupportedPlatformError(): never {\n throw satyaError(SatyaErrorCode.UNSUPPORTED_PLATFORM, UNSUPPORTED);\n}\n\nexport default registerWebModule(SatyaAttestModule, 'SatyaAttestModule');\n"]}
|