summary refs log tree commit diff
path: root/extra/admin-api/Interop/Spacebar.Interop.Cdn.Signing/CdnSigningService.cs
blob: 1fb32b962e3bee947b19944cfa4ed32cdf5cdb41 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
using System.Security.Cryptography;
using ArcaneLibs.Extensions;
using Microsoft.Extensions.Logging;

namespace Spacebar.Interop.Cdn.Signing;

public class CdnSigningService(ILogger<CdnSigningService> logger, byte[] signatureKey, bool requireUserAgent, bool requireIpAddress, TimeSpan expiryTime) {
    public CdnSignatureResult Sign(CdnSignature data) {
        if (requireIpAddress && string.IsNullOrEmpty(data.IpAddress)) {
            logger.LogWarning("Signing request missing required IP address");
            throw new ArgumentException("IP address is required for signing");
        }

        if (requireUserAgent && string.IsNullOrEmpty(data.UserAgent)) {
            logger.LogWarning("Signing request missing required User-Agent");
            throw new ArgumentException("User-Agent is required for signing");
        }

        var now = DateTimeOffset.UtcNow;
        var expiresAt = now.Add(expiryTime);

        return Hash(new() {
            Path = data.Path,
            IpAddress = data.IpAddress,
            UserAgent = data.UserAgent,
            CreatedAt = now,
            ExpiresAt = expiresAt,
            Signature = null!
        });
    }

    private CdnSignatureResult Hash(CdnSignatureResult data) {
        byte[] signatureData = [
            .. data.Path.AsBytes(),
            .. data.CreatedAt.ToUnixTimeMilliseconds().ToString("x").AsBytes(),
            .. data.ExpiresAt.ToUnixTimeMilliseconds().ToString("x").AsBytes(),
            .. (requireIpAddress ? data.IpAddress?.AsBytes() : []) ?? [],
            .. (requireUserAgent ? data.UserAgent?.AsBytes() : []) ?? []
        ];
        var hash = HMACSHA256.HashData(signatureKey, signatureData).AsHexString().Replace(" ", "").ToLower();

        logger.LogTrace("Hash: creating new hash for {path}", data.Path);
        if (logger.IsEnabled(LogLevel.Trace)) {
            signatureData.HexDump();
        }

        var sr = new CdnSignatureResult() {
            Path = data.Path,
            IpAddress = data.IpAddress,
            UserAgent = data.UserAgent,
            CreatedAt = data.CreatedAt,
            ExpiresAt = data.ExpiresAt,
            Signature = hash,
        };

        logger.LogTrace("Hash: created new hash for {path}, valid between {start} .. {end}: {json}", data.Path, data.CreatedAt, data.ExpiresAt, sr.ToJson());

        return sr;
    }
}