Skip to content

Commit

Permalink
Optimize DkimPublicKeyRecordParser
Browse files Browse the repository at this point in the history
  • Loading branch information
tinohager committed Jan 27, 2025
1 parent 8d77a31 commit 62cfc74
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,39 @@
public sealed class BasicTest
{
[TestMethod]
public void TryParse_ValidDkimHeaderString1_ReturnsTrueAndPopulatesDataFragment()
public void TryParse_ValidDkimPublicKeyRecord1_ReturnsTrueAndPopulatesDataFragment()
{
var dkimPublicKeyRecord = "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuu8v3TAGlg2cKEVtHbqh5QfebUSdVp2qwH4NFaIG/rEsBshHI97fdP6TqiJCmpLhK8mHSQMit2HiHAEUApa0xAw7SI68XiBr6epKpTkHaUx27C/kjyuxYmGFOJy6mrDzeC2E5+Lp1u9QifjuBtUk78ORSA+EXeEMxssHy51NdHT0BlZGk1M+wXTxniQ2d198gDVjjqRGM433Q0AP6uSJac9LQj80tHkWrnr/bjct7EOdtF+6mDl4qjAaJTruk03Xt3Alaj+DIOPmnwP1mbPLQmK4blnzM7jwQc2kZz9gSJocc0nhs8KfuR6Xj23iSOJV+WEt6WoLoJzSl8/Dx5CKJwIDAQAB";

var isSuccessful = DkimPublicKeyRecordParser.TryParse(dkimPublicKeyRecord, out var dkimPublicKeyRecordDataFragment, out var parsingResults);

Assert.IsTrue(isSuccessful);
Assert.IsNotNull(dkimPublicKeyRecordDataFragment);
//Assert.IsNull(parsingResults, "ParsingResults is not null");
Assert.IsNull(parsingResults, "ParsingResults is not null");
}

[TestMethod]
public void TryParse_InvalidDkimPublicKeyRecord2_ReturnsTrueAndPopulatesDataFragment()
{
var dkimPublicKeyRecord = "v=DKIM1; k=rsa; p=";

var isSuccessful = DkimPublicKeyRecordParser.TryParse(dkimPublicKeyRecord, out var dkimPublicKeyRecordDataFragment, out var parsingResults);

Assert.IsTrue(isSuccessful);
Assert.IsNotNull(dkimPublicKeyRecordDataFragment);
Assert.IsNotNull(parsingResults, "ParsingResults is null");
}

[TestMethod]
public void TryParse_InvalidDkimPublicKeyRecord3_ReturnsTrueAndPopulatesDataFragment()
{
var dkimPublicKeyRecord = "v=DKIM1; k=rsa; p=1";

var isSuccessful = DkimPublicKeyRecordParser.TryParse(dkimPublicKeyRecord, out var dkimPublicKeyRecordDataFragment, out var parsingResults);

Assert.IsTrue(isSuccessful);
Assert.IsNotNull(dkimPublicKeyRecordDataFragment);
Assert.IsNotNull(parsingResults, "ParsingResults is null");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
public sealed class BasicTest
{
[TestMethod]
public void TryParse_ValidDkimHeaderString1_ReturnsTrueAndPopulatesDataFragment()
public void TryParse_ValidDkimSignature1_ReturnsTrueAndPopulatesDataFragment()
{
var dkimSignature = "v=1; a=rsa-sha256; c=relaxed/simple; q=dns/txt; d=domain.com; [email protected]; s=mailjet; x=1737017824; h=message-id:from:from:reply-to:to:to:subject:subject:date:date:list-unsubscribe-post:list-unsubscribe:feedback-id:x-csa-complaints:x-mj-mid:x-report-abuse-to:mime-version:content-type; bh=TyN/x6t3AOfI298rgJAgZHgdWcq/XLISGen5nN3NLAc=; b=HLCLiikV92Ku/k9mGlZM0bmqPjKggGnMI0igqhXmPRzPJUC+5SUWRS6/FLUpxbX6AUGJRDYQnKKMtp6uZkYVuKG8SPZ01cUkvIiiAkczb4bK6IVvPbZOnsWqHkD6EvK3TrpIhgFfGLlcG+zIwgdDZ3O++uhpJkIX1WJlkXZYqxQ=";

Expand Down
116 changes: 105 additions & 11 deletions src/Nager.EmailAuthentication/DkimPublicKeyRecordParser.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Nager.EmailAuthentication.Handlers;
using Nager.EmailAuthentication.Models;
using System.Buffers.Text;

namespace Nager.EmailAuthentication
{
Expand Down Expand Up @@ -35,34 +36,31 @@ public static bool TryParse(
{
var handlers = new Dictionary<string, MappingHandler<DkimPublicKeyRecordDataFragment>>
{
{
"v", new MappingHandler<DkimPublicKeyRecordDataFragment>
{
Map = (dataFragment, value) => dataFragment.Version = value,
}
},
{
"p", new MappingHandler<DkimPublicKeyRecordDataFragment>
{
Map = (dataFragment, value) => dataFragment.PublicKeyData = value,
Validate = ValidatePublicKeyData
}
},
{
"n", new MappingHandler<DkimPublicKeyRecordDataFragment>
"v", new MappingHandler<DkimPublicKeyRecordDataFragment>
{
Map = (dataFragment, value) => dataFragment.Notes = value
Map = (dataFragment, value) => dataFragment.Version = value,
Validate = ValidateVersion
}
},
{
"k", new MappingHandler<DkimPublicKeyRecordDataFragment>
{
Map = (dataFragment, value) => dataFragment.KeyType = value
Map = (dataFragment, value) => dataFragment.KeyType = value,
Validate = ValidateKeyType
}
},
{
"g", new MappingHandler<DkimPublicKeyRecordDataFragment>
"n", new MappingHandler<DkimPublicKeyRecordDataFragment>
{
Map = (dataFragment, value) => dataFragment.Granularity = value
Map = (dataFragment, value) => dataFragment.Notes = value
}
},
{
Expand All @@ -71,10 +69,106 @@ public static bool TryParse(
Map = (dataFragment, value) => dataFragment.Flags = value
}
},
{
"g", new MappingHandler<DkimPublicKeyRecordDataFragment>
{
Map = (dataFragment, value) => dataFragment.Granularity = value,
Validate = ValidateGranularity
}
}
};

var parserBase = new KeyValueParserBase<DkimPublicKeyRecordDataFragment>(handlers);
return parserBase.TryParse(dkimPublicKeyRecord, out dkimPublicKeyRecordDataFragment, out parsingResults);
}

private static ParsingResult[] ValidatePublicKeyData(ValidateRequest validateRequest)
{
if (string.IsNullOrEmpty(validateRequest.Value))
{
return
[
new ParsingResult
{
Status = ParsingStatus.Critical,
Message = "Public key data is emtpty"
}
];
}

if (Base64.IsValid(validateRequest.Value))
{
return [];
}

return
[
new ParsingResult
{
Status = ParsingStatus.Critical,
Message = "Invalid Public Key data, not base64 encoded"
}
];
}

private static ParsingResult[] ValidateVersion(ValidateRequest validateRequest)
{
var errors = new List<ParsingResult>();

if (string.IsNullOrEmpty(validateRequest.Value))
{
return [
new ParsingResult
{
Status = ParsingStatus.Critical,
Message = "DKIM record is invalid: it must start with 'v=DKIM1' if it specified"
}
];
}

if (validateRequest.Value.Equals("DKIM1", StringComparison.OrdinalIgnoreCase))
{
return [];
}

return [
new ParsingResult
{
Status = ParsingStatus.Critical,
Message = "DKIM record is invalid: it must start with 'v=DKIM1'."
}
];
}

private static ParsingResult[] ValidateGranularity(ValidateRequest validateRequest)
{
return
[
new ParsingResult
{
Status = ParsingStatus.Warning,
Message = "Granularity is deprecated."
}
];
}

private static ParsingResult[] ValidateKeyType(ValidateRequest validateRequest)
{
var allowedKeyTypes = new string[] { "rsa", "ed25519 " };

if (allowedKeyTypes.Contains(validateRequest.Value, StringComparer.CurrentCultureIgnoreCase))
{
return [];
}

return
[
new ParsingResult
{
Status = ParsingStatus.Critical,
Message = "The type of the key is invalid"
}
];
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

<TargetFrameworks>net8.0;net9.0</TargetFrameworks>

<Version>1.4.0</Version>
<Version>1.4.1</Version>
</PropertyGroup>

<ItemGroup>
Expand Down

0 comments on commit 62cfc74

Please sign in to comment.