glow.ts 1.2.1 → 1.3.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/dist/index.d.ts CHANGED
@@ -221,27 +221,69 @@ export declare class Mailbox {
221
221
  /**
222
222
  * Sends a free-form object to a guest we already have in our keyring. Set `encrypt` to `false` to
223
223
  * send a plaintext message. Returns a token that can be used with `messageStatus` command to check
224
- * the status of the message
224
+ * the status of the message.
225
+ *
226
+ * WARNING: a plaintext message (`encrypt = false`) has no confidentiality and no authenticity:
227
+ * on receipt it is indistinguishable from a message forged by the relay. `download` never
228
+ * returns it as an authenticated `message`: it is classified as `ZaxMessageKind.unverified` if
229
+ * the recipient already has the sender's key in their keyring, or as `ZaxMessageKind.plain` if
230
+ * they do not. Only use plaintext for bootstrap flows where the recipient does not have the
231
+ * sender's key yet, and never trust its content
225
232
  */
226
233
  upload(url: string, guestKey: string, message: string, encrypt?: boolean): Promise<Base64>;
227
234
  /**
228
235
  * Downloads all messages from a relay, decrypts them with a relay key,
229
- * and then parses each message to find out if it's a text message, file message,
230
- * or if it can't be decrypted because HPK is missing in the keyring.
231
- * Returns an array of mixed messages
236
+ * and then parses each message to find out if it's an authenticated text message (`message`),
237
+ * a file message (`file`), a message from a sender whose HPK is missing in the keyring
238
+ * (`plain`), or a message claiming to be from a known sender whose payload failed
239
+ * authenticated decryption (`unverified`). Returns an array of mixed messages.
240
+ *
241
+ * A single malformed message — text or file — never rejects the batch: when its sender
242
+ * is in the keyring it is returned as `unverified` alongside the messages that did parse
243
+ * (an unknown sender's payload is returned as `plain` in any case), and the same applies
244
+ * to a message with an unknown `kind`, which an untrusted relay may set to an arbitrary value
232
245
  */
233
246
  download(url: string): Promise<ZaxParsedMessage[]>;
247
+ /**
248
+ * Classifies a single raw relay message by sender and `kind`
249
+ */
250
+ private parseMessage;
234
251
  /**
235
252
  * Marks a raw Zax message as one that can't be decrypted,
236
253
  * because sender's HPK is not found in the keyring
237
254
  */
238
255
  private parsePlainMessage;
239
256
  /**
240
- * Decrypts a message that represents uploaded file metadata
257
+ * Marks a raw Zax message claiming to be from a known sender as one that failed
258
+ * authentication, passing the relay-supplied payload through untouched
259
+ */
260
+ private markUnverified;
261
+ /**
262
+ * Decrypts a message that represents uploaded file metadata. A payload that can not be
263
+ * authenticated (a garbage envelope, a forged ciphertext, or malformed metadata)
264
+ * is returned as `ZaxMessageKind.unverified` with the raw relay-supplied bytes.
265
+ *
266
+ * Unlike text messages, file metadata is always encrypted on upload (`startFileUpload`
267
+ * has no plaintext option), so an `unverified` file message always indicates forgery,
268
+ * tampering, or corruption — never a legitimate plaintext upload. Authenticated metadata
269
+ * is returned as a `file` whatever fields it carries, see `isFileMetadata`
241
270
  */
242
271
  private parseFileMessage;
272
+ private static readonly fileMetadataFieldTypes;
273
+ /**
274
+ * Runtime check of decrypted file metadata: `JSON.parse` alone accepts any JSON value
275
+ * (`null`, `42`, `[]`), so require a JSON object. No field is mandatory: which ones a sender
276
+ * includes is its own contract with the recipient (a client may send nothing but `orig_size`
277
+ * and the `skey` added by `startFileUpload`), and a missing field says nothing about
278
+ * authenticity — the payload already passed authenticated decryption. A field that is present
279
+ * must have its declared type, so that `ReceivedFileMetadata` holds for every `file` message
280
+ */
281
+ private static isFileMetadata;
243
282
  /**
244
- * Attempts to decrypt a regular encrypted Zax message. Returns plain message if it was sent encrypted
283
+ * Attempts authenticated decryption of a regular Zax message. A payload that can not be
284
+ * authenticated with the sender's key (a plaintext upload, a forged message, or a tampered
285
+ * ciphertext — indistinguishable cases on receipt) is returned as `ZaxMessageKind.unverified`
286
+ * with the raw relay-supplied bytes, so that the application can decide whether to trust it
245
287
  */
246
288
  private parseTextMessage;
247
289
  /**
@@ -265,7 +307,8 @@ export declare class Mailbox {
265
307
  */
266
308
  startFileUpload(url: string, guest: string, rawMetadata: FileUploadMetadata): Promise<StartFileUploadResponse>;
267
309
  /**
268
- * Encrypts the file chunk symmetrically and transfers it to a relay
310
+ * Encrypts the file chunk symmetrically and transfers it to a relay.
311
+ * The chunk must not exceed `max_chunk_size` returned by `startFileUpload`.
269
312
  */
270
313
  uploadFileChunk(url: string, uploadID: string, chunk: Uint8Array, part: number, totalParts: number, skey: Uint8Array): Promise<UploadFileChunkResponse>;
271
314
  /**
@@ -275,9 +318,10 @@ export declare class Mailbox {
275
318
  */
276
319
  getFileStatus(url: string, uploadID: string): Promise<FileStatusResponse>;
277
320
  /**
278
- * Fetches the file metadata by uploadID, which was declared by the uploader
321
+ * Fetches the file metadata by uploadID, which was declared by the uploader,
322
+ * or `undefined` when the mailbox holds no `file` message with that uploadID
279
323
  */
280
- getFileMetadata(url: string, uploadID: string): Promise<FileUploadMetadata>;
324
+ getFileMetadata(url: string, uploadID: string): Promise<ReceivedFileMetadata | undefined>;
281
325
  /**
282
326
  * Downloads a binary chunk of a file from a relay by a given uploadID. The total number of chunks
283
327
  * can be retrieved via a `getFileStatus` request
@@ -316,7 +360,9 @@ export declare class Mailbox {
316
360
  encodeMessage(guest: string, message: string): Promise<EncryptedMessage>;
317
361
  /**
318
362
  * Decodes a ciphertext from a guest key already in our keyring with this nonce
319
- * @returns null if failed to decode
363
+ * @returns null if the payload could not be authenticated and decrypted with this guest's key.
364
+ * A `null` carries no information about why: the payload may have been sent as plaintext,
365
+ * forged, or tampered with — these cases can not be told apart on the receiving side
320
366
  */
321
367
  decodeMessage(guest: string, nonce: Base64, ctext: Base64): Promise<string | null>;
322
368
  /**
@@ -380,6 +426,14 @@ export declare interface NaClDriver {
380
426
  from_hex(data: string): Promise<Uint8Array>;
381
427
  }
382
428
 
429
+ /**
430
+ * File metadata as it arrives in a `file` message. A sender may include only the fields its
431
+ * recipient needs, so none of them is guaranteed, but a field that is present has the type
432
+ * declared in `FileUploadMetadata`: `Mailbox.download` returns metadata that breaks this as
433
+ * `unverified` instead of a `file`. Fields outside `FileUploadMetadata` pass through untyped
434
+ */
435
+ export declare type ReceivedFileMetadata = Partial<FileUploadMetadata>;
436
+
383
437
  /**
384
438
  * Low-level operations with Zax relay
385
439
  */
@@ -517,7 +571,7 @@ export declare class Utils {
517
571
  }
518
572
 
519
573
  export declare interface ZaxFileMessage {
520
- data: FileUploadMetadata;
574
+ data: ReceivedFileMetadata;
521
575
  time: number;
522
576
  senderTag: string;
523
577
  uploadID: string;
@@ -528,10 +582,11 @@ export declare interface ZaxFileMessage {
528
582
  export declare enum ZaxMessageKind {
529
583
  message = "message",
530
584
  file = "file",
531
- plain = "plain"
585
+ plain = "plain",
586
+ unverified = "unverified"
532
587
  }
533
588
 
534
- export declare type ZaxParsedMessage = ZaxPlainMessage | ZaxFileMessage | ZaxTextMessage;
589
+ export declare type ZaxParsedMessage = ZaxPlainMessage | ZaxFileMessage | ZaxTextMessage | ZaxUnverifiedMessage;
535
590
 
536
591
  export declare interface ZaxPlainMessage {
537
592
  data: string;
@@ -553,4 +608,29 @@ export declare interface ZaxTextMessage {
553
608
  kind: ZaxMessageKind.message;
554
609
  }
555
610
 
611
+ /**
612
+ * A message that claims to come from a known guest (the relay-supplied sender HPK
613
+ * matched a keyring entry), but whose payload could NOT be authenticated with that
614
+ * guest's key. This happens both when the sender deliberately uploaded plaintext
615
+ * (`upload` with `encrypt = false`) and when the ciphertext was forged or tampered
616
+ * with in transit or by the relay — the two cases are indistinguishable on receipt.
617
+ * The claimed `senderTag` is therefore unproven: never treat `data` as authentic
618
+ * unless the application has its own reason to trust unauthenticated content
619
+ * on this channel.
620
+ */
621
+ export declare interface ZaxUnverifiedMessage {
622
+ /**
623
+ * Raw payload exactly as supplied by the relay, not authenticated or decrypted
624
+ */
625
+ data: string;
626
+ time: number;
627
+ /**
628
+ * Tag of the keyring guest the message claims to be from. NOT verified
629
+ */
630
+ senderTag: string;
631
+ from: Base64;
632
+ nonce: Base64;
633
+ kind: ZaxMessageKind.unverified;
634
+ }
635
+
556
636
  export { }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "glow.ts",
3
- "version": "1.2.1",
3
+ "version": "1.3.1",
4
4
  "displayName": "Glow.ts",
5
5
  "description": "Client library for interacting with Zax Cryptographic Relay Servers",
6
6
  "keywords": [
@@ -52,7 +52,7 @@
52
52
  "devDependencies": {
53
53
  "@eslint/js": "10.0.1",
54
54
  "@jest/types": "30.4.1",
55
- "@microsoft/api-extractor": "^7.57.7",
55
+ "@microsoft/api-extractor": "7.59.1",
56
56
  "@types/jest": "30.0.0",
57
57
  "@types/node": "24.5.2",
58
58
  "eslint": "10.6.0",