v1 major improvements and fixes and migration to Eclipse - #28
Draft
Nicolas-Peiffer wants to merge 60 commits into
Draft
v1 major improvements and fixes and migration to Eclipse#28Nicolas-Peiffer wants to merge 60 commits into
Nicolas-Peiffer wants to merge 60 commits into
Conversation
but for now crypto11's tag is rc1 Signed-off-by: Nicolas-Peiffer <102670102+Nicolas-Peiffer@users.noreply.github.com>
Signed-off-by: Nicolas-Peiffer <102670102+Nicolas-Peiffer@users.noreply.github.com>
Signed-off-by: Nicolas-Peiffer <102670102+Nicolas-Peiffer@users.noreply.github.com>
Signed-off-by: Nicolas-Peiffer <102670102+Nicolas-Peiffer@users.noreply.github.com>
Signed-off-by: Nicolas-Peiffer <102670102+Nicolas-Peiffer@users.noreply.github.com>
…weProtectedHeader and JweRfc7516Compact respectively. Jwe.Unmarshal inline DEPRECATED comment converted to standard GoDoc format
interfaces.go — Get(issuer, kid string) → Get(ctx context.Context, issuer, kid string) keystore.go — TrustKeyStoreImpl.Get updated; ctx is _ since the store is in-memory jwks_truststore.go — httpClient interface: Get(url) → Do(req *http.Request); JwksTrustStore.Get accepts and threads ctx via http.NewRequestWithContext; TODO comment removed jwt_verifier.go — passes context.Background() (keeps JwtVerifier interface stable) jwks_truststore_test.go — mock updated to Do, all ioutil.NopCloser → io.NopCloser, all store.Get calls pass context.Background() keystore_test.go — same jwt_verifier_test.go — mock Get signature updated Signed-off-by: Nicolas-Peiffer <102670102+Nicolas-Peiffer@users.noreply.github.com>
…me.Now in the constructor. Verify now calls verifier.now() instead of time.Now()
… verifier.now = atUnix(...) — which works cleanly since the test is in the same package
Replaced the single overloaded rsaBitsToAlg (with its TODO) with two clear functions: rsaBitsToAlg for signing (PS family by NIST recommendation) and rsaAlgFromOps which picks AlgRSAOAEP when the operations include encrypt/decrypt, falling back to rsaBitsToAlg otherwise. Both JwkFromPrivateKey and JwkFromPublicKey now use rsaAlgFromOps, which also fixes the HSM path (hsm/asymmetric_decryption_key.go) that was silently getting a wrong PS algorithm for an encryption key
Replaced the // TODO: add symmetric verification. stub with an explanatory comment describing the actual design constraint: the VerificationKey interface includes Certificates() and MarshalPem() which have no meaningful implementation for symmetric keys
added identical doc comments to algToOptsMap (signer.go), gcmAlgToEncMap (jwe_direct_encryptor_aead.go), and cbcAlgToEncMap (jwe_direct_encryptor_block.go) making it explicit they are initialized once and must not be mutated at runtime.
CrvMLKEM512/768/1024 — parameter set identifiers Six AlgMLKEM* constants — three direct key-agreement (MLKEM768-KMAC256, etc.) and three with AES key-wrapping
…per the draft spec
EncapsPubMlKemKey interface — Encapsulate() → (kemCiphertext, sharedSecret, err) DecapsPrivMlKemKey interface — Decapsulate(kemCiphertext) → (sharedSecret, err) + Encapsulator() DecapsPrivMlKemKeyStore interface — Get(kid) → DecapsPrivMlKemKey
EncapsPubMlKemKey struct — JWK for public key (kty=LWE, crv, x) DecapsPrivMlKemKey struct — JWK for private key (adds d seed) UnmarshalJwk updated to dispatch on KtyLWE, distinguishing pub vs priv by whether d is populated
Encapsulate() calls ek.Encapsulate() (method, not package function; returns sharedKey, ciphertext in that order — reordered to match the interface's kemCiphertext, sharedSecret) Jwk() reconstructs a jose.EncapsPubMlKemKey from the live key bytes NewEncapsPubMlKemKeyImpl validates key_ops, switches on curve, returns a clear error for ML-KEM-512 mlkem_private.go — DecapsPrivMlKemKeyImpl + GenerateMlKemKeyPair mlkem_private.go — DecapsPrivMlKemKeyImpl + GenerateMlKemKeyPair Decapsulate() delegates to dk.Decapsulate(ciphertext) Encapsulator() derives the public key from the live decapsulation key (no need to store separately) GenerateMlKemKeyPair(crv, kid, alg) generates a fresh pair and returns both impls, ready for use
hsm/mlkem_key.go EncapsPubMlKemHsmKey — wraps crypto11.MLKEMEncapsulator; calls Encapsulate() on the HSM, extracts shared secret via ss.Bytes() (requires CKA_EXTRACTABLE=true in template), returns (kemCt, ssBytes) DecapsPrivMlKemHsmKey — wraps crypto11.MLKEMKeyPair; private key stays on HSM, only 32-byte shared secret crosses the boundary for the software KMAC KDF sharedSecretTemplate() — transient CKO_SECRET_KEY / CKK_AES / 32 bytes / CKA_EXTRACTABLE=true (same as k8s-kms-plugin) paramSetToAlg — maps MLKEM512/768/1024 → AlgMLKEM512KMAC128/AlgMLKEM768KMAC256/AlgMLKEM1024KMAC256 Both types have compile-time interface assertions hsm/mlkem_key_store.go DecapsPrivMlKemHsmKeyStore — Get(kid) calls ctx.FindMLKEMKeyPair([]byte(kid), nil), wraps result in DecapsPrivMlKemHsmKey
…yPair that returns an unrecognised paramSet value, confirming the constructor rejects it with the right message. No HSM needed since the failure happens before any crypto11 call. TestDecapsPrivMlKemHsmKeyStore_Get_InvalidHexKid — passes a non-hex string to Get with a nil context; the function returns before ever touching ctx, so no HSM needed there either.
Add the shared CI standard used across pkcs11-go / crypto11 / gose: CI (build/vet/test), CodeQL, govulncheck, Gitleaks, OpenSSF Scorecard, dependency review, golangci-lint and grouped Dependabot, plus .golangci.yml. All go tooling runs in vendor mode so the local `replace` directives resolve from vendor/. Remove the obsolete .travis.yml (Go 1.13). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
draft-ietf-jose-pqc-kem-06 dropped JOSE from its scope and now covers COSE only (retitled "PQ KEMs for COSE"); there is no standards-track way left to express ML-KEM in JWE. Rather than ship a bespoke, non-interoperable JWE envelope, pull ML-KEM support until a JOSE-side draft exists again. Removed: - mlkem_private.go, mlkem_public.go - jwe_mlkem_encryptor.go, jwe_mlkem_decryptor.go, jwe_mlkem_test.go - hsm/mlkem_key.go, hsm/mlkem_key_store.go, hsm/mlkem_test.go Trimmed (ML-KEM-specific pieces only, rest of each file untouched): - interfaces.go: EncapsPubMlKemKey, DecapsPrivMlKemKey, DecapsPrivMlKemKeyStore - jose/types.go: AlgMLKEM*, CrvMLKEM*, KtyLWE - jose/jwk.go: EncapsPubMlKemKey/DecapsPrivMlKemKey JWK types and the KtyLWE case in UnmarshalJwk - jose/jwe.go: the ek header field
Update go.mod, all internal imports, and doc/link references (README, NOTICES, golangci-lint, Makefile) from github.com/ThalesGroup/gose to github.com/eclipse-keypont/gose.
Pin GitHub Actions to the same commit SHAs used in pkcs11-go for supply-chain consistency, replace the deprecated golint Makefile target with golangci-lint (adding lint-fix), and add the missing Go Reference badge to the README.
crypto11 moved to a real /v2 module path (breaking API: new PKCS#11 binding, ML-KEM support). Update gose's dependency accordingly and use it as the integration test ahead of tagging crypto11 v2.0.0 stable. - go.mod/go.sum: require github.com/eclipse-keypont/crypto11/v2 v2.0.0-rc3 (fetched for real, no local replace) - hsm/*.go: update the two crypto11 imports to the /v2 path - Makefile: fix the notices ignore-list entry to the new import path - Update go toolchain to go1.26.5 Dropping the local pkcs11-go replace as a side effect removed the last reason CI needed vendor mode. vendor/ was never actually committed (gitignored), so ci.yml/codeql.yml/lint.yml/govulncheck.yml setting GOFLAGS=-mod=vendor meant a fresh checkout had no vendor tree to build against - this is fixed now, matching how crypto11 already resolved the same issue (a9d4f90). Verified clean: build, vet, full test suite, golangci-lint (0 issues), govulncheck (0 vulnerabilities) all pass without vendor mode or local replaces.
Each concrete JWK's UnmarshalJSON (PublicRsaKey, PrivateRsaKey, PublicEcKey, PrivateEcKey, OctSecretKey) set err to ErrUnexpectedKeyType when the JSON kty field didn't match the target type, but then unconditionally overwrote err with the result of CheckConsistency(). A genuine kty mismatch (e.g. unmarshalling an EC key into a PublicRsaKey) was silently discarded whenever CheckConsistency() happened to pass, so callers never saw the error. Found while fixing ineffassign findings from golangci-lint.
Resolves all 78 findings from `make lint` (bodyclose, errcheck, errorlint, gofmt, goimports, gosec, ineffassign, misspell, prealloc, revive, staticcheck, unconvert, unused): - close the JWKS HTTP response body and check/wrap Close errors - justify the two intentional file-path-from-caller reads with #nosec G304 - add missing doc comments on exported types/methods (revive) - drop dead code (unused base64EncodeUInt32 helper, unused cekAlg field) - fix several tests that dropped or duplicated error checks (ineffassign) - rename a local variable shadowing the len builtin - drop unnecessary type conversions, preallocate a slice
Rewrites the module path to github.com/eclipse-keypont/gose/v2, as required by Go's semantic import versioning for major version >=2, and updates all internal imports, golangci-lint local-prefixes, the go-licenses ignore list, and the pkg.go.dev README badge to match. Reflects the breaking API surface introduced by the crypto11/v2 and pkcs11-go migration (replacing miekg/pkcs11).
Documents the v2.0.0 breaking changes (pkcs11-go/crypto11-v2 migration, /v2 module path, ML-KEM removal), fixes, and additions in one place, and links it from a changelog badge and an "upgrading from v1?" note so README readers know it exists.
gose has been v0.x with no stability contract since its start. The /v2 module path convention exists to let a real v1 and v2 coexist for consumers migrating between them; there's no v1 of gose to coexist with, so it buys nothing here and just adds import-path churn. Depending on crypto11/v2 doesn't require gose itself to be v2 — a library's major version reflects its own API stability, not its dependencies'. This is gose's first stable release, so it's v1.0.0. Reverts the module path, internal imports, golangci-lint local-prefix, go-licenses ignore list, and README badges back to the non-suffixed path, and reframes CHANGELOG.md as v0.x -> v1.0.0.
…S.md Two separate hazards in the same file. The `command -v` guards on lint and govulncheck asked whether a name resolved, not whether it ran. Under goenv that is always true: a shim stays on PATH for every tool it has ever seen, so the guard passed and the recipe then died with "goenv: '<tool>' command not found" instead of printing the install hint. The shared $(call require,...) macro probes by running the tool and treats exit 127 — what both a missing binary and a dead shim return — as missing. lint-fix and notices had no guard at all and are now covered. More seriously, notices redirected go-licenses straight into NOTICES.md, so any failure truncated the committed file to zero bytes before anyone saw the error. That is not hypothetical: go-licenses v1.6.0 currently fails against Go 1.26 on stdlib packages (google/go-licenses#128), so running the target destroys NOTICES.md and reports nothing useful. It now writes a temp file and moves it into place only on success, matching crypto11. The underlying go-licenses breakage is untouched here — `make notices` still cannot regenerate the file, it just no longer destroys it in the attempt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
RFC 7518 §4.3 defines "RSA-OAEP" as OAEP with SHA-1 and "RSA-OAEP-256" as
the SHA-256 variant. gose emitted "RSA-OAEP" for both, because the three
constants AlgRSAOAEP, AlgRSAOAEPSHA1 and AlgRSAOAEPSHA2 all held the same
string — the SHA-256 variant had no wire representation at all, so it could
not have been labelled correctly even in principle. The output round-tripped
against itself, but a conformant recipient derives SHA-1 from the header and
fails to unwrap the CEK, so these JWEs were not portable.
AlgRSAOAEPSHA2 becomes "RSA-OAEP-256". New rsa_oaep_alg.go holds the
alg<->digest mapping in one place, with OaepHashFromAlg / OaepAlgFromHash
exported for callers that need to inspect or label a JWE.
The encryptor now derives the header alg from the digest passed to Encrypt
rather than copying the recipient JWK's alg, and rejects digests RFC 7518
registers no OAEP algorithm for. Previously any digest was accepted and
labelled "RSA-OAEP", so SHA-512 produced a JWE that no alg value described.
The constructor also rejects RSA keys whose alg is not OAEP, checked after
the key-type parse so a non-RSA key still yields ErrInvalidKeyType.
The decryptor derives the digest from the header when passed crypto.Hash(0),
which is the conformant path. Its alg check is now family-based, since the
header names a variant while key.Algorithm() names the family.
Generated JWKs and hsm.AsymmetricDecryptionKey keep "RSA-OAEP" as their alg,
naming the OAEP family: the per-message digest comes from the JWE header, so
one key still serves both variants. SoftHSMv2 implements only SHA-1.
Existing ciphertexts stay readable. Passing a non-zero crypto.Hash to Decrypt
still overrides the header, which is how JWEs written by earlier versions
("RSA-OAEP" in the header, SHA-256 on the wire) are read; no data at rest
needs re-encrypting. Old headers cannot be rewritten in place instead, since
the protected header is the AEAD's additional authenticated data.
Tests: the RFC 7516 A.1 KAT now also passes with the digest derived from the
header; both variants round-trip with their emitted alg asserted; and
TestJweRsaKeyEncryptionDecryptorImpl_Decrypt_LegacyMislabelledJwe builds a
genuine pre-fix JWE and asserts header-derived decryption fails while the
override succeeds.
The CBOM in assets/cbom gains an RSA-OAEP-256 entry. It previously listed one
RSA-OAEP asset named RSA/ECB/OAEPWithSHA-1AndMGF1Padding, so SHA-256 wrapping
— always reachable, since the digest was a runtime parameter — was missing
from the inventory. RSA-OAEP-256 shares id-RSAES-OAEP (1.2.840.113549.1.1.7),
which carries the digest in RSAES-OAEP-params rather than a separate arc, and
uses the XML Encryption 1.1 URI, the 1.0 one being fixed to SHA-1.
cbom.cyclonedx.json was hand-edited to match algo-list.csv, xbom-manager not
being released yet.
BREAKING CHANGE: jose.AlgRSAOAEPSHA2 is now "RSA-OAEP-256" rather than
"RSA-OAEP". JWEs produced with crypto.SHA256 carry the new value and are
readable by other RFC 7518 implementations; peers pinned to the previous
mislabelled output will not interoperate until updated.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.