mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-06-19 13:28:58 -04:00
build(deps): bump github.com/open-policy-agent/opa from 1.15.2 to 1.17.1
Bumps [github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa) from 1.15.2 to 1.17.1. - [Release notes](https://github.com/open-policy-agent/opa/releases) - [Changelog](https://github.com/open-policy-agent/opa/blob/v1.17.1/CHANGELOG.md) - [Commits](https://github.com/open-policy-agent/opa/compare/v1.15.2...v1.17.1) --- updated-dependencies: - dependency-name: github.com/open-policy-agent/opa dependency-version: 1.17.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
24
go.mod
24
go.mod
@@ -61,7 +61,7 @@ require (
|
||||
github.com/onsi/ginkgo v1.16.5
|
||||
github.com/onsi/ginkgo/v2 v2.28.3
|
||||
github.com/onsi/gomega v1.40.0
|
||||
github.com/open-policy-agent/opa v1.15.2
|
||||
github.com/open-policy-agent/opa v1.17.1
|
||||
github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89
|
||||
github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260310090739-853d972b282d
|
||||
github.com/opencloud-eu/reva/v2 v2.46.4-0.20260615073558-209c2cd3b52b
|
||||
@@ -95,7 +95,7 @@ require (
|
||||
go-micro.dev/v4 v4.11.0
|
||||
go.etcd.io/bbolt v1.4.3
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.69.0
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0
|
||||
go.opentelemetry.io/contrib/zpages v0.68.0
|
||||
go.opentelemetry.io/otel v1.44.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.44.0
|
||||
@@ -182,7 +182,7 @@ require (
|
||||
github.com/cyphar/filepath-securejoin v0.6.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/deckarep/golang-set v1.8.0 // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1 // indirect
|
||||
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect
|
||||
github.com/dgraph-io/ristretto v0.2.0 // indirect
|
||||
github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da // indirect
|
||||
@@ -199,7 +199,7 @@ require (
|
||||
github.com/evanphx/json-patch/v5 v5.5.0 // indirect
|
||||
github.com/fatih/color v1.18.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.10.1 // indirect
|
||||
github.com/gdexlab/go-render v1.0.1 // indirect
|
||||
github.com/go-acme/lego/v4 v4.4.0 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect
|
||||
@@ -227,7 +227,7 @@ require (
|
||||
github.com/gobwas/httphead v0.1.0 // indirect
|
||||
github.com/gobwas/pool v0.2.1 // indirect
|
||||
github.com/gobwas/ws v1.2.1 // indirect
|
||||
github.com/goccy/go-json v0.10.5 // indirect
|
||||
github.com/goccy/go-json v0.10.6 // indirect
|
||||
github.com/goccy/go-yaml v1.18.0 // indirect
|
||||
github.com/gofrs/flock v0.13.0 // indirect
|
||||
github.com/gofrs/uuid v4.4.0+incompatible // indirect
|
||||
@@ -263,11 +263,11 @@ require (
|
||||
github.com/kovidgoyal/go-shm v1.0.0 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/lestrrat-go/blackmagic v1.0.4 // indirect
|
||||
github.com/lestrrat-go/dsig v1.0.0 // indirect
|
||||
github.com/lestrrat-go/dsig v1.2.1 // indirect
|
||||
github.com/lestrrat-go/dsig-secp256k1 v1.0.0 // indirect
|
||||
github.com/lestrrat-go/httpcc v1.0.1 // indirect
|
||||
github.com/lestrrat-go/httprc/v3 v3.0.2 // indirect
|
||||
github.com/lestrrat-go/jwx/v3 v3.0.13 // indirect
|
||||
github.com/lestrrat-go/httprc/v3 v3.0.5 // indirect
|
||||
github.com/lestrrat-go/jwx/v3 v3.1.1 // indirect
|
||||
github.com/lestrrat-go/option/v2 v2.0.0 // indirect
|
||||
github.com/libregraph/oidc-go v1.1.0 // indirect
|
||||
github.com/longsleep/go-metrics v1.0.0 // indirect
|
||||
@@ -327,7 +327,7 @@ require (
|
||||
github.com/prometheus/alertmanager v0.31.1 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.67.5 // indirect
|
||||
github.com/prometheus/procfs v0.17.0 // indirect
|
||||
github.com/prometheus/procfs v0.20.1 // indirect
|
||||
github.com/prometheus/statsd_exporter v0.22.8 // indirect
|
||||
github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9 // indirect
|
||||
github.com/rs/xid v1.6.0 // indirect
|
||||
@@ -365,8 +365,8 @@ require (
|
||||
github.com/toorop/go-dkim v0.0.0-20201103131630-e1cd1a0a5208 // indirect
|
||||
github.com/trustelem/zxcvbn v1.0.1 // indirect
|
||||
github.com/urfave/cli/v2 v2.27.7 // indirect
|
||||
github.com/valyala/fastjson v1.6.7 // indirect
|
||||
github.com/vektah/gqlparser/v2 v2.5.32 // indirect
|
||||
github.com/valyala/fastjson v1.6.10 // indirect
|
||||
github.com/vektah/gqlparser/v2 v2.5.33 // indirect
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||
github.com/wk8/go-ordered-map v1.0.0 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||
@@ -386,7 +386,7 @@ require (
|
||||
go.opentelemetry.io/proto/otlp v1.10.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/mod v0.35.0 // indirect
|
||||
golang.org/x/sys v0.45.0 // indirect
|
||||
|
||||
52
go.sum
52
go.sum
@@ -196,8 +196,8 @@ github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/
|
||||
github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c=
|
||||
github.com/butonic/go-micro/v4 v4.11.1-0.20241115112658-b5d4de5ed9b3 h1:h8Z0hBv5tg/uZMKu8V47+DKWYVQg0lYP8lXDQq7uRpE=
|
||||
github.com/butonic/go-micro/v4 v4.11.1-0.20241115112658-b5d4de5ed9b3/go.mod h1:eE/tD53n3KbVrzrWxKLxdkGw45Fg1qaNLWjpJMvIUF4=
|
||||
github.com/bytecodealliance/wasmtime-go/v39 v39.0.1 h1:RibaT47yiyCRxMOj/l2cvL8cWiWBSqDXHyqsa9sGcCE=
|
||||
github.com/bytecodealliance/wasmtime-go/v39 v39.0.1/go.mod h1:miR4NYIEBXeDNamZIzpskhJ0z/p8al+lwMWylQ/ZJb4=
|
||||
github.com/bytecodealliance/wasmtime-go/v44 v44.0.0 h1:WRZXnLPIer/TWs5aYPaMlmVcOlzmR6Ur6wjLRIQOhTQ=
|
||||
github.com/bytecodealliance/wasmtime-go/v44 v44.0.0/go.mod h1:GP93piU+39CoFVCQ5xfHrPOUtL0APlMnkbblJ2d3YY0=
|
||||
github.com/c-bata/go-prompt v0.2.5/go.mod h1:vFnjEGDIIA/Lib7giyE4E9c50Lvl8j0S+7FVlAwDAVw=
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
@@ -277,8 +277,8 @@ github.com/davidbyttow/govips/v2 v2.18.0 h1:pZRshWVYvewP/TZx3yZ7YeC42WyLXg53tHy5
|
||||
github.com/davidbyttow/govips/v2 v2.18.0/go.mod h1:8+nst5zfMoats12PgmmAPh6p5OfjDaXK0BXMFl/vOcM=
|
||||
github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4=
|
||||
github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1 h1:5RVFMOWjMyRy8cARdy79nAmgYw3hK/4HUq48LQ6Wwqo=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
|
||||
github.com/deepmap/oapi-codegen v1.3.11/go.mod h1:suMvK7+rKlx3+tpa8ByptmvoXbAV70wERKTOGH3hLp0=
|
||||
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I=
|
||||
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE=
|
||||
@@ -349,8 +349,8 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx59Ho=
|
||||
github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo=
|
||||
github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
||||
github.com/gdexlab/go-render v1.0.1 h1:rxqB3vo5s4n1kF0ySmoNeSPRYkEsyHgln4jFIQY7v0U=
|
||||
@@ -477,8 +477,8 @@ github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
|
||||
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||
github.com/gobwas/ws v1.2.1 h1:F2aeBZrm2NDsc7vbovKrWSogd4wvfAxg0FQ89/iqOTk=
|
||||
github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
|
||||
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=
|
||||
github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
|
||||
github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||
github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw=
|
||||
@@ -760,16 +760,16 @@ github.com/leonelquinteros/gotext v1.7.3-0.20260422134830-b012b4ccae69 h1:ZLo0bX
|
||||
github.com/leonelquinteros/gotext v1.7.3-0.20260422134830-b012b4ccae69/go.mod h1:ksG5iXViKefoupjy+0qQjAVoaDnylnQ1ejWl9g14wh8=
|
||||
github.com/lestrrat-go/blackmagic v1.0.4 h1:IwQibdnf8l2KoO+qC3uT4OaTWsW7tuRQXy9TRN9QanA=
|
||||
github.com/lestrrat-go/blackmagic v1.0.4/go.mod h1:6AWFyKNNj0zEXQYfTMPfZrAXUWUfTIZ5ECEUEJaijtw=
|
||||
github.com/lestrrat-go/dsig v1.0.0 h1:OE09s2r9Z81kxzJYRn07TFM9XA4akrUdoMwr0L8xj38=
|
||||
github.com/lestrrat-go/dsig v1.0.0/go.mod h1:dEgoOYYEJvW6XGbLasr8TFcAxoWrKlbQvmJgCR0qkDo=
|
||||
github.com/lestrrat-go/dsig v1.2.1 h1:MwxzZhE4+4fguHi+uDALKVlC3Cn+O1QU1Q/F8D7hVIc=
|
||||
github.com/lestrrat-go/dsig v1.2.1/go.mod h1:RD2eOaidyPvpc7IJQoO3Qq52RWdy8ZcJs8lrOnoa1Kc=
|
||||
github.com/lestrrat-go/dsig-secp256k1 v1.0.0 h1:JpDe4Aybfl0soBvoVwjqDbp+9S1Y2OM7gcrVVMFPOzY=
|
||||
github.com/lestrrat-go/dsig-secp256k1 v1.0.0/go.mod h1:CxUgAhssb8FToqbL8NjSPoGQlnO4w3LG1P0qPWQm/NU=
|
||||
github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=
|
||||
github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E=
|
||||
github.com/lestrrat-go/httprc/v3 v3.0.2 h1:7u4HUaD0NQbf2/n5+fyp+T10hNCsAnwKfqn4A4Baif0=
|
||||
github.com/lestrrat-go/httprc/v3 v3.0.2/go.mod h1:mSMtkZW92Z98M5YoNNztbRGxbXHql7tSitCvaxvo9l0=
|
||||
github.com/lestrrat-go/jwx/v3 v3.0.13 h1:AdHKiPIYeCSnOJtvdpipPg/0SuFh9rdkN+HF3O0VdSk=
|
||||
github.com/lestrrat-go/jwx/v3 v3.0.13/go.mod h1:2m0PV1A9tM4b/jVLMx8rh6rBl7F6WGb3EG2hufN9OQU=
|
||||
github.com/lestrrat-go/httprc/v3 v3.0.5 h1:S+Mb4L2I+bM6JGTibLmxExhyTOqnXjqx+zi9MoXw/TM=
|
||||
github.com/lestrrat-go/httprc/v3 v3.0.5/go.mod h1:mSMtkZW92Z98M5YoNNztbRGxbXHql7tSitCvaxvo9l0=
|
||||
github.com/lestrrat-go/jwx/v3 v3.1.1 h1:yd9AdPmZ4INnQ7k42IrzXYpnEG803+SrQ6hdMvzHJzw=
|
||||
github.com/lestrrat-go/jwx/v3 v3.1.1/go.mod h1:uw/MN2M/Xiu4FhwcIwH11Zsh9JWx9SWzgALl7/uIEkU=
|
||||
github.com/lestrrat-go/option/v2 v2.0.0 h1:XxrcaJESE1fokHy3FpaQ/cXW8ZsIdWcdFzzLOcID3Ss=
|
||||
github.com/lestrrat-go/option/v2 v2.0.0/go.mod h1:oSySsmzMoR0iRzCDCaUfsCzxQHUEuhOViQObyy7S6Vg=
|
||||
github.com/libregraph/idm v0.5.0 h1:tDMwKbAOZzdeDYMxVlY5PbSqRKO7dbAW9KT42A51WSk=
|
||||
@@ -940,8 +940,8 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.40.0 h1:Vtol0e1MghCD2ZVIilPDIg44XSL9l2QAn8ZNaljWcJc=
|
||||
github.com/onsi/gomega v1.40.0/go.mod h1:M/Uqpu/8qTjtzCLUA2zJHX9Iilrau25x1PdoSRbWh5A=
|
||||
github.com/open-policy-agent/opa v1.15.2 h1:dS9q+0Yvruq/VNvWJc5qCvCchn715OWc3HLHXn/UCCc=
|
||||
github.com/open-policy-agent/opa v1.15.2/go.mod h1:c6SN+7jSsUcKJLQc5P4yhwx8YYDRbjpAiGkBOTqxaa4=
|
||||
github.com/open-policy-agent/opa v1.17.1 h1:wO0MOux/VCqY41aVAD6Toe1p3A7O7DlRZ1RHmYSpoS8=
|
||||
github.com/open-policy-agent/opa v1.17.1/go.mod h1:lcuZYSlqQpXFzsA6EJCELmfR5+nNOpZYX+eo7xaIIlk=
|
||||
github.com/opencloud-eu/go-micro-plugins/v4/store/nats-js-kv v0.0.0-20250512152754-23325793059a h1:Sakl76blJAaM6NxylVkgSzktjo2dS504iDotEFJsh3M=
|
||||
github.com/opencloud-eu/go-micro-plugins/v4/store/nats-js-kv v0.0.0-20250512152754-23325793059a/go.mod h1:pjcozWijkNPbEtX5SIQaxEW/h8VAVZYTLx+70bmB3LY=
|
||||
github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89 h1:W1ms+lP5lUUIzjRGDg93WrQfZJZCaV1ZP3KeyXi8bzY=
|
||||
@@ -1051,8 +1051,8 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O
|
||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
|
||||
github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0=
|
||||
github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw=
|
||||
github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc=
|
||||
github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo=
|
||||
github.com/prometheus/statsd_exporter v0.22.7/go.mod h1:N/TevpjkIh9ccs6nuzY3jQn9dFqnUakOjnEuMPJJJnI=
|
||||
github.com/prometheus/statsd_exporter v0.22.8 h1:Qo2D9ZzaQG+id9i5NYNGmbf1aa/KxKbB9aKfMS+Yib0=
|
||||
github.com/prometheus/statsd_exporter v0.22.8/go.mod h1:/DzwbTEaFTE0Ojz5PqcSk6+PFHOPWGxdXVr6yC8eFOM=
|
||||
@@ -1229,12 +1229,12 @@ github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/X
|
||||
github.com/urfave/cli/v2 v2.27.7 h1:bH59vdhbjLv3LAvIu6gd0usJHgoTTPhCFib8qqOwXYU=
|
||||
github.com/urfave/cli/v2 v2.27.7/go.mod h1:CyNAG/xg+iAOg0N4MPGZqVmv2rCoP267496AOXUZjA4=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fastjson v1.6.7 h1:ZE4tRy0CIkh+qDc5McjatheGX2czdn8slQjomexVpBM=
|
||||
github.com/valyala/fastjson v1.6.7/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
|
||||
github.com/valyala/fastjson v1.6.10 h1:/yjJg8jaVQdYR3arGxPE2X5z89xrlhS0eGXdv+ADTh4=
|
||||
github.com/valyala/fastjson v1.6.10/go.mod h1:e6FubmQouUNP73jtMLmcbxS6ydWIpOfhz34TSfO3JaE=
|
||||
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
||||
github.com/valyala/fasttemplate v1.1.0/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
||||
github.com/vektah/gqlparser/v2 v2.5.32 h1:k9QPJd4sEDTL+qB4ncPLflqTJ3MmjB9SrVzJrawpFSc=
|
||||
github.com/vektah/gqlparser/v2 v2.5.32/go.mod h1:c1I28gSOVNzlfc4WuDlqU7voQnsqI6OG2amkBAFmgts=
|
||||
github.com/vektah/gqlparser/v2 v2.5.33 h1:lRp8aIeNUNbimf/axZd7ETg24q06hBtPaas+TcvI/7E=
|
||||
github.com/vektah/gqlparser/v2 v2.5.33/go.mod h1:c1I28gSOVNzlfc4WuDlqU7voQnsqI6OG2amkBAFmgts=
|
||||
github.com/vinyldns/go-vinyldns v0.0.0-20200917153823-148a5f6b8f14/go.mod h1:RWc47jtnVuQv6+lY3c768WtXCas/Xi+U5UFc5xULmYg=
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
|
||||
@@ -1300,8 +1300,8 @@ go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.69.0 h1:2yEATaop1/a1I4psnSLgWVPLWwCzkqWakgJy7xTDVy0=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.69.0/go.mod h1:D7J12YRapIekYyPWgGPlA/23pRmpSEZC5xJC/TTLI9U=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 h1:OyrsyzuttWTSur2qN/Lm0m2a8yqyIjUVBZcxFPuXq2o=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0/go.mod h1:C2NGBr+kAB4bk3xtMXfZ94gqFDtg/GkI7e9zqGh5Beg=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 h1:CqXxU8VOmDefoh0+ztfGaymYbhdB/tT3zs79QaZTNGY=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0/go.mod h1:BuhAPThV8PBHBvg8ZzZ/Ok3idOdhWIodywz2xEcRbJo=
|
||||
go.opentelemetry.io/contrib/zpages v0.68.0 h1:H5yrUwxPrbvhzdBxjQD+VXMtPjIBfp8NWNVvQT8E30M=
|
||||
go.opentelemetry.io/contrib/zpages v0.68.0/go.mod h1:sZGctYYO4UOHItj9bx3F+t/s+u1Fv8CHCJ5s2eR2cjU=
|
||||
go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU=
|
||||
@@ -1337,8 +1337,8 @@ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
|
||||
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
|
||||
go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ=
|
||||
go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/crypto v0.0.0-20180621125126-a49355c7e3f8/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
|
||||
47
vendor/github.com/decred/dcrd/dcrec/secp256k1/v4/ellipticadaptor.go
generated
vendored
47
vendor/github.com/decred/dcrd/dcrec/secp256k1/v4/ellipticadaptor.go
generated
vendored
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2022 The Decred developers
|
||||
// Copyright 2020-2026 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
@@ -63,8 +63,8 @@ type KoblitzCurve struct {
|
||||
// bigAffineToJacobian takes an affine point (x, y) as big integers and converts
|
||||
// it to Jacobian point with Z=1.
|
||||
func bigAffineToJacobian(x, y *big.Int, result *JacobianPoint) {
|
||||
result.X.SetByteSlice(x.Bytes())
|
||||
result.Y.SetByteSlice(y.Bytes())
|
||||
result.X.SetByteSlice(new(big.Int).Mod(x, curveParams.P).Bytes())
|
||||
result.Y.SetByteSlice(new(big.Int).Mod(y, curveParams.P).Bytes())
|
||||
result.Z.SetInt(1)
|
||||
}
|
||||
|
||||
@@ -91,6 +91,15 @@ func (curve *KoblitzCurve) Params() *elliptic.CurveParams {
|
||||
//
|
||||
// This is part of the elliptic.Curve interface implementation. This function
|
||||
// differs from the crypto/elliptic algorithm since a = 0 not -3.
|
||||
//
|
||||
// NOTE: Unfortunately, the Go stdlib elliptic.Curve interface requires that the
|
||||
// conventional point at infinity (0, 0) is not considered on the curve which is
|
||||
// contrary to what is typically expected since the point at infinity is in fact
|
||||
// is a valid curve point.
|
||||
//
|
||||
// Deprecated: The standard library elliptic.Curve interface is now deprecated
|
||||
// and callers should interact with the safer, and much faster, specialized
|
||||
// methods instead.
|
||||
func (curve *KoblitzCurve) IsOnCurve(x, y *big.Int) bool {
|
||||
// Convert big ints to a Jacobian point for faster arithmetic.
|
||||
var point JacobianPoint
|
||||
@@ -101,6 +110,14 @@ func (curve *KoblitzCurve) IsOnCurve(x, y *big.Int) bool {
|
||||
// Add returns the sum of (x1,y1) and (x2,y2).
|
||||
//
|
||||
// This is part of the elliptic.Curve interface implementation.
|
||||
//
|
||||
// NOTE: Per the documentation of the elliptic.Curve interface, the behavior
|
||||
// when the input is not a point on the curve is undefined. Callers must ensure
|
||||
// they are calling this method with valid points.
|
||||
//
|
||||
// Deprecated: The standard library elliptic.Curve interface is now deprecated
|
||||
// and callers should interact with the safer, and much faster, specialized
|
||||
// methods instead.
|
||||
func (curve *KoblitzCurve) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) {
|
||||
// The point at infinity is the identity according to the group law for
|
||||
// elliptic curve cryptography. Thus, ∞ + P = P and P + ∞ = P.
|
||||
@@ -124,6 +141,14 @@ func (curve *KoblitzCurve) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) {
|
||||
// Double returns 2*(x1,y1).
|
||||
//
|
||||
// This is part of the elliptic.Curve interface implementation.
|
||||
//
|
||||
// NOTE: Per the documentation of the elliptic.Curve interface, the behavior
|
||||
// when the input is not a point on the curve is undefined. Callers must ensure
|
||||
// they are calling this method with valid points.
|
||||
//
|
||||
// Deprecated: The standard library elliptic.Curve interface is now deprecated
|
||||
// and callers should interact with the safer, and much faster, specialized
|
||||
// methods instead.
|
||||
func (curve *KoblitzCurve) Double(x1, y1 *big.Int) (*big.Int, *big.Int) {
|
||||
if y1.Sign() == 0 {
|
||||
return new(big.Int), new(big.Int)
|
||||
@@ -156,6 +181,14 @@ func moduloReduce(k []byte) []byte {
|
||||
// ScalarMult returns k*(bx, by) where k is a big endian integer.
|
||||
//
|
||||
// This is part of the elliptic.Curve interface implementation.
|
||||
//
|
||||
// NOTE: Per the documentation of the elliptic.Curve interface, the behavior
|
||||
// when the input is not a point on the curve is undefined. Callers must ensure
|
||||
// they are calling this method with valid points.
|
||||
//
|
||||
// Deprecated: The standard library elliptic.Curve interface is now deprecated
|
||||
// and callers should interact with the safer, and much faster, specialized
|
||||
// methods instead.
|
||||
func (curve *KoblitzCurve) ScalarMult(bx, by *big.Int, k []byte) (*big.Int, *big.Int) {
|
||||
// Convert the affine coordinates from big integers to Jacobian points,
|
||||
// do the multiplication in Jacobian projective space, and convert the
|
||||
@@ -172,6 +205,10 @@ func (curve *KoblitzCurve) ScalarMult(bx, by *big.Int, k []byte) (*big.Int, *big
|
||||
// big endian integer.
|
||||
//
|
||||
// This is part of the elliptic.Curve interface implementation.
|
||||
//
|
||||
// Deprecated: The standard library elliptic.Curve interface is now deprecated
|
||||
// and callers should interact with the safer, and much faster, specialized
|
||||
// methods instead.
|
||||
func (curve *KoblitzCurve) ScalarBaseMult(k []byte) (*big.Int, *big.Int) {
|
||||
// Perform the multiplication and convert the Jacobian point back to affine
|
||||
// big.Ints.
|
||||
@@ -250,6 +287,10 @@ var secp256k1 = &KoblitzCurve{
|
||||
}
|
||||
|
||||
// S256 returns an elliptic.Curve which implements secp256k1.
|
||||
//
|
||||
// Deprecated: The standard library elliptic.Curve interface is now deprecated
|
||||
// and callers should interact with the safer, and much faster, specialized
|
||||
// methods instead.
|
||||
func S256() *KoblitzCurve {
|
||||
return secp256k1
|
||||
}
|
||||
|
||||
14
vendor/github.com/fsnotify/fsnotify/.cirrus.yml
generated
vendored
14
vendor/github.com/fsnotify/fsnotify/.cirrus.yml
generated
vendored
@@ -1,14 +0,0 @@
|
||||
freebsd_task:
|
||||
name: 'FreeBSD'
|
||||
freebsd_instance:
|
||||
image_family: freebsd-14-2
|
||||
install_script:
|
||||
- pkg update -f
|
||||
- pkg install -y go
|
||||
test_script:
|
||||
# run tests as user "cirrus" instead of root
|
||||
- pw useradd cirrus -m
|
||||
- chown -R cirrus:cirrus .
|
||||
- FSNOTIFY_BUFFER=4096 sudo --preserve-env=FSNOTIFY_BUFFER -u cirrus go test -parallel 1 -race ./...
|
||||
- sudo --preserve-env=FSNOTIFY_BUFFER -u cirrus go test -parallel 1 -race ./...
|
||||
- FSNOTIFY_DEBUG=1 sudo --preserve-env=FSNOTIFY_BUFFER -u cirrus go test -parallel 1 -race -v ./...
|
||||
49
vendor/github.com/fsnotify/fsnotify/CHANGELOG.md
generated
vendored
49
vendor/github.com/fsnotify/fsnotify/CHANGELOG.md
generated
vendored
@@ -1,5 +1,54 @@
|
||||
# Changelog
|
||||
|
||||
1.10.1 2026-05-04
|
||||
-----------------
|
||||
|
||||
### Changes and fixes
|
||||
|
||||
- inotify: don't remove sibling watches sharing a path prefix ([#754])
|
||||
|
||||
- inotify, windows: don't rename sibling watches sharing a path prefix
|
||||
([#755])
|
||||
|
||||
|
||||
[#754]: https://github.com/fsnotify/fsnotify/pull/754
|
||||
[#755]: https://github.com/fsnotify/fsnotify/pull/755
|
||||
|
||||
|
||||
1.10.0 2026-04-30
|
||||
-----------------
|
||||
This version of fsnotify needs Go 1.23.
|
||||
|
||||
### Changes and fixes
|
||||
|
||||
- inotify: improve initialization error message ([#731])
|
||||
|
||||
- inotify: send Rename event if recursive watch is renamed ([#696])
|
||||
|
||||
- inotify: avoid copying event buffers when reading names ([#741])
|
||||
|
||||
- kqueue: skip dangling symlinks (ENOENT) in watchDirectoryFiles, so a
|
||||
bad entry no longer aborts Watcher.Add for the whole directory ([#748])
|
||||
|
||||
- kqueue: drop watches directly in Close() to fix a file descriptor leak
|
||||
when recycling watchers ([#740])
|
||||
|
||||
- windows: fix nil pointer dereference in remWatch ([#736])
|
||||
|
||||
- windows: lock watch field updates against concurrent WatchList to fix
|
||||
a race introduced in v1.9.0 ([#709], [#749])
|
||||
|
||||
|
||||
[#696]: https://github.com/fsnotify/fsnotify/pull/696
|
||||
[#709]: https://github.com/fsnotify/fsnotify/pull/709
|
||||
[#731]: https://github.com/fsnotify/fsnotify/pull/731
|
||||
[#736]: https://github.com/fsnotify/fsnotify/pull/736
|
||||
[#740]: https://github.com/fsnotify/fsnotify/pull/740
|
||||
[#741]: https://github.com/fsnotify/fsnotify/pull/741
|
||||
[#748]: https://github.com/fsnotify/fsnotify/pull/748
|
||||
[#749]: https://github.com/fsnotify/fsnotify/pull/749
|
||||
|
||||
|
||||
1.9.0 2024-04-04
|
||||
----------------
|
||||
|
||||
|
||||
2
vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md
generated
vendored
2
vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md
generated
vendored
@@ -77,6 +77,8 @@ End-of-line escapes with `\` are not supported.
|
||||
debug [yes/no] # Enable/disable FSNOTIFY_DEBUG (tests are run in
|
||||
parallel by default, so -parallel=1 is probably a good
|
||||
idea).
|
||||
state # Print internal state to stderr (exact output differs
|
||||
# per backend).
|
||||
print [any strings] # Print text to stdout; for debugging.
|
||||
|
||||
touch path
|
||||
|
||||
56
vendor/github.com/fsnotify/fsnotify/README.md
generated
vendored
56
vendor/github.com/fsnotify/fsnotify/README.md
generated
vendored
@@ -1,7 +1,7 @@
|
||||
fsnotify is a Go library to provide cross-platform filesystem notifications on
|
||||
Windows, Linux, macOS, BSD, and illumos.
|
||||
|
||||
Go 1.17 or newer is required; the full documentation is at
|
||||
Go 1.23 or newer is required; the full documentation is at
|
||||
https://pkg.go.dev/github.com/fsnotify/fsnotify
|
||||
|
||||
---
|
||||
@@ -12,7 +12,7 @@ Platform support:
|
||||
| :-------------------- | :--------- | :------------------------------------------------------------------------ |
|
||||
| inotify | Linux | Supported |
|
||||
| kqueue | BSD, macOS | Supported |
|
||||
| ReadDirectoryChangesW | Windows | Supported |
|
||||
| ReadDirectoryChangesW | Windows | Supported ([excluding `Chmod` operations][#487]) |
|
||||
| FEN | illumos | Supported |
|
||||
| fanotify | Linux 5.9+ | [Not yet](https://github.com/fsnotify/fsnotify/issues/114) |
|
||||
| FSEvents | macOS | [Needs support in x/sys/unix][fsevents] |
|
||||
@@ -22,6 +22,7 @@ Platform support:
|
||||
Linux and illumos should include Android and Solaris, but these are currently
|
||||
untested.
|
||||
|
||||
[#487]: https://github.com/fsnotify/fsnotify/issues/487
|
||||
[fsevents]: https://github.com/fsnotify/fsnotify/issues/11#issuecomment-1279133120
|
||||
[usn]: https://github.com/fsnotify/fsnotify/issues/53#issuecomment-1279829847
|
||||
|
||||
@@ -126,7 +127,7 @@ settings* until we have a native FSEvents implementation (see [#11]).
|
||||
### Watching a file doesn't work well
|
||||
Watching individual files (rather than directories) is generally not recommended
|
||||
as many programs (especially editors) update files atomically: it will write to
|
||||
a temporary file which is then moved to to destination, overwriting the original
|
||||
a temporary file which is then moved to a destination, overwriting the original
|
||||
(or some variant thereof). The watcher on the original file is now lost, as that
|
||||
no longer exists.
|
||||
|
||||
@@ -151,26 +152,57 @@ This is the event that inotify sends, so not much can be changed about this.
|
||||
The `fs.inotify.max_user_watches` sysctl variable specifies the upper limit for
|
||||
the number of watches per user, and `fs.inotify.max_user_instances` specifies
|
||||
the maximum number of inotify instances per user. Every Watcher you create is an
|
||||
"instance", and every path you add is a "watch".
|
||||
"instance", and every path you add is a "watch". Reaching the limit will result
|
||||
in a "no space left on device" or "too many open files" error.
|
||||
|
||||
These are also exposed in `/proc` as `/proc/sys/fs/inotify/max_user_watches` and
|
||||
`/proc/sys/fs/inotify/max_user_instances`
|
||||
`/proc/sys/fs/inotify/max_user_instances`. The default values differ per distro
|
||||
and available memory.
|
||||
|
||||
To increase them you can use `sysctl` or write the value to proc file:
|
||||
|
||||
# The default values on Linux 5.18
|
||||
sysctl fs.inotify.max_user_watches=124983
|
||||
sysctl fs.inotify.max_user_instances=128
|
||||
sysctl fs.inotify.max_user_watches=200000
|
||||
sysctl fs.inotify.max_user_instances=256
|
||||
|
||||
To make the changes persist on reboot edit `/etc/sysctl.conf` or
|
||||
`/usr/lib/sysctl.d/50-default.conf` (details differ per Linux distro; check your
|
||||
distro's documentation):
|
||||
|
||||
fs.inotify.max_user_watches=124983
|
||||
fs.inotify.max_user_instances=128
|
||||
fs.inotify.max_user_watches=200000
|
||||
fs.inotify.max_user_instances=256
|
||||
|
||||
### Windows
|
||||
Recursive watching is not currently enabled through fsnotify's public API
|
||||
(see the FAQ "Are subdirectories watched?" above). The notes below
|
||||
describe Windows backend behavior observed when recursive watching is
|
||||
enabled internally (for example, in fsnotify's own tests). They are kept
|
||||
here as a reference for maintainers and contributors who encounter the
|
||||
behavior, since the recursive code path still exists in the backend.
|
||||
|
||||
When recursive watching is enabled and you watch a directory, you may
|
||||
receive a `Write` event for an intermediate directory whenever a child
|
||||
entry inside it is created, renamed, or removed. For example, with a
|
||||
recursive watch on `/a` and a new file `/a/b/c`, you will receive
|
||||
`Create /a/b/c` and may also receive `Write /a/b`.
|
||||
|
||||
This happens because, on NTFS-backed volumes, modifying the entries of a
|
||||
directory updates that directory's last-write time, and the Windows
|
||||
backend requests `FILE_NOTIFY_CHANGE_LAST_WRITE` to support `Write` events
|
||||
on files. The same `Write` filter therefore picks up the directory's
|
||||
metadata update.
|
||||
|
||||
kqueue has the same "directory `Write` = directory contents changed"
|
||||
semantics, so portable code that treats `Write` on a directory as
|
||||
"something inside it changed" works on Windows and BSD/macOS, but not on
|
||||
Linux (inotify uses `Write` only for file-content changes). If you only
|
||||
care about file content, filter out `Write` events whose path refers to a
|
||||
directory.
|
||||
|
||||
Whether the directory `Write` is actually delivered alongside the child
|
||||
events is not guaranteed: it depends on `ReadDirectoryChangesW` buffering,
|
||||
NTFS metadata update timing, and event coalescing, none of which fsnotify
|
||||
controls.
|
||||
|
||||
Reaching the limit will result in a "no space left on device" or "too many open
|
||||
files" error.
|
||||
|
||||
### kqueue (macOS, all BSD systems)
|
||||
kqueue requires opening a file descriptor for every file that's being watched;
|
||||
|
||||
4
vendor/github.com/fsnotify/fsnotify/backend_fen.go
generated
vendored
4
vendor/github.com/fsnotify/fsnotify/backend_fen.go
generated
vendored
@@ -158,7 +158,9 @@ func (w *fen) readEvents() {
|
||||
|
||||
pevents := make([]unix.PortEvent, 8)
|
||||
for {
|
||||
count, err := w.port.Get(pevents, 1, nil)
|
||||
count, err := internal.IgnoringEINTR(func() (int, error) {
|
||||
return w.port.Get(pevents, 1, nil)
|
||||
})
|
||||
if err != nil && err != unix.ETIME {
|
||||
// Interrupted system call (count should be 0) ignore and continue
|
||||
if errors.Is(err, unix.EINTR) && count == 0 {
|
||||
|
||||
84
vendor/github.com/fsnotify/fsnotify/backend_inotify.go
generated
vendored
84
vendor/github.com/fsnotify/fsnotify/backend_inotify.go
generated
vendored
@@ -55,10 +55,10 @@ type (
|
||||
path map[string]uint32 // pathname → wd
|
||||
}
|
||||
watch struct {
|
||||
wd uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall)
|
||||
flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags)
|
||||
path string // Watch path.
|
||||
recurse bool // Recursion with ./...?
|
||||
wd uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall)
|
||||
flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags)
|
||||
path string // Watch path.
|
||||
watchFlags watchFlag
|
||||
}
|
||||
koekje struct {
|
||||
cookie uint32
|
||||
@@ -66,6 +66,9 @@ type (
|
||||
}
|
||||
)
|
||||
|
||||
func (w watch) byUser() bool { return w.watchFlags&flagByUser != 0 }
|
||||
func (w watch) recurse() bool { return w.watchFlags&flagRecurse != 0 }
|
||||
|
||||
func newWatches() *watches {
|
||||
return &watches{
|
||||
wd: make(map[uint32]*watch),
|
||||
@@ -79,6 +82,13 @@ func (w *watches) len() int { return len(w.wd) }
|
||||
func (w *watches) add(ww *watch) { w.wd[ww.wd] = ww; w.path[ww.path] = ww.wd }
|
||||
func (w *watches) remove(watch *watch) { delete(w.path, watch.path); delete(w.wd, watch.wd) }
|
||||
|
||||
func isSameOrDescendantPath(path, root string) bool {
|
||||
if path == root {
|
||||
return true
|
||||
}
|
||||
return strings.HasPrefix(path, root+string(os.PathSeparator))
|
||||
}
|
||||
|
||||
func (w *watches) removePath(path string) ([]uint32, error) {
|
||||
path, recurse := recursivePath(path)
|
||||
wd, ok := w.path[path]
|
||||
@@ -87,20 +97,20 @@ func (w *watches) removePath(path string) ([]uint32, error) {
|
||||
}
|
||||
|
||||
watch := w.wd[wd]
|
||||
if recurse && !watch.recurse {
|
||||
if recurse && !watch.recurse() {
|
||||
return nil, fmt.Errorf("can't use /... with non-recursive watch %q", path)
|
||||
}
|
||||
|
||||
delete(w.path, path)
|
||||
delete(w.wd, wd)
|
||||
if !watch.recurse {
|
||||
if !watch.recurse() {
|
||||
return []uint32{wd}, nil
|
||||
}
|
||||
|
||||
wds := make([]uint32, 0, 8)
|
||||
wds = append(wds, wd)
|
||||
for p, rwd := range w.path {
|
||||
if strings.HasPrefix(p, path) {
|
||||
if isSameOrDescendantPath(p, path) {
|
||||
delete(w.path, p)
|
||||
delete(w.wd, rwd)
|
||||
wds = append(wds, rwd)
|
||||
@@ -139,7 +149,7 @@ func newBackend(ev chan Event, errs chan error) (backend, error) {
|
||||
// I/O operations won't terminate on close.
|
||||
fd, errno := unix.InotifyInit1(unix.IN_CLOEXEC | unix.IN_NONBLOCK)
|
||||
if fd == -1 {
|
||||
return nil, errno
|
||||
return nil, fmt.Errorf("couldn't initialize inotify: %w", errno)
|
||||
}
|
||||
|
||||
w := &inotify{
|
||||
@@ -188,11 +198,8 @@ func (w *inotify) AddWith(path string, opts ...addOpt) error {
|
||||
return fmt.Errorf("%w: %s", xErrUnsupported, with.op)
|
||||
}
|
||||
|
||||
add := func(path string, with withOpts, recurse bool) error {
|
||||
add := func(path string, with withOpts, wf watchFlag) error {
|
||||
var flags uint32
|
||||
if with.noFollow {
|
||||
flags |= unix.IN_DONT_FOLLOW
|
||||
}
|
||||
if with.op.Has(Create) {
|
||||
flags |= unix.IN_CREATE
|
||||
}
|
||||
@@ -220,7 +227,7 @@ func (w *inotify) AddWith(path string, opts ...addOpt) error {
|
||||
if with.op.Has(xUnportableCloseRead) {
|
||||
flags |= unix.IN_CLOSE_NOWRITE
|
||||
}
|
||||
return w.register(path, flags, recurse)
|
||||
return w.register(path, flags, wf)
|
||||
}
|
||||
|
||||
w.mu.Lock()
|
||||
@@ -248,14 +255,18 @@ func (w *inotify) AddWith(path string, opts ...addOpt) error {
|
||||
w.sendEvent(Event{Name: root, Op: Create})
|
||||
}
|
||||
|
||||
return add(root, with, true)
|
||||
wf := flagRecurse
|
||||
if root == path {
|
||||
wf |= flagByUser
|
||||
}
|
||||
return add(root, with, wf)
|
||||
})
|
||||
}
|
||||
|
||||
return add(path, with, false)
|
||||
return add(path, with, 0)
|
||||
}
|
||||
|
||||
func (w *inotify) register(path string, flags uint32, recurse bool) error {
|
||||
func (w *inotify) register(path string, flags uint32, wf watchFlag) error {
|
||||
return w.watches.updatePath(path, func(existing *watch) (*watch, error) {
|
||||
if existing != nil {
|
||||
flags |= existing.flags | unix.IN_MASK_ADD
|
||||
@@ -272,10 +283,10 @@ func (w *inotify) register(path string, flags uint32, recurse bool) error {
|
||||
|
||||
if existing == nil {
|
||||
return &watch{
|
||||
wd: uint32(wd),
|
||||
path: path,
|
||||
flags: flags,
|
||||
recurse: recurse,
|
||||
wd: uint32(wd),
|
||||
path: path,
|
||||
flags: flags,
|
||||
watchFlags: wf,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -425,11 +436,7 @@ func (w *inotify) handleEvent(inEvent *unix.InotifyEvent, buf *[65536]byte, offs
|
||||
nameLen = uint32(inEvent.Len)
|
||||
)
|
||||
if nameLen > 0 {
|
||||
/// Point "bytes" at the first byte of the filename
|
||||
bb := *buf
|
||||
bytes := (*[unix.PathMax]byte)(unsafe.Pointer(&bb[offset+unix.SizeofInotifyEvent]))[:nameLen:nameLen]
|
||||
/// The filename is padded with NULL bytes. TrimRight() gets rid of those.
|
||||
name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\x00")
|
||||
name += "/" + inotifyEventName(buf, offset, nameLen)
|
||||
}
|
||||
|
||||
if debug {
|
||||
@@ -450,7 +457,9 @@ func (w *inotify) handleEvent(inEvent *unix.InotifyEvent, buf *[65536]byte, offs
|
||||
// We can't really update the state when a watched path is moved; only
|
||||
// IN_MOVE_SELF is sent and not IN_MOVED_{FROM,TO}. So remove the watch.
|
||||
if inEvent.Mask&unix.IN_MOVE_SELF == unix.IN_MOVE_SELF {
|
||||
if watch.recurse { // Do nothing
|
||||
// Watch is set up as part of recurse: do nothing as the move gets
|
||||
// registered from the parent directory.
|
||||
if watch.recurse() && !watch.byUser() {
|
||||
return Event{}, true
|
||||
}
|
||||
|
||||
@@ -460,6 +469,10 @@ func (w *inotify) handleEvent(inEvent *unix.InotifyEvent, buf *[65536]byte, offs
|
||||
return Event{}, false
|
||||
}
|
||||
}
|
||||
|
||||
if watch.recurse() {
|
||||
return Event{Name: watch.path, Op: Rename}, true
|
||||
}
|
||||
}
|
||||
|
||||
/// Skip if we're watching both this path and the parent; the parent will
|
||||
@@ -473,11 +486,11 @@ func (w *inotify) handleEvent(inEvent *unix.InotifyEvent, buf *[65536]byte, offs
|
||||
|
||||
ev := w.newEvent(name, inEvent.Mask, inEvent.Cookie)
|
||||
// Need to update watch path for recurse.
|
||||
if watch.recurse {
|
||||
if watch.recurse() {
|
||||
isDir := inEvent.Mask&unix.IN_ISDIR == unix.IN_ISDIR
|
||||
/// New directory created: set up watch on it.
|
||||
if isDir && ev.Has(Create) {
|
||||
err := w.register(ev.Name, watch.flags, true)
|
||||
err := w.register(ev.Name, watch.flags, flagRecurse)
|
||||
if !w.sendError(err) {
|
||||
return Event{}, false
|
||||
}
|
||||
@@ -495,7 +508,7 @@ func (w *inotify) handleEvent(inEvent *unix.InotifyEvent, buf *[65536]byte, offs
|
||||
if k == watch.wd || ww.path == ev.Name {
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(ww.path, ev.renamedFrom) {
|
||||
if isSameOrDescendantPath(ww.path, ev.renamedFrom) {
|
||||
ww.path = strings.Replace(ww.path, ev.renamedFrom, ev.Name, 1)
|
||||
w.watches.wd[k] = ww
|
||||
}
|
||||
@@ -507,12 +520,13 @@ func (w *inotify) handleEvent(inEvent *unix.InotifyEvent, buf *[65536]byte, offs
|
||||
return ev, true
|
||||
}
|
||||
|
||||
func (w *inotify) isRecursive(path string) bool {
|
||||
ww := w.watches.byPath(path)
|
||||
if ww == nil { // path could be a file, so also check the Dir.
|
||||
ww = w.watches.byPath(filepath.Dir(path))
|
||||
func inotifyEventName(buf *[65536]byte, offset, nameLen uint32) string {
|
||||
start := int(offset + unix.SizeofInotifyEvent)
|
||||
bytes := (*[unix.PathMax]byte)(unsafe.Pointer(&buf[start]))[:nameLen:nameLen]
|
||||
for nameLen > 0 && bytes[nameLen-1] == 0 {
|
||||
nameLen--
|
||||
}
|
||||
return ww != nil && ww.recurse
|
||||
return string(bytes[:nameLen])
|
||||
}
|
||||
|
||||
func (w *inotify) newEvent(name string, mask, cookie uint32) Event {
|
||||
@@ -578,6 +592,6 @@ func (w *inotify) state() {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
for wd, ww := range w.watches.wd {
|
||||
fmt.Fprintf(os.Stderr, "%4d: recurse=%t %q\n", wd, ww.recurse, ww.path)
|
||||
fmt.Fprintf(os.Stderr, "%4d: %q watchFlags=0x%x\n", wd, ww.path, ww.watchFlags)
|
||||
}
|
||||
}
|
||||
|
||||
68
vendor/github.com/fsnotify/fsnotify/backend_kqueue.go
generated
vendored
68
vendor/github.com/fsnotify/fsnotify/backend_kqueue.go
generated
vendored
@@ -8,6 +8,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -245,9 +246,26 @@ func (w *kqueue) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Snapshot and drop all watches directly. w.Remove -> w.remove
|
||||
// short-circuits on isClosed() (which is already true after
|
||||
// w.shared.close() above), so calling Remove here in the happy path
|
||||
// leaked every watched directory + file descriptor. On macOS a
|
||||
// single directory watch opens an fd for every file in the dir, so
|
||||
// long-running processes that recreate watchers (hot-reload dev
|
||||
// servers, etc.) ran out of fds with EMFILE (#732).
|
||||
pathsToRemove := w.watches.listPaths(false)
|
||||
for _, name := range pathsToRemove {
|
||||
w.Remove(name)
|
||||
info, ok := w.watches.byPath(name)
|
||||
if !ok {
|
||||
// w.path has an entry for name but w.wd doesn't --
|
||||
// drop the stale lookup entry so the map state is
|
||||
// consistent after Close.
|
||||
w.watches.remove(0, name)
|
||||
continue
|
||||
}
|
||||
_ = w.register([]int{info.wd}, unix.EV_DELETE, 0)
|
||||
unix.Close(info.wd)
|
||||
w.watches.remove(info.wd, name)
|
||||
}
|
||||
|
||||
unix.Close(w.closepipe[1]) // Send "quit" message to readEvents
|
||||
@@ -376,19 +394,12 @@ func (w *kqueue) addWatch(name string, flags uint32, listDir bool) (string, erro
|
||||
}
|
||||
}
|
||||
|
||||
// Retry on EINTR; open() can return EINTR in practice on macOS.
|
||||
// See #354, and Go issues 11180 and 39237.
|
||||
for {
|
||||
info.wd, err = unix.Open(name, openMode, 0)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
if errors.Is(err, unix.EINTR) {
|
||||
continue
|
||||
}
|
||||
info.wd, err = internal.IgnoringEINTR(func() (int, error) {
|
||||
return unix.Open(name, openMode, 0)
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
info.isDir = fi.IsDir()
|
||||
}
|
||||
|
||||
@@ -436,9 +447,10 @@ func (w *kqueue) readEvents() {
|
||||
|
||||
eventBuffer := make([]unix.Kevent_t, 10)
|
||||
for {
|
||||
kevents, err := w.read(eventBuffer)
|
||||
// EINTR is okay, the syscall was interrupted before timeout expired.
|
||||
if err != nil && err != unix.EINTR {
|
||||
kevents, err := internal.IgnoringEINTR(func() ([]unix.Kevent_t, error) {
|
||||
return w.read(eventBuffer)
|
||||
})
|
||||
if err != nil {
|
||||
if !w.sendError(fmt.Errorf("fsnotify.readEvents: %w", err)) {
|
||||
return
|
||||
}
|
||||
@@ -583,12 +595,14 @@ func (w *kqueue) watchDirectoryFiles(dirPath string) error {
|
||||
|
||||
cleanPath, err := w.internalWatch(path, fi)
|
||||
if err != nil {
|
||||
// No permission to read the file; that's not a problem: just skip.
|
||||
// But do add it to w.fileExists to prevent it from being picked up
|
||||
// as a "new" file later (it still shows up in the directory
|
||||
// No permission, or the entry resolved to a missing target
|
||||
// (e.g. a dangling symlink): not a problem, just skip. But
|
||||
// do mark it as seen to prevent it from being picked up as
|
||||
// a "new" file later (it still shows up in the directory
|
||||
// listing).
|
||||
switch {
|
||||
case errors.Is(err, unix.EACCES) || errors.Is(err, unix.EPERM):
|
||||
case errors.Is(err, unix.EACCES) || errors.Is(err, unix.EPERM) ||
|
||||
errors.Is(err, os.ErrNotExist):
|
||||
cleanPath = filepath.Clean(path)
|
||||
default:
|
||||
return fmt.Errorf("%q: %w", path, err)
|
||||
@@ -703,3 +717,19 @@ func (w *kqueue) xSupports(op Op) bool {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (w *kqueue) state() {
|
||||
w.watches.mu.Lock()
|
||||
defer w.watches.mu.Unlock()
|
||||
|
||||
all := make([]int, 0, len(w.watches.wd))
|
||||
for wd := range w.watches.wd {
|
||||
all = append(all, wd)
|
||||
}
|
||||
sort.Ints(all)
|
||||
|
||||
for _, wd := range all {
|
||||
ww := w.watches.wd[wd]
|
||||
fmt.Fprintf(os.Stderr, "%4d %q linkname=%q\n", wd, ww.name, ww.linkName)
|
||||
}
|
||||
}
|
||||
|
||||
78
vendor/github.com/fsnotify/fsnotify/backend_windows.go
generated
vendored
78
vendor/github.com/fsnotify/fsnotify/backend_windows.go
generated
vendored
@@ -11,7 +11,6 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -37,6 +36,13 @@ type readDirChangesW struct {
|
||||
|
||||
var defaultBufferSize = 50
|
||||
|
||||
func isSameOrDescendantPath(path, root string) bool {
|
||||
if path == root {
|
||||
return true
|
||||
}
|
||||
return strings.HasPrefix(path, root+string(os.PathSeparator))
|
||||
}
|
||||
|
||||
func newBackend(ev chan Event, errs chan error) (backend, error) {
|
||||
port, err := windows.CreateIoCompletionPort(windows.InvalidHandle, 0, 0, 0)
|
||||
if err != nil {
|
||||
@@ -359,22 +365,26 @@ func (w *readDirChangesW) addWatch(pathname string, flags uint64, bufsize int) e
|
||||
} else {
|
||||
windows.CloseHandle(ino.handle)
|
||||
}
|
||||
w.mu.Lock()
|
||||
if pathname == dir {
|
||||
watchEntry.mask |= flags
|
||||
} else {
|
||||
watchEntry.names[filepath.Base(pathname)] |= flags
|
||||
}
|
||||
w.mu.Unlock()
|
||||
|
||||
err = w.startRead(watchEntry)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w.mu.Lock()
|
||||
if pathname == dir {
|
||||
watchEntry.mask &= ^provisional
|
||||
} else {
|
||||
watchEntry.names[filepath.Base(pathname)] &= ^provisional
|
||||
}
|
||||
w.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -394,8 +404,13 @@ func (w *readDirChangesW) remWatch(pathname string) error {
|
||||
w.mu.Lock()
|
||||
watch := w.watches.get(ino)
|
||||
w.mu.Unlock()
|
||||
if watch == nil {
|
||||
windows.CloseHandle(ino.handle)
|
||||
return fmt.Errorf("%w: %s", ErrNonExistentWatch, pathname)
|
||||
}
|
||||
|
||||
if recurse && !watch.recurse {
|
||||
windows.CloseHandle(ino.handle)
|
||||
return fmt.Errorf("can't use \\... with non-recursive watch %q", pathname)
|
||||
}
|
||||
|
||||
@@ -403,16 +418,19 @@ func (w *readDirChangesW) remWatch(pathname string) error {
|
||||
if err != nil {
|
||||
w.sendError(os.NewSyscallError("CloseHandle", err))
|
||||
}
|
||||
if watch == nil {
|
||||
return fmt.Errorf("%w: %s", ErrNonExistentWatch, pathname)
|
||||
}
|
||||
if pathname == dir {
|
||||
w.sendEvent(watch.path, "", watch.mask&sysFSIGNORED)
|
||||
w.mu.Lock()
|
||||
mask := watch.mask
|
||||
watch.mask = 0
|
||||
w.mu.Unlock()
|
||||
w.sendEvent(watch.path, "", mask&sysFSIGNORED)
|
||||
} else {
|
||||
name := filepath.Base(pathname)
|
||||
w.sendEvent(filepath.Join(watch.path, name), "", watch.names[name]&sysFSIGNORED)
|
||||
w.mu.Lock()
|
||||
mask := watch.names[name]
|
||||
delete(watch.names, name)
|
||||
w.mu.Unlock()
|
||||
w.sendEvent(filepath.Join(watch.path, name), "", mask&sysFSIGNORED)
|
||||
}
|
||||
|
||||
return w.startRead(watch)
|
||||
@@ -420,17 +438,23 @@ func (w *readDirChangesW) remWatch(pathname string) error {
|
||||
|
||||
// Must run within the I/O thread.
|
||||
func (w *readDirChangesW) deleteWatch(watch *watch) {
|
||||
for name, mask := range watch.names {
|
||||
if mask&provisional == 0 {
|
||||
w.sendEvent(filepath.Join(watch.path, name), "", mask&sysFSIGNORED)
|
||||
// Snapshot+clear under the lock so concurrent WatchList() readers see a
|
||||
// consistent state. sendEvent must run outside the lock since it can
|
||||
// block on the user-facing Events channel.
|
||||
w.mu.Lock()
|
||||
names := watch.names
|
||||
watch.names = make(map[string]uint64)
|
||||
mask := watch.mask
|
||||
watch.mask = 0
|
||||
w.mu.Unlock()
|
||||
|
||||
for name, m := range names {
|
||||
if m&provisional == 0 {
|
||||
w.sendEvent(filepath.Join(watch.path, name), "", m&sysFSIGNORED)
|
||||
}
|
||||
delete(watch.names, name)
|
||||
}
|
||||
if watch.mask != 0 {
|
||||
if watch.mask&provisional == 0 {
|
||||
w.sendEvent(watch.path, "", watch.mask&sysFSIGNORED)
|
||||
}
|
||||
watch.mask = 0
|
||||
if mask != 0 && mask&provisional == 0 {
|
||||
w.sendEvent(watch.path, "", mask&sysFSIGNORED)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -457,9 +481,8 @@ func (w *readDirChangesW) startRead(watch *watch) error {
|
||||
}
|
||||
|
||||
// We need to pass the array, rather than the slice.
|
||||
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&watch.buf))
|
||||
rdErr := windows.ReadDirectoryChanges(watch.ino.handle,
|
||||
(*byte)(unsafe.Pointer(hdr.Data)), uint32(hdr.Len),
|
||||
unsafe.SliceData(watch.buf), uint32(len(watch.buf)),
|
||||
watch.recurse, mask, nil, &watch.ov, 0)
|
||||
if rdErr != nil {
|
||||
err := os.NewSyscallError("ReadDirectoryChanges", rdErr)
|
||||
@@ -565,12 +588,7 @@ func (w *readDirChangesW) readEvents() {
|
||||
|
||||
// Create a buf that is the size of the path name
|
||||
size := int(raw.FileNameLength / 2)
|
||||
var buf []uint16
|
||||
// TODO: Use unsafe.Slice in Go 1.17; https://stackoverflow.com/questions/51187973
|
||||
sh := (*reflect.SliceHeader)(unsafe.Pointer(&buf))
|
||||
sh.Data = uintptr(unsafe.Pointer(&raw.FileName))
|
||||
sh.Len = size
|
||||
sh.Cap = size
|
||||
buf := unsafe.Slice(&raw.FileName, size)
|
||||
name := windows.UTF16ToString(buf)
|
||||
fullname := filepath.Join(watch.path, name)
|
||||
|
||||
@@ -587,31 +605,35 @@ func (w *readDirChangesW) readEvents() {
|
||||
case windows.FILE_ACTION_RENAMED_OLD_NAME:
|
||||
watch.rename = name
|
||||
case windows.FILE_ACTION_RENAMED_NEW_NAME:
|
||||
// Update saved path of all sub-watches.
|
||||
// Update saved path of all sub-watches and rename the
|
||||
// names entry under the lock so WatchList() can't observe
|
||||
// a torn state.
|
||||
old := filepath.Join(watch.path, watch.rename)
|
||||
w.mu.Lock()
|
||||
for _, watchMap := range w.watches {
|
||||
for _, ww := range watchMap {
|
||||
if strings.HasPrefix(ww.path, old) {
|
||||
if isSameOrDescendantPath(ww.path, old) {
|
||||
ww.path = filepath.Join(fullname, strings.TrimPrefix(ww.path, old))
|
||||
}
|
||||
}
|
||||
}
|
||||
w.mu.Unlock()
|
||||
|
||||
if watch.names[watch.rename] != 0 {
|
||||
watch.names[name] |= watch.names[watch.rename]
|
||||
delete(watch.names, watch.rename)
|
||||
mask = sysFSMOVESELF
|
||||
}
|
||||
w.mu.Unlock()
|
||||
}
|
||||
|
||||
if raw.Action != windows.FILE_ACTION_RENAMED_NEW_NAME {
|
||||
w.sendEvent(fullname, "", watch.names[name]&mask)
|
||||
}
|
||||
if raw.Action == windows.FILE_ACTION_REMOVED {
|
||||
w.sendEvent(fullname, "", watch.names[name]&sysFSIGNORED)
|
||||
w.mu.Lock()
|
||||
ignored := watch.names[name] & sysFSIGNORED
|
||||
delete(watch.names, name)
|
||||
w.mu.Unlock()
|
||||
w.sendEvent(fullname, "", ignored)
|
||||
}
|
||||
|
||||
if watch.rename != "" && raw.Action == windows.FILE_ACTION_RENAMED_NEW_NAME {
|
||||
|
||||
72
vendor/github.com/fsnotify/fsnotify/fsnotify.go
generated
vendored
72
vendor/github.com/fsnotify/fsnotify/fsnotify.go
generated
vendored
@@ -51,26 +51,25 @@ import (
|
||||
// The fs.inotify.max_user_watches sysctl variable specifies the upper limit
|
||||
// for the number of watches per user, and fs.inotify.max_user_instances
|
||||
// specifies the maximum number of inotify instances per user. Every Watcher you
|
||||
// create is an "instance", and every path you add is a "watch".
|
||||
// create is an "instance", and every path you add is a "watch". Reaching the
|
||||
// limit will result in a "no space left on device" or "too many open files"
|
||||
// error.
|
||||
//
|
||||
// These are also exposed in /proc as /proc/sys/fs/inotify/max_user_watches and
|
||||
// /proc/sys/fs/inotify/max_user_instances
|
||||
// /proc/sys/fs/inotify/max_user_instances. The default values differ per distro
|
||||
// and available memory.
|
||||
//
|
||||
// To increase them you can use sysctl or write the value to the /proc file:
|
||||
//
|
||||
// # Default values on Linux 5.18
|
||||
// sysctl fs.inotify.max_user_watches=124983
|
||||
// sysctl fs.inotify.max_user_instances=128
|
||||
// sysctl fs.inotify.max_user_watches=200000
|
||||
// sysctl fs.inotify.max_user_instances=256
|
||||
//
|
||||
// To make the changes persist on reboot edit /etc/sysctl.conf or
|
||||
// /usr/lib/sysctl.d/50-default.conf (details differ per Linux distro; check
|
||||
// your distro's documentation):
|
||||
//
|
||||
// fs.inotify.max_user_watches=124983
|
||||
// fs.inotify.max_user_instances=128
|
||||
//
|
||||
// Reaching the limit will result in a "no space left on device" or "too many open
|
||||
// files" error.
|
||||
// fs.inotify.max_user_watches=200000
|
||||
// fs.inotify.max_user_instances=256
|
||||
//
|
||||
// # kqueue notes (macOS, BSD)
|
||||
//
|
||||
@@ -93,6 +92,28 @@ import (
|
||||
// Sometimes it will send events for all files, sometimes it will send no
|
||||
// events, and often only for some files.
|
||||
//
|
||||
// Recursive watching is not currently enabled through fsnotify's public
|
||||
// API; the recursive code path is gated and only exercised by fsnotify's
|
||||
// own tests. The note below describes backend behavior observed when
|
||||
// recursive watching is enabled internally, and is kept here as a
|
||||
// reference for maintainers and contributors who encounter it.
|
||||
//
|
||||
// When recursive watching is enabled and you watch a directory, you may
|
||||
// receive a Write event for an intermediate directory whenever a child
|
||||
// entry inside it is created, renamed, or removed. For example, with a
|
||||
// recursive watch on /a and a new file /a/b/c, you will receive
|
||||
// Create /a/b/c and may also receive Write /a/b.
|
||||
//
|
||||
// This happens because, on NTFS-backed volumes, modifying the entries of a
|
||||
// directory updates that directory's last-write time, and the Windows
|
||||
// backend requests FILE_NOTIFY_CHANGE_LAST_WRITE to support Write events
|
||||
// on files. The same Write filter therefore picks up the directory's
|
||||
// metadata update.
|
||||
//
|
||||
// Whether the directory Write is actually delivered alongside the child
|
||||
// events is not guaranteed; it depends on ReadDirectoryChangesW buffering,
|
||||
// NTFS metadata update timing, and event coalescing.
|
||||
//
|
||||
// The default ReadDirectoryChangesW() buffer size is 64K, which is the largest
|
||||
// value that is guaranteed to work with SMB filesystems. If you have many
|
||||
// events in quick succession this may not be enough, and you will have to use
|
||||
@@ -129,8 +150,12 @@ type Watcher struct {
|
||||
// want to wait until you've stopped receiving them
|
||||
// (see the dedup example in cmd/fsnotify).
|
||||
//
|
||||
// Some systems may send Write event for directories
|
||||
// when the directory content changes.
|
||||
// Some systems also send Write events for directories
|
||||
// when the directory contents change. This is the
|
||||
// case for kqueue, and on Windows for the directory
|
||||
// that contains a created, renamed, or removed child
|
||||
// entry. It does not happen on inotify. See the
|
||||
// per-platform notes on [Watcher].
|
||||
//
|
||||
// fsnotify.Chmod Attributes were changed. On Linux this is also sent
|
||||
// when a file is removed (or more accurately, when a
|
||||
@@ -179,7 +204,9 @@ const (
|
||||
Create Op = 1 << iota
|
||||
|
||||
// The pathname was written to; this does *not* mean the write has finished,
|
||||
// and a write can be followed by more writes.
|
||||
// and a write can be followed by more writes. On Windows and kqueue, a
|
||||
// Write on a directory can also indicate that its contents changed; see
|
||||
// the per-platform notes on [Watcher].
|
||||
Write
|
||||
|
||||
// The path was removed; any watches on it will be removed. Some "remove"
|
||||
@@ -220,7 +247,7 @@ const (
|
||||
|
||||
// File opened for reading was closed.
|
||||
//
|
||||
// Only works on Linux and FreeBSD.
|
||||
// Only works on Linux.
|
||||
xUnportableCloseRead
|
||||
)
|
||||
|
||||
@@ -410,7 +437,6 @@ type (
|
||||
withOpts struct {
|
||||
bufsize int
|
||||
op Op
|
||||
noFollow bool
|
||||
sendCreate bool
|
||||
}
|
||||
)
|
||||
@@ -469,12 +495,6 @@ func withOps(op Op) addOpt {
|
||||
return func(opt *withOpts) { opt.op = op }
|
||||
}
|
||||
|
||||
// WithNoFollow disables following symlinks, so the symlinks themselves are
|
||||
// watched.
|
||||
func withNoFollow() addOpt {
|
||||
return func(opt *withOpts) { opt.noFollow = true }
|
||||
}
|
||||
|
||||
// "Internal" option for recursive watches on inotify.
|
||||
func withCreate() addOpt {
|
||||
return func(opt *withOpts) { opt.sendCreate = true }
|
||||
@@ -494,3 +514,13 @@ func recursivePath(path string) (string, bool) {
|
||||
}
|
||||
return path, false
|
||||
}
|
||||
|
||||
type watchFlag uint8
|
||||
|
||||
const (
|
||||
// Added by user with Add(), rather than an internal watch.
|
||||
flagByUser = watchFlag(0x01)
|
||||
// Part of recursive watch; as the top-level path added by the user or an
|
||||
// "internal" watch.
|
||||
flagRecurse = watchFlag(0x02)
|
||||
)
|
||||
|
||||
19
vendor/github.com/fsnotify/fsnotify/internal/darwin.go
generated
vendored
19
vendor/github.com/fsnotify/fsnotify/internal/darwin.go
generated
vendored
@@ -15,25 +15,6 @@ var (
|
||||
|
||||
var maxfiles uint64
|
||||
|
||||
func SetRlimit() {
|
||||
// Go 1.19 will do this automatically: https://go-review.googlesource.com/c/go/+/393354/
|
||||
var l syscall.Rlimit
|
||||
err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &l)
|
||||
if err == nil && l.Cur != l.Max {
|
||||
l.Cur = l.Max
|
||||
syscall.Setrlimit(syscall.RLIMIT_NOFILE, &l)
|
||||
}
|
||||
maxfiles = l.Cur
|
||||
|
||||
if n, err := syscall.SysctlUint32("kern.maxfiles"); err == nil && uint64(n) < maxfiles {
|
||||
maxfiles = uint64(n)
|
||||
}
|
||||
|
||||
if n, err := syscall.SysctlUint32("kern.maxfilesperproc"); err == nil && uint64(n) < maxfiles {
|
||||
maxfiles = uint64(n)
|
||||
}
|
||||
}
|
||||
|
||||
func Maxfiles() uint64 { return maxfiles }
|
||||
func Mkfifo(path string, mode uint32) error { return unix.Mkfifo(path, mode) }
|
||||
func Mknod(path string, mode uint32, dev int) error { return unix.Mknod(path, mode, dev) }
|
||||
|
||||
42
vendor/github.com/fsnotify/fsnotify/internal/debug_darwin.go
generated
vendored
42
vendor/github.com/fsnotify/fsnotify/internal/debug_darwin.go
generated
vendored
@@ -6,52 +6,10 @@ var names = []struct {
|
||||
n string
|
||||
m uint32
|
||||
}{
|
||||
{"NOTE_ABSOLUTE", unix.NOTE_ABSOLUTE},
|
||||
{"NOTE_ATTRIB", unix.NOTE_ATTRIB},
|
||||
{"NOTE_BACKGROUND", unix.NOTE_BACKGROUND},
|
||||
{"NOTE_CHILD", unix.NOTE_CHILD},
|
||||
{"NOTE_CRITICAL", unix.NOTE_CRITICAL},
|
||||
{"NOTE_DELETE", unix.NOTE_DELETE},
|
||||
{"NOTE_EXEC", unix.NOTE_EXEC},
|
||||
{"NOTE_EXIT", unix.NOTE_EXIT},
|
||||
{"NOTE_EXITSTATUS", unix.NOTE_EXITSTATUS},
|
||||
{"NOTE_EXIT_CSERROR", unix.NOTE_EXIT_CSERROR},
|
||||
{"NOTE_EXIT_DECRYPTFAIL", unix.NOTE_EXIT_DECRYPTFAIL},
|
||||
{"NOTE_EXIT_DETAIL", unix.NOTE_EXIT_DETAIL},
|
||||
{"NOTE_EXIT_DETAIL_MASK", unix.NOTE_EXIT_DETAIL_MASK},
|
||||
{"NOTE_EXIT_MEMORY", unix.NOTE_EXIT_MEMORY},
|
||||
{"NOTE_EXIT_REPARENTED", unix.NOTE_EXIT_REPARENTED},
|
||||
{"NOTE_EXTEND", unix.NOTE_EXTEND},
|
||||
{"NOTE_FFAND", unix.NOTE_FFAND},
|
||||
{"NOTE_FFCOPY", unix.NOTE_FFCOPY},
|
||||
{"NOTE_FFCTRLMASK", unix.NOTE_FFCTRLMASK},
|
||||
{"NOTE_FFLAGSMASK", unix.NOTE_FFLAGSMASK},
|
||||
{"NOTE_FFNOP", unix.NOTE_FFNOP},
|
||||
{"NOTE_FFOR", unix.NOTE_FFOR},
|
||||
{"NOTE_FORK", unix.NOTE_FORK},
|
||||
{"NOTE_FUNLOCK", unix.NOTE_FUNLOCK},
|
||||
{"NOTE_LEEWAY", unix.NOTE_LEEWAY},
|
||||
{"NOTE_LINK", unix.NOTE_LINK},
|
||||
{"NOTE_LOWAT", unix.NOTE_LOWAT},
|
||||
{"NOTE_MACHTIME", unix.NOTE_MACHTIME},
|
||||
{"NOTE_MACH_CONTINUOUS_TIME", unix.NOTE_MACH_CONTINUOUS_TIME},
|
||||
{"NOTE_NONE", unix.NOTE_NONE},
|
||||
{"NOTE_NSECONDS", unix.NOTE_NSECONDS},
|
||||
{"NOTE_OOB", unix.NOTE_OOB},
|
||||
//{"NOTE_PCTRLMASK", unix.NOTE_PCTRLMASK}, -0x100000 (?!)
|
||||
{"NOTE_PDATAMASK", unix.NOTE_PDATAMASK},
|
||||
{"NOTE_REAP", unix.NOTE_REAP},
|
||||
{"NOTE_RENAME", unix.NOTE_RENAME},
|
||||
{"NOTE_REVOKE", unix.NOTE_REVOKE},
|
||||
{"NOTE_SECONDS", unix.NOTE_SECONDS},
|
||||
{"NOTE_SIGNAL", unix.NOTE_SIGNAL},
|
||||
{"NOTE_TRACK", unix.NOTE_TRACK},
|
||||
{"NOTE_TRACKERR", unix.NOTE_TRACKERR},
|
||||
{"NOTE_TRIGGER", unix.NOTE_TRIGGER},
|
||||
{"NOTE_USECONDS", unix.NOTE_USECONDS},
|
||||
{"NOTE_VM_ERROR", unix.NOTE_VM_ERROR},
|
||||
{"NOTE_VM_PRESSURE", unix.NOTE_VM_PRESSURE},
|
||||
{"NOTE_VM_PRESSURE_SUDDEN_TERMINATE", unix.NOTE_VM_PRESSURE_SUDDEN_TERMINATE},
|
||||
{"NOTE_VM_PRESSURE_TERMINATE", unix.NOTE_VM_PRESSURE_TERMINATE},
|
||||
{"NOTE_WRITE", unix.NOTE_WRITE},
|
||||
}
|
||||
|
||||
18
vendor/github.com/fsnotify/fsnotify/internal/debug_dragonfly.go
generated
vendored
18
vendor/github.com/fsnotify/fsnotify/internal/debug_dragonfly.go
generated
vendored
@@ -7,27 +7,9 @@ var names = []struct {
|
||||
m uint32
|
||||
}{
|
||||
{"NOTE_ATTRIB", unix.NOTE_ATTRIB},
|
||||
{"NOTE_CHILD", unix.NOTE_CHILD},
|
||||
{"NOTE_DELETE", unix.NOTE_DELETE},
|
||||
{"NOTE_EXEC", unix.NOTE_EXEC},
|
||||
{"NOTE_EXIT", unix.NOTE_EXIT},
|
||||
{"NOTE_EXTEND", unix.NOTE_EXTEND},
|
||||
{"NOTE_FFAND", unix.NOTE_FFAND},
|
||||
{"NOTE_FFCOPY", unix.NOTE_FFCOPY},
|
||||
{"NOTE_FFCTRLMASK", unix.NOTE_FFCTRLMASK},
|
||||
{"NOTE_FFLAGSMASK", unix.NOTE_FFLAGSMASK},
|
||||
{"NOTE_FFNOP", unix.NOTE_FFNOP},
|
||||
{"NOTE_FFOR", unix.NOTE_FFOR},
|
||||
{"NOTE_FORK", unix.NOTE_FORK},
|
||||
{"NOTE_LINK", unix.NOTE_LINK},
|
||||
{"NOTE_LOWAT", unix.NOTE_LOWAT},
|
||||
{"NOTE_OOB", unix.NOTE_OOB},
|
||||
{"NOTE_PCTRLMASK", unix.NOTE_PCTRLMASK},
|
||||
{"NOTE_PDATAMASK", unix.NOTE_PDATAMASK},
|
||||
{"NOTE_RENAME", unix.NOTE_RENAME},
|
||||
{"NOTE_REVOKE", unix.NOTE_REVOKE},
|
||||
{"NOTE_TRACK", unix.NOTE_TRACK},
|
||||
{"NOTE_TRACKERR", unix.NOTE_TRACKERR},
|
||||
{"NOTE_TRIGGER", unix.NOTE_TRIGGER},
|
||||
{"NOTE_WRITE", unix.NOTE_WRITE},
|
||||
}
|
||||
|
||||
34
vendor/github.com/fsnotify/fsnotify/internal/debug_freebsd.go
generated
vendored
34
vendor/github.com/fsnotify/fsnotify/internal/debug_freebsd.go
generated
vendored
@@ -6,37 +6,15 @@ var names = []struct {
|
||||
n string
|
||||
m uint32
|
||||
}{
|
||||
{"NOTE_ABSTIME", unix.NOTE_ABSTIME},
|
||||
{"NOTE_ATTRIB", unix.NOTE_ATTRIB},
|
||||
{"NOTE_CHILD", unix.NOTE_CHILD},
|
||||
{"NOTE_CLOSE", unix.NOTE_CLOSE},
|
||||
{"NOTE_CLOSE_WRITE", unix.NOTE_CLOSE_WRITE},
|
||||
{"NOTE_DELETE", unix.NOTE_DELETE},
|
||||
{"NOTE_EXEC", unix.NOTE_EXEC},
|
||||
{"NOTE_EXIT", unix.NOTE_EXIT},
|
||||
{"NOTE_WRITE", unix.NOTE_WRITE},
|
||||
{"NOTE_EXTEND", unix.NOTE_EXTEND},
|
||||
{"NOTE_FFAND", unix.NOTE_FFAND},
|
||||
{"NOTE_FFCOPY", unix.NOTE_FFCOPY},
|
||||
{"NOTE_FFCTRLMASK", unix.NOTE_FFCTRLMASK},
|
||||
{"NOTE_FFLAGSMASK", unix.NOTE_FFLAGSMASK},
|
||||
{"NOTE_FFNOP", unix.NOTE_FFNOP},
|
||||
{"NOTE_FFOR", unix.NOTE_FFOR},
|
||||
{"NOTE_FILE_POLL", unix.NOTE_FILE_POLL},
|
||||
{"NOTE_FORK", unix.NOTE_FORK},
|
||||
{"NOTE_ATTRIB", unix.NOTE_ATTRIB},
|
||||
{"NOTE_LINK", unix.NOTE_LINK},
|
||||
{"NOTE_LOWAT", unix.NOTE_LOWAT},
|
||||
{"NOTE_MSECONDS", unix.NOTE_MSECONDS},
|
||||
{"NOTE_NSECONDS", unix.NOTE_NSECONDS},
|
||||
{"NOTE_OPEN", unix.NOTE_OPEN},
|
||||
{"NOTE_PCTRLMASK", unix.NOTE_PCTRLMASK},
|
||||
{"NOTE_PDATAMASK", unix.NOTE_PDATAMASK},
|
||||
{"NOTE_READ", unix.NOTE_READ},
|
||||
{"NOTE_RENAME", unix.NOTE_RENAME},
|
||||
{"NOTE_REVOKE", unix.NOTE_REVOKE},
|
||||
{"NOTE_SECONDS", unix.NOTE_SECONDS},
|
||||
{"NOTE_TRACK", unix.NOTE_TRACK},
|
||||
{"NOTE_TRACKERR", unix.NOTE_TRACKERR},
|
||||
{"NOTE_TRIGGER", unix.NOTE_TRIGGER},
|
||||
{"NOTE_USECONDS", unix.NOTE_USECONDS},
|
||||
{"NOTE_WRITE", unix.NOTE_WRITE},
|
||||
{"NOTE_OPEN", unix.NOTE_OPEN},
|
||||
{"NOTE_CLOSE", unix.NOTE_CLOSE},
|
||||
{"NOTE_CLOSE_WRITE", unix.NOTE_CLOSE_WRITE},
|
||||
{"NOTE_READ", unix.NOTE_READ},
|
||||
}
|
||||
|
||||
2
vendor/github.com/fsnotify/fsnotify/internal/debug_kqueue.go
generated
vendored
2
vendor/github.com/fsnotify/fsnotify/internal/debug_kqueue.go
generated
vendored
@@ -27,6 +27,6 @@ func Debug(name string, kevent *unix.Kevent_t) {
|
||||
if unknown > 0 {
|
||||
l = append(l, fmt.Sprintf("0x%x", unknown))
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "FSNOTIFY_DEBUG: %s %10d:%-60s → %q\n",
|
||||
fmt.Fprintf(os.Stderr, "FSNOTIFY_DEBUG: %s %10d:%-20s → %q\n",
|
||||
time.Now().Format("15:04:05.000000000"), mask, strings.Join(l, " | "), name)
|
||||
}
|
||||
|
||||
10
vendor/github.com/fsnotify/fsnotify/internal/debug_netbsd.go
generated
vendored
10
vendor/github.com/fsnotify/fsnotify/internal/debug_netbsd.go
generated
vendored
@@ -7,19 +7,9 @@ var names = []struct {
|
||||
m uint32
|
||||
}{
|
||||
{"NOTE_ATTRIB", unix.NOTE_ATTRIB},
|
||||
{"NOTE_CHILD", unix.NOTE_CHILD},
|
||||
{"NOTE_DELETE", unix.NOTE_DELETE},
|
||||
{"NOTE_EXEC", unix.NOTE_EXEC},
|
||||
{"NOTE_EXIT", unix.NOTE_EXIT},
|
||||
{"NOTE_EXTEND", unix.NOTE_EXTEND},
|
||||
{"NOTE_FORK", unix.NOTE_FORK},
|
||||
{"NOTE_LINK", unix.NOTE_LINK},
|
||||
{"NOTE_LOWAT", unix.NOTE_LOWAT},
|
||||
{"NOTE_PCTRLMASK", unix.NOTE_PCTRLMASK},
|
||||
{"NOTE_PDATAMASK", unix.NOTE_PDATAMASK},
|
||||
{"NOTE_RENAME", unix.NOTE_RENAME},
|
||||
{"NOTE_REVOKE", unix.NOTE_REVOKE},
|
||||
{"NOTE_TRACK", unix.NOTE_TRACK},
|
||||
{"NOTE_TRACKERR", unix.NOTE_TRACKERR},
|
||||
{"NOTE_WRITE", unix.NOTE_WRITE},
|
||||
}
|
||||
|
||||
12
vendor/github.com/fsnotify/fsnotify/internal/debug_openbsd.go
generated
vendored
12
vendor/github.com/fsnotify/fsnotify/internal/debug_openbsd.go
generated
vendored
@@ -7,22 +7,10 @@ var names = []struct {
|
||||
m uint32
|
||||
}{
|
||||
{"NOTE_ATTRIB", unix.NOTE_ATTRIB},
|
||||
// {"NOTE_CHANGE", unix.NOTE_CHANGE}, // Not on 386?
|
||||
{"NOTE_CHILD", unix.NOTE_CHILD},
|
||||
{"NOTE_DELETE", unix.NOTE_DELETE},
|
||||
{"NOTE_EOF", unix.NOTE_EOF},
|
||||
{"NOTE_EXEC", unix.NOTE_EXEC},
|
||||
{"NOTE_EXIT", unix.NOTE_EXIT},
|
||||
{"NOTE_EXTEND", unix.NOTE_EXTEND},
|
||||
{"NOTE_FORK", unix.NOTE_FORK},
|
||||
{"NOTE_LINK", unix.NOTE_LINK},
|
||||
{"NOTE_LOWAT", unix.NOTE_LOWAT},
|
||||
{"NOTE_PCTRLMASK", unix.NOTE_PCTRLMASK},
|
||||
{"NOTE_PDATAMASK", unix.NOTE_PDATAMASK},
|
||||
{"NOTE_RENAME", unix.NOTE_RENAME},
|
||||
{"NOTE_REVOKE", unix.NOTE_REVOKE},
|
||||
{"NOTE_TRACK", unix.NOTE_TRACK},
|
||||
{"NOTE_TRACKERR", unix.NOTE_TRACKERR},
|
||||
{"NOTE_TRUNCATE", unix.NOTE_TRUNCATE},
|
||||
{"NOTE_WRITE", unix.NOTE_WRITE},
|
||||
}
|
||||
|
||||
11
vendor/github.com/fsnotify/fsnotify/internal/freebsd.go
generated
vendored
11
vendor/github.com/fsnotify/fsnotify/internal/freebsd.go
generated
vendored
@@ -15,17 +15,6 @@ var (
|
||||
|
||||
var maxfiles uint64
|
||||
|
||||
func SetRlimit() {
|
||||
// Go 1.19 will do this automatically: https://go-review.googlesource.com/c/go/+/393354/
|
||||
var l syscall.Rlimit
|
||||
err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &l)
|
||||
if err == nil && l.Cur != l.Max {
|
||||
l.Cur = l.Max
|
||||
syscall.Setrlimit(syscall.RLIMIT_NOFILE, &l)
|
||||
}
|
||||
maxfiles = uint64(l.Cur)
|
||||
}
|
||||
|
||||
func Maxfiles() uint64 { return maxfiles }
|
||||
func Mkfifo(path string, mode uint32) error { return unix.Mkfifo(path, mode) }
|
||||
func Mknod(path string, mode uint32, dev int) error { return unix.Mknod(path, mode, uint64(dev)) }
|
||||
|
||||
11
vendor/github.com/fsnotify/fsnotify/internal/unix.go
generated
vendored
11
vendor/github.com/fsnotify/fsnotify/internal/unix.go
generated
vendored
@@ -15,17 +15,6 @@ var (
|
||||
|
||||
var maxfiles uint64
|
||||
|
||||
func SetRlimit() {
|
||||
// Go 1.19 will do this automatically: https://go-review.googlesource.com/c/go/+/393354/
|
||||
var l syscall.Rlimit
|
||||
err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &l)
|
||||
if err == nil && l.Cur != l.Max {
|
||||
l.Cur = l.Max
|
||||
syscall.Setrlimit(syscall.RLIMIT_NOFILE, &l)
|
||||
}
|
||||
maxfiles = uint64(l.Cur)
|
||||
}
|
||||
|
||||
func Maxfiles() uint64 { return maxfiles }
|
||||
func Mkfifo(path string, mode uint32) error { return unix.Mkfifo(path, mode) }
|
||||
func Mknod(path string, mode uint32, dev int) error { return unix.Mknod(path, mode, dev) }
|
||||
|
||||
18
vendor/github.com/fsnotify/fsnotify/internal/unix2.go
generated
vendored
18
vendor/github.com/fsnotify/fsnotify/internal/unix2.go
generated
vendored
@@ -2,6 +2,24 @@
|
||||
|
||||
package internal
|
||||
|
||||
import "syscall"
|
||||
|
||||
func HasPrivilegesForSymlink() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// IgnoringEINTR makes a function call and repeats it if it returns an
|
||||
// EINTR error. This appears to be required even though we install all
|
||||
// signal handlers with SA_RESTART: see #22838, #38033, #38836, #40846.
|
||||
// Also #20400 and #36644 are issues in which a signal handler is
|
||||
// installed without setting SA_RESTART. None of these are the common case,
|
||||
// but there are enough of them that it seems that we can't avoid
|
||||
// an EINTR loop.
|
||||
func IgnoringEINTR[T any](fn func() (T, error)) (T, error) {
|
||||
for {
|
||||
v, err := fn()
|
||||
if err != syscall.EINTR {
|
||||
return v, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1
vendor/github.com/fsnotify/fsnotify/internal/windows.go
generated
vendored
1
vendor/github.com/fsnotify/fsnotify/internal/windows.go
generated
vendored
@@ -14,7 +14,6 @@ var (
|
||||
ErrUnixEACCES = errors.New("dummy")
|
||||
)
|
||||
|
||||
func SetRlimit() {}
|
||||
func Maxfiles() uint64 { return 1<<64 - 1 }
|
||||
func Mkfifo(path string, mode uint32) error { return errors.New("no FIFOs on Windows") }
|
||||
func Mknod(path string, mode uint32, dev int) error { return errors.New("no device nodes on Windows") }
|
||||
|
||||
1
vendor/github.com/goccy/go-json/internal/encoder/code.go
generated
vendored
1
vendor/github.com/goccy/go-json/internal/encoder/code.go
generated
vendored
@@ -518,6 +518,7 @@ func (c *StructCode) ToAnonymousOpcode(ctx *compileContext) Opcodes {
|
||||
prevField = firstField
|
||||
codes = codes.Add(fieldCodes...)
|
||||
}
|
||||
ctx.structTypeToCodes[uintptr(unsafe.Pointer(c.typ))] = codes
|
||||
return codes
|
||||
}
|
||||
|
||||
|
||||
7
vendor/github.com/lestrrat-go/dsig/.goreleaser.yml
generated
vendored
Normal file
7
vendor/github.com/lestrrat-go/dsig/.goreleaser.yml
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
version: 2
|
||||
|
||||
builds:
|
||||
- skip: true
|
||||
|
||||
changelog:
|
||||
use: github-native
|
||||
25
vendor/github.com/lestrrat-go/dsig/Changes
generated
vendored
25
vendor/github.com/lestrrat-go/dsig/Changes
generated
vendored
@@ -1,5 +1,28 @@
|
||||
Changes
|
||||
=======
|
||||
|
||||
v1.2.1 7 Apr 2026
|
||||
* Add `SignDigest()` for signing pre-computed digests. Supported for HMAC,
|
||||
RSA (PKCS1v15 and PSS), and ECDSA families. EdDSA and Custom return an error.
|
||||
|
||||
v1.2.0 6 Apr 2026
|
||||
* Add `VerifyDigest()` for verifying signatures against pre-computed digests.
|
||||
Supported for HMAC, RSA, and ECDSA families. EdDSA and Custom return an error.
|
||||
|
||||
* Add low-level digest verification functions: `VerifyHMACDigest()`,
|
||||
`VerifyRSADigest()`, `VerifyECDSADigest()`.
|
||||
|
||||
v1.1.0 2 Apr 2026
|
||||
* Add `Custom` algorithm family for registering user-defined sign/verify
|
||||
implementations. For the `Custom` family, `AlgorithmInfo.Meta` must implement
|
||||
the `Signer` and/or `Verifier` interfaces. The implementation struct can
|
||||
carry any additional metadata it needs (hash functions, curves, etc.).
|
||||
|
||||
* Add `UnregisterAlgorithm()` for removing previously registered custom
|
||||
algorithms. Built-in algorithms are protected and cannot be unregistered.
|
||||
|
||||
* `RegisterAlgorithm()` now rejects re-registration of an already-registered
|
||||
algorithm name. Use `UnregisterAlgorithm()` first if you need to replace it.
|
||||
|
||||
v1.0.0 - 18 Aug 2025
|
||||
* Initial release
|
||||
* Initial release
|
||||
2
vendor/github.com/lestrrat-go/dsig/README.md
generated
vendored
2
vendor/github.com/lestrrat-go/dsig/README.md
generated
vendored
@@ -11,7 +11,7 @@ While there are many standards for generating and verifying digital signatures,
|
||||
* EdDSA signatures (Ed25519, Ed448)
|
||||
* HMAC signatures (SHA-256, SHA-384, SHA-512)
|
||||
* Support for crypto.Signer interface
|
||||
* Allows for dynamic additions of algorithms in limited cases.
|
||||
* Custom algorithm registration via `Signer`/`Verifier` interfaces
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
|
||||
57
vendor/github.com/lestrrat-go/dsig/dsig.go
generated
vendored
57
vendor/github.com/lestrrat-go/dsig/dsig.go
generated
vendored
@@ -14,6 +14,7 @@ import (
|
||||
"crypto/sha512"
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
@@ -26,6 +27,7 @@ const (
|
||||
RSA
|
||||
ECDSA
|
||||
EdDSAFamily
|
||||
Custom
|
||||
maxFamily
|
||||
)
|
||||
|
||||
@@ -40,6 +42,8 @@ func (f Family) String() string {
|
||||
return "ECDSA"
|
||||
case EdDSAFamily:
|
||||
return "EdDSA"
|
||||
case Custom:
|
||||
return "Custom"
|
||||
default:
|
||||
return "InvalidFamily"
|
||||
}
|
||||
@@ -73,18 +77,43 @@ type EdDSAFamilyMeta struct {
|
||||
// Reserved for future use
|
||||
}
|
||||
|
||||
// Signer is an interface for custom signing implementations.
|
||||
// For the Custom algorithm family, info.Meta must implement this interface
|
||||
// to support signing. The implementation struct can carry any additional
|
||||
// metadata it needs (hash functions, curves, etc.).
|
||||
type Signer interface {
|
||||
Sign(key any, payload []byte, rand io.Reader) ([]byte, error)
|
||||
}
|
||||
|
||||
// Verifier is an interface for custom verification implementations.
|
||||
// For the Custom algorithm family, info.Meta must implement this interface
|
||||
// to support verification. The implementation struct can carry any additional
|
||||
// metadata it needs (hash functions, curves, etc.).
|
||||
type Verifier interface {
|
||||
Verify(key any, payload, signature []byte) error
|
||||
}
|
||||
|
||||
var algorithms = make(map[string]AlgorithmInfo)
|
||||
var builtinAlgorithms = make(map[string]struct{})
|
||||
var muAlgorithms sync.RWMutex
|
||||
|
||||
// RegisterAlgorithm registers a new digital signature algorithm with the specified family and metadata.
|
||||
//
|
||||
// info.Meta should contain extra metadata for some algorithms. Currently HMAC, RSA,
|
||||
// and ECDSA family of algorithms need their respective metadata (HMACFamilyMeta,
|
||||
// RSAFamilyMeta, and ECDSAFamilyMeta). Metadata for other families are ignored.
|
||||
// info.Meta should contain extra metadata for some algorithms. HMAC, RSA, and ECDSA
|
||||
// families need their respective metadata (HMACFamilyMeta, RSAFamilyMeta, and
|
||||
// ECDSAFamilyMeta). Metadata for EdDSA is optional. For the Custom family, Meta
|
||||
// must implement at least one of the Signer or Verifier interfaces.
|
||||
//
|
||||
// Re-registration of an already-registered algorithm name is rejected. Use
|
||||
// UnregisterAlgorithm to remove it first if you need to replace it.
|
||||
func RegisterAlgorithm(name string, info AlgorithmInfo) error {
|
||||
muAlgorithms.Lock()
|
||||
defer muAlgorithms.Unlock()
|
||||
|
||||
if _, exists := algorithms[name]; exists {
|
||||
return fmt.Errorf("algorithm %s is already registered", name)
|
||||
}
|
||||
|
||||
// Validate the metadata matches the family
|
||||
switch info.Family {
|
||||
case HMAC:
|
||||
@@ -101,6 +130,12 @@ func RegisterAlgorithm(name string, info AlgorithmInfo) error {
|
||||
}
|
||||
case EdDSAFamily:
|
||||
// EdDSA metadata is optional for now
|
||||
case Custom:
|
||||
_, isSigner := info.Meta.(Signer)
|
||||
_, isVerifier := info.Meta.(Verifier)
|
||||
if !isSigner && !isVerifier {
|
||||
return fmt.Errorf("custom algorithm %s: Meta must implement Signer and/or Verifier", name)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unsupported algorithm family %s for algorithm %s", info.Family, name)
|
||||
}
|
||||
@@ -109,6 +144,21 @@ func RegisterAlgorithm(name string, info AlgorithmInfo) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnregisterAlgorithm removes a previously registered algorithm by name.
|
||||
// Built-in algorithms cannot be unregistered.
|
||||
// It is a no-op if the algorithm is not registered.
|
||||
func UnregisterAlgorithm(name string) error {
|
||||
muAlgorithms.Lock()
|
||||
defer muAlgorithms.Unlock()
|
||||
|
||||
if _, ok := builtinAlgorithms[name]; ok {
|
||||
return fmt.Errorf("algorithm %s is a built-in algorithm and cannot be unregistered", name)
|
||||
}
|
||||
|
||||
delete(algorithms, name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetAlgorithmInfo retrieves the algorithm information for a given algorithm name.
|
||||
// Returns the info and true if found, zero value and false if not found.
|
||||
func GetAlgorithmInfo(name string) (AlgorithmInfo, bool) {
|
||||
@@ -219,6 +269,7 @@ func init() {
|
||||
if err := RegisterAlgorithm(name, info); err != nil {
|
||||
panic(fmt.Sprintf("failed to register algorithm %s: %v", name, err))
|
||||
}
|
||||
builtinAlgorithms[name] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
15
vendor/github.com/lestrrat-go/dsig/ecdsa.go
generated
vendored
15
vendor/github.com/lestrrat-go/dsig/ecdsa.go
generated
vendored
@@ -176,6 +176,21 @@ func VerifyECDSA(key *ecdsa.PublicKey, payload, signature []byte, h crypto.Hash)
|
||||
return ecdsaVerify(key, payload, h, &r, &s)
|
||||
}
|
||||
|
||||
// VerifyECDSADigest verifies an ECDSA signature given a pre-computed digest.
|
||||
// The caller is responsible for hashing the signing input with the correct
|
||||
// hash function for the algorithm (e.g. SHA-256 for ES256). This function
|
||||
// does not validate the digest length.
|
||||
func VerifyECDSADigest(key *ecdsa.PublicKey, digest, signature []byte) error {
|
||||
var r, s big.Int
|
||||
if err := UnpackECDSASignature(signature, key, &r, &s); err != nil {
|
||||
return fmt.Errorf("dsig.VerifyECDSADigest: %w", err)
|
||||
}
|
||||
if !ecdsa.Verify(key, digest, &r, &s) {
|
||||
return NewVerificationError("invalid ECDSA signature")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// VerifyECDSACryptoSigner verifies an ECDSA signature for crypto.Signer implementations.
|
||||
// This function is useful for verifying signatures created by hardware security modules
|
||||
// or other implementations of the crypto.Signer interface.
|
||||
|
||||
8
vendor/github.com/lestrrat-go/dsig/hmac.go
generated
vendored
8
vendor/github.com/lestrrat-go/dsig/hmac.go
generated
vendored
@@ -30,6 +30,14 @@ func SignHMAC(key, payload []byte, hfunc func() hash.Hash) ([]byte, error) {
|
||||
return h.Sum(nil), nil
|
||||
}
|
||||
|
||||
// VerifyHMACDigest verifies an HMAC signature given a pre-computed MAC.
|
||||
func VerifyHMACDigest(computedMAC, signature []byte) error {
|
||||
if !hmac.Equal(computedMAC, signature) {
|
||||
return NewVerificationError("invalid HMAC signature")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// VerifyHMAC verifies an HMAC signature for the given payload.
|
||||
// This function verifies the signature using the specified key and hash function.
|
||||
// The payload parameter should be the pre-computed signing input (typically header.payload).
|
||||
|
||||
14
vendor/github.com/lestrrat-go/dsig/rsa.go
generated
vendored
14
vendor/github.com/lestrrat-go/dsig/rsa.go
generated
vendored
@@ -61,3 +61,17 @@ func VerifyRSA(key *rsa.PublicKey, payload, signature []byte, h crypto.Hash, pss
|
||||
}
|
||||
return rsa.VerifyPKCS1v15(key, h, digest, signature)
|
||||
}
|
||||
|
||||
// VerifyRSADigest verifies an RSA signature given a pre-computed digest.
|
||||
// If pss is true, RSA-PSS verification is used; otherwise, PKCS#1 v1.5 is used.
|
||||
func VerifyRSADigest(key *rsa.PublicKey, digest, signature []byte, h crypto.Hash, pss bool) error {
|
||||
// isValidRSAKey only rejects non-RSA private key types, so this check is
|
||||
// a no-op for *rsa.PublicKey. Kept for consistency with VerifyRSA.
|
||||
if !isValidRSAKey(key) {
|
||||
return fmt.Errorf(`invalid key type %T for RSA algorithm`, key)
|
||||
}
|
||||
if pss {
|
||||
return rsa.VerifyPSS(key, h, digest, signature, &rsa.PSSOptions{Hash: h, SaltLength: rsa.PSSSaltLengthEqualsHash})
|
||||
}
|
||||
return rsa.VerifyPKCS1v15(key, h, digest, signature)
|
||||
}
|
||||
|
||||
116
vendor/github.com/lestrrat-go/dsig/sign.go
generated
vendored
116
vendor/github.com/lestrrat-go/dsig/sign.go
generated
vendored
@@ -2,6 +2,8 @@ package dsig
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -9,9 +11,6 @@ import (
|
||||
|
||||
// Sign generates a digital signature using the specified key and algorithm.
|
||||
//
|
||||
// This function loads the signer registered in the dsig package _ONLY_.
|
||||
// It does not support custom signers that the user might have registered.
|
||||
//
|
||||
// rr is an io.Reader that provides randomness for signing. If rr is nil, it defaults to rand.Reader.
|
||||
// Not all algorithms require this parameter, but it is included for consistency.
|
||||
// 99% of the time, you can pass nil for rr, and it will work fine.
|
||||
@@ -30,6 +29,8 @@ func Sign(key any, alg string, payload []byte, rr io.Reader) ([]byte, error) {
|
||||
return dispatchECDSASign(key, info, payload, rr)
|
||||
case EdDSAFamily:
|
||||
return dispatchEdDSASign(key, info, payload, rr)
|
||||
case Custom:
|
||||
return dispatchCustomSign(key, info, payload, rr)
|
||||
default:
|
||||
return nil, fmt.Errorf(`dsig.Sign: unsupported signature family %q`, info.Family)
|
||||
}
|
||||
@@ -98,3 +99,112 @@ func dispatchECDSASign(key any, info AlgorithmInfo, payload []byte, rr io.Reader
|
||||
}
|
||||
return SignECDSA(privkey, payload, meta.Hash, rr)
|
||||
}
|
||||
|
||||
func dispatchCustomSign(key any, info AlgorithmInfo, payload []byte, rr io.Reader) ([]byte, error) {
|
||||
signer, ok := info.Meta.(Signer)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf(`dsig.Sign: algorithm has no signer registered`)
|
||||
}
|
||||
return signer.Sign(key, payload, rr)
|
||||
}
|
||||
|
||||
// SignDigest generates a digital signature from a pre-computed digest.
|
||||
//
|
||||
// For RSA/ECDSA, digest is the hash of the signing input and key is the
|
||||
// private key used for signing.
|
||||
//
|
||||
// For HMAC, the digest must be the pre-computed MAC (i.e. the output of
|
||||
// hmac.New(hashFunc, key) after writing the signing input). The digest IS
|
||||
// the signature, so it is returned as-is.
|
||||
//
|
||||
// EdDSA and Custom families are not supported and return an error.
|
||||
//
|
||||
// rr is an io.Reader that provides randomness for signing. If rr is nil,
|
||||
// it defaults to rand.Reader.
|
||||
func SignDigest(key any, alg string, digest []byte, rr io.Reader) ([]byte, error) {
|
||||
info, ok := GetAlgorithmInfo(alg)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf(`dsig.SignDigest: unsupported signature algorithm %q`, alg)
|
||||
}
|
||||
|
||||
switch info.Family {
|
||||
case HMAC:
|
||||
// The caller already computed the HMAC (which incorporates the key)
|
||||
// and passed it as digest. The digest IS the signature.
|
||||
return digest, nil
|
||||
case RSA:
|
||||
return dispatchRSASignDigest(key, info, digest, rr)
|
||||
case ECDSA:
|
||||
return dispatchECDSASignDigest(key, info, digest, rr)
|
||||
case EdDSAFamily:
|
||||
return nil, fmt.Errorf(`dsig.SignDigest: EdDSA does not support digest-based signing`)
|
||||
case Custom:
|
||||
return nil, fmt.Errorf(`dsig.SignDigest: custom algorithms do not support digest-based signing`)
|
||||
default:
|
||||
return nil, fmt.Errorf(`dsig.SignDigest: unsupported signature family %q`, info.Family)
|
||||
}
|
||||
}
|
||||
|
||||
func dispatchRSASignDigest(key any, info AlgorithmInfo, digest []byte, rr io.Reader) ([]byte, error) {
|
||||
meta, ok := info.Meta.(RSAFamilyMeta)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf(`dsig.SignDigest: invalid RSA metadata`)
|
||||
}
|
||||
|
||||
if rr == nil {
|
||||
rr = rand.Reader
|
||||
}
|
||||
|
||||
cs, isCryptoSigner, err := rsaGetSignerCryptoSignerKey(key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`dsig.SignDigest: %w`, err)
|
||||
}
|
||||
if isCryptoSigner {
|
||||
var opts crypto.SignerOpts = meta.Hash
|
||||
if meta.PSS {
|
||||
rsaopts := rsaPSSOptions(meta.Hash)
|
||||
opts = &rsaopts
|
||||
}
|
||||
return cs.Sign(rr, digest, opts)
|
||||
}
|
||||
|
||||
privkey, ok := key.(*rsa.PrivateKey)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf(`dsig.SignDigest: invalid key type %T. *rsa.PrivateKey is required`, key)
|
||||
}
|
||||
if meta.PSS {
|
||||
rsaopts := rsaPSSOptions(meta.Hash)
|
||||
return rsa.SignPSS(rr, privkey, meta.Hash, digest, &rsaopts)
|
||||
}
|
||||
return rsa.SignPKCS1v15(rr, privkey, meta.Hash, digest)
|
||||
}
|
||||
|
||||
func dispatchECDSASignDigest(key any, info AlgorithmInfo, digest []byte, rr io.Reader) ([]byte, error) {
|
||||
meta, ok := info.Meta.(ECDSAFamilyMeta)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf(`dsig.SignDigest: invalid ECDSA metadata`)
|
||||
}
|
||||
|
||||
if rr == nil {
|
||||
rr = rand.Reader
|
||||
}
|
||||
|
||||
privkey, cs, isCryptoSigner, err := ecdsaGetSignerKey(key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`dsig.SignDigest: %w`, err)
|
||||
}
|
||||
if isCryptoSigner {
|
||||
signed, err := cs.Sign(rr, digest, meta.Hash)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`dsig.SignDigest: failed to sign digest using crypto.Signer: %w`, err)
|
||||
}
|
||||
return signECDSACryptoSigner(cs, signed)
|
||||
}
|
||||
|
||||
r, s, err := ecdsa.Sign(rr, privkey, digest)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`dsig.SignDigest: failed to sign digest using ecdsa: %w`, err)
|
||||
}
|
||||
return PackECDSASignature(r, s, privkey.Curve.Params().BitSize)
|
||||
}
|
||||
|
||||
|
||||
100
vendor/github.com/lestrrat-go/dsig/verify.go
generated
vendored
100
vendor/github.com/lestrrat-go/dsig/verify.go
generated
vendored
@@ -9,9 +9,6 @@ import (
|
||||
)
|
||||
|
||||
// Verify verifies a digital signature using the specified key and algorithm.
|
||||
//
|
||||
// This function loads the verifier registered in the dsig package _ONLY_.
|
||||
// It does not support custom verifiers that the user might have registered.
|
||||
func Verify(key any, alg string, payload, signature []byte) error {
|
||||
info, ok := GetAlgorithmInfo(alg)
|
||||
if !ok {
|
||||
@@ -27,11 +24,100 @@ func Verify(key any, alg string, payload, signature []byte) error {
|
||||
return dispatchECDSAVerify(key, info, payload, signature)
|
||||
case EdDSAFamily:
|
||||
return dispatchEdDSAVerify(key, info, payload, signature)
|
||||
case Custom:
|
||||
return dispatchCustomVerify(key, info, payload, signature)
|
||||
default:
|
||||
return fmt.Errorf(`dsig.Verify: unsupported signature family %q`, info.Family)
|
||||
}
|
||||
}
|
||||
|
||||
// VerifyDigest verifies a signature given a pre-computed digest.
|
||||
//
|
||||
// For RSA/ECDSA, digest is the hash of the signing input and key is the
|
||||
// public key used for verification.
|
||||
//
|
||||
// For HMAC, digest must be the pre-computed MAC (i.e. the output of
|
||||
// hmac.New(hashFunc, key) after writing the signing input). The key
|
||||
// parameter is not used because it is already incorporated into the MAC.
|
||||
//
|
||||
// EdDSA and Custom families are not supported and return an error.
|
||||
func VerifyDigest(key any, alg string, digest, signature []byte) error {
|
||||
info, ok := GetAlgorithmInfo(alg)
|
||||
if !ok {
|
||||
return fmt.Errorf(`dsig.VerifyDigest: unsupported signature algorithm %q`, alg)
|
||||
}
|
||||
|
||||
switch info.Family {
|
||||
case HMAC:
|
||||
// key is not used here: the caller has already computed the HMAC
|
||||
// (which incorporates the key) and passed it as digest.
|
||||
return VerifyHMACDigest(digest, signature)
|
||||
case RSA:
|
||||
return dispatchRSAVerifyDigest(key, info, digest, signature)
|
||||
case ECDSA:
|
||||
return dispatchECDSAVerifyDigest(key, info, digest, signature)
|
||||
case EdDSAFamily:
|
||||
return fmt.Errorf(`dsig.VerifyDigest: EdDSA does not support digest-based verification`)
|
||||
case Custom:
|
||||
// TODO: a DigestVerifier interface (optional, checked here) would let
|
||||
// custom algorithms opt in to digest-based verification.
|
||||
return fmt.Errorf(`dsig.VerifyDigest: custom algorithms do not support digest-based verification`)
|
||||
default:
|
||||
return fmt.Errorf(`dsig.VerifyDigest: unsupported signature family %q`, info.Family)
|
||||
}
|
||||
}
|
||||
|
||||
func dispatchRSAVerifyDigest(key any, info AlgorithmInfo, digest, signature []byte) error {
|
||||
meta, ok := info.Meta.(RSAFamilyMeta)
|
||||
if !ok {
|
||||
return fmt.Errorf(`dsig.VerifyDigest: invalid RSA metadata`)
|
||||
}
|
||||
|
||||
var pubkey *rsa.PublicKey
|
||||
|
||||
if cs, ok := key.(crypto.Signer); ok {
|
||||
cpub := cs.Public()
|
||||
switch cpub := cpub.(type) {
|
||||
case rsa.PublicKey:
|
||||
pubkey = &cpub
|
||||
case *rsa.PublicKey:
|
||||
pubkey = cpub
|
||||
default:
|
||||
return fmt.Errorf(`dsig.VerifyDigest: failed to retrieve rsa.PublicKey out of crypto.Signer %T`, key)
|
||||
}
|
||||
} else {
|
||||
var ok bool
|
||||
pubkey, ok = key.(*rsa.PublicKey)
|
||||
if !ok {
|
||||
return fmt.Errorf(`dsig.VerifyDigest: failed to retrieve *rsa.PublicKey out of %T`, key)
|
||||
}
|
||||
}
|
||||
|
||||
return VerifyRSADigest(pubkey, digest, signature, meta.Hash, meta.PSS)
|
||||
}
|
||||
|
||||
// Note: the crypto.Signer → *ecdsa.PublicKey extraction below duplicates
|
||||
// logic in VerifyECDSACryptoSigner. We can't call that function because it
|
||||
// hashes the payload internally. If the extraction logic changes, update both.
|
||||
func dispatchECDSAVerifyDigest(key any, info AlgorithmInfo, digest, signature []byte) error {
|
||||
pubkey, cs, isCryptoSigner, err := ecdsaGetVerifierKey(key)
|
||||
if err != nil {
|
||||
return fmt.Errorf(`dsig.VerifyDigest: %w`, err)
|
||||
}
|
||||
if isCryptoSigner {
|
||||
cpub := cs.Public()
|
||||
switch cpub := cpub.(type) {
|
||||
case ecdsa.PublicKey:
|
||||
pubkey = &cpub
|
||||
case *ecdsa.PublicKey:
|
||||
pubkey = cpub
|
||||
default:
|
||||
return fmt.Errorf(`dsig.VerifyDigest: expected *ecdsa.PublicKey from crypto.Signer, got %T`, cpub)
|
||||
}
|
||||
}
|
||||
return VerifyECDSADigest(pubkey, digest, signature)
|
||||
}
|
||||
|
||||
func dispatchHMACVerify(key any, info AlgorithmInfo, payload, signature []byte) error {
|
||||
meta, ok := info.Meta.(HMACFamilyMeta)
|
||||
if !ok {
|
||||
@@ -110,6 +196,14 @@ func dispatchEdDSAVerify(key any, _ AlgorithmInfo, payload, signature []byte) er
|
||||
return VerifyEdDSA(pubkey, payload, signature)
|
||||
}
|
||||
|
||||
func dispatchCustomVerify(key any, info AlgorithmInfo, payload, signature []byte) error {
|
||||
verifier, ok := info.Meta.(Verifier)
|
||||
if !ok {
|
||||
return fmt.Errorf(`dsig.Verify: algorithm has no verifier registered`)
|
||||
}
|
||||
return verifier.Verify(key, payload, signature)
|
||||
}
|
||||
|
||||
func ecdsaGetVerifierKey(key any) (*ecdsa.PublicKey, crypto.Signer, bool, error) {
|
||||
cs, isCryptoSigner := key.(crypto.Signer)
|
||||
if isCryptoSigner {
|
||||
|
||||
16
vendor/github.com/lestrrat-go/httprc/v3/Changes
generated
vendored
16
vendor/github.com/lestrrat-go/httprc/v3/Changes
generated
vendored
@@ -1,6 +1,22 @@
|
||||
Changes
|
||||
=======
|
||||
|
||||
v3.0.5 30 Mar 2026
|
||||
* Fix periodic check deadlock when number of ready resources exceeds
|
||||
outgoing channel buffer, which caused circular wait between controller
|
||||
and worker goroutines (#113, #116)
|
||||
* Fix proxysink self-deadlock caused by missing mutex unlock on context
|
||||
cancellation path
|
||||
|
||||
v3.0.4 08 Feb 2026
|
||||
* Fix worker goroutine dying on sync refresh failure, which could cause
|
||||
deadlocks after repeated failures (lestrrat-go/jwx#1551)
|
||||
* Move ErrNotReady example functions out of client_example_test.go into
|
||||
separate files to avoid triggering autodoc workflow
|
||||
|
||||
v3.0.3 23 Dec 2025
|
||||
* Add ErrNotReady error state to avoid waiting for unstable URLs
|
||||
|
||||
v3.0.2 05 Dev 2025
|
||||
* Code changes mainly due to upgraded linter.
|
||||
* github.com/lestrrat-go/option upgraded to v2
|
||||
|
||||
81
vendor/github.com/lestrrat-go/httprc/v3/backend.go
generated
vendored
81
vendor/github.com/lestrrat-go/httprc/v3/backend.go
generated
vendored
@@ -118,14 +118,6 @@ func (c *ctrlBackend) handleRequest(ctx context.Context, req any) {
|
||||
}
|
||||
}
|
||||
|
||||
func sendWorker(ctx context.Context, ch chan Resource, r Resource) {
|
||||
r.SetBusy(true)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
case ch <- r:
|
||||
}
|
||||
}
|
||||
|
||||
func sendWorkerSynchronous(ctx context.Context, ch chan synchronousRequest, r synchronousRequest) {
|
||||
r.resource.SetBusy(true)
|
||||
select {
|
||||
@@ -159,26 +151,67 @@ func (c *ctrlBackend) loop(ctx context.Context, readywg, donewg *sync.WaitGroup)
|
||||
readywg.Done()
|
||||
defer c.traceSink.Put(ctx, "httprc controller: stopping main controller loop")
|
||||
defer donewg.Done()
|
||||
|
||||
var pending []Resource
|
||||
for {
|
||||
c.traceSink.Put(ctx, fmt.Sprintf("httprc controller: waiting for request or tick (tick interval=%s)", c.tickInterval))
|
||||
select {
|
||||
case req := <-c.incoming:
|
||||
c.traceSink.Put(ctx, fmt.Sprintf("httprc controller: got request %T", req))
|
||||
c.handleRequest(ctx, req)
|
||||
case t := <-c.check.C:
|
||||
c.periodicCheck(ctx, t)
|
||||
case <-ctx.Done():
|
||||
return
|
||||
if len(pending) > 0 {
|
||||
// Dispatch pending items while remaining responsive to incoming
|
||||
// requests. This prevents a deadlock where periodicCheck blocks
|
||||
// on c.outgoing while a worker blocks on c.incoming (issue #113).
|
||||
|
||||
// Skip resources that were removed (or replaced) after periodicCheck
|
||||
// queued them. Without this check, a stale resource could be sent to
|
||||
// a worker, causing an unnecessary fetch and a subsequent
|
||||
// adjustIntervalRequest for a resource that is no longer registered.
|
||||
r := pending[0]
|
||||
// Compare interface values directly. This is safe because all
|
||||
// Resource implementations are pointer types (*ResourceBase[T]),
|
||||
// so the comparison is a pointer identity check.
|
||||
if cur, ok := c.items[r.URL()]; !ok || cur != r {
|
||||
c.traceSink.Put(ctx, fmt.Sprintf("httprc controller: skipping pending resource %q (no longer registered or replaced)", r.URL()))
|
||||
r.SetBusy(false)
|
||||
pending = pending[1:]
|
||||
continue
|
||||
}
|
||||
|
||||
c.traceSink.Put(ctx, fmt.Sprintf("httprc controller: dispatching pending resource %q to worker pool (%d remaining)", pending[0].URL(), len(pending)))
|
||||
select {
|
||||
case req := <-c.incoming:
|
||||
c.traceSink.Put(ctx, fmt.Sprintf("httprc controller: got request %T (while dispatching)", req))
|
||||
c.handleRequest(ctx, req)
|
||||
case c.outgoing <- pending[0]:
|
||||
pending = pending[1:]
|
||||
case t := <-c.check.C:
|
||||
pending = append(pending, c.periodicCheck(ctx, t)...)
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
} else {
|
||||
c.traceSink.Put(ctx, fmt.Sprintf("httprc controller: waiting for request or tick (tick interval=%s)", c.tickInterval))
|
||||
select {
|
||||
case req := <-c.incoming:
|
||||
c.traceSink.Put(ctx, fmt.Sprintf("httprc controller: got request %T", req))
|
||||
c.handleRequest(ctx, req)
|
||||
case t := <-c.check.C:
|
||||
pending = c.periodicCheck(ctx, t)
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ctrlBackend) periodicCheck(ctx context.Context, t time.Time) {
|
||||
// periodicCheck examines all registered resources and returns those that are
|
||||
// due for refresh. Items are marked busy here so they won't be selected again
|
||||
// on the next tick. The caller (loop) is responsible for dispatching them to
|
||||
// the worker pool, interleaved with incoming request handling, to avoid the
|
||||
// deadlock described in https://github.com/lestrrat-go/httprc/issues/113.
|
||||
func (c *ctrlBackend) periodicCheck(ctx context.Context, t time.Time) []Resource {
|
||||
c.traceSink.Put(ctx, "httprc controller: START periodic check")
|
||||
defer c.traceSink.Put(ctx, "httprc controller: END periodic check")
|
||||
var minNext time.Time
|
||||
var dispatched int
|
||||
minInterval := -1 * time.Second
|
||||
var toDispatch []Resource
|
||||
for _, item := range c.items {
|
||||
c.traceSink.Put(ctx, fmt.Sprintf("httprc controller: checking resource %q", item.URL()))
|
||||
|
||||
@@ -196,14 +229,13 @@ func (c *ctrlBackend) periodicCheck(ctx context.Context, t time.Time) {
|
||||
c.traceSink.Put(ctx, fmt.Sprintf("httprc controller: resource %q is busy or not ready yet, skipping", item.URL()))
|
||||
continue
|
||||
}
|
||||
c.traceSink.Put(ctx, fmt.Sprintf("httprc controller: resource %q is ready, dispatching to worker pool", item.URL()))
|
||||
c.traceSink.Put(ctx, fmt.Sprintf("httprc controller: resource %q is ready, queuing for dispatch", item.URL()))
|
||||
|
||||
dispatched++
|
||||
c.traceSink.Put(ctx, fmt.Sprintf("httprc controller: dispatching resource %q to worker pool", item.URL()))
|
||||
sendWorker(ctx, c.outgoing, item)
|
||||
item.SetBusy(true)
|
||||
toDispatch = append(toDispatch, item)
|
||||
}
|
||||
|
||||
c.traceSink.Put(ctx, fmt.Sprintf("httprc controller: dispatched %d resources", dispatched))
|
||||
c.traceSink.Put(ctx, fmt.Sprintf("httprc controller: queued %d resources for dispatch", len(toDispatch)))
|
||||
|
||||
// Next check is always at the earliest next check + 1 second.
|
||||
// The extra second makes sure that we are _past_ the actual next check time
|
||||
@@ -223,6 +255,7 @@ func (c *ctrlBackend) periodicCheck(ctx context.Context, t time.Time) {
|
||||
}
|
||||
|
||||
c.traceSink.Put(ctx, fmt.Sprintf("httprc controller: next check in %s", c.tickInterval))
|
||||
return toDispatch
|
||||
}
|
||||
|
||||
func (c *ctrlBackend) SetTickInterval(d time.Duration) {
|
||||
|
||||
11
vendor/github.com/lestrrat-go/httprc/v3/controller.go
generated
vendored
11
vendor/github.com/lestrrat-go/httprc/v3/controller.go
generated
vendored
@@ -142,14 +142,23 @@ func (c *controller) Add(ctx context.Context, r Resource, options ...AddOption)
|
||||
resource: r,
|
||||
}
|
||||
c.traceSink.Put(ctx, fmt.Sprintf("httprc controller: sending add request for %q to backend", r.URL()))
|
||||
// Send to backend and wait for registration confirmation.
|
||||
// If this succeeds, the resource is in the backend.
|
||||
if _, err := sendBackend[addRequest, struct{}](ctx, c.incoming, req, reply); err != nil {
|
||||
return err
|
||||
}
|
||||
// IMPORTANT: At this point, the resource has been successfully registered
|
||||
// in the backend (stored in c.items map). The backend worker will fetch
|
||||
// this resource periodically.
|
||||
|
||||
if waitReady {
|
||||
c.traceSink.Put(ctx, fmt.Sprintf("httprc controller: waiting for resource %q to be ready", r.URL()))
|
||||
if err := r.Ready(ctx); err != nil {
|
||||
return err
|
||||
// CHANGE: Wrap Ready() errors with errNotReady to indicate that
|
||||
// registration succeeded but the first fetch hasn't completed.
|
||||
// Using %w twice creates a multi-error chain (Go 1.20+), allowing
|
||||
// errors.Is() to check both errNotReady and the underlying error.
|
||||
return fmt.Errorf("%w: %w", errNotReady, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
48
vendor/github.com/lestrrat-go/httprc/v3/errors.go
generated
vendored
48
vendor/github.com/lestrrat-go/httprc/v3/errors.go
generated
vendored
@@ -55,3 +55,51 @@ var errBlockedByWhitelist = errors.New(`blocked by whitelist`)
|
||||
func ErrBlockedByWhitelist() error {
|
||||
return errBlockedByWhitelist
|
||||
}
|
||||
|
||||
var errNotReady = errors.New(`resource registered but not ready`)
|
||||
|
||||
// ErrNotReady returns a sentinel error indicating that the resource was
|
||||
// successfully registered with the backend and is being actively managed,
|
||||
// but the first fetch and transformation has not completed successfully yet.
|
||||
//
|
||||
// This error is returned by Add() when:
|
||||
// - The resource was successfully added to the backend (registration succeeded)
|
||||
// - WithWaitReady(true) was specified (the default)
|
||||
// - The Ready() call failed (timeout, transform error, context cancelled, etc.)
|
||||
//
|
||||
// When Add() returns this error, the resource IS in the backend's resource map
|
||||
// and will continue to be fetched periodically in the background according to
|
||||
// the refresh interval. The application can safely proceed - the resource data
|
||||
// may become available later when a fetch succeeds.
|
||||
//
|
||||
// IMPORTANT: "Not ready" means the first fetch and transformation has not completed
|
||||
// successfully. The resource may eventually become ready (if the transformation
|
||||
// succeeds on a subsequent retry), or it may never become ready (if the data is
|
||||
// permanently invalid or the server is unreachable). The backend will continue
|
||||
// retrying according to the configured refresh interval.
|
||||
//
|
||||
// The underlying error (context deadline, transform failure, etc.) is wrapped
|
||||
// using Go 1.20+ multiple error wrapping and can be examined with errors.Is()
|
||||
// or errors.As(). You do not need to manually unwrap the error.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// err := ctrl.Add(ctx, resource)
|
||||
// if err != nil {
|
||||
// if errors.Is(err, httprc.ErrNotReady()) {
|
||||
// // Resource registered, will fetch in background
|
||||
// log.Print("Resource not ready yet, continuing startup")
|
||||
//
|
||||
// // Can also check the underlying cause
|
||||
// if errors.Is(err, context.DeadlineExceeded) {
|
||||
// log.Print("Timed out waiting for first fetch")
|
||||
// }
|
||||
// return nil
|
||||
// }
|
||||
// // Registration failed
|
||||
// return fmt.Errorf("failed to register resource: %w", err)
|
||||
// }
|
||||
// // Resource registered AND ready with data
|
||||
func ErrNotReady() error {
|
||||
return errNotReady
|
||||
}
|
||||
|
||||
1
vendor/github.com/lestrrat-go/httprc/v3/proxysink/proxysink.go
generated
vendored
1
vendor/github.com/lestrrat-go/httprc/v3/proxysink/proxysink.go
generated
vendored
@@ -72,6 +72,7 @@ func (p *Proxy[T]) flushloop(ctx context.Context) {
|
||||
p.mu.Unlock()
|
||||
return
|
||||
}
|
||||
p.mu.Unlock()
|
||||
default:
|
||||
}
|
||||
|
||||
|
||||
2
vendor/github.com/lestrrat-go/httprc/v3/worker.go
generated
vendored
2
vendor/github.com/lestrrat-go/httprc/v3/worker.go
generated
vendored
@@ -42,7 +42,7 @@ func (w worker) Run(ctx context.Context, readywg *sync.WaitGroup, donewg *sync.W
|
||||
w.traceSink.Put(ctx, fmt.Sprintf("httprc worker: FAILED to sync %q (synchronous): %s", sr.resource.URL(), err))
|
||||
sendReply(ctx, sr.reply, struct{}{}, err)
|
||||
sr.resource.SetBusy(false)
|
||||
return
|
||||
continue
|
||||
}
|
||||
w.traceSink.Put(ctx, fmt.Sprintf("httprc worker: SUCCESS syncing %q (synchronous)", sr.resource.URL()))
|
||||
sr.resource.SetBusy(false)
|
||||
|
||||
4
vendor/github.com/lestrrat-go/jwx/v3/.gitignore
generated
vendored
4
vendor/github.com/lestrrat-go/jwx/v3/.gitignore
generated
vendored
@@ -37,3 +37,7 @@ out
|
||||
cmd/jwx/jwx
|
||||
|
||||
bazel-*
|
||||
|
||||
# Go workspace files (local development only)
|
||||
go.work
|
||||
go.work.sum
|
||||
|
||||
12
vendor/github.com/lestrrat-go/jwx/v3/.golangci.yml
generated
vendored
12
vendor/github.com/lestrrat-go/jwx/v3/.golangci.yml
generated
vendored
@@ -106,6 +106,18 @@ linters:
|
||||
- revive
|
||||
path: jwt/internal/types/
|
||||
text: "var-naming: avoid meaningless package names"
|
||||
- linters:
|
||||
- revive
|
||||
path: internal/json/
|
||||
text: "var-naming: avoid package names"
|
||||
- linters:
|
||||
- revive
|
||||
path: jwe/internal/cipher/
|
||||
text: "var-naming: avoid package names that conflict with Go standard library package names"
|
||||
- linters:
|
||||
- revive
|
||||
path: jwk/ecdsa/
|
||||
text: "var-naming: avoid package names that conflict with Go standard library package names"
|
||||
- linters:
|
||||
- godoclint
|
||||
path: (^|/)internal/
|
||||
|
||||
266
vendor/github.com/lestrrat-go/jwx/v3/AGENTS.md
generated
vendored
Normal file
266
vendor/github.com/lestrrat-go/jwx/v3/AGENTS.md
generated
vendored
Normal file
@@ -0,0 +1,266 @@
|
||||
# AGENTS.md
|
||||
|
||||
## For Module Consumers
|
||||
|
||||
If you are writing code that *uses* jwx (not developing jwx itself):
|
||||
|
||||
- **Examples**: See `examples/` directory for runnable usage patterns
|
||||
- **Documentation**: See `docs/` directory and package READMEs
|
||||
- **API Reference**: Use `go doc` or https://pkg.go.dev/github.com/lestrrat-go/jwx/v3
|
||||
|
||||
The rest of this document focuses on developing the jwx library itself.
|
||||
|
||||
---
|
||||
|
||||
## Go Version
|
||||
|
||||
This project requires **Go 1.25.0** or later. Check `go.mod` for the exact version.
|
||||
|
||||
## Module Path vs Physical Layout
|
||||
|
||||
This repository uses a **flat layout** with vanity import paths. There is no physical `v3/` directory.
|
||||
|
||||
| Branch | Module Path | Physical Root |
|
||||
|--------|-------------|---------------|
|
||||
| `develop/v3` | `github.com/lestrrat-go/jwx/v3` | `/` (repo root) |
|
||||
|
||||
`import "github.com/lestrrat-go/jwx/v3/jwt"` → files are at `./jwt/`, not `./v3/jwt/`.
|
||||
|
||||
## Code Generation
|
||||
|
||||
### Immutable Rule
|
||||
|
||||
**NEVER edit files ending in `_gen.go` directly.** These are generated files. Edit the generator sources instead.
|
||||
|
||||
### Generated Files Pattern
|
||||
|
||||
Files matching `*_gen.go` are generated. Examples:
|
||||
- `jwt/options_gen.go`
|
||||
- `jwt/token_gen.go`
|
||||
- `jws/headers_gen.go`
|
||||
- `jwk/rsa_gen.go`
|
||||
- `jwa/signature_gen.go`
|
||||
|
||||
### Generator Locations
|
||||
|
||||
| Generator | Location | Input Files | Output |
|
||||
|-----------|----------|-------------|--------|
|
||||
| `genoptions` | `tools/cmd/genoptions/` | `{jwa,jwe,jwk,jws,jwt}/options.yaml` | `*/options_gen.go` |
|
||||
| `genjwt` | `tools/cmd/genjwt/` | `tools/cmd/genjwt/objects.yml` | `jwt/*_gen.go` |
|
||||
| `genjws` | `tools/cmd/genjws/` | `tools/cmd/genjws/objects.yml` | `jws/*_gen.go` |
|
||||
| `genjwe` | `tools/cmd/genjwe/` | `tools/cmd/genjwe/objects.yml` | `jwe/*_gen.go` |
|
||||
| `genjwk` | `tools/cmd/genjwk/` | `tools/cmd/genjwk/objects.yml` | `jwk/*_gen.go` |
|
||||
| `genjwa` | `tools/cmd/genjwa/` | `tools/cmd/genjwa/objects.yml` | `jwa/*_gen.go` |
|
||||
| `genreadfile` | `tools/cmd/genreadfile/` | - | ReadFile helpers |
|
||||
|
||||
### Regeneration Commands
|
||||
|
||||
```bash
|
||||
# Regenerate all code (includes options via `go generate .`)
|
||||
make generate
|
||||
|
||||
# Regenerate specific package (objects/types only, NOT options)
|
||||
make generate-jwt
|
||||
make generate-jws
|
||||
make generate-jwe
|
||||
make generate-jwk
|
||||
make generate-jwa
|
||||
|
||||
# Regenerate options only (options.yaml → options_gen.go for all packages)
|
||||
go generate .
|
||||
# or directly:
|
||||
./tools/cmd/genoptions.sh
|
||||
```
|
||||
|
||||
**Important:** `make generate-<pkg>` does **not** regenerate options. If you
|
||||
edit an `options.yaml` file, run `make generate` or `go generate .`.
|
||||
|
||||
## Functional Options Pattern
|
||||
|
||||
Options are defined in `{package}/options.yaml` and generated into `{package}/options_gen.go`.
|
||||
|
||||
Example `options.yaml` entry:
|
||||
|
||||
```yaml
|
||||
options:
|
||||
- ident: Token
|
||||
interface: ParseOption
|
||||
argument_type: Token
|
||||
comment: |
|
||||
WithToken specifies the token instance...
|
||||
```
|
||||
|
||||
Generates `WithToken(v Token) ParseOption` function.
|
||||
|
||||
## Multi-Module Structure
|
||||
|
||||
This repository contains multiple Go modules. The nested modules use `replace` directives for local development.
|
||||
|
||||
| Module | Path | Purpose |
|
||||
|--------|------|---------|
|
||||
| Main | `./go.mod` | Core library |
|
||||
| Examples | `./examples/go.mod` | Usage examples |
|
||||
| CLI | `./cmd/jwx/go.mod` | Command-line tool |
|
||||
| Perf Bench | `./bench/performance/go.mod` | Performance benchmarks |
|
||||
| Comparison | `./bench/comparison/go.mod` | Library comparison |
|
||||
| Generators | `./tools/cmd/*/go.mod` | Code generators |
|
||||
|
||||
### Local Development
|
||||
|
||||
The `examples/go.mod` contains:
|
||||
```go
|
||||
replace github.com/lestrrat-go/jwx/v3 v3.0.0 => ../
|
||||
```
|
||||
|
||||
No `go.work` file is committed. When working across modules, either:
|
||||
1. Create a temporary `go.work` file (it is .gitignored)
|
||||
2. Rely on the `replace` directives already in place
|
||||
|
||||
## Development Commands
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
make test
|
||||
|
||||
# Run tests with specific build tags
|
||||
make test-goccy # Use goccy/go-json
|
||||
make test-es256k # Enable ES256K support
|
||||
make test-alltags # All optional features
|
||||
|
||||
# Run short/smoke tests
|
||||
make smoke
|
||||
|
||||
# Generate coverage report
|
||||
make cover
|
||||
make viewcover
|
||||
|
||||
# Lint
|
||||
make lint
|
||||
|
||||
# Format and tidy
|
||||
make imports
|
||||
make tidy
|
||||
```
|
||||
|
||||
### Test Script Details
|
||||
|
||||
Tests are run via `./tools/test.sh` which iterates over:
|
||||
- `.` (main module)
|
||||
- `./examples`
|
||||
- `./bench/performance`
|
||||
- `./cmd/jwx`
|
||||
|
||||
## Package Directory Map
|
||||
|
||||
| Package | Responsibility |
|
||||
|---------|----------------|
|
||||
| `jwa/` | Algorithm identifiers (e.g., `RS256`, `ES384`, `A128GCM`) |
|
||||
| `jwk/` | JSON Web Keys - key representation and management |
|
||||
| `jws/` | JSON Web Signatures - `Sign()` and `Verify()` |
|
||||
| `jwe/` | JSON Web Encryption - `Encrypt()` and `Decrypt()` |
|
||||
| `jwt/` | JSON Web Tokens - claims and validation |
|
||||
| `jwt/openid/` | OpenID Connect ID tokens |
|
||||
| `transform/` | Token transformation utilities |
|
||||
|
||||
## Relevant RFCs
|
||||
|
||||
- RFC 7515 - JWS (JSON Web Signature)
|
||||
- RFC 7516 - JWE (JSON Web Encryption)
|
||||
- RFC 7517 - JWK (JSON Web Key)
|
||||
- RFC 7518 - JWA (JSON Web Algorithms)
|
||||
- RFC 7519 - JWT (JSON Web Token)
|
||||
- OpenID Connect Core 1.0
|
||||
|
||||
## Error Handling
|
||||
|
||||
Sentinel errors are exposed via functions. Use `errors.Is()`:
|
||||
|
||||
```go
|
||||
if errors.Is(err, jwt.TokenExpiredError()) { ... }
|
||||
```
|
||||
|
||||
| Package | Function | Meaning |
|
||||
|---------|----------|---------|
|
||||
| `jwt` | `TokenExpiredError()` | `exp` claim not satisfied |
|
||||
| `jwt` | `TokenNotYetValidError()` | `nbf` claim not satisfied |
|
||||
| `jwt` | `InvalidIssuerError()` | `iss` claim not satisfied |
|
||||
| `jwt` | `InvalidAudienceError()` | `aud` claim not satisfied |
|
||||
| `jwt` | `ValidateError()` | Generic validation failure |
|
||||
| `jwt` | `ParseError()` | Parse failed |
|
||||
| `jws` | `VerificationError()` | Signature verification failed |
|
||||
| `jwe` | `DecryptError()` | Decryption failed |
|
||||
|
||||
## Testing
|
||||
|
||||
Use `github.com/stretchr/testify/require` for assertions (not `assert`).
|
||||
|
||||
## Build Tags
|
||||
|
||||
| Tag | Effect |
|
||||
|-----|--------|
|
||||
| `jwx_goccy` | Use `goccy/go-json` instead of `encoding/json` |
|
||||
| `jwx_es256k` | Enable secp256k1/ES256K algorithm support |
|
||||
| `jwx_secp256k1_pem` | Enable PEM encoding for secp256k1 keys |
|
||||
| `jwx_asmbase64` | Use assembly-optimized base64 |
|
||||
|
||||
## Quick Reference: Common Modifications
|
||||
|
||||
| Task | Edit This | Then Run |
|
||||
|------|-----------|----------|
|
||||
| Add/edit any option | `{pkg}/options.yaml` | `make generate` or `go generate .` |
|
||||
| Add new JWS header field | `tools/cmd/genjws/objects.yml` | `make generate-jws` |
|
||||
| Add new JWK key field | `tools/cmd/genjwk/objects.yml` | `make generate-jwk` |
|
||||
| Add new algorithm | `tools/cmd/genjwa/objects.yml` | `make generate-jwa` |
|
||||
| Modify token fields | `tools/cmd/genjwt/objects.yml` | `make generate-jwt` |
|
||||
|
||||
## File Naming Conventions
|
||||
|
||||
| Pattern | Meaning |
|
||||
|---------|---------|
|
||||
| `*_gen.go` | Generated code - DO NOT EDIT |
|
||||
| `*_test.go` | Test files |
|
||||
| `*_gen_test.go` | Generated tests - DO NOT EDIT |
|
||||
| `options.yaml` | Option definitions (input to genoptions) |
|
||||
| `objects.yml` | Object definitions (input to package-specific generators) |
|
||||
|
||||
## Examples Directory
|
||||
|
||||
Naming convention: `{package}_xxx_example_test.go`
|
||||
- `jwt_parse_example_test.go`
|
||||
- `jws_sign_example_test.go`
|
||||
- `jwx_example_test.go` (cross-package)
|
||||
- `jwx_readme_example_test.go` (cross-package, used in README)
|
||||
- `jwx_register_ec_and_key_example_test.go` (cross-package, key registration)
|
||||
|
||||
Examples are included in `docs/` via autodoc markers:
|
||||
```markdown
|
||||
<!-- INCLUDE(examples/jwt_parse_example_test.go) -->
|
||||
<!-- END INCLUDE -->
|
||||
```
|
||||
|
||||
## Pre-Read Rules
|
||||
|
||||
Read linked doc BEFORE working in that area. No exceptions.
|
||||
|
||||
| Trigger | Doc |
|
||||
|---------|-----|
|
||||
| Looking up package APIs, types, functions | `.claude/docs/packages.md` |
|
||||
| Running or writing tests, fuzz tests | `.claude/docs/testing.md` |
|
||||
| Understanding package relationships, imports | `.claude/docs/dependencies.md` |
|
||||
| Working with errors, error handling patterns | `.claude/docs/error-formatting.md` |
|
||||
| Code generation, options pattern, extension points, JSON/base64 backends | `.claude/docs/internals.md` |
|
||||
|
||||
## Cache Maintenance
|
||||
|
||||
These docs cache repository state. Still read source before modifying code.
|
||||
|
||||
1. When your changes affect a doc below, update it in the same commit.
|
||||
2. If you notice any doc is wrong or stale — even on an unrelated task — fix it immediately.
|
||||
|
||||
| Doc | Update trigger |
|
||||
|-----|----------------|
|
||||
| `.claude/docs/packages.md` | New/renamed/removed exported functions, types, or packages |
|
||||
| `.claude/docs/testing.md` | Changes to test infrastructure, build tags, test helpers, fuzz targets |
|
||||
| `.claude/docs/dependencies.md` | New internal imports between packages, new external dependencies |
|
||||
| `.claude/docs/error-formatting.md` | New sentinel errors, changes to error wrapping patterns |
|
||||
| `.claude/docs/internals.md` | Changes to generators, options YAML schema, registration points, multi-module layout |
|
||||
331
vendor/github.com/lestrrat-go/jwx/v3/Changes
generated
vendored
331
vendor/github.com/lestrrat-go/jwx/v3/Changes
generated
vendored
@@ -4,6 +4,337 @@ Changes
|
||||
v3 has many incompatibilities with v2. To see the full list of differences between
|
||||
v2 and v3, please read the Changes-v3.md file (https://github.com/lestrrat-go/jwx/blob/develop/v3/Changes-v3.md)
|
||||
|
||||
v3.1.1 7 May 2026
|
||||
* [jws] Coordinated RFC 7797 `b64=false` handling pass: `jws.Verify`
|
||||
rejects payloads with `b64=false` unless `b64` is also listed in
|
||||
`crit`; `jws.Sign` auto-declares `b64` in `crit` when emitting
|
||||
`b64=false`; `Message.MarshalJSON` honors `b64=false` instead of
|
||||
silently re-encoding; `jws.VerifyCompactFast` refuses any compact
|
||||
JWS carrying `b64` (the fast path doesn't process extension
|
||||
headers); and `b64` is now declared as a typed boolean header
|
||||
field rather than handled ad-hoc.
|
||||
(#2081, #2087, #2102, #2104, #2106)
|
||||
|
||||
* [jws] Reject malformed general-form JSON-serialized JWS: inputs
|
||||
with a top-level `header` member as a sibling of `signatures` are
|
||||
rejected (the spec only permits `header` inside per-signature
|
||||
objects), as are inputs whose `protected` member is a literal
|
||||
JSON object instead of a base64url-encoded string.
|
||||
(#2089, #2108)
|
||||
|
||||
* [jws] `jws.AlgorithmsForKey` failures from unclassifiable keys
|
||||
are now wrapped in a typed sentinel so callers can branch on
|
||||
"couldn't categorize this key" without string matching the error
|
||||
message. (#2110)
|
||||
|
||||
* [jws] Verify error-shape consistency: `VerifyCompactFast`
|
||||
refusals now match the `jws.VerifyError()` taxonomy used by the
|
||||
slow path, fan-out verify errors name the loose `WithKeySet`
|
||||
options that were tried, multi-signature `b64` mismatches name
|
||||
the offending signature index and conflicting value, and the
|
||||
compact `b64=false`+payload-contains-`.` error references RFC
|
||||
7797 §5.2 and points at `WithDetachedPayload`.
|
||||
(#2083, #2085, #2114)
|
||||
|
||||
* [jws] Keys fetched via the `jku` header are no longer accepted
|
||||
for signature verification when the JWK declares `use=enc`.
|
||||
(#2060)
|
||||
|
||||
* [jws][jwe] `jws.VerifyMessage` and `jwe.DecryptMessage` observe
|
||||
context cancellation between loop iterations rather than only at
|
||||
boundaries. Long fan-out verify/decrypt loops now respond to a
|
||||
cancelled context promptly. (#2112, #2117)
|
||||
|
||||
* [jwe] Reject PBES2 messages whose `p2c` (iteration count) does
|
||||
not parse cleanly into int64 or violates the configured bound.
|
||||
The error now names the violated bound (min vs max) instead of
|
||||
the generic "out of range". (#2119)
|
||||
|
||||
* [jwe] `jwe.WithKey()` validates the alg-vs-key shape at option
|
||||
construction time rather than during encryption, so misuse
|
||||
surfaces at the call site instead of inside the encrypt loop.
|
||||
(#2121)
|
||||
|
||||
* [jwe] Decrypt error-path cleanup: per-key failures from key-set
|
||||
providers are surfaced via `errors.Join` so each underlying
|
||||
error remains inspectable; the joined error count is bounded to
|
||||
keep diagnostics readable; the redundant outer `Decrypt:` prefix
|
||||
is dropped; and the compression-cap error names the
|
||||
"decompressed" payload, the option, and the size.
|
||||
(#2123, #2125, #2127)
|
||||
|
||||
* [jwe] Add `jwe.WithDisabledKeyAlgorithms(...)` as a global
|
||||
policy hook (`jwe.Configure(...)` or per-call) for refusing
|
||||
specific key-management algorithms across all `jwe.Decrypt`
|
||||
calls. (#2129)
|
||||
|
||||
* [jwe] Reject messages whose protected-header `alg` conflicts
|
||||
with a per-recipient `alg`. The previous behavior silently
|
||||
preferred the protected-header value. (#2052)
|
||||
|
||||
* [jwk] Stop duplicating JWK fields at the JWKS top level on
|
||||
parse. A JWKS whose top-level object carries fields with the
|
||||
same names as JWK members no longer copies those values into
|
||||
every key in `keys`. (#2133)
|
||||
|
||||
* [jwk] A custom `jwk.KeyParser` returning `(nil, nil)` now
|
||||
signals "continue to the next parser" rather than "successfully
|
||||
produced a nil key". Callers no longer end up with a nil
|
||||
`jwk.Key` from a successful parse when an extension returns the
|
||||
empty pair. (#2140)
|
||||
|
||||
* [jwk] Stream the JWKS `keys` array with a cap-before-allocate
|
||||
strategy. Inputs respect `WithMaxKeys` before any unbounded
|
||||
slice growth, and bounded-size JWKS no longer over-allocate
|
||||
based on attacker-controlled length hints. (#2137)
|
||||
|
||||
* [jwk] Wrap `jwk.ParseKey` errors with the `jwk.ParseError`
|
||||
sentinel so callers can branch on parse-vs-other failures with
|
||||
`errors.Is(err, jwk.ParseError())`. (#2135)
|
||||
|
||||
* [jwk] ECDSA public keys whose X / Y coordinates exceed the
|
||||
curve's byte length are rejected with a typed error instead of
|
||||
reaching curve arithmetic with an out-of-range `big.Int`.
|
||||
(#2050)
|
||||
|
||||
* [jwt] `jwt.ParseRequest` no longer skips the request body when
|
||||
the request uses chunked transfer encoding. The
|
||||
Content-Length-based fast path previously bypassed `ParseForm`
|
||||
for chunked requests even when `WithFormKey` was supplied.
|
||||
(#2091)
|
||||
|
||||
* [jwt] Pedantic mode (`jwt.WithPedanticParse(true)`) enforces
|
||||
that nested-envelope JWTs declare `cty=JWT`. Tokens missing
|
||||
`cty` or carrying a different value are rejected. (#2094)
|
||||
|
||||
* [jwt] `jwt.ParseInsecure` parses the loop-local payload split
|
||||
from the input rather than the original input bytes. Fixes a
|
||||
case where `ParseInsecure` could return a token assembled from
|
||||
a different signature segment than the one being inspected.
|
||||
(#2097)
|
||||
|
||||
* [jwt] `jwt.WithMaxDeltaIs(...)` and `jwt.WithMinDeltaIs(...)`
|
||||
reject tokens whose compared claim is missing rather than
|
||||
treating it as zero. Validation no longer silently succeeds
|
||||
when the claim isn't present on the token. (#2099)
|
||||
|
||||
* [jwt] `jwt.Parse` / `jwt.ParseRequest` only call `ParseForm`
|
||||
when `WithFormKey` is supplied. Previously the form body could
|
||||
be consumed even when the caller did not opt in to form-source
|
||||
extraction. (#2058)
|
||||
|
||||
* [jwt] Fix `AddressClaim.MarshalJSON` to handle non-printable
|
||||
bytes correctly. (#2056)
|
||||
|
||||
* [jwa] Unify the signature, key-encryption, and
|
||||
content-encryption algorithm tables behind a single
|
||||
registration entry point. Extension algorithms register once
|
||||
rather than via three parallel APIs. (#2066)
|
||||
|
||||
* [cmd/jwx] Warn before writing a private key to a TTY; reject
|
||||
`keysize <= 0` for `oct` key generation. (#2071)
|
||||
|
||||
v3.1.0 19 Apr 2026
|
||||
* [jwk] Add `jwk.WithRejectDuplicateKID(bool)` — when enabled, `jwk.Parse` /
|
||||
`jwk.ParseReader` / `jwk.ParseString` return an error if the input JWKS
|
||||
contains more than one key sharing the same non-empty `kid`. Usable as a
|
||||
`jwk.Configure()` global or a per-call override. Default behavior
|
||||
(first-match-wins) is unchanged.
|
||||
|
||||
* [jwk] Add `jwk.WithMaxKeys(int)` — caps the number of keys accepted
|
||||
by `jwk.Parse` / `jwk.ParseReader` / `jwk.ParseString` in both the
|
||||
JSON `"keys"` array and the PEM/X.509 block stream (default 1000).
|
||||
Usable as a `jwk.Configure()` global or a per-call override.
|
||||
Replaces the hardcoded internal PEM cap of the same value, so the
|
||||
default behavior is unchanged. Backport of the v4 amplification
|
||||
cap that mirrors `jws.WithMaxSignatures` and
|
||||
`jwe.WithMaxRecipients`.
|
||||
|
||||
* [jws] Add `jws.WithDetachedPayloadReader(io.Reader)` — a streaming
|
||||
variant of `jws.WithDetachedPayload([]byte)` that consumes the
|
||||
payload from an `io.Reader` instead of a byte slice, so the payload
|
||||
is never materialized in memory. It is a `jws.Sign()` / `jws.Verify()`
|
||||
option; the two remain the default entry points. Only HMAC/RSA/ECDSA
|
||||
algorithms are supported; EdDSA, custom-family algorithms, and
|
||||
algorithms registered via `jws.RegisterSigner()` / `jws.RegisterVerifier()`
|
||||
are rejected with a clear error pointing at `jws.WithDetachedPayload()`.
|
||||
On sign, multiple `jws.WithKey()` options combined with `jws.WithJSON()`
|
||||
produce a general-form multi-signature JWS (the payload is streamed
|
||||
once and fanned out to each signer). On verify, only single-signature
|
||||
JWS input is supported; `jws.WithKeySet()`, `jws.WithKeyProvider()`,
|
||||
and `jws.WithVerifyAuto()` are not accepted. (#1663)
|
||||
|
||||
* [jws] Add `jws.Base64StreamEncoder` — the stream-capable extension
|
||||
of `jws.Base64Encoder`. The default encoder and `*base64.Encoding`
|
||||
values supplied via `jws.WithBase64Encoder()` are auto-wrapped, so
|
||||
typical callers see no change. Custom encoders only need to
|
||||
implement this additional interface if they want to be usable with
|
||||
`jws.WithDetachedPayloadReader()`. (#1663)
|
||||
|
||||
* [jws] Fix `jws.Sign` with `WithDetachedPayload` + `WithJSON` to
|
||||
omit the `"payload"` member from the output per RFC 7515
|
||||
Appendix F. Previously the payload was still emitted, producing
|
||||
non-detached JSON. (#1663)
|
||||
|
||||
* [jwk] BREAKING: `jwk.PublicSetOf` now returns an error when the input
|
||||
set contains a symmetric (oct) key. Previously, symmetric keys were
|
||||
silently passed through — which meant callers following the documented
|
||||
"publish my public JWKS" pattern could leak HMAC secret material.
|
||||
Callers who genuinely want the legacy pass-through behavior can opt in
|
||||
with `jwk.WithAllowSymmetric(true)`. The signature is now variadic
|
||||
(`PublicSetOf(v Set, options ...PublicSetOption)`), so existing call
|
||||
sites compile unchanged. The minor version is bumped from v3.0.x →
|
||||
v3.1.0 to reflect this deliberate behavior change.
|
||||
|
||||
`jwk.PublicKeyOf` on a single symmetric key is unchanged — it still
|
||||
returns the key as-is, matching its documented behavior.
|
||||
|
||||
* [jws][jwe][jwk] Replace intermediate map[string]any allocation in
|
||||
MarshalJSON with a pair-slice + sync.Pool pattern, matching the approach
|
||||
already used in jwt. Eliminates per-call map and key-slice allocations
|
||||
in the serialization hot path.
|
||||
|
||||
* [jwt][jwe][jws][jwk] Fix inconsistent mutex locking across main data
|
||||
structures. Named getters on JWK key types, MarshalJSON on JWK keys,
|
||||
UnmarshalJSON on JWE headers, makePairs/MarshalJSON on JWT tokens,
|
||||
rawBuffer on JWS headers, and Set/Keys on jwk.Set were missing proper
|
||||
lock protection. Switch all mutex fields from *sync.RWMutex (pointer)
|
||||
to sync.RWMutex (value) so go vet -copylocks catches accidental copies,
|
||||
and convert affected value-receiver methods to pointer receivers.
|
||||
|
||||
* [jwe] Add `WithMaxRecipients(int)` to reject JWE messages with more recipients
|
||||
than the configured limit. Default is 100. Can be set globally via
|
||||
`jwe.Settings()` or per-call in `jwe.Decrypt()` / `jwe.Parse()`. (#1633)
|
||||
|
||||
* [jws] Add `WithMaxSignatures(int)` to reject JWS JSON-serialized messages with
|
||||
more signatures than the configured limit. Default is 100. Can be set globally
|
||||
via `jws.Settings()` or per-call in `jws.Parse()`. (#1636)
|
||||
|
||||
* [jwk] The default HTTP client used by `jwk.Fetch()` and `jwk.Cache` now
|
||||
enforces a 30-second timeout, blocks HTTPS-to-HTTP redirect downgrades at
|
||||
every hop, and limits redirect chains to 5 hops. This mitigates SSRF via
|
||||
redirect chains and slowloris-style DoS from unresponsive JWKS endpoints.
|
||||
Callers who provide their own `http.Client` via `jwk.WithHTTPClient()` are
|
||||
not affected. (#1634, #1637, #1639, #1640)
|
||||
|
||||
* [jwk] Add `jwk.DefaultHTTPClient()` which returns a new `*http.Client`
|
||||
configured with the library's default protections. Useful for restoring
|
||||
defaults after calling `jwk.Configure(jwk.WithHTTPClient(...))`. (#1638)
|
||||
|
||||
* [jwk] `WithMaxFetchBodySize(int64)` can now be set globally via
|
||||
`jwk.Configure()` in addition to per-call. (#1631)
|
||||
|
||||
* [jwk] Add `jwk.WrapHTTPClientDefaults()` to apply the library's default
|
||||
safety behaviors (timeout, redirect policy) to a caller-provided
|
||||
`*http.Client`. Existing client settings (Transport, Jar, etc.) are
|
||||
preserved; CheckRedirect is wrapped rather than overwritten.
|
||||
|
||||
* [jwt] Add `jwt.WithStrictStringClaims(true)` option for `jwt.Parse()` and
|
||||
`jwt.ReadFile()` to reject JSON `null` for string registered claims
|
||||
(`iss`, `sub`, `jti`). By default, null is silently accepted as an empty
|
||||
string. (#1484)
|
||||
|
||||
* [jwa] Add fully-specified EdDSA signature algorithms `Ed25519` and `Ed448` per
|
||||
RFC 9864. The polymorphic `EdDSA` algorithm is now marked as deprecated.
|
||||
New Go accessors: `jwa.EdDSAEd25519()` and `jwa.EdDSAEd448()` (function names
|
||||
are tentative and may change in future releases).
|
||||
Ed448 signing/verification requires `github.com/cloudflare/circl` because
|
||||
Go's standard library does not support Ed448. To avoid pulling in this
|
||||
extra dependency for all users, Ed448 support is provided as a separate
|
||||
module (`github.com/lestrrat-go/jwx-circl-ed448`). Import it for side
|
||||
effects to enable Ed448:
|
||||
import _ "github.com/lestrrat-go/jwx-circl-ed448"
|
||||
Without this import, Ed448 is registered as an algorithm identifier but
|
||||
will return an error at sign/verify time.
|
||||
|
||||
* [jwk] Add `jwk/jwkunsafe` package with `NewKey(kty)` and `NewPublicKey(kty)`
|
||||
functions for creating empty, unpopulated JWK key objects. This is intended
|
||||
for extension module authors who need to register custom KeyImporter
|
||||
implementations for new key types.
|
||||
|
||||
* [jws] Add `jws.RegisterAlgorithmForKeyType()` for external modules to register
|
||||
additional algorithm-to-key-type mappings.
|
||||
|
||||
* [jws/jwsbb] Add `jwsbb.RegisterAlgorithm()` for external modules to
|
||||
register custom algorithm implementations (e.g. Ed448).
|
||||
|
||||
* [jws] `jws.Verify()` and `jws.VerifyCompactFast()` now validate the "crit"
|
||||
(Critical) header parameter per RFC 7515 Section 4.1.11. Signatures with an
|
||||
empty "crit" array, standard JOSE header names in "crit", or "crit"-listed
|
||||
extensions not present in the protected header are now rejected. To disable
|
||||
this validation on a per-call basis, pass
|
||||
`jws.WithCritValidation(false)` to `jws.Verify()`.
|
||||
|
||||
* [jwe] `jwe.Decrypt()` now validates the "crit" (Critical) header parameter
|
||||
per RFC 7516 Section 4.1.13, matching the jws behavior above. Messages with
|
||||
an empty "crit" array, standard JOSE header names in "crit", or "crit"-listed
|
||||
extensions not present in the protected header are now rejected. Declare
|
||||
extensions with `jwe.WithCritExtension()`, or disable validation on a
|
||||
per-call basis with `jwe.WithCritValidation(false)`. (#1735)
|
||||
|
||||
* [jwk] BREAKING (extension modules): `jwk.RegisterKeyExporter` now takes a
|
||||
`jwk.KeyKind` instead of `jwa.KeyType`. Call sites migrate with
|
||||
`jwk.KeyKind(kty.String())`; for curve-specific exporters, use a compound
|
||||
identity like `jwk.KeyKind("OKP:Ed448")`. Only affects extension-module
|
||||
authors registering custom exporters; library users calling `jwk.Export`
|
||||
are unaffected.
|
||||
|
||||
* [jwk] Fixed inverted rlocker condition in RSA key export. (#1576)
|
||||
|
||||
* [jwe] Fixed X25519 ECDH-ES key agreement to include `apu` and `apv` parameters
|
||||
in the Concat KDF derivation, matching the ECDSA path. Previously these values
|
||||
were silently discarded during encryption, weakening the key derivation per
|
||||
RFC 7518 Section 4.6.2.
|
||||
|
||||
* [jwe] POTENTIALLY BREAKING: `jwe.Decrypt()` now rejects PBES2 messages with
|
||||
a `p2c` (iteration count) below 1,000 by default. This prevents accepting
|
||||
tokens with trivially low iteration counts that eliminate PBKDF2 brute-force
|
||||
protection. To restore the previous behavior or adjust the threshold, call
|
||||
`jwe.Settings(jwe.WithMinPBES2Count(0))`.
|
||||
|
||||
* [jwk] Fixed a deadlock in `jwk.Cache` that occurred when repeated `Refresh()` calls
|
||||
failed (e.g. HTTP 500 responses). Each failure killed an httprc worker goroutine,
|
||||
and after all workers were exhausted, subsequent `Refresh()` calls would block forever. (#1551)
|
||||
|
||||
* [jws] Add `jws.RegisterAlgorithmForCurve()` for external modules to register
|
||||
algorithm-to-curve mappings. `jws.AlgorithmsForKey()` now filters results
|
||||
by the key's curve when applicable. (#1620)
|
||||
|
||||
* [jwk] Add `jwk.WithMaxFetchBodySize()` option to limit the response body size
|
||||
when fetching remote JWK Sets via `jwk.Fetch()` and `jwk.Cache`. (#1622)
|
||||
|
||||
* [jwe] Add `jwe.WithMaxPBES2Count()` and `jwe.WithMinPBES2Count()` as per-call
|
||||
options to `jwe.Decrypt()`, allowing callers to override the global PBES2
|
||||
iteration count limits on a per-decryption basis. (#1623)
|
||||
|
||||
* [jwk] `jwk.X509CertChain()` now correctly returns `false` as the second return
|
||||
value when the certificate chain is nil. (#1624)
|
||||
|
||||
* [jwk] Fixed a data race in the x509 decoder registry iteration. (#1625)
|
||||
|
||||
* [jwk] `jwk.Parse()` now limits PEM input to 1,000 blocks maximum to prevent
|
||||
resource exhaustion from inputs containing thousands of small PEM blocks. (#1626)
|
||||
|
||||
* [jwk] RSA JWK validation is now enforced consistently across JSON parse,
|
||||
JWKS parse, PEM/X.509 parse, and `jwk.Import()`. Keys with moduli smaller
|
||||
than 2048 bits or unsafe public exponents are now rejected by default.
|
||||
Compatibility knobs were added to `jwk.Configure()` via
|
||||
`jwk.WithMinRSAModulusBits(...)` and `jwk.WithMinRSAPublicExponent(...)`.
|
||||
|
||||
* [jwt] `jwt.Validate()` now rejects negative durations passed to
|
||||
`jwt.WithAcceptableSkew()`. (#1627)
|
||||
|
||||
* [jwt/openid] `openid.Birthdate` now accepts year `0000` as a valid value per
|
||||
the OpenID Connect Core specification. (#1628)
|
||||
|
||||
* [jwk][jws][jwe] Generated `Set()` methods now deep-copy slice and byte-slice
|
||||
values so callers cannot mutate internal header or key state after setting
|
||||
a field. (#1659)
|
||||
|
||||
* [jwk] When `UnmarshalJSON` fails on a private key (RSA, EC, OKP, Symmetric),
|
||||
all sensitive fields are now zeroed before the error is returned. This
|
||||
prevents leaking partial key material through half-constructed objects. (#1660)
|
||||
|
||||
v3.0.13 12 Jan 2026
|
||||
* [jwt] The `jwt.WithContext()` option is now properly being passed to `jws.Verify()` from
|
||||
`jwt.Parse()`.
|
||||
|
||||
2
vendor/github.com/lestrrat-go/jwx/v3/MODULE.bazel
generated
vendored
2
vendor/github.com/lestrrat-go/jwx/v3/MODULE.bazel
generated
vendored
@@ -5,7 +5,7 @@ module(
|
||||
)
|
||||
|
||||
bazel_dep(name = "bazel_skylib", version = "1.7.1")
|
||||
bazel_dep(name = "rules_go", version = "0.55.1")
|
||||
bazel_dep(name = "rules_go", version = "0.57.0")
|
||||
bazel_dep(name = "gazelle", version = "0.44.0")
|
||||
bazel_dep(name = "aspect_bazel_lib", version = "2.11.0")
|
||||
|
||||
|
||||
7
vendor/github.com/lestrrat-go/jwx/v3/MODULE.bazel.lock
generated
vendored
7
vendor/github.com/lestrrat-go/jwx/v3/MODULE.bazel.lock
generated
vendored
@@ -56,12 +56,13 @@
|
||||
"https://bcr.bazel.build/modules/package_metadata/0.0.2/source.json": "e53a759a72488d2c0576f57491ef2da0cf4aab05ac0997314012495935531b73",
|
||||
"https://bcr.bazel.build/modules/platforms/0.0.10/MODULE.bazel": "8cb8efaf200bdeb2150d93e162c40f388529a25852b332cec879373771e48ed5",
|
||||
"https://bcr.bazel.build/modules/platforms/0.0.11/MODULE.bazel": "0daefc49732e227caa8bfa834d65dc52e8cc18a2faf80df25e8caea151a9413f",
|
||||
"https://bcr.bazel.build/modules/platforms/0.0.11/source.json": "f7e188b79ebedebfe75e9e1d098b8845226c7992b307e28e1496f23112e8fc29",
|
||||
"https://bcr.bazel.build/modules/platforms/0.0.4/MODULE.bazel": "9b328e31ee156f53f3c416a64f8491f7eb731742655a47c9eec4703a71644aee",
|
||||
"https://bcr.bazel.build/modules/platforms/0.0.5/MODULE.bazel": "5733b54ea419d5eaf7997054bb55f6a1d0b5ff8aedf0176fef9eea44f3acda37",
|
||||
"https://bcr.bazel.build/modules/platforms/0.0.6/MODULE.bazel": "ad6eeef431dc52aefd2d77ed20a4b353f8ebf0f4ecdd26a807d2da5aa8cd0615",
|
||||
"https://bcr.bazel.build/modules/platforms/0.0.7/MODULE.bazel": "72fd4a0ede9ee5c021f6a8dd92b503e089f46c227ba2813ff183b71616034814",
|
||||
"https://bcr.bazel.build/modules/platforms/0.0.8/MODULE.bazel": "9f142c03e348f6d263719f5074b21ef3adf0b139ee4c5133e2aa35664da9eb2d",
|
||||
"https://bcr.bazel.build/modules/platforms/1.0.0/MODULE.bazel": "f05feb42b48f1b3c225e4ccf351f367be0371411a803198ec34a389fb22aa580",
|
||||
"https://bcr.bazel.build/modules/platforms/1.0.0/source.json": "f4ff1fd412e0246fd38c82328eb209130ead81d62dcd5a9e40910f867f733d96",
|
||||
"https://bcr.bazel.build/modules/protobuf/21.7/MODULE.bazel": "a5a29bb89544f9b97edce05642fac225a808b5b7be74038ea3640fae2f8e66a7",
|
||||
"https://bcr.bazel.build/modules/protobuf/27.0/MODULE.bazel": "7873b60be88844a0a1d8f80b9d5d20cfbd8495a689b8763e76c6372998d3f64c",
|
||||
"https://bcr.bazel.build/modules/protobuf/27.1/MODULE.bazel": "703a7b614728bb06647f965264967a8ef1c39e09e8f167b3ca0bb1fd80449c0d",
|
||||
@@ -97,8 +98,8 @@
|
||||
"https://bcr.bazel.build/modules/rules_go/0.42.0/MODULE.bazel": "8cfa875b9aa8c6fce2b2e5925e73c1388173ea3c32a0db4d2b4804b453c14270",
|
||||
"https://bcr.bazel.build/modules/rules_go/0.46.0/MODULE.bazel": "3477df8bdcc49e698b9d25f734c4f3a9f5931ff34ee48a2c662be168f5f2d3fd",
|
||||
"https://bcr.bazel.build/modules/rules_go/0.51.0/MODULE.bazel": "b6920f505935bfd69381651c942496d99b16e2a12f3dd5263b90ded16f3b4d0f",
|
||||
"https://bcr.bazel.build/modules/rules_go/0.55.1/MODULE.bazel": "a57a6fc59a74326c0b440d07cca209edf13c7d1a641e48cfbeab56e79f873609",
|
||||
"https://bcr.bazel.build/modules/rules_go/0.55.1/source.json": "827a740c8959c9d20616889e7746cde4dcc6ee80d25146943627ccea0736328f",
|
||||
"https://bcr.bazel.build/modules/rules_go/0.57.0/MODULE.bazel": "bee44028b527cd6d1b7699a2c78714bba237b40ee21f90a83b472c94bc53159d",
|
||||
"https://bcr.bazel.build/modules/rules_go/0.57.0/source.json": "a782b756d87c68a223a48848eda4b0dac1c5fd1d925d648d7598b68aa1fb6d6d",
|
||||
"https://bcr.bazel.build/modules/rules_java/4.0.0/MODULE.bazel": "5a78a7ae82cd1a33cef56dc578c7d2a46ed0dca12643ee45edbb8417899e6f74",
|
||||
"https://bcr.bazel.build/modules/rules_java/5.3.5/MODULE.bazel": "a4ec4f2db570171e3e5eb753276ee4b389bae16b96207e9d3230895c99644b86",
|
||||
"https://bcr.bazel.build/modules/rules_java/6.0.0/MODULE.bazel": "8a43b7df601a7ec1af61d79345c17b31ea1fedc6711fd4abfd013ea612978e39",
|
||||
|
||||
23
vendor/github.com/lestrrat-go/jwx/v3/Makefile
generated
vendored
23
vendor/github.com/lestrrat-go/jwx/v3/Makefile
generated
vendored
@@ -1,4 +1,4 @@
|
||||
.PHONY: generate realclean cover viewcover test lint check_diffs imports tidy jwx
|
||||
.PHONY: generate realclean cover viewcover test lint check_diffs imports tidy jwx fuzz fuzz-jwt fuzz-jws fuzz-jwe fuzz-jwk
|
||||
generate:
|
||||
@go generate
|
||||
@$(MAKE) generate-jwa generate-jwe generate-jwk generate-jws generate-jwt
|
||||
@@ -94,5 +94,26 @@ imports:
|
||||
tidy:
|
||||
./scripts/tidy.sh
|
||||
|
||||
FUZZTIME ?= 30s
|
||||
|
||||
fuzz: fuzz-jwt fuzz-jws fuzz-jwe fuzz-jwk
|
||||
|
||||
fuzz-jwt:
|
||||
go test ./jwt/ -run "^$$" -fuzz FuzzParse -fuzztime $(FUZZTIME)
|
||||
go test ./jwt/ -run "^$$" -fuzz FuzzSignAndParse -fuzztime $(FUZZTIME)
|
||||
|
||||
fuzz-jws:
|
||||
go test ./jws/ -run "^$$" -fuzz FuzzParse -fuzztime $(FUZZTIME)
|
||||
go test ./jws/ -run "^$$" -fuzz FuzzSignAndVerify -fuzztime $(FUZZTIME)
|
||||
|
||||
fuzz-jwe:
|
||||
go test ./jwe/ -run "^$$" -fuzz FuzzParse -fuzztime $(FUZZTIME)
|
||||
go test ./jwe/ -run "^$$" -fuzz FuzzEncryptAndDecrypt -fuzztime $(FUZZTIME)
|
||||
|
||||
fuzz-jwk:
|
||||
go test ./jwk/ -run "^$$" -fuzz "^FuzzParseKey$$" -fuzztime $(FUZZTIME)
|
||||
go test ./jwk/ -run "^$$" -fuzz "^FuzzParse$$" -fuzztime $(FUZZTIME)
|
||||
go test ./jwk/ -run "^$$" -fuzz FuzzParseKeyRoundtrip -fuzztime $(FUZZTIME)
|
||||
|
||||
jwx:
|
||||
@./tools/cmd/install-jwx.sh
|
||||
|
||||
7
vendor/github.com/lestrrat-go/jwx/v3/cert/BUILD.bazel
generated
vendored
7
vendor/github.com/lestrrat-go/jwx/v3/cert/BUILD.bazel
generated
vendored
@@ -5,12 +5,15 @@ go_library(
|
||||
srcs = [
|
||||
"cert.go",
|
||||
"chain.go",
|
||||
"options.go",
|
||||
"settings.go",
|
||||
],
|
||||
importpath = "github.com/lestrrat-go/jwx/v3/cert",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//internal/base64",
|
||||
"//internal/tokens",
|
||||
"//internal/base64",
|
||||
"//internal/tokens",
|
||||
"@com_github_lestrrat_go_option_v2//:option",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
72
vendor/github.com/lestrrat-go/jwx/v3/cert/cert.go
generated
vendored
72
vendor/github.com/lestrrat-go/jwx/v3/cert/cert.go
generated
vendored
@@ -1,6 +1,7 @@
|
||||
package cert
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/x509"
|
||||
stdlibb64 "encoding/base64"
|
||||
"fmt"
|
||||
@@ -31,18 +32,81 @@ func EncodeBase64(der []byte) ([]byte, error) {
|
||||
return dst, nil
|
||||
}
|
||||
|
||||
// Parse is a utility function to decode a base64 encoded
|
||||
// ASN.1 DER format certificate, and to parse the byte sequence.
|
||||
// The certificate must be in PKIX format, and it must not contain PEM markers
|
||||
// Parse decodes a base64-encoded ASN.1 DER certificate and validates that it
|
||||
// parses as X.509.
|
||||
//
|
||||
// The certificate must be in PKIX format and it must not contain PEM markers.
|
||||
// The maximum decoded certificate size is controlled by `cert.Settings()`.
|
||||
func Parse(src []byte) (*x509.Certificate, error) {
|
||||
src = stripASCIIWhitespace(bytes.TrimSpace(src))
|
||||
if err := validateEncodedCertificateSize(src); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dst, err := base64.Decode(src)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to base64 decode the certificate: %w`, err)
|
||||
}
|
||||
|
||||
cert, err := x509.ParseCertificate(dst)
|
||||
return validateDERCertificate(dst)
|
||||
}
|
||||
|
||||
func validateDERCertificate(der []byte) (*x509.Certificate, error) {
|
||||
if err := validateCertificateSize(len(der)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cert, err := x509.ParseCertificate(der)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to parse x509 certificate: %w`, err)
|
||||
}
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
func validateEncodedCertificateSize(src []byte) error {
|
||||
return validateCertificateSize(decodedCertificateSize(src))
|
||||
}
|
||||
|
||||
func decodedCertificateSize(src []byte) int {
|
||||
n := len(src)
|
||||
if n == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
size := n / 4 * 3
|
||||
switch n % 4 {
|
||||
case 2:
|
||||
size++
|
||||
case 3:
|
||||
size += 2
|
||||
}
|
||||
|
||||
if n%4 == 0 && src[n-1] == '=' {
|
||||
size--
|
||||
if n > 1 && src[n-2] == '=' {
|
||||
size--
|
||||
}
|
||||
}
|
||||
|
||||
if size < 0 {
|
||||
return 0
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
func normalizeAndValidateChainCertificate(src []byte) ([]byte, error) {
|
||||
normalized := stripASCIIWhitespace(src)
|
||||
if err := validateEncodedCertificateSize(normalized); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
der, err := stdlibb64.StdEncoding.DecodeString(string(normalized))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to base64 decode the certificate: %w`, err)
|
||||
}
|
||||
|
||||
if _, err := validateDERCertificate(der); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return normalized, nil
|
||||
}
|
||||
|
||||
114
vendor/github.com/lestrrat-go/jwx/v3/cert/chain.go
generated
vendored
114
vendor/github.com/lestrrat-go/jwx/v3/cert/chain.go
generated
vendored
@@ -2,8 +2,11 @@ package cert
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/lestrrat-go/jwx/v3/internal/tokens"
|
||||
)
|
||||
@@ -11,8 +14,9 @@ import (
|
||||
// Chain represents a certificate chain as used in the `x5c` field of
|
||||
// various objects within JOSE.
|
||||
//
|
||||
// It stores the certificates as a list of base64 encoded []byte
|
||||
// sequence. By definition these values must PKIX encoded.
|
||||
// It stores the certificates as a list of base64-encoded byte sequences. Every
|
||||
// certificate added to or decoded into the chain must parse as X.509 and is
|
||||
// subject to the global limits configured by `cert.Settings()`.
|
||||
type Chain struct {
|
||||
certificates [][]byte
|
||||
}
|
||||
@@ -24,24 +28,65 @@ func (cc Chain) MarshalJSON() ([]byte, error) {
|
||||
if i > 0 {
|
||||
buf.WriteByte(tokens.Comma)
|
||||
}
|
||||
buf.WriteByte('"')
|
||||
buf.Write(cert)
|
||||
buf.WriteByte('"')
|
||||
encoded, err := json.Marshal(string(cert))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to encode certificate at index %d: %w`, i, err)
|
||||
}
|
||||
buf.Write(encoded)
|
||||
}
|
||||
buf.WriteByte(tokens.CloseSquareBracket)
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes an `x5c` JSON array and validates each entry as a
|
||||
// base64-encoded X.509 certificate.
|
||||
func (cc *Chain) UnmarshalJSON(data []byte) error {
|
||||
var tmp []string
|
||||
if err := json.Unmarshal(data, &tmp); err != nil {
|
||||
dec := json.NewDecoder(bytes.NewReader(data))
|
||||
tok, err := dec.Token()
|
||||
if err != nil {
|
||||
return fmt.Errorf(`failed to unmarshal certificate chain: %w`, err)
|
||||
}
|
||||
|
||||
certs := make([][]byte, len(tmp))
|
||||
for i, cert := range tmp {
|
||||
certs[i] = []byte(cert)
|
||||
delim, ok := tok.(json.Delim)
|
||||
if !ok || delim != '[' {
|
||||
return fmt.Errorf(`failed to unmarshal certificate chain: expected JSON array`)
|
||||
}
|
||||
|
||||
var certs [][]byte
|
||||
for dec.More() {
|
||||
if err := validateChainLength(len(certs) + 1); err != nil {
|
||||
return fmt.Errorf(`failed to unmarshal certificate chain: %w`, err)
|
||||
}
|
||||
|
||||
var cert string
|
||||
if err := dec.Decode(&cert); err != nil {
|
||||
return fmt.Errorf(`failed to decode certificate at index %d: %w`, len(certs), err)
|
||||
}
|
||||
|
||||
normalized, err := normalizeAndValidateChainCertificate([]byte(cert))
|
||||
if err != nil {
|
||||
return fmt.Errorf(`failed to decode certificate at index %d: %w`, len(certs), err)
|
||||
}
|
||||
certs = append(certs, normalized)
|
||||
}
|
||||
|
||||
tok, err = dec.Token()
|
||||
if err != nil {
|
||||
return fmt.Errorf(`failed to unmarshal certificate chain: %w`, err)
|
||||
}
|
||||
|
||||
delim, ok = tok.(json.Delim)
|
||||
if !ok || delim != ']' {
|
||||
return fmt.Errorf(`failed to unmarshal certificate chain: expected closing array`)
|
||||
}
|
||||
|
||||
if _, err := dec.Token(); err != io.EOF {
|
||||
if err != nil {
|
||||
return fmt.Errorf(`failed to unmarshal certificate chain: %w`, err)
|
||||
}
|
||||
return fmt.Errorf(`failed to unmarshal certificate chain: unexpected trailing data`)
|
||||
}
|
||||
|
||||
cc.certificates = certs
|
||||
return nil
|
||||
}
|
||||
@@ -62,19 +107,52 @@ func (cc *Chain) Len() int {
|
||||
return len(cc.certificates)
|
||||
}
|
||||
|
||||
var pemStart = []byte("----- BEGIN CERTIFICATE -----")
|
||||
var pemEnd = []byte("----- END CERTIFICATE -----")
|
||||
|
||||
func (cc *Chain) AddString(der string) error {
|
||||
return cc.Add([]byte(der))
|
||||
}
|
||||
|
||||
// Add appends a certificate to the chain.
|
||||
//
|
||||
// Input may be either a PEM `CERTIFICATE` block or a base64-encoded DER value
|
||||
// as stored in JOSE `x5c` fields. The certificate is validated as X.509 and is
|
||||
// subject to the global limits configured by `cert.Settings()`.
|
||||
func (cc *Chain) Add(der []byte) error {
|
||||
// We're going to be nice and remove marker lines if they
|
||||
// give it to us
|
||||
der = bytes.TrimPrefix(der, pemStart)
|
||||
der = bytes.TrimSuffix(der, pemEnd)
|
||||
if err := validateChainLength(len(cc.certificates) + 1); err != nil {
|
||||
return fmt.Errorf(`cert.Chain.Add: %w`, err)
|
||||
}
|
||||
|
||||
der = bytes.TrimSpace(der)
|
||||
cc.certificates = append(cc.certificates, der)
|
||||
// Accept a PEM-encoded CERTIFICATE block and convert it to the
|
||||
// base64(DER) form that x5c requires.
|
||||
if block, _ := pem.Decode(der); block != nil && block.Type == "CERTIFICATE" {
|
||||
if _, err := validateDERCertificate(block.Bytes); err != nil {
|
||||
return fmt.Errorf(`cert.Chain.Add: %w`, err)
|
||||
}
|
||||
|
||||
encoded := make([]byte, base64.StdEncoding.EncodedLen(len(block.Bytes)))
|
||||
base64.StdEncoding.Encode(encoded, block.Bytes)
|
||||
cc.certificates = append(cc.certificates, encoded)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Non-PEM input must be base64(DER). Strip any internal whitespace
|
||||
// (callers commonly pass multi-line base64 literals) and validate.
|
||||
normalized, err := normalizeAndValidateChainCertificate(der)
|
||||
if err != nil {
|
||||
return fmt.Errorf(`cert.Chain.Add: %w`, err)
|
||||
}
|
||||
cc.certificates = append(cc.certificates, normalized)
|
||||
return nil
|
||||
}
|
||||
|
||||
func stripASCIIWhitespace(src []byte) []byte {
|
||||
dst := make([]byte, 0, len(src))
|
||||
for _, b := range src {
|
||||
switch b {
|
||||
case ' ', '\t', '\r', '\n', '\v', '\f':
|
||||
continue
|
||||
}
|
||||
dst = append(dst, b)
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
34
vendor/github.com/lestrrat-go/jwx/v3/cert/options.go
generated
vendored
Normal file
34
vendor/github.com/lestrrat-go/jwx/v3/cert/options.go
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
package cert
|
||||
|
||||
import "github.com/lestrrat-go/option/v2"
|
||||
|
||||
// GlobalOption describes an option that can be passed to `cert.Settings()`.
|
||||
type GlobalOption interface {
|
||||
option.Interface
|
||||
globalOption()
|
||||
}
|
||||
|
||||
type globalOption struct {
|
||||
option.Interface
|
||||
}
|
||||
|
||||
func (*globalOption) globalOption() {}
|
||||
|
||||
type identMaxChainLength struct{}
|
||||
type identMaxCertificateSize struct{}
|
||||
|
||||
// WithMaxChainLength specifies the maximum number of certificates allowed in
|
||||
// a certificate chain handled by `cert.Chain`.
|
||||
//
|
||||
// The default is 10. Set to 0 to disable the limit.
|
||||
func WithMaxChainLength(v int) GlobalOption {
|
||||
return &globalOption{option.New(identMaxChainLength{}, v)}
|
||||
}
|
||||
|
||||
// WithMaxCertificateSize specifies the maximum decoded DER size, in bytes,
|
||||
// accepted by `cert.Parse()` and `cert.Chain` ingestion.
|
||||
//
|
||||
// The default is 256 KiB. Set to 0 to disable the limit.
|
||||
func WithMaxCertificateSize(v int64) GlobalOption {
|
||||
return &globalOption{option.New(identMaxCertificateSize{}, v)}
|
||||
}
|
||||
76
vendor/github.com/lestrrat-go/jwx/v3/cert/settings.go
generated
vendored
Normal file
76
vendor/github.com/lestrrat-go/jwx/v3/cert/settings.go
generated
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
package cert
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultMaxChainLength = 10
|
||||
defaultMaxCertificateSize = 256 * 1024
|
||||
)
|
||||
|
||||
var maxChainLength atomic.Int64
|
||||
var maxCertificateSize atomic.Int64
|
||||
|
||||
func init() {
|
||||
maxChainLength.Store(defaultMaxChainLength)
|
||||
maxCertificateSize.Store(defaultMaxCertificateSize)
|
||||
}
|
||||
|
||||
// Settings configures process-global validation limits for `cert.Parse()` and
|
||||
// `cert.Chain` ingestion.
|
||||
//
|
||||
// These settings are read atomically, so changing them at runtime is race-free.
|
||||
// However, concurrent parses may observe a mix of old and new values. Configure
|
||||
// them once at program startup when possible.
|
||||
func Settings(options ...GlobalOption) {
|
||||
for _, opt := range options {
|
||||
switch opt.Ident() {
|
||||
case identMaxChainLength{}:
|
||||
var v int
|
||||
if err := opt.Value(&v); err != nil {
|
||||
panic(fmt.Sprintf("cert.Settings: value for option WithMaxChainLength must be an int: %s", err))
|
||||
}
|
||||
if v < 0 {
|
||||
panic("cert.Settings: WithMaxChainLength must be greater than or equal to zero")
|
||||
}
|
||||
maxChainLength.Store(int64(v))
|
||||
case identMaxCertificateSize{}:
|
||||
var v int64
|
||||
if err := opt.Value(&v); err != nil {
|
||||
panic(fmt.Sprintf("cert.Settings: value for option WithMaxCertificateSize must be an int64: %s", err))
|
||||
}
|
||||
if v < 0 {
|
||||
panic("cert.Settings: WithMaxCertificateSize must be greater than or equal to zero")
|
||||
}
|
||||
maxCertificateSize.Store(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func currentMaxChainLength() int64 {
|
||||
return maxChainLength.Load()
|
||||
}
|
||||
|
||||
func currentMaxCertificateSize() int64 {
|
||||
return maxCertificateSize.Load()
|
||||
}
|
||||
|
||||
func validateChainLength(n int) error {
|
||||
limit := currentMaxChainLength()
|
||||
if limit == 0 || int64(n) <= limit {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf(`certificate chain length %d exceeds maximum allowed length of %d`, n, limit)
|
||||
}
|
||||
|
||||
func validateCertificateSize(n int) error {
|
||||
limit := currentMaxCertificateSize()
|
||||
if limit == 0 || int64(n) <= limit {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf(`certificate size %d exceeds maximum allowed size of %d bytes`, n, limit)
|
||||
}
|
||||
11
vendor/github.com/lestrrat-go/jwx/v3/internal/base64/asmbase64.go
generated
vendored
11
vendor/github.com/lestrrat-go/jwx/v3/internal/base64/asmbase64.go
generated
vendored
@@ -3,7 +3,9 @@
|
||||
package base64
|
||||
|
||||
import (
|
||||
stdbase64 "encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"slices"
|
||||
|
||||
asmbase64 "github.com/segmentio/asm/base64"
|
||||
@@ -25,6 +27,15 @@ func (e asmEncoder) AppendEncode(dst, src []byte) []byte {
|
||||
return dst[:len(dst)+n]
|
||||
}
|
||||
|
||||
// NewEncoder satisfies [StreamEncoder]. segmentio/asm's base64 package
|
||||
// does not provide a streaming encoder, so this falls back to the
|
||||
// stdlib's RawURLEncoding streaming encoder — output is byte-identical
|
||||
// for RFC 4648 raw URL encoding, so the signing prefix (produced via
|
||||
// asm) and the streamed payload remain consistent.
|
||||
func (e asmEncoder) NewEncoder(w io.Writer) io.WriteCloser {
|
||||
return stdbase64.NewEncoder(stdbase64.RawURLEncoding, w)
|
||||
}
|
||||
|
||||
type asmDecoder struct{}
|
||||
|
||||
func (d asmDecoder) Decode(src []byte) ([]byte, error) {
|
||||
|
||||
58
vendor/github.com/lestrrat-go/jwx/v3/internal/base64/base64.go
generated
vendored
58
vendor/github.com/lestrrat-go/jwx/v3/internal/base64/base64.go
generated
vendored
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
@@ -19,6 +20,55 @@ type Encoder interface {
|
||||
AppendEncode([]byte, []byte) []byte
|
||||
}
|
||||
|
||||
// StreamEncoder is an [Encoder] that can also produce an incremental
|
||||
// [io.WriteCloser] for encoding a byte stream directly into a downstream
|
||||
// writer. This is the shape the jws streaming detached-payload path
|
||||
// needs to avoid materializing the payload in memory.
|
||||
//
|
||||
// The stdlib *[encoding/base64.Encoding] satisfies this interface
|
||||
// automatically via [AsStreamEncoder] (the stdlib exposes NewEncoder as
|
||||
// a package-level function rather than a method, so a small wrapper is
|
||||
// applied on lookup). Extension modules providing custom encoders
|
||||
// should implement [io.WriteCloser]-returning NewEncoder if they want
|
||||
// their encoder honored by the streaming path.
|
||||
type StreamEncoder interface {
|
||||
Encoder
|
||||
// NewEncoder returns a new [io.WriteCloser] that encodes bytes
|
||||
// written to it and forwards the encoded output to w. Close must
|
||||
// be called to flush any partial final block.
|
||||
NewEncoder(w io.Writer) io.WriteCloser
|
||||
}
|
||||
|
||||
// AsStreamEncoder reports whether e can be used as a [StreamEncoder]
|
||||
// and returns the stream-capable view. Callers should error out when
|
||||
// the second return value is false rather than silently falling back
|
||||
// to a different encoder, to avoid mixing encodings within a single
|
||||
// signing operation.
|
||||
//
|
||||
// The stdlib [*encoding/base64.Encoding] is supported as a special
|
||||
// case (its streaming form is a top-level function rather than a
|
||||
// method, so it does not directly satisfy the interface).
|
||||
func AsStreamEncoder(e Encoder) (StreamEncoder, bool) {
|
||||
if s, ok := e.(StreamEncoder); ok {
|
||||
return s, true
|
||||
}
|
||||
if enc, ok := e.(*base64.Encoding); ok {
|
||||
return stdStreamEncoder{Encoding: enc}, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// stdStreamEncoder wraps the stdlib [*base64.Encoding] so it satisfies
|
||||
// [StreamEncoder]. It is used as the fallback in [AsStreamEncoder]
|
||||
// when a caller passes a raw [*base64.Encoding].
|
||||
type stdStreamEncoder struct {
|
||||
*base64.Encoding
|
||||
}
|
||||
|
||||
func (e stdStreamEncoder) NewEncoder(w io.Writer) io.WriteCloser {
|
||||
return base64.NewEncoder(e.Encoding, w)
|
||||
}
|
||||
|
||||
var muEncoder sync.RWMutex
|
||||
var encoder Encoder = base64.RawURLEncoding
|
||||
var muDecoder sync.RWMutex
|
||||
@@ -59,6 +109,14 @@ func Encode(src []byte) []byte {
|
||||
return dst
|
||||
}
|
||||
|
||||
func AppendEncode(dst, src []byte) []byte {
|
||||
return getEncoder().AppendEncode(dst, src)
|
||||
}
|
||||
|
||||
func EncodedLen(n int) int {
|
||||
return getEncoder().EncodedLen(n)
|
||||
}
|
||||
|
||||
func EncodeToString(src []byte) string {
|
||||
return getEncoder().EncodeToString(src)
|
||||
}
|
||||
|
||||
69
vendor/github.com/lestrrat-go/jwx/v3/internal/json/json.go
generated
vendored
69
vendor/github.com/lestrrat-go/jwx/v3/internal/json/json.go
generated
vendored
@@ -3,16 +3,16 @@ package json
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/lestrrat-go/jwx/v3/internal/base64"
|
||||
)
|
||||
|
||||
var useNumber uint32 // TODO: at some point, change to atomic.Bool
|
||||
var useNumber atomic.Uint32
|
||||
|
||||
func UseNumber() bool {
|
||||
return atomic.LoadUint32(&useNumber) == 1
|
||||
return useNumber.Load() == 1
|
||||
}
|
||||
|
||||
// Sets the global configuration for json decoding
|
||||
@@ -21,7 +21,7 @@ func DecoderSettings(inUseNumber bool) {
|
||||
if inUseNumber {
|
||||
val = 1
|
||||
}
|
||||
atomic.StoreUint32(&useNumber, val)
|
||||
useNumber.Store(val)
|
||||
}
|
||||
|
||||
// Unmarshal respects the values specified in DecoderSettings,
|
||||
@@ -45,7 +45,35 @@ func AssignNextBytesToken(dst *[]byte, dec *Decoder) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func ReadNextStringToken(dec *Decoder) (string, error) {
|
||||
func shouldRejectNullStrings(dc DecodeCtx) bool {
|
||||
if dc != nil {
|
||||
if sdc, ok := dc.(StrictStringDecodeCtx); ok {
|
||||
return sdc.StrictStrings()
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ReadNextStringToken reads the next JSON token from the decoder and
|
||||
// returns it as a string. By default, JSON null is silently accepted as "".
|
||||
// When the given DecodeCtx implements StrictStringDecodeCtx and StrictStrings()
|
||||
// returns true, null values are rejected.
|
||||
func ReadNextStringToken(dec *Decoder, dc DecodeCtx) (string, error) {
|
||||
if shouldRejectNullStrings(dc) {
|
||||
var val any
|
||||
if err := dec.Decode(&val); err != nil {
|
||||
return "", fmt.Errorf(`error reading next value: %w`, err)
|
||||
}
|
||||
if val == nil {
|
||||
return "", fmt.Errorf(`error reading next value: expected string, got null`)
|
||||
}
|
||||
s, ok := val.(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf(`error reading next value: expected string, got %T`, val)
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
var val string
|
||||
if err := dec.Decode(&val); err != nil {
|
||||
return "", fmt.Errorf(`error reading next value: %w`, err)
|
||||
@@ -53,8 +81,8 @@ func ReadNextStringToken(dec *Decoder) (string, error) {
|
||||
return val, nil
|
||||
}
|
||||
|
||||
func AssignNextStringToken(dst **string, dec *Decoder) error {
|
||||
val, err := ReadNextStringToken(dec)
|
||||
func AssignNextStringToken(dst **string, dec *Decoder, dc DecodeCtx) error {
|
||||
val, err := ReadNextStringToken(dec, dc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -106,22 +134,33 @@ type DecodeCtxContainer interface {
|
||||
SetDecodeCtx(DecodeCtx)
|
||||
}
|
||||
|
||||
// stock decodeCtx. should cover 80% of the cases
|
||||
type decodeCtx struct {
|
||||
registry *Registry
|
||||
// StrictStringDecodeCtx is an optional interface that DecodeCtx implementations
|
||||
// can satisfy to control per-call null string rejection.
|
||||
type StrictStringDecodeCtx interface {
|
||||
StrictStrings() bool
|
||||
}
|
||||
|
||||
// stock decodeCtx. should cover 80% of the cases
|
||||
type decodeCtx struct {
|
||||
registry *Registry
|
||||
strictStrings bool
|
||||
}
|
||||
|
||||
// NewDecodeCtx creates a new DecodeCtx with the given registry.
|
||||
func NewDecodeCtx(r *Registry) DecodeCtx {
|
||||
return &decodeCtx{registry: r}
|
||||
}
|
||||
|
||||
// NewDecodeCtxStrictStrings creates a new DecodeCtx with the given registry
|
||||
// and strict string rejection flag.
|
||||
func NewDecodeCtxStrictStrings(r *Registry, strict bool) DecodeCtx {
|
||||
return &decodeCtx{registry: r, strictStrings: strict}
|
||||
}
|
||||
|
||||
func (dc *decodeCtx) Registry() *Registry {
|
||||
return dc.registry
|
||||
}
|
||||
|
||||
func Dump(v any) {
|
||||
enc := NewEncoder(os.Stdout)
|
||||
enc.SetIndent("", " ")
|
||||
//nolint:errchkjson
|
||||
_ = enc.Encode(v)
|
||||
func (dc *decodeCtx) StrictStrings() bool {
|
||||
return dc.strictStrings
|
||||
}
|
||||
|
||||
1
vendor/github.com/lestrrat-go/jwx/v3/internal/json/stdlib.go
generated
vendored
1
vendor/github.com/lestrrat-go/jwx/v3/internal/json/stdlib.go
generated
vendored
@@ -1,6 +1,5 @@
|
||||
//go:build !jwx_goccy
|
||||
|
||||
//nolint:revive
|
||||
package json
|
||||
|
||||
import (
|
||||
|
||||
8
vendor/github.com/lestrrat-go/jwx/v3/internal/jwxio/BUILD.bazel
generated
vendored
8
vendor/github.com/lestrrat-go/jwx/v3/internal/jwxio/BUILD.bazel
generated
vendored
@@ -1,8 +0,0 @@
|
||||
load("@rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "jwxio",
|
||||
srcs = ["jwxio.go"],
|
||||
importpath = "github.com/lestrrat-go/jwx/v3/internal/jwxio",
|
||||
visibility = ["//:__subpackages__"],
|
||||
)
|
||||
29
vendor/github.com/lestrrat-go/jwx/v3/internal/jwxio/jwxio.go
generated
vendored
29
vendor/github.com/lestrrat-go/jwx/v3/internal/jwxio/jwxio.go
generated
vendored
@@ -1,29 +0,0 @@
|
||||
package jwxio
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var errNonFiniteSource = errors.New(`cannot read from non-finite source`)
|
||||
|
||||
func NonFiniteSourceError() error {
|
||||
return errNonFiniteSource
|
||||
}
|
||||
|
||||
// ReadAllFromFiniteSource reads all data from a io.Reader _if_ it comes from a
|
||||
// finite source.
|
||||
func ReadAllFromFiniteSource(rdr io.Reader) ([]byte, error) {
|
||||
switch rdr.(type) {
|
||||
case *bytes.Reader, *bytes.Buffer, *strings.Reader:
|
||||
data, err := io.ReadAll(rdr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data, nil
|
||||
default:
|
||||
return nil, errNonFiniteSource
|
||||
}
|
||||
}
|
||||
1
vendor/github.com/lestrrat-go/jwx/v3/internal/pool/BUILD.bazel
generated
vendored
1
vendor/github.com/lestrrat-go/jwx/v3/internal/pool/BUILD.bazel
generated
vendored
@@ -3,7 +3,6 @@ load("@rules_go//go:def.bzl", "go_library", "go_test")
|
||||
go_library(
|
||||
name = "pool",
|
||||
srcs = [
|
||||
"big_int.go",
|
||||
"byte_slice.go",
|
||||
"bytes_buffer.go",
|
||||
"error_slice.go",
|
||||
|
||||
19
vendor/github.com/lestrrat-go/jwx/v3/internal/pool/big_int.go
generated
vendored
19
vendor/github.com/lestrrat-go/jwx/v3/internal/pool/big_int.go
generated
vendored
@@ -1,19 +0,0 @@
|
||||
package pool
|
||||
|
||||
import "math/big"
|
||||
|
||||
var bigIntPool = New[*big.Int](allocBigInt, freeBigInt)
|
||||
|
||||
func allocBigInt() *big.Int {
|
||||
return &big.Int{}
|
||||
}
|
||||
|
||||
func freeBigInt(b *big.Int) *big.Int {
|
||||
b.SetInt64(0) // Reset the value to zero
|
||||
return b
|
||||
}
|
||||
|
||||
// BigInt returns a pool of *big.Int instances.
|
||||
func BigInt() *Pool[*big.Int] {
|
||||
return bigIntPool
|
||||
}
|
||||
9
vendor/github.com/lestrrat-go/jwx/v3/internal/pool/byte_slice.go
generated
vendored
9
vendor/github.com/lestrrat-go/jwx/v3/internal/pool/byte_slice.go
generated
vendored
@@ -9,9 +9,14 @@ func allocByteSlice() []byte {
|
||||
}
|
||||
|
||||
func freeByteSlice(b []byte) []byte {
|
||||
// Defensive: scrub the entire backing array, not just b[:len(b)]. No
|
||||
// current caller is known to reslice past len(b) and observe stale
|
||||
// bytes, but a defer Put(buf) that captures buf at len=0 (before a
|
||||
// subsequent buf = buf[:n]) would otherwise leave plaintext resident
|
||||
// in the pool's backing storage.
|
||||
b = b[:cap(b)]
|
||||
clear(b)
|
||||
b = b[:0] // Reset the slice to zero length
|
||||
return b
|
||||
return b[:0]
|
||||
}
|
||||
|
||||
func ByteSlice() SlicePool[byte] {
|
||||
|
||||
7
vendor/github.com/lestrrat-go/jwx/v3/internal/pool/bytes_buffer.go
generated
vendored
7
vendor/github.com/lestrrat-go/jwx/v3/internal/pool/bytes_buffer.go
generated
vendored
@@ -9,6 +9,13 @@ func allocBytesBuffer() *bytes.Buffer {
|
||||
}
|
||||
|
||||
func freeBytesBuffer(b *bytes.Buffer) *bytes.Buffer {
|
||||
// Zero the backing array before returning to pool — the buffer
|
||||
// may hold private-key material, plaintext, or HMAC input.
|
||||
// b.Bytes() shares the internal slice (offset is always 0 in
|
||||
// our write-only usage); reslicing to cap reaches all residual bytes.
|
||||
if buf := b.Bytes(); cap(buf) > 0 {
|
||||
clear(buf[:cap(buf)])
|
||||
}
|
||||
b.Reset()
|
||||
return b
|
||||
}
|
||||
|
||||
4
vendor/github.com/lestrrat-go/jwx/v3/jwa/BUILD.bazel
generated
vendored
4
vendor/github.com/lestrrat-go/jwx/v3/jwa/BUILD.bazel
generated
vendored
@@ -5,6 +5,7 @@ go_library(
|
||||
srcs = [
|
||||
"compression_gen.go",
|
||||
"content_encryption_gen.go",
|
||||
"ed448.go",
|
||||
"elliptic_gen.go",
|
||||
"jwa.go",
|
||||
"key_encryption_gen.go",
|
||||
@@ -29,10 +30,11 @@ go_test(
|
||||
"jwa_test.go",
|
||||
"key_encryption_gen_test.go",
|
||||
"key_type_gen_test.go",
|
||||
"options_gen_test.go",
|
||||
"signature_gen_test.go",
|
||||
],
|
||||
embed = [":jwa"],
|
||||
deps = [
|
||||
":jwa",
|
||||
"@com_github_stretchr_testify//require",
|
||||
"@com_github_lestrrat_go_option_v2//:option",
|
||||
],
|
||||
|
||||
25
vendor/github.com/lestrrat-go/jwx/v3/jwa/compression_gen.go
generated
vendored
25
vendor/github.com/lestrrat-go/jwx/v3/jwa/compression_gen.go
generated
vendored
@@ -22,6 +22,9 @@ func init() {
|
||||
algorithms[1] = NewCompressionAlgorithm("")
|
||||
|
||||
RegisterCompressionAlgorithm(algorithms...)
|
||||
for _, alg := range algorithms {
|
||||
builtinCompressionAlgorithm[alg.String()] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// Deflate returns an object representing the "DEF" content compression algorithm value. Using this value specifies that the content should be compressed using DEFLATE (RFC 1951).
|
||||
@@ -88,36 +91,44 @@ func LookupCompressionAlgorithm(name string) (CompressionAlgorithm, bool) {
|
||||
|
||||
// RegisterCompressionAlgorithm registers a new CompressionAlgorithm. The signature value must be immutable
|
||||
// and safe to be used by multiple goroutines, as it is going to be shared with all other users of this library.
|
||||
//
|
||||
// Registration is process-global. Built-in identifiers such as RS256 are
|
||||
// reserved and cannot be replaced by callers after init has completed; use a
|
||||
// distinct name for third-party algorithms.
|
||||
func RegisterCompressionAlgorithm(algorithms ...CompressionAlgorithm) {
|
||||
muAllCompressionAlgorithm.Lock()
|
||||
defer muAllCompressionAlgorithm.Unlock()
|
||||
for _, alg := range algorithms {
|
||||
if _, ok := builtinCompressionAlgorithm[alg.String()]; ok {
|
||||
if existing, ok := allCompressionAlgorithm[alg.String()]; ok && existing != alg {
|
||||
continue
|
||||
}
|
||||
continue
|
||||
}
|
||||
allCompressionAlgorithm[alg.String()] = alg
|
||||
}
|
||||
muAllCompressionAlgorithm.Unlock()
|
||||
rebuildCompressionAlgorithm()
|
||||
rebuildCompressionAlgorithmLocked()
|
||||
}
|
||||
|
||||
// UnregisterCompressionAlgorithm unregisters a CompressionAlgorithm from its known database.
|
||||
// Non-existent entries, as well as built-in algorithms will silently be ignored.
|
||||
func UnregisterCompressionAlgorithm(algorithms ...CompressionAlgorithm) {
|
||||
muAllCompressionAlgorithm.Lock()
|
||||
defer muAllCompressionAlgorithm.Unlock()
|
||||
for _, alg := range algorithms {
|
||||
if _, ok := builtinCompressionAlgorithm[alg.String()]; ok {
|
||||
continue
|
||||
}
|
||||
delete(allCompressionAlgorithm, alg.String())
|
||||
}
|
||||
muAllCompressionAlgorithm.Unlock()
|
||||
rebuildCompressionAlgorithm()
|
||||
rebuildCompressionAlgorithmLocked()
|
||||
}
|
||||
|
||||
func rebuildCompressionAlgorithm() {
|
||||
func rebuildCompressionAlgorithmLocked() {
|
||||
list := make([]CompressionAlgorithm, 0, len(allCompressionAlgorithm))
|
||||
muAllCompressionAlgorithm.RLock()
|
||||
for _, v := range allCompressionAlgorithm {
|
||||
list = append(list, v)
|
||||
}
|
||||
muAllCompressionAlgorithm.RUnlock()
|
||||
sort.Slice(list, func(i, j int) bool {
|
||||
return list[i].String() < list[j].String()
|
||||
})
|
||||
|
||||
72
vendor/github.com/lestrrat-go/jwx/v3/jwa/content_encryption_gen.go
generated
vendored
72
vendor/github.com/lestrrat-go/jwx/v3/jwa/content_encryption_gen.go
generated
vendored
@@ -5,18 +5,10 @@ package jwa
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
"sync"
|
||||
|
||||
"github.com/lestrrat-go/jwx/v3/internal/tokens"
|
||||
)
|
||||
|
||||
var muAllContentEncryptionAlgorithm sync.RWMutex
|
||||
var allContentEncryptionAlgorithm = map[string]ContentEncryptionAlgorithm{}
|
||||
var muListContentEncryptionAlgorithm sync.RWMutex
|
||||
var listContentEncryptionAlgorithm []ContentEncryptionAlgorithm
|
||||
var builtinContentEncryptionAlgorithm = map[string]struct{}{}
|
||||
|
||||
func init() {
|
||||
// builtin values for ContentEncryptionAlgorithm
|
||||
algorithms := make([]ContentEncryptionAlgorithm, 6)
|
||||
@@ -28,6 +20,9 @@ func init() {
|
||||
algorithms[5] = NewContentEncryptionAlgorithm(tokens.A256GCM)
|
||||
|
||||
RegisterContentEncryptionAlgorithm(algorithms...)
|
||||
for _, alg := range algorithms {
|
||||
markBuiltin(alg.String())
|
||||
}
|
||||
}
|
||||
|
||||
// A128CBC_HS256 returns an object representing A128CBC-HS256. Using this value specifies that the content should be encrypted using AES-CBC + HMAC-SHA256 (128).
|
||||
@@ -61,13 +56,11 @@ func A256GCM() ContentEncryptionAlgorithm {
|
||||
}
|
||||
|
||||
func lookupBuiltinContentEncryptionAlgorithm(name string) ContentEncryptionAlgorithm {
|
||||
muAllContentEncryptionAlgorithm.RLock()
|
||||
v, ok := allContentEncryptionAlgorithm[name]
|
||||
muAllContentEncryptionAlgorithm.RUnlock()
|
||||
v, ok := lookupAlgorithm(algKindContentEncryption, name)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf(`jwa: ContentEncryptionAlgorithm %q not registered`, name))
|
||||
}
|
||||
return v
|
||||
return v.(ContentEncryptionAlgorithm)
|
||||
}
|
||||
|
||||
// ContentEncryptionAlgorithm represents the various encryption algorithms as described in https://tools.ietf.org/html/rfc7518#section-5
|
||||
@@ -106,57 +99,46 @@ func NewContentEncryptionAlgorithm(name string, options ...NewAlgorithmOption) C
|
||||
|
||||
// LookupContentEncryptionAlgorithm returns the ContentEncryptionAlgorithm object for the given name.
|
||||
func LookupContentEncryptionAlgorithm(name string) (ContentEncryptionAlgorithm, bool) {
|
||||
muAllContentEncryptionAlgorithm.RLock()
|
||||
v, ok := allContentEncryptionAlgorithm[name]
|
||||
muAllContentEncryptionAlgorithm.RUnlock()
|
||||
return v, ok
|
||||
if v, ok := lookupAlgorithm(algKindContentEncryption, name); ok {
|
||||
return v.(ContentEncryptionAlgorithm), true
|
||||
}
|
||||
var zero ContentEncryptionAlgorithm
|
||||
return zero, false
|
||||
}
|
||||
|
||||
// RegisterContentEncryptionAlgorithm registers a new ContentEncryptionAlgorithm. The signature value must be immutable
|
||||
// and safe to be used by multiple goroutines, as it is going to be shared with all other users of this library.
|
||||
//
|
||||
// Registration is process-global. Built-in identifiers such as RS256 are
|
||||
// reserved and cannot be replaced by callers after init has completed; use a
|
||||
// distinct name for third-party algorithms.
|
||||
//
|
||||
// SignatureAlgorithm, KeyEncryptionAlgorithm, and ContentEncryptionAlgorithm
|
||||
// share a single algorithm-name namespace so that KeyAlgorithmFrom can
|
||||
// resolve unambiguously. Registering a name that is already registered as a
|
||||
// different kind is a silent no-op (the first Register* call wins).
|
||||
func RegisterContentEncryptionAlgorithm(algorithms ...ContentEncryptionAlgorithm) {
|
||||
muAllContentEncryptionAlgorithm.Lock()
|
||||
for _, alg := range algorithms {
|
||||
allContentEncryptionAlgorithm[alg.String()] = alg
|
||||
registerAlgorithm(algKindContentEncryption, alg)
|
||||
}
|
||||
muAllContentEncryptionAlgorithm.Unlock()
|
||||
rebuildContentEncryptionAlgorithm()
|
||||
}
|
||||
|
||||
// UnregisterContentEncryptionAlgorithm unregisters a ContentEncryptionAlgorithm from its known database.
|
||||
// Non-existent entries, as well as built-in algorithms will silently be ignored.
|
||||
func UnregisterContentEncryptionAlgorithm(algorithms ...ContentEncryptionAlgorithm) {
|
||||
muAllContentEncryptionAlgorithm.Lock()
|
||||
for _, alg := range algorithms {
|
||||
if _, ok := builtinContentEncryptionAlgorithm[alg.String()]; ok {
|
||||
continue
|
||||
}
|
||||
delete(allContentEncryptionAlgorithm, alg.String())
|
||||
unregisterAlgorithm(algKindContentEncryption, alg.String())
|
||||
}
|
||||
muAllContentEncryptionAlgorithm.Unlock()
|
||||
rebuildContentEncryptionAlgorithm()
|
||||
}
|
||||
|
||||
func rebuildContentEncryptionAlgorithm() {
|
||||
list := make([]ContentEncryptionAlgorithm, 0, len(allContentEncryptionAlgorithm))
|
||||
muAllContentEncryptionAlgorithm.RLock()
|
||||
for _, v := range allContentEncryptionAlgorithm {
|
||||
list = append(list, v)
|
||||
}
|
||||
muAllContentEncryptionAlgorithm.RUnlock()
|
||||
sort.Slice(list, func(i, j int) bool {
|
||||
return list[i].String() < list[j].String()
|
||||
})
|
||||
muListContentEncryptionAlgorithm.Lock()
|
||||
listContentEncryptionAlgorithm = list
|
||||
muListContentEncryptionAlgorithm.Unlock()
|
||||
}
|
||||
|
||||
// ContentEncryptionAlgorithms returns a list of all available values for ContentEncryptionAlgorithm.
|
||||
func ContentEncryptionAlgorithms() []ContentEncryptionAlgorithm {
|
||||
muListContentEncryptionAlgorithm.RLock()
|
||||
defer muListContentEncryptionAlgorithm.RUnlock()
|
||||
return listContentEncryptionAlgorithm
|
||||
raw := listAlgorithmsByKind(algKindContentEncryption)
|
||||
out := make([]ContentEncryptionAlgorithm, len(raw))
|
||||
for i, alg := range raw {
|
||||
out[i] = alg.(ContentEncryptionAlgorithm)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// MarshalJSON serializes the ContentEncryptionAlgorithm object to a JSON string.
|
||||
|
||||
14
vendor/github.com/lestrrat-go/jwx/v3/jwa/ed448.go
generated
vendored
Normal file
14
vendor/github.com/lestrrat-go/jwx/v3/jwa/ed448.go
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
package jwa
|
||||
|
||||
// EdDSAEd448 returns an object representing the EdDSA signature algorithm
|
||||
// using Ed448 (RFC 9864).
|
||||
//
|
||||
// Unlike built-in algorithms, Ed448 is not registered by default. Import
|
||||
// the ed448 module for its side effects to enable Ed448 support:
|
||||
//
|
||||
// import _ "github.com/lestrrat-go/jwx-circl-ed448"
|
||||
//
|
||||
// The function name is tentative and may change in future releases.
|
||||
func EdDSAEd448() SignatureAlgorithm {
|
||||
return NewSignatureAlgorithm("Ed448")
|
||||
}
|
||||
25
vendor/github.com/lestrrat-go/jwx/v3/jwa/elliptic_gen.go
generated
vendored
25
vendor/github.com/lestrrat-go/jwx/v3/jwa/elliptic_gen.go
generated
vendored
@@ -27,6 +27,9 @@ func init() {
|
||||
algorithms[6] = NewEllipticCurveAlgorithm("X448")
|
||||
|
||||
RegisterEllipticCurveAlgorithm(algorithms...)
|
||||
for _, alg := range algorithms {
|
||||
builtinEllipticCurveAlgorithm[alg.String()] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// Ed25519 returns an object representing Ed25519 algorithm for EdDSA operations.
|
||||
@@ -125,36 +128,44 @@ func LookupEllipticCurveAlgorithm(name string) (EllipticCurveAlgorithm, bool) {
|
||||
|
||||
// RegisterEllipticCurveAlgorithm registers a new EllipticCurveAlgorithm. The signature value must be immutable
|
||||
// and safe to be used by multiple goroutines, as it is going to be shared with all other users of this library.
|
||||
//
|
||||
// Registration is process-global. Built-in identifiers such as RS256 are
|
||||
// reserved and cannot be replaced by callers after init has completed; use a
|
||||
// distinct name for third-party algorithms.
|
||||
func RegisterEllipticCurveAlgorithm(algorithms ...EllipticCurveAlgorithm) {
|
||||
muAllEllipticCurveAlgorithm.Lock()
|
||||
defer muAllEllipticCurveAlgorithm.Unlock()
|
||||
for _, alg := range algorithms {
|
||||
if _, ok := builtinEllipticCurveAlgorithm[alg.String()]; ok {
|
||||
if existing, ok := allEllipticCurveAlgorithm[alg.String()]; ok && existing != alg {
|
||||
continue
|
||||
}
|
||||
continue
|
||||
}
|
||||
allEllipticCurveAlgorithm[alg.String()] = alg
|
||||
}
|
||||
muAllEllipticCurveAlgorithm.Unlock()
|
||||
rebuildEllipticCurveAlgorithm()
|
||||
rebuildEllipticCurveAlgorithmLocked()
|
||||
}
|
||||
|
||||
// UnregisterEllipticCurveAlgorithm unregisters a EllipticCurveAlgorithm from its known database.
|
||||
// Non-existent entries, as well as built-in algorithms will silently be ignored.
|
||||
func UnregisterEllipticCurveAlgorithm(algorithms ...EllipticCurveAlgorithm) {
|
||||
muAllEllipticCurveAlgorithm.Lock()
|
||||
defer muAllEllipticCurveAlgorithm.Unlock()
|
||||
for _, alg := range algorithms {
|
||||
if _, ok := builtinEllipticCurveAlgorithm[alg.String()]; ok {
|
||||
continue
|
||||
}
|
||||
delete(allEllipticCurveAlgorithm, alg.String())
|
||||
}
|
||||
muAllEllipticCurveAlgorithm.Unlock()
|
||||
rebuildEllipticCurveAlgorithm()
|
||||
rebuildEllipticCurveAlgorithmLocked()
|
||||
}
|
||||
|
||||
func rebuildEllipticCurveAlgorithm() {
|
||||
func rebuildEllipticCurveAlgorithmLocked() {
|
||||
list := make([]EllipticCurveAlgorithm, 0, len(allEllipticCurveAlgorithm))
|
||||
muAllEllipticCurveAlgorithm.RLock()
|
||||
for _, v := range allEllipticCurveAlgorithm {
|
||||
list = append(list, v)
|
||||
}
|
||||
muAllEllipticCurveAlgorithm.RUnlock()
|
||||
sort.Slice(list, func(i, j int) bool {
|
||||
return list[i].String() < list[j].String()
|
||||
})
|
||||
|
||||
193
vendor/github.com/lestrrat-go/jwx/v3/jwa/jwa.go
generated
vendored
193
vendor/github.com/lestrrat-go/jwx/v3/jwa/jwa.go
generated
vendored
@@ -6,8 +6,12 @@ package jwa
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const maxKeyAlgorithmErrorPreview = 64
|
||||
|
||||
// KeyAlgorithm is a workaround for jwk.Key being able to contain different
|
||||
// types of algorithms in its `alg` field.
|
||||
//
|
||||
@@ -30,38 +34,189 @@ func ErrInvalidKeyAlgorithm() error {
|
||||
return errInvalidKeyAlgorithm
|
||||
}
|
||||
|
||||
func formatInvalidKeyAlgorithmValue(v string) string {
|
||||
runes := []rune(v)
|
||||
if len(runes) <= maxKeyAlgorithmErrorPreview {
|
||||
return fmt.Sprintf("%q", v)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%q", string(runes[:maxKeyAlgorithmErrorPreview])+`...`)
|
||||
}
|
||||
|
||||
// algorithmKind tags entries in the shared algRegistry so the
|
||||
// per-kind public Register/Lookup/Unregister/<Kind>s functions can
|
||||
// dispatch through one map without losing the typed identity of each
|
||||
// algorithm.
|
||||
type algorithmKind uint8
|
||||
|
||||
const (
|
||||
algKindUnknown algorithmKind = iota
|
||||
algKindSignature
|
||||
algKindKeyEncryption
|
||||
algKindContentEncryption
|
||||
)
|
||||
|
||||
func (k algorithmKind) String() string {
|
||||
switch k {
|
||||
case algKindSignature:
|
||||
return "SignatureAlgorithm"
|
||||
case algKindKeyEncryption:
|
||||
return "KeyEncryptionAlgorithm"
|
||||
case algKindContentEncryption:
|
||||
return "ContentEncryptionAlgorithm"
|
||||
default:
|
||||
return "unknown algorithm kind"
|
||||
}
|
||||
}
|
||||
|
||||
type algRegistryEntry struct {
|
||||
kind algorithmKind
|
||||
alg KeyAlgorithm
|
||||
builtin bool
|
||||
}
|
||||
|
||||
// algRegistry is the single shared namespace for the three
|
||||
// KeyAlgorithm-implementing kinds. Independent per-kind maps would
|
||||
// let an extension register the same name as both (say) a
|
||||
// SignatureAlgorithm and a KeyEncryptionAlgorithm, after which
|
||||
// KeyAlgorithmFrom("X") would resolve to whichever kind was tried
|
||||
// first — silently flipping with import order. Funnelling all three
|
||||
// through one map fixes that ambiguity at registration time.
|
||||
var (
|
||||
muAlgRegistry sync.RWMutex
|
||||
algRegistry = map[string]algRegistryEntry{}
|
||||
)
|
||||
|
||||
// registerAlgorithm is the shared backend for the three public
|
||||
// Register{Signature,KeyEncryption,ContentEncryption}Algorithm
|
||||
// functions.
|
||||
//
|
||||
// Behavior:
|
||||
// - Re-registering the exact same value (same kind, same alg) is a
|
||||
// no-op.
|
||||
// - Cross-kind name reuse is a silent no-op: the first registration
|
||||
// wins and the second Register* call has no effect. v3's
|
||||
// pre-existing Register* signature returns no error and v3 does
|
||||
// not change observable error/panic behavior, so the cross-kind
|
||||
// case is silently skipped — KeyAlgorithmFrom now resolves
|
||||
// unambiguously to the first-registered kind. (v4 escalates this
|
||||
// to a returned error from Register*.)
|
||||
// - Built-in replacement is a silent no-op, preserving the
|
||||
// pre-unification per-kind v3 Register* behavior.
|
||||
// - Same-kind, non-builtin re-registration with a different value
|
||||
// silently overwrites. This preserves the pre-unification
|
||||
// behavior of the per-kind Register* functions.
|
||||
func registerAlgorithm(kind algorithmKind, alg KeyAlgorithm) {
|
||||
name := alg.String()
|
||||
muAlgRegistry.Lock()
|
||||
defer muAlgRegistry.Unlock()
|
||||
if existing, ok := algRegistry[name]; ok {
|
||||
if existing.kind == kind && existing.alg == alg {
|
||||
return
|
||||
}
|
||||
if existing.kind != kind {
|
||||
return
|
||||
}
|
||||
if existing.builtin {
|
||||
return
|
||||
}
|
||||
}
|
||||
algRegistry[name] = algRegistryEntry{kind: kind, alg: alg}
|
||||
}
|
||||
|
||||
// markBuiltin flips the builtin flag on an already-registered name.
|
||||
// Called by the per-kind generated init() after the bulk Register*
|
||||
// pass, preserving the existing two-phase init pattern.
|
||||
func markBuiltin(name string) {
|
||||
muAlgRegistry.Lock()
|
||||
defer muAlgRegistry.Unlock()
|
||||
if entry, ok := algRegistry[name]; ok {
|
||||
entry.builtin = true
|
||||
algRegistry[name] = entry
|
||||
}
|
||||
}
|
||||
|
||||
// unregisterAlgorithm is the shared backend for the three public
|
||||
// Unregister*Algorithm functions. No-op for built-ins, no-op for a
|
||||
// kind mismatch, no-op for unknown names — same surface contract as
|
||||
// the pre-unification per-kind Unregister*.
|
||||
func unregisterAlgorithm(kind algorithmKind, name string) {
|
||||
muAlgRegistry.Lock()
|
||||
defer muAlgRegistry.Unlock()
|
||||
if entry, ok := algRegistry[name]; ok && entry.kind == kind && !entry.builtin {
|
||||
delete(algRegistry, name)
|
||||
}
|
||||
}
|
||||
|
||||
// lookupAlgorithm returns the registered KeyAlgorithm for name iff it
|
||||
// is registered as the requested kind. Used by the per-kind
|
||||
// generated Lookup* wrappers.
|
||||
func lookupAlgorithm(kind algorithmKind, name string) (KeyAlgorithm, bool) {
|
||||
muAlgRegistry.RLock()
|
||||
defer muAlgRegistry.RUnlock()
|
||||
if entry, ok := algRegistry[name]; ok && entry.kind == kind {
|
||||
return entry.alg, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// listAlgorithmsByKind returns every registered algorithm of the
|
||||
// given kind, sorted by name. Used by the per-kind generated
|
||||
// <Kind>s() functions.
|
||||
func listAlgorithmsByKind(kind algorithmKind) []KeyAlgorithm {
|
||||
muAlgRegistry.RLock()
|
||||
defer muAlgRegistry.RUnlock()
|
||||
out := make([]KeyAlgorithm, 0, len(algRegistry))
|
||||
for _, entry := range algRegistry {
|
||||
if entry.kind == kind {
|
||||
out = append(out, entry.alg)
|
||||
}
|
||||
}
|
||||
sort.Slice(out, func(i, j int) bool { return out[i].String() < out[j].String() })
|
||||
return out
|
||||
}
|
||||
|
||||
// KeyAlgorithmFrom takes either a string, `jwa.SignatureAlgorithm`,
|
||||
// `jwa.KeyEncryptionAlgorithm`, or `jwa.ContentEncryptionAlgorithm`.
|
||||
// `jwa.KeyEncryptionAlgorithm`, or `jwa.ContentEncryptionAlgorithm`,
|
||||
// and returns a `jwa.KeyAlgorithm`.
|
||||
//
|
||||
// If the value cannot be handled, it returns an `jwa.InvalidKeyAlgorithm`
|
||||
// object instead of returning an error. This design choice was made to allow
|
||||
// users to directly pass the return value to functions such as `jws.Sign()`
|
||||
// String inputs resolve through the shared algorithm registry: the
|
||||
// returned KeyAlgorithm holds the concrete typed value (Signature,
|
||||
// KeyEncryption, or ContentEncryption) for whichever kind owns the
|
||||
// name. Cross-kind name reuse is structurally avoided — the first
|
||||
// Register* wins and subsequent cross-kind registrations are silent
|
||||
// no-ops — so KeyAlgorithmFrom no longer needs precedence rules.
|
||||
//
|
||||
// Typed inputs whose String() is empty (for example a zero-value
|
||||
// `var sa jwa.SignatureAlgorithm`) are rejected with
|
||||
// ErrInvalidKeyAlgorithm. Without this check the typed arms accepted
|
||||
// names that would never resolve through any registry, surfacing as
|
||||
// confusing failures far from the call site.
|
||||
func KeyAlgorithmFrom(v any) (KeyAlgorithm, error) {
|
||||
switch v := v.(type) {
|
||||
case SignatureAlgorithm:
|
||||
if v.String() == "" {
|
||||
return nil, fmt.Errorf(`invalid key value: zero-value %T: %w`, v, errInvalidKeyAlgorithm)
|
||||
}
|
||||
return v, nil
|
||||
case KeyEncryptionAlgorithm:
|
||||
if v.String() == "" {
|
||||
return nil, fmt.Errorf(`invalid key value: zero-value %T: %w`, v, errInvalidKeyAlgorithm)
|
||||
}
|
||||
return v, nil
|
||||
case ContentEncryptionAlgorithm:
|
||||
if v.String() == "" {
|
||||
return nil, fmt.Errorf(`invalid key value: zero-value %T: %w`, v, errInvalidKeyAlgorithm)
|
||||
}
|
||||
return v, nil
|
||||
case string:
|
||||
salg, ok := LookupSignatureAlgorithm(v)
|
||||
if ok {
|
||||
return salg, nil
|
||||
muAlgRegistry.RLock()
|
||||
entry, ok := algRegistry[v]
|
||||
muAlgRegistry.RUnlock()
|
||||
if !ok {
|
||||
return nil, fmt.Errorf(`invalid key value: %s: %w`, formatInvalidKeyAlgorithmValue(v), errInvalidKeyAlgorithm)
|
||||
}
|
||||
|
||||
kalg, ok := LookupKeyEncryptionAlgorithm(v)
|
||||
if ok {
|
||||
return kalg, nil
|
||||
}
|
||||
|
||||
calg, ok := LookupContentEncryptionAlgorithm(v)
|
||||
if ok {
|
||||
return calg, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf(`invalid key value: %q: %w`, v, errInvalidKeyAlgorithm)
|
||||
return entry.alg, nil
|
||||
default:
|
||||
return nil, fmt.Errorf(`invalid key type: %T: %w`, v, errInvalidKeyAlgorithm)
|
||||
}
|
||||
|
||||
72
vendor/github.com/lestrrat-go/jwx/v3/jwa/key_encryption_gen.go
generated
vendored
72
vendor/github.com/lestrrat-go/jwx/v3/jwa/key_encryption_gen.go
generated
vendored
@@ -5,18 +5,10 @@ package jwa
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
"sync"
|
||||
|
||||
"github.com/lestrrat-go/jwx/v3/internal/tokens"
|
||||
)
|
||||
|
||||
var muAllKeyEncryptionAlgorithm sync.RWMutex
|
||||
var allKeyEncryptionAlgorithm = map[string]KeyEncryptionAlgorithm{}
|
||||
var muListKeyEncryptionAlgorithm sync.RWMutex
|
||||
var listKeyEncryptionAlgorithm []KeyEncryptionAlgorithm
|
||||
var builtinKeyEncryptionAlgorithm = map[string]struct{}{}
|
||||
|
||||
func init() {
|
||||
// builtin values for KeyEncryptionAlgorithm
|
||||
algorithms := make([]KeyEncryptionAlgorithm, 19)
|
||||
@@ -41,6 +33,9 @@ func init() {
|
||||
algorithms[18] = NewKeyEncryptionAlgorithm(tokens.RSA_OAEP_512)
|
||||
|
||||
RegisterKeyEncryptionAlgorithm(algorithms...)
|
||||
for _, alg := range algorithms {
|
||||
markBuiltin(alg.String())
|
||||
}
|
||||
}
|
||||
|
||||
// A128GCMKW returns an object representing AES-GCM key wrap (128) key encryption algorithm.
|
||||
@@ -139,13 +134,11 @@ func RSA_OAEP_512() KeyEncryptionAlgorithm {
|
||||
}
|
||||
|
||||
func lookupBuiltinKeyEncryptionAlgorithm(name string) KeyEncryptionAlgorithm {
|
||||
muAllKeyEncryptionAlgorithm.RLock()
|
||||
v, ok := allKeyEncryptionAlgorithm[name]
|
||||
muAllKeyEncryptionAlgorithm.RUnlock()
|
||||
v, ok := lookupAlgorithm(algKindKeyEncryption, name)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf(`jwa: KeyEncryptionAlgorithm %q not registered`, name))
|
||||
}
|
||||
return v
|
||||
return v.(KeyEncryptionAlgorithm)
|
||||
}
|
||||
|
||||
// KeyEncryptionAlgorithm represents the various encryption algorithms as described in https://tools.ietf.org/html/rfc7518#section-4.1
|
||||
@@ -195,57 +188,46 @@ func NewKeyEncryptionAlgorithm(name string, options ...NewKeyEncryptionAlgorithm
|
||||
|
||||
// LookupKeyEncryptionAlgorithm returns the KeyEncryptionAlgorithm object for the given name.
|
||||
func LookupKeyEncryptionAlgorithm(name string) (KeyEncryptionAlgorithm, bool) {
|
||||
muAllKeyEncryptionAlgorithm.RLock()
|
||||
v, ok := allKeyEncryptionAlgorithm[name]
|
||||
muAllKeyEncryptionAlgorithm.RUnlock()
|
||||
return v, ok
|
||||
if v, ok := lookupAlgorithm(algKindKeyEncryption, name); ok {
|
||||
return v.(KeyEncryptionAlgorithm), true
|
||||
}
|
||||
var zero KeyEncryptionAlgorithm
|
||||
return zero, false
|
||||
}
|
||||
|
||||
// RegisterKeyEncryptionAlgorithm registers a new KeyEncryptionAlgorithm. The signature value must be immutable
|
||||
// and safe to be used by multiple goroutines, as it is going to be shared with all other users of this library.
|
||||
//
|
||||
// Registration is process-global. Built-in identifiers such as RS256 are
|
||||
// reserved and cannot be replaced by callers after init has completed; use a
|
||||
// distinct name for third-party algorithms.
|
||||
//
|
||||
// SignatureAlgorithm, KeyEncryptionAlgorithm, and ContentEncryptionAlgorithm
|
||||
// share a single algorithm-name namespace so that KeyAlgorithmFrom can
|
||||
// resolve unambiguously. Registering a name that is already registered as a
|
||||
// different kind is a silent no-op (the first Register* call wins).
|
||||
func RegisterKeyEncryptionAlgorithm(algorithms ...KeyEncryptionAlgorithm) {
|
||||
muAllKeyEncryptionAlgorithm.Lock()
|
||||
for _, alg := range algorithms {
|
||||
allKeyEncryptionAlgorithm[alg.String()] = alg
|
||||
registerAlgorithm(algKindKeyEncryption, alg)
|
||||
}
|
||||
muAllKeyEncryptionAlgorithm.Unlock()
|
||||
rebuildKeyEncryptionAlgorithm()
|
||||
}
|
||||
|
||||
// UnregisterKeyEncryptionAlgorithm unregisters a KeyEncryptionAlgorithm from its known database.
|
||||
// Non-existent entries, as well as built-in algorithms will silently be ignored.
|
||||
func UnregisterKeyEncryptionAlgorithm(algorithms ...KeyEncryptionAlgorithm) {
|
||||
muAllKeyEncryptionAlgorithm.Lock()
|
||||
for _, alg := range algorithms {
|
||||
if _, ok := builtinKeyEncryptionAlgorithm[alg.String()]; ok {
|
||||
continue
|
||||
}
|
||||
delete(allKeyEncryptionAlgorithm, alg.String())
|
||||
unregisterAlgorithm(algKindKeyEncryption, alg.String())
|
||||
}
|
||||
muAllKeyEncryptionAlgorithm.Unlock()
|
||||
rebuildKeyEncryptionAlgorithm()
|
||||
}
|
||||
|
||||
func rebuildKeyEncryptionAlgorithm() {
|
||||
list := make([]KeyEncryptionAlgorithm, 0, len(allKeyEncryptionAlgorithm))
|
||||
muAllKeyEncryptionAlgorithm.RLock()
|
||||
for _, v := range allKeyEncryptionAlgorithm {
|
||||
list = append(list, v)
|
||||
}
|
||||
muAllKeyEncryptionAlgorithm.RUnlock()
|
||||
sort.Slice(list, func(i, j int) bool {
|
||||
return list[i].String() < list[j].String()
|
||||
})
|
||||
muListKeyEncryptionAlgorithm.Lock()
|
||||
listKeyEncryptionAlgorithm = list
|
||||
muListKeyEncryptionAlgorithm.Unlock()
|
||||
}
|
||||
|
||||
// KeyEncryptionAlgorithms returns a list of all available values for KeyEncryptionAlgorithm.
|
||||
func KeyEncryptionAlgorithms() []KeyEncryptionAlgorithm {
|
||||
muListKeyEncryptionAlgorithm.RLock()
|
||||
defer muListKeyEncryptionAlgorithm.RUnlock()
|
||||
return listKeyEncryptionAlgorithm
|
||||
raw := listAlgorithmsByKind(algKindKeyEncryption)
|
||||
out := make([]KeyEncryptionAlgorithm, len(raw))
|
||||
for i, alg := range raw {
|
||||
out[i] = alg.(KeyEncryptionAlgorithm)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// MarshalJSON serializes the KeyEncryptionAlgorithm object to a JSON string.
|
||||
|
||||
25
vendor/github.com/lestrrat-go/jwx/v3/jwa/key_type_gen.go
generated
vendored
25
vendor/github.com/lestrrat-go/jwx/v3/jwa/key_type_gen.go
generated
vendored
@@ -24,6 +24,9 @@ func init() {
|
||||
algorithms[3] = NewKeyType("RSA")
|
||||
|
||||
RegisterKeyType(algorithms...)
|
||||
for _, alg := range algorithms {
|
||||
builtinKeyType[alg.String()] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// EC returns an object representing EC. Elliptic Curve
|
||||
@@ -107,36 +110,44 @@ func LookupKeyType(name string) (KeyType, bool) {
|
||||
|
||||
// RegisterKeyType registers a new KeyType. The signature value must be immutable
|
||||
// and safe to be used by multiple goroutines, as it is going to be shared with all other users of this library.
|
||||
//
|
||||
// Registration is process-global. Built-in identifiers such as RS256 are
|
||||
// reserved and cannot be replaced by callers after init has completed; use a
|
||||
// distinct name for third-party algorithms.
|
||||
func RegisterKeyType(algorithms ...KeyType) {
|
||||
muAllKeyType.Lock()
|
||||
defer muAllKeyType.Unlock()
|
||||
for _, alg := range algorithms {
|
||||
if _, ok := builtinKeyType[alg.String()]; ok {
|
||||
if existing, ok := allKeyType[alg.String()]; ok && existing != alg {
|
||||
continue
|
||||
}
|
||||
continue
|
||||
}
|
||||
allKeyType[alg.String()] = alg
|
||||
}
|
||||
muAllKeyType.Unlock()
|
||||
rebuildKeyType()
|
||||
rebuildKeyTypeLocked()
|
||||
}
|
||||
|
||||
// UnregisterKeyType unregisters a KeyType from its known database.
|
||||
// Non-existent entries, as well as built-in algorithms will silently be ignored.
|
||||
func UnregisterKeyType(algorithms ...KeyType) {
|
||||
muAllKeyType.Lock()
|
||||
defer muAllKeyType.Unlock()
|
||||
for _, alg := range algorithms {
|
||||
if _, ok := builtinKeyType[alg.String()]; ok {
|
||||
continue
|
||||
}
|
||||
delete(allKeyType, alg.String())
|
||||
}
|
||||
muAllKeyType.Unlock()
|
||||
rebuildKeyType()
|
||||
rebuildKeyTypeLocked()
|
||||
}
|
||||
|
||||
func rebuildKeyType() {
|
||||
func rebuildKeyTypeLocked() {
|
||||
list := make([]KeyType, 0, len(allKeyType))
|
||||
muAllKeyType.RLock()
|
||||
for _, v := range allKeyType {
|
||||
list = append(list, v)
|
||||
}
|
||||
muAllKeyType.RUnlock()
|
||||
sort.Slice(list, func(i, j int) bool {
|
||||
return list[i].String() < list[j].String()
|
||||
})
|
||||
|
||||
104
vendor/github.com/lestrrat-go/jwx/v3/jwa/signature_gen.go
generated
vendored
104
vendor/github.com/lestrrat-go/jwx/v3/jwa/signature_gen.go
generated
vendored
@@ -5,36 +5,32 @@ package jwa
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var muAllSignatureAlgorithm sync.RWMutex
|
||||
var allSignatureAlgorithm = map[string]SignatureAlgorithm{}
|
||||
var muListSignatureAlgorithm sync.RWMutex
|
||||
var listSignatureAlgorithm []SignatureAlgorithm
|
||||
var builtinSignatureAlgorithm = map[string]struct{}{}
|
||||
|
||||
func init() {
|
||||
// builtin values for SignatureAlgorithm
|
||||
algorithms := make([]SignatureAlgorithm, 15)
|
||||
algorithms := make([]SignatureAlgorithm, 16)
|
||||
algorithms[0] = NewSignatureAlgorithm("ES256")
|
||||
algorithms[1] = NewSignatureAlgorithm("ES256K")
|
||||
algorithms[2] = NewSignatureAlgorithm("ES384")
|
||||
algorithms[3] = NewSignatureAlgorithm("ES512")
|
||||
algorithms[4] = NewSignatureAlgorithm("EdDSA")
|
||||
algorithms[5] = NewSignatureAlgorithm("HS256", WithIsSymmetric(true))
|
||||
algorithms[6] = NewSignatureAlgorithm("HS384", WithIsSymmetric(true))
|
||||
algorithms[7] = NewSignatureAlgorithm("HS512", WithIsSymmetric(true))
|
||||
algorithms[8] = NewSignatureAlgorithm("none")
|
||||
algorithms[9] = NewSignatureAlgorithm("PS256")
|
||||
algorithms[10] = NewSignatureAlgorithm("PS384")
|
||||
algorithms[11] = NewSignatureAlgorithm("PS512")
|
||||
algorithms[12] = NewSignatureAlgorithm("RS256")
|
||||
algorithms[13] = NewSignatureAlgorithm("RS384")
|
||||
algorithms[14] = NewSignatureAlgorithm("RS512")
|
||||
algorithms[4] = NewSignatureAlgorithm("EdDSA", WithDeprecated(true))
|
||||
algorithms[5] = NewSignatureAlgorithm("Ed25519")
|
||||
algorithms[6] = NewSignatureAlgorithm("HS256", WithIsSymmetric(true))
|
||||
algorithms[7] = NewSignatureAlgorithm("HS384", WithIsSymmetric(true))
|
||||
algorithms[8] = NewSignatureAlgorithm("HS512", WithIsSymmetric(true))
|
||||
algorithms[9] = NewSignatureAlgorithm("none")
|
||||
algorithms[10] = NewSignatureAlgorithm("PS256")
|
||||
algorithms[11] = NewSignatureAlgorithm("PS384")
|
||||
algorithms[12] = NewSignatureAlgorithm("PS512")
|
||||
algorithms[13] = NewSignatureAlgorithm("RS256")
|
||||
algorithms[14] = NewSignatureAlgorithm("RS384")
|
||||
algorithms[15] = NewSignatureAlgorithm("RS512")
|
||||
|
||||
RegisterSignatureAlgorithm(algorithms...)
|
||||
for _, alg := range algorithms {
|
||||
markBuiltin(alg.String())
|
||||
}
|
||||
}
|
||||
|
||||
// ES256 returns an object representing ECDSA signature algorithm using P-256 curve and SHA-256.
|
||||
@@ -57,11 +53,16 @@ func ES512() SignatureAlgorithm {
|
||||
return lookupBuiltinSignatureAlgorithm("ES512")
|
||||
}
|
||||
|
||||
// EdDSA returns an object representing EdDSA signature algorithms.
|
||||
// EdDSA returns an object representing EdDSA signature algorithms (deprecated by RFC 9864, use EdDSAEd25519 or EdDSAEd448).
|
||||
func EdDSA() SignatureAlgorithm {
|
||||
return lookupBuiltinSignatureAlgorithm("EdDSA")
|
||||
}
|
||||
|
||||
// EdDSAEd25519 returns an object representing EdDSA signature algorithm using Ed25519 (RFC 9864). The function name is tentative and may change in future releases.
|
||||
func EdDSAEd25519() SignatureAlgorithm {
|
||||
return lookupBuiltinSignatureAlgorithm("Ed25519")
|
||||
}
|
||||
|
||||
// HS256 returns an object representing HMAC signature algorithm using SHA-256.
|
||||
func HS256() SignatureAlgorithm {
|
||||
return lookupBuiltinSignatureAlgorithm("HS256")
|
||||
@@ -113,13 +114,11 @@ func RS512() SignatureAlgorithm {
|
||||
}
|
||||
|
||||
func lookupBuiltinSignatureAlgorithm(name string) SignatureAlgorithm {
|
||||
muAllSignatureAlgorithm.RLock()
|
||||
v, ok := allSignatureAlgorithm[name]
|
||||
muAllSignatureAlgorithm.RUnlock()
|
||||
v, ok := lookupAlgorithm(algKindSignature, name)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf(`jwa: SignatureAlgorithm %q not registered`, name))
|
||||
}
|
||||
return v
|
||||
return v.(SignatureAlgorithm)
|
||||
}
|
||||
|
||||
// SignatureAlgorithm represents the various signature algorithms as described in https://tools.ietf.org/html/rfc7518#section-3.1
|
||||
@@ -169,57 +168,46 @@ func NewSignatureAlgorithm(name string, options ...NewSignatureAlgorithmOption)
|
||||
|
||||
// LookupSignatureAlgorithm returns the SignatureAlgorithm object for the given name.
|
||||
func LookupSignatureAlgorithm(name string) (SignatureAlgorithm, bool) {
|
||||
muAllSignatureAlgorithm.RLock()
|
||||
v, ok := allSignatureAlgorithm[name]
|
||||
muAllSignatureAlgorithm.RUnlock()
|
||||
return v, ok
|
||||
if v, ok := lookupAlgorithm(algKindSignature, name); ok {
|
||||
return v.(SignatureAlgorithm), true
|
||||
}
|
||||
var zero SignatureAlgorithm
|
||||
return zero, false
|
||||
}
|
||||
|
||||
// RegisterSignatureAlgorithm registers a new SignatureAlgorithm. The signature value must be immutable
|
||||
// and safe to be used by multiple goroutines, as it is going to be shared with all other users of this library.
|
||||
//
|
||||
// Registration is process-global. Built-in identifiers such as RS256 are
|
||||
// reserved and cannot be replaced by callers after init has completed; use a
|
||||
// distinct name for third-party algorithms.
|
||||
//
|
||||
// SignatureAlgorithm, KeyEncryptionAlgorithm, and ContentEncryptionAlgorithm
|
||||
// share a single algorithm-name namespace so that KeyAlgorithmFrom can
|
||||
// resolve unambiguously. Registering a name that is already registered as a
|
||||
// different kind is a silent no-op (the first Register* call wins).
|
||||
func RegisterSignatureAlgorithm(algorithms ...SignatureAlgorithm) {
|
||||
muAllSignatureAlgorithm.Lock()
|
||||
for _, alg := range algorithms {
|
||||
allSignatureAlgorithm[alg.String()] = alg
|
||||
registerAlgorithm(algKindSignature, alg)
|
||||
}
|
||||
muAllSignatureAlgorithm.Unlock()
|
||||
rebuildSignatureAlgorithm()
|
||||
}
|
||||
|
||||
// UnregisterSignatureAlgorithm unregisters a SignatureAlgorithm from its known database.
|
||||
// Non-existent entries, as well as built-in algorithms will silently be ignored.
|
||||
func UnregisterSignatureAlgorithm(algorithms ...SignatureAlgorithm) {
|
||||
muAllSignatureAlgorithm.Lock()
|
||||
for _, alg := range algorithms {
|
||||
if _, ok := builtinSignatureAlgorithm[alg.String()]; ok {
|
||||
continue
|
||||
}
|
||||
delete(allSignatureAlgorithm, alg.String())
|
||||
unregisterAlgorithm(algKindSignature, alg.String())
|
||||
}
|
||||
muAllSignatureAlgorithm.Unlock()
|
||||
rebuildSignatureAlgorithm()
|
||||
}
|
||||
|
||||
func rebuildSignatureAlgorithm() {
|
||||
list := make([]SignatureAlgorithm, 0, len(allSignatureAlgorithm))
|
||||
muAllSignatureAlgorithm.RLock()
|
||||
for _, v := range allSignatureAlgorithm {
|
||||
list = append(list, v)
|
||||
}
|
||||
muAllSignatureAlgorithm.RUnlock()
|
||||
sort.Slice(list, func(i, j int) bool {
|
||||
return list[i].String() < list[j].String()
|
||||
})
|
||||
muListSignatureAlgorithm.Lock()
|
||||
listSignatureAlgorithm = list
|
||||
muListSignatureAlgorithm.Unlock()
|
||||
}
|
||||
|
||||
// SignatureAlgorithms returns a list of all available values for SignatureAlgorithm.
|
||||
func SignatureAlgorithms() []SignatureAlgorithm {
|
||||
muListSignatureAlgorithm.RLock()
|
||||
defer muListSignatureAlgorithm.RUnlock()
|
||||
return listSignatureAlgorithm
|
||||
raw := listAlgorithmsByKind(algKindSignature)
|
||||
out := make([]SignatureAlgorithm, len(raw))
|
||||
for i, alg := range raw {
|
||||
out[i] = alg.(SignatureAlgorithm)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// MarshalJSON serializes the SignatureAlgorithm object to a JSON string.
|
||||
|
||||
58
vendor/github.com/lestrrat-go/jwx/v3/jwe/README.md
generated
vendored
58
vendor/github.com/lestrrat-go/jwx/v3/jwe/README.md
generated
vendored
@@ -12,38 +12,38 @@ Examples are located in the examples directory ([jwe_example_test.go](../example
|
||||
|
||||
Supported key encryption algorithm:
|
||||
|
||||
| Algorithm | Supported? | Constant in [jwa](../jwa) |
|
||||
|:-----------------------------------------|:-----------|:-------------------------|
|
||||
| RSA-PKCS1v1.5 | YES | jwa.RSA1_5 |
|
||||
| RSA-OAEP-SHA1 | YES | jwa.RSA_OAEP |
|
||||
| RSA-OAEP-SHA256 | YES | jwa.RSA_OAEP_256 |
|
||||
| AES key wrap (128) | YES | jwa.A128KW |
|
||||
| AES key wrap (192) | YES | jwa.A192KW |
|
||||
| AES key wrap (256) | YES | jwa.A256KW |
|
||||
| Direct encryption | YES (1) | jwa.DIRECT |
|
||||
| ECDH-ES | YES (1) | jwa.ECDH_ES |
|
||||
| ECDH-ES + AES key wrap (128) | YES | jwa.ECDH_ES_A128KW |
|
||||
| ECDH-ES + AES key wrap (192) | YES | jwa.ECDH_ES_A192KW |
|
||||
| ECDH-ES + AES key wrap (256) | YES | jwa.ECDH_ES_A256KW |
|
||||
| AES-GCM key wrap (128) | YES | jwa.A128GCMKW |
|
||||
| AES-GCM key wrap (192) | YES | jwa.A192GCMKW |
|
||||
| AES-GCM key wrap (256) | YES | jwa.A256GCMKW |
|
||||
| PBES2 + HMAC-SHA256 + AES key wrap (128) | YES | jwa.PBES2_HS256_A128KW |
|
||||
| PBES2 + HMAC-SHA384 + AES key wrap (192) | YES | jwa.PBES2_HS384_A192KW |
|
||||
| PBES2 + HMAC-SHA512 + AES key wrap (256) | YES | jwa.PBES2_HS512_A256KW |
|
||||
| Algorithm | Supported? | Constant in [jwa](../jwa) | Note |
|
||||
|:-----------------------------------------|:-----------|:-------------------------|:-----|
|
||||
| RSA-PKCS1v1.5 | YES | jwa.RSA1_5 | Legacy interop only; prefer RSA-OAEP for new code |
|
||||
| RSA-OAEP-SHA1 | YES | jwa.RSA_OAEP | |
|
||||
| RSA-OAEP-SHA256 | YES | jwa.RSA_OAEP_256 | |
|
||||
| AES key wrap (128) | YES | jwa.A128KW | |
|
||||
| AES key wrap (192) | YES | jwa.A192KW | |
|
||||
| AES key wrap (256) | YES | jwa.A256KW | |
|
||||
| Direct encryption | YES (1) | jwa.DIRECT | |
|
||||
| ECDH-ES | YES (1) | jwa.ECDH_ES | |
|
||||
| ECDH-ES + AES key wrap (128) | YES | jwa.ECDH_ES_A128KW | |
|
||||
| ECDH-ES + AES key wrap (192) | YES | jwa.ECDH_ES_A192KW | |
|
||||
| ECDH-ES + AES key wrap (256) | YES | jwa.ECDH_ES_A256KW | |
|
||||
| AES-GCM key wrap (128) | YES | jwa.A128GCMKW | |
|
||||
| AES-GCM key wrap (192) | YES | jwa.A192GCMKW | |
|
||||
| AES-GCM key wrap (256) | YES | jwa.A256GCMKW | |
|
||||
| PBES2 + HMAC-SHA256 + AES key wrap (128) | YES | jwa.PBES2_HS256_A128KW | |
|
||||
| PBES2 + HMAC-SHA384 + AES key wrap (192) | YES | jwa.PBES2_HS384_A192KW | |
|
||||
| PBES2 + HMAC-SHA512 + AES key wrap (256) | YES | jwa.PBES2_HS512_A256KW | |
|
||||
|
||||
* Note 1: Single-recipient only
|
||||
|
||||
Supported content encryption algorithm:
|
||||
|
||||
| Algorithm | Supported? | Constant in [jwa](../jwa) |
|
||||
|:----------------------------|:-----------|:--------------------------|
|
||||
| AES-CBC + HMAC-SHA256 (128) | YES | jwa.A128CBC_HS256 |
|
||||
| AES-CBC + HMAC-SHA384 (192) | YES | jwa.A192CBC_HS384 |
|
||||
| AES-CBC + HMAC-SHA512 (256) | YES | jwa.A256CBC_HS512 |
|
||||
| AES-GCM (128) | YES | jwa.A128GCM |
|
||||
| AES-GCM (192) | YES | jwa.A192GCM |
|
||||
| AES-GCM (256) | YES | jwa.A256GCM |
|
||||
| Algorithm | Supported? | Constant in [jwa](../jwa) | Required CEK length |
|
||||
|:----------------------------|:-----------|:--------------------------|:--------------------|
|
||||
| AES-CBC + HMAC-SHA256 (128) | YES | jwa.A128CBC_HS256 | 32 bytes |
|
||||
| AES-CBC + HMAC-SHA384 (192) | YES | jwa.A192CBC_HS384 | 48 bytes |
|
||||
| AES-CBC + HMAC-SHA512 (256) | YES | jwa.A256CBC_HS512 | 64 bytes |
|
||||
| AES-GCM (128) | YES | jwa.A128GCM | 16 bytes |
|
||||
| AES-GCM (192) | YES | jwa.A192GCM | 24 bytes |
|
||||
| AES-GCM (256) | YES | jwa.A256GCM | 32 bytes |
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
@@ -59,7 +59,7 @@ func ExampleEncrypt() {
|
||||
|
||||
payload := []byte("Lorem Ipsum")
|
||||
|
||||
encrypted, err := jwe.Encrypt(payload, jwe.WithKey(jwa.RSA1_5, &privkey.PublicKey), jwe.WithContentEncryption(jwa.A128CBC_HS256))
|
||||
encrypted, err := jwe.Encrypt(payload, jwe.WithKey(jwa.RSA_OAEP, &privkey.PublicKey), jwe.WithContentEncryption(jwa.A128CBC_HS256))
|
||||
if err != nil {
|
||||
log.Printf("failed to encrypt payload: %s", err)
|
||||
return
|
||||
@@ -79,7 +79,7 @@ func ExampleDecrypt() {
|
||||
return
|
||||
}
|
||||
|
||||
decrypted, err := jwe.Decrypt(encrypted, jwe.WithKey(jwa.RSA1_5, privkey))
|
||||
decrypted, err := jwe.Decrypt(encrypted, jwe.WithKey(jwa.RSA_OAEP, privkey))
|
||||
if err != nil {
|
||||
log.Printf("failed to decrypt: %s", err)
|
||||
return
|
||||
|
||||
5
vendor/github.com/lestrrat-go/jwx/v3/jwe/compress.go
generated
vendored
5
vendor/github.com/lestrrat-go/jwx/v3/jwe/compress.go
generated
vendored
@@ -19,7 +19,7 @@ func uncompress(src []byte, maxBufferSize int64) ([]byte, error) {
|
||||
n, readErr := r.Read(buf[:])
|
||||
sofar += int64(n)
|
||||
if sofar > maxBufferSize {
|
||||
return nil, fmt.Errorf(`compressed payload exceeds maximum allowed size`)
|
||||
return nil, fmt.Errorf(`decompressed payload exceeds WithMaxDecompressBufferSize=%d (saw %d bytes after a %d-byte read)`, maxBufferSize, sofar, n)
|
||||
}
|
||||
if readErr != nil {
|
||||
// if we have a read error, and it's not EOF, then we need to stop
|
||||
@@ -56,7 +56,6 @@ func compress(plaintext []byte) ([]byte, error) {
|
||||
return nil, fmt.Errorf(`failed to close compression writer: %w`, err)
|
||||
}
|
||||
|
||||
ret := make([]byte, buf.Len())
|
||||
copy(ret, buf.Bytes())
|
||||
ret := bytes.Clone(buf.Bytes())
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
53
vendor/github.com/lestrrat-go/jwx/v3/jwe/decrypt.go
generated
vendored
53
vendor/github.com/lestrrat-go/jwx/v3/jwe/decrypt.go
generated
vendored
@@ -3,7 +3,6 @@ package jwe
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/lestrrat-go/jwx/v3/internal/tokens"
|
||||
"github.com/lestrrat-go/jwx/v3/jwa"
|
||||
"github.com/lestrrat-go/jwx/v3/jwe/internal/content_crypt"
|
||||
"github.com/lestrrat-go/jwx/v3/jwe/jwebb"
|
||||
@@ -140,10 +139,11 @@ func (d *decrypter) Decrypt(recipient Recipient, ciphertext []byte, msg *Message
|
||||
return
|
||||
}
|
||||
|
||||
computedAad := d.computedAad
|
||||
if d.aad != nil {
|
||||
computedAad = append(append(computedAad, tokens.Period), d.aad...)
|
||||
}
|
||||
// When an external aad is present we must NOT append into
|
||||
// d.computedAad's backing array: it aliases msg.rawProtectedHeaders
|
||||
// in the caller, and appending would mutate bytes past its length
|
||||
// in storage still referenced by the Message.
|
||||
computedAad := concatAAD(d.computedAad, d.aad)
|
||||
|
||||
plaintext, err = cipher.Decrypt(cek, d.iv, ciphertext, d.tag, computedAad)
|
||||
if err != nil {
|
||||
@@ -158,40 +158,43 @@ func (d *decrypter) Decrypt(recipient Recipient, ciphertext []byte, msg *Message
|
||||
}
|
||||
|
||||
func (d *decrypter) DecryptKey(recipient Recipient, msg *Message) (cek []byte, err error) {
|
||||
keyalgStr := d.keyalg.String()
|
||||
ctalgStr := d.ctalg.String()
|
||||
|
||||
recipientKey := recipient.EncryptedKey()
|
||||
if kd, ok := d.privkey.(KeyDecrypter); ok {
|
||||
return kd.DecryptKey(d.keyalg, recipientKey, recipient, msg)
|
||||
}
|
||||
|
||||
if jwebb.IsDirect(d.keyalg.String()) {
|
||||
if jwebb.IsDirect(keyalgStr) {
|
||||
cek, ok := d.privkey.([]byte)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("decrypt key: []byte is required as the key for %s (got %T)", d.keyalg, d.privkey)
|
||||
return nil, fmt.Errorf("decrypt key: []byte is required as the key for %s (got %T)", keyalgStr, d.privkey)
|
||||
}
|
||||
return jwebb.KeyDecryptDirect(recipientKey, recipientKey, d.keyalg.String(), cek)
|
||||
return jwebb.KeyDecryptDirect(recipientKey, recipientKey, keyalgStr, cek)
|
||||
}
|
||||
|
||||
if jwebb.IsPBES2(d.keyalg.String()) {
|
||||
if jwebb.IsPBES2(keyalgStr) {
|
||||
password, ok := d.privkey.([]byte)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("decrypt key: []byte is required as the password for %s (got %T)", d.keyalg, d.privkey)
|
||||
return nil, fmt.Errorf("decrypt key: []byte is required as the password for %s (got %T)", keyalgStr, d.privkey)
|
||||
}
|
||||
salt := []byte(d.keyalg.String())
|
||||
salt := []byte(keyalgStr)
|
||||
salt = append(salt, byte(0))
|
||||
salt = append(salt, d.keysalt...)
|
||||
return jwebb.KeyDecryptPBES2(recipientKey, recipientKey, d.keyalg.String(), password, salt, d.keycount)
|
||||
return jwebb.KeyDecryptPBES2(recipientKey, recipientKey, keyalgStr, password, salt, d.keycount)
|
||||
}
|
||||
|
||||
if jwebb.IsAESGCMKW(d.keyalg.String()) {
|
||||
if jwebb.IsAESGCMKW(keyalgStr) {
|
||||
sharedkey, ok := d.privkey.([]byte)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("decrypt key: []byte is required as the key for %s (got %T)", d.keyalg, d.privkey)
|
||||
return nil, fmt.Errorf("decrypt key: []byte is required as the key for %s (got %T)", keyalgStr, d.privkey)
|
||||
}
|
||||
return jwebb.KeyDecryptAESGCMKW(recipientKey, recipientKey, d.keyalg.String(), sharedkey, d.keyiv, d.keytag)
|
||||
return jwebb.KeyDecryptAESGCMKW(recipientKey, recipientKey, keyalgStr, sharedkey, d.keyiv, d.keytag)
|
||||
}
|
||||
|
||||
if jwebb.IsECDHES(d.keyalg.String()) {
|
||||
alg, keysize, keywrap, err := jwebb.KeyEncryptionECDHESKeySize(d.keyalg.String(), d.ctalg.String())
|
||||
if jwebb.IsECDHES(keyalgStr) {
|
||||
alg, keysize, keywrap, err := jwebb.KeyEncryptionECDHESKeySize(keyalgStr, ctalgStr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to determine ECDH-ES key size: %w`, err)
|
||||
}
|
||||
@@ -199,10 +202,10 @@ func (d *decrypter) DecryptKey(recipient Recipient, msg *Message) (cek []byte, e
|
||||
if !keywrap {
|
||||
return jwebb.KeyDecryptECDHES(recipientKey, cek, alg, d.apu, d.apv, d.privkey, d.pubkey, keysize)
|
||||
}
|
||||
return jwebb.KeyDecryptECDHESKeyWrap(recipientKey, recipientKey, d.keyalg.String(), d.apu, d.apv, d.privkey, d.pubkey, keysize)
|
||||
return jwebb.KeyDecryptECDHESKeyWrap(recipientKey, recipientKey, keyalgStr, d.apu, d.apv, d.privkey, d.pubkey, keysize)
|
||||
}
|
||||
|
||||
if jwebb.IsRSA15(d.keyalg.String()) {
|
||||
if jwebb.IsRSA15(keyalgStr) {
|
||||
cipher, err := d.ContentCipher()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to fetch content crypt cipher: %w`, err)
|
||||
@@ -211,17 +214,17 @@ func (d *decrypter) DecryptKey(recipient Recipient, msg *Message) (cek []byte, e
|
||||
return jwebb.KeyDecryptRSA15(recipientKey, recipientKey, d.privkey, keysize)
|
||||
}
|
||||
|
||||
if jwebb.IsRSAOAEP(d.keyalg.String()) {
|
||||
return jwebb.KeyDecryptRSAOAEP(recipientKey, recipientKey, d.keyalg.String(), d.privkey)
|
||||
if jwebb.IsRSAOAEP(keyalgStr) {
|
||||
return jwebb.KeyDecryptRSAOAEP(recipientKey, recipientKey, keyalgStr, d.privkey)
|
||||
}
|
||||
|
||||
if jwebb.IsAESKW(d.keyalg.String()) {
|
||||
if jwebb.IsAESKW(keyalgStr) {
|
||||
sharedkey, ok := d.privkey.([]byte)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("[]byte is required as the key to decrypt %s", d.keyalg.String())
|
||||
return nil, fmt.Errorf("[]byte is required as the key to decrypt %s", keyalgStr)
|
||||
}
|
||||
return jwebb.KeyDecryptAESKW(recipientKey, recipientKey, d.keyalg.String(), sharedkey)
|
||||
return jwebb.KeyDecryptAESKW(recipientKey, recipientKey, keyalgStr, sharedkey)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf(`unsupported algorithm for key decryption (%s)`, d.keyalg)
|
||||
return nil, fmt.Errorf(`unsupported algorithm for key decryption (%s)`, keyalgStr)
|
||||
}
|
||||
|
||||
148
vendor/github.com/lestrrat-go/jwx/v3/jwe/encrypt.go
generated
vendored
148
vendor/github.com/lestrrat-go/jwx/v3/jwe/encrypt.go
generated
vendored
@@ -8,9 +8,9 @@ import (
|
||||
|
||||
"github.com/lestrrat-go/jwx/v3/internal/keyconv"
|
||||
"github.com/lestrrat-go/jwx/v3/jwa"
|
||||
"github.com/lestrrat-go/jwx/v3/jwe/internal/content_crypt"
|
||||
"github.com/lestrrat-go/jwx/v3/jwe/internal/keygen"
|
||||
"github.com/lestrrat-go/jwx/v3/jwe/jwebb"
|
||||
"github.com/lestrrat-go/jwx/v3/jwk"
|
||||
)
|
||||
|
||||
// encrypter is responsible for taking various components to encrypt a key.
|
||||
@@ -18,13 +18,13 @@ import (
|
||||
//
|
||||
//nolint:govet
|
||||
type encrypter struct {
|
||||
apu []byte
|
||||
apv []byte
|
||||
ctalg jwa.ContentEncryptionAlgorithm
|
||||
keyalg jwa.KeyEncryptionAlgorithm
|
||||
pubkey any
|
||||
rawKey any
|
||||
cipher content_crypt.Cipher
|
||||
apu []byte
|
||||
apv []byte
|
||||
ctalg jwa.ContentEncryptionAlgorithm
|
||||
keyalg jwa.KeyEncryptionAlgorithm
|
||||
pubkey any
|
||||
rawKey any
|
||||
pbes2Count int
|
||||
}
|
||||
|
||||
// newEncrypter creates a new Encrypter instance with all required parameters.
|
||||
@@ -34,24 +34,22 @@ type encrypter struct {
|
||||
// *rsa.PublicKey, instead of jwk.Key)
|
||||
//
|
||||
// You should consider this object immutable once created.
|
||||
func newEncrypter(keyalg jwa.KeyEncryptionAlgorithm, ctalg jwa.ContentEncryptionAlgorithm, pubkey any, rawKey any, apu, apv []byte) (*encrypter, error) {
|
||||
cipher, err := jwebb.CreateContentCipher(ctalg.String())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to create content cipher: %w`, err)
|
||||
}
|
||||
|
||||
func newEncrypter(keyalg jwa.KeyEncryptionAlgorithm, ctalg jwa.ContentEncryptionAlgorithm, pubkey any, rawKey any, apu, apv []byte, pbes2Count int) *encrypter {
|
||||
return &encrypter{
|
||||
apu: apu,
|
||||
apv: apv,
|
||||
ctalg: ctalg,
|
||||
keyalg: keyalg,
|
||||
pubkey: pubkey,
|
||||
rawKey: rawKey,
|
||||
cipher: cipher,
|
||||
}, nil
|
||||
apu: apu,
|
||||
apv: apv,
|
||||
ctalg: ctalg,
|
||||
keyalg: keyalg,
|
||||
pubkey: pubkey,
|
||||
rawKey: rawKey,
|
||||
pbes2Count: pbes2Count,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *encrypter) EncryptKey(cek []byte) (keygen.ByteSource, error) {
|
||||
keyalgStr := e.keyalg.String()
|
||||
ctalgStr := e.ctalg.String()
|
||||
|
||||
if ke, ok := e.pubkey.(KeyEncrypter); ok {
|
||||
encrypted, err := ke.EncryptKey(cek)
|
||||
if err != nil {
|
||||
@@ -60,32 +58,32 @@ func (e *encrypter) EncryptKey(cek []byte) (keygen.ByteSource, error) {
|
||||
return keygen.ByteKey(encrypted), nil
|
||||
}
|
||||
|
||||
if jwebb.IsDirect(e.keyalg.String()) {
|
||||
if jwebb.IsDirect(keyalgStr) {
|
||||
sharedkey, ok := e.rawKey.([]byte)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("encrypt key: []byte is required as the key for %s (got %T)", e.keyalg, e.rawKey)
|
||||
return nil, fmt.Errorf("encrypt key: []byte is required as the key for %s (got %T)", keyalgStr, e.rawKey)
|
||||
}
|
||||
return jwebb.KeyEncryptDirect(cek, e.keyalg.String(), sharedkey)
|
||||
return jwebb.KeyEncryptDirect(cek, keyalgStr, sharedkey)
|
||||
}
|
||||
|
||||
if jwebb.IsPBES2(e.keyalg.String()) {
|
||||
if jwebb.IsPBES2(keyalgStr) {
|
||||
password, ok := e.rawKey.([]byte)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("encrypt key: []byte is required as the password for %s (got %T)", e.keyalg, e.rawKey)
|
||||
return nil, fmt.Errorf("encrypt key: []byte is required as the password for %s (got %T)", keyalgStr, e.rawKey)
|
||||
}
|
||||
return jwebb.KeyEncryptPBES2(cek, e.keyalg.String(), password)
|
||||
return jwebb.KeyEncryptPBES2(cek, keyalgStr, password, e.pbes2Count)
|
||||
}
|
||||
|
||||
if jwebb.IsAESGCMKW(e.keyalg.String()) {
|
||||
if jwebb.IsAESGCMKW(keyalgStr) {
|
||||
sharedkey, ok := e.rawKey.([]byte)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("encrypt key: []byte is required as the key for %s (got %T)", e.keyalg, e.rawKey)
|
||||
return nil, fmt.Errorf("encrypt key: []byte is required as the key for %s (got %T)", keyalgStr, e.rawKey)
|
||||
}
|
||||
return jwebb.KeyEncryptAESGCMKW(cek, e.keyalg.String(), sharedkey)
|
||||
return jwebb.KeyEncryptAESGCMKW(cek, keyalgStr, sharedkey)
|
||||
}
|
||||
|
||||
if jwebb.IsECDHES(e.keyalg.String()) {
|
||||
_, keysize, keywrap, err := jwebb.KeyEncryptionECDHESKeySize(e.keyalg.String(), e.ctalg.String())
|
||||
if jwebb.IsECDHES(keyalgStr) {
|
||||
_, keysize, keywrap, err := jwebb.KeyEncryptionECDHESKeySize(keyalgStr, ctalgStr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to determine ECDH-ES key size: %w`, err)
|
||||
}
|
||||
@@ -120,9 +118,9 @@ func (e *encrypter) EncryptKey(cek []byte) (keygen.ByteSource, error) {
|
||||
case *ecdh.PublicKey:
|
||||
if key.Curve() == ecdh.X25519() {
|
||||
if !keywrap {
|
||||
return jwebb.KeyEncryptECDHESX25519(cek, e.keyalg.String(), e.apu, e.apv, key, keysize, e.ctalg.String())
|
||||
return jwebb.KeyEncryptECDHESX25519(cek, keyalgStr, e.apu, e.apv, key, keysize, ctalgStr)
|
||||
}
|
||||
return jwebb.KeyEncryptECDHESKeyWrapX25519(cek, e.keyalg.String(), e.apu, e.apv, key, keysize, e.ctalg.String())
|
||||
return jwebb.KeyEncryptECDHESKeyWrapX25519(cek, keyalgStr, e.apu, e.apv, key, keysize, ctalgStr)
|
||||
}
|
||||
|
||||
var ecdsaKey *ecdsa.PublicKey
|
||||
@@ -135,15 +133,15 @@ func (e *encrypter) EncryptKey(cek []byte) (keygen.ByteSource, error) {
|
||||
switch key := keyToUse.(type) {
|
||||
case *ecdsa.PublicKey:
|
||||
if !keywrap {
|
||||
return jwebb.KeyEncryptECDHESECDSA(cek, e.keyalg.String(), e.apu, e.apv, key, keysize, e.ctalg.String())
|
||||
return jwebb.KeyEncryptECDHESECDSA(cek, keyalgStr, e.apu, e.apv, key, keysize, ctalgStr)
|
||||
}
|
||||
return jwebb.KeyEncryptECDHESKeyWrapECDSA(cek, e.keyalg.String(), e.apu, e.apv, key, keysize, e.ctalg.String())
|
||||
return jwebb.KeyEncryptECDHESKeyWrapECDSA(cek, keyalgStr, e.apu, e.apv, key, keysize, ctalgStr)
|
||||
default:
|
||||
return nil, fmt.Errorf(`encrypt: unsupported key type for ECDH-ES: %T`, keyToUse)
|
||||
}
|
||||
}
|
||||
|
||||
if jwebb.IsRSA15(e.keyalg.String()) {
|
||||
if jwebb.IsRSA15(keyalgStr) {
|
||||
keyToUse := e.rawKey
|
||||
if keyToUse == nil {
|
||||
keyToUse = e.pubkey
|
||||
@@ -159,10 +157,10 @@ func (e *encrypter) EncryptKey(cek []byte) (keygen.ByteSource, error) {
|
||||
return nil, fmt.Errorf(`encrypt: failed to convert to RSA public key: %w`, err)
|
||||
}
|
||||
|
||||
return jwebb.KeyEncryptRSA15(cek, e.keyalg.String(), pubkey)
|
||||
return jwebb.KeyEncryptRSA15(cek, keyalgStr, pubkey)
|
||||
}
|
||||
|
||||
if jwebb.IsRSAOAEP(e.keyalg.String()) {
|
||||
if jwebb.IsRSAOAEP(keyalgStr) {
|
||||
keyToUse := e.rawKey
|
||||
if keyToUse == nil {
|
||||
keyToUse = e.pubkey
|
||||
@@ -178,16 +176,76 @@ func (e *encrypter) EncryptKey(cek []byte) (keygen.ByteSource, error) {
|
||||
return nil, fmt.Errorf(`encrypt: failed to convert to RSA public key: %w`, err)
|
||||
}
|
||||
|
||||
return jwebb.KeyEncryptRSAOAEP(cek, e.keyalg.String(), pubkey)
|
||||
return jwebb.KeyEncryptRSAOAEP(cek, keyalgStr, pubkey)
|
||||
}
|
||||
|
||||
if jwebb.IsAESKW(e.keyalg.String()) {
|
||||
if jwebb.IsAESKW(keyalgStr) {
|
||||
sharedkey, ok := e.rawKey.([]byte)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("[]byte is required as the key to encrypt %s", e.keyalg.String())
|
||||
return nil, fmt.Errorf("[]byte is required as the key to encrypt %s", keyalgStr)
|
||||
}
|
||||
return jwebb.KeyEncryptAESKW(cek, e.keyalg.String(), sharedkey)
|
||||
return jwebb.KeyEncryptAESKW(cek, keyalgStr, sharedkey)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf(`unsupported algorithm for key encryption (%s)`, e.keyalg)
|
||||
return nil, fmt.Errorf(`unsupported algorithm for key encryption (%s)`, keyalgStr)
|
||||
}
|
||||
|
||||
// validateAlgorithmForKey checks that alg is family-compatible with
|
||||
// key at the WithKey option boundary, surfacing wrong-shape mismatches
|
||||
// as crisp `jwe.WithKey: ...` errors instead of nested errors deep in
|
||||
// the dispatcher (e.g. `[]byte is required as the key to encrypt ...`
|
||||
// from inside the AESKW path).
|
||||
//
|
||||
// Permissive carve-outs (return nil, deferring validation):
|
||||
//
|
||||
// - jwk.Key wrappers: kty-vs-alg check happens at jwk.Export time.
|
||||
// - Caller-supplied KeyEncrypter / KeyDecrypter implementations:
|
||||
// the caller takes responsibility for the key-shape contract.
|
||||
// - Nil key: legitimate for `dir` (caller provides CEK separately).
|
||||
//
|
||||
// All other built-in algorithm families enforce a concrete key-shape
|
||||
// expectation here. The error is wrapped by the WithKey site so the
|
||||
// caller sees `jwe.WithKey: ...` consistently.
|
||||
func validateAlgorithmForKey(alg jwa.KeyEncryptionAlgorithm, key any) error {
|
||||
if key == nil {
|
||||
return nil
|
||||
}
|
||||
if _, ok := key.(jwk.Key); ok {
|
||||
return nil
|
||||
}
|
||||
if _, ok := key.(KeyEncrypter); ok {
|
||||
return nil
|
||||
}
|
||||
if _, ok := key.(KeyDecrypter); ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
algStr := alg.String()
|
||||
switch {
|
||||
case jwebb.IsDirect(algStr):
|
||||
if _, ok := key.([]byte); !ok {
|
||||
return fmt.Errorf(`algorithm %q requires a []byte key (got %T)`, algStr, key)
|
||||
}
|
||||
case jwebb.IsAESKW(algStr) || jwebb.IsAESGCMKW(algStr) || jwebb.IsPBES2(algStr):
|
||||
if _, ok := key.([]byte); !ok {
|
||||
return fmt.Errorf(`algorithm %q requires a []byte key (got %T)`, algStr, key)
|
||||
}
|
||||
case jwebb.IsRSA15(algStr) || jwebb.IsRSAOAEP(algStr):
|
||||
switch key.(type) {
|
||||
case *rsa.PublicKey, rsa.PublicKey, *rsa.PrivateKey, rsa.PrivateKey:
|
||||
default:
|
||||
return fmt.Errorf(`algorithm %q requires an RSA key (got %T)`, algStr, key)
|
||||
}
|
||||
case jwebb.IsECDHES(algStr):
|
||||
switch key.(type) {
|
||||
case *ecdsa.PublicKey, ecdsa.PublicKey, *ecdsa.PrivateKey, ecdsa.PrivateKey,
|
||||
*ecdh.PublicKey, ecdh.PublicKey, *ecdh.PrivateKey, ecdh.PrivateKey:
|
||||
default:
|
||||
return fmt.Errorf(`algorithm %q requires an ECDSA or ECDH key (got %T)`, algStr, key)
|
||||
}
|
||||
default:
|
||||
// Unknown algorithm family: defer to dispatch.
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
21
vendor/github.com/lestrrat-go/jwx/v3/jwe/errors.go
generated
vendored
21
vendor/github.com/lestrrat-go/jwx/v3/jwe/errors.go
generated
vendored
@@ -1,6 +1,9 @@
|
||||
package jwe
|
||||
|
||||
import "errors"
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type encryptError struct {
|
||||
error
|
||||
@@ -22,6 +25,10 @@ func EncryptError() error {
|
||||
return errDefaultEncryptError
|
||||
}
|
||||
|
||||
func makeEncryptError(prefix string, f string, args ...any) error {
|
||||
return encryptError{fmt.Errorf(prefix+": "+f, args...)}
|
||||
}
|
||||
|
||||
type decryptError struct {
|
||||
error
|
||||
}
|
||||
@@ -42,6 +49,10 @@ func DecryptError() error {
|
||||
return errDefaultDecryptError
|
||||
}
|
||||
|
||||
func makeDecryptError(f string, args ...any) error {
|
||||
return decryptError{fmt.Errorf("jwe.Decrypt: "+f, args...)}
|
||||
}
|
||||
|
||||
type recipientError struct {
|
||||
error
|
||||
}
|
||||
@@ -68,6 +79,10 @@ func RecipientError() error {
|
||||
return errDefaultRecipientError
|
||||
}
|
||||
|
||||
func makeRecipientError(err error) error {
|
||||
return recipientError{err}
|
||||
}
|
||||
|
||||
type parseError struct {
|
||||
error
|
||||
}
|
||||
@@ -88,3 +103,7 @@ var errDefaultParseError = parseError{errors.New(`parse error`)}
|
||||
func ParseError() error {
|
||||
return errDefaultParseError
|
||||
}
|
||||
|
||||
func makeParseError(prefix string, f string, args ...any) error {
|
||||
return parseError{fmt.Errorf(prefix+": "+f, args...)}
|
||||
}
|
||||
|
||||
244
vendor/github.com/lestrrat-go/jwx/v3/jwe/headers_gen.go
generated
vendored
244
vendor/github.com/lestrrat-go/jwx/v3/jwe/headers_gen.go
generated
vendored
@@ -108,12 +108,11 @@ type stdHeaders struct {
|
||||
x509CertThumbprintS256 *string
|
||||
x509URL *string
|
||||
privateParams map[string]any
|
||||
mu *sync.RWMutex
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
func NewHeaders() Headers {
|
||||
return &stdHeaders{
|
||||
mu: &sync.RWMutex{},
|
||||
privateParams: map[string]any{},
|
||||
}
|
||||
}
|
||||
@@ -430,13 +429,23 @@ func (h *stdHeaders) setNoLock(name string, value any) error {
|
||||
switch name {
|
||||
case AgreementPartyUInfoKey:
|
||||
if v, ok := value.([]byte); ok {
|
||||
h.agreementPartyUInfo = v
|
||||
if v == nil {
|
||||
h.agreementPartyUInfo = nil
|
||||
} else {
|
||||
h.agreementPartyUInfo = make([]byte, len(v))
|
||||
copy(h.agreementPartyUInfo, v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf(`invalid value for %s key: %T`, AgreementPartyUInfoKey, value)
|
||||
case AgreementPartyVInfoKey:
|
||||
if v, ok := value.([]byte); ok {
|
||||
h.agreementPartyVInfo = v
|
||||
if v == nil {
|
||||
h.agreementPartyVInfo = nil
|
||||
} else {
|
||||
h.agreementPartyVInfo = make([]byte, len(v))
|
||||
copy(h.agreementPartyVInfo, v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf(`invalid value for %s key: %T`, AgreementPartyVInfoKey, value)
|
||||
@@ -469,7 +478,12 @@ func (h *stdHeaders) setNoLock(name string, value any) error {
|
||||
return fmt.Errorf(`invalid value for %s key: %T`, ContentTypeKey, value)
|
||||
case CriticalKey:
|
||||
if v, ok := value.([]string); ok {
|
||||
h.critical = v
|
||||
if v == nil {
|
||||
h.critical = nil
|
||||
} else {
|
||||
h.critical = make([]string, len(v))
|
||||
copy(h.critical, v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf(`invalid value for %s key: %T`, CriticalKey, value)
|
||||
@@ -579,6 +593,8 @@ func (h *stdHeaders) Remove(key string) error {
|
||||
}
|
||||
|
||||
func (h *stdHeaders) UnmarshalJSON(buf []byte) error {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
h.agreementPartyUInfo = nil
|
||||
h.agreementPartyVInfo = nil
|
||||
h.algorithm = nil
|
||||
@@ -640,7 +656,7 @@ LOOP:
|
||||
}
|
||||
h.contentEncryption = &decoded
|
||||
case ContentTypeKey:
|
||||
if err := json.AssignNextStringToken(&h.contentType, dec); err != nil {
|
||||
if err := json.AssignNextStringToken(&h.contentType, dec, nil); err != nil {
|
||||
return fmt.Errorf(`failed to decode value for key %s: %w`, ContentTypeKey, err)
|
||||
}
|
||||
case CriticalKey:
|
||||
@@ -670,15 +686,15 @@ LOOP:
|
||||
}
|
||||
h.jwk = key
|
||||
case JWKSetURLKey:
|
||||
if err := json.AssignNextStringToken(&h.jwkSetURL, dec); err != nil {
|
||||
if err := json.AssignNextStringToken(&h.jwkSetURL, dec, nil); err != nil {
|
||||
return fmt.Errorf(`failed to decode value for key %s: %w`, JWKSetURLKey, err)
|
||||
}
|
||||
case KeyIDKey:
|
||||
if err := json.AssignNextStringToken(&h.keyID, dec); err != nil {
|
||||
if err := json.AssignNextStringToken(&h.keyID, dec, nil); err != nil {
|
||||
return fmt.Errorf(`failed to decode value for key %s: %w`, KeyIDKey, err)
|
||||
}
|
||||
case TypeKey:
|
||||
if err := json.AssignNextStringToken(&h.typ, dec); err != nil {
|
||||
if err := json.AssignNextStringToken(&h.typ, dec, nil); err != nil {
|
||||
return fmt.Errorf(`failed to decode value for key %s: %w`, TypeKey, err)
|
||||
}
|
||||
case X509CertChainKey:
|
||||
@@ -688,15 +704,15 @@ LOOP:
|
||||
}
|
||||
h.x509CertChain = &decoded
|
||||
case X509CertThumbprintKey:
|
||||
if err := json.AssignNextStringToken(&h.x509CertThumbprint, dec); err != nil {
|
||||
if err := json.AssignNextStringToken(&h.x509CertThumbprint, dec, nil); err != nil {
|
||||
return fmt.Errorf(`failed to decode value for key %s: %w`, X509CertThumbprintKey, err)
|
||||
}
|
||||
case X509CertThumbprintS256Key:
|
||||
if err := json.AssignNextStringToken(&h.x509CertThumbprintS256, dec); err != nil {
|
||||
if err := json.AssignNextStringToken(&h.x509CertThumbprintS256, dec, nil); err != nil {
|
||||
return fmt.Errorf(`failed to decode value for key %s: %w`, X509CertThumbprintS256Key, err)
|
||||
}
|
||||
case X509URLKey:
|
||||
if err := json.AssignNextStringToken(&h.x509URL, dec); err != nil {
|
||||
if err := json.AssignNextStringToken(&h.x509URL, dec, nil); err != nil {
|
||||
return fmt.Errorf(`failed to decode value for key %s: %w`, X509URLKey, err)
|
||||
}
|
||||
default:
|
||||
@@ -771,108 +787,190 @@ func (h *stdHeaders) Keys() []string {
|
||||
return keys
|
||||
}
|
||||
|
||||
func (h stdHeaders) MarshalJSON() ([]byte, error) {
|
||||
data := make(map[string]any)
|
||||
keys := make([]string, 0, 16+len(h.privateParams))
|
||||
type headerPair struct {
|
||||
Name string
|
||||
Value any
|
||||
}
|
||||
|
||||
var headerPairPool = sync.Pool{
|
||||
New: func() any {
|
||||
return make([]headerPair, 0, 16)
|
||||
},
|
||||
}
|
||||
|
||||
func getHeaderPairList() []headerPair {
|
||||
return headerPairPool.Get().([]headerPair)
|
||||
}
|
||||
|
||||
func putHeaderPairList(list []headerPair) {
|
||||
list = list[:0]
|
||||
headerPairPool.Put(list)
|
||||
}
|
||||
|
||||
func (h *stdHeaders) makePairs() ([]headerPair, error) {
|
||||
pairs := getHeaderPairList()
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
if h.agreementPartyUInfo != nil {
|
||||
data[AgreementPartyUInfoKey] = h.agreementPartyUInfo
|
||||
keys = append(keys, AgreementPartyUInfoKey)
|
||||
v, err := json.Marshal(base64.EncodeToString(h.agreementPartyUInfo))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, AgreementPartyUInfoKey, err)
|
||||
}
|
||||
pairs = append(pairs, headerPair{Name: AgreementPartyUInfoKey, Value: v})
|
||||
}
|
||||
if h.agreementPartyVInfo != nil {
|
||||
data[AgreementPartyVInfoKey] = h.agreementPartyVInfo
|
||||
keys = append(keys, AgreementPartyVInfoKey)
|
||||
v, err := json.Marshal(base64.EncodeToString(h.agreementPartyVInfo))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, AgreementPartyVInfoKey, err)
|
||||
}
|
||||
pairs = append(pairs, headerPair{Name: AgreementPartyVInfoKey, Value: v})
|
||||
}
|
||||
if h.algorithm != nil {
|
||||
data[AlgorithmKey] = *(h.algorithm)
|
||||
keys = append(keys, AlgorithmKey)
|
||||
v, err := json.Marshal(*(h.algorithm))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, AlgorithmKey, err)
|
||||
}
|
||||
pairs = append(pairs, headerPair{Name: AlgorithmKey, Value: v})
|
||||
}
|
||||
if h.compression != nil {
|
||||
data[CompressionKey] = *(h.compression)
|
||||
keys = append(keys, CompressionKey)
|
||||
v, err := json.Marshal(*(h.compression))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, CompressionKey, err)
|
||||
}
|
||||
pairs = append(pairs, headerPair{Name: CompressionKey, Value: v})
|
||||
}
|
||||
if h.contentEncryption != nil {
|
||||
data[ContentEncryptionKey] = *(h.contentEncryption)
|
||||
keys = append(keys, ContentEncryptionKey)
|
||||
v, err := json.Marshal(*(h.contentEncryption))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, ContentEncryptionKey, err)
|
||||
}
|
||||
pairs = append(pairs, headerPair{Name: ContentEncryptionKey, Value: v})
|
||||
}
|
||||
if h.contentType != nil {
|
||||
data[ContentTypeKey] = *(h.contentType)
|
||||
keys = append(keys, ContentTypeKey)
|
||||
v, err := json.Marshal(*(h.contentType))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, ContentTypeKey, err)
|
||||
}
|
||||
pairs = append(pairs, headerPair{Name: ContentTypeKey, Value: v})
|
||||
}
|
||||
if h.critical != nil {
|
||||
data[CriticalKey] = h.critical
|
||||
keys = append(keys, CriticalKey)
|
||||
v, err := json.Marshal(h.critical)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, CriticalKey, err)
|
||||
}
|
||||
pairs = append(pairs, headerPair{Name: CriticalKey, Value: v})
|
||||
}
|
||||
if h.ephemeralPublicKey != nil {
|
||||
data[EphemeralPublicKeyKey] = h.ephemeralPublicKey
|
||||
keys = append(keys, EphemeralPublicKeyKey)
|
||||
v, err := json.Marshal(h.ephemeralPublicKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, EphemeralPublicKeyKey, err)
|
||||
}
|
||||
pairs = append(pairs, headerPair{Name: EphemeralPublicKeyKey, Value: v})
|
||||
}
|
||||
if h.jwk != nil {
|
||||
data[JWKKey] = h.jwk
|
||||
keys = append(keys, JWKKey)
|
||||
v, err := json.Marshal(h.jwk)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, JWKKey, err)
|
||||
}
|
||||
pairs = append(pairs, headerPair{Name: JWKKey, Value: v})
|
||||
}
|
||||
if h.jwkSetURL != nil {
|
||||
data[JWKSetURLKey] = *(h.jwkSetURL)
|
||||
keys = append(keys, JWKSetURLKey)
|
||||
v, err := json.Marshal(*(h.jwkSetURL))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, JWKSetURLKey, err)
|
||||
}
|
||||
pairs = append(pairs, headerPair{Name: JWKSetURLKey, Value: v})
|
||||
}
|
||||
if h.keyID != nil {
|
||||
data[KeyIDKey] = *(h.keyID)
|
||||
keys = append(keys, KeyIDKey)
|
||||
v, err := json.Marshal(*(h.keyID))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, KeyIDKey, err)
|
||||
}
|
||||
pairs = append(pairs, headerPair{Name: KeyIDKey, Value: v})
|
||||
}
|
||||
if h.typ != nil {
|
||||
data[TypeKey] = *(h.typ)
|
||||
keys = append(keys, TypeKey)
|
||||
v, err := json.Marshal(*(h.typ))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, TypeKey, err)
|
||||
}
|
||||
pairs = append(pairs, headerPair{Name: TypeKey, Value: v})
|
||||
}
|
||||
if h.x509CertChain != nil {
|
||||
data[X509CertChainKey] = h.x509CertChain
|
||||
keys = append(keys, X509CertChainKey)
|
||||
v, err := json.Marshal(h.x509CertChain)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, X509CertChainKey, err)
|
||||
}
|
||||
pairs = append(pairs, headerPair{Name: X509CertChainKey, Value: v})
|
||||
}
|
||||
if h.x509CertThumbprint != nil {
|
||||
data[X509CertThumbprintKey] = *(h.x509CertThumbprint)
|
||||
keys = append(keys, X509CertThumbprintKey)
|
||||
v, err := json.Marshal(*(h.x509CertThumbprint))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, X509CertThumbprintKey, err)
|
||||
}
|
||||
pairs = append(pairs, headerPair{Name: X509CertThumbprintKey, Value: v})
|
||||
}
|
||||
if h.x509CertThumbprintS256 != nil {
|
||||
data[X509CertThumbprintS256Key] = *(h.x509CertThumbprintS256)
|
||||
keys = append(keys, X509CertThumbprintS256Key)
|
||||
v, err := json.Marshal(*(h.x509CertThumbprintS256))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, X509CertThumbprintS256Key, err)
|
||||
}
|
||||
pairs = append(pairs, headerPair{Name: X509CertThumbprintS256Key, Value: v})
|
||||
}
|
||||
if h.x509URL != nil {
|
||||
data[X509URLKey] = *(h.x509URL)
|
||||
keys = append(keys, X509URLKey)
|
||||
v, err := json.Marshal(*(h.x509URL))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, X509URLKey, err)
|
||||
}
|
||||
pairs = append(pairs, headerPair{Name: X509URLKey, Value: v})
|
||||
}
|
||||
for k, v := range h.privateParams {
|
||||
data[k] = v
|
||||
keys = append(keys, k)
|
||||
}
|
||||
h.mu.RUnlock()
|
||||
|
||||
sort.Strings(keys)
|
||||
buf := pool.BytesBuffer().Get()
|
||||
defer pool.BytesBuffer().Put(buf)
|
||||
enc := json.NewEncoder(buf)
|
||||
buf.WriteByte(tokens.OpenCurlyBracket)
|
||||
for i, k := range keys {
|
||||
if i > 0 {
|
||||
buf.WriteRune(tokens.Comma)
|
||||
}
|
||||
buf.WriteRune(tokens.DoubleQuote)
|
||||
buf.WriteString(k)
|
||||
buf.WriteString(`":`)
|
||||
v := data[k]
|
||||
var encoded []byte
|
||||
switch v := v.(type) {
|
||||
case []byte:
|
||||
buf.WriteRune(tokens.DoubleQuote)
|
||||
buf.WriteString(base64.EncodeToString(v))
|
||||
buf.WriteRune(tokens.DoubleQuote)
|
||||
default:
|
||||
if err := enc.Encode(v); err != nil {
|
||||
return nil, fmt.Errorf(`failed to encode value for field %s`, k)
|
||||
var err error
|
||||
encoded, err = json.Marshal(base64.EncodeToString(v))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, k, err)
|
||||
}
|
||||
default:
|
||||
var err error
|
||||
encoded, err = json.Marshal(v)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, k, err)
|
||||
}
|
||||
buf.Truncate(buf.Len() - 1)
|
||||
}
|
||||
pairs = append(pairs, headerPair{Name: k, Value: encoded})
|
||||
}
|
||||
|
||||
sort.Slice(pairs, func(i, j int) bool {
|
||||
return pairs[i].Name < pairs[j].Name
|
||||
})
|
||||
|
||||
return pairs, nil
|
||||
}
|
||||
|
||||
func (h *stdHeaders) MarshalJSON() ([]byte, error) {
|
||||
buf := pool.BytesBuffer().Get()
|
||||
defer pool.BytesBuffer().Put(buf)
|
||||
pairs, err := h.makePairs()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to make pairs: %w`, err)
|
||||
}
|
||||
buf.WriteByte(tokens.OpenCurlyBracket)
|
||||
|
||||
for i, pair := range pairs {
|
||||
if i > 0 {
|
||||
buf.WriteByte(tokens.Comma)
|
||||
}
|
||||
buf.WriteByte('"')
|
||||
buf.WriteString(pair.Name)
|
||||
buf.WriteString(`": `)
|
||||
buf.Write(pair.Value.([]byte))
|
||||
}
|
||||
buf.WriteByte(tokens.CloseCurlyBracket)
|
||||
ret := make([]byte, buf.Len())
|
||||
copy(ret, buf.Bytes())
|
||||
putHeaderPairList(pairs)
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
@@ -894,6 +992,6 @@ func (h *stdHeaders) clear() {
|
||||
h.x509CertThumbprint = nil
|
||||
h.x509CertThumbprintS256 = nil
|
||||
h.x509URL = nil
|
||||
h.privateParams = map[string]any{}
|
||||
clear(h.privateParams)
|
||||
h.mu.Unlock()
|
||||
}
|
||||
|
||||
19
vendor/github.com/lestrrat-go/jwx/v3/jwe/interface.go
generated
vendored
19
vendor/github.com/lestrrat-go/jwx/v3/jwe/interface.go
generated
vendored
@@ -39,6 +39,25 @@ type KeyIDer interface {
|
||||
// expose the secret key in memory, for example, when you want to use
|
||||
// hardware security modules (HSMs) to decrypt the key.
|
||||
//
|
||||
// Library contract for implementers (read carefully):
|
||||
//
|
||||
// - The library has already verified that the wire-level `alg` is
|
||||
// consistent across the protected header and per-recipient header
|
||||
// (RFC 7516 §7.2.1 disjointness). Your DecryptKey is invoked with
|
||||
// the alg the library has decided to use for this attempt.
|
||||
// - The library has NOT validated key-shape-vs-alg compatibility for
|
||||
// your custom decrypter. You receive the raw recipient and message;
|
||||
// headers are split between protected (signed/integrity-protected)
|
||||
// and per-recipient (unprotected). If you read a value from the
|
||||
// unprotected per-recipient header for a security decision, you
|
||||
// must enforce its consistency with the protected header yourself.
|
||||
// - Returning a non-nil error short-circuits this recipient. Returning
|
||||
// nil bytes with nil error is treated as "decryption failed" by the
|
||||
// dispatcher (use a non-nil error for clarity).
|
||||
// - You are responsible for any constant-time considerations relevant
|
||||
// to your decryption primitive (e.g. RFC 3218 random-CEK fallback
|
||||
// for RSA-PKCS1v1.5; the library does this for the built-in path).
|
||||
//
|
||||
// This API is experimental and may change without notice, even
|
||||
// in minor releases.
|
||||
type KeyDecrypter interface {
|
||||
|
||||
98
vendor/github.com/lestrrat-go/jwx/v3/jwe/internal/aescbc/aescbc.go
generated
vendored
98
vendor/github.com/lestrrat-go/jwx/v3/jwe/internal/aescbc/aescbc.go
generated
vendored
@@ -10,6 +10,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash"
|
||||
"slices"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/lestrrat-go/jwx/v3/internal/pool"
|
||||
@@ -23,6 +24,12 @@ const defaultBufSize int64 = 256 * 1024 * 1024
|
||||
|
||||
var maxBufSize atomic.Int64
|
||||
|
||||
// errInvalidCiphertext is the single opaque error returned by Hmac.Open for
|
||||
// every failure mode (pre-MAC structural checks and post-MAC tag mismatch).
|
||||
// Keeping one value across all paths prevents a structural-vs-cryptographic
|
||||
// oracle on remote decrypt endpoints.
|
||||
var errInvalidCiphertext = errors.New("invalid ciphertext")
|
||||
|
||||
func init() {
|
||||
SetMaxBufferSize(defaultBufSize)
|
||||
}
|
||||
@@ -108,7 +115,7 @@ type Hmac struct {
|
||||
blockCipher cipher.Block
|
||||
hash func() hash.Hash
|
||||
keysize int
|
||||
tagsize int
|
||||
tlen int
|
||||
integrityKey []byte
|
||||
}
|
||||
|
||||
@@ -125,14 +132,23 @@ func New(key []byte, f BlockCipherFunc) (hmac *Hmac, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// Per RFC 7518 §5.2.2.1, T_LEN is the authentication tag length. For the
|
||||
// three defined AES-CBC-HMAC variants (A128CBC-HS256, A192CBC-HS384,
|
||||
// A256CBC-HS512) T_LEN happens to equal MAC_KEY_LEN (== keysize here),
|
||||
// but we track it independently so a future variant with a different
|
||||
// T_LEN won't silently mis-truncate the HMAC output.
|
||||
var hfunc func() hash.Hash
|
||||
var tlen int
|
||||
switch keysize {
|
||||
case 16:
|
||||
case 16: // A128CBC-HS256
|
||||
hfunc = sha256.New
|
||||
case 24:
|
||||
tlen = 16
|
||||
case 24: // A192CBC-HS384
|
||||
hfunc = sha512.New384
|
||||
case 32:
|
||||
tlen = 24
|
||||
case 32: // A256CBC-HS512
|
||||
hfunc = sha512.New
|
||||
tlen = 32
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported key size %d", keysize)
|
||||
}
|
||||
@@ -142,11 +158,7 @@ func New(key []byte, f BlockCipherFunc) (hmac *Hmac, err error) {
|
||||
hash: hfunc,
|
||||
integrityKey: ikey,
|
||||
keysize: keysize,
|
||||
tagsize: keysize, // NonceSize,
|
||||
// While investigating GH #207, I stumbled upon another problem where
|
||||
// the computed tags don't match on decrypt. After poking through the
|
||||
// code using a bunch of debug statements, I've finally found out that
|
||||
// tagsize = keysize makes the whole thing work.
|
||||
tlen: tlen,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -157,7 +169,7 @@ func (c Hmac) NonceSize() int {
|
||||
|
||||
// Overhead fulfills the crypto.AEAD interface
|
||||
func (c Hmac) Overhead() int {
|
||||
return c.blockCipher.BlockSize() + c.tagsize
|
||||
return c.blockCipher.BlockSize() + c.tlen
|
||||
}
|
||||
|
||||
func (c Hmac) ComputeAuthTag(aad, nonce, ciphertext []byte) ([]byte, error) {
|
||||
@@ -176,20 +188,26 @@ func (c Hmac) ComputeAuthTag(aad, nonce, ciphertext []byte) ([]byte, error) {
|
||||
h.Write(ciphertext)
|
||||
h.Write(buf[:])
|
||||
s := h.Sum(nil)
|
||||
return s[:c.tagsize], nil
|
||||
return s[:c.tlen], nil
|
||||
}
|
||||
|
||||
func ensureSize(dst []byte, n int) []byte {
|
||||
// if the dst buffer has enough length just copy the relevant parts to it.
|
||||
// Otherwise create a new slice that's big enough, and operate on that
|
||||
// Note: I think go-jose has a bug in that it checks for cap(), but not len().
|
||||
ret := dst
|
||||
if diff := n - len(dst); diff > 0 {
|
||||
// dst is not big enough
|
||||
ret = make([]byte, n)
|
||||
copy(ret, dst)
|
||||
// Grow dst by n bytes, preserving its current contents as the prefix.
|
||||
// This matches the crypto.AEAD append contract used by Seal/Open.
|
||||
if n < 0 {
|
||||
panic(fmt.Errorf("failed to allocate buffer"))
|
||||
}
|
||||
return ret
|
||||
|
||||
const maxInt = int64(^uint(0) >> 1)
|
||||
maxAlloc := min(maxBufSize.Load(), maxInt)
|
||||
|
||||
if int64(len(dst)) > maxAlloc-int64(n) {
|
||||
panic(fmt.Errorf("failed to allocate buffer"))
|
||||
}
|
||||
|
||||
retlen := len(dst) + n
|
||||
dst = slices.Grow(dst, n)
|
||||
return dst[:retlen]
|
||||
}
|
||||
|
||||
// Seal fulfills the crypto.AEAD interface
|
||||
@@ -215,9 +233,7 @@ func (c Hmac) Seal(dst, nonce, plaintext, data []byte) []byte {
|
||||
panic(fmt.Errorf("failed to seal on hmac: %v", err))
|
||||
}
|
||||
|
||||
retlen := len(dst) + len(ciphertext) + len(authtag)
|
||||
|
||||
ret := ensureSize(dst, retlen)
|
||||
ret := ensureSize(dst, len(ciphertext)+len(authtag))
|
||||
out := ret[len(dst):]
|
||||
n := copy(out, ciphertext)
|
||||
copy(out[n:], authtag)
|
||||
@@ -227,17 +243,32 @@ func (c Hmac) Seal(dst, nonce, plaintext, data []byte) []byte {
|
||||
|
||||
// Open fulfills the crypto.AEAD interface
|
||||
func (c Hmac) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
|
||||
if len(ciphertext) < c.keysize {
|
||||
return nil, fmt.Errorf(`invalid ciphertext (too short)`)
|
||||
// Validate the IV length explicitly instead of letting
|
||||
// cipher.NewCBCDecrypter panic on a mismatched nonce. The caller in
|
||||
// jwe/internal/cipher also wraps Open in a defer/recover, and we
|
||||
// intentionally keep BOTH layers: the explicit check turns a malformed
|
||||
// IV into a normal error on the happy path (reviewable, testable, no
|
||||
// stack unwind), while the recover stays as a belt-and-braces guard
|
||||
// against other panics inside the stdlib CBC path (e.g. future
|
||||
// invariants we don't currently enforce). Removing either layer would
|
||||
// mean relying on the other — this way a regression in one is still
|
||||
// caught by the other. See JWE-005 in the v4 security review.
|
||||
// All pre-MAC structural failures return the exact same error value
|
||||
// as the post-MAC failure below. Distinguishing "malformed nonce",
|
||||
// "ciphertext too short", "ciphertext length not block-aligned", and
|
||||
// "MAC mismatch" at the caller would leak whether an attacker probe
|
||||
// is block-aligned vs cryptographically invalid — a structural-vs-MAC
|
||||
// oracle that composes with other leaks. Keep all four paths opaque.
|
||||
if len(nonce) != c.blockCipher.BlockSize() {
|
||||
return nil, errInvalidCiphertext
|
||||
}
|
||||
if len(ciphertext) < c.tlen {
|
||||
return nil, errInvalidCiphertext
|
||||
}
|
||||
|
||||
tagOffset := len(ciphertext) - c.tagsize
|
||||
tagOffset := len(ciphertext) - c.tlen
|
||||
if tagOffset%c.blockCipher.BlockSize() != 0 {
|
||||
return nil, fmt.Errorf(
|
||||
"invalid ciphertext (invalid length: %d %% %d != 0)",
|
||||
tagOffset,
|
||||
c.blockCipher.BlockSize(),
|
||||
)
|
||||
return nil, errInvalidCiphertext
|
||||
}
|
||||
tag := ciphertext[tagOffset:]
|
||||
ciphertext = ciphertext[:tagOffset]
|
||||
@@ -248,16 +279,15 @@ func (c Hmac) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
|
||||
}
|
||||
|
||||
cbc := cipher.NewCBCDecrypter(c.blockCipher, nonce)
|
||||
buf := pool.ByteSlice().GetCapacity(tagOffset)
|
||||
buf := pool.ByteSlice().GetCapacity(tagOffset)[:tagOffset]
|
||||
defer pool.ByteSlice().Put(buf)
|
||||
buf = buf[:tagOffset]
|
||||
|
||||
cbc.CryptBlocks(buf, ciphertext)
|
||||
|
||||
toRemove, good := extractPadding(buf)
|
||||
cmp := subtle.ConstantTimeCompare(expectedTag, tag) & int(good)
|
||||
if cmp != 1 {
|
||||
return nil, errors.New(`invalid ciphertext`)
|
||||
return nil, errInvalidCiphertext
|
||||
}
|
||||
|
||||
plaintext := buf[:len(buf)-toRemove]
|
||||
|
||||
3
vendor/github.com/lestrrat-go/jwx/v3/jwe/internal/cipher/cipher.go
generated
vendored
3
vendor/github.com/lestrrat-go/jwx/v3/jwe/internal/cipher/cipher.go
generated
vendored
@@ -128,9 +128,8 @@ func (c AesContentCipher) Encrypt(cek, plaintext, aad []byte) (iv, ciphertxt, ta
|
||||
panic(fmt.Sprintf("tag offset is less than 0 (combined len = %d, tagsize = %d)", len(combined), c.TagSize()))
|
||||
}
|
||||
|
||||
ciphertxt = combined[:tagoffset:tagoffset]
|
||||
tag = combined[tagoffset:]
|
||||
ciphertxt = make([]byte, tagoffset)
|
||||
copy(ciphertxt, combined[:tagoffset])
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
37
vendor/github.com/lestrrat-go/jwx/v3/jwe/internal/concatkdf/concatkdf.go
generated
vendored
37
vendor/github.com/lestrrat-go/jwx/v3/jwe/internal/concatkdf/concatkdf.go
generated
vendored
@@ -13,22 +13,25 @@ type KDF struct {
|
||||
hash crypto.Hash
|
||||
}
|
||||
|
||||
func ndata(src []byte) []byte {
|
||||
buf := make([]byte, 4+len(src))
|
||||
binary.BigEndian.PutUint32(buf, uint32(len(src)))
|
||||
copy(buf[4:], src)
|
||||
return buf
|
||||
}
|
||||
|
||||
func New(hash crypto.Hash, alg, Z, apu, apv, pubinfo, privinfo []byte) *KDF {
|
||||
algbuf := ndata(alg)
|
||||
apubuf := ndata(apu)
|
||||
apvbuf := ndata(apv)
|
||||
// Write length-prefixed fields directly into a single buffer,
|
||||
// avoiding intermediate allocations from ndata().
|
||||
totalSize := (4 + len(alg)) + (4 + len(apu)) + (4 + len(apv)) + len(pubinfo) + len(privinfo)
|
||||
concat := make([]byte, totalSize)
|
||||
|
||||
n := 0
|
||||
binary.BigEndian.PutUint32(concat[n:], uint32(len(alg)))
|
||||
n += 4
|
||||
n += copy(concat[n:], alg)
|
||||
|
||||
binary.BigEndian.PutUint32(concat[n:], uint32(len(apu)))
|
||||
n += 4
|
||||
n += copy(concat[n:], apu)
|
||||
|
||||
binary.BigEndian.PutUint32(concat[n:], uint32(len(apv)))
|
||||
n += 4
|
||||
n += copy(concat[n:], apv)
|
||||
|
||||
concat := make([]byte, len(algbuf)+len(apubuf)+len(apvbuf)+len(pubinfo)+len(privinfo))
|
||||
n := copy(concat, algbuf)
|
||||
n += copy(concat[n:], apubuf)
|
||||
n += copy(concat[n:], apvbuf)
|
||||
n += copy(concat[n:], pubinfo)
|
||||
copy(concat[n:], privinfo)
|
||||
|
||||
@@ -42,11 +45,13 @@ func New(hash crypto.Hash, alg, Z, apu, apv, pubinfo, privinfo []byte) *KDF {
|
||||
func (k *KDF) Read(out []byte) (int, error) {
|
||||
var round uint32 = 1
|
||||
h := k.hash.New()
|
||||
var roundBuf [4]byte
|
||||
|
||||
for len(out) > len(k.buf) {
|
||||
h.Reset()
|
||||
|
||||
if err := binary.Write(h, binary.BigEndian, round); err != nil {
|
||||
binary.BigEndian.PutUint32(roundBuf[:], round)
|
||||
if _, err := h.Write(roundBuf[:]); err != nil {
|
||||
return 0, fmt.Errorf(`failed to write round using kdf: %w`, err)
|
||||
}
|
||||
if _, err := h.Write(k.z); err != nil {
|
||||
@@ -56,7 +61,7 @@ func (k *KDF) Read(out []byte) (int, error) {
|
||||
return 0, fmt.Errorf(`failed to write other info using kdf: %w`, err)
|
||||
}
|
||||
|
||||
k.buf = append(k.buf, h.Sum(nil)...)
|
||||
k.buf = h.Sum(k.buf)
|
||||
round++
|
||||
}
|
||||
|
||||
|
||||
38
vendor/github.com/lestrrat-go/jwx/v3/jwe/internal/keygen/keygen.go
generated
vendored
38
vendor/github.com/lestrrat-go/jwx/v3/jwe/internal/keygen/keygen.go
generated
vendored
@@ -9,7 +9,6 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/lestrrat-go/jwx/v3/internal/ecutil"
|
||||
"github.com/lestrrat-go/jwx/v3/internal/tokens"
|
||||
"github.com/lestrrat-go/jwx/v3/jwe/internal/concatkdf"
|
||||
"github.com/lestrrat-go/jwx/v3/jwk"
|
||||
@@ -28,9 +27,27 @@ func Random(n int) (ByteSource, error) {
|
||||
return ByteKey(buf), nil
|
||||
}
|
||||
|
||||
// Ecdhes generates a new key using ECDH-ES
|
||||
// Ecdhes generates a new key using ECDH-ES.
|
||||
//
|
||||
// The recipient pubkey is converted to *ecdh.PublicKey via stdlib
|
||||
// (*ecdsa.PublicKey).ECDH() before any cryptographic operation. That
|
||||
// conversion uses identity matching against the named NIST curves
|
||||
// (elliptic.P256/P384/P521) and rejects anything else — including a
|
||||
// caller-controlled or tampered elliptic.Curve and the generic big-int
|
||||
// CurveParams path. This closes the invalid-curve attack surface that
|
||||
// the previous deprecated crypto/elliptic.Curve.ScalarMult code path
|
||||
// exposed when the recipient's *ecdsa.PublicKey.Curve field was
|
||||
// attacker-influenced.
|
||||
func Ecdhes(alg string, enc string, keysize int, pubkey *ecdsa.PublicKey, apu, apv []byte) (ByteSource, error) {
|
||||
priv, err := ecdsa.GenerateKey(pubkey.Curve, rand.Reader)
|
||||
if pubkey == nil || pubkey.X == nil || pubkey.Y == nil {
|
||||
return nil, fmt.Errorf(`invalid ECDH-ES public key: nil X or Y`)
|
||||
}
|
||||
ecdhPub, err := pubkey.ECDH()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to convert ECDH-ES public key to *ecdh.PublicKey: %w`, err)
|
||||
}
|
||||
|
||||
priv, err := ecdhPub.Curve().GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to generate key for ECDH-ES: %w`, err)
|
||||
}
|
||||
@@ -45,12 +62,11 @@ func Ecdhes(alg string, enc string, keysize int, pubkey *ecdsa.PublicKey, apu, a
|
||||
pubinfo := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(pubinfo, uint32(keysize)*8)
|
||||
|
||||
if !priv.PublicKey.Curve.IsOnCurve(pubkey.X, pubkey.Y) {
|
||||
return nil, fmt.Errorf(`public key used does not contain a point (X,Y) on the curve`)
|
||||
zBytes, err := priv.ECDH(ecdhPub)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to compute Z: %w`, err)
|
||||
}
|
||||
z, _ := priv.PublicKey.Curve.ScalarMult(pubkey.X, pubkey.Y, priv.D.Bytes())
|
||||
zBytes := ecutil.AllocECPointBuffer(z, priv.PublicKey.Curve)
|
||||
defer ecutil.ReleaseECPointBuffer(zBytes)
|
||||
|
||||
kdf := concatkdf.New(crypto.SHA256, []byte(algorithm), zBytes, apu, apv, pubinfo, []byte{})
|
||||
kek := make([]byte, keysize)
|
||||
if _, err := kdf.Read(kek); err != nil {
|
||||
@@ -58,13 +74,13 @@ func Ecdhes(alg string, enc string, keysize int, pubkey *ecdsa.PublicKey, apu, a
|
||||
}
|
||||
|
||||
return ByteWithECPublicKey{
|
||||
PublicKey: &priv.PublicKey,
|
||||
PublicKey: priv.PublicKey(),
|
||||
ByteKey: ByteKey(kek),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// X25519 generates a new key using ECDH-ES with X25519
|
||||
func X25519(alg string, enc string, keysize int, pubkey *ecdh.PublicKey) (ByteSource, error) {
|
||||
func X25519(alg string, enc string, keysize int, pubkey *ecdh.PublicKey, apu, apv []byte) (ByteSource, error) {
|
||||
priv, err := ecdh.X25519().GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to generate key for X25519: %w`, err)
|
||||
@@ -84,7 +100,7 @@ func X25519(alg string, enc string, keysize int, pubkey *ecdh.PublicKey) (ByteSo
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to compute Z: %w`, err)
|
||||
}
|
||||
kdf := concatkdf.New(crypto.SHA256, []byte(algorithm), zBytes, []byte{}, []byte{}, pubinfo, []byte{})
|
||||
kdf := concatkdf.New(crypto.SHA256, []byte(algorithm), zBytes, apu, apv, pubinfo, []byte{})
|
||||
kek := make([]byte, keysize)
|
||||
if _, err := kdf.Read(kek); err != nil {
|
||||
return nil, fmt.Errorf(`failed to read kdf: %w`, err)
|
||||
|
||||
8
vendor/github.com/lestrrat-go/jwx/v3/jwe/io.go
generated
vendored
8
vendor/github.com/lestrrat-go/jwx/v3/jwe/io.go
generated
vendored
@@ -15,6 +15,12 @@ func (sysFS) Open(path string) (fs.File, error) {
|
||||
}
|
||||
|
||||
func ReadFile(path string, options ...ReadFileOption) (*Message, error) {
|
||||
var parseOptions []ParseOption
|
||||
for _, option := range options {
|
||||
if po, ok := option.(ParseOption); ok {
|
||||
parseOptions = append(parseOptions, po)
|
||||
}
|
||||
}
|
||||
|
||||
var srcFS fs.FS = sysFS{}
|
||||
for _, option := range options {
|
||||
@@ -32,5 +38,5 @@ func ReadFile(path string, options ...ReadFileOption) (*Message, error) {
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
return ParseReader(f)
|
||||
return ParseReader(f, parseOptions...)
|
||||
}
|
||||
|
||||
542
vendor/github.com/lestrrat-go/jwx/v3/jwe/jwe.go
generated
vendored
542
vendor/github.com/lestrrat-go/jwx/v3/jwe/jwe.go
generated
vendored
@@ -1,6 +1,11 @@
|
||||
//go:generate ../tools/cmd/genjwe.sh
|
||||
|
||||
// Package jwe implements JWE as described in https://tools.ietf.org/html/rfc7516
|
||||
// Package jwe implements JWE as described in https://tools.ietf.org/html/rfc7516.
|
||||
//
|
||||
// Legacy note: RSA-PKCS1 v1.5 key encryption (`jwa.RSA1_5()`) is supported
|
||||
// only for interoperability with existing peers. New applications should
|
||||
// prefer an RSA-OAEP variant such as `jwa.RSA_OAEP_256()` because PKCS#1 v1.5
|
||||
// decryption is exposed to Bleichenbacher-style oracle attacks.
|
||||
package jwe
|
||||
|
||||
// #region imports
|
||||
@@ -11,7 +16,9 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
"math"
|
||||
"slices"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/lestrrat-go/blackmagic"
|
||||
"github.com/lestrrat-go/jwx/v3/internal/base64"
|
||||
@@ -28,33 +35,92 @@ import (
|
||||
|
||||
// #region globals
|
||||
|
||||
var muSettings sync.RWMutex
|
||||
var maxPBES2Count = 10000
|
||||
var maxDecompressBufferSize int64 = 10 * 1024 * 1024 // 10MB
|
||||
var maxPBES2Count atomic.Int64
|
||||
var minPBES2Count atomic.Int64
|
||||
var pbes2Count atomic.Int64
|
||||
var maxRecipients atomic.Int64
|
||||
var maxDecompressBufferSize atomic.Int64
|
||||
var disabledKeyAlgs atomic.Pointer[map[string]struct{}]
|
||||
|
||||
func init() {
|
||||
maxPBES2Count.Store(10000)
|
||||
minPBES2Count.Store(1000)
|
||||
pbes2Count.Store(int64(tokens.PBES2DefaultIterations))
|
||||
maxRecipients.Store(100)
|
||||
maxDecompressBufferSize.Store(10 * 1024 * 1024) // 10MB
|
||||
}
|
||||
|
||||
func Settings(options ...GlobalOption) {
|
||||
muSettings.Lock()
|
||||
defer muSettings.Unlock()
|
||||
for _, option := range options {
|
||||
switch option.Ident() {
|
||||
case identMaxPBES2Count{}:
|
||||
if err := option.Value(&maxPBES2Count); err != nil {
|
||||
var v int
|
||||
if err := option.Value(&v); err != nil {
|
||||
panic(fmt.Sprintf("jwe.Settings: value for option WithMaxPBES2Count must be an int: %s", err))
|
||||
}
|
||||
maxPBES2Count.Store(int64(v))
|
||||
case identMinPBES2Count{}:
|
||||
var v int
|
||||
if err := option.Value(&v); err != nil {
|
||||
panic(fmt.Sprintf("jwe.Settings: value for option WithMinPBES2Count must be an int: %s", err))
|
||||
}
|
||||
minPBES2Count.Store(int64(v))
|
||||
case identPBES2Count{}:
|
||||
var v int
|
||||
if err := option.Value(&v); err != nil {
|
||||
panic(fmt.Sprintf("jwe.Settings: value for option WithPBES2Count must be an int: %s", err))
|
||||
}
|
||||
if v <= 0 {
|
||||
v = tokens.PBES2DefaultIterations
|
||||
}
|
||||
pbes2Count.Store(int64(v))
|
||||
case identMaxRecipients{}:
|
||||
var v int
|
||||
if err := option.Value(&v); err != nil {
|
||||
panic(fmt.Sprintf("jwe.Settings: value for option WithMaxRecipients must be an int: %s", err))
|
||||
}
|
||||
maxRecipients.Store(int64(v))
|
||||
case identMaxDecompressBufferSize{}:
|
||||
if err := option.Value(&maxDecompressBufferSize); err != nil {
|
||||
var v int64
|
||||
if err := option.Value(&v); err != nil {
|
||||
panic(fmt.Sprintf("jwe.Settings: value for option WithMaxDecompressBufferSize must be an int64: %s", err))
|
||||
}
|
||||
maxDecompressBufferSize.Store(v)
|
||||
case identCBCBufferSize{}:
|
||||
var v int64
|
||||
if err := option.Value(&v); err != nil {
|
||||
panic(fmt.Sprintf("jwe.Settings: value for option WithCBCBufferSize must be an int64: %s", err))
|
||||
}
|
||||
aescbc.SetMaxBufferSize(v)
|
||||
case identDisabledKeyAlgorithms{}:
|
||||
var algs []jwa.KeyEncryptionAlgorithm
|
||||
if err := option.Value(&algs); err != nil {
|
||||
panic(fmt.Sprintf("jwe.Settings: value for option WithDisabledKeyAlgorithms must be []jwa.KeyEncryptionAlgorithm: %s", err))
|
||||
}
|
||||
if len(algs) == 0 {
|
||||
disabledKeyAlgs.Store(nil)
|
||||
continue
|
||||
}
|
||||
m := make(map[string]struct{}, len(algs))
|
||||
for _, alg := range algs {
|
||||
m[alg.String()] = struct{}{}
|
||||
}
|
||||
disabledKeyAlgs.Store(&m)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// isKeyAlgorithmDisabled reports whether alg is in the global
|
||||
// jwe.WithDisabledKeyAlgorithms set.
|
||||
func isKeyAlgorithmDisabled(alg jwa.KeyEncryptionAlgorithm) bool {
|
||||
m := disabledKeyAlgs.Load()
|
||||
if m == nil {
|
||||
return false
|
||||
}
|
||||
_, ok := (*m)[alg.String()]
|
||||
return ok
|
||||
}
|
||||
|
||||
const (
|
||||
fmtInvalid = iota
|
||||
fmtCompact
|
||||
@@ -63,18 +129,19 @@ const (
|
||||
fmtMax
|
||||
)
|
||||
|
||||
var _ = fmtInvalid
|
||||
var _ = fmtMax
|
||||
|
||||
var registry = json.NewRegistry()
|
||||
|
||||
type recipientBuilder struct {
|
||||
alg jwa.KeyEncryptionAlgorithm
|
||||
key any
|
||||
headers Headers
|
||||
alg jwa.KeyEncryptionAlgorithm
|
||||
key any
|
||||
headers Headers
|
||||
pbes2Count int
|
||||
}
|
||||
|
||||
func (b *recipientBuilder) Build(r Recipient, cek []byte, calg jwa.ContentEncryptionAlgorithm, _ *content_crypt.Generic) ([]byte, error) {
|
||||
if isKeyAlgorithmDisabled(b.alg) {
|
||||
return nil, fmt.Errorf(`jwe.Encrypt: key encryption algorithm %q is disabled by jwe.WithDisabledKeyAlgorithms`, b.alg)
|
||||
}
|
||||
// we need the raw key for later use
|
||||
rawKey := b.key
|
||||
|
||||
@@ -104,7 +171,7 @@ func (b *recipientBuilder) Build(r Recipient, cek []byte, calg jwa.ContentEncryp
|
||||
|
||||
hdr := b.headers
|
||||
if hdr == nil {
|
||||
hdr = NewHeaders()
|
||||
hdr = r.Headers()
|
||||
}
|
||||
|
||||
if val, ok := hdr.AgreementPartyUInfo(); ok {
|
||||
@@ -116,10 +183,7 @@ func (b *recipientBuilder) Build(r Recipient, cek []byte, calg jwa.ContentEncryp
|
||||
}
|
||||
|
||||
// Create the encrypter using the new jwebb pattern
|
||||
enc, err := newEncrypter(b.alg, calg, b.key, rawKey, apu, apv)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`jwe.Encrypt: recipientBuilder: failed to create encrypter: %w`, err)
|
||||
}
|
||||
enc := newEncrypter(b.alg, calg, b.key, rawKey, apu, apv, b.pbes2Count)
|
||||
|
||||
_ = r.SetHeaders(hdr)
|
||||
|
||||
@@ -168,9 +232,9 @@ func (b *recipientBuilder) Build(r Recipient, cek []byte, calg jwa.ContentEncryp
|
||||
// option.
|
||||
//
|
||||
// jwe.Encrypt(payload, jwe.WithKey(alg, key))
|
||||
// jwe.Encrypt(payload, jws.WithJSON(), jws.WithKey(alg1, key1), jws.WithKey(alg2, key2))
|
||||
// jwe.Encrypt(payload, jwe.WithJSON(), jwe.WithKey(alg1, key1), jwe.WithKey(alg2, key2))
|
||||
//
|
||||
// Note that in the second example the `jws.WithJSON()` option is
|
||||
// Note that in the second example the `jwe.WithJSON()` option is
|
||||
// specified as well. This is because the compact serialization
|
||||
// format does not support multiple recipients, and users must
|
||||
// specifically ask for the JSON serialization format.
|
||||
@@ -178,7 +242,18 @@ func (b *recipientBuilder) Build(r Recipient, cek []byte, calg jwa.ContentEncryp
|
||||
// Read the documentation for `jwe.WithKey()` to learn more about the
|
||||
// possible values that can be used for `alg` and `key`.
|
||||
//
|
||||
// Look for options that return `jwe.EncryptOption` or `jws.EncryptDecryptOption`
|
||||
// `jwa.RSA1_5()` is supported only for interoperability with legacy peers.
|
||||
// New applications should prefer an RSA-OAEP variant such as
|
||||
// `jwa.RSA_OAEP_256()` because PKCS#1 v1.5 decryption is exposed to
|
||||
// Bleichenbacher-style oracle attacks.
|
||||
// If you enable `jwe.WithCompress()`, this library does not enforce a
|
||||
// producer-side payload size limit before compression. Callers that accept
|
||||
// untrusted or arbitrarily large plaintext must bound the input size before
|
||||
// calling `jwe.Encrypt()`. Recipients may also reject compressed messages
|
||||
// whose decompressed payload exceeds their `jwe.WithMaxDecompressBufferSize()`
|
||||
// setting.
|
||||
//
|
||||
// Look for options that return `jwe.EncryptOption` or `jwe.EncryptDecryptOption`
|
||||
// for a complete list of options that can be passed to this function.
|
||||
//
|
||||
// As of v3.0.12, users can specify `jwe.WithLegacyHeaderMerging()` to
|
||||
@@ -188,11 +263,11 @@ func Encrypt(payload []byte, options ...EncryptOption) ([]byte, error) {
|
||||
ec := encryptContextPool.Get()
|
||||
defer encryptContextPool.Put(ec)
|
||||
if err := ec.ProcessOptions(options); err != nil {
|
||||
return nil, encryptError{fmt.Errorf(`jwe.Encrypt: failed to process options: %w`, err)}
|
||||
return nil, makeEncryptError(`jwe.Encrypt`, `failed to process options: %w`, err)
|
||||
}
|
||||
ret, err := ec.EncryptMessage(payload, nil)
|
||||
if err != nil {
|
||||
return nil, encryptError{fmt.Errorf(`jwe.Encrypt: %w`, err)}
|
||||
return nil, makeEncryptError(`jwe.Encrypt`, `%w`, err)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
@@ -202,6 +277,32 @@ func Encrypt(payload []byte, options ...EncryptOption) ([]byte, error) {
|
||||
// Encrypt function such that the latter does not accidentally use a static
|
||||
// CEK.
|
||||
//
|
||||
// Unless `jwe.WithContentEncryption()` is provided, `EncryptStatic` uses
|
||||
// `jwa.A256GCM()`, which requires a 32-byte CEK.
|
||||
//
|
||||
// The CEK used to encrypt the payload must match the selected content
|
||||
// encryption algorithm:
|
||||
//
|
||||
// - `jwa.A128GCM()`: 16 bytes
|
||||
// - `jwa.A192GCM()`: 24 bytes
|
||||
// - `jwa.A256GCM()`: 32 bytes
|
||||
// - `jwa.A128CBC_HS256()`: 32 bytes
|
||||
// - `jwa.A192CBC_HS384()`: 48 bytes
|
||||
// - `jwa.A256CBC_HS512()`: 64 bytes
|
||||
//
|
||||
// `EncryptStatic` validates the final CEK length before payload encryption
|
||||
// and returns an error if it does not match the selected `enc` algorithm.
|
||||
//
|
||||
// NOTE: when the chosen key-encryption algorithm derives the CEK rather than
|
||||
// wrapping it — specifically `jwa.DIRECT()` and bare `jwa.ECDH_ES()` (without
|
||||
// a key-wrap suffix) — the `cek` argument supplied here is ignored for
|
||||
// content encryption. In those modes the effective CEK is the shared/derived
|
||||
// key produced by the `jwe.WithKey()` input, and the byte-length check
|
||||
// described above is enforced against that derived CEK, not against the
|
||||
// value passed as `cek`. To pin the CEK deterministically, pair
|
||||
// `EncryptStatic` only with key-wrapping algorithms such as
|
||||
// `jwa.RSA_OAEP()`, `jwa.A256KW()`, or `jwa.ECDH_ES_A256KW()`.
|
||||
//
|
||||
// DO NOT attempt to use this function unless you completely understand the
|
||||
// security implications to using static CEKs. You have been warned.
|
||||
//
|
||||
@@ -209,16 +310,16 @@ func Encrypt(payload []byte, options ...EncryptOption) ([]byte, error) {
|
||||
// future changes across minor/micro versions.
|
||||
func EncryptStatic(payload, cek []byte, options ...EncryptOption) ([]byte, error) {
|
||||
if len(cek) <= 0 {
|
||||
return nil, encryptError{fmt.Errorf(`jwe.EncryptStatic: empty CEK`)}
|
||||
return nil, makeEncryptError(`jwe.EncryptStatic`, `empty CEK`)
|
||||
}
|
||||
ec := encryptContextPool.Get()
|
||||
defer encryptContextPool.Put(ec)
|
||||
if err := ec.ProcessOptions(options); err != nil {
|
||||
return nil, encryptError{fmt.Errorf(`jwe.EncryptStatic: failed to process options: %w`, err)}
|
||||
return nil, makeEncryptError(`jwe.EncryptStatic`, `failed to process options: %w`, err)
|
||||
}
|
||||
ret, err := ec.EncryptMessage(payload, cek)
|
||||
if err != nil {
|
||||
return nil, encryptError{fmt.Errorf(`jwe.EncryptStatic: %w`, err)}
|
||||
return nil, makeEncryptError(`jwe.EncryptStatic`, `%w`, err)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
@@ -229,7 +330,12 @@ type decryptContext struct {
|
||||
keyUsed any
|
||||
cek *[]byte
|
||||
dst *Message
|
||||
maxRecipients int
|
||||
maxDecompressBufferSize int64
|
||||
maxPBES2Count int
|
||||
minPBES2Count int
|
||||
critValidation bool
|
||||
criticalExtensions []string
|
||||
//nolint:containedctx
|
||||
ctx context.Context
|
||||
}
|
||||
@@ -247,16 +353,21 @@ func freeDecryptContext(dc *decryptContext) *decryptContext {
|
||||
dc.keyUsed = nil
|
||||
dc.cek = nil
|
||||
dc.dst = nil
|
||||
dc.maxRecipients = 0
|
||||
dc.maxDecompressBufferSize = 0
|
||||
dc.maxPBES2Count = 0
|
||||
dc.minPBES2Count = 0
|
||||
dc.critValidation = false
|
||||
dc.criticalExtensions = dc.criticalExtensions[:0]
|
||||
dc.ctx = context.Background()
|
||||
return dc
|
||||
}
|
||||
|
||||
func (dc *decryptContext) ProcessOptions(options []DecryptOption) error {
|
||||
// Set default max decompress buffer size
|
||||
muSettings.RLock()
|
||||
dc.maxDecompressBufferSize = maxDecompressBufferSize
|
||||
muSettings.RUnlock()
|
||||
dc.maxRecipients = int(maxRecipients.Load())
|
||||
dc.maxDecompressBufferSize = maxDecompressBufferSize.Load()
|
||||
dc.maxPBES2Count = int(maxPBES2Count.Load())
|
||||
dc.minPBES2Count = int(minPBES2Count.Load())
|
||||
|
||||
for _, option := range options {
|
||||
switch option.Ident() {
|
||||
@@ -283,19 +394,44 @@ func (dc *decryptContext) ProcessOptions(options []DecryptOption) error {
|
||||
if !ok {
|
||||
return fmt.Errorf("jwe.decrypt: WithKey() option must be specified using jwa.KeyEncryptionAlgorithm (got %T)", pair.alg)
|
||||
}
|
||||
if err := validateAlgorithmForKey(alg, pair.key); err != nil {
|
||||
return fmt.Errorf("jwe.WithKey: %w", err)
|
||||
}
|
||||
dc.keyProviders = append(dc.keyProviders, &staticKeyProvider{alg: alg, key: pair.key})
|
||||
case identCEK{}:
|
||||
if err := option.Value(&dc.cek); err != nil {
|
||||
return fmt.Errorf("jwe.decrypt: WithCEK must be a *[]byte: %w", err)
|
||||
}
|
||||
case identMaxRecipients{}:
|
||||
if err := option.Value(&dc.maxRecipients); err != nil {
|
||||
return fmt.Errorf("jwe.decrypt: WithMaxRecipients must be int: %w", err)
|
||||
}
|
||||
case identMaxDecompressBufferSize{}:
|
||||
if err := option.Value(&dc.maxDecompressBufferSize); err != nil {
|
||||
return fmt.Errorf("jwe.decrypt: WithMaxDecompressBufferSize must be int64: %w", err)
|
||||
}
|
||||
case identMaxPBES2Count{}:
|
||||
if err := option.Value(&dc.maxPBES2Count); err != nil {
|
||||
return fmt.Errorf("jwe.decrypt: WithMaxPBES2Count must be int: %w", err)
|
||||
}
|
||||
case identMinPBES2Count{}:
|
||||
if err := option.Value(&dc.minPBES2Count); err != nil {
|
||||
return fmt.Errorf("jwe.decrypt: WithMinPBES2Count must be int: %w", err)
|
||||
}
|
||||
case identContext{}:
|
||||
if err := option.Value(&dc.ctx); err != nil {
|
||||
return fmt.Errorf("jwe.decrypt: WithContext must be a context.Context: %w", err)
|
||||
}
|
||||
case identCritValidation{}:
|
||||
if err := option.Value(&dc.critValidation); err != nil {
|
||||
return fmt.Errorf("jwe.decrypt: WithCritValidation must be a bool: %w", err)
|
||||
}
|
||||
case identCritExtension{}:
|
||||
var names []string
|
||||
if err := option.Value(&names); err != nil {
|
||||
return fmt.Errorf("jwe.decrypt: WithCritExtension must be a string: %w", err)
|
||||
}
|
||||
dc.criticalExtensions = append(dc.criticalExtensions, names...)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,20 +442,107 @@ func (dc *decryptContext) ProcessOptions(options []DecryptOption) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dc *decryptContext) DecryptMessage(buf []byte) ([]byte, error) {
|
||||
msg, err := parseJSONOrCompact(buf, true)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to parse buffer for Decrypt: %w`, err)
|
||||
// validateCritical checks the "crit" header per RFC 7516 Section 4.1.13
|
||||
// (which references RFC 7515 Section 4.1.11). It enforces:
|
||||
// - the list is non-empty
|
||||
// - no entry is the empty string
|
||||
// - no entry duplicates another
|
||||
// - no entry names a standard JOSE/JWE header parameter
|
||||
// - every entry appears as a header parameter in the protected header
|
||||
// - every entry is in the caller-supplied allowedExtensions allowlist
|
||||
//
|
||||
// The last check is the central RFC requirement: recipients MUST reject
|
||||
// any "crit" extension they do not understand, and the only way the
|
||||
// library knows which extensions the caller understands is via the
|
||||
// allowlist (populated from jwe.WithCritExtension()).
|
||||
func validateCritical(protected Headers, allowedExtensions []string) error {
|
||||
if !protected.Has(CriticalKey) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Process things that are common to the message
|
||||
crit, _ := protected.Critical()
|
||||
if len(crit) == 0 {
|
||||
return makeDecryptError(`"crit" header must not be empty`)
|
||||
}
|
||||
|
||||
seen := make(map[string]struct{}, len(crit))
|
||||
for _, name := range crit {
|
||||
if name == "" {
|
||||
return makeDecryptError(`"crit" header must not contain an empty extension name`)
|
||||
}
|
||||
if _, dup := seen[name]; dup {
|
||||
return makeDecryptError(`"crit" header must not contain duplicate extension %q`, name)
|
||||
}
|
||||
seen[name] = struct{}{}
|
||||
|
||||
// RFC 7515 Section 4.1.11: "crit" MUST NOT include names defined
|
||||
// by the JOSE Header specification itself.
|
||||
if slices.Contains(stdHeaderNames, name) {
|
||||
return makeDecryptError(`"crit" header must not contain standard header parameter %q`, name)
|
||||
}
|
||||
|
||||
// The extension must be present in the protected header.
|
||||
if !protected.Has(name) {
|
||||
return makeDecryptError(`"crit" header references extension %q, but it is not present in the protected header`, name)
|
||||
}
|
||||
|
||||
// The recipient must have declared support for the extension.
|
||||
if !slices.Contains(allowedExtensions, name) {
|
||||
return makeDecryptError(`"crit" header references extension %q, but the recipient has not declared support for it (use jwe.WithCritExtension(%q))`, name, name)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// concatAAD returns the AAD value used to seal or open a JWE payload:
|
||||
// the protected-header segment, optionally followed by ASCII '.' and
|
||||
// the caller-supplied external aad (RFC 7516 §5.1 step 14 / §5.2
|
||||
// step 14). A fresh slice is always allocated so the caller's computed
|
||||
// and aad slices are never appended into, which matters because
|
||||
// computedAad often aliases a Message field whose backing array is
|
||||
// still referenced elsewhere.
|
||||
func concatAAD(computed, aad []byte) []byte {
|
||||
if len(aad) == 0 {
|
||||
return computed
|
||||
}
|
||||
out := make([]byte, len(computed)+1+len(aad))
|
||||
n := copy(out, computed)
|
||||
out[n] = tokens.Period
|
||||
copy(out[n+1:], aad)
|
||||
return out
|
||||
}
|
||||
|
||||
func (dc *decryptContext) DecryptMessage(buf []byte) ([]byte, error) {
|
||||
msg, err := parseJSONOrCompact(buf, true, dc.maxRecipients)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`jwe.Decrypt: failed to parse buffer: %w`, err)
|
||||
}
|
||||
|
||||
// Validate the "crit" header per RFC 7516 Section 4.1.13. The check
|
||||
// runs against the protected header only — RFC says "crit" MUST live
|
||||
// there — and short-circuits before any key-decrypt or content-decrypt
|
||||
// work happens.
|
||||
if dc.critValidation {
|
||||
if err := validateCritical(msg.protectedHeaders, dc.criticalExtensions); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Clone the shared (top-level) protected header as our working copy.
|
||||
// We deliberately do NOT merge msg.unprotectedHeaders (the shared,
|
||||
// top-level *unprotected* header) here: it is never covered by the
|
||||
// AEAD tag, so it must not contribute algorithm parameters.
|
||||
//
|
||||
// Per-recipient unprotected headers are a separate case — RFC 7516
|
||||
// §5.3 explicitly permits them to carry recipient-specific algorithm
|
||||
// parameters (alg, epk, p2s, p2c, iv, tag, apu, apv, …), and
|
||||
// decryptContent merges recipient.Headers() onto this base below.
|
||||
// That merge is bounded by WithMaxRecipients and, for PBES2, by
|
||||
// WithMaxPBES2Count (applied per recipient).
|
||||
h, err := msg.protectedHeaders.Clone()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to copy protected headers: %w`, err)
|
||||
}
|
||||
h, err = h.Merge(msg.unprotectedHeaders)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to merge headers for message decryption: %w`, err)
|
||||
return nil, fmt.Errorf(`jwe.Decrypt: failed to copy protected headers: %w`, err)
|
||||
}
|
||||
|
||||
var aad []byte
|
||||
@@ -335,7 +558,7 @@ func (dc *decryptContext) DecryptMessage(buf []byte) ([]byte, error) {
|
||||
var err error
|
||||
computedAad, err = msg.protectedHeaders.Encode()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to encode protected headers: %w`, err)
|
||||
return nil, fmt.Errorf(`jwe.Decrypt: failed to encode protected headers: %w`, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -345,16 +568,22 @@ func (dc *decryptContext) DecryptMessage(buf []byte) ([]byte, error) {
|
||||
if len(recipients) == 0 {
|
||||
r := NewRecipient()
|
||||
if err := r.SetHeaders(msg.protectedHeaders); err != nil {
|
||||
return nil, fmt.Errorf(`failed to set headers to recipient: %w`, err)
|
||||
return nil, fmt.Errorf(`jwe.Decrypt: failed to set headers to recipient: %w`, err)
|
||||
}
|
||||
recipients = append(recipients, r)
|
||||
}
|
||||
|
||||
errs := make([]error, 0, len(recipients))
|
||||
for _, recipient := range recipients {
|
||||
// Honor caller's deadline between recipients. Symmetric with
|
||||
// the per-keyProvider and per-(alg,key) checks in tryRecipient.
|
||||
if err := dc.ctx.Err(); err != nil {
|
||||
return nil, makeDecryptError(`%w`, err)
|
||||
}
|
||||
|
||||
decrypted, err := dc.tryRecipient(msg, recipient, h, aad, computedAad)
|
||||
if err != nil {
|
||||
errs = append(errs, recipientError{err})
|
||||
errs = append(errs, makeRecipientError(err))
|
||||
continue
|
||||
}
|
||||
if dc.dst != nil {
|
||||
@@ -364,19 +593,48 @@ func (dc *decryptContext) DecryptMessage(buf []byte) ([]byte, error) {
|
||||
}
|
||||
return decrypted, nil
|
||||
}
|
||||
return nil, fmt.Errorf(`failed to decrypt any of the recipients: %w`, errors.Join(errs...))
|
||||
// Bound the joined-error count so a hostile JWE with many recipients
|
||||
// can't produce an unbounded error string. Keep the first
|
||||
// decryptErrorJoinCap entries verbatim and replace the rest with a
|
||||
// single "... and N more" sentinel.
|
||||
return nil, fmt.Errorf(`jwe.Decrypt: failed to decrypt any of the recipients: %w`, joinDecryptErrors(errs))
|
||||
}
|
||||
|
||||
// decryptErrorJoinCap caps how many per-recipient constituent errors
|
||||
// get joined into the final Decrypt error so the resulting err.Error()
|
||||
// can't grow unboundedly under a hostile multi-recipient JWE.
|
||||
const decryptErrorJoinCap = 10
|
||||
|
||||
func joinDecryptErrors(errs []error) error {
|
||||
if len(errs) <= decryptErrorJoinCap {
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
kept := make([]error, decryptErrorJoinCap, decryptErrorJoinCap+1)
|
||||
copy(kept, errs[:decryptErrorJoinCap])
|
||||
kept = append(kept, fmt.Errorf("... and %d more error(s) suppressed", len(errs)-decryptErrorJoinCap))
|
||||
return errors.Join(kept...)
|
||||
}
|
||||
|
||||
func (dc *decryptContext) tryRecipient(msg *Message, recipient Recipient, protectedHeaders Headers, aad, computedAad []byte) ([]byte, error) {
|
||||
var tried int
|
||||
var lastError error
|
||||
for i, kp := range dc.keyProviders {
|
||||
// Honor caller's deadline between key providers.
|
||||
if err := dc.ctx.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var sink algKeySink
|
||||
if err := kp.FetchKeys(dc.ctx, &sink, recipient, msg); err != nil {
|
||||
return nil, fmt.Errorf(`key provider %d failed: %w`, i, err)
|
||||
}
|
||||
|
||||
for _, pair := range sink.list {
|
||||
// Honor caller's deadline between (alg,key) pairs.
|
||||
if err := dc.ctx.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tried++
|
||||
// alg is converted here because pair.alg is of type jwa.KeyAlgorithm.
|
||||
// this may seem ugly, but we're trying to avoid declaring separate
|
||||
@@ -403,6 +661,9 @@ func (dc *decryptContext) tryRecipient(msg *Message, recipient Recipient, protec
|
||||
}
|
||||
|
||||
func (dc *decryptContext) decryptContent(msg *Message, alg jwa.KeyEncryptionAlgorithm, key any, recipient Recipient, protectedHeaders Headers, aad, computedAad []byte) ([]byte, error) {
|
||||
if isKeyAlgorithmDisabled(alg) {
|
||||
return nil, makeDecryptError(`key encryption algorithm %q is disabled by jwe.WithDisabledKeyAlgorithms`, alg)
|
||||
}
|
||||
if jwkKey, ok := key.(jwk.Key); ok {
|
||||
var raw any
|
||||
if err := jwk.Export(jwkKey, &raw); err != nil {
|
||||
@@ -422,9 +683,34 @@ func (dc *decryptContext) decryptContent(msg *Message, alg jwa.KeyEncryptionAlgo
|
||||
Tag(msg.tag).
|
||||
CEK(dc.cek)
|
||||
|
||||
// The "alg" header can be in either protected/unprotected headers.
|
||||
// prefer per-recipient headers (as it might be the case that the algorithm differs
|
||||
// by each recipient), then look at protected headers.
|
||||
// RFC 7516 §7.2.1 requires header parameter names to be disjoint
|
||||
// across the protected, shared-unprotected, and per-recipient
|
||||
// header locations. For "alg" specifically, allowing protected
|
||||
// and per-recipient headers to declare conflicting values is an
|
||||
// algorithm-confusion vector: an attacker who can rewrite the
|
||||
// per-recipient (unprotected) location can claim a different alg
|
||||
// than the integrity-protected one, and the alg-match loop below
|
||||
// would silently break on whichever it sees first.
|
||||
//
|
||||
// Compact-form JWE legitimately has the same alg value in both
|
||||
// places — parseCompact synthesizes a per-recipient header by
|
||||
// cloning the protected header (minus enc), so a strict-disjoint
|
||||
// check would reject every compact JWE. We therefore allow the
|
||||
// duplication when the values agree, and reject only when they
|
||||
// disagree.
|
||||
if rh := recipient.Headers(); rh != nil {
|
||||
if recipAlg, recipHas := rh.Algorithm(); recipHas {
|
||||
if protectedAlg, protectedHas := protectedHeaders.Algorithm(); protectedHas && protectedAlg != recipAlg {
|
||||
return nil, makeDecryptError(`malformed JWE — "alg" header value differs between protected (%q) and per-recipient (%q) headers (RFC 7516 §7.2.1)`, protectedAlg, recipAlg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The "alg" header can be in either protected or per-recipient
|
||||
// headers. With disjointness enforced above, only one location can
|
||||
// have it, so iteration order does not affect security; we keep
|
||||
// per-recipient first to match the historical preference for
|
||||
// recipient-specific algs in multi-recipient JWE.
|
||||
var algMatched bool
|
||||
for _, hdr := range []Headers{recipient.Headers(), protectedHeaders} {
|
||||
v, ok := hdr.Algorithm()
|
||||
@@ -484,7 +770,10 @@ func (dc *decryptContext) decryptContent(msg *Message, alg jwa.KeyEncryptionAlgo
|
||||
}
|
||||
case jwa.A128GCMKW(), jwa.A192GCMKW(), jwa.A256GCMKW():
|
||||
var ivB64 string
|
||||
if err := h2.Get(InitializationVectorKey, &ivB64); err == nil {
|
||||
if h2.Has(InitializationVectorKey) {
|
||||
if err := h2.Get(InitializationVectorKey, &ivB64); err != nil {
|
||||
return nil, fmt.Errorf(`field %q is not a string: %w`, InitializationVectorKey, err)
|
||||
}
|
||||
iv, err := base64.DecodeString(ivB64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to b64-decode 'iv': %w`, err)
|
||||
@@ -492,7 +781,10 @@ func (dc *decryptContext) decryptContent(msg *Message, alg jwa.KeyEncryptionAlgo
|
||||
dec.KeyInitializationVector(iv)
|
||||
}
|
||||
var tagB64 string
|
||||
if err := h2.Get(TagKey, &tagB64); err == nil {
|
||||
if h2.Has(TagKey) {
|
||||
if err := h2.Get(TagKey, &tagB64); err != nil {
|
||||
return nil, fmt.Errorf(`field %q is not a string: %w`, TagKey, err)
|
||||
}
|
||||
tag, err := base64.DecodeString(tagB64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to b64-decode 'tag': %w`, err)
|
||||
@@ -505,39 +797,58 @@ func (dc *decryptContext) decryptContent(msg *Message, alg jwa.KeyEncryptionAlgo
|
||||
return nil, fmt.Errorf(`failed to get %q field`, SaltKey)
|
||||
}
|
||||
|
||||
// check if WithUseNumber is effective, because it will change the
|
||||
// type of the underlying value (#1140)
|
||||
var countFlt float64
|
||||
// Parse p2c into int64 directly. Float64 cannot represent
|
||||
// integers above 2^53 exactly; comparing a parsed value
|
||||
// against a high MaxPBES2Count cap in float-space and then
|
||||
// casting via int(...) lets out-of-range values silently
|
||||
// round into the accepted range when callers raise the cap
|
||||
// past 2^53. int64 keeps the bound check exact.
|
||||
var count int64
|
||||
if json.UseNumber() {
|
||||
var count json.Number
|
||||
if err := h2.Get(CountKey, &count); err != nil {
|
||||
var n json.Number
|
||||
if err := h2.Get(CountKey, &n); err != nil {
|
||||
return nil, fmt.Errorf(`failed to get %q field`, CountKey)
|
||||
}
|
||||
v, err := count.Float64()
|
||||
c, err := n.Int64()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert 'p2c' to float64: %w", err)
|
||||
return nil, fmt.Errorf(`invalid 'p2c' value: %q is not a valid integer: %w`, n.String(), err)
|
||||
}
|
||||
countFlt = v
|
||||
count = c
|
||||
} else {
|
||||
var count float64
|
||||
if err := h2.Get(CountKey, &count); err != nil {
|
||||
var v float64
|
||||
if err := h2.Get(CountKey, &v); err != nil {
|
||||
return nil, fmt.Errorf(`failed to get %q field`, CountKey)
|
||||
}
|
||||
countFlt = count
|
||||
if math.IsNaN(v) || math.IsInf(v, 0) || math.Trunc(v) != v {
|
||||
return nil, fmt.Errorf(`invalid 'p2c' value: not a positive integer (got %v)`, v)
|
||||
}
|
||||
// Use explicit float-domain bounds (2^63 / -2^63) so
|
||||
// the comparison is platform-independent and does not
|
||||
// go through math.MaxInt64's implicit conversion.
|
||||
const (
|
||||
int64MaxAsFloat = float64(1 << 63) // 2^63, smallest float > MaxInt64
|
||||
int64MinAsFloat = -int64MaxAsFloat // -2^63, exact float = MinInt64
|
||||
)
|
||||
if v >= int64MaxAsFloat || v < int64MinAsFloat {
|
||||
return nil, fmt.Errorf(`invalid 'p2c' value: not representable as int64 (got %v)`, v)
|
||||
}
|
||||
count = int64(v)
|
||||
}
|
||||
|
||||
muSettings.RLock()
|
||||
maxCount := maxPBES2Count
|
||||
muSettings.RUnlock()
|
||||
if countFlt > float64(maxCount) {
|
||||
return nil, fmt.Errorf("invalid 'p2c' value")
|
||||
maxCount := dc.maxPBES2Count
|
||||
minCount := dc.minPBES2Count
|
||||
if count < int64(minCount) {
|
||||
return nil, fmt.Errorf(`invalid 'p2c' value: %d is below WithMinPBES2Count=%d (RFC 7518 §4.8.1.2 floor; loosen via jwe.WithMinPBES2Count)`, count, minCount)
|
||||
}
|
||||
if count > int64(maxCount) {
|
||||
return nil, fmt.Errorf(`invalid 'p2c' value: %d exceeds WithMaxPBES2Count=%d (DoS amplification cap; raise via jwe.WithMaxPBES2Count)`, count, maxCount)
|
||||
}
|
||||
salt, err := base64.DecodeString(saltB64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to b64-decode 'salt': %w`, err)
|
||||
}
|
||||
dec.KeySalt(salt)
|
||||
dec.KeyCount(int(countFlt))
|
||||
dec.KeyCount(int(count))
|
||||
}
|
||||
|
||||
plaintext, err := dec.Decrypt(recipient, msg.cipherText, msg)
|
||||
@@ -548,7 +859,7 @@ func (dc *decryptContext) decryptContent(msg *Message, alg jwa.KeyEncryptionAlgo
|
||||
if v, ok := h2.Compression(); ok && v == jwa.Deflate() {
|
||||
buf, err := uncompress(plaintext, dc.maxDecompressBufferSize)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`jwe.Derypt: failed to uncompress payload: %w`, err)
|
||||
return nil, fmt.Errorf(`jwe.Decrypt: failed to uncompress payload: %w`, err)
|
||||
}
|
||||
plaintext = buf
|
||||
}
|
||||
@@ -565,6 +876,7 @@ type encryptContext struct {
|
||||
calg jwa.ContentEncryptionAlgorithm
|
||||
compression jwa.CompressionAlgorithm
|
||||
format int
|
||||
pbes2Count int
|
||||
builders []*recipientBuilder
|
||||
protected Headers
|
||||
legacyHeaderMerging bool
|
||||
@@ -584,6 +896,7 @@ func freeEncryptContext(ec *encryptContext) *encryptContext {
|
||||
ec.calg = jwa.A256GCM()
|
||||
ec.compression = jwa.NoCompress()
|
||||
ec.format = fmtCompact
|
||||
ec.pbes2Count = 0
|
||||
ec.builders = ec.builders[:0]
|
||||
ec.protected = nil
|
||||
return ec
|
||||
@@ -591,6 +904,7 @@ func freeEncryptContext(ec *encryptContext) *encryptContext {
|
||||
|
||||
func (ec *encryptContext) ProcessOptions(options []EncryptOption) error {
|
||||
ec.legacyHeaderMerging = true
|
||||
ec.pbes2Count = int(pbes2Count.Load())
|
||||
var mergeProtected bool
|
||||
var useRawCEK bool
|
||||
for _, option := range options {
|
||||
@@ -604,6 +918,9 @@ func (ec *encryptContext) ProcessOptions(options []EncryptOption) error {
|
||||
if !ok {
|
||||
return fmt.Errorf("jwe.encrypt: WithKey() option must be specified using jwa.KeyEncryptionAlgorithm (got %T)", wk.alg)
|
||||
}
|
||||
if err := validateAlgorithmForKey(v, wk.key); err != nil {
|
||||
return fmt.Errorf("jwe.WithKey: %w", err)
|
||||
}
|
||||
if v == jwa.DIRECT() || v == jwa.ECDH_ES() {
|
||||
useRawCEK = true
|
||||
}
|
||||
@@ -612,6 +929,14 @@ func (ec *encryptContext) ProcessOptions(options []EncryptOption) error {
|
||||
key: wk.key,
|
||||
headers: wk.headers,
|
||||
})
|
||||
case identPBES2Count{}:
|
||||
var v int
|
||||
if err := option.Value(&v); err != nil {
|
||||
return fmt.Errorf("jwe.encrypt: WithPBES2Count must be int: %w", err)
|
||||
}
|
||||
if v > 0 {
|
||||
ec.pbes2Count = v
|
||||
}
|
||||
case identContentEncryptionAlgorithm{}:
|
||||
var c jwa.ContentEncryptionAlgorithm
|
||||
if err := option.Value(&c); err != nil {
|
||||
@@ -671,7 +996,7 @@ func (ec *encryptContext) ProcessOptions(options []EncryptOption) error {
|
||||
|
||||
if useRawCEK {
|
||||
if len(ec.builders) != 1 {
|
||||
return fmt.Errorf(`multiple recipients for ECDH-ES/DIRECT mode supported`)
|
||||
return fmt.Errorf(`multiple recipients for ECDH-ES/DIRECT mode are not supported`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -714,10 +1039,14 @@ func freeHeaders(h Headers) Headers {
|
||||
var recipientPool = pool.New(NewRecipient, freeRecipient)
|
||||
|
||||
func freeRecipient(r Recipient) Recipient {
|
||||
// Return the recipient's headers to headerPool and install a fresh
|
||||
// instance so the next recipientPool.Get() never hands out a
|
||||
// pointer the caller may still hold a reference to. This is safe
|
||||
// because WithPerRecipientHeaders clones the caller-supplied
|
||||
// Headers, so anything we receive here is already library-owned.
|
||||
if h := r.Headers(); h != nil {
|
||||
if c, ok := h.(interface{ clear() }); ok {
|
||||
c.clear()
|
||||
}
|
||||
headerPool.Put(h)
|
||||
_ = r.SetHeaders(headerPool.Get())
|
||||
}
|
||||
|
||||
if sr, ok := r.(*stdRecipient); ok {
|
||||
@@ -777,6 +1106,7 @@ func (ec *encryptContext) EncryptMessage(payload []byte, cek []byte) ([]byte, er
|
||||
defer recipientSlicePool.Put(recipients)
|
||||
|
||||
for i, builder := range ec.builders {
|
||||
builder.pbes2Count = ec.pbes2Count
|
||||
r := recipientPool.Get()
|
||||
defer recipientPool.Put(r)
|
||||
|
||||
@@ -794,6 +1124,10 @@ func (ec *encryptContext) EncryptMessage(payload []byte, cek []byte) ([]byte, er
|
||||
}
|
||||
}
|
||||
|
||||
if len(cek) != contentcrypt.KeySize() {
|
||||
return nil, fmt.Errorf(`content encryption key length %d does not match enc %q (expected %d bytes)`, len(cek), ec.calg.String(), contentcrypt.KeySize())
|
||||
}
|
||||
|
||||
if err := protected.Set(ContentEncryptionKey, ec.calg); err != nil {
|
||||
return nil, fmt.Errorf(`failed to set "enc" in protected header: %w`, err)
|
||||
}
|
||||
@@ -869,6 +1203,13 @@ func (ec *encryptContext) EncryptMessage(payload []byte, cek []byte) ([]byte, er
|
||||
return nil, fmt.Errorf(`failed to encrypt payload: %w`, err)
|
||||
}
|
||||
|
||||
// Fast path for compact serialization: assemble directly from
|
||||
// pre-encoded headers and raw fields, avoiding the full Message
|
||||
// construction and redundant header re-encoding that Compact() does.
|
||||
if ec.format == fmtCompact {
|
||||
return compactSerialize(aad, recipients[0].EncryptedKey(), iv, ciphertext, tag), nil
|
||||
}
|
||||
|
||||
msg := msgPool.Get()
|
||||
defer msgPool.Put(msg)
|
||||
|
||||
@@ -889,8 +1230,6 @@ func (ec *encryptContext) EncryptMessage(payload []byte, cek []byte) ([]byte, er
|
||||
}
|
||||
|
||||
switch ec.format {
|
||||
case fmtCompact:
|
||||
return Compact(msg)
|
||||
case fmtJSON:
|
||||
return json.Marshal(msg)
|
||||
case fmtJSONPretty:
|
||||
@@ -904,11 +1243,11 @@ func (ec *encryptContext) EncryptMessage(payload []byte, cek []byte) ([]byte, er
|
||||
// payload (e.g. the key encryption algorithm and the corresponding
|
||||
// key to decrypt the JWE message) in its optional arguments. See
|
||||
// the examples and list of options that return a DecryptOption for possible
|
||||
// values. Upon successful decryptiond returns the decrypted payload.
|
||||
// values. Upon successful decryption returns the decrypted payload.
|
||||
//
|
||||
// The JWE message can be either compact or full JSON format.
|
||||
//
|
||||
// When using `jwe.WithKeyEncryptionAlgorithm()`, you can pass a `jwa.KeyAlgorithm`
|
||||
// When using `jwe.WithKey()`, you can pass a `jwa.KeyAlgorithm`
|
||||
// for convenience: this is mainly to allow you to directly pass the result of `(jwk.Key).Algorithm()`.
|
||||
// However, do note that while `(jwk.Key).Algorithm()` could very well contain key encryption
|
||||
// algorithms, it could also contain other types of values, such as _signature algorithms_.
|
||||
@@ -929,17 +1268,31 @@ func (ec *encryptContext) EncryptMessage(payload []byte, cek []byte) ([]byte, er
|
||||
//
|
||||
// jwe.Settings(jwe.WithMaxDecompressBufferSize(10*1024*1024)) // changes value globally
|
||||
// jwe.Decrypt(..., jwe.WithMaxDecompressBufferSize(250*1024)) // changes just for this call
|
||||
//
|
||||
// PBES2 amplification: PBES2 algorithms (PBES2-HS256+A128KW, etc.)
|
||||
// derive the CEK via PBKDF2 with the iteration count taken from the
|
||||
// JWE's `p2c` header. An attacker-controlled iteration count multiplied
|
||||
// by `WithMaxRecipients` is the major CPU-amplification vector on the
|
||||
// decrypt side. Bound it via `WithMaxPBES2Count` (default 1,000,000)
|
||||
// and reject too-low counts via `WithMinPBES2Count` (default 1000;
|
||||
// RFC 7518 §4.8.1.2 floor — note OWASP 2023 recommends ≥600,000 for
|
||||
// production password-derived key material). Both options accept a
|
||||
// `Settings()` global or a per-call value.
|
||||
func Decrypt(buf []byte, options ...DecryptOption) ([]byte, error) {
|
||||
dc := decryptContextPool.Get()
|
||||
defer decryptContextPool.Put(dc)
|
||||
|
||||
if err := dc.ProcessOptions(options); err != nil {
|
||||
return nil, decryptError{fmt.Errorf(`jwe.Decrypt: failed to process options: %w`, err)}
|
||||
return nil, makeDecryptError(`failed to process options: %w`, err)
|
||||
}
|
||||
|
||||
ret, err := dc.DecryptMessage(buf)
|
||||
if err != nil {
|
||||
return nil, decryptError{fmt.Errorf(`jwe.Decrypt: %w`, err)}
|
||||
// DecryptMessage already returns errors prefixed with
|
||||
// "jwe.Decrypt:" — wrap as decryptError without adding a
|
||||
// second prefix, otherwise multi-recipient errors carry
|
||||
// the "jwe.Decrypt:" string multiple times.
|
||||
return nil, decryptError{err}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
@@ -947,18 +1300,18 @@ func Decrypt(buf []byte, options ...DecryptOption) ([]byte, error) {
|
||||
// Parse parses the JWE message into a Message object. The JWE message
|
||||
// can be either compact or full JSON format.
|
||||
//
|
||||
// Parse() currently does not take any options, but the API accepts it
|
||||
// in anticipation of future addition.
|
||||
// Bounding the input size is the caller's responsibility; this function
|
||||
// trusts the caller-provided buf. See docs/13-input-size.md.
|
||||
func Parse(buf []byte, _ ...ParseOption) (*Message, error) {
|
||||
return parseJSONOrCompact(buf, false)
|
||||
return parseJSONOrCompact(buf, false, int(maxRecipients.Load()))
|
||||
}
|
||||
|
||||
// errors are wrapped within this function, because we call it directly
|
||||
// from Decrypt as well.
|
||||
func parseJSONOrCompact(buf []byte, storeProtectedHeaders bool) (*Message, error) {
|
||||
func parseJSONOrCompact(buf []byte, storeProtectedHeaders bool, maxR int) (*Message, error) {
|
||||
buf = bytes.TrimSpace(buf)
|
||||
if len(buf) == 0 {
|
||||
return nil, parseError{fmt.Errorf(`jwe.Parse: empty buffer`)}
|
||||
return nil, makeParseError(`jwe.Parse`, `empty buffer`)
|
||||
}
|
||||
|
||||
var msg *Message
|
||||
@@ -970,29 +1323,38 @@ func parseJSONOrCompact(buf []byte, storeProtectedHeaders bool) (*Message, error
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, parseError{fmt.Errorf(`jwe.Parse: %w`, err)}
|
||||
return nil, makeParseError(`jwe.Parse`, `%w`, err)
|
||||
}
|
||||
|
||||
if maxR > 0 && len(msg.recipients) > maxR {
|
||||
return nil, makeParseError(`jwe.Parse`, `too many recipients in JWE message (%d > %d)`, len(msg.recipients), maxR)
|
||||
}
|
||||
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
// ParseString is the same as Parse, but takes a string.
|
||||
func ParseString(s string) (*Message, error) {
|
||||
func ParseString(s string, _ ...ParseOption) (*Message, error) {
|
||||
msg, err := Parse([]byte(s))
|
||||
if err != nil {
|
||||
return nil, parseError{fmt.Errorf(`jwe.ParseString: %w`, err)}
|
||||
return nil, makeParseError(`jwe.ParseString`, `%w`, err)
|
||||
}
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
// ParseReader is the same as Parse, but takes an io.Reader.
|
||||
func ParseReader(src io.Reader) (*Message, error) {
|
||||
//
|
||||
// Bounding the input size is the caller's responsibility: wrap src with
|
||||
// [io.LimitReader] or [net/http.MaxBytesReader] before passing it in. See
|
||||
// docs/13-input-size.md for the rationale.
|
||||
func ParseReader(src io.Reader, _ ...ParseOption) (*Message, error) {
|
||||
buf, err := io.ReadAll(src)
|
||||
if err != nil {
|
||||
return nil, parseError{fmt.Errorf(`jwe.ParseReader: failed to read from io.Reader: %w`, err)}
|
||||
return nil, makeParseError(`jwe.ParseReader`, `failed to read from io.Reader: %w`, err)
|
||||
}
|
||||
msg, err := Parse(buf)
|
||||
if err != nil {
|
||||
return nil, parseError{fmt.Errorf(`jwe.ParseReader: %w`, err)}
|
||||
return nil, makeParseError(`jwe.ParseReader`, `%w`, err)
|
||||
}
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
6
vendor/github.com/lestrrat-go/jwx/v3/jwe/jwebb/key_decrypt_asymmetric.go
generated
vendored
6
vendor/github.com/lestrrat-go/jwx/v3/jwe/jwebb/key_decrypt_asymmetric.go
generated
vendored
@@ -113,8 +113,10 @@ func KeyDecryptRSA15(_, enckey []byte, privkeyif any, keysize int) ([]byte, erro
|
||||
return nil, fmt.Errorf(`jwebb.KeyDecryptRSA15: %w`, err)
|
||||
}
|
||||
|
||||
// Perform some input validation.
|
||||
expectedlen := privkey.PublicKey.N.BitLen() / tokens.BitsPerByte
|
||||
// Perform some input validation. Use privkey.Size() which applies
|
||||
// ceiling division on the modulus bit length, avoiding silent truncation
|
||||
// if N.BitLen() is not a multiple of 8.
|
||||
expectedlen := privkey.Size()
|
||||
if expectedlen != len(enckey) {
|
||||
// Input size is incorrect, the encrypted payload should always match
|
||||
// the size of the public modulus (e.g. using a 2048 bit key will
|
||||
|
||||
7
vendor/github.com/lestrrat-go/jwx/v3/jwe/jwebb/key_decrypt_symmetric.go
generated
vendored
7
vendor/github.com/lestrrat-go/jwx/v3/jwe/jwebb/key_decrypt_symmetric.go
generated
vendored
@@ -78,8 +78,11 @@ func KeyDecryptAESGCMKW(recipientKey, _ []byte, _ string, sharedkey []byte, iv [
|
||||
return nil, fmt.Errorf(`failed to create new GCM wrap: %w`, err)
|
||||
}
|
||||
|
||||
// Combine recipient key and tag for GCM decryption
|
||||
ciphertext := recipientKey[:]
|
||||
// Combine recipient key and tag for GCM decryption. Allocate a fresh
|
||||
// buffer so we never alias into recipientKey's backing array, which
|
||||
// is owned by the parsed message.
|
||||
ciphertext := make([]byte, 0, len(recipientKey)+len(tag))
|
||||
ciphertext = append(ciphertext, recipientKey...)
|
||||
ciphertext = append(ciphertext, tag...)
|
||||
|
||||
jek, err := aesgcm.Open(nil, iv, ciphertext, nil)
|
||||
|
||||
12
vendor/github.com/lestrrat-go/jwx/v3/jwe/jwebb/key_encrypt_asymmetric.go
generated
vendored
12
vendor/github.com/lestrrat-go/jwx/v3/jwe/jwebb/key_encrypt_asymmetric.go
generated
vendored
@@ -65,9 +65,9 @@ func generateECDHESKeyECDSA(alg string, calg string, keysize uint32, pubkey *ecd
|
||||
}
|
||||
|
||||
// generateECDHESKeyX25519 generates the key material for X25519 keys using ECDH-ES
|
||||
func generateECDHESKeyX25519(alg string, calg string, keysize uint32, pubkey *ecdh.PublicKey) (keygen.ByteWithECPublicKey, error) {
|
||||
func generateECDHESKeyX25519(alg string, calg string, keysize uint32, pubkey *ecdh.PublicKey, apu, apv []byte) (keygen.ByteWithECPublicKey, error) {
|
||||
// Generate the key directly
|
||||
kg, err := keygen.X25519(alg, calg, int(keysize), pubkey)
|
||||
kg, err := keygen.X25519(alg, calg, int(keysize), pubkey, apu, apv)
|
||||
if err != nil {
|
||||
return keygen.ByteWithECPublicKey{}, fmt.Errorf(`failed to generate X25519 key: %w`, err)
|
||||
}
|
||||
@@ -103,8 +103,8 @@ func KeyEncryptECDHESKeyWrapECDSA(cek []byte, alg string, apu, apv []byte, pubke
|
||||
}
|
||||
|
||||
// KeyEncryptECDHESKeyWrapX25519 encrypts the CEK using ECDH-ES with key wrapping for X25519 keys
|
||||
func KeyEncryptECDHESKeyWrapX25519(cek []byte, alg string, _ []byte, _ []byte, pubkey *ecdh.PublicKey, keysize uint32, calg string) (keygen.ByteSource, error) {
|
||||
bwpk, err := generateECDHESKeyX25519(alg, calg, keysize, pubkey)
|
||||
func KeyEncryptECDHESKeyWrapX25519(cek []byte, alg string, apu []byte, apv []byte, pubkey *ecdh.PublicKey, keysize uint32, calg string) (keygen.ByteSource, error) {
|
||||
bwpk, err := generateECDHESKeyX25519(alg, calg, keysize, pubkey, apu, apv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -136,8 +136,8 @@ func KeyEncryptECDHESECDSA(_ []byte, alg string, apu, apv []byte, pubkey *ecdsa.
|
||||
}
|
||||
|
||||
// KeyEncryptECDHESX25519 encrypts using ECDH-ES direct (no key wrapping) for X25519 keys
|
||||
func KeyEncryptECDHESX25519(_ []byte, alg string, _, _ []byte, pubkey *ecdh.PublicKey, keysize uint32, calg string) (keygen.ByteSource, error) {
|
||||
bwpk, err := generateECDHESKeyX25519(alg, calg, keysize, pubkey)
|
||||
func KeyEncryptECDHESX25519(_ []byte, alg string, apu, apv []byte, pubkey *ecdh.PublicKey, keysize uint32, calg string) (keygen.ByteSource, error) {
|
||||
bwpk, err := generateECDHESKeyX25519(alg, calg, keysize, pubkey, apu, apv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
11
vendor/github.com/lestrrat-go/jwx/v3/jwe/jwebb/key_encrypt_symmetric.go
generated
vendored
11
vendor/github.com/lestrrat-go/jwx/v3/jwe/jwebb/key_encrypt_symmetric.go
generated
vendored
@@ -35,8 +35,11 @@ func KeyEncryptDirect(_ []byte, _ string, sharedkey []byte) (keygen.ByteSource,
|
||||
return keygen.ByteKey(sharedkey), nil
|
||||
}
|
||||
|
||||
// KeyEncryptPBES2 encrypts the CEK using PBES2 password-based encryption
|
||||
func KeyEncryptPBES2(cek []byte, alg string, password []byte) (keygen.ByteSource, error) {
|
||||
// KeyEncryptPBES2 encrypts the CEK using PBES2 password-based encryption.
|
||||
// count is the PBKDF2 iteration count. If count <= 0, tokens.PBES2DefaultIterations
|
||||
// is used as a safety fallback; public callers go through jwe.Encrypt / jwe.Settings
|
||||
// and always provide a positive value via the WithPBES2Count option.
|
||||
func KeyEncryptPBES2(cek []byte, alg string, password []byte, count int) (keygen.ByteSource, error) {
|
||||
var hashFunc func() hash.Hash
|
||||
var keylen int
|
||||
|
||||
@@ -54,7 +57,9 @@ func KeyEncryptPBES2(cek []byte, alg string, password []byte) (keygen.ByteSource
|
||||
return nil, fmt.Errorf(`unsupported PBES2 algorithm: %s`, alg)
|
||||
}
|
||||
|
||||
count := tokens.PBES2DefaultIterations
|
||||
if count <= 0 {
|
||||
count = tokens.PBES2DefaultIterations
|
||||
}
|
||||
salt := make([]byte, keylen)
|
||||
_, err := io.ReadFull(rand.Reader, salt)
|
||||
if err != nil {
|
||||
|
||||
22
vendor/github.com/lestrrat-go/jwx/v3/jwe/jwebb/keywrap.go
generated
vendored
22
vendor/github.com/lestrrat-go/jwx/v3/jwe/jwebb/keywrap.go
generated
vendored
@@ -13,6 +13,9 @@ import (
|
||||
var keywrapDefaultIV = []byte{0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6}
|
||||
|
||||
func Wrap(kek cipher.Block, cek []byte) ([]byte, error) {
|
||||
if len(cek) < tokens.KeywrapChunkLen {
|
||||
return nil, fmt.Errorf(`keywrap input must be at least %d bytes`, tokens.KeywrapChunkLen)
|
||||
}
|
||||
if len(cek)%tokens.KeywrapBlockSize != 0 {
|
||||
return nil, fmt.Errorf(`keywrap input must be %d byte blocks`, tokens.KeywrapBlockSize)
|
||||
}
|
||||
@@ -25,15 +28,11 @@ func Wrap(kek cipher.Block, cek []byte) ([]byte, error) {
|
||||
copy(r[i], cek[i*tokens.KeywrapChunkLen:])
|
||||
}
|
||||
|
||||
buffer := pool.ByteSlice().GetCapacity(tokens.KeywrapChunkLen * 2)
|
||||
buffer := pool.ByteSlice().GetCapacity(tokens.KeywrapChunkLen * 2)[:tokens.KeywrapChunkLen*2]
|
||||
defer pool.ByteSlice().Put(buffer)
|
||||
// the byte slice has the capacity, but len is 0
|
||||
buffer = buffer[:tokens.KeywrapChunkLen*2]
|
||||
|
||||
tBytes := pool.ByteSlice().GetCapacity(tokens.KeywrapChunkLen)
|
||||
tBytes := pool.ByteSlice().GetCapacity(tokens.KeywrapChunkLen)[:tokens.KeywrapChunkLen]
|
||||
defer pool.ByteSlice().Put(tBytes)
|
||||
// the byte slice has the capacity, but len is 0
|
||||
tBytes = tBytes[:tokens.KeywrapChunkLen]
|
||||
|
||||
copy(buffer, keywrapDefaultIV)
|
||||
|
||||
@@ -60,6 +59,9 @@ func Wrap(kek cipher.Block, cek []byte) ([]byte, error) {
|
||||
}
|
||||
|
||||
func Unwrap(block cipher.Block, ciphertxt []byte) ([]byte, error) {
|
||||
if len(ciphertxt) < 2*tokens.KeywrapChunkLen {
|
||||
return nil, fmt.Errorf(`keyunwrap input must be at least %d bytes`, 2*tokens.KeywrapChunkLen)
|
||||
}
|
||||
if len(ciphertxt)%tokens.KeywrapChunkLen != 0 {
|
||||
return nil, fmt.Errorf(`keyunwrap input must be %d byte blocks`, tokens.KeywrapChunkLen)
|
||||
}
|
||||
@@ -72,15 +74,11 @@ func Unwrap(block cipher.Block, ciphertxt []byte) ([]byte, error) {
|
||||
copy(r[i], ciphertxt[(i+1)*tokens.KeywrapChunkLen:])
|
||||
}
|
||||
|
||||
buffer := pool.ByteSlice().GetCapacity(tokens.KeywrapChunkLen * 2)
|
||||
buffer := pool.ByteSlice().GetCapacity(tokens.KeywrapChunkLen * 2)[:tokens.KeywrapChunkLen*2]
|
||||
defer pool.ByteSlice().Put(buffer)
|
||||
// the byte slice has the capacity, but len is 0
|
||||
buffer = buffer[:tokens.KeywrapChunkLen*2]
|
||||
|
||||
tBytes := pool.ByteSlice().GetCapacity(tokens.KeywrapChunkLen)
|
||||
tBytes := pool.ByteSlice().GetCapacity(tokens.KeywrapChunkLen)[:tokens.KeywrapChunkLen]
|
||||
defer pool.ByteSlice().Put(tBytes)
|
||||
// the byte slice has the capacity, but len is 0
|
||||
tBytes = tBytes[:tokens.KeywrapChunkLen]
|
||||
|
||||
copy(buffer[:tokens.KeywrapChunkLen], ciphertxt[:tokens.KeywrapChunkLen])
|
||||
|
||||
|
||||
44
vendor/github.com/lestrrat-go/jwx/v3/jwe/key_provider.go
generated
vendored
44
vendor/github.com/lestrrat-go/jwx/v3/jwe/key_provider.go
generated
vendored
@@ -2,6 +2,7 @@ package jwe
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
@@ -106,10 +107,11 @@ type keySetProvider struct {
|
||||
requireKid bool
|
||||
}
|
||||
|
||||
func (kp *keySetProvider) selectKey(sink KeySink, key jwk.Key, _ Recipient, _ *Message) error {
|
||||
func (kp *keySetProvider) selectKey(sink KeySink, key jwk.Key, r Recipient, msg *Message) error {
|
||||
if usage, ok := key.KeyUsage(); ok {
|
||||
if usage != "" && usage != jwk.ForEncryption.String() {
|
||||
return nil
|
||||
kid, _ := key.KeyID()
|
||||
return fmt.Errorf(`key %q has key_use=%q (expected %q for encryption)`, kid, usage, jwk.ForEncryption.String())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,7 +125,30 @@ func (kp *keySetProvider) selectKey(sink KeySink, key jwk.Key, _ Recipient, _ *M
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
// The JWK has no "alg" — common for IdP-published encryption keys.
|
||||
// Fall back to the recipient's declared "alg" (per-recipient header,
|
||||
// then protected header), matching the preference order used when
|
||||
// jwe.Decrypt verifies the chosen key's algorithm against the message.
|
||||
// jwe.Decrypt re-checks agreement before use, so trusting the header
|
||||
// alg here does not widen the attack surface.
|
||||
for _, hdr := range []Headers{r.Headers(), msg.ProtectedHeaders()} {
|
||||
if hdr == nil {
|
||||
continue
|
||||
}
|
||||
v, ok := hdr.Algorithm()
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
kalg, ok := jwa.LookupKeyEncryptionAlgorithm(v.String())
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
sink.Key(kalg, key)
|
||||
return nil
|
||||
}
|
||||
|
||||
kid, _ := key.KeyID()
|
||||
return fmt.Errorf(`key %q in set has no "alg" field and the JWE message has no recoverable "alg" header; declare "alg" on the JWK or use jwe.WithKey(alg, key) directly`, kid)
|
||||
}
|
||||
|
||||
func (kp *keySetProvider) FetchKeys(_ context.Context, sink KeySink, r Recipient, msg *Message) error {
|
||||
@@ -144,11 +169,22 @@ func (kp *keySetProvider) FetchKeys(_ context.Context, sink KeySink, r Recipient
|
||||
return kp.selectKey(sink, key, r, msg)
|
||||
}
|
||||
|
||||
// Collect per-key errors and surface them via errors.Join when
|
||||
// nothing produced a usable (alg, key) pair. Without this, a
|
||||
// caller debugging "why didn't my keyset match" got no signal.
|
||||
var perKeyErrs []error
|
||||
var emitted bool
|
||||
for i := range kp.set.Len() {
|
||||
key, _ := kp.set.Key(i)
|
||||
if err := kp.selectKey(sink, key, r, msg); err != nil {
|
||||
err := kp.selectKey(sink, key, r, msg)
|
||||
if err != nil {
|
||||
perKeyErrs = append(perKeyErrs, err)
|
||||
continue
|
||||
}
|
||||
emitted = true
|
||||
}
|
||||
if !emitted && len(perKeyErrs) > 0 {
|
||||
return fmt.Errorf(`failed to select any usable key from set of %d (no key produced a usable (alg, key) pair): %w`, kp.set.Len(), errors.Join(perKeyErrs...))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
76
vendor/github.com/lestrrat-go/jwx/v3/jwe/message.go
generated
vendored
76
vendor/github.com/lestrrat-go/jwx/v3/jwe/message.go
generated
vendored
@@ -1,6 +1,7 @@
|
||||
package jwe
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
@@ -71,8 +72,7 @@ func (r *stdRecipient) MarshalJSON() ([]byte, error) {
|
||||
buf.WriteString(base64.EncodeToString(r.encryptedKey))
|
||||
buf.WriteString(`"}`)
|
||||
|
||||
ret := make([]byte, buf.Len())
|
||||
copy(ret, buf.Bytes())
|
||||
ret := bytes.Clone(buf.Bytes())
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
@@ -248,8 +248,7 @@ func (m *Message) MarshalJSON() ([]byte, error) {
|
||||
if aad := m.AuthenticatedData(); len(aad) > 0 {
|
||||
aad = base64.Encode(aad)
|
||||
if encodedProtectedHeaders != nil {
|
||||
tmp := append(encodedProtectedHeaders, tokens.Period)
|
||||
aad = append(tmp, aad...)
|
||||
aad = concatAAD(encodedProtectedHeaders, aad)
|
||||
}
|
||||
|
||||
buf.Reset()
|
||||
@@ -344,8 +343,7 @@ func (m *Message) MarshalJSON() ([]byte, error) {
|
||||
}
|
||||
fmt.Fprintf(buf, `}`)
|
||||
|
||||
ret := make([]byte, buf.Len())
|
||||
copy(ret, buf.Bytes())
|
||||
ret := bytes.Clone(buf.Bytes())
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
@@ -422,29 +420,36 @@ func (m *Message) UnmarshalJSON(buf []byte) error {
|
||||
m.authenticatedData = v
|
||||
}
|
||||
|
||||
if src := proxy.CipherText; len(src) > 0 {
|
||||
v, err := base64.DecodeString(src)
|
||||
if err != nil {
|
||||
return fmt.Errorf(`failed to decode "ciphertext": %w`, err)
|
||||
}
|
||||
m.cipherText = v
|
||||
// RFC 7516 §7.2: "ciphertext", "iv", and "tag" MUST be present and
|
||||
// non-empty for any AEAD-protected JWE. Reject missing/empty values
|
||||
// here so that a zero-length authentication tag cannot reach the
|
||||
// AEAD verification code path.
|
||||
if len(proxy.CipherText) == 0 {
|
||||
return fmt.Errorf(`missing or empty "ciphertext" field`)
|
||||
}
|
||||
ctbuf, err := base64.DecodeString(proxy.CipherText)
|
||||
if err != nil {
|
||||
return fmt.Errorf(`failed to decode "ciphertext": %w`, err)
|
||||
}
|
||||
m.cipherText = ctbuf
|
||||
|
||||
if src := proxy.InitializationVector; len(src) > 0 {
|
||||
v, err := base64.DecodeString(src)
|
||||
if err != nil {
|
||||
return fmt.Errorf(`failed to decode "iv": %w`, err)
|
||||
}
|
||||
m.initializationVector = v
|
||||
if len(proxy.InitializationVector) == 0 {
|
||||
return fmt.Errorf(`missing or empty "iv" field`)
|
||||
}
|
||||
ivbuf, err := base64.DecodeString(proxy.InitializationVector)
|
||||
if err != nil {
|
||||
return fmt.Errorf(`failed to decode "iv": %w`, err)
|
||||
}
|
||||
m.initializationVector = ivbuf
|
||||
|
||||
if src := proxy.Tag; len(src) > 0 {
|
||||
v, err := base64.DecodeString(src)
|
||||
if err != nil {
|
||||
return fmt.Errorf(`failed to decode "tag": %w`, err)
|
||||
}
|
||||
m.tag = v
|
||||
if len(proxy.Tag) == 0 {
|
||||
return fmt.Errorf(`missing or empty "tag" field`)
|
||||
}
|
||||
tagbuf, err := base64.DecodeString(proxy.Tag)
|
||||
if err != nil {
|
||||
return fmt.Errorf(`failed to decode "tag": %w`, err)
|
||||
}
|
||||
m.tag = tagbuf
|
||||
|
||||
m.protectedHeaders = h
|
||||
if m.storeProtectedHeaders {
|
||||
@@ -554,7 +559,26 @@ func Compact(m *Message, _ ...CompactOption) ([]byte, error) {
|
||||
buf.WriteByte(tokens.Period)
|
||||
buf.Write(tag)
|
||||
|
||||
result := make([]byte, buf.Len())
|
||||
copy(result, buf.Bytes())
|
||||
result := bytes.Clone(buf.Bytes())
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// compactSerialize assembles a JWE compact serialization from pre-encoded
|
||||
// protected headers (aad) and raw binary fields. This avoids the redundant
|
||||
// Clone/Merge/Encode cycle that Compact() performs.
|
||||
func compactSerialize(aad, encryptedKey, iv, ciphertext, tag []byte) []byte {
|
||||
size := len(aad) + base64.EncodedLen(len(encryptedKey)) + base64.EncodedLen(len(iv)) + base64.EncodedLen(len(ciphertext)) + base64.EncodedLen(len(tag)) + 4
|
||||
buf := make([]byte, 0, size)
|
||||
|
||||
buf = append(buf, aad...)
|
||||
buf = append(buf, tokens.Period)
|
||||
buf = base64.AppendEncode(buf, encryptedKey)
|
||||
buf = append(buf, tokens.Period)
|
||||
buf = base64.AppendEncode(buf, iv)
|
||||
buf = append(buf, tokens.Period)
|
||||
buf = base64.AppendEncode(buf, ciphertext)
|
||||
buf = append(buf, tokens.Period)
|
||||
buf = base64.AppendEncode(buf, tag)
|
||||
|
||||
return buf
|
||||
}
|
||||
|
||||
114
vendor/github.com/lestrrat-go/jwx/v3/jwe/options.go
generated
vendored
114
vendor/github.com/lestrrat-go/jwx/v3/jwe/options.go
generated
vendored
@@ -6,6 +6,66 @@ import (
|
||||
"github.com/lestrrat-go/option/v2"
|
||||
)
|
||||
|
||||
type identCritExtension struct{}
|
||||
type identDisabledKeyAlgorithms struct{}
|
||||
|
||||
// WithDisabledKeyAlgorithms returns a process-global option for jwe.Settings()
|
||||
// that refuses the named key encryption algorithms in both directions. After
|
||||
// the call returns, jwe.Encrypt() will not produce a recipient using any
|
||||
// listed algorithm, and jwe.Decrypt() will reject any recipient whose "alg"
|
||||
// is in the list, before any cryptographic work runs. The check fires per
|
||||
// recipient: a multi-recipient JWE is rejected as soon as a disabled "alg"
|
||||
// is seen on any recipient.
|
||||
//
|
||||
// The list is replaced (not unioned) on each Settings() call. To clear the
|
||||
// disabled set, call jwe.Settings(jwe.WithDisabledKeyAlgorithms()) with no
|
||||
// arguments.
|
||||
//
|
||||
// This is a deployment-time policy hook for the canonical "disable RSA1_5"
|
||||
// case (RFC 8725 §3.1) and similar legacy-algorithm bans. The jwa package
|
||||
// does not unregister these algorithms — keeping them registered preserves
|
||||
// header parsing for diagnostic logs, while this option blocks any actual
|
||||
// crypto use.
|
||||
func WithDisabledKeyAlgorithms(algorithms ...jwa.KeyEncryptionAlgorithm) GlobalOption {
|
||||
return &globalOption{option.New(identDisabledKeyAlgorithms{}, algorithms)}
|
||||
}
|
||||
|
||||
// WithCritExtension declares that the caller understands and will process
|
||||
// the named "crit" (Critical) header parameter extension(s) per RFC 7516
|
||||
// Section 4.1.13 (which references RFC 7515 Section 4.1.11). The option
|
||||
// is variadic and accumulating: a single call may register any number
|
||||
// of extension names, and the option may be passed multiple times to add
|
||||
// more.
|
||||
//
|
||||
// This option only takes effect when jwe.WithCritValidation(true) is
|
||||
// also passed. With validation enabled, jwe.Decrypt() rejects any JWE
|
||||
// whose protected header lists a "crit" extension that has not been
|
||||
// declared via this option, satisfying the RFC's requirement that
|
||||
// recipients MUST reject any "crit" extension they do not understand.
|
||||
//
|
||||
// IMPORTANT: declaring an extension here is a promise to the library
|
||||
// that the caller knows what the extension means and will perform any
|
||||
// validation, side effect, or policy enforcement the extension requires
|
||||
// AFTER jwe.Decrypt() returns successfully. The library cannot inspect
|
||||
// or enforce the semantics of an extension; it only checks that every
|
||||
// "crit" entry in the message has been declared. If you register an
|
||||
// extension and then forget to act on its value, you have effectively
|
||||
// disabled the protection the producer was trying to obtain by listing
|
||||
// the extension as critical.
|
||||
//
|
||||
// Concretely, the post-decrypt code path for a declared extension must:
|
||||
//
|
||||
// 1. Read the value of the named header from the decrypted message.
|
||||
// 2. Apply whatever check or transformation the extension specifies
|
||||
// (e.g. for an "x-tenant-binding" extension, refuse to act on the
|
||||
// payload unless the binding matches the current tenant).
|
||||
// 3. Treat any failure of that check as a decryption failure for
|
||||
// the application's purposes, even though jwe.Decrypt() returned
|
||||
// no error.
|
||||
func WithCritExtension(names ...string) DecryptOption {
|
||||
return &decryptOption{option.New(identCritExtension{}, names)}
|
||||
}
|
||||
|
||||
// WithProtectedHeaders is used to specify contents of the protected header.
|
||||
// Some fields such as "enc" and "zip" will be overwritten when encryption is
|
||||
// performed.
|
||||
@@ -35,12 +95,22 @@ func (*withKeySuboption) withKeySuboption() {}
|
||||
|
||||
// WithPerRecipientHeaders is used to pass header values for each recipient.
|
||||
// Note that these headers are by definition _unprotected_.
|
||||
//
|
||||
// The supplied Headers is cloned before being stored in the option, so the
|
||||
// caller retains exclusive ownership of the original instance and the
|
||||
// library never mutates or pools it.
|
||||
func WithPerRecipientHeaders(hdr Headers) WithKeySuboption {
|
||||
if hdr != nil {
|
||||
if cloned, err := hdr.Clone(); err == nil {
|
||||
hdr = cloned
|
||||
}
|
||||
}
|
||||
return &withKeySuboption{option.New(identPerRecipientHeaders{}, hdr)}
|
||||
}
|
||||
|
||||
// WithKey is used to pass a static algorithm/key pair to either `jwe.Encrypt()` or `jwe.Decrypt()`.
|
||||
// either a raw key or `jwk.Key` may be passed as `key`.
|
||||
// Either a raw key or `jwk.Key` may be passed as `key`. If `key` is a `jwk.Key`,
|
||||
// it must export to one of the raw key types described below.
|
||||
//
|
||||
// The `alg` parameter is the identifier for the key encryption algorithm that should be used.
|
||||
// It is of type `jwa.KeyAlgorithm` but in reality you can only pass `jwa.KeyEncryptionAlgorithm`
|
||||
@@ -48,6 +118,30 @@ func WithPerRecipientHeaders(hdr Headers) WithKeySuboption {
|
||||
// passed to the option. If you specify other algorithm types such as `jwa.SignatureAlgorithm`,
|
||||
// then you will get an error when `jwe.Encrypt()` or `jwe.Decrypt()` is executed.
|
||||
//
|
||||
// Built-in algorithm/key pairs are:
|
||||
//
|
||||
// - `jwa.RSA1_5()` and `jwa.RSA_OAEP*()`: `*rsa.PublicKey` for `jwe.Encrypt()`
|
||||
// and the matching `*rsa.PrivateKey` for `jwe.Decrypt()`
|
||||
// - `jwa.A128KW()`, `jwa.A192KW()`, `jwa.A256KW()`, `jwa.A128GCMKW()`,
|
||||
// `jwa.A192GCMKW()`, and `jwa.A256GCMKW()`: shared symmetric key bytes of
|
||||
// the size required by the selected algorithm
|
||||
// - `jwa.DIRECT()`: shared symmetric key bytes used as the CEK. The key length
|
||||
// must match the selected `enc`, and DIRECT supports only a single recipient
|
||||
// - `jwa.ECDH_ES()` and `jwa.ECDH_ES_A*KW()`: recipient public key for
|
||||
// `jwe.Encrypt()` and the matching private key for `jwe.Decrypt()`. Built-in
|
||||
// support accepts `*ecdsa.PublicKey`, `*ecdsa.PrivateKey`,
|
||||
// `*ecdh.PublicKey`, and `*ecdh.PrivateKey`; `jwa.ECDH_ES()` also supports
|
||||
// only a single recipient
|
||||
// - `jwa.PBES2_*()`: password bytes
|
||||
//
|
||||
// `jwa.RSA1_5()` is supported only for interoperability with legacy peers.
|
||||
// New applications should prefer an RSA-OAEP variant such as
|
||||
// `jwa.RSA_OAEP_256()` because PKCS#1 v1.5 decryption is exposed to
|
||||
// Bleichenbacher-style oracle attacks.
|
||||
//
|
||||
// Additional algorithms may be added by extension packages, but the key must
|
||||
// still match the contract for the selected `alg`.
|
||||
//
|
||||
// Unlike `jwe.WithKeySet()`, the `kid` field does not need to match for the key
|
||||
// to be tried.
|
||||
func WithKey(alg jwa.KeyAlgorithm, key any, options ...WithKeySuboption) EncryptDecryptOption {
|
||||
@@ -68,6 +162,24 @@ func WithKey(alg jwa.KeyAlgorithm, key any, options ...WithKeySuboption) Encrypt
|
||||
})}
|
||||
}
|
||||
|
||||
// WithKeySet specifies a JWKS (jwk.Set) to use for decryption. The
|
||||
// recipient's `kid` header selects a key from the set, and the key's
|
||||
// `alg` (or, when the JWK lacks `alg`, the recipient's declared `alg`)
|
||||
// drives the decrypt-time dispatch.
|
||||
//
|
||||
// By default WithKeySet requires the JWE to carry a `kid` header that
|
||||
// matches a key in the set. Pass `WithRequireKid(false)` to fall back
|
||||
// to trying every key in the set (slower, looser; intended for legacy
|
||||
// peers that don't emit `kid`). Per-key errors from the set are
|
||||
// surfaced via `errors.Join` when nothing matched, so a caller
|
||||
// debugging "why didn't my keyset match" sees the per-key reasons.
|
||||
//
|
||||
// Security note: the recipient's per-recipient header is unprotected.
|
||||
// When the selected JWK has no `alg`, the keyset provider falls back
|
||||
// to the per-recipient `alg`, then the protected header's `alg`.
|
||||
// `jwe.Decrypt` re-checks `alg` against the integrity-protected
|
||||
// protected header before any cryptographic call (RFC 7516 §7.2.1
|
||||
// disjointness).
|
||||
func WithKeySet(set jwk.Set, options ...WithKeySetSuboption) DecryptOption {
|
||||
requireKid := true
|
||||
for _, option := range options {
|
||||
|
||||
132
vendor/github.com/lestrrat-go/jwx/v3/jwe/options.yaml
generated
vendored
132
vendor/github.com/lestrrat-go/jwx/v3/jwe/options.yaml
generated
vendored
@@ -10,6 +10,12 @@ interfaces:
|
||||
methods:
|
||||
- globalOption
|
||||
- decryptOption
|
||||
- name: GlobalEncryptOption
|
||||
comment: |
|
||||
GlobalEncryptOption describes options that changes global settings and for each call of the `jwe.Encrypt` function
|
||||
methods:
|
||||
- globalOption
|
||||
- encryptOption
|
||||
- name: CompactOption
|
||||
comment: |
|
||||
CompactOption describes options that can be passed to `jwe.Compact`
|
||||
@@ -37,6 +43,13 @@ interfaces:
|
||||
- readFileOption
|
||||
comment: |
|
||||
ReadFileOption is a type of `Option` that can be passed to `jwe.Parse`
|
||||
- name: GlobalParseOption
|
||||
methods:
|
||||
- globalOption
|
||||
- readFileOption
|
||||
comment: |
|
||||
GlobalParseOption describes an Option that can be passed to `jwe.Settings()`
|
||||
and `jwe.ReadFile()`.
|
||||
- name: ReadFileOption
|
||||
comment: |
|
||||
ReadFileOption is a type of `Option` that can be passed to `jwe.ReadFile`
|
||||
@@ -76,6 +89,17 @@ options:
|
||||
a payload using `jwe.Encrypt` (Yes, we know it can only be "" or "DEF",
|
||||
but the way the specification is written it could allow for more options,
|
||||
and therefore this option takes an argument)
|
||||
|
||||
Compression can leak information about the plaintext through message
|
||||
length, so enable it only when you understand that tradeoff.
|
||||
|
||||
This library does not enforce an encrypt-side plaintext size limit before
|
||||
compression. Callers that accept untrusted or arbitrarily large payloads
|
||||
must bound the input size before calling `jwe.Encrypt` with this option.
|
||||
|
||||
Recipients may independently reject compressed messages whose
|
||||
decompressed payload exceeds their `jwe.WithMaxDecompressBufferSize`
|
||||
setting.
|
||||
- ident: ContentEncryptionAlgorithm
|
||||
interface: EncryptOption
|
||||
option_name: WithContentEncryption
|
||||
@@ -140,14 +164,77 @@ options:
|
||||
This option is currently considered EXPERIMENTAL, and is subject to
|
||||
future changes across minor/micro versions.
|
||||
- ident: MaxPBES2Count
|
||||
interface: GlobalOption
|
||||
interface: GlobalDecryptOption
|
||||
argument_type: int
|
||||
comment: |
|
||||
WithMaxPBES2Count specifies the maximum number of PBES2 iterations
|
||||
to use when decrypting a message. If not specified, the default
|
||||
value of 10,000 is used.
|
||||
|
||||
This option has a global effect.
|
||||
The cap is applied per recipient. RFC 7516 §5.3 allows each
|
||||
recipient of a JSON-serialized JWE to carry its own "p2c" iteration
|
||||
count in its per-recipient unprotected header, and jwe.Decrypt
|
||||
honors that. For a JWE with N recipients, the worst-case PBKDF2
|
||||
cost is therefore on the order of N * MaxPBES2Count iterations per
|
||||
decrypt attempt. When accepting multi-recipient JSON JWEs from
|
||||
untrusted senders, also clamp jwe.WithMaxRecipients.
|
||||
|
||||
This option can be used for `jwe.Settings()`, which changes the behavior
|
||||
globally, or for `jwe.Decrypt()`, which changes the behavior for that
|
||||
specific call.
|
||||
- ident: MinPBES2Count
|
||||
interface: GlobalDecryptOption
|
||||
argument_type: int
|
||||
comment: |
|
||||
WithMinPBES2Count specifies the minimum number of PBES2 iterations
|
||||
to accept when decrypting a message. If not specified, the default
|
||||
value of 1,000 is used (RFC 7518 §4.8.1.2 floor). Set to 0 to
|
||||
disable the minimum check (NOT RECOMMENDED for untrusted issuers
|
||||
— without a floor, recipients accept arbitrarily-weak password-
|
||||
derived keys and silently spend the producer-chosen amount of
|
||||
crypto work to verify them).
|
||||
|
||||
Threat model: a malicious or careless issuer signs a PBES2-wrapped
|
||||
JWE with a very low p2c (e.g. 100) so they spend almost no CPU on
|
||||
their side, while the recipient happily derives the wrap key with
|
||||
the same low iteration count. The result is an authenticated
|
||||
message whose key-derivation strength is well below industry
|
||||
practice (OWASP 2023 recommends ≥600,000 PBKDF2-SHA256 iterations
|
||||
for password-derived keys; the RFC floor of 1,000 is far below
|
||||
that). For receiver-side hardening against under-iteration, raise
|
||||
WithMinPBES2Count above 1,000.
|
||||
|
||||
This option can be used for `jwe.Settings()`, which changes the behavior
|
||||
globally, or for `jwe.Decrypt()`, which changes the behavior for that
|
||||
specific call.
|
||||
- ident: PBES2Count
|
||||
interface: GlobalEncryptOption
|
||||
argument_type: int
|
||||
comment: |
|
||||
WithPBES2Count specifies the number of PBKDF2 iterations to use when
|
||||
encrypting a key with the PBES2 family of algorithms. If not specified,
|
||||
the default value of 10,000 is used. Modern guidance (OWASP 2023)
|
||||
recommends 600,000 or more for PBKDF2-HMAC-SHA256.
|
||||
|
||||
This option only affects encryption. Iteration counts on incoming
|
||||
messages are validated separately on decrypt via WithMinPBES2Count
|
||||
and WithMaxPBES2Count.
|
||||
|
||||
This option can be used for `jwe.Settings()`, which changes the behavior
|
||||
globally, or for `jwe.Encrypt()`, which changes the behavior for that
|
||||
specific call.
|
||||
- ident: MaxRecipients
|
||||
interface: GlobalDecryptOption
|
||||
argument_type: int
|
||||
comment: |
|
||||
WithMaxRecipients specifies the maximum number of recipients allowed
|
||||
in a JWE message. If a JWE message contains more recipients than this
|
||||
value, parsing or decryption will return an error. The default value
|
||||
is 100.
|
||||
|
||||
This option can be used for `jwe.Settings()`, which changes the behavior
|
||||
globally, or for `jwe.Decrypt()`, which changes the behavior for that
|
||||
specific call.
|
||||
- ident: MaxDecompressBufferSize
|
||||
interface: GlobalDecryptOption
|
||||
argument_type: int64
|
||||
@@ -157,6 +244,12 @@ options:
|
||||
exceeds this amount when decompressed, jwe.Decrypt will return an error.
|
||||
The default value is 10MB.
|
||||
|
||||
A non-positive value rejects every compressed JWE: the cap fires on
|
||||
the first byte of inflated output, so any "zip"-compressed message
|
||||
fails with an exceeds-cap error before any payload is delivered. Use
|
||||
this when the deployment refuses to accept compressed JWEs at all.
|
||||
Pass an explicit positive cap when compressed payloads are expected.
|
||||
|
||||
This option can be used for `jwe.Settings()`, which changes the behavior
|
||||
globally, or for `jwe.Decrypt()`, which changes the behavior for that
|
||||
specific call.
|
||||
@@ -170,6 +263,39 @@ options:
|
||||
In v2, this option was called MaxBufferSize.
|
||||
|
||||
This option has a global effect.
|
||||
- ident: CritValidation
|
||||
interface: DecryptOption
|
||||
argument_type: bool
|
||||
comment: |
|
||||
WithCritValidation enables RFC 7516 Section 4.1.13 (via RFC 7515
|
||||
Section 4.1.11) validation of the "crit" (Critical) header parameter
|
||||
during decryption. The default is false, matching the behavior of
|
||||
v3.0.13 and earlier (the "crit" header is silently ignored).
|
||||
|
||||
When enabled, jwe.Decrypt() will reject any JWE whose protected
|
||||
header lists "crit" entries that the recipient has not declared
|
||||
support for via jwe.WithCritExtension(). It will also reject
|
||||
structurally invalid "crit" lists: empty arrays, duplicate names,
|
||||
empty extension names, names of standard JOSE/JWE header parameters,
|
||||
and names that do not appear as header parameters in the protected
|
||||
header.
|
||||
|
||||
Per RFC 7516 Section 4.1.13 (referencing RFC 7515 Section 4.1.11),
|
||||
recipients MUST reject a JWE whose "crit" list names extensions
|
||||
they do not understand. Enabling this option together with one or
|
||||
more jwe.WithCritExtension() calls is the only way to satisfy that
|
||||
requirement with this library.
|
||||
|
||||
IMPORTANT: enabling this option makes the library check that every
|
||||
"crit" entry has been declared via jwe.WithCritExtension(), but the
|
||||
library cannot perform the actual extension-specific processing on
|
||||
your behalf. After jwe.Decrypt() returns successfully, your code
|
||||
MUST read each declared extension header and apply whatever check
|
||||
or side effect the extension semantics demand. If you declare an
|
||||
extension and then forget to act on its value, you have defeated
|
||||
the protection the producer was trying to obtain by marking that
|
||||
extension critical. See the documentation on jwe.WithCritExtension
|
||||
for details.
|
||||
- ident: LegacyHeaderMerging
|
||||
interface: EncryptOption
|
||||
argument_type: bool
|
||||
@@ -207,4 +333,4 @@ options:
|
||||
merged into the protected headers).
|
||||
|
||||
In future versions, the new behavior will be the default. New users are
|
||||
encouraged to set this option to `false` now to avoid future issues.
|
||||
encouraged to set this option to `false` now to avoid future issues.
|
||||
|
||||
170
vendor/github.com/lestrrat-go/jwx/v3/jwe/options_gen.go
generated
vendored
170
vendor/github.com/lestrrat-go/jwx/v3/jwe/options_gen.go
generated
vendored
@@ -78,6 +78,21 @@ func (*globalDecryptOption) globalOption() {}
|
||||
|
||||
func (*globalDecryptOption) decryptOption() {}
|
||||
|
||||
// GlobalEncryptOption describes options that changes global settings and for each call of the `jwe.Encrypt` function
|
||||
type GlobalEncryptOption interface {
|
||||
Option
|
||||
globalOption()
|
||||
encryptOption()
|
||||
}
|
||||
|
||||
type globalEncryptOption struct {
|
||||
Option
|
||||
}
|
||||
|
||||
func (*globalEncryptOption) globalOption() {}
|
||||
|
||||
func (*globalEncryptOption) encryptOption() {}
|
||||
|
||||
// GlobalOption describes options that changes global settings for this package
|
||||
type GlobalOption interface {
|
||||
Option
|
||||
@@ -90,6 +105,22 @@ type globalOption struct {
|
||||
|
||||
func (*globalOption) globalOption() {}
|
||||
|
||||
// GlobalParseOption describes an Option that can be passed to `jwe.Settings()`
|
||||
// and `jwe.ReadFile()`.
|
||||
type GlobalParseOption interface {
|
||||
Option
|
||||
globalOption()
|
||||
readFileOption()
|
||||
}
|
||||
|
||||
type globalParseOption struct {
|
||||
Option
|
||||
}
|
||||
|
||||
func (*globalParseOption) globalOption() {}
|
||||
|
||||
func (*globalParseOption) readFileOption() {}
|
||||
|
||||
// ReadFileOption is a type of `Option` that can be passed to `jwe.Parse`
|
||||
type ParseOption interface {
|
||||
Option
|
||||
@@ -143,6 +174,7 @@ type identCEK struct{}
|
||||
type identCompress struct{}
|
||||
type identContentEncryptionAlgorithm struct{}
|
||||
type identContext struct{}
|
||||
type identCritValidation struct{}
|
||||
type identFS struct{}
|
||||
type identKey struct{}
|
||||
type identKeyProvider struct{}
|
||||
@@ -150,8 +182,11 @@ type identKeyUsed struct{}
|
||||
type identLegacyHeaderMerging struct{}
|
||||
type identMaxDecompressBufferSize struct{}
|
||||
type identMaxPBES2Count struct{}
|
||||
type identMaxRecipients struct{}
|
||||
type identMergeProtectedHeaders struct{}
|
||||
type identMessage struct{}
|
||||
type identMinPBES2Count struct{}
|
||||
type identPBES2Count struct{}
|
||||
type identPerRecipientHeaders struct{}
|
||||
type identPretty struct{}
|
||||
type identProtectedHeaders struct{}
|
||||
@@ -178,6 +213,10 @@ func (identContext) String() string {
|
||||
return "WithContext"
|
||||
}
|
||||
|
||||
func (identCritValidation) String() string {
|
||||
return "WithCritValidation"
|
||||
}
|
||||
|
||||
func (identFS) String() string {
|
||||
return "WithFS"
|
||||
}
|
||||
@@ -206,6 +245,10 @@ func (identMaxPBES2Count) String() string {
|
||||
return "WithMaxPBES2Count"
|
||||
}
|
||||
|
||||
func (identMaxRecipients) String() string {
|
||||
return "WithMaxRecipients"
|
||||
}
|
||||
|
||||
func (identMergeProtectedHeaders) String() string {
|
||||
return "WithMergeProtectedHeaders"
|
||||
}
|
||||
@@ -214,6 +257,14 @@ func (identMessage) String() string {
|
||||
return "WithMessage"
|
||||
}
|
||||
|
||||
func (identMinPBES2Count) String() string {
|
||||
return "WithMinPBES2Count"
|
||||
}
|
||||
|
||||
func (identPBES2Count) String() string {
|
||||
return "WithPBES2Count"
|
||||
}
|
||||
|
||||
func (identPerRecipientHeaders) String() string {
|
||||
return "WithPerRecipientHeaders"
|
||||
}
|
||||
@@ -258,6 +309,17 @@ func WithCEK(v *[]byte) DecryptOption {
|
||||
// a payload using `jwe.Encrypt` (Yes, we know it can only be "" or "DEF",
|
||||
// but the way the specification is written it could allow for more options,
|
||||
// and therefore this option takes an argument)
|
||||
//
|
||||
// Compression can leak information about the plaintext through message
|
||||
// length, so enable it only when you understand that tradeoff.
|
||||
//
|
||||
// This library does not enforce an encrypt-side plaintext size limit before
|
||||
// compression. Callers that accept untrusted or arbitrarily large payloads
|
||||
// must bound the input size before calling `jwe.Encrypt` with this option.
|
||||
//
|
||||
// Recipients may independently reject compressed messages whose
|
||||
// decompressed payload exceeds their `jwe.WithMaxDecompressBufferSize`
|
||||
// setting.
|
||||
func WithCompress(v jwa.CompressionAlgorithm) EncryptOption {
|
||||
return &encryptOption{option.New(identCompress{}, v)}
|
||||
}
|
||||
@@ -274,6 +336,39 @@ func WithContext(v context.Context) DecryptOption {
|
||||
return &decryptOption{option.New(identContext{}, v)}
|
||||
}
|
||||
|
||||
// WithCritValidation enables RFC 7516 Section 4.1.13 (via RFC 7515
|
||||
// Section 4.1.11) validation of the "crit" (Critical) header parameter
|
||||
// during decryption. The default is false, matching the behavior of
|
||||
// v3.0.13 and earlier (the "crit" header is silently ignored).
|
||||
//
|
||||
// When enabled, jwe.Decrypt() will reject any JWE whose protected
|
||||
// header lists "crit" entries that the recipient has not declared
|
||||
// support for via jwe.WithCritExtension(). It will also reject
|
||||
// structurally invalid "crit" lists: empty arrays, duplicate names,
|
||||
// empty extension names, names of standard JOSE/JWE header parameters,
|
||||
// and names that do not appear as header parameters in the protected
|
||||
// header.
|
||||
//
|
||||
// Per RFC 7516 Section 4.1.13 (referencing RFC 7515 Section 4.1.11),
|
||||
// recipients MUST reject a JWE whose "crit" list names extensions
|
||||
// they do not understand. Enabling this option together with one or
|
||||
// more jwe.WithCritExtension() calls is the only way to satisfy that
|
||||
// requirement with this library.
|
||||
//
|
||||
// IMPORTANT: enabling this option makes the library check that every
|
||||
// "crit" entry has been declared via jwe.WithCritExtension(), but the
|
||||
// library cannot perform the actual extension-specific processing on
|
||||
// your behalf. After jwe.Decrypt() returns successfully, your code
|
||||
// MUST read each declared extension header and apply whatever check
|
||||
// or side effect the extension semantics demand. If you declare an
|
||||
// extension and then forget to act on its value, you have defeated
|
||||
// the protection the producer was trying to obtain by marking that
|
||||
// extension critical. See the documentation on jwe.WithCritExtension
|
||||
// for details.
|
||||
func WithCritValidation(v bool) DecryptOption {
|
||||
return &decryptOption{option.New(identCritValidation{}, v)}
|
||||
}
|
||||
|
||||
// WithFS specifies the source `fs.FS` object to read the file from.
|
||||
func WithFS(v fs.FS) ReadFileOption {
|
||||
return &readFileOption{option.New(identFS{}, v)}
|
||||
@@ -339,6 +434,12 @@ func WithLegacyHeaderMerging(v bool) EncryptOption {
|
||||
// exceeds this amount when decompressed, jwe.Decrypt will return an error.
|
||||
// The default value is 10MB.
|
||||
//
|
||||
// A non-positive value rejects every compressed JWE: the cap fires on
|
||||
// the first byte of inflated output, so any "zip"-compressed message
|
||||
// fails with an exceeds-cap error before any payload is delivered. Use
|
||||
// this when the deployment refuses to accept compressed JWEs at all.
|
||||
// Pass an explicit positive cap when compressed payloads are expected.
|
||||
//
|
||||
// This option can be used for `jwe.Settings()`, which changes the behavior
|
||||
// globally, or for `jwe.Decrypt()`, which changes the behavior for that
|
||||
// specific call.
|
||||
@@ -350,9 +451,31 @@ func WithMaxDecompressBufferSize(v int64) GlobalDecryptOption {
|
||||
// to use when decrypting a message. If not specified, the default
|
||||
// value of 10,000 is used.
|
||||
//
|
||||
// This option has a global effect.
|
||||
func WithMaxPBES2Count(v int) GlobalOption {
|
||||
return &globalOption{option.New(identMaxPBES2Count{}, v)}
|
||||
// The cap is applied per recipient. RFC 7516 §5.3 allows each
|
||||
// recipient of a JSON-serialized JWE to carry its own "p2c" iteration
|
||||
// count in its per-recipient unprotected header, and jwe.Decrypt
|
||||
// honors that. For a JWE with N recipients, the worst-case PBKDF2
|
||||
// cost is therefore on the order of N * MaxPBES2Count iterations per
|
||||
// decrypt attempt. When accepting multi-recipient JSON JWEs from
|
||||
// untrusted senders, also clamp jwe.WithMaxRecipients.
|
||||
//
|
||||
// This option can be used for `jwe.Settings()`, which changes the behavior
|
||||
// globally, or for `jwe.Decrypt()`, which changes the behavior for that
|
||||
// specific call.
|
||||
func WithMaxPBES2Count(v int) GlobalDecryptOption {
|
||||
return &globalDecryptOption{option.New(identMaxPBES2Count{}, v)}
|
||||
}
|
||||
|
||||
// WithMaxRecipients specifies the maximum number of recipients allowed
|
||||
// in a JWE message. If a JWE message contains more recipients than this
|
||||
// value, parsing or decryption will return an error. The default value
|
||||
// is 100.
|
||||
//
|
||||
// This option can be used for `jwe.Settings()`, which changes the behavior
|
||||
// globally, or for `jwe.Decrypt()`, which changes the behavior for that
|
||||
// specific call.
|
||||
func WithMaxRecipients(v int) GlobalDecryptOption {
|
||||
return &globalDecryptOption{option.New(identMaxRecipients{}, v)}
|
||||
}
|
||||
|
||||
// WithMergeProtectedHeaders specify that when given multiple headers
|
||||
@@ -369,6 +492,47 @@ func WithMessage(v *Message) DecryptOption {
|
||||
return &decryptOption{option.New(identMessage{}, v)}
|
||||
}
|
||||
|
||||
// WithMinPBES2Count specifies the minimum number of PBES2 iterations
|
||||
// to accept when decrypting a message. If not specified, the default
|
||||
// value of 1,000 is used (RFC 7518 §4.8.1.2 floor). Set to 0 to
|
||||
// disable the minimum check (NOT RECOMMENDED for untrusted issuers
|
||||
// — without a floor, recipients accept arbitrarily-weak password-
|
||||
// derived keys and silently spend the producer-chosen amount of
|
||||
// crypto work to verify them).
|
||||
//
|
||||
// Threat model: a malicious or careless issuer signs a PBES2-wrapped
|
||||
// JWE with a very low p2c (e.g. 100) so they spend almost no CPU on
|
||||
// their side, while the recipient happily derives the wrap key with
|
||||
// the same low iteration count. The result is an authenticated
|
||||
// message whose key-derivation strength is well below industry
|
||||
// practice (OWASP 2023 recommends ≥600,000 PBKDF2-SHA256 iterations
|
||||
// for password-derived keys; the RFC floor of 1,000 is far below
|
||||
// that). For receiver-side hardening against under-iteration, raise
|
||||
// WithMinPBES2Count above 1,000.
|
||||
//
|
||||
// This option can be used for `jwe.Settings()`, which changes the behavior
|
||||
// globally, or for `jwe.Decrypt()`, which changes the behavior for that
|
||||
// specific call.
|
||||
func WithMinPBES2Count(v int) GlobalDecryptOption {
|
||||
return &globalDecryptOption{option.New(identMinPBES2Count{}, v)}
|
||||
}
|
||||
|
||||
// WithPBES2Count specifies the number of PBKDF2 iterations to use when
|
||||
// encrypting a key with the PBES2 family of algorithms. If not specified,
|
||||
// the default value of 10,000 is used. Modern guidance (OWASP 2023)
|
||||
// recommends 600,000 or more for PBKDF2-HMAC-SHA256.
|
||||
//
|
||||
// This option only affects encryption. Iteration counts on incoming
|
||||
// messages are validated separately on decrypt via WithMinPBES2Count
|
||||
// and WithMaxPBES2Count.
|
||||
//
|
||||
// This option can be used for `jwe.Settings()`, which changes the behavior
|
||||
// globally, or for `jwe.Encrypt()`, which changes the behavior for that
|
||||
// specific call.
|
||||
func WithPBES2Count(v int) GlobalEncryptOption {
|
||||
return &globalEncryptOption{option.New(identPBES2Count{}, v)}
|
||||
}
|
||||
|
||||
// WithPretty specifies whether the JSON output should be formatted and
|
||||
// indented
|
||||
func WithPretty(v bool) WithJSONSuboption {
|
||||
|
||||
1
vendor/github.com/lestrrat-go/jwx/v3/jwk/BUILD.bazel
generated
vendored
1
vendor/github.com/lestrrat-go/jwx/v3/jwk/BUILD.bazel
generated
vendored
@@ -41,6 +41,7 @@ go_library(
|
||||
"//internal/tokens",
|
||||
"//jwa",
|
||||
"//jwk/ecdsa",
|
||||
"//jwk/internal/registry",
|
||||
"//jwk/jwkbb",
|
||||
"@com_github_lestrrat_go_blackmagic//:blackmagic",
|
||||
"@com_github_lestrrat_go_httprc_v3//:httprc",
|
||||
|
||||
37
vendor/github.com/lestrrat-go/jwx/v3/jwk/cache.go
generated
vendored
37
vendor/github.com/lestrrat-go/jwx/v3/jwk/cache.go
generated
vendored
@@ -81,14 +81,23 @@ type Cache struct {
|
||||
// conjection with `httprc.NewResource` to create a `httprc.Resource` object
|
||||
// to auto-update `jwk.Set` objects.
|
||||
type Transformer struct {
|
||||
parseOptions []ParseOption
|
||||
parseOptions []ParseOption
|
||||
maxFetchBodySize int64
|
||||
}
|
||||
|
||||
func (t Transformer) Transform(_ context.Context, res *http.Response) (Set, error) {
|
||||
buf, err := io.ReadAll(res.Body)
|
||||
maxBodySize := t.maxFetchBodySize
|
||||
if maxBodySize <= 0 {
|
||||
maxBodySize = maxFetchBodySize.Load()
|
||||
}
|
||||
|
||||
buf, err := io.ReadAll(io.LimitReader(res.Body, maxBodySize+1))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to read response body status: %w`, err)
|
||||
}
|
||||
if int64(len(buf)) > maxBodySize {
|
||||
return nil, fmt.Errorf(`response body at %q exceeded max size of %d bytes`, res.Request.URL.String(), maxBodySize)
|
||||
}
|
||||
|
||||
set, err := Parse(buf, t.parseOptions...)
|
||||
if err != nil {
|
||||
@@ -121,10 +130,17 @@ func NewCache(ctx context.Context, client *httprc.Client) (*Cache, error) {
|
||||
// Register registers a URL to be managed by the cache. URLs must
|
||||
// be registered before issuing `Get`
|
||||
//
|
||||
// The `Register` method is a thin wrapper around `(httprc.Controller).Add`
|
||||
// The `Register` method is a thin wrapper around `(httprc.Controller).Add`.
|
||||
//
|
||||
// As with `jwk.Fetch`, the default whitelist is `jwk.InsecureWhitelist{}`
|
||||
// — every URL is allowed. Supply `jwk.WithFetchWhitelist()` when the URL
|
||||
// originates from untrusted input. See `jwk.Fetch` for the full security
|
||||
// rationale.
|
||||
func (c *Cache) Register(ctx context.Context, u string, options ...RegisterOption) error {
|
||||
var parseOptions []ParseOption
|
||||
var resourceOptions []httprc.NewResourceOption
|
||||
var maxFetchBodySize int64
|
||||
var hasHTTPClient bool
|
||||
waitReady := true
|
||||
for _, option := range options {
|
||||
switch option := option.(type) {
|
||||
@@ -144,16 +160,29 @@ func (c *Cache) Register(ctx context.Context, u string, options ...RegisterOptio
|
||||
return fmt.Errorf(`failed to retrieve HTTPClient option value: %w`, err)
|
||||
}
|
||||
resourceOptions = append(resourceOptions, httprc.WithHTTPClient(cli))
|
||||
hasHTTPClient = true
|
||||
case identWaitReady{}:
|
||||
if err := option.Value(&waitReady); err != nil {
|
||||
return fmt.Errorf(`failed to retrieve WaitReady option value: %w`, err)
|
||||
}
|
||||
case identMaxFetchBodySize{}:
|
||||
if err := option.Value(&maxFetchBodySize); err != nil {
|
||||
return fmt.Errorf(`failed to retrieve MaxFetchBodySize option value: %w`, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If no HTTP client was explicitly provided, use the library's default
|
||||
// client which includes timeout and redirect protections. Without this,
|
||||
// httprc would fall back to http.DefaultClient which has no such protections.
|
||||
if !hasHTTPClient {
|
||||
resourceOptions = append(resourceOptions, httprc.WithHTTPClient(getFetchHTTPClient()))
|
||||
}
|
||||
|
||||
r, err := httprc.NewResource[Set](u, &Transformer{
|
||||
parseOptions: parseOptions,
|
||||
parseOptions: parseOptions,
|
||||
maxFetchBodySize: maxFetchBodySize,
|
||||
}, resourceOptions...)
|
||||
if err != nil {
|
||||
return fmt.Errorf(`failed to create httprc.Resource: %w`, err)
|
||||
|
||||
79
vendor/github.com/lestrrat-go/jwx/v3/jwk/convert.go
generated
vendored
79
vendor/github.com/lestrrat-go/jwx/v3/jwk/convert.go
generated
vendored
@@ -14,7 +14,6 @@ import (
|
||||
|
||||
"github.com/lestrrat-go/blackmagic"
|
||||
"github.com/lestrrat-go/jwx/v3/internal/ecutil"
|
||||
"github.com/lestrrat-go/jwx/v3/jwa"
|
||||
)
|
||||
|
||||
// # Converting between Raw Keys and `jwk.Key`s
|
||||
@@ -23,33 +22,72 @@ import (
|
||||
// A converter that converts from a `jwk.Key` to a raw key is called a KeyExporter.
|
||||
|
||||
var keyImporters = make(map[reflect.Type]KeyImporter)
|
||||
var keyExporters = make(map[jwa.KeyType][]KeyExporter)
|
||||
|
||||
// KeyKind identifies a key for exporter dispatch. Built-in key types
|
||||
// use the key type string (e.g. "RSA", "EC", "OKP", "oct"). Keys that
|
||||
// implement KeyKinder can return a more specific identity
|
||||
// (e.g. "OKP:Ed448") to select a curve-specific exporter.
|
||||
type KeyKind string
|
||||
|
||||
// KeyKinder is implemented by keys that need exporter dispatch
|
||||
// beyond just their key type. For example, OKP keys return
|
||||
// "OKP:<curve>" so that curve-specific exporters can be registered
|
||||
// by external modules.
|
||||
//
|
||||
// Keys that do not implement this interface are dispatched by
|
||||
// KeyType().String() alone.
|
||||
type KeyKinder interface {
|
||||
KeyKind() KeyKind
|
||||
}
|
||||
|
||||
var keyExporters = make(map[KeyKind][]KeyExporter)
|
||||
|
||||
var muKeyImporters sync.RWMutex
|
||||
var muKeyExporters sync.RWMutex
|
||||
|
||||
// RegisterKeyImporter registers a KeyImporter for the given raw key. When `jwk.Import()` is called,
|
||||
// the library will look up the appropriate KeyImporter for the given raw key type (via `reflect`)
|
||||
// and execute the KeyImporters in succession until either one of them succeeds, or all of them fail.
|
||||
// RegisterKeyImporter registers a KeyImporter for the given raw key.
|
||||
// When `jwk.Import()` is called, the library looks up the importer for
|
||||
// the given raw key type (via `reflect`) and executes it.
|
||||
//
|
||||
// Importer dispatch is single-valued per Go type: there is exactly
|
||||
// one importer registered per `reflect.TypeOf(from)`. Registering a
|
||||
// second importer for the same raw-key type silently replaces the
|
||||
// previous entry — including built-in importers for
|
||||
// `*rsa.PrivateKey`, `*ecdsa.PrivateKey`, and so on. Callers that
|
||||
// need to guard against accidental overwrites should keep track of
|
||||
// registrations themselves and avoid double-registration at init()
|
||||
// time.
|
||||
//
|
||||
// This deliberately differs from the stacking behavior of
|
||||
// [RegisterKeyExporter] (keyed by [KeyKind] strings) and
|
||||
// [RegisterKeyParser] (an untyped-JSON fallback chain); importer
|
||||
// dispatch is a single-value map keyed by Go type, with no
|
||||
// equivalent dimension to try next. v4 turns the overwrite into
|
||||
// an error; v3 keeps the frozen silent-overwrite behavior for
|
||||
// backward compatibility.
|
||||
func RegisterKeyImporter(from any, conv KeyImporter) {
|
||||
muKeyImporters.Lock()
|
||||
defer muKeyImporters.Unlock()
|
||||
keyImporters[reflect.TypeOf(from)] = conv
|
||||
}
|
||||
|
||||
// RegisterKeyExporter registers a KeyExporter for the given key type. When `key.Raw()` is called,
|
||||
// the library will look up the appropriate KeyExporter for the given key type and execute the
|
||||
// KeyExporters in succession until either one of them succeeds, or all of them fail.
|
||||
func RegisterKeyExporter(kty jwa.KeyType, conv KeyExporter) {
|
||||
// RegisterKeyExporter registers a KeyExporter for the given key identity.
|
||||
// When `jwk.Export()` is called, the library first tries exporters registered
|
||||
// for the key's specific identity (via [KeyKinder]), then falls back to
|
||||
// exporters registered for the key type alone.
|
||||
//
|
||||
// For most key types, pass `KeyKind(kty.String())` (e.g. `KeyKind("RSA")`).
|
||||
// For curve-specific exporters, use a compound identity like `KeyKind("OKP:Ed448")`.
|
||||
func RegisterKeyExporter(ident KeyKind, conv KeyExporter) {
|
||||
muKeyExporters.Lock()
|
||||
defer muKeyExporters.Unlock()
|
||||
convs, ok := keyExporters[kty]
|
||||
convs, ok := keyExporters[ident]
|
||||
if !ok {
|
||||
convs = []KeyExporter{conv}
|
||||
} else {
|
||||
convs = append([]KeyExporter{conv}, convs...)
|
||||
}
|
||||
keyExporters[kty] = convs
|
||||
keyExporters[ident] = convs
|
||||
}
|
||||
|
||||
// KeyImporter is used to convert from a raw key to a `jwk.Key`. mneumonic: from the PoV of the `jwk.Key`,
|
||||
@@ -376,10 +414,12 @@ func Export(key Key, dst any) error {
|
||||
if rv.Kind() != reflect.Ptr {
|
||||
return fmt.Errorf(`jwk.Export: destination object must be a pointer`)
|
||||
}
|
||||
|
||||
muKeyExporters.RLock()
|
||||
exporters, ok := keyExporters[key.KeyType()]
|
||||
exporters := findExporters(key)
|
||||
muKeyExporters.RUnlock()
|
||||
if !ok {
|
||||
|
||||
if len(exporters) == 0 {
|
||||
return fmt.Errorf(`jwk.Export: no exporters registered for key type '%T'`, key)
|
||||
}
|
||||
for _, conv := range exporters {
|
||||
@@ -397,3 +437,16 @@ func Export(key Key, dst any) error {
|
||||
}
|
||||
return fmt.Errorf(`jwk.Export: no suitable exporter found for key type '%T'`, key)
|
||||
}
|
||||
|
||||
// findExporters returns exporters for the key, trying the specific
|
||||
// KeyKind first, then falling back to the key type. Caller must
|
||||
// hold muKeyExporters.RLock.
|
||||
func findExporters(key Key) []KeyExporter {
|
||||
if ki, ok := key.(KeyKinder); ok {
|
||||
ident := ki.KeyKind()
|
||||
if exporters, ok := keyExporters[ident]; ok {
|
||||
return exporters
|
||||
}
|
||||
}
|
||||
return keyExporters[KeyKind(key.KeyType().String())]
|
||||
}
|
||||
|
||||
8
vendor/github.com/lestrrat-go/jwx/v3/jwk/doc.go
generated
vendored
8
vendor/github.com/lestrrat-go/jwx/v3/jwk/doc.go
generated
vendored
@@ -22,7 +22,7 @@
|
||||
//
|
||||
// jwkKey, _ := jwk.Import(rsaPrivateKey)
|
||||
// var rawKey *rsa.PRrivateKey
|
||||
// jwkKey.Raw(&rawKey)
|
||||
// jwk.Export(jwkKey, &rawKey)
|
||||
//
|
||||
// You can use them to sign/verify/encrypt/decrypt:
|
||||
//
|
||||
@@ -177,9 +177,9 @@
|
||||
// var hint string
|
||||
// if err := probe.Get("MyHint", &hint); err != nil {
|
||||
// // if it doesn't have the `my_hint` field, it probably means
|
||||
// // it's not for us, so we return ContinueParseError so that
|
||||
// // it's not for us, so we return ContinueError so that
|
||||
// // the next parser can pick it up
|
||||
// return nil, jwk.ContinueParseError()
|
||||
// return nil, jwk.ContinueError()
|
||||
// }
|
||||
//
|
||||
// // Use hint to determine concrete key type
|
||||
@@ -190,7 +190,7 @@
|
||||
// ...
|
||||
// }
|
||||
//
|
||||
// return unmarshaler.Unmarshal(data, key)
|
||||
// return unmarshaler.UnmarshalKey(data, key)
|
||||
// }
|
||||
//
|
||||
// ## Registering KeyImporter/KeyExporter
|
||||
|
||||
248
vendor/github.com/lestrrat-go/jwx/v3/jwk/ecdsa.go
generated
vendored
248
vendor/github.com/lestrrat-go/jwx/v3/jwk/ecdsa.go
generated
vendored
@@ -20,7 +20,7 @@ func init() {
|
||||
ourecdsa.RegisterCurve(jwa.P384(), elliptic.P384())
|
||||
ourecdsa.RegisterCurve(jwa.P521(), elliptic.P521())
|
||||
|
||||
RegisterKeyExporter(jwa.EC(), KeyExportFunc(ecdsaJWKToRaw))
|
||||
RegisterKeyExporter(KeyKind(jwa.EC().String()), KeyExportFunc(ecdsaJWKToRaw))
|
||||
}
|
||||
|
||||
func (k *ecdsaPublicKey) Import(rawKey *ecdsa.PublicKey) error {
|
||||
@@ -35,6 +35,10 @@ func (k *ecdsaPublicKey) Import(rawKey *ecdsa.PublicKey) error {
|
||||
return fmt.Errorf(`invalid ecdsa.PublicKey`)
|
||||
}
|
||||
|
||||
if err := validateECDSAPoint(rawKey.Curve, rawKey.X, rawKey.Y); err != nil {
|
||||
return fmt.Errorf(`jwk: %w`, err)
|
||||
}
|
||||
|
||||
xbuf := ecutil.AllocECPointBuffer(rawKey.X, rawKey.Curve)
|
||||
ybuf := ecutil.AllocECPointBuffer(rawKey.Y, rawKey.Curve)
|
||||
defer ecutil.ReleaseECPointBuffer(xbuf)
|
||||
@@ -68,6 +72,10 @@ func (k *ecdsaPrivateKey) Import(rawKey *ecdsa.PrivateKey) error {
|
||||
return fmt.Errorf(`invalid ecdsa.PrivateKey`)
|
||||
}
|
||||
|
||||
if err := validateECDSAPoint(rawKey.Curve, rawKey.PublicKey.X, rawKey.PublicKey.Y); err != nil {
|
||||
return fmt.Errorf(`jwk: %w`, err)
|
||||
}
|
||||
|
||||
xbuf := ecutil.AllocECPointBuffer(rawKey.PublicKey.X, rawKey.Curve)
|
||||
ybuf := ecutil.AllocECPointBuffer(rawKey.PublicKey.Y, rawKey.Curve)
|
||||
dbuf := ecutil.AllocECPointBuffer(rawKey.D, rawKey.Curve)
|
||||
@@ -101,9 +109,121 @@ func buildECDSAPublicKey(alg jwa.EllipticCurveAlgorithm, xbuf, ybuf []byte) (*ec
|
||||
x.SetBytes(xbuf)
|
||||
y.SetBytes(ybuf)
|
||||
|
||||
if err := validateECDSAPoint(crv, &x, &y); err != nil {
|
||||
return nil, fmt.Errorf(`jwk: %w`, err)
|
||||
}
|
||||
|
||||
return &ecdsa.PublicKey{Curve: crv, X: &x, Y: &y}, nil
|
||||
}
|
||||
|
||||
// validateECDSAPoint rejects ECDSA public key coordinates that are not
|
||||
// safe to use: the identity point (0, 0) and any point that does not lie
|
||||
// on the named curve. Without these checks, attacker-supplied JWKs can
|
||||
// smuggle off-curve or small-subgroup points into downstream ECDSA/ECDH
|
||||
// operations (invalid-curve attacks). See JWK-003.
|
||||
//
|
||||
// The implementation is split into two branches for a reason:
|
||||
//
|
||||
// 1. For the NIST P-256/P-384/P-521 curves we route through crypto/ecdh.
|
||||
// Go 1.21 deprecated most of crypto/elliptic's Curve methods — not
|
||||
// because point-on-curve validation stopped being necessary, but
|
||||
// because the generic big.Int implementation in crypto/elliptic had
|
||||
// subtle edge cases and the Go team wanted users off it. The blessed
|
||||
// replacement for "parse and validate an uncompressed point" on
|
||||
// stdlib curves is ecdh.Curve.NewPublicKey, which enforces on-curve
|
||||
// membership and rejects the identity as part of parsing the SEC1
|
||||
// 0x04 || X || Y encoding. Using ecdh here means we're using exactly
|
||||
// the Go team's recommended replacement, and the deprecated stdlib
|
||||
// elliptic methods are never reached for any NIST-curve input.
|
||||
//
|
||||
// 2. For any other curve registered through jwk/ecdsa.RegisterCurve
|
||||
// (most importantly secp256k1 via the ES256K extension module),
|
||||
// crypto/ecdh has no entry point — it only supports the four curves
|
||||
// listed above. The only mechanism available for validating a point
|
||||
// on a custom curve is the elliptic.Curve interface's IsOnCurve
|
||||
// method. Calling it here is correct despite the staticcheck
|
||||
// deprecation notice, for three reasons:
|
||||
//
|
||||
// a. The deprecation targets the *stdlib* elliptic.Curve
|
||||
// implementations (elliptic.P256() etc.). Custom curves such as
|
||||
// btcec/secp256k1 ship their own IsOnCurve implementation; the
|
||||
// interface dispatch lands in that implementation, not in the
|
||||
// deprecated stdlib one. staticcheck cannot see through interface
|
||||
// dispatch, so the lint scope is suppressed on just this line.
|
||||
//
|
||||
// b. The elliptic.Curve interface itself remains part of Go's
|
||||
// supported API because crypto/ecdsa.Verify and
|
||||
// crypto/ecdsa.Sign continue to take elliptic.Curve-backed keys.
|
||||
// Any third-party curve that plugs into crypto/ecdsa is
|
||||
// contractually required to implement a working IsOnCurve; that
|
||||
// is the only thing crypto/ecdsa has to validate the public point
|
||||
// before verification. Calling it from here is the same contract.
|
||||
//
|
||||
// c. The remaining alternatives are worse: (i) refusing to validate
|
||||
// non-stdlib curves at all reintroduces JWK-003 for ES256K users;
|
||||
// (ii) refusing to *support* non-stdlib curves is a regression
|
||||
// for ES256K users. A cleaner long-term fix is to extend
|
||||
// jwk/ecdsa.RegisterCurve so extension modules can register a
|
||||
// validator function alongside the curve, letting us drop the
|
||||
// IsOnCurve call entirely. That is a deliberate follow-up, not a
|
||||
// blocker for this security fix.
|
||||
func validateECDSAPoint(crv elliptic.Curve, x, y *big.Int) error {
|
||||
if x.Sign() == 0 && y.Sign() == 0 {
|
||||
return fmt.Errorf(`invalid ECDSA public key: identity point is not a valid public key`)
|
||||
}
|
||||
|
||||
// Coordinates must fit in the curve's field. The NIST P-curve
|
||||
// branch below pads x and y into a fixed-size SEC1 buffer via
|
||||
// big.Int.FillBytes, which panics on oversized input. Bounding
|
||||
// here makes the function safe by construction for every caller,
|
||||
// including jwk.Import handed a hand-built *ecdsa.PublicKey from
|
||||
// raw bytes.
|
||||
bits := crv.Params().BitSize
|
||||
if x.BitLen() > bits {
|
||||
return fmt.Errorf(`invalid ECDSA public key: x coordinate is %d bits, exceeds curve %q field size of %d bits`, x.BitLen(), crv.Params().Name, bits)
|
||||
}
|
||||
if y.BitLen() > bits {
|
||||
return fmt.Errorf(`invalid ECDSA public key: y coordinate is %d bits, exceeds curve %q field size of %d bits`, y.BitLen(), crv.Params().Name, bits)
|
||||
}
|
||||
|
||||
if ecdhCrv, ok := stdlibECDHCurve(crv); ok {
|
||||
size := (crv.Params().BitSize + 7) / 8
|
||||
buf := make([]byte, 1+2*size)
|
||||
buf[0] = 0x04
|
||||
x.FillBytes(buf[1 : 1+size])
|
||||
y.FillBytes(buf[1+size:])
|
||||
if _, err := ecdhCrv.NewPublicKey(buf); err != nil {
|
||||
return fmt.Errorf(`invalid ECDSA public key: %w`, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Custom-curve fallback. See the block comment on validateECDSAPoint
|
||||
// for the full justification of calling a deprecated-marked method;
|
||||
// the short version is that interface dispatch lands in the custom
|
||||
// curve's own IsOnCurve, not in deprecated stdlib code.
|
||||
if !crv.IsOnCurve(x, y) { //nolint:staticcheck // see validateECDSAPoint godoc: only path that validates custom curves
|
||||
return fmt.Errorf(`invalid ECDSA public key: point is not on curve %q`, crv.Params().Name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// stdlibECDHCurve maps a crypto/elliptic curve to its crypto/ecdh
|
||||
// counterpart when one exists. Only the NIST P-curves supported by both
|
||||
// packages are mapped; everything else returns ok=false and falls back
|
||||
// to the elliptic.Curve path in validateECDSAPoint.
|
||||
func stdlibECDHCurve(crv elliptic.Curve) (ecdh.Curve, bool) {
|
||||
switch crv {
|
||||
case elliptic.P256():
|
||||
return ecdh.P256(), true
|
||||
case elliptic.P384():
|
||||
return ecdh.P384(), true
|
||||
case elliptic.P521():
|
||||
return ecdh.P521(), true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func buildECDHPublicKey(alg jwa.EllipticCurveAlgorithm, xbuf, ybuf []byte) (*ecdh.PublicKey, error) {
|
||||
var ecdhcrv ecdh.Curve
|
||||
switch alg {
|
||||
@@ -170,34 +290,64 @@ func ecdsaJWKToRaw(keyif Key, hint any) (any, error) {
|
||||
}
|
||||
}
|
||||
|
||||
locker, ok := k.(rlocker)
|
||||
if ok {
|
||||
// rlocker is unexported with unexported methods, so only our
|
||||
// concrete types implement it. A successful assertion lets us
|
||||
// type-assert to the concrete struct and read fields directly
|
||||
// under a single batch lock. This avoids nested RLock (which
|
||||
// deadlocks when a writer is pending) while preserving an
|
||||
// atomic snapshot of all fields.
|
||||
var crv jwa.EllipticCurveAlgorithm
|
||||
var hasCrv bool
|
||||
var od, ox, oy []byte
|
||||
if locker, ok := k.(rlocker); ok {
|
||||
locker.rlock()
|
||||
defer locker.runlock()
|
||||
concrete := k.(*ecdsaPrivateKey) //nolint:forcetypeassert // rlocker is unexported; only our concrete types implement it
|
||||
if concrete.crv != nil {
|
||||
crv = *(concrete.crv)
|
||||
hasCrv = true
|
||||
}
|
||||
od, ox, oy = concrete.d, concrete.x, concrete.y
|
||||
locker.runlock()
|
||||
} else {
|
||||
// External implementation — use self-locking interface getters.
|
||||
var ok bool
|
||||
if crv, ok = k.Crv(); !ok {
|
||||
return nil, fmt.Errorf(`missing "crv" field`)
|
||||
}
|
||||
hasCrv = true
|
||||
if od, ok = k.D(); !ok {
|
||||
return nil, fmt.Errorf(`missing "d" field`)
|
||||
}
|
||||
if ox, ok = k.X(); !ok {
|
||||
return nil, fmt.Errorf(`missing "x" field`)
|
||||
}
|
||||
if oy, ok = k.Y(); !ok {
|
||||
return nil, fmt.Errorf(`missing "y" field`)
|
||||
}
|
||||
}
|
||||
|
||||
crv, ok := k.Crv()
|
||||
if !ok {
|
||||
if !hasCrv {
|
||||
return nil, fmt.Errorf(`missing "crv" field`)
|
||||
}
|
||||
|
||||
if isECDH {
|
||||
d, ok := k.D()
|
||||
if !ok {
|
||||
if od == nil {
|
||||
return nil, fmt.Errorf(`missing "d" field`)
|
||||
}
|
||||
return buildECDHPrivateKey(crv, d)
|
||||
return buildECDHPrivateKey(crv, od)
|
||||
}
|
||||
|
||||
x, ok := k.X()
|
||||
if !ok {
|
||||
if ox == nil {
|
||||
return nil, fmt.Errorf(`missing "x" field`)
|
||||
}
|
||||
y, ok := k.Y()
|
||||
if !ok {
|
||||
if oy == nil {
|
||||
return nil, fmt.Errorf(`missing "y" field`)
|
||||
}
|
||||
pubk, err := buildECDSAPublicKey(crv, x, y)
|
||||
if od == nil {
|
||||
return nil, fmt.Errorf(`missing "d" field`)
|
||||
}
|
||||
|
||||
pubk, err := buildECDSAPublicKey(crv, ox, oy)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to build public key: %w`, err)
|
||||
}
|
||||
@@ -205,12 +355,7 @@ func ecdsaJWKToRaw(keyif Key, hint any) (any, error) {
|
||||
var key ecdsa.PrivateKey
|
||||
var d big.Int
|
||||
|
||||
origD, ok := k.D()
|
||||
if !ok {
|
||||
return nil, fmt.Errorf(`missing "d" field`)
|
||||
}
|
||||
|
||||
d.SetBytes(origD)
|
||||
d.SetBytes(od)
|
||||
key.D = &d
|
||||
key.PublicKey = *pubk
|
||||
|
||||
@@ -231,24 +376,40 @@ func ecdsaJWKToRaw(keyif Key, hint any) (any, error) {
|
||||
}
|
||||
}
|
||||
|
||||
locker, ok := k.(rlocker)
|
||||
if ok {
|
||||
// See ECDSAPrivateKey case above for explanation of the rlocker pattern.
|
||||
var crv jwa.EllipticCurveAlgorithm
|
||||
var hasCrv bool
|
||||
var x, y []byte
|
||||
if locker, ok := k.(rlocker); ok {
|
||||
locker.rlock()
|
||||
defer locker.runlock()
|
||||
concrete := k.(*ecdsaPublicKey) //nolint:forcetypeassert // rlocker is unexported; only our concrete types implement it
|
||||
if concrete.crv != nil {
|
||||
crv = *(concrete.crv)
|
||||
hasCrv = true
|
||||
}
|
||||
x, y = concrete.x, concrete.y
|
||||
locker.runlock()
|
||||
} else {
|
||||
var ok bool
|
||||
if crv, ok = k.Crv(); !ok {
|
||||
return nil, fmt.Errorf(`missing "crv" field`)
|
||||
}
|
||||
hasCrv = true
|
||||
if x, ok = k.X(); !ok {
|
||||
return nil, fmt.Errorf(`missing "x" field`)
|
||||
}
|
||||
if y, ok = k.Y(); !ok {
|
||||
return nil, fmt.Errorf(`missing "y" field`)
|
||||
}
|
||||
}
|
||||
|
||||
crv, ok := k.Crv()
|
||||
if !ok {
|
||||
if !hasCrv {
|
||||
return nil, fmt.Errorf(`missing "crv" field`)
|
||||
}
|
||||
|
||||
x, ok := k.X()
|
||||
if !ok {
|
||||
if x == nil {
|
||||
return nil, fmt.Errorf(`missing "x" field`)
|
||||
}
|
||||
|
||||
y, ok := k.Y()
|
||||
if !ok {
|
||||
if y == nil {
|
||||
return nil, fmt.Errorf(`missing "y" field`)
|
||||
}
|
||||
if isECDH {
|
||||
@@ -305,12 +466,12 @@ func ecdsaThumbprint(hash crypto.Hash, crv, x, y string) []byte {
|
||||
|
||||
// Thumbprint returns the JWK thumbprint using the indicated
|
||||
// hashing algorithm, according to RFC 7638
|
||||
func (k ecdsaPublicKey) Thumbprint(hash crypto.Hash) ([]byte, error) {
|
||||
func (k *ecdsaPublicKey) Thumbprint(hash crypto.Hash) ([]byte, error) {
|
||||
k.mu.RLock()
|
||||
defer k.mu.RUnlock()
|
||||
|
||||
var key ecdsa.PublicKey
|
||||
if err := Export(&k, &key); err != nil {
|
||||
if err := Export(k, &key); err != nil {
|
||||
return nil, fmt.Errorf(`failed to export ecdsa.PublicKey for thumbprint generation: %w`, err)
|
||||
}
|
||||
|
||||
@@ -329,12 +490,12 @@ func (k ecdsaPublicKey) Thumbprint(hash crypto.Hash) ([]byte, error) {
|
||||
|
||||
// Thumbprint returns the JWK thumbprint using the indicated
|
||||
// hashing algorithm, according to RFC 7638
|
||||
func (k ecdsaPrivateKey) Thumbprint(hash crypto.Hash) ([]byte, error) {
|
||||
func (k *ecdsaPrivateKey) Thumbprint(hash crypto.Hash) ([]byte, error) {
|
||||
k.mu.RLock()
|
||||
defer k.mu.RUnlock()
|
||||
|
||||
var key ecdsa.PrivateKey
|
||||
if err := Export(&k, &key); err != nil {
|
||||
if err := Export(k, &key); err != nil {
|
||||
return nil, fmt.Errorf(`failed to export ecdsa.PrivateKey for thumbprint generation: %w`, err)
|
||||
}
|
||||
|
||||
@@ -367,12 +528,21 @@ func ecdsaValidateKey(k interface {
|
||||
}
|
||||
|
||||
keySize := ecutil.CalculateKeySize(crv)
|
||||
if x, ok := k.X(); !ok || len(x) != keySize {
|
||||
return fmt.Errorf(`invalid "x" length (%d) for curve %q`, len(x), crv.Params().Name)
|
||||
xbuf, ok := k.X()
|
||||
if !ok || len(xbuf) != keySize {
|
||||
return fmt.Errorf(`invalid "x" length (%d) for curve %q`, len(xbuf), crv.Params().Name)
|
||||
}
|
||||
|
||||
if y, ok := k.Y(); !ok || len(y) != keySize {
|
||||
return fmt.Errorf(`invalid "y" length (%d) for curve %q`, len(y), crv.Params().Name)
|
||||
ybuf, ok := k.Y()
|
||||
if !ok || len(ybuf) != keySize {
|
||||
return fmt.Errorf(`invalid "y" length (%d) for curve %q`, len(ybuf), crv.Params().Name)
|
||||
}
|
||||
|
||||
var x, y big.Int
|
||||
x.SetBytes(xbuf)
|
||||
y.SetBytes(ybuf)
|
||||
if err := validateECDSAPoint(crv, &x, &y); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if checkPrivate {
|
||||
|
||||
18
vendor/github.com/lestrrat-go/jwx/v3/jwk/ecdsa/ecdsa.go
generated
vendored
18
vendor/github.com/lestrrat-go/jwx/v3/jwk/ecdsa/ecdsa.go
generated
vendored
@@ -45,16 +45,22 @@ func rebuildCurves() {
|
||||
}
|
||||
}
|
||||
|
||||
// Algorithms returns the list of registered jwa.EllipticCurveAlgorithms
|
||||
// that ca be used for ECDSA keys.
|
||||
// Algorithms returns a snapshot of the registered
|
||||
// jwa.EllipticCurveAlgorithms that can be used for ECDSA keys.
|
||||
//
|
||||
// The returned slice is caller-owned. Modifying it does not affect the
|
||||
// package registry, and ordering is unspecified.
|
||||
func Algorithms() []jwa.EllipticCurveAlgorithm {
|
||||
muCurves.RLock()
|
||||
defer muCurves.RUnlock()
|
||||
|
||||
return algList
|
||||
return append([]jwa.EllipticCurveAlgorithm(nil), algList...)
|
||||
}
|
||||
|
||||
func AlgorithmFromCurve(crv elliptic.Curve) (jwa.EllipticCurveAlgorithm, error) {
|
||||
muCurves.RLock()
|
||||
defer muCurves.RUnlock()
|
||||
|
||||
alg, ok := curveToAlgMap[crv]
|
||||
if !ok {
|
||||
return jwa.InvalidEllipticCurve(), fmt.Errorf(`unknown elliptic curve: %q`, crv)
|
||||
@@ -63,6 +69,9 @@ func AlgorithmFromCurve(crv elliptic.Curve) (jwa.EllipticCurveAlgorithm, error)
|
||||
}
|
||||
|
||||
func CurveFromAlgorithm(alg jwa.EllipticCurveAlgorithm) (elliptic.Curve, error) {
|
||||
muCurves.RLock()
|
||||
defer muCurves.RUnlock()
|
||||
|
||||
crv, ok := algToCurveMap[alg]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf(`unknown elliptic curve algorithm: %q`, alg)
|
||||
@@ -71,6 +80,9 @@ func CurveFromAlgorithm(alg jwa.EllipticCurveAlgorithm) (elliptic.Curve, error)
|
||||
}
|
||||
|
||||
func IsCurveAvailable(alg jwa.EllipticCurveAlgorithm) bool {
|
||||
muCurves.RLock()
|
||||
defer muCurves.RUnlock()
|
||||
|
||||
_, ok := algToCurveMap[alg]
|
||||
return ok
|
||||
}
|
||||
|
||||
480
vendor/github.com/lestrrat-go/jwx/v3/jwk/ecdsa_gen.go
generated
vendored
480
vendor/github.com/lestrrat-go/jwx/v3/jwk/ecdsa_gen.go
generated
vendored
@@ -15,6 +15,7 @@ import (
|
||||
"github.com/lestrrat-go/jwx/v3/internal/pool"
|
||||
"github.com/lestrrat-go/jwx/v3/internal/tokens"
|
||||
"github.com/lestrrat-go/jwx/v3/jwa"
|
||||
"github.com/lestrrat-go/jwx/v3/jwk/internal/registry"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -44,7 +45,7 @@ type ecdsaPublicKey struct {
|
||||
x509URL *string // https://tools.ietf.org/html/rfc7515#section-4.1.5
|
||||
y []byte
|
||||
privateParams map[string]any
|
||||
mu *sync.RWMutex
|
||||
mu sync.RWMutex
|
||||
dc json.DecodeCtx
|
||||
}
|
||||
|
||||
@@ -53,28 +54,29 @@ var _ Key = &ecdsaPublicKey{}
|
||||
|
||||
func newECDSAPublicKey() *ecdsaPublicKey {
|
||||
return &ecdsaPublicKey{
|
||||
mu: &sync.RWMutex{},
|
||||
privateParams: make(map[string]any),
|
||||
}
|
||||
}
|
||||
|
||||
func (h ecdsaPublicKey) KeyType() jwa.KeyType {
|
||||
func (h *ecdsaPublicKey) KeyType() jwa.KeyType {
|
||||
return jwa.EC()
|
||||
}
|
||||
|
||||
func (h ecdsaPublicKey) rlock() {
|
||||
func (h *ecdsaPublicKey) rlock() {
|
||||
h.mu.RLock()
|
||||
}
|
||||
|
||||
func (h ecdsaPublicKey) runlock() {
|
||||
func (h *ecdsaPublicKey) runlock() {
|
||||
h.mu.RUnlock()
|
||||
}
|
||||
|
||||
func (h ecdsaPublicKey) IsPrivate() bool {
|
||||
func (h *ecdsaPublicKey) IsPrivate() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (h *ecdsaPublicKey) Algorithm() (jwa.KeyAlgorithm, bool) {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
if h.algorithm != nil {
|
||||
return *(h.algorithm), true
|
||||
}
|
||||
@@ -82,6 +84,8 @@ func (h *ecdsaPublicKey) Algorithm() (jwa.KeyAlgorithm, bool) {
|
||||
}
|
||||
|
||||
func (h *ecdsaPublicKey) Crv() (jwa.EllipticCurveAlgorithm, bool) {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
if h.crv != nil {
|
||||
return *(h.crv), true
|
||||
}
|
||||
@@ -89,6 +93,8 @@ func (h *ecdsaPublicKey) Crv() (jwa.EllipticCurveAlgorithm, bool) {
|
||||
}
|
||||
|
||||
func (h *ecdsaPublicKey) KeyID() (string, bool) {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
if h.keyID != nil {
|
||||
return *(h.keyID), true
|
||||
}
|
||||
@@ -96,6 +102,8 @@ func (h *ecdsaPublicKey) KeyID() (string, bool) {
|
||||
}
|
||||
|
||||
func (h *ecdsaPublicKey) KeyOps() (KeyOperationList, bool) {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
if h.keyOps != nil {
|
||||
return *(h.keyOps), true
|
||||
}
|
||||
@@ -103,6 +111,8 @@ func (h *ecdsaPublicKey) KeyOps() (KeyOperationList, bool) {
|
||||
}
|
||||
|
||||
func (h *ecdsaPublicKey) KeyUsage() (string, bool) {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
if h.keyUsage != nil {
|
||||
return *(h.keyUsage), true
|
||||
}
|
||||
@@ -110,6 +120,8 @@ func (h *ecdsaPublicKey) KeyUsage() (string, bool) {
|
||||
}
|
||||
|
||||
func (h *ecdsaPublicKey) X() ([]byte, bool) {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
if h.x != nil {
|
||||
return h.x, true
|
||||
}
|
||||
@@ -117,10 +129,17 @@ func (h *ecdsaPublicKey) X() ([]byte, bool) {
|
||||
}
|
||||
|
||||
func (h *ecdsaPublicKey) X509CertChain() (*cert.Chain, bool) {
|
||||
return h.x509CertChain, true
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
if h.x509CertChain != nil {
|
||||
return h.x509CertChain, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (h *ecdsaPublicKey) X509CertThumbprint() (string, bool) {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
if h.x509CertThumbprint != nil {
|
||||
return *(h.x509CertThumbprint), true
|
||||
}
|
||||
@@ -128,6 +147,8 @@ func (h *ecdsaPublicKey) X509CertThumbprint() (string, bool) {
|
||||
}
|
||||
|
||||
func (h *ecdsaPublicKey) X509CertThumbprintS256() (string, bool) {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
if h.x509CertThumbprintS256 != nil {
|
||||
return *(h.x509CertThumbprintS256), true
|
||||
}
|
||||
@@ -135,6 +156,8 @@ func (h *ecdsaPublicKey) X509CertThumbprintS256() (string, bool) {
|
||||
}
|
||||
|
||||
func (h *ecdsaPublicKey) X509URL() (string, bool) {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
if h.x509URL != nil {
|
||||
return *(h.x509URL), true
|
||||
}
|
||||
@@ -142,6 +165,8 @@ func (h *ecdsaPublicKey) X509URL() (string, bool) {
|
||||
}
|
||||
|
||||
func (h *ecdsaPublicKey) Y() ([]byte, bool) {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
if h.y != nil {
|
||||
return h.y, true
|
||||
}
|
||||
@@ -348,7 +373,12 @@ func (h *ecdsaPublicKey) setNoLock(name string, value any) error {
|
||||
}
|
||||
case ECDSAXKey:
|
||||
if v, ok := value.([]byte); ok {
|
||||
h.x = v
|
||||
if v == nil {
|
||||
h.x = nil
|
||||
} else {
|
||||
h.x = make([]byte, len(v))
|
||||
copy(h.x, v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf(`invalid value for %s key: %T`, ECDSAXKey, value)
|
||||
@@ -378,7 +408,12 @@ func (h *ecdsaPublicKey) setNoLock(name string, value any) error {
|
||||
return fmt.Errorf(`invalid value for %s key: %T`, X509URLKey, value)
|
||||
case ECDSAYKey:
|
||||
if v, ok := value.([]byte); ok {
|
||||
h.y = v
|
||||
if v == nil {
|
||||
h.y = nil
|
||||
} else {
|
||||
h.y = make([]byte, len(v))
|
||||
copy(h.y, v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf(`invalid value for %s key: %T`, ECDSAYKey, value)
|
||||
@@ -476,7 +511,7 @@ LOOP:
|
||||
case string: // Objects can only have string keys
|
||||
switch tok {
|
||||
case KeyTypeKey:
|
||||
val, err := json.ReadNextStringToken(dec)
|
||||
val, err := json.ReadNextStringToken(dec, h.dc)
|
||||
if err != nil {
|
||||
return fmt.Errorf(`error reading token: %w`, err)
|
||||
}
|
||||
@@ -500,7 +535,7 @@ LOOP:
|
||||
}
|
||||
h.crv = &decoded
|
||||
case KeyIDKey:
|
||||
if err := json.AssignNextStringToken(&h.keyID, dec); err != nil {
|
||||
if err := json.AssignNextStringToken(&h.keyID, dec, h.dc); err != nil {
|
||||
return fmt.Errorf(`failed to decode value for key %s: %w`, KeyIDKey, err)
|
||||
}
|
||||
case KeyOpsKey:
|
||||
@@ -510,7 +545,7 @@ LOOP:
|
||||
}
|
||||
h.keyOps = &decoded
|
||||
case KeyUsageKey:
|
||||
if err := json.AssignNextStringToken(&h.keyUsage, dec); err != nil {
|
||||
if err := json.AssignNextStringToken(&h.keyUsage, dec, h.dc); err != nil {
|
||||
return fmt.Errorf(`failed to decode value for key %s: %w`, KeyUsageKey, err)
|
||||
}
|
||||
case ECDSAXKey:
|
||||
@@ -524,15 +559,15 @@ LOOP:
|
||||
}
|
||||
h.x509CertChain = &decoded
|
||||
case X509CertThumbprintKey:
|
||||
if err := json.AssignNextStringToken(&h.x509CertThumbprint, dec); err != nil {
|
||||
if err := json.AssignNextStringToken(&h.x509CertThumbprint, dec, h.dc); err != nil {
|
||||
return fmt.Errorf(`failed to decode value for key %s: %w`, X509CertThumbprintKey, err)
|
||||
}
|
||||
case X509CertThumbprintS256Key:
|
||||
if err := json.AssignNextStringToken(&h.x509CertThumbprintS256, dec); err != nil {
|
||||
if err := json.AssignNextStringToken(&h.x509CertThumbprintS256, dec, h.dc); err != nil {
|
||||
return fmt.Errorf(`failed to decode value for key %s: %w`, X509CertThumbprintS256Key, err)
|
||||
}
|
||||
case X509URLKey:
|
||||
if err := json.AssignNextStringToken(&h.x509URL, dec); err != nil {
|
||||
if err := json.AssignNextStringToken(&h.x509URL, dec, h.dc); err != nil {
|
||||
return fmt.Errorf(`failed to decode value for key %s: %w`, X509URLKey, err)
|
||||
}
|
||||
case ECDSAYKey:
|
||||
@@ -549,7 +584,7 @@ LOOP:
|
||||
}
|
||||
}
|
||||
}
|
||||
decoded, err := registry.Decode(dec, tok)
|
||||
decoded, err := fieldRegistry.Decode(dec, tok)
|
||||
if err == nil {
|
||||
h.setNoLock(tok, decoded)
|
||||
continue
|
||||
@@ -572,88 +607,142 @@ LOOP:
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h ecdsaPublicKey) MarshalJSON() ([]byte, error) {
|
||||
data := make(map[string]any)
|
||||
fields := make([]string, 0, 11)
|
||||
data[KeyTypeKey] = jwa.EC()
|
||||
fields = append(fields, KeyTypeKey)
|
||||
func (h *ecdsaPublicKey) makePairs() ([]fieldPair, error) {
|
||||
pairs := getFieldPairList()
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
{
|
||||
v, err := json.Marshal(jwa.EC())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, KeyTypeKey, err)
|
||||
}
|
||||
pairs = append(pairs, fieldPair{Name: KeyTypeKey, Value: v})
|
||||
}
|
||||
if h.algorithm != nil {
|
||||
data[AlgorithmKey] = *(h.algorithm)
|
||||
fields = append(fields, AlgorithmKey)
|
||||
v, err := json.Marshal(*(h.algorithm))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, AlgorithmKey, err)
|
||||
}
|
||||
pairs = append(pairs, fieldPair{Name: AlgorithmKey, Value: v})
|
||||
}
|
||||
if h.crv != nil {
|
||||
data[ECDSACrvKey] = *(h.crv)
|
||||
fields = append(fields, ECDSACrvKey)
|
||||
v, err := json.Marshal(*(h.crv))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, ECDSACrvKey, err)
|
||||
}
|
||||
pairs = append(pairs, fieldPair{Name: ECDSACrvKey, Value: v})
|
||||
}
|
||||
if h.keyID != nil {
|
||||
data[KeyIDKey] = *(h.keyID)
|
||||
fields = append(fields, KeyIDKey)
|
||||
v, err := json.Marshal(*(h.keyID))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, KeyIDKey, err)
|
||||
}
|
||||
pairs = append(pairs, fieldPair{Name: KeyIDKey, Value: v})
|
||||
}
|
||||
if h.keyOps != nil {
|
||||
data[KeyOpsKey] = *(h.keyOps)
|
||||
fields = append(fields, KeyOpsKey)
|
||||
v, err := json.Marshal(*(h.keyOps))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, KeyOpsKey, err)
|
||||
}
|
||||
pairs = append(pairs, fieldPair{Name: KeyOpsKey, Value: v})
|
||||
}
|
||||
if h.keyUsage != nil {
|
||||
data[KeyUsageKey] = *(h.keyUsage)
|
||||
fields = append(fields, KeyUsageKey)
|
||||
v, err := json.Marshal(*(h.keyUsage))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, KeyUsageKey, err)
|
||||
}
|
||||
pairs = append(pairs, fieldPair{Name: KeyUsageKey, Value: v})
|
||||
}
|
||||
if h.x != nil {
|
||||
data[ECDSAXKey] = h.x
|
||||
fields = append(fields, ECDSAXKey)
|
||||
v, err := json.Marshal(base64.EncodeToString(h.x))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, ECDSAXKey, err)
|
||||
}
|
||||
pairs = append(pairs, fieldPair{Name: ECDSAXKey, Value: v})
|
||||
}
|
||||
if h.x509CertChain != nil {
|
||||
data[X509CertChainKey] = h.x509CertChain
|
||||
fields = append(fields, X509CertChainKey)
|
||||
v, err := json.Marshal(h.x509CertChain)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, X509CertChainKey, err)
|
||||
}
|
||||
pairs = append(pairs, fieldPair{Name: X509CertChainKey, Value: v})
|
||||
}
|
||||
if h.x509CertThumbprint != nil {
|
||||
data[X509CertThumbprintKey] = *(h.x509CertThumbprint)
|
||||
fields = append(fields, X509CertThumbprintKey)
|
||||
v, err := json.Marshal(*(h.x509CertThumbprint))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, X509CertThumbprintKey, err)
|
||||
}
|
||||
pairs = append(pairs, fieldPair{Name: X509CertThumbprintKey, Value: v})
|
||||
}
|
||||
if h.x509CertThumbprintS256 != nil {
|
||||
data[X509CertThumbprintS256Key] = *(h.x509CertThumbprintS256)
|
||||
fields = append(fields, X509CertThumbprintS256Key)
|
||||
v, err := json.Marshal(*(h.x509CertThumbprintS256))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, X509CertThumbprintS256Key, err)
|
||||
}
|
||||
pairs = append(pairs, fieldPair{Name: X509CertThumbprintS256Key, Value: v})
|
||||
}
|
||||
if h.x509URL != nil {
|
||||
data[X509URLKey] = *(h.x509URL)
|
||||
fields = append(fields, X509URLKey)
|
||||
v, err := json.Marshal(*(h.x509URL))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, X509URLKey, err)
|
||||
}
|
||||
pairs = append(pairs, fieldPair{Name: X509URLKey, Value: v})
|
||||
}
|
||||
if h.y != nil {
|
||||
data[ECDSAYKey] = h.y
|
||||
fields = append(fields, ECDSAYKey)
|
||||
v, err := json.Marshal(base64.EncodeToString(h.y))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, ECDSAYKey, err)
|
||||
}
|
||||
pairs = append(pairs, fieldPair{Name: ECDSAYKey, Value: v})
|
||||
}
|
||||
for k, v := range h.privateParams {
|
||||
data[k] = v
|
||||
fields = append(fields, k)
|
||||
}
|
||||
|
||||
sort.Strings(fields)
|
||||
buf := pool.BytesBuffer().Get()
|
||||
defer pool.BytesBuffer().Put(buf)
|
||||
buf.WriteByte(tokens.OpenCurlyBracket)
|
||||
enc := json.NewEncoder(buf)
|
||||
for i, f := range fields {
|
||||
if i > 0 {
|
||||
buf.WriteRune(tokens.Comma)
|
||||
}
|
||||
buf.WriteRune(tokens.DoubleQuote)
|
||||
buf.WriteString(f)
|
||||
buf.WriteString(`":`)
|
||||
v := data[f]
|
||||
var encoded []byte
|
||||
switch v := v.(type) {
|
||||
case []byte:
|
||||
buf.WriteRune(tokens.DoubleQuote)
|
||||
buf.WriteString(base64.EncodeToString(v))
|
||||
buf.WriteRune(tokens.DoubleQuote)
|
||||
default:
|
||||
if err := enc.Encode(v); err != nil {
|
||||
return nil, fmt.Errorf(`failed to encode value for field %s: %w`, f, err)
|
||||
var err error
|
||||
encoded, err = json.Marshal(base64.EncodeToString(v))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, k, err)
|
||||
}
|
||||
default:
|
||||
var err error
|
||||
encoded, err = json.Marshal(v)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, k, err)
|
||||
}
|
||||
buf.Truncate(buf.Len() - 1)
|
||||
}
|
||||
pairs = append(pairs, fieldPair{Name: k, Value: encoded})
|
||||
}
|
||||
|
||||
sort.Slice(pairs, func(i, j int) bool {
|
||||
return pairs[i].Name < pairs[j].Name
|
||||
})
|
||||
|
||||
return pairs, nil
|
||||
}
|
||||
|
||||
func (h *ecdsaPublicKey) MarshalJSON() ([]byte, error) {
|
||||
buf := pool.BytesBuffer().Get()
|
||||
defer pool.BytesBuffer().Put(buf)
|
||||
pairs, err := h.makePairs()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to make pairs: %w`, err)
|
||||
}
|
||||
buf.WriteByte(tokens.OpenCurlyBracket)
|
||||
|
||||
for i, pair := range pairs {
|
||||
if i > 0 {
|
||||
buf.WriteByte(tokens.Comma)
|
||||
}
|
||||
buf.WriteByte('"')
|
||||
buf.WriteString(pair.Name)
|
||||
buf.WriteString(`": `)
|
||||
buf.Write(pair.Value.([]byte))
|
||||
}
|
||||
buf.WriteByte(tokens.CloseCurlyBracket)
|
||||
ret := make([]byte, buf.Len())
|
||||
copy(ret, buf.Bytes())
|
||||
putFieldPairList(pairs)
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
@@ -723,7 +812,7 @@ type ecdsaPrivateKey struct {
|
||||
x509URL *string // https://tools.ietf.org/html/rfc7515#section-4.1.5
|
||||
y []byte
|
||||
privateParams map[string]any
|
||||
mu *sync.RWMutex
|
||||
mu sync.RWMutex
|
||||
dc json.DecodeCtx
|
||||
}
|
||||
|
||||
@@ -732,28 +821,29 @@ var _ Key = &ecdsaPrivateKey{}
|
||||
|
||||
func newECDSAPrivateKey() *ecdsaPrivateKey {
|
||||
return &ecdsaPrivateKey{
|
||||
mu: &sync.RWMutex{},
|
||||
privateParams: make(map[string]any),
|
||||
}
|
||||
}
|
||||
|
||||
func (h ecdsaPrivateKey) KeyType() jwa.KeyType {
|
||||
func (h *ecdsaPrivateKey) KeyType() jwa.KeyType {
|
||||
return jwa.EC()
|
||||
}
|
||||
|
||||
func (h ecdsaPrivateKey) rlock() {
|
||||
func (h *ecdsaPrivateKey) rlock() {
|
||||
h.mu.RLock()
|
||||
}
|
||||
|
||||
func (h ecdsaPrivateKey) runlock() {
|
||||
func (h *ecdsaPrivateKey) runlock() {
|
||||
h.mu.RUnlock()
|
||||
}
|
||||
|
||||
func (h ecdsaPrivateKey) IsPrivate() bool {
|
||||
func (h *ecdsaPrivateKey) IsPrivate() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (h *ecdsaPrivateKey) Algorithm() (jwa.KeyAlgorithm, bool) {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
if h.algorithm != nil {
|
||||
return *(h.algorithm), true
|
||||
}
|
||||
@@ -761,6 +851,8 @@ func (h *ecdsaPrivateKey) Algorithm() (jwa.KeyAlgorithm, bool) {
|
||||
}
|
||||
|
||||
func (h *ecdsaPrivateKey) Crv() (jwa.EllipticCurveAlgorithm, bool) {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
if h.crv != nil {
|
||||
return *(h.crv), true
|
||||
}
|
||||
@@ -768,6 +860,8 @@ func (h *ecdsaPrivateKey) Crv() (jwa.EllipticCurveAlgorithm, bool) {
|
||||
}
|
||||
|
||||
func (h *ecdsaPrivateKey) D() ([]byte, bool) {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
if h.d != nil {
|
||||
return h.d, true
|
||||
}
|
||||
@@ -775,6 +869,8 @@ func (h *ecdsaPrivateKey) D() ([]byte, bool) {
|
||||
}
|
||||
|
||||
func (h *ecdsaPrivateKey) KeyID() (string, bool) {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
if h.keyID != nil {
|
||||
return *(h.keyID), true
|
||||
}
|
||||
@@ -782,6 +878,8 @@ func (h *ecdsaPrivateKey) KeyID() (string, bool) {
|
||||
}
|
||||
|
||||
func (h *ecdsaPrivateKey) KeyOps() (KeyOperationList, bool) {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
if h.keyOps != nil {
|
||||
return *(h.keyOps), true
|
||||
}
|
||||
@@ -789,6 +887,8 @@ func (h *ecdsaPrivateKey) KeyOps() (KeyOperationList, bool) {
|
||||
}
|
||||
|
||||
func (h *ecdsaPrivateKey) KeyUsage() (string, bool) {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
if h.keyUsage != nil {
|
||||
return *(h.keyUsage), true
|
||||
}
|
||||
@@ -796,6 +896,8 @@ func (h *ecdsaPrivateKey) KeyUsage() (string, bool) {
|
||||
}
|
||||
|
||||
func (h *ecdsaPrivateKey) X() ([]byte, bool) {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
if h.x != nil {
|
||||
return h.x, true
|
||||
}
|
||||
@@ -803,10 +905,17 @@ func (h *ecdsaPrivateKey) X() ([]byte, bool) {
|
||||
}
|
||||
|
||||
func (h *ecdsaPrivateKey) X509CertChain() (*cert.Chain, bool) {
|
||||
return h.x509CertChain, true
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
if h.x509CertChain != nil {
|
||||
return h.x509CertChain, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (h *ecdsaPrivateKey) X509CertThumbprint() (string, bool) {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
if h.x509CertThumbprint != nil {
|
||||
return *(h.x509CertThumbprint), true
|
||||
}
|
||||
@@ -814,6 +923,8 @@ func (h *ecdsaPrivateKey) X509CertThumbprint() (string, bool) {
|
||||
}
|
||||
|
||||
func (h *ecdsaPrivateKey) X509CertThumbprintS256() (string, bool) {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
if h.x509CertThumbprintS256 != nil {
|
||||
return *(h.x509CertThumbprintS256), true
|
||||
}
|
||||
@@ -821,6 +932,8 @@ func (h *ecdsaPrivateKey) X509CertThumbprintS256() (string, bool) {
|
||||
}
|
||||
|
||||
func (h *ecdsaPrivateKey) X509URL() (string, bool) {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
if h.x509URL != nil {
|
||||
return *(h.x509URL), true
|
||||
}
|
||||
@@ -828,6 +941,8 @@ func (h *ecdsaPrivateKey) X509URL() (string, bool) {
|
||||
}
|
||||
|
||||
func (h *ecdsaPrivateKey) Y() ([]byte, bool) {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
if h.y != nil {
|
||||
return h.y, true
|
||||
}
|
||||
@@ -1016,7 +1131,12 @@ func (h *ecdsaPrivateKey) setNoLock(name string, value any) error {
|
||||
return fmt.Errorf(`invalid value for %s key: %T`, ECDSACrvKey, value)
|
||||
case ECDSADKey:
|
||||
if v, ok := value.([]byte); ok {
|
||||
h.d = v
|
||||
if v == nil {
|
||||
h.d = nil
|
||||
} else {
|
||||
h.d = make([]byte, len(v))
|
||||
copy(h.d, v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf(`invalid value for %s key: %T`, ECDSADKey, value)
|
||||
@@ -1050,7 +1170,12 @@ func (h *ecdsaPrivateKey) setNoLock(name string, value any) error {
|
||||
}
|
||||
case ECDSAXKey:
|
||||
if v, ok := value.([]byte); ok {
|
||||
h.x = v
|
||||
if v == nil {
|
||||
h.x = nil
|
||||
} else {
|
||||
h.x = make([]byte, len(v))
|
||||
copy(h.x, v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf(`invalid value for %s key: %T`, ECDSAXKey, value)
|
||||
@@ -1080,7 +1205,12 @@ func (h *ecdsaPrivateKey) setNoLock(name string, value any) error {
|
||||
return fmt.Errorf(`invalid value for %s key: %T`, X509URLKey, value)
|
||||
case ECDSAYKey:
|
||||
if v, ok := value.([]byte); ok {
|
||||
h.y = v
|
||||
if v == nil {
|
||||
h.y = nil
|
||||
} else {
|
||||
h.y = make([]byte, len(v))
|
||||
copy(h.y, v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf(`invalid value for %s key: %T`, ECDSAYKey, value)
|
||||
@@ -1147,7 +1277,7 @@ func (k *ecdsaPrivateKey) SetDecodeCtx(dc json.DecodeCtx) {
|
||||
k.dc = dc
|
||||
}
|
||||
|
||||
func (h *ecdsaPrivateKey) UnmarshalJSON(buf []byte) error {
|
||||
func (h *ecdsaPrivateKey) UnmarshalJSON(buf []byte) (retErr error) {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
h.algorithm = nil
|
||||
@@ -1162,6 +1292,16 @@ func (h *ecdsaPrivateKey) UnmarshalJSON(buf []byte) error {
|
||||
h.x509CertThumbprintS256 = nil
|
||||
h.x509URL = nil
|
||||
h.y = nil
|
||||
defer func() {
|
||||
if retErr != nil {
|
||||
clear(h.d)
|
||||
h.d = nil
|
||||
clear(h.x)
|
||||
h.x = nil
|
||||
clear(h.y)
|
||||
h.y = nil
|
||||
}
|
||||
}()
|
||||
dec := json.NewDecoder(bytes.NewReader(buf))
|
||||
LOOP:
|
||||
for {
|
||||
@@ -1181,7 +1321,7 @@ LOOP:
|
||||
case string: // Objects can only have string keys
|
||||
switch tok {
|
||||
case KeyTypeKey:
|
||||
val, err := json.ReadNextStringToken(dec)
|
||||
val, err := json.ReadNextStringToken(dec, h.dc)
|
||||
if err != nil {
|
||||
return fmt.Errorf(`error reading token: %w`, err)
|
||||
}
|
||||
@@ -1209,7 +1349,7 @@ LOOP:
|
||||
return fmt.Errorf(`failed to decode value for key %s: %w`, ECDSADKey, err)
|
||||
}
|
||||
case KeyIDKey:
|
||||
if err := json.AssignNextStringToken(&h.keyID, dec); err != nil {
|
||||
if err := json.AssignNextStringToken(&h.keyID, dec, h.dc); err != nil {
|
||||
return fmt.Errorf(`failed to decode value for key %s: %w`, KeyIDKey, err)
|
||||
}
|
||||
case KeyOpsKey:
|
||||
@@ -1219,7 +1359,7 @@ LOOP:
|
||||
}
|
||||
h.keyOps = &decoded
|
||||
case KeyUsageKey:
|
||||
if err := json.AssignNextStringToken(&h.keyUsage, dec); err != nil {
|
||||
if err := json.AssignNextStringToken(&h.keyUsage, dec, h.dc); err != nil {
|
||||
return fmt.Errorf(`failed to decode value for key %s: %w`, KeyUsageKey, err)
|
||||
}
|
||||
case ECDSAXKey:
|
||||
@@ -1233,15 +1373,15 @@ LOOP:
|
||||
}
|
||||
h.x509CertChain = &decoded
|
||||
case X509CertThumbprintKey:
|
||||
if err := json.AssignNextStringToken(&h.x509CertThumbprint, dec); err != nil {
|
||||
if err := json.AssignNextStringToken(&h.x509CertThumbprint, dec, h.dc); err != nil {
|
||||
return fmt.Errorf(`failed to decode value for key %s: %w`, X509CertThumbprintKey, err)
|
||||
}
|
||||
case X509CertThumbprintS256Key:
|
||||
if err := json.AssignNextStringToken(&h.x509CertThumbprintS256, dec); err != nil {
|
||||
if err := json.AssignNextStringToken(&h.x509CertThumbprintS256, dec, h.dc); err != nil {
|
||||
return fmt.Errorf(`failed to decode value for key %s: %w`, X509CertThumbprintS256Key, err)
|
||||
}
|
||||
case X509URLKey:
|
||||
if err := json.AssignNextStringToken(&h.x509URL, dec); err != nil {
|
||||
if err := json.AssignNextStringToken(&h.x509URL, dec, h.dc); err != nil {
|
||||
return fmt.Errorf(`failed to decode value for key %s: %w`, X509URLKey, err)
|
||||
}
|
||||
case ECDSAYKey:
|
||||
@@ -1258,7 +1398,7 @@ LOOP:
|
||||
}
|
||||
}
|
||||
}
|
||||
decoded, err := registry.Decode(dec, tok)
|
||||
decoded, err := fieldRegistry.Decode(dec, tok)
|
||||
if err == nil {
|
||||
h.setNoLock(tok, decoded)
|
||||
continue
|
||||
@@ -1284,92 +1424,149 @@ LOOP:
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h ecdsaPrivateKey) MarshalJSON() ([]byte, error) {
|
||||
data := make(map[string]any)
|
||||
fields := make([]string, 0, 12)
|
||||
data[KeyTypeKey] = jwa.EC()
|
||||
fields = append(fields, KeyTypeKey)
|
||||
func (h *ecdsaPrivateKey) makePairs() ([]fieldPair, error) {
|
||||
pairs := getFieldPairList()
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
{
|
||||
v, err := json.Marshal(jwa.EC())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, KeyTypeKey, err)
|
||||
}
|
||||
pairs = append(pairs, fieldPair{Name: KeyTypeKey, Value: v})
|
||||
}
|
||||
if h.algorithm != nil {
|
||||
data[AlgorithmKey] = *(h.algorithm)
|
||||
fields = append(fields, AlgorithmKey)
|
||||
v, err := json.Marshal(*(h.algorithm))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, AlgorithmKey, err)
|
||||
}
|
||||
pairs = append(pairs, fieldPair{Name: AlgorithmKey, Value: v})
|
||||
}
|
||||
if h.crv != nil {
|
||||
data[ECDSACrvKey] = *(h.crv)
|
||||
fields = append(fields, ECDSACrvKey)
|
||||
v, err := json.Marshal(*(h.crv))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, ECDSACrvKey, err)
|
||||
}
|
||||
pairs = append(pairs, fieldPair{Name: ECDSACrvKey, Value: v})
|
||||
}
|
||||
if h.d != nil {
|
||||
data[ECDSADKey] = h.d
|
||||
fields = append(fields, ECDSADKey)
|
||||
v, err := json.Marshal(base64.EncodeToString(h.d))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, ECDSADKey, err)
|
||||
}
|
||||
pairs = append(pairs, fieldPair{Name: ECDSADKey, Value: v})
|
||||
}
|
||||
if h.keyID != nil {
|
||||
data[KeyIDKey] = *(h.keyID)
|
||||
fields = append(fields, KeyIDKey)
|
||||
v, err := json.Marshal(*(h.keyID))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, KeyIDKey, err)
|
||||
}
|
||||
pairs = append(pairs, fieldPair{Name: KeyIDKey, Value: v})
|
||||
}
|
||||
if h.keyOps != nil {
|
||||
data[KeyOpsKey] = *(h.keyOps)
|
||||
fields = append(fields, KeyOpsKey)
|
||||
v, err := json.Marshal(*(h.keyOps))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, KeyOpsKey, err)
|
||||
}
|
||||
pairs = append(pairs, fieldPair{Name: KeyOpsKey, Value: v})
|
||||
}
|
||||
if h.keyUsage != nil {
|
||||
data[KeyUsageKey] = *(h.keyUsage)
|
||||
fields = append(fields, KeyUsageKey)
|
||||
v, err := json.Marshal(*(h.keyUsage))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, KeyUsageKey, err)
|
||||
}
|
||||
pairs = append(pairs, fieldPair{Name: KeyUsageKey, Value: v})
|
||||
}
|
||||
if h.x != nil {
|
||||
data[ECDSAXKey] = h.x
|
||||
fields = append(fields, ECDSAXKey)
|
||||
v, err := json.Marshal(base64.EncodeToString(h.x))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, ECDSAXKey, err)
|
||||
}
|
||||
pairs = append(pairs, fieldPair{Name: ECDSAXKey, Value: v})
|
||||
}
|
||||
if h.x509CertChain != nil {
|
||||
data[X509CertChainKey] = h.x509CertChain
|
||||
fields = append(fields, X509CertChainKey)
|
||||
v, err := json.Marshal(h.x509CertChain)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, X509CertChainKey, err)
|
||||
}
|
||||
pairs = append(pairs, fieldPair{Name: X509CertChainKey, Value: v})
|
||||
}
|
||||
if h.x509CertThumbprint != nil {
|
||||
data[X509CertThumbprintKey] = *(h.x509CertThumbprint)
|
||||
fields = append(fields, X509CertThumbprintKey)
|
||||
v, err := json.Marshal(*(h.x509CertThumbprint))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, X509CertThumbprintKey, err)
|
||||
}
|
||||
pairs = append(pairs, fieldPair{Name: X509CertThumbprintKey, Value: v})
|
||||
}
|
||||
if h.x509CertThumbprintS256 != nil {
|
||||
data[X509CertThumbprintS256Key] = *(h.x509CertThumbprintS256)
|
||||
fields = append(fields, X509CertThumbprintS256Key)
|
||||
v, err := json.Marshal(*(h.x509CertThumbprintS256))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, X509CertThumbprintS256Key, err)
|
||||
}
|
||||
pairs = append(pairs, fieldPair{Name: X509CertThumbprintS256Key, Value: v})
|
||||
}
|
||||
if h.x509URL != nil {
|
||||
data[X509URLKey] = *(h.x509URL)
|
||||
fields = append(fields, X509URLKey)
|
||||
v, err := json.Marshal(*(h.x509URL))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, X509URLKey, err)
|
||||
}
|
||||
pairs = append(pairs, fieldPair{Name: X509URLKey, Value: v})
|
||||
}
|
||||
if h.y != nil {
|
||||
data[ECDSAYKey] = h.y
|
||||
fields = append(fields, ECDSAYKey)
|
||||
v, err := json.Marshal(base64.EncodeToString(h.y))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, ECDSAYKey, err)
|
||||
}
|
||||
pairs = append(pairs, fieldPair{Name: ECDSAYKey, Value: v})
|
||||
}
|
||||
for k, v := range h.privateParams {
|
||||
data[k] = v
|
||||
fields = append(fields, k)
|
||||
}
|
||||
|
||||
sort.Strings(fields)
|
||||
buf := pool.BytesBuffer().Get()
|
||||
defer pool.BytesBuffer().Put(buf)
|
||||
buf.WriteByte(tokens.OpenCurlyBracket)
|
||||
enc := json.NewEncoder(buf)
|
||||
for i, f := range fields {
|
||||
if i > 0 {
|
||||
buf.WriteRune(tokens.Comma)
|
||||
}
|
||||
buf.WriteRune(tokens.DoubleQuote)
|
||||
buf.WriteString(f)
|
||||
buf.WriteString(`":`)
|
||||
v := data[f]
|
||||
var encoded []byte
|
||||
switch v := v.(type) {
|
||||
case []byte:
|
||||
buf.WriteRune(tokens.DoubleQuote)
|
||||
buf.WriteString(base64.EncodeToString(v))
|
||||
buf.WriteRune(tokens.DoubleQuote)
|
||||
default:
|
||||
if err := enc.Encode(v); err != nil {
|
||||
return nil, fmt.Errorf(`failed to encode value for field %s: %w`, f, err)
|
||||
var err error
|
||||
encoded, err = json.Marshal(base64.EncodeToString(v))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, k, err)
|
||||
}
|
||||
default:
|
||||
var err error
|
||||
encoded, err = json.Marshal(v)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to marshal field %q: %w`, k, err)
|
||||
}
|
||||
buf.Truncate(buf.Len() - 1)
|
||||
}
|
||||
pairs = append(pairs, fieldPair{Name: k, Value: encoded})
|
||||
}
|
||||
|
||||
sort.Slice(pairs, func(i, j int) bool {
|
||||
return pairs[i].Name < pairs[j].Name
|
||||
})
|
||||
|
||||
return pairs, nil
|
||||
}
|
||||
|
||||
func (h *ecdsaPrivateKey) MarshalJSON() ([]byte, error) {
|
||||
buf := pool.BytesBuffer().Get()
|
||||
defer pool.BytesBuffer().Put(buf)
|
||||
pairs, err := h.makePairs()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to make pairs: %w`, err)
|
||||
}
|
||||
buf.WriteByte(tokens.OpenCurlyBracket)
|
||||
|
||||
for i, pair := range pairs {
|
||||
if i > 0 {
|
||||
buf.WriteByte(tokens.Comma)
|
||||
}
|
||||
buf.WriteByte('"')
|
||||
buf.WriteString(pair.Name)
|
||||
buf.WriteString(`": `)
|
||||
buf.Write(pair.Value.([]byte))
|
||||
}
|
||||
buf.WriteByte(tokens.CloseCurlyBracket)
|
||||
ret := make([]byte, buf.Len())
|
||||
copy(ret, buf.Bytes())
|
||||
putFieldPairList(pairs)
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
@@ -1430,3 +1627,10 @@ func init() {
|
||||
func ECDSAStandardFieldsFilter() KeyFilter {
|
||||
return ecdsaStandardFields
|
||||
}
|
||||
|
||||
func init() {
|
||||
registry.Register(jwa.EC().String(), registry.Constructor{
|
||||
Public: func() any { return newECDSAPublicKey() },
|
||||
Private: func() any { return newECDSAPrivateKey() },
|
||||
})
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user