Finish implementing and test HttpSignatureService
This commit is contained in:
parent
032113492c
commit
47d384332d
@ -1,6 +1,9 @@
|
||||
<?php
|
||||
namespace ActivityPub\Auth;
|
||||
namespace ActivityPub\Crypto;
|
||||
|
||||
use DateTime;
|
||||
use ActivityPub\Utils\DateTimeProvider;
|
||||
use ActivityPub\Utils\SimpleDateTimeProvider;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\HeaderUtils;
|
||||
|
||||
@ -15,13 +18,50 @@ class HttpSignatureService
|
||||
'host',
|
||||
'date',
|
||||
);
|
||||
|
||||
public function sign( Request $request, string $privateKey, $headers = self::DEFAULT_HEADERS )
|
||||
|
||||
const REPLAY_THRESHOLD = 300;
|
||||
|
||||
/**
|
||||
* @var DateTimeProvider
|
||||
*/
|
||||
private $dateTimeProvider;
|
||||
|
||||
/**
|
||||
* Constructs a new HttpSignatureService
|
||||
*
|
||||
* @param DateTimeProvider $dateTimeProvider The DateTimeProvider,
|
||||
* defaults to SimpleDateTimeProvider
|
||||
*/
|
||||
public function __construct( DateTimeProvider $dateTimeProvider = null )
|
||||
{
|
||||
// To generate a signature for a request:
|
||||
// 1. put together the signing string from the headers list
|
||||
// 2. generate an RSA-sha256 signature of the signing string using the private key
|
||||
// 3. return the signature base64-encoded
|
||||
if ( ! $dateTimeProvider ) {
|
||||
$dateTimeProvider = new SimpleDateTimeProvider();
|
||||
}
|
||||
$this->dateTimeProvider = $dateTimeProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a signature given the request and private key
|
||||
*
|
||||
* @param Request $request The request to be signed
|
||||
* @param string $privateKey The private key to use to sign the request
|
||||
* @param string $keyId The id of the signing key
|
||||
* @param array $headers The headers to use in the signature
|
||||
* (default ['(request-target)', 'host', 'date'])
|
||||
* @return string The Signature header value
|
||||
*/
|
||||
public function sign( Request $request, string $privateKey, string $keyId,
|
||||
$headers = self::DEFAULT_HEADERS )
|
||||
{
|
||||
$headers = array_map( 'strtolower', $headers );
|
||||
$signingString = $this->getSigningString( $request, $headers );
|
||||
$keypair = RsaKeypair::fromPrivateKey( $privateKey );
|
||||
$signature = base64_encode( $keypair->sign( $signingString, 'rsa256' ) );
|
||||
$headersStr = implode( ' ', $headers );
|
||||
return "keyId=\"$keyId\"," .
|
||||
"algorithm=\"rsa-sha256\"," .
|
||||
"headers=\"$headersStr\"," .
|
||||
"signature=\"$signature\"";
|
||||
}
|
||||
|
||||
/**
|
||||
@ -33,9 +73,18 @@ class HttpSignatureService
|
||||
*/
|
||||
public function verify( Request $request, string $publicKey )
|
||||
{
|
||||
// TODO fail verification if date is > 300 seconds ago to prevent replay attacks
|
||||
$params = array();
|
||||
$headers = $request->headers;
|
||||
|
||||
if ( ! $headers->has( 'date' ) ) {
|
||||
return false;
|
||||
}
|
||||
$now = $this->dateTimeProvider->getTime( 'http-signature.verify' );
|
||||
$then = DateTime::createFromFormat( DateTime::RFC2822, $headers->get( 'date' ) );
|
||||
if ( abs( $now->getTimestamp() - $then->getTimestamp() ) > self::REPLAY_THRESHOLD ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( $headers->has( 'signature' ) ) {
|
||||
$params = $this->parseSignatureParams( $headers->get( 'signature' ) );
|
||||
} else if ( $headers->has( 'authorization' ) &&
|
||||
@ -43,19 +92,21 @@ class HttpSignatureService
|
||||
$paramsStr = substr( $headers->get( 'authorization' ), 10 );
|
||||
$params = $this->parseSignatureParams( $paramsStr );
|
||||
}
|
||||
|
||||
if ( count( $params ) === 0 ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$targetHeaders = array( 'date' );
|
||||
if ( array_key_exists( 'headers', $params ) ) {
|
||||
$targetHeaders = $params['headers'];
|
||||
}
|
||||
|
||||
$signingString = $this->getSigningString( $request, $targetHeaders );
|
||||
$signature = base64_decode( $params['signature'] );
|
||||
// TODO handle different algorithms here, checking the 'algorithm' param and the key headers
|
||||
return openssl_verify(
|
||||
$signingString, $signature, $publicKey, OPENSSL_ALGO_SHA256
|
||||
) === 1;
|
||||
$keypair = RsaKeypair::fromPublicKey( $publicKey );
|
||||
return $keypair->verify($signingString, $signature, 'sha256');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -71,7 +122,7 @@ class HttpSignatureService
|
||||
foreach ( $headers as $header ) {
|
||||
$component = "${header}: ";
|
||||
if ( $header == '(request-target)' ) {
|
||||
$method = strtolower( $request->method );
|
||||
$method = strtolower( $request->getMethod());
|
||||
$path = $request->getRequestUri();
|
||||
$component = $component . $method . ' ' . $path;
|
||||
} else {
|
||||
@ -81,7 +132,7 @@ class HttpSignatureService
|
||||
}
|
||||
$signingComponents[] = $component;
|
||||
}
|
||||
return implode( '\n', $signingComponents );
|
||||
return implode( "\n", $signingComponents );
|
||||
}
|
||||
|
||||
/**
|
||||
@ -94,12 +145,12 @@ class HttpSignatureService
|
||||
private function parseSignatureParams( string $paramsStr )
|
||||
{
|
||||
$params = array();
|
||||
$split = HeaderUtils::split( $paramsStr, ',= ' );
|
||||
$split = HeaderUtils::split( $paramsStr, ',=' );
|
||||
foreach ( $split as $paramArr ) {
|
||||
$paramName = $paramArr[0][0];
|
||||
$paramName = $paramArr[0];
|
||||
$paramValue = $paramArr[1];
|
||||
if ( count( $paramValue ) === 1 ) {
|
||||
$paramValue = $paramValue[0];
|
||||
if ( $paramName == 'headers' ) {
|
||||
$paramValue = explode(' ', $paramValue);
|
||||
}
|
||||
$params[$paramName] = $paramValue;
|
||||
}
|
@ -22,7 +22,7 @@ class RsaKeypair
|
||||
*/
|
||||
private $privateKey;
|
||||
|
||||
private function __construct( string $publicKey, string $privateKey )
|
||||
public function __construct( string $publicKey, string $privateKey )
|
||||
{
|
||||
$this->publicKey = $publicKey;
|
||||
$this->privateKey = $privateKey;
|
||||
@ -53,9 +53,11 @@ class RsaKeypair
|
||||
*
|
||||
* Throws a BadMethodCallException if this RsaKeypair does not have a private key.
|
||||
* @param string $data The data to sign
|
||||
* @param string $hash The hash algorithm to use. One of:
|
||||
* 'md2', 'md5', 'sha1', 'sha256', 'sha384', 'sha512'. Default: 'sha256'
|
||||
* @return string The signature
|
||||
*/
|
||||
public function sign( $data )
|
||||
public function sign( $data, $hash = 'sha256' )
|
||||
{
|
||||
if ( empty( $this->privateKey ) ) {
|
||||
throw new BadMethodCallException(
|
||||
@ -64,6 +66,7 @@ class RsaKeypair
|
||||
}
|
||||
$rsa = new RSA();
|
||||
$rsa->setHash( 'sha256' );
|
||||
$rsa->setSignatureMode(RSA::SIGNATURE_PKCS1);
|
||||
$rsa->loadKey( $this->privateKey );
|
||||
return $rsa->sign( $data );
|
||||
}
|
||||
@ -73,12 +76,15 @@ class RsaKeypair
|
||||
*
|
||||
* @param string $data The data
|
||||
* @param string $signature The signature
|
||||
* @param string $hash The hash algorithm to use. One of:
|
||||
* 'md2', 'md5', 'sha1', 'sha256', 'sha384', 'sha512'. Default: 'sha256'
|
||||
* @return bool
|
||||
*/
|
||||
public function verify( $data, $signature )
|
||||
public function verify( $data, $signature, $hash = 'sha256' )
|
||||
{
|
||||
$rsa = new RSA();
|
||||
$rsa->setHash( 'sha256' );
|
||||
$rsa->setHash( $hash );
|
||||
$rsa->setSignatureMode(RSA::SIGNATURE_PKCS1);
|
||||
$rsa->loadKey( $this->publicKey );
|
||||
return $rsa->verify( $data, $signature );
|
||||
}
|
||||
@ -108,5 +114,19 @@ class RsaKeypair
|
||||
{
|
||||
return new RsaKeypair( $publicKey, '' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an RsaKeypair with the given private key
|
||||
*
|
||||
* The generated RsaKeypair will be able to sign data but
|
||||
* not verify signatures, since it won't have a public key.
|
||||
*
|
||||
* @param string $privateKey The private key
|
||||
* @return RsaKeypair
|
||||
*/
|
||||
public function fromPrivateKey( string $privateKey)
|
||||
{
|
||||
return new RsaKeypair( '', $privateKey );
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
@ -1,73 +0,0 @@
|
||||
<?php
|
||||
// tests:
|
||||
// - signature for request that specifies a header but is missing that header
|
||||
// - signature for request with malformed Signature header
|
||||
namespace ActivityPub\Test\Auth;
|
||||
|
||||
use ActivityPub\Auth\HttpSignatureService;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
class HttpSignatureServiceTest extends TestCase
|
||||
{
|
||||
const PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----
|
||||
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDCFENGw33yGihy92pDjZQhl0C3
|
||||
6rPJj+CvfSC8+q28hxA161QFNUd13wuCTUcq0Qd2qsBe/2hFyc2DCJJg0h1L78+6
|
||||
Z4UMR7EOcpfdUE9Hf3m/hs+FUR45uBJeDK1HSFHD8bHKD6kv8FPGfJTotc+2xjJw
|
||||
oYi+1hqp1fIekaxsyQIDAQAB
|
||||
-----END PUBLIC KEY-----";
|
||||
|
||||
const PRIVATE_KEY = "-----BEGIN RSA PRIVATE KEY-----
|
||||
MIICXgIBAAKBgQDCFENGw33yGihy92pDjZQhl0C36rPJj+CvfSC8+q28hxA161QF
|
||||
NUd13wuCTUcq0Qd2qsBe/2hFyc2DCJJg0h1L78+6Z4UMR7EOcpfdUE9Hf3m/hs+F
|
||||
UR45uBJeDK1HSFHD8bHKD6kv8FPGfJTotc+2xjJwoYi+1hqp1fIekaxsyQIDAQAB
|
||||
AoGBAJR8ZkCUvx5kzv+utdl7T5MnordT1TvoXXJGXK7ZZ+UuvMNUCdN2QPc4sBiA
|
||||
QWvLw1cSKt5DsKZ8UETpYPy8pPYnnDEz2dDYiaew9+xEpubyeW2oH4Zx71wqBtOK
|
||||
kqwrXa/pzdpiucRRjk6vE6YY7EBBs/g7uanVpGibOVAEsqH1AkEA7DkjVH28WDUg
|
||||
f1nqvfn2Kj6CT7nIcE3jGJsZZ7zlZmBmHFDONMLUrXR/Zm3pR5m0tCmBqa5RK95u
|
||||
412jt1dPIwJBANJT3v8pnkth48bQo/fKel6uEYyboRtA5/uHuHkZ6FQF7OUkGogc
|
||||
mSJluOdc5t6hI1VsLn0QZEjQZMEOWr+wKSMCQQCC4kXJEsHAve77oP6HtG/IiEn7
|
||||
kpyUXRNvFsDE0czpJJBvL/aRFUJxuRK91jhjC68sA7NsKMGg5OXb5I5Jj36xAkEA
|
||||
gIT7aFOYBFwGgQAQkWNKLvySgKbAZRTeLBacpHMuQdl1DfdntvAyqpAZ0lY0RKmW
|
||||
G6aFKaqQfOXKCyWoUiVknQJAXrlgySFci/2ueKlIE1QqIiLSZ8V8OlpFLRnb1pzI
|
||||
7U1yQXnTAEFYM560yJlzUpOb1V4cScGd365tiSMvxLOvTA==
|
||||
-----END RSA PRIVATE KEY-----";
|
||||
|
||||
private $httpSignatureService;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->httpSignatureService = new HttpSignatureService();
|
||||
}
|
||||
|
||||
private static function getRequest()
|
||||
{
|
||||
$request = Request::create(
|
||||
'https://example.com/foo',
|
||||
Request::METHOD_POST,
|
||||
array( 'param' => 'value', 'pet' => 'dog' ),
|
||||
array(),
|
||||
array(),
|
||||
array(),
|
||||
'{"hello": "world"}'
|
||||
);
|
||||
$request->headers->set( 'host', 'example.com' );
|
||||
$request->headers->set( 'content-type', 'application/json' );
|
||||
$request->headers->set(
|
||||
'digest', 'SHA-256=X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE='
|
||||
);
|
||||
$request->headers->set( 'content-length', 18 );
|
||||
$request->headers->set( 'date', 'Sun, 05 Jan 2014 21:31:40 GMT' );
|
||||
return $request;
|
||||
}
|
||||
|
||||
public function testItVerifies()
|
||||
{
|
||||
$request = self::getRequest();
|
||||
$authHeader = 'Signature keyId="Test",algorithm="rsa-sha256",signature="SjWJWbWN7i0wzBvtPl8rbASWz5xQW6mcJmn+ibttBqtifLN7Sazz6m79cNfwwb8DMJ5cou1s7uEGKKCs+FLEEaDV5lp7q25WqS+lavg7T8hc0GppauB6hbgEKTwblDHYGEtbGmtdHgVCk9SuS13F0hZ8FD0k/5OxEPXe5WozsbM="';
|
||||
$request->headers->set( 'authorization', $authHeader );
|
||||
$verified = $this->httpSignatureService->verify( $request, self::PUBLIC_KEY );
|
||||
$this->assertTrue( $verified );
|
||||
}
|
||||
}
|
||||
?>
|
217
test/Crypto/HttpSignatureServiceTest.php
Normal file
217
test/Crypto/HttpSignatureServiceTest.php
Normal file
@ -0,0 +1,217 @@
|
||||
<?php
|
||||
namespace ActivityPub\Test\Crypto;
|
||||
|
||||
use DateTime;
|
||||
use ActivityPub\Crypto\HttpSignatureService;
|
||||
use ActivityPub\Test\TestUtils\TestDateTimeProvider;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
class HttpSignatureServiceTest extends TestCase
|
||||
{
|
||||
const PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----
|
||||
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDCFENGw33yGihy92pDjZQhl0C3
|
||||
6rPJj+CvfSC8+q28hxA161QFNUd13wuCTUcq0Qd2qsBe/2hFyc2DCJJg0h1L78+6
|
||||
Z4UMR7EOcpfdUE9Hf3m/hs+FUR45uBJeDK1HSFHD8bHKD6kv8FPGfJTotc+2xjJw
|
||||
oYi+1hqp1fIekaxsyQIDAQAB
|
||||
-----END PUBLIC KEY-----";
|
||||
|
||||
const PRIVATE_KEY = "-----BEGIN RSA PRIVATE KEY-----
|
||||
MIICXgIBAAKBgQDCFENGw33yGihy92pDjZQhl0C36rPJj+CvfSC8+q28hxA161QF
|
||||
NUd13wuCTUcq0Qd2qsBe/2hFyc2DCJJg0h1L78+6Z4UMR7EOcpfdUE9Hf3m/hs+F
|
||||
UR45uBJeDK1HSFHD8bHKD6kv8FPGfJTotc+2xjJwoYi+1hqp1fIekaxsyQIDAQAB
|
||||
AoGBAJR8ZkCUvx5kzv+utdl7T5MnordT1TvoXXJGXK7ZZ+UuvMNUCdN2QPc4sBiA
|
||||
QWvLw1cSKt5DsKZ8UETpYPy8pPYnnDEz2dDYiaew9+xEpubyeW2oH4Zx71wqBtOK
|
||||
kqwrXa/pzdpiucRRjk6vE6YY7EBBs/g7uanVpGibOVAEsqH1AkEA7DkjVH28WDUg
|
||||
f1nqvfn2Kj6CT7nIcE3jGJsZZ7zlZmBmHFDONMLUrXR/Zm3pR5m0tCmBqa5RK95u
|
||||
412jt1dPIwJBANJT3v8pnkth48bQo/fKel6uEYyboRtA5/uHuHkZ6FQF7OUkGogc
|
||||
mSJluOdc5t6hI1VsLn0QZEjQZMEOWr+wKSMCQQCC4kXJEsHAve77oP6HtG/IiEn7
|
||||
kpyUXRNvFsDE0czpJJBvL/aRFUJxuRK91jhjC68sA7NsKMGg5OXb5I5Jj36xAkEA
|
||||
gIT7aFOYBFwGgQAQkWNKLvySgKbAZRTeLBacpHMuQdl1DfdntvAyqpAZ0lY0RKmW
|
||||
G6aFKaqQfOXKCyWoUiVknQJAXrlgySFci/2ueKlIE1QqIiLSZ8V8OlpFLRnb1pzI
|
||||
7U1yQXnTAEFYM560yJlzUpOb1V4cScGd365tiSMvxLOvTA==
|
||||
-----END RSA PRIVATE KEY-----";
|
||||
|
||||
private $httpSignatureService;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$dateTimeProvider = new TestDateTimeProvider( array(
|
||||
'http-signature.verify' => DateTime::createFromFormat(
|
||||
DateTime::RFC2822, 'Sun, 05 Jan 2014 21:31:40 GMT'
|
||||
),
|
||||
) );
|
||||
$this->httpSignatureService = new HttpSignatureService( $dateTimeProvider );
|
||||
}
|
||||
|
||||
private static function getRequest()
|
||||
{
|
||||
$request = Request::create(
|
||||
'https://example.com/foo?param=value&pet=dog',
|
||||
Request::METHOD_POST,
|
||||
array(),
|
||||
array(),
|
||||
array(),
|
||||
array(),
|
||||
'{"hello": "world"}'
|
||||
);
|
||||
$request->headers->set( 'host', 'example.com' );
|
||||
$request->headers->set( 'content-type', 'application/json' );
|
||||
$request->headers->set(
|
||||
'digest', 'SHA-256=X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE='
|
||||
);
|
||||
$request->headers->set( 'content-length', 18 );
|
||||
$request->headers->set( 'date', 'Sun, 05 Jan 2014 21:31:40 GMT' );
|
||||
return $request;
|
||||
}
|
||||
|
||||
public function testItVerifies()
|
||||
{
|
||||
$testCases = array(
|
||||
array(
|
||||
'id' => 'defaultTest',
|
||||
'headers' => array(
|
||||
'Authorization' => 'Signature keyId="Test",algorithm="rsa-sha256",signature="SjWJWbWN7i0wzBvtPl8rbASWz5xQW6mcJmn+ibttBqtifLN7Sazz6m79cNfwwb8DMJ5cou1s7uEGKKCs+FLEEaDV5lp7q25WqS+lavg7T8hc0GppauB6hbgEKTwblDHYGEtbGmtdHgVCk9SuS13F0hZ8FD0k/5OxEPXe5WozsbM="',
|
||||
),
|
||||
'expectedResult' => true,
|
||||
),
|
||||
array(
|
||||
'id' => 'basicTest',
|
||||
'headers' => array(
|
||||
'Authorization' => 'Signature keyId="Test",algorithm="rsa-sha256",headers="(request-target) host date", signature="qdx+H7PHHDZgy4y/Ahn9Tny9V3GP6YgBPyUXMmoxWtLbHpUnXS2mg2+SbrQDMCJypxBLSPQR2aAjn7ndmw2iicw3HMbe8VfEdKFYRqzic+efkb3nndiv/x1xSHDJWeSWkx3ButlYSuBskLu6kd9Fswtemr3lgdDEmn04swr2Os0="',
|
||||
),
|
||||
'expectedResult' => true,
|
||||
),
|
||||
array(
|
||||
'id' => 'allHeadersTest',
|
||||
'headers' => array(
|
||||
'Authorization' => 'Signature keyId="Test",algorithm="rsa-sha256",headers="(request-target) host date content-type digest content-length",signature="vSdrb+dS3EceC9bcwHSo4MlyKS59iFIrhgYkz8+oVLEEzmYZZvRs8rgOp+63LEM3v+MFHB32NfpB2bEKBIvB1q52LaEUHFv120V01IL+TAD48XaERZFukWgHoBTLMhYS2Gb51gWxpeIq8knRmPnYePbF5MOkR0Zkly4zKH7s1dE="',
|
||||
),
|
||||
'expectedResult' => true,
|
||||
),
|
||||
array(
|
||||
'id' => 'defaultTestSigHeader',
|
||||
'headers' => array(
|
||||
'Signature' => 'keyId="Test",algorithm="rsa-sha256",signature="SjWJWbWN7i0wzBvtPl8rbASWz5xQW6mcJmn+ibttBqtifLN7Sazz6m79cNfwwb8DMJ5cou1s7uEGKKCs+FLEEaDV5lp7q25WqS+lavg7T8hc0GppauB6hbgEKTwblDHYGEtbGmtdHgVCk9SuS13F0hZ8FD0k/5OxEPXe5WozsbM="',
|
||||
),
|
||||
'expectedResult' => true,
|
||||
),
|
||||
array(
|
||||
'id' => 'basicTestSigHeader',
|
||||
'headers' => array(
|
||||
'Signature' => 'keyId="Test",algorithm="rsa-sha256",headers="(request-target) host date", signature="qdx+H7PHHDZgy4y/Ahn9Tny9V3GP6YgBPyUXMmoxWtLbHpUnXS2mg2+SbrQDMCJypxBLSPQR2aAjn7ndmw2iicw3HMbe8VfEdKFYRqzic+efkb3nndiv/x1xSHDJWeSWkx3ButlYSuBskLu6kd9Fswtemr3lgdDEmn04swr2Os0="',
|
||||
),
|
||||
'expectedResult' => true,
|
||||
),
|
||||
array(
|
||||
'id' => 'allHeadersTestSigHeader',
|
||||
'headers' => array(
|
||||
'Signature' => 'keyId="Test",algorithm="rsa-sha256",headers="(request-target) host date content-type digest content-length",signature="vSdrb+dS3EceC9bcwHSo4MlyKS59iFIrhgYkz8+oVLEEzmYZZvRs8rgOp+63LEM3v+MFHB32NfpB2bEKBIvB1q52LaEUHFv120V01IL+TAD48XaERZFukWgHoBTLMhYS2Gb51gWxpeIq8knRmPnYePbF5MOkR0Zkly4zKH7s1dE="',
|
||||
),
|
||||
'expectedResult' => true,
|
||||
),
|
||||
array(
|
||||
'id' => 'noHeaders',
|
||||
'headers' => array(),
|
||||
'expectedResult' => false,
|
||||
),
|
||||
array(
|
||||
'id' => 'headerMissing',
|
||||
'headers' => array(
|
||||
'Authorization' => 'Signature keyId="Test",algorithm="rsa-sha256",headers="(request-target) host date content-type digest content-length x-foo-header",signature="vSdrb+dS3EceC9bcwHSo4MlyKS59iFIrhgYkz8+oVLEEzmYZZvRs8rgOp+63LEM3v+MFHB32NfpB2bEKBIvB1q52LaEUHFv120V01IL+TAD48XaERZFukWgHoBTLMhYS2Gb51gWxpeIq8knRmPnYePbF5MOkR0Zkly4zKH7s1dE="',
|
||||
),
|
||||
'expectedResult' => false,
|
||||
),
|
||||
array(
|
||||
'id' => 'malformedHeader',
|
||||
'headers' => array(
|
||||
'Authorization' => 'not a real auth header',
|
||||
),
|
||||
'expectedResult' => false,
|
||||
),
|
||||
array(
|
||||
'id' => 'partlyMalformedHeader',
|
||||
'headers' => array(
|
||||
'Authorization' => 'Signature keyId="Test",algorithm="rsa-sha256",headers-malformed="(request-target) host date content-type digest content-length",signature="vSdrb+dS3EceC9bcwHSo4MlyKS59iFIrhgYkz8+oVLEEzmYZZvRs8rgOp+63LEM3v+MFHB32NfpB2bEKBIvB1q52LaEUHFv120V01IL+TAD48XaERZFukWgHoBTLMhYS2Gb51gWxpeIq8knRmPnYePbF5MOkR0Zkly4zKH7s1dE="',
|
||||
),
|
||||
'expectedResult' => false,
|
||||
),
|
||||
array(
|
||||
'id' => 'dateTooFarInPast',
|
||||
'headers' => array(
|
||||
'Authorization' => 'Signature keyId="Test",algorithm="rsa-sha256",headers="(request-target) host date", signature="qdx+H7PHHDZgy4y/Ahn9Tny9V3GP6YgBPyUXMmoxWtLbHpUnXS2mg2+SbrQDMCJypxBLSPQR2aAjn7ndmw2iicw3HMbe8VfEdKFYRqzic+efkb3nndiv/x1xSHDJWeSWkx3ButlYSuBskLu6kd9Fswtemr3lgdDEmn04swr2Os0="',
|
||||
),
|
||||
'expectedResult' => false,
|
||||
'currentDatetime' => DateTime::createFromFormat(
|
||||
DateTime::RFC2822, 'Sun, 05 Jan 2014 21:36:41 GMT'
|
||||
),
|
||||
),
|
||||
array(
|
||||
'id' => 'dateTooFarInFuture',
|
||||
'headers' => array(
|
||||
'Authorization' => 'Signature keyId="Test",algorithm="rsa-sha256",headers="(request-target) host date", signature="qdx+H7PHHDZgy4y/Ahn9Tny9V3GP6YgBPyUXMmoxWtLbHpUnXS2mg2+SbrQDMCJypxBLSPQR2aAjn7ndmw2iicw3HMbe8VfEdKFYRqzic+efkb3nndiv/x1xSHDJWeSWkx3ButlYSuBskLu6kd9Fswtemr3lgdDEmn04swr2Os0="',
|
||||
),
|
||||
'expectedResult' => false,
|
||||
'currentDatetime' => DateTime::createFromFormat(
|
||||
DateTime::RFC2822, 'Sun, 05 Jan 2014 21:26:39 GMT'
|
||||
),
|
||||
),
|
||||
);
|
||||
foreach ( $testCases as $testCase ) {
|
||||
if ( array_key_exists( 'currentDatetime', $testCase ) ) {
|
||||
$dateTimeProvider = new TestDateTimeProvider( array(
|
||||
'http-signature.verify' => $testCase['currentDatetime'],
|
||||
) );
|
||||
$this->httpSignatureService = new HttpSignatureService( $dateTimeProvider );
|
||||
}
|
||||
$request = self::getRequest();
|
||||
foreach ( $testCase['headers'] as $header => $value ) {
|
||||
$request->headers->set( $header, $value );
|
||||
}
|
||||
$actual = $this->httpSignatureService->verify( $request, self::PUBLIC_KEY );
|
||||
$this->assertEquals(
|
||||
$testCase['expectedResult'], $actual, "Error on test $testCase[id]"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function testItSigns()
|
||||
{
|
||||
$testCases = array(
|
||||
array(
|
||||
'id' => 'basicTest',
|
||||
'keyId' => 'Test',
|
||||
'expected' => 'keyId="Test",algorithm="rsa-sha256",headers="(request-target) host date",signature="qdx+H7PHHDZgy4y/Ahn9Tny9V3GP6YgBPyUXMmoxWtLbHpUnXS2mg2+SbrQDMCJypxBLSPQR2aAjn7ndmw2iicw3HMbe8VfEdKFYRqzic+efkb3nndiv/x1xSHDJWeSWkx3ButlYSuBskLu6kd9Fswtemr3lgdDEmn04swr2Os0="',
|
||||
),
|
||||
array(
|
||||
'id' => 'allHeadersTest',
|
||||
'keyId' => 'Test',
|
||||
'headers' => array(
|
||||
'(request-target)',
|
||||
'host',
|
||||
'date',
|
||||
'content-type',
|
||||
'digest',
|
||||
'content-length',
|
||||
),
|
||||
'expected' => 'keyId="Test",algorithm="rsa-sha256",headers="(request-target) host date content-type digest content-length",signature="vSdrb+dS3EceC9bcwHSo4MlyKS59iFIrhgYkz8+oVLEEzmYZZvRs8rgOp+63LEM3v+MFHB32NfpB2bEKBIvB1q52LaEUHFv120V01IL+TAD48XaERZFukWgHoBTLMhYS2Gb51gWxpeIq8knRmPnYePbF5MOkR0Zkly4zKH7s1dE="',
|
||||
),
|
||||
);
|
||||
foreach ( $testCases as $testCase ) {
|
||||
$request = self::getRequest();
|
||||
if ( array_key_exists( 'headers', $testCase ) ) {
|
||||
$actual = $this->httpSignatureService->sign(
|
||||
$request, self::PRIVATE_KEY, $testCase['keyId'], $testCase['headers']
|
||||
);
|
||||
} else {
|
||||
$actual= $this->httpSignatureService->sign(
|
||||
$request, self::PRIVATE_KEY, $testCase['keyId']
|
||||
);
|
||||
}
|
||||
$this->assertEquals(
|
||||
$testCase['expected'], $actual, "Error on test $testCase[id]"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
@ -39,7 +39,7 @@ class ObjectsServiceTest extends SQLiteTestCase
|
||||
);
|
||||
$this->entityManager = EntityManager::create( $dbParams, $dbConfig );
|
||||
$this->dateTimeProvider = new TestDateTimeProvider(
|
||||
new DateTime( "12:00" ), new DateTime( "12:01" )
|
||||
array( 'create' => new DateTime( "12:00" ), 'update' => new DateTime( "12:01" ) )
|
||||
);
|
||||
$this->objectsService = new ObjectsService(
|
||||
$this->entityManager, $this->dateTimeProvider
|
||||
|
@ -9,21 +9,20 @@ use ActivityPub\Utils\DateTimeProvider;
|
||||
*/
|
||||
class TestDateTimeProvider implements DateTimeProvider
|
||||
{
|
||||
protected $createTime;
|
||||
protected $updateTime;
|
||||
protected $context;
|
||||
|
||||
public function __construct( DateTime $createTime, DateTime $updateTime )
|
||||
/**
|
||||
* @param array $context An array mapping context strings to DateTime instances
|
||||
*/
|
||||
public function __construct( $context )
|
||||
{
|
||||
$this->createTime = $createTime;
|
||||
$this->updateTime = $updateTime;
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
public function getTime( $context = '' )
|
||||
{
|
||||
if ( $context === 'create' ) {
|
||||
return $this->createTime;
|
||||
} else if ( $context === 'update' ) {
|
||||
return $this->updateTime;
|
||||
if ( array_key_exists( $context, $this->context )) {
|
||||
return $this->context[$context];
|
||||
} else {
|
||||
return new DateTime( 'now' );
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user