mirror of
https://github.com/nextcloud/server.git
synced 2026-05-28 04:32:30 -04:00
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>
76 lines
2.7 KiB
PHP
76 lines
2.7 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
/**
|
|
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
|
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
*/
|
|
|
|
namespace Test\Security\Signature\Rfc9421;
|
|
|
|
use OC\Security\Signature\Rfc9421\ContentDigest;
|
|
use Test\TestCase;
|
|
|
|
class ContentDigestTest extends TestCase {
|
|
public function testComputeRoundTrip(): void {
|
|
$body = '{"hello":"world"}';
|
|
$header = ContentDigest::compute($body, ContentDigest::ALGO_SHA256);
|
|
$this->assertStringStartsWith('sha-256=:', $header);
|
|
$this->assertStringEndsWith(':', $header);
|
|
$this->assertTrue(ContentDigest::verify($header, $body));
|
|
}
|
|
|
|
public function testDifferentBodyFails(): void {
|
|
$header = ContentDigest::compute('hello', ContentDigest::ALGO_SHA256);
|
|
$this->assertFalse(ContentDigest::verify($header, 'goodbye'));
|
|
}
|
|
|
|
public function testSha512(): void {
|
|
$header = ContentDigest::compute('payload', ContentDigest::ALGO_SHA512);
|
|
$this->assertStringStartsWith('sha-512=:', $header);
|
|
$this->assertTrue(ContentDigest::verify($header, 'payload'));
|
|
}
|
|
|
|
public function testParseMultipleAlgorithmsAcceptsAnyMatch(): void {
|
|
$body = 'data';
|
|
$sha256 = ContentDigest::compute($body, ContentDigest::ALGO_SHA256);
|
|
$sha512 = ContentDigest::compute($body, ContentDigest::ALGO_SHA512);
|
|
$header = $sha256 . ', ' . $sha512;
|
|
$this->assertTrue(ContentDigest::verify($header, $body));
|
|
}
|
|
|
|
public function testFailsIfAnyRecognisedAlgorithmMismatches(): void {
|
|
// All recognised digests must agree. A correct sha-256 alongside a
|
|
// wrong sha-512 is treated as an attack on the weaker algorithm,
|
|
// not as a successful match on the stronger one.
|
|
$body = 'data';
|
|
$sha256 = ContentDigest::compute($body, ContentDigest::ALGO_SHA256);
|
|
$wrongSha512 = 'sha-512=:' . base64_encode(hash('sha512', 'tampered', true)) . ':';
|
|
$this->assertFalse(ContentDigest::verify($sha256 . ', ' . $wrongSha512, $body));
|
|
// And the inverse ordering.
|
|
$this->assertFalse(ContentDigest::verify($wrongSha512 . ', ' . $sha256, $body));
|
|
}
|
|
|
|
public function testUnknownAlgorithmIsIgnored(): void {
|
|
$body = 'data';
|
|
$sha256 = ContentDigest::compute($body, ContentDigest::ALGO_SHA256);
|
|
$header = 'md5=:abcd:, ' . $sha256;
|
|
$this->assertTrue(ContentDigest::verify($header, $body));
|
|
}
|
|
|
|
public function testEmptyHeaderFails(): void {
|
|
$this->assertFalse(ContentDigest::verify('', 'body'));
|
|
}
|
|
|
|
public function testGarbageHeaderFails(): void {
|
|
$this->assertFalse(ContentDigest::verify('not a digest', 'body'));
|
|
}
|
|
|
|
public function testParseExtractsRawBytes(): void {
|
|
$header = ContentDigest::compute('abc', ContentDigest::ALGO_SHA256);
|
|
$parsed = ContentDigest::parse($header);
|
|
$this->assertArrayHasKey('sha-256', $parsed);
|
|
$this->assertSame(hash('sha256', 'abc', true), $parsed['sha-256']);
|
|
}
|
|
}
|