Give JWK independent value semantics for thread-safe copies

This commit is contained in:
jdb committed 2026-09-29 08:26:50 -05:00
1 parent 47393ec84f
commit d037c84e8c
8 files changed
+220 -9

No files matched your search

+24 -6
View File
@@ -11,7 +11,7 @@
##
## Nim implementation of the data structures and constants defined in RFC 7518.
import std/json, std/options, std/sequtils, std/strutils
import std/json, std/options, std/sequtils, std/strutils, std/tables
import ./jwa
import ./private/jsonutils
@@ -56,7 +56,12 @@ type
k*: string
JWK* = object
json: JsonNode
## Owns its key material and metadata with value semantics. Copies may be
## handed to separate threads; synchronize access to a source being mutated.
## JSON member access returns a fresh node, independent of the key.
jsonMembers: Table[string, string]
# Keep arbitrary JSON members as encoded values, without JsonNode refs.
# Cryptographic operations use the parsed fields below, without decoding.
kty: string
use: Option[string]
key_ops: Option[string]
@@ -96,8 +101,18 @@ func rsaPrv*(jwk: JWK): RsaPrvKey = jwk.rsaPrv
func octKey*(jwk: JWK): OctetKey = jwk.octKey
func `[]`*(jwk: JWK, key: string): Option[JsonNode] =
if jwk.json.hasKey(key): some(jwk.json[key])
else: none[JsonNode]()
## Return an independent JSON value for a member, or none if it is absent.
if jwk.jsonMembers.hasKey(key):
# The string parser only touches a newly allocated in-memory stream, but
# its stream-based implementation is not annotated noSideEffect in std/json.
{.cast(noSideEffect).}:
result = some(parseJson(jwk.jsonMembers[key]))
func `%`*(jwk: JWK): JsonNode =
## Serialize the key and its extension members to an independent JSON object.
result = newJObject()
for name in jwk.jsonMembers.keys:
result[name] = jwk[name].get
# Public Parsing Functions
# ------------------------
@@ -143,7 +158,9 @@ func parseOctetKey*(n: JsonNode): OctetKey =
OctetKey(k: n.reqStrVal("k"))
func initJwk*(n: JsonNode): JWK =
# TODO: documentation, example, handle encrypted keys (JWEs)
## Parse an owned snapshot of a key, including its extension metadata.
## Later changes to n or to nodes returned by [] do not affect the key.
# TODO: example, handle encrypted keys (JWEs)
case n["kty"].getStr(""):
of $ktyEC:
@@ -179,7 +196,8 @@ func initJwk*(n: JsonNode): JWK =
else: raise newException(ValueError,
"Unrecognized or missing kty: '" & n["kty"].getStr("") & "'")
result.json = n
for name, value in n.pairs:
result.jsonMembers[name] = $value
result.use = n.optStrVal("use")
result.key_ops = n.optStrVal("key_ops")
result.alg = n.optStrVal("alg")