gose/jose/jws_multi

JWS JSON Serialization for multi-signer signing and verification (RFC 7515 Section 7.2.1).

Example

import gleam/json
import gose/algorithm
import gose/jose/jws_multi
import gose/key
import kryptos/ec
import kryptos/eddsa

let payload = <<"hello":utf8>>
let k1 = key.generate_ec(ec.P256)
let k2 = key.generate_eddsa(eddsa.Ed25519)

let assert Ok(body) =
  jws_multi.new(payload:)
  |> jws_multi.sign(
    algorithm.DigitalSignature(algorithm.Ecdsa(algorithm.EcdsaP256)),
    key: k1,
  )
let assert Ok(body) =
  body
  |> jws_multi.sign(algorithm.DigitalSignature(algorithm.Eddsa), key: k2)
let multi = jws_multi.assemble(body)

let json_str = jws_multi.serialize_json(multi) |> json.to_string
let assert Ok(parsed) = jws_multi.parse_json(json_str)
let assert Ok(v) =
  jws_multi.verifier(
    algorithm.DigitalSignature(algorithm.Ecdsa(algorithm.EcdsaP256)),
    keys: [k1],
  )
let assert Ok(Nil) = jws_multi.verify(v, parsed)

Phantom States

Body(Building) supports payload configuration (with_detached). Calling sign transitions the body to Body(Signed), which assemble finalizes into a serializable MultiJws. The type system prevents modifying the body after any signature has been computed.

Algorithm Pinning

Each verifier is pinned to a single algorithm. The matched signer’s protected header alg must match the verifier’s expected algorithm.

Types

Outer body for a multi-signer JWS, parameterized by signing state.

pub opaque type Body(state)

Phantom state: body under construction, no signatures yet.

pub type Building

A multi-signer JWS message (JSON General Serialization).

pub opaque type MultiJws

Phantom state: body with at least one signature.

pub type Signed

A verifier pinned to a single algorithm and one or more keys.

pub opaque type Verifier

Values

pub fn assemble(body: Body(Signed)) -> MultiJws

Finalize a signed body into a serializable multi-signer JWS.

pub fn is_detached(message: MultiJws) -> Bool

Check whether the message was built or parsed with a detached payload.

pub fn new(payload payload: BitArray) -> Body(Building)

Create a new body pinned to the payload all signers will sign.

pub fn parse_json(
  json_str: String,
) -> Result(MultiJws, gose.GoseError)

Parse a JWS from JSON General Serialization format. A missing payload field indicates a detached payload per RFC 7515 Appendix F.

pub fn payload(message: MultiJws) -> BitArray

Return the payload. Returns an empty BitArray for messages parsed with a detached payload.

pub fn serialize_json(message: MultiJws) -> json.Json

Serialize as JWS JSON General Serialization. For messages built with with_detached, the payload field is omitted.

pub fn sign(
  body: Body(state),
  alg alg: algorithm.SigningAlg,
  key key: key.Key(String),
) -> Result(Body(Signed), gose.GoseError)

Compute a per-signer JWS signature over the body’s payload and append it to the body. Transitions the body to Signed state, preventing further with_* mutations at compile time.

pub fn verifier(
  alg: algorithm.SigningAlg,
  keys keys: List(key.Key(String)),
) -> Result(Verifier, gose.GoseError)

Build a verifier pinned to a single algorithm and one or more keys.

pub fn verify(
  verifier: Verifier,
  message: MultiJws,
) -> Result(Nil, gose.GoseError)

Verify the first matching signer’s signature.

Returns InvalidState if the message was parsed with a detached payload; use verify_detached instead.

pub fn verify_detached(
  verifier: Verifier,
  message message: MultiJws,
  payload payload: BitArray,
) -> Result(Nil, gose.GoseError)

Verify a detached-payload JWS by supplying the payload at verify time.

pub fn with_detached(body: Body(Building)) -> Body(Building)

Mark the body as using a detached payload (RFC 7515 Appendix F).

The payload is still signed but omitted from the serialized JSON. Callers verify with verify_detached, supplying the payload separately.

Search Document