Verify exactly one compact JWS inside one authenticated compact JWE.
Both policies are explicit; no recursive dispatch or fallback occurs. Failure
returns a redacted RequestSeal.JOSE.Error, never a decrypted partial result.
These are envelope facts, not JWT claims validation or authorization.
Summary
Functions
Decrypt one compact JWE, then verify the compact JWS it carries.
Functions
@spec verify(binary(), map(), map(), keyword()) :: {:ok, map()} | {:error, RequestSeal.JOSE.Error.t()}
Decrypt one compact JWE, then verify the compact JWS it carries.
The outer protected cty must be one of the selected content types. Inner
envelopes (plaintext that is not a three-segment compact JWS) reject as
:nesting_depth. So does an inner JWS whose cty names a JOSE content type
— the registered application/jose and application/jose+json, the
per-structure application/jws and application/jwe, and RFC 7519's
application/jwt — all case-insensitively, with RFC 7515 Section 4.1.10's
abbreviation rule (a value with no / prepends application/, so jose,
JWS, and JWT land in that set) — because such a value declares the
payload is another JOSE envelope and this module verifies exactly one level.
Any other inner cty (for example application/json) names ordinary typed
payload content and is the application's business. Only complete success
returns a result. Each policy supplies its own stage timeout; there is no
combined timeout option.
Parameters
compact- the outer compact JWE, as accepted byRequestSeal.JOSE.JWE.decrypt/2.jwe_policy- the decryption policy map forRequestSeal.JOSE.JWE.decrypt/2.jws_policy- the verification policy map forRequestSeal.JOSE.JWS.verify/2.opts- keyword list; required (there is no default).
Options
:content_types- required. Unique nonempty list of at most 64 nonempty strings, each at most 256 bytes, compared exactly with the outercty.
Returns
{:ok, %{jwe: %RequestSeal.JOSE.JWE.Result{}, jws: %RequestSeal.JOSE.JWS.Result{}}}.{:error, %RequestSeal.JOSE.Error{}}-:invalid_options(missing, unknown, duplicate, or invalid option),:content_type_mismatch(outerctyabsent or not selected),:nesting_depth, and every reasonRequestSeal.JOSE.JWE.decrypt/2andRequestSeal.JOSE.JWS.verify/2return. Meanings:RequestSeal.JOSE.Error.
Examples
iex> {_, seed} = :crypto.generate_key(:eddsa, :ed25519)
iex> {:ok, handle} = RequestSeal.Custody.Local.new({:jws, "EdDSA"}, {:ed25519, seed})
iex> {:ok, key} = RequestSeal.Custody.public_key(handle)
iex> {:ok, jws} = RequestSeal.JOSE.JWS.sign([{"alg", "EdDSA"}], ~s({"order":"A-1001","status":"shipped"}), handle)
iex> kek = {:aes, :crypto.strong_rand_bytes(32)}
iex> wrap = fn algorithm, cek -> RequestSeal.JOSE.KeyManagement.wrap(algorithm, cek, %{}, kek) end
iex> {:ok, jwe} = RequestSeal.JOSE.JWE.encrypt([{"alg", "A256GCMKW"}, {"enc", "A256GCM"}, {"cty", "JWT"}], jws, wrap)
iex> unwrap = fn bytes, header -> RequestSeal.JOSE.KeyManagement.unwrap("A256GCMKW", bytes, header, kek) end
iex> jwe_policy = %{algorithms: ["A256GCMKW"], encryption: ["A256GCM"], max_plaintext: 4_096, timeout: 5_000, key_resolver: fn _ -> {:ok, %{algorithm: "A256GCMKW", unwrap: unwrap}} end}
iex> jws_policy = %{algorithms: ["EdDSA"], timeout: 5_000, key_resolver: fn _ -> {:ok, %{algorithm: "EdDSA", key: key}} end}
iex> {:ok, %{jws: inner}} = RequestSeal.JOSE.Nested.verify(jwe, jwe_policy, jws_policy, content_types: ["JWT"])
iex> inner.payload
~s({"order":"A-1001","status":"shipped"})
iex> {:error, error} = RequestSeal.JOSE.Nested.verify(jwe, jwe_policy, jws_policy, content_types: ["application/example+jws"])
iex> error.reason
:content_type_mismatch
iex> RequestSeal.Custody.Local.release(handle)
:ok