Two CI failures introduced by the test additions in this PR:
1. testEd25519VerifyAcceptedWhenSodiumLoaded calls setSignature() to inject
an externally-produced Ed25519 signature (since Algorithm::sign() rejects
Ed25519 by design). setSignature was declared protected, so the test
couldn't call it from outside the class hierarchy. Make it public —
SignedRequest lives in the OC\ private namespace, so this widens
internal-only visibility, not the public API surface.
2. testParseKeyRejectsContradictoryAlg expected firebase/php-jwt's
JWK::parseKey() to throw on a kty=OKP/crv=Ed25519/alg=ES256 key. The
current firebase/php-jwt version does not validate that coherence at
parse time, so the test now fails to see any throwable. The actual
security check happens at Algorithm::verify() time and is covered by
testVerifyEd25519KeyAgainstES256Alg right above it. Skip the parse-time
test with a comment pointing at the verify-time coverage.
Signed-off-by: Micke Nordin <kano@sunet.se>
Apps implementing OCM endpoints via OCMEndpointRequestEvent (e.g.
SUNET/nextcloud-ocm_request_share for request-share, nextcloud/contacts
for invite-accepted) need to apply the same identity check that the
built-in addShare and receiveNotification handlers apply, so it makes
sense to make it publicly accessible.
It also allows us to refactor RequestHandlerController::confirmSignedOrigin
to use the new public method and drop the confirmNotificationIdentity helper.
Signed-off-by: Micke Nordin <kano@sunet.se>
This commit switches the default signature algorithm to
ecdsa-p256-sha256 instead of Ed25519. This allows us to make sodium
optional again, and we only pull it in to use it for verifying incomming
signatures. If sodium is not installed, we throw on Ed25519 signatures
instead. At least it is easy for most people to make their Nextcloud
install fully RFC compliant by installing sodium.
I also renamed all the Ed25519 function names to be more precis, using
Jwks for the JSON Web Keys, and RFC9421 for the http-signature code,
where it is needed to distinguish from draft-cavage signatures.
Signed-off-by: Micke Nordin <kano@sunet.se>
Throw when one of the headers are empty
Enumerate all the allowed algorithms in th NATIVE constant
Co-authored-by: Carl Schwan <carl@carlschwan.eu>
Signed-off-by: Micke Nordin <kano@sunet.se>
Use constants instead of 0/1
Also fix PHPDoc to use correct return values.
Co-authored-by: Carl Schwan <carl@carlschwan.eu>
Signed-off-by: Micke Nordin <kano@sunet.se>
OCM dual-stack integration of RFC 9421 alongside the existing cavage
publicKey path:
- OCMSignatoryManager: Ed25519 active/pending/retiring slot rotation
backed by numbered pool appkeys, getRemoteKey for inbound JWK lookup
with per-origin cache + cache-miss refetch, and getLocalEd25519Jwks
for the JWKS endpoint.
- Rfc9421SignatoryManager: per-call wrapper that swaps in the Ed25519
signatory and toggles `rfc9421.format`.
- OCMJwksHandler: serves /.well-known/jwks.json (RFC 7517) when signing
is enabled.
- OCMDiscoveryService: advertises `http-sig` in capabilities when
signing is enabled, and picks the signature scheme on outbound based
on the remote's advertised capabilities.
- Application.php: register the JWKS well-known handler.
Signed-off-by: Micke Nordin <kano@sunet.se>
Add Manager::generateEd25519AppKey: persist a sodium-generated
Ed25519 keypair (raw 32-byte public, 64-byte secret) under the same
appdata layout the existing RSA path uses. Used by OCMSignatoryManager
for the slotted RFC 9421 signing keys.
Signed-off-by: Micke Nordin <kano@sunet.se>
Add the RFC 9421 (HTTP Message Signatures) sign/verify path alongside
the existing draft-cavage implementation:
- Algorithm: sodium for Ed25519, JWT::sign for RSA / ECDSA, ecdsaRawToDer
for the ECDSA wire format. JWK parsing via JWK::parseKey.
- SignatureBase: RFC 9421 §2.5 base construction for the derived
components OCM uses plus plain HTTP fields.
- ContentDigest: RFC 9530 helpers used as a covered component.
- Rfc9421IncomingSignedRequest / Rfc9421OutgoingSignedRequest:
request models. Parsing of Signature-Input / Signature delegates
to gapple\\StructuredFields\\Parser.
- IJwkResolvingSignatoryManager: capability bit signatory managers
advertise to participate in RFC 9421 verification.
- OcmProfile: OCM-mandated dictionary label.
- SignatureManager: dispatch to RFC 9421 inbound when Signature-Input
is present, outbound when rfc9421.format is set.
Plus tests for each primitive and a full round-trip across the model.
Signed-off-by: Micke Nordin <kano@sunet.se>
Followup to #59646: guard against null reaching strtolower() in both
AppConfig and UserConfig getValueBool(). Also aligns AppConfig with the
(string) cast added in UserConfig by the original PR.
Signed-off-by: skjnldsv <skjnldsv@protonmail.com>
- Attempt delete before logging the warning, so the warning only fires
when we know recovery will succeed
- Log an error (not silently return) when delete itself fails
- Use catch (\Exception) without variable (PHP 8)
- Replace willReturnArgument(1) with explicit willReturn(true) in test
- Add blank lines between logical blocks in test for readability
Signed-off-by: Anna Larch <anna@nextcloud.com>
AI-Assisted-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When the appstore cache file exists but getContent() throws a
GenericFileException (I/O error or OS-level permission failure), explicitly
delete the file and recreate it before writing fresh data — mirroring the
NotFoundException recovery path. If deletion itself fails, return [] cleanly.
Previously, the unhandled exception caused the entire apps settings page to
crash. The new test covers both the recovery path and deletion failure.
Signed-off-by: Anna Larch <anna@nextcloud.com>
AI-Assisted-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- resolves https://github.com/nextcloud/server/issues/55659
First we need to properly handle shared storages,
because there the quota is the quota of the user who owns the nodes,
not the user who shared the nodes.
Second if no user can be fetched then we cannot get the global storage
info, thus in this case (public share?) we need to safe-guard.
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
Injecting IAppConfig as a constructor parameter into Encryption\Manager (and
through it into EncryptionWrapper) caused IDBConnection to be eagerly resolved
during OC::init() on PHP <8.4 (no lazy ghost objects). This happened before
maintenance:install's Sqlite::initialize() wrote dbname to config.php, so the
connection latched onto the default database name ('owncloud') instead of the
configured one ('nextcloud'). All migrations then ran against owncloud.db, and
the subsequent enable_all.php process opened an empty nextcloud.db — crashing
with "no such table: oc_appconfig".
Remove IAppConfig from Manager's constructor and Server.php's factory closure.
Resolve it lazily via Server::get(IAppConfig::class) inside
EncryptionWrapper::wrapStorage(), which is only called after the filesystem is
set up, never during bootstrap.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Stephen Cuppett <steve@cuppett.com>
Rewrite conditional flow to use early-return guards: skip IDisableEncryptionStorage,
skip the root mount, respect encryptHomeStorage for HomeMountPoints. Uses IAppConfig
for the encryptHomeStorage setting with a legacy string fallback for the upgrade window.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Stephen Cuppett <steve@cuppett.com>
PHP 8.4 made passing non-strings to strtolower() a fatal TypeError.
getTypedValue() can return a non-string under certain conditions, causing
the strtolower() call to throw. The (string) cast guards against this.
Signed-off-by: There Is No TIme <37583483+thereisnotime@users.noreply.github.com>
Signed-off-by: Joas Schilling <coding@schilljs.com>
Maximum value of a server ID is 511 (9 bits) and not 1023.
Also adjust SetupCheck
Signed-off-by: Benjamin Gaussorgues <benjamin.gaussorgues@nextcloud.com>