RFC 9421 signatures are verified, not refused
WordPress's ActivityPub plugin (and Ghost and Fedify) sign with RFC 9421 first and fall back to draft-cavage only after a refusal, so each first delivery cost two requests and a 401 in our statistics. Now a request carrying Signature-Input is verified as an HTTP message signature: its covered components (the method and our own public target, the body's Content-Digest), its created and expires, with the actor's RSA key under PKCS#1 v1.5 or PSS. Deliveries and signed fetches both take it; the ledger names the scheme (rfc9421:rsa-v1_5-sha256). What PrivaPub sends stays draft-cavage, which every server reads. Ed25519 waits for FEP-521a keys. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
This commit is contained in:
1 parent
26dac40720
commit
c5a69d240a
10 files changed
+423
-30
No files matched your search
@@ -25,23 +25,28 @@ namespace PrivaPub.Federation.Signing
|
||||
readonly IRemoteActorService _remoteActors;
|
||||
readonly DbEntities _dbEntities;
|
||||
|
||||
public SignedFetchAuthorizer(IRemoteActorService remoteActors, DbEntities dbEntities)
|
||||
readonly ILocalActorService _localActors;
|
||||
|
||||
public SignedFetchAuthorizer(IRemoteActorService remoteActors, DbEntities dbEntities, ILocalActorService localActors = default)
|
||||
{
|
||||
_remoteActors = remoteActors;
|
||||
_dbEntities = dbEntities;
|
||||
_localActors = localActors;
|
||||
}
|
||||
|
||||
// the actor whose signature (draft-cavage, or RFC 9421 when there is a base address to check its target against)
|
||||
// verifies on the request
|
||||
public async Task<ForeignAvatar> Requester(HttpRequest request, CancellationToken token)
|
||||
{
|
||||
var parameters = HttpSignatures.Parse(request.Headers["Signature"].ToString());
|
||||
if (parameters == default || HttpSignatures.CheckRequest(request, parameters, body: default) != default)
|
||||
var signature = RequestSignature.Of(request);
|
||||
if (signature == default || signature.Problem(request, body: default) != default)
|
||||
return default;
|
||||
var signingString = HttpSignatures.SigningString(request, parameters);
|
||||
var actor = await _remoteActors.GetActorByKeyId(parameters.KeyId, refresh: false, token);
|
||||
if (actor != default && HttpSignatures.Verify(actor.PublicKey, signingString, parameters.Signature))
|
||||
var signed = signature.Signed(request, _localActors?.BaseAddress ?? $"{request.Scheme}://{request.Host}");
|
||||
var actor = await _remoteActors.GetActorByKeyId(signature.KeyId, refresh: false, token);
|
||||
if (actor != default && signature.VerifiedBy(actor.PublicKey, signed))
|
||||
return actor;
|
||||
actor = await _remoteActors.GetActorByKeyId(parameters.KeyId, refresh: true, token);
|
||||
return actor != default && HttpSignatures.Verify(actor.PublicKey, signingString, parameters.Signature) ? actor : default;
|
||||
actor = await _remoteActors.GetActorByKeyId(signature.KeyId, refresh: true, token);
|
||||
return actor != default && signature.VerifiedBy(actor.PublicKey, signed) ? actor : default;
|
||||
}
|
||||
|
||||
public async Task<bool> MayRead(PostEntity post, ForeignAvatar requester, CancellationToken token)
|
||||
|
||||
Reference in new issue
Block a user