tisura-light 0.0.1-100
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 +176 -0
- package/dist/certify/embedCertificateEml.d.ts +21 -0
- package/dist/certify/embedCertificatePdf.d.ts +23 -0
- package/dist/certify/embedQrCode.d.ts +27 -0
- package/dist/certify/findBestQrPlacement.d.ts +13 -0
- package/dist/certify/utils.d.ts +5 -0
- package/dist/client-light.d.ts +110 -0
- package/dist/config.d.ts +13 -0
- package/dist/connect/connect.d.ts +81 -0
- package/dist/connect/ftp/ftpClient.d.ts +18 -0
- package/dist/connect/index.d.ts +67 -0
- package/dist/connect/internal/connectionContext.d.ts +50 -0
- package/dist/connect/internal/fileTypes.d.ts +3 -0
- package/dist/connect/internal/httpRequest.d.ts +57 -0
- package/dist/connect/internal/httpStatus.d.ts +6 -0
- package/dist/connect/internal/tlsSession.d.ts +6 -0
- package/dist/connect/internal/tokenize.d.ts +6 -0
- package/dist/connect/internal/tunnel.d.ts +15 -0
- package/dist/connect/tls/index.d.ts +20 -0
- package/dist/connect/tls/wasm/tls.d.ts +148 -0
- package/dist/connect/tls/wasm/tls.js +758 -0
- package/dist/connect/tls/wasm/tls_bg.wasm +0 -0
- package/dist/connect/tls/wasm/tls_bg.wasm.d.ts +32 -0
- package/dist/index.js +83325 -0
- package/dist/index.light.d.ts +11 -0
- package/dist/inputs.d.ts +101 -0
- package/dist/metrics.d.ts +13 -0
- package/dist/parse/fetcher.d.ts +7 -0
- package/dist/parse/index.d.ts +12 -0
- package/dist/parse/parser.d.ts +14 -0
- package/dist/parse/types.d.ts +31 -0
- package/dist/tls13.d.ts +32 -0
- package/dist/tls_bg.wasm +0 -0
- package/dist/types.d.ts +6 -0
- package/dist/utils.d.ts +12 -0
- package/dist/verify/ftps.d.ts +32 -0
- package/package.json +99 -0
- package/public/.gitkeep +0 -0
package/README.md
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# Sentinel SDK
|
|
2
|
+
|
|
3
|
+
This project contains the code for [tisura](https://www.npmjs.com/package/tisura) npm package.
|
|
4
|
+
It provides modules for parsing network packets, proving statements, managing connections, and handling TLS operations.
|
|
5
|
+
|
|
6
|
+
## Build
|
|
7
|
+
|
|
8
|
+
### Build Wasm files
|
|
9
|
+
|
|
10
|
+
Make sure to use `rustc` version of `1.82.0`.
|
|
11
|
+
|
|
12
|
+
To install it, you can run:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
# first install rustup
|
|
16
|
+
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
|
17
|
+
# then install rust
|
|
18
|
+
rustup install 1.82.0
|
|
19
|
+
rustup default 1.82.0
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Build Noir files
|
|
23
|
+
|
|
24
|
+
Make sure to install `noir`.
|
|
25
|
+
|
|
26
|
+
To install it, you can run:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# first install noirup
|
|
30
|
+
curl -L https://raw.githubusercontent.com/noir-lang/noirup/refs/heads/main/install | bash
|
|
31
|
+
# then install noir
|
|
32
|
+
noirup --version 1.0.0-beta.6
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Local SDK Development
|
|
36
|
+
|
|
37
|
+
The `build-and-install.sh` script builds the SDK as a `.tgz` (exactly as it would be published to npm) and installs it into a sibling consumer project.
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
./build-and-install.sh <mode> <dest>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
- **`<mode>`** (optional, default `full`): `full` builds with WASM + TS, `light` produces a slim ESM bundle (no TLS/circuit code, just stamping + provider requests).
|
|
44
|
+
- **`<dest>`** (optional, default `twallet`): one of `extension`, `platform`, `airdrop`, `twallet`, `explorer`. `platform` and `airdrop` are installed via `yarn`, the others via `npm`.
|
|
45
|
+
|
|
46
|
+
The script removes any existing `tisura*.tgz` and `node_modules/tisura` in the destination, clears the package manager's cache for tisura, and force-installs the new tarball — so re-running with the same version reliably picks up the new SDK code.
|
|
47
|
+
|
|
48
|
+
Examples:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
./build-and-install.sh # full build → twallet (npm)
|
|
52
|
+
./build-and-install.sh light extension # light build → sentinel_extension (npm)
|
|
53
|
+
./build-and-install.sh full platform # full build → sentinel/platform (yarn)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Installation
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
npm install tisura
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Usage
|
|
63
|
+
|
|
64
|
+
### Basic Setup
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
import { Tisura } from "tisura";
|
|
68
|
+
|
|
69
|
+
const client = new Tisura({
|
|
70
|
+
apiKey: "your-api-key", // Format: sk_dev_xxx key, sk_prod_xxx key, or simply sk_local for local development
|
|
71
|
+
});
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Sending a request to the server
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
const connectClient = client.connect;
|
|
78
|
+
|
|
79
|
+
const conn = await connectClient.sendRequest({
|
|
80
|
+
serverHost, // e.g. "api.github.com"
|
|
81
|
+
serverPort, // e.g. 443
|
|
82
|
+
endpoint, // e.g. "/users"
|
|
83
|
+
params, // e.g. [{ key: "since", value: "1" }]
|
|
84
|
+
method, // e.g. "POST"
|
|
85
|
+
body , // eg. [{ key: "name", value: "sherlock" }] - Optional
|
|
86
|
+
headers, // e.g. { key: "Authorization", value: `Bearer ${token}` } - Optional
|
|
87
|
+
}: SendRequestArgs);
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Generating a proof and verifying it
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
const proveClient = client.prove;
|
|
94
|
+
|
|
95
|
+
const proof = await proveClient.generateProof({
|
|
96
|
+
circuitName, // e.g. "ownership"
|
|
97
|
+
connectionId, // optional, defaults to the last connection
|
|
98
|
+
plaintext, // the data you want to prove in "substring" circuit - Optional
|
|
99
|
+
nodeId, // the github node ID to prove in "ownership" circuit - Optional
|
|
100
|
+
repoName, // the github repo name to prove in "contributions" circuit - Optional
|
|
101
|
+
repoOwner, // the github repo owner to prove in "contributions" circuit - Optional
|
|
102
|
+
count, // the number of contributions to prove in "contributions" circuit - Optional
|
|
103
|
+
}: GenerateProofArgs);
|
|
104
|
+
|
|
105
|
+
const isValid = await proveClient.verifyProof({
|
|
106
|
+
circuitName, // e.g. "ownership"
|
|
107
|
+
proof, // the proof above
|
|
108
|
+
}: VerifyProofArgs);
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Certifying a statement
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
const certifyClient = client.certify;
|
|
115
|
+
|
|
116
|
+
const cert = await certifyClient.certify({
|
|
117
|
+
inputBytes, // the data you want to certify (in bytes)
|
|
118
|
+
connectionId, // optional, defaults to the last connection
|
|
119
|
+
inputSessionKey, // optional, defaults to the last session key
|
|
120
|
+
}: CertifyArgs)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Decrypting an encrypted statement
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
const certifyClient = client.certify;
|
|
127
|
+
const decrypted = await certifyClient.decrypt({
|
|
128
|
+
connectionId, // optional, defaults to the last connection
|
|
129
|
+
}: DecryptArgs);
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Available Modules
|
|
133
|
+
|
|
134
|
+
The SDK consists of several modules:
|
|
135
|
+
|
|
136
|
+
- **Parse**: Network packet parsing and analysis
|
|
137
|
+
- **Prove**: Statement proving and verification
|
|
138
|
+
- **Certify**: Statement certifying from client side
|
|
139
|
+
- **Connect**: Connection management
|
|
140
|
+
|
|
141
|
+
### Module-specific Imports
|
|
142
|
+
|
|
143
|
+
You can import specific modules directly:
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
import { ParseClient } from "tisura/parse";
|
|
147
|
+
import { ProveClient } from "tisura/prove";
|
|
148
|
+
import { CertifyClient } from "tisura/certify";
|
|
149
|
+
import { ConnectClient } from "tisura/connect";
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Configuration
|
|
153
|
+
|
|
154
|
+
The SDK requires the following configuration parameters:
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
interface TisuraConfig {
|
|
158
|
+
proxyUrl: string;
|
|
159
|
+
storeUrl: string;
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Development
|
|
164
|
+
|
|
165
|
+
### Install, build and run tests
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
# Install dependencies
|
|
169
|
+
npm install
|
|
170
|
+
|
|
171
|
+
# Build the package
|
|
172
|
+
npm run build
|
|
173
|
+
|
|
174
|
+
# Run tests
|
|
175
|
+
npm test
|
|
176
|
+
```
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { SessionKey } from "../inputs.js";
|
|
2
|
+
/**
|
|
3
|
+
* Embed certificate.json as a folded `X-Tisura-Certificate` header (RFC 5322).
|
|
4
|
+
*
|
|
5
|
+
* The cert JSON is base64-encoded and inserted right before the header/body
|
|
6
|
+
* separator. Body bytes are preserved byte-identical, and the original `.eml`
|
|
7
|
+
* is recoverable by stripping the inserted header.
|
|
8
|
+
*/
|
|
9
|
+
export declare function embedCertificateInEml(originalEmlBytes: Uint8Array, certificate: SessionKey): {
|
|
10
|
+
stampedEmlBytes: Uint8Array;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Extract the embedded certificate from a stamped `.eml` and return the
|
|
14
|
+
* recovered original bytes (with the `X-Tisura-Certificate` header stripped).
|
|
15
|
+
* Returns null if no embedded certificate is found.
|
|
16
|
+
*/
|
|
17
|
+
export declare function extractCertificateFromEml(emlBytes: Uint8Array): {
|
|
18
|
+
certObj: unknown;
|
|
19
|
+
originalEmlBytes: Uint8Array;
|
|
20
|
+
} | null;
|
|
21
|
+
export declare function isEmlWithCertificate(emlBytes: Uint8Array): boolean;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { SessionKey } from "../inputs.js";
|
|
2
|
+
/**
|
|
3
|
+
* Embed certificate.json as a PDF file attachment (ISO 32000-2:2020, 7.11.4).
|
|
4
|
+
*
|
|
5
|
+
* Uses incremental save to preserve original bytes as a prefix of the stamped
|
|
6
|
+
* PDF. The certificate includes `original_content_length` so the verifier can
|
|
7
|
+
* slice the stamped PDF to recover the exact original bytes and compare them
|
|
8
|
+
* against the TLS-decrypted body.
|
|
9
|
+
*/
|
|
10
|
+
export declare function embedCertificateInPdf(originalPdfBytes: Uint8Array, certificate: SessionKey): Promise<{
|
|
11
|
+
stampedPdfBytes: Uint8Array;
|
|
12
|
+
}>;
|
|
13
|
+
/**
|
|
14
|
+
* Extract certificate.json from a PDF's EmbeddedFiles name tree.
|
|
15
|
+
* Returns null if no embedded certificate is found.
|
|
16
|
+
*/
|
|
17
|
+
export declare function extractCertificateFromPdf(pdfBytes: Uint8Array): Promise<{
|
|
18
|
+
certObj: unknown;
|
|
19
|
+
} | null>;
|
|
20
|
+
/**
|
|
21
|
+
* Check if a PDF has an embedded certificate attachment.
|
|
22
|
+
*/
|
|
23
|
+
export declare function isPdfWithCertificate(pdfBytes: Uint8Array): Promise<boolean>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { SessionKey } from "../inputs.js";
|
|
2
|
+
export type QrPlacement = {
|
|
3
|
+
pageIndex: number;
|
|
4
|
+
x: number;
|
|
5
|
+
y: number;
|
|
6
|
+
size?: number;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Embed a QR code on the PDF.
|
|
10
|
+
*
|
|
11
|
+
* Defaults to bottom-right of the last page. Pass `placement` to override —
|
|
12
|
+
* callers that detect whitespace can pick a corner that won't collide with
|
|
13
|
+
* existing content.
|
|
14
|
+
*
|
|
15
|
+
* The QR encodes a verification URL with certificate query params:
|
|
16
|
+
* <verifyBaseUrl>?cr=<client_random>&secret=<secret>&len=<original_content_length>
|
|
17
|
+
*
|
|
18
|
+
* Uses QRCode.toCanvas with OffscreenCanvas in service workers (no DOM needed),
|
|
19
|
+
* falls back to QRCode.toDataURL in Node.js.
|
|
20
|
+
*
|
|
21
|
+
* Uses incremental save to preserve all preceding bytes (including any
|
|
22
|
+
* certificate attachment from embedCertificateInPdf) as a prefix of the
|
|
23
|
+
* output, so `original_content_length` slice still works for verification.
|
|
24
|
+
*/
|
|
25
|
+
export declare function embedQrCodeInPdf(pdfBytes: Uint8Array, certificate: SessionKey & {
|
|
26
|
+
original_content_length: number;
|
|
27
|
+
}, verifyBaseUrl?: string, placement?: QrPlacement): Promise<Uint8Array>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { QrPlacement } from "./embedQrCode.js";
|
|
2
|
+
/**
|
|
3
|
+
* Pick the brightest whitespace spot inside any of the four corner regions
|
|
4
|
+
* of the last page so a QR stamp avoids both page-edge content (decorative
|
|
5
|
+
* borders) and the page interior (body text).
|
|
6
|
+
*
|
|
7
|
+
* For each corner, slides a (qrSize + 2·margin)² probe and picks the
|
|
8
|
+
* position with the highest mean luminance across all four corners.
|
|
9
|
+
*
|
|
10
|
+
* Coordinates are returned in PDF user space (origin at bottom-left), ready
|
|
11
|
+
* to hand to embedQrCodeInPdf.
|
|
12
|
+
*/
|
|
13
|
+
export declare function findBestQrPlacement(pdfBytes: Uint8Array, qrSize?: number, margin?: number, cornerSize?: number): Promise<QrPlacement>;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function concatUint8Arrays(arrays: Uint8Array[]): Uint8Array;
|
|
2
|
+
export declare function decodeToBytes(input: string | ArrayBuffer | Uint8Array): Uint8Array;
|
|
3
|
+
export declare function includes(source: Uint8Array, needle: Uint8Array): boolean;
|
|
4
|
+
export declare function bytesToBase64(bytes: Uint8Array): string;
|
|
5
|
+
export declare function extractPdfFromEnvelope(body: Uint8Array): Uint8Array | null;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { TisuraConfig } from "./config.js";
|
|
2
|
+
import { ConnectClient } from "./connect/index.js";
|
|
3
|
+
import { SessionKey, LinkedSessionCertificate } from "./inputs.js";
|
|
4
|
+
import { FtpsVerification, VerifyFtpsOptions } from "./verify/ftps.js";
|
|
5
|
+
export type SendRequestArgs = {
|
|
6
|
+
serverHost: string;
|
|
7
|
+
endpoint: string;
|
|
8
|
+
method: "GET" | "POST";
|
|
9
|
+
params?: Array<{
|
|
10
|
+
key: string;
|
|
11
|
+
value: string;
|
|
12
|
+
}>;
|
|
13
|
+
body?: Array<{
|
|
14
|
+
key: string;
|
|
15
|
+
value: string;
|
|
16
|
+
}>;
|
|
17
|
+
headers?: Array<{
|
|
18
|
+
key: string;
|
|
19
|
+
value: string;
|
|
20
|
+
}>;
|
|
21
|
+
serverPort?: number;
|
|
22
|
+
onProgress?: (...args: [number, number?]) => void;
|
|
23
|
+
};
|
|
24
|
+
export type SendProviderRequestArgs = {
|
|
25
|
+
providerKey: string;
|
|
26
|
+
providerContext?: unknown;
|
|
27
|
+
headers?: Array<{
|
|
28
|
+
key: string;
|
|
29
|
+
value: string;
|
|
30
|
+
}>;
|
|
31
|
+
onProgress?: (...args: [number, number?]) => void;
|
|
32
|
+
};
|
|
33
|
+
export type ProviderInfo = {
|
|
34
|
+
id: string;
|
|
35
|
+
responseStatus: boolean;
|
|
36
|
+
};
|
|
37
|
+
export type DownloadFtpsArgs = {
|
|
38
|
+
serverHost: string;
|
|
39
|
+
user: string;
|
|
40
|
+
pass: string;
|
|
41
|
+
remotePath: string;
|
|
42
|
+
};
|
|
43
|
+
export declare class TisuraLight {
|
|
44
|
+
config: TisuraConfig;
|
|
45
|
+
protected connectClient: ConnectClient;
|
|
46
|
+
constructor({ apiKey }: {
|
|
47
|
+
apiKey: string;
|
|
48
|
+
});
|
|
49
|
+
sendRequest(args: SendRequestArgs): Promise<string | Blob>;
|
|
50
|
+
sendRequestRawResponse({ serverHost, endpoint, params, method, body, headers, serverPort, onProgress, }: SendRequestArgs): Promise<Uint8Array>;
|
|
51
|
+
/**
|
|
52
|
+
* Sends a request expected to return a PDF and returns the raw PDF bytes
|
|
53
|
+
* along with the TLS session key captured from the connection.
|
|
54
|
+
*
|
|
55
|
+
* Use this when you want to run logic between fetch and stamping (for
|
|
56
|
+
* example, scanning the PDF for whitespace before placing the QR). For the
|
|
57
|
+
* common case, prefer `sendRequestAndStampPdf`.
|
|
58
|
+
*
|
|
59
|
+
* Throws if the response is not application/pdf or if no
|
|
60
|
+
* SERVER_TRAFFIC_SECRET_0 key was captured.
|
|
61
|
+
*/
|
|
62
|
+
sendRequestForPdf(args: SendRequestArgs): Promise<{
|
|
63
|
+
pdfBytes: Uint8Array;
|
|
64
|
+
sessionKey: SessionKey;
|
|
65
|
+
}>;
|
|
66
|
+
/**
|
|
67
|
+
* Sends a request expected to return a PDF and stamps a Tisura verification
|
|
68
|
+
* QR code in the bottom-right of the last page. The QR encodes the
|
|
69
|
+
* environment-aware verifyBaseUrl plus the TLS session key from this
|
|
70
|
+
* connection, so the recipient can verify the PDF's authenticity.
|
|
71
|
+
*
|
|
72
|
+
* Reads the session key directly from the connection's in-memory buffer, so
|
|
73
|
+
* this works in any runtime including Node — no localStorage required.
|
|
74
|
+
*
|
|
75
|
+
* Returns the QR-stamped PDF as a Uint8Array. Throws if the response is not
|
|
76
|
+
* application/pdf or if no SERVER_TRAFFIC_SECRET_0 key was captured.
|
|
77
|
+
*/
|
|
78
|
+
sendRequestAndStampPdf(args: SendRequestArgs): Promise<Uint8Array>;
|
|
79
|
+
sendProviderRequest(args: SendProviderRequestArgs): Promise<string | Blob>;
|
|
80
|
+
sendProviderRequestRawResponse({ providerKey, providerContext, headers, onProgress, }: SendProviderRequestArgs): Promise<Uint8Array>;
|
|
81
|
+
/**
|
|
82
|
+
* POC: download a single file over implicit FTPS (port 990) and return just
|
|
83
|
+
* the file bytes. Establishes two tunnels (control + data), each with its own
|
|
84
|
+
* TLS session captured by the proxy.
|
|
85
|
+
*/
|
|
86
|
+
downloadFtpsFile(args: DownloadFtpsArgs): Promise<Uint8Array>;
|
|
87
|
+
/**
|
|
88
|
+
* Like {@link downloadFtpsFile}, but also returns a {@link
|
|
89
|
+
* LinkedSessionCertificate} bundling the control- and data-channel TLS keys
|
|
90
|
+
* plus the PASV data endpoint. These are what a verifier needs to bind the
|
|
91
|
+
* two sessions into a single proof ("file F answered the command issued on
|
|
92
|
+
* the control channel"). Verification / the proxy-attested dial-address
|
|
93
|
+
* cross-check is separate (later phases); this surfaces the inputs.
|
|
94
|
+
*
|
|
95
|
+
* Throws if the expected traffic secrets were not captured on either channel.
|
|
96
|
+
*/
|
|
97
|
+
downloadFtpsFileWithCertificate({ serverHost, user, pass, remotePath, }: DownloadFtpsArgs): Promise<{
|
|
98
|
+
bytes: Uint8Array;
|
|
99
|
+
certificate: LinkedSessionCertificate;
|
|
100
|
+
}>;
|
|
101
|
+
/**
|
|
102
|
+
* Verifies an FTPS download from its {@link LinkedSessionCertificate}: decrypts
|
|
103
|
+
* the proxy-captured control + data transcripts, checks the proxy-signed dial
|
|
104
|
+
* address of the data session matches the PASV endpoint named on the control
|
|
105
|
+
* channel, and returns the bound claim (command + file).
|
|
106
|
+
*/
|
|
107
|
+
verifyFtpsDownload(cert: LinkedSessionCertificate, opts?: VerifyFtpsOptions): Promise<FtpsVerification>;
|
|
108
|
+
updateProviderMetrics({ id, responseStatus, }: ProviderInfo): Promise<void>;
|
|
109
|
+
protected incrementApiKeyCounter(): Promise<void>;
|
|
110
|
+
}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface TisuraConfig {
|
|
2
|
+
proxyUrl: string;
|
|
3
|
+
storeUrl: string;
|
|
4
|
+
verifyBaseUrl: string;
|
|
5
|
+
apiKey: string;
|
|
6
|
+
proxyPublicKeyHex?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function deriveConfigFromAPIKey(apiKey: string): TisuraConfig;
|
|
9
|
+
export declare class TisuraBase {
|
|
10
|
+
config: TisuraConfig;
|
|
11
|
+
constructor(config: TisuraConfig);
|
|
12
|
+
protected getStoreFetcherEndpoint(): string;
|
|
13
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { TisuraConfig } from "../config.js";
|
|
2
|
+
import { SessionKey } from "../inputs.js";
|
|
3
|
+
import { BuiltRequest } from "./internal/connectionContext.js";
|
|
4
|
+
import { SessionStore } from "./tls/index.js";
|
|
5
|
+
export type ConnectOptions = {
|
|
6
|
+
/** Shared TLS session store to enable resumption (FTPS data channel). */
|
|
7
|
+
sessionStore?: SessionStore;
|
|
8
|
+
/**
|
|
9
|
+
* TLS server name (SNI) for the handshake, when it must differ from the
|
|
10
|
+
* tunnel target host — e.g. a PASV data channel that connects to a bare IP
|
|
11
|
+
* but must resume the control session keyed by its hostname.
|
|
12
|
+
*/
|
|
13
|
+
tlsServerName?: string;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Class to create a raw TCP tunnel between the client and the server and upgrade to TLS.
|
|
17
|
+
*/
|
|
18
|
+
export declare class Connect {
|
|
19
|
+
private config;
|
|
20
|
+
private tlsClient;
|
|
21
|
+
private serverHost;
|
|
22
|
+
private serverPort;
|
|
23
|
+
private ctx;
|
|
24
|
+
constructor(config: TisuraConfig);
|
|
25
|
+
/**
|
|
26
|
+
* Establishes a secure TLS connection to a target server via a proxy tunnel.
|
|
27
|
+
*/
|
|
28
|
+
connectToServer(serverHost: string, serverPort?: number, opts?: ConnectOptions): Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* Establishes a secure TLS connection where the proxy builds the request
|
|
31
|
+
* from its stored config for `providerKey`. Returns the built request fields
|
|
32
|
+
* so the caller can use them for the HTTP request sent through the tunnel.
|
|
33
|
+
*/
|
|
34
|
+
connectToProviderServer(providerKey: string, providerContext?: unknown): Promise<BuiltRequest>;
|
|
35
|
+
/**
|
|
36
|
+
* Sends a message to the server via the established TLS connection.
|
|
37
|
+
* @param {string} path - The path for the HTTP request
|
|
38
|
+
* @param {Array<{ key: string; value: string }>} params - Query parameters
|
|
39
|
+
* @param {string} method - HTTP method (GET, POST, etc.)
|
|
40
|
+
* @param {Array<{ key: string; value: string }>} body - Request body fields
|
|
41
|
+
* @param {Array<{ key: string; value: string }>} headers - HTTP headers
|
|
42
|
+
* @param {Function} onProgress - Optional progress callback
|
|
43
|
+
* @returns {Promise<Uint8Array>} - The server's response
|
|
44
|
+
*/
|
|
45
|
+
sendRequest(path: string, params: Array<{
|
|
46
|
+
key: string;
|
|
47
|
+
value: string;
|
|
48
|
+
}>, method: string, body: Array<{
|
|
49
|
+
key: string;
|
|
50
|
+
value: string;
|
|
51
|
+
}>, headers: Array<{
|
|
52
|
+
key: string;
|
|
53
|
+
value: string;
|
|
54
|
+
}>, onProgress?: (bytesReceived: number, totalBytes?: number) => void): Promise<Uint8Array>;
|
|
55
|
+
/**
|
|
56
|
+
* Registers a callback to receive decrypted TLS application-data bytes,
|
|
57
|
+
* bypassing the HTTP response accumulator. Used by non-HTTP protocols.
|
|
58
|
+
*/
|
|
59
|
+
setOnDecryptedData(cb: (data: Uint8Array) => void): void;
|
|
60
|
+
/**
|
|
61
|
+
* Registers a callback fired when the underlying WebSocket closes. Used by
|
|
62
|
+
* non-HTTP protocols whose end-of-stream signal is the socket close.
|
|
63
|
+
*/
|
|
64
|
+
setOnClose(cb: () => void): void;
|
|
65
|
+
/**
|
|
66
|
+
* Encrypts and sends raw bytes over the established TLS session. Throws if
|
|
67
|
+
* the handshake has not completed or the connection is not initialized.
|
|
68
|
+
*/
|
|
69
|
+
sendEncrypted(data: Uint8Array): void;
|
|
70
|
+
/**
|
|
71
|
+
* Drains and returns the TLS session keys captured during this connection's
|
|
72
|
+
* handshake. Returns [] if the TLS connection is no longer available (e.g.
|
|
73
|
+
* after disconnect). Calling this twice returns [] the second time.
|
|
74
|
+
*/
|
|
75
|
+
takeSessionKeys(): SessionKey[];
|
|
76
|
+
/**
|
|
77
|
+
* Disconnects from the server and cleans up resources.
|
|
78
|
+
* Closes the WebSocket connection and resets internal state variables.
|
|
79
|
+
*/
|
|
80
|
+
disconnectFromServer(): Promise<void>;
|
|
81
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { TisuraConfig } from "../../config.js";
|
|
2
|
+
import { SessionKey } from "../../inputs.js";
|
|
3
|
+
export type FtpsDownloadResult = {
|
|
4
|
+
bytes: Uint8Array;
|
|
5
|
+
controlSessionKeys: SessionKey[];
|
|
6
|
+
dataSessionKeys: SessionKey[];
|
|
7
|
+
pasvAddr: string;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Minimal implicit-FTPS client (port 990) that downloads a single file via
|
|
11
|
+
* two tunnels (control + data). POC scope: USER/PASS auth, binary transfer,
|
|
12
|
+
* passive mode, no explicit AUTH TLS upgrade, no listings, no resume.
|
|
13
|
+
*/
|
|
14
|
+
export declare class FtpClient {
|
|
15
|
+
private config;
|
|
16
|
+
constructor(config: TisuraConfig);
|
|
17
|
+
download(host: string, user: string, pass: string, remotePath: string): Promise<FtpsDownloadResult>;
|
|
18
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { TisuraBase, TisuraConfig } from "../config.js";
|
|
2
|
+
import { SessionKey } from "../inputs.js";
|
|
3
|
+
type KV = {
|
|
4
|
+
key: string;
|
|
5
|
+
value: string;
|
|
6
|
+
};
|
|
7
|
+
type OnProgress = (bytesReceived: number, totalBytes?: number) => void;
|
|
8
|
+
export type SendRequestOptions = {
|
|
9
|
+
serverHost: string;
|
|
10
|
+
endpoint: string;
|
|
11
|
+
params: KV[];
|
|
12
|
+
method: string;
|
|
13
|
+
body: KV[];
|
|
14
|
+
headers?: KV[];
|
|
15
|
+
serverPort?: number;
|
|
16
|
+
onProgress?: OnProgress;
|
|
17
|
+
maxRedirects?: number;
|
|
18
|
+
};
|
|
19
|
+
export type SendProviderRequestOptions = {
|
|
20
|
+
providerKey: string;
|
|
21
|
+
providerContext?: unknown;
|
|
22
|
+
headers?: KV[];
|
|
23
|
+
onProgress?: OnProgress;
|
|
24
|
+
maxRedirects?: number;
|
|
25
|
+
};
|
|
26
|
+
export declare class ConnectClient extends TisuraBase {
|
|
27
|
+
constructor(config: TisuraConfig);
|
|
28
|
+
/**
|
|
29
|
+
* Establishes a secure TLS connection to the server via a proxy tunnel.
|
|
30
|
+
* Sends a request to the server and waits for a response.
|
|
31
|
+
* Closes the connection after receiving the response.
|
|
32
|
+
* @param onProgress - Optional callback for download progress (bytesReceived, totalBytes)
|
|
33
|
+
*/
|
|
34
|
+
sendRequest(opts: SendRequestOptions): Promise<Uint8Array>;
|
|
35
|
+
/**
|
|
36
|
+
* Variant of {@link sendRequest} that also returns the TLS session keys
|
|
37
|
+
* captured during the request. Lets callers (e.g.
|
|
38
|
+
* `sendRequestAndStampPdf`) avoid round-tripping through localStorage.
|
|
39
|
+
* Useful in environments without browser storage, like Node.
|
|
40
|
+
*
|
|
41
|
+
* Redirect semantics: if the request follows redirects, the returned
|
|
42
|
+
* `sessionKeys` are from the **final hop only** — the connection that
|
|
43
|
+
* actually delivered the response. Keys from intermediate hops are
|
|
44
|
+
* discarded with their connections. If you need keys for an intermediate
|
|
45
|
+
* hop, disable redirects (`maxRedirects: 0`) and follow them manually.
|
|
46
|
+
*/
|
|
47
|
+
sendRequestWithKeys({ serverHost, endpoint, params, method, body, headers, serverPort, onProgress, maxRedirects, }: SendRequestOptions): Promise<{
|
|
48
|
+
response: Uint8Array;
|
|
49
|
+
sessionKeys: SessionKey[];
|
|
50
|
+
}>;
|
|
51
|
+
/**
|
|
52
|
+
* Sends a request built by the proxy from the provider's stored template.
|
|
53
|
+
*/
|
|
54
|
+
sendProviderRequest({ providerKey, providerContext, headers, onProgress, maxRedirects, }: SendProviderRequestOptions): Promise<Uint8Array>;
|
|
55
|
+
private sendHop;
|
|
56
|
+
private withConnection;
|
|
57
|
+
private followRedirects;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Process the complete response after all data has been received.
|
|
61
|
+
*/
|
|
62
|
+
export declare function extractHeadersAndBodyFromFullResponse(fullData: Uint8Array): {
|
|
63
|
+
body: Uint8Array;
|
|
64
|
+
headers: Record<string, string>;
|
|
65
|
+
};
|
|
66
|
+
export declare function processCompleteResponse(data: Uint8Array): string | Blob;
|
|
67
|
+
export {};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { TlsConnection } from "../tls/wasm/tls.js";
|
|
2
|
+
export type BuiltRequest = {
|
|
3
|
+
serverHost: string;
|
|
4
|
+
serverPort?: number;
|
|
5
|
+
method: string;
|
|
6
|
+
endpoint: string;
|
|
7
|
+
params: Array<{
|
|
8
|
+
key: string;
|
|
9
|
+
value: string;
|
|
10
|
+
}>;
|
|
11
|
+
headers: Array<{
|
|
12
|
+
key: string;
|
|
13
|
+
value: string;
|
|
14
|
+
}>;
|
|
15
|
+
body?: unknown;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Holds all state for a single connection instance.
|
|
19
|
+
* Eliminates global state by encapsulating WebSocket, TLS, and response tracking.
|
|
20
|
+
*/
|
|
21
|
+
export declare class ConnectionContext {
|
|
22
|
+
ws: WebSocket | null;
|
|
23
|
+
isTunnelEstablished: boolean;
|
|
24
|
+
tlsConnection: TlsConnection | undefined;
|
|
25
|
+
hasTlsSessionStarted: boolean;
|
|
26
|
+
hasCompletedHandshake: boolean;
|
|
27
|
+
hasCompletedSession: boolean;
|
|
28
|
+
wasmInitialized: boolean;
|
|
29
|
+
pendingResponse: ResponseAccumulator | null;
|
|
30
|
+
onDecryptedData?: (data: Uint8Array) => void;
|
|
31
|
+
onClose?: () => void;
|
|
32
|
+
/**
|
|
33
|
+
* Clean up all resources for this connection
|
|
34
|
+
*/
|
|
35
|
+
cleanup(): void;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Type definition for response tracking
|
|
39
|
+
*/
|
|
40
|
+
export interface ResponseAccumulator {
|
|
41
|
+
buffer: Uint8Array;
|
|
42
|
+
isComplete: boolean;
|
|
43
|
+
resolve: (value: Uint8Array) => void;
|
|
44
|
+
reject: (reason: any) => void;
|
|
45
|
+
absoluteTimeout: ReturnType<typeof setTimeout>;
|
|
46
|
+
idleTimeout: ReturnType<typeof setTimeout>;
|
|
47
|
+
startTime: number;
|
|
48
|
+
onProgress?: (bytesReceived: number, totalBytes?: number) => void;
|
|
49
|
+
contentLength?: number;
|
|
50
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { ConnectionContext } from "./connectionContext.js";
|
|
2
|
+
/**
|
|
3
|
+
* Create the HTTP request, encrypt it and send it to the server.
|
|
4
|
+
*/
|
|
5
|
+
export declare function createAndEncryptHttpRequest(ctx: ConnectionContext, serverHost: string, path: string, params: Array<{
|
|
6
|
+
key: string;
|
|
7
|
+
value: string;
|
|
8
|
+
}>, method: string, body: Array<{
|
|
9
|
+
key: string;
|
|
10
|
+
value: string;
|
|
11
|
+
}>, headers: Array<{
|
|
12
|
+
key: string;
|
|
13
|
+
value: string;
|
|
14
|
+
}>, serverPort?: number): Uint8Array;
|
|
15
|
+
/**
|
|
16
|
+
* Create the HTTP request.
|
|
17
|
+
*/
|
|
18
|
+
export declare function createHttpRequest(serverHost: string, path: string, params: Array<{
|
|
19
|
+
key: string;
|
|
20
|
+
value: string;
|
|
21
|
+
}>, method: string, body: Array<{
|
|
22
|
+
key: string;
|
|
23
|
+
value: string;
|
|
24
|
+
}>, extraHeaders: Array<{
|
|
25
|
+
key: string;
|
|
26
|
+
value: string;
|
|
27
|
+
}>, serverPort?: number): string;
|
|
28
|
+
/**
|
|
29
|
+
* Handles the server response received over the TLS connection.
|
|
30
|
+
*/
|
|
31
|
+
export declare function handleServerResponse(ctx: ConnectionContext, onProgress?: (bytesReceived: number, totalBytes?: number) => void): Promise<Uint8Array>;
|
|
32
|
+
/**
|
|
33
|
+
* Called by TLS layer to notify HTTP layer of new data.
|
|
34
|
+
*/
|
|
35
|
+
export declare function notifyTlsDataReceived(ctx: ConnectionContext, data: Uint8Array): void;
|
|
36
|
+
export declare function parseHeaders(headerText: string): Record<string, string>;
|
|
37
|
+
export declare function isChunkedBodyComplete(body: Uint8Array): boolean;
|
|
38
|
+
export declare function parseChunkedBody(body: Uint8Array): Uint8Array;
|
|
39
|
+
/**
|
|
40
|
+
* Gets appropriate MIME type and file type for binary content
|
|
41
|
+
*/
|
|
42
|
+
export declare function getBinaryContentInfo(contentType: string, headers: Record<string, string>): {
|
|
43
|
+
mimeType: string;
|
|
44
|
+
fileType: string;
|
|
45
|
+
};
|
|
46
|
+
export declare function handleContentType(bodyBytes: Uint8Array, headers: Record<string, string>): string | Blob;
|
|
47
|
+
/**
|
|
48
|
+
* Split the headers and body of the HTTP response.
|
|
49
|
+
*/
|
|
50
|
+
export declare function splitHeadersAndBody(data: Uint8Array): {
|
|
51
|
+
headers: string;
|
|
52
|
+
body: Uint8Array;
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Parse the server response.
|
|
56
|
+
*/
|
|
57
|
+
export declare function parseServerResponse(responseData: string): string;
|