npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

jose-stream

v0.7.0

Published

Signed and encrypted streams with JOSE (JWE, JWS, JWK) and JSONL

Downloads

3

Readme

JOSE-Stream

Signed and encrypted streams with JOSE (JWE, JWS, JWK) and JSONL/NDJSON

JWE and JWS have become popular formats for the exchange of encrypted and signed data on the web. While they are well-suited for encryption and signing of short messages, they're less suitable for long messages and large files owing to the lack of streaming JSON or JWE parsers. Typically an entire JWE or JWS is held in memory during encryption/decryption/signing/verification, which imposes limits on scalability in memory-constrained and server environments. In addition, multiple layers of Base64 encoding result in additional bandwidth and storage overhead when utilizing sign-encrypt or sign-encrypt-sign pipelines.

JOSE-Stream proposes a streaming signing and encryption format based on JWS and JWE. Plaintext is signed, compressed, and split into fixed-length chunks. Chunks are encrypted with JWE and—along with a JWE header and JWS signatures—are streamed in the JSONL line-delimited JSON format. Compression and signatures are both optional.

For implementation examples see JOST, a command line tool for working with JOSE streams, which depends upon this jose-stream package.

Warning

  • Beta
  • Until it hits 1.0 there will be frequent changes to API and format
  • Some features not functional or buggy
  • Bug reports welcome
  • PRs welcome but get in touch first

Install

$ npm install jose-stream

API Documentation

jose-stream API Documentation

Format

  • Newline-delimited UTF-8, one JSON per line.
  • LF is preferred, but CRLF is allowed, as JSON parsers will eat the CR as whitespace
  • Each line contains one complete JWE or JWS serialization, which itself must not contain any unescaped newline characters
  • All JWE or JWS instances must contain a "seq" sequence property in their protected headers
  • The value of the "seq" sequence property is an unsigned integer, starting at 0, and incrementing by 1 with each subsequent JWE or JWS instance
  • A JWE or JWS instance with a missing or out-of-order "seq" sequence value must invalidate the entire JSON-Stream

Header

The header is a JWE using the fully general JWE JSON Serialization syntax. Its "typ" property is "jose-stream". It carries:

  • In the ciphertext, the secret key used to encrypt all subsequent JWE instances
  • (Optionally) the public key corresponding to the private key used to sign all subsequent JWS instances
  • The identities of the digest, compression, and encryption algorithms used with subsequent JWE and JWS instances, in the "dig", "cmp", and "enc" properties, respectively

Header Tag Signature

A tag signature is a JWS signature of the digest of the concatenation of base64url decoded authentication tags from all prior JWEs. In the case of the header tag signature, there is only one prior JWE — the header.

The header tag signature is a JWS using the flattened JWS JSON Serialization syntax. Its "typ" property is "tag". It is produced by signing the base64url decoded "tag" value of the preceding header instance using the private key corresponding to the "pub" public-key included in the header instance. It is conditional, and is required if the header instance includes a public key in its "pub" value. Otherwise it must not appear in the jose-stream.

Body

A body instance is a JWE using the flattened JWE JSON Serialization syntax. Its "typ" property is "bdy". It is produced by encrypting a chunk of the (optionally compressed) plaintext using the secret key encrypted and base64url encoded in the ciphertext property of the header instance. Multiple body instances are allowed, one for each fixed-size chunk of the compressed plaintext. The final body instance in the jose-stream must include the boolean value true in its "end" property.

Content Signature

The content signature is a JWE using the flattened JWE JSON Serialization syntax. Its "typ" property is "sig". It is produced by encrypting a JWS instance using the secret key encrypted and base64url encoded in the ciphertext property of the header instance. The JWS instance is produced by signing a digest of the plaintext using the private key corresponding to the "pub" public-key included in the header instance. It is conditional, and is required if the header instance includes a public key in its "pub" value. Otherwise it must not appear in the jose-stream.

Final Tag Signature

A tag signature is a JWS signature of the digest of the concatenation of base64url decoded authentication tags from all prior JWEs.

The final tag signature is a JWS using the flattened JWS JSON Serialization syntax. Its "typ" property is "tag". It is produced by signing the base64url decoded and concatenated "tag" values of all preceding JWE instances using the private key corresponding to the "pub" public-key included in the header instance. It is conditional, and is required if the header instance includes a public key in its "pub" value. Otherwise it must not appear in the jose-stream.

EBNF Grammar

Here's an incomplete and non-standard EBNF-ish grammar describing the format:

State diagram

Magnifying glass not included

An XState state diagram

Example jose-stream formatted file

An example of a jose-stream formatted file with a single recipient, with protected headers decoded from base64url to JSON, pretty-printed, and with each newline marked explicitly:

{
  "protected": {
    "typ": "jose-stream",
    "pub": {
      "crv": "Ed25519",
      "x": "cXbDRvACe2NSsaTpOOWUZv_mH1wiPoE6Y5Jff4IyWiM",
      "kty": "OKP"
    },
    "dig": "blake2b512",
    "cmp": "DEF",
    "enc": "A256GCM",
    "seq": 0,
    "epk": {
      "x": "qBBa0dpSYokFMmHt6s0KIKs1cFfqXtfJnXKV8y169T0",
      "crv": "X25519",
      "kty": "OKP"
    }
  },
  "recipients": [
    {
      "encrypted_key": "P2MEQVOueTL7GLCawJJCp0_hvDks78dYKkWQzOa6tf1AsMfHsqGEGQ",
      "header": {
        "alg": "ECDH-ES+A256KW",
        "kid": "OhHmvNaYntMdpoH9LlPyUg9svcMzp3Jqj6zCjKK_rGs"
      }
    }
  ],
  "iv": "OnFMc_YacqlaF7ON",
  "ciphertext": "b-Lr_JteXKj9yt22cMTP37n1E9yrPLhqK5l0pdfEof_lg8PHe2TqRG5hSPNpzCOhG0iOMMb-VMBV4KjBFwg9",
  "tag": "46GkceBB8ALz1kE7HwbrCQ"
}
<newline>
{
  "protected": {
    "typ": "tag",
    "alg": "EdDSA",
    "crv": "Ed25519",
    "b64": false,
    "seq": 1
  },
  "signature": "7UoTDnGuC-RYE2pI1lUgbcWSn057GY5vaugPXijKmDVR_n9iRdwa0G36KAYWx7dLNCT93yYIlslgAgFrZIh9Dw"
}
<newline>
{
  "protected": {
    "typ": "bdy",
    "alg": "dir",
    "enc": "A256GCM",
    "end": true,
    "seq": 2
  },
  "iv": "kgVytUX9Xx24SgaG",
  "ciphertext": "aWkkgmPgGUspNW_kWjW_tL3G947dD6IUA3-RTPeg2ssjqDYBbhGQzlj1SPdtZdMN0Tt2g4xAEkeqUjH2Q393h-FZ5ZUux_P8ARqba__Keqn6mJukEnMNqlVPZDaOersUSZ3lBxGMI9pUWFbl-9mYQEDxK1xt0UwUIpXwnRSdMOJynyWWhMrKzFNvUTIQ3UMwDOTB33vH8yj-8LtlTrvFwHJH_Lw6mrPTJSmd1QyTY8lvMgECMsqEGGBqsISljGRWMA4j5D-wpfLozaiHyd4G6MTjMBHZdg",
  "tag": "iV2ghlZlF3SlLEw6DDZ5xg"
}
<newline>
{
  "protected": {
    "typ": "sig",
    "alg": "dir",
    "enc": "A256GCM",
    "seq": 3
  },
  "iv": "zTXmMgqpkB-0-t2R",
  "ciphertext": "gZ3ilSyj1mWSg4kH0JiscyeP8U1V9UX1sTlijc9IaoUOYN2BaH4_In7Pzn1pHBWNCyV-zyhKBj57iayLlb2v_Ne4zAV1adt7E0soF70rNcjjlncPi67zPgXnYYLICJ_4Xg4l1UEwbaeGP2eTIQtDA8WQdAvPnfea4e6RSwy3_358y0EZBctQF-6S4LLlcyqpOMT_j8rqpzbIJTq6sSKbIbpnnF_6Ygl307WLVFLR9Q",
  "tag": "JVN9qtLtm75u9crHcB4RXg"
}
<newline>
{
  "protected": {
    "typ": "tag",
    "alg": "EdDSA",
    "crv": "Ed25519",
    "b64": false,
    "seq": 4
  },
  "signature": "swqq_9RkAsUkRjcrfs979UlqZOix35C1D-dFGzcbo7h4cTDMxq07Ee7N4x983uvG-DDgdnMoYwjBJsBgpTxMCg"
}
<newline>

Goals:

  • Be secure. Follow best practices.
  • Signing always signs both plaintext and ciphertext. Why?
  • Work within the standards: Read and write JOSE-Stream encoded streams utilizing existing JOSE framework libraries. Try to invent as little as possible.
  • Allow stream readers to invalidate a stream as early as is practical, e.g. after reading a header without a valid public key, or a missing header tag signature, or a body chunk that can't be decrypted.
  • Eventual binary format(s) (BSON? protobuf?). This should not be difficult given that JOSE uses base64url encoding to establish boundaries between format-sensitive and format-insensitive portions of the standard (see: RFC 7165 § 6.3 D2 Avoid JSON canonicalization to the extent possible.)

Similar to JOSE-Stream: