diff --git a/src/OrasProject.Oras/Content/Digest.cs b/src/OrasProject.Oras/Content/Digest.cs index bd0cb15..2e8c035 100644 --- a/src/OrasProject.Oras/Content/Digest.cs +++ b/src/OrasProject.Oras/Content/Digest.cs @@ -13,6 +13,7 @@ using OrasProject.Oras.Exceptions; using System; +using System.Collections.Generic; using System.Security.Cryptography; using System.Text.RegularExpressions; @@ -23,6 +24,9 @@ internal static class Digest private const string _digestRegexPattern = @"[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+"; private static readonly Regex _digestRegex = new Regex(_digestRegexPattern, RegexOptions.Compiled); + // List of registered and supported algorithms as per the specification + private static readonly HashSet _supportedAlgorithms = new HashSet { "sha256", "sha512" }; + /// /// Verifies the digest header and throws an exception if it is invalid. /// @@ -33,6 +37,13 @@ internal static string Validate(string? digest) { throw new InvalidDigestException($"Invalid digest: {digest}"); } + + var algorithm = digest.Split(':')[0]; + if (!_supportedAlgorithms.Contains(algorithm)) + { + throw new InvalidDigestException($"Unrecognized, unregistered or unsupported digest algorithm: {algorithm}"); + } + return digest; } @@ -48,4 +59,4 @@ internal static string ComputeSHA256(byte[] content) var output = $"sha256:{BitConverter.ToString(hash).Replace("-", "")}"; return output.ToLower(); } -} +} \ No newline at end of file diff --git a/tests/OrasProject.Oras.Tests/Content/ContentTest.cs b/tests/OrasProject.Oras.Tests/Content/ContentTest.cs index 1a583e8..d39c937 100644 --- a/tests/OrasProject.Oras.Tests/Content/ContentTest.cs +++ b/tests/OrasProject.Oras.Tests/Content/ContentTest.cs @@ -12,6 +12,7 @@ // limitations under the License. using OrasProject.Oras.Content; +using OrasProject.Oras.Exceptions; using System.Text; using Xunit; @@ -19,6 +20,7 @@ namespace OrasProject.Oras.Tests.Content; public class CalculateDigest { + /// /// This method tests if the digest is calculated properly /// @@ -30,4 +32,39 @@ public void VerifiesIfDigestMatches() var calculateHelloWorldDigest = Digest.ComputeSHA256(content); Assert.Equal(helloWorldDigest, calculateHelloWorldDigest); } + + /// + /// This method tests if the digest validation passes for registered algorithms + /// + [Theory] + [InlineData("sha256:6c3c624b58dbbcd3c0dd82b4c53f04194d1247c6eebdaab7c610cf7d66709b3b")] + [InlineData("sha512:401b09eab3c013d4ca54922bb802bec8fd5318192b0a75f201d8b372742c513925d98f76b340d9e59a4efdc45db9f5c640a21831b3d08be")] + public void Validate_ReturnsDigest_ForRegisteredAlgorithms(string validDigest) + { + var result = Digest.Validate(validDigest); + Assert.Equal(validDigest, result); + } + + /// + /// This method tests if the digest validation throws an exception for unregistered or unsupported algorithms + /// + [Theory] + [InlineData("md5:098f6bcd4621d373cade4e832627b4f6")] // MD5, unregistered digest + [InlineData("sha1:3b8b5a6b79f6d1114a7b7e95b3e3bc74dd1b6a2a")] // SHA-1, unregistered digest + [InlineData("multihash+base58:QmRZxt2b1FVZPNqd8hsiykDL3TdBDeTSPX9Kv46HmX4Gx8")] // Multihash, unregistered digest + public void Validate_ThrowsException_ForUnregisteredAlgorithms(string invalidDigest) + { + Assert.Throws(() => Digest.Validate(invalidDigest)); + } + + /// + /// This method tests if the digest validation throws an exception for various invalid digest formats + /// + [Theory] + [InlineData("sha256:")] // Missing encoded portion + [InlineData("sha256+b64u!LCa0a2j_xo_5m0U8HTBBNBNCLXBkg7-g-YpeiGJm564")] // Invalid character in encoded portion + public void Validate_ThrowsException_ForInvalidDigestFormats(string invalidDigest) + { + Assert.Throws(() => Digest.Validate(invalidDigest)); + } }