mirror of
https://github.com/nextcloud/server.git
synced 2026-11-01 19:11:49 +01:00
PHP 8.3 allows class constants to declare a type. Adopt it where doing so cannot break third-party apps. Typing an inheritable constant is a hard BC break: a subclass that redeclares it untyped fails to load with "Type of C::FOO must be compatible with P::FOO of type string". Changes are therefore limited to constants that cannot be redeclared by a subclass: - private const (not inherited) - final public/protected const - constants declared in a final class or an enum Interface constants, trait constants, and public/protected constants in non-final (including abstract) classes are left untyped, as is all of lib/public (OCP) and lib/unstable (NCU). One review-requested exception: the public OBJECT_PREFIX/OBJECT_SUFFIX constants of the app-internal CalDAV import helpers (TextImporter, XmlImporter) are typed as well; these classes are not public API and have no subclasses. Only string, int and array are used. float is avoided because it would silently coerce an int literal and change === comparisons. No constant name or value is modified: every changed line adds only the type token, so runtime behaviour is unchanged. Signed-off-by: Git'Fellow <12234510+solracsf@users.noreply.github.com>
73 lines
2.0 KiB
PHP
73 lines
2.0 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
/**
|
|
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
|
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
*/
|
|
|
|
namespace OC\Security\Signature\Rfc9421;
|
|
|
|
use InvalidArgumentException;
|
|
|
|
/** RFC 9530 `Content-Digest` helpers; covered by RFC 9421 §7.2.5 in OCM signatures. */
|
|
final class ContentDigest {
|
|
public const string ALGO_SHA256 = 'sha-256';
|
|
public const string ALGO_SHA512 = 'sha-512';
|
|
|
|
public static function compute(string $body, string $algorithm = self::ALGO_SHA256): string {
|
|
$hashAlgorithm = self::hashAlgorithmFor($algorithm);
|
|
return $algorithm . '=:' . base64_encode(hash($hashAlgorithm, $body, true)) . ':';
|
|
}
|
|
|
|
/**
|
|
* True iff at least one recognised algorithm matches and none mismatch.
|
|
* Stricter than RFC 9530 §2's "any-match"; OCM treats mismatches as an
|
|
* attack on the weaker algorithm.
|
|
*/
|
|
public static function verify(string $header, string $body): bool {
|
|
$matched = false;
|
|
foreach (self::parse($header) as $algorithm => $digest) {
|
|
try {
|
|
$hashAlgorithm = self::hashAlgorithmFor($algorithm);
|
|
} catch (InvalidArgumentException) {
|
|
continue;
|
|
}
|
|
if (!hash_equals(hash($hashAlgorithm, $body, true), $digest)) {
|
|
return false;
|
|
}
|
|
$matched = true;
|
|
}
|
|
return $matched;
|
|
}
|
|
|
|
/** @return array<string, string> [algorithm => raw bytes] */
|
|
public static function parse(string $header): array {
|
|
$out = [];
|
|
foreach (explode(',', $header) as $entry) {
|
|
$entry = trim($entry);
|
|
if ($entry === '') {
|
|
continue;
|
|
}
|
|
if (!preg_match('#^([a-z0-9-]+)=:([A-Za-z0-9+/=]*):$#', $entry, $m)) {
|
|
continue;
|
|
}
|
|
$decoded = base64_decode($m[2], true);
|
|
if ($decoded === false) {
|
|
continue;
|
|
}
|
|
$out[strtolower($m[1])] = $decoded;
|
|
}
|
|
return $out;
|
|
}
|
|
|
|
private static function hashAlgorithmFor(string $algorithm): string {
|
|
return match (strtolower($algorithm)) {
|
|
self::ALGO_SHA256 => 'sha256',
|
|
self::ALGO_SHA512 => 'sha512',
|
|
default => throw new InvalidArgumentException('unsupported content-digest algorithm: ' . $algorithm),
|
|
};
|
|
}
|
|
}
|