< Summary

Information
Class: ArturRios.Jwt.JwkThumbprint
Assembly: ArturRios.Jwt
File(s): /home/runner/work/dotnet-jwt/dotnet-jwt/src/JwkThumbprint.cs
Line coverage
100%
Covered lines: 19
Uncovered lines: 0
Coverable lines: 19
Total lines: 63
Line coverage: 100%
Branch coverage
100%
Covered branches: 2
Total branches: 2
Branch coverage: 100%
Method coverage
100%
Covered methods: 4
Fully covered methods: 4
Total methods: 4
Method coverage: 100%
Full method coverage: 100%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
CanonicalJson(...)100%11100%
WriteMembers(...)100%22100%
Compute(...)100%11100%
ForP256(...)100%11100%

File(s)

/home/runner/work/dotnet-jwt/dotnet-jwt/src/JwkThumbprint.cs

#LineLine coverage
 1using System.Buffers.Text;
 2using System.Security.Cryptography;
 3using System.Text;
 4using System.Text.Json;
 5
 6namespace ArturRios.Jwt;
 7
 8/// <summary>
 9/// RFC 7638 JWK thumbprints: the SHA-256 of a key's required members, in lexicographic order, as
 10/// compact JSON, base64url-encoded. It is the id an ES256 key carries, so the id is derived from the
 11/// key itself and never configured.
 12/// </summary>
 13internal static class JwkThumbprint
 14{
 15    /// <summary>
 16    /// The JSON object RFC 7638 hashes: the given members only, ordered by name (ordinal, which is
 17    /// Unicode code point order for the ASCII names a JWK uses), with no whitespace.
 18    /// </summary>
 19    public static string CanonicalJson(IReadOnlyDictionary<string, string> requiredMembers)
 20    {
 13021        using var buffer = new MemoryStream();
 22
 13023        using (var writer = new Utf8JsonWriter(buffer))
 24        {
 13025            writer.WriteStartObject();
 64826            WriteMembers(writer, requiredMembers.OrderBy(member => member.Key, StringComparer.Ordinal));
 13027            writer.WriteEndObject();
 13028        }
 29
 13030        return Encoding.UTF8.GetString(buffer.ToArray());
 13031    }
 32
 33    /// <summary>
 34    /// Writes string members into the object the writer has open, in the order given. Shared with the
 35    /// key set writer so a JWK is serialized by one piece of code.
 36    /// </summary>
 37    /// <remarks>
 38    /// Every name is a fixed ASCII JWK member name and every value is base64url or a fixed ASCII word, so
 39    /// the writer's default encoder, which escapes <c>+ &lt; &gt; &amp; '</c> and non-ASCII characters,
 40    /// never changes them, and RFC 7638's unescaped serialization is what is hashed.
 41    /// </remarks>
 42    internal static void WriteMembers(Utf8JsonWriter writer, IEnumerable<KeyValuePair<string, string>> members)
 43    {
 150444        foreach (var (name, value) in members)
 45        {
 60946            writer.WriteString(name, value);
 47        }
 14348    }
 49
 50    /// <summary>The thumbprint of a key given by its required members.</summary>
 51    public static string Compute(IReadOnlyDictionary<string, string> requiredMembers) =>
 12952        Base64Url.EncodeToString(SHA256.HashData(Encoding.UTF8.GetBytes(CanonicalJson(requiredMembers))));
 53
 54    /// <summary>The thumbprint of a P-256 public key given by its base64url coordinates.</summary>
 55    public static string ForP256(string x, string y) =>
 12856        Compute(new Dictionary<string, string>
 12857        {
 12858            ["crv"] = P256.JwkCurveName,
 12859            ["kty"] = "EC",
 12860            ["x"] = x,
 12861            ["y"] = y
 12862        });
 63}