validate digest, fix #13

This commit is contained in:
Nicolas Constant 2020-12-28 00:43:02 -05:00
parent 5d86ebf618
commit 20a05e9e9f
No known key found for this signature in database
GPG key ID: 1E9F677FB01A5688
4 changed files with 30 additions and 20 deletions

View file

@ -90,7 +90,7 @@ namespace BirdsiteLive.Domain
var date = DateTime.UtcNow.ToUniversalTime(); var date = DateTime.UtcNow.ToUniversalTime();
var httpDate = date.ToString("r"); var httpDate = date.ToString("r");
var digest = ComputeSha256Hash(json); var digest = _cryptoService.ComputeSha256Hash(json);
var signature = _cryptoService.SignAndGetSignatureHeader(date, actorUrl, targetHost, digest, usedInbox); var signature = _cryptoService.SignAndGetSignatureHeader(date, actorUrl, targetHost, digest, usedInbox);
@ -113,15 +113,6 @@ namespace BirdsiteLive.Domain
return response.StatusCode; return response.StatusCode;
} }
static string ComputeSha256Hash(string rawData)
{
// Create a SHA256
using (SHA256 sha256Hash = SHA256.Create())
{
// ComputeHash - returns byte array
byte[] bytes = sha256Hash.ComputeHash(Encoding.UTF8.GetBytes(rawData));
return Convert.ToBase64String(bytes);
}
}
} }
} }

View file

@ -1,4 +1,5 @@
using System; using System;
using System.Security.Cryptography;
using System.Text; using System.Text;
using BirdsiteLive.Domain.Factories; using BirdsiteLive.Domain.Factories;
@ -8,6 +9,7 @@ namespace BirdsiteLive.Domain
{ {
string GetUserPem(string id); string GetUserPem(string id);
string SignAndGetSignatureHeader(DateTime date, string actor, string host, string digest, string inbox); string SignAndGetSignatureHeader(DateTime date, string actor, string host, string digest, string inbox);
string ComputeSha256Hash(string data);
} }
public class CryptoService : ICryptoService public class CryptoService : ICryptoService
@ -49,5 +51,16 @@ namespace BirdsiteLive.Domain
var header = "keyId=\"" + actor + "\",algorithm=\"rsa-sha256\",headers=\"(request-target) host date digest\",signature=\"" + sig64 + "\""; var header = "keyId=\"" + actor + "\",algorithm=\"rsa-sha256\",headers=\"(request-target) host date digest\",signature=\"" + sig64 + "\"";
return header; return header;
} }
public string ComputeSha256Hash(string data)
{
// Create a SHA256
using (SHA256 sha256Hash = SHA256.Create())
{
// ComputeHash - returns byte array
byte[] bytes = sha256Hash.ComputeHash(Encoding.UTF8.GetBytes(data));
return Convert.ToBase64String(bytes);
}
}
} }
} }

View file

@ -18,8 +18,8 @@ namespace BirdsiteLive.Domain
public interface IUserService public interface IUserService
{ {
Actor GetUser(TwitterUser twitterUser); Actor GetUser(TwitterUser twitterUser);
Task<bool> FollowRequestedAsync(string signature, string method, string path, string queryString, Dictionary<string, string> requestHeaders, ActivityFollow activity); Task<bool> FollowRequestedAsync(string signature, string method, string path, string queryString, Dictionary<string, string> requestHeaders, ActivityFollow activity, string body);
Task<bool> UndoFollowRequestedAsync(string signature, string method, string path, string queryString, Dictionary<string, string> requestHeaders, ActivityUndoFollow activity); Task<bool> UndoFollowRequestedAsync(string signature, string method, string path, string queryString, Dictionary<string, string> requestHeaders, ActivityUndoFollow activity, string body);
} }
public class UserService : IUserService public class UserService : IUserService
@ -79,10 +79,10 @@ namespace BirdsiteLive.Domain
return user; return user;
} }
public async Task<bool> FollowRequestedAsync(string signature, string method, string path, string queryString, Dictionary<string, string> requestHeaders, ActivityFollow activity) public async Task<bool> FollowRequestedAsync(string signature, string method, string path, string queryString, Dictionary<string, string> requestHeaders, ActivityFollow activity, string body)
{ {
// Validate // Validate
var sigValidation = await ValidateSignature(activity.actor, signature, method, path, queryString, requestHeaders); var sigValidation = await ValidateSignature(activity.actor, signature, method, path, queryString, requestHeaders, body);
if (!sigValidation.SignatureIsValidated) return false; if (!sigValidation.SignatureIsValidated) return false;
// Save Follow in DB // Save Follow in DB
@ -130,10 +130,10 @@ namespace BirdsiteLive.Domain
} }
public async Task<bool> UndoFollowRequestedAsync(string signature, string method, string path, string queryString, public async Task<bool> UndoFollowRequestedAsync(string signature, string method, string path, string queryString,
Dictionary<string, string> requestHeaders, ActivityUndoFollow activity) Dictionary<string, string> requestHeaders, ActivityUndoFollow activity, string body)
{ {
// Validate // Validate
var sigValidation = await ValidateSignature(activity.actor, signature, method, path, queryString, requestHeaders); var sigValidation = await ValidateSignature(activity.actor, signature, method, path, queryString, requestHeaders, body);
if (!sigValidation.SignatureIsValidated) return false; if (!sigValidation.SignatureIsValidated) return false;
// Save Follow in DB // Save Follow in DB
@ -162,7 +162,7 @@ namespace BirdsiteLive.Domain
return result == HttpStatusCode.Accepted; return result == HttpStatusCode.Accepted;
} }
private async Task<SignatureValidationResult> ValidateSignature(string actor, string rawSig, string method, string path, string queryString, Dictionary<string, string> requestHeaders) private async Task<SignatureValidationResult> ValidateSignature(string actor, string rawSig, string method, string path, string queryString, Dictionary<string, string> requestHeaders, string body)
{ {
//Check Date Validity //Check Date Validity
var date = requestHeaders["date"]; var date = requestHeaders["date"];
@ -171,6 +171,12 @@ namespace BirdsiteLive.Domain
var delta = Math.Abs((d - now).TotalSeconds); var delta = Math.Abs((d - now).TotalSeconds);
if (delta > 30) return new SignatureValidationResult { SignatureIsValidated = false }; if (delta > 30) return new SignatureValidationResult { SignatureIsValidated = false };
//Check Digest
var digest = requestHeaders["digest"];
var digestHash = digest.Split(new [] {"SHA-256="},StringSplitOptions.RemoveEmptyEntries).LastOrDefault();
var calculatedDigestHash = _cryptoService.ComputeSha256Hash(body);
if (digestHash != calculatedDigestHash) return new SignatureValidationResult { SignatureIsValidated = false };
//Check Signature //Check Signature
var signatures = rawSig.Split(','); var signatures = rawSig.Split(',');
var signature_header = new Dictionary<string, string>(); var signature_header = new Dictionary<string, string>();

View file

@ -111,7 +111,7 @@ namespace BirdsiteLive.Controllers
case "Follow": case "Follow":
{ {
var succeeded = await _userService.FollowRequestedAsync(signature, r.Method, r.Path, var succeeded = await _userService.FollowRequestedAsync(signature, r.Method, r.Path,
r.QueryString.ToString(), RequestHeaders(r.Headers), activity as ActivityFollow); r.QueryString.ToString(), RequestHeaders(r.Headers), activity as ActivityFollow, body);
if (succeeded) return Accepted(); if (succeeded) return Accepted();
else return Unauthorized(); else return Unauthorized();
} }
@ -119,7 +119,7 @@ namespace BirdsiteLive.Controllers
if (activity is ActivityUndoFollow) if (activity is ActivityUndoFollow)
{ {
var succeeded = await _userService.UndoFollowRequestedAsync(signature, r.Method, r.Path, var succeeded = await _userService.UndoFollowRequestedAsync(signature, r.Method, r.Path,
r.QueryString.ToString(), RequestHeaders(r.Headers), activity as ActivityUndoFollow); r.QueryString.ToString(), RequestHeaders(r.Headers), activity as ActivityUndoFollow, body);
if (succeeded) return Accepted(); if (succeeded) return Accepted();
else return Unauthorized(); else return Unauthorized();
} }